mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
Auto approve follow up questions (#5296)
This commit is contained in:
parent
a348a2ae9a
commit
781beeb30f
51 changed files with 453 additions and 68 deletions
41
packages/types/src/followup.ts
Normal file
41
packages/types/src/followup.ts
Normal 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>
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ export interface WebviewMessage {
|
|||
| "alwaysAllowWriteOutsideWorkspace"
|
||||
| "alwaysAllowWriteProtected"
|
||||
| "alwaysAllowExecute"
|
||||
| "alwaysAllowFollowupQuestions"
|
||||
| "followupAutoApproveTimeoutMs"
|
||||
| "webviewDidLaunch"
|
||||
| "newTask"
|
||||
| "askResponse"
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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 />
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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 || "")}>
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ describe("AutoApproveToggle", () => {
|
|||
alwaysAllowModeSwitch: true,
|
||||
alwaysAllowSubtasks: false,
|
||||
alwaysAllowExecute: true,
|
||||
alwaysAllowFollowupQuestions: false,
|
||||
onToggle: mockOnToggle,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 })),
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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ó",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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:",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -234,7 +234,9 @@
|
|||
"tokens": "टोकन"
|
||||
},
|
||||
"followUpSuggest": {
|
||||
"copyToInput": "इनपुट में कॉपी करें (या Shift + क्लिक)"
|
||||
"copyToInput": "इनपुट में कॉपी करें (या Shift + क्लिक)",
|
||||
"autoSelectCountdown": "{{count}}s में स्वचालित रूप से चयन हो रहा है",
|
||||
"countdownDisplay": "{{count}}सेकंड"
|
||||
},
|
||||
"announcement": {
|
||||
"title": "🎉 Roo Code {{version}} रिलीज़ हुआ",
|
||||
|
|
|
|||
|
|
@ -114,6 +114,11 @@
|
|||
"label": "उप-कार्य",
|
||||
"description": "अनुमोदन की आवश्यकता के बिना उप-कार्यों के निर्माण और पूर्णता की अनुमति दें"
|
||||
},
|
||||
"followupQuestions": {
|
||||
"label": "प्रश्न",
|
||||
"description": "कॉन्फ़िगर किए गए टाइमआउट के बाद अनुवर्ती प्रश्नों के लिए पहले सुझाए गए उत्तर को स्वचालित रूप से चुनें",
|
||||
"timeoutLabel": "पहले उत्तर को स्वचालित रूप से चुनने से पहले प्रतीक्षा करने का समय"
|
||||
},
|
||||
"execute": {
|
||||
"label": "निष्पादित करें",
|
||||
"description": "अनुमोदन की आवश्यकता के बिना स्वचालित रूप से अनुमत टर्मिनल कमांड निष्पादित करें",
|
||||
|
|
|
|||
|
|
@ -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:",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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}}",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -234,7 +234,9 @@
|
|||
"tokens": "トークン"
|
||||
},
|
||||
"followUpSuggest": {
|
||||
"copyToInput": "入力欄にコピー(またはShift + クリック)"
|
||||
"copyToInput": "入力欄にコピー(またはShift + クリック)",
|
||||
"autoSelectCountdown": "{{count}}秒後に自動選択します",
|
||||
"countdownDisplay": "{{count}}秒"
|
||||
},
|
||||
"announcement": {
|
||||
"title": "🎉 Roo Code {{version}} リリース",
|
||||
|
|
|
|||
|
|
@ -114,6 +114,11 @@
|
|||
"label": "サブタスク",
|
||||
"description": "承認なしでサブタスクの作成と完了を許可"
|
||||
},
|
||||
"followupQuestions": {
|
||||
"label": "質問",
|
||||
"description": "設定された時間が経過すると、フォローアップ質問の最初の提案回答を自動的に選択します",
|
||||
"timeoutLabel": "最初の回答を自動選択するまでの待機時間"
|
||||
},
|
||||
"execute": {
|
||||
"label": "実行",
|
||||
"description": "承認なしで自動的に許可されたターミナルコマンドを実行",
|
||||
|
|
|
|||
|
|
@ -234,7 +234,9 @@
|
|||
"tokens": "토큰"
|
||||
},
|
||||
"followUpSuggest": {
|
||||
"copyToInput": "입력창에 복사 (또는 Shift + 클릭)"
|
||||
"copyToInput": "입력창에 복사 (또는 Shift + 클릭)",
|
||||
"autoSelectCountdown": "{{count}}초 후 자동 선택",
|
||||
"countdownDisplay": "{{count}}초"
|
||||
},
|
||||
"announcement": {
|
||||
"title": "🎉 Roo Code {{version}} 출시",
|
||||
|
|
|
|||
|
|
@ -114,6 +114,11 @@
|
|||
"label": "하위 작업",
|
||||
"description": "승인 없이 하위 작업 생성 및 완료 허용"
|
||||
},
|
||||
"followupQuestions": {
|
||||
"label": "질문",
|
||||
"description": "설정된 시간이 지나면 후속 질문에 대한 첫 번째 제안 답변을 자동으로 선택합니다",
|
||||
"timeoutLabel": "첫 번째 답변을 자동 선택하기 전 대기 시간"
|
||||
},
|
||||
"execute": {
|
||||
"label": "실행",
|
||||
"description": "승인 없이 자동으로 허용된 터미널 명령 실행",
|
||||
|
|
|
|||
|
|
@ -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:",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -244,7 +244,9 @@
|
|||
"tokens": "токены"
|
||||
},
|
||||
"followUpSuggest": {
|
||||
"copyToInput": "Скопировать во ввод (то же, что shift + клик)"
|
||||
"copyToInput": "Скопировать во ввод (то же, что shift + клик)",
|
||||
"autoSelectCountdown": "Автовыбор через {{count}}с",
|
||||
"countdownDisplay": "{{count}}с"
|
||||
},
|
||||
"browser": {
|
||||
"rooWantsToUse": "Roo хочет использовать браузер:",
|
||||
|
|
|
|||
|
|
@ -114,6 +114,11 @@
|
|||
"label": "Подзадачи",
|
||||
"description": "Разрешить создание и выполнение подзадач без необходимости одобрения"
|
||||
},
|
||||
"followupQuestions": {
|
||||
"label": "Вопрос",
|
||||
"description": "Автоматически выбирать первый предложенный ответ на дополнительные вопросы после настроенного тайм-аута",
|
||||
"timeoutLabel": "Время ожидания перед автоматическим выбором первого ответа"
|
||||
},
|
||||
"execute": {
|
||||
"label": "Выполнение",
|
||||
"description": "Автоматически выполнять разрешённые команды терминала без необходимости одобрения",
|
||||
|
|
|
|||
|
|
@ -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ı",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -234,7 +234,9 @@
|
|||
"tokens": "tokens"
|
||||
},
|
||||
"followUpSuggest": {
|
||||
"copyToInput": "复制到输入框(或按住Shift点击)"
|
||||
"copyToInput": "复制到输入框(或按住Shift点击)",
|
||||
"autoSelectCountdown": "{{count}}秒后自动选择",
|
||||
"countdownDisplay": "{{count}}秒"
|
||||
},
|
||||
"announcement": {
|
||||
"title": "🎉 Roo Code {{version}} 已发布",
|
||||
|
|
|
|||
|
|
@ -114,6 +114,11 @@
|
|||
"label": "子任务",
|
||||
"description": "允许创建和完成子任务而无需批准"
|
||||
},
|
||||
"followupQuestions": {
|
||||
"label": "问题",
|
||||
"description": "在配置的超时时间后自动选择后续问题的第一个建议答案",
|
||||
"timeoutLabel": "自动选择第一个答案前的等待时间"
|
||||
},
|
||||
"execute": {
|
||||
"label": "执行",
|
||||
"description": "自动执行白名单中的命令而无需批准",
|
||||
|
|
|
|||
|
|
@ -234,7 +234,9 @@
|
|||
"tokens": "tokens"
|
||||
},
|
||||
"followUpSuggest": {
|
||||
"copyToInput": "複製到輸入框(或按住 Shift 並點選)"
|
||||
"copyToInput": "複製到輸入框(或按住 Shift 並點選)",
|
||||
"autoSelectCountdown": "{{count}}秒後自動選擇",
|
||||
"countdownDisplay": "{{count}}秒"
|
||||
},
|
||||
"announcement": {
|
||||
"title": "🎉 Roo Code {{version}} 已發布",
|
||||
|
|
|
|||
|
|
@ -114,6 +114,11 @@
|
|||
"label": "子工作",
|
||||
"description": "允許建立和完成子工作而無需核准"
|
||||
},
|
||||
"followupQuestions": {
|
||||
"label": "問題",
|
||||
"description": "在設定的逾時時間後自動選擇後續問題的第一個建議答案",
|
||||
"timeoutLabel": "自動選擇第一個答案前的等待時間"
|
||||
},
|
||||
"execute": {
|
||||
"label": "執行",
|
||||
"description": "自動執行允許的終端機命令而無需核准",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue