@@ -681,6 +681,132 @@ describe('Vercel decoder', () => {
681681 expect ( eventTypesOf ( outputs ) ) . toContain ( 'start-step' ) ;
682682 expect ( eventTypesOf ( outputs ) ) . not . toContain ( 'start' ) ; // start already emitted for this turn
683683 } ) ;
684+
685+ it ( 'clears lifecycle scope after finish' , ( ) => {
686+ const decoder = createDecoder ( ) ;
687+
688+ // Stream content — synthesizes start + start-step
689+ decoder . decode (
690+ withHeaders (
691+ { action : 'message.create' , serial : 's1' , name : 'text' , data : '' } ,
692+ {
693+ [ HEADER_STREAM ] : 'true' ,
694+ [ HEADER_STATUS ] : 'streaming' ,
695+ [ HEADER_STREAM_ID ] : 'txt-1' ,
696+ [ HEADER_TURN_ID ] : 'turn-1' ,
697+ [ `${ D } id` ] : 'txt-1' ,
698+ } ,
699+ ) ,
700+ ) ;
701+
702+ // finish — clears scope
703+ decoder . decode (
704+ withHeaders (
705+ { action : 'message.create' , name : 'finish' , data : '' } ,
706+ { [ HEADER_STREAM ] : 'false' , [ HEADER_TURN_ID ] : 'turn-1' , [ `${ D } finishReason` ] : 'stop' } ,
707+ ) ,
708+ ) ;
709+
710+ // New content on same turn — should re-synthesize start + start-step
711+ const outputs = decoder . decode (
712+ withHeaders (
713+ { action : 'message.create' , serial : 's2' , name : 'text' , data : '' } ,
714+ {
715+ [ HEADER_STREAM ] : 'true' ,
716+ [ HEADER_STATUS ] : 'streaming' ,
717+ [ HEADER_STREAM_ID ] : 'txt-2' ,
718+ [ HEADER_TURN_ID ] : 'turn-1' ,
719+ [ `${ D } id` ] : 'txt-2' ,
720+ } ,
721+ ) ,
722+ ) ;
723+ expect ( eventTypesOf ( outputs ) ) . toContain ( 'start' ) ;
724+ expect ( eventTypesOf ( outputs ) ) . toContain ( 'start-step' ) ;
725+ } ) ;
726+
727+ it ( 'clears lifecycle scope after abort' , ( ) => {
728+ const decoder = createDecoder ( ) ;
729+
730+ // Stream content — synthesizes start + start-step
731+ decoder . decode (
732+ withHeaders (
733+ { action : 'message.create' , serial : 's1' , name : 'text' , data : '' } ,
734+ {
735+ [ HEADER_STREAM ] : 'true' ,
736+ [ HEADER_STATUS ] : 'streaming' ,
737+ [ HEADER_STREAM_ID ] : 'txt-1' ,
738+ [ HEADER_TURN_ID ] : 'turn-1' ,
739+ [ `${ D } id` ] : 'txt-1' ,
740+ } ,
741+ ) ,
742+ ) ;
743+
744+ // abort — clears scope
745+ decoder . decode (
746+ withHeaders (
747+ { action : 'message.create' , name : 'abort' , data : 'cancelled' } ,
748+ { [ HEADER_STREAM ] : 'false' , [ HEADER_TURN_ID ] : 'turn-1' } ,
749+ ) ,
750+ ) ;
751+
752+ // New content on same turn — should re-synthesize start + start-step
753+ const outputs = decoder . decode (
754+ withHeaders (
755+ { action : 'message.create' , serial : 's2' , name : 'text' , data : '' } ,
756+ {
757+ [ HEADER_STREAM ] : 'true' ,
758+ [ HEADER_STATUS ] : 'streaming' ,
759+ [ HEADER_STREAM_ID ] : 'txt-2' ,
760+ [ HEADER_TURN_ID ] : 'turn-1' ,
761+ [ `${ D } id` ] : 'txt-2' ,
762+ } ,
763+ ) ,
764+ ) ;
765+ expect ( eventTypesOf ( outputs ) ) . toContain ( 'start' ) ;
766+ expect ( eventTypesOf ( outputs ) ) . toContain ( 'start-step' ) ;
767+ } ) ;
768+
769+ it ( 'clears lifecycle scope after error' , ( ) => {
770+ const decoder = createDecoder ( ) ;
771+
772+ // Stream content — synthesizes start + start-step
773+ decoder . decode (
774+ withHeaders (
775+ { action : 'message.create' , serial : 's1' , name : 'text' , data : '' } ,
776+ {
777+ [ HEADER_STREAM ] : 'true' ,
778+ [ HEADER_STATUS ] : 'streaming' ,
779+ [ HEADER_STREAM_ID ] : 'txt-1' ,
780+ [ HEADER_TURN_ID ] : 'turn-1' ,
781+ [ `${ D } id` ] : 'txt-1' ,
782+ } ,
783+ ) ,
784+ ) ;
785+
786+ // error — clears scope
787+ decoder . decode (
788+ withHeaders (
789+ { action : 'message.create' , name : 'error' , data : 'something broke' } ,
790+ { [ HEADER_STREAM ] : 'false' , [ HEADER_TURN_ID ] : 'turn-1' } ,
791+ ) ,
792+ ) ;
793+
794+ // New content on same turn — should re-synthesize start + start-step
795+ const outputs = decoder . decode (
796+ withHeaders (
797+ { action : 'message.create' , serial : 's2' , name : 'text' , data : '' } ,
798+ {
799+ [ HEADER_STREAM ] : 'true' ,
800+ [ HEADER_STATUS ] : 'streaming' ,
801+ [ HEADER_STREAM_ID ] : 'txt-2' ,
802+ [ HEADER_TURN_ID ] : 'turn-1' ,
803+ [ `${ D } id` ] : 'txt-2' ,
804+ } ,
805+ ) ,
806+ ) ;
807+ expect ( eventTypesOf ( outputs ) ) . toContain ( 'start' ) ;
808+ expect ( eventTypesOf ( outputs ) ) . toContain ( 'start-step' ) ;
809+ } ) ;
684810 } ) ;
685811
686812 // -- first-contact update -------------------------------------------------
0 commit comments