Auto approve follow up questions (#5296)

This commit is contained in:
Will Li 2025-07-01 11:43:16 -07:00 committed by GitHub
parent a348a2ae9a
commit 781beeb30f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
51 changed files with 453 additions and 68 deletions

View file

@ -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>

View file

@ -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,

View file

@ -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"

View file

@ -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,

View file

@ -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()

View file

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

View file

@ -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,
],
)

View file

@ -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}
/>
</>
)

View file

@ -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(

View file

@ -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 />

View file

@ -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">

View file

@ -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 || "")}>

View file

@ -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}
/>

View file

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

View file

@ -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 })),

View file

@ -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",

View file

@ -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ó",

View file

@ -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",

View file

@ -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",

View file

@ -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:",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

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

View file

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

View file

@ -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:",

View file

@ -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",

View file

@ -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}}",

View file

@ -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",

View file

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

View file

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

View file

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

View file

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

View file

@ -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:",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

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

View file

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

View file

@ -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ı",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

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

View file

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

View file

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

View file

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