@@ -440,22 +440,6 @@ describe('appendTextToRootStream', () => {
440440 } )
441441
442442 // Think tag parsing tests
443- test ( 'parses think tags in text and creates reasoning blocks' , ( ) => {
444- const result = appendTextToRootStream ( [ ] , {
445- type : 'text' ,
446- text : 'Before <think>My thoughts</think> after' ,
447- } )
448-
449- expect ( result ) . toHaveLength ( 3 )
450- expect ( ( result [ 0 ] as any ) . content ) . toBe ( 'Before ' )
451- expect ( ( result [ 0 ] as any ) . textType ) . toBe ( 'text' )
452- expect ( ( result [ 1 ] as any ) . content ) . toBe ( 'My thoughts' )
453- expect ( ( result [ 1 ] as any ) . textType ) . toBe ( 'reasoning' )
454- expect ( ( result [ 1 ] as any ) . isCollapsed ) . toBe ( true )
455- expect ( ( result [ 2 ] as any ) . content ) . toBe ( ' after' )
456- expect ( ( result [ 2 ] as any ) . textType ) . toBe ( 'text' )
457- } )
458-
459443 test ( 'handles unclosed think tag' , ( ) => {
460444 const result = appendTextToRootStream ( [ ] , {
461445 type : 'text' ,
@@ -514,34 +498,6 @@ describe('appendTextToRootStream', () => {
514498 expect ( ( result [ 1 ] as any ) . textType ) . toBe ( 'text' )
515499 } )
516500
517- test ( 'handles multiple think tags in one chunk' , ( ) => {
518- const result = appendTextToRootStream ( [ ] , {
519- type : 'text' ,
520- text : '<think>first</think> middle <think>second</think>' ,
521- } )
522-
523- expect ( result ) . toHaveLength ( 3 )
524- expect ( ( result [ 0 ] as any ) . textType ) . toBe ( 'reasoning' )
525- expect ( ( result [ 0 ] as any ) . content ) . toBe ( 'first' )
526- expect ( ( result [ 1 ] as any ) . textType ) . toBe ( 'text' )
527- expect ( ( result [ 1 ] as any ) . content ) . toBe ( ' middle ' )
528- expect ( ( result [ 2 ] as any ) . textType ) . toBe ( 'reasoning' )
529- expect ( ( result [ 2 ] as any ) . content ) . toBe ( 'second' )
530- } )
531-
532- test ( 'handles think tag at start of text' , ( ) => {
533- const result = appendTextToRootStream ( [ ] , {
534- type : 'text' ,
535- text : '<think>thoughts</think> after' ,
536- } )
537-
538- expect ( result ) . toHaveLength ( 2 )
539- expect ( ( result [ 0 ] as any ) . textType ) . toBe ( 'reasoning' )
540- expect ( ( result [ 0 ] as any ) . content ) . toBe ( 'thoughts' )
541- expect ( ( result [ 1 ] as any ) . textType ) . toBe ( 'text' )
542- expect ( ( result [ 1 ] as any ) . content ) . toBe ( ' after' )
543- } )
544-
545501 test ( 'text without think tags works normally' , ( ) => {
546502 const result = appendTextToRootStream ( [ ] , {
547503 type : 'text' ,
@@ -598,6 +554,34 @@ describe('appendTextToRootStream', () => {
598554 expect ( ( result [ 1 ] as any ) . content ) . toBe ( 'after' )
599555 expect ( ( result [ 1 ] as any ) . textType ) . toBe ( 'text' )
600556 } )
557+
558+ // Streaming simulation tests
559+ test ( 'streaming: does not create duplicate block when closing existing thinking block' , ( ) => {
560+ // Simulate streaming: first chunk opens thinking, second chunk closes it
561+ // First chunk: '<think>My thoughts' creates open thinking block
562+ const afterFirstChunk = appendTextToRootStream ( [ ] , {
563+ type : 'text' ,
564+ text : '<think>My thoughts' ,
565+ } )
566+
567+ expect ( afterFirstChunk ) . toHaveLength ( 1 )
568+ expect ( ( afterFirstChunk [ 0 ] as any ) . textType ) . toBe ( 'reasoning' )
569+ expect ( ( afterFirstChunk [ 0 ] as any ) . content ) . toBe ( 'My thoughts' )
570+ expect ( ( afterFirstChunk [ 0 ] as any ) . thinkingOpen ) . toBe ( true )
571+
572+ // Second chunk: '</think> after' should close the block, not create a duplicate
573+ const afterSecondChunk = appendTextToRootStream ( afterFirstChunk , {
574+ type : 'text' ,
575+ text : '</think> after' ,
576+ } )
577+
578+ expect ( afterSecondChunk ) . toHaveLength ( 2 )
579+ expect ( ( afterSecondChunk [ 0 ] as any ) . textType ) . toBe ( 'reasoning' )
580+ expect ( ( afterSecondChunk [ 0 ] as any ) . content ) . toBe ( 'My thoughts' )
581+ expect ( ( afterSecondChunk [ 0 ] as any ) . thinkingOpen ) . toBe ( false )
582+ expect ( ( afterSecondChunk [ 1 ] as any ) . textType ) . toBe ( 'text' )
583+ expect ( ( afterSecondChunk [ 1 ] as any ) . content ) . toBe ( ' after' )
584+ } )
601585} )
602586
603587describe ( 'extractPlanFromBuffer' , ( ) => {
0 commit comments