diff --git a/src/core/Cline.ts b/src/core/Cline.ts
index e2911976d..a31c02a2e 100644
--- a/src/core/Cline.ts
+++ b/src/core/Cline.ts
@@ -1085,35 +1085,23 @@ export class Cline {
const askApproval = async (type: ClineAsk, partialMessage?: string) => {
const { response, text, images } = await this.ask(type, partialMessage, false)
if (response !== "yesButtonClicked") {
- if (response === "messageResponse") {
+ // Handle both messageResponse and noButtonClicked with text
+ if (text) {
await this.say("user_feedback", text, images)
pushToolResult(
formatResponse.toolResult(formatResponse.toolDeniedWithFeedback(text), images),
)
- // this.userMessageContent.push({
- // type: "text",
- // text: `${toolDescription()}`,
- // })
- // this.toolResults.push({
- // type: "tool_result",
- // tool_use_id: toolUseId,
- // content: this.formatToolResponseWithImages(
- // await this.formatToolDeniedFeedback(text),
- // images
- // ),
- // })
- this.didRejectTool = true
- return false
+ } else {
+ pushToolResult(formatResponse.toolDenied())
}
- pushToolResult(formatResponse.toolDenied())
- // this.toolResults.push({
- // type: "tool_result",
- // tool_use_id: toolUseId,
- // content: await this.formatToolDenied(),
- // })
this.didRejectTool = true
return false
}
+ // Handle yesButtonClicked with text
+ if (text) {
+ await this.say("user_feedback", text, images)
+ pushToolResult(formatResponse.toolResult(formatResponse.toolApprovedWithFeedback(text), images))
+ }
return true
}
diff --git a/src/core/prompts/responses.ts b/src/core/prompts/responses.ts
index 05f33ba71..f06dff3d8 100644
--- a/src/core/prompts/responses.ts
+++ b/src/core/prompts/responses.ts
@@ -8,6 +8,9 @@ export const formatResponse = {
toolDeniedWithFeedback: (feedback?: string) =>
`The user denied this operation and provided the following feedback:\n\n${feedback}\n`,
+ toolApprovedWithFeedback: (feedback?: string) =>
+ `The user approved this operation and provided the following context:\n\n${feedback}\n`,
+
toolError: (error?: string) => `The tool execution failed with the following error:\n\n${error}\n`,
noToolsUsed: () =>
diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx
index 0a32eef77..7f89c434e 100644
--- a/webview-ui/src/components/chat/ChatView.tsx
+++ b/webview-ui/src/components/chat/ChatView.tsx
@@ -337,56 +337,96 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
/*
This logic depends on the useEffect[messages] above to set clineAsk, after which buttons are shown and we then send an askResponse to the extension.
*/
- const handlePrimaryButtonClick = useCallback(() => {
- switch (clineAsk) {
- case "api_req_failed":
- case "command":
- case "command_output":
- case "tool":
- case "browser_action_launch":
- case "use_mcp_server":
- case "resume_task":
- case "mistake_limit_reached":
- vscode.postMessage({ type: "askResponse", askResponse: "yesButtonClicked" })
- break
- case "completion_result":
- case "resume_completed_task":
- // extension waiting for feedback. but we can just present a new task button
- startNewTask()
- break
- }
- setTextAreaDisabled(true)
- setClineAsk(undefined)
- setEnableButtons(false)
- disableAutoScrollRef.current = false
- }, [clineAsk, startNewTask])
-
- const handleSecondaryButtonClick = useCallback(() => {
- if (isStreaming) {
- vscode.postMessage({ type: "cancelTask" })
- setDidClickCancel(true)
- return
- }
+ const handlePrimaryButtonClick = useCallback(
+ (text?: string, images?: string[]) => {
+ const trimmedInput = text?.trim()
+ switch (clineAsk) {
+ case "api_req_failed":
+ case "command":
+ case "command_output":
+ case "tool":
+ case "browser_action_launch":
+ case "use_mcp_server":
+ case "resume_task":
+ case "mistake_limit_reached":
+ // Only send text/images if they exist
+ if (trimmedInput || (images && images.length > 0)) {
+ vscode.postMessage({
+ type: "askResponse",
+ askResponse: "yesButtonClicked",
+ text: trimmedInput,
+ images: images,
+ })
+ } else {
+ vscode.postMessage({
+ type: "askResponse",
+ askResponse: "yesButtonClicked",
+ })
+ }
+ // Clear input state after sending
+ setInputValue("")
+ setSelectedImages([])
+ break
+ case "completion_result":
+ case "resume_completed_task":
+ // extension waiting for feedback. but we can just present a new task button
+ startNewTask()
+ break
+ }
+ setTextAreaDisabled(true)
+ setClineAsk(undefined)
+ setEnableButtons(false)
+ disableAutoScrollRef.current = false
+ },
+ [clineAsk, startNewTask],
+ )
- switch (clineAsk) {
- case "api_req_failed":
- case "mistake_limit_reached":
- case "resume_task":
- startNewTask()
- break
- case "command":
- case "tool":
- case "browser_action_launch":
- case "use_mcp_server":
- // responds to the API with a "This operation failed" and lets it try again
- vscode.postMessage({ type: "askResponse", askResponse: "noButtonClicked" })
- break
- }
- setTextAreaDisabled(true)
- setClineAsk(undefined)
- setEnableButtons(false)
- disableAutoScrollRef.current = false
- }, [clineAsk, startNewTask, isStreaming])
+ const handleSecondaryButtonClick = useCallback(
+ (text?: string, images?: string[]) => {
+ const trimmedInput = text?.trim()
+ if (isStreaming) {
+ vscode.postMessage({ type: "cancelTask" })
+ setDidClickCancel(true)
+ return
+ }
+
+ switch (clineAsk) {
+ case "api_req_failed":
+ case "mistake_limit_reached":
+ case "resume_task":
+ startNewTask()
+ break
+ case "command":
+ case "tool":
+ case "browser_action_launch":
+ case "use_mcp_server":
+ // Only send text/images if they exist
+ if (trimmedInput || (images && images.length > 0)) {
+ vscode.postMessage({
+ type: "askResponse",
+ askResponse: "noButtonClicked",
+ text: trimmedInput,
+ images: images,
+ })
+ } else {
+ // responds to the API with a "This operation failed" and lets it try again
+ vscode.postMessage({
+ type: "askResponse",
+ askResponse: "noButtonClicked",
+ })
+ }
+ // Clear input state after sending
+ setInputValue("")
+ setSelectedImages([])
+ break
+ }
+ setTextAreaDisabled(true)
+ setClineAsk(undefined)
+ setEnableButtons(false)
+ disableAutoScrollRef.current = false
+ },
+ [clineAsk, startNewTask, isStreaming],
+ )
const handleTaskCloseButtonClick = useCallback(() => {
startNewTask()
@@ -430,10 +470,10 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
handleSendMessage(message.text ?? "", message.images ?? [])
break
case "primaryButtonClick":
- handlePrimaryButtonClick()
+ handlePrimaryButtonClick(message.text ?? "", message.images ?? [])
break
case "secondaryButtonClick":
- handleSecondaryButtonClick()
+ handleSecondaryButtonClick(message.text ?? "", message.images ?? [])
break
}
}
@@ -1038,7 +1078,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
flex: secondaryButtonText ? 1 : 2,
marginRight: secondaryButtonText ? "6px" : "0",
}}
- onClick={handlePrimaryButtonClick}>
+ onClick={(e) => handlePrimaryButtonClick(inputValue, selectedImages)}>
{primaryButtonText}
)}
@@ -1050,7 +1090,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
flex: isStreaming ? 2 : 1,
marginLeft: isStreaming ? 0 : "6px",
}}
- onClick={handleSecondaryButtonClick}>
+ onClick={(e) => handleSecondaryButtonClick(inputValue, selectedImages)}>
{isStreaming ? "Cancel" : secondaryButtonText}
)}