@@ -2,6 +2,7 @@ import type {
22 AccountProfile ,
33 AdapterCapability ,
44 CancelResult ,
5+ CloudAgentInteraction ,
56 DispatchRequest ,
67 LogChunk ,
78 TaskHandle ,
@@ -80,6 +81,39 @@ export interface AgentDispatchStdioClientOptions extends StdioServerParameters {
8081 clientVersion ?: string ;
8182}
8283
84+ export interface A2AMessagePart {
85+ kind : "text" | ( string & { } ) ;
86+ text ?: string ;
87+ [ key : string ] : unknown ;
88+ }
89+
90+ export interface CloudAgentA2AMessage {
91+ id ?: string ;
92+ role ?: "user" | "agent" | ( string & { } ) ;
93+ text ?: string ;
94+ parts ?: A2AMessagePart [ ] ;
95+ messageId ?: string ;
96+ metadata ?: Record < string , unknown > ;
97+ }
98+
99+ export interface CloudAgentA2AHttpRequest {
100+ url : string ;
101+ method : "POST" ;
102+ headers : Record < string , string > ;
103+ body : string ;
104+ cloudAgent : CloudAgentInteraction ;
105+ }
106+
107+ export interface CloudAgentA2AResult {
108+ raw : unknown ;
109+ text ?: string ;
110+ metadata ?: Record < string , unknown > ;
111+ }
112+
113+ export type CloudAgentA2ATransport =
114+ | ( ( request : CloudAgentA2AHttpRequest ) => Promise < unknown > )
115+ | { send ( request : CloudAgentA2AHttpRequest ) : Promise < unknown > } ;
116+
83117export interface SpawnCloudAgentRequest {
84118 instruction : string ;
85119 runtime ?: string ;
@@ -194,6 +228,68 @@ export function connectAgentDispatchStdioClient(options: AgentDispatchStdioClien
194228 return AgentDispatchStdioClient . connect ( options ) ;
195229}
196230
231+ export function createA2AMessageSendPayload ( message : CloudAgentA2AMessage ) : Record < string , unknown > {
232+ const parts = message . parts ?? ( message . text !== undefined ? [ { kind : "text" , text : message . text } ] : undefined ) ;
233+ if ( ! parts ?. length ) {
234+ throw new Error ( "A2A follow-up requires message.text or message.parts." ) ;
235+ }
236+
237+ return {
238+ jsonrpc : "2.0" ,
239+ id : message . id ?? createClientId ( "a2a" ) ,
240+ method : "message/send" ,
241+ params : {
242+ message : {
243+ role : message . role ?? "user" ,
244+ parts,
245+ messageId : message . messageId ?? createClientId ( "msg" )
246+ } ,
247+ ...( message . metadata ? { metadata : message . metadata } : { } )
248+ }
249+ } ;
250+ }
251+
252+ export function createCloudAgentA2AHttpRequest ( cloudAgent : CloudAgentInteraction , message : CloudAgentA2AMessage ) : CloudAgentA2AHttpRequest {
253+ if ( cloudAgent . protocol !== "a2a" ) {
254+ throw new Error ( `Cloud agent protocol is ${ cloudAgent . protocol } , not a2a.` ) ;
255+ }
256+ const url = stringValue ( cloudAgent . a2a ?. endpointUrl ) ?? stringValue ( cloudAgent . invocation ?. runtimeUrl ) ;
257+ if ( ! url ) {
258+ throw new Error ( "Cloud agent A2A endpoint URL is missing." ) ;
259+ }
260+ const sessionHeaderName = stringValue ( cloudAgent . a2a ?. sessionHeaderName ) ?? stringValue ( cloudAgent . invocation ?. sessionHeaderName ) ;
261+ const sessionHeaderValue = stringValue ( cloudAgent . a2a ?. sessionHeaderValue ) ?? stringValue ( cloudAgent . invocation ?. sessionHeaderValue ) ;
262+ const payload = createA2AMessageSendPayload ( message ) ;
263+ const method = stringValue ( cloudAgent . a2a ?. messageMethod ) ?? "message/send" ;
264+ payload . method = method ;
265+
266+ const headers : Record < string , string > = {
267+ "content-type" : stringValue ( cloudAgent . invocation ?. contentType ) ?? "application/json" ,
268+ accept : stringValue ( cloudAgent . invocation ?. accept ) ?? "application/json"
269+ } ;
270+ if ( sessionHeaderName && sessionHeaderValue ) {
271+ headers [ sessionHeaderName ] = sessionHeaderValue ;
272+ }
273+
274+ return {
275+ url,
276+ method : "POST" ,
277+ headers,
278+ body : JSON . stringify ( payload ) ,
279+ cloudAgent
280+ } ;
281+ }
282+
283+ export async function sendCloudAgentA2AMessage (
284+ cloudAgent : CloudAgentInteraction ,
285+ message : CloudAgentA2AMessage ,
286+ transport : CloudAgentA2ATransport
287+ ) : Promise < CloudAgentA2AResult > {
288+ const request = createCloudAgentA2AHttpRequest ( cloudAgent , message ) ;
289+ const raw = typeof transport === "function" ? await transport ( request ) : await transport . send ( request ) ;
290+ return decodeA2AResult ( raw ) ;
291+ }
292+
197293function removeUndefinedValues ( input : Record < string , unknown > ) : Record < string , unknown > {
198294 return Object . fromEntries ( Object . entries ( input ) . filter ( ( [ , value ] ) => value !== undefined ) ) ;
199295}
@@ -228,3 +324,32 @@ function isTextContentResult(result: unknown): result is { content: Array<{ type
228324 Array . isArray ( ( result as { content ?: unknown } ) . content )
229325 ) ;
230326}
327+
328+ function decodeA2AResult ( raw : unknown ) : CloudAgentA2AResult {
329+ const candidate = isRecord ( raw ) && isRecord ( raw . result ) ? raw . result : raw ;
330+ const message = isRecord ( candidate ) && isRecord ( candidate . message ) ? candidate . message : candidate ;
331+ const parts = isRecord ( message ) && Array . isArray ( message . parts ) ? message . parts : [ ] ;
332+ const text = parts
333+ . filter ( isRecord )
334+ . map ( ( part ) => typeof part . text === "string" ? part . text : "" )
335+ . filter ( Boolean )
336+ . join ( "\n" ) || undefined ;
337+ const metadata = isRecord ( message ) && isRecord ( message . metadata )
338+ ? message . metadata
339+ : isRecord ( candidate ) && isRecord ( candidate . metadata )
340+ ? candidate . metadata
341+ : undefined ;
342+ return { raw, text, metadata } ;
343+ }
344+
345+ function stringValue ( value : unknown ) : string | undefined {
346+ return typeof value === "string" && value . length > 0 ? value : undefined ;
347+ }
348+
349+ function isRecord ( value : unknown ) : value is Record < string , unknown > {
350+ return Boolean ( value ) && typeof value === "object" && ! Array . isArray ( value ) ;
351+ }
352+
353+ function createClientId ( prefix : string ) : string {
354+ return `${ prefix } _${ Date . now ( ) . toString ( 36 ) } _${ Math . random ( ) . toString ( 36 ) . slice ( 2 , 10 ) } ` ;
355+ }
0 commit comments