@@ -1103,34 +1103,59 @@ <h3>Subtasks</h3>
11031103 const reader = response . body . getReader ( ) ;
11041104 const decoder = new TextDecoder ( ) ;
11051105 let fullText = '' ;
1106+ // SSE can be split across chunks; keep an incremental buffer so we only
1107+ // process complete lines like `data: xxx`.
1108+ let buffer = '' ;
1109+
1110+ const handleData = ( data ) => {
1111+ if ( data === '[PAUSED]' ) {
1112+ isPaused = true ;
1113+ stopRequested = false ;
1114+ controlState = 'continue' ;
1115+ updateControlUI ( ) ;
1116+ if ( ! fullText ) {
1117+ fullText = '(Plan updated - Review and edit the plan, then Continue)' ;
1118+ }
1119+ } else {
1120+ fullText += data ;
1121+ }
1122+ contentDiv . textContent = fullText ;
1123+ } ;
11061124
11071125 while ( true ) {
11081126 const { done, value } = await reader . read ( ) ;
11091127 if ( done ) break ;
11101128
1111- const chunk = decoder . decode ( value ) ;
1112- const lines = chunk . split ( '\n' ) ;
1129+ const chunk = decoder . decode ( value , { stream : true } ) ;
1130+ buffer += chunk ;
1131+
1132+ const lines = buffer . split ( '\n' ) ;
1133+ // Keep the last (possibly incomplete) line for the next chunk.
1134+ buffer = lines . pop ( ) ;
1135+
11131136 for ( const line of lines ) {
1114- if ( line . startsWith ( 'data:' ) ) {
1115- const data = line . substring ( 5 ) . trim ( ) ;
1116- if ( data ) {
1117- if ( data === '[PAUSED]' ) {
1118- isPaused = true ;
1119- stopRequested = false ;
1120- controlState = 'continue' ;
1121- updateControlUI ( ) ;
1122- if ( ! fullText ) {
1123- fullText = '(Plan updated - Review and edit the plan, then Continue)' ;
1124- }
1125- } else {
1126- fullText += data ;
1127- }
1128- contentDiv . textContent = fullText ;
1129- }
1137+ const lineClean = line . trimEnd ( ) ;
1138+ if ( ! lineClean . startsWith ( 'data:' ) ) continue ;
1139+
1140+ const data = lineClean . substring ( 5 ) . trim ( ) ;
1141+ if ( data ) {
1142+ handleData ( data ) ;
11301143 }
11311144 }
11321145 }
11331146
1147+ // Flush any remaining decoded text and process the final buffered line
1148+ // (in case the stream ends without a trailing newline).
1149+ const remaining = decoder . decode ( ) ;
1150+ if ( remaining ) buffer += remaining ;
1151+ if ( buffer ) {
1152+ const lineClean = buffer . trimEnd ( ) ;
1153+ if ( lineClean . startsWith ( 'data:' ) ) {
1154+ const data = lineClean . substring ( 5 ) . trim ( ) ;
1155+ if ( data ) handleData ( data ) ;
1156+ }
1157+ }
1158+
11341159 if ( ! fullText ) {
11351160 contentDiv . textContent = defaultMessage ;
11361161 }
0 commit comments