Browse Source

Fix issues with subtasks attempting completion along with commands (#3156)

Matt Rubens 8 months ago
parent
commit
756c0a0014

+ 5 - 0
.changeset/dull-flowers-trade.md

@@ -0,0 +1,5 @@
+---
+"roo-cline": patch
+---
+
+Fix auto-approvals

+ 1 - 1
src/core/Cline.ts

@@ -161,7 +161,7 @@ export class Cline extends EventEmitter<ClineEvents> {
 	private askResponse?: ClineAskResponse
 	private askResponseText?: string
 	private askResponseImages?: string[]
-	private lastMessageTs?: number
+	public lastMessageTs?: number
 
 	// Not private since it needs to be accessible by tools.
 	consecutiveMistakeCount: number = 0

+ 3 - 3
src/core/tools/attemptCompletionTool.ts

@@ -76,13 +76,13 @@ export async function attemptCompletionTool(
 				}
 
 				// Complete command message.
-				const executionId = Date.now().toString()
-				const didApprove = await askApproval("command", command, { id: executionId })
+				const didApprove = await askApproval("command", command)
 
 				if (!didApprove) {
 					return
 				}
 
+				const executionId = cline.lastMessageTs?.toString() ?? Date.now().toString()
 				const options: ExecuteCommandOptions = { executionId, command }
 				const [userRejected, execCommandResult] = await executeCommand(cline, options)
 
@@ -108,7 +108,7 @@ export async function attemptCompletionTool(
 				}
 
 				// tell the provider to remove the current subtask and resume the previous task in the stack
-				await cline.providerRef.deref()?.finishSubTask(lastMessage?.text ?? "")
+				await cline.providerRef.deref()?.finishSubTask(result)
 				return
 			}
 

+ 2 - 2
src/core/tools/executeCommandTool.ts

@@ -48,14 +48,14 @@ export async function executeCommandTool(
 
 			cline.consecutiveMistakeCount = 0
 
-			const executionId = Date.now().toString()
 			command = unescapeHtmlEntities(command) // Unescape HTML entities.
-			const didApprove = await askApproval("command", command, { id: executionId })
+			const didApprove = await askApproval("command", command)
 
 			if (!didApprove) {
 				return
 			}
 
+			const executionId = cline.lastMessageTs?.toString() ?? Date.now().toString()
 			const clineProvider = await cline.providerRef.deref()
 			const clineProviderState = await clineProvider?.getState()
 			const { terminalOutputLineLimit = 500, terminalShellIntegrationDisabled = false } = clineProviderState ?? {}

+ 0 - 2
src/exports/roo-code.d.ts

@@ -353,7 +353,6 @@ type ClineMessage = {
 		| undefined
 	progressStatus?:
 		| {
-				id?: string | undefined
 				icon?: string | undefined
 				text?: string | undefined
 		  }
@@ -429,7 +428,6 @@ type RooCodeEvents = {
 					| undefined
 				progressStatus?:
 					| {
-							id?: string | undefined
 							icon?: string | undefined
 							text?: string | undefined
 					  }

+ 0 - 2
src/exports/types.ts

@@ -358,7 +358,6 @@ type ClineMessage = {
 		| undefined
 	progressStatus?:
 		| {
-				id?: string | undefined
 				icon?: string | undefined
 				text?: string | undefined
 		  }
@@ -438,7 +437,6 @@ type RooCodeEvents = {
 					| undefined
 				progressStatus?:
 					| {
-							id?: string | undefined
 							icon?: string | undefined
 							text?: string | undefined
 					  }

+ 0 - 1
src/schemas/index.ts

@@ -817,7 +817,6 @@ export type ClineSay = z.infer<typeof clineSaySchema>
  */
 
 export const toolProgressStatusSchema = z.object({
-	id: z.string().optional(),
 	icon: z.string().optional(),
 	text: z.string().optional(),
 })

+ 1 - 1
webview-ui/src/components/chat/ChatRow.tsx

@@ -985,7 +985,7 @@ export const ChatRowContent = ({
 								{icon}
 								{title}
 							</div>
-							<CommandExecution executionId={message.progressStatus?.id} text={message.text} />
+							<CommandExecution executionId={message.ts.toString()} text={message.text} />
 						</>
 					)
 				case "use_mcp_server":

+ 21 - 5
webview-ui/src/components/chat/ChatView.tsx

@@ -735,7 +735,9 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
 
 	const isAutoApproved = useCallback(
 		(message: ClineMessage | undefined) => {
-			if (!autoApprovalEnabled || !message || message.type !== "ask") return false
+			if (!autoApprovalEnabled || !message || message.type !== "ask") {
+				return false
+			}
 
 			if (message.ask === "browser_action_launch") {
 				return alwaysAllowBrowser
@@ -749,7 +751,8 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
 				return alwaysAllowExecute && isAllowedCommand(message)
 			}
 
-			// For read/write operations, check if it's outside workspace and if we have permission for that
+			// For read/write operations, check if it's outside workspace and if
+			// we have permission for that.
 			if (message.ask === "tool") {
 				let tool: any = {}
 
@@ -1114,13 +1117,26 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
 		}
 
 		const autoApprove = async () => {
-			if (isAutoApproved(lastMessage)) {
+			if (lastMessage?.ask && isAutoApproved(lastMessage)) {
+				// Note that `isAutoApproved` can only return true if
+				// lastMessage is an ask of type "browser_action_launch",
+				// "use_mcp_server", "command", or "tool".
+
 				// Add delay for write operations.
-				if (lastMessage?.ask === "tool" && isWriteToolAction(lastMessage)) {
+				if (lastMessage.ask === "tool" && isWriteToolAction(lastMessage)) {
 					await new Promise((resolve) => setTimeout(resolve, writeDelayMs))
 				}
 
-				handlePrimaryButtonClick()
+				vscode.postMessage({ type: "askResponse", askResponse: "yesButtonClicked" })
+
+				// This is copied from `handlePrimaryButtonClick`, which we used
+				// to call from `autoApprove`. I'm not sure how many of these
+				// things are actually needed.
+				setInputValue("")
+				setSelectedImages([])
+				setTextAreaDisabled(true)
+				setClineAsk(undefined)
+				setEnableButtons(false)
 			}
 		}
 		autoApprove()

+ 1 - 5
webview-ui/src/components/chat/CommandExecution.tsx

@@ -14,7 +14,7 @@ import { cn } from "@src/lib/utils"
 import { Button } from "@src/components/ui"
 
 interface CommandExecutionProps {
-	executionId?: string
+	executionId: string
 	text?: string
 }
 
@@ -36,10 +36,6 @@ export const CommandExecution = ({ executionId, text }: CommandExecutionProps) =
 
 	const onMessage = useCallback(
 		(event: MessageEvent) => {
-			if (!executionId) {
-				return
-			}
-
 			const message: ExtensionMessage = event.data
 
 			if (message.type === "commandExecutionStatus") {