@a2a-js/sdk 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.
@@ -29,11 +29,22 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
29
29
  // src/server/express/index.ts
30
30
  var express_exports = {};
31
31
  __export(express_exports, {
32
- A2AExpressApp: () => A2AExpressApp
32
+ A2AExpressApp: () => A2AExpressApp,
33
+ UserBuilder: () => UserBuilder,
34
+ agentCardHandler: () => agentCardHandler,
35
+ jsonRpcHandler: () => jsonRpcHandler,
36
+ restHandler: () => restHandler
33
37
  });
34
38
  module.exports = __toCommonJS(express_exports);
35
39
 
36
40
  // src/server/express/a2a_express_app.ts
41
+ var import_express3 = __toESM(require("express"), 1);
42
+
43
+ // src/constants.ts
44
+ var AGENT_CARD_PATH = ".well-known/agent-card.json";
45
+ var HTTP_EXTENSION_HEADER = "X-A2A-Extensions";
46
+
47
+ // src/server/express/json_rpc_handler.ts
37
48
  var import_express = __toESM(require("express"), 1);
38
49
 
39
50
  // src/server/error.ts
@@ -70,10 +81,7 @@ var A2AError = class _A2AError extends Error {
70
81
  return new _A2AError(-32600, message, data);
71
82
  }
72
83
  static methodNotFound(method) {
73
- return new _A2AError(
74
- -32601,
75
- `Method not found: ${method}`
76
- );
84
+ return new _A2AError(-32601, `Method not found: ${method}`);
77
85
  }
78
86
  static invalidParams(message, data) {
79
87
  return new _A2AError(-32602, message, data);
@@ -82,42 +90,23 @@ var A2AError = class _A2AError extends Error {
82
90
  return new _A2AError(-32603, message, data);
83
91
  }
84
92
  static taskNotFound(taskId) {
85
- return new _A2AError(
86
- -32001,
87
- `Task not found: ${taskId}`,
88
- void 0,
89
- taskId
90
- );
93
+ return new _A2AError(-32001, `Task not found: ${taskId}`, void 0, taskId);
91
94
  }
92
95
  static taskNotCancelable(taskId) {
93
- return new _A2AError(
94
- -32002,
95
- `Task not cancelable: ${taskId}`,
96
- void 0,
97
- taskId
98
- );
96
+ return new _A2AError(-32002, `Task not cancelable: ${taskId}`, void 0, taskId);
99
97
  }
100
98
  static pushNotificationNotSupported() {
101
- return new _A2AError(
102
- -32003,
103
- "Push Notification is not supported"
104
- );
99
+ return new _A2AError(-32003, "Push Notification is not supported");
105
100
  }
106
101
  static unsupportedOperation(operation) {
107
- return new _A2AError(
108
- -32004,
109
- `Unsupported operation: ${operation}`
110
- );
102
+ return new _A2AError(-32004, `Unsupported operation: ${operation}`);
111
103
  }
112
104
  static authenticatedExtendedCardNotConfigured() {
113
- return new _A2AError(
114
- -32007,
115
- `Extended card not configured.`
116
- );
105
+ return new _A2AError(-32007, `Extended card not configured.`);
117
106
  }
118
107
  };
119
108
 
120
- // src/server/transports/jsonrpc_transport_handler.ts
109
+ // src/server/transports/jsonrpc/jsonrpc_transport_handler.ts
121
110
  var JsonRpcTransportHandler = class {
122
111
  requestHandler;
123
112
  constructor(requestHandler) {
@@ -128,7 +117,7 @@ var JsonRpcTransportHandler = class {
128
117
  * For streaming methods, it returns an AsyncGenerator of JSONRPCResult.
129
118
  * For non-streaming methods, it returns a Promise of a single JSONRPCMessage (Result or ErrorResponse).
130
119
  */
131
- async handle(requestBody) {
120
+ async handle(requestBody, context) {
132
121
  let rpcRequest;
133
122
  try {
134
123
  if (typeof requestBody === "string") {
@@ -142,24 +131,18 @@ var JsonRpcTransportHandler = class {
142
131
  throw A2AError.invalidRequest("Invalid JSON-RPC Request.");
143
132
  }
144
133
  } catch (error) {
145
- const a2aError = error instanceof A2AError ? error : A2AError.parseError(error.message || "Failed to parse JSON request.");
134
+ const a2aError = error instanceof A2AError ? error : A2AError.parseError(
135
+ error instanceof SyntaxError && error.message || "Failed to parse JSON request."
136
+ );
146
137
  return {
147
138
  jsonrpc: "2.0",
148
- id: typeof rpcRequest?.id !== "undefined" ? rpcRequest.id : null,
139
+ id: rpcRequest?.id !== void 0 ? rpcRequest.id : null,
149
140
  error: a2aError.toJSONRPCError()
150
141
  };
151
142
  }
152
143
  const { method, id: requestId = null } = rpcRequest;
153
144
  try {
154
- if (method === "agent/getAuthenticatedExtendedCard") {
155
- const result = await this.requestHandler.getAuthenticatedExtendedAgentCard();
156
- return {
157
- jsonrpc: "2.0",
158
- id: requestId,
159
- result
160
- };
161
- }
162
- if (!this.paramsAreValid(rpcRequest.params)) {
145
+ if (method !== "agent/getAuthenticatedExtendedCard" && !this.paramsAreValid(rpcRequest.params)) {
163
146
  throw A2AError.invalidParams(`Invalid method parameters.`);
164
147
  }
165
148
  if (method === "message/stream" || method === "tasks/resubscribe") {
@@ -168,8 +151,8 @@ var JsonRpcTransportHandler = class {
168
151
  if (!agentCard.capabilities.streaming) {
169
152
  throw A2AError.unsupportedOperation(`Method ${method} requires streaming capability.`);
170
153
  }
171
- const agentEventStream = method === "message/stream" ? this.requestHandler.sendMessageStream(params) : this.requestHandler.resubscribe(params);
172
- return async function* jsonRpcEventStream() {
154
+ const agentEventStream = method === "message/stream" ? this.requestHandler.sendMessageStream(params, context) : this.requestHandler.resubscribe(params, context);
155
+ return (async function* jsonRpcEventStream() {
173
156
  try {
174
157
  for await (const event of agentEventStream) {
175
158
  yield {
@@ -180,43 +163,50 @@ var JsonRpcTransportHandler = class {
180
163
  };
181
164
  }
182
165
  } catch (streamError) {
183
- console.error(`Error in agent event stream for ${method} (request ${requestId}):`, streamError);
166
+ console.error(
167
+ `Error in agent event stream for ${method} (request ${requestId}):`,
168
+ streamError
169
+ );
184
170
  throw streamError;
185
171
  }
186
- }();
172
+ })();
187
173
  } else {
188
174
  let result;
189
175
  switch (method) {
190
176
  case "message/send":
191
- result = await this.requestHandler.sendMessage(rpcRequest.params);
177
+ result = await this.requestHandler.sendMessage(rpcRequest.params, context);
192
178
  break;
193
179
  case "tasks/get":
194
- result = await this.requestHandler.getTask(rpcRequest.params);
180
+ result = await this.requestHandler.getTask(rpcRequest.params, context);
195
181
  break;
196
182
  case "tasks/cancel":
197
- result = await this.requestHandler.cancelTask(rpcRequest.params);
183
+ result = await this.requestHandler.cancelTask(rpcRequest.params, context);
198
184
  break;
199
185
  case "tasks/pushNotificationConfig/set":
200
186
  result = await this.requestHandler.setTaskPushNotificationConfig(
201
- rpcRequest.params
187
+ rpcRequest.params,
188
+ context
202
189
  );
203
190
  break;
204
191
  case "tasks/pushNotificationConfig/get":
205
192
  result = await this.requestHandler.getTaskPushNotificationConfig(
206
- rpcRequest.params
193
+ rpcRequest.params,
194
+ context
207
195
  );
208
196
  break;
209
197
  case "tasks/pushNotificationConfig/delete":
210
- await this.requestHandler.deleteTaskPushNotificationConfig(
211
- rpcRequest.params
212
- );
198
+ await this.requestHandler.deleteTaskPushNotificationConfig(rpcRequest.params, context);
213
199
  result = null;
214
200
  break;
215
201
  case "tasks/pushNotificationConfig/list":
216
202
  result = await this.requestHandler.listTaskPushNotificationConfigs(
217
- rpcRequest.params
203
+ rpcRequest.params,
204
+ context
218
205
  );
219
206
  break;
207
+ case "agent/getAuthenticatedExtendedCard":
208
+ result = await this.requestHandler.getAuthenticatedExtendedAgentCard(context);
209
+ break;
220
210
  default:
221
211
  throw A2AError.methodNotFound(method);
222
212
  }
@@ -227,7 +217,14 @@ var JsonRpcTransportHandler = class {
227
217
  };
228
218
  }
229
219
  } catch (error) {
230
- const a2aError = error instanceof A2AError ? error : A2AError.internalError(error.message || "An unexpected error occurred.");
220
+ let a2aError;
221
+ if (error instanceof A2AError) {
222
+ a2aError = error;
223
+ } else {
224
+ a2aError = A2AError.internalError(
225
+ error instanceof Error && error.message || "An unexpected error occurred."
226
+ );
227
+ }
231
228
  return {
232
229
  jsonrpc: "2.0",
233
230
  id: requestId,
@@ -268,17 +265,209 @@ var JsonRpcTransportHandler = class {
268
265
  }
269
266
  };
270
267
 
271
- // src/constants.ts
272
- var AGENT_CARD_PATH = ".well-known/agent-card.json";
268
+ // src/extensions.ts
269
+ var Extensions = {
270
+ /**
271
+ * Creates new {@link Extensions} from `current` and `additional`.
272
+ * If `current` already contains `additional` it is returned unmodified.
273
+ */
274
+ createFrom: (current, additional) => {
275
+ if (current?.includes(additional)) {
276
+ return current;
277
+ }
278
+ return [...current ?? [], additional];
279
+ },
280
+ /**
281
+ * Creates {@link Extensions} from comma separated extensions identifiers as per
282
+ * https://a2a-protocol.org/latest/specification/#326-service-parameters.
283
+ * Parses the output of `toServiceParameter`.
284
+ */
285
+ parseServiceParameter: (value) => {
286
+ if (!value) {
287
+ return [];
288
+ }
289
+ const unique = new Set(
290
+ value.split(",").map((ext) => ext.trim()).filter((ext) => ext.length > 0)
291
+ );
292
+ return Array.from(unique);
293
+ },
294
+ /**
295
+ * Converts {@link Extensions} to comma separated extensions identifiers as per
296
+ * https://a2a-protocol.org/latest/specification/#326-service-parameters.
297
+ */
298
+ toServiceParameter: (value) => {
299
+ return value.join(",");
300
+ }
301
+ };
302
+
303
+ // src/server/context.ts
304
+ var ServerCallContext = class {
305
+ _requestedExtensions;
306
+ _user;
307
+ _activatedExtensions;
308
+ constructor(requestedExtensions, user) {
309
+ this._requestedExtensions = requestedExtensions;
310
+ this._user = user;
311
+ }
312
+ get user() {
313
+ return this._user;
314
+ }
315
+ get activatedExtensions() {
316
+ return this._activatedExtensions;
317
+ }
318
+ get requestedExtensions() {
319
+ return this._requestedExtensions;
320
+ }
321
+ addActivatedExtension(uri) {
322
+ this._activatedExtensions = Extensions.createFrom(this._activatedExtensions, uri);
323
+ }
324
+ };
325
+
326
+ // src/server/authentication/user.ts
327
+ var UnauthenticatedUser = class {
328
+ get isAuthenticated() {
329
+ return false;
330
+ }
331
+ get userName() {
332
+ return "";
333
+ }
334
+ };
335
+
336
+ // src/sse_utils.ts
337
+ var SSE_HEADERS = {
338
+ "Content-Type": "text/event-stream",
339
+ "Cache-Control": "no-cache",
340
+ Connection: "keep-alive",
341
+ "X-Accel-Buffering": "no"
342
+ // Disable buffering in nginx
343
+ };
344
+ function formatSSEEvent(event) {
345
+ return `data: ${JSON.stringify(event)}
346
+
347
+ `;
348
+ }
349
+ function formatSSEErrorEvent(error) {
350
+ return `event: error
351
+ data: ${JSON.stringify(error)}
352
+
353
+ `;
354
+ }
355
+
356
+ // src/server/express/json_rpc_handler.ts
357
+ function jsonRpcHandler(options) {
358
+ const jsonRpcTransportHandler = new JsonRpcTransportHandler(options.requestHandler);
359
+ const router = import_express.default.Router();
360
+ router.use(import_express.default.json(), jsonErrorHandler);
361
+ router.post("/", async (req, res) => {
362
+ try {
363
+ const user = await options.userBuilder(req);
364
+ const context = new ServerCallContext(
365
+ Extensions.parseServiceParameter(req.header(HTTP_EXTENSION_HEADER)),
366
+ user ?? new UnauthenticatedUser()
367
+ );
368
+ const rpcResponseOrStream = await jsonRpcTransportHandler.handle(req.body, context);
369
+ if (context.activatedExtensions) {
370
+ res.setHeader(HTTP_EXTENSION_HEADER, Array.from(context.activatedExtensions));
371
+ }
372
+ if (typeof rpcResponseOrStream?.[Symbol.asyncIterator] === "function") {
373
+ const stream = rpcResponseOrStream;
374
+ Object.entries(SSE_HEADERS).forEach(([key, value]) => {
375
+ res.setHeader(key, value);
376
+ });
377
+ res.flushHeaders();
378
+ try {
379
+ for await (const event of stream) {
380
+ res.write(formatSSEEvent(event));
381
+ }
382
+ } catch (streamError) {
383
+ console.error(`Error during SSE streaming (request ${req.body?.id}):`, streamError);
384
+ let a2aError;
385
+ if (streamError instanceof A2AError) {
386
+ a2aError = streamError;
387
+ } else {
388
+ a2aError = A2AError.internalError(
389
+ streamError instanceof Error && streamError.message || "Streaming error."
390
+ );
391
+ }
392
+ const errorResponse = {
393
+ jsonrpc: "2.0",
394
+ id: req.body?.id || null,
395
+ // Use original request ID if available
396
+ error: a2aError.toJSONRPCError()
397
+ };
398
+ if (!res.headersSent) {
399
+ res.status(500).json(errorResponse);
400
+ } else {
401
+ res.write(formatSSEErrorEvent(errorResponse));
402
+ }
403
+ } finally {
404
+ if (!res.writableEnded) {
405
+ res.end();
406
+ }
407
+ }
408
+ } else {
409
+ const rpcResponse = rpcResponseOrStream;
410
+ res.status(200).json(rpcResponse);
411
+ }
412
+ } catch (error) {
413
+ console.error("Unhandled error in JSON-RPC POST handler:", error);
414
+ const a2aError = error instanceof A2AError ? error : A2AError.internalError("General processing error.");
415
+ const errorResponse = {
416
+ jsonrpc: "2.0",
417
+ id: req.body?.id || null,
418
+ error: a2aError.toJSONRPCError()
419
+ };
420
+ if (!res.headersSent) {
421
+ res.status(500).json(errorResponse);
422
+ } else if (!res.writableEnded) {
423
+ res.end();
424
+ }
425
+ }
426
+ });
427
+ return router;
428
+ }
429
+ var jsonErrorHandler = (err, _req, res, next) => {
430
+ if (err instanceof SyntaxError && "body" in err) {
431
+ const a2aError = A2AError.parseError("Invalid JSON payload.");
432
+ const errorResponse = {
433
+ jsonrpc: "2.0",
434
+ id: null,
435
+ error: a2aError.toJSONRPCError()
436
+ };
437
+ return res.status(400).json(errorResponse);
438
+ }
439
+ next(err);
440
+ };
441
+
442
+ // src/server/express/agent_card_handler.ts
443
+ var import_express2 = __toESM(require("express"), 1);
444
+ function agentCardHandler(options) {
445
+ const router = import_express2.default.Router();
446
+ const provider = typeof options.agentCardProvider === "function" ? options.agentCardProvider : options.agentCardProvider.getAgentCard.bind(options.agentCardProvider);
447
+ router.get("/", async (_req, res) => {
448
+ try {
449
+ const agentCard = await provider();
450
+ res.json(agentCard);
451
+ } catch (error) {
452
+ console.error("Error fetching agent card:", error);
453
+ res.status(500).json({ error: "Failed to retrieve agent card" });
454
+ }
455
+ });
456
+ return router;
457
+ }
458
+
459
+ // src/server/express/common.ts
460
+ var UserBuilder = {
461
+ noAuthentication: () => Promise.resolve(new UnauthenticatedUser())
462
+ };
273
463
 
274
464
  // src/server/express/a2a_express_app.ts
275
465
  var A2AExpressApp = class {
276
466
  requestHandler;
277
- // Kept for getAgentCard
278
- jsonRpcTransportHandler;
279
- constructor(requestHandler) {
467
+ userBuilder;
468
+ constructor(requestHandler, userBuilder = UserBuilder.noAuthentication) {
280
469
  this.requestHandler = requestHandler;
281
- this.jsonRpcTransportHandler = new JsonRpcTransportHandler(requestHandler);
470
+ this.userBuilder = userBuilder;
282
471
  }
283
472
  /**
284
473
  * Adds A2A routes to an existing Express app.
@@ -289,95 +478,517 @@ var A2AExpressApp = class {
289
478
  * @returns The Express app with A2A routes.
290
479
  */
291
480
  setupRoutes(app, baseUrl = "", middlewares, agentCardPath = AGENT_CARD_PATH) {
292
- const router = import_express.default.Router();
293
- router.use(import_express.default.json(), ...middlewares ?? []);
294
- router.use((err, req, res, next) => {
295
- if (err instanceof SyntaxError && "body" in err) {
296
- const a2aError = A2AError.parseError("Invalid JSON payload.");
297
- const errorResponse = {
298
- jsonrpc: "2.0",
299
- id: null,
300
- error: a2aError.toJSONRPCError()
301
- };
302
- return res.status(400).json(errorResponse);
303
- }
304
- next(err);
481
+ const router = import_express3.default.Router();
482
+ router.use(import_express3.default.json(), jsonErrorHandler);
483
+ if (middlewares && middlewares.length > 0) {
484
+ router.use(middlewares);
485
+ }
486
+ router.use(
487
+ jsonRpcHandler({
488
+ requestHandler: this.requestHandler,
489
+ userBuilder: this.userBuilder
490
+ })
491
+ );
492
+ router.use(`/${agentCardPath}`, agentCardHandler({ agentCardProvider: this.requestHandler }));
493
+ app.use(baseUrl, router);
494
+ return app;
495
+ }
496
+ };
497
+
498
+ // src/server/express/rest_handler.ts
499
+ var import_express4 = __toESM(require("express"), 1);
500
+
501
+ // src/errors.ts
502
+ var A2A_ERROR_CODE = {
503
+ PARSE_ERROR: -32700,
504
+ INVALID_REQUEST: -32600,
505
+ METHOD_NOT_FOUND: -32601,
506
+ INVALID_PARAMS: -32602,
507
+ INTERNAL_ERROR: -32603,
508
+ TASK_NOT_FOUND: -32001,
509
+ TASK_NOT_CANCELABLE: -32002,
510
+ PUSH_NOTIFICATION_NOT_SUPPORTED: -32003,
511
+ UNSUPPORTED_OPERATION: -32004,
512
+ CONTENT_TYPE_NOT_SUPPORTED: -32005,
513
+ INVALID_AGENT_RESPONSE: -32006,
514
+ AUTHENTICATED_EXTENDED_CARD_NOT_CONFIGURED: -32007
515
+ };
516
+
517
+ // src/server/transports/rest/rest_transport_handler.ts
518
+ var HTTP_STATUS = {
519
+ OK: 200,
520
+ CREATED: 201,
521
+ ACCEPTED: 202,
522
+ NO_CONTENT: 204,
523
+ BAD_REQUEST: 400,
524
+ UNAUTHORIZED: 401,
525
+ NOT_FOUND: 404,
526
+ CONFLICT: 409,
527
+ INTERNAL_SERVER_ERROR: 500,
528
+ NOT_IMPLEMENTED: 501
529
+ };
530
+ function mapErrorToStatus(errorCode) {
531
+ switch (errorCode) {
532
+ case A2A_ERROR_CODE.PARSE_ERROR:
533
+ case A2A_ERROR_CODE.INVALID_REQUEST:
534
+ case A2A_ERROR_CODE.INVALID_PARAMS:
535
+ return HTTP_STATUS.BAD_REQUEST;
536
+ case A2A_ERROR_CODE.METHOD_NOT_FOUND:
537
+ case A2A_ERROR_CODE.TASK_NOT_FOUND:
538
+ return HTTP_STATUS.NOT_FOUND;
539
+ case A2A_ERROR_CODE.TASK_NOT_CANCELABLE:
540
+ return HTTP_STATUS.CONFLICT;
541
+ case A2A_ERROR_CODE.PUSH_NOTIFICATION_NOT_SUPPORTED:
542
+ case A2A_ERROR_CODE.UNSUPPORTED_OPERATION:
543
+ return HTTP_STATUS.BAD_REQUEST;
544
+ default:
545
+ return HTTP_STATUS.INTERNAL_SERVER_ERROR;
546
+ }
547
+ }
548
+ function toHTTPError(error) {
549
+ const errorObject = {
550
+ code: error.code,
551
+ message: error.message
552
+ };
553
+ if (error.data !== void 0) {
554
+ errorObject.data = error.data;
555
+ }
556
+ return errorObject;
557
+ }
558
+ var RestTransportHandler = class _RestTransportHandler {
559
+ requestHandler;
560
+ constructor(requestHandler) {
561
+ this.requestHandler = requestHandler;
562
+ }
563
+ // ==========================================================================
564
+ // Public API Methods
565
+ // ==========================================================================
566
+ /**
567
+ * Gets the agent card (for capability checks).
568
+ */
569
+ async getAgentCard() {
570
+ return this.requestHandler.getAgentCard();
571
+ }
572
+ /**
573
+ * Gets the authenticated extended agent card.
574
+ */
575
+ async getAuthenticatedExtendedAgentCard(context) {
576
+ return this.requestHandler.getAuthenticatedExtendedAgentCard(context);
577
+ }
578
+ /**
579
+ * Sends a message to the agent.
580
+ * Accepts both snake_case and camelCase input, returns camelCase.
581
+ */
582
+ async sendMessage(params, context) {
583
+ const normalized = this.normalizeMessageParams(params);
584
+ return this.requestHandler.sendMessage(normalized, context);
585
+ }
586
+ /**
587
+ * Sends a message with streaming response.
588
+ * Accepts both snake_case and camelCase input, returns camelCase stream.
589
+ * @throws {A2AError} UnsupportedOperation if streaming not supported
590
+ */
591
+ async sendMessageStream(params, context) {
592
+ await this.requireCapability("streaming");
593
+ const normalized = this.normalizeMessageParams(params);
594
+ return this.requestHandler.sendMessageStream(normalized, context);
595
+ }
596
+ /**
597
+ * Gets a task by ID.
598
+ * Validates historyLength parameter if provided.
599
+ */
600
+ async getTask(taskId, context, historyLength) {
601
+ const params = { id: taskId };
602
+ if (historyLength !== void 0) {
603
+ params.historyLength = this.parseHistoryLength(historyLength);
604
+ }
605
+ return this.requestHandler.getTask(params, context);
606
+ }
607
+ /**
608
+ * Cancels a task.
609
+ */
610
+ async cancelTask(taskId, context) {
611
+ const params = { id: taskId };
612
+ return this.requestHandler.cancelTask(params, context);
613
+ }
614
+ /**
615
+ * Resubscribes to task updates.
616
+ * Returns camelCase stream of task updates.
617
+ * @throws {A2AError} UnsupportedOperation if streaming not supported
618
+ */
619
+ async resubscribe(taskId, context) {
620
+ await this.requireCapability("streaming");
621
+ const params = { id: taskId };
622
+ return this.requestHandler.resubscribe(params, context);
623
+ }
624
+ /**
625
+ * Sets a push notification configuration.
626
+ * Accepts both snake_case and camelCase input, returns camelCase.
627
+ * @throws {A2AError} PushNotificationNotSupported if push notifications not supported
628
+ */
629
+ async setTaskPushNotificationConfig(config, context) {
630
+ await this.requireCapability("pushNotifications");
631
+ const normalized = this.normalizeTaskPushNotificationConfig(config);
632
+ return this.requestHandler.setTaskPushNotificationConfig(normalized, context);
633
+ }
634
+ /**
635
+ * Lists all push notification configurations for a task.
636
+ */
637
+ async listTaskPushNotificationConfigs(taskId, context) {
638
+ return this.requestHandler.listTaskPushNotificationConfigs({ id: taskId }, context);
639
+ }
640
+ /**
641
+ * Gets a specific push notification configuration.
642
+ */
643
+ async getTaskPushNotificationConfig(taskId, configId, context) {
644
+ return this.requestHandler.getTaskPushNotificationConfig(
645
+ { id: taskId, pushNotificationConfigId: configId },
646
+ context
647
+ );
648
+ }
649
+ /**
650
+ * Deletes a push notification configuration.
651
+ */
652
+ async deleteTaskPushNotificationConfig(taskId, configId, context) {
653
+ await this.requestHandler.deleteTaskPushNotificationConfig(
654
+ { id: taskId, pushNotificationConfigId: configId },
655
+ context
656
+ );
657
+ }
658
+ // ==========================================================================
659
+ // Private Transformation Methods
660
+ // ==========================================================================
661
+ // All type conversion between REST (snake_case) and internal (camelCase) formats
662
+ /**
663
+ * Validates and normalizes message parameters.
664
+ * Accepts both snake_case and camelCase input.
665
+ * @throws {A2AError} InvalidParams if message is missing or conversion fails
666
+ */
667
+ normalizeMessageParams(input) {
668
+ if (!input.message) {
669
+ throw A2AError.invalidParams("message is required");
670
+ }
671
+ try {
672
+ return this.normalizeMessageSendParams(input);
673
+ } catch (error) {
674
+ if (error instanceof A2AError) throw error;
675
+ throw A2AError.invalidParams(
676
+ error instanceof Error ? error.message : "Invalid message parameters"
677
+ );
678
+ }
679
+ }
680
+ /**
681
+ * Static map of capability to error for missing capabilities.
682
+ */
683
+ static CAPABILITY_ERRORS = {
684
+ streaming: () => A2AError.unsupportedOperation("Agent does not support streaming"),
685
+ pushNotifications: () => A2AError.pushNotificationNotSupported()
686
+ };
687
+ /**
688
+ * Validates that the agent supports a required capability.
689
+ * @throws {A2AError} UnsupportedOperation for streaming, PushNotificationNotSupported for push notifications
690
+ */
691
+ async requireCapability(capability) {
692
+ const agentCard = await this.getAgentCard();
693
+ if (!agentCard.capabilities?.[capability]) {
694
+ throw _RestTransportHandler.CAPABILITY_ERRORS[capability]();
695
+ }
696
+ }
697
+ /**
698
+ * Parses and validates historyLength query parameter.
699
+ */
700
+ parseHistoryLength(value) {
701
+ if (value === void 0 || value === null) {
702
+ throw A2AError.invalidParams("historyLength is required");
703
+ }
704
+ const parsed = parseInt(String(value), 10);
705
+ if (isNaN(parsed)) {
706
+ throw A2AError.invalidParams("historyLength must be a valid integer");
707
+ }
708
+ if (parsed < 0) {
709
+ throw A2AError.invalidParams("historyLength must be non-negative");
710
+ }
711
+ return parsed;
712
+ }
713
+ /**
714
+ * Normalizes Part input - accepts both snake_case and camelCase for file mimeType.
715
+ */
716
+ normalizePart(part) {
717
+ if (part.kind === "text") return { kind: "text", text: part.text };
718
+ if (part.kind === "file") {
719
+ const file = this.normalizeFile(part.file);
720
+ return { kind: "file", file, metadata: part.metadata };
721
+ }
722
+ return { kind: "data", data: part.data, metadata: part.metadata };
723
+ }
724
+ /**
725
+ * Normalizes File input - accepts both snake_case (mime_type) and camelCase (mimeType).
726
+ */
727
+ normalizeFile(f) {
728
+ const file = f;
729
+ const mimeType = file.mimeType ?? file.mime_type;
730
+ if ("bytes" in file) {
731
+ return { bytes: file.bytes, mimeType, name: file.name };
732
+ }
733
+ return { uri: file.uri, mimeType, name: file.name };
734
+ }
735
+ /**
736
+ * Normalizes Message input - accepts both snake_case and camelCase.
737
+ */
738
+ normalizeMessage(input) {
739
+ const m = input;
740
+ const messageId = m.messageId ?? m.message_id;
741
+ if (!messageId) {
742
+ throw A2AError.invalidParams("message.messageId is required");
743
+ }
744
+ if (!m.parts || !Array.isArray(m.parts)) {
745
+ throw A2AError.invalidParams("message.parts must be an array");
746
+ }
747
+ return {
748
+ contextId: m.contextId ?? m.context_id,
749
+ extensions: m.extensions,
750
+ kind: "message",
751
+ messageId,
752
+ metadata: m.metadata,
753
+ parts: m.parts.map((p) => this.normalizePart(p)),
754
+ referenceTaskIds: m.referenceTaskIds ?? m.reference_task_ids,
755
+ role: m.role,
756
+ taskId: m.taskId ?? m.task_id
757
+ };
758
+ }
759
+ /**
760
+ * Normalizes MessageSendParams - accepts both snake_case and camelCase.
761
+ */
762
+ normalizeMessageSendParams(input) {
763
+ const p = input;
764
+ const config = p.configuration;
765
+ return {
766
+ configuration: config ? {
767
+ acceptedOutputModes: config.acceptedOutputModes ?? config.accepted_output_modes,
768
+ blocking: config.blocking,
769
+ historyLength: config.historyLength ?? config.history_length
770
+ } : void 0,
771
+ message: this.normalizeMessage(p.message),
772
+ metadata: p.metadata
773
+ };
774
+ }
775
+ /**
776
+ * Normalizes TaskPushNotificationConfig - accepts both snake_case and camelCase.
777
+ */
778
+ normalizeTaskPushNotificationConfig(input) {
779
+ const c = input;
780
+ const taskId = c.taskId ?? c.task_id;
781
+ if (!taskId) {
782
+ throw A2AError.invalidParams("taskId is required");
783
+ }
784
+ const pnConfig = c.pushNotificationConfig ?? c.push_notification_config;
785
+ if (!pnConfig) {
786
+ throw A2AError.invalidParams("pushNotificationConfig is required");
787
+ }
788
+ return {
789
+ pushNotificationConfig: pnConfig,
790
+ taskId
791
+ };
792
+ }
793
+ };
794
+
795
+ // src/server/express/rest_handler.ts
796
+ var restErrorHandler = (err, _req, res, next) => {
797
+ if (err instanceof SyntaxError && "body" in err) {
798
+ const a2aError = A2AError.parseError("Invalid JSON payload.");
799
+ return res.status(400).json(toHTTPError(a2aError));
800
+ }
801
+ next(err);
802
+ };
803
+ function restHandler(options) {
804
+ const router = import_express4.default.Router();
805
+ const restTransportHandler = new RestTransportHandler(options.requestHandler);
806
+ router.use(import_express4.default.json(), restErrorHandler);
807
+ const buildContext = async (req) => {
808
+ const user = await options.userBuilder(req);
809
+ return new ServerCallContext(
810
+ Extensions.parseServiceParameter(req.header(HTTP_EXTENSION_HEADER)),
811
+ user
812
+ );
813
+ };
814
+ const setExtensionsHeader = (res, context) => {
815
+ if (context.activatedExtensions) {
816
+ res.setHeader(HTTP_EXTENSION_HEADER, Array.from(context.activatedExtensions));
817
+ }
818
+ };
819
+ const sendResponse = (res, statusCode, context, body) => {
820
+ setExtensionsHeader(res, context);
821
+ res.status(statusCode);
822
+ if (statusCode === HTTP_STATUS.NO_CONTENT) {
823
+ res.end();
824
+ } else {
825
+ res.json(body);
826
+ }
827
+ };
828
+ const sendStreamResponse = async (res, stream, context) => {
829
+ const iterator = stream[Symbol.asyncIterator]();
830
+ let firstResult;
831
+ try {
832
+ firstResult = await iterator.next();
833
+ } catch (error) {
834
+ const a2aError = error instanceof A2AError ? error : A2AError.internalError(error instanceof Error ? error.message : "Streaming error");
835
+ const statusCode = mapErrorToStatus(a2aError.code);
836
+ sendResponse(res, statusCode, context, toHTTPError(a2aError));
837
+ return;
838
+ }
839
+ Object.entries(SSE_HEADERS).forEach(([key, value]) => {
840
+ res.setHeader(key, value);
305
841
  });
306
- router.get(`/${agentCardPath}`, async (req, res) => {
307
- try {
308
- const agentCard = await this.requestHandler.getAgentCard();
309
- res.json(agentCard);
310
- } catch (error) {
311
- console.error("Error fetching agent card:", error);
312
- res.status(500).json({ error: "Failed to retrieve agent card" });
842
+ setExtensionsHeader(res, context);
843
+ res.flushHeaders();
844
+ try {
845
+ if (!firstResult.done) {
846
+ res.write(formatSSEEvent(firstResult.value));
313
847
  }
314
- });
315
- router.post("/", async (req, res) => {
848
+ for await (const event of { [Symbol.asyncIterator]: () => iterator }) {
849
+ res.write(formatSSEEvent(event));
850
+ }
851
+ } catch (streamError) {
852
+ console.error("SSE streaming error:", streamError);
853
+ const a2aError = streamError instanceof A2AError ? streamError : A2AError.internalError(
854
+ streamError instanceof Error ? streamError.message : "Streaming error"
855
+ );
856
+ if (!res.writableEnded) {
857
+ res.write(formatSSEErrorEvent(toHTTPError(a2aError)));
858
+ }
859
+ } finally {
860
+ if (!res.writableEnded) {
861
+ res.end();
862
+ }
863
+ }
864
+ };
865
+ const handleError = (res, error) => {
866
+ if (res.headersSent) {
867
+ if (!res.writableEnded) {
868
+ res.end();
869
+ }
870
+ return;
871
+ }
872
+ const a2aError = error instanceof A2AError ? error : A2AError.internalError(error instanceof Error ? error.message : "Internal server error");
873
+ const statusCode = mapErrorToStatus(a2aError.code);
874
+ res.status(statusCode).json(toHTTPError(a2aError));
875
+ };
876
+ const asyncHandler = (handler) => {
877
+ return async (req, res) => {
316
878
  try {
317
- const rpcResponseOrStream = await this.jsonRpcTransportHandler.handle(req.body);
318
- if (typeof rpcResponseOrStream?.[Symbol.asyncIterator] === "function") {
319
- const stream = rpcResponseOrStream;
320
- res.setHeader("Content-Type", "text/event-stream");
321
- res.setHeader("Cache-Control", "no-cache");
322
- res.setHeader("Connection", "keep-alive");
323
- res.flushHeaders();
324
- try {
325
- for await (const event of stream) {
326
- res.write(`id: ${(/* @__PURE__ */ new Date()).getTime()}
327
- `);
328
- res.write(`data: ${JSON.stringify(event)}
329
-
330
- `);
331
- }
332
- } catch (streamError) {
333
- console.error(`Error during SSE streaming (request ${req.body?.id}):`, streamError);
334
- const a2aError = streamError instanceof A2AError ? streamError : A2AError.internalError(streamError.message || "Streaming error.");
335
- const errorResponse = {
336
- jsonrpc: "2.0",
337
- id: req.body?.id || null,
338
- // Use original request ID if available
339
- error: a2aError.toJSONRPCError()
340
- };
341
- if (!res.headersSent) {
342
- res.status(500).json(errorResponse);
343
- } else {
344
- res.write(`id: ${(/* @__PURE__ */ new Date()).getTime()}
345
- `);
346
- res.write(`event: error
347
- `);
348
- res.write(`data: ${JSON.stringify(errorResponse)}
349
-
350
- `);
351
- }
352
- } finally {
353
- if (!res.writableEnded) {
354
- res.end();
355
- }
356
- }
357
- } else {
358
- const rpcResponse = rpcResponseOrStream;
359
- res.status(200).json(rpcResponse);
360
- }
879
+ await handler(req, res);
361
880
  } catch (error) {
362
- console.error("Unhandled error in A2AExpressApp POST handler:", error);
363
- const a2aError = error instanceof A2AError ? error : A2AError.internalError("General processing error.");
364
- const errorResponse = {
365
- jsonrpc: "2.0",
366
- id: req.body?.id || null,
367
- error: a2aError.toJSONRPCError()
368
- };
369
- if (!res.headersSent) {
370
- res.status(500).json(errorResponse);
371
- } else if (!res.writableEnded) {
372
- res.end();
373
- }
881
+ handleError(res, error);
374
882
  }
375
- });
376
- app.use(baseUrl, router);
377
- return app;
378
- }
379
- };
883
+ };
884
+ };
885
+ router.get(
886
+ "/v1/card",
887
+ asyncHandler(async (req, res) => {
888
+ const context = await buildContext(req);
889
+ const result = await restTransportHandler.getAuthenticatedExtendedAgentCard(context);
890
+ sendResponse(res, HTTP_STATUS.OK, context, result);
891
+ })
892
+ );
893
+ router.post(
894
+ "/v1/message\\:send",
895
+ asyncHandler(async (req, res) => {
896
+ const context = await buildContext(req);
897
+ const result = await restTransportHandler.sendMessage(req.body, context);
898
+ sendResponse(res, HTTP_STATUS.CREATED, context, result);
899
+ })
900
+ );
901
+ router.post(
902
+ "/v1/message\\:stream",
903
+ asyncHandler(async (req, res) => {
904
+ const context = await buildContext(req);
905
+ const stream = await restTransportHandler.sendMessageStream(req.body, context);
906
+ await sendStreamResponse(res, stream, context);
907
+ })
908
+ );
909
+ router.get(
910
+ "/v1/tasks/:taskId",
911
+ asyncHandler(async (req, res) => {
912
+ const context = await buildContext(req);
913
+ const result = await restTransportHandler.getTask(
914
+ req.params.taskId,
915
+ context,
916
+ req.query.historyLength
917
+ );
918
+ sendResponse(res, HTTP_STATUS.OK, context, result);
919
+ })
920
+ );
921
+ router.post(
922
+ "/v1/tasks/:taskId\\:cancel",
923
+ asyncHandler(async (req, res) => {
924
+ const context = await buildContext(req);
925
+ const result = await restTransportHandler.cancelTask(req.params.taskId, context);
926
+ sendResponse(res, HTTP_STATUS.ACCEPTED, context, result);
927
+ })
928
+ );
929
+ router.post(
930
+ "/v1/tasks/:taskId\\:subscribe",
931
+ asyncHandler(async (req, res) => {
932
+ const context = await buildContext(req);
933
+ const stream = await restTransportHandler.resubscribe(req.params.taskId, context);
934
+ await sendStreamResponse(res, stream, context);
935
+ })
936
+ );
937
+ router.post(
938
+ "/v1/tasks/:taskId/pushNotificationConfigs",
939
+ asyncHandler(async (req, res) => {
940
+ const context = await buildContext(req);
941
+ const config = {
942
+ ...req.body,
943
+ taskId: req.params.taskId,
944
+ task_id: req.params.taskId
945
+ };
946
+ const result = await restTransportHandler.setTaskPushNotificationConfig(config, context);
947
+ sendResponse(res, HTTP_STATUS.CREATED, context, result);
948
+ })
949
+ );
950
+ router.get(
951
+ "/v1/tasks/:taskId/pushNotificationConfigs",
952
+ asyncHandler(async (req, res) => {
953
+ const context = await buildContext(req);
954
+ const result = await restTransportHandler.listTaskPushNotificationConfigs(
955
+ req.params.taskId,
956
+ context
957
+ );
958
+ sendResponse(res, HTTP_STATUS.OK, context, result);
959
+ })
960
+ );
961
+ router.get(
962
+ "/v1/tasks/:taskId/pushNotificationConfigs/:configId",
963
+ asyncHandler(async (req, res) => {
964
+ const context = await buildContext(req);
965
+ const result = await restTransportHandler.getTaskPushNotificationConfig(
966
+ req.params.taskId,
967
+ req.params.configId,
968
+ context
969
+ );
970
+ sendResponse(res, HTTP_STATUS.OK, context, result);
971
+ })
972
+ );
973
+ router.delete(
974
+ "/v1/tasks/:taskId/pushNotificationConfigs/:configId",
975
+ asyncHandler(async (req, res) => {
976
+ const context = await buildContext(req);
977
+ await restTransportHandler.deleteTaskPushNotificationConfig(
978
+ req.params.taskId,
979
+ req.params.configId,
980
+ context
981
+ );
982
+ sendResponse(res, HTTP_STATUS.NO_CONTENT, context);
983
+ })
984
+ );
985
+ return router;
986
+ }
380
987
  // Annotate the CommonJS export names for ESM import in node:
381
988
  0 && (module.exports = {
382
- A2AExpressApp
989
+ A2AExpressApp,
990
+ UserBuilder,
991
+ agentCardHandler,
992
+ jsonRpcHandler,
993
+ restHandler
383
994
  });