Skip to content

Commit aa37e37

Browse files
committed
feat: separate Task Sync and Roomote Control settings
- Add new taskSyncEnabled setting to control task content syncing - Keep remoteControlEnabled for Roomote Control functionality - Task Sync controls whether task content is sent to cloud - Roomote Control controls whether cloud can send instructions back - Roomote Control now depends on Task Sync being enabled - Usage metrics (tokens, cost) always reported regardless of settings - Update UI with two separate toggles and clear descriptions - Add info text explaining usage metrics are always reported
1 parent d789692 commit aa37e37

File tree

9 files changed

+103
-21
lines changed

9 files changed

+103
-21
lines changed

packages/types/src/cloud.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ export type UserFeatures = z.infer<typeof userFeaturesSchema>
162162

163163
export const userSettingsConfigSchema = z.object({
164164
extensionBridgeEnabled: z.boolean().optional(),
165+
taskSyncEnabled: z.boolean().optional(),
165166
})
166167

167168
export type UserSettingsConfig = z.infer<typeof userSettingsConfigSchema>

packages/types/src/global-settings.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ export const globalSettingsSchema = z.object({
139139
enableMcpServerCreation: z.boolean().optional(),
140140

141141
remoteControlEnabled: z.boolean().optional(),
142+
taskSyncEnabled: z.boolean().optional(),
142143

143144
mode: z.string().optional(),
144145
modeApiConfigs: z.record(z.string(), z.string()).optional(),
@@ -316,6 +317,7 @@ export const EVALS_SETTINGS: RooCodeSettings = {
316317
mcpEnabled: false,
317318

318319
remoteControlEnabled: false,
320+
taskSyncEnabled: true, // Default to true for backward compatibility
319321

320322
mode: "code", // "architect",
321323

src/core/task/Task.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,11 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
607607
this.emit(RooCodeEventName.Message, { action: "created", message })
608608
await this.saveClineMessages()
609609

610-
const shouldCaptureMessage = message.partial !== true && CloudService.isEnabled()
610+
// Check if we should capture the message
611+
// Only capture if: not partial, cloud is enabled, and taskSyncEnabled is true
612+
const state = await this.providerRef.deref()?.getState()
613+
const taskSyncEnabled = state?.taskSyncEnabled ?? true // Default to true for backward compatibility
614+
const shouldCaptureMessage = message.partial !== true && CloudService.isEnabled() && taskSyncEnabled
611615

612616
if (shouldCaptureMessage) {
613617
CloudService.instance.captureEvent({
@@ -640,7 +644,11 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
640644
await provider?.postMessageToWebview({ type: "messageUpdated", clineMessage: message })
641645
this.emit(RooCodeEventName.Message, { action: "updated", message })
642646

643-
const shouldCaptureMessage = message.partial !== true && CloudService.isEnabled()
647+
// Check if we should capture the message
648+
// Only capture if: not partial, cloud is enabled, and taskSyncEnabled is true
649+
const state = await this.providerRef.deref()?.getState()
650+
const taskSyncEnabled = state?.taskSyncEnabled ?? true // Default to true for backward compatibility
651+
const shouldCaptureMessage = message.partial !== true && CloudService.isEnabled() && taskSyncEnabled
644652

645653
if (shouldCaptureMessage) {
646654
CloudService.instance.captureEvent({

src/core/webview/webviewMessageHandler.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,17 @@ export const webviewMessageHandler = async (
965965
await provider.remoteControlEnabled(message.bool ?? false)
966966
await provider.postStateToWebview()
967967
break
968+
case "taskSyncEnabled":
969+
try {
970+
await CloudService.instance.updateUserSettings({
971+
taskSyncEnabled: message.bool ?? true,
972+
})
973+
} catch (error) {
974+
provider.log(`Failed to update cloud settings for task sync: ${error}`)
975+
}
976+
await updateGlobalState("taskSyncEnabled", message.bool ?? true)
977+
await provider.postStateToWebview()
978+
break
968979
case "refreshAllMcpServers": {
969980
const mcpHub = provider.getMcpHub()
970981

src/shared/ExtensionMessage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ export type ExtensionState = Pick<
278278
| "includeDiagnosticMessages"
279279
| "maxDiagnosticMessages"
280280
| "remoteControlEnabled"
281+
| "taskSyncEnabled"
281282
| "openRouterImageGenerationSelectedModel"
282283
| "includeTaskHistoryInEnhance"
283284
> & {

src/shared/WebviewMessage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ export interface WebviewMessage {
134134
| "mcpEnabled"
135135
| "enableMcpServerCreation"
136136
| "remoteControlEnabled"
137+
| "taskSyncEnabled"
137138
| "searchCommits"
138139
| "alwaysApproveResubmit"
139140
| "requestDelaySeconds"

webview-ui/src/components/cloud/CloudView.tsx

Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ type CloudViewProps = {
2323

2424
export const CloudView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone }: CloudViewProps) => {
2525
const { t } = useAppTranslation()
26-
const { remoteControlEnabled, setRemoteControlEnabled } = useExtensionState()
26+
const { remoteControlEnabled, setRemoteControlEnabled, taskSyncEnabled, setTaskSyncEnabled } = useExtensionState()
2727
const wasAuthenticatedRef = useRef(false)
2828

2929
const rooLogoUri = (window as any).IMAGES_BASE_URI + "/roo-logo.svg"
@@ -75,6 +75,17 @@ export const CloudView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone }: Cl
7575
vscode.postMessage({ type: "remoteControlEnabled", bool: newValue })
7676
}
7777

78+
const handleTaskSyncToggle = () => {
79+
const newValue = !taskSyncEnabled
80+
setTaskSyncEnabled(newValue)
81+
vscode.postMessage({ type: "taskSyncEnabled", bool: newValue })
82+
// If disabling task sync, also disable remote control
83+
if (!newValue && remoteControlEnabled) {
84+
setRemoteControlEnabled(false)
85+
vscode.postMessage({ type: "remoteControlEnabled", bool: false })
86+
}
87+
}
88+
7889
return (
7990
<div className="flex flex-col h-full">
8091
<div className="flex justify-between items-center mb-6">
@@ -121,24 +132,56 @@ export const CloudView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone }: Cl
121132
</div>
122133
)}
123134

124-
{userInfo?.extensionBridgeEnabled && (
125-
<div className="border-t border-vscode-widget-border pt-4 mt-4">
126-
<div className="flex items-center gap-3 mb-2">
127-
<ToggleSwitch
128-
checked={remoteControlEnabled}
129-
onChange={handleRemoteControlToggle}
130-
size="medium"
131-
aria-label={t("cloud:remoteControl")}
132-
data-testid="remote-control-toggle"
133-
/>
134-
<span className="font-medium text-vscode-foreground">{t("cloud:remoteControl")}</span>
135-
</div>
136-
<div className="text-vscode-descriptionForeground text-sm mt-1 mb-4 ml-8">
137-
{t("cloud:remoteControlDescription")}
138-
</div>
139-
<hr className="border-vscode-widget-border mb-4" />
135+
{/* Task Sync Toggle - Always shown when authenticated */}
136+
<div className="border-t border-vscode-widget-border pt-4 mt-4">
137+
<div className="flex items-center gap-3 mb-2">
138+
<ToggleSwitch
139+
checked={taskSyncEnabled}
140+
onChange={handleTaskSyncToggle}
141+
size="medium"
142+
aria-label={t("cloud:taskSync")}
143+
data-testid="task-sync-toggle"
144+
/>
145+
<span className="font-medium text-vscode-foreground">{t("cloud:taskSync")}</span>
140146
</div>
141-
)}
147+
<div className="text-vscode-descriptionForeground text-sm mt-1 mb-4 ml-8">
148+
{t("cloud:taskSyncDescription")}
149+
</div>
150+
151+
{/* Remote Control Toggle - Only shown when extensionBridgeEnabled is true */}
152+
{userInfo?.extensionBridgeEnabled && (
153+
<>
154+
<div className="flex items-center gap-3 mb-2">
155+
<ToggleSwitch
156+
checked={remoteControlEnabled}
157+
onChange={handleRemoteControlToggle}
158+
size="medium"
159+
aria-label={t("cloud:remoteControl")}
160+
data-testid="remote-control-toggle"
161+
disabled={!taskSyncEnabled}
162+
/>
163+
<span className="font-medium text-vscode-foreground">
164+
{t("cloud:remoteControl")}
165+
</span>
166+
</div>
167+
<div className="text-vscode-descriptionForeground text-sm mt-1 mb-4 ml-8">
168+
{t("cloud:remoteControlDescription")}
169+
{!taskSyncEnabled && (
170+
<div className="text-vscode-errorForeground mt-2">
171+
{t("cloud:remoteControlRequiresTaskSync")}
172+
</div>
173+
)}
174+
</div>
175+
</>
176+
)}
177+
178+
{/* Info text about usage metrics */}
179+
<div className="text-vscode-descriptionForeground text-sm mt-4 mb-4 ml-8 italic">
180+
{t("cloud:usageMetricsAlwaysReported")}
181+
</div>
182+
183+
<hr className="border-vscode-widget-border mb-4" />
184+
</div>
142185

143186
<div className="flex flex-col gap-2 mt-4">
144187
<VSCodeButton appearance="secondary" onClick={handleVisitCloudWebsite} className="w-full">

webview-ui/src/context/ExtensionStateContext.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ export interface ExtensionStateContextType extends ExtensionState {
101101
setEnableMcpServerCreation: (value: boolean) => void
102102
remoteControlEnabled: boolean
103103
setRemoteControlEnabled: (value: boolean) => void
104+
taskSyncEnabled: boolean
105+
setTaskSyncEnabled: (value: boolean) => void
104106
alwaysApproveResubmit?: boolean
105107
setAlwaysApproveResubmit: (value: boolean) => void
106108
requestDelaySeconds: number
@@ -299,6 +301,13 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
299301
setState((prevState) => mergeExtensionState(prevState, newState))
300302
setShowWelcome(!checkExistKey(newState.apiConfiguration))
301303
setDidHydrateState(true)
304+
// Update taskSyncEnabled if present in state message
305+
if ((newState as any).taskSyncEnabled !== undefined) {
306+
setState(
307+
(prevState) =>
308+
({ ...prevState, taskSyncEnabled: (newState as any).taskSyncEnabled }) as any,
309+
)
310+
}
302311
// Update alwaysAllowFollowupQuestions if present in state message
303312
if ((newState as any).alwaysAllowFollowupQuestions !== undefined) {
304313
setAlwaysAllowFollowupQuestions((newState as any).alwaysAllowFollowupQuestions)
@@ -417,6 +426,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
417426
alwaysAllowFollowupQuestions,
418427
followupAutoApproveTimeoutMs,
419428
remoteControlEnabled: state.remoteControlEnabled ?? false,
429+
taskSyncEnabled: (state as any).taskSyncEnabled ?? true,
420430
setExperimentEnabled: (id, enabled) =>
421431
setState((prevState) => ({ ...prevState, experiments: { ...prevState.experiments, [id]: enabled } })),
422432
setApiConfiguration,
@@ -464,6 +474,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
464474
setEnableMcpServerCreation: (value) =>
465475
setState((prevState) => ({ ...prevState, enableMcpServerCreation: value })),
466476
setRemoteControlEnabled: (value) => setState((prevState) => ({ ...prevState, remoteControlEnabled: value })),
477+
setTaskSyncEnabled: (value) => setState((prevState) => ({ ...prevState, taskSyncEnabled: value }) as any),
467478
setAlwaysApproveResubmit: (value) => setState((prevState) => ({ ...prevState, alwaysApproveResubmit: value })),
468479
setRequestDelaySeconds: (value) => setState((prevState) => ({ ...prevState, requestDelaySeconds: value })),
469480
setCurrentApiConfigName: (value) => setState((prevState) => ({ ...prevState, currentApiConfigName: value })),

webview-ui/src/i18n/locales/en/cloud.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111
"cloudBenefitHistory": "Access your task history",
1212
"cloudBenefitMetrics": "Get a holistic view of your token consumption",
1313
"visitCloudWebsite": "Visit Roo Code Cloud",
14+
"taskSync": "Task sync",
15+
"taskSyncDescription": "Sync your tasks for viewing and sharing on Roo Code Cloud",
1416
"remoteControl": "Roomote Control",
15-
"remoteControlDescription": "Enable following and interacting with tasks in this workspace with Roo Code Cloud",
17+
"remoteControlDescription": "Allow controlling tasks from Roo Code Cloud",
18+
"remoteControlRequiresTaskSync": "Task sync must be enabled to use Roomote Control",
19+
"usageMetricsAlwaysReported": "Model usage info is always reported when logged in",
1620
"cloudUrlPillLabel": "Roo Code Cloud URL"
1721
}

0 commit comments

Comments
 (0)