@@ -54,6 +54,18 @@ class FakeTransport implements OpenClawTransport {
5454 }
5555}
5656
57+ class EventsOnlyTransport implements OpenClawTransport {
58+ constructor ( private readonly eventSource : AsyncIterable < GatewayEvent > ) { }
59+
60+ async request < T = unknown > ( ) : Promise < T > {
61+ return { } as T ;
62+ }
63+
64+ events ( ) : AsyncIterable < GatewayEvent > {
65+ return this . eventSource ;
66+ }
67+ }
68+
5769function requireTransportCall ( calls : readonly RequestCall [ ] , index : number ) : RequestCall {
5870 const call = calls [ index ] ;
5971 if ( ! call ) {
@@ -683,6 +695,84 @@ describe("OpenClaw SDK", () => {
683695 expect ( seen ) . toEqual ( [ "run.started" , "assistant.delta" , "run.completed" ] ) ;
684696 } ) ;
685697
698+ it ( "rejects normalized event streams when the event pump fails before yielding" , async ( ) => {
699+ const failure = new Error ( "synthetic transport event failure" ) ;
700+ const transport = new EventsOnlyTransport ( {
701+ [ Symbol . asyncIterator ] ( ) : AsyncIterator < GatewayEvent > {
702+ return {
703+ next : async ( ) => {
704+ throw failure ;
705+ } ,
706+ } ;
707+ } ,
708+ } ) ;
709+ const oc = new OpenClaw ( { transport } ) ;
710+ const iterator = oc . events ( ) [ Symbol . asyncIterator ] ( ) ;
711+ let futureIterator : AsyncIterator < OpenClawEvent > | undefined ;
712+
713+ try {
714+ await expect ( iterator . next ( ) ) . rejects . toThrow ( "synthetic transport event failure" ) ;
715+
716+ futureIterator = oc . events ( ) [ Symbol . asyncIterator ] ( ) ;
717+ await expect ( futureIterator . next ( ) ) . rejects . toThrow ( "synthetic transport event failure" ) ;
718+ } finally {
719+ await futureIterator ?. return ?.( ) ;
720+ await iterator . return ?.( ) ;
721+ await oc . close ( ) ;
722+ }
723+ } ) ;
724+
725+ it ( "rejects run event streams after replaying events when the event pump fails" , async ( ) => {
726+ const failure = new Error ( "synthetic post-yield transport event failure" ) ;
727+ const rawEvent : GatewayEvent = {
728+ event : "agent" ,
729+ seq : 1 ,
730+ payload : {
731+ runId : "run_pump_failure" ,
732+ stream : "lifecycle" ,
733+ ts : 1_777_000_000_050 ,
734+ data : { phase : "start" } ,
735+ } ,
736+ } ;
737+ const transport = new EventsOnlyTransport ( {
738+ async * [ Symbol . asyncIterator ] ( ) {
739+ yield rawEvent ;
740+ throw failure ;
741+ } ,
742+ } ) ;
743+ const oc = new OpenClaw ( { transport } ) ;
744+ const run = await oc . runs . get ( "run_pump_failure" ) ;
745+ const iterator = run . events ( ) [ Symbol . asyncIterator ] ( ) ;
746+ let futureIterator : AsyncIterator < OpenClawEvent > | undefined ;
747+
748+ try {
749+ const first = await iterator . next ( ) ;
750+ expect ( first . done ) . toBe ( false ) ;
751+ if ( first . done !== false ) {
752+ throw new Error ( "expected first run event" ) ;
753+ }
754+ expect ( first . value . type ) . toBe ( "run.started" ) ;
755+ expect ( first . value . runId ) . toBe ( "run_pump_failure" ) ;
756+
757+ await expect ( iterator . next ( ) ) . rejects . toThrow ( "synthetic post-yield transport event failure" ) ;
758+
759+ futureIterator = run . events ( ) [ Symbol . asyncIterator ] ( ) ;
760+ const replayed = await futureIterator . next ( ) ;
761+ expect ( replayed . done ) . toBe ( false ) ;
762+ if ( replayed . done !== false ) {
763+ throw new Error ( "expected replayed run event" ) ;
764+ }
765+ expect ( replayed . value . type ) . toBe ( "run.started" ) ;
766+ await expect ( futureIterator . next ( ) ) . rejects . toThrow (
767+ "synthetic post-yield transport event failure" ,
768+ ) ;
769+ } finally {
770+ await futureIterator ?. return ?.( ) ;
771+ await iterator . return ?.( ) ;
772+ await oc . close ( ) ;
773+ }
774+ } ) ;
775+
686776 it ( "does not surface raw chat projection events in per-run streams" , async ( ) => {
687777 const ts = 1_777_000_000_100 ;
688778 const transport = new FakeTransport ( {
0 commit comments