Просмотр исходного кода

Auto approve follow up questions (#5296)

Will Li 6 месяцев назад
Родитель
Сommit
781beeb30f
51 измененных файлов с 453 добавлено и 68 удалено
  1. 41 0
      packages/types/src/followup.ts
  2. 4 0
      packages/types/src/global-settings.ts
  3. 1 0
      packages/types/src/index.ts
  4. 6 0
      src/core/webview/ClineProvider.ts
  5. 8 0
      src/core/webview/webviewMessageHandler.ts
  6. 2 0
      src/shared/WebviewMessage.ts
  7. 8 0
      webview-ui/src/components/chat/AutoApproveMenu.tsx
  8. 6 2
      webview-ui/src/components/chat/ChatRow.tsx
  9. 82 17
      webview-ui/src/components/chat/ChatView.tsx
  10. 80 29
      webview-ui/src/components/chat/FollowUpSuggest.tsx
  11. 34 0
      webview-ui/src/components/settings/AutoApproveSettings.tsx
  12. 11 2
      webview-ui/src/components/settings/AutoApproveToggle.tsx
  13. 6 0
      webview-ui/src/components/settings/SettingsView.tsx
  14. 1 0
      webview-ui/src/components/settings/__tests__/AutoApproveToggle.spec.tsx
  15. 19 0
      webview-ui/src/context/ExtensionStateContext.tsx
  16. 3 1
      webview-ui/src/i18n/locales/ca/chat.json
  17. 5 0
      webview-ui/src/i18n/locales/ca/settings.json
  18. 3 1
      webview-ui/src/i18n/locales/de/chat.json
  19. 5 0
      webview-ui/src/i18n/locales/de/settings.json
  20. 3 1
      webview-ui/src/i18n/locales/en/chat.json
  21. 5 0
      webview-ui/src/i18n/locales/en/settings.json
  22. 3 1
      webview-ui/src/i18n/locales/es/chat.json
  23. 5 0
      webview-ui/src/i18n/locales/es/settings.json
  24. 3 1
      webview-ui/src/i18n/locales/fr/chat.json
  25. 5 0
      webview-ui/src/i18n/locales/fr/settings.json
  26. 3 1
      webview-ui/src/i18n/locales/hi/chat.json
  27. 5 0
      webview-ui/src/i18n/locales/hi/settings.json
  28. 3 1
      webview-ui/src/i18n/locales/id/chat.json
  29. 5 0
      webview-ui/src/i18n/locales/id/settings.json
  30. 3 1
      webview-ui/src/i18n/locales/it/chat.json
  31. 5 0
      webview-ui/src/i18n/locales/it/settings.json
  32. 3 1
      webview-ui/src/i18n/locales/ja/chat.json
  33. 5 0
      webview-ui/src/i18n/locales/ja/settings.json
  34. 3 1
      webview-ui/src/i18n/locales/ko/chat.json
  35. 5 0
      webview-ui/src/i18n/locales/ko/settings.json
  36. 3 1
      webview-ui/src/i18n/locales/nl/chat.json
  37. 5 0
      webview-ui/src/i18n/locales/nl/settings.json
  38. 3 1
      webview-ui/src/i18n/locales/pl/chat.json
  39. 5 0
      webview-ui/src/i18n/locales/pl/settings.json
  40. 3 1
      webview-ui/src/i18n/locales/pt-BR/chat.json
  41. 5 0
      webview-ui/src/i18n/locales/pt-BR/settings.json
  42. 3 1
      webview-ui/src/i18n/locales/ru/chat.json
  43. 5 0
      webview-ui/src/i18n/locales/ru/settings.json
  44. 3 1
      webview-ui/src/i18n/locales/tr/chat.json
  45. 5 0
      webview-ui/src/i18n/locales/tr/settings.json
  46. 3 1
      webview-ui/src/i18n/locales/vi/chat.json
  47. 5 0
      webview-ui/src/i18n/locales/vi/settings.json
  48. 3 1
      webview-ui/src/i18n/locales/zh-CN/chat.json
  49. 5 0
      webview-ui/src/i18n/locales/zh-CN/settings.json
  50. 3 1
      webview-ui/src/i18n/locales/zh-TW/chat.json
  51. 5 0
      webview-ui/src/i18n/locales/zh-TW/settings.json

+ 41 - 0
packages/types/src/followup.ts

@@ -0,0 +1,41 @@
+import { z } from "zod"
+
+/**
+ * Interface for follow-up data structure used in follow-up questions
+ * This represents the data structure for follow-up questions that the LLM can ask
+ * to gather more information needed to complete a task.
+ */
+export interface FollowUpData {
+	/** The question being asked by the LLM */
+	question?: string
+	/** Array of suggested answers that the user can select */
+	suggest?: Array<SuggestionItem>
+}
+
+/**
+ * Interface for a suggestion item with optional mode switching
+ */
+export interface SuggestionItem {
+	/** The text of the suggestion */
+	answer: string
+	/** Optional mode to switch to when selecting this suggestion */
+	mode?: string
+}
+
+/**
+ * Zod schema for SuggestionItem
+ */
+export const suggestionItemSchema = z.object({
+	answer: z.string(),
+	mode: z.string().optional(),
+})
+
+/**
+ * Zod schema for FollowUpData
+ */
+export const followUpDataSchema = z.object({
+	question: z.string().optional(),
+	suggest: z.array(suggestionItemSchema).optional(),
+})
+
+export type FollowUpDataType = z.infer<typeof followUpDataSchema>

+ 4 - 0
packages/types/src/global-settings.ts

@@ -45,6 +45,8 @@ export const globalSettingsSchema = z.object({
 	alwaysAllowModeSwitch: z.boolean().optional(),
 	alwaysAllowSubtasks: z.boolean().optional(),
 	alwaysAllowExecute: z.boolean().optional(),
+	alwaysAllowFollowupQuestions: z.boolean().optional(),
+	followupAutoApproveTimeoutMs: z.number().optional(),
 	allowedCommands: z.array(z.string()).optional(),
 	allowedMaxRequests: z.number().nullish(),
 	autoCondenseContext: z.boolean().optional(),
@@ -189,6 +191,8 @@ export const EVALS_SETTINGS: RooCodeSettings = {
 	alwaysAllowModeSwitch: true,
 	alwaysAllowSubtasks: true,
 	alwaysAllowExecute: true,
+	alwaysAllowFollowupQuestions: true,
+	followupAutoApproveTimeoutMs: 0,
 	allowedCommands: ["*"],
 
 	browserToolEnabled: false,

+ 1 - 0
packages/types/src/index.ts

@@ -4,6 +4,7 @@ export * from "./api.js"
 export * from "./codebase-index.js"
 export * from "./cloud.js"
 export * from "./experiment.js"
+export * from "./followup.js"
 export * from "./global-settings.js"
 export * from "./history.js"
 export * from "./ipc.js"

+ 6 - 0
src/core/webview/ClineProvider.ts

@@ -1411,6 +1411,8 @@ export class ClineProvider
 			codebaseIndexConfig,
 			codebaseIndexModels,
 			profileThresholds,
+			alwaysAllowFollowupQuestions,
+			followupAutoApproveTimeoutMs,
 		} = await this.getState()
 
 		const telemetryKey = process.env.POSTHOG_API_KEY
@@ -1521,6 +1523,8 @@ export class ClineProvider
 			profileThresholds: profileThresholds ?? {},
 			cloudApiUrl: getRooCodeApiUrl(),
 			hasOpenedModeSelector: this.getGlobalState("hasOpenedModeSelector") ?? false,
+			alwaysAllowFollowupQuestions: alwaysAllowFollowupQuestions ?? false,
+			followupAutoApproveTimeoutMs: followupAutoApproveTimeoutMs ?? 60000,
 		}
 	}
 
@@ -1601,6 +1605,8 @@ export class ClineProvider
 			alwaysAllowMcp: stateValues.alwaysAllowMcp ?? false,
 			alwaysAllowModeSwitch: stateValues.alwaysAllowModeSwitch ?? false,
 			alwaysAllowSubtasks: stateValues.alwaysAllowSubtasks ?? false,
+			alwaysAllowFollowupQuestions: stateValues.alwaysAllowFollowupQuestions ?? false,
+			followupAutoApproveTimeoutMs: stateValues.followupAutoApproveTimeoutMs ?? 60000,
 			allowedMaxRequests: stateValues.allowedMaxRequests,
 			autoCondenseContext: stateValues.autoCondenseContext ?? true,
 			autoCondenseContextPercent: stateValues.autoCondenseContextPercent ?? 100,

+ 8 - 0
src/core/webview/webviewMessageHandler.ts

@@ -1100,6 +1100,14 @@ export const webviewMessageHandler = async (
 			await updateGlobalState("maxWorkspaceFiles", fileCount)
 			await provider.postStateToWebview()
 			break
+		case "alwaysAllowFollowupQuestions":
+			await updateGlobalState("alwaysAllowFollowupQuestions", message.bool ?? false)
+			await provider.postStateToWebview()
+			break
+		case "followupAutoApproveTimeoutMs":
+			await updateGlobalState("followupAutoApproveTimeoutMs", message.value)
+			await provider.postStateToWebview()
+			break
 		case "browserToolEnabled":
 			await updateGlobalState("browserToolEnabled", message.bool ?? true)
 			await provider.postStateToWebview()

+ 2 - 0
src/shared/WebviewMessage.ts

@@ -37,6 +37,8 @@ export interface WebviewMessage {
 		| "alwaysAllowWriteOutsideWorkspace"
 		| "alwaysAllowWriteProtected"
 		| "alwaysAllowExecute"
+		| "alwaysAllowFollowupQuestions"
+		| "followupAutoApproveTimeoutMs"
 		| "webviewDidLaunch"
 		| "newTask"
 		| "askResponse"

+ 8 - 0
webview-ui/src/components/chat/AutoApproveMenu.tsx

@@ -25,6 +25,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
 		alwaysAllowModeSwitch,
 		alwaysAllowSubtasks,
 		alwaysApproveResubmit,
+		alwaysAllowFollowupQuestions,
 		allowedMaxRequests,
 		setAlwaysAllowReadOnly,
 		setAlwaysAllowWrite,
@@ -34,6 +35,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
 		setAlwaysAllowModeSwitch,
 		setAlwaysAllowSubtasks,
 		setAlwaysApproveResubmit,
+		setAlwaysAllowFollowupQuestions,
 		setAllowedMaxRequests,
 	} = useExtensionState()
 
@@ -68,6 +70,9 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
 				case "alwaysApproveResubmit":
 					setAlwaysApproveResubmit(value)
 					break
+				case "alwaysAllowFollowupQuestions":
+					setAlwaysAllowFollowupQuestions(value)
+					break
 			}
 		},
 		[
@@ -79,6 +84,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
 			setAlwaysAllowModeSwitch,
 			setAlwaysAllowSubtasks,
 			setAlwaysApproveResubmit,
+			setAlwaysAllowFollowupQuestions,
 		],
 	)
 
@@ -94,6 +100,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
 			alwaysAllowModeSwitch: alwaysAllowModeSwitch,
 			alwaysAllowSubtasks: alwaysAllowSubtasks,
 			alwaysApproveResubmit: alwaysApproveResubmit,
+			alwaysAllowFollowupQuestions: alwaysAllowFollowupQuestions,
 		}),
 		[
 			alwaysAllowReadOnly,
@@ -104,6 +111,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
 			alwaysAllowModeSwitch,
 			alwaysAllowSubtasks,
 			alwaysApproveResubmit,
+			alwaysAllowFollowupQuestions,
 		],
 	)
 

+ 6 - 2
webview-ui/src/components/chat/ChatRow.tsx

@@ -10,6 +10,7 @@ import type { ClineMessage } from "@roo-code/types"
 import { ClineApiReqInfo, ClineAskUseMcpServer, ClineSayTool } from "@roo/ExtensionMessage"
 import { COMMAND_OUTPUT_STRING } from "@roo/combineCommandSequences"
 import { safeJsonParse } from "@roo/safeJsonParse"
+import { FollowUpData, SuggestionItem } from "@roo-code/types"
 
 import { useCopyToClipboard } from "@src/utils/clipboard"
 import { useExtensionState } from "@src/context/ExtensionStateContext"
@@ -48,8 +49,9 @@ interface ChatRowProps {
 	isStreaming: boolean
 	onToggleExpand: (ts: number) => void
 	onHeightChange: (isTaller: boolean) => void
-	onSuggestionClick?: (answer: string, event?: React.MouseEvent) => void
+	onSuggestionClick?: (suggestion: SuggestionItem, event?: React.MouseEvent) => void
 	onBatchFileResponse?: (response: { [key: string]: boolean }) => void
+	onFollowUpUnmount?: () => void
 }
 
 // eslint-disable-next-line @typescript-eslint/no-empty-object-type
@@ -98,6 +100,7 @@ export const ChatRowContent = ({
 	isStreaming,
 	onToggleExpand,
 	onSuggestionClick,
+	onFollowUpUnmount,
 	onBatchFileResponse,
 }: ChatRowContentProps) => {
 	const { t } = useTranslation()
@@ -279,7 +282,7 @@ export const ChatRowContent = ({
 
 	const followUpData = useMemo(() => {
 		if (message.type === "ask" && message.ask === "followup" && !message.partial) {
-			return safeJsonParse<any>(message.text)
+			return safeJsonParse<FollowUpData>(message.text)
 		}
 		return null
 	}, [message.type, message.ask, message.partial, message.text])
@@ -1215,6 +1218,7 @@ export const ChatRowContent = ({
 								suggestions={followUpData?.suggest}
 								onSuggestionClick={onSuggestionClick}
 								ts={message?.ts}
+								onUnmount={onFollowUpUnmount}
 							/>
 						</>
 					)

+ 82 - 17
webview-ui/src/components/chat/ChatView.tsx

@@ -15,6 +15,7 @@ import type { ClineAsk, ClineMessage } from "@roo-code/types"
 import { ClineSayBrowserAction, ClineSayTool, ExtensionMessage } from "@roo/ExtensionMessage"
 import { McpServer, McpTool } from "@roo/mcp"
 import { findLast } from "@roo/array"
+import { FollowUpData, SuggestionItem } from "@roo-code/types"
 import { combineApiRequests } from "@roo/combineApiRequests"
 import { combineCommandSequences } from "@roo/combineCommandSequences"
 import { getApiMetrics } from "@roo/getApiMetrics"
@@ -88,11 +89,13 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
 		alwaysAllowMcp,
 		allowedCommands,
 		writeDelayMs,
+		followupAutoApproveTimeoutMs,
 		mode,
 		setMode,
 		autoApprovalEnabled,
 		alwaysAllowModeSwitch,
 		alwaysAllowSubtasks,
+		alwaysAllowFollowupQuestions,
 		customModes,
 		telemetrySetting,
 		hasSystemPromptOverride,
@@ -879,6 +882,10 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
 				return false
 			}
 
+			if (message.ask === "followup") {
+				return alwaysAllowFollowupQuestions
+			}
+
 			if (message.ask === "browser_action_launch") {
 				return alwaysAllowBrowser
 			}
@@ -957,6 +964,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
 			alwaysAllowMcp,
 			isMcpToolAlwaysAllowed,
 			alwaysAllowModeSwitch,
+			alwaysAllowFollowupQuestions,
 			alwaysAllowSubtasks,
 		],
 	)
@@ -1189,18 +1197,43 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
 
 	const placeholderText = task ? t("chat:typeMessage") : t("chat:typeTask")
 
+	// Function to switch to a specific mode
+	const switchToMode = useCallback(
+		(modeSlug: string): void => {
+			// Update local state and notify extension to sync mode change
+			setMode(modeSlug)
+
+			// Send the mode switch message
+			vscode.postMessage({
+				type: "mode",
+				text: modeSlug,
+			})
+		},
+		[setMode],
+	)
+
 	const handleSuggestionClickInRow = useCallback(
-		(answer: string, event?: React.MouseEvent) => {
+		(suggestion: SuggestionItem, event?: React.MouseEvent) => {
+			// Check if we need to switch modes
+			if (suggestion.mode) {
+				// Only switch modes if it's a manual click (event exists) or auto-approval is allowed
+				const isManualClick = !!event
+				if (isManualClick || alwaysAllowModeSwitch) {
+					// Switch mode without waiting
+					switchToMode(suggestion.mode)
+				}
+			}
+
 			if (event?.shiftKey) {
 				// Always append to existing text, don't overwrite
 				setInputValue((currentValue) => {
-					return currentValue !== "" ? `${currentValue} \n${answer}` : answer
+					return currentValue !== "" ? `${currentValue} \n${suggestion.answer}` : suggestion.answer
 				})
 			} else {
-				handleSendMessage(answer, [])
+				handleSendMessage(suggestion.answer, [])
 			}
 		},
-		[handleSendMessage, setInputValue], // setInputValue is stable, handleSendMessage depends on clineAsk
+		[handleSendMessage, setInputValue, switchToMode, alwaysAllowModeSwitch],
 	)
 
 	const handleBatchFileResponse = useCallback((response: { [key: string]: boolean }) => {
@@ -1208,6 +1241,15 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
 		vscode.postMessage({ type: "askResponse", askResponse: "objectResponse", text: JSON.stringify(response) })
 	}, [])
 
+	// Handler for when FollowUpSuggest component unmounts
+	const handleFollowUpUnmount = useCallback(() => {
+		// Clear the auto-approve timeout to prevent race conditions
+		if (autoApproveTimeoutRef.current) {
+			clearTimeout(autoApproveTimeoutRef.current)
+			autoApproveTimeoutRef.current = null
+		}
+	}, [])
+
 	const itemContent = useCallback(
 		(index: number, messageOrGroup: ClineMessage | ClineMessage[]) => {
 			// browser session group
@@ -1243,6 +1285,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
 					isStreaming={isStreaming}
 					onSuggestionClick={handleSuggestionClickInRow} // This was already stabilized
 					onBatchFileResponse={handleBatchFileResponse}
+					onFollowUpUnmount={handleFollowUpUnmount}
 				/>
 			)
 		},
@@ -1255,6 +1298,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
 			isStreaming,
 			handleSuggestionClickInRow,
 			handleBatchFileResponse,
+			handleFollowUpUnmount,
 		],
 	)
 
@@ -1270,19 +1314,41 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
 
 		const autoApprove = async () => {
 			if (lastMessage?.ask && isAutoApproved(lastMessage)) {
-				if (lastMessage.ask === "tool" && isWriteToolAction(lastMessage)) {
+				// Special handling for follow-up questions
+				if (lastMessage.ask === "followup") {
+					// Handle invalid JSON
+					let followUpData: FollowUpData = {}
+					try {
+						followUpData = JSON.parse(lastMessage.text || "{}") as FollowUpData
+					} catch (error) {
+						console.error("Failed to parse follow-up data:", error)
+						return
+					}
+
+					if (followUpData && followUpData.suggest && followUpData.suggest.length > 0) {
+						// Wait for the configured timeout before auto-selecting the first suggestion
+						await new Promise<void>((resolve) => {
+							autoApproveTimeoutRef.current = setTimeout(resolve, followupAutoApproveTimeoutMs)
+						})
+
+						// Get the first suggestion
+						const firstSuggestion = followUpData.suggest[0]
+
+						// Handle the suggestion click
+						handleSuggestionClickInRow(firstSuggestion)
+						return
+					}
+				} else if (lastMessage.ask === "tool" && isWriteToolAction(lastMessage)) {
 					await new Promise<void>((resolve) => {
 						autoApproveTimeoutRef.current = setTimeout(resolve, writeDelayMs)
 					})
 				}
 
-				if (autoApproveTimeoutRef.current === null || autoApproveTimeoutRef.current) {
-					vscode.postMessage({ type: "askResponse", askResponse: "yesButtonClicked" })
+				vscode.postMessage({ type: "askResponse", askResponse: "yesButtonClicked" })
 
-					setSendingDisabled(true)
-					setClineAsk(undefined)
-					setEnableButtons(false)
-				}
+				setSendingDisabled(true)
+				setClineAsk(undefined)
+				setEnableButtons(false)
 			}
 		}
 		autoApprove()
@@ -1303,6 +1369,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
 		alwaysAllowWrite,
 		alwaysAllowWriteOutsideWorkspace,
 		alwaysAllowExecute,
+		followupAutoApproveTimeoutMs,
 		alwaysAllowMcp,
 		messages,
 		allowedCommands,
@@ -1311,6 +1378,8 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
 		lastMessage,
 		writeDelayMs,
 		isWriteToolAction,
+		alwaysAllowFollowupQuestions,
+		handleSuggestionClickInRow,
 	])
 
 	// Function to handle mode switching
@@ -1319,12 +1388,8 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
 		const currentModeIndex = allModes.findIndex((m) => m.slug === mode)
 		const nextModeIndex = (currentModeIndex + 1) % allModes.length
 		// Update local state and notify extension to sync mode change
-		setMode(allModes[nextModeIndex].slug)
-		vscode.postMessage({
-			type: "mode",
-			text: allModes[nextModeIndex].slug,
-		})
-	}, [mode, setMode, customModes])
+		switchToMode(allModes[nextModeIndex].slug)
+	}, [mode, customModes, switchToMode])
 
 	// Add keyboard event handler
 	const handleKeyDown = useCallback(

+ 80 - 29
webview-ui/src/components/chat/FollowUpSuggest.tsx

@@ -1,40 +1,85 @@
-import { useCallback } from "react"
+import { useCallback, useEffect, useState } from "react"
 import { Edit } from "lucide-react"
 
 import { Button, StandardTooltip } from "@/components/ui"
-import { vscode } from "@/utils/vscode"
 
 import { useAppTranslation } from "@src/i18n/TranslationContext"
+import { useExtensionState } from "@src/context/ExtensionStateContext"
+import { SuggestionItem } from "@roo-code/types"
 
-interface SuggestionItem {
-	answer: string
-	mode?: string
-}
+const DEFAULT_FOLLOWUP_TIMEOUT_MS = 60000
+const COUNTDOWN_INTERVAL_MS = 1000
 
 interface FollowUpSuggestProps {
-	suggestions?: (string | SuggestionItem)[]
-	onSuggestionClick?: (answer: string, event?: React.MouseEvent) => void
+	suggestions?: SuggestionItem[]
+	onSuggestionClick?: (suggestion: SuggestionItem, event?: React.MouseEvent) => void
 	ts: number
+	onUnmount?: () => void
 }
 
-export const FollowUpSuggest = ({ suggestions = [], onSuggestionClick, ts = 1 }: FollowUpSuggestProps) => {
+export const FollowUpSuggest = ({ suggestions = [], onSuggestionClick, ts = 1, onUnmount }: FollowUpSuggestProps) => {
+	const { autoApprovalEnabled, alwaysAllowFollowupQuestions, followupAutoApproveTimeoutMs } = useExtensionState()
+	const [countdown, setCountdown] = useState<number | null>(null)
+	const [suggestionSelected, setSuggestionSelected] = useState(false)
 	const { t } = useAppTranslation()
-	const handleSuggestionClick = useCallback(
-		(suggestion: string | SuggestionItem, event: React.MouseEvent) => {
-			const suggestionText = typeof suggestion === "string" ? suggestion : suggestion.answer
-			const mode = typeof suggestion === "object" ? suggestion.mode : undefined
 
-			// If there's a mode switch and it's not a shift-click (which just copies to input), switch modes first
-			if (mode && !event.shiftKey) {
-				vscode.postMessage({
-					type: "mode",
-					text: mode,
+	// Start countdown timer when auto-approval is enabled for follow-up questions
+	useEffect(() => {
+		// Only start countdown if auto-approval is enabled for follow-up questions and no suggestion has been selected
+		if (autoApprovalEnabled && alwaysAllowFollowupQuestions && suggestions.length > 0 && !suggestionSelected) {
+			// Start with the configured timeout in seconds
+			const timeoutMs =
+				typeof followupAutoApproveTimeoutMs === "number" && !isNaN(followupAutoApproveTimeoutMs)
+					? followupAutoApproveTimeoutMs
+					: DEFAULT_FOLLOWUP_TIMEOUT_MS
+
+			// Convert milliseconds to seconds for the countdown
+			setCountdown(Math.floor(timeoutMs / 1000))
+
+			// Update countdown every second
+			const intervalId = setInterval(() => {
+				setCountdown((prevCountdown) => {
+					if (prevCountdown === null || prevCountdown <= 1) {
+						clearInterval(intervalId)
+						return null
+					}
+					return prevCountdown - 1
 				})
+			}, COUNTDOWN_INTERVAL_MS)
+
+			// Clean up interval on unmount and notify parent component
+			return () => {
+				clearInterval(intervalId)
+				// Notify parent component that this component is unmounting
+				// so it can clear any related timeouts
+				onUnmount?.()
+			}
+		} else {
+			setCountdown(null)
+		}
+	}, [
+		autoApprovalEnabled,
+		alwaysAllowFollowupQuestions,
+		suggestions,
+		followupAutoApproveTimeoutMs,
+		suggestionSelected,
+		onUnmount,
+	])
+	const handleSuggestionClick = useCallback(
+		(suggestion: SuggestionItem, event: React.MouseEvent) => {
+			// Mark a suggestion as selected if it's not a shift-click (which just copies to input)
+			if (!event.shiftKey) {
+				setSuggestionSelected(true)
+				// Also notify parent component to cancel auto-approval timeout
+				// This prevents race conditions between visual countdown and actual timeout
+				onUnmount?.()
 			}
 
-			onSuggestionClick?.(suggestionText, event)
+			// Pass the suggestion object to the parent component
+			// The parent component will handle mode switching if needed
+			onSuggestionClick?.(suggestion, event)
 		},
-		[onSuggestionClick],
+		[onSuggestionClick, onUnmount],
 	)
 
 	// Don't render if there are no suggestions or no click handler.
@@ -44,23 +89,29 @@ export const FollowUpSuggest = ({ suggestions = [], onSuggestionClick, ts = 1 }:
 
 	return (
 		<div className="flex mb-2 flex-col h-full gap-2">
-			{suggestions.map((suggestion) => {
-				const suggestionText = typeof suggestion === "string" ? suggestion : suggestion.answer
-				const mode = typeof suggestion === "object" ? suggestion.mode : undefined
+			{suggestions.map((suggestion, index) => {
+				const isFirstSuggestion = index === 0
 
 				return (
-					<div key={`${suggestionText}-${ts}`} className="w-full relative group">
+					<div key={`${suggestion.answer}-${ts}`} className="w-full relative group">
 						<Button
 							variant="outline"
 							className="text-left whitespace-normal break-words w-full h-auto py-3 justify-start pr-8"
 							onClick={(event) => handleSuggestionClick(suggestion, event)}
-							aria-label={suggestionText}>
-							{suggestionText}
+							aria-label={suggestion.answer}>
+							{suggestion.answer}
+							{isFirstSuggestion && countdown !== null && !suggestionSelected && (
+								<span
+									className="ml-2 px-1.5 py-0.5 text-xs rounded-full bg-vscode-badge-background text-vscode-badge-foreground"
+									title={t("chat:followUpSuggest.autoSelectCountdown", { count: countdown })}>
+									{t("chat:followUpSuggest.countdownDisplay", { count: countdown })}
+								</span>
+							)}
 						</Button>
-						{mode && (
+						{suggestion.mode && (
 							<div className="absolute bottom-0 right-0 text-[10px] bg-vscode-badge-background text-vscode-badge-foreground px-1 py-0.5 border border-vscode-badge-background flex items-center gap-0.5">
 								<span className="codicon codicon-arrow-right" style={{ fontSize: "8px" }} />
-								{mode}
+								{suggestion.mode}
 							</div>
 						)}
 						<StandardTooltip content={t("chat:followUpSuggest.copyToInput")}>
@@ -69,7 +120,7 @@ export const FollowUpSuggest = ({ suggestions = [], onSuggestionClick, ts = 1 }:
 								onClick={(e) => {
 									e.stopPropagation()
 									// Simulate shift-click by directly calling the handler with shiftKey=true.
-									onSuggestionClick?.(suggestionText, { ...e, shiftKey: true })
+									onSuggestionClick?.(suggestion, { ...e, shiftKey: true })
 								}}>
 								<Button variant="ghost" size="icon">
 									<Edit />

+ 34 - 0
webview-ui/src/components/settings/AutoApproveSettings.tsx

@@ -25,6 +25,8 @@ type AutoApproveSettingsProps = HTMLAttributes<HTMLDivElement> & {
 	alwaysAllowModeSwitch?: boolean
 	alwaysAllowSubtasks?: boolean
 	alwaysAllowExecute?: boolean
+	alwaysAllowFollowupQuestions?: boolean
+	followupAutoApproveTimeoutMs?: number
 	allowedCommands?: string[]
 	setCachedStateField: SetCachedStateField<
 		| "alwaysAllowReadOnly"
@@ -40,6 +42,8 @@ type AutoApproveSettingsProps = HTMLAttributes<HTMLDivElement> & {
 		| "alwaysAllowModeSwitch"
 		| "alwaysAllowSubtasks"
 		| "alwaysAllowExecute"
+		| "alwaysAllowFollowupQuestions"
+		| "followupAutoApproveTimeoutMs"
 		| "allowedCommands"
 	>
 }
@@ -58,6 +62,8 @@ export const AutoApproveSettings = ({
 	alwaysAllowModeSwitch,
 	alwaysAllowSubtasks,
 	alwaysAllowExecute,
+	alwaysAllowFollowupQuestions,
+	followupAutoApproveTimeoutMs = 60000,
 	allowedCommands,
 	setCachedStateField,
 	...props
@@ -95,6 +101,7 @@ export const AutoApproveSettings = ({
 					alwaysAllowModeSwitch={alwaysAllowModeSwitch}
 					alwaysAllowSubtasks={alwaysAllowSubtasks}
 					alwaysAllowExecute={alwaysAllowExecute}
+					alwaysAllowFollowupQuestions={alwaysAllowFollowupQuestions}
 					onToggle={(key, value) => setCachedStateField(key, value)}
 				/>
 
@@ -202,6 +209,33 @@ export const AutoApproveSettings = ({
 					</div>
 				)}
 
+				{alwaysAllowFollowupQuestions && (
+					<div className="flex flex-col gap-3 pl-3 border-l-2 border-vscode-button-background">
+						<div className="flex items-center gap-4 font-bold">
+							<span className="codicon codicon-question" />
+							<div>{t("settings:autoApprove.followupQuestions.label")}</div>
+						</div>
+						<div>
+							<div className="flex items-center gap-2">
+								<Slider
+									min={1000}
+									max={300000}
+									step={1000}
+									value={[followupAutoApproveTimeoutMs]}
+									onValueChange={([value]) =>
+										setCachedStateField("followupAutoApproveTimeoutMs", value)
+									}
+									data-testid="followup-timeout-slider"
+								/>
+								<span className="w-20">{followupAutoApproveTimeoutMs / 1000}s</span>
+							</div>
+							<div className="text-vscode-descriptionForeground text-sm mt-1">
+								{t("settings:autoApprove.followupQuestions.timeoutLabel")}
+							</div>
+						</div>
+					</div>
+				)}
+
 				{alwaysAllowExecute && (
 					<div className="flex flex-col gap-3 pl-3 border-l-2 border-vscode-button-background">
 						<div className="flex items-center gap-4 font-bold">

+ 11 - 2
webview-ui/src/components/settings/AutoApproveToggle.tsx

@@ -14,6 +14,7 @@ type AutoApproveToggles = Pick<
 	| "alwaysAllowModeSwitch"
 	| "alwaysAllowSubtasks"
 	| "alwaysAllowExecute"
+	| "alwaysAllowFollowupQuestions"
 >
 
 export type AutoApproveSetting = keyof AutoApproveToggles
@@ -83,6 +84,13 @@ export const autoApproveSettingsConfig: Record<AutoApproveSetting, AutoApproveCo
 		icon: "terminal",
 		testId: "always-allow-execute-toggle",
 	},
+	alwaysAllowFollowupQuestions: {
+		key: "alwaysAllowFollowupQuestions",
+		labelKey: "settings:autoApprove.followupQuestions.label",
+		descriptionKey: "settings:autoApprove.followupQuestions.description",
+		icon: "question",
+		testId: "always-allow-followup-questions-toggle",
+	},
 }
 
 type AutoApproveToggleProps = AutoApproveToggles & {
@@ -95,9 +103,10 @@ export const AutoApproveToggle = ({ onToggle, ...props }: AutoApproveToggleProps
 	return (
 		<div
 			className={cn(
-				"flex flex-row flex-wrap justify-center gap-2 max-w-[400px] mx-auto my-2 ",
+				"flex flex-row flex-wrap justify-center gap-2 max-w-[600px] mx-auto my-2 ",
 				"[@media(min-width:600px)]:gap-4",
-				"[@media(min-width:800px)]:max-w-[800px]",
+				"[@media(min-width:800px)]:max-w-[900px]",
+				"[@media(min-width:1200px)]:max-w-[1800px]",
 			)}>
 			{Object.values(autoApproveSettingsConfig).map(({ key, descriptionKey, labelKey, icon, testId }) => (
 				<StandardTooltip key={key} content={t(descriptionKey || "")}>

+ 6 - 0
webview-ui/src/components/settings/SettingsView.tsx

@@ -174,6 +174,8 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 		codebaseIndexModels,
 		customSupportPrompts,
 		profileThresholds,
+		alwaysAllowFollowupQuestions,
+		followupAutoApproveTimeoutMs,
 	} = cachedState
 
 	const apiConfiguration = useMemo(() => cachedState.apiConfiguration ?? {}, [cachedState.apiConfiguration])
@@ -311,6 +313,8 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 			vscode.postMessage({ type: "updateExperimental", values: experiments })
 			vscode.postMessage({ type: "alwaysAllowModeSwitch", bool: alwaysAllowModeSwitch })
 			vscode.postMessage({ type: "alwaysAllowSubtasks", bool: alwaysAllowSubtasks })
+			vscode.postMessage({ type: "alwaysAllowFollowupQuestions", bool: alwaysAllowFollowupQuestions })
+			vscode.postMessage({ type: "followupAutoApproveTimeoutMs", value: followupAutoApproveTimeoutMs })
 			vscode.postMessage({ type: "condensingApiConfigId", text: condensingApiConfigId || "" })
 			vscode.postMessage({ type: "updateCondensingPrompt", text: customCondensingPrompt || "" })
 			vscode.postMessage({ type: "updateSupportPrompt", values: customSupportPrompts || {} })
@@ -599,6 +603,8 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 							alwaysAllowModeSwitch={alwaysAllowModeSwitch}
 							alwaysAllowSubtasks={alwaysAllowSubtasks}
 							alwaysAllowExecute={alwaysAllowExecute}
+							alwaysAllowFollowupQuestions={alwaysAllowFollowupQuestions}
+							followupAutoApproveTimeoutMs={followupAutoApproveTimeoutMs}
 							allowedCommands={allowedCommands}
 							setCachedStateField={setCachedStateField}
 						/>

+ 1 - 0
webview-ui/src/components/settings/__tests__/AutoApproveToggle.spec.tsx

@@ -25,6 +25,7 @@ describe("AutoApproveToggle", () => {
 		alwaysAllowModeSwitch: true,
 		alwaysAllowSubtasks: false,
 		alwaysAllowExecute: true,
+		alwaysAllowFollowupQuestions: false,
 		onToggle: mockOnToggle,
 	}
 

+ 19 - 0
webview-ui/src/context/ExtensionStateContext.tsx

@@ -40,6 +40,10 @@ export interface ExtensionStateContextType extends ExtensionState {
 	mdmCompliant?: boolean
 	hasOpenedModeSelector: boolean // New property to track if user has opened mode selector
 	setHasOpenedModeSelector: (value: boolean) => void // Setter for the new property
+	alwaysAllowFollowupQuestions: boolean // New property for follow-up questions auto-approve
+	setAlwaysAllowFollowupQuestions: (value: boolean) => void // Setter for the new property
+	followupAutoApproveTimeoutMs: number | undefined // Timeout in ms for auto-approving follow-up questions
+	setFollowupAutoApproveTimeoutMs: (value: number) => void // Setter for the timeout
 	condensingApiConfigId?: string
 	setCondensingApiConfigId: (value: string) => void
 	customCondensingPrompt?: string
@@ -226,6 +230,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
 	const [currentCheckpoint, setCurrentCheckpoint] = useState<string>()
 	const [extensionRouterModels, setExtensionRouterModels] = useState<RouterModels | undefined>(undefined)
 	const [marketplaceItems, setMarketplaceItems] = useState<any[]>([])
+	const [alwaysAllowFollowupQuestions, setAlwaysAllowFollowupQuestions] = useState(false) // Add state for follow-up questions auto-approve
+	const [followupAutoApproveTimeoutMs, setFollowupAutoApproveTimeoutMs] = useState<number | undefined>(undefined) // Will be set from global settings
 	const [marketplaceInstalledMetadata, setMarketplaceInstalledMetadata] = useState<MarketplaceInstalledMetadata>({
 		project: {},
 		global: {},
@@ -255,6 +261,14 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
 					setState((prevState) => mergeExtensionState(prevState, newState))
 					setShowWelcome(!checkExistKey(newState.apiConfiguration))
 					setDidHydrateState(true)
+					// Update alwaysAllowFollowupQuestions if present in state message
+					if ((newState as any).alwaysAllowFollowupQuestions !== undefined) {
+						setAlwaysAllowFollowupQuestions((newState as any).alwaysAllowFollowupQuestions)
+					}
+					// Update followupAutoApproveTimeoutMs if present in state message
+					if ((newState as any).followupAutoApproveTimeoutMs !== undefined) {
+						setFollowupAutoApproveTimeoutMs((newState as any).followupAutoApproveTimeoutMs)
+					}
 					// Handle marketplace data if present in state message
 					if (newState.marketplaceItems !== undefined) {
 						setMarketplaceItems(newState.marketplaceItems)
@@ -352,6 +366,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
 		marketplaceItems,
 		marketplaceInstalledMetadata,
 		profileThresholds: state.profileThresholds ?? {},
+		alwaysAllowFollowupQuestions,
+		followupAutoApproveTimeoutMs,
 		setExperimentEnabled: (id, enabled) =>
 			setState((prevState) => ({ ...prevState, experiments: { ...prevState.experiments, [id]: enabled } })),
 		setApiConfiguration,
@@ -367,6 +383,9 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
 		setAlwaysAllowMcp: (value) => setState((prevState) => ({ ...prevState, alwaysAllowMcp: value })),
 		setAlwaysAllowModeSwitch: (value) => setState((prevState) => ({ ...prevState, alwaysAllowModeSwitch: value })),
 		setAlwaysAllowSubtasks: (value) => setState((prevState) => ({ ...prevState, alwaysAllowSubtasks: value })),
+		setAlwaysAllowFollowupQuestions,
+		setFollowupAutoApproveTimeoutMs: (value) =>
+			setState((prevState) => ({ ...prevState, followupAutoApproveTimeoutMs: value })),
 		setShowAnnouncement: (value) => setState((prevState) => ({ ...prevState, shouldShowAnnouncement: value })),
 		setAllowedCommands: (value) => setState((prevState) => ({ ...prevState, allowedCommands: value })),
 		setAllowedMaxRequests: (value) => setState((prevState) => ({ ...prevState, allowedMaxRequests: value })),

+ 3 - 1
webview-ui/src/i18n/locales/ca/chat.json

@@ -234,7 +234,9 @@
 		"tokens": "tokens"
 	},
 	"followUpSuggest": {
-		"copyToInput": "Copiar a l'entrada (o Shift + clic)"
+		"copyToInput": "Copiar a l'entrada (o Shift + clic)",
+		"autoSelectCountdown": "Selecció automàtica en {{count}}s",
+		"countdownDisplay": "{{count}}s"
 	},
 	"announcement": {
 		"title": "🎉 Roo Code {{version}} publicat",

+ 5 - 0
webview-ui/src/i18n/locales/ca/settings.json

@@ -114,6 +114,11 @@
 			"label": "Subtasques",
 			"description": "Permetre la creació i finalització de subtasques sense requerir aprovació"
 		},
+		"followupQuestions": {
+			"label": "Pregunta",
+			"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"
+		},
 		"execute": {
 			"label": "Executar",
 			"description": "Executar automàticament comandes de terminal permeses sense requerir aprovació",

+ 3 - 1
webview-ui/src/i18n/locales/de/chat.json

@@ -234,7 +234,9 @@
 		"tokens": "Tokens"
 	},
 	"followUpSuggest": {
-		"copyToInput": "In Eingabefeld kopieren (oder Shift + Klick)"
+		"copyToInput": "In Eingabefeld kopieren (oder Shift + Klick)",
+		"autoSelectCountdown": "Automatische Auswahl in {{count}}s",
+		"countdownDisplay": "{{count}}s"
 	},
 	"announcement": {
 		"title": "🎉 Roo Code {{version}} veröffentlicht",

+ 5 - 0
webview-ui/src/i18n/locales/de/settings.json

@@ -114,6 +114,11 @@
 			"label": "Teilaufgaben",
 			"description": "Erstellung und Abschluss von Unteraufgaben ohne Genehmigung erlauben"
 		},
+		"followupQuestions": {
+			"label": "Frage",
+			"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"
+		},
 		"execute": {
 			"label": "Ausführen",
 			"description": "Erlaubte Terminal-Befehle automatisch ohne Genehmigung ausführen",

+ 3 - 1
webview-ui/src/i18n/locales/en/chat.json

@@ -258,7 +258,9 @@
 		"seconds": "{{count}}s"
 	},
 	"followUpSuggest": {
-		"copyToInput": "Copy to input (same as shift + click)"
+		"copyToInput": "Copy to input (same as shift + click)",
+		"autoSelectCountdown": "Auto-selecting in {{count}}s",
+		"countdownDisplay": "{{count}}s"
 	},
 	"browser": {
 		"rooWantsToUse": "Roo wants to use the browser:",

+ 5 - 0
webview-ui/src/i18n/locales/en/settings.json

@@ -114,6 +114,11 @@
 			"label": "Subtasks",
 			"description": "Allow creation and completion of subtasks without requiring approval"
 		},
+		"followupQuestions": {
+			"label": "Question",
+			"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"
+		},
 		"execute": {
 			"label": "Execute",
 			"description": "Automatically execute allowed terminal commands without requiring approval",

+ 3 - 1
webview-ui/src/i18n/locales/es/chat.json

@@ -234,7 +234,9 @@
 		"tokens": "tokens"
 	},
 	"followUpSuggest": {
-		"copyToInput": "Copiar a la entrada (o Shift + clic)"
+		"copyToInput": "Copiar a la entrada (o Shift + clic)",
+		"autoSelectCountdown": "Selección automática en {{count}}s",
+		"countdownDisplay": "{{count}}s"
 	},
 	"announcement": {
 		"title": "🎉 Roo Code {{version}} publicado",

+ 5 - 0
webview-ui/src/i18n/locales/es/settings.json

@@ -114,6 +114,11 @@
 			"label": "Subtareas",
 			"description": "Permitir la creación y finalización de subtareas sin requerir aprobación"
 		},
+		"followupQuestions": {
+			"label": "Pregunta",
+			"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"
+		},
 		"execute": {
 			"label": "Ejecutar",
 			"description": "Ejecutar automáticamente comandos de terminal permitidos sin requerir aprobación",

+ 3 - 1
webview-ui/src/i18n/locales/fr/chat.json

@@ -234,7 +234,9 @@
 		"tokens": "tokens"
 	},
 	"followUpSuggest": {
-		"copyToInput": "Copier vers l'entrée (ou Shift + clic)"
+		"copyToInput": "Copier vers l'entrée (ou Shift + clic)",
+		"autoSelectCountdown": "Sélection automatique dans {{count}}s",
+		"countdownDisplay": "{{count}}s"
 	},
 	"announcement": {
 		"title": "🎉 Roo Code {{version}} est sortie",

+ 5 - 0
webview-ui/src/i18n/locales/fr/settings.json

@@ -114,6 +114,11 @@
 			"label": "Sous-tâches",
 			"description": "Permettre la création et l'achèvement des sous-tâches sans nécessiter d'approbation"
 		},
+		"followupQuestions": {
+			"label": "Question",
+			"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"
+		},
 		"execute": {
 			"label": "Exécuter",
 			"description": "Exécuter automatiquement les commandes de terminal autorisées sans nécessiter d'approbation",

+ 3 - 1
webview-ui/src/i18n/locales/hi/chat.json

@@ -234,7 +234,9 @@
 		"tokens": "टोकन"
 	},
 	"followUpSuggest": {
-		"copyToInput": "इनपुट में कॉपी करें (या Shift + क्लिक)"
+		"copyToInput": "इनपुट में कॉपी करें (या Shift + क्लिक)",
+		"autoSelectCountdown": "{{count}}s में स्वचालित रूप से चयन हो रहा है",
+		"countdownDisplay": "{{count}}सेकंड"
 	},
 	"announcement": {
 		"title": "🎉 Roo Code {{version}} रिलीज़ हुआ",

+ 5 - 0
webview-ui/src/i18n/locales/hi/settings.json

@@ -114,6 +114,11 @@
 			"label": "उप-कार्य",
 			"description": "अनुमोदन की आवश्यकता के बिना उप-कार्यों के निर्माण और पूर्णता की अनुमति दें"
 		},
+		"followupQuestions": {
+			"label": "प्रश्न",
+			"description": "कॉन्फ़िगर किए गए टाइमआउट के बाद अनुवर्ती प्रश्नों के लिए पहले सुझाए गए उत्तर को स्वचालित रूप से चुनें",
+			"timeoutLabel": "पहले उत्तर को स्वचालित रूप से चुनने से पहले प्रतीक्षा करने का समय"
+		},
 		"execute": {
 			"label": "निष्पादित करें",
 			"description": "अनुमोदन की आवश्यकता के बिना स्वचालित रूप से अनुमत टर्मिनल कमांड निष्पादित करें",

+ 3 - 1
webview-ui/src/i18n/locales/id/chat.json

@@ -264,7 +264,9 @@
 		"seconds": "{{count}}d"
 	},
 	"followUpSuggest": {
-		"copyToInput": "Salin ke input (sama dengan shift + klik)"
+		"copyToInput": "Salin ke input (sama dengan shift + klik)",
+		"autoSelectCountdown": "Pemilihan otomatis dalam {{count}}dtk",
+		"countdownDisplay": "{{count}}dtk"
 	},
 	"browser": {
 		"rooWantsToUse": "Roo ingin menggunakan browser:",

+ 5 - 0
webview-ui/src/i18n/locales/id/settings.json

@@ -114,6 +114,11 @@
 			"label": "Subtugas",
 			"description": "Izinkan pembuatan dan penyelesaian subtugas tanpa memerlukan persetujuan"
 		},
+		"followupQuestions": {
+			"label": "Pertanyaan",
+			"description": "Secara otomatis memilih jawaban pertama yang disarankan untuk pertanyaan lanjutan setelah batas waktu yang dikonfigurasi",
+			"timeoutLabel": "Waktu tunggu sebelum otomatis memilih jawaban pertama"
+		},
 		"execute": {
 			"label": "Eksekusi",
 			"description": "Secara otomatis mengeksekusi perintah terminal yang diizinkan tanpa memerlukan persetujuan",

+ 3 - 1
webview-ui/src/i18n/locales/it/chat.json

@@ -234,7 +234,9 @@
 		"tokens": "token"
 	},
 	"followUpSuggest": {
-		"copyToInput": "Copia nell'input (o Shift + clic)"
+		"copyToInput": "Copia nell'input (o Shift + clic)",
+		"autoSelectCountdown": "Selezione automatica in {{count}}s",
+		"countdownDisplay": "{{count}}s"
 	},
 	"announcement": {
 		"title": "🎉 Rilasciato Roo Code {{version}}",

+ 5 - 0
webview-ui/src/i18n/locales/it/settings.json

@@ -114,6 +114,11 @@
 			"label": "Sottoattività",
 			"description": "Consenti la creazione e il completamento di attività secondarie senza richiedere approvazione"
 		},
+		"followupQuestions": {
+			"label": "Domanda",
+			"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"
+		},
 		"execute": {
 			"label": "Esegui",
 			"description": "Esegui automaticamente i comandi del terminale consentiti senza richiedere approvazione",

+ 3 - 1
webview-ui/src/i18n/locales/ja/chat.json

@@ -234,7 +234,9 @@
 		"tokens": "トークン"
 	},
 	"followUpSuggest": {
-		"copyToInput": "入力欄にコピー(またはShift + クリック)"
+		"copyToInput": "入力欄にコピー(またはShift + クリック)",
+		"autoSelectCountdown": "{{count}}秒後に自動選択します",
+		"countdownDisplay": "{{count}}秒"
 	},
 	"announcement": {
 		"title": "🎉 Roo Code {{version}} リリース",

+ 5 - 0
webview-ui/src/i18n/locales/ja/settings.json

@@ -114,6 +114,11 @@
 			"label": "サブタスク",
 			"description": "承認なしでサブタスクの作成と完了を許可"
 		},
+		"followupQuestions": {
+			"label": "質問",
+			"description": "設定された時間が経過すると、フォローアップ質問の最初の提案回答を自動的に選択します",
+			"timeoutLabel": "最初の回答を自動選択するまでの待機時間"
+		},
 		"execute": {
 			"label": "実行",
 			"description": "承認なしで自動的に許可されたターミナルコマンドを実行",

+ 3 - 1
webview-ui/src/i18n/locales/ko/chat.json

@@ -234,7 +234,9 @@
 		"tokens": "토큰"
 	},
 	"followUpSuggest": {
-		"copyToInput": "입력창에 복사 (또는 Shift + 클릭)"
+		"copyToInput": "입력창에 복사 (또는 Shift + 클릭)",
+		"autoSelectCountdown": "{{count}}초 후 자동 선택",
+		"countdownDisplay": "{{count}}초"
 	},
 	"announcement": {
 		"title": "🎉 Roo Code {{version}} 출시",

+ 5 - 0
webview-ui/src/i18n/locales/ko/settings.json

@@ -114,6 +114,11 @@
 			"label": "하위 작업",
 			"description": "승인 없이 하위 작업 생성 및 완료 허용"
 		},
+		"followupQuestions": {
+			"label": "질문",
+			"description": "설정된 시간이 지나면 후속 질문에 대한 첫 번째 제안 답변을 자동으로 선택합니다",
+			"timeoutLabel": "첫 번째 답변을 자동 선택하기 전 대기 시간"
+		},
 		"execute": {
 			"label": "실행",
 			"description": "승인 없이 자동으로 허용된 터미널 명령 실행",

+ 3 - 1
webview-ui/src/i18n/locales/nl/chat.json

@@ -244,7 +244,9 @@
 		"tokens": "tokens"
 	},
 	"followUpSuggest": {
-		"copyToInput": "Kopiëren naar invoer (zelfde als shift + klik)"
+		"copyToInput": "Kopiëren naar invoer (zelfde als shift + klik)",
+		"autoSelectCountdown": "Automatische selectie in {{count}}s",
+		"countdownDisplay": "{{count}}s"
 	},
 	"browser": {
 		"rooWantsToUse": "Roo wil de browser gebruiken:",

+ 5 - 0
webview-ui/src/i18n/locales/nl/settings.json

@@ -114,6 +114,11 @@
 			"label": "Subtaken",
 			"description": "Subtaken aanmaken en afronden zonder goedkeuring"
 		},
+		"followupQuestions": {
+			"label": "Vraag",
+			"description": "Selecteer automatisch het eerste voorgestelde antwoord voor vervolgvragen na de geconfigureerde time-out",
+			"timeoutLabel": "Wachttijd voordat het eerste antwoord automatisch wordt geselecteerd"
+		},
 		"execute": {
 			"label": "Uitvoeren",
 			"description": "Automatisch toegestane terminalcommando's uitvoeren zonder goedkeuring",

+ 3 - 1
webview-ui/src/i18n/locales/pl/chat.json

@@ -234,7 +234,9 @@
 		"tokens": "tokeny"
 	},
 	"followUpSuggest": {
-		"copyToInput": "Kopiuj do pola wprowadzania (lub Shift + kliknięcie)"
+		"copyToInput": "Kopiuj do pola wprowadzania (lub Shift + kliknięcie)",
+		"autoSelectCountdown": "Automatyczny wybór za {{count}}s",
+		"countdownDisplay": "{{count}}s"
 	},
 	"announcement": {
 		"title": "🎉 Roo Code {{version}} wydany",

+ 5 - 0
webview-ui/src/i18n/locales/pl/settings.json

@@ -114,6 +114,11 @@
 			"label": "Podzadania",
 			"description": "Zezwalaj na tworzenie i ukończenie podzadań bez konieczności zatwierdzania"
 		},
+		"followupQuestions": {
+			"label": "Pytanie",
+			"description": "Automatycznie wybierz pierwszą sugerowaną odpowiedź na pytania uzupełniające po skonfigurowanym limicie czasu",
+			"timeoutLabel": "Czas oczekiwania przed automatycznym wybraniem pierwszej odpowiedzi"
+		},
 		"execute": {
 			"label": "Wykonaj",
 			"description": "Automatycznie wykonuj dozwolone polecenia terminala bez konieczności zatwierdzania",

+ 3 - 1
webview-ui/src/i18n/locales/pt-BR/chat.json

@@ -234,7 +234,9 @@
 		"tokens": "tokens"
 	},
 	"followUpSuggest": {
-		"copyToInput": "Copiar para entrada (ou Shift + clique)"
+		"copyToInput": "Copiar para entrada (ou Shift + clique)",
+		"autoSelectCountdown": "Seleção automática em {{count}}s",
+		"countdownDisplay": "{{count}}s"
 	},
 	"announcement": {
 		"title": "🎉 Roo Code {{version}} Lançado",

+ 5 - 0
webview-ui/src/i18n/locales/pt-BR/settings.json

@@ -114,6 +114,11 @@
 			"label": "Subtarefas",
 			"description": "Permitir a criação e conclusão de subtarefas sem exigir aprovação"
 		},
+		"followupQuestions": {
+			"label": "Pergunta",
+			"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"
+		},
 		"execute": {
 			"label": "Executar",
 			"description": "Executar automaticamente comandos de terminal permitidos sem exigir aprovação",

+ 3 - 1
webview-ui/src/i18n/locales/ru/chat.json

@@ -244,7 +244,9 @@
 		"tokens": "токены"
 	},
 	"followUpSuggest": {
-		"copyToInput": "Скопировать во ввод (то же, что shift + клик)"
+		"copyToInput": "Скопировать во ввод (то же, что shift + клик)",
+		"autoSelectCountdown": "Автовыбор через {{count}}с",
+		"countdownDisplay": "{{count}}с"
 	},
 	"browser": {
 		"rooWantsToUse": "Roo хочет использовать браузер:",

+ 5 - 0
webview-ui/src/i18n/locales/ru/settings.json

@@ -114,6 +114,11 @@
 			"label": "Подзадачи",
 			"description": "Разрешить создание и выполнение подзадач без необходимости одобрения"
 		},
+		"followupQuestions": {
+			"label": "Вопрос",
+			"description": "Автоматически выбирать первый предложенный ответ на дополнительные вопросы после настроенного тайм-аута",
+			"timeoutLabel": "Время ожидания перед автоматическим выбором первого ответа"
+		},
 		"execute": {
 			"label": "Выполнение",
 			"description": "Автоматически выполнять разрешённые команды терминала без необходимости одобрения",

+ 3 - 1
webview-ui/src/i18n/locales/tr/chat.json

@@ -234,7 +234,9 @@
 		"tokens": "token"
 	},
 	"followUpSuggest": {
-		"copyToInput": "Giriş alanına kopyala (veya Shift + tıklama)"
+		"copyToInput": "Giriş alanına kopyala (veya Shift + tıklama)",
+		"autoSelectCountdown": "{{count}}s içinde otomatik seçilecek",
+		"countdownDisplay": "{{count}}sn"
 	},
 	"announcement": {
 		"title": "🎉 Roo Code {{version}} Yayınlandı",

+ 5 - 0
webview-ui/src/i18n/locales/tr/settings.json

@@ -114,6 +114,11 @@
 			"label": "Alt Görevler",
 			"description": "Onay gerektirmeden alt görevlerin oluşturulmasına ve tamamlanmasına izin ver"
 		},
+		"followupQuestions": {
+			"label": "Soru",
+			"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"
+		},
 		"execute": {
 			"label": "Yürüt",
 			"description": "Onay gerektirmeden otomatik olarak izin verilen terminal komutlarını yürüt",

+ 3 - 1
webview-ui/src/i18n/locales/vi/chat.json

@@ -234,7 +234,9 @@
 		"tokens": "token"
 	},
 	"followUpSuggest": {
-		"copyToInput": "Sao chép vào ô nhập liệu (hoặc Shift + nhấp chuột)"
+		"copyToInput": "Sao chép vào ô nhập liệu (hoặc Shift + nhấp chuột)",
+		"autoSelectCountdown": "Tự động chọn sau {{count}}s",
+		"countdownDisplay": "{{count}}s"
 	},
 	"announcement": {
 		"title": "🎉 Roo Code {{version}} Đã phát hành",

+ 5 - 0
webview-ui/src/i18n/locales/vi/settings.json

@@ -114,6 +114,11 @@
 			"label": "Công việc phụ",
 			"description": "Cho phép tạo và hoàn thành các công việc phụ mà không cần phê duyệt"
 		},
+		"followupQuestions": {
+			"label": "Câu hỏi",
+			"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"
+		},
 		"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",

+ 3 - 1
webview-ui/src/i18n/locales/zh-CN/chat.json

@@ -234,7 +234,9 @@
 		"tokens": "tokens"
 	},
 	"followUpSuggest": {
-		"copyToInput": "复制到输入框(或按住Shift点击)"
+		"copyToInput": "复制到输入框(或按住Shift点击)",
+		"autoSelectCountdown": "{{count}}秒后自动选择",
+		"countdownDisplay": "{{count}}秒"
 	},
 	"announcement": {
 		"title": "🎉 Roo Code {{version}} 已发布",

+ 5 - 0
webview-ui/src/i18n/locales/zh-CN/settings.json

@@ -114,6 +114,11 @@
 			"label": "子任务",
 			"description": "允许创建和完成子任务而无需批准"
 		},
+		"followupQuestions": {
+			"label": "问题",
+			"description": "在配置的超时时间后自动选择后续问题的第一个建议答案",
+			"timeoutLabel": "自动选择第一个答案前的等待时间"
+		},
 		"execute": {
 			"label": "执行",
 			"description": "自动执行白名单中的命令而无需批准",

+ 3 - 1
webview-ui/src/i18n/locales/zh-TW/chat.json

@@ -234,7 +234,9 @@
 		"tokens": "tokens"
 	},
 	"followUpSuggest": {
-		"copyToInput": "複製到輸入框(或按住 Shift 並點選)"
+		"copyToInput": "複製到輸入框(或按住 Shift 並點選)",
+		"autoSelectCountdown": "{{count}}秒後自動選擇",
+		"countdownDisplay": "{{count}}秒"
 	},
 	"announcement": {
 		"title": "🎉 Roo Code {{version}} 已發布",

+ 5 - 0
webview-ui/src/i18n/locales/zh-TW/settings.json

@@ -114,6 +114,11 @@
 			"label": "子工作",
 			"description": "允許建立和完成子工作而無需核准"
 		},
+		"followupQuestions": {
+			"label": "問題",
+			"description": "在設定的逾時時間後自動選擇後續問題的第一個建議答案",
+			"timeoutLabel": "自動選擇第一個答案前的等待時間"
+		},
 		"execute": {
 			"label": "執行",
 			"description": "自動執行允許的終端機命令而無需核准",