@a2a-js/sdk 0.2.3 → 0.2.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.
@@ -0,0 +1,842 @@
1
+ // src/server/agent_execution/request_context.ts
2
+ var RequestContext = class {
3
+ userMessage;
4
+ task;
5
+ referenceTasks;
6
+ taskId;
7
+ contextId;
8
+ constructor(userMessage, taskId, contextId, task, referenceTasks) {
9
+ this.userMessage = userMessage;
10
+ this.taskId = taskId;
11
+ this.contextId = contextId;
12
+ this.task = task;
13
+ this.referenceTasks = referenceTasks;
14
+ }
15
+ };
16
+
17
+ // src/server/events/execution_event_bus.ts
18
+ import { EventEmitter } from "events";
19
+ var DefaultExecutionEventBus = class extends EventEmitter {
20
+ constructor() {
21
+ super();
22
+ }
23
+ publish(event) {
24
+ this.emit("event", event);
25
+ }
26
+ finished() {
27
+ this.emit("finished");
28
+ }
29
+ };
30
+
31
+ // src/server/events/execution_event_bus_manager.ts
32
+ var DefaultExecutionEventBusManager = class {
33
+ taskIdToBus = /* @__PURE__ */ new Map();
34
+ /**
35
+ * Creates or retrieves an existing ExecutionEventBus based on the taskId.
36
+ * @param taskId The ID of the task.
37
+ * @returns An instance of IExecutionEventBus.
38
+ */
39
+ createOrGetByTaskId(taskId) {
40
+ if (!this.taskIdToBus.has(taskId)) {
41
+ this.taskIdToBus.set(taskId, new DefaultExecutionEventBus());
42
+ }
43
+ return this.taskIdToBus.get(taskId);
44
+ }
45
+ /**
46
+ * Retrieves an existing ExecutionEventBus based on the taskId.
47
+ * @param taskId The ID of the task.
48
+ * @returns An instance of IExecutionEventBus or undefined if not found.
49
+ */
50
+ getByTaskId(taskId) {
51
+ return this.taskIdToBus.get(taskId);
52
+ }
53
+ /**
54
+ * Removes the event bus for a given taskId.
55
+ * This should be called when an execution flow is complete to free resources.
56
+ * @param taskId The ID of the task.
57
+ */
58
+ cleanupByTaskId(taskId) {
59
+ const bus = this.taskIdToBus.get(taskId);
60
+ if (bus) {
61
+ bus.removeAllListeners();
62
+ }
63
+ this.taskIdToBus.delete(taskId);
64
+ }
65
+ };
66
+
67
+ // src/server/events/execution_event_queue.ts
68
+ var ExecutionEventQueue = class {
69
+ eventBus;
70
+ eventQueue = [];
71
+ resolvePromise;
72
+ stopped = false;
73
+ boundHandleEvent;
74
+ constructor(eventBus) {
75
+ this.eventBus = eventBus;
76
+ this.eventBus.on("event", this.handleEvent);
77
+ this.eventBus.on("finished", this.handleFinished);
78
+ }
79
+ handleEvent = (event) => {
80
+ if (this.stopped) return;
81
+ this.eventQueue.push(event);
82
+ if (this.resolvePromise) {
83
+ this.resolvePromise();
84
+ this.resolvePromise = void 0;
85
+ }
86
+ };
87
+ handleFinished = () => {
88
+ this.stop();
89
+ };
90
+ /**
91
+ * Provides an async generator that yields events from the event bus.
92
+ * Stops when a Message event is received or a TaskStatusUpdateEvent with final=true is received.
93
+ */
94
+ async *events() {
95
+ while (!this.stopped || this.eventQueue.length > 0) {
96
+ if (this.eventQueue.length > 0) {
97
+ const event = this.eventQueue.shift();
98
+ yield event;
99
+ if (event.kind === "message" || event.kind === "status-update" && event.final) {
100
+ this.handleFinished();
101
+ break;
102
+ }
103
+ } else if (!this.stopped) {
104
+ await new Promise((resolve) => {
105
+ this.resolvePromise = resolve;
106
+ });
107
+ }
108
+ }
109
+ }
110
+ /**
111
+ * Stops the event queue from processing further events.
112
+ */
113
+ stop() {
114
+ this.stopped = true;
115
+ if (this.resolvePromise) {
116
+ this.resolvePromise();
117
+ this.resolvePromise = void 0;
118
+ }
119
+ this.eventBus.off("event", this.handleEvent);
120
+ this.eventBus.off("finished", this.handleFinished);
121
+ }
122
+ };
123
+
124
+ // src/server/request_handler/default_request_handler.ts
125
+ import { v4 as uuidv4 } from "uuid";
126
+
127
+ // src/server/error.ts
128
+ var A2AError = class _A2AError extends Error {
129
+ code;
130
+ data;
131
+ taskId;
132
+ // Optional task ID context
133
+ constructor(code, message, data, taskId) {
134
+ super(message);
135
+ this.name = "A2AError";
136
+ this.code = code;
137
+ this.data = data;
138
+ this.taskId = taskId;
139
+ }
140
+ /**
141
+ * Formats the error into a standard JSON-RPC error object structure.
142
+ */
143
+ toJSONRPCError() {
144
+ const errorObject = {
145
+ code: this.code,
146
+ message: this.message
147
+ };
148
+ if (this.data !== void 0) {
149
+ errorObject.data = this.data;
150
+ }
151
+ return errorObject;
152
+ }
153
+ // Static factory methods for common errors
154
+ static parseError(message, data) {
155
+ return new _A2AError(-32700, message, data);
156
+ }
157
+ static invalidRequest(message, data) {
158
+ return new _A2AError(-32600, message, data);
159
+ }
160
+ static methodNotFound(method) {
161
+ return new _A2AError(
162
+ -32601,
163
+ `Method not found: ${method}`
164
+ );
165
+ }
166
+ static invalidParams(message, data) {
167
+ return new _A2AError(-32602, message, data);
168
+ }
169
+ static internalError(message, data) {
170
+ return new _A2AError(-32603, message, data);
171
+ }
172
+ static taskNotFound(taskId) {
173
+ return new _A2AError(
174
+ -32001,
175
+ `Task not found: ${taskId}`,
176
+ void 0,
177
+ taskId
178
+ );
179
+ }
180
+ static taskNotCancelable(taskId) {
181
+ return new _A2AError(
182
+ -32002,
183
+ `Task not cancelable: ${taskId}`,
184
+ void 0,
185
+ taskId
186
+ );
187
+ }
188
+ static pushNotificationNotSupported() {
189
+ return new _A2AError(
190
+ -32003,
191
+ "Push Notification is not supported"
192
+ );
193
+ }
194
+ static unsupportedOperation(operation) {
195
+ return new _A2AError(
196
+ -32004,
197
+ `Unsupported operation: ${operation}`
198
+ );
199
+ }
200
+ };
201
+
202
+ // src/server/result_manager.ts
203
+ var ResultManager = class {
204
+ taskStore;
205
+ currentTask;
206
+ latestUserMessage;
207
+ // To add to history if a new task is created
208
+ finalMessageResult;
209
+ // Stores the message if it's the final result
210
+ constructor(taskStore) {
211
+ this.taskStore = taskStore;
212
+ }
213
+ setContext(latestUserMessage) {
214
+ this.latestUserMessage = latestUserMessage;
215
+ }
216
+ /**
217
+ * Processes an agent execution event and updates the task store.
218
+ * @param event The agent execution event.
219
+ */
220
+ async processEvent(event) {
221
+ if (event.kind === "message") {
222
+ this.finalMessageResult = event;
223
+ } else if (event.kind === "task") {
224
+ const taskEvent = event;
225
+ this.currentTask = { ...taskEvent };
226
+ if (this.latestUserMessage) {
227
+ if (!this.currentTask.history?.find((msg) => msg.messageId === this.latestUserMessage.messageId)) {
228
+ this.currentTask.history = [this.latestUserMessage, ...this.currentTask.history || []];
229
+ }
230
+ }
231
+ await this.saveCurrentTask();
232
+ } else if (event.kind === "status-update") {
233
+ const updateEvent = event;
234
+ if (this.currentTask && this.currentTask.id === updateEvent.taskId) {
235
+ this.currentTask.status = updateEvent.status;
236
+ if (updateEvent.status.message) {
237
+ if (!this.currentTask.history?.find((msg) => msg.messageId === updateEvent.status.message.messageId)) {
238
+ this.currentTask.history = [...this.currentTask.history || [], updateEvent.status.message];
239
+ }
240
+ }
241
+ await this.saveCurrentTask();
242
+ } else if (!this.currentTask && updateEvent.taskId) {
243
+ const loaded = await this.taskStore.load(updateEvent.taskId);
244
+ if (loaded) {
245
+ this.currentTask = loaded;
246
+ this.currentTask.status = updateEvent.status;
247
+ if (updateEvent.status.message) {
248
+ if (!this.currentTask.history?.find((msg) => msg.messageId === updateEvent.status.message.messageId)) {
249
+ this.currentTask.history = [...this.currentTask.history || [], updateEvent.status.message];
250
+ }
251
+ }
252
+ await this.saveCurrentTask();
253
+ } else {
254
+ console.warn(`ResultManager: Received status update for unknown task ${updateEvent.taskId}`);
255
+ }
256
+ }
257
+ } else if (event.kind === "artifact-update") {
258
+ const artifactEvent = event;
259
+ if (this.currentTask && this.currentTask.id === artifactEvent.taskId) {
260
+ if (!this.currentTask.artifacts) {
261
+ this.currentTask.artifacts = [];
262
+ }
263
+ const existingArtifactIndex = this.currentTask.artifacts.findIndex(
264
+ (art) => art.artifactId === artifactEvent.artifact.artifactId
265
+ );
266
+ if (existingArtifactIndex !== -1) {
267
+ if (artifactEvent.append) {
268
+ const existingArtifact = this.currentTask.artifacts[existingArtifactIndex];
269
+ existingArtifact.parts.push(...artifactEvent.artifact.parts);
270
+ if (artifactEvent.artifact.description) existingArtifact.description = artifactEvent.artifact.description;
271
+ if (artifactEvent.artifact.name) existingArtifact.name = artifactEvent.artifact.name;
272
+ if (artifactEvent.artifact.metadata) existingArtifact.metadata = { ...existingArtifact.metadata, ...artifactEvent.artifact.metadata };
273
+ } else {
274
+ this.currentTask.artifacts[existingArtifactIndex] = artifactEvent.artifact;
275
+ }
276
+ } else {
277
+ this.currentTask.artifacts.push(artifactEvent.artifact);
278
+ }
279
+ await this.saveCurrentTask();
280
+ } else if (!this.currentTask && artifactEvent.taskId) {
281
+ const loaded = await this.taskStore.load(artifactEvent.taskId);
282
+ if (loaded) {
283
+ this.currentTask = loaded;
284
+ if (!this.currentTask.artifacts) this.currentTask.artifacts = [];
285
+ const existingArtifactIndex = this.currentTask.artifacts.findIndex(
286
+ (art) => art.artifactId === artifactEvent.artifact.artifactId
287
+ );
288
+ if (existingArtifactIndex !== -1) {
289
+ if (artifactEvent.append) {
290
+ this.currentTask.artifacts[existingArtifactIndex].parts.push(...artifactEvent.artifact.parts);
291
+ } else {
292
+ this.currentTask.artifacts[existingArtifactIndex] = artifactEvent.artifact;
293
+ }
294
+ } else {
295
+ this.currentTask.artifacts.push(artifactEvent.artifact);
296
+ }
297
+ await this.saveCurrentTask();
298
+ } else {
299
+ console.warn(`ResultManager: Received artifact update for unknown task ${artifactEvent.taskId}`);
300
+ }
301
+ }
302
+ }
303
+ }
304
+ async saveCurrentTask() {
305
+ if (this.currentTask) {
306
+ await this.taskStore.save(this.currentTask);
307
+ }
308
+ }
309
+ /**
310
+ * Gets the final result, which could be a Message or a Task.
311
+ * This should be called after the event stream has been fully processed.
312
+ * @returns The final Message or the current Task.
313
+ */
314
+ getFinalResult() {
315
+ if (this.finalMessageResult) {
316
+ return this.finalMessageResult;
317
+ }
318
+ return this.currentTask;
319
+ }
320
+ /**
321
+ * Gets the task currently being managed by this ResultManager instance.
322
+ * This task could be one that was started with or one created during agent execution.
323
+ * @returns The current Task or undefined if no task is active.
324
+ */
325
+ getCurrentTask() {
326
+ return this.currentTask;
327
+ }
328
+ };
329
+
330
+ // src/server/request_handler/default_request_handler.ts
331
+ var terminalStates = ["completed", "failed", "canceled", "rejected"];
332
+ var DefaultRequestHandler = class {
333
+ agentCard;
334
+ taskStore;
335
+ agentExecutor;
336
+ eventBusManager;
337
+ // Store for push notification configurations (could be part of TaskStore or separate)
338
+ pushNotificationConfigs = /* @__PURE__ */ new Map();
339
+ constructor(agentCard, taskStore, agentExecutor, eventBusManager = new DefaultExecutionEventBusManager()) {
340
+ this.agentCard = agentCard;
341
+ this.taskStore = taskStore;
342
+ this.agentExecutor = agentExecutor;
343
+ this.eventBusManager = eventBusManager;
344
+ }
345
+ async getAgentCard() {
346
+ return this.agentCard;
347
+ }
348
+ async _createRequestContext(incomingMessage, taskId, isStream) {
349
+ let task;
350
+ let referenceTasks;
351
+ if (incomingMessage.taskId) {
352
+ task = await this.taskStore.load(incomingMessage.taskId);
353
+ if (!task) {
354
+ throw A2AError.taskNotFound(incomingMessage.taskId);
355
+ }
356
+ if (terminalStates.includes(task.status.state)) {
357
+ throw A2AError.invalidRequest(`Task ${task.id} is in a terminal state (${task.status.state}) and cannot be modified.`);
358
+ }
359
+ }
360
+ if (incomingMessage.referenceTaskIds && incomingMessage.referenceTaskIds.length > 0) {
361
+ referenceTasks = [];
362
+ for (const refId of incomingMessage.referenceTaskIds) {
363
+ const refTask = await this.taskStore.load(refId);
364
+ if (refTask) {
365
+ referenceTasks.push(refTask);
366
+ } else {
367
+ console.warn(`Reference task ${refId} not found.`);
368
+ }
369
+ }
370
+ }
371
+ const contextId = incomingMessage.contextId || task?.contextId || uuidv4();
372
+ const messageForContext = {
373
+ ...incomingMessage,
374
+ contextId
375
+ };
376
+ return new RequestContext(
377
+ messageForContext,
378
+ taskId,
379
+ contextId,
380
+ task,
381
+ referenceTasks
382
+ );
383
+ }
384
+ async _processEvents(taskId, resultManager, eventQueue, options) {
385
+ let firstResultSent = false;
386
+ try {
387
+ for await (const event of eventQueue.events()) {
388
+ await resultManager.processEvent(event);
389
+ if (options?.firstResultResolver && !firstResultSent) {
390
+ if (event.kind === "message" || event.kind === "task") {
391
+ options.firstResultResolver(event);
392
+ firstResultSent = true;
393
+ }
394
+ }
395
+ }
396
+ if (options?.firstResultRejector && !firstResultSent) {
397
+ options.firstResultRejector(A2AError.internalError("Execution finished before a message or task was produced."));
398
+ }
399
+ } catch (error) {
400
+ console.error(`Event processing loop failed for task ${taskId}:`, error);
401
+ if (options?.firstResultRejector && !firstResultSent) {
402
+ options.firstResultRejector(error);
403
+ }
404
+ throw error;
405
+ } finally {
406
+ this.eventBusManager.cleanupByTaskId(taskId);
407
+ }
408
+ }
409
+ async sendMessage(params) {
410
+ const incomingMessage = params.message;
411
+ if (!incomingMessage.messageId) {
412
+ throw A2AError.invalidParams("message.messageId is required.");
413
+ }
414
+ const isBlocking = params.configuration?.blocking !== false;
415
+ const taskId = incomingMessage.taskId || uuidv4();
416
+ const resultManager = new ResultManager(this.taskStore);
417
+ resultManager.setContext(incomingMessage);
418
+ const requestContext = await this._createRequestContext(incomingMessage, taskId, false);
419
+ const finalMessageForAgent = requestContext.userMessage;
420
+ const eventBus = this.eventBusManager.createOrGetByTaskId(taskId);
421
+ const eventQueue = new ExecutionEventQueue(eventBus);
422
+ this.agentExecutor.execute(requestContext, eventBus).catch((err) => {
423
+ console.error(`Agent execution failed for message ${finalMessageForAgent.messageId}:`, err);
424
+ const errorTask = {
425
+ id: requestContext.task?.id || uuidv4(),
426
+ // Use existing task ID or generate new
427
+ contextId: finalMessageForAgent.contextId,
428
+ status: {
429
+ state: "failed",
430
+ message: {
431
+ kind: "message",
432
+ role: "agent",
433
+ messageId: uuidv4(),
434
+ parts: [{ kind: "text", text: `Agent execution error: ${err.message}` }],
435
+ taskId: requestContext.task?.id,
436
+ contextId: finalMessageForAgent.contextId
437
+ },
438
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
439
+ },
440
+ history: requestContext.task?.history ? [...requestContext.task.history] : [],
441
+ kind: "task"
442
+ };
443
+ if (finalMessageForAgent) {
444
+ if (!errorTask.history?.find((m) => m.messageId === finalMessageForAgent.messageId)) {
445
+ errorTask.history?.push(finalMessageForAgent);
446
+ }
447
+ }
448
+ eventBus.publish(errorTask);
449
+ eventBus.publish({
450
+ // And publish a final status update
451
+ kind: "status-update",
452
+ taskId: errorTask.id,
453
+ contextId: errorTask.contextId,
454
+ status: errorTask.status,
455
+ final: true
456
+ });
457
+ eventBus.finished();
458
+ });
459
+ if (isBlocking) {
460
+ await this._processEvents(taskId, resultManager, eventQueue);
461
+ const finalResult = resultManager.getFinalResult();
462
+ if (!finalResult) {
463
+ throw A2AError.internalError("Agent execution finished without a result, and no task context found.");
464
+ }
465
+ return finalResult;
466
+ } else {
467
+ return new Promise((resolve, reject) => {
468
+ this._processEvents(taskId, resultManager, eventQueue, {
469
+ firstResultResolver: resolve,
470
+ firstResultRejector: reject
471
+ });
472
+ });
473
+ }
474
+ }
475
+ async *sendMessageStream(params) {
476
+ const incomingMessage = params.message;
477
+ if (!incomingMessage.messageId) {
478
+ throw A2AError.invalidParams("message.messageId is required for streaming.");
479
+ }
480
+ const taskId = incomingMessage.taskId || uuidv4();
481
+ const resultManager = new ResultManager(this.taskStore);
482
+ resultManager.setContext(incomingMessage);
483
+ const requestContext = await this._createRequestContext(incomingMessage, taskId, true);
484
+ const finalMessageForAgent = requestContext.userMessage;
485
+ const eventBus = this.eventBusManager.createOrGetByTaskId(taskId);
486
+ const eventQueue = new ExecutionEventQueue(eventBus);
487
+ this.agentExecutor.execute(requestContext, eventBus).catch((err) => {
488
+ console.error(`Agent execution failed for stream message ${finalMessageForAgent.messageId}:`, err);
489
+ const errorTaskStatus = {
490
+ kind: "status-update",
491
+ taskId: requestContext.task?.id || uuidv4(),
492
+ // Use existing or a placeholder
493
+ contextId: finalMessageForAgent.contextId,
494
+ status: {
495
+ state: "failed",
496
+ message: {
497
+ kind: "message",
498
+ role: "agent",
499
+ messageId: uuidv4(),
500
+ parts: [{ kind: "text", text: `Agent execution error: ${err.message}` }],
501
+ taskId: requestContext.task?.id,
502
+ contextId: finalMessageForAgent.contextId
503
+ },
504
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
505
+ },
506
+ final: true
507
+ // This will terminate the stream for the client
508
+ };
509
+ eventBus.publish(errorTaskStatus);
510
+ });
511
+ try {
512
+ for await (const event of eventQueue.events()) {
513
+ await resultManager.processEvent(event);
514
+ yield event;
515
+ }
516
+ } finally {
517
+ this.eventBusManager.cleanupByTaskId(taskId);
518
+ }
519
+ }
520
+ async getTask(params) {
521
+ const task = await this.taskStore.load(params.id);
522
+ if (!task) {
523
+ throw A2AError.taskNotFound(params.id);
524
+ }
525
+ if (params.historyLength !== void 0 && params.historyLength >= 0) {
526
+ if (task.history) {
527
+ task.history = task.history.slice(-params.historyLength);
528
+ }
529
+ } else {
530
+ task.history = [];
531
+ }
532
+ return task;
533
+ }
534
+ async cancelTask(params) {
535
+ const task = await this.taskStore.load(params.id);
536
+ if (!task) {
537
+ throw A2AError.taskNotFound(params.id);
538
+ }
539
+ const nonCancelableStates = ["completed", "failed", "canceled", "rejected"];
540
+ if (nonCancelableStates.includes(task.status.state)) {
541
+ throw A2AError.taskNotCancelable(params.id);
542
+ }
543
+ const eventBus = this.eventBusManager.getByTaskId(params.id);
544
+ if (eventBus) {
545
+ await this.agentExecutor.cancelTask(params.id, eventBus);
546
+ } else {
547
+ task.status = {
548
+ state: "canceled",
549
+ message: {
550
+ // Optional: Add a system message indicating cancellation
551
+ kind: "message",
552
+ role: "agent",
553
+ messageId: uuidv4(),
554
+ parts: [{ kind: "text", text: "Task cancellation requested by user." }],
555
+ taskId: task.id,
556
+ contextId: task.contextId
557
+ },
558
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
559
+ };
560
+ task.history = [...task.history || [], task.status.message];
561
+ await this.taskStore.save(task);
562
+ }
563
+ const latestTask = await this.taskStore.load(params.id);
564
+ return latestTask;
565
+ }
566
+ async setTaskPushNotificationConfig(params) {
567
+ if (!this.agentCard.capabilities.pushNotifications) {
568
+ throw A2AError.pushNotificationNotSupported();
569
+ }
570
+ const taskAndHistory = await this.taskStore.load(params.taskId);
571
+ if (!taskAndHistory) {
572
+ throw A2AError.taskNotFound(params.taskId);
573
+ }
574
+ this.pushNotificationConfigs.set(params.taskId, params.pushNotificationConfig);
575
+ return params;
576
+ }
577
+ async getTaskPushNotificationConfig(params) {
578
+ if (!this.agentCard.capabilities.pushNotifications) {
579
+ throw A2AError.pushNotificationNotSupported();
580
+ }
581
+ const taskAndHistory = await this.taskStore.load(params.id);
582
+ if (!taskAndHistory) {
583
+ throw A2AError.taskNotFound(params.id);
584
+ }
585
+ const config = this.pushNotificationConfigs.get(params.id);
586
+ if (!config) {
587
+ throw A2AError.internalError(`Push notification config not found for task ${params.id}.`);
588
+ }
589
+ return { taskId: params.id, pushNotificationConfig: config };
590
+ }
591
+ async *resubscribe(params) {
592
+ if (!this.agentCard.capabilities.streaming) {
593
+ throw A2AError.unsupportedOperation("Streaming (and thus resubscription) is not supported.");
594
+ }
595
+ const task = await this.taskStore.load(params.id);
596
+ if (!task) {
597
+ throw A2AError.taskNotFound(params.id);
598
+ }
599
+ yield task;
600
+ const finalStates = ["completed", "failed", "canceled", "rejected"];
601
+ if (finalStates.includes(task.status.state)) {
602
+ return;
603
+ }
604
+ const eventBus = this.eventBusManager.getByTaskId(params.id);
605
+ if (!eventBus) {
606
+ console.warn(`Resubscribe: No active event bus for task ${params.id}.`);
607
+ return;
608
+ }
609
+ const eventQueue = new ExecutionEventQueue(eventBus);
610
+ try {
611
+ for await (const event of eventQueue.events()) {
612
+ if (event.kind === "status-update" && event.taskId === params.id) {
613
+ yield event;
614
+ } else if (event.kind === "artifact-update" && event.taskId === params.id) {
615
+ yield event;
616
+ } else if (event.kind === "task" && event.id === params.id) {
617
+ yield event;
618
+ }
619
+ }
620
+ } finally {
621
+ eventQueue.stop();
622
+ }
623
+ }
624
+ };
625
+
626
+ // src/server/store.ts
627
+ var InMemoryTaskStore = class {
628
+ store = /* @__PURE__ */ new Map();
629
+ async load(taskId) {
630
+ const entry = this.store.get(taskId);
631
+ return entry ? { ...entry } : void 0;
632
+ }
633
+ async save(task) {
634
+ this.store.set(task.id, { ...task });
635
+ }
636
+ };
637
+
638
+ // src/server/transports/jsonrpc_transport_handler.ts
639
+ var JsonRpcTransportHandler = class {
640
+ requestHandler;
641
+ constructor(requestHandler) {
642
+ this.requestHandler = requestHandler;
643
+ }
644
+ /**
645
+ * Handles an incoming JSON-RPC request.
646
+ * For streaming methods, it returns an AsyncGenerator of JSONRPCResult.
647
+ * For non-streaming methods, it returns a Promise of a single JSONRPCMessage (Result or ErrorResponse).
648
+ */
649
+ async handle(requestBody) {
650
+ let rpcRequest;
651
+ try {
652
+ if (typeof requestBody === "string") {
653
+ rpcRequest = JSON.parse(requestBody);
654
+ } else if (typeof requestBody === "object" && requestBody !== null) {
655
+ rpcRequest = requestBody;
656
+ } else {
657
+ throw A2AError.parseError("Invalid request body type.");
658
+ }
659
+ if (rpcRequest.jsonrpc !== "2.0" || !rpcRequest.method || typeof rpcRequest.method !== "string") {
660
+ throw A2AError.invalidRequest(
661
+ "Invalid JSON-RPC request structure."
662
+ );
663
+ }
664
+ } catch (error) {
665
+ const a2aError = error instanceof A2AError ? error : A2AError.parseError(error.message || "Failed to parse JSON request.");
666
+ return {
667
+ jsonrpc: "2.0",
668
+ id: typeof rpcRequest?.id !== "undefined" ? rpcRequest.id : null,
669
+ error: a2aError.toJSONRPCError()
670
+ };
671
+ }
672
+ const { method, params = {}, id: requestId = null } = rpcRequest;
673
+ try {
674
+ if (method === "message/stream" || method === "tasks/resubscribe") {
675
+ const agentCard = await this.requestHandler.getAgentCard();
676
+ if (!agentCard.capabilities.streaming) {
677
+ throw A2AError.unsupportedOperation(`Method ${method} requires streaming capability.`);
678
+ }
679
+ const agentEventStream = method === "message/stream" ? this.requestHandler.sendMessageStream(params) : this.requestHandler.resubscribe(params);
680
+ return async function* jsonRpcEventStream() {
681
+ try {
682
+ for await (const event of agentEventStream) {
683
+ yield {
684
+ jsonrpc: "2.0",
685
+ id: requestId,
686
+ // Use the original request ID for all streamed responses
687
+ result: event
688
+ };
689
+ }
690
+ } catch (streamError) {
691
+ console.error(`Error in agent event stream for ${method} (request ${requestId}):`, streamError);
692
+ throw streamError;
693
+ }
694
+ }();
695
+ } else {
696
+ let result;
697
+ switch (method) {
698
+ case "message/send":
699
+ result = await this.requestHandler.sendMessage(params);
700
+ break;
701
+ case "tasks/get":
702
+ result = await this.requestHandler.getTask(params);
703
+ break;
704
+ case "tasks/cancel":
705
+ result = await this.requestHandler.cancelTask(params);
706
+ break;
707
+ case "tasks/pushNotificationConfig/set":
708
+ result = await this.requestHandler.setTaskPushNotificationConfig(
709
+ params
710
+ );
711
+ break;
712
+ case "tasks/pushNotificationConfig/get":
713
+ result = await this.requestHandler.getTaskPushNotificationConfig(
714
+ params
715
+ );
716
+ break;
717
+ default:
718
+ throw A2AError.methodNotFound(method);
719
+ }
720
+ return {
721
+ jsonrpc: "2.0",
722
+ id: requestId,
723
+ result
724
+ };
725
+ }
726
+ } catch (error) {
727
+ const a2aError = error instanceof A2AError ? error : A2AError.internalError(error.message || "An unexpected error occurred.");
728
+ return {
729
+ jsonrpc: "2.0",
730
+ id: requestId,
731
+ error: a2aError.toJSONRPCError()
732
+ };
733
+ }
734
+ }
735
+ };
736
+
737
+ // src/server/a2a_express_app.ts
738
+ import express from "express";
739
+ var A2AExpressApp = class {
740
+ requestHandler;
741
+ // Kept for getAgentCard
742
+ jsonRpcTransportHandler;
743
+ constructor(requestHandler) {
744
+ this.requestHandler = requestHandler;
745
+ this.jsonRpcTransportHandler = new JsonRpcTransportHandler(requestHandler);
746
+ }
747
+ /**
748
+ * Adds A2A routes to an existing Express app.
749
+ * @param app Optional existing Express app.
750
+ * @param baseUrl The base URL for A2A endpoints (e.g., "/a2a/api").
751
+ * @param middlewares Optional array of Express middlewares to apply to the A2A routes.
752
+ * @returns The Express app with A2A routes.
753
+ */
754
+ setupRoutes(app, baseUrl = "", middlewares) {
755
+ const router = express.Router();
756
+ router.use(express.json(), ...middlewares ?? []);
757
+ router.get("/.well-known/agent.json", async (req, res) => {
758
+ try {
759
+ const agentCard = await this.requestHandler.getAgentCard();
760
+ res.json(agentCard);
761
+ } catch (error) {
762
+ console.error("Error fetching agent card:", error);
763
+ res.status(500).json({ error: "Failed to retrieve agent card" });
764
+ }
765
+ });
766
+ router.post("/", async (req, res) => {
767
+ try {
768
+ const rpcResponseOrStream = await this.jsonRpcTransportHandler.handle(req.body);
769
+ if (typeof rpcResponseOrStream?.[Symbol.asyncIterator] === "function") {
770
+ const stream = rpcResponseOrStream;
771
+ res.setHeader("Content-Type", "text/event-stream");
772
+ res.setHeader("Cache-Control", "no-cache");
773
+ res.setHeader("Connection", "keep-alive");
774
+ res.flushHeaders();
775
+ try {
776
+ for await (const event of stream) {
777
+ res.write(`id: ${(/* @__PURE__ */ new Date()).getTime()}
778
+ `);
779
+ res.write(`data: ${JSON.stringify(event)}
780
+
781
+ `);
782
+ }
783
+ } catch (streamError) {
784
+ console.error(`Error during SSE streaming (request ${req.body?.id}):`, streamError);
785
+ const a2aError = streamError instanceof A2AError ? streamError : A2AError.internalError(streamError.message || "Streaming error.");
786
+ const errorResponse = {
787
+ jsonrpc: "2.0",
788
+ id: req.body?.id || null,
789
+ // Use original request ID if available
790
+ error: a2aError.toJSONRPCError()
791
+ };
792
+ if (!res.headersSent) {
793
+ res.status(500).json(errorResponse);
794
+ } else {
795
+ res.write(`id: ${(/* @__PURE__ */ new Date()).getTime()}
796
+ `);
797
+ res.write(`event: error
798
+ `);
799
+ res.write(`data: ${JSON.stringify(errorResponse)}
800
+
801
+ `);
802
+ }
803
+ } finally {
804
+ if (!res.writableEnded) {
805
+ res.end();
806
+ }
807
+ }
808
+ } else {
809
+ const rpcResponse = rpcResponseOrStream;
810
+ res.status(200).json(rpcResponse);
811
+ }
812
+ } catch (error) {
813
+ console.error("Unhandled error in A2AExpressApp POST handler:", error);
814
+ const a2aError = error instanceof A2AError ? error : A2AError.internalError("General processing error.");
815
+ const errorResponse = {
816
+ jsonrpc: "2.0",
817
+ id: req.body?.id || null,
818
+ error: a2aError.toJSONRPCError()
819
+ };
820
+ if (!res.headersSent) {
821
+ res.status(500).json(errorResponse);
822
+ } else if (!res.writableEnded) {
823
+ res.end();
824
+ }
825
+ }
826
+ });
827
+ app.use(baseUrl, router);
828
+ return app;
829
+ }
830
+ };
831
+ export {
832
+ A2AError,
833
+ A2AExpressApp,
834
+ DefaultExecutionEventBus,
835
+ DefaultExecutionEventBusManager,
836
+ DefaultRequestHandler,
837
+ ExecutionEventQueue,
838
+ InMemoryTaskStore,
839
+ JsonRpcTransportHandler,
840
+ RequestContext,
841
+ ResultManager
842
+ };