mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
Merge pull request #1 from cannuri/update-from-upstream
Update from upstream
This commit is contained in:
commit
58532bc6db
23 changed files with 323 additions and 52 deletions
5
.changeset/empty-bees-suffer.md
Normal file
5
.changeset/empty-bees-suffer.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"roo-cline": patch
|
||||
---
|
||||
|
||||
Revert tool progress for now
|
||||
5
.changeset/sixty-ants-begin.md
Normal file
5
.changeset/sixty-ants-begin.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"roo-cline": patch
|
||||
---
|
||||
|
||||
v3.8.4
|
||||
12
CHANGELOG.md
12
CHANGELOG.md
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
26
package-lock.json
generated
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
13
src/__mocks__/strip-bom.js
Normal file
13
src/__mocks__/strip-bom.js
Normal 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
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
isn’t 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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 })),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue