Merge pull request #1 from cannuri/update-from-upstream

Update from upstream
This commit is contained in:
cannuri 2025-03-10 00:51:35 +01:00 committed by GitHub
commit 58532bc6db
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 323 additions and 52 deletions

View file

@ -0,0 +1,5 @@
---
"roo-cline": patch
---
Revert tool progress for now

View file

@ -0,0 +1,5 @@
---
"roo-cline": patch
---
v3.8.4

View file

@ -1,5 +1,17 @@
# Roo Code Changelog
## [3.8.3] - 2025-03-09
- Fix VS Code LM API model picker truncation issue
## [3.8.2] - 2025-03-08
- Create an auto-approval toggle for subtask creation and completion (thanks @shaybc!)
- Show a progress indicator when using the multi-diff editing strategy (thanks @qdaxb!)
- Add o3-mini support to the OpenAI-compatible provider (thanks @yt3trees!)
- Fix encoding issue where unreadable characters were sometimes getting added to the beginning of files
- Fix issue where settings dropdowns were getting truncated in some cases
## [3.8.1] - 2025-03-07
- Show the reserved output tokens in the context window visualization

View file

@ -30,9 +30,10 @@ module.exports = {
"^strip-ansi$": "<rootDir>/src/__mocks__/strip-ansi.js",
"^default-shell$": "<rootDir>/src/__mocks__/default-shell.js",
"^os-name$": "<rootDir>/src/__mocks__/os-name.js",
"^strip-bom$": "<rootDir>/src/__mocks__/strip-bom.js",
},
transformIgnorePatterns: [
"node_modules/(?!(@modelcontextprotocol|delay|p-wait-for|globby|serialize-error|strip-ansi|default-shell|os-name)/)",
"node_modules/(?!(@modelcontextprotocol|delay|p-wait-for|globby|serialize-error|strip-ansi|default-shell|os-name|strip-bom)/)",
],
roots: ["<rootDir>/src", "<rootDir>/webview-ui/src"],
modulePathIgnorePatterns: [".vscode-test"],

26
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "roo-cline",
"version": "3.8.1",
"version": "3.8.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "roo-cline",
"version": "3.8.1",
"version": "3.8.3",
"dependencies": {
"@anthropic-ai/bedrock-sdk": "^0.10.2",
"@anthropic-ai/sdk": "^0.37.0",
@ -51,6 +51,7 @@
"sound-play": "^1.1.0",
"string-similarity": "^4.0.4",
"strip-ansi": "^7.1.0",
"strip-bom": "^5.0.0",
"tmp": "^0.2.3",
"tree-sitter-wasms": "^0.1.11",
"turndown": "^7.2.0",
@ -10782,6 +10783,15 @@
"node": ">=8"
}
},
"node_modules/jest-runtime/node_modules/strip-bom": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
"integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/jest-simple-dot-reporter": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/jest-simple-dot-reporter/-/jest-simple-dot-reporter-1.0.5.tgz",
@ -14170,12 +14180,14 @@
}
},
"node_modules/strip-bom": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
"integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
"dev": true,
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-5.0.0.tgz",
"integrity": "sha512-p+byADHF7SzEcVnLvc/r3uognM1hUhObuHXxJcgLCfD194XAkaLbjq3Wzb0N5G2tgIjH0dgT708Z51QxMeu60A==",
"engines": {
"node": ">=8"
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/strip-final-newline": {

View file

@ -3,7 +3,7 @@
"displayName": "Roo Code (prev. Roo Cline)",
"description": "A whole dev team of AI agents in your editor.",
"publisher": "RooVeterinaryInc",
"version": "3.8.1",
"version": "3.8.3",
"icon": "assets/icons/rocket.png",
"galleryBanner": {
"color": "#617A91",
@ -265,8 +265,8 @@
"@anthropic-ai/sdk": "^0.37.0",
"@anthropic-ai/vertex-sdk": "^0.7.0",
"@aws-sdk/client-bedrock-runtime": "^3.706.0",
"@google/generative-ai": "^0.18.0",
"@google-cloud/vertexai": "^1.9.3",
"@google/generative-ai": "^0.18.0",
"@mistralai/mistralai": "^1.3.6",
"@modelcontextprotocol/sdk": "^1.0.1",
"@types/clone-deep": "^4.0.4",
@ -304,6 +304,7 @@
"sound-play": "^1.1.0",
"string-similarity": "^4.0.4",
"strip-ansi": "^7.1.0",
"strip-bom": "^5.0.0",
"tmp": "^0.2.3",
"tree-sitter-wasms": "^0.1.11",
"turndown": "^7.2.0",

View file

@ -0,0 +1,13 @@
// Mock implementation of strip-bom
module.exports = function stripBom(string) {
if (typeof string !== "string") {
throw new TypeError("Expected a string")
}
// Removes UTF-8 BOM
if (string.charCodeAt(0) === 0xfeff) {
return string.slice(1)
}
return string
}

View file

@ -66,6 +66,11 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
const deepseekReasoner = modelId.includes("deepseek-reasoner")
const ark = modelUrl.includes(".volces.com")
if (modelId.startsWith("o3-mini")) {
yield* this.handleO3FamilyMessage(modelId, systemPrompt, messages)
return
}
if (this.options.openAiStreamingEnabled ?? true) {
const systemMessage: OpenAI.Chat.ChatCompletionSystemMessageParam = {
role: "system",
@ -169,6 +174,69 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
throw error
}
}
private async *handleO3FamilyMessage(
modelId: string,
systemPrompt: string,
messages: Anthropic.Messages.MessageParam[],
): ApiStream {
if (this.options.openAiStreamingEnabled ?? true) {
const stream = await this.client.chat.completions.create({
model: "o3-mini",
messages: [
{
role: "developer",
content: `Formatting re-enabled\n${systemPrompt}`,
},
...convertToOpenAiMessages(messages),
],
stream: true,
stream_options: { include_usage: true },
reasoning_effort: this.getModel().info.reasoningEffort,
})
yield* this.handleStreamResponse(stream)
} else {
const requestOptions: OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming = {
model: modelId,
messages: [
{
role: "developer",
content: `Formatting re-enabled\n${systemPrompt}`,
},
...convertToOpenAiMessages(messages),
],
}
const response = await this.client.chat.completions.create(requestOptions)
yield {
type: "text",
text: response.choices[0]?.message.content || "",
}
yield this.processUsageMetrics(response.usage)
}
}
private async *handleStreamResponse(stream: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>): ApiStream {
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta
if (delta?.content) {
yield {
type: "text",
text: delta.content,
}
}
if (chunk.usage) {
yield {
type: "usage",
inputTokens: chunk.usage.prompt_tokens || 0,
outputTokens: chunk.usage.completion_tokens || 0,
}
}
}
}
}
export async function getOpenAiModels(baseUrl?: string, apiKey?: string) {

View file

@ -1417,6 +1417,18 @@ export class Cline {
return true
}
const askFinishSubTaskApproval = async () => {
// ask the user to approve this task has completed, and he has reviewd it, and we can declare task is finished
// and return control to the parent task to continue running the rest of the sub-tasks
const toolMessage = JSON.stringify({
tool: "finishTask",
content:
"Subtask completed! You can review the results and suggest any corrections or next steps. If everything looks good, confirm to return the result to the parent task.",
})
return await askApproval("tool", toolMessage)
}
const handleError = async (action: string, error: Error) => {
const errorString = `Error ${action}: ${JSON.stringify(serializeError(error))}`
await this.say(
@ -2945,13 +2957,6 @@ export class Cline {
// havent sent a command message yet so first send completion_result then command
await this.say("completion_result", result, undefined, false)
telemetryService.captureTaskCompleted(this.taskId)
if (this.isSubTask) {
// tell the provider to remove the current subtask and resume the previous task in the stack
await this.providerRef
.deref()
?.finishSubTask(`Task complete: ${lastMessage?.text}`)
break
}
}
// complete command message
@ -2970,13 +2975,17 @@ export class Cline {
} else {
await this.say("completion_result", result, undefined, false)
telemetryService.captureTaskCompleted(this.taskId)
if (this.isSubTask) {
// tell the provider to remove the current subtask and resume the previous task in the stack
await this.providerRef
.deref()
?.finishSubTask(`Task complete: ${lastMessage?.text}`)
}
if (this.isSubTask) {
const didApprove = await askFinishSubTaskApproval()
if (!didApprove) {
break
}
// tell the provider to remove the current subtask and resume the previous task in the stack
await this.providerRef.deref()?.finishSubTask(`Task complete: ${lastMessage?.text}`)
break
}
// we already sent completion_result says, an empty string asks relinquishes control over button and field

View file

@ -11,12 +11,19 @@ export async function getModesSection(context: vscode.ExtensionContext): Promise
// Get all modes with their overrides from extension state
const allModes = await getAllModesWithPrompts(context)
return `====
// Get enableCustomModeCreation setting from extension state
const shouldEnableCustomModeCreation = await context.globalState.get<boolean>("enableCustomModeCreation") ?? true
let modesContent = `====
MODES
- These are the currently available modes:
${allModes.map((mode: ModeConfig) => ` * "${mode.name}" mode (${mode.slug}) - ${mode.roleDefinition.split(".")[0]}`).join("\n")}
${allModes.map((mode: ModeConfig) => ` * "${mode.name}" mode (${mode.slug}) - ${mode.roleDefinition.split(".")[0]}`).join("\n")}`
// Only include custom modes documentation if the feature is enabled
if (shouldEnableCustomModeCreation) {
modesContent += `
- Custom modes can be configured in two ways:
1. Globally via '${customModesPath}' (created automatically on startup)
@ -56,4 +63,7 @@ Both files should follow this structure:
}
]
}`
}
return modesContent
}

View file

@ -984,6 +984,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
await this.updateGlobalState("alwaysAllowModeSwitch", message.bool)
await this.postStateToWebview()
break
case "alwaysAllowSubtasks":
await this.updateGlobalState("alwaysAllowSubtasks", message.bool)
await this.postStateToWebview()
break
case "askResponse":
this.getCurrentCline()?.handleWebviewAskResponse(
message.askResponse!,
@ -993,9 +997,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
break
case "clearTask":
// clear task resets the current session and allows for a new task to be started, if this session is a subtask - it allows the parent task to be resumed
await this.finishSubTask(
`new_task finished with an error!, it was stopped and canceled by the user.`,
)
await this.finishSubTask(`Task error: It was stopped and canceled by the user.`)
await this.postStateToWebview()
break
case "didShowAnnouncement":
@ -1476,6 +1478,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
await this.updateGlobalState("enhancementApiConfigId", message.text)
await this.postStateToWebview()
break
case "enableCustomModeCreation":
await this.updateGlobalState("enableCustomModeCreation", message.bool ?? true)
await this.postStateToWebview()
break
case "autoApprovalEnabled":
await this.updateGlobalState("autoApprovalEnabled", message.bool ?? false)
await this.postStateToWebview()
@ -2177,6 +2183,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
alwaysAllowBrowser,
alwaysAllowMcp,
alwaysAllowModeSwitch,
alwaysAllowSubtasks,
soundEnabled,
diffEnabled,
enableCheckpoints,
@ -2224,6 +2231,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
alwaysAllowBrowser: alwaysAllowBrowser ?? false,
alwaysAllowMcp: alwaysAllowMcp ?? false,
alwaysAllowModeSwitch: alwaysAllowModeSwitch ?? false,
alwaysAllowSubtasks: alwaysAllowSubtasks ?? false,
uriScheme: vscode.env.uriScheme,
currentTaskItem: this.getCurrentCline()?.taskId
? (taskHistory || []).find((item: HistoryItem) => item.id === this.getCurrentCline()?.taskId)
@ -2385,6 +2393,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
alwaysAllowBrowser: stateValues.alwaysAllowBrowser ?? false,
alwaysAllowMcp: stateValues.alwaysAllowMcp ?? false,
alwaysAllowModeSwitch: stateValues.alwaysAllowModeSwitch ?? false,
alwaysAllowSubtasks: stateValues.alwaysAllowSubtasks ?? false,
taskHistory: stateValues.taskHistory,
allowedCommands: stateValues.allowedCommands,
soundEnabled: stateValues.soundEnabled ?? false,

View file

@ -7,6 +7,7 @@ import { formatResponse } from "../../core/prompts/responses"
import { DecorationController } from "./DecorationController"
import * as diff from "diff"
import { diagnosticsToProblemsString, getNewDiagnostics } from "../diagnostics"
import stripBom from "strip-bom"
export const DIFF_VIEW_URI_SCHEME = "cline-diff"
@ -104,7 +105,7 @@ export class DiffViewProvider {
const edit = new vscode.WorkspaceEdit()
const rangeToReplace = new vscode.Range(0, 0, endLine + 1, 0)
const contentToReplace = accumulatedLines.slice(0, endLine + 1).join("\n") + "\n"
edit.replace(document.uri, rangeToReplace, contentToReplace)
edit.replace(document.uri, rangeToReplace, this.stripAllBOMs(contentToReplace))
await vscode.workspace.applyEdit(edit)
// Update decorations
this.activeLineController.setActiveLine(endLine)
@ -128,7 +129,11 @@ export class DiffViewProvider {
}
// Apply the final content
const finalEdit = new vscode.WorkspaceEdit()
finalEdit.replace(document.uri, new vscode.Range(0, 0, document.lineCount, 0), accumulatedContent)
finalEdit.replace(
document.uri,
new vscode.Range(0, 0, document.lineCount, 0),
this.stripAllBOMs(accumulatedContent),
)
await vscode.workspace.applyEdit(finalEdit)
// Clear all decorations at the end (after applying final edit)
this.fadedOverlayController.clear()
@ -336,6 +341,16 @@ export class DiffViewProvider {
}
}
private stripAllBOMs(input: string): string {
let result = input
let previous
do {
previous = result
result = stripBom(result)
} while (result !== previous)
return result
}
// close editor if open?
async reset() {
this.editType = undefined

View file

@ -109,6 +109,7 @@ export interface ExtensionState {
alwaysAllowMcp?: boolean
alwaysApproveResubmit?: boolean
alwaysAllowModeSwitch?: boolean
alwaysAllowSubtasks?: boolean
browserToolEnabled?: boolean
requestDelaySeconds: number
rateLimitSeconds: number // Minimum time between successive requests (0 = disabled)
@ -128,6 +129,7 @@ export interface ExtensionState {
terminalOutputLimit?: number
mcpEnabled: boolean
enableMcpServerCreation: boolean
enableCustomModeCreation?: boolean
mode: Mode
modeApiConfigs?: Record<Mode, string>
enhancementApiConfigId?: string
@ -168,6 +170,7 @@ export type ClineAsk =
| "mistake_limit_reached"
| "browser_action_launch"
| "use_mcp_server"
| "finishTask"
export type ClineSay =
| "task"
@ -207,6 +210,7 @@ export interface ClineSayTool {
| "searchFiles"
| "switchMode"
| "newTask"
| "finishTask"
path?: string
diff?: string
content?: string

View file

@ -48,6 +48,7 @@ export interface WebviewMessage {
| "alwaysAllowBrowser"
| "alwaysAllowMcp"
| "alwaysAllowModeSwitch"
| "alwaysAllowSubtasks"
| "playSound"
| "soundEnabled"
| "soundVolume"
@ -71,6 +72,7 @@ export interface WebviewMessage {
| "terminalOutputLimit"
| "mcpEnabled"
| "enableMcpServerCreation"
| "enableCustomModeCreation"
| "searchCommits"
| "alwaysApproveResubmit"
| "requestDelaySeconds"

View file

@ -40,6 +40,7 @@ export const GLOBAL_STATE_KEYS = [
"alwaysAllowBrowser",
"alwaysAllowMcp",
"alwaysAllowModeSwitch",
"alwaysAllowSubtasks",
"taskHistory",
"openAiBaseUrl",
"openAiModelId",
@ -84,6 +85,7 @@ export const GLOBAL_STATE_KEYS = [
"enhancementApiConfigId",
"experiments", // Map of experiment IDs to their enabled state
"autoApprovalEnabled",
"enableCustomModeCreation", // Enable the ability for Roo to create custom modes
"customModes", // Array of custom modes
"unboundModelId",
"requestyModelId",

View file

@ -30,6 +30,8 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
setAlwaysAllowMcp,
alwaysAllowModeSwitch,
setAlwaysAllowModeSwitch,
alwaysAllowSubtasks,
setAlwaysAllowSubtasks,
alwaysApproveResubmit,
setAlwaysApproveResubmit,
autoApprovalEnabled,
@ -75,11 +77,17 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
},
{
id: "switchModes",
label: "Switch modes & create tasks",
label: "Switch modes",
shortName: "Modes",
enabled: alwaysAllowModeSwitch ?? false,
description:
"Allows automatic switching between different AI modes and creating new tasks without requiring approval.",
description: "Allows automatic switching between different modes without requiring approval.",
},
{
id: "subtasks",
label: "Create & complete subtasks",
shortName: "Subtasks",
enabled: alwaysAllowSubtasks ?? false,
description: "Allow creation and completion of subtasks without requiring approval.",
},
{
id: "retryRequests",
@ -136,6 +144,12 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
vscode.postMessage({ type: "alwaysAllowModeSwitch", bool: newValue })
}, [alwaysAllowModeSwitch, setAlwaysAllowModeSwitch])
const handleSubtasksChange = useCallback(() => {
const newValue = !(alwaysAllowSubtasks ?? false)
setAlwaysAllowSubtasks(newValue)
vscode.postMessage({ type: "alwaysAllowSubtasks", bool: newValue })
}, [alwaysAllowSubtasks, setAlwaysAllowSubtasks])
const handleRetryChange = useCallback(() => {
const newValue = !(alwaysApproveResubmit ?? false)
setAlwaysApproveResubmit(newValue)
@ -150,6 +164,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
useBrowser: handleBrowserChange,
useMcp: handleMcpChange,
switchModes: handleModeSwitchChange,
subtasks: handleSubtasksChange,
retryRequests: handleRetryChange,
}

View file

@ -451,7 +451,7 @@ export const ChatRowContent = ({
<div style={headerStyle}>
{toolIcon("new-file")}
<span style={{ fontWeight: "bold" }}>
Roo wants to create a new task in <code>{tool.mode}</code> mode:
Roo wants to create a new subtask in <code>{tool.mode}</code> mode:
</span>
</div>
<div style={{ paddingLeft: "26px", marginTop: "4px" }}>
@ -459,6 +459,18 @@ export const ChatRowContent = ({
</div>
</>
)
case "finishTask":
return (
<>
<div style={headerStyle}>
{toolIcon("checklist")}
<span style={{ fontWeight: "bold" }}>Roo wants to finish this subtask</span>
</div>
<div style={{ paddingLeft: "26px", marginTop: "4px" }}>
<code>{tool.content}</code>
</div>
</>
)
default:
return null
}

View file

@ -61,6 +61,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
setMode,
autoApprovalEnabled,
alwaysAllowModeSwitch,
alwaysAllowSubtasks,
customModes,
telemetrySetting,
} = useExtensionState()
@ -148,6 +149,10 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
setPrimaryButtonText("Save")
setSecondaryButtonText("Reject")
break
case "finishTask":
setPrimaryButtonText("Complete Subtask and Return")
setSecondaryButtonText(undefined)
break
default:
setPrimaryButtonText("Approve")
setSecondaryButtonText("Reject")
@ -641,8 +646,10 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
(alwaysAllowMcp && message.ask === "use_mcp_server" && isMcpToolAlwaysAllowed(message)) ||
(alwaysAllowModeSwitch &&
message.ask === "tool" &&
(JSON.parse(message.text || "{}")?.tool === "switchMode" ||
JSON.parse(message.text || "{}")?.tool === "newTask"))
JSON.parse(message.text || "{}")?.tool === "switchMode") ||
(alwaysAllowSubtasks &&
message.ask === "tool" &&
["newTask", "finishTask"].includes(JSON.parse(message.text || "{}")?.tool))
)
},
[
@ -657,6 +664,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
alwaysAllowMcp,
isMcpToolAlwaysAllowed,
alwaysAllowModeSwitch,
alwaysAllowSubtasks,
],
)

View file

@ -71,6 +71,8 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
preferredLanguage,
setPreferredLanguage,
customModes,
enableCustomModeCreation,
setEnableCustomModeCreation,
} = useExtensionState()
// Memoize modes to preserve array order
@ -341,6 +343,17 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
return () => document.removeEventListener("click", handleClickOutside)
}, [showConfigMenu])
// Add effect to sync enableCustomModeCreation with backend
useEffect(() => {
if (enableCustomModeCreation !== undefined) {
// Send the value to the extension's global state
vscode.postMessage({
type: "enableCustomModeCreation", // Using dedicated message type
bool: enableCustomModeCreation,
})
}
}, [enableCustomModeCreation])
useEffect(() => {
const handler = (event: MessageEvent) => {
const message = event.data
@ -541,7 +554,6 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
in your workspace.
</div>
</div>
<div className="mt-5">
<div onClick={(e) => e.stopPropagation()} className="flex justify-between items-center mb-3">
<h3 className="text-vscode-foreground m-0">Modes</h3>
@ -1010,6 +1022,35 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
</div>
)}
</div>
{/*
NOTE: This setting is placed in PromptsView rather than SettingsView since it
directly affects the functionality related to modes and custom mode creation,
which are managed in this component. This is an intentional deviation from
the standard pattern described in cline_docs/settings.md.
*/}
<div className="mb-4 mt-4">
<VSCodeCheckbox
checked={enableCustomModeCreation ?? true}
onChange={(e: any) => {
// Just update the local state through React context
// The React context will update the global state
setEnableCustomModeCreation(e.target.checked)
}}>
<span style={{ fontWeight: "500" }}>Enable Custom Mode Creation Through Prompts</span>
</VSCodeCheckbox>
<p
style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
When enabled, Roo allows you to create custom modes using prompts like Make me a custom
mode that. Disabling this reduces your system prompt by about 700 tokens when this feature
isnt needed. When disabled you can still manually create custom modes using the + button
above or by editing the related config JSON.
</p>
</div>
</div>
<div

View file

@ -1076,27 +1076,31 @@ const ApiOptions = ({
Language Model
</label>
{vsCodeLmModels.length > 0 ? (
<Dropdown
id="vscode-lm-model"
<Select
value={
apiConfiguration?.vsCodeLmModelSelector
? `${apiConfiguration.vsCodeLmModelSelector.vendor ?? ""}/${apiConfiguration.vsCodeLmModelSelector.family ?? ""}`
: ""
}
onChange={handleInputChange("vsCodeLmModelSelector", (e) => {
const valueStr = (e as DropdownOption)?.value
onValueChange={handleInputChange("vsCodeLmModelSelector", (valueStr) => {
const [vendor, family] = valueStr.split("/")
return { vendor, family }
})}
options={[
{ value: "", label: "Select a model..." },
...vsCodeLmModels.map((model) => ({
value: `${model.vendor}/${model.family}`,
label: `${model.vendor} - ${model.family}`,
})),
]}
className="w-full"
/>
})}>
<SelectTrigger className="w-full">
<SelectValue placeholder="Select a model..." />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{vsCodeLmModels.map((model) => (
<SelectItem
key={`${model.vendor}/${model.family}`}
value={`${model.vendor}/${model.family}`}>
{`${model.vendor} - ${model.family}`}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
) : (
<div className="text-sm text-vscode-descriptionForeground">
The VS Code Language Model API allows you to run models provided by other VS Code

View file

@ -18,6 +18,7 @@ type AutoApproveSettingsProps = HTMLAttributes<HTMLDivElement> & {
requestDelaySeconds: number
alwaysAllowMcp?: boolean
alwaysAllowModeSwitch?: boolean
alwaysAllowSubtasks?: boolean
alwaysAllowExecute?: boolean
allowedCommands?: string[]
setCachedStateField: SetCachedStateField<keyof ExtensionStateContextType>
@ -32,6 +33,7 @@ export const AutoApproveSettings = ({
requestDelaySeconds,
alwaysAllowMcp,
alwaysAllowModeSwitch,
alwaysAllowSubtasks,
alwaysAllowExecute,
allowedCommands,
setCachedStateField,
@ -173,10 +175,21 @@ export const AutoApproveSettings = ({
<VSCodeCheckbox
checked={alwaysAllowModeSwitch}
onChange={(e: any) => setCachedStateField("alwaysAllowModeSwitch", e.target.checked)}>
<span className="font-medium">Always approve mode switching & task creation</span>
<span className="font-medium">Always approve mode switching</span>
</VSCodeCheckbox>
<p className="text-vscode-descriptionForeground text-sm mt-0">
Automatically switch between different AI modes and create new tasks without requiring approval
Automatically switch between different modes without requiring approval
</p>
</div>
<div>
<VSCodeCheckbox
checked={alwaysAllowSubtasks}
onChange={(e: any) => setCachedStateField("alwaysAllowSubtasks", e.target.checked)}>
<span className="font-medium">Always approve creation & completion of subtasks</span>
</VSCodeCheckbox>
<p className="text-vscode-descriptionForeground text-sm mt-0">
Allow creation and completion of subtasks without requiring approval
</p>
</div>

View file

@ -63,6 +63,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
alwaysAllowExecute,
alwaysAllowMcp,
alwaysAllowModeSwitch,
alwaysAllowSubtasks,
alwaysAllowWrite,
alwaysApproveResubmit,
browserToolEnabled,
@ -184,6 +185,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
vscode.postMessage({ type: "currentApiConfigName", text: currentApiConfigName })
vscode.postMessage({ type: "updateExperimental", values: experiments })
vscode.postMessage({ type: "alwaysAllowModeSwitch", bool: alwaysAllowModeSwitch })
vscode.postMessage({ type: "alwaysAllowSubtasks", bool: alwaysAllowSubtasks })
vscode.postMessage({ type: "upsertApiConfiguration", text: currentApiConfigName, apiConfiguration })
vscode.postMessage({ type: "telemetrySetting", text: telemetrySetting })
setChangeDetected(false)
@ -364,6 +366,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
requestDelaySeconds={requestDelaySeconds}
alwaysAllowMcp={alwaysAllowMcp}
alwaysAllowModeSwitch={alwaysAllowModeSwitch}
alwaysAllowSubtasks={alwaysAllowSubtasks}
alwaysAllowExecute={alwaysAllowExecute}
allowedCommands={allowedCommands}
setCachedStateField={setCachedStateField}

View file

@ -31,6 +31,7 @@ export interface ExtensionStateContextType extends ExtensionState {
setAlwaysAllowBrowser: (value: boolean) => void
setAlwaysAllowMcp: (value: boolean) => void
setAlwaysAllowModeSwitch: (value: boolean) => void
setAlwaysAllowSubtasks: (value: boolean) => void
setBrowserToolEnabled: (value: boolean) => void
setShowRooIgnoredFiles: (value: boolean) => void
setShowAnnouncement: (value: boolean) => void
@ -52,6 +53,8 @@ export interface ExtensionStateContextType extends ExtensionState {
setMcpEnabled: (value: boolean) => void
enableMcpServerCreation: boolean
setEnableMcpServerCreation: (value: boolean) => void
enableCustomModeCreation?: boolean
setEnableCustomModeCreation: (value: boolean) => void
alwaysApproveResubmit?: boolean
setAlwaysApproveResubmit: (value: boolean) => void
requestDelaySeconds: number
@ -117,6 +120,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
checkpointStorage: "task",
fuzzyMatchThreshold: 1.0,
preferredLanguage: "English",
enableCustomModeCreation: true,
writeDelayMs: 1000,
browserViewportSize: "900x600",
screenshotQuality: 75,
@ -247,6 +251,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
setAlwaysAllowBrowser: (value) => setState((prevState) => ({ ...prevState, alwaysAllowBrowser: value })),
setAlwaysAllowMcp: (value) => setState((prevState) => ({ ...prevState, alwaysAllowMcp: value })),
setAlwaysAllowModeSwitch: (value) => setState((prevState) => ({ ...prevState, alwaysAllowModeSwitch: value })),
setAlwaysAllowSubtasks: (value) => setState((prevState) => ({ ...prevState, alwaysAllowSubtasks: value })),
setShowAnnouncement: (value) => setState((prevState) => ({ ...prevState, shouldShowAnnouncement: value })),
setAllowedCommands: (value) => setState((prevState) => ({ ...prevState, allowedCommands: value })),
setSoundEnabled: (value) => setState((prevState) => ({ ...prevState, soundEnabled: value })),
@ -273,6 +278,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
setCustomSupportPrompts: (value) => setState((prevState) => ({ ...prevState, customSupportPrompts: value })),
setEnhancementApiConfigId: (value) =>
setState((prevState) => ({ ...prevState, enhancementApiConfigId: value })),
setEnableCustomModeCreation: (value) =>
setState((prevState) => ({ ...prevState, enableCustomModeCreation: value })),
setAutoApprovalEnabled: (value) => setState((prevState) => ({ ...prevState, autoApprovalEnabled: value })),
setCustomModes: (value) => setState((prevState) => ({ ...prevState, customModes: value })),
setMaxOpenTabsContext: (value) => setState((prevState) => ({ ...prevState, maxOpenTabsContext: value })),