33 * This is used for building both an Agent2Agent (A2A) server and an A2A client with Google Apps Script.
44 *
55 * Author: Kanshi Tanaike
6- * 20250529 10:04
7- * version 2.0.1
6+ * 20250618 10:40
7+ * version 2.0.2
88 * @class
99 */
1010class A2AApp {
@@ -24,6 +24,9 @@ class A2AApp {
2424 /** @private */
2525 this . model = "models/gemini-2.0-flash" ; // or "models/gemini-2.5-flash-preview-04-17"
2626
27+ /** @private */
28+ this . jsonrpc = "2.0" ;
29+
2730 /** @private */
2831 this . date = new Date ( ) ;
2932
@@ -158,7 +161,7 @@ class A2AApp {
158161 } catch ( { stack } ) {
159162 console . error ( stack ) ;
160163 const err = "Internal server error" ;
161- const errObj = { "error" : { "code" : this . ErrorCode [ err ] , "message" : `${ err } . Error message: ${ stack } ` } , "jsonrpc" : "2.0" , id } ;
164+ const errObj = { "error" : { "code" : this . ErrorCode [ err ] , "message" : `${ err } . Error message: ${ stack } ` } , "jsonrpc" : this . jsonrpc , id } ;
162165 this . values . push ( [ this . date , null , id , "server --> client" , JSON . stringify ( errObj ) ] ) ;
163166 if ( this . log ) {
164167 this . log_ ( ) ;
@@ -170,7 +173,7 @@ class A2AApp {
170173 } else {
171174 console . error ( "Timeout." ) ;
172175 const err = "Internal server error" ;
173- const errObj = { "error" : { "code" : this . ErrorCode [ err ] , "message" : `${ err } . Error message: Timeout.` } , "jsonrpc" : "2.0" , id } ;
176+ const errObj = { "error" : { "code" : this . ErrorCode [ err ] , "message" : `${ err } . Error message: Timeout.` } , "jsonrpc" : this . jsonrpc , id } ;
174177 this . values . push ( [ this . date , null , id , "server --> client" , JSON . stringify ( errObj ) ] ) ;
175178 if ( this . log ) {
176179 this . log_ ( ) ;
@@ -295,18 +298,87 @@ class A2AApp {
295298 if ( this . accessKey && eventObject . parameter . accessKey && eventObject . parameter . accessKey != this . accessKey ) {
296299 this . values . push ( [ this . date , method , id , "At server" , "Invalid accessKey." ] ) ;
297300 const err = "Authorization failed" ;
298- const errObj = { "error" : { "code" : this . ErrorCode [ err ] , "message" : `${ err } . Invalid access key.` } , "jsonrpc" : "2.0" , id } ;
301+ const errObj = { "error" : { "code" : this . ErrorCode [ err ] , "message" : `${ err } . Invalid access key.` } , "jsonrpc" : this . jsonrpc , id } ;
299302 this . values . push ( [ this . date , method , id , "server --> client" , JSON . stringify ( errObj ) ] ) ;
300303 return this . createContent_ ( errObj ) ;
301304 }
302305
303- if ( method == "tasks/send" && functions ) {
306+ if ( method == "message/send" && functions ) {
307+ let resObj ;
308+ let messageId ;
309+ try {
310+ if ( typeof functions != "function" ) {
311+ const err = "Internal server error" ;
312+ const errObj = { "error" : { "code" : this . ErrorCode [ err ] , "message" : `${ err } . Invalid functions.` } , "jsonrpc" : this . jsonrpc , id } ;
313+ this . values . push ( [ this . date , method , id , "server --> client" , JSON . stringify ( errObj ) ] ) ;
314+ return this . createContent_ ( errObj ) ;
315+ }
316+ const { params } = obj ;
317+ const { message } = params ;
318+ messageId = params . messageId ;
319+ const prompt = message . parts [ 0 ] . text ;
320+
321+ const { result, history } = this . processAgents_ ( {
322+ apiKey,
323+ prompt,
324+ functions : functions ( ) ,
325+ fileAsBlob : true ,
326+ agentCards,
327+ } ) ;
328+ for ( let i = 0 ; i < result . length ; i ++ ) {
329+ if ( typeof result [ i ] == "string" ) {
330+ result [ i ] = { type : "text" , kind : "text" , text : result [ i ] } ;
331+ }
332+ }
333+ const { messageParts } = result . reduce ( ( o , e , i ) => {
334+ const type = e . type ;
335+ if ( type == "text" ) {
336+ const gg = new GeminiWithFiles ( { apiKey, model : this . model , history } ) ;
337+ const res = gg . generateContent ( {
338+ parts : [
339+ { text : `Summarize answers by considering the question.` } ,
340+ { text : `<Question>${ prompt } </Question>` } ,
341+ { text : `<Answers>${ e [ e . type ] } </Answers>` }
342+ ]
343+ } ) ;
344+ o . messageParts . push ( { type : "text" , kind : "text" , text : res } ) ;
345+ o . artifacts . push ( { name : "Answer" , index : i , parts : [ { type : "text" , kind : "text" , text : res } ] } ) ;
346+ } else {
347+ if ( type != "file" && type != "data" ) {
348+ o . messageParts . push ( e ) ;
349+ } else {
350+ o . messageParts . push ( { type : "text" , kind : "text" , text : `The data "${ e [ type ] . name } " was downloaded.` } ) ;
351+ }
352+ o . artifacts . push ( { name : "Answer" , index : i , parts : [ e ] } ) ;
353+ }
354+ return o ;
355+ } , { artifacts : [ ] , messageParts : [ ] } ) ;
356+ resObj = {
357+ jsonrpc : this . jsonrpc ,
358+ result : {
359+ kind : "message" ,
360+ messageId,
361+ parts : messageParts ,
362+ role : "agent"
363+ } ,
364+ id
365+ } ;
366+ } catch ( { stack } ) {
367+ console . error ( stack ) ;
368+ const err = "Internal server error" ;
369+ resObj = { "error" : { "code" : this . ErrorCode [ err ] , "message" : `${ err } . Error message: ${ stack } ` } , "jsonrpc" : this . jsonrpc , id } ;
370+ }
371+
372+ this . values . push ( [ this . date , method , id , "server --> client" , JSON . stringify ( resObj ) ] ) ;
373+ return this . createContent_ ( resObj ) ;
374+
375+ } else if ( method == "tasks/send" && functions ) {
304376 let resObj ;
305377 let paramsId , sessionId ;
306378 try {
307379 if ( typeof functions != "function" ) {
308380 const err = "Internal server error" ;
309- const errObj = { "error" : { "code" : this . ErrorCode [ err ] , "message" : `${ err } . Invalid functions.` } , "jsonrpc" : "2.0" , id } ;
381+ const errObj = { "error" : { "code" : this . ErrorCode [ err ] , "message" : `${ err } . Invalid functions.` } , "jsonrpc" : this . jsonrpc , id } ;
310382 this . values . push ( [ this . date , method , id , "server --> client" , JSON . stringify ( errObj ) ] ) ;
311383 return this . createContent_ ( errObj ) ;
312384 }
@@ -325,7 +397,7 @@ class A2AApp {
325397 } ) ;
326398 for ( let i = 0 ; i < result . length ; i ++ ) {
327399 if ( typeof result [ i ] == "string" ) {
328- result [ i ] = { type : "text" , text : result [ i ] } ;
400+ result [ i ] = { type : "text" , kind : "text" , text : result [ i ] } ;
329401 }
330402 }
331403 const { artifacts, messageParts } = result . reduce ( ( o , e , i ) => {
@@ -339,22 +411,23 @@ class A2AApp {
339411 { text : `<Answers>${ e [ e . type ] } </Answers>` }
340412 ]
341413 } ) ;
342- o . messageParts . push ( { type : "text" , text : res } ) ;
343- o . artifacts . push ( { name : "Answer" , index : i , parts : [ { type : "text" , text : res } ] } ) ;
414+ o . messageParts . push ( { type : "text" , kind : "text" , text : res } ) ;
415+ o . artifacts . push ( { name : "Answer" , index : i , parts : [ { type : "text" , kind : "text" , text : res } ] } ) ;
344416 } else {
345417 if ( type != "file" && type != "data" ) {
346418 o . messageParts . push ( e ) ;
347419 } else {
348- o . messageParts . push ( { type : "text" , text : `The data "${ e [ type ] . name } " was downloaded.` } ) ;
420+ o . messageParts . push ( { type : "text" , kind : "text" , text : `The data "${ e [ type ] . name } " was downloaded.` } ) ;
349421 }
350422 o . artifacts . push ( { name : "Answer" , index : i , parts : [ e ] } ) ;
351423 }
352424 return o ;
353425 } , { artifacts : [ ] , messageParts : [ ] } ) ;
354426
355427 resObj = {
356- jsonrpc : "2.0" ,
428+ jsonrpc : this . jsonrpc ,
357429 result : {
430+ kind : "task" ,
358431 id : paramsId ,
359432 sessionId : sessionId ,
360433 status : {
@@ -368,31 +441,14 @@ class A2AApp {
368441 } ;
369442 } catch ( { stack } ) {
370443 console . error ( stack ) ;
371- resObj = {
372- jsonrpc : "2.0" ,
373- result : {
374- id : paramsId ,
375- sessionId : sessionId ,
376- status : {
377- state : this . TaskState . failed ,
378- message : { role : "agent" , parts : [ { type : "text" , text : stack } ] } ,
379- timestamp : this . date . toISOString ( )
380- } ,
381- artifacts : [
382- {
383- name : "Answer" ,
384- index : 0 ,
385- parts : [ { type : "text" , text : stack } ]
386- }
387- ]
388- } ,
389- id,
390- } ;
444+ const err = "Internal server error" ;
445+ resObj = { "error" : { "code" : this . ErrorCode [ err ] , "message" : `${ err } . Error message: ${ stack } ` } , "jsonrpc" : this . jsonrpc , id } ;
391446 }
392447
393448 this . values . push ( [ this . date , method , id , "server --> client" , JSON . stringify ( resObj ) ] ) ;
394449 return this . createContent_ ( resObj ) ;
395450 }
451+
396452 return null ;
397453 }
398454
@@ -517,9 +573,9 @@ class A2AApp {
517573 const id2 = Utilities . getUuid ( ) ;
518574 const id3 = Utilities . getUuid ( ) ;
519575 const resObj = {
520- jsonrpc : '2.0' ,
576+ jsonrpc : this . jsonrpc ,
521577 id : id1 ,
522- method : ' tasks/send' ,
578+ method : " tasks/send" ,
523579 params :
524580 {
525581 id : id2 ,
@@ -714,7 +770,7 @@ class A2AApp {
714770 const orderAr = g . generateContent ( { q : textPrompt } ) ;
715771 if ( ! Array . isArray ( orderAr ) || orderAr . length == 0 ) {
716772 const err = "Internal server error" ;
717- const errObj = { "error" : { "code" : this . ErrorCode [ err ] , "message" : `${ err } . Try again.` } , "jsonrpc" : "2.0" , id } ;
773+ const errObj = { "error" : { "code" : this . ErrorCode [ err ] , "message" : `${ err } . Try again.` } , "jsonrpc" : this . jsonrpc , id } ;
718774 this . values . push ( [ this . date , null , null , "Client side" , JSON . stringify ( errObj ) ] ) ;
719775 return errObj ;
720776 }
@@ -903,4 +959,4 @@ class A2AApp {
903959 . join ( "&" )
904960 ) ;
905961 }
906- }
962+ }
0 commit comments