@@ -1344,6 +1344,25 @@ const sseHandlers: Record<string, SSEHandler> = {
13441344 context . contentBlocks . push ( context . currentTextBlock )
13451345 }
13461346
1347+ const splitTrailingPartialTag = (
1348+ text : string ,
1349+ tags : string [ ]
1350+ ) : { text : string ; remaining : string } => {
1351+ const partialIndex = text . lastIndexOf ( '<' )
1352+ if ( partialIndex < 0 ) {
1353+ return { text, remaining : '' }
1354+ }
1355+ const possibleTag = text . substring ( partialIndex )
1356+ const matchesTagStart = tags . some ( ( tag ) => tag . startsWith ( possibleTag ) )
1357+ if ( ! matchesTagStart ) {
1358+ return { text, remaining : '' }
1359+ }
1360+ return {
1361+ text : text . substring ( 0 , partialIndex ) ,
1362+ remaining : possibleTag ,
1363+ }
1364+ }
1365+
13471366 while ( contentToProcess . length > 0 ) {
13481367 // Handle design_workflow tags (takes priority over other content processing)
13491368 if ( context . isInDesignWorkflowBlock ) {
@@ -1363,13 +1382,17 @@ const sseHandlers: Record<string, SSEHandler> = {
13631382 hasProcessedContent = true
13641383 } else {
13651384 // Still in design_workflow block, accumulate content
1366- context . designWorkflowContent += contentToProcess
1385+ const { text, remaining } = splitTrailingPartialTag ( contentToProcess , [ '</design_workflow>' ] )
1386+ context . designWorkflowContent += text
13671387
13681388 // Update store with partial content for streaming effect (available in all modes)
13691389 set ( { streamingPlanContent : context . designWorkflowContent } )
13701390
1371- contentToProcess = ''
1391+ contentToProcess = remaining
13721392 hasProcessedContent = true
1393+ if ( remaining ) {
1394+ break
1395+ }
13731396 }
13741397 continue
13751398 }
@@ -1491,18 +1514,24 @@ const sseHandlers: Record<string, SSEHandler> = {
14911514 contentToProcess = contentToProcess . substring ( endMatch . index + endMatch [ 0 ] . length )
14921515 hasProcessedContent = true
14931516 } else {
1494- if ( context . currentThinkingBlock ) {
1495- context . currentThinkingBlock . content += contentToProcess
1496- } else {
1497- context . currentThinkingBlock = contentBlockPool . get ( )
1498- context . currentThinkingBlock . type = THINKING_BLOCK_TYPE
1499- context . currentThinkingBlock . content = contentToProcess
1500- context . currentThinkingBlock . timestamp = Date . now ( )
1501- context . currentThinkingBlock . startTime = Date . now ( )
1502- context . contentBlocks . push ( context . currentThinkingBlock )
1517+ const { text, remaining } = splitTrailingPartialTag ( contentToProcess , [ '</thinking>' ] )
1518+ if ( text ) {
1519+ if ( context . currentThinkingBlock ) {
1520+ context . currentThinkingBlock . content += text
1521+ } else {
1522+ context . currentThinkingBlock = contentBlockPool . get ( )
1523+ context . currentThinkingBlock . type = THINKING_BLOCK_TYPE
1524+ context . currentThinkingBlock . content = text
1525+ context . currentThinkingBlock . timestamp = Date . now ( )
1526+ context . currentThinkingBlock . startTime = Date . now ( )
1527+ context . contentBlocks . push ( context . currentThinkingBlock )
1528+ }
1529+ hasProcessedContent = true
1530+ }
1531+ contentToProcess = remaining
1532+ if ( remaining ) {
1533+ break
15031534 }
1504- contentToProcess = ''
1505- hasProcessedContent = true
15061535 }
15071536 } else {
15081537 const startMatch = thinkingStartRegex . exec ( contentToProcess )
0 commit comments