Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fruity-spoons-smash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"roo-cline": minor
---

Added an auto-approve API request limit setting similar to Cline
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ mock/

.DS_Store

# IDEs
.idea

# Builds
bin/
roo-cline-*.vsix
Expand Down
1 change: 1 addition & 0 deletions evals/packages/types/src/roo-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,7 @@ export const clineAsks = [
"resume_task",
"resume_completed_task",
"mistake_limit_reached",
"auto_approval_max_req_reached",
"browser_action_launch",
"use_mcp_server",
] as const
Expand Down
16 changes: 16 additions & 0 deletions src/core/task/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ export class Task extends EventEmitter<ClineEvents> {
readonly apiConfiguration: ProviderSettings
api: ApiHandler
private lastApiRequestTime?: number
private consecutiveAutoApprovedRequestsCount: number = 0

toolRepetitionDetector: ToolRepetitionDetector
rooIgnoreController?: RooIgnoreController
Expand Down Expand Up @@ -1505,6 +1506,21 @@ export class Task extends EventEmitter<ClineEvents> {
({ role, content }) => ({ role, content }),
)

// Check if we've reached the maximum number of auto-approved requests
const { allowedMaxRequests } = (await this.providerRef.deref()?.getState()) ?? {}
const maxRequests = allowedMaxRequests || Infinity

// Increment the counter for each new API request
this.consecutiveAutoApprovedRequestsCount++

if (this.consecutiveAutoApprovedRequestsCount > maxRequests) {
const { response } = await this.ask("auto_approval_max_req_reached", JSON.stringify({ count: maxRequests }))
// If we get past the promise, it means the user approved and did not start a new task
if (response === "yesButtonClicked") {
this.consecutiveAutoApprovedRequestsCount = 0
}
}

const stream = this.api.createMessage(systemPrompt, cleanConversationHistory)
const iterator = stream[Symbol.asyncIterator]()

Expand Down
4 changes: 3 additions & 1 deletion src/core/webview/ClineProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1193,6 +1193,7 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
alwaysAllowMcp,
alwaysAllowModeSwitch,
alwaysAllowSubtasks,
allowedMaxRequests,
soundEnabled,
ttsEnabled,
ttsSpeed,
Expand Down Expand Up @@ -1263,6 +1264,7 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
alwaysAllowMcp: alwaysAllowMcp ?? false,
alwaysAllowModeSwitch: alwaysAllowModeSwitch ?? false,
alwaysAllowSubtasks: alwaysAllowSubtasks ?? false,
allowedMaxRequests: allowedMaxRequests ?? Infinity,
uriScheme: vscode.env.uriScheme,
currentTaskItem: this.getCurrentCline()?.taskId
? (taskHistory || []).find((item: HistoryItem) => item.id === this.getCurrentCline()?.taskId)
Expand Down Expand Up @@ -1337,7 +1339,6 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements

async getState() {
const stateValues = this.contextProxy.getValues()

const customModes = await this.customModesManager.getCustomModes()

// Determine apiProvider with the same logic as before.
Expand Down Expand Up @@ -1366,6 +1367,7 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
alwaysAllowMcp: stateValues.alwaysAllowMcp ?? false,
alwaysAllowModeSwitch: stateValues.alwaysAllowModeSwitch ?? false,
alwaysAllowSubtasks: stateValues.alwaysAllowSubtasks ?? false,
allowedMaxRequests: stateValues.allowedMaxRequests ?? Infinity,
taskHistory: stateValues.taskHistory,
allowedCommands: stateValues.allowedCommands,
soundEnabled: stateValues.soundEnabled ?? false,
Expand Down
4 changes: 4 additions & 0 deletions src/core/webview/webviewMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
await updateGlobalState("alwaysAllowModeSwitch", message.bool)
await provider.postStateToWebview()
break
case "allowedMaxRequests":
await updateGlobalState("allowedMaxRequests", message.value)
await provider.postStateToWebview()
break
case "alwaysAllowSubtasks":
await updateGlobalState("alwaysAllowSubtasks", message.bool)
await provider.postStateToWebview()
Expand Down
7 changes: 7 additions & 0 deletions src/exports/roo-code.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ type GlobalSettings = {
alwaysAllowSubtasks?: boolean | undefined
alwaysAllowExecute?: boolean | undefined
allowedCommands?: string[] | undefined
allowedMaxRequests?: number | undefined
browserToolEnabled?: boolean | undefined
browserViewportSize?: string | undefined
screenshotQuality?: number | undefined
Expand Down Expand Up @@ -380,6 +381,7 @@ type ClineMessage = {
| "mistake_limit_reached"
| "browser_action_launch"
| "use_mcp_server"
| "auto_approval_max_req_reached"
)
| undefined
say?:
Expand Down Expand Up @@ -464,6 +466,7 @@ type RooCodeEvents = {
| "mistake_limit_reached"
| "browser_action_launch"
| "use_mcp_server"
| "auto_approval_max_req_reached"
)
| undefined
say?:
Expand Down Expand Up @@ -801,6 +804,7 @@ type IpcMessage =
alwaysAllowSubtasks?: boolean | undefined
alwaysAllowExecute?: boolean | undefined
allowedCommands?: string[] | undefined
allowedMaxRequests?: number | undefined
browserToolEnabled?: boolean | undefined
browserViewportSize?: string | undefined
screenshotQuality?: number | undefined
Expand Down Expand Up @@ -942,6 +946,7 @@ type IpcMessage =
| "mistake_limit_reached"
| "browser_action_launch"
| "use_mcp_server"
| "auto_approval_max_req_reached"
)
| undefined
say?:
Expand Down Expand Up @@ -1273,6 +1278,7 @@ type TaskCommand =
alwaysAllowSubtasks?: boolean | undefined
alwaysAllowExecute?: boolean | undefined
allowedCommands?: string[] | undefined
allowedMaxRequests?: number | undefined
browserToolEnabled?: boolean | undefined
browserViewportSize?: string | undefined
screenshotQuality?: number | undefined
Expand Down Expand Up @@ -1410,6 +1416,7 @@ type TaskEvent =
| "mistake_limit_reached"
| "browser_action_launch"
| "use_mcp_server"
| "auto_approval_max_req_reached"
)
| undefined
say?:
Expand Down
7 changes: 7 additions & 0 deletions src/exports/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ type GlobalSettings = {
alwaysAllowSubtasks?: boolean | undefined
alwaysAllowExecute?: boolean | undefined
allowedCommands?: string[] | undefined
allowedMaxRequests?: number | undefined
browserToolEnabled?: boolean | undefined
browserViewportSize?: string | undefined
screenshotQuality?: number | undefined
Expand Down Expand Up @@ -388,6 +389,7 @@ type ClineMessage = {
| "mistake_limit_reached"
| "browser_action_launch"
| "use_mcp_server"
| "auto_approval_max_req_reached"
)
| undefined
say?:
Expand Down Expand Up @@ -476,6 +478,7 @@ type RooCodeEvents = {
| "mistake_limit_reached"
| "browser_action_launch"
| "use_mcp_server"
| "auto_approval_max_req_reached"
)
| undefined
say?:
Expand Down Expand Up @@ -815,6 +818,7 @@ type IpcMessage =
alwaysAllowSubtasks?: boolean | undefined
alwaysAllowExecute?: boolean | undefined
allowedCommands?: string[] | undefined
allowedMaxRequests?: number | undefined
browserToolEnabled?: boolean | undefined
browserViewportSize?: string | undefined
screenshotQuality?: number | undefined
Expand Down Expand Up @@ -956,6 +960,7 @@ type IpcMessage =
| "mistake_limit_reached"
| "browser_action_launch"
| "use_mcp_server"
| "auto_approval_max_req_reached"
)
| undefined
say?:
Expand Down Expand Up @@ -1289,6 +1294,7 @@ type TaskCommand =
alwaysAllowSubtasks?: boolean | undefined
alwaysAllowExecute?: boolean | undefined
allowedCommands?: string[] | undefined
allowedMaxRequests?: number | undefined
browserToolEnabled?: boolean | undefined
browserViewportSize?: string | undefined
screenshotQuality?: number | undefined
Expand Down Expand Up @@ -1428,6 +1434,7 @@ type TaskEvent =
| "mistake_limit_reached"
| "browser_action_launch"
| "use_mcp_server"
| "auto_approval_max_req_reached"
)
| undefined
say?:
Expand Down
3 changes: 3 additions & 0 deletions src/schemas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,7 @@ export const globalSettingsSchema = z.object({
alwaysAllowSubtasks: z.boolean().optional(),
alwaysAllowExecute: z.boolean().optional(),
allowedCommands: z.array(z.string()).optional(),
allowedMaxRequests: z.number().optional(),

browserToolEnabled: z.boolean().optional(),
browserViewportSize: z.string().optional(),
Expand Down Expand Up @@ -756,6 +757,7 @@ const globalSettingsRecord: GlobalSettingsRecord = {
alwaysAllowSubtasks: undefined,
alwaysAllowExecute: undefined,
allowedCommands: undefined,
allowedMaxRequests: undefined,

browserToolEnabled: undefined,
browserViewportSize: undefined,
Expand Down Expand Up @@ -899,6 +901,7 @@ export const clineAsks = [
"mistake_limit_reached",
"browser_action_launch",
"use_mcp_server",
"auto_approval_max_req_reached",
] as const

export const clineAskSchema = z.enum(clineAsks)
Expand Down
1 change: 1 addition & 0 deletions src/shared/ExtensionMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export type ExtensionState = Pick<
| "alwaysAllowSubtasks"
| "alwaysAllowExecute"
| "allowedCommands"
| "allowedMaxRequests"
| "browserToolEnabled"
| "browserViewportSize"
| "screenshotQuality"
Expand Down
1 change: 1 addition & 0 deletions src/shared/WebviewMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export interface WebviewMessage {
| "alwaysAllowBrowser"
| "alwaysAllowMcp"
| "alwaysAllowModeSwitch"
| "allowedMaxRequests"
| "alwaysAllowSubtasks"
| "playSound"
| "playTts"
Expand Down
42 changes: 41 additions & 1 deletion webview-ui/src/components/chat/AutoApproveMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback, useMemo, useState } from "react"
import { Trans } from "react-i18next"
import { VSCodeCheckbox, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
import { VSCodeCheckbox, VSCodeLink, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"

import { vscode } from "@src/utils/vscode"
import { useExtensionState } from "@src/context/ExtensionStateContext"
Expand All @@ -25,6 +25,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
alwaysAllowModeSwitch,
alwaysAllowSubtasks,
alwaysApproveResubmit,
allowedMaxRequests,
setAlwaysAllowReadOnly,
setAlwaysAllowWrite,
setAlwaysAllowExecute,
Expand All @@ -33,6 +34,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
setAlwaysAllowModeSwitch,
setAlwaysAllowSubtasks,
setAlwaysApproveResubmit,
setAllowedMaxRequests,
} = useExtensionState()

const { t } = useAppTranslation()
Expand Down Expand Up @@ -196,7 +198,45 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
}}
/>
</div>

<AutoApproveToggle {...toggles} onToggle={onAutoApproveToggle} />

{/* Auto-approve API request count limit input row inspired by Cline */}
<div
style={{
display: "flex",
alignItems: "center",
gap: "8px",
marginTop: "10px",
marginBottom: "8px",
color: "var(--vscode-descriptionForeground)",
}}>
<span style={{ flexShrink: 1, minWidth: 0 }}>
<Trans i18nKey="settings:autoApprove.apiRequestLimit.title" />:
</span>
<VSCodeTextField
placeholder={t("settings:autoApprove.apiRequestLimit.unlimited")}
value={(allowedMaxRequests ?? Infinity) === Infinity ? "" : allowedMaxRequests?.toString()}
onInput={(e) => {
const input = e.target as HTMLInputElement
// Remove any non-numeric characters
input.value = input.value.replace(/[^0-9]/g, "")
const value = parseInt(input.value)
const parsedValue = !isNaN(value) && value > 0 ? value : undefined
setAllowedMaxRequests(parsedValue)
vscode.postMessage({ type: "allowedMaxRequests", value: parsedValue })
}}
style={{ flex: 1 }}
/>
</div>
<div
style={{
color: "var(--vscode-descriptionForeground)",
fontSize: "12px",
marginBottom: "10px",
}}>
<Trans i18nKey="settings:autoApprove.apiRequestLimit.description" />
</div>
</div>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React, { memo, useState } from "react"
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
import { ClineMessage } from "@roo/shared/ExtensionMessage"
import { vscode } from "@src/utils/vscode"
import { Trans } from "react-i18next"

type AutoApprovedRequestLimitWarningProps = {
message: ClineMessage
}

export const AutoApprovedRequestLimitWarning = memo(({ message }: AutoApprovedRequestLimitWarningProps) => {
const [buttonClicked, setButtonClicked] = useState(false)
const { count } = JSON.parse(message.text ?? "{}")

if (buttonClicked) {
return null
}

return (
<>
<div style={{ display: "flex", alignItems: "center", gap: "8px", color: "var(--vscode-foreground)" }}>
<span className="codicon codicon-warning" />
<span style={{ fontWeight: "bold" }}>
<Trans i18nKey="ask.autoApprovedRequestLimitReached.title" ns="chat" />
</span>
</div>

<div
className="bg-vscode-panel-border flex flex-col gap-3"
style={{
borderRadius: "4px",
display: "flex",
marginTop: "15px",
padding: "14px 16px 22px",
justifyContent: "center",
}}>
<div className="flex justify-between items-center">
<Trans i18nKey="ask.autoApprovedRequestLimitReached.description" ns="chat" values={{ count }} />
</div>
<VSCodeButton
style={{ width: "100%", padding: "6px", borderRadius: "4px" }}
onClick={(e) => {
e.preventDefault()
setButtonClicked(true)
vscode.postMessage({ type: "askResponse", askResponse: "yesButtonClicked" })
}}>
<Trans i18nKey="ask.autoApprovedRequestLimitReached.button" ns="chat" />
</VSCodeButton>
</div>
</>
)
})
4 changes: 4 additions & 0 deletions webview-ui/src/components/chat/ChatRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { ProgressIndicator } from "./ProgressIndicator"
import { Markdown } from "./Markdown"
import { CommandExecution } from "./CommandExecution"
import { CommandExecutionError } from "./CommandExecutionError"
import { AutoApprovedRequestLimitWarning } from "./AutoApprovedRequestLimitWarning"
import { ContextCondenseRow } from "./ContextCondenseRow"

interface ChatRowProps {
Expand Down Expand Up @@ -1086,6 +1087,9 @@ export const ChatRowContent = ({
/>
</>
)
case "auto_approval_max_req_reached": {
return <AutoApprovedRequestLimitWarning message={message} />
}
default:
return null
}
Expand Down
Loading
Loading