@@ -922,5 +922,151 @@ describe("AnthropicHandler", () => {
922922 arguments : '"London"}' ,
923923 } )
924924 } )
925+
926+ it ( "should emit advisor_tool_use chunk with id from server_tool_use block" , async ( ) => {
927+ mockCreate . mockImplementationOnce ( async ( ) => ( {
928+ async * [ Symbol . asyncIterator ] ( ) {
929+ yield {
930+ type : "message_start" ,
931+ message : { usage : { input_tokens : 100 , output_tokens : 50 } } ,
932+ }
933+ yield {
934+ type : "content_block_start" ,
935+ index : 0 ,
936+ content_block : {
937+ type : "server_tool_use" ,
938+ id : "srvtoolu_abc123" ,
939+ name : "advisor" ,
940+ input : { } ,
941+ } ,
942+ }
943+ } ,
944+ } ) )
945+
946+ const stream = handler . createMessage ( "system" , [ { role : "user" , content : "Hello" } ] , {
947+ taskId : "test-task" ,
948+ } )
949+
950+ const chunks : any [ ] = [ ]
951+ for await ( const chunk of stream ) {
952+ chunks . push ( chunk )
953+ }
954+
955+ const advisorUseChunk = chunks . find ( ( c ) => c . type === "advisor_tool_use" )
956+ expect ( advisorUseChunk ) . toBeDefined ( )
957+ expect ( advisorUseChunk . id ) . toBe ( "srvtoolu_abc123" )
958+ expect ( advisorUseChunk . name ) . toBe ( "advisor" )
959+ } )
960+
961+ it ( "should emit advisor_tool_result chunk with tool_use_id and text content" , async ( ) => {
962+ mockCreate . mockImplementationOnce ( async ( ) => ( {
963+ async * [ Symbol . asyncIterator ] ( ) {
964+ yield {
965+ type : "message_start" ,
966+ message : { usage : { input_tokens : 100 , output_tokens : 50 } } ,
967+ }
968+ yield {
969+ type : "content_block_start" ,
970+ index : 1 ,
971+ content_block : {
972+ type : "advisor_tool_result" ,
973+ tool_use_id : "srvtoolu_abc123" ,
974+ content : { type : "advisor_result" , text : "Use channel-based coordination." } ,
975+ } ,
976+ }
977+ } ,
978+ } ) )
979+
980+ const stream = handler . createMessage ( "system" , [ { role : "user" , content : "Hello" } ] , {
981+ taskId : "test-task" ,
982+ } )
983+
984+ const chunks : any [ ] = [ ]
985+ for await ( const chunk of stream ) {
986+ chunks . push ( chunk )
987+ }
988+
989+ const resultChunk = chunks . find ( ( c ) => c . type === "advisor_tool_result" )
990+ expect ( resultChunk ) . toBeDefined ( )
991+ expect ( resultChunk . tool_use_id ) . toBe ( "srvtoolu_abc123" )
992+ expect ( resultChunk . content ) . toBe ( "Use channel-based coordination." )
993+ // rawContent must carry the verbatim object for round-tripping to the API
994+ expect ( resultChunk . rawContent ) . toEqual ( { type : "advisor_result" , text : "Use channel-based coordination." } )
995+ } )
996+
997+ it ( "should extract text from advisor_result object content shape" , async ( ) => {
998+ mockCreate . mockImplementationOnce ( async ( ) => ( {
999+ async * [ Symbol . asyncIterator ] ( ) {
1000+ yield {
1001+ type : "message_start" ,
1002+ message : { usage : { input_tokens : 100 , output_tokens : 50 } } ,
1003+ }
1004+ yield {
1005+ type : "content_block_start" ,
1006+ index : 0 ,
1007+ content_block : {
1008+ type : "advisor_tool_result" ,
1009+ tool_use_id : "srvtoolu_xyz" ,
1010+ content : { type : "advisor_result" , text : "Plan: do X then Y." } ,
1011+ } ,
1012+ }
1013+ } ,
1014+ } ) )
1015+
1016+ const stream = handler . createMessage ( "system" , [ { role : "user" , content : "Plan?" } ] , {
1017+ taskId : "test-task" ,
1018+ } )
1019+
1020+ const chunks : any [ ] = [ ]
1021+ for await ( const chunk of stream ) {
1022+ chunks . push ( chunk )
1023+ }
1024+
1025+ const resultChunk = chunks . find ( ( c ) => c . type === "advisor_tool_result" )
1026+ expect ( resultChunk ) . toBeDefined ( )
1027+ expect ( resultChunk . content ) . toBe ( "Plan: do X then Y." )
1028+ // rawContent must be the verbatim object, not just the extracted text
1029+ expect ( resultChunk . rawContent ) . toEqual ( { type : "advisor_result" , text : "Plan: do X then Y." } )
1030+ } )
1031+
1032+ it ( "should emit empty string for encrypted advisor_redacted_result content" , async ( ) => {
1033+ mockCreate . mockImplementationOnce ( async ( ) => ( {
1034+ async * [ Symbol . asyncIterator ] ( ) {
1035+ yield {
1036+ type : "message_start" ,
1037+ message : { usage : { input_tokens : 100 , output_tokens : 50 } } ,
1038+ }
1039+ yield {
1040+ type : "content_block_start" ,
1041+ index : 0 ,
1042+ content_block : {
1043+ type : "advisor_tool_result" ,
1044+ tool_use_id : "srvtoolu_redacted" ,
1045+ content : { type : "advisor_redacted_result" , encrypted_content : "opaque-blob" } ,
1046+ } ,
1047+ }
1048+ } ,
1049+ } ) )
1050+
1051+ const stream = handler . createMessage ( "system" , [ { role : "user" , content : "Plan?" } ] , {
1052+ taskId : "test-task" ,
1053+ } )
1054+
1055+ const chunks : any [ ] = [ ]
1056+ for await ( const chunk of stream ) {
1057+ chunks . push ( chunk )
1058+ }
1059+
1060+ const resultChunk = chunks . find ( ( c ) => c . type === "advisor_tool_result" )
1061+ expect ( resultChunk ) . toBeDefined ( )
1062+ expect ( resultChunk . tool_use_id ) . toBe ( "srvtoolu_redacted" )
1063+ // Encrypted content has no text field — content should be empty string
1064+ expect ( resultChunk . content ) . toBe ( "" )
1065+ // rawContent must carry the verbatim encrypted object for round-tripping
1066+ expect ( resultChunk . rawContent ) . toEqual ( {
1067+ type : "advisor_redacted_result" ,
1068+ encrypted_content : "opaque-blob" ,
1069+ } )
1070+ } )
9251071 } )
9261072} )
0 commit comments