@@ -497,6 +497,139 @@ describe("applyThreadDetailEvent", () => {
497497 expect ( result . thread . latestTurn ?. completedAt ) . toBeNull ( ) ;
498498 }
499499 } ) ;
500+
501+ it ( "keeps latestTurn and checkpoints references across a streaming delta" , ( ) => {
502+ const streamingThread : OrchestrationThread = {
503+ ...baseThread ,
504+ session : {
505+ threadId : ThreadId . make ( "thread-1" ) ,
506+ status : "running" ,
507+ providerName : "claude" ,
508+ runtimeMode : "full-access" ,
509+ activeTurnId : TurnId . make ( "turn-1" ) ,
510+ lastError : null ,
511+ updatedAt : "2026-04-01T06:59:00.000Z" ,
512+ } ,
513+ latestTurn : {
514+ turnId : TurnId . make ( "turn-1" ) ,
515+ state : "running" ,
516+ requestedAt : "2026-04-01T06:59:00.000Z" ,
517+ startedAt : "2026-04-01T06:59:00.000Z" ,
518+ completedAt : null ,
519+ assistantMessageId : MessageId . make ( "msg-2" ) ,
520+ } ,
521+ messages : [
522+ {
523+ id : MessageId . make ( "msg-2" ) ,
524+ role : "assistant" ,
525+ text : "Hello" ,
526+ turnId : TurnId . make ( "turn-1" ) ,
527+ streaming : true ,
528+ createdAt : "2026-04-01T06:00:00.000Z" ,
529+ updatedAt : "2026-04-01T06:00:00.000Z" ,
530+ } ,
531+ ] ,
532+ checkpoints : [
533+ {
534+ turnId : TurnId . make ( "turn-1" ) ,
535+ checkpointTurnCount : 1 ,
536+ checkpointRef : CheckpointRef . make ( "ref-1" ) ,
537+ status : "ready" ,
538+ files : [ ] ,
539+ assistantMessageId : MessageId . make ( "msg-2" ) ,
540+ completedAt : "2026-04-01T06:00:30.000Z" ,
541+ } ,
542+ ] ,
543+ } ;
544+
545+ const result = applyThreadDetailEvent ( streamingThread , {
546+ ...baseEventFields ,
547+ sequence : 9 ,
548+ occurredAt : "2026-04-01T07:00:00.000Z" ,
549+ aggregateKind : "thread" ,
550+ aggregateId : ThreadId . make ( "thread-1" ) ,
551+ type : "thread.message-sent" ,
552+ payload : {
553+ threadId : ThreadId . make ( "thread-1" ) ,
554+ messageId : MessageId . make ( "msg-2" ) ,
555+ role : "assistant" ,
556+ text : ", world" ,
557+ turnId : TurnId . make ( "turn-1" ) ,
558+ streaming : true ,
559+ createdAt : "2026-04-01T06:00:00.000Z" ,
560+ updatedAt : "2026-04-01T07:00:00.000Z" ,
561+ } ,
562+ } ) ;
563+
564+ expect ( result . kind ) . toBe ( "updated" ) ;
565+ if ( result . kind === "updated" ) {
566+ expect ( result . thread . messages ) . not . toBe ( streamingThread . messages ) ;
567+ expect ( result . thread . messages [ 0 ] ?. text ) . toBe ( "Hello, world" ) ;
568+ expect ( result . thread . latestTurn ) . toBe ( streamingThread . latestTurn ) ;
569+ expect ( result . thread . checkpoints ) . toBe ( streamingThread . checkpoints ) ;
570+ }
571+ } ) ;
572+
573+ it ( "replaces latestTurn and checkpoints when the first assistant message binds the turn" , ( ) => {
574+ const unboundThread : OrchestrationThread = {
575+ ...baseThread ,
576+ session : {
577+ threadId : ThreadId . make ( "thread-1" ) ,
578+ status : "running" ,
579+ providerName : "claude" ,
580+ runtimeMode : "full-access" ,
581+ activeTurnId : TurnId . make ( "turn-1" ) ,
582+ lastError : null ,
583+ updatedAt : "2026-04-01T06:59:00.000Z" ,
584+ } ,
585+ latestTurn : {
586+ turnId : TurnId . make ( "turn-1" ) ,
587+ state : "running" ,
588+ requestedAt : "2026-04-01T06:59:00.000Z" ,
589+ startedAt : "2026-04-01T06:59:00.000Z" ,
590+ completedAt : null ,
591+ assistantMessageId : null ,
592+ } ,
593+ checkpoints : [
594+ {
595+ turnId : TurnId . make ( "turn-1" ) ,
596+ checkpointTurnCount : 1 ,
597+ checkpointRef : CheckpointRef . make ( "ref-1" ) ,
598+ status : "ready" ,
599+ files : [ ] ,
600+ assistantMessageId : null ,
601+ completedAt : "2026-04-01T06:59:30.000Z" ,
602+ } ,
603+ ] ,
604+ } ;
605+
606+ const result = applyThreadDetailEvent ( unboundThread , {
607+ ...baseEventFields ,
608+ sequence : 9 ,
609+ occurredAt : "2026-04-01T07:00:00.000Z" ,
610+ aggregateKind : "thread" ,
611+ aggregateId : ThreadId . make ( "thread-1" ) ,
612+ type : "thread.message-sent" ,
613+ payload : {
614+ threadId : ThreadId . make ( "thread-1" ) ,
615+ messageId : MessageId . make ( "msg-2" ) ,
616+ role : "assistant" ,
617+ text : "Hello" ,
618+ turnId : TurnId . make ( "turn-1" ) ,
619+ streaming : true ,
620+ createdAt : "2026-04-01T07:00:00.000Z" ,
621+ updatedAt : "2026-04-01T07:00:00.000Z" ,
622+ } ,
623+ } ) ;
624+
625+ expect ( result . kind ) . toBe ( "updated" ) ;
626+ if ( result . kind === "updated" ) {
627+ expect ( result . thread . latestTurn ) . not . toBe ( unboundThread . latestTurn ) ;
628+ expect ( result . thread . latestTurn ?. assistantMessageId ) . toBe ( "msg-2" ) ;
629+ expect ( result . thread . checkpoints ) . not . toBe ( unboundThread . checkpoints ) ;
630+ expect ( result . thread . checkpoints [ 0 ] ?. assistantMessageId ) . toBe ( "msg-2" ) ;
631+ }
632+ } ) ;
500633 } ) ;
501634
502635 describe ( "thread.session-set" , ( ) => {
0 commit comments