Browse Source

Extension bridge (#6677)

Co-authored-by: Matt Rubens <[email protected]>
Chris Estreich 5 months ago
parent
commit
1d714c8ce4
32 changed files with 404 additions and 86 deletions
  1. 15 13
      pnpm-lock.yaml
  2. 28 1
      src/core/task/Task.ts
  3. 112 3
      src/core/webview/ClineProvider.ts
  4. 5 0
      src/core/webview/webviewMessageHandler.ts
  5. 36 28
      src/extension.ts
  6. 1 1
      src/package.json
  7. 1 0
      src/shared/ExtensionMessage.ts
  8. 1 0
      src/shared/WebviewMessage.ts
  9. 11 0
      src/utils/remoteControl.ts
  10. 52 19
      webview-ui/src/components/account/AccountView.tsx
  11. 74 13
      webview-ui/src/components/account/__tests__/AccountView.spec.tsx
  12. 3 1
      webview-ui/src/components/modes/ModesView.tsx
  13. 5 0
      webview-ui/src/context/ExtensionStateContext.tsx
  14. 3 0
      webview-ui/src/i18n/locales/ca/account.json
  15. 3 0
      webview-ui/src/i18n/locales/de/account.json
  16. 8 6
      webview-ui/src/i18n/locales/en/account.json
  17. 1 1
      webview-ui/src/i18n/locales/en/settings.json
  18. 3 0
      webview-ui/src/i18n/locales/es/account.json
  19. 3 0
      webview-ui/src/i18n/locales/fr/account.json
  20. 3 0
      webview-ui/src/i18n/locales/hi/account.json
  21. 3 0
      webview-ui/src/i18n/locales/id/account.json
  22. 3 0
      webview-ui/src/i18n/locales/it/account.json
  23. 3 0
      webview-ui/src/i18n/locales/ja/account.json
  24. 3 0
      webview-ui/src/i18n/locales/ko/account.json
  25. 3 0
      webview-ui/src/i18n/locales/nl/account.json
  26. 3 0
      webview-ui/src/i18n/locales/pl/account.json
  27. 3 0
      webview-ui/src/i18n/locales/pt-BR/account.json
  28. 3 0
      webview-ui/src/i18n/locales/ru/account.json
  29. 3 0
      webview-ui/src/i18n/locales/tr/account.json
  30. 3 0
      webview-ui/src/i18n/locales/vi/account.json
  31. 3 0
      webview-ui/src/i18n/locales/zh-CN/account.json
  32. 3 0
      webview-ui/src/i18n/locales/zh-TW/account.json

+ 15 - 13
pnpm-lock.yaml

@@ -563,8 +563,8 @@ importers:
         specifier: ^1.14.0
         version: 1.14.0([email protected])
       '@roo-code/cloud':
-        specifier: ^0.4.0
-        version: 0.4.0
+        specifier: ^0.5.0
+        version: 0.5.0
       '@roo-code/ipc':
         specifier: workspace:^
         version: link:../packages/ipc
@@ -3065,11 +3065,11 @@ packages:
     cpu: [x64]
     os: [win32]
 
-  '@roo-code/cloud@0.4.0':
-    resolution: {integrity: sha512-1a27RG2YjQFfsU5UlfbQnpj/K/6gYBcysp2FXaX9+VaaTh5ZzReQeHJ9uREnyE059zoFpVuNywwNxGadzyotWw==}
+  '@roo-code/cloud@0.5.0':
+    resolution: {integrity: sha512-4u6Ce2Rmr5a9nxhjGUMRRWUWhZc63EmF/UJ/+Az5/1JARMOp0kHN5Pwqz2QAgfD137+TFSBKQORpiN0GXrdt2w==}
 
-  '@roo-code/[email protected]2.0':
-    resolution: {integrity: sha512-AITVSV6WFd17jE8lQXFy7PkHam8M+mMkT7o9ipGZZ3cV7SbrnmL/Hg/HjkA9lkdJYbcC5dEK94py8KVBQn8Umw==}
+  '@roo-code/[email protected]4.0':
+    resolution: {integrity: sha512-3xbW4pYaCgWuHF5qOsiXpIcd281dlFTe1zboUGgcUUsB414Hu3pQI86PdgJxVGtZgxtaca0eHTQ2Sqjqq8nPlA==}
 
   '@sec-ant/[email protected]':
     resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
@@ -6267,8 +6267,8 @@ packages:
     resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
     engines: {node: '>=12'}
 
-  ioredis@5.7.0:
-    resolution: {integrity: sha512-NUcA93i1lukyXU+riqEyPtSEkyFq8tX90uL659J+qpCZ3rEdViB/APC58oAhIh3+bJln2hzdlZbBZsGNrlsR8g==}
+  ioredis@5.6.1:
+    resolution: {integrity: sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==}
     engines: {node: '>=12.22.0'}
 
   [email protected]:
@@ -12191,16 +12191,18 @@ snapshots:
   '@rollup/[email protected]':
     optional: true
 
-  '@roo-code/cloud@0.4.0':
+  '@roo-code/cloud@0.5.0':
     dependencies:
-      '@roo-code/types': 1.42.0
-      ioredis: 5.7.0
+      '@roo-code/types': 1.44.0
+      ioredis: 5.6.1
       p-wait-for: 5.0.2
       zod: 3.25.76
     transitivePeerDependencies:
       - supports-color
 
-  '@roo-code/[email protected]': {}
+  '@roo-code/[email protected]':
+    dependencies:
+      zod: 3.25.76
 
   '@sec-ant/[email protected]': {}
 
@@ -15963,7 +15965,7 @@ snapshots:
 
   [email protected]: {}
 
-  ioredis@5.7.0:
+  ioredis@5.6.1:
     dependencies:
       '@ioredis/commands': 1.3.0
       cluster-key-slot: 1.1.2

+ 28 - 1
src/core/task/Task.ts

@@ -32,7 +32,7 @@ import {
 	isBlockingAsk,
 } from "@roo-code/types"
 import { TelemetryService } from "@roo-code/telemetry"
-import { CloudService } from "@roo-code/cloud"
+import { CloudService, TaskBridgeService } from "@roo-code/cloud"
 
 // api
 import { ApiHandler, ApiHandlerCreateMessageMetadata, buildApiHandler } from "../../api"
@@ -118,6 +118,7 @@ export type TaskOptions = {
 	parentTask?: Task
 	taskNumber?: number
 	onCreated?: (task: Task) => void
+	enableTaskBridge?: boolean
 }
 
 export class Task extends EventEmitter<TaskEvents> implements TaskLike {
@@ -237,6 +238,9 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 	checkpointService?: RepoPerTaskCheckpointService
 	checkpointServiceInitializing = false
 
+	// Task Bridge
+	taskBridgeService?: TaskBridgeService
+
 	// Streaming
 	isWaitingForFirstChunk = false
 	isStreaming = false
@@ -268,6 +272,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 		parentTask,
 		taskNumber = -1,
 		onCreated,
+		enableTaskBridge = false,
 	}: TaskOptions) {
 		super()
 
@@ -345,6 +350,11 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 
 		this.toolRepetitionDetector = new ToolRepetitionDetector(this.consecutiveMistakeLimit)
 
+		// Initialize TaskBridgeService only if enabled
+		if (enableTaskBridge) {
+			this.taskBridgeService = TaskBridgeService.getInstance()
+		}
+
 		onCreated?.(this)
 
 		if (startTask) {
@@ -931,6 +941,11 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 	// Start / Abort / Resume
 
 	private async startTask(task?: string, images?: string[]): Promise<void> {
+		if (this.taskBridgeService) {
+			await this.taskBridgeService.initialize()
+			await this.taskBridgeService.subscribeToTask(this)
+		}
+
 		// `conversationHistory` (for API) and `clineMessages` (for webview)
 		// need to be in sync.
 		// If the extension process were killed, then on restart the
@@ -982,6 +997,11 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 	}
 
 	private async resumeTaskFromHistory() {
+		if (this.taskBridgeService) {
+			await this.taskBridgeService.initialize()
+			await this.taskBridgeService.subscribeToTask(this)
+		}
+
 		const modifiedClineMessages = await this.getSavedClineMessages()
 
 		// Remove any resume messages that may have been added before
@@ -1227,6 +1247,13 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 			this.pauseInterval = undefined
 		}
 
+		// Unsubscribe from TaskBridge service.
+		if (this.taskBridgeService) {
+			this.taskBridgeService
+				.unsubscribeFromTask(this.taskId)
+				.catch((error) => console.error("Error unsubscribing from task bridge:", error))
+		}
+
 		// Release any terminals associated with this task.
 		try {
 			// Release any terminals associated with this task.

+ 112 - 3
src/core/webview/ClineProvider.ts

@@ -17,7 +17,6 @@ import {
 	type ProviderSettings,
 	type RooCodeSettings,
 	type ProviderSettingsEntry,
-	type ProviderSettingsWithId,
 	type TelemetryProperties,
 	type TelemetryPropertiesProvider,
 	type CodeActionId,
@@ -66,6 +65,7 @@ import { fileExistsAtPath } from "../../utils/fs"
 import { setTtsEnabled, setTtsSpeed } from "../../utils/tts"
 import { getWorkspaceGitInfo } from "../../utils/git"
 import { getWorkspacePath } from "../../utils/path"
+import { isRemoteControlEnabled } from "../../utils/remoteControl"
 
 import { setPanel } from "../../activate/registerCommands"
 
@@ -111,6 +111,8 @@ export class ClineProvider
 	protected mcpHub?: McpHub // Change from private to protected
 	private marketplaceManager: MarketplaceManager
 	private mdmService?: MdmService
+	private taskCreationCallback: (task: Task) => void
+	private taskEventListeners: WeakMap<Task, Array<() => void>> = new WeakMap()
 
 	public isViewLaunched = false
 	public settingsImportedAt?: number
@@ -162,6 +164,40 @@ export class ClineProvider
 
 		this.marketplaceManager = new MarketplaceManager(this.context, this.customModesManager)
 
+		this.taskCreationCallback = (instance: Task) => {
+			this.emit(RooCodeEventName.TaskCreated, instance)
+
+			// Create named listener functions so we can remove them later.
+			const onTaskStarted = () => this.emit(RooCodeEventName.TaskStarted, instance.taskId)
+			const onTaskCompleted = (taskId: string, tokenUsage: any, toolUsage: any) =>
+				this.emit(RooCodeEventName.TaskCompleted, taskId, tokenUsage, toolUsage)
+			const onTaskAborted = () => this.emit(RooCodeEventName.TaskAborted, instance.taskId)
+			const onTaskFocused = () => this.emit(RooCodeEventName.TaskFocused, instance.taskId)
+			const onTaskUnfocused = () => this.emit(RooCodeEventName.TaskUnfocused, instance.taskId)
+			const onTaskActive = (taskId: string) => this.emit(RooCodeEventName.TaskActive, taskId)
+			const onTaskIdle = (taskId: string) => this.emit(RooCodeEventName.TaskIdle, taskId)
+
+			// Attach the listeners.
+			instance.on(RooCodeEventName.TaskStarted, onTaskStarted)
+			instance.on(RooCodeEventName.TaskCompleted, onTaskCompleted)
+			instance.on(RooCodeEventName.TaskAborted, onTaskAborted)
+			instance.on(RooCodeEventName.TaskFocused, onTaskFocused)
+			instance.on(RooCodeEventName.TaskUnfocused, onTaskUnfocused)
+			instance.on(RooCodeEventName.TaskActive, onTaskActive)
+			instance.on(RooCodeEventName.TaskIdle, onTaskIdle)
+
+			// Store the cleanup functions for later removal.
+			this.taskEventListeners.set(instance, [
+				() => instance.off(RooCodeEventName.TaskStarted, onTaskStarted),
+				() => instance.off(RooCodeEventName.TaskCompleted, onTaskCompleted),
+				() => instance.off(RooCodeEventName.TaskAborted, onTaskAborted),
+				() => instance.off(RooCodeEventName.TaskFocused, onTaskFocused),
+				() => instance.off(RooCodeEventName.TaskUnfocused, onTaskUnfocused),
+				() => instance.off(RooCodeEventName.TaskActive, onTaskActive),
+				() => instance.off(RooCodeEventName.TaskIdle, onTaskIdle),
+			])
+		}
+
 		// Initialize Roo Code Cloud profile sync.
 		this.initializeCloudProfileSync().catch((error) => {
 			this.log(`Failed to initialize cloud profile sync: ${error}`)
@@ -297,6 +333,14 @@ export class ClineProvider
 
 			task.emit(RooCodeEventName.TaskUnfocused)
 
+			// Remove event listeners before clearing the reference.
+			const cleanupFunctions = this.taskEventListeners.get(task)
+
+			if (cleanupFunctions) {
+				cleanupFunctions.forEach((cleanup) => cleanup())
+				this.taskEventListeners.delete(task)
+			}
+
 			// Make sure no reference kept, once promises end it will be
 			// garbage collected.
 			task = undefined
@@ -654,12 +698,17 @@ export class ClineProvider
 			enableCheckpoints,
 			fuzzyMatchThreshold,
 			experiments,
+			cloudUserInfo,
+			remoteControlEnabled,
 		} = await this.getState()
 
 		if (!ProfileValidator.isProfileAllowed(apiConfiguration, organizationAllowList)) {
 			throw new OrganizationAllowListViolationError(t("common:errors.violated_organization_allowlist"))
 		}
 
+		// Determine if TaskBridge should be enabled
+		const enableTaskBridge = isRemoteControlEnabled(cloudUserInfo, remoteControlEnabled)
+
 		const task = new Task({
 			provider: this,
 			apiConfiguration,
@@ -673,7 +722,8 @@ export class ClineProvider
 			rootTask: this.clineStack.length > 0 ? this.clineStack[0] : undefined,
 			parentTask,
 			taskNumber: this.clineStack.length + 1,
-			onCreated: (instance) => this.emit(RooCodeEventName.TaskCreated, instance),
+			onCreated: this.taskCreationCallback,
+			enableTaskBridge,
 			...options,
 		})
 
@@ -738,8 +788,13 @@ export class ClineProvider
 			enableCheckpoints,
 			fuzzyMatchThreshold,
 			experiments,
+			cloudUserInfo,
+			remoteControlEnabled,
 		} = await this.getState()
 
+		// Determine if TaskBridge should be enabled
+		const enableTaskBridge = isRemoteControlEnabled(cloudUserInfo, remoteControlEnabled)
+
 		const task = new Task({
 			provider: this,
 			apiConfiguration,
@@ -752,7 +807,8 @@ export class ClineProvider
 			rootTask: historyItem.rootTask,
 			parentTask: historyItem.parentTask,
 			taskNumber: historyItem.number,
-			onCreated: (instance) => this.emit(RooCodeEventName.TaskCreated, instance),
+			onCreated: this.taskCreationCallback,
+			enableTaskBridge,
 		})
 
 		await this.addClineToStack(task)
@@ -1631,6 +1687,7 @@ export class ClineProvider
 			includeDiagnosticMessages,
 			maxDiagnosticMessages,
 			includeTaskHistoryInEnhance,
+			remoteControlEnabled,
 		} = await this.getState()
 
 		const telemetryKey = process.env.POSTHOG_API_KEY
@@ -1758,6 +1815,7 @@ export class ClineProvider
 			includeDiagnosticMessages: includeDiagnosticMessages ?? true,
 			maxDiagnosticMessages: maxDiagnosticMessages ?? 50,
 			includeTaskHistoryInEnhance: includeTaskHistoryInEnhance ?? false,
+			remoteControlEnabled: remoteControlEnabled ?? false,
 		}
 	}
 
@@ -1945,6 +2003,8 @@ export class ClineProvider
 			maxDiagnosticMessages: stateValues.maxDiagnosticMessages ?? 50,
 			// Add includeTaskHistoryInEnhance setting
 			includeTaskHistoryInEnhance: stateValues.includeTaskHistoryInEnhance ?? false,
+			// Add remoteControlEnabled setting
+			remoteControlEnabled: stateValues.remoteControlEnabled ?? false,
 		}
 	}
 
@@ -2057,6 +2117,55 @@ export class ClineProvider
 		return true
 	}
 
+	/**
+	 * Handle remote control enabled/disabled state changes
+	 * Manages ExtensionBridgeService and TaskBridgeService lifecycle
+	 */
+	public async handleRemoteControlToggle(enabled: boolean): Promise<void> {
+		const {
+			CloudService: CloudServiceImport,
+			ExtensionBridgeService,
+			TaskBridgeService,
+		} = await import("@roo-code/cloud")
+		const userInfo = CloudServiceImport.instance.getUserInfo()
+
+		// Handle ExtensionBridgeService using static method
+		await ExtensionBridgeService.handleRemoteControlState(userInfo, enabled, this, (message: string) =>
+			this.log(message),
+		)
+
+		if (isRemoteControlEnabled(userInfo, enabled)) {
+			// Set up TaskBridgeService for the currently active task if one exists
+			const currentTask = this.getCurrentCline()
+			if (currentTask && !currentTask.taskBridgeService) {
+				try {
+					currentTask.taskBridgeService = TaskBridgeService.getInstance()
+					await currentTask.taskBridgeService.subscribeToTask(currentTask)
+					this.log(`[TaskBridgeService] Subscribed current task ${currentTask.taskId} to TaskBridge`)
+				} catch (error) {
+					const message = `[TaskBridgeService#subscribeToTask] ${error instanceof Error ? error.message : String(error)}`
+					this.log(message)
+					console.error(message)
+				}
+			}
+		} else {
+			// Disconnect TaskBridgeService for all tasks in the stack
+			for (const task of this.clineStack) {
+				if (task.taskBridgeService) {
+					try {
+						await task.taskBridgeService.unsubscribeFromTask(task.taskId)
+						task.taskBridgeService = undefined
+						this.log(`[TaskBridgeService] Unsubscribed task ${task.taskId} from TaskBridge`)
+					} catch (error) {
+						const message = `[TaskBridgeService#unsubscribeFromTask] for task ${task.taskId}: ${error instanceof Error ? error.message : String(error)}`
+						this.log(message)
+						console.error(message)
+					}
+				}
+			}
+		}
+	}
+
 	/**
 	 * Returns properties to be included in every telemetry event
 	 * This method is called by the telemetry service to get context information

+ 5 - 0
src/core/webview/webviewMessageHandler.ts

@@ -906,6 +906,11 @@ export const webviewMessageHandler = async (
 			await updateGlobalState("enableMcpServerCreation", message.bool ?? true)
 			await provider.postStateToWebview()
 			break
+		case "remoteControlEnabled":
+			await updateGlobalState("remoteControlEnabled", message.bool ?? false)
+			await provider.handleRemoteControlToggle(message.bool ?? false)
+			await provider.postStateToWebview()
+			break
 		case "refreshAllMcpServers": {
 			const mcpHub = provider.getMcpHub()
 			if (mcpHub) {

+ 36 - 28
src/extension.ts

@@ -12,7 +12,7 @@ try {
 	console.warn("Failed to load environment variables:", e)
 }
 
-import { CloudService } from "@roo-code/cloud"
+import { CloudService, ExtensionBridgeService } from "@roo-code/cloud"
 import { TelemetryService, PostHogTelemetryClient } from "@roo-code/telemetry"
 
 import "./utils/path" // Necessary to have access to String.prototype.toPosix.
@@ -29,6 +29,7 @@ import { CodeIndexManager } from "./services/code-index/manager"
 import { MdmService } from "./services/mdm/MdmService"
 import { migrateSettings } from "./utils/migrateSettings"
 import { autoImportSettings } from "./utils/autoImportSettings"
+import { isRemoteControlEnabled } from "./utils/remoteControl"
 import { API } from "./extension/api"
 
 import {
@@ -71,37 +72,13 @@ export async function activate(context: vscode.ExtensionContext) {
 		console.warn("Failed to register PostHogTelemetryClient:", error)
 	}
 
-	// Create logger for cloud services
+	// Create logger for cloud services.
 	const cloudLogger = createDualLogger(createOutputChannelLogger(outputChannel))
 
-	// Initialize Roo Code Cloud service.
-	const cloudService = await CloudService.createInstance(context, cloudLogger)
-
-	try {
-		if (cloudService.telemetryClient) {
-			TelemetryService.instance.register(cloudService.telemetryClient)
-		}
-	} catch (error) {
-		outputChannel.appendLine(
-			`[CloudService] Failed to register TelemetryClient: ${error instanceof Error ? error.message : String(error)}`,
-		)
-	}
-
-	const postStateListener = () => {
-		ClineProvider.getVisibleInstance()?.postStateToWebview()
-	}
-
-	cloudService.on("auth-state-changed", postStateListener)
-	cloudService.on("user-info", postStateListener)
-	cloudService.on("settings-updated", postStateListener)
-
-	// Add to subscriptions for proper cleanup on deactivate
-	context.subscriptions.push(cloudService)
-
 	// Initialize MDM service
 	const mdmService = await MdmService.createInstance(cloudLogger)
 
-	// Initialize i18n for internationalization support
+	// Initialize i18n for internationalization support.
 	initializeI18n(context.globalState.get("language") ?? formatLanguage(vscode.env.language))
 
 	// Initialize terminal shell execution handlers.
@@ -126,6 +103,29 @@ export async function activate(context: vscode.ExtensionContext) {
 		)
 	}
 
+	// Initialize Roo Code Cloud service.
+	const cloudService = await CloudService.createInstance(context, cloudLogger)
+
+	const postStateListener = () => ClineProvider.getVisibleInstance()?.postStateToWebview()
+
+	cloudService.on("auth-state-changed", postStateListener)
+	cloudService.on("settings-updated", postStateListener)
+
+	cloudService.on("user-info", ({ userInfo }) => {
+		postStateListener()
+
+		// Check if remote control is enabled in user settings
+		const remoteControlEnabled = contextProxy.getValue("remoteControlEnabled")
+
+		// Handle ExtensionBridgeService state using static method
+		ExtensionBridgeService.handleRemoteControlState(userInfo, remoteControlEnabled, provider, (message: string) =>
+			outputChannel.appendLine(message),
+		)
+	})
+
+	// Add to subscriptions for proper cleanup on deactivate.
+	context.subscriptions.push(cloudService)
+
 	const provider = new ClineProvider(context, outputChannel, "sidebar", contextProxy, codeIndexManager, mdmService)
 	TelemetryService.instance.setProvider(provider)
 
@@ -139,7 +139,7 @@ export async function activate(context: vscode.ExtensionContext) {
 		}),
 	)
 
-	// Auto-import configuration if specified in settings
+	// Auto-import configuration if specified in settings.
 	try {
 		await autoImportSettings(outputChannel, {
 			providerSettingsManager: provider.providerSettingsManager,
@@ -232,6 +232,14 @@ export async function activate(context: vscode.ExtensionContext) {
 // This method is called when your extension is deactivated.
 export async function deactivate() {
 	outputChannel.appendLine(`${Package.name} extension deactivated`)
+
+	// Cleanup Extension Bridge service.
+	const extensionBridgeService = ExtensionBridgeService.getInstance()
+
+	if (extensionBridgeService) {
+		await extensionBridgeService.disconnect()
+	}
+
 	await McpServerManager.cleanup(extensionContext)
 	TelemetryService.instance.shutdown()
 	TerminalRegistry.cleanup()

+ 1 - 1
src/package.json

@@ -420,7 +420,7 @@
 		"@mistralai/mistralai": "^1.3.6",
 		"@modelcontextprotocol/sdk": "^1.9.0",
 		"@qdrant/js-client-rest": "^1.14.0",
-		"@roo-code/cloud": "^0.4.0",
+		"@roo-code/cloud": "^0.5.0",
 		"@roo-code/ipc": "workspace:^",
 		"@roo-code/telemetry": "workspace:^",
 		"@roo-code/types": "workspace:^",

+ 1 - 0
src/shared/ExtensionMessage.ts

@@ -270,6 +270,7 @@ export type ExtensionState = Pick<
 	| "profileThresholds"
 	| "includeDiagnosticMessages"
 	| "maxDiagnosticMessages"
+	| "remoteControlEnabled"
 > & {
 	version: string
 	clineMessages: ClineMessage[]

+ 1 - 0
src/shared/WebviewMessage.ts

@@ -130,6 +130,7 @@ export interface WebviewMessage {
 		| "terminalCompressProgressBar"
 		| "mcpEnabled"
 		| "enableMcpServerCreation"
+		| "remoteControlEnabled"
 		| "searchCommits"
 		| "alwaysApproveResubmit"
 		| "requestDelaySeconds"

+ 11 - 0
src/utils/remoteControl.ts

@@ -0,0 +1,11 @@
+import type { CloudUserInfo } from "@roo-code/types"
+
+/**
+ * Determines if remote control features should be enabled
+ * @param cloudUserInfo - User information from cloud service
+ * @param remoteControlEnabled - User's remote control setting
+ * @returns true if remote control should be enabled
+ */
+export function isRemoteControlEnabled(cloudUserInfo?: CloudUserInfo | null, remoteControlEnabled?: boolean): boolean {
+	return !!(cloudUserInfo?.id && cloudUserInfo.extensionBridgeEnabled && remoteControlEnabled)
+}

+ 52 - 19
webview-ui/src/components/account/AccountView.tsx

@@ -5,8 +5,12 @@ import type { CloudUserInfo } from "@roo-code/types"
 import { TelemetryEventName } from "@roo-code/types"
 
 import { useAppTranslation } from "@src/i18n/TranslationContext"
+import { useExtensionState } from "@src/context/ExtensionStateContext"
 import { vscode } from "@src/utils/vscode"
 import { telemetryClient } from "@src/utils/TelemetryClient"
+import { ToggleSwitch } from "@/components/ui/toggle-switch"
+
+import { History, PiggyBank, Router, SquareArrowOutUpRightIcon } from "lucide-react"
 
 type AccountViewProps = {
 	userInfo: CloudUserInfo | null
@@ -17,6 +21,7 @@ type AccountViewProps = {
 
 export const AccountView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone }: AccountViewProps) => {
 	const { t } = useAppTranslation()
+	const { remoteControlEnabled, setRemoteControlEnabled } = useExtensionState()
 	const wasAuthenticatedRef = useRef(false)
 
 	const rooLogoUri = (window as any).IMAGES_BASE_URI + "/roo-logo.svg"
@@ -51,11 +56,17 @@ export const AccountView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone }:
 		vscode.postMessage({ type: "openExternal", url: cloudUrl })
 	}
 
+	const handleRemoteControlToggle = () => {
+		const newValue = !remoteControlEnabled
+		setRemoteControlEnabled(newValue)
+		vscode.postMessage({ type: "remoteControlEnabled", bool: newValue })
+	}
+
 	return (
-		<div className="flex flex-col h-full p-4 bg-vscode-editor-background">
+		<div className="flex flex-col h-full">
 			<div className="flex justify-between items-center mb-6">
 				<h1 className="text-xl font-medium text-vscode-foreground">{t("account:title")}</h1>
-				<VSCodeButton appearance="primary" onClick={onDone}>
+				<VSCodeButton appearance="secondary" onClick={onDone}>
 					{t("settings:common.done")}
 				</VSCodeButton>
 			</div>
@@ -77,13 +88,13 @@ export const AccountView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone }:
 								)}
 							</div>
 							{userInfo.name && (
-								<h2 className="text-lg font-medium text-vscode-foreground mb-0">{userInfo.name}</h2>
+								<h2 className="text-lg font-medium text-vscode-foreground my-0">{userInfo.name}</h2>
 							)}
 							{userInfo?.email && (
-								<p className="text-sm text-vscode-descriptionForeground">{userInfo?.email}</p>
+								<p className="text-sm text-vscode-descriptionForeground my-0">{userInfo?.email}</p>
 							)}
 							{userInfo?.organizationName && (
-								<div className="flex items-center gap-2 text-sm text-vscode-descriptionForeground">
+								<div className="flex items-center gap-2 text-sm text-vscode-descriptionForeground mt-2">
 									{userInfo.organizationImageUrl && (
 										<img
 											src={userInfo.organizationImageUrl}
@@ -96,6 +107,27 @@ export const AccountView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone }:
 							)}
 						</div>
 					)}
+
+					{/* Remote Control Toggle - only show if user has extension bridge enabled */}
+					{userInfo?.extensionBridgeEnabled && (
+						<div className="border-t border-vscode-widget-border pt-4 mt-4">
+							<div className="flex items-center gap-3 mb-2">
+								<ToggleSwitch
+									checked={remoteControlEnabled}
+									onChange={handleRemoteControlToggle}
+									size="medium"
+									aria-label={t("account:remoteControl")}
+									data-testid="remote-control-toggle"
+								/>
+								<span className="font-medium text-vscode-foreground">{t("account:remoteControl")}</span>
+							</div>
+							<div className="text-vscode-descriptionForeground text-sm mt-1 mb-4 ml-8">
+								{t("account:remoteControlDescription")}
+							</div>
+							<hr className="border-vscode-widget-border mb-4" />
+						</div>
+					)}
+
 					<div className="flex flex-col gap-2 mt-4">
 						<VSCodeButton appearance="secondary" onClick={handleVisitCloudWebsite} className="w-full">
 							{t("account:visitCloudWebsite")}
@@ -125,30 +157,31 @@ export const AccountView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone }:
 					</div>
 
 					<div className="flex flex-col mb-6 text-center">
-						<h2 className="text-lg font-medium text-vscode-foreground mb-2">
+						<h2 className="text-xl font-bold text-vscode-foreground mb-2">
 							{t("account:cloudBenefitsTitle")}
 						</h2>
-						<p className="text-md text-vscode-descriptionForeground mb-4">
-							{t("account:cloudBenefitsSubtitle")}
-						</p>
-						<ul className="text-sm text-vscode-descriptionForeground space-y-2 max-w-xs mx-auto">
-							<li className="flex items-start">
-								<span className="mr-2 text-vscode-foreground">•</span>
-								{t("account:cloudBenefitHistory")}
+						<ul className="text-vscode-descriptionForeground space-y-3 mx-auto px-8">
+							<li className="flex items-start text-left gap-4">
+								<Router size="16" className="shrink-0" />
+								<span>{t("account:cloudBenefitWalkaway")}</span>
 							</li>
-							<li className="flex items-start">
-								<span className="mr-2 text-vscode-foreground">•</span>
+							<li className="flex items-start text-left gap-4">
+								<SquareArrowOutUpRightIcon size="16" className="shrink-0" />
 								{t("account:cloudBenefitSharing")}
 							</li>
-							<li className="flex items-start">
-								<span className="mr-2 text-vscode-foreground">•</span>
+							<li className="flex items-start text-left gap-4">
+								<History size="16" className="shrink-0" />
+								{t("account:cloudBenefitHistory")}
+							</li>
+							<li className="flex items-start text-left gap-4">
+								<PiggyBank size="16" className="shrink-0" />
 								{t("account:cloudBenefitMetrics")}
 							</li>
 						</ul>
 					</div>
 
-					<div className="flex flex-col gap-4">
-						<VSCodeButton appearance="primary" onClick={handleConnectClick} className="w-full">
+					<div className="flex flex-col items-center gap-4">
+						<VSCodeButton appearance="primary" onClick={handleConnectClick} className="w-1/2">
 							{t("account:connect")}
 						</VSCodeButton>
 					</div>

+ 74 - 13
webview-ui/src/components/account/__tests__/AccountView.spec.tsx

@@ -11,11 +11,17 @@ vi.mock("@src/i18n/TranslationContext", () => ({
 				"settings:common.done": "Done",
 				"account:signIn": "Connect to Roo Code Cloud",
 				"account:cloudBenefitsTitle": "Connect to Roo Code Cloud",
-				"account:cloudBenefitsSubtitle": "Sync your prompts and telemetry to enable:",
-				"account:cloudBenefitHistory": "Online task history",
-				"account:cloudBenefitSharing": "Sharing and collaboration features",
-				"account:cloudBenefitMetrics": "Task, token, and cost-based usage metrics",
+				"account:cloudBenefitWalkaway": "Follow and control tasks from anywhere with Roomote Control",
+				"account:cloudBenefitSharing": "Share tasks with others",
+				"account:cloudBenefitHistory": "Access your task history",
+				"account:cloudBenefitMetrics": "Get a holistic view of your token consumption",
 				"account:logOut": "Log out",
+				"account:connect": "Connect Now",
+				"account:visitCloudWebsite": "Visit Roo Code Cloud",
+				"account:remoteControl": "Roomote Control",
+				"account:remoteControlDescription":
+					"Enable following and interacting with tasks in this workspace with Roo Code Cloud",
+				"account:profilePicture": "Profile picture",
 			}
 			return translations[key] || key
 		},
@@ -36,6 +42,14 @@ vi.mock("@src/utils/TelemetryClient", () => ({
 	},
 }))
 
+// Mock the extension state context
+vi.mock("@src/context/ExtensionStateContext", () => ({
+	useExtensionState: () => ({
+		remoteControlEnabled: false,
+		setRemoteControlEnabled: vi.fn(),
+	}),
+}))
+
 // Mock window global for images
 Object.defineProperty(window, "IMAGES_BASE_URI", {
 	value: "/images",
@@ -55,13 +69,13 @@ describe("AccountView", () => {
 
 		// Check that the benefits section is displayed
 		expect(screen.getByRole("heading", { name: "Connect to Roo Code Cloud" })).toBeInTheDocument()
-		expect(screen.getByText("Sync your prompts and telemetry to enable:")).toBeInTheDocument()
-		expect(screen.getByText("Online task history")).toBeInTheDocument()
-		expect(screen.getByText("Sharing and collaboration features")).toBeInTheDocument()
-		expect(screen.getByText("Task, token, and cost-based usage metrics")).toBeInTheDocument()
+		expect(screen.getByText("Follow and control tasks from anywhere with Roomote Control")).toBeInTheDocument()
+		expect(screen.getByText("Share tasks with others")).toBeInTheDocument()
+		expect(screen.getByText("Access your task history")).toBeInTheDocument()
+		expect(screen.getByText("Get a holistic view of your token consumption")).toBeInTheDocument()
 
 		// Check that the connect button is also present
-		expect(screen.getByText("account:connect")).toBeInTheDocument()
+		expect(screen.getByText("Connect Now")).toBeInTheDocument()
 	})
 
 	it("should not display benefits when user is authenticated", () => {
@@ -80,13 +94,60 @@ describe("AccountView", () => {
 		)
 
 		// Check that the benefits section is NOT displayed
-		expect(screen.queryByText("Sync your prompts and telemetry to enable:")).not.toBeInTheDocument()
-		expect(screen.queryByText("Online task history")).not.toBeInTheDocument()
-		expect(screen.queryByText("Sharing and collaboration features")).not.toBeInTheDocument()
-		expect(screen.queryByText("Task, token, and cost-based usage metrics")).not.toBeInTheDocument()
+		expect(
+			screen.queryByText("Follow and control tasks from anywhere with Roomote Control"),
+		).not.toBeInTheDocument()
+		expect(screen.queryByText("Share tasks with others")).not.toBeInTheDocument()
+		expect(screen.queryByText("Access your task history")).not.toBeInTheDocument()
+		expect(screen.queryByText("Get a holistic view of your token consumption")).not.toBeInTheDocument()
 
 		// Check that user info is displayed instead
 		expect(screen.getByText("Test User")).toBeInTheDocument()
 		expect(screen.getByText("[email protected]")).toBeInTheDocument()
 	})
+
+	it("should display remote control toggle when user has extension bridge enabled", () => {
+		const mockUserInfo = {
+			name: "Test User",
+			email: "[email protected]",
+			extensionBridgeEnabled: true,
+		}
+
+		render(
+			<AccountView
+				userInfo={mockUserInfo}
+				isAuthenticated={true}
+				cloudApiUrl="https://app.roocode.com"
+				onDone={() => {}}
+			/>,
+		)
+
+		// Check that the remote control toggle is displayed
+		expect(screen.getByTestId("remote-control-toggle")).toBeInTheDocument()
+		expect(screen.getByText("Roomote Control")).toBeInTheDocument()
+		expect(
+			screen.getByText("Enable following and interacting with tasks in this workspace with Roo Code Cloud"),
+		).toBeInTheDocument()
+	})
+
+	it("should not display remote control toggle when user does not have extension bridge enabled", () => {
+		const mockUserInfo = {
+			name: "Test User",
+			email: "[email protected]",
+			extensionBridgeEnabled: false,
+		}
+
+		render(
+			<AccountView
+				userInfo={mockUserInfo}
+				isAuthenticated={true}
+				cloudApiUrl="https://app.roocode.com"
+				onDone={() => {}}
+			/>,
+		)
+
+		// Check that the remote control toggle is NOT displayed
+		expect(screen.queryByTestId("remote-control-toggle")).not.toBeInTheDocument()
+		expect(screen.queryByText("Roomote Control")).not.toBeInTheDocument()
+	})
 })

+ 3 - 1
webview-ui/src/components/modes/ModesView.tsx

@@ -617,7 +617,9 @@ const ModesView = ({ onDone }: ModesViewProps) => {
 									aria-expanded={open}
 									className="justify-between w-full"
 									data-testid="mode-select-trigger">
-									<div className="truncate">{getCurrentMode()?.name || t("prompts:modes.selectMode")}</div>
+									<div className="truncate">
+										{getCurrentMode()?.name || t("prompts:modes.selectMode")}
+									</div>
 									<ChevronDown className="opacity-50" />
 								</Button>
 							</PopoverTrigger>

+ 5 - 0
webview-ui/src/context/ExtensionStateContext.tsx

@@ -97,6 +97,8 @@ export interface ExtensionStateContextType extends ExtensionState {
 	setMcpEnabled: (value: boolean) => void
 	enableMcpServerCreation: boolean
 	setEnableMcpServerCreation: (value: boolean) => void
+	remoteControlEnabled: boolean
+	setRemoteControlEnabled: (value: boolean) => void
 	alwaysApproveResubmit?: boolean
 	setAlwaysApproveResubmit: (value: boolean) => void
 	requestDelaySeconds: number
@@ -195,6 +197,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
 		terminalShellIntegrationTimeout: 4000,
 		mcpEnabled: true,
 		enableMcpServerCreation: false,
+		remoteControlEnabled: false,
 		alwaysApproveResubmit: false,
 		requestDelaySeconds: 5,
 		currentApiConfigName: "default",
@@ -408,6 +411,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
 		profileThresholds: state.profileThresholds ?? {},
 		alwaysAllowFollowupQuestions,
 		followupAutoApproveTimeoutMs,
+		remoteControlEnabled: state.remoteControlEnabled ?? false,
 		setExperimentEnabled: (id, enabled) =>
 			setState((prevState) => ({ ...prevState, experiments: { ...prevState.experiments, [id]: enabled } })),
 		setApiConfiguration,
@@ -454,6 +458,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
 		setMcpEnabled: (value) => setState((prevState) => ({ ...prevState, mcpEnabled: value })),
 		setEnableMcpServerCreation: (value) =>
 			setState((prevState) => ({ ...prevState, enableMcpServerCreation: value })),
+		setRemoteControlEnabled: (value) => setState((prevState) => ({ ...prevState, remoteControlEnabled: value })),
 		setAlwaysApproveResubmit: (value) => setState((prevState) => ({ ...prevState, alwaysApproveResubmit: value })),
 		setRequestDelaySeconds: (value) => setState((prevState) => ({ ...prevState, requestDelaySeconds: value })),
 		setCurrentApiConfigName: (value) => setState((prevState) => ({ ...prevState, currentApiConfigName: value })),

+ 3 - 0
webview-ui/src/i18n/locales/ca/account.json

@@ -10,5 +10,8 @@
 	"cloudBenefitHistory": "Historial de tasques en línia",
 	"cloudBenefitSharing": "Funcions de compartició i col·laboració",
 	"cloudBenefitMetrics": "Mètriques d'ús basades en tasques, tokens i costos",
+	"cloudBenefitWalkaway": "Segueix i controla tasques des de qualsevol lloc amb Roomote Control",
+	"remoteControl": "Roomote Control",
+	"remoteControlDescription": "Permet seguir i interactuar amb tasques en aquest espai de treball amb Roo Code Cloud",
 	"visitCloudWebsite": "Visita Roo Code Cloud"
 }

+ 3 - 0
webview-ui/src/i18n/locales/de/account.json

@@ -10,5 +10,8 @@
 	"cloudBenefitHistory": "Online-Aufgabenverlauf",
 	"cloudBenefitSharing": "Freigabe- und Kollaborationsfunktionen",
 	"cloudBenefitMetrics": "Aufgaben-, Token- und kostenbasierte Nutzungsmetriken",
+	"cloudBenefitWalkaway": "Verfolge und steuere Aufgaben von überall mit Roomote Control",
+	"remoteControl": "Roomote Control",
+	"remoteControlDescription": "Ermöglicht das Verfolgen und Interagieren mit Aufgaben in diesem Arbeitsbereich mit Roo Code Cloud",
 	"visitCloudWebsite": "Roo Code Cloud besuchen"
 }

+ 8 - 6
webview-ui/src/i18n/locales/en/account.json

@@ -4,11 +4,13 @@
 	"logOut": "Log out",
 	"testApiAuthentication": "Test API Authentication",
 	"signIn": "Connect to Roo Code Cloud",
-	"connect": "Connect",
+	"connect": "Connect Now",
 	"cloudBenefitsTitle": "Connect to Roo Code Cloud",
-	"cloudBenefitsSubtitle": "Sync your prompts and telemetry to enable:",
-	"cloudBenefitHistory": "Online task history",
-	"cloudBenefitSharing": "Sharing and collaboration features",
-	"cloudBenefitMetrics": "Task, token, and cost-based usage metrics",
-	"visitCloudWebsite": "Visit Roo Code Cloud"
+	"cloudBenefitWalkaway": "Follow and control tasks from anywhere with Roomote Control",
+	"cloudBenefitSharing": "Share tasks with others",
+	"cloudBenefitHistory": "Access your task history",
+	"cloudBenefitMetrics": "Get a holistic view of your token consumption",
+	"visitCloudWebsite": "Visit Roo Code Cloud",
+	"remoteControl": "Roomote Control",
+	"remoteControlDescription": "Enable following and interacting with tasks in this workspace with Roo Code Cloud"
 }

+ 1 - 1
webview-ui/src/i18n/locales/en/settings.json

@@ -686,7 +686,7 @@
 			"name": "Enable concurrent file edits",
 			"description": "When enabled, Roo can edit multiple files in a single request. When disabled, Roo must edit files one at a time. Disabling this can help when working with less capable models or when you want more control over file modifications."
 		},
-"PREVENT_FOCUS_DISRUPTION": {
+		"PREVENT_FOCUS_DISRUPTION": {
 			"name": "Background editing",
 			"description": "Prevent editor focus disruption when enabled. File edits happen in the background without opening diff views or stealing focus. You can continue working uninterrupted while Roo makes changes. Files can be opened without focus to capture diagnostics or kept closed entirely."
 		},

+ 3 - 0
webview-ui/src/i18n/locales/es/account.json

@@ -10,5 +10,8 @@
 	"cloudBenefitHistory": "Historial de tareas en línea",
 	"cloudBenefitSharing": "Funciones de compartir y colaboración",
 	"cloudBenefitMetrics": "Métricas de uso basadas en tareas, tokens y costos",
+	"cloudBenefitWalkaway": "Sigue y controla tareas desde cualquier lugar con Roomote Control",
+	"remoteControl": "Roomote Control",
+	"remoteControlDescription": "Permite seguir e interactuar con tareas en este espacio de trabajo con Roo Code Cloud",
 	"visitCloudWebsite": "Visitar Roo Code Cloud"
 }

+ 3 - 0
webview-ui/src/i18n/locales/fr/account.json

@@ -10,5 +10,8 @@
 	"cloudBenefitHistory": "Historique des tâches en ligne",
 	"cloudBenefitSharing": "Fonctionnalités de partage et collaboration",
 	"cloudBenefitMetrics": "Métriques d'utilisation basées sur les tâches, tokens et coûts",
+	"cloudBenefitWalkaway": "Suivez et contrôlez les tâches depuis n'importe où avec Roomote Control",
+	"remoteControl": "Roomote Control",
+	"remoteControlDescription": "Permet de suivre et d'interagir avec les tâches dans cet espace de travail avec Roo Code Cloud",
 	"visitCloudWebsite": "Visiter Roo Code Cloud"
 }

+ 3 - 0
webview-ui/src/i18n/locales/hi/account.json

@@ -10,5 +10,8 @@
 	"cloudBenefitHistory": "ऑनलाइन कार्य इतिहास",
 	"cloudBenefitSharing": "साझाकरण और सहयोग सुविधाएं",
 	"cloudBenefitMetrics": "कार्य, token और लागत आधारित उपयोग मेट्रिक्स",
+	"cloudBenefitWalkaway": "Roomote Control के साथ कहीं से भी कार्यों को फॉलो और नियंत्रित करें",
+	"remoteControl": "Roomote Control",
+	"remoteControlDescription": "Roo Code Cloud के साथ इस वर्कस्पेस में कार्यों को फॉलो और इंटरैक्ट करने की सुविधा दें",
 	"visitCloudWebsite": "Roo Code Cloud पर जाएं"
 }

+ 3 - 0
webview-ui/src/i18n/locales/id/account.json

@@ -10,5 +10,8 @@
 	"cloudBenefitHistory": "Riwayat tugas online",
 	"cloudBenefitSharing": "Fitur berbagi dan kolaborasi",
 	"cloudBenefitMetrics": "Metrik penggunaan berdasarkan tugas, token, dan biaya",
+	"cloudBenefitWalkaway": "Ikuti dan kontrol tugas dari mana saja dengan Roomote Control",
+	"remoteControl": "Roomote Control",
+	"remoteControlDescription": "Memungkinkan mengikuti dan berinteraksi dengan tugas di workspace ini dengan Roo Code Cloud",
 	"visitCloudWebsite": "Kunjungi Roo Code Cloud"
 }

+ 3 - 0
webview-ui/src/i18n/locales/it/account.json

@@ -10,5 +10,8 @@
 	"cloudBenefitHistory": "Cronologia attività online",
 	"cloudBenefitSharing": "Funzionalità di condivisione e collaborazione",
 	"cloudBenefitMetrics": "Metriche di utilizzo basate su attività, token e costi",
+	"cloudBenefitWalkaway": "Segui e controlla le attività da qualsiasi luogo con Roomote Control",
+	"remoteControl": "Roomote Control",
+	"remoteControlDescription": "Abilita il monitoraggio e l'interazione con le attività in questo workspace con Roo Code Cloud",
 	"visitCloudWebsite": "Visita Roo Code Cloud"
 }

+ 3 - 0
webview-ui/src/i18n/locales/ja/account.json

@@ -10,5 +10,8 @@
 	"cloudBenefitHistory": "オンラインタスク履歴",
 	"cloudBenefitSharing": "共有とコラボレーション機能",
 	"cloudBenefitMetrics": "タスク、Token、コストベースの使用メトリクス",
+	"cloudBenefitWalkaway": "Roomote Controlでどこからでもタスクをフォローし制御",
+	"remoteControl": "Roomote Control",
+	"remoteControlDescription": "Roo Code Cloudでこのワークスペースのタスクをフォローし操作することを有効にする",
 	"visitCloudWebsite": "Roo Code Cloudを訪問"
 }

+ 3 - 0
webview-ui/src/i18n/locales/ko/account.json

@@ -10,5 +10,8 @@
 	"cloudBenefitHistory": "온라인 작업 기록",
 	"cloudBenefitSharing": "공유 및 협업 기능",
 	"cloudBenefitMetrics": "작업, 토큰, 비용 기반 사용 메트릭",
+	"cloudBenefitWalkaway": "Roomote Control로 어디서나 작업을 팔로우하고 제어하세요",
+	"remoteControl": "Roomote Control",
+	"remoteControlDescription": "Roo Code Cloud로 이 워크스페이스의 작업을 팔로우하고 상호작용할 수 있게 합니다",
 	"visitCloudWebsite": "Roo Code Cloud 방문"
 }

+ 3 - 0
webview-ui/src/i18n/locales/nl/account.json

@@ -10,5 +10,8 @@
 	"cloudBenefitHistory": "Online taakgeschiedenis",
 	"cloudBenefitSharing": "Deel- en samenwerkingsfuncties",
 	"cloudBenefitMetrics": "Taak-, token- en kostengebaseerde gebruiksstatistieken",
+	"cloudBenefitWalkaway": "Volg en beheer taken van overal met Roomote Control",
+	"remoteControl": "Roomote Control",
+	"remoteControlDescription": "Schakel het volgen en interacteren met taken in deze workspace in met Roo Code Cloud",
 	"visitCloudWebsite": "Bezoek Roo Code Cloud"
 }

+ 3 - 0
webview-ui/src/i18n/locales/pl/account.json

@@ -10,5 +10,8 @@
 	"cloudBenefitHistory": "Historia zadań online",
 	"cloudBenefitSharing": "Funkcje udostępniania i współpracy",
 	"cloudBenefitMetrics": "Metryki użycia oparte na zadaniach, tokenach i kosztach",
+	"cloudBenefitWalkaway": "Śledź i kontroluj zadania z dowolnego miejsca za pomocą Roomote Control",
+	"remoteControl": "Roomote Control",
+	"remoteControlDescription": "Umożliwia śledzenie i interakcję z zadaniami w tym obszarze roboczym za pomocą Roo Code Cloud",
 	"visitCloudWebsite": "Odwiedź Roo Code Cloud"
 }

+ 3 - 0
webview-ui/src/i18n/locales/pt-BR/account.json

@@ -10,5 +10,8 @@
 	"cloudBenefitHistory": "Histórico de tarefas online",
 	"cloudBenefitSharing": "Recursos de compartilhamento e colaboração",
 	"cloudBenefitMetrics": "Métricas de uso baseadas em tarefas, tokens e custos",
+	"cloudBenefitWalkaway": "Acompanhe e controle tarefas de qualquer lugar com Roomote Control",
+	"remoteControl": "Roomote Control",
+	"remoteControlDescription": "Permite acompanhar e interagir com tarefas neste workspace com Roo Code Cloud",
 	"visitCloudWebsite": "Visitar Roo Code Cloud"
 }

+ 3 - 0
webview-ui/src/i18n/locales/ru/account.json

@@ -10,5 +10,8 @@
 	"cloudBenefitHistory": "Онлайн-история задач",
 	"cloudBenefitSharing": "Функции обмена и совместной работы",
 	"cloudBenefitMetrics": "Метрики использования на основе задач, токенов и затрат",
+	"cloudBenefitWalkaway": "Отслеживайте и управляйте задачами откуда угодно с Roomote Control",
+	"remoteControl": "Roomote Control",
+	"remoteControlDescription": "Позволяет отслеживать и взаимодействовать с задачами в этом рабочем пространстве с Roo Code Cloud",
 	"visitCloudWebsite": "Посетить Roo Code Cloud"
 }

+ 3 - 0
webview-ui/src/i18n/locales/tr/account.json

@@ -10,5 +10,8 @@
 	"cloudBenefitHistory": "Çevrimiçi görev geçmişi",
 	"cloudBenefitSharing": "Paylaşım ve işbirliği özellikleri",
 	"cloudBenefitMetrics": "Görev, token ve maliyet tabanlı kullanım metrikleri",
+	"cloudBenefitWalkaway": "Roomote Control ile görevleri her yerden takip et ve kontrol et",
+	"remoteControl": "Roomote Control",
+	"remoteControlDescription": "Bu çalışma alanındaki görevleri Roo Code Cloud ile takip etme ve etkileşim kurma imkanı sağlar",
 	"visitCloudWebsite": "Roo Code Cloud'u ziyaret et"
 }

+ 3 - 0
webview-ui/src/i18n/locales/vi/account.json

@@ -10,5 +10,8 @@
 	"cloudBenefitHistory": "Lịch sử tác vụ trực tuyến",
 	"cloudBenefitSharing": "Tính năng chia sẻ và cộng tác",
 	"cloudBenefitMetrics": "Số liệu sử dụng dựa trên tác vụ, token và chi phí",
+	"cloudBenefitWalkaway": "Theo dõi và điều khiển tác vụ từ bất kỳ đâu với Roomote Control",
+	"remoteControl": "Roomote Control",
+	"remoteControlDescription": "Cho phép theo dõi và tương tác với các tác vụ trong workspace này với Roo Code Cloud",
 	"visitCloudWebsite": "Truy cập Roo Code Cloud"
 }

+ 3 - 0
webview-ui/src/i18n/locales/zh-CN/account.json

@@ -10,5 +10,8 @@
 	"cloudBenefitHistory": "在线任务历史",
 	"cloudBenefitSharing": "共享和协作功能",
 	"cloudBenefitMetrics": "基于任务、Token 和成本的使用指标",
+	"cloudBenefitWalkaway": "使用 Roomote Control 随时随地跟踪和控制任务",
+	"remoteControl": "Roomote Control",
+	"remoteControlDescription": "允许通过 Roo Code Cloud 跟踪和操作此工作区中的任务",
 	"visitCloudWebsite": "访问 Roo Code Cloud"
 }

+ 3 - 0
webview-ui/src/i18n/locales/zh-TW/account.json

@@ -10,5 +10,8 @@
 	"cloudBenefitHistory": "線上工作歷史",
 	"cloudBenefitSharing": "分享和協作功能",
 	"cloudBenefitMetrics": "基於工作、Token 和成本的使用指標",
+	"cloudBenefitWalkaway": "使用 Roomote Control 隨時隨地追蹤和控制工作",
+	"remoteControl": "Roomote Control",
+	"remoteControlDescription": "允許透過 Roo Code Cloud 追蹤和操作此工作區中的工作",
 	"visitCloudWebsite": "造訪 Roo Code Cloud"
 }