@@ -145,7 +145,10 @@ export async function presentAssistantMessage(cline: Task) {
145145 const toolCallId = mcpBlock . id
146146 const toolProtocol = TOOL_PROTOCOL . NATIVE // MCP tools in native mode always use native protocol
147147
148- const pushToolResult = ( content : ToolResponse ) => {
148+ // Store approval feedback to merge into tool result (GitHub #10465)
149+ let approvalFeedback : { text : string ; images ?: string [ ] } | undefined
150+
151+ const pushToolResult = ( content : ToolResponse , feedbackImages ?: string [ ] ) => {
149152 if ( hasToolResult ) {
150153 console . warn (
151154 `[presentAssistantMessage] Skipping duplicate tool_result for mcp_tool_use: ${ toolCallId } ` ,
@@ -166,6 +169,18 @@ export async function presentAssistantMessage(cline: Task) {
166169 "(tool did not return anything)"
167170 }
168171
172+ // Merge approval feedback into tool result (GitHub #10465)
173+ if ( approvalFeedback ) {
174+ const feedbackText = formatResponse . toolApprovedWithFeedback ( approvalFeedback . text , toolProtocol )
175+ resultContent = `${ feedbackText } \n\n${ resultContent } `
176+
177+ // Add feedback images to the image blocks
178+ if ( approvalFeedback . images ) {
179+ const feedbackImageBlocks = formatResponse . imageBlocks ( approvalFeedback . images )
180+ imageBlocks = [ ...feedbackImageBlocks , ...imageBlocks ]
181+ }
182+ }
183+
169184 if ( toolCallId ) {
170185 cline . pushToolResultToUserContent ( {
171186 type : "tool_result" ,
@@ -214,11 +229,12 @@ export async function presentAssistantMessage(cline: Task) {
214229 return false
215230 }
216231
232+ // Store approval feedback to be merged into tool result (GitHub #10465)
233+ // Don't push it as a separate tool_result here - that would create duplicates.
234+ // The tool will call pushToolResult, which will merge the feedback into the actual result.
217235 if ( text ) {
218236 await cline . say ( "user_feedback" , text , images )
219- pushToolResult (
220- formatResponse . toolResult ( formatResponse . toolApprovedWithFeedback ( text , toolProtocol ) , images ) ,
221- )
237+ approvalFeedback = { text, images }
222238 }
223239
224240 return true
@@ -501,6 +517,9 @@ export async function presentAssistantMessage(cline: Task) {
501517 // Previously resolved from experiments.isEnabled(..., EXPERIMENT_IDS.MULTIPLE_NATIVE_TOOL_CALLS)
502518 const isMultipleNativeToolCallsEnabled = false
503519
520+ // Store approval feedback to merge into tool result (GitHub #10465)
521+ let approvalFeedback : { text : string ; images ?: string [ ] } | undefined
522+
504523 const pushToolResult = ( content : ToolResponse ) => {
505524 if ( toolProtocol === TOOL_PROTOCOL . NATIVE ) {
506525 // For native protocol, only allow ONE tool_result per tool call
@@ -529,6 +548,21 @@ export async function presentAssistantMessage(cline: Task) {
529548 "(tool did not return anything)"
530549 }
531550
551+ // Merge approval feedback into tool result (GitHub #10465)
552+ if ( approvalFeedback ) {
553+ const feedbackText = formatResponse . toolApprovedWithFeedback (
554+ approvalFeedback . text ,
555+ toolProtocol ,
556+ )
557+ resultContent = `${ feedbackText } \n\n${ resultContent } `
558+
559+ // Add feedback images to the image blocks
560+ if ( approvalFeedback . images ) {
561+ const feedbackImageBlocks = formatResponse . imageBlocks ( approvalFeedback . images )
562+ imageBlocks = [ ...feedbackImageBlocks , ...imageBlocks ]
563+ }
564+ }
565+
532566 // Add tool_result with text content only
533567 cline . pushToolResultToUserContent ( {
534568 type : "tool_result" ,
@@ -544,15 +578,44 @@ export async function presentAssistantMessage(cline: Task) {
544578 hasToolResult = true
545579 } else {
546580 // For XML protocol, add as text blocks (legacy behavior)
581+ let resultContent : string
582+
583+ if ( typeof content === "string" ) {
584+ resultContent = content || "(tool did not return anything)"
585+ } else {
586+ const textBlocks = content . filter ( ( item ) => item . type === "text" )
587+ resultContent =
588+ textBlocks . map ( ( item ) => ( item as Anthropic . TextBlockParam ) . text ) . join ( "\n" ) ||
589+ "(tool did not return anything)"
590+ }
591+
592+ // Merge approval feedback into tool result (GitHub #10465)
593+ if ( approvalFeedback ) {
594+ const feedbackText = formatResponse . toolApprovedWithFeedback (
595+ approvalFeedback . text ,
596+ toolProtocol ,
597+ )
598+ resultContent = `${ feedbackText } \n\n${ resultContent } `
599+ }
600+
547601 cline . userMessageContent . push ( { type : "text" , text : `${ toolDescription ( ) } Result:` } )
548602
549603 if ( typeof content === "string" ) {
550604 cline . userMessageContent . push ( {
551605 type : "text" ,
552- text : content || "(tool did not return anything)" ,
606+ text : resultContent ,
553607 } )
554608 } else {
555- cline . userMessageContent . push ( ...content )
609+ // Add text content with merged feedback
610+ cline . userMessageContent . push ( {
611+ type : "text" ,
612+ text : resultContent ,
613+ } )
614+ // Add any images from the tool result
615+ const imageBlocks = content . filter ( ( item ) => item . type === "image" )
616+ if ( imageBlocks . length > 0 ) {
617+ cline . userMessageContent . push ( ...imageBlocks )
618+ }
556619 }
557620 }
558621
@@ -603,12 +666,12 @@ export async function presentAssistantMessage(cline: Task) {
603666 return false
604667 }
605668
606- // Handle yesButtonClicked with text.
669+ // Store approval feedback to be merged into tool result (GitHub #10465)
670+ // Don't push it as a separate tool_result here - that would create duplicates.
671+ // The tool will call pushToolResult, which will merge the feedback into the actual result.
607672 if ( text ) {
608673 await cline . say ( "user_feedback" , text , images )
609- pushToolResult (
610- formatResponse . toolResult ( formatResponse . toolApprovedWithFeedback ( text , toolProtocol ) , images ) ,
611- )
674+ approvalFeedback = { text, images }
612675 }
613676
614677 return true
0 commit comments