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,98 @@ 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+ }
117+ }
118+
119+ const filePart : FilePart = {
120+ kind : 'file' ,
121+ file : {
122+ name : file . name ,
123+ mimeType : mimeType || 'application/octet-stream' ,
124+ bytes,
125+ } ,
126+ }
127+ parts . push ( filePart )
128+ }
129+ }
130+ }
55131
56132 const message : Message = {
57133 kind : 'message' ,
58134 messageId : crypto . randomUUID ( ) ,
59135 role : 'user' ,
60- parts : [ { kind : 'text' , text : validatedData . message } ] ,
136+ parts,
61137 ...( validatedData . taskId && { taskId : validatedData . taskId } ) ,
62138 ...( validatedData . contextId && { contextId : validatedData . contextId } ) ,
63139 }
64140
65- const result = await client . sendMessage ( { message } )
141+ let result
142+ try {
143+ result = await client . sendMessage ( { message } )
144+ logger . info ( `[${ requestId } ] A2A sendMessage completed` , { resultKind : result ?. kind } )
145+ } catch ( sendError ) {
146+ logger . error ( `[${ requestId } ] Failed to send A2A message:` , sendError )
147+ return NextResponse . json (
148+ {
149+ success : false ,
150+ error : `Failed to send message: ${ sendError instanceof Error ? sendError . message : 'Unknown error' } ` ,
151+ } ,
152+ { status : 502 }
153+ )
154+ }
66155
67156 if ( result . kind === 'message' ) {
68157 const responseMessage = result as Message
0 commit comments