diff --git a/.changeset/many-results-read.md b/.changeset/many-results-read.md new file mode 100644 index 00000000000..ab2194808a6 --- /dev/null +++ b/.changeset/many-results-read.md @@ -0,0 +1,5 @@ +--- +"kilo-code": minor +--- + +feat(mode): implement Ralph mode for infinite task loops diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index c19880ec70b..9ba59ae3427 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -245,6 +245,10 @@ export const globalSettingsSchema = z.object({ lastModeExportPath: z.string().optional(), lastModeImportPath: z.string().optional(), appendSystemPrompt: z.string().optional(), // kilocode_change: Custom text to append to system prompt (CLI only) + alwaysAllowRalph: z.boolean().optional(), // kilocode_change + ralphEnabled: z.boolean().optional(), // kilocode_change + ralphLoopLimit: z.number().int().min(0).optional(), // kilocode_change + ralphCompletionDelimiter: z.string().optional(), // kilocode_change }) export type GlobalSettings = z.infer @@ -370,6 +374,9 @@ export const EVALS_SETTINGS: RooCodeSettings = { alwaysAllowModeSwitch: true, alwaysAllowSubtasks: true, alwaysAllowExecute: true, + alwaysAllowRalph: false, // kilocode_change + ralphLoopLimit: 5, // kilocode_change + ralphCompletionDelimiter: "COMPLETED", // kilocode_change alwaysAllowFollowupQuestions: true, followupAutoApproveTimeoutMs: 0, allowedCommands: ["*"], diff --git a/packages/types/src/task.ts b/packages/types/src/task.ts index 327e2a32866..e0bb48e21c8 100644 --- a/packages/types/src/task.ts +++ b/packages/types/src/task.ts @@ -97,6 +97,7 @@ export interface CreateTaskOptions { initialTodos?: TodoItem[] /** Initial status for the task's history item (e.g., "active" for child tasks) */ initialStatus?: "active" | "delegated" | "completed" + ralphLoopCount?: number // kilocode_change } export enum TaskStatus { @@ -110,6 +111,7 @@ export enum TaskStatus { export const taskMetadataSchema = z.object({ task: z.string().optional(), images: z.array(z.string()).optional(), + ralphLoopCount: z.number().int().min(0).optional(), // kilocode_change }) export type TaskMetadata = z.infer diff --git a/packages/types/src/vscode-extension-host.ts b/packages/types/src/vscode-extension-host.ts index 47ed8753402..1a0be756f04 100644 --- a/packages/types/src/vscode-extension-host.ts +++ b/packages/types/src/vscode-extension-host.ts @@ -494,6 +494,7 @@ export type ExtensionState = Pick< | "alwaysAllowSubtasks" | "alwaysAllowFollowupQuestions" | "alwaysAllowExecute" + | "alwaysAllowRalph" // kilocode_change | "followupAutoApproveTimeoutMs" | "allowedCommands" | "deniedCommands" @@ -569,6 +570,9 @@ export type ExtensionState = Pick< | "maxGitStatusFiles" | "requestDelaySeconds" | "selectedMicrophoneDevice" // kilocode_change: Selected microphone device for STT + | "ralphEnabled" // kilocode_change + | "ralphLoopLimit" // kilocode_change + | "ralphCompletionDelimiter" // kilocode_change > & { version: string clineMessages: ClineMessage[] @@ -958,6 +962,9 @@ export interface WebviewMessage { | "requestClaudeCodeRateLimits" | "requestOpenAiCodexRateLimits" | "refreshCustomTools" + | "ralphEnabled" // kilocode_change + | "ralphLoopLimit" // kilocode_change + | "ralphCompletionDelimiter" // kilocode_change | "requestModes" | "switchMode" | "debugSetting" diff --git a/src/core/assistant-message/presentAssistantMessage.ts b/src/core/assistant-message/presentAssistantMessage.ts index d6b12795a1e..3ef502a454e 100644 --- a/src/core/assistant-message/presentAssistantMessage.ts +++ b/src/core/assistant-message/presentAssistantMessage.ts @@ -891,11 +891,16 @@ export async function presentAssistantMessage(cline: Task) { // If execution is not allowed, notify user and break. if (!repetitionCheck.allowExecution && repetitionCheck.askUser) { // Handle repetition similar to mistake_limit_reached pattern. - const { response, text, images } = await cline.ask( - repetitionCheck.askUser.messageKey as ClineAsk, + const result = await cline.handleMistakeLimitReached( repetitionCheck.askUser.messageDetail.replace("{toolName}", block.name), ) + if (!result) { + return + } + + const { response, text, images } = result + if (response === "messageResponse") { // Add user feedback to userContent. cline.userMessageContent.push( diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 80ad4165f72..244e020b91b 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -165,6 +165,7 @@ const MAX_CHUTES_TERMINATED_RETRY_ATTEMPTS = 2 // Allow up to 2 retries (3 total export interface TaskOptions extends CreateTaskOptions { context: vscode.ExtensionContext // kilocode_change provider: ClineProvider + ralphLoopCount?: number // kilocode_change apiConfiguration: ProviderSettings enableDiff?: boolean enableCheckpoints?: boolean @@ -493,6 +494,7 @@ export class Task extends EventEmitter implements TaskLike { initialTodos, workspacePath, initialStatus, + ralphLoopCount, // kilocode_change }: TaskOptions) { super() this.context = context // kilocode_change @@ -524,6 +526,7 @@ export class Task extends EventEmitter implements TaskLike { this.metadata = { task: historyItem ? historyItem.task : task, images: historyItem ? [] : images, + ralphLoopCount: ralphLoopCount ?? 0, // kilocode_change } // Normal use-case is usually retry similar history task with new workspace. @@ -2656,6 +2659,82 @@ export class Task extends EventEmitter implements TaskLike { } } + // kilocode_change start + /** + * Handles the Ralph mode restart logic. + * If Ralph mode is enabled and within limits, restarts the task. + * + * @param result Optional completion result to check for delimiter + * @returns true if task was restarted, false otherwise + */ + public async handleRalphRestart(result?: string): Promise { + const provider = this.providerRef.deref() + const state = await provider?.getState() + if (provider && state?.alwaysAllowRalph && state?.ralphEnabled) { + const delimiter = state.ralphCompletionDelimiter + + // Check delimiter if configured and result is provided + if (result !== undefined && delimiter && delimiter.trim() !== "") { + if (result.indexOf(delimiter) !== -1) { + return false + } + + const assistantMessages = this.apiConversationHistory.filter((m) => m.role === "assistant").slice(-5) + + for (const m of assistantMessages) { + const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content) + if (content.indexOf(delimiter) !== -1) { + return false + } + } + } + + const loopLimit = state.ralphLoopLimit ?? 5 + const currentLoopCount = this.metadata.ralphLoopCount ?? 0 + + if (loopLimit <= 0 || currentLoopCount + 1 < loopLimit) { + const firstPrompt = this.metadata.task + const images = this.metadata.images + + setTimeout(async () => { + try { + await provider.createTask(firstPrompt, images, undefined, { + ralphLoopCount: currentLoopCount + 1, + }) + await provider.postMessageToWebview({ type: "invoke", invoke: "newChat" }) + } catch (error) { + console.error("[Task] Failed to restart task in Ralph mode:", error) + } + }, 1000) + + await this.abortTask() + return true + } + } + return false + } + + /** + * Handles the case where the mistake limit has been reached. + * If Ralph mode is enabled and within limits, restarts the task. + * Otherwise, asks the user for guidance. + * + * @param guidance Optional guidance text to show the user + * @returns Object containing the user's response if Ralph restart didn't happen + */ + public async handleMistakeLimitReached( + guidance: string = t("common:errors.mistake_limit_guidance"), + ): Promise<{ response: ClineAskResponse; text?: string; images?: string[] } | undefined> { + const restarted = await this.handleRalphRestart() + if (restarted) { + await this.say("error", "Mistake limit reached. Ralph mode is restarting the task...") + return undefined + } + + return await this.ask("mistake_limit_reached", guidance) + } + // kilocode_change end + public async recursivelyMakeClineRequests( userContent: Anthropic.Messages.ContentBlockParam[], includeFileDetails: boolean = false, @@ -2697,10 +2776,13 @@ export class Task extends EventEmitter implements TaskLike { ), ) - const { response, text, images } = await this.ask( - "mistake_limit_reached", - t("common:errors.mistake_limit_guidance"), - ) + const result = await this.handleMistakeLimitReached() + + if (!result) { + return true + } + + const { response, text, images } = result if (response === "messageResponse") { currentUserContent.push( diff --git a/src/core/tools/AttemptCompletionTool.ts b/src/core/tools/AttemptCompletionTool.ts index 0b13d8f876e..08f030fb323 100644 --- a/src/core/tools/AttemptCompletionTool.ts +++ b/src/core/tools/AttemptCompletionTool.ts @@ -170,6 +170,13 @@ export class AttemptCompletionTool extends BaseTool<"attempt_completion"> { } } + // kilocode_change start + const restarted = await task.handleRalphRestart(result) + if (restarted) { + return + } + // kilocode_change end + const { response, text, images } = await task.ask("completion_result", "", false) if (response === "yesButtonClicked") { diff --git a/src/core/tools/__tests__/AttemptCompletionToolRalph.spec.ts b/src/core/tools/__tests__/AttemptCompletionToolRalph.spec.ts new file mode 100644 index 00000000000..8fa3470acc3 --- /dev/null +++ b/src/core/tools/__tests__/AttemptCompletionToolRalph.spec.ts @@ -0,0 +1,219 @@ +import { AttemptCompletionToolUse } from "../../../shared/tools" +import { attemptCompletionTool, AttemptCompletionCallbacks } from "../AttemptCompletionTool" +import { Task } from "../../task/Task" + +// Mock the formatResponse module +vi.mock("../../prompts/responses", () => ({ + formatResponse: { + toolError: vi.fn((msg: string) => `Error: ${msg}`), + toolResult: vi.fn((text: string) => text), + }, +})) + +// Mock vscode module +vi.mock("vscode", () => ({ + workspace: { + getConfiguration: vi.fn(() => ({ + get: vi.fn((key: string, defaultValue: any) => defaultValue), + })), + }, + window: { + showErrorMessage: vi.fn(), + createTextEditorDecorationType: vi.fn(() => ({ dispose: vi.fn() })), + }, +})) + +// Mock Package module +vi.mock("../../../shared/package", () => ({ + Package: { + name: "kilo-code", + }, +})) + +describe("AttemptCompletionTool Ralph Mode", () => { + let mockTask: any + let mockProvider: any + let mockCallbacks: AttemptCompletionCallbacks + + beforeEach(() => { + mockProvider = { + getState: vi.fn(), + createTask: vi.fn(), + postMessageToWebview: vi.fn(), + } + + mockTask = { + taskId: "test-task-id", + consecutiveMistakeCount: 0, + recordToolError: vi.fn(), + todoList: undefined, + didToolFailInCurrentTurn: false, + providerRef: { + deref: () => mockProvider, + }, + metadata: { + task: "initial prompt", + images: [], + ralphLoopCount: 0, + }, + apiConversationHistory: [], + clineMessages: [], + say: vi.fn(), + emit: vi.fn(), + getTokenUsage: vi.fn(() => ({})), + emitFinalTokenUsageUpdate: vi.fn(), + ask: vi.fn(() => Promise.resolve({ response: "yesButtonClicked" })), + } + + mockCallbacks = { + askApproval: vi.fn(), + handleError: vi.fn(), + pushToolResult: vi.fn(), + removeClosingTag: vi.fn(), + askFinishSubTaskApproval: vi.fn(), + toolDescription: vi.fn(), + toolProtocol: "xml", + } + + vi.useFakeTimers() + }) + + afterEach(() => { + vi.useRealTimers() + }) + + it("should restart task in Ralph mode when enabled", async () => { + mockProvider.getState.mockResolvedValue({ ralphEnabled: true }) + + const block: AttemptCompletionToolUse = { + type: "tool_use", + name: "attempt_completion", + params: { result: "Task completed" }, + partial: false, + } + + await attemptCompletionTool.handle(mockTask as Task, block, mockCallbacks) + + // Fast-forward 1 second for the setTimeout + vi.advanceTimersByTime(1000) + + expect(mockProvider.createTask).toHaveBeenCalledWith( + "initial prompt", + [], + undefined, + expect.objectContaining({ ralphLoopCount: 1 }), + ) + expect(mockProvider.postMessageToWebview).toHaveBeenCalledWith({ type: "invoke", invoke: "newChat" }) + }) + + it("should stop Ralph mode when loop limit is reached", async () => { + mockProvider.getState.mockResolvedValue({ + ralphEnabled: true, + ralphLoopLimit: 2, + }) + mockTask.metadata.ralphLoopCount = 1 // Already ran once, this is the second time + + const block: AttemptCompletionToolUse = { + type: "tool_use", + name: "attempt_completion", + params: { result: "Task completed" }, + partial: false, + } + + await attemptCompletionTool.handle(mockTask as Task, block, mockCallbacks) + + vi.advanceTimersByTime(1000) + + // Should NOT restart the task because loop limit (2) is reached + expect(mockProvider.createTask).not.toHaveBeenCalled() + // Should instead call task.ask to show completion result to user + expect(mockTask.ask).toHaveBeenCalledWith("completion_result", "", false) + }) + + it("should stop Ralph mode when custom delimiter is found in result", async () => { + mockProvider.getState.mockResolvedValue({ + ralphEnabled: true, + ralphCompletionDelimiter: "COMPLETED", + }) + + const block: AttemptCompletionToolUse = { + type: "tool_use", + name: "attempt_completion", + params: { result: "Here is the result COMPLETED" }, + partial: false, + } + + await attemptCompletionTool.handle(mockTask as Task, block, mockCallbacks) + + vi.advanceTimersByTime(1000) + + // Should NOT restart the task because delimiter is found + expect(mockProvider.createTask).not.toHaveBeenCalled() + expect(mockTask.ask).toHaveBeenCalled() + }) + + it("should stop Ralph mode when custom delimiter is found in previous messages", async () => { + mockProvider.getState.mockResolvedValue({ + ralphEnabled: true, + ralphCompletionDelimiter: "STOP_NOW", + }) + + // Delimiter is in a previous message (e.g., a file write) + mockTask.apiConversationHistory = [ + { + role: "assistant", + content: [ + { + type: "tool_use", + name: "write_to_file", + id: "1", + input: { content: "Some content with STOP_NOW delimiter" }, + }, + ], + }, + ] + + const block: AttemptCompletionToolUse = { + type: "tool_use", + name: "attempt_completion", + params: { result: "Finalizing task" }, + partial: false, + } + + await attemptCompletionTool.handle(mockTask as Task, block, mockCallbacks) + + vi.advanceTimersByTime(1000) + + // Should NOT restart the task because delimiter is found in history + expect(mockProvider.createTask).not.toHaveBeenCalled() + expect(mockTask.ask).toHaveBeenCalled() + }) + + it("should continue Ralph mode when custom delimiter is NOT found anywhere", async () => { + mockProvider.getState.mockResolvedValue({ + ralphEnabled: true, + ralphCompletionDelimiter: "COMPLETED", + }) + + mockTask.apiConversationHistory = [ + { + role: "assistant", + content: "Just a regular message", + }, + ] + + const block: AttemptCompletionToolUse = { + type: "tool_use", + name: "attempt_completion", + params: { result: "Task not yet fully done" }, + partial: false, + } + + await attemptCompletionTool.handle(mockTask as Task, block, mockCallbacks) + + vi.advanceTimersByTime(1000) + + // Should restart the task + expect(mockProvider.createTask).toHaveBeenCalled() + }) +}) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 5fe344ef1d9..f4d167c8745 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -2203,6 +2203,7 @@ export class ClineProvider alwaysAllowWriteOutsideWorkspace, alwaysAllowWriteProtected, alwaysAllowDelete, // kilocode_change + alwaysAllowRalph, // kilocode_change alwaysAllowExecute, allowedCommands, deniedCommands, @@ -2311,6 +2312,9 @@ export class ClineProvider yoloGatekeeperApiConfigId, // kilocode_change: AI gatekeeper for YOLO mode selectedMicrophoneDevice, // kilocode_change: Selected microphone device for STT isBrowserSessionActive, + ralphEnabled, // kilocode_change + ralphLoopLimit, // kilocode_change + ralphCompletionDelimiter, // kilocode_change } = await this.getState() // kilocode_change start: Get active model for virtual quota fallback UI display @@ -2375,6 +2379,7 @@ export class ClineProvider alwaysAllowWriteOutsideWorkspace: alwaysAllowWriteOutsideWorkspace ?? false, alwaysAllowWriteProtected: alwaysAllowWriteProtected ?? false, alwaysAllowDelete: alwaysAllowDelete ?? false, // kilocode_change + alwaysAllowRalph: alwaysAllowRalph ?? false, // kilocode_change alwaysAllowExecute: alwaysAllowExecute ?? false, alwaysAllowBrowser: alwaysAllowBrowser ?? false, alwaysAllowMcp: alwaysAllowMcp ?? false, @@ -2566,6 +2571,9 @@ export class ClineProvider } })(), debug: vscode.workspace.getConfiguration(Package.name).get("debug", false), + ralphEnabled: ralphEnabled ?? false, // kilocode_change + ralphLoopLimit: ralphLoopLimit ?? 5, // kilocode_change + ralphCompletionDelimiter: ralphCompletionDelimiter !== undefined ? ralphCompletionDelimiter : "COMPLETED", // kilocode_change } } @@ -2696,6 +2704,7 @@ export class ClineProvider alwaysAllowWriteOutsideWorkspace: stateValues.alwaysAllowWriteOutsideWorkspace ?? false, alwaysAllowWriteProtected: stateValues.alwaysAllowWriteProtected ?? false, alwaysAllowDelete: stateValues.alwaysAllowDelete ?? false, // kilocode_change + alwaysAllowRalph: stateValues.alwaysAllowRalph ?? false, // kilocode_change alwaysAllowExecute: stateValues.alwaysAllowExecute ?? true, alwaysAllowBrowser: stateValues.alwaysAllowBrowser ?? true, alwaysAllowMcp: stateValues.alwaysAllowMcp ?? true, @@ -2869,6 +2878,9 @@ export class ClineProvider } })(), appendSystemPrompt: stateValues.appendSystemPrompt, // kilocode_change: CLI append system prompt + ralphEnabled: stateValues.ralphEnabled ?? false, // kilocode_change + ralphLoopLimit: stateValues.ralphLoopLimit ?? 5, // kilocode_change + ralphCompletionDelimiter: stateValues.ralphCompletionDelimiter !== undefined ? stateValues.ralphCompletionDelimiter : "COMPLETED", // kilocode_change } } diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 303877fdcec..ea39da3b4b5 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -2007,6 +2007,20 @@ export const webviewMessageHandler = async ( await provider.postStateToWebview() break // kilocode_change end + // kilocode_change start + case "ralphEnabled": + await updateGlobalState("ralphEnabled", message.bool ?? false) + await provider.postStateToWebview() + break + case "ralphLoopLimit": + await updateGlobalState("ralphLoopLimit", message.value ?? 5) + await provider.postStateToWebview() + break + case "ralphCompletionDelimiter": + await updateGlobalState("ralphCompletionDelimiter", message.text !== undefined ? message.text : "COMPLETED") + await provider.postStateToWebview() + break + // kilocode_change end case "enhancePrompt": if (message.text) { try { diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index 455cc8c6d0c..c63f5c97ba9 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -31,7 +31,7 @@ import { KiloProfileSelector } from "../kilocode/chat/KiloProfileSelector" import { MAX_IMAGES_PER_MESSAGE } from "./ChatView" import ContextMenu from "./ContextMenu" import { ImageWarningBanner } from "./ImageWarningBanner" -import { VolumeX, Pin, Check, WandSparkles, SendHorizontal, Paperclip, MessageSquareX } from "lucide-react" +import { VolumeX, Pin, Check, WandSparkles, SendHorizontal, Paperclip, MessageSquareX, Infinity } from "lucide-react" import { IndexingStatusBadge } from "./IndexingStatusBadge" import { MicrophoneButton } from "./MicrophoneButton" // kilocode_change: STT microphone button import { VolumeVisualizer } from "./VolumeVisualizer" // kilocode_change: STT volume level visual @@ -164,6 +164,9 @@ export const ChatTextArea = forwardRef( ghostServiceSettings, // kilocode_change language, // User's VSCode display language experiments, // kilocode_change: For speechToText experiment flag + ralphEnabled, // kilocode_change + setRalphEnabled, // kilocode_change + alwaysAllowRalph, // kilocode_change } = useExtensionState() // kilocode_change start: Manage STT status and error state with auto-clearing @@ -1965,6 +1968,35 @@ export const ChatTextArea = forwardRef( {/* kilocode_change end */} + {/* kilocode_change start */} + {alwaysAllowRalph && ( + + + + )} + {/* kilocode_change end */} + & { alwaysAllowModeSwitch?: boolean alwaysAllowSubtasks?: boolean alwaysAllowExecute?: boolean + alwaysAllowRalph?: boolean // kilocode_change + ralphLoopLimit?: number // kilocode_change + ralphCompletionDelimiter?: string // kilocode_change alwaysAllowFollowupQuestions?: boolean followupAutoApproveTimeoutMs?: number allowedCommands?: string[] @@ -51,6 +54,9 @@ type AutoApproveSettingsProps = HTMLAttributes & { | "alwaysAllowModeSwitch" | "alwaysAllowSubtasks" | "alwaysAllowExecute" + | "alwaysAllowRalph" // kilocode_change + | "ralphLoopLimit" // kilocode_change + | "ralphCompletionDelimiter" // kilocode_change | "alwaysAllowFollowupQuestions" | "followupAutoApproveTimeoutMs" | "allowedCommands" @@ -75,6 +81,9 @@ export const AutoApproveSettings = ({ alwaysAllowModeSwitch, alwaysAllowSubtasks, alwaysAllowExecute, + alwaysAllowRalph, // kilocode_change + ralphLoopLimit, // kilocode_change + ralphCompletionDelimiter, // kilocode_change alwaysAllowFollowupQuestions, followupAutoApproveTimeoutMs = 60000, allowedCommands, @@ -200,6 +209,7 @@ export const AutoApproveSettings = ({ alwaysAllowModeSwitch={alwaysAllowModeSwitch} alwaysAllowSubtasks={alwaysAllowSubtasks} alwaysAllowExecute={alwaysAllowExecute} + alwaysAllowRalph={alwaysAllowRalph} // kilocode_change alwaysAllowFollowupQuestions={alwaysAllowFollowupQuestions} onToggle={(key, value) => setCachedStateField(key, value)} /> @@ -435,6 +445,53 @@ export const AutoApproveSettings = ({ )} + + {/* kilocode_change start */} + {alwaysAllowRalph && ( +
+
+ +
{t("settings:autoApprove.ralph.label")}
+
+
+
+ +
+ setCachedStateField("ralphLoopLimit", value)} + /> + + {ralphLoopLimit === 0 ? "∞" : ralphLoopLimit} + +
+
+ {t("settings:autoApprove.ralph.loopLimit.description")} +
+
+
+ + setCachedStateField("ralphCompletionDelimiter", e.target.value)} + placeholder="e.g., COMPLETED" + className="w-full" + /> +
+ {t("settings:autoApprove.ralph.completionDelimiter.description")} +
+
+
+
+ )} + {/* kilocode_change end */} {/* kilocode_change start */} diff --git a/webview-ui/src/components/settings/AutoApproveToggle.tsx b/webview-ui/src/components/settings/AutoApproveToggle.tsx index 6bdc12bc715..5b3cc05e45b 100644 --- a/webview-ui/src/components/settings/AutoApproveToggle.tsx +++ b/webview-ui/src/components/settings/AutoApproveToggle.tsx @@ -2,6 +2,7 @@ import type { GlobalSettings } from "@roo-code/types" import { useAppTranslation } from "@/i18n/TranslationContext" import { cn } from "@/lib/utils" +import { Infinity } from "lucide-react" // kilocode_change import { Button, StandardTooltip } from "@/components/ui" type AutoApproveToggles = Pick< @@ -14,6 +15,7 @@ type AutoApproveToggles = Pick< | "alwaysAllowModeSwitch" | "alwaysAllowSubtasks" | "alwaysAllowExecute" + | "alwaysAllowRalph" // kilocode_change | "alwaysAllowFollowupQuestions" > @@ -93,6 +95,15 @@ export const autoApproveSettingsConfig: Record - + {icon === "infinity" ? ( + + ) : ( + + )} {t(labelKey)} diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index a67bfa40655..4155b295de2 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -178,6 +178,9 @@ const SettingsView = forwardRef((props, ref) language, alwaysAllowBrowser, alwaysAllowExecute, + alwaysAllowRalph, // kilocode_change + ralphLoopLimit, // kilocode_change + ralphCompletionDelimiter, // kilocode_change alwaysAllowMcp, alwaysAllowModeSwitch, alwaysAllowSubtasks, @@ -541,6 +544,9 @@ const SettingsView = forwardRef((props, ref) alwaysAllowWriteProtected: alwaysAllowWriteProtected ?? undefined, alwaysAllowDelete: alwaysAllowDelete ?? undefined, // kilocode_change alwaysAllowExecute: alwaysAllowExecute ?? undefined, + alwaysAllowRalph: alwaysAllowRalph ?? undefined, // kilocode_change + ralphLoopLimit: ralphLoopLimit ?? 5, // kilocode_change + ralphCompletionDelimiter: ralphCompletionDelimiter !== undefined ? ralphCompletionDelimiter : "COMPLETED", // kilocode_change alwaysAllowBrowser: alwaysAllowBrowser ?? undefined, alwaysAllowMcp, alwaysAllowModeSwitch, @@ -1123,6 +1129,9 @@ const SettingsView = forwardRef((props, ref) alwaysAllowDelete={alwaysAllowDelete} // kilocode_change alwaysAllowBrowser={alwaysAllowBrowser} alwaysAllowMcp={alwaysAllowMcp} + alwaysAllowRalph={alwaysAllowRalph} // kilocode_change + ralphLoopLimit={ralphLoopLimit} // kilocode_change + ralphCompletionDelimiter={ralphCompletionDelimiter} // kilocode_change alwaysAllowModeSwitch={alwaysAllowModeSwitch} alwaysAllowSubtasks={alwaysAllowSubtasks} alwaysAllowExecute={alwaysAllowExecute} diff --git a/webview-ui/src/components/settings/__tests__/AutoApproveToggle.spec.tsx b/webview-ui/src/components/settings/__tests__/AutoApproveToggle.spec.tsx index bd423e7bf56..2b2d6cd0d20 100644 --- a/webview-ui/src/components/settings/__tests__/AutoApproveToggle.spec.tsx +++ b/webview-ui/src/components/settings/__tests__/AutoApproveToggle.spec.tsx @@ -20,6 +20,7 @@ describe("AutoApproveToggle", () => { alwaysAllowReadOnly: true, alwaysAllowWrite: false, alwaysAllowDelete: false, // kilocode_change + alwaysAllowRalph: false, // kilocode_change alwaysAllowBrowser: false, alwaysAllowMcp: false, alwaysAllowModeSwitch: true, diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index 1c80c594270..74feb0dc91b 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -53,6 +53,8 @@ export interface ExtensionStateContextType extends ExtensionState { dismissedNotificationIds: string[] // kilocode_change yoloMode?: boolean // kilocode_change setYoloMode: (value: boolean) => void // kilocode_Change + ralphEnabled?: boolean // kilocode_change + setRalphEnabled: (value: boolean) => void // kilocode_change // kilocode_change start - Auto-purge settings autoPurgeEnabled?: boolean setAutoPurgeEnabled: (value: boolean) => void @@ -120,6 +122,8 @@ export interface ExtensionStateContextType extends ExtensionState { setAlwaysAllowWriteOutsideWorkspace: (value: boolean) => void setAlwaysAllowDelete: (value: boolean) => void // kilocode_change setAlwaysAllowExecute: (value: boolean) => void + alwaysAllowRalph?: boolean // kilocode_change + setAlwaysAllowRalph: (value: boolean) => void // kilocode_change setAlwaysAllowBrowser: (value: boolean) => void setAlwaysAllowMcp: (value: boolean) => void setAlwaysAllowModeSwitch: (value: boolean) => void @@ -295,6 +299,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode customCondensingPrompt: "", // Default empty string for custom condensing prompt yoloGatekeeperApiConfigId: "", // kilocode_change: Default empty string for gatekeeper API config ID hasOpenedModeSelector: false, // Default to false (not opened yet) + ralphEnabled: false, // kilocode_change hasCompletedOnboarding: undefined, // kilocode_change: Leave unset until extension sends value autoApprovalEnabled: true, customModes: [], @@ -605,6 +610,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode setState((prevState) => ({ ...prevState, alwaysAllowWriteOutsideWorkspace: value })), setAlwaysAllowDelete: (value) => setState((prevState) => ({ ...prevState, alwaysAllowDelete: value })), // kilocode_change setAlwaysAllowExecute: (value) => setState((prevState) => ({ ...prevState, alwaysAllowExecute: value })), + setAlwaysAllowRalph: (value) => setState((prevState) => ({ ...prevState, alwaysAllowRalph: value })), // kilocode_change setAlwaysAllowBrowser: (value) => setState((prevState) => ({ ...prevState, alwaysAllowBrowser: value })), setAlwaysAllowMcp: (value) => setState((prevState) => ({ ...prevState, alwaysAllowMcp: value })), setAlwaysAllowModeSwitch: (value) => setState((prevState) => ({ ...prevState, alwaysAllowModeSwitch: value })), @@ -673,6 +679,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode setShowTimestamps: (value) => setState((prevState) => ({ ...prevState, showTimestamps: value })), setShowDiffStats: (value) => setState((prevState) => ({ ...prevState, showDiffStats: value })), // kilocode_change setYoloMode: (value) => setState((prevState) => ({ ...prevState, yoloMode: value })), // kilocode_change + setRalphEnabled: (value) => setState((prevState) => ({ ...prevState, ralphEnabled: value })), // kilocode_change // kilocode_change end setAutoApprovalEnabled: (value) => setState((prevState) => ({ ...prevState, autoApprovalEnabled: value })), setCustomModes: (value) => setState((prevState) => ({ ...prevState, customModes: value })), diff --git a/webview-ui/src/hooks/useAutoApprovalToggles.ts b/webview-ui/src/hooks/useAutoApprovalToggles.ts index d52ad373b69..ab908c169c8 100644 --- a/webview-ui/src/hooks/useAutoApprovalToggles.ts +++ b/webview-ui/src/hooks/useAutoApprovalToggles.ts @@ -15,6 +15,7 @@ export function useAutoApprovalToggles() { alwaysAllowMcp, alwaysAllowModeSwitch, alwaysAllowSubtasks, + alwaysAllowRalph, // kilocode_change alwaysAllowFollowupQuestions, } = useExtensionState() @@ -28,6 +29,7 @@ export function useAutoApprovalToggles() { alwaysAllowMcp, alwaysAllowModeSwitch, alwaysAllowSubtasks, + alwaysAllowRalph, // kilocode_change alwaysAllowFollowupQuestions, }), [ @@ -39,6 +41,7 @@ export function useAutoApprovalToggles() { alwaysAllowMcp, alwaysAllowModeSwitch, alwaysAllowSubtasks, + alwaysAllowRalph, // kilocode_change alwaysAllowFollowupQuestions, ], ) diff --git a/webview-ui/src/i18n/locales/ar/settings.json b/webview-ui/src/i18n/locales/ar/settings.json index 770b857f4ca..12cc23b7ee3 100644 --- a/webview-ui/src/i18n/locales/ar/settings.json +++ b/webview-ui/src/i18n/locales/ar/settings.json @@ -278,6 +278,18 @@ "description": "اختيار الإجابة الأولى المقترحة تلقائيًا بعد المهلة المحددة", "timeoutLabel": "وقت الانتظار قبل الاختيار التلقائي للإجابة الأولى" }, + "ralph": { + "label": "Ralph", + "description": "إظهار زر Ralph في وضع الدردشة الذي يمكّن قدرة حلقة الوكيل المستقل", + "loopLimit": { + "label": "حد الحلقة", + "description": "الحد الأقصى لعدد المرات التي سيقوم فيها Ralph بإعادة تشغيل المهمة تلقائيًا. اضبط على 0 للحلقات غير المحدودة." + }, + "completionDelimiter": { + "label": "محدد الاكتمال", + "description": "إذا تم العثور على هذا النص في نتيجة الاكتمال، سيتوقف Ralph عن التكرار." + } + }, "execute": { "label": "تشغيل", "description": "تنفيذ أوامر الطرفية المسموحة تلقائيًا", diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index ce6630cc6e4..283a84b1282 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -239,6 +239,18 @@ "description": "Seleccionar automàticament la primera resposta suggerida per a preguntes de seguiment després del temps d'espera configurat", "timeoutLabel": "Temps d'espera abans de seleccionar automàticament la primera resposta" }, + "ralph": { + "label": "Ralph", + "description": "Mostra el botó Ralph al mode xat que habilita la capacitat de bucle d'agent autònom", + "loopLimit": { + "label": "Límit de bucle", + "description": "Nombre màxim de vegades que Ralph reiniciarà automàticament la tasca. Estableix a 0 per a bucles il·limitats." + }, + "completionDelimiter": { + "label": "Delimitador de finalització", + "description": "Si es troba aquest text en el resultat de la finalització, Ralph deixarà de fer bucles." + } + }, "execute": { "label": "Executar", "description": "Executar automàticament comandes de terminal permeses sense requerir aprovació", diff --git a/webview-ui/src/i18n/locales/cs/settings.json b/webview-ui/src/i18n/locales/cs/settings.json index aa0a965ee9f..0d1134033fb 100644 --- a/webview-ui/src/i18n/locales/cs/settings.json +++ b/webview-ui/src/i18n/locales/cs/settings.json @@ -262,7 +262,19 @@ "followupQuestions": { "label": "Otázka", "description": "Automaticky vybrat první navrhovanou odpověď pro následné otázky po uplynutí nastaveného časového limitu", - "timeoutLabel": "Doba čekání před automatickým výběrem první odpovědi" + "timeoutLabel": "Doba čekání przed automatickým výběrem první odpovědi" + }, + "ralph": { + "label": "Ralph", + "description": "Zobrazit tlačítko Ralph v režimu chatu, které umožňuje schopnost smyčky autonomního agenta", + "loopLimit": { + "label": "Limit smyčky", + "description": "Maximální počet opakování, kdy Ralph automaticky restartuje úkol. Nastavte na 0 pro neomezené smyčky." + }, + "completionDelimiter": { + "label": "Oddělovač dokončení", + "description": "Pokud je tento text nalezen ve výsledku dokončení, Ralph přestane opakovat smyčku." + } }, "execute": { "label": "Spustit", diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index ce47aa05270..135f9d2f671 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -239,6 +239,18 @@ "description": "Automatisch die erste vorgeschlagene Antwort für Folgefragen nach der konfigurierten Zeitüberschreitung auswählen", "timeoutLabel": "Wartezeit vor der automatischen Auswahl der ersten Antwort" }, + "ralph": { + "label": "Ralph", + "description": "Zeige Ralph-Button im Chat-Modus, der die autonome Agenten-Schleifen-Fähigkeit aktiviert", + "loopLimit": { + "label": "Schleifenlimit", + "description": "Maximale Anzahl der Male, die Ralph die Aufgabe automatisch neu startet. Auf 0 setzen für unbegrenzte Schleifen." + }, + "completionDelimiter": { + "label": "Abschluss-Trennzeichen", + "description": "Wenn dieser Text im Abschlussergebnis gefunden wird, stoppt Ralph die Schleife." + } + }, "execute": { "label": "Ausführen", "description": "Erlaubte Terminal-Befehle automatisch ohne Genehmigung ausführen", diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 4e72a0e5d98..b8d5a881a25 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -251,6 +251,18 @@ "description": "Automatically select the first suggested answer for follow-up questions after the configured timeout", "timeoutLabel": "Time to wait before auto-selecting the first answer" }, + "ralph": { + "label": "Ralph", + "description": "Show Ralph button in chat mode that enable autonomous agent loop ability", + "loopLimit": { + "label": "Loop Limit", + "description": "Maximum number of times Ralph will automatically restart the task. Set to 0 for unlimited loops." + }, + "completionDelimiter": { + "label": "Completion Delimiter", + "description": "If this text is found in the completion result, Ralph will stop looping." + } + }, "execute": { "label": "Execute", "description": "Automatically execute allowed terminal commands without requiring approval", diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index 1d5b8494861..3937317b72e 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -243,6 +243,18 @@ "description": "Seleccionar automáticamente la primera respuesta sugerida para preguntas de seguimiento después del tiempo de espera configurado", "timeoutLabel": "Tiempo de espera antes de seleccionar automáticamente la primera respuesta" }, + "ralph": { + "label": "Ralph", + "description": "Mostrar el botón Ralph en el modo chat que habilita la capacidad de bucle del agente autónomo", + "loopLimit": { + "label": "Límite de bucles", + "description": "Número máximo de veces que Ralph reiniciará automáticamente la tarea. Establecer en 0 para bucles ilimitados." + }, + "completionDelimiter": { + "label": "Delimitador de finalización", + "description": "Si se encuentra este texto en el resultado de la finalización, Ralph dejará de hacer bucles." + } + }, "execute": { "label": "Ejecutar", "description": "Ejecutar automáticamente comandos de terminal permitidos sin requerir aprobación", diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 1ffebf58edf..c428450e0ab 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -244,6 +244,18 @@ "description": "Sélectionner automatiquement la première réponse suggérée pour les questions de suivi après le délai configuré", "timeoutLabel": "Temps d'attente avant la sélection automatique de la première réponse" }, + "ralph": { + "label": "Ralph", + "description": "Afficher le bouton Ralph en mode chat pour activer la capacité de boucle de l'agent autonome", + "loopLimit": { + "label": "Limite de boucles", + "description": "Nombre maximum de fois que Ralph redémarrera automatiquement la tâche. Réglez sur 0 choisis pour des boucles illimitées." + }, + "completionDelimiter": { + "label": "Délimiteur d'achèvement", + "description": "Si ce texte est trouvé dans le résultat final, Ralph arrêtera de boucler." + } + }, "execute": { "label": "Exécuter", "description": "Exécuter automatiquement les commandes de terminal autorisées sans nécessiter d'approbation", diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index 381e764d08d..ed56504b27c 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -243,6 +243,18 @@ "description": "कॉन्फ़िगर किए गए टाइमआउट के बाद अनुवर्ती प्रश्नों के लिए पहले सुझाए गए उत्तर को स्वचालित रूप से चुनें", "timeoutLabel": "पहले उत्तर को स्वचालित रूप से चुनने से पहले प्रतीक्षा करने का समय" }, + "ralph": { + "label": "Ralph", + "description": "चैट मोड में Ralph बटन दिखाएं जो स्वायत्त एजेंट लूप क्षमता को सक्षम करता है", + "loopLimit": { + "label": "लूप सीमा", + "description": "Ralph द्वारा कार्य को स्वचालित रूप से पुनरारंभ करने की अधिकतम संख्या। असीमित लूप के लिए 0 पर सेट करें।" + }, + "completionDelimiter": { + "label": "पूर्णता सीमांकक", + "description": "यदि पूर्णता परिणाम में यह टेक्स्ट मिलता है, तो Ralph लूपिंग बंद कर देगा।" + } + }, "execute": { "label": "निष्पादित करें", "description": "अनुमोदन की आवश्यकता के बिना स्वचालित रूप से अनुमत टर्मिनल कमांड निष्पादित करें", diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index e197d1cb6db..fe1fc467069 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -243,6 +243,18 @@ "description": "Secara otomatis memilih jawaban pertama yang disarankan untuk pertanyaan lanjutan setelah batas waktu yang dikonfigurasi", "timeoutLabel": "Waktu tunggu sebelum otomatis memilih jawaban pertama" }, + "ralph": { + "label": "Ralph", + "description": "Tampilkan tombol Ralph dalam mode chat yang mengaktifkan kemampuan loop agen otonom", + "loopLimit": { + "label": "Batas Loop", + "description": "Jumlah maksimum kali Ralph akan secara otomatis memulai ulang tugas. Atur ke 0 untuk loop tak terbatas." + }, + "completionDelimiter": { + "label": "Pemisah Penyelesaian", + "description": "Jika teks ini ditemukan dalam hasil penyelesaian, Ralph akan berhenti melakukan looping." + } + }, "execute": { "label": "Eksekusi", "description": "Secara otomatis mengeksekusi perintah terminal yang diizinkan tanpa memerlukan persetujuan", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index 22276314148..cc7abab572f 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -248,6 +248,18 @@ "description": "Seleziona automaticamente la prima risposta suggerita per le domande di follow-up dopo il timeout configurato", "timeoutLabel": "Tempo di attesa prima di selezionare automaticamente la prima risposta" }, + "ralph": { + "label": "Ralph", + "description": "Mostra il pulsante Ralph in modalità chat che abilita la capacità di loop dell'agente autonomo", + "loopLimit": { + "label": "Limite di loop", + "description": "Numero massimo di volte in cui Ralph riavvierà automaticamente l'attività. Impostare a 0 per loop illimitati." + }, + "completionDelimiter": { + "label": "Delimitatore di completamento", + "description": "Se questo testo viene trovato nel risultato del completamento, Ralph smetterà di eseguire il loop." + } + }, "execute": { "label": "Esegui", "description": "Esegui automaticamente i comandi del terminale consentiti senza richiedere approvazione", diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index 939d415dca4..e0af5634f9d 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -248,6 +248,18 @@ "description": "設定された時間が経過すると、フォローアップ質問の最初の提案回答を自動的に選択します", "timeoutLabel": "最初の回答を自動選択するまでの待機時間" }, + "ralph": { + "label": "Ralph", + "description": "チャットモードで自律型エージェントのループ機能を有効にするRalphボタンを表示します", + "loopLimit": { + "label": "ループ制限", + "description": "Ralphがタスクを自動的に再開する最大回数。無制限のループにするには0に設定してください。" + }, + "completionDelimiter": { + "label": "完了デリミタ", + "description": "完了結果にこのテキストが含まれている場合、Ralphはループを停止します。" + } + }, "execute": { "label": "実行", "description": "承認なしで自動的に許可されたターミナルコマンドを実行", diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 7094fcbe62d..2ed24b33ee6 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -243,6 +243,18 @@ "description": "설정된 시간이 지나면 후속 질문에 대한 첫 번째 제안 답변을 자동으로 선택합니다", "timeoutLabel": "첫 번째 답변을 자동 선택하기 전 대기 시간" }, + "ralph": { + "label": "Ralph", + "description": "자율 에이전트 루프 기능을 활성화하는 Ralph 버튼을 채팅 모드에서 표시합니다", + "loopLimit": { + "label": "루프 제한", + "description": "Ralph가 작업을 자동으로 재시작하는 최대 횟수입니다. 무제한 루프를 위해 0으로 설정하십시오." + }, + "completionDelimiter": { + "label": "완료 구분 기호", + "description": "완료 결과에서 이 텍스트가 발견되면 Ralph는 루프를 중지합니다." + } + }, "execute": { "label": "실행", "description": "승인 없이 자동으로 허용된 터미널 명령 실행", diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index 028d5cf59e4..08cf17ab024 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -243,6 +243,18 @@ "description": "Selecteer automatisch het eerste voorgestelde antwoord voor vervolgvragen na de geconfigureerde time-out", "timeoutLabel": "Wachttijd voordat het eerste antwoord automatisch wordt geselecteerd" }, + "ralph": { + "label": "Ralph", + "description": "Toon de Ralph-knop in de chatmodus die de autonome agent-loop-mogelijkheid activeert", + "loopLimit": { + "label": "Loop-limiet", + "description": "Maximaal aantal keren dat Ralph de taak automatisch herstart. Stel in op 0 voor onbeperkte loops." + }, + "completionDelimiter": { + "label": "Voltooiingsscheidingsteken", + "description": "Als deze tekst wordt gevonden in het voltooiingsresultaat, stopt Ralph met loopen." + } + }, "execute": { "label": "Uitvoeren", "description": "Automatisch toegestane terminalcommando's uitvoeren zonder goedkeuring", diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index 2f9171a9d0a..f70cf683e10 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -243,6 +243,18 @@ "description": "Automatycznie wybierz pierwszą sugerowaną odpowiedź na pytania uzupełniające po skonfigurowanym limicie czasu", "timeoutLabel": "Czas oczekiwania przed automatycznym wybraniem pierwszej odpowiedzi" }, + "ralph": { + "label": "Ralph", + "description": "Pokaż przycisk Ralph w trybie czatu, który umożliwia autonomiczną pętlę agenta", + "loopLimit": { + "label": "Limit pętli", + "description": "Maksymalna liczba automatycznych restartów zadania przez Ralpha. Ustaw na 0 dla nieograniczonej liczby pętli." + }, + "completionDelimiter": { + "label": "Separator zakończenia", + "description": "Jeśli ten tekst zostanie znaleziony w wyniku zakończenia, Ralph przestanie zapętlać." + } + }, "execute": { "label": "Wykonaj", "description": "Automatycznie wykonuj dozwolone polecenia terminala bez konieczności zatwierdzania", diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index 47db460a514..2ff2b2a8623 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -243,6 +243,18 @@ "description": "Selecionar automaticamente a primeira resposta sugerida para perguntas de acompanhamento após o tempo limite configurado", "timeoutLabel": "Tempo de espera antes de selecionar automaticamente a primeira resposta" }, + "ralph": { + "label": "Ralph", + "description": "Mostrar o botão Ralph no modo chat que habilita a capacidade de loop do agente autônomo", + "loopLimit": { + "label": "Limite de Loop", + "description": "Número máximo de vezes que o Ralph reiniciará automaticamente a tarefa. Defina como 0 para loops ilimitados." + }, + "completionDelimiter": { + "label": "Delimitador de Conclusão", + "description": "Se este texto for encontrado no resultado da conclusão, o Ralph parará de repetir o loop." + } + }, "execute": { "label": "Executar", "description": "Executar automaticamente comandos de terminal permitidos sem exigir aprovação", diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index 63799de771b..b8861ed44ab 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -243,6 +243,18 @@ "description": "Автоматически выбирать первый предложенный ответ на дополнительные вопросы после настроенного тайм-аута", "timeoutLabel": "Время ожидания перед автоматическим выбором первого ответа" }, + "ralph": { + "label": "Ralph", + "description": "Показать кнопку Ralph в режиме чата, которая включает возможность цикла автономного агента", + "loopLimit": { + "label": "Лимит циклов", + "description": "Максимальное количество автоматических перезапусков задачи Ralph. Установите 0 для неограниченных циклов." + }, + "completionDelimiter": { + "label": "Разделитель завершения", + "description": "Если этот текст найден в результате завершения, Ralph прекратит циклическое выполнение." + } + }, "execute": { "label": "Выполнение", "description": "Автоматически выполнять разрешённые команды терминала без необходимости одобрения", diff --git a/webview-ui/src/i18n/locales/th/settings.json b/webview-ui/src/i18n/locales/th/settings.json index b0aa10c7f10..222f379da9e 100644 --- a/webview-ui/src/i18n/locales/th/settings.json +++ b/webview-ui/src/i18n/locales/th/settings.json @@ -274,6 +274,18 @@ "description": "เลือกคำตอบแรกที่แนะนำสำหรับคำถามติดตามโดยอัตโนมัติหลังจากหมดเวลาที่กำหนด", "timeoutLabel": "เวลารอก่อนเลือกคำตอบแรกโดยอัตโนมัติ" }, + "ralph": { + "label": "Ralph", + "description": "แสดงปุ่ม Ralph ในโหมดแชทที่เปิดใช้งานความสามารถในการวนซ้ำของตัวแทนอิสระ", + "loopLimit": { + "label": "จำกัดการวนซ้ำ", + "description": "จำนวนครั้งสูงสุดที่ Ralph จะเริ่มงานใหม่โดยอัตโนมัติ ตั้งค่าเป็น 0 สำหรับการวนซ้ำไม่จำกัด" + }, + "completionDelimiter": { + "label": "ตัวคั่นการเสร็จสิ้น", + "description": "หากพบข้อความนี้ในผลลัพธ์การเสร็จสิ้น Ralph จะหยุดการวนซ้ำ" + } + }, "execute": { "label": "ดำเนินการ", "description": "ดำเนินการคำสั่งเทอร์มินัลที่อนุญาตโดยอัตโนมัติโดยไม่ต้องขออนุมัติ", diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index cab6d28a1a7..c888344ff40 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -243,6 +243,18 @@ "description": "Yapılandırılan zaman aşımından sonra takip sorularına ilişkin ilk önerilen yanıtı otomatik olarak seç", "timeoutLabel": "İlk yanıtı otomatik olarak seçmeden önce beklenecek süre" }, + "ralph": { + "label": "Ralph", + "description": "Sohbet modunda otonom ajan döngü yeteneğini etkinleştiren Ralph düğmesini göster", + "loopLimit": { + "label": "Döngü Sınırı", + "description": "Ralph'ın görevi otomatik olarak yeniden başlatma maksimum sayısı. Sınırsız döngü için 0 olarak ayarlayın." + }, + "completionDelimiter": { + "label": "Tamamlama Sınırlayıcı", + "description": "Tamamlama sonucunda bu metin bulunursa, Ralph döngüyü durduracaktır." + } + }, "execute": { "label": "Yürüt", "description": "Onay gerektirmeden otomatik olarak izin verilen terminal komutlarını yürüt", diff --git a/webview-ui/src/i18n/locales/uk/settings.json b/webview-ui/src/i18n/locales/uk/settings.json index f9bdb92c49c..a7003bea1c5 100644 --- a/webview-ui/src/i18n/locales/uk/settings.json +++ b/webview-ui/src/i18n/locales/uk/settings.json @@ -280,6 +280,18 @@ "description": "Автоматично вибирати першу запропоновану відповідь на додаткові питання після налаштованого тайм-ауту", "timeoutLabel": "Час очікування перед автоматичним вибором першої відповіді" }, + "ralph": { + "label": "Ralph", + "description": "Показувати кнопку Ralph у режимі чату, що вмикає можливість циклу автономного агента", + "loopLimit": { + "label": "Ліміт циклів", + "description": "Максимальна кількість разів, коли Ralph автоматично перезапускатиме завдання. Встановіть 0 для необмеженої кількості циклів." + }, + "completionDelimiter": { + "label": "Розділювач завершення", + "description": "Якщо цей текст буде знайдено в результаті завершення, Ralph припинить циклічне виконання." + } + }, "execute": { "label": "Виконати", "description": "Автоматично виконувати дозволені команди терміналу без необхідності затвердження", diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index 8b930042f88..59fc943ef22 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -243,6 +243,18 @@ "description": "Tự động chọn câu trả lời đầu tiên được đề xuất cho các câu hỏi tiếp theo sau thời gian chờ đã cấu hình", "timeoutLabel": "Thời gian chờ trước khi tự động chọn câu trả lời đầu tiên" }, + "ralph": { + "label": "Ralph", + "description": "Hiển thị nút Ralph trong chế độ chat để kích hoạt khả năng vòng lặp tác nhân tự trị", + "loopLimit": { + "label": "Giới hạn vòng lặp", + "description": "Số lần tối đa Ralph sẽ tự động khởi động lại tác vụ. Đặt thành 0 cho các vòng lặp không giới hạn." + }, + "completionDelimiter": { + "label": "Dấu phân cách hoàn thành", + "description": "Nếu văn bản này được tìm thấy trong kết quả hoàn thành, Ralph sẽ ngừng lặp lại." + } + }, "execute": { "label": "Thực thi", "description": "Tự động thực thi các lệnh terminal được phép mà không cần phê duyệt", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index e15401916c2..e03601ee793 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -243,6 +243,18 @@ "description": "在配置的超时时间后自动选择后续问题的第一个建议答案", "timeoutLabel": "自动选择第一个答案前的等待时间" }, + "ralph": { + "label": "Ralph", + "description": "在聊天模式下显示 Ralph 按钮,启用自主代理循环能力", + "loopLimit": { + "label": "循环限制", + "description": "Ralph 自动重启任务的最大次数。设置为 0 表示无限循环。" + }, + "completionDelimiter": { + "label": "完成分隔符", + "description": "如果在完成结果中找到此文本,Ralph 将停止循环。" + } + }, "execute": { "label": "执行", "description": "自动执行白名单中的命令而无需批准", diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index c5abecdbae2..b737848ddab 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -247,6 +247,18 @@ "description": "在設定的逾時時間過後,自動選擇後續問題的第一個建議答案", "timeoutLabel": "自動選擇第一個答案前的等待時間" }, + "ralph": { + "label": "Ralph", + "description": "在聊天模式下顯示 Ralph 按鈕,啟用自主代理循環能力", + "loopLimit": { + "label": "循環限制", + "description": "Ralph 自動重啟任務的最大次數。設置為 0 表示無限循環。" + }, + "completionDelimiter": { + "label": "完成分隔符", + "description": "如果在完成結果中找到此文本,Ralph 將停止循環。" + } + }, "execute": { "label": "執行命令", "description": "無需核准即可自動執行允許的終端機命令",