@@ -559,17 +559,98 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => {
559559 } ) ,
560560 ) ;
561561
562- it . effect ( "deduplicates overlapping assistant text deltas after part updates " , ( ) =>
562+ it . effect ( "appends raw assistant text deltas and reconciles part update snapshots " , ( ) =>
563563 Effect . sync ( ( ) => {
564564 const firstUpdate = mergeOpenCodeAssistantText ( undefined , "Hello" ) ;
565565 const overlapDelta = appendOpenCodeAssistantTextDelta ( firstUpdate . latestText , "lo world" ) ;
566- const secondUpdate = mergeOpenCodeAssistantText ( overlapDelta . nextText , "Hello world! " ) ;
566+ const secondUpdate = mergeOpenCodeAssistantText ( overlapDelta . nextText , "Hellolo world" ) ;
567567
568568 assert . deepEqual (
569569 [ firstUpdate . deltaToEmit , overlapDelta . deltaToEmit , secondUpdate . deltaToEmit ] ,
570- [ "Hello" , " world" , "! " ] ,
570+ [ "Hello" , "lo world" , "" ] ,
571571 ) ;
572- assert . equal ( secondUpdate . latestText , "Hello world!" ) ;
572+ assert . equal ( secondUpdate . latestText , "Hellolo world" ) ;
573+ } ) ,
574+ ) ;
575+
576+ it . effect ( "does not strip coincidental prefix overlap from OpenCode part deltas" , ( ) =>
577+ Effect . gen ( function * ( ) {
578+ const adapter = yield * OpenCodeAdapter ;
579+ const threadId = asThreadId ( "thread-opencode-raw-delta" ) ;
580+ const part = {
581+ id : "part-raw-delta" ,
582+ sessionID : "http://127.0.0.1:9999/session" ,
583+ messageID : "msg-raw-delta" ,
584+ type : "text" ,
585+ text : "A B" ,
586+ time : { start : 1 } ,
587+ } ;
588+ runtimeMock . state . subscribedEvents = [
589+ {
590+ type : "message.updated" ,
591+ properties : {
592+ sessionID : "http://127.0.0.1:9999/session" ,
593+ info : {
594+ id : "msg-raw-delta" ,
595+ role : "assistant" ,
596+ } ,
597+ } ,
598+ } ,
599+ {
600+ type : "message.part.updated" ,
601+ properties : {
602+ sessionID : "http://127.0.0.1:9999/session" ,
603+ part,
604+ time : 1 ,
605+ } ,
606+ } ,
607+ {
608+ type : "message.part.delta" ,
609+ properties : {
610+ sessionID : "http://127.0.0.1:9999/session" ,
611+ messageID : "msg-raw-delta" ,
612+ partID : "part-raw-delta" ,
613+ field : "text" ,
614+ delta : "Bonus" ,
615+ } ,
616+ } ,
617+ {
618+ type : "message.part.updated" ,
619+ properties : {
620+ sessionID : "http://127.0.0.1:9999/session" ,
621+ part : {
622+ ...part ,
623+ text : "A BBonus" ,
624+ time : { start : 1 , end : 2 } ,
625+ } ,
626+ time : 2 ,
627+ } ,
628+ } ,
629+ ] ;
630+ const eventsFiber = yield * adapter . streamEvents . pipe (
631+ Stream . filter ( ( event ) => event . threadId === threadId ) ,
632+ Stream . take ( 5 ) ,
633+ Stream . runCollect ,
634+ Effect . forkChild ,
635+ ) ;
636+
637+ yield * adapter . startSession ( {
638+ provider : ProviderDriverKind . make ( "opencode" ) ,
639+ threadId,
640+ runtimeMode : "full-access" ,
641+ } ) ;
642+
643+ const events = Array . from ( yield * Fiber . join ( eventsFiber ) . pipe ( Effect . timeout ( "1 second" ) ) ) ;
644+ const deltas = events . filter ( ( event ) => event . type === "content.delta" ) ;
645+ assert . deepEqual (
646+ deltas . map ( ( event ) => ( event . type === "content.delta" ? event . payload . delta : "" ) ) ,
647+ [ "A B" , "Bonus" ] ,
648+ ) ;
649+ assert . equal ( events . at ( - 1 ) ?. type , "item.completed" ) ;
650+ const completed = events . at ( - 1 ) ;
651+ if ( completed ?. type === "item.completed" ) {
652+ assert . equal ( completed . payload . detail , "A BBonus" ) ;
653+ }
573654 } ) ,
574655 ) ;
575656
0 commit comments