@@ -966,6 +966,97 @@ describe("ClaudeAdapterLive", () => {
966966 ) ;
967967 } ) ;
968968
969+ it . effect ( "falls back to a default plan step label for blank TodoWrite content" , ( ) => {
970+ const harness = makeHarness ( ) ;
971+ return Effect . gen ( function * ( ) {
972+ const adapter = yield * ClaudeAdapter ;
973+
974+ const runtimeEventsFiber = yield * Stream . take ( adapter . streamEvents , 10 ) . pipe (
975+ Stream . runCollect ,
976+ Effect . forkChild ,
977+ ) ;
978+
979+ const session = yield * adapter . startSession ( {
980+ threadId : THREAD_ID ,
981+ provider : "claudeAgent" ,
982+ runtimeMode : "full-access" ,
983+ } ) ;
984+
985+ const turn = yield * adapter . sendTurn ( {
986+ threadId : session . threadId ,
987+ input : "hello" ,
988+ attachments : [ ] ,
989+ } ) ;
990+
991+ harness . query . emit ( {
992+ type : "stream_event" ,
993+ session_id : "sdk-session-todo-plan" ,
994+ uuid : "stream-todo-start" ,
995+ parent_tool_use_id : null ,
996+ event : {
997+ type : "content_block_start" ,
998+ index : 1 ,
999+ content_block : {
1000+ type : "tool_use" ,
1001+ id : "tool-todo-1" ,
1002+ name : "TodoWrite" ,
1003+ input : { } ,
1004+ } ,
1005+ } ,
1006+ } as unknown as SDKMessage ) ;
1007+
1008+ harness . query . emit ( {
1009+ type : "stream_event" ,
1010+ session_id : "sdk-session-todo-plan" ,
1011+ uuid : "stream-todo-input" ,
1012+ parent_tool_use_id : null ,
1013+ event : {
1014+ type : "content_block_delta" ,
1015+ index : 1 ,
1016+ delta : {
1017+ type : "input_json_delta" ,
1018+ partial_json :
1019+ '{"todos":[{"content":" ","status":"in_progress"},{"content":"Ship it","status":"completed"}]}' ,
1020+ } ,
1021+ } ,
1022+ } as unknown as SDKMessage ) ;
1023+
1024+ harness . query . emit ( {
1025+ type : "stream_event" ,
1026+ session_id : "sdk-session-todo-plan" ,
1027+ uuid : "stream-todo-stop" ,
1028+ parent_tool_use_id : null ,
1029+ event : {
1030+ type : "content_block_stop" ,
1031+ index : 1 ,
1032+ } ,
1033+ } as unknown as SDKMessage ) ;
1034+
1035+ harness . query . emit ( {
1036+ type : "result" ,
1037+ subtype : "success" ,
1038+ is_error : false ,
1039+ errors : [ ] ,
1040+ session_id : "sdk-session-todo-plan" ,
1041+ uuid : "result-todo-plan" ,
1042+ } as unknown as SDKMessage ) ;
1043+
1044+ const runtimeEvents = Array . from ( yield * Fiber . join ( runtimeEventsFiber ) ) ;
1045+ const planUpdated = runtimeEvents . find ( ( event ) => event . type === "turn.plan.updated" ) ;
1046+ assert . equal ( planUpdated ?. type , "turn.plan.updated" ) ;
1047+ if ( planUpdated ?. type === "turn.plan.updated" ) {
1048+ assert . equal ( String ( planUpdated . turnId ) , String ( turn . turnId ) ) ;
1049+ assert . deepEqual ( planUpdated . payload . plan , [
1050+ { step : "Task" , status : "inProgress" } ,
1051+ { step : "Ship it" , status : "completed" } ,
1052+ ] ) ;
1053+ }
1054+ } ) . pipe (
1055+ Effect . provideService ( Random . Random , makeDeterministicRandomService ( ) ) ,
1056+ Effect . provide ( harness . layer ) ,
1057+ ) ;
1058+ } ) ;
1059+
9691060 it . effect ( "classifies Claude Task tool invocations as collaboration agent work" , ( ) => {
9701061 const harness = makeHarness ( ) ;
9711062 return Effect . gen ( function * ( ) {
0 commit comments