@a2a-js/sdk 0.3.5 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,20 +20,423 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  var client_exports = {};
21
21
  __export(client_exports, {
22
22
  A2AClient: () => A2AClient,
23
- createAuthenticatingFetchWithRetry: () => createAuthenticatingFetchWithRetry
23
+ AgentCardResolver: () => AgentCardResolver,
24
+ Client: () => Client,
25
+ ClientCallContext: () => ClientCallContext,
26
+ ClientCallContextKey: () => ClientCallContextKey,
27
+ ClientFactory: () => ClientFactory,
28
+ ClientFactoryOptions: () => ClientFactoryOptions,
29
+ DefaultAgentCardResolver: () => DefaultAgentCardResolver,
30
+ JsonRpcTransport: () => JsonRpcTransport,
31
+ JsonRpcTransportFactory: () => JsonRpcTransportFactory,
32
+ ServiceParameters: () => ServiceParameters,
33
+ createAuthenticatingFetchWithRetry: () => createAuthenticatingFetchWithRetry,
34
+ withA2AExtensions: () => withA2AExtensions
24
35
  });
25
36
  module.exports = __toCommonJS(client_exports);
26
37
 
27
38
  // src/constants.ts
28
39
  var AGENT_CARD_PATH = ".well-known/agent-card.json";
40
+ var HTTP_EXTENSION_HEADER = "X-A2A-Extensions";
41
+
42
+ // src/errors.ts
43
+ var TaskNotFoundError = class extends Error {
44
+ constructor(message) {
45
+ super(message ?? "Task not found");
46
+ this.name = "TaskNotFoundError";
47
+ }
48
+ };
49
+ var TaskNotCancelableError = class extends Error {
50
+ constructor(message) {
51
+ super(message ?? "Task cannot be canceled");
52
+ this.name = "TaskNotCancelableError";
53
+ }
54
+ };
55
+ var PushNotificationNotSupportedError = class extends Error {
56
+ constructor(message) {
57
+ super(message ?? "Push Notification is not supported");
58
+ this.name = "PushNotificationNotSupportedError";
59
+ }
60
+ };
61
+ var UnsupportedOperationError = class extends Error {
62
+ constructor(message) {
63
+ super(message ?? "This operation is not supported");
64
+ this.name = "UnsupportedOperationError";
65
+ }
66
+ };
67
+ var ContentTypeNotSupportedError = class extends Error {
68
+ constructor(message) {
69
+ super(message ?? "Incompatible content types");
70
+ this.name = "ContentTypeNotSupportedError";
71
+ }
72
+ };
73
+ var InvalidAgentResponseError = class extends Error {
74
+ constructor(message) {
75
+ super(message ?? "Invalid agent response type");
76
+ this.name = "InvalidAgentResponseError";
77
+ }
78
+ };
79
+ var AuthenticatedExtendedCardNotConfiguredError = class extends Error {
80
+ constructor(message) {
81
+ super(message ?? "Authenticated Extended Card not configured");
82
+ this.name = "AuthenticatedExtendedCardNotConfiguredError";
83
+ }
84
+ };
85
+
86
+ // src/client/transports/json_rpc_transport.ts
87
+ var JsonRpcTransport = class _JsonRpcTransport {
88
+ customFetchImpl;
89
+ endpoint;
90
+ requestIdCounter = 1;
91
+ constructor(options) {
92
+ this.endpoint = options.endpoint;
93
+ this.customFetchImpl = options.fetchImpl;
94
+ }
95
+ async getExtendedAgentCard(options, idOverride) {
96
+ const rpcResponse = await this._sendRpcRequest("agent/getAuthenticatedExtendedCard", void 0, idOverride, options);
97
+ return rpcResponse.result;
98
+ }
99
+ async sendMessage(params, options, idOverride) {
100
+ const rpcResponse = await this._sendRpcRequest(
101
+ "message/send",
102
+ params,
103
+ idOverride,
104
+ options
105
+ );
106
+ return rpcResponse.result;
107
+ }
108
+ async *sendMessageStream(params, options) {
109
+ yield* this._sendStreamingRequest("message/stream", params, options);
110
+ }
111
+ async setTaskPushNotificationConfig(params, options, idOverride) {
112
+ const rpcResponse = await this._sendRpcRequest("tasks/pushNotificationConfig/set", params, idOverride, options);
113
+ return rpcResponse.result;
114
+ }
115
+ async getTaskPushNotificationConfig(params, options, idOverride) {
116
+ const rpcResponse = await this._sendRpcRequest("tasks/pushNotificationConfig/get", params, idOverride, options);
117
+ return rpcResponse.result;
118
+ }
119
+ async listTaskPushNotificationConfig(params, options, idOverride) {
120
+ const rpcResponse = await this._sendRpcRequest("tasks/pushNotificationConfig/list", params, idOverride, options);
121
+ return rpcResponse.result;
122
+ }
123
+ async deleteTaskPushNotificationConfig(params, options, idOverride) {
124
+ await this._sendRpcRequest("tasks/pushNotificationConfig/delete", params, idOverride, options);
125
+ }
126
+ async getTask(params, options, idOverride) {
127
+ const rpcResponse = await this._sendRpcRequest(
128
+ "tasks/get",
129
+ params,
130
+ idOverride,
131
+ options
132
+ );
133
+ return rpcResponse.result;
134
+ }
135
+ async cancelTask(params, options, idOverride) {
136
+ const rpcResponse = await this._sendRpcRequest(
137
+ "tasks/cancel",
138
+ params,
139
+ idOverride,
140
+ options
141
+ );
142
+ return rpcResponse.result;
143
+ }
144
+ async *resubscribeTask(params, options) {
145
+ yield* this._sendStreamingRequest("tasks/resubscribe", params, options);
146
+ }
147
+ async callExtensionMethod(method, params, idOverride, options) {
148
+ return await this._sendRpcRequest(
149
+ method,
150
+ params,
151
+ idOverride,
152
+ options
153
+ );
154
+ }
155
+ _fetch(...args) {
156
+ if (this.customFetchImpl) {
157
+ return this.customFetchImpl(...args);
158
+ }
159
+ if (typeof fetch === "function") {
160
+ return fetch(...args);
161
+ }
162
+ throw new Error(
163
+ "A `fetch` implementation was not provided and is not available in the global scope. Please provide a `fetchImpl` in the A2ATransportOptions. "
164
+ );
165
+ }
166
+ async _sendRpcRequest(method, params, idOverride, options) {
167
+ const requestId = idOverride ?? this.requestIdCounter++;
168
+ const rpcRequest = {
169
+ jsonrpc: "2.0",
170
+ method,
171
+ params,
172
+ id: requestId
173
+ };
174
+ const httpResponse = await this._fetchRpc(rpcRequest, "application/json", options);
175
+ if (!httpResponse.ok) {
176
+ let errorBodyText = "(empty or non-JSON response)";
177
+ let errorJson;
178
+ try {
179
+ errorBodyText = await httpResponse.text();
180
+ errorJson = JSON.parse(errorBodyText);
181
+ } catch (e) {
182
+ throw new Error(
183
+ `HTTP error for ${method}! Status: ${httpResponse.status} ${httpResponse.statusText}. Response: ${errorBodyText}`,
184
+ { cause: e }
185
+ );
186
+ }
187
+ if (errorJson.jsonrpc && errorJson.error) {
188
+ throw _JsonRpcTransport.mapToError(errorJson);
189
+ } else {
190
+ throw new Error(
191
+ `HTTP error for ${method}! Status: ${httpResponse.status} ${httpResponse.statusText}. Response: ${errorBodyText}`
192
+ );
193
+ }
194
+ }
195
+ const rpcResponse = await httpResponse.json();
196
+ if (rpcResponse.id !== requestId) {
197
+ console.error(
198
+ `CRITICAL: RPC response ID mismatch for method ${method}. Expected ${requestId}, got ${rpcResponse.id}.`
199
+ );
200
+ }
201
+ if ("error" in rpcResponse) {
202
+ throw _JsonRpcTransport.mapToError(rpcResponse);
203
+ }
204
+ return rpcResponse;
205
+ }
206
+ async _fetchRpc(rpcRequest, acceptHeader = "application/json", options) {
207
+ const requestInit = {
208
+ method: "POST",
209
+ headers: {
210
+ ...options?.serviceParameters,
211
+ "Content-Type": "application/json",
212
+ Accept: acceptHeader
213
+ },
214
+ body: JSON.stringify(rpcRequest),
215
+ signal: options?.signal
216
+ };
217
+ return this._fetch(this.endpoint, requestInit);
218
+ }
219
+ async *_sendStreamingRequest(method, params, options) {
220
+ const clientRequestId = this.requestIdCounter++;
221
+ const rpcRequest = {
222
+ jsonrpc: "2.0",
223
+ method,
224
+ params,
225
+ id: clientRequestId
226
+ };
227
+ const response = await this._fetchRpc(rpcRequest, "text/event-stream", options);
228
+ if (!response.ok) {
229
+ let errorBody = "";
230
+ let errorJson;
231
+ try {
232
+ errorBody = await response.text();
233
+ errorJson = JSON.parse(errorBody);
234
+ } catch (e) {
235
+ throw new Error(
236
+ `HTTP error establishing stream for ${method}: ${response.status} ${response.statusText}. Response: ${errorBody || "(empty)"}`,
237
+ { cause: e }
238
+ );
239
+ }
240
+ if (errorJson.error) {
241
+ throw new Error(
242
+ `HTTP error establishing stream for ${method}: ${response.status} ${response.statusText}. RPC Error: ${errorJson.error.message} (Code: ${errorJson.error.code})`
243
+ );
244
+ }
245
+ throw new Error(
246
+ `HTTP error establishing stream for ${method}: ${response.status} ${response.statusText}`
247
+ );
248
+ }
249
+ if (!response.headers.get("Content-Type")?.startsWith("text/event-stream")) {
250
+ throw new Error(
251
+ `Invalid response Content-Type for SSE stream for ${method}. Expected 'text/event-stream'.`
252
+ );
253
+ }
254
+ yield* this._parseA2ASseStream(response, clientRequestId);
255
+ }
256
+ async *_parseA2ASseStream(response, originalRequestId) {
257
+ if (!response.body) {
258
+ throw new Error("SSE response body is undefined. Cannot read stream.");
259
+ }
260
+ const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
261
+ let buffer = "";
262
+ let eventDataBuffer = "";
263
+ try {
264
+ while (true) {
265
+ const { done, value } = await reader.read();
266
+ if (done) {
267
+ if (eventDataBuffer.trim()) {
268
+ const result = this._processSseEventData(
269
+ eventDataBuffer,
270
+ originalRequestId
271
+ );
272
+ yield result;
273
+ }
274
+ break;
275
+ }
276
+ buffer += value;
277
+ let lineEndIndex;
278
+ while ((lineEndIndex = buffer.indexOf("\n")) >= 0) {
279
+ const line = buffer.substring(0, lineEndIndex).trim();
280
+ buffer = buffer.substring(lineEndIndex + 1);
281
+ if (line === "") {
282
+ if (eventDataBuffer) {
283
+ const result = this._processSseEventData(
284
+ eventDataBuffer,
285
+ originalRequestId
286
+ );
287
+ yield result;
288
+ eventDataBuffer = "";
289
+ }
290
+ } else if (line.startsWith("data:")) {
291
+ eventDataBuffer += line.substring(5).trimStart() + "\n";
292
+ }
293
+ }
294
+ }
295
+ } catch (error) {
296
+ console.error(
297
+ "Error reading or parsing SSE stream:",
298
+ error instanceof Error && error.message || "Error unknown"
299
+ );
300
+ throw error;
301
+ } finally {
302
+ reader.releaseLock();
303
+ }
304
+ }
305
+ _processSseEventData(jsonData, originalRequestId) {
306
+ if (!jsonData.trim()) {
307
+ throw new Error("Attempted to process empty SSE event data.");
308
+ }
309
+ try {
310
+ const sseJsonRpcResponse = JSON.parse(jsonData.replace(/\n$/, ""));
311
+ const a2aStreamResponse = sseJsonRpcResponse;
312
+ if (a2aStreamResponse.id !== originalRequestId) {
313
+ console.warn(
314
+ `SSE Event's JSON-RPC response ID mismatch. Client request ID: ${originalRequestId}, event response ID: ${a2aStreamResponse.id}.`
315
+ );
316
+ }
317
+ if ("error" in a2aStreamResponse) {
318
+ const err = a2aStreamResponse.error;
319
+ throw new Error(
320
+ `SSE event contained an error: ${err.message} (Code: ${err.code}) Data: ${JSON.stringify(err.data || {})}`
321
+ );
322
+ }
323
+ if (!("result" in a2aStreamResponse) || typeof a2aStreamResponse.result === "undefined") {
324
+ throw new Error(`SSE event JSON-RPC response is missing 'result' field. Data: ${jsonData}`);
325
+ }
326
+ return a2aStreamResponse.result;
327
+ } catch (e) {
328
+ if (e instanceof Error && (e.message.startsWith("SSE event contained an error") || e.message.startsWith("SSE event JSON-RPC response is missing 'result' field"))) {
329
+ throw e;
330
+ }
331
+ console.error(
332
+ "Failed to parse SSE event data string or unexpected JSON-RPC structure:",
333
+ jsonData,
334
+ e
335
+ );
336
+ throw new Error(
337
+ `Failed to parse SSE event data: "${jsonData.substring(0, 100)}...". Original error: ${e instanceof Error && e.message || "Unknown error"}`
338
+ );
339
+ }
340
+ }
341
+ static mapToError(response) {
342
+ switch (response.error.code) {
343
+ case -32001:
344
+ return new TaskNotFoundJSONRPCError(response);
345
+ case -32002:
346
+ return new TaskNotCancelableJSONRPCError(response);
347
+ case -32003:
348
+ return new PushNotificationNotSupportedJSONRPCError(response);
349
+ case -32004:
350
+ return new UnsupportedOperationJSONRPCError(response);
351
+ case -32005:
352
+ return new ContentTypeNotSupportedJSONRPCError(response);
353
+ case -32006:
354
+ return new InvalidAgentResponseJSONRPCError(response);
355
+ case -32007:
356
+ return new AuthenticatedExtendedCardNotConfiguredJSONRPCError(response);
357
+ default:
358
+ return new JSONRPCTransportError(response);
359
+ }
360
+ }
361
+ };
362
+ var JsonRpcTransportFactory = class _JsonRpcTransportFactory {
363
+ constructor(options) {
364
+ this.options = options;
365
+ }
366
+ static name = "JSONRPC";
367
+ get protocolName() {
368
+ return _JsonRpcTransportFactory.name;
369
+ }
370
+ async create(url, _agentCard) {
371
+ return new JsonRpcTransport({
372
+ endpoint: url,
373
+ fetchImpl: this.options?.fetchImpl
374
+ });
375
+ }
376
+ };
377
+ var JSONRPCTransportError = class extends Error {
378
+ constructor(errorResponse) {
379
+ super(
380
+ `JSON-RPC error: ${errorResponse.error.message} (Code: ${errorResponse.error.code}) Data: ${JSON.stringify(errorResponse.error.data || {})}`
381
+ );
382
+ this.errorResponse = errorResponse;
383
+ }
384
+ };
385
+ var TaskNotFoundJSONRPCError = class extends TaskNotFoundError {
386
+ constructor(errorResponse) {
387
+ super();
388
+ this.errorResponse = errorResponse;
389
+ }
390
+ };
391
+ var TaskNotCancelableJSONRPCError = class extends TaskNotCancelableError {
392
+ constructor(errorResponse) {
393
+ super();
394
+ this.errorResponse = errorResponse;
395
+ }
396
+ };
397
+ var PushNotificationNotSupportedJSONRPCError = class extends PushNotificationNotSupportedError {
398
+ constructor(errorResponse) {
399
+ super();
400
+ this.errorResponse = errorResponse;
401
+ }
402
+ };
403
+ var UnsupportedOperationJSONRPCError = class extends UnsupportedOperationError {
404
+ constructor(errorResponse) {
405
+ super();
406
+ this.errorResponse = errorResponse;
407
+ }
408
+ };
409
+ var ContentTypeNotSupportedJSONRPCError = class extends ContentTypeNotSupportedError {
410
+ constructor(errorResponse) {
411
+ super();
412
+ this.errorResponse = errorResponse;
413
+ }
414
+ };
415
+ var InvalidAgentResponseJSONRPCError = class extends InvalidAgentResponseError {
416
+ constructor(errorResponse) {
417
+ super();
418
+ this.errorResponse = errorResponse;
419
+ }
420
+ };
421
+ var AuthenticatedExtendedCardNotConfiguredJSONRPCError = class extends AuthenticatedExtendedCardNotConfiguredError {
422
+ constructor(errorResponse) {
423
+ super();
424
+ this.errorResponse = errorResponse;
425
+ }
426
+ };
29
427
 
30
428
  // src/client/client.ts
31
429
  var A2AClient = class _A2AClient {
430
+ static emptyOptions = void 0;
32
431
  agentCardPromise;
33
- requestIdCounter = 1;
34
- serviceEndpointUrl;
35
- // To be populated from AgentCard after fetching
36
432
  customFetchImpl;
433
+ serviceEndpointUrl;
434
+ // To be populated from AgentCard after fetchin
435
+ // A2AClient is built around JSON-RPC types, so it will only support JSON-RPC transport, new client with transport agnostic interface is going to be created for multi-transport.
436
+ // New transport abstraction isn't going to expose individual transport specific fields, so to keep returning JSON-RPC IDs here for compatibility,
437
+ // keep counter here and pass it to JsonRpcTransport via an optional idOverride parameter (which is not visible via transport-agnostic A2ATransport interface).
438
+ transport;
439
+ requestIdCounter = 1;
37
440
  /**
38
441
  * Constructs an A2AClient instance from an AgentCard.
39
442
  * @param agentCard The AgentCard object.
@@ -42,18 +445,22 @@ var A2AClient = class _A2AClient {
42
445
  constructor(agentCard, options) {
43
446
  this.customFetchImpl = options?.fetchImpl;
44
447
  if (typeof agentCard === "string") {
45
- console.warn("Warning: Constructing A2AClient with a URL is deprecated. Please use A2AClient.fromCardUrl() instead.");
448
+ console.warn(
449
+ "Warning: Constructing A2AClient with a URL is deprecated. Please use A2AClient.fromCardUrl() instead."
450
+ );
46
451
  this.agentCardPromise = this._fetchAndCacheAgentCard(agentCard, options?.agentCardPath);
47
452
  } else {
48
453
  if (!agentCard.url) {
49
- throw new Error("Provided Agent Card does not contain a valid 'url' for the service endpoint.");
454
+ throw new Error(
455
+ "Provided Agent Card does not contain a valid 'url' for the service endpoint."
456
+ );
50
457
  }
51
458
  this.serviceEndpointUrl = agentCard.url;
52
459
  this.agentCardPromise = Promise.resolve(agentCard);
53
460
  }
54
461
  }
55
462
  /**
56
- * Dynamically resolves the fetch implementation to use for requests.
463
+ * Dynamically resolves the fetch implementation to use for requests.
57
464
  * Prefers a custom implementation if provided, otherwise falls back to the global fetch.
58
465
  * @returns The fetch implementation.
59
466
  * @param args Arguments to pass to the fetch implementation.
@@ -79,7 +486,7 @@ var A2AClient = class _A2AClient {
79
486
  static async fromCardUrl(agentCardUrl, options) {
80
487
  const fetchImpl = options?.fetchImpl;
81
488
  const requestInit = {
82
- headers: { "Accept": "application/json" }
489
+ headers: { Accept: "application/json" }
83
490
  };
84
491
  let response;
85
492
  if (fetchImpl) {
@@ -92,76 +499,21 @@ var A2AClient = class _A2AClient {
92
499
  );
93
500
  }
94
501
  if (!response.ok) {
95
- throw new Error(`Failed to fetch Agent Card from ${agentCardUrl}: ${response.status} ${response.statusText}`);
502
+ throw new Error(
503
+ `Failed to fetch Agent Card from ${agentCardUrl}: ${response.status} ${response.statusText}`
504
+ );
96
505
  }
97
506
  let agentCard;
98
507
  try {
99
508
  agentCard = await response.json();
100
509
  } catch (error) {
101
510
  console.error("Failed to parse Agent Card JSON:", error);
102
- throw new Error(`Failed to parse Agent Card JSON from ${agentCardUrl}. Original error: ${error.message}`);
511
+ throw new Error(
512
+ `Failed to parse Agent Card JSON from ${agentCardUrl}. Original error: ${error.message}`
513
+ );
103
514
  }
104
515
  return new _A2AClient(agentCard, options);
105
516
  }
106
- /**
107
- * Helper method to make a generic JSON-RPC POST request.
108
- * @param method The RPC method name.
109
- * @param params The parameters for the RPC method.
110
- * @returns A Promise that resolves to the RPC response.
111
- */
112
- async _postRpcRequest(method, params) {
113
- const endpoint = await this._getServiceEndpoint();
114
- const requestId = this.requestIdCounter++;
115
- const rpcRequest = {
116
- jsonrpc: "2.0",
117
- method,
118
- params,
119
- // Cast because TParams structure varies per method
120
- id: requestId
121
- };
122
- const httpResponse = await this._fetchRpc(endpoint, rpcRequest);
123
- if (!httpResponse.ok) {
124
- let errorBodyText = "(empty or non-JSON response)";
125
- try {
126
- errorBodyText = await httpResponse.text();
127
- const errorJson = JSON.parse(errorBodyText);
128
- if (errorJson.jsonrpc && errorJson.error) {
129
- return errorJson;
130
- } else if (!errorJson.jsonrpc && errorJson.error) {
131
- throw new Error(`RPC error for ${method}: ${errorJson.error.message} (Code: ${errorJson.error.code}, HTTP Status: ${httpResponse.status}) Data: ${JSON.stringify(errorJson.error.data || {})}`);
132
- } else if (!errorJson.jsonrpc) {
133
- throw new Error(`HTTP error for ${method}! Status: ${httpResponse.status} ${httpResponse.statusText}. Response: ${errorBodyText}`);
134
- }
135
- } catch (e) {
136
- if (e.message.startsWith("RPC error for") || e.message.startsWith("HTTP error for")) throw e;
137
- throw new Error(`HTTP error for ${method}! Status: ${httpResponse.status} ${httpResponse.statusText}. Response: ${errorBodyText}`);
138
- }
139
- }
140
- const rpcResponse = await httpResponse.json();
141
- if (rpcResponse.id !== requestId) {
142
- console.error(`CRITICAL: RPC response ID mismatch for method ${method}. Expected ${requestId}, got ${rpcResponse.id}. This may lead to incorrect response handling.`);
143
- }
144
- return rpcResponse;
145
- }
146
- /**
147
- * Internal helper method to fetch the RPC service endpoint.
148
- * @param url The URL to fetch.
149
- * @param rpcRequest The JSON-RPC request to send.
150
- * @param acceptHeader The Accept header to use. Defaults to "application/json".
151
- * @returns A Promise that resolves to the fetch HTTP response.
152
- */
153
- async _fetchRpc(url, rpcRequest, acceptHeader = "application/json") {
154
- const requestInit = {
155
- method: "POST",
156
- headers: {
157
- "Content-Type": "application/json",
158
- "Accept": acceptHeader
159
- // Expect JSON response for non-streaming requests
160
- },
161
- body: JSON.stringify(rpcRequest)
162
- };
163
- return this._fetch(url, requestInit);
164
- }
165
517
  /**
166
518
  * Sends a message to the agent.
167
519
  * The behavior (blocking/non-blocking) and push notification configuration
@@ -171,7 +523,10 @@ var A2AClient = class _A2AClient {
171
523
  * @returns A Promise resolving to SendMessageResponse, which can be a Message, Task, or an error.
172
524
  */
173
525
  async sendMessage(params) {
174
- return this._postRpcRequest("message/send", params);
526
+ return await this.invokeJsonRpc(
527
+ (t, p, id) => t.sendMessage(p, _A2AClient.emptyOptions, id),
528
+ params
529
+ );
175
530
  }
176
531
  /**
177
532
  * Sends a message to the agent and streams back responses using Server-Sent Events (SSE).
@@ -185,36 +540,12 @@ var A2AClient = class _A2AClient {
185
540
  async *sendMessageStream(params) {
186
541
  const agentCard = await this.agentCardPromise;
187
542
  if (!agentCard.capabilities?.streaming) {
188
- throw new Error("Agent does not support streaming (AgentCard.capabilities.streaming is not true).");
189
- }
190
- const endpoint = await this._getServiceEndpoint();
191
- const clientRequestId = this.requestIdCounter++;
192
- const rpcRequest = {
193
- // This is the initial JSON-RPC request to establish the stream
194
- jsonrpc: "2.0",
195
- method: "message/stream",
196
- params,
197
- id: clientRequestId
198
- };
199
- const response = await this._fetchRpc(endpoint, rpcRequest, "text/event-stream");
200
- if (!response.ok) {
201
- let errorBody = "";
202
- try {
203
- errorBody = await response.text();
204
- const errorJson = JSON.parse(errorBody);
205
- if (errorJson.error) {
206
- throw new Error(`HTTP error establishing stream for message/stream: ${response.status} ${response.statusText}. RPC Error: ${errorJson.error.message} (Code: ${errorJson.error.code})`);
207
- }
208
- } catch (e) {
209
- if (e.message.startsWith("HTTP error establishing stream")) throw e;
210
- throw new Error(`HTTP error establishing stream for message/stream: ${response.status} ${response.statusText}. Response: ${errorBody || "(empty)"}`);
211
- }
212
- throw new Error(`HTTP error establishing stream for message/stream: ${response.status} ${response.statusText}`);
213
- }
214
- if (!response.headers.get("Content-Type")?.startsWith("text/event-stream")) {
215
- throw new Error("Invalid response Content-Type for SSE stream. Expected 'text/event-stream'.");
543
+ throw new Error(
544
+ "Agent does not support streaming (AgentCard.capabilities.streaming is not true)."
545
+ );
216
546
  }
217
- yield* this._parseA2ASseStream(response, clientRequestId);
547
+ const transport = await this._getOrCreateTransport();
548
+ yield* transport.sendMessageStream(params);
218
549
  }
219
550
  /**
220
551
  * Sets or updates the push notification configuration for a given task.
@@ -225,12 +556,11 @@ var A2AClient = class _A2AClient {
225
556
  async setTaskPushNotificationConfig(params) {
226
557
  const agentCard = await this.agentCardPromise;
227
558
  if (!agentCard.capabilities?.pushNotifications) {
228
- throw new Error("Agent does not support push notifications (AgentCard.capabilities.pushNotifications is not true).");
559
+ throw new Error(
560
+ "Agent does not support push notifications (AgentCard.capabilities.pushNotifications is not true)."
561
+ );
229
562
  }
230
- return this._postRpcRequest(
231
- "tasks/pushNotificationConfig/set",
232
- params
233
- );
563
+ return await this.invokeJsonRpc((t, p, id) => t.setTaskPushNotificationConfig(p, _A2AClient.emptyOptions, id), params);
234
564
  }
235
565
  /**
236
566
  * Gets the push notification configuration for a given task.
@@ -238,8 +568,8 @@ var A2AClient = class _A2AClient {
238
568
  * @returns A Promise resolving to GetTaskPushNotificationConfigResponse.
239
569
  */
240
570
  async getTaskPushNotificationConfig(params) {
241
- return this._postRpcRequest(
242
- "tasks/pushNotificationConfig/get",
571
+ return await this.invokeJsonRpc(
572
+ (t, p, id) => t.getTaskPushNotificationConfig(p, _A2AClient.emptyOptions, id),
243
573
  params
244
574
  );
245
575
  }
@@ -249,10 +579,7 @@ var A2AClient = class _A2AClient {
249
579
  * @returns A Promise resolving to ListTaskPushNotificationConfigResponse.
250
580
  */
251
581
  async listTaskPushNotificationConfig(params) {
252
- return this._postRpcRequest(
253
- "tasks/pushNotificationConfig/list",
254
- params
255
- );
582
+ return await this.invokeJsonRpc((t, p, id) => t.listTaskPushNotificationConfig(p, _A2AClient.emptyOptions, id), params);
256
583
  }
257
584
  /**
258
585
  * Deletes the push notification configuration for a given task.
@@ -260,10 +587,7 @@ var A2AClient = class _A2AClient {
260
587
  * @returns A Promise resolving to DeleteTaskPushNotificationConfigResponse.
261
588
  */
262
589
  async deleteTaskPushNotificationConfig(params) {
263
- return this._postRpcRequest(
264
- "tasks/pushNotificationConfig/delete",
265
- params
266
- );
590
+ return await this.invokeJsonRpc((t, p, id) => t.deleteTaskPushNotificationConfig(p, _A2AClient.emptyOptions, id), params);
267
591
  }
268
592
  /**
269
593
  * Retrieves a task by its ID.
@@ -271,7 +595,10 @@ var A2AClient = class _A2AClient {
271
595
  * @returns A Promise resolving to GetTaskResponse, which contains the Task object or an error.
272
596
  */
273
597
  async getTask(params) {
274
- return this._postRpcRequest("tasks/get", params);
598
+ return await this.invokeJsonRpc(
599
+ (t, p, id) => t.getTask(p, _A2AClient.emptyOptions, id),
600
+ params
601
+ );
275
602
  }
276
603
  /**
277
604
  * Cancels a task by its ID.
@@ -279,18 +606,34 @@ var A2AClient = class _A2AClient {
279
606
  * @returns A Promise resolving to CancelTaskResponse, which contains the updated Task object or an error.
280
607
  */
281
608
  async cancelTask(params) {
282
- return this._postRpcRequest("tasks/cancel", params);
609
+ return await this.invokeJsonRpc(
610
+ (t, p, id) => t.cancelTask(p, _A2AClient.emptyOptions, id),
611
+ params
612
+ );
283
613
  }
284
614
  /**
285
615
  * @template TExtensionParams The type of parameters for the custom extension method.
286
- * @template TExtensionResponse The type of response expected from the custom extension method.
616
+ * @template TExtensionResponse The type of response expected from the custom extension method.
287
617
  * This should extend JSONRPCResponse. This ensures the extension response is still a valid A2A response.
288
618
  * @param method Custom JSON-RPC method defined in the AgentCard's extensions.
289
619
  * @param params Extension paramters defined in the AgentCard's extensions.
290
620
  * @returns A Promise that resolves to the RPC response.
291
621
  */
292
622
  async callExtensionMethod(method, params) {
293
- return this._postRpcRequest(method, params);
623
+ const transport = await this._getOrCreateTransport();
624
+ try {
625
+ return await transport.callExtensionMethod(
626
+ method,
627
+ params,
628
+ this.requestIdCounter++
629
+ );
630
+ } catch (e) {
631
+ const errorResponse = extractJSONRPCError(e);
632
+ if (errorResponse) {
633
+ return errorResponse;
634
+ }
635
+ throw e;
636
+ }
294
637
  }
295
638
  /**
296
639
  * Resubscribes to a task's event stream using Server-Sent Events (SSE).
@@ -304,129 +647,8 @@ var A2AClient = class _A2AClient {
304
647
  if (!agentCard.capabilities?.streaming) {
305
648
  throw new Error("Agent does not support streaming (required for tasks/resubscribe).");
306
649
  }
307
- const endpoint = await this._getServiceEndpoint();
308
- const clientRequestId = this.requestIdCounter++;
309
- const rpcRequest = {
310
- // Initial JSON-RPC request to establish the stream
311
- jsonrpc: "2.0",
312
- method: "tasks/resubscribe",
313
- params,
314
- id: clientRequestId
315
- };
316
- const response = await this._fetch(endpoint, {
317
- method: "POST",
318
- headers: {
319
- "Content-Type": "application/json",
320
- "Accept": "text/event-stream"
321
- },
322
- body: JSON.stringify(rpcRequest)
323
- });
324
- if (!response.ok) {
325
- let errorBody = "";
326
- try {
327
- errorBody = await response.text();
328
- const errorJson = JSON.parse(errorBody);
329
- if (errorJson.error) {
330
- throw new Error(`HTTP error establishing stream for tasks/resubscribe: ${response.status} ${response.statusText}. RPC Error: ${errorJson.error.message} (Code: ${errorJson.error.code})`);
331
- }
332
- } catch (e) {
333
- if (e.message.startsWith("HTTP error establishing stream")) throw e;
334
- throw new Error(`HTTP error establishing stream for tasks/resubscribe: ${response.status} ${response.statusText}. Response: ${errorBody || "(empty)"}`);
335
- }
336
- throw new Error(`HTTP error establishing stream for tasks/resubscribe: ${response.status} ${response.statusText}`);
337
- }
338
- if (!response.headers.get("Content-Type")?.startsWith("text/event-stream")) {
339
- throw new Error("Invalid response Content-Type for SSE stream on resubscribe. Expected 'text/event-stream'.");
340
- }
341
- yield* this._parseA2ASseStream(response, clientRequestId);
342
- }
343
- /**
344
- * Parses an HTTP response body as an A2A Server-Sent Event stream.
345
- * Each 'data' field of an SSE event is expected to be a JSON-RPC 2.0 Response object,
346
- * specifically a SendStreamingMessageResponse (or similar structure for resubscribe).
347
- * @param response The HTTP Response object whose body is the SSE stream.
348
- * @param originalRequestId The ID of the client's JSON-RPC request that initiated this stream.
349
- * Used to validate the `id` in the streamed JSON-RPC responses.
350
- * @returns An AsyncGenerator yielding the `result` field of each valid JSON-RPC success response from the stream.
351
- */
352
- async *_parseA2ASseStream(response, originalRequestId) {
353
- if (!response.body) {
354
- throw new Error("SSE response body is undefined. Cannot read stream.");
355
- }
356
- const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
357
- let buffer = "";
358
- let eventDataBuffer = "";
359
- try {
360
- while (true) {
361
- const { done, value } = await reader.read();
362
- if (done) {
363
- if (eventDataBuffer.trim()) {
364
- const result = this._processSseEventData(eventDataBuffer, originalRequestId);
365
- yield result;
366
- }
367
- break;
368
- }
369
- buffer += value;
370
- let lineEndIndex;
371
- while ((lineEndIndex = buffer.indexOf("\n")) >= 0) {
372
- const line = buffer.substring(0, lineEndIndex).trim();
373
- buffer = buffer.substring(lineEndIndex + 1);
374
- if (line === "") {
375
- if (eventDataBuffer) {
376
- const result = this._processSseEventData(eventDataBuffer, originalRequestId);
377
- yield result;
378
- eventDataBuffer = "";
379
- }
380
- } else if (line.startsWith("data:")) {
381
- eventDataBuffer += line.substring(5).trimStart() + "\n";
382
- } else if (line.startsWith(":")) {
383
- } else if (line.includes(":")) {
384
- }
385
- }
386
- }
387
- } catch (error) {
388
- console.error("Error reading or parsing SSE stream:", error.message);
389
- throw error;
390
- } finally {
391
- reader.releaseLock();
392
- }
393
- }
394
- /**
395
- * Processes a single SSE event's data string, expecting it to be a JSON-RPC response.
396
- * @param jsonData The string content from one or more 'data:' lines of an SSE event.
397
- * @param originalRequestId The ID of the client's request that initiated the stream.
398
- * @returns The `result` field of the parsed JSON-RPC success response.
399
- * @throws Error if data is not valid JSON, not a valid JSON-RPC response, an error response, or ID mismatch.
400
- */
401
- _processSseEventData(jsonData, originalRequestId) {
402
- if (!jsonData.trim()) {
403
- throw new Error("Attempted to process empty SSE event data.");
404
- }
405
- try {
406
- const sseJsonRpcResponse = JSON.parse(jsonData.replace(/\n$/, ""));
407
- const a2aStreamResponse = sseJsonRpcResponse;
408
- if (a2aStreamResponse.id !== originalRequestId) {
409
- console.warn(`SSE Event's JSON-RPC response ID mismatch. Client request ID: ${originalRequestId}, event response ID: ${a2aStreamResponse.id}.`);
410
- }
411
- if (this.isErrorResponse(a2aStreamResponse)) {
412
- const err = a2aStreamResponse.error;
413
- throw new Error(`SSE event contained an error: ${err.message} (Code: ${err.code}) Data: ${JSON.stringify(err.data || {})}`);
414
- }
415
- if (!("result" in a2aStreamResponse) || typeof a2aStreamResponse.result === "undefined") {
416
- throw new Error(`SSE event JSON-RPC response is missing 'result' field. Data: ${jsonData}`);
417
- }
418
- const successResponse = a2aStreamResponse;
419
- return successResponse.result;
420
- } catch (e) {
421
- if (e.message.startsWith("SSE event contained an error") || e.message.startsWith("SSE event JSON-RPC response is missing 'result' field")) {
422
- throw e;
423
- }
424
- console.error("Failed to parse SSE event data string or unexpected JSON-RPC structure:", jsonData, e);
425
- throw new Error(`Failed to parse SSE event data: "${jsonData.substring(0, 100)}...". Original error: ${e.message}`);
426
- }
427
- }
428
- isErrorResponse(response) {
429
- return "error" in response;
650
+ const transport = await this._getOrCreateTransport();
651
+ yield* transport.resubscribeTask(params);
430
652
  }
431
653
  ////////////////////////////////////////////////////////////////////////////////
432
654
  // Functions used to support old A2AClient Constructor to be deprecated soon
@@ -437,7 +659,16 @@ var A2AClient = class _A2AClient {
437
659
  // * getAgentCard changed to this.agentCard
438
660
  // * delete resolveAgentCardUrl(), _fetchAndCacheAgentCard(),
439
661
  // agentCardPath from A2AClientOptions
662
+ // * delete _getOrCreateTransport
440
663
  ////////////////////////////////////////////////////////////////////////////////
664
+ async _getOrCreateTransport() {
665
+ if (this.transport) {
666
+ return this.transport;
667
+ }
668
+ const endpoint = await this._getServiceEndpoint();
669
+ this.transport = new JsonRpcTransport({ fetchImpl: this.customFetchImpl, endpoint });
670
+ return this.transport;
671
+ }
441
672
  /**
442
673
  * Fetches the Agent Card from the agent's well-known URI and caches its service endpoint URL.
443
674
  * This method is called by the constructor.
@@ -449,14 +680,18 @@ var A2AClient = class _A2AClient {
449
680
  try {
450
681
  const agentCardUrl = this.resolveAgentCardUrl(agentBaseUrl, agentCardPath);
451
682
  const response = await this._fetch(agentCardUrl, {
452
- headers: { "Accept": "application/json" }
683
+ headers: { Accept: "application/json" }
453
684
  });
454
685
  if (!response.ok) {
455
- throw new Error(`Failed to fetch Agent Card from ${agentCardUrl}: ${response.status} ${response.statusText}`);
686
+ throw new Error(
687
+ `Failed to fetch Agent Card from ${agentCardUrl}: ${response.status} ${response.statusText}`
688
+ );
456
689
  }
457
690
  const agentCard = await response.json();
458
691
  if (!agentCard.url) {
459
- throw new Error("Fetched Agent Card does not contain a valid 'url' for the service endpoint.");
692
+ throw new Error(
693
+ "Fetched Agent Card does not contain a valid 'url' for the service endpoint."
694
+ );
460
695
  }
461
696
  this.serviceEndpointUrl = agentCard.url;
462
697
  return agentCard;
@@ -466,22 +701,24 @@ var A2AClient = class _A2AClient {
466
701
  }
467
702
  }
468
703
  /**
469
- * Retrieves the Agent Card.
470
- * If an `agentBaseUrl` is provided, it fetches the card from that specific URL.
471
- * Otherwise, it returns the card fetched and cached during client construction.
472
- * @param agentBaseUrl Optional. The base URL of the agent to fetch the card from.
473
- * @param agentCardPath path to the agent card, defaults to .well-known/agent-card.json
474
- * If provided, this will fetch a new card, not use the cached one from the constructor's URL.
475
- * @returns A Promise that resolves to the AgentCard.
476
- */
704
+ * Retrieves the Agent Card.
705
+ * If an `agentBaseUrl` is provided, it fetches the card from that specific URL.
706
+ * Otherwise, it returns the card fetched and cached during client construction.
707
+ * @param agentBaseUrl Optional. The base URL of the agent to fetch the card from.
708
+ * @param agentCardPath path to the agent card, defaults to .well-known/agent-card.json
709
+ * If provided, this will fetch a new card, not use the cached one from the constructor's URL.
710
+ * @returns A Promise that resolves to the AgentCard.
711
+ */
477
712
  async getAgentCard(agentBaseUrl, agentCardPath) {
478
713
  if (agentBaseUrl) {
479
714
  const agentCardUrl = this.resolveAgentCardUrl(agentBaseUrl, agentCardPath);
480
715
  const response = await this._fetch(agentCardUrl, {
481
- headers: { "Accept": "application/json" }
716
+ headers: { Accept: "application/json" }
482
717
  });
483
718
  if (!response.ok) {
484
- throw new Error(`Failed to fetch Agent Card from ${agentCardUrl}: ${response.status} ${response.statusText}`);
719
+ throw new Error(
720
+ `Failed to fetch Agent Card from ${agentCardUrl}: ${response.status} ${response.statusText}`
721
+ );
485
722
  }
486
723
  return await response.json();
487
724
  }
@@ -505,11 +742,39 @@ var A2AClient = class _A2AClient {
505
742
  }
506
743
  await this.agentCardPromise;
507
744
  if (!this.serviceEndpointUrl) {
508
- throw new Error("Agent Card URL for RPC endpoint is not available. Fetching might have failed.");
745
+ throw new Error(
746
+ "Agent Card URL for RPC endpoint is not available. Fetching might have failed."
747
+ );
509
748
  }
510
749
  return this.serviceEndpointUrl;
511
750
  }
751
+ async invokeJsonRpc(caller, params) {
752
+ const transport = await this._getOrCreateTransport();
753
+ const requestId = this.requestIdCounter++;
754
+ try {
755
+ const result = await caller(transport, params, requestId);
756
+ return {
757
+ id: requestId,
758
+ jsonrpc: "2.0",
759
+ result: result ?? null
760
+ // JSON-RPC requires result property on success, it will be null for "void" methods.
761
+ };
762
+ } catch (e) {
763
+ const errorResponse = extractJSONRPCError(e);
764
+ if (errorResponse) {
765
+ return errorResponse;
766
+ }
767
+ throw e;
768
+ }
769
+ }
512
770
  };
771
+ function extractJSONRPCError(error) {
772
+ if (error instanceof Object && "errorResponse" in error && error.errorResponse instanceof Object && "jsonrpc" in error.errorResponse && error.errorResponse.jsonrpc === "2.0" && "error" in error.errorResponse && error.errorResponse.error !== null) {
773
+ return error.errorResponse;
774
+ } else {
775
+ return void 0;
776
+ }
777
+ }
513
778
 
514
779
  // src/client/auth-handler.ts
515
780
  function createAuthenticatingFetchWithRetry(fetchImpl, authHandler) {
@@ -543,8 +808,544 @@ function createAuthenticatingFetchWithRetry(fetchImpl, authHandler) {
543
808
  Object.defineProperties(authFetch, Object.getOwnPropertyDescriptors(fetchImpl));
544
809
  return authFetch;
545
810
  }
811
+
812
+ // src/client/card-resolver.ts
813
+ var DefaultAgentCardResolver = class {
814
+ constructor(options) {
815
+ this.options = options;
816
+ }
817
+ /**
818
+ * Fetches the agent card based on provided base URL and path.
819
+ * Path is selected in the following order:
820
+ * 1) path parameter
821
+ * 2) path from options
822
+ * 3) .well-known/agent-card.json
823
+ */
824
+ async resolve(baseUrl, path) {
825
+ const agentCardUrl = new URL(path ?? this.options?.path ?? AGENT_CARD_PATH, baseUrl);
826
+ const response = await this.fetchImpl(agentCardUrl);
827
+ if (!response.ok) {
828
+ throw new Error(`Failed to fetch Agent Card from ${agentCardUrl}: ${response.status}`);
829
+ }
830
+ return await response.json();
831
+ }
832
+ fetchImpl(...args) {
833
+ if (this.options?.fetchImpl) {
834
+ return this.options.fetchImpl(...args);
835
+ }
836
+ return fetch(...args);
837
+ }
838
+ };
839
+ var AgentCardResolver = {
840
+ default: new DefaultAgentCardResolver()
841
+ };
842
+
843
+ // src/client/multitransport-client.ts
844
+ var Client = class {
845
+ constructor(transport, agentCard, config) {
846
+ this.transport = transport;
847
+ this.agentCard = agentCard;
848
+ this.config = config;
849
+ }
850
+ /**
851
+ * If the current agent card supports the extended feature, it will try to fetch the extended agent card from the server,
852
+ * Otherwise it will return the current agent card value.
853
+ */
854
+ async getAgentCard(options) {
855
+ if (this.agentCard.supportsAuthenticatedExtendedCard) {
856
+ this.agentCard = await this.executeWithInterceptors(
857
+ { method: "getAgentCard" },
858
+ options,
859
+ (_, options2) => this.transport.getExtendedAgentCard(options2)
860
+ );
861
+ }
862
+ return this.agentCard;
863
+ }
864
+ /**
865
+ * Sends a message to an agent to initiate a new interaction or to continue an existing one.
866
+ * Uses blocking mode by default.
867
+ */
868
+ sendMessage(params, options) {
869
+ params = this.applyClientConfig({
870
+ params,
871
+ blocking: !(this.config?.polling ?? false)
872
+ });
873
+ return this.executeWithInterceptors(
874
+ { method: "sendMessage", value: params },
875
+ options,
876
+ this.transport.sendMessage.bind(this.transport)
877
+ );
878
+ }
879
+ /**
880
+ * Sends a message to an agent to initiate/continue a task AND subscribes the client to real-time updates for that task.
881
+ * Performs fallback to non-streaming if not supported by the agent.
882
+ */
883
+ async *sendMessageStream(params, options) {
884
+ const method = "sendMessageStream";
885
+ params = this.applyClientConfig({ params, blocking: true });
886
+ const beforeArgs = {
887
+ input: { method, value: params },
888
+ agentCard: this.agentCard,
889
+ options
890
+ };
891
+ const beforeResult = await this.interceptBefore(beforeArgs);
892
+ if (beforeResult) {
893
+ const earlyReturn = beforeResult.earlyReturn.value;
894
+ const afterArgs = {
895
+ result: { method, value: earlyReturn },
896
+ agentCard: this.agentCard,
897
+ options: beforeArgs.options
898
+ };
899
+ await this.interceptAfter(afterArgs, beforeResult.executed);
900
+ yield afterArgs.result.value;
901
+ return;
902
+ }
903
+ if (!this.agentCard.capabilities.streaming) {
904
+ const result = await this.transport.sendMessage(beforeArgs.input.value, beforeArgs.options);
905
+ const afterArgs = {
906
+ result: { method, value: result },
907
+ agentCard: this.agentCard,
908
+ options: beforeArgs.options
909
+ };
910
+ await this.interceptAfter(afterArgs);
911
+ yield afterArgs.result.value;
912
+ return;
913
+ }
914
+ for await (const event of this.transport.sendMessageStream(
915
+ beforeArgs.input.value,
916
+ beforeArgs.options
917
+ )) {
918
+ const afterArgs = {
919
+ result: { method, value: event },
920
+ agentCard: this.agentCard,
921
+ options: beforeArgs.options
922
+ };
923
+ await this.interceptAfter(afterArgs);
924
+ yield afterArgs.result.value;
925
+ if (afterArgs.earlyReturn) {
926
+ return;
927
+ }
928
+ }
929
+ }
930
+ /**
931
+ * Sets or updates the push notification configuration for a specified task.
932
+ * Requires the server to have AgentCard.capabilities.pushNotifications: true.
933
+ */
934
+ setTaskPushNotificationConfig(params, options) {
935
+ if (!this.agentCard.capabilities.pushNotifications) {
936
+ throw new PushNotificationNotSupportedError();
937
+ }
938
+ return this.executeWithInterceptors(
939
+ { method: "setTaskPushNotificationConfig", value: params },
940
+ options,
941
+ this.transport.setTaskPushNotificationConfig.bind(this.transport)
942
+ );
943
+ }
944
+ /**
945
+ * Retrieves the current push notification configuration for a specified task.
946
+ * Requires the server to have AgentCard.capabilities.pushNotifications: true.
947
+ */
948
+ getTaskPushNotificationConfig(params, options) {
949
+ if (!this.agentCard.capabilities.pushNotifications) {
950
+ throw new PushNotificationNotSupportedError();
951
+ }
952
+ return this.executeWithInterceptors(
953
+ { method: "getTaskPushNotificationConfig", value: params },
954
+ options,
955
+ this.transport.getTaskPushNotificationConfig.bind(this.transport)
956
+ );
957
+ }
958
+ /**
959
+ * Retrieves the associated push notification configurations for a specified task.
960
+ * Requires the server to have AgentCard.capabilities.pushNotifications: true.
961
+ */
962
+ listTaskPushNotificationConfig(params, options) {
963
+ if (!this.agentCard.capabilities.pushNotifications) {
964
+ throw new PushNotificationNotSupportedError();
965
+ }
966
+ return this.executeWithInterceptors(
967
+ { method: "listTaskPushNotificationConfig", value: params },
968
+ options,
969
+ this.transport.listTaskPushNotificationConfig.bind(this.transport)
970
+ );
971
+ }
972
+ /**
973
+ * Deletes an associated push notification configuration for a task.
974
+ */
975
+ deleteTaskPushNotificationConfig(params, options) {
976
+ return this.executeWithInterceptors(
977
+ { method: "deleteTaskPushNotificationConfig", value: params },
978
+ options,
979
+ this.transport.deleteTaskPushNotificationConfig.bind(this.transport)
980
+ );
981
+ }
982
+ /**
983
+ * Retrieves the current state (including status, artifacts, and optionally history) of a previously initiated task.
984
+ */
985
+ getTask(params, options) {
986
+ return this.executeWithInterceptors(
987
+ { method: "getTask", value: params },
988
+ options,
989
+ this.transport.getTask.bind(this.transport)
990
+ );
991
+ }
992
+ /**
993
+ * Requests the cancellation of an ongoing task. The server will attempt to cancel the task,
994
+ * but success is not guaranteed (e.g., the task might have already completed or failed, or cancellation might not be supported at its current stage).
995
+ */
996
+ cancelTask(params, options) {
997
+ return this.executeWithInterceptors(
998
+ { method: "cancelTask", value: params },
999
+ options,
1000
+ this.transport.cancelTask.bind(this.transport)
1001
+ );
1002
+ }
1003
+ /**
1004
+ * Allows a client to reconnect to an updates stream for an ongoing task after a previous connection was interrupted.
1005
+ */
1006
+ async *resubscribeTask(params, options) {
1007
+ const method = "resubscribeTask";
1008
+ const beforeArgs = {
1009
+ input: { method, value: params },
1010
+ agentCard: this.agentCard,
1011
+ options
1012
+ };
1013
+ const beforeResult = await this.interceptBefore(beforeArgs);
1014
+ if (beforeResult) {
1015
+ const earlyReturn = beforeResult.earlyReturn.value;
1016
+ const afterArgs = {
1017
+ result: { method, value: earlyReturn },
1018
+ agentCard: this.agentCard,
1019
+ options: beforeArgs.options
1020
+ };
1021
+ await this.interceptAfter(afterArgs, beforeResult.executed);
1022
+ yield afterArgs.result.value;
1023
+ return;
1024
+ }
1025
+ for await (const event of this.transport.resubscribeTask(
1026
+ beforeArgs.input.value,
1027
+ beforeArgs.options
1028
+ )) {
1029
+ const afterArgs = {
1030
+ result: { method, value: event },
1031
+ agentCard: this.agentCard,
1032
+ options: beforeArgs.options
1033
+ };
1034
+ await this.interceptAfter(afterArgs);
1035
+ yield afterArgs.result.value;
1036
+ if (afterArgs.earlyReturn) {
1037
+ return;
1038
+ }
1039
+ }
1040
+ }
1041
+ applyClientConfig({
1042
+ params,
1043
+ blocking
1044
+ }) {
1045
+ const result = { ...params, configuration: params.configuration ?? {} };
1046
+ if (!result.configuration.acceptedOutputModes && this.config?.acceptedOutputModes) {
1047
+ result.configuration.acceptedOutputModes = this.config.acceptedOutputModes;
1048
+ }
1049
+ if (!result.configuration.pushNotificationConfig && this.config?.pushNotificationConfig) {
1050
+ result.configuration.pushNotificationConfig = this.config.pushNotificationConfig;
1051
+ }
1052
+ result.configuration.blocking ??= blocking;
1053
+ return result;
1054
+ }
1055
+ async executeWithInterceptors(input, options, transportCall) {
1056
+ const beforeArgs = {
1057
+ input,
1058
+ agentCard: this.agentCard,
1059
+ options
1060
+ };
1061
+ const beforeResult = await this.interceptBefore(beforeArgs);
1062
+ if (beforeResult) {
1063
+ const afterArgs2 = {
1064
+ result: {
1065
+ method: input.method,
1066
+ value: beforeResult.earlyReturn.value
1067
+ },
1068
+ agentCard: this.agentCard,
1069
+ options: beforeArgs.options
1070
+ };
1071
+ await this.interceptAfter(afterArgs2, beforeResult.executed);
1072
+ return afterArgs2.result.value;
1073
+ }
1074
+ const result = await transportCall(beforeArgs.input.value, beforeArgs.options);
1075
+ const afterArgs = {
1076
+ result: { method: input.method, value: result },
1077
+ agentCard: this.agentCard,
1078
+ options: beforeArgs.options
1079
+ };
1080
+ await this.interceptAfter(afterArgs);
1081
+ return afterArgs.result.value;
1082
+ }
1083
+ async interceptBefore(args) {
1084
+ if (!this.config?.interceptors || this.config.interceptors.length === 0) {
1085
+ return;
1086
+ }
1087
+ const executed = [];
1088
+ for (const interceptor of this.config.interceptors) {
1089
+ await interceptor.before(args);
1090
+ executed.push(interceptor);
1091
+ if (args.earlyReturn) {
1092
+ return {
1093
+ earlyReturn: args.earlyReturn,
1094
+ executed
1095
+ };
1096
+ }
1097
+ }
1098
+ }
1099
+ async interceptAfter(args, interceptors) {
1100
+ const reversedInterceptors = [...interceptors ?? this.config?.interceptors ?? []].reverse();
1101
+ for (const interceptor of reversedInterceptors) {
1102
+ await interceptor.after(args);
1103
+ if (args.earlyReturn) {
1104
+ return;
1105
+ }
1106
+ }
1107
+ }
1108
+ };
1109
+
1110
+ // src/client/factory.ts
1111
+ var ClientFactoryOptions = {
1112
+ /**
1113
+ * SDK default options for {@link ClientFactory}.
1114
+ */
1115
+ default: {
1116
+ transports: [new JsonRpcTransportFactory()]
1117
+ },
1118
+ /**
1119
+ * Creates new options by merging an original and an override object.
1120
+ * Transports are merged based on `TransportFactory.protocolName`,
1121
+ * interceptors are concatenated, other fields are overriden.
1122
+ *
1123
+ * @example
1124
+ * ```ts
1125
+ * const options = ClientFactoryOptions.createFrom(ClientFactoryOptions.default, {
1126
+ * transports: [new MyCustomTransportFactory()], // adds a custom transport
1127
+ * clientConfig: { interceptors: [new MyInterceptor()] }, // adds a custom interceptor
1128
+ * });
1129
+ * ```
1130
+ */
1131
+ createFrom(original, overrides) {
1132
+ return {
1133
+ ...original,
1134
+ ...overrides,
1135
+ transports: mergeTransports(original.transports, overrides.transports),
1136
+ clientConfig: {
1137
+ ...original.clientConfig ?? {},
1138
+ ...overrides.clientConfig ?? {},
1139
+ interceptors: mergeArrays(
1140
+ original.clientConfig?.interceptors,
1141
+ overrides.clientConfig?.interceptors
1142
+ ),
1143
+ acceptedOutputModes: overrides.clientConfig?.acceptedOutputModes ?? original.clientConfig?.acceptedOutputModes
1144
+ },
1145
+ preferredTransports: overrides.preferredTransports ?? original.preferredTransports
1146
+ };
1147
+ }
1148
+ };
1149
+ var ClientFactory = class {
1150
+ constructor(options = ClientFactoryOptions.default) {
1151
+ this.options = options;
1152
+ if (!options.transports || options.transports.length === 0) {
1153
+ throw new Error("No transports provided");
1154
+ }
1155
+ this.transportsByName = transportsByName(options.transports);
1156
+ for (const transport of options.preferredTransports ?? []) {
1157
+ const factory = this.options.transports.find((t) => t.protocolName === transport);
1158
+ if (!factory) {
1159
+ throw new Error(
1160
+ `Unknown preferred transport: ${transport}, available transports: ${[...this.transportsByName.keys()].join()}`
1161
+ );
1162
+ }
1163
+ }
1164
+ this.agentCardResolver = options.cardResolver ?? AgentCardResolver.default;
1165
+ }
1166
+ transportsByName;
1167
+ agentCardResolver;
1168
+ /**
1169
+ * Creates a new client from the provided agent card.
1170
+ */
1171
+ async createFromAgentCard(agentCard) {
1172
+ const agentCardPreferred = agentCard.preferredTransport ?? JsonRpcTransportFactory.name;
1173
+ const additionalInterfaces = agentCard.additionalInterfaces ?? [];
1174
+ const urlsPerAgentTransports = new Map([
1175
+ [agentCardPreferred, agentCard.url],
1176
+ ...additionalInterfaces.map((i) => [i.transport, i.url])
1177
+ ]);
1178
+ const transportsByPreference = [
1179
+ ...this.options.preferredTransports ?? [],
1180
+ agentCardPreferred,
1181
+ ...additionalInterfaces.map((i) => i.transport)
1182
+ ];
1183
+ for (const transport of transportsByPreference) {
1184
+ if (!urlsPerAgentTransports.has(transport)) {
1185
+ continue;
1186
+ }
1187
+ const factory = this.transportsByName.get(transport);
1188
+ if (!factory) {
1189
+ continue;
1190
+ }
1191
+ return new Client(
1192
+ await factory.create(urlsPerAgentTransports.get(transport), agentCard),
1193
+ agentCard,
1194
+ this.options.clientConfig
1195
+ );
1196
+ }
1197
+ throw new Error(
1198
+ "No compatible transport found, available transports: " + [...this.transportsByName.keys()].join()
1199
+ );
1200
+ }
1201
+ /**
1202
+ * Downloads agent card using AgentCardResolver from options
1203
+ * and creates a new client from the downloaded card.
1204
+ *
1205
+ * @example
1206
+ * ```ts
1207
+ * const factory = new ClientFactory(); // use default options and default {@link AgentCardResolver}.
1208
+ * const client1 = await factory.createFromUrl('https://example.com'); // /.well-known/agent-card.json is used by default
1209
+ * const client2 = await factory.createFromUrl('https://example.com', '/my-agent-card.json'); // specify custom path
1210
+ * const client3 = await factory.createFromUrl('https://example.com/my-agent-card.json', ''); // specify full URL and set path to empty
1211
+ * ```
1212
+ */
1213
+ async createFromUrl(baseUrl, path) {
1214
+ const agentCard = await this.agentCardResolver.resolve(baseUrl, path);
1215
+ return this.createFromAgentCard(agentCard);
1216
+ }
1217
+ };
1218
+ function mergeTransports(original, overrides) {
1219
+ if (!overrides) {
1220
+ return original;
1221
+ }
1222
+ const result = transportsByName(original);
1223
+ const overridesByName = transportsByName(overrides);
1224
+ for (const [name, factory] of overridesByName) {
1225
+ result.set(name, factory);
1226
+ }
1227
+ return Array.from(result.values());
1228
+ }
1229
+ function transportsByName(transports) {
1230
+ const result = /* @__PURE__ */ new Map();
1231
+ if (!transports) {
1232
+ return result;
1233
+ }
1234
+ for (const t of transports) {
1235
+ if (result.has(t.protocolName)) {
1236
+ throw new Error(`Duplicate protocol name: ${t.protocolName}`);
1237
+ }
1238
+ result.set(t.protocolName, t);
1239
+ }
1240
+ return result;
1241
+ }
1242
+ function mergeArrays(a1, a2) {
1243
+ if (!a1 && !a2) {
1244
+ return void 0;
1245
+ }
1246
+ return [...a1 ?? [], ...a2 ?? []];
1247
+ }
1248
+
1249
+ // src/extensions.ts
1250
+ var Extensions = {
1251
+ /**
1252
+ * Creates new {@link Extensions} from `current` and `additional`.
1253
+ * If `current` already contains `additional` it is returned unmodified.
1254
+ */
1255
+ createFrom: (current, additional) => {
1256
+ if (current?.includes(additional)) {
1257
+ return current;
1258
+ }
1259
+ return [...current ?? [], additional];
1260
+ },
1261
+ /**
1262
+ * Creates {@link Extensions} from comma separated extensions identifiers as per
1263
+ * https://a2a-protocol.org/latest/specification/#326-service-parameters.
1264
+ * Parses the output of `toServiceParameter`.
1265
+ */
1266
+ parseServiceParameter: (value) => {
1267
+ if (!value) {
1268
+ return [];
1269
+ }
1270
+ const unique = new Set(
1271
+ value.split(",").map((ext) => ext.trim()).filter((ext) => ext.length > 0)
1272
+ );
1273
+ return Array.from(unique);
1274
+ },
1275
+ /**
1276
+ * Converts {@link Extensions} to comma separated extensions identifiers as per
1277
+ * https://a2a-protocol.org/latest/specification/#326-service-parameters.
1278
+ */
1279
+ toServiceParameter: (value) => {
1280
+ return value.join(",");
1281
+ }
1282
+ };
1283
+
1284
+ // src/client/service-parameters.ts
1285
+ var ServiceParameters = {
1286
+ create(...updates) {
1287
+ return ServiceParameters.createFrom(void 0, ...updates);
1288
+ },
1289
+ createFrom: (serviceParameters, ...updates) => {
1290
+ const result = serviceParameters ? { ...serviceParameters } : {};
1291
+ for (const update of updates) {
1292
+ update(result);
1293
+ }
1294
+ return result;
1295
+ }
1296
+ };
1297
+ function withA2AExtensions(...extensions) {
1298
+ return (parameters) => {
1299
+ parameters[HTTP_EXTENSION_HEADER] = Extensions.toServiceParameter(extensions);
1300
+ };
1301
+ }
1302
+
1303
+ // src/client/context.ts
1304
+ var ClientCallContext = {
1305
+ /**
1306
+ * Create a new {@link ClientCallContext} with optional updates applied.
1307
+ */
1308
+ create: (...updates) => {
1309
+ return ClientCallContext.createFrom(void 0, ...updates);
1310
+ },
1311
+ /**
1312
+ * Create a new {@link ClientCallContext} based on an existing one with updates applied.
1313
+ */
1314
+ createFrom: (context, ...updates) => {
1315
+ const result = context ? { ...context } : {};
1316
+ for (const update of updates) {
1317
+ update(result);
1318
+ }
1319
+ return result;
1320
+ }
1321
+ };
1322
+ var ClientCallContextKey = class {
1323
+ symbol;
1324
+ constructor(description) {
1325
+ this.symbol = Symbol(description);
1326
+ }
1327
+ set(value) {
1328
+ return (context) => {
1329
+ context[this.symbol] = value;
1330
+ };
1331
+ }
1332
+ get(context) {
1333
+ return context[this.symbol];
1334
+ }
1335
+ };
546
1336
  // Annotate the CommonJS export names for ESM import in node:
547
1337
  0 && (module.exports = {
548
1338
  A2AClient,
549
- createAuthenticatingFetchWithRetry
1339
+ AgentCardResolver,
1340
+ Client,
1341
+ ClientCallContext,
1342
+ ClientCallContextKey,
1343
+ ClientFactory,
1344
+ ClientFactoryOptions,
1345
+ DefaultAgentCardResolver,
1346
+ JsonRpcTransport,
1347
+ JsonRpcTransportFactory,
1348
+ ServiceParameters,
1349
+ createAuthenticatingFetchWithRetry,
1350
+ withA2AExtensions
550
1351
  });