1- import type { Message , Task } from '@a2a-js/sdk'
1+ import type { DataPart , FilePart , Message , Part , Task , TextPart } from '@a2a-js/sdk'
22import { createLogger } from '@sim/logger'
33import { type NextRequest , NextResponse } from 'next/server'
44import { z } from 'zod'
@@ -10,11 +10,20 @@ export const dynamic = 'force-dynamic'
1010
1111const logger = createLogger ( 'A2ASendMessageAPI' )
1212
13+ const FileInputSchema = z . object ( {
14+ type : z . enum ( [ 'file' , 'url' ] ) ,
15+ data : z . string ( ) ,
16+ name : z . string ( ) ,
17+ mime : z . string ( ) . optional ( ) ,
18+ } )
19+
1320const A2ASendMessageSchema = z . object ( {
1421 agentUrl : z . string ( ) . min ( 1 , 'Agent URL is required' ) ,
1522 message : z . string ( ) . min ( 1 , 'Message is required' ) ,
1623 taskId : z . string ( ) . optional ( ) ,
1724 contextId : z . string ( ) . optional ( ) ,
25+ data : z . string ( ) . optional ( ) ,
26+ files : z . array ( FileInputSchema ) . optional ( ) ,
1827 apiKey : z . string ( ) . optional ( ) ,
1928} )
2029
@@ -51,18 +60,100 @@ export async function POST(request: NextRequest) {
5160 hasContextId : ! ! validatedData . contextId ,
5261 } )
5362
54- const client = await createA2AClient ( validatedData . agentUrl , validatedData . apiKey )
63+ let client
64+ try {
65+ client = await createA2AClient ( validatedData . agentUrl , validatedData . apiKey )
66+ logger . info ( `[${ requestId } ] A2A client created successfully` )
67+ } catch ( clientError ) {
68+ logger . error ( `[${ requestId } ] Failed to create A2A client:` , clientError )
69+ return NextResponse . json (
70+ {
71+ success : false ,
72+ error : `Failed to connect to agent: ${ clientError instanceof Error ? clientError . message : 'Unknown error' } ` ,
73+ } ,
74+ { status : 502 }
75+ )
76+ }
77+
78+ const parts : Part [ ] = [ ]
79+
80+ const textPart : TextPart = { kind : 'text' , text : validatedData . message }
81+ parts . push ( textPart )
82+
83+ if ( validatedData . data ) {
84+ try {
85+ const parsedData = JSON . parse ( validatedData . data )
86+ const dataPart : DataPart = { kind : 'data' , data : parsedData }
87+ parts . push ( dataPart )
88+ } catch ( parseError ) {
89+ logger . warn ( `[${ requestId } ] Failed to parse data as JSON, skipping DataPart` , {
90+ error : parseError instanceof Error ? parseError . message : String ( parseError ) ,
91+ } )
92+ }
93+ }
94+
95+ if ( validatedData . files && validatedData . files . length > 0 ) {
96+ for ( const file of validatedData . files ) {
97+ if ( file . type === 'url' ) {
98+ const filePart : FilePart = {
99+ kind : 'file' ,
100+ file : {
101+ name : file . name ,
102+ mimeType : file . mime ,
103+ uri : file . data ,
104+ } ,
105+ }
106+ parts . push ( filePart )
107+ } else if ( file . type === 'file' ) {
108+ let bytes = file . data
109+ let mimeType = file . mime
110+
111+ if ( file . data . startsWith ( 'data:' ) ) {
112+ const match = file . data . match ( / ^ d a t a : ( [ ^ ; ] + ) ; b a s e 6 4 , ( .+ ) $ / )
113+ if ( match ) {
114+ mimeType = mimeType || match [ 1 ]
115+ bytes = match [ 2 ]
116+ } else {
117+ bytes = file . data
118+ }
119+ }
120+
121+ const filePart : FilePart = {
122+ kind : 'file' ,
123+ file : {
124+ name : file . name ,
125+ mimeType : mimeType || 'application/octet-stream' ,
126+ bytes,
127+ } ,
128+ }
129+ parts . push ( filePart )
130+ }
131+ }
132+ }
55133
56134 const message : Message = {
57135 kind : 'message' ,
58136 messageId : crypto . randomUUID ( ) ,
59137 role : 'user' ,
60- parts : [ { kind : 'text' , text : validatedData . message } ] ,
138+ parts,
61139 ...( validatedData . taskId && { taskId : validatedData . taskId } ) ,
62140 ...( validatedData . contextId && { contextId : validatedData . contextId } ) ,
63141 }
64142
65- const result = await client . sendMessage ( { message } )
143+ let result
144+ try {
145+ result = await client . sendMessage ( { message } )
146+ logger . info ( `[${ requestId } ] A2A sendMessage completed` , { resultKind : result ?. kind } )
147+ } catch ( sendError ) {
148+ logger . error ( `[${ requestId } ] Failed to send A2A message:` , sendError )
149+ return NextResponse . json (
150+ {
151+ success : false ,
152+ error : `Failed to send message: ${ sendError instanceof Error ? sendError . message : 'Unknown error' } ` ,
153+ } ,
154+ { status : 502 }
155+ )
156+ }
66157
67158 if ( result . kind === 'message' ) {
68159 const responseMessage = result as Message
0 commit comments