@a2a-js/sdk 0.3.3 → 0.3.5
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.
- package/README.md +155 -22
- package/dist/{chunk-JA52GYRU.js → chunk-SY3G7ITG.js} +35 -6
- package/dist/client/index.cjs +33 -0
- package/dist/client/index.d.cts +22 -1
- package/dist/client/index.d.ts +22 -1
- package/dist/client/index.js +33 -0
- package/dist/server/express/index.cjs +48 -7
- package/dist/server/express/index.d.cts +1 -1
- package/dist/server/express/index.d.ts +1 -1
- package/dist/server/express/index.js +14 -2
- package/dist/server/index.cjs +201 -27
- package/dist/server/index.d.cts +43 -4
- package/dist/server/index.d.ts +43 -4
- package/dist/server/index.js +165 -22
- package/package.json +7 -4
package/dist/server/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
A2AError,
|
|
3
3
|
JsonRpcTransportHandler
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-SY3G7ITG.js";
|
|
5
5
|
|
|
6
6
|
// src/server/agent_execution/request_context.ts
|
|
7
7
|
var RequestContext = class {
|
|
@@ -257,6 +257,109 @@ 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
|
+
notificationChain;
|
|
303
|
+
options;
|
|
304
|
+
constructor(pushNotificationStore, options = {}) {
|
|
305
|
+
this.pushNotificationStore = pushNotificationStore;
|
|
306
|
+
this.notificationChain = /* @__PURE__ */ new Map();
|
|
307
|
+
this.options = {
|
|
308
|
+
timeout: 5e3,
|
|
309
|
+
tokenHeaderName: "X-A2A-Notification-Token",
|
|
310
|
+
...options
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
async send(task) {
|
|
314
|
+
const pushConfigs = await this.pushNotificationStore.load(task.id);
|
|
315
|
+
if (!pushConfigs || pushConfigs.length === 0) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
const lastPromise = this.notificationChain.get(task.id) ?? Promise.resolve();
|
|
319
|
+
const newPromise = lastPromise.then(async () => {
|
|
320
|
+
const dispatches = pushConfigs.map(async (pushConfig) => {
|
|
321
|
+
try {
|
|
322
|
+
await this._dispatchNotification(task, pushConfig);
|
|
323
|
+
} catch (error) {
|
|
324
|
+
console.error(`Error sending push notification for task_id=${task.id} to URL: ${pushConfig.url}. Error:`, error);
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
await Promise.all(dispatches);
|
|
328
|
+
});
|
|
329
|
+
this.notificationChain.set(task.id, newPromise);
|
|
330
|
+
newPromise.finally(() => {
|
|
331
|
+
if (this.notificationChain.get(task.id) === newPromise) {
|
|
332
|
+
this.notificationChain.delete(task.id);
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
async _dispatchNotification(task, pushConfig) {
|
|
337
|
+
const url = pushConfig.url;
|
|
338
|
+
const controller = new AbortController();
|
|
339
|
+
const timeoutId = setTimeout(() => controller.abort(), this.options.timeout);
|
|
340
|
+
try {
|
|
341
|
+
const headers = {
|
|
342
|
+
"Content-Type": "application/json"
|
|
343
|
+
};
|
|
344
|
+
if (pushConfig.token) {
|
|
345
|
+
headers[this.options.tokenHeaderName] = pushConfig.token;
|
|
346
|
+
}
|
|
347
|
+
const response = await fetch(url, {
|
|
348
|
+
method: "POST",
|
|
349
|
+
headers,
|
|
350
|
+
body: JSON.stringify(task),
|
|
351
|
+
signal: controller.signal
|
|
352
|
+
});
|
|
353
|
+
if (!response.ok) {
|
|
354
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
355
|
+
}
|
|
356
|
+
console.info(`Push notification sent for task_id=${task.id} to URL: ${url}`);
|
|
357
|
+
} finally {
|
|
358
|
+
clearTimeout(timeoutId);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
|
|
260
363
|
// src/server/request_handler/default_request_handler.ts
|
|
261
364
|
var terminalStates = ["completed", "failed", "canceled", "rejected"];
|
|
262
365
|
var DefaultRequestHandler = class {
|
|
@@ -265,14 +368,18 @@ var DefaultRequestHandler = class {
|
|
|
265
368
|
taskStore;
|
|
266
369
|
agentExecutor;
|
|
267
370
|
eventBusManager;
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
constructor(agentCard, taskStore, agentExecutor, eventBusManager = new DefaultExecutionEventBusManager(), extendedAgentCard) {
|
|
371
|
+
pushNotificationStore;
|
|
372
|
+
pushNotificationSender;
|
|
373
|
+
constructor(agentCard, taskStore, agentExecutor, eventBusManager = new DefaultExecutionEventBusManager(), pushNotificationStore, pushNotificationSender, extendedAgentCard) {
|
|
271
374
|
this.agentCard = agentCard;
|
|
272
375
|
this.taskStore = taskStore;
|
|
273
376
|
this.agentExecutor = agentExecutor;
|
|
274
377
|
this.eventBusManager = eventBusManager;
|
|
275
378
|
this.extendedAgentCard = extendedAgentCard;
|
|
379
|
+
if (agentCard.capabilities.pushNotifications) {
|
|
380
|
+
this.pushNotificationStore = pushNotificationStore || new InMemoryPushNotificationStore();
|
|
381
|
+
this.pushNotificationSender = pushNotificationSender || new DefaultPushNotificationSender(this.pushNotificationStore);
|
|
382
|
+
}
|
|
276
383
|
}
|
|
277
384
|
async getAgentCard() {
|
|
278
385
|
return this.agentCard;
|
|
@@ -294,6 +401,8 @@ var DefaultRequestHandler = class {
|
|
|
294
401
|
if (terminalStates.includes(task.status.state)) {
|
|
295
402
|
throw A2AError.invalidRequest(`Task ${task.id} is in a terminal state (${task.status.state}) and cannot be modified.`);
|
|
296
403
|
}
|
|
404
|
+
task.history = [...task.history || [], incomingMessage];
|
|
405
|
+
await this.taskStore.save(task);
|
|
297
406
|
}
|
|
298
407
|
if (incomingMessage.referenceTaskIds && incomingMessage.referenceTaskIds.length > 0) {
|
|
299
408
|
referenceTasks = [];
|
|
@@ -324,9 +433,16 @@ var DefaultRequestHandler = class {
|
|
|
324
433
|
try {
|
|
325
434
|
for await (const event of eventQueue.events()) {
|
|
326
435
|
await resultManager.processEvent(event);
|
|
436
|
+
await this._sendPushNotificationIfNeeded(event);
|
|
327
437
|
if (options?.firstResultResolver && !firstResultSent) {
|
|
328
|
-
|
|
329
|
-
|
|
438
|
+
let firstResult;
|
|
439
|
+
if (event.kind === "message") {
|
|
440
|
+
firstResult = event;
|
|
441
|
+
} else {
|
|
442
|
+
firstResult = resultManager.getCurrentTask();
|
|
443
|
+
}
|
|
444
|
+
if (firstResult) {
|
|
445
|
+
options.firstResultResolver(firstResult);
|
|
330
446
|
firstResultSent = true;
|
|
331
447
|
}
|
|
332
448
|
}
|
|
@@ -355,6 +471,9 @@ var DefaultRequestHandler = class {
|
|
|
355
471
|
resultManager.setContext(incomingMessage);
|
|
356
472
|
const requestContext = await this._createRequestContext(incomingMessage, taskId, false);
|
|
357
473
|
const finalMessageForAgent = requestContext.userMessage;
|
|
474
|
+
if (params.configuration?.pushNotificationConfig && this.agentCard.capabilities.pushNotifications) {
|
|
475
|
+
await this.pushNotificationStore?.save(taskId, params.configuration.pushNotificationConfig);
|
|
476
|
+
}
|
|
358
477
|
const eventBus = this.eventBusManager.createOrGetByTaskId(taskId);
|
|
359
478
|
const eventQueue = new ExecutionEventQueue(eventBus);
|
|
360
479
|
this.agentExecutor.execute(requestContext, eventBus).catch((err) => {
|
|
@@ -422,6 +541,9 @@ var DefaultRequestHandler = class {
|
|
|
422
541
|
const finalMessageForAgent = requestContext.userMessage;
|
|
423
542
|
const eventBus = this.eventBusManager.createOrGetByTaskId(taskId);
|
|
424
543
|
const eventQueue = new ExecutionEventQueue(eventBus);
|
|
544
|
+
if (params.configuration?.pushNotificationConfig && this.agentCard.capabilities.pushNotifications) {
|
|
545
|
+
await this.pushNotificationStore?.save(taskId, params.configuration.pushNotificationConfig);
|
|
546
|
+
}
|
|
425
547
|
this.agentExecutor.execute(requestContext, eventBus).catch((err) => {
|
|
426
548
|
console.error(`Agent execution failed for stream message ${finalMessageForAgent.messageId}:`, err);
|
|
427
549
|
const errorTaskStatus = {
|
|
@@ -449,6 +571,7 @@ var DefaultRequestHandler = class {
|
|
|
449
571
|
try {
|
|
450
572
|
for await (const event of eventQueue.events()) {
|
|
451
573
|
await resultManager.processEvent(event);
|
|
574
|
+
await this._sendPushNotificationIfNeeded(event);
|
|
452
575
|
yield event;
|
|
453
576
|
}
|
|
454
577
|
} finally {
|
|
@@ -480,7 +603,9 @@ var DefaultRequestHandler = class {
|
|
|
480
603
|
}
|
|
481
604
|
const eventBus = this.eventBusManager.getByTaskId(params.id);
|
|
482
605
|
if (eventBus) {
|
|
606
|
+
const eventQueue = new ExecutionEventQueue(eventBus);
|
|
483
607
|
await this.agentExecutor.cancelTask(params.id, eventBus);
|
|
608
|
+
await this._processEvents(params.id, new ResultManager(this.taskStore), eventQueue);
|
|
484
609
|
} else {
|
|
485
610
|
task.status = {
|
|
486
611
|
state: "canceled",
|
|
@@ -499,6 +624,12 @@ var DefaultRequestHandler = class {
|
|
|
499
624
|
await this.taskStore.save(task);
|
|
500
625
|
}
|
|
501
626
|
const latestTask = await this.taskStore.load(params.id);
|
|
627
|
+
if (!latestTask) {
|
|
628
|
+
throw A2AError.internalError(`Task ${params.id} not found after cancellation.`);
|
|
629
|
+
}
|
|
630
|
+
if (latestTask.status.state != "canceled") {
|
|
631
|
+
throw A2AError.taskNotCancelable(params.id);
|
|
632
|
+
}
|
|
502
633
|
return latestTask;
|
|
503
634
|
}
|
|
504
635
|
async setTaskPushNotificationConfig(params) {
|
|
@@ -513,10 +644,7 @@ var DefaultRequestHandler = class {
|
|
|
513
644
|
if (!pushNotificationConfig.id) {
|
|
514
645
|
pushNotificationConfig.id = taskId;
|
|
515
646
|
}
|
|
516
|
-
|
|
517
|
-
const updatedConfigs = configs.filter((c) => c.id !== pushNotificationConfig.id);
|
|
518
|
-
updatedConfigs.push(pushNotificationConfig);
|
|
519
|
-
this.pushNotificationConfigs.set(taskId, updatedConfigs);
|
|
647
|
+
await this.pushNotificationStore?.save(taskId, pushNotificationConfig);
|
|
520
648
|
return params;
|
|
521
649
|
}
|
|
522
650
|
async getTaskPushNotificationConfig(params) {
|
|
@@ -527,7 +655,7 @@ var DefaultRequestHandler = class {
|
|
|
527
655
|
if (!task) {
|
|
528
656
|
throw A2AError.taskNotFound(params.id);
|
|
529
657
|
}
|
|
530
|
-
const configs = this.
|
|
658
|
+
const configs = await this.pushNotificationStore?.load(params.id) || [];
|
|
531
659
|
if (configs.length === 0) {
|
|
532
660
|
throw A2AError.internalError(`Push notification config not found for task ${params.id}.`);
|
|
533
661
|
}
|
|
@@ -551,7 +679,7 @@ var DefaultRequestHandler = class {
|
|
|
551
679
|
if (!task) {
|
|
552
680
|
throw A2AError.taskNotFound(params.id);
|
|
553
681
|
}
|
|
554
|
-
const configs = this.
|
|
682
|
+
const configs = await this.pushNotificationStore?.load(params.id) || [];
|
|
555
683
|
return configs.map((config) => ({
|
|
556
684
|
taskId: params.id,
|
|
557
685
|
pushNotificationConfig: config
|
|
@@ -566,16 +694,7 @@ var DefaultRequestHandler = class {
|
|
|
566
694
|
throw A2AError.taskNotFound(params.id);
|
|
567
695
|
}
|
|
568
696
|
const { id: taskId, pushNotificationConfigId } = params;
|
|
569
|
-
|
|
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
|
-
}
|
|
697
|
+
await this.pushNotificationStore?.delete(taskId, pushNotificationConfigId);
|
|
579
698
|
}
|
|
580
699
|
async *resubscribe(params) {
|
|
581
700
|
if (!this.agentCard.capabilities.streaming) {
|
|
@@ -610,6 +729,28 @@ var DefaultRequestHandler = class {
|
|
|
610
729
|
eventQueue.stop();
|
|
611
730
|
}
|
|
612
731
|
}
|
|
732
|
+
async _sendPushNotificationIfNeeded(event) {
|
|
733
|
+
if (!this.agentCard.capabilities.pushNotifications) {
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
let taskId = "";
|
|
737
|
+
if (event.kind == "task") {
|
|
738
|
+
const task2 = event;
|
|
739
|
+
taskId = task2.id;
|
|
740
|
+
} else {
|
|
741
|
+
taskId = event.taskId;
|
|
742
|
+
}
|
|
743
|
+
if (!taskId) {
|
|
744
|
+
console.error(`Task ID not found for event ${event.kind}.`);
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
const task = await this.taskStore.load(taskId);
|
|
748
|
+
if (!task) {
|
|
749
|
+
console.error(`Task ${taskId} not found.`);
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
this.pushNotificationSender?.send(task);
|
|
753
|
+
}
|
|
613
754
|
};
|
|
614
755
|
|
|
615
756
|
// src/server/store.ts
|
|
@@ -627,8 +768,10 @@ export {
|
|
|
627
768
|
A2AError,
|
|
628
769
|
DefaultExecutionEventBus,
|
|
629
770
|
DefaultExecutionEventBusManager,
|
|
771
|
+
DefaultPushNotificationSender,
|
|
630
772
|
DefaultRequestHandler,
|
|
631
773
|
ExecutionEventQueue,
|
|
774
|
+
InMemoryPushNotificationStore,
|
|
632
775
|
InMemoryTaskStore,
|
|
633
776
|
JsonRpcTransportHandler,
|
|
634
777
|
RequestContext,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@a2a-js/sdk",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "Server & Client SDK for Agent2Agent protocol",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"@types/mocha": "^10.0.10",
|
|
48
48
|
"@types/node": "^22.13.14",
|
|
49
49
|
"@types/sinon": "^17.0.4",
|
|
50
|
+
"@types/supertest": "^6.0.3",
|
|
50
51
|
"c8": "^10.1.3",
|
|
51
52
|
"chai": "^5.2.0",
|
|
52
53
|
"express": "^5.1.0",
|
|
@@ -55,6 +56,7 @@
|
|
|
55
56
|
"json-schema-to-typescript": "^15.0.4",
|
|
56
57
|
"mocha": "^11.6.0",
|
|
57
58
|
"sinon": "^20.0.0",
|
|
59
|
+
"supertest": "^7.1.4",
|
|
58
60
|
"tsup": "^8.5.0",
|
|
59
61
|
"tsx": "^4.19.3",
|
|
60
62
|
"typescript": "^5.8.2"
|
|
@@ -66,8 +68,9 @@
|
|
|
66
68
|
"test": "mocha test/**/*.spec.ts",
|
|
67
69
|
"coverage": "c8 npm run test",
|
|
68
70
|
"generate": "curl https://raw.githubusercontent.com/google-a2a/A2A/refs/heads/main/specification/json/a2a.json > spec.json && node scripts/generateTypes.js && rm spec.json",
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
+
"a2a:cli": "tsx src/samples/cli.ts",
|
|
72
|
+
"agents:movie-agent": "tsx src/samples/agents/movie-agent/index.ts",
|
|
73
|
+
"agents:sample-agent": "tsx src/samples/agents/sample-agent/index.ts"
|
|
71
74
|
},
|
|
72
75
|
"dependencies": {
|
|
73
76
|
"uuid": "^11.1.0"
|
|
@@ -83,4 +86,4 @@
|
|
|
83
86
|
"mocha": {
|
|
84
87
|
"require": "tsx"
|
|
85
88
|
}
|
|
86
|
-
}
|
|
89
|
+
}
|