@a2a-js/sdk 0.3.2 → 0.3.4

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.
@@ -1,5 +1,5 @@
1
1
  import { EventEmitter } from 'events';
2
- import { B as Message, aw as Task, aO as TaskStatusUpdateEvent, aQ as TaskArtifactUpdateEvent, ac as AgentCard, w as MessageSendParams, V as TaskQueryParams, X as TaskIdParams, Z as TaskPushNotificationConfig, a1 as GetTaskPushNotificationConfigParams, a5 as ListTaskPushNotificationConfigParams, a7 as DeleteTaskPushNotificationConfigParams, i as JSONRPCResponse, au as JSONRPCError } from '../types-DNKcmF0f.js';
2
+ import { B as Message, aw as Task, aO as TaskStatusUpdateEvent, aQ as TaskArtifactUpdateEvent, y as PushNotificationConfig, ac as AgentCard, w as MessageSendParams, V as TaskQueryParams, X as TaskIdParams, Z as TaskPushNotificationConfig, a1 as GetTaskPushNotificationConfigParams, a5 as ListTaskPushNotificationConfigParams, a7 as DeleteTaskPushNotificationConfigParams, i as JSONRPCResponse, au as JSONRPCError } from '../types-DNKcmF0f.js';
3
3
  import { A as A2ARequestHandler } from '../a2a_request_handler-B5t-IxgA.js';
4
4
 
5
5
  type AgentExecutionEvent = Message | Task | TaskStatusUpdateEvent | TaskArtifactUpdateEvent;
@@ -119,14 +119,31 @@ declare class InMemoryTaskStore implements TaskStore {
119
119
  save(task: Task): Promise<void>;
120
120
  }
121
121
 
122
+ interface PushNotificationStore {
123
+ save(taskId: string, pushNotificationConfig: PushNotificationConfig): Promise<void>;
124
+ load(taskId: string): Promise<PushNotificationConfig[]>;
125
+ delete(taskId: string, configId?: string): Promise<void>;
126
+ }
127
+ declare class InMemoryPushNotificationStore implements PushNotificationStore {
128
+ private store;
129
+ save(taskId: string, pushNotificationConfig: PushNotificationConfig): Promise<void>;
130
+ load(taskId: string): Promise<PushNotificationConfig[]>;
131
+ delete(taskId: string, configId?: string): Promise<void>;
132
+ }
133
+
134
+ interface PushNotificationSender {
135
+ send(task: Task): Promise<void>;
136
+ }
137
+
122
138
  declare class DefaultRequestHandler implements A2ARequestHandler {
123
139
  private readonly agentCard;
124
140
  private readonly extendedAgentCard?;
125
141
  private readonly taskStore;
126
142
  private readonly agentExecutor;
127
143
  private readonly eventBusManager;
128
- private readonly pushNotificationConfigs;
129
- constructor(agentCard: AgentCard, taskStore: TaskStore, agentExecutor: AgentExecutor, eventBusManager?: ExecutionEventBusManager, extendedAgentCard?: AgentCard);
144
+ private readonly pushNotificationStore?;
145
+ private readonly pushNotificationSender?;
146
+ constructor(agentCard: AgentCard, taskStore: TaskStore, agentExecutor: AgentExecutor, eventBusManager?: ExecutionEventBusManager, pushNotificationStore?: PushNotificationStore, pushNotificationSender?: PushNotificationSender, extendedAgentCard?: AgentCard);
130
147
  getAgentCard(): Promise<AgentCard>;
131
148
  getAuthenticatedExtendedAgentCard(): Promise<AgentCard>;
132
149
  private _createRequestContext;
@@ -140,6 +157,7 @@ declare class DefaultRequestHandler implements A2ARequestHandler {
140
157
  listTaskPushNotificationConfigs(params: ListTaskPushNotificationConfigParams): Promise<TaskPushNotificationConfig[]>;
141
158
  deleteTaskPushNotificationConfig(params: DeleteTaskPushNotificationConfigParams): Promise<void>;
142
159
  resubscribe(params: TaskIdParams): AsyncGenerator<Task | TaskStatusUpdateEvent | TaskArtifactUpdateEvent, void, undefined>;
160
+ private _sendPushNotificationIfNeeded;
143
161
  }
144
162
 
145
163
  declare class ResultManager {
@@ -207,4 +225,22 @@ declare class A2AError extends Error {
207
225
  static authenticatedExtendedCardNotConfigured(): A2AError;
208
226
  }
209
227
 
210
- export { A2AError, A2ARequestHandler, type AgentExecutionEvent, type AgentExecutor, DefaultExecutionEventBus, DefaultExecutionEventBusManager, DefaultRequestHandler, type ExecutionEventBus, type ExecutionEventBusManager, ExecutionEventQueue, InMemoryTaskStore, JsonRpcTransportHandler, RequestContext, ResultManager, type TaskStore };
228
+ interface DefaultPushNotificationSenderOptions {
229
+ /**
230
+ * Timeout in milliseconds for the abort controller. Defaults to 5000ms.
231
+ */
232
+ timeout?: number;
233
+ /**
234
+ * Custom header name for the token. Defaults to 'X-A2A-Notification-Token'.
235
+ */
236
+ tokenHeaderName?: string;
237
+ }
238
+ declare class DefaultPushNotificationSender implements PushNotificationSender {
239
+ private readonly pushNotificationStore;
240
+ private readonly options;
241
+ constructor(pushNotificationStore: PushNotificationStore, options?: DefaultPushNotificationSenderOptions);
242
+ send(task: Task): Promise<void>;
243
+ private _dispatchNotification;
244
+ }
245
+
246
+ export { A2AError, A2ARequestHandler, type AgentExecutionEvent, type AgentExecutor, DefaultExecutionEventBus, DefaultExecutionEventBusManager, DefaultPushNotificationSender, type DefaultPushNotificationSenderOptions, DefaultRequestHandler, type ExecutionEventBus, type ExecutionEventBusManager, ExecutionEventQueue, InMemoryPushNotificationStore, InMemoryTaskStore, JsonRpcTransportHandler, type PushNotificationSender, type PushNotificationStore, RequestContext, ResultManager, type TaskStore };
@@ -257,6 +257,97 @@ var ResultManager = class {
257
257
  }
258
258
  };
259
259
 
260
+ // src/server/push_notification/push_notification_store.ts
261
+ var InMemoryPushNotificationStore = class {
262
+ store = /* @__PURE__ */ new Map();
263
+ async save(taskId, pushNotificationConfig) {
264
+ const configs = this.store.get(taskId) || [];
265
+ if (!pushNotificationConfig.id) {
266
+ pushNotificationConfig.id = taskId;
267
+ }
268
+ const existingIndex = configs.findIndex((config) => config.id === pushNotificationConfig.id);
269
+ if (existingIndex !== -1) {
270
+ configs.splice(existingIndex, 1);
271
+ }
272
+ configs.push(pushNotificationConfig);
273
+ this.store.set(taskId, configs);
274
+ }
275
+ async load(taskId) {
276
+ const configs = this.store.get(taskId);
277
+ return configs || [];
278
+ }
279
+ async delete(taskId, configId) {
280
+ if (configId === void 0) {
281
+ configId = taskId;
282
+ }
283
+ const configs = this.store.get(taskId);
284
+ if (!configs) {
285
+ return;
286
+ }
287
+ const configIndex = configs.findIndex((config) => config.id === configId);
288
+ if (configIndex !== -1) {
289
+ configs.splice(configIndex, 1);
290
+ }
291
+ if (configs.length === 0) {
292
+ this.store.delete(taskId);
293
+ } else {
294
+ this.store.set(taskId, configs);
295
+ }
296
+ }
297
+ };
298
+
299
+ // src/server/push_notification/default_push_notification_sender.ts
300
+ var DefaultPushNotificationSender = class {
301
+ pushNotificationStore;
302
+ options;
303
+ constructor(pushNotificationStore, options = {}) {
304
+ this.pushNotificationStore = pushNotificationStore;
305
+ this.options = {
306
+ timeout: 5e3,
307
+ tokenHeaderName: "X-A2A-Notification-Token",
308
+ ...options
309
+ };
310
+ }
311
+ async send(task) {
312
+ const pushConfigs = await this.pushNotificationStore.load(task.id);
313
+ if (!pushConfigs || pushConfigs.length === 0) {
314
+ return;
315
+ }
316
+ pushConfigs.forEach((pushConfig) => {
317
+ this._dispatchNotification(task, pushConfig).catch((error) => {
318
+ console.error(`Error sending push notification for task_id=${task.id} to URL: ${pushConfig.url}. Error:`, error);
319
+ });
320
+ });
321
+ }
322
+ async _dispatchNotification(task, pushConfig) {
323
+ const url = pushConfig.url;
324
+ const controller = new AbortController();
325
+ const timeoutId = setTimeout(() => controller.abort(), this.options.timeout);
326
+ try {
327
+ const headers = {
328
+ "Content-Type": "application/json"
329
+ };
330
+ if (pushConfig.token) {
331
+ headers[this.options.tokenHeaderName] = pushConfig.token;
332
+ }
333
+ const response = await fetch(url, {
334
+ method: "POST",
335
+ headers,
336
+ body: JSON.stringify(task),
337
+ signal: controller.signal
338
+ });
339
+ if (!response.ok) {
340
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
341
+ }
342
+ console.info(`Push notification sent for task_id=${task.id} to URL: ${url}`);
343
+ } catch (error) {
344
+ console.error(`Error sending push notification for task_id=${task.id} to URL: ${url}. Error:`, error);
345
+ } finally {
346
+ clearTimeout(timeoutId);
347
+ }
348
+ }
349
+ };
350
+
260
351
  // src/server/request_handler/default_request_handler.ts
261
352
  var terminalStates = ["completed", "failed", "canceled", "rejected"];
262
353
  var DefaultRequestHandler = class {
@@ -265,14 +356,18 @@ var DefaultRequestHandler = class {
265
356
  taskStore;
266
357
  agentExecutor;
267
358
  eventBusManager;
268
- // Store for push notification configurations (could be part of TaskStore or separate)
269
- pushNotificationConfigs = /* @__PURE__ */ new Map();
270
- constructor(agentCard, taskStore, agentExecutor, eventBusManager = new DefaultExecutionEventBusManager(), extendedAgentCard) {
359
+ pushNotificationStore;
360
+ pushNotificationSender;
361
+ constructor(agentCard, taskStore, agentExecutor, eventBusManager = new DefaultExecutionEventBusManager(), pushNotificationStore, pushNotificationSender, extendedAgentCard) {
271
362
  this.agentCard = agentCard;
272
363
  this.taskStore = taskStore;
273
364
  this.agentExecutor = agentExecutor;
274
365
  this.eventBusManager = eventBusManager;
275
366
  this.extendedAgentCard = extendedAgentCard;
367
+ if (agentCard.capabilities.pushNotifications) {
368
+ this.pushNotificationStore = pushNotificationStore || new InMemoryPushNotificationStore();
369
+ this.pushNotificationSender = pushNotificationSender || new DefaultPushNotificationSender(this.pushNotificationStore);
370
+ }
276
371
  }
277
372
  async getAgentCard() {
278
373
  return this.agentCard;
@@ -324,6 +419,7 @@ var DefaultRequestHandler = class {
324
419
  try {
325
420
  for await (const event of eventQueue.events()) {
326
421
  await resultManager.processEvent(event);
422
+ await this._sendPushNotificationIfNeeded(event);
327
423
  if (options?.firstResultResolver && !firstResultSent) {
328
424
  if (event.kind === "message" || event.kind === "task") {
329
425
  options.firstResultResolver(event);
@@ -355,6 +451,9 @@ var DefaultRequestHandler = class {
355
451
  resultManager.setContext(incomingMessage);
356
452
  const requestContext = await this._createRequestContext(incomingMessage, taskId, false);
357
453
  const finalMessageForAgent = requestContext.userMessage;
454
+ if (params.configuration?.pushNotificationConfig && this.agentCard.capabilities.pushNotifications) {
455
+ await this.pushNotificationStore?.save(taskId, params.configuration.pushNotificationConfig);
456
+ }
358
457
  const eventBus = this.eventBusManager.createOrGetByTaskId(taskId);
359
458
  const eventQueue = new ExecutionEventQueue(eventBus);
360
459
  this.agentExecutor.execute(requestContext, eventBus).catch((err) => {
@@ -422,6 +521,9 @@ var DefaultRequestHandler = class {
422
521
  const finalMessageForAgent = requestContext.userMessage;
423
522
  const eventBus = this.eventBusManager.createOrGetByTaskId(taskId);
424
523
  const eventQueue = new ExecutionEventQueue(eventBus);
524
+ if (params.configuration?.pushNotificationConfig && this.agentCard.capabilities.pushNotifications) {
525
+ await this.pushNotificationStore?.save(taskId, params.configuration.pushNotificationConfig);
526
+ }
425
527
  this.agentExecutor.execute(requestContext, eventBus).catch((err) => {
426
528
  console.error(`Agent execution failed for stream message ${finalMessageForAgent.messageId}:`, err);
427
529
  const errorTaskStatus = {
@@ -449,6 +551,7 @@ var DefaultRequestHandler = class {
449
551
  try {
450
552
  for await (const event of eventQueue.events()) {
451
553
  await resultManager.processEvent(event);
554
+ await this._sendPushNotificationIfNeeded(event);
452
555
  yield event;
453
556
  }
454
557
  } finally {
@@ -513,10 +616,7 @@ var DefaultRequestHandler = class {
513
616
  if (!pushNotificationConfig.id) {
514
617
  pushNotificationConfig.id = taskId;
515
618
  }
516
- const configs = this.pushNotificationConfigs.get(taskId) || [];
517
- const updatedConfigs = configs.filter((c) => c.id !== pushNotificationConfig.id);
518
- updatedConfigs.push(pushNotificationConfig);
519
- this.pushNotificationConfigs.set(taskId, updatedConfigs);
619
+ await this.pushNotificationStore?.save(taskId, pushNotificationConfig);
520
620
  return params;
521
621
  }
522
622
  async getTaskPushNotificationConfig(params) {
@@ -527,7 +627,7 @@ var DefaultRequestHandler = class {
527
627
  if (!task) {
528
628
  throw A2AError.taskNotFound(params.id);
529
629
  }
530
- const configs = this.pushNotificationConfigs.get(params.id) || [];
630
+ const configs = await this.pushNotificationStore?.load(params.id) || [];
531
631
  if (configs.length === 0) {
532
632
  throw A2AError.internalError(`Push notification config not found for task ${params.id}.`);
533
633
  }
@@ -551,7 +651,7 @@ var DefaultRequestHandler = class {
551
651
  if (!task) {
552
652
  throw A2AError.taskNotFound(params.id);
553
653
  }
554
- const configs = this.pushNotificationConfigs.get(params.id) || [];
654
+ const configs = await this.pushNotificationStore?.load(params.id) || [];
555
655
  return configs.map((config) => ({
556
656
  taskId: params.id,
557
657
  pushNotificationConfig: config
@@ -566,16 +666,7 @@ var DefaultRequestHandler = class {
566
666
  throw A2AError.taskNotFound(params.id);
567
667
  }
568
668
  const { id: taskId, pushNotificationConfigId } = params;
569
- const configs = this.pushNotificationConfigs.get(taskId);
570
- if (!configs) {
571
- return;
572
- }
573
- const updatedConfigs = configs.filter((c) => c.id !== pushNotificationConfigId);
574
- if (updatedConfigs.length === 0) {
575
- this.pushNotificationConfigs.delete(taskId);
576
- } else if (updatedConfigs.length < configs.length) {
577
- this.pushNotificationConfigs.set(taskId, updatedConfigs);
578
- }
669
+ await this.pushNotificationStore?.delete(taskId, pushNotificationConfigId);
579
670
  }
580
671
  async *resubscribe(params) {
581
672
  if (!this.agentCard.capabilities.streaming) {
@@ -610,6 +701,28 @@ var DefaultRequestHandler = class {
610
701
  eventQueue.stop();
611
702
  }
612
703
  }
704
+ async _sendPushNotificationIfNeeded(event) {
705
+ if (!this.agentCard.capabilities.pushNotifications) {
706
+ return;
707
+ }
708
+ let taskId = "";
709
+ if (event.kind == "task") {
710
+ const task2 = event;
711
+ taskId = task2.id;
712
+ } else {
713
+ taskId = event.taskId;
714
+ }
715
+ if (!taskId) {
716
+ console.error(`Task ID not found for event ${event.kind}.`);
717
+ return;
718
+ }
719
+ const task = await this.taskStore.load(taskId);
720
+ if (!task) {
721
+ console.error(`Task ${taskId} not found.`);
722
+ return;
723
+ }
724
+ this.pushNotificationSender?.send(task);
725
+ }
613
726
  };
614
727
 
615
728
  // src/server/store.ts
@@ -627,8 +740,10 @@ export {
627
740
  A2AError,
628
741
  DefaultExecutionEventBus,
629
742
  DefaultExecutionEventBusManager,
743
+ DefaultPushNotificationSender,
630
744
  DefaultRequestHandler,
631
745
  ExecutionEventQueue,
746
+ InMemoryPushNotificationStore,
632
747
  InMemoryTaskStore,
633
748
  JsonRpcTransportHandler,
634
749
  RequestContext,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a2a-js/sdk",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "Server & Client SDK for Agent2Agent protocol",
5
5
  "repository": {
6
6
  "type": "git",
@@ -43,13 +43,13 @@
43
43
  "@genkit-ai/googleai": "^1.8.0",
44
44
  "@genkit-ai/vertexai": "^1.8.0",
45
45
  "@types/chai": "^5.2.2",
46
- "@types/express": "^4.17.23",
46
+ "@types/express": "^5.0.3",
47
47
  "@types/mocha": "^10.0.10",
48
48
  "@types/node": "^22.13.14",
49
49
  "@types/sinon": "^17.0.4",
50
50
  "c8": "^10.1.3",
51
51
  "chai": "^5.2.0",
52
- "express": "^4.21.2",
52
+ "express": "^5.1.0",
53
53
  "genkit": "^1.8.0",
54
54
  "gts": "^6.0.2",
55
55
  "json-schema-to-typescript": "^15.0.4",
@@ -73,7 +73,7 @@
73
73
  "uuid": "^11.1.0"
74
74
  },
75
75
  "peerDependencies": {
76
- "express": "^4.21.2"
76
+ "express": "^4.21.2 || ^5.1.0"
77
77
  },
78
78
  "peerDependenciesMeta": {
79
79
  "express": {