@@ -304,6 +304,23 @@ describe('AgentRunHandle', () => {
304304 expect ( getSignal ( ) ?. aborted ) . toBe ( true ) ;
305305 expect ( agent . state . isStreaming ) . toBe ( false ) ;
306306 } ) ;
307+
308+ it ( 'aborts streamFn when events() iteration breaks early' , async ( ) => {
309+ const { streamFn, getSignal } = makeAbortableStreamFn ( ) ;
310+ const agent = new Agent ( {
311+ initialState : { model : makeFakeModel ( ) } ,
312+ streamFn,
313+ } ) ;
314+
315+ const handle = agent . prompt ( 'go' ) ;
316+ for await ( const _event of handle . events ( ) ) {
317+ break ;
318+ }
319+ await agent . waitForIdle ( ) ;
320+
321+ expect ( getSignal ( ) ?. aborted ) . toBe ( true ) ;
322+ expect ( agent . state . isStreaming ) . toBe ( false ) ;
323+ } ) ;
307324 } ) ;
308325
309326 describe ( 'single-use enforcement' , ( ) => {
@@ -328,6 +345,29 @@ describe('AgentRunHandle', () => {
328345 expect ( ( ) => handle . events ( ) ) . toThrow ( / a l r e a d y c o n s u m e d / ) ;
329346 } ) ;
330347
348+ it ( 'throws on a second prompt() while a handle from the first is still unconsumed' , ( ) => {
349+ const provider = createScriptedProvider ( {
350+ responses : [
351+ makeFakeAssistantMessage ( {
352+ stopReason : 'stop' ,
353+ content : [ { type : 'text' , text : 'x' } ] ,
354+ } ) ,
355+ ] ,
356+ } ) ;
357+ const agent = new Agent ( {
358+ initialState : { model : makeFakeModel ( ) } ,
359+ streamFn : provider . stream ,
360+ } ) ;
361+
362+ const first = agent . prompt ( 'hi' ) ;
363+ expect ( ( ) => agent . prompt ( 'hi again' ) ) . toThrow ( / u n c o n s u m e d r u n h a n d l e / ) ;
364+
365+ // abort frees the agent state; first remains a dangling handle reference
366+ agent . abort ( ) ;
367+ expect ( ( ) => agent . prompt ( 'after abort' ) ) . not . toThrow ( ) ;
368+ void first ;
369+ } ) ;
370+
331371 it ( 'throws when toResponse() is called twice' , ( ) => {
332372 const provider = createScriptedProvider ( {
333373 responses : [
@@ -349,8 +389,8 @@ describe('AgentRunHandle', () => {
349389 } ) ;
350390 } ) ;
351391
352- describe ( 'PromiseLike auto-sink ' , ( ) => {
353- it ( 'await on the handle drives the run to completion without an explicit consumer' , async ( ) => {
392+ describe ( 'wait() ' , ( ) => {
393+ it ( 'drives the run to completion without an explicit event consumer' , async ( ) => {
354394 const provider = createScriptedProvider ( {
355395 responses : [
356396 makeFakeAssistantMessage ( {
@@ -373,15 +413,15 @@ describe('AgentRunHandle', () => {
373413 expect ( agent . state . isStreaming ) . toBe ( false ) ;
374414 } ) ;
375415
376- it ( 'rejects the awaited handle when the binder rejects (e.g. streamFn throws)' , async ( ) => {
416+ it ( 'rejects when the binder rejects (e.g. streamFn throws)' , async ( ) => {
377417 const agent = new Agent ( {
378418 initialState : { model : makeFakeModel ( ) } ,
379419 streamFn : ( ) => {
380420 throw new Error ( 'binder failure' ) ;
381421 } ,
382422 } ) ;
383423
384- await expect ( agent . prompt ( 'hi' ) ) . rejects . toThrow ( / b i n d e r f a i l u r e / ) ;
424+ await expect ( agent . prompt ( 'hi' ) . wait ( ) ) . rejects . toThrow ( / b i n d e r f a i l u r e / ) ;
385425 expect ( agent . state . isStreaming ) . toBe ( false ) ;
386426 } ) ;
387427
0 commit comments