diff --git a/.changeset/nervous-candles-refuse.md b/.changeset/nervous-candles-refuse.md new file mode 100644 index 0000000000..314711606d --- /dev/null +++ b/.changeset/nervous-candles-refuse.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Add a checkbox to disable the browser tool diff --git a/src/core/Cline.ts b/src/core/Cline.ts index c339e713c3..c66dec2c86 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -946,6 +946,7 @@ export class Cline { preferredLanguage, experiments, enableMcpServerCreation, + browserToolEnabled, } = (await this.providerRef.deref()?.getState()) ?? {} const { customModes } = (await this.providerRef.deref()?.getState()) ?? {} const systemPrompt = await (async () => { @@ -956,7 +957,7 @@ export class Cline { return SYSTEM_PROMPT( provider.context, cwd, - this.api.getModel().info.supportsComputerUse ?? false, + (this.api.getModel().info.supportsComputerUse ?? false) && (browserToolEnabled ?? true), mcpHub, this.diffStrategy, browserViewportSize, diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index e23e8762a7..0a2e385b6a 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1224,6 +1224,10 @@ export class ClineProvider implements vscode.WebviewViewProvider { await this.updateGlobalState("maxOpenTabsContext", tabCount) await this.postStateToWebview() break + case "browserToolEnabled": + await this.updateGlobalState("browserToolEnabled", message.bool ?? true) + await this.postStateToWebview() + break case "enhancementApiConfigId": await this.updateGlobalState("enhancementApiConfigId", message.text) await this.postStateToWebview() @@ -2022,6 +2026,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { autoApprovalEnabled, experiments, maxOpenTabsContext, + browserToolEnabled, } = await this.getState() const allowedCommands = vscode.workspace.getConfiguration("roo-cline").get("allowedCommands") || [] @@ -2075,6 +2080,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { mcpServers: this.mcpHub?.getAllServers() ?? [], maxOpenTabsContext: maxOpenTabsContext ?? 20, cwd: cwd, + browserToolEnabled: browserToolEnabled ?? true, } } @@ -2214,6 +2220,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { modelMaxTokens, modelMaxThinkingTokens, maxOpenTabsContext, + browserToolEnabled, ] = await Promise.all([ this.getGlobalState("apiProvider") as Promise, this.getGlobalState("apiModelId") as Promise, @@ -2298,6 +2305,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { this.getGlobalState("modelMaxTokens") as Promise, this.getGlobalState("anthropicThinking") as Promise, this.getGlobalState("maxOpenTabsContext") as Promise, + this.getGlobalState("browserToolEnabled") as Promise, ]) let apiProvider: ApiProvider @@ -2431,6 +2439,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { customModes, maxOpenTabsContext: maxOpenTabsContext ?? 20, openRouterUseMiddleOutTransform: openRouterUseMiddleOutTransform ?? true, + browserToolEnabled: browserToolEnabled ?? true, } } diff --git a/src/core/webview/__tests__/ClineProvider.test.ts b/src/core/webview/__tests__/ClineProvider.test.ts index c8742cd3f4..154c24bc27 100644 --- a/src/core/webview/__tests__/ClineProvider.test.ts +++ b/src/core/webview/__tests__/ClineProvider.test.ts @@ -381,6 +381,7 @@ describe("ClineProvider", () => { customModes: [], experiments: experimentDefault, maxOpenTabsContext: 20, + browserToolEnabled: true, } const message: ExtensionMessage = { @@ -591,6 +592,21 @@ describe("ClineProvider", () => { expect(provider.configManager.setModeConfig).toHaveBeenCalledWith("architect", "new-id") }) + test("handles browserToolEnabled setting", async () => { + await provider.resolveWebviewView(mockWebviewView) + const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0] + + // Test browserToolEnabled + await messageHandler({ type: "browserToolEnabled", bool: true }) + expect(mockContext.globalState.update).toHaveBeenCalledWith("browserToolEnabled", true) + expect(mockPostMessage).toHaveBeenCalled() + + // Verify state includes browserToolEnabled + const state = await provider.getState() + expect(state).toHaveProperty("browserToolEnabled") + expect(state.browserToolEnabled).toBe(true) // Default value should be true + }) + test("handles request delay settings messages", async () => { await provider.resolveWebviewView(mockWebviewView) const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0] diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 34abd38dbf..858b20ca7f 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -45,6 +45,7 @@ export interface ExtensionMessage { | "updateCustomMode" | "deleteCustomMode" | "currentCheckpointUpdated" + | "browserToolEnabled" text?: string action?: | "chatButtonClicked" @@ -103,6 +104,7 @@ export interface ExtensionState { alwaysAllowMcp?: boolean alwaysApproveResubmit?: boolean alwaysAllowModeSwitch?: boolean + browserToolEnabled?: boolean requestDelaySeconds: number rateLimitSeconds: number // Minimum time between successive requests (0 = disabled) uriScheme?: string diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 8d3a114e65..3b1750bcb6 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -94,6 +94,7 @@ export interface WebviewMessage { | "checkpointRestore" | "deleteMcpServer" | "maxOpenTabsContext" + | "browserToolEnabled" text?: string disabled?: boolean askResponse?: ClineAskResponse diff --git a/src/shared/globalState.ts b/src/shared/globalState.ts index aabc77cc01..64a222be81 100644 --- a/src/shared/globalState.ts +++ b/src/shared/globalState.ts @@ -84,3 +84,4 @@ export type GlobalStateKey = | "anthropicThinking" // TODO: Rename to `modelMaxThinkingTokens`. | "mistralCodestralUrl" | "maxOpenTabsContext" + | "browserToolEnabled" // Setting to enable/disable the browser tool diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 51ef4fe81d..612be3aab3 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -51,6 +51,7 @@ const SettingsView = forwardRef(({ onDone }, alwaysAllowModeSwitch, alwaysAllowWrite, alwaysApproveResubmit, + browserToolEnabled, browserViewportSize, enableCheckpoints, diffEnabled, @@ -140,6 +141,7 @@ const SettingsView = forwardRef(({ onDone }, vscode.postMessage({ type: "alwaysAllowBrowser", bool: alwaysAllowBrowser }) vscode.postMessage({ type: "alwaysAllowMcp", bool: alwaysAllowMcp }) vscode.postMessage({ type: "allowedCommands", commands: allowedCommands ?? [] }) + vscode.postMessage({ type: "browserToolEnabled", bool: browserToolEnabled }) vscode.postMessage({ type: "soundEnabled", bool: soundEnabled }) vscode.postMessage({ type: "soundVolume", value: soundVolume }) vscode.postMessage({ type: "diffEnabled", bool: diffEnabled }) @@ -537,59 +539,83 @@ const SettingsView = forwardRef(({ onDone },

Browser Settings

- -
- { - setCachedStateField("browserViewportSize", (value as DropdownOption).value) - }} - style={{ width: "100%" }} - options={[ - { value: "1280x800", label: "Large Desktop (1280x800)" }, - { value: "900x600", label: "Small Desktop (900x600)" }, - { value: "768x1024", label: "Tablet (768x1024)" }, - { value: "360x640", label: "Mobile (360x640)" }, - ]} - /> -
-

- Select the viewport size for browser interactions. This affects how websites are displayed - and interacted with. + setCachedStateField("browserToolEnabled", e.target.checked)}> + Enable browser tool + +

+ When enabled, Roo can use a browser to interact with websites when using models that support + computer use.

+ {browserToolEnabled && ( +
+
+ +
+ { + setCachedStateField("browserViewportSize", (value as DropdownOption).value) + }} + style={{ width: "100%" }} + options={[ + { value: "1280x800", label: "Large Desktop (1280x800)" }, + { value: "900x600", label: "Small Desktop (900x600)" }, + { value: "768x1024", label: "Tablet (768x1024)" }, + { value: "360x640", label: "Mobile (360x640)" }, + ]} + /> +
+

+ Select the viewport size for browser interactions. This affects how websites are + displayed and interacted with. +

+
-
-
- Screenshot quality -
- setCachedStateField("screenshotQuality", parseInt(e.target.value))} - /> - {screenshotQuality ?? 75}% +
+
+ Screenshot quality +
+ + setCachedStateField("screenshotQuality", parseInt(e.target.value)) + } + /> + {screenshotQuality ?? 75}% +
+
+

+ Adjust the WebP quality of browser screenshots. Higher values provide clearer + screenshots but increase token usage. +

-

- Adjust the WebP quality of browser screenshots. Higher values provide clearer screenshots - but increase token usage. -

-
+ )}
diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index abf921dc2b..ba735f27b2 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -27,6 +27,7 @@ export interface ExtensionStateContextType extends ExtensionState { setAlwaysAllowBrowser: (value: boolean) => void setAlwaysAllowMcp: (value: boolean) => void setAlwaysAllowModeSwitch: (value: boolean) => void + setBrowserToolEnabled: (value: boolean) => void setShowAnnouncement: (value: boolean) => void setAllowedCommands: (value: string[]) => void setSoundEnabled: (value: boolean) => void @@ -128,6 +129,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode customModes: [], maxOpenTabsContext: 20, cwd: "", + browserToolEnabled: true, }) const [didHydrateState, setDidHydrateState] = useState(false) @@ -265,6 +267,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode setAutoApprovalEnabled: (value) => setState((prevState) => ({ ...prevState, autoApprovalEnabled: value })), setCustomModes: (value) => setState((prevState) => ({ ...prevState, customModes: value })), setMaxOpenTabsContext: (value) => setState((prevState) => ({ ...prevState, maxOpenTabsContext: value })), + setBrowserToolEnabled: (value) => setState((prevState) => ({ ...prevState, browserToolEnabled: value })), } return {children}