11import { NodeFileSystem } from "@effect/platform-node"
22import { expect } from "bun:test"
3- import { Cause , Effect , Exit , Fiber , Layer } from "effect"
3+ import { Cause , Effect , Exit , Fiber , Layer , Stream } from "effect"
44import path from "path"
55import type { Agent } from "../../src/agent/agent"
66import { Agent as AgentSvc } from "../../src/agent/agent"
@@ -183,6 +183,18 @@ const deps = Layer.mergeAll(
183183 SyncEvent . defaultLayer ,
184184 EventV2Bridge . defaultLayer ,
185185) . pipe ( Layer . provideMerge ( infra ) )
186+ const depsWithoutLLM = Layer . mergeAll (
187+ Session . defaultLayer ,
188+ Snapshot . defaultLayer ,
189+ AgentSvc . defaultLayer ,
190+ Permission . defaultLayer ,
191+ Plugin . defaultLayer ,
192+ Config . defaultLayer ,
193+ Provider . defaultLayer ,
194+ status ,
195+ SyncEvent . defaultLayer ,
196+ EventV2Bridge . defaultLayer ,
197+ ) . pipe ( Layer . provideMerge ( infra ) )
186198const env = Layer . mergeAll (
187199 TestLLMServer . layer ,
188200 SessionProcessor . layer . pipe (
@@ -195,6 +207,33 @@ const env = Layer.mergeAll(
195207
196208const it = testEffect ( env )
197209
210+ const splitReasoningLLM = Layer . succeed (
211+ LLM . Service ,
212+ LLM . Service . of ( {
213+ stream : ( ) =>
214+ Stream . make (
215+ { type : "reasoning-start" , id : "reason-1" } as LLM . Event ,
216+ { type : "reasoning-delta" , id : "reason-1" , text : "The" } as LLM . Event ,
217+ { type : "reasoning-end" , id : "reason-1" } as LLM . Event ,
218+ { type : "reasoning-start" , id : "reason-2" } as LLM . Event ,
219+ { type : "reasoning-delta" , id : "reason-2" , text : " user" } as LLM . Event ,
220+ { type : "reasoning-end" , id : "reason-2" } as LLM . Event ,
221+ { type : "finish" } as LLM . Event ,
222+ ) ,
223+ } ) ,
224+ )
225+ const splitReasoningEnv = Layer . mergeAll (
226+ TestLLMServer . layer ,
227+ SessionProcessor . layer . pipe (
228+ Layer . provide ( summary ) ,
229+ Layer . provide ( Image . defaultLayer ) ,
230+ Layer . provide ( RuntimeFlags . layer ( { experimentalEventSystem : true } ) ) ,
231+ Layer . provide ( splitReasoningLLM ) ,
232+ Layer . provideMerge ( depsWithoutLLM ) ,
233+ ) ,
234+ )
235+ const splitReasoningIt = testEffect ( splitReasoningEnv )
236+
198237const boot = Effect . fn ( "test.boot" ) ( function * ( ) {
199238 const processors = yield * SessionProcessor . Service
200239 const session = yield * Session . Service
@@ -429,6 +468,52 @@ it.live("session.processor effect tests capture reasoning from http mock", () =>
429468 ) ,
430469)
431470
471+ splitReasoningIt . live ( "session.processor effect tests merge adjacent reasoning cycles" , ( ) =>
472+ provideTmpdirServer (
473+ ( { dir } ) =>
474+ Effect . gen ( function * ( ) {
475+ const { processors, session, provider } = yield * boot ( )
476+
477+ const chat = yield * session . create ( { } )
478+ const parent = yield * user ( chat . id , "reason" )
479+ const msg = yield * assistant ( chat . id , parent . id , path . resolve ( dir ) )
480+ const mdl = yield * provider . getModel ( ref . providerID , ref . modelID )
481+ const handle = yield * processors . create ( {
482+ assistantMessage : msg ,
483+ sessionID : chat . id ,
484+ model : mdl ,
485+ } )
486+
487+ const value = yield * handle . process ( {
488+ user : {
489+ id : parent . id ,
490+ sessionID : chat . id ,
491+ role : "user" ,
492+ time : parent . time ,
493+ agent : parent . agent ,
494+ model : { providerID : ref . providerID , modelID : ref . modelID } ,
495+ } satisfies MessageV2 . User ,
496+ sessionID : chat . id ,
497+ model : mdl ,
498+ agent : agent ( ) ,
499+ system : [ ] ,
500+ messages : [ { role : "user" , content : "reason" } ] ,
501+ tools : { } ,
502+ } )
503+
504+ const reasoning = MessageV2 . parts ( msg . id ) . filter (
505+ ( part ) : part is MessageV2 . ReasoningPart => part . type === "reasoning" ,
506+ )
507+
508+ expect ( value ) . toBe ( "continue" )
509+ expect ( reasoning ) . toHaveLength ( 1 )
510+ expect ( reasoning [ 0 ] ?. text ) . toBe ( "The user" )
511+ expect ( reasoning [ 0 ] ?. time . end ) . toBeDefined ( )
512+ } ) ,
513+ { git : true , config : ( url ) => providerCfg ( url ) } ,
514+ ) ,
515+ )
516+
432517it . live ( "session.processor effect tests reset reasoning state across retries" , ( ) =>
433518 provideTmpdirServer (
434519 ( { dir, llm } ) =>
0 commit comments