@@ -37,15 +37,9 @@ fn jsonrpc_error_response(
3737 "message" : message
3838 }
3939 } ) ;
40- serde_json:: to_vec ( & response) . unwrap_or_else ( |e| {
41- format ! (
42- "{{\" jsonrpc\" :\" 2.0\" ,\" id\" :null,\" error\" :{{\" code\" :{},\" message\" :\" failed to serialize error response: {}\" }}}}" ,
43- i32 :: from( code) ,
44- e
45- )
46- . into_bytes ( )
47- } )
48- . into ( )
40+ serde_json:: to_vec ( & response)
41+ . expect ( "serializing JSON-RPC error response should not fail" )
42+ . into ( )
4943}
5044
5145async fn publish_backpressure_error_reply < N : PublishClient + FlushClient , S : JsonSerialize > (
@@ -301,6 +295,7 @@ mod tests {
301295 KillTerminalCommandRequest , KillTerminalCommandResponse , ReadTextFileRequest ,
302296 ReadTextFileResponse , Request , RequestId , RequestPermissionOutcome ,
303297 RequestPermissionRequest , RequestPermissionResponse , SessionNotification , SessionUpdate ,
298+ ToolCallUpdate , ToolCallUpdateFields ,
304299 } ;
305300 use async_trait:: async_trait;
306301 use std:: cell:: RefCell ;
@@ -849,6 +844,150 @@ mod tests {
849844 assert_eq ! ( nats. published_messages( ) , vec![ "_INBOX.reply" ] ) ;
850845 }
851846
847+ #[ tokio:: test]
848+ async fn dispatch_client_method_dispatches_session_update_with_terminal_kill_failing_client ( ) {
849+ let nats = MockNatsClient :: new ( ) ;
850+ let client = TerminalKillFailingClient ;
851+ let session_id = AcpSessionId :: new ( "sess-1" ) . unwrap ( ) ;
852+
853+ let notification = SessionNotification :: new (
854+ "sess-1" ,
855+ SessionUpdate :: AgentMessageChunk ( ContentChunk :: new ( ContentBlock :: from ( "hi" ) ) ) ,
856+ ) ;
857+ let payload = bytes:: Bytes :: from ( serde_json:: to_vec ( & notification) . unwrap ( ) ) ;
858+
859+ let parsed = crate :: nats:: ParsedClientSubject {
860+ session_id,
861+ method : ClientMethod :: SessionUpdate ,
862+ } ;
863+
864+ let ctx = DispatchContext {
865+ nats : & nats,
866+ client : & client,
867+ serializer : & StdJsonSerialize ,
868+ } ;
869+ dispatch_client_method (
870+ "acp.sess-1.client.session.update" ,
871+ parsed,
872+ payload,
873+ None ,
874+ & ctx,
875+ )
876+ . await ;
877+ }
878+
879+ #[ tokio:: test]
880+ async fn dispatch_client_method_dispatches_fs_read_text_file_with_terminal_kill_failing_client ( )
881+ {
882+ let nats = MockNatsClient :: new ( ) ;
883+ let client = TerminalKillFailingClient ;
884+ let session_id = AcpSessionId :: new ( "sess-1" ) . unwrap ( ) ;
885+
886+ let envelope = Request {
887+ id : RequestId :: Number ( 1 ) ,
888+ method : std:: sync:: Arc :: from ( "fs/read_text_file" ) ,
889+ params : Some ( ReadTextFileRequest :: new (
890+ agent_client_protocol:: SessionId :: from ( "sess-1" ) ,
891+ "/tmp/foo.txt" . to_string ( ) ,
892+ ) ) ,
893+ } ;
894+ let payload = bytes:: Bytes :: from ( serde_json:: to_vec ( & envelope) . unwrap ( ) ) ;
895+
896+ let parsed = crate :: nats:: ParsedClientSubject {
897+ session_id,
898+ method : ClientMethod :: FsReadTextFile ,
899+ } ;
900+
901+ let ctx = DispatchContext {
902+ nats : & nats,
903+ client : & client,
904+ serializer : & StdJsonSerialize ,
905+ } ;
906+ dispatch_client_method (
907+ "acp.sess-1.client.fs.read_text_file" ,
908+ parsed,
909+ payload,
910+ Some ( "_INBOX.reply" . to_string ( ) ) ,
911+ & ctx,
912+ )
913+ . await ;
914+
915+ assert_eq ! ( nats. published_messages( ) , vec![ "_INBOX.reply" ] ) ;
916+ }
917+
918+ #[ tokio:: test]
919+ async fn dispatch_client_method_dispatches_terminal_create_with_terminal_kill_failing_client ( ) {
920+ let nats = MockNatsClient :: new ( ) ;
921+ let client = TerminalKillFailingClient ;
922+ let session_id = AcpSessionId :: new ( "sess-1" ) . unwrap ( ) ;
923+
924+ let envelope = Request {
925+ id : RequestId :: Number ( 1 ) ,
926+ method : std:: sync:: Arc :: from ( "terminal/create" ) ,
927+ params : Some ( CreateTerminalRequest :: new ( "sess-1" , "echo hi" ) ) ,
928+ } ;
929+ let payload = bytes:: Bytes :: from ( serde_json:: to_vec ( & envelope) . unwrap ( ) ) ;
930+
931+ let parsed = crate :: nats:: ParsedClientSubject {
932+ session_id,
933+ method : ClientMethod :: TerminalCreate ,
934+ } ;
935+
936+ let ctx = DispatchContext {
937+ nats : & nats,
938+ client : & client,
939+ serializer : & StdJsonSerialize ,
940+ } ;
941+ dispatch_client_method (
942+ "acp.sess-1.client.terminal.create" ,
943+ parsed,
944+ payload,
945+ Some ( "_INBOX.reply" . to_string ( ) ) ,
946+ & ctx,
947+ )
948+ . await ;
949+
950+ assert_eq ! ( nats. published_messages( ) , vec![ "_INBOX.reply" ] ) ;
951+ }
952+
953+ #[ tokio:: test]
954+ async fn dispatch_client_method_dispatches_request_permission_with_terminal_kill_failing_client ( )
955+ {
956+ let nats = MockNatsClient :: new ( ) ;
957+ let client = TerminalKillFailingClient ;
958+ let session_id = AcpSessionId :: new ( "sess-1" ) . unwrap ( ) ;
959+
960+ let tool_call = ToolCallUpdate :: new ( "call-1" , ToolCallUpdateFields :: new ( ) ) ;
961+ let request = RequestPermissionRequest :: new ( "sess-1" , tool_call, vec ! [ ] ) ;
962+ let envelope = Request {
963+ id : RequestId :: Number ( 1 ) ,
964+ method : std:: sync:: Arc :: from ( "session/request_permission" ) ,
965+ params : Some ( request) ,
966+ } ;
967+ let payload = bytes:: Bytes :: from ( serde_json:: to_vec ( & envelope) . unwrap ( ) ) ;
968+
969+ let parsed = crate :: nats:: ParsedClientSubject {
970+ session_id,
971+ method : ClientMethod :: SessionRequestPermission ,
972+ } ;
973+
974+ let ctx = DispatchContext {
975+ nats : & nats,
976+ client : & client,
977+ serializer : & StdJsonSerialize ,
978+ } ;
979+ dispatch_client_method (
980+ "acp.sess-1.client.session.request_permission" ,
981+ parsed,
982+ payload,
983+ Some ( "_INBOX.reply" . to_string ( ) ) ,
984+ & ctx,
985+ )
986+ . await ;
987+
988+ assert_eq ! ( nats. published_messages( ) , vec![ "_INBOX.reply" ] ) ;
989+ }
990+
852991 #[ tokio:: test]
853992 async fn dispatch_client_method_dispatches_fs_read_text_file_with_advanced_mock ( ) {
854993 let nats = AdvancedMockNatsClient :: new ( ) ;
0 commit comments