@mailmodo/a2a 0.3.5 → 0.3.7

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.
@@ -21,20 +21,48 @@ var client_exports = {};
21
21
  __export(client_exports, {
22
22
  A2AClient: () => A2AClient,
23
23
  AgentCardResolver: () => AgentCardResolver,
24
+ AuthenticatedExtendedCardNotConfiguredError: () => AuthenticatedExtendedCardNotConfiguredError,
24
25
  Client: () => Client,
26
+ ClientCallContext: () => ClientCallContext,
27
+ ClientCallContextKey: () => ClientCallContextKey,
25
28
  ClientFactory: () => ClientFactory,
26
29
  ClientFactoryOptions: () => ClientFactoryOptions,
30
+ ContentTypeNotSupportedError: () => ContentTypeNotSupportedError,
27
31
  DefaultAgentCardResolver: () => DefaultAgentCardResolver,
32
+ InvalidAgentResponseError: () => InvalidAgentResponseError,
28
33
  JsonRpcTransport: () => JsonRpcTransport,
29
34
  JsonRpcTransportFactory: () => JsonRpcTransportFactory,
30
- createAuthenticatingFetchWithRetry: () => createAuthenticatingFetchWithRetry
35
+ PushNotificationNotSupportedError: () => PushNotificationNotSupportedError,
36
+ RestTransport: () => RestTransport,
37
+ RestTransportFactory: () => RestTransportFactory,
38
+ ServiceParameters: () => ServiceParameters,
39
+ TaskNotCancelableError: () => TaskNotCancelableError,
40
+ TaskNotFoundError: () => TaskNotFoundError,
41
+ UnsupportedOperationError: () => UnsupportedOperationError,
42
+ createAuthenticatingFetchWithRetry: () => createAuthenticatingFetchWithRetry,
43
+ withA2AExtensions: () => withA2AExtensions
31
44
  });
32
45
  module.exports = __toCommonJS(client_exports);
33
46
 
34
47
  // src/constants.ts
35
48
  var AGENT_CARD_PATH = ".well-known/agent-card.json";
49
+ var HTTP_EXTENSION_HEADER = "X-A2A-Extensions";
36
50
 
37
51
  // src/errors.ts
52
+ var A2A_ERROR_CODE = {
53
+ PARSE_ERROR: -32700,
54
+ INVALID_REQUEST: -32600,
55
+ METHOD_NOT_FOUND: -32601,
56
+ INVALID_PARAMS: -32602,
57
+ INTERNAL_ERROR: -32603,
58
+ TASK_NOT_FOUND: -32001,
59
+ TASK_NOT_CANCELABLE: -32002,
60
+ PUSH_NOTIFICATION_NOT_SUPPORTED: -32003,
61
+ UNSUPPORTED_OPERATION: -32004,
62
+ CONTENT_TYPE_NOT_SUPPORTED: -32005,
63
+ INVALID_AGENT_RESPONSE: -32006,
64
+ AUTHENTICATED_EXTENDED_CARD_NOT_CONFIGURED: -32007
65
+ };
38
66
  var TaskNotFoundError = class extends Error {
39
67
  constructor(message) {
40
68
  super(message ?? "Task not found");
@@ -78,6 +106,38 @@ var AuthenticatedExtendedCardNotConfiguredError = class extends Error {
78
106
  }
79
107
  };
80
108
 
109
+ // src/sse_utils.ts
110
+ async function* parseSseStream(response) {
111
+ if (!response.body) {
112
+ throw new Error("SSE response body is undefined. Cannot read stream.");
113
+ }
114
+ let buffer = "";
115
+ let eventType = "message";
116
+ let eventData = "";
117
+ for await (const value of response.body.pipeThrough(new TextDecoderStream())) {
118
+ buffer += value;
119
+ let lineEndIndex;
120
+ while ((lineEndIndex = buffer.indexOf("\n")) >= 0) {
121
+ const line = buffer.substring(0, lineEndIndex).trim();
122
+ buffer = buffer.substring(lineEndIndex + 1);
123
+ if (line === "") {
124
+ if (eventData) {
125
+ yield { type: eventType, data: eventData };
126
+ eventData = "";
127
+ eventType = "message";
128
+ }
129
+ } else if (line.startsWith("event:")) {
130
+ eventType = line.substring("event:".length).trim();
131
+ } else if (line.startsWith("data:")) {
132
+ eventData = line.substring("data:".length).trim();
133
+ }
134
+ }
135
+ }
136
+ if (eventData) {
137
+ yield { type: eventType, data: eventData };
138
+ }
139
+ }
140
+
81
141
  // src/client/transports/json_rpc_transport.ts
82
142
  var JsonRpcTransport = class _JsonRpcTransport {
83
143
  customFetchImpl;
@@ -87,6 +147,10 @@ var JsonRpcTransport = class _JsonRpcTransport {
87
147
  this.endpoint = options.endpoint;
88
148
  this.customFetchImpl = options.fetchImpl;
89
149
  }
150
+ async getExtendedAgentCard(options, idOverride) {
151
+ const rpcResponse = await this._sendRpcRequest("agent/getAuthenticatedExtendedCard", void 0, idOverride, options);
152
+ return rpcResponse.result;
153
+ }
90
154
  async sendMessage(params, options, idOverride) {
91
155
  const rpcResponse = await this._sendRpcRequest(
92
156
  "message/send",
@@ -162,7 +226,7 @@ var JsonRpcTransport = class _JsonRpcTransport {
162
226
  params,
163
227
  id: requestId
164
228
  };
165
- const httpResponse = await this._fetchRpc(rpcRequest, "application/json", options?.signal);
229
+ const httpResponse = await this._fetchRpc(rpcRequest, "application/json", options);
166
230
  if (!httpResponse.ok) {
167
231
  let errorBodyText = "(empty or non-JSON response)";
168
232
  let errorJson;
@@ -194,15 +258,16 @@ var JsonRpcTransport = class _JsonRpcTransport {
194
258
  }
195
259
  return rpcResponse;
196
260
  }
197
- async _fetchRpc(rpcRequest, acceptHeader = "application/json", signal) {
261
+ async _fetchRpc(rpcRequest, acceptHeader = "application/json", options) {
198
262
  const requestInit = {
199
263
  method: "POST",
200
264
  headers: {
265
+ ...options?.serviceParameters,
201
266
  "Content-Type": "application/json",
202
267
  Accept: acceptHeader
203
268
  },
204
269
  body: JSON.stringify(rpcRequest),
205
- signal
270
+ signal: options?.signal
206
271
  };
207
272
  return this._fetch(this.endpoint, requestInit);
208
273
  }
@@ -214,7 +279,7 @@ var JsonRpcTransport = class _JsonRpcTransport {
214
279
  params,
215
280
  id: clientRequestId
216
281
  };
217
- const response = await this._fetchRpc(rpcRequest, "text/event-stream", options?.signal);
282
+ const response = await this._fetchRpc(rpcRequest, "text/event-stream", options);
218
283
  if (!response.ok) {
219
284
  let errorBody = "";
220
285
  let errorJson;
@@ -241,55 +306,8 @@ var JsonRpcTransport = class _JsonRpcTransport {
241
306
  `Invalid response Content-Type for SSE stream for ${method}. Expected 'text/event-stream'.`
242
307
  );
243
308
  }
244
- yield* this._parseA2ASseStream(response, clientRequestId);
245
- }
246
- async *_parseA2ASseStream(response, originalRequestId) {
247
- if (!response.body) {
248
- throw new Error("SSE response body is undefined. Cannot read stream.");
249
- }
250
- const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
251
- let buffer = "";
252
- let eventDataBuffer = "";
253
- try {
254
- while (true) {
255
- const { done, value } = await reader.read();
256
- if (done) {
257
- if (eventDataBuffer.trim()) {
258
- const result = this._processSseEventData(
259
- eventDataBuffer,
260
- originalRequestId
261
- );
262
- yield result;
263
- }
264
- break;
265
- }
266
- buffer += value;
267
- let lineEndIndex;
268
- while ((lineEndIndex = buffer.indexOf("\n")) >= 0) {
269
- const line = buffer.substring(0, lineEndIndex).trim();
270
- buffer = buffer.substring(lineEndIndex + 1);
271
- if (line === "") {
272
- if (eventDataBuffer) {
273
- const result = this._processSseEventData(
274
- eventDataBuffer,
275
- originalRequestId
276
- );
277
- yield result;
278
- eventDataBuffer = "";
279
- }
280
- } else if (line.startsWith("data:")) {
281
- eventDataBuffer += line.substring(5).trimStart() + "\n";
282
- }
283
- }
284
- }
285
- } catch (error) {
286
- console.error(
287
- "Error reading or parsing SSE stream:",
288
- error instanceof Error && error.message || "Error unknown"
289
- );
290
- throw error;
291
- } finally {
292
- reader.releaseLock();
309
+ for await (const event of parseSseStream(response)) {
310
+ yield this._processSseEventData(event.data, clientRequestId);
293
311
  }
294
312
  }
295
313
  _processSseEventData(jsonData, originalRequestId) {
@@ -297,7 +315,7 @@ var JsonRpcTransport = class _JsonRpcTransport {
297
315
  throw new Error("Attempted to process empty SSE event data.");
298
316
  }
299
317
  try {
300
- const sseJsonRpcResponse = JSON.parse(jsonData.replace(/\n$/, ""));
318
+ const sseJsonRpcResponse = JSON.parse(jsonData);
301
319
  const a2aStreamResponse = sseJsonRpcResponse;
302
320
  if (a2aStreamResponse.id !== originalRequestId) {
303
321
  console.warn(
@@ -827,7 +845,7 @@ var DefaultAgentCardResolver = class {
827
845
  }
828
846
  };
829
847
  var AgentCardResolver = {
830
- Default: new DefaultAgentCardResolver()
848
+ default: new DefaultAgentCardResolver()
831
849
  };
832
850
 
833
851
  // src/client/multitransport-client.ts
@@ -837,6 +855,20 @@ var Client = class {
837
855
  this.agentCard = agentCard;
838
856
  this.config = config;
839
857
  }
858
+ /**
859
+ * If the current agent card supports the extended feature, it will try to fetch the extended agent card from the server,
860
+ * Otherwise it will return the current agent card value.
861
+ */
862
+ async getAgentCard(options) {
863
+ if (this.agentCard.supportsAuthenticatedExtendedCard) {
864
+ this.agentCard = await this.executeWithInterceptors(
865
+ { method: "getAgentCard" },
866
+ options,
867
+ (_, options2) => this.transport.getExtendedAgentCard(options2)
868
+ );
869
+ }
870
+ return this.agentCard;
871
+ }
840
872
  /**
841
873
  * Sends a message to an agent to initiate a new interaction or to continue an existing one.
842
874
  * Uses blocking mode by default.
@@ -861,6 +893,7 @@ var Client = class {
861
893
  params = this.applyClientConfig({ params, blocking: true });
862
894
  const beforeArgs = {
863
895
  input: { method, value: params },
896
+ agentCard: this.agentCard,
864
897
  options
865
898
  };
866
899
  const beforeResult = await this.interceptBefore(beforeArgs);
@@ -868,6 +901,7 @@ var Client = class {
868
901
  const earlyReturn = beforeResult.earlyReturn.value;
869
902
  const afterArgs = {
870
903
  result: { method, value: earlyReturn },
904
+ agentCard: this.agentCard,
871
905
  options: beforeArgs.options
872
906
  };
873
907
  await this.interceptAfter(afterArgs, beforeResult.executed);
@@ -878,6 +912,7 @@ var Client = class {
878
912
  const result = await this.transport.sendMessage(beforeArgs.input.value, beforeArgs.options);
879
913
  const afterArgs = {
880
914
  result: { method, value: result },
915
+ agentCard: this.agentCard,
881
916
  options: beforeArgs.options
882
917
  };
883
918
  await this.interceptAfter(afterArgs);
@@ -890,6 +925,7 @@ var Client = class {
890
925
  )) {
891
926
  const afterArgs = {
892
927
  result: { method, value: event },
928
+ agentCard: this.agentCard,
893
929
  options: beforeArgs.options
894
930
  };
895
931
  await this.interceptAfter(afterArgs);
@@ -977,12 +1013,17 @@ var Client = class {
977
1013
  */
978
1014
  async *resubscribeTask(params, options) {
979
1015
  const method = "resubscribeTask";
980
- const beforeArgs = { input: { method, value: params }, options };
1016
+ const beforeArgs = {
1017
+ input: { method, value: params },
1018
+ agentCard: this.agentCard,
1019
+ options
1020
+ };
981
1021
  const beforeResult = await this.interceptBefore(beforeArgs);
982
1022
  if (beforeResult) {
983
1023
  const earlyReturn = beforeResult.earlyReturn.value;
984
1024
  const afterArgs = {
985
1025
  result: { method, value: earlyReturn },
1026
+ agentCard: this.agentCard,
986
1027
  options: beforeArgs.options
987
1028
  };
988
1029
  await this.interceptAfter(afterArgs, beforeResult.executed);
@@ -995,6 +1036,7 @@ var Client = class {
995
1036
  )) {
996
1037
  const afterArgs = {
997
1038
  result: { method, value: event },
1039
+ agentCard: this.agentCard,
998
1040
  options: beforeArgs.options
999
1041
  };
1000
1042
  await this.interceptAfter(afterArgs);
@@ -1021,6 +1063,7 @@ var Client = class {
1021
1063
  async executeWithInterceptors(input, options, transportCall) {
1022
1064
  const beforeArgs = {
1023
1065
  input,
1066
+ agentCard: this.agentCard,
1024
1067
  options
1025
1068
  };
1026
1069
  const beforeResult = await this.interceptBefore(beforeArgs);
@@ -1030,6 +1073,7 @@ var Client = class {
1030
1073
  method: input.method,
1031
1074
  value: beforeResult.earlyReturn.value
1032
1075
  },
1076
+ agentCard: this.agentCard,
1033
1077
  options: beforeArgs.options
1034
1078
  };
1035
1079
  await this.interceptAfter(afterArgs2, beforeResult.executed);
@@ -1038,6 +1082,7 @@ var Client = class {
1038
1082
  const result = await transportCall(beforeArgs.input.value, beforeArgs.options);
1039
1083
  const afterArgs = {
1040
1084
  result: { method: input.method, value: result },
1085
+ agentCard: this.agentCard,
1041
1086
  options: beforeArgs.options
1042
1087
  };
1043
1088
  await this.interceptAfter(afterArgs);
@@ -1060,10 +1105,8 @@ var Client = class {
1060
1105
  }
1061
1106
  }
1062
1107
  async interceptAfter(args, interceptors) {
1063
- if (!this.config?.interceptors || this.config.interceptors.length === 0) {
1064
- return;
1065
- }
1066
- for (const interceptor of interceptors ?? this.config.interceptors) {
1108
+ const reversedInterceptors = [...interceptors ?? this.config?.interceptors ?? []].reverse();
1109
+ for (const interceptor of reversedInterceptors) {
1067
1110
  await interceptor.after(args);
1068
1111
  if (args.earlyReturn) {
1069
1112
  return;
@@ -1072,35 +1115,283 @@ var Client = class {
1072
1115
  }
1073
1116
  };
1074
1117
 
1118
+ // src/client/transports/rest_transport.ts
1119
+ var RestTransport = class _RestTransport {
1120
+ customFetchImpl;
1121
+ endpoint;
1122
+ constructor(options) {
1123
+ this.endpoint = options.endpoint.replace(/\/+$/, "");
1124
+ this.customFetchImpl = options.fetchImpl;
1125
+ }
1126
+ async getExtendedAgentCard(options) {
1127
+ return this._sendRequest("GET", "/v1/card", void 0, options);
1128
+ }
1129
+ async sendMessage(params, options) {
1130
+ return this._sendRequest("POST", "/v1/message:send", params, options);
1131
+ }
1132
+ async *sendMessageStream(params, options) {
1133
+ yield* this._sendStreamingRequest("/v1/message:stream", params, options);
1134
+ }
1135
+ async setTaskPushNotificationConfig(params, options) {
1136
+ return this._sendRequest(
1137
+ "POST",
1138
+ `/v1/tasks/${encodeURIComponent(params.taskId)}/pushNotificationConfigs`,
1139
+ {
1140
+ pushNotificationConfig: params.pushNotificationConfig
1141
+ },
1142
+ options
1143
+ );
1144
+ }
1145
+ async getTaskPushNotificationConfig(params, options) {
1146
+ const { pushNotificationConfigId } = params;
1147
+ if (!pushNotificationConfigId) {
1148
+ throw new Error(
1149
+ "pushNotificationConfigId is required for getTaskPushNotificationConfig with REST transport."
1150
+ );
1151
+ }
1152
+ return this._sendRequest(
1153
+ "GET",
1154
+ `/v1/tasks/${encodeURIComponent(params.id)}/pushNotificationConfigs/${encodeURIComponent(pushNotificationConfigId)}`,
1155
+ void 0,
1156
+ options
1157
+ );
1158
+ }
1159
+ async listTaskPushNotificationConfig(params, options) {
1160
+ return this._sendRequest(
1161
+ "GET",
1162
+ `/v1/tasks/${encodeURIComponent(params.id)}/pushNotificationConfigs`,
1163
+ void 0,
1164
+ options
1165
+ );
1166
+ }
1167
+ async deleteTaskPushNotificationConfig(params, options) {
1168
+ await this._sendRequest(
1169
+ "DELETE",
1170
+ `/v1/tasks/${encodeURIComponent(params.id)}/pushNotificationConfigs/${encodeURIComponent(params.pushNotificationConfigId)}`,
1171
+ void 0,
1172
+ options
1173
+ );
1174
+ }
1175
+ async getTask(params, options) {
1176
+ const queryParams = new URLSearchParams();
1177
+ if (params.historyLength !== void 0) {
1178
+ queryParams.set("historyLength", String(params.historyLength));
1179
+ }
1180
+ const queryString = queryParams.toString();
1181
+ const path = `/v1/tasks/${encodeURIComponent(params.id)}${queryString ? `?${queryString}` : ""}`;
1182
+ return this._sendRequest("GET", path, void 0, options);
1183
+ }
1184
+ async cancelTask(params, options) {
1185
+ return this._sendRequest(
1186
+ "POST",
1187
+ `/v1/tasks/${encodeURIComponent(params.id)}:cancel`,
1188
+ void 0,
1189
+ options
1190
+ );
1191
+ }
1192
+ async *resubscribeTask(params, options) {
1193
+ yield* this._sendStreamingRequest(
1194
+ `/v1/tasks/${encodeURIComponent(params.id)}:subscribe`,
1195
+ void 0,
1196
+ options
1197
+ );
1198
+ }
1199
+ _fetch(...args) {
1200
+ if (this.customFetchImpl) {
1201
+ return this.customFetchImpl(...args);
1202
+ }
1203
+ if (typeof fetch === "function") {
1204
+ return fetch(...args);
1205
+ }
1206
+ throw new Error(
1207
+ "A `fetch` implementation was not provided and is not available in the global scope. Please provide a `fetchImpl` in the RestTransportOptions."
1208
+ );
1209
+ }
1210
+ _buildHeaders(options, acceptHeader = "application/json") {
1211
+ return {
1212
+ ...options?.serviceParameters,
1213
+ "Content-Type": "application/json",
1214
+ Accept: acceptHeader
1215
+ };
1216
+ }
1217
+ async _sendRequest(method, path, body, options) {
1218
+ const url = `${this.endpoint}${path}`;
1219
+ const requestInit = {
1220
+ method,
1221
+ headers: this._buildHeaders(options),
1222
+ signal: options?.signal
1223
+ };
1224
+ if (body !== void 0 && method !== "GET") {
1225
+ requestInit.body = JSON.stringify(body);
1226
+ }
1227
+ const response = await this._fetch(url, requestInit);
1228
+ if (!response.ok) {
1229
+ await this._handleErrorResponse(response, path);
1230
+ }
1231
+ if (response.status === 204) {
1232
+ return void 0;
1233
+ }
1234
+ const result = await response.json();
1235
+ return result;
1236
+ }
1237
+ async _handleErrorResponse(response, path) {
1238
+ let errorBodyText = "(empty or non-JSON response)";
1239
+ let errorBody;
1240
+ try {
1241
+ errorBodyText = await response.text();
1242
+ if (errorBodyText) {
1243
+ errorBody = JSON.parse(errorBodyText);
1244
+ }
1245
+ } catch (e) {
1246
+ throw new Error(
1247
+ `HTTP error for ${path}! Status: ${response.status} ${response.statusText}. Response: ${errorBodyText}`,
1248
+ { cause: e }
1249
+ );
1250
+ }
1251
+ if (errorBody && typeof errorBody.code === "number") {
1252
+ throw _RestTransport.mapToError(errorBody);
1253
+ }
1254
+ throw new Error(
1255
+ `HTTP error for ${path}! Status: ${response.status} ${response.statusText}. Response: ${errorBodyText}`
1256
+ );
1257
+ }
1258
+ async *_sendStreamingRequest(path, body, options) {
1259
+ const url = `${this.endpoint}${path}`;
1260
+ const requestInit = {
1261
+ method: "POST",
1262
+ headers: this._buildHeaders(options, "text/event-stream"),
1263
+ signal: options?.signal
1264
+ };
1265
+ if (body !== void 0) {
1266
+ requestInit.body = JSON.stringify(body);
1267
+ }
1268
+ const response = await this._fetch(url, requestInit);
1269
+ if (!response.ok) {
1270
+ await this._handleErrorResponse(response, path);
1271
+ }
1272
+ const contentType = response.headers.get("Content-Type");
1273
+ if (!contentType?.startsWith("text/event-stream")) {
1274
+ throw new Error(
1275
+ `Invalid response Content-Type for SSE stream. Expected 'text/event-stream', got '${contentType}'.`
1276
+ );
1277
+ }
1278
+ for await (const event of parseSseStream(response)) {
1279
+ if (event.type === "error") {
1280
+ const errorData = JSON.parse(event.data);
1281
+ throw _RestTransport.mapToError(errorData);
1282
+ }
1283
+ yield this._processSseEventData(event.data);
1284
+ }
1285
+ }
1286
+ _processSseEventData(jsonData) {
1287
+ if (!jsonData.trim()) {
1288
+ throw new Error("Attempted to process empty SSE event data.");
1289
+ }
1290
+ try {
1291
+ const data = JSON.parse(jsonData);
1292
+ return data;
1293
+ } catch (e) {
1294
+ console.error("Failed to parse SSE event data:", jsonData, e);
1295
+ throw new Error(
1296
+ `Failed to parse SSE event data: "${jsonData.substring(0, 100)}...". Original error: ${e instanceof Error && e.message || "Unknown error"}`
1297
+ );
1298
+ }
1299
+ }
1300
+ static mapToError(error) {
1301
+ switch (error.code) {
1302
+ case A2A_ERROR_CODE.TASK_NOT_FOUND:
1303
+ return new TaskNotFoundError(error.message);
1304
+ case A2A_ERROR_CODE.TASK_NOT_CANCELABLE:
1305
+ return new TaskNotCancelableError(error.message);
1306
+ case A2A_ERROR_CODE.PUSH_NOTIFICATION_NOT_SUPPORTED:
1307
+ return new PushNotificationNotSupportedError(error.message);
1308
+ case A2A_ERROR_CODE.UNSUPPORTED_OPERATION:
1309
+ return new UnsupportedOperationError(error.message);
1310
+ case A2A_ERROR_CODE.CONTENT_TYPE_NOT_SUPPORTED:
1311
+ return new ContentTypeNotSupportedError(error.message);
1312
+ case A2A_ERROR_CODE.INVALID_AGENT_RESPONSE:
1313
+ return new InvalidAgentResponseError(error.message);
1314
+ case A2A_ERROR_CODE.AUTHENTICATED_EXTENDED_CARD_NOT_CONFIGURED:
1315
+ return new AuthenticatedExtendedCardNotConfiguredError(error.message);
1316
+ default:
1317
+ return new Error(
1318
+ `REST error: ${error.message} (Code: ${error.code})${error.data ? ` Data: ${JSON.stringify(error.data)}` : ""}`
1319
+ );
1320
+ }
1321
+ }
1322
+ };
1323
+ var RestTransportFactory = class _RestTransportFactory {
1324
+ constructor(options) {
1325
+ this.options = options;
1326
+ }
1327
+ static name = "HTTP+JSON";
1328
+ get protocolName() {
1329
+ return _RestTransportFactory.name;
1330
+ }
1331
+ async create(url, _agentCard) {
1332
+ return new RestTransport({
1333
+ endpoint: url,
1334
+ fetchImpl: this.options?.fetchImpl
1335
+ });
1336
+ }
1337
+ };
1338
+
1075
1339
  // src/client/factory.ts
1076
1340
  var ClientFactoryOptions = {
1077
- Default: {
1078
- transports: [new JsonRpcTransportFactory()]
1341
+ /**
1342
+ * SDK default options for {@link ClientFactory}.
1343
+ */
1344
+ default: {
1345
+ transports: [new JsonRpcTransportFactory(), new RestTransportFactory()]
1346
+ },
1347
+ /**
1348
+ * Creates new options by merging an original and an override object.
1349
+ * Transports are merged based on `TransportFactory.protocolName`,
1350
+ * interceptors are concatenated, other fields are overriden.
1351
+ *
1352
+ * @example
1353
+ * ```ts
1354
+ * const options = ClientFactoryOptions.createFrom(ClientFactoryOptions.default, {
1355
+ * transports: [new MyCustomTransportFactory()], // adds a custom transport
1356
+ * clientConfig: { interceptors: [new MyInterceptor()] }, // adds a custom interceptor
1357
+ * });
1358
+ * ```
1359
+ */
1360
+ createFrom(original, overrides) {
1361
+ return {
1362
+ ...original,
1363
+ ...overrides,
1364
+ transports: mergeTransports(original.transports, overrides.transports),
1365
+ clientConfig: {
1366
+ ...original.clientConfig ?? {},
1367
+ ...overrides.clientConfig ?? {},
1368
+ interceptors: mergeArrays(
1369
+ original.clientConfig?.interceptors,
1370
+ overrides.clientConfig?.interceptors
1371
+ ),
1372
+ acceptedOutputModes: overrides.clientConfig?.acceptedOutputModes ?? original.clientConfig?.acceptedOutputModes
1373
+ },
1374
+ preferredTransports: overrides.preferredTransports ?? original.preferredTransports
1375
+ };
1079
1376
  }
1080
1377
  };
1081
1378
  var ClientFactory = class {
1082
- constructor(options = ClientFactoryOptions.Default) {
1379
+ constructor(options = ClientFactoryOptions.default) {
1083
1380
  this.options = options;
1084
1381
  if (!options.transports || options.transports.length === 0) {
1085
1382
  throw new Error("No transports provided");
1086
1383
  }
1087
- for (const transport of options.transports) {
1088
- if (this.transportsByName.has(transport.protocolName)) {
1089
- throw new Error(`Duplicate transport name: ${transport.protocolName}`);
1090
- }
1091
- this.transportsByName.set(transport.protocolName, transport);
1092
- }
1384
+ this.transportsByName = transportsByName(options.transports);
1093
1385
  for (const transport of options.preferredTransports ?? []) {
1094
- const factory = this.options.transports.find((t) => t.protocolName === transport);
1095
- if (!factory) {
1386
+ if (!this.transportsByName.has(transport)) {
1096
1387
  throw new Error(
1097
1388
  `Unknown preferred transport: ${transport}, available transports: ${[...this.transportsByName.keys()].join()}`
1098
1389
  );
1099
1390
  }
1100
1391
  }
1101
- this.agentCardResolver = options.cardResolver ?? AgentCardResolver.Default;
1392
+ this.agentCardResolver = options.cardResolver ?? AgentCardResolver.default;
1102
1393
  }
1103
- transportsByName = /* @__PURE__ */ new Map();
1394
+ transportsByName;
1104
1395
  agentCardResolver;
1105
1396
  /**
1106
1397
  * Creates a new client from the provided agent card.
@@ -1108,7 +1399,7 @@ var ClientFactory = class {
1108
1399
  async createFromAgentCard(agentCard) {
1109
1400
  const agentCardPreferred = agentCard.preferredTransport ?? JsonRpcTransportFactory.name;
1110
1401
  const additionalInterfaces = agentCard.additionalInterfaces ?? [];
1111
- const urlsPerAgentTransports = new Map([
1402
+ const urlsPerAgentTransports = new CaseInsensitiveMap([
1112
1403
  [agentCardPreferred, agentCard.url],
1113
1404
  ...additionalInterfaces.map((i) => [i.transport, i.url])
1114
1405
  ]);
@@ -1138,21 +1429,177 @@ var ClientFactory = class {
1138
1429
  /**
1139
1430
  * Downloads agent card using AgentCardResolver from options
1140
1431
  * and creates a new client from the downloaded card.
1432
+ *
1433
+ * @example
1434
+ * ```ts
1435
+ * const factory = new ClientFactory(); // use default options and default {@link AgentCardResolver}.
1436
+ * const client1 = await factory.createFromUrl('https://example.com'); // /.well-known/agent-card.json is used by default
1437
+ * const client2 = await factory.createFromUrl('https://example.com', '/my-agent-card.json'); // specify custom path
1438
+ * const client3 = await factory.createFromUrl('https://example.com/my-agent-card.json', ''); // specify full URL and set path to empty
1439
+ * ```
1141
1440
  */
1142
- async createFromAgentCardUrl(baseUrl, path) {
1441
+ async createFromUrl(baseUrl, path) {
1143
1442
  const agentCard = await this.agentCardResolver.resolve(baseUrl, path);
1144
- return await this.createFromAgentCard(agentCard);
1443
+ return this.createFromAgentCard(agentCard);
1444
+ }
1445
+ };
1446
+ function mergeTransports(original, overrides) {
1447
+ if (!overrides) {
1448
+ return original;
1449
+ }
1450
+ const result = transportsByName(original);
1451
+ const overridesByName = transportsByName(overrides);
1452
+ for (const [name, factory] of overridesByName) {
1453
+ result.set(name, factory);
1454
+ }
1455
+ return Array.from(result.values());
1456
+ }
1457
+ function transportsByName(transports) {
1458
+ const result = new CaseInsensitiveMap();
1459
+ if (!transports) {
1460
+ return result;
1461
+ }
1462
+ for (const t of transports) {
1463
+ if (result.has(t.protocolName)) {
1464
+ throw new Error(`Duplicate protocol name: ${t.protocolName}`);
1465
+ }
1466
+ result.set(t.protocolName, t);
1467
+ }
1468
+ return result;
1469
+ }
1470
+ function mergeArrays(a1, a2) {
1471
+ if (!a1 && !a2) {
1472
+ return void 0;
1473
+ }
1474
+ return [...a1 ?? [], ...a2 ?? []];
1475
+ }
1476
+ var CaseInsensitiveMap = class extends Map {
1477
+ normalizeKey(key) {
1478
+ return key.toUpperCase();
1479
+ }
1480
+ set(key, value) {
1481
+ return super.set(this.normalizeKey(key), value);
1482
+ }
1483
+ get(key) {
1484
+ return super.get(this.normalizeKey(key));
1485
+ }
1486
+ has(key) {
1487
+ return super.has(this.normalizeKey(key));
1488
+ }
1489
+ delete(key) {
1490
+ return super.delete(this.normalizeKey(key));
1491
+ }
1492
+ };
1493
+
1494
+ // src/extensions.ts
1495
+ var Extensions = {
1496
+ /**
1497
+ * Creates new {@link Extensions} from `current` and `additional`.
1498
+ * If `current` already contains `additional` it is returned unmodified.
1499
+ */
1500
+ createFrom: (current, additional) => {
1501
+ if (current?.includes(additional)) {
1502
+ return current;
1503
+ }
1504
+ return [...current ?? [], additional];
1505
+ },
1506
+ /**
1507
+ * Creates {@link Extensions} from comma separated extensions identifiers as per
1508
+ * https://a2a-protocol.org/latest/specification/#326-service-parameters.
1509
+ * Parses the output of `toServiceParameter`.
1510
+ */
1511
+ parseServiceParameter: (value) => {
1512
+ if (!value) {
1513
+ return [];
1514
+ }
1515
+ const unique = new Set(
1516
+ value.split(",").map((ext) => ext.trim()).filter((ext) => ext.length > 0)
1517
+ );
1518
+ return Array.from(unique);
1519
+ },
1520
+ /**
1521
+ * Converts {@link Extensions} to comma separated extensions identifiers as per
1522
+ * https://a2a-protocol.org/latest/specification/#326-service-parameters.
1523
+ */
1524
+ toServiceParameter: (value) => {
1525
+ return value.join(",");
1526
+ }
1527
+ };
1528
+
1529
+ // src/client/service-parameters.ts
1530
+ var ServiceParameters = {
1531
+ create(...updates) {
1532
+ return ServiceParameters.createFrom(void 0, ...updates);
1533
+ },
1534
+ createFrom: (serviceParameters, ...updates) => {
1535
+ const result = serviceParameters ? { ...serviceParameters } : {};
1536
+ for (const update of updates) {
1537
+ update(result);
1538
+ }
1539
+ return result;
1540
+ }
1541
+ };
1542
+ function withA2AExtensions(...extensions) {
1543
+ return (parameters) => {
1544
+ parameters[HTTP_EXTENSION_HEADER] = Extensions.toServiceParameter(extensions);
1545
+ };
1546
+ }
1547
+
1548
+ // src/client/context.ts
1549
+ var ClientCallContext = {
1550
+ /**
1551
+ * Create a new {@link ClientCallContext} with optional updates applied.
1552
+ */
1553
+ create: (...updates) => {
1554
+ return ClientCallContext.createFrom(void 0, ...updates);
1555
+ },
1556
+ /**
1557
+ * Create a new {@link ClientCallContext} based on an existing one with updates applied.
1558
+ */
1559
+ createFrom: (context, ...updates) => {
1560
+ const result = context ? { ...context } : {};
1561
+ for (const update of updates) {
1562
+ update(result);
1563
+ }
1564
+ return result;
1565
+ }
1566
+ };
1567
+ var ClientCallContextKey = class {
1568
+ symbol;
1569
+ constructor(description) {
1570
+ this.symbol = Symbol(description);
1571
+ }
1572
+ set(value) {
1573
+ return (context) => {
1574
+ context[this.symbol] = value;
1575
+ };
1576
+ }
1577
+ get(context) {
1578
+ return context[this.symbol];
1145
1579
  }
1146
1580
  };
1147
1581
  // Annotate the CommonJS export names for ESM import in node:
1148
1582
  0 && (module.exports = {
1149
1583
  A2AClient,
1150
1584
  AgentCardResolver,
1585
+ AuthenticatedExtendedCardNotConfiguredError,
1151
1586
  Client,
1587
+ ClientCallContext,
1588
+ ClientCallContextKey,
1152
1589
  ClientFactory,
1153
1590
  ClientFactoryOptions,
1591
+ ContentTypeNotSupportedError,
1154
1592
  DefaultAgentCardResolver,
1593
+ InvalidAgentResponseError,
1155
1594
  JsonRpcTransport,
1156
1595
  JsonRpcTransportFactory,
1157
- createAuthenticatingFetchWithRetry
1596
+ PushNotificationNotSupportedError,
1597
+ RestTransport,
1598
+ RestTransportFactory,
1599
+ ServiceParameters,
1600
+ TaskNotCancelableError,
1601
+ TaskNotFoundError,
1602
+ UnsupportedOperationError,
1603
+ createAuthenticatingFetchWithRetry,
1604
+ withA2AExtensions
1158
1605
  });