From ce3a492d8f34e9a6812f2d18cfbe0c889072b18c Mon Sep 17 00:00:00 2001 From: Alex Bason Date: Thu, 26 Jun 2025 09:33:51 -0400 Subject: [PATCH 1/3] feat: implement retry delay range selection with min/max bounds Description: - Add requestDelaySeconds and maxRequestDelaySeconds settings (defaults: 1-100) - Convert single retry delay to configurable range with exponential backoff clamping - Update UI to use dual-thumb range slider for min/max selection - Add comprehensive test coverage for new range settings - Update all locale translations to reflect range concept --- packages/types/src/global-settings.ts | 4 ++- src/core/task/Task.ts | 11 ++++++-- src/core/webview/ClineProvider.ts | 7 +++-- .../webview/__tests__/ClineProvider.spec.ts | 27 ++++++++++++++++--- src/core/webview/webviewMessageHandler.ts | 4 +++ src/shared/ExtensionMessage.ts | 1 + src/shared/WebviewMessage.ts | 1 + .../settings/AutoApproveSettings.tsx | 23 +++++++++++----- .../src/components/settings/SettingsView.tsx | 3 +++ webview-ui/src/components/ui/slider.tsx | 11 +++++++- .../src/context/ExtensionStateContext.tsx | 4 +++ .../__tests__/ExtensionStateContext.spec.tsx | 1 + webview-ui/src/i18n/locales/ca/settings.json | 2 +- webview-ui/src/i18n/locales/de/settings.json | 2 +- webview-ui/src/i18n/locales/en/settings.json | 2 +- webview-ui/src/i18n/locales/es/settings.json | 2 +- webview-ui/src/i18n/locales/fr/settings.json | 2 +- webview-ui/src/i18n/locales/hi/settings.json | 2 +- webview-ui/src/i18n/locales/id/settings.json | 2 +- webview-ui/src/i18n/locales/it/settings.json | 2 +- webview-ui/src/i18n/locales/ja/settings.json | 2 +- webview-ui/src/i18n/locales/ko/settings.json | 2 +- webview-ui/src/i18n/locales/nl/settings.json | 2 +- webview-ui/src/i18n/locales/pl/settings.json | 2 +- .../src/i18n/locales/pt-BR/settings.json | 2 +- webview-ui/src/i18n/locales/ru/settings.json | 2 +- webview-ui/src/i18n/locales/tr/settings.json | 2 +- webview-ui/src/i18n/locales/vi/settings.json | 2 +- .../src/i18n/locales/zh-CN/settings.json | 2 +- .../src/i18n/locales/zh-TW/settings.json | 2 +- 30 files changed, 99 insertions(+), 34 deletions(-) diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index e713cafa4c..bce5995b40 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -41,6 +41,7 @@ export const globalSettingsSchema = z.object({ alwaysAllowBrowser: z.boolean().optional(), alwaysApproveResubmit: z.boolean().optional(), requestDelaySeconds: z.number().optional(), + maxRequestDelaySeconds: z.number().optional(), alwaysAllowMcp: z.boolean().optional(), alwaysAllowModeSwitch: z.boolean().optional(), alwaysAllowSubtasks: z.boolean().optional(), @@ -184,7 +185,8 @@ export const EVALS_SETTINGS: RooCodeSettings = { writeDelayMs: 1000, alwaysAllowBrowser: true, alwaysApproveResubmit: true, - requestDelaySeconds: 10, + requestDelaySeconds: 5, + maxRequestDelaySeconds: 100, alwaysAllowMcp: true, alwaysAllowModeSwitch: true, alwaysAllowSubtasks: true, diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 46da7485ed..dad3554993 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -1640,6 +1640,7 @@ export class Task extends EventEmitter { autoApprovalEnabled, alwaysApproveResubmit, requestDelaySeconds, + maxRequestDelaySeconds, mode, autoCondenseContext = true, autoCondenseContextPercent = 100, @@ -1791,8 +1792,14 @@ export class Task extends EventEmitter { errorMsg = "Unknown error" } - const baseDelay = requestDelaySeconds || 5 - let exponentialDelay = Math.ceil(baseDelay * Math.pow(2, retryAttempt)) + const minDelay = requestDelaySeconds ?? 5 + const maxDelay = maxRequestDelaySeconds ?? 100 + + // Use the minimum delay as the base for exponential backoff + let exponentialDelay = Math.ceil(minDelay * Math.pow(2, retryAttempt)) + + // Clamp exponential delay to the configured range + exponentialDelay = Math.max(minDelay, Math.min(exponentialDelay, maxDelay)) // If the error is a 429, and the error details contain a retry delay, use that delay instead of exponential backoff if (error.status === 429) { diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 51cb9a275b..4d2bf072ed 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1382,6 +1382,7 @@ export class ClineProvider enableMcpServerCreation, alwaysApproveResubmit, requestDelaySeconds, + maxRequestDelaySeconds, currentApiConfigName, listApiConfigMeta, pinnedApiConfigs, @@ -1475,7 +1476,8 @@ export class ClineProvider mcpEnabled: mcpEnabled ?? true, enableMcpServerCreation: enableMcpServerCreation ?? true, alwaysApproveResubmit: alwaysApproveResubmit ?? false, - requestDelaySeconds: requestDelaySeconds ?? 10, + requestDelaySeconds: requestDelaySeconds ?? 5, + maxRequestDelaySeconds: maxRequestDelaySeconds ?? 100, currentApiConfigName: currentApiConfigName ?? "default", listApiConfigMeta: listApiConfigMeta ?? [], pinnedApiConfigs: pinnedApiConfigs ?? {}, @@ -1635,7 +1637,8 @@ export class ClineProvider mcpEnabled: stateValues.mcpEnabled ?? true, enableMcpServerCreation: stateValues.enableMcpServerCreation ?? true, alwaysApproveResubmit: stateValues.alwaysApproveResubmit ?? false, - requestDelaySeconds: Math.max(5, stateValues.requestDelaySeconds ?? 10), + requestDelaySeconds: Math.max(1, stateValues.requestDelaySeconds ?? 5), + maxRequestDelaySeconds: Math.min(100, stateValues.maxRequestDelaySeconds ?? 100), currentApiConfigName: stateValues.currentApiConfigName ?? "default", listApiConfigMeta: stateValues.listApiConfigMeta ?? [], pinnedApiConfigs: stateValues.pinnedApiConfigs ?? {}, diff --git a/src/core/webview/__tests__/ClineProvider.spec.ts b/src/core/webview/__tests__/ClineProvider.spec.ts index 801c6c4774..b3be00b655 100644 --- a/src/core/webview/__tests__/ClineProvider.spec.ts +++ b/src/core/webview/__tests__/ClineProvider.spec.ts @@ -521,6 +521,7 @@ describe("ClineProvider", () => { mcpEnabled: true, enableMcpServerCreation: false, requestDelaySeconds: 5, + maxRequestDelaySeconds: 100, mode: defaultModeSlug, customModes: [], experiments: experimentDefault, @@ -800,7 +801,7 @@ describe("ClineProvider", () => { expect(mockPostMessage).toHaveBeenCalled() }) - test("requestDelaySeconds defaults to 10 seconds", async () => { + test("requestDelaySeconds defaults to 5 seconds", async () => { // Mock globalState.get to return undefined for requestDelaySeconds ;(mockContext.globalState.get as any).mockImplementation((key: string) => { if (key === "requestDelaySeconds") { @@ -810,7 +811,20 @@ describe("ClineProvider", () => { }) const state = await provider.getState() - expect(state.requestDelaySeconds).toBe(10) + expect(state.requestDelaySeconds).toBe(5) + }) + + test("maxRequestDelaySeconds defaults to 100 seconds", async () => { + // Mock globalState.get to return undefined for requestDelaySeconds + ;(mockContext.globalState.get as any).mockImplementation((key: string) => { + if (key === "maxRequestDelaySeconds") { + return undefined + } + return null + }) + + const state = await provider.getState() + expect(state.maxRequestDelaySeconds).toBe(100) }) test("alwaysApproveResubmit defaults to false", async () => { @@ -1002,8 +1016,13 @@ describe("ClineProvider", () => { expect(mockPostMessage).toHaveBeenCalled() // Test requestDelaySeconds - await messageHandler({ type: "requestDelaySeconds", value: 10 }) - expect(mockContext.globalState.update).toHaveBeenCalledWith("requestDelaySeconds", 10) + await messageHandler({ type: "requestDelaySeconds", value: 5 }) + expect(mockContext.globalState.update).toHaveBeenCalledWith("requestDelaySeconds", 5) + expect(mockPostMessage).toHaveBeenCalled() + + // Test maxRequestDelaySeconds + await messageHandler({ type: "maxRequestDelaySeconds", value: 100 }) + expect(mockContext.globalState.update).toHaveBeenCalledWith("maxRequestDelaySeconds", 100) expect(mockPostMessage).toHaveBeenCalled() }) diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index cac94aa0ce..996ddf4eb3 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -861,6 +861,10 @@ export const webviewMessageHandler = async ( await updateGlobalState("requestDelaySeconds", message.value ?? 5) await provider.postStateToWebview() break + case "maxRequestDelaySeconds": + await updateGlobalState("maxRequestDelaySeconds", Math.max(1, message.value ?? 100)) + await provider.postStateToWebview() + break case "writeDelayMs": await updateGlobalState("writeDelayMs", message.value) await provider.postStateToWebview() diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 73ebf59d4c..0c9154a8a0 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -229,6 +229,7 @@ export type ExtensionState = Pick< writeDelayMs: number requestDelaySeconds: number + maxRequestDelaySeconds: number enableCheckpoints: boolean maxOpenTabsContext: number // Maximum number of VSCode open tabs to include in context (0-500) diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 7efc97e8c7..b287ae5170 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -116,6 +116,7 @@ export interface WebviewMessage { | "searchCommits" | "alwaysApproveResubmit" | "requestDelaySeconds" + | "maxRequestDelaySeconds" | "setApiConfigPassword" | "mode" | "updatePrompt" diff --git a/webview-ui/src/components/settings/AutoApproveSettings.tsx b/webview-ui/src/components/settings/AutoApproveSettings.tsx index e825ab8d7c..789add1b2a 100644 --- a/webview-ui/src/components/settings/AutoApproveSettings.tsx +++ b/webview-ui/src/components/settings/AutoApproveSettings.tsx @@ -21,6 +21,7 @@ type AutoApproveSettingsProps = HTMLAttributes & { alwaysAllowBrowser?: boolean alwaysApproveResubmit?: boolean requestDelaySeconds: number + maxRequestDelaySeconds: number alwaysAllowMcp?: boolean alwaysAllowModeSwitch?: boolean alwaysAllowSubtasks?: boolean @@ -36,6 +37,7 @@ type AutoApproveSettingsProps = HTMLAttributes & { | "alwaysAllowBrowser" | "alwaysApproveResubmit" | "requestDelaySeconds" + | "maxRequestDelaySeconds" | "alwaysAllowMcp" | "alwaysAllowModeSwitch" | "alwaysAllowSubtasks" @@ -54,6 +56,7 @@ export const AutoApproveSettings = ({ alwaysAllowBrowser, alwaysApproveResubmit, requestDelaySeconds, + maxRequestDelaySeconds, alwaysAllowMcp, alwaysAllowModeSwitch, alwaysAllowSubtasks, @@ -186,17 +189,25 @@ export const AutoApproveSettings = ({
setCachedStateField("requestDelaySeconds", value)} - data-testid="request-delay-slider" + value={[requestDelaySeconds, maxRequestDelaySeconds]} + onValueChange={([min, max]) => { + // Ensure min <= max + const actualMin = Math.min(min, max) + const actualMax = Math.max(min, max) + setCachedStateField("requestDelaySeconds", actualMin) + setCachedStateField("maxRequestDelaySeconds", actualMax) + }} + data-testid="retry-delay-range-slider" /> - {requestDelaySeconds}s + + {requestDelaySeconds}s - {maxRequestDelaySeconds}s +
- {t("settings:autoApprove.retry.delayLabel")} + {t("settings:autoApprove.retry.rangeLabel")}
diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 8712b81cf2..ad502007a5 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -146,6 +146,7 @@ const SettingsView = forwardRef(({ onDone, t maxWorkspaceFiles, mcpEnabled, requestDelaySeconds, + maxRequestDelaySeconds, remoteBrowserHost, screenshotQuality, soundEnabled, @@ -302,6 +303,7 @@ const SettingsView = forwardRef(({ onDone, t vscode.postMessage({ type: "mcpEnabled", bool: mcpEnabled }) vscode.postMessage({ type: "alwaysApproveResubmit", bool: alwaysApproveResubmit }) vscode.postMessage({ type: "requestDelaySeconds", value: requestDelaySeconds }) + vscode.postMessage({ type: "maxRequestDelaySeconds", value: maxRequestDelaySeconds }) vscode.postMessage({ type: "maxOpenTabsContext", value: maxOpenTabsContext }) vscode.postMessage({ type: "maxWorkspaceFiles", value: maxWorkspaceFiles ?? 200 }) vscode.postMessage({ type: "showRooIgnoredFiles", bool: showRooIgnoredFiles }) @@ -595,6 +597,7 @@ const SettingsView = forwardRef(({ onDone, t alwaysAllowBrowser={alwaysAllowBrowser} alwaysApproveResubmit={alwaysApproveResubmit} requestDelaySeconds={requestDelaySeconds} + maxRequestDelaySeconds={maxRequestDelaySeconds} alwaysAllowMcp={alwaysAllowMcp} alwaysAllowModeSwitch={alwaysAllowModeSwitch} alwaysAllowSubtasks={alwaysAllowSubtasks} diff --git a/webview-ui/src/components/ui/slider.tsx b/webview-ui/src/components/ui/slider.tsx index bb127bfb22..c2f05deed5 100644 --- a/webview-ui/src/components/ui/slider.tsx +++ b/webview-ui/src/components/ui/slider.tsx @@ -14,7 +14,16 @@ const Slider = React.forwardRef< - + {Array.isArray(props.value) ? ( + props.value.map((_, i) => ( + + )) + ) : ( + + )} )) Slider.displayName = SliderPrimitive.Root.displayName diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index c87ccdb6e9..8b913c64e5 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -90,7 +90,9 @@ export interface ExtensionStateContextType extends ExtensionState { alwaysApproveResubmit?: boolean setAlwaysApproveResubmit: (value: boolean) => void requestDelaySeconds: number + maxRequestDelaySeconds: number setRequestDelaySeconds: (value: number) => void + setMaxRequestDelaySeconds: (value: number) => void setCurrentApiConfigName: (value: string) => void setListApiConfigMeta: (value: ProviderSettingsEntry[]) => void mode: Mode @@ -173,6 +175,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode enableMcpServerCreation: false, alwaysApproveResubmit: false, requestDelaySeconds: 5, + maxRequestDelaySeconds: 100, currentApiConfigName: "default", listApiConfigMeta: [], mode: defaultModeSlug, @@ -393,6 +396,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode setState((prevState) => ({ ...prevState, enableMcpServerCreation: value })), setAlwaysApproveResubmit: (value) => setState((prevState) => ({ ...prevState, alwaysApproveResubmit: value })), setRequestDelaySeconds: (value) => setState((prevState) => ({ ...prevState, requestDelaySeconds: value })), + setMaxRequestDelaySeconds: (value) => setState((prevState) => ({ ...prevState, maxRequestDelaySeconds: value })), setCurrentApiConfigName: (value) => setState((prevState) => ({ ...prevState, currentApiConfigName: value })), setListApiConfigMeta, setMode: (value: Mode) => setState((prevState) => ({ ...prevState, mode: value })), diff --git a/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx b/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx index 1e5867d3fc..74cdf7b0c9 100644 --- a/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx +++ b/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx @@ -191,6 +191,7 @@ describe("mergeExtensionState", () => { enableCheckpoints: true, writeDelayMs: 1000, requestDelaySeconds: 5, + maxRequestDelaySeconds: 100, mode: "default", experiments: {} as Record, customModes: [], diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index b3906fb50e..5f6b66e57f 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -96,7 +96,7 @@ "retry": { "label": "Reintentar", "description": "Tornar a intentar sol·licituds d'API fallides automàticament quan el servidor retorna una resposta d'error", - "delayLabel": "Retard abans de tornar a intentar la sol·licitud" + "rangeLabel": "Rang de retard de reintent (min - màx segons)" }, "mcp": { "label": "MCP", diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index ad78931d81..20e4e06364 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -96,7 +96,7 @@ "retry": { "label": "Wiederholung", "description": "Fehlgeschlagene API-Anfragen automatisch wiederholen, wenn der Server eine Fehlerantwort zurückgibt", - "delayLabel": "Verzögerung vor dem Wiederholen der Anfrage" + "rangeLabel": "Wiederholungsverzögerungsbereich (min - max Sekunden)" }, "mcp": { "label": "MCP", diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 9083d4a204..41e341f553 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -96,7 +96,7 @@ "retry": { "label": "Retry", "description": "Automatically retry failed API requests when server returns an error response", - "delayLabel": "Delay before retrying the request" + "rangeLabel": "Retry delay range (min - max seconds)" }, "mcp": { "label": "MCP", diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index c83237d4f3..bb8f92655b 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -96,7 +96,7 @@ "retry": { "label": "Reintentar", "description": "Reintentar automáticamente solicitudes de API fallidas cuando el servidor devuelve una respuesta de error", - "delayLabel": "Retraso antes de reintentar la solicitud" + "rangeLabel": "Rango de retraso de reintento (min - máx segundos)" }, "mcp": { "label": "MCP", diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 681d95be98..8185cbcd81 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -96,7 +96,7 @@ "retry": { "label": "Réessayer", "description": "Réessayer automatiquement les requêtes API échouées lorsque le serveur renvoie une réponse d'erreur", - "delayLabel": "Délai avant de réessayer la requête" + "rangeLabel": "Plage de délai de nouvelle tentative (min - max secondes)" }, "mcp": { "label": "MCP", diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index 9d8bef3711..efd7014cdd 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -96,7 +96,7 @@ "retry": { "label": "पुनः प्रयास", "description": "जब सर्वर त्रुटि प्रतिक्रिया देता है तो स्वचालित रूप से विफल API अनुरोधों को पुनः प्रयास करें", - "delayLabel": "अनुरोध को पुनः प्रयास करने से पहले विलंब" + "rangeLabel": "पुनः प्रयास विलंब सीमा (न्यूनतम - अधिकतम सेकंड)" }, "mcp": { "label": "MCP", diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index c5e3abf45c..9948c6bea1 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -96,7 +96,7 @@ "retry": { "label": "Coba Lagi", "description": "Secara otomatis mencoba ulang permintaan API yang gagal ketika server mengembalikan respons error", - "delayLabel": "Delay sebelum mencoba ulang permintaan" + "rangeLabel": "Rentang delay percobaan ulang (min - maks detik)" }, "mcp": { "label": "MCP", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index fefdcd2ca2..d4187cf749 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -96,7 +96,7 @@ "retry": { "label": "Riprova", "description": "Riprova automaticamente le richieste API fallite quando il server restituisce una risposta di errore", - "delayLabel": "Ritardo prima di riprovare la richiesta" + "rangeLabel": "Intervallo di ritardo per i tentativi (min - max secondi)" }, "mcp": { "label": "MCP", diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index f5d198a8d4..bb22717ea2 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -96,7 +96,7 @@ "retry": { "label": "再試行", "description": "サーバーがエラーレスポンスを返した場合、自動的に失敗したAPIリクエストを再試行", - "delayLabel": "リクエスト再試行前の遅延" + "rangeLabel": "再試行遅延範囲(最小 - 最大秒)" }, "mcp": { "label": "MCP", diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 5476b044bf..d3874820b6 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -96,7 +96,7 @@ "retry": { "label": "재시도", "description": "서버가 오류 응답을 반환할 때 자동으로 실패한 API 요청 재시도", - "delayLabel": "요청 재시도 전 지연" + "rangeLabel": "재시도 지연 범위 (최소 - 최대 초)" }, "mcp": { "label": "MCP", diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index 88a66c0d5f..e6f7a0f87e 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -96,7 +96,7 @@ "retry": { "label": "Opnieuw proberen", "description": "Automatisch mislukte API-verzoeken opnieuw proberen wanneer de server een foutmelding geeft", - "delayLabel": "Vertraging voordat het verzoek opnieuw wordt geprobeerd" + "rangeLabel": "Bereik van herhalingvertraging (min - max seconden)" }, "mcp": { "label": "MCP", diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index 4a446f7095..ba0f45d8f5 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -96,7 +96,7 @@ "retry": { "label": "Ponów", "description": "Automatycznie ponawiaj nieudane żądania API, gdy serwer zwraca odpowiedź z błędem", - "delayLabel": "Opóźnienie przed ponowieniem żądania" + "rangeLabel": "Zakres opóźnienia ponowienia (min - maks sekund)" }, "mcp": { "label": "MCP", diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index b4ae11c2ac..6540b74150 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -96,7 +96,7 @@ "retry": { "label": "Tentar novamente", "description": "Tentar novamente automaticamente requisições de API com falha quando o servidor retorna uma resposta de erro", - "delayLabel": "Atraso antes de tentar novamente a requisição" + "rangeLabel": "Faixa de atraso de nova tentativa (mín - máx segundos)" }, "mcp": { "label": "MCP", diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index 79c13741da..9901be95b8 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -96,7 +96,7 @@ "retry": { "label": "Повтор", "description": "Автоматически повторять неудачные запросы к API при ошибке сервера", - "delayLabel": "Задержка перед повтором запроса" + "rangeLabel": "Диапазон задержки повтора (мин - макс секунд)" }, "mcp": { "label": "MCP", diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index e6335c6dad..adb85d1bda 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -96,7 +96,7 @@ "retry": { "label": "Yeniden Dene", "description": "Sunucu bir hata yanıtı döndürdüğünde başarısız API isteklerini otomatik olarak yeniden dene", - "delayLabel": "İsteği yeniden denemeden önce gecikme" + "rangeLabel": "Yeniden deneme gecikme aralığı (min - maks saniye)" }, "mcp": { "label": "MCP", diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index f860292e02..b0fdd7fae5 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -96,7 +96,7 @@ "retry": { "label": "Thử lại", "description": "Tự động thử lại các yêu cầu API thất bại khi máy chủ trả về phản hồi lỗi", - "delayLabel": "Trì hoãn trước khi thử lại yêu cầu" + "rangeLabel": "Phạm vi trì hoãn thử lại (tối thiểu - tối đa giây)" }, "mcp": { "label": "MCP", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index f2e80cee7f..4fe2c510cb 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -96,7 +96,7 @@ "retry": { "label": "重试", "description": "当服务器返回错误响应时自动重试失败的 API 请求", - "delayLabel": "重试请求前的延迟" + "rangeLabel": "重试延迟范围(最小 - 最大秒数)" }, "mcp": { "label": "MCP", diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index 874fbe7a37..cc2dd94bd3 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -96,7 +96,7 @@ "retry": { "label": "重試", "description": "當伺服器回傳錯誤回應時自動重試失敗的 API 請求", - "delayLabel": "重試請求前的延遲" + "rangeLabel": "重試延遲範圍(最小 - 最大秒數)" }, "mcp": { "label": "MCP", From fe4f691304e002a667635a8167995ac12b5da93b Mon Sep 17 00:00:00 2001 From: Alex Bason Date: Thu, 3 Jul 2025 00:08:11 -0400 Subject: [PATCH 2/3] Update max retry delay to 600 since this is what's set by MAX_EXPONENTIAL_BACKOFF_SECONDS --- packages/types/src/global-settings.ts | 2 +- src/core/task/Task.ts | 2 -- src/core/webview/ClineProvider.ts | 4 ++-- src/core/webview/__tests__/ClineProvider.spec.ts | 10 +++++----- src/core/webview/webviewMessageHandler.ts | 2 +- .../src/components/settings/AutoApproveSettings.tsx | 2 +- webview-ui/src/context/ExtensionStateContext.tsx | 2 +- .../context/__tests__/ExtensionStateContext.spec.tsx | 2 +- 8 files changed, 12 insertions(+), 14 deletions(-) diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index cab2204860..f37b7239b7 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -190,7 +190,7 @@ export const EVALS_SETTINGS: RooCodeSettings = { alwaysAllowBrowser: true, alwaysApproveResubmit: true, requestDelaySeconds: 5, - maxRequestDelaySeconds: 100, + maxRequestDelaySeconds: 600, alwaysAllowMcp: true, alwaysAllowModeSwitch: true, alwaysAllowSubtasks: true, diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 9591268ba6..959fd3996d 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -1807,10 +1807,8 @@ export class Task extends EventEmitter { const minDelay = requestDelaySeconds ?? 5 const maxDelay = maxRequestDelaySeconds ?? MAX_EXPONENTIAL_BACKOFF_SECONDS - // Use the minimum delay as the base for exponential backoff let exponentialDelay = Math.ceil(minDelay * Math.pow(2, retryAttempt)) - // Clamp exponential delay to the configured range exponentialDelay = Math.max(minDelay, Math.min(exponentialDelay, maxDelay)) // If the error is a 429, and the error details contain a retry delay, use that delay instead of exponential backoff diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 709341c471..18eaa62931 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1479,7 +1479,7 @@ export class ClineProvider enableMcpServerCreation: enableMcpServerCreation ?? true, alwaysApproveResubmit: alwaysApproveResubmit ?? false, requestDelaySeconds: requestDelaySeconds ?? 5, - maxRequestDelaySeconds: maxRequestDelaySeconds ?? 100, + maxRequestDelaySeconds: maxRequestDelaySeconds ?? 600, currentApiConfigName: currentApiConfigName ?? "default", listApiConfigMeta: listApiConfigMeta ?? [], pinnedApiConfigs: pinnedApiConfigs ?? {}, @@ -1644,7 +1644,7 @@ export class ClineProvider enableMcpServerCreation: stateValues.enableMcpServerCreation ?? true, alwaysApproveResubmit: stateValues.alwaysApproveResubmit ?? false, requestDelaySeconds: Math.max(1, stateValues.requestDelaySeconds ?? 5), - maxRequestDelaySeconds: Math.min(100, stateValues.maxRequestDelaySeconds ?? 100), + maxRequestDelaySeconds: Math.min(600, stateValues.maxRequestDelaySeconds ?? 600), currentApiConfigName: stateValues.currentApiConfigName ?? "default", listApiConfigMeta: stateValues.listApiConfigMeta ?? [], pinnedApiConfigs: stateValues.pinnedApiConfigs ?? {}, diff --git a/src/core/webview/__tests__/ClineProvider.spec.ts b/src/core/webview/__tests__/ClineProvider.spec.ts index b3be00b655..fee21bbabb 100644 --- a/src/core/webview/__tests__/ClineProvider.spec.ts +++ b/src/core/webview/__tests__/ClineProvider.spec.ts @@ -521,7 +521,7 @@ describe("ClineProvider", () => { mcpEnabled: true, enableMcpServerCreation: false, requestDelaySeconds: 5, - maxRequestDelaySeconds: 100, + maxRequestDelaySeconds: 600, mode: defaultModeSlug, customModes: [], experiments: experimentDefault, @@ -814,7 +814,7 @@ describe("ClineProvider", () => { expect(state.requestDelaySeconds).toBe(5) }) - test("maxRequestDelaySeconds defaults to 100 seconds", async () => { + test("maxRequestDelaySeconds defaults to 600 seconds", async () => { // Mock globalState.get to return undefined for requestDelaySeconds ;(mockContext.globalState.get as any).mockImplementation((key: string) => { if (key === "maxRequestDelaySeconds") { @@ -824,7 +824,7 @@ describe("ClineProvider", () => { }) const state = await provider.getState() - expect(state.maxRequestDelaySeconds).toBe(100) + expect(state.maxRequestDelaySeconds).toBe(600) }) test("alwaysApproveResubmit defaults to false", async () => { @@ -1021,8 +1021,8 @@ describe("ClineProvider", () => { expect(mockPostMessage).toHaveBeenCalled() // Test maxRequestDelaySeconds - await messageHandler({ type: "maxRequestDelaySeconds", value: 100 }) - expect(mockContext.globalState.update).toHaveBeenCalledWith("maxRequestDelaySeconds", 100) + await messageHandler({ type: "maxRequestDelaySeconds", value: 600 }) + expect(mockContext.globalState.update).toHaveBeenCalledWith("maxRequestDelaySeconds", 600) expect(mockPostMessage).toHaveBeenCalled() }) diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 93b81edef6..4e8f13a60e 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -863,7 +863,7 @@ export const webviewMessageHandler = async ( await provider.postStateToWebview() break case "maxRequestDelaySeconds": - await updateGlobalState("maxRequestDelaySeconds", Math.max(1, message.value ?? 100)) + await updateGlobalState("maxRequestDelaySeconds", Math.max(1, message.value ?? 600)) await provider.postStateToWebview() break case "writeDelayMs": diff --git a/webview-ui/src/components/settings/AutoApproveSettings.tsx b/webview-ui/src/components/settings/AutoApproveSettings.tsx index f449e9bbf3..a4063d05d8 100644 --- a/webview-ui/src/components/settings/AutoApproveSettings.tsx +++ b/webview-ui/src/components/settings/AutoApproveSettings.tsx @@ -197,7 +197,7 @@ export const AutoApproveSettings = ({
{ diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index 3adbed9d85..71c96eb482 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -179,7 +179,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode enableMcpServerCreation: false, alwaysApproveResubmit: false, requestDelaySeconds: 5, - maxRequestDelaySeconds: 100, + maxRequestDelaySeconds: 600, currentApiConfigName: "default", listApiConfigMeta: [], mode: defaultModeSlug, diff --git a/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx b/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx index 74cdf7b0c9..71f0a5981a 100644 --- a/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx +++ b/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx @@ -191,7 +191,7 @@ describe("mergeExtensionState", () => { enableCheckpoints: true, writeDelayMs: 1000, requestDelaySeconds: 5, - maxRequestDelaySeconds: 100, + maxRequestDelaySeconds: 600, mode: "default", experiments: {} as Record, customModes: [], From 42161a7b770bd7d732280255a66374806287be63 Mon Sep 17 00:00:00 2001 From: Alex Bason Date: Thu, 3 Jul 2025 07:02:32 -0400 Subject: [PATCH 3/3] Update src/core/webview/__tests__/ClineProvider.spec.ts Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> --- src/core/webview/__tests__/ClineProvider.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/webview/__tests__/ClineProvider.spec.ts b/src/core/webview/__tests__/ClineProvider.spec.ts index fee21bbabb..921f32b282 100644 --- a/src/core/webview/__tests__/ClineProvider.spec.ts +++ b/src/core/webview/__tests__/ClineProvider.spec.ts @@ -815,7 +815,7 @@ describe("ClineProvider", () => { }) test("maxRequestDelaySeconds defaults to 600 seconds", async () => { - // Mock globalState.get to return undefined for requestDelaySeconds + // Mock globalState.get to return undefined for maxRequestDelaySeconds ;(mockContext.globalState.get as any).mockImplementation((key: string) => { if (key === "maxRequestDelaySeconds") { return undefined