@@ -7,6 +7,10 @@ import { memo, useEffect, useRef, useState } from "react";
77import { WaveUIMessagePart } from "./aitypes" ;
88import { WaveAIModel } from "./waveai-model" ;
99
10+ function getEffectiveApprovalStatus ( baseApproval : string , isStreaming : boolean ) : string {
11+ return ! isStreaming && baseApproval === "needs-approval" ? "timeout" : baseApproval ;
12+ }
13+
1014interface AIToolApprovalButtonsProps {
1115 count : number ;
1216 onApprove : ( ) => void ;
@@ -73,10 +77,10 @@ interface AIToolUseBatchProps {
7377const AIToolUseBatch = memo ( ( { parts, isStreaming } : AIToolUseBatchProps ) => {
7478 const [ userApprovalOverride , setUserApprovalOverride ] = useState < string | null > ( null ) ;
7579
80+ // All parts in a batch have the same approval status (enforced by grouping logic in AIToolUseGroup)
7681 const firstTool = parts [ 0 ] . data ;
7782 const baseApproval = userApprovalOverride || firstTool . approval ;
78- const effectiveApproval = ! isStreaming && baseApproval === "needs-approval" ? "timeout" : baseApproval ;
79- const allNeedApproval = parts . every ( ( p ) => ( userApprovalOverride || p . data . approval ) === "needs-approval" ) ;
83+ const effectiveApproval = getEffectiveApprovalStatus ( baseApproval , isStreaming ) ;
8084
8185 useEffect ( ( ) => {
8286 if ( ! isStreaming || effectiveApproval !== "needs-approval" ) return ;
@@ -113,7 +117,7 @@ const AIToolUseBatch = memo(({ parts, isStreaming }: AIToolUseBatchProps) => {
113117 < AIToolUseBatchItem key = { idx } part = { part } effectiveApproval = { effectiveApproval } />
114118 ) ) }
115119 </ div >
116- { allNeedApproval && effectiveApproval === "needs-approval" && (
120+ { effectiveApproval === "needs-approval" && (
117121 < AIToolApprovalButtons count = { parts . length } onApprove = { handleApprove } onDeny = { handleDeny } />
118122 ) }
119123 </ div >
@@ -139,7 +143,7 @@ const AIToolUse = memo(({ part, isStreaming }: AIToolUseProps) => {
139143 toolData . status === "completed" ? "text-success" : toolData . status === "error" ? "text-error" : "text-gray-400" ;
140144
141145 const baseApproval = userApprovalOverride || toolData . approval ;
142- const effectiveApproval = ! isStreaming && baseApproval === "needs-approval" ? "timeout" : baseApproval ;
146+ const effectiveApproval = getEffectiveApprovalStatus ( baseApproval , isStreaming ) ;
143147
144148 const isFileWriteTool = toolData . toolname === "write_text_file" || toolData . toolname === "edit_text_file" ;
145149
@@ -258,23 +262,40 @@ export const AIToolUseGroup = memo(({ parts, isStreaming }: AIToolUseGroupProps)
258262 return toolName === "read_text_file" || toolName === "read_dir" ;
259263 } ;
260264
261- const groupedItems : ToolGroupItem [ ] = [ ] ;
262- let currentBatch : Array < WaveUIMessagePart & { type : "data-tooluse" } > = [ ] ;
265+ const needsApproval = ( part : WaveUIMessagePart & { type : "data-tooluse" } ) => {
266+ return getEffectiveApprovalStatus ( part . data ?. approval , isStreaming ) === "needs-approval" ;
267+ } ;
268+
269+ const readFileNeedsApproval : Array < WaveUIMessagePart & { type : "data-tooluse" } > = [ ] ;
270+ const readFileOther : Array < WaveUIMessagePart & { type : "data-tooluse" } > = [ ] ;
263271
264272 for ( const part of parts ) {
265273 if ( isFileOp ( part ) ) {
266- currentBatch . push ( part ) ;
267- } else {
268- if ( currentBatch . length > 0 ) {
269- groupedItems . push ( { type : "batch" , parts : currentBatch } ) ;
270- currentBatch = [ ] ;
274+ if ( needsApproval ( part ) ) {
275+ readFileNeedsApproval . push ( part ) ;
276+ } else {
277+ readFileOther . push ( part ) ;
271278 }
272- groupedItems . push ( { type : "single" , part } ) ;
273279 }
274280 }
275281
276- if ( currentBatch . length > 0 ) {
277- groupedItems . push ( { type : "batch" , parts : currentBatch } ) ;
282+ const groupedItems : ToolGroupItem [ ] = [ ] ;
283+ let addedApprovalBatch = false ;
284+ let addedOtherBatch = false ;
285+
286+ for ( const part of parts ) {
287+ const isFileOpPart = isFileOp ( part ) ;
288+ const partNeedsApproval = needsApproval ( part ) ;
289+
290+ if ( isFileOpPart && partNeedsApproval && ! addedApprovalBatch ) {
291+ groupedItems . push ( { type : "batch" , parts : readFileNeedsApproval } ) ;
292+ addedApprovalBatch = true ;
293+ } else if ( isFileOpPart && ! partNeedsApproval && ! addedOtherBatch ) {
294+ groupedItems . push ( { type : "batch" , parts : readFileOther } ) ;
295+ addedOtherBatch = true ;
296+ } else if ( ! isFileOpPart ) {
297+ groupedItems . push ( { type : "single" , part } ) ;
298+ }
278299 }
279300
280301 return (
0 commit comments