@@ -930,6 +930,36 @@ it.instance("loop continues when finish is tool-calls", () =>
930930 } ) ,
931931)
932932
933+ it . instance ( "loop resamples when the provider closes a stream without a finish reason" , ( ) =>
934+ Effect . gen ( function * ( ) {
935+ const { llm } = yield * useServerConfig ( providerCfg )
936+ const prompt = yield * SessionPrompt . Service
937+ const sessions = yield * Session . Service
938+ const session = yield * sessions . create ( {
939+ title : "Pinned" ,
940+ permission : [ { permission : "*" , pattern : "*" , action : "allow" } ] ,
941+ } )
942+ yield * prompt . prompt ( {
943+ sessionID : session . id ,
944+ agent : "build" ,
945+ noReply : true ,
946+ parts : [ { type : "text" , text : "hello" } ] ,
947+ } )
948+ // No finish chunk: the AI SDK reports this as finishReason "other", which we
949+ // map to "unknown". The loop must resample instead of ending the run.
950+ yield * llm . push ( reply ( ) . item ( ) )
951+ yield * llm . text ( "second" )
952+
953+ const result = yield * prompt . loop ( { sessionID : session . id } )
954+ expect ( yield * llm . calls ) . toBe ( 2 )
955+ expect ( result . info . role ) . toBe ( "assistant" )
956+ if ( result . info . role === "assistant" ) {
957+ expect ( result . parts . some ( ( part ) => part . type === "text" && part . text === "second" ) ) . toBe ( true )
958+ expect ( result . info . finish ) . toBe ( "stop" )
959+ }
960+ } ) ,
961+ )
962+
933963it . instance ( "glob tool keeps instance context during prompt runs" , ( ) =>
934964 Effect . gen ( function * ( ) {
935965 const { dir, llm } = yield * useServerConfig ( providerCfg )
0 commit comments