@mailmodo/a2a 0.3.5 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +171 -93
- package/dist/{a2a_request_handler-DQfg1Q-R.d.ts → a2a_request_handler-1Isk3l-0.d.mts} +16 -4
- package/dist/{a2a_request_handler-DPkhsCMt.d.mts → a2a_request_handler-BuP9LgXH.d.ts} +16 -4
- package/dist/chunk-7JFJW6P6.mjs +38 -0
- package/dist/{chunk-LVD4GF26.mjs → chunk-AZGEDUZX.mjs} +6 -7
- package/dist/chunk-BNBEZNW7.mjs +122 -0
- package/dist/client/index.d.mts +401 -47
- package/dist/client/index.d.ts +401 -47
- package/dist/client/index.js +525 -78
- package/dist/client/index.mjs +1110 -29
- package/dist/{types-Due_Cv6t.d.mts → extensions-DvruCIzw.d.mts} +40 -1
- package/dist/{types-Due_Cv6t.d.ts → extensions-DvruCIzw.d.ts} +40 -1
- package/dist/index.d.mts +5 -33
- package/dist/index.d.ts +5 -33
- package/dist/index.js +27 -2083
- package/dist/index.mjs +6 -42
- package/dist/server/express/index.d.mts +76 -4
- package/dist/server/express/index.d.ts +76 -4
- package/dist/server/express/index.js +563 -37
- package/dist/server/express/index.mjs +642 -6
- package/dist/server/index.d.mts +296 -6
- package/dist/server/index.d.ts +296 -6
- package/dist/server/index.js +265 -52
- package/dist/server/index.mjs +1050 -12
- package/package.json +39 -17
- package/dist/auth-handler-DVLcl8yj.d.mts +0 -209
- package/dist/auth-handler-Gzpf3xHC.d.ts +0 -209
- package/dist/chunk-HZFUOBJQ.mjs +0 -198
- package/dist/chunk-LIEYEFQG.mjs +0 -879
- package/dist/chunk-UBRSFN2J.mjs +0 -776
- package/dist/error-DExKs0Q3.d.mts +0 -233
- package/dist/error-j1vYKII2.d.ts +0 -233
package/dist/index.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
var __create = Object.create;
|
|
2
1
|
var __defProp = Object.defineProperty;
|
|
3
2
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
6
4
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
5
|
var __export = (target, all) => {
|
|
8
6
|
for (var name in all)
|
|
@@ -16,37 +14,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
14
|
}
|
|
17
15
|
return to;
|
|
18
16
|
};
|
|
19
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
-
mod
|
|
26
|
-
));
|
|
27
17
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
18
|
|
|
29
19
|
// src/index.ts
|
|
30
20
|
var index_exports = {};
|
|
31
21
|
__export(index_exports, {
|
|
32
|
-
A2AClient: () => A2AClient,
|
|
33
|
-
A2AError: () => A2AError,
|
|
34
|
-
A2AExpressApp: () => A2AExpressApp,
|
|
35
22
|
AGENT_CARD_PATH: () => AGENT_CARD_PATH,
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
DefaultRequestHandler: () => DefaultRequestHandler,
|
|
39
|
-
ExecutionEventQueue: () => ExecutionEventQueue,
|
|
40
|
-
InMemoryTaskStore: () => InMemoryTaskStore,
|
|
41
|
-
JsonRpcTransportHandler: () => JsonRpcTransportHandler,
|
|
42
|
-
RequestContext: () => RequestContext,
|
|
43
|
-
ResultManager: () => ResultManager,
|
|
44
|
-
createAuthenticatingFetchWithRetry: () => createAuthenticatingFetchWithRetry,
|
|
45
|
-
getCurrentTimestamp: () => getCurrentTimestamp,
|
|
46
|
-
getRequestedExtensions: () => getRequestedExtensions,
|
|
47
|
-
isArtifactUpdate: () => isArtifactUpdate,
|
|
48
|
-
isObject: () => isObject,
|
|
49
|
-
isTaskStatusUpdate: () => isTaskStatusUpdate
|
|
23
|
+
Extensions: () => Extensions,
|
|
24
|
+
HTTP_EXTENSION_HEADER: () => HTTP_EXTENSION_HEADER
|
|
50
25
|
});
|
|
51
26
|
module.exports = __toCommonJS(index_exports);
|
|
52
27
|
|
|
@@ -54,2074 +29,43 @@ module.exports = __toCommonJS(index_exports);
|
|
|
54
29
|
var AGENT_CARD_PATH = ".well-known/agent-card.json";
|
|
55
30
|
var HTTP_EXTENSION_HEADER = "X-A2A-Extensions";
|
|
56
31
|
|
|
57
|
-
// src/
|
|
58
|
-
var
|
|
59
|
-
userMessage;
|
|
60
|
-
taskId;
|
|
61
|
-
contextId;
|
|
62
|
-
task;
|
|
63
|
-
referenceTasks;
|
|
64
|
-
context;
|
|
65
|
-
constructor(userMessage, taskId, contextId, task, referenceTasks, context) {
|
|
66
|
-
this.userMessage = userMessage;
|
|
67
|
-
this.taskId = taskId;
|
|
68
|
-
this.contextId = contextId;
|
|
69
|
-
this.task = task;
|
|
70
|
-
this.referenceTasks = referenceTasks;
|
|
71
|
-
this.context = context;
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
// src/server/events/execution_event_bus.ts
|
|
76
|
-
var import_events = require("events");
|
|
77
|
-
var DefaultExecutionEventBus = class extends import_events.EventEmitter {
|
|
78
|
-
constructor() {
|
|
79
|
-
super();
|
|
80
|
-
}
|
|
81
|
-
publish(event) {
|
|
82
|
-
this.emit("event", event);
|
|
83
|
-
}
|
|
84
|
-
finished() {
|
|
85
|
-
this.emit("finished");
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
// src/server/events/execution_event_bus_manager.ts
|
|
90
|
-
var DefaultExecutionEventBusManager = class {
|
|
91
|
-
taskIdToBus = /* @__PURE__ */ new Map();
|
|
92
|
-
/**
|
|
93
|
-
* Creates or retrieves an existing ExecutionEventBus based on the taskId.
|
|
94
|
-
* @param taskId The ID of the task.
|
|
95
|
-
* @returns An instance of IExecutionEventBus.
|
|
96
|
-
*/
|
|
97
|
-
createOrGetByTaskId(taskId) {
|
|
98
|
-
if (!this.taskIdToBus.has(taskId)) {
|
|
99
|
-
this.taskIdToBus.set(taskId, new DefaultExecutionEventBus());
|
|
100
|
-
}
|
|
101
|
-
return this.taskIdToBus.get(taskId);
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Retrieves an existing ExecutionEventBus based on the taskId.
|
|
105
|
-
* @param taskId The ID of the task.
|
|
106
|
-
* @returns An instance of IExecutionEventBus or undefined if not found.
|
|
107
|
-
*/
|
|
108
|
-
getByTaskId(taskId) {
|
|
109
|
-
return this.taskIdToBus.get(taskId);
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Removes the event bus for a given taskId.
|
|
113
|
-
* This should be called when an execution flow is complete to free resources.
|
|
114
|
-
* @param taskId The ID of the task.
|
|
115
|
-
*/
|
|
116
|
-
cleanupByTaskId(taskId) {
|
|
117
|
-
const bus = this.taskIdToBus.get(taskId);
|
|
118
|
-
if (bus) {
|
|
119
|
-
bus.removeAllListeners();
|
|
120
|
-
}
|
|
121
|
-
this.taskIdToBus.delete(taskId);
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
// src/server/events/execution_event_queue.ts
|
|
126
|
-
var ExecutionEventQueue = class {
|
|
127
|
-
eventBus;
|
|
128
|
-
eventQueue = [];
|
|
129
|
-
resolvePromise;
|
|
130
|
-
stopped = false;
|
|
131
|
-
boundHandleEvent;
|
|
132
|
-
constructor(eventBus) {
|
|
133
|
-
this.eventBus = eventBus;
|
|
134
|
-
this.eventBus.on("event", this.handleEvent);
|
|
135
|
-
this.eventBus.on("finished", this.handleFinished);
|
|
136
|
-
}
|
|
137
|
-
handleEvent = (event) => {
|
|
138
|
-
if (this.stopped) return;
|
|
139
|
-
this.eventQueue.push(event);
|
|
140
|
-
if (this.resolvePromise) {
|
|
141
|
-
this.resolvePromise();
|
|
142
|
-
this.resolvePromise = void 0;
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
handleFinished = () => {
|
|
146
|
-
this.stop();
|
|
147
|
-
};
|
|
148
|
-
/**
|
|
149
|
-
* Provides an async generator that yields events from the event bus.
|
|
150
|
-
* Stops when a Message event is received or a TaskStatusUpdateEvent with final=true is received.
|
|
151
|
-
*/
|
|
152
|
-
async *events() {
|
|
153
|
-
while (!this.stopped || this.eventQueue.length > 0) {
|
|
154
|
-
if (this.eventQueue.length > 0) {
|
|
155
|
-
const event = this.eventQueue.shift();
|
|
156
|
-
yield event;
|
|
157
|
-
if (event.kind === "message" || event.kind === "status-update" && event.final) {
|
|
158
|
-
this.handleFinished();
|
|
159
|
-
break;
|
|
160
|
-
}
|
|
161
|
-
} else if (!this.stopped) {
|
|
162
|
-
await new Promise((resolve) => {
|
|
163
|
-
this.resolvePromise = resolve;
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* Stops the event queue from processing further events.
|
|
170
|
-
*/
|
|
171
|
-
stop() {
|
|
172
|
-
this.stopped = true;
|
|
173
|
-
if (this.resolvePromise) {
|
|
174
|
-
this.resolvePromise();
|
|
175
|
-
this.resolvePromise = void 0;
|
|
176
|
-
}
|
|
177
|
-
this.eventBus.off("event", this.handleEvent);
|
|
178
|
-
this.eventBus.off("finished", this.handleFinished);
|
|
179
|
-
}
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
// src/server/utils.ts
|
|
183
|
-
function getCurrentTimestamp() {
|
|
184
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
185
|
-
}
|
|
186
|
-
function isObject(value) {
|
|
187
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
188
|
-
}
|
|
189
|
-
function isTaskStatusUpdate(update) {
|
|
190
|
-
return isObject(update) && "state" in update && !("parts" in update);
|
|
191
|
-
}
|
|
192
|
-
function isArtifactUpdate(update) {
|
|
193
|
-
return isObject(update) && "parts" in update;
|
|
194
|
-
}
|
|
195
|
-
function getRequestedExtensions(values) {
|
|
196
|
-
if (!values) {
|
|
197
|
-
return /* @__PURE__ */ new Set();
|
|
198
|
-
}
|
|
199
|
-
return new Set(
|
|
200
|
-
values.split(",").map((ext) => ext.trim()).filter((ext) => ext.length > 0)
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// src/server/request_handler/default_request_handler.ts
|
|
205
|
-
var import_uuid = require("uuid");
|
|
206
|
-
|
|
207
|
-
// src/server/error.ts
|
|
208
|
-
var A2AError = class _A2AError extends Error {
|
|
209
|
-
code;
|
|
210
|
-
data;
|
|
211
|
-
taskId;
|
|
212
|
-
// Optional task ID context
|
|
213
|
-
constructor(code, message, data, taskId) {
|
|
214
|
-
super(message);
|
|
215
|
-
this.name = "A2AError";
|
|
216
|
-
this.code = code;
|
|
217
|
-
this.data = data;
|
|
218
|
-
this.taskId = taskId;
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* Formats the error into a standard JSON-RPC error object structure.
|
|
222
|
-
*/
|
|
223
|
-
toJSONRPCError() {
|
|
224
|
-
const errorObject = {
|
|
225
|
-
code: this.code,
|
|
226
|
-
message: this.message
|
|
227
|
-
};
|
|
228
|
-
if (this.data !== void 0) {
|
|
229
|
-
errorObject.data = this.data;
|
|
230
|
-
}
|
|
231
|
-
return errorObject;
|
|
232
|
-
}
|
|
233
|
-
// Static factory methods for common errors
|
|
234
|
-
static parseError(message, data) {
|
|
235
|
-
return new _A2AError(-32700, message, data);
|
|
236
|
-
}
|
|
237
|
-
static invalidRequest(message, data) {
|
|
238
|
-
return new _A2AError(-32600, message, data);
|
|
239
|
-
}
|
|
240
|
-
static methodNotFound(method) {
|
|
241
|
-
return new _A2AError(-32601, `Method not found: ${method}`);
|
|
242
|
-
}
|
|
243
|
-
static invalidParams(message, data) {
|
|
244
|
-
return new _A2AError(-32602, message, data);
|
|
245
|
-
}
|
|
246
|
-
static internalError(message, data) {
|
|
247
|
-
return new _A2AError(-32603, message, data);
|
|
248
|
-
}
|
|
249
|
-
static taskNotFound(taskId) {
|
|
250
|
-
return new _A2AError(-32001, `Task not found: ${taskId}`, void 0, taskId);
|
|
251
|
-
}
|
|
252
|
-
static taskNotCancelable(taskId) {
|
|
253
|
-
return new _A2AError(-32002, `Task not cancelable: ${taskId}`, void 0, taskId);
|
|
254
|
-
}
|
|
255
|
-
static pushNotificationNotSupported() {
|
|
256
|
-
return new _A2AError(-32003, "Push Notification is not supported");
|
|
257
|
-
}
|
|
258
|
-
static unsupportedOperation(operation) {
|
|
259
|
-
return new _A2AError(-32004, `Unsupported operation: ${operation}`);
|
|
260
|
-
}
|
|
261
|
-
static authenticatedExtendedCardNotConfigured() {
|
|
262
|
-
return new _A2AError(-32007, `Extended card not configured.`);
|
|
263
|
-
}
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
// src/server/result_manager.ts
|
|
267
|
-
var ResultManager = class {
|
|
268
|
-
taskStore;
|
|
269
|
-
currentTask;
|
|
270
|
-
latestUserMessage;
|
|
271
|
-
// To add to history if a new task is created
|
|
272
|
-
finalMessageResult;
|
|
273
|
-
// Stores the message if it's the final result
|
|
274
|
-
constructor(taskStore) {
|
|
275
|
-
this.taskStore = taskStore;
|
|
276
|
-
}
|
|
277
|
-
setContext(latestUserMessage) {
|
|
278
|
-
this.latestUserMessage = latestUserMessage;
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Processes an agent execution event and updates the task store.
|
|
282
|
-
* @param event The agent execution event.
|
|
283
|
-
*/
|
|
284
|
-
async processEvent(event) {
|
|
285
|
-
if (event.kind === "message") {
|
|
286
|
-
this.finalMessageResult = event;
|
|
287
|
-
} else if (event.kind === "task") {
|
|
288
|
-
const taskEvent = event;
|
|
289
|
-
this.currentTask = { ...taskEvent };
|
|
290
|
-
if (this.latestUserMessage) {
|
|
291
|
-
if (!this.currentTask.history?.find(
|
|
292
|
-
(msg) => msg.messageId === this.latestUserMessage.messageId
|
|
293
|
-
)) {
|
|
294
|
-
this.currentTask.history = [this.latestUserMessage, ...this.currentTask.history || []];
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
await this.saveCurrentTask();
|
|
298
|
-
} else if (event.kind === "status-update") {
|
|
299
|
-
const updateEvent = event;
|
|
300
|
-
if (this.currentTask && this.currentTask.id === updateEvent.taskId) {
|
|
301
|
-
this.currentTask.status = updateEvent.status;
|
|
302
|
-
if (updateEvent.status.message) {
|
|
303
|
-
if (!this.currentTask.history?.find(
|
|
304
|
-
(msg) => msg.messageId === updateEvent.status.message.messageId
|
|
305
|
-
)) {
|
|
306
|
-
this.currentTask.history = [
|
|
307
|
-
...this.currentTask.history || [],
|
|
308
|
-
updateEvent.status.message
|
|
309
|
-
];
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
await this.saveCurrentTask();
|
|
313
|
-
} else if (!this.currentTask && updateEvent.taskId) {
|
|
314
|
-
const loaded = await this.taskStore.load(updateEvent.taskId);
|
|
315
|
-
if (loaded) {
|
|
316
|
-
this.currentTask = loaded;
|
|
317
|
-
this.currentTask.status = updateEvent.status;
|
|
318
|
-
if (updateEvent.status.message) {
|
|
319
|
-
if (!this.currentTask.history?.find(
|
|
320
|
-
(msg) => msg.messageId === updateEvent.status.message.messageId
|
|
321
|
-
)) {
|
|
322
|
-
this.currentTask.history = [
|
|
323
|
-
...this.currentTask.history || [],
|
|
324
|
-
updateEvent.status.message
|
|
325
|
-
];
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
await this.saveCurrentTask();
|
|
329
|
-
} else {
|
|
330
|
-
console.warn(
|
|
331
|
-
`ResultManager: Received status update for unknown task ${updateEvent.taskId}`
|
|
332
|
-
);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
} else if (event.kind === "artifact-update") {
|
|
336
|
-
const artifactEvent = event;
|
|
337
|
-
if (this.currentTask && this.currentTask.id === artifactEvent.taskId) {
|
|
338
|
-
if (!this.currentTask.artifacts) {
|
|
339
|
-
this.currentTask.artifacts = [];
|
|
340
|
-
}
|
|
341
|
-
const existingArtifactIndex = this.currentTask.artifacts.findIndex(
|
|
342
|
-
(art) => art.artifactId === artifactEvent.artifact.artifactId
|
|
343
|
-
);
|
|
344
|
-
if (existingArtifactIndex !== -1) {
|
|
345
|
-
if (artifactEvent.append) {
|
|
346
|
-
const existingArtifact = this.currentTask.artifacts[existingArtifactIndex];
|
|
347
|
-
existingArtifact.parts.push(...artifactEvent.artifact.parts);
|
|
348
|
-
if (artifactEvent.artifact.description)
|
|
349
|
-
existingArtifact.description = artifactEvent.artifact.description;
|
|
350
|
-
if (artifactEvent.artifact.name) existingArtifact.name = artifactEvent.artifact.name;
|
|
351
|
-
if (artifactEvent.artifact.metadata)
|
|
352
|
-
existingArtifact.metadata = {
|
|
353
|
-
...existingArtifact.metadata,
|
|
354
|
-
...artifactEvent.artifact.metadata
|
|
355
|
-
};
|
|
356
|
-
} else {
|
|
357
|
-
this.currentTask.artifacts[existingArtifactIndex] = artifactEvent.artifact;
|
|
358
|
-
}
|
|
359
|
-
} else {
|
|
360
|
-
this.currentTask.artifacts.push(artifactEvent.artifact);
|
|
361
|
-
}
|
|
362
|
-
await this.saveCurrentTask();
|
|
363
|
-
} else if (!this.currentTask && artifactEvent.taskId) {
|
|
364
|
-
const loaded = await this.taskStore.load(artifactEvent.taskId);
|
|
365
|
-
if (loaded) {
|
|
366
|
-
this.currentTask = loaded;
|
|
367
|
-
if (!this.currentTask.artifacts) this.currentTask.artifacts = [];
|
|
368
|
-
const existingArtifactIndex = this.currentTask.artifacts.findIndex(
|
|
369
|
-
(art) => art.artifactId === artifactEvent.artifact.artifactId
|
|
370
|
-
);
|
|
371
|
-
if (existingArtifactIndex !== -1) {
|
|
372
|
-
if (artifactEvent.append) {
|
|
373
|
-
this.currentTask.artifacts[existingArtifactIndex].parts.push(
|
|
374
|
-
...artifactEvent.artifact.parts
|
|
375
|
-
);
|
|
376
|
-
} else {
|
|
377
|
-
this.currentTask.artifacts[existingArtifactIndex] = artifactEvent.artifact;
|
|
378
|
-
}
|
|
379
|
-
} else {
|
|
380
|
-
this.currentTask.artifacts.push(artifactEvent.artifact);
|
|
381
|
-
}
|
|
382
|
-
await this.saveCurrentTask();
|
|
383
|
-
} else {
|
|
384
|
-
console.warn(
|
|
385
|
-
`ResultManager: Received artifact update for unknown task ${artifactEvent.taskId}`
|
|
386
|
-
);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
async saveCurrentTask() {
|
|
392
|
-
if (this.currentTask) {
|
|
393
|
-
await this.taskStore.save(this.currentTask);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
/**
|
|
397
|
-
* Gets the final result, which could be a Message or a Task.
|
|
398
|
-
* This should be called after the event stream has been fully processed.
|
|
399
|
-
* @returns The final Message or the current Task.
|
|
400
|
-
*/
|
|
401
|
-
getFinalResult() {
|
|
402
|
-
if (this.finalMessageResult) {
|
|
403
|
-
return this.finalMessageResult;
|
|
404
|
-
}
|
|
405
|
-
return this.currentTask;
|
|
406
|
-
}
|
|
407
|
-
/**
|
|
408
|
-
* Gets the task currently being managed by this ResultManager instance.
|
|
409
|
-
* This task could be one that was started with or one created during agent execution.
|
|
410
|
-
* @returns The current Task or undefined if no task is active.
|
|
411
|
-
*/
|
|
412
|
-
getCurrentTask() {
|
|
413
|
-
return this.currentTask;
|
|
414
|
-
}
|
|
415
|
-
};
|
|
416
|
-
|
|
417
|
-
// src/server/push_notification/push_notification_store.ts
|
|
418
|
-
var InMemoryPushNotificationStore = class {
|
|
419
|
-
store = /* @__PURE__ */ new Map();
|
|
420
|
-
async save(taskId, pushNotificationConfig) {
|
|
421
|
-
const configs = this.store.get(taskId) || [];
|
|
422
|
-
if (!pushNotificationConfig.id) {
|
|
423
|
-
pushNotificationConfig.id = taskId;
|
|
424
|
-
}
|
|
425
|
-
const existingIndex = configs.findIndex((config) => config.id === pushNotificationConfig.id);
|
|
426
|
-
if (existingIndex !== -1) {
|
|
427
|
-
configs.splice(existingIndex, 1);
|
|
428
|
-
}
|
|
429
|
-
configs.push(pushNotificationConfig);
|
|
430
|
-
this.store.set(taskId, configs);
|
|
431
|
-
}
|
|
432
|
-
async load(taskId) {
|
|
433
|
-
const configs = this.store.get(taskId);
|
|
434
|
-
return configs || [];
|
|
435
|
-
}
|
|
436
|
-
async delete(taskId, configId) {
|
|
437
|
-
if (configId === void 0) {
|
|
438
|
-
configId = taskId;
|
|
439
|
-
}
|
|
440
|
-
const configs = this.store.get(taskId);
|
|
441
|
-
if (!configs) {
|
|
442
|
-
return;
|
|
443
|
-
}
|
|
444
|
-
const configIndex = configs.findIndex((config) => config.id === configId);
|
|
445
|
-
if (configIndex !== -1) {
|
|
446
|
-
configs.splice(configIndex, 1);
|
|
447
|
-
}
|
|
448
|
-
if (configs.length === 0) {
|
|
449
|
-
this.store.delete(taskId);
|
|
450
|
-
} else {
|
|
451
|
-
this.store.set(taskId, configs);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
};
|
|
455
|
-
|
|
456
|
-
// src/server/push_notification/default_push_notification_sender.ts
|
|
457
|
-
var DefaultPushNotificationSender = class {
|
|
458
|
-
pushNotificationStore;
|
|
459
|
-
notificationChain;
|
|
460
|
-
options;
|
|
461
|
-
constructor(pushNotificationStore, options = {}) {
|
|
462
|
-
this.pushNotificationStore = pushNotificationStore;
|
|
463
|
-
this.notificationChain = /* @__PURE__ */ new Map();
|
|
464
|
-
this.options = {
|
|
465
|
-
timeout: 5e3,
|
|
466
|
-
tokenHeaderName: "X-A2A-Notification-Token",
|
|
467
|
-
...options
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
async send(task) {
|
|
471
|
-
const pushConfigs = await this.pushNotificationStore.load(task.id);
|
|
472
|
-
if (!pushConfigs || pushConfigs.length === 0) {
|
|
473
|
-
return;
|
|
474
|
-
}
|
|
475
|
-
const lastPromise = this.notificationChain.get(task.id) ?? Promise.resolve();
|
|
476
|
-
const newPromise = lastPromise.then(async () => {
|
|
477
|
-
const dispatches = pushConfigs.map(async (pushConfig) => {
|
|
478
|
-
try {
|
|
479
|
-
await this._dispatchNotification(task, pushConfig);
|
|
480
|
-
} catch (error) {
|
|
481
|
-
console.error(
|
|
482
|
-
`Error sending push notification for task_id=${task.id} to URL: ${pushConfig.url}. Error:`,
|
|
483
|
-
error
|
|
484
|
-
);
|
|
485
|
-
}
|
|
486
|
-
});
|
|
487
|
-
await Promise.all(dispatches);
|
|
488
|
-
});
|
|
489
|
-
this.notificationChain.set(task.id, newPromise);
|
|
490
|
-
newPromise.finally(() => {
|
|
491
|
-
if (this.notificationChain.get(task.id) === newPromise) {
|
|
492
|
-
this.notificationChain.delete(task.id);
|
|
493
|
-
}
|
|
494
|
-
});
|
|
495
|
-
}
|
|
496
|
-
async _dispatchNotification(task, pushConfig) {
|
|
497
|
-
const url = pushConfig.url;
|
|
498
|
-
const controller = new AbortController();
|
|
499
|
-
const timeoutId = setTimeout(() => controller.abort(), this.options.timeout);
|
|
500
|
-
try {
|
|
501
|
-
const headers = {
|
|
502
|
-
"Content-Type": "application/json"
|
|
503
|
-
};
|
|
504
|
-
if (pushConfig.token) {
|
|
505
|
-
headers[this.options.tokenHeaderName] = pushConfig.token;
|
|
506
|
-
}
|
|
507
|
-
const response = await fetch(url, {
|
|
508
|
-
method: "POST",
|
|
509
|
-
headers,
|
|
510
|
-
body: JSON.stringify(task),
|
|
511
|
-
signal: controller.signal
|
|
512
|
-
});
|
|
513
|
-
if (!response.ok) {
|
|
514
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
515
|
-
}
|
|
516
|
-
console.info(`Push notification sent for task_id=${task.id} to URL: ${url}`);
|
|
517
|
-
} finally {
|
|
518
|
-
clearTimeout(timeoutId);
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
};
|
|
522
|
-
|
|
523
|
-
// src/server/context.ts
|
|
524
|
-
var ServerCallContext = class {
|
|
525
|
-
_requestedExtensions;
|
|
526
|
-
_user;
|
|
527
|
-
_activatedExtensions;
|
|
528
|
-
constructor(requestedExtensions, user) {
|
|
529
|
-
this._requestedExtensions = requestedExtensions;
|
|
530
|
-
this._user = user;
|
|
531
|
-
}
|
|
532
|
-
get user() {
|
|
533
|
-
return this._user;
|
|
534
|
-
}
|
|
535
|
-
get activatedExtensions() {
|
|
536
|
-
return this._activatedExtensions;
|
|
537
|
-
}
|
|
538
|
-
get requestedExtensions() {
|
|
539
|
-
return this._requestedExtensions;
|
|
540
|
-
}
|
|
541
|
-
addActivatedExtension(uri) {
|
|
542
|
-
if (this._requestedExtensions?.has(uri)) {
|
|
543
|
-
if (!this._activatedExtensions) {
|
|
544
|
-
this._activatedExtensions = /* @__PURE__ */ new Set();
|
|
545
|
-
}
|
|
546
|
-
this._activatedExtensions.add(uri);
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
};
|
|
550
|
-
|
|
551
|
-
// src/server/request_handler/default_request_handler.ts
|
|
552
|
-
var terminalStates = ["completed", "failed", "canceled", "rejected"];
|
|
553
|
-
var DefaultRequestHandler = class {
|
|
554
|
-
agentCard;
|
|
555
|
-
taskStore;
|
|
556
|
-
agentExecutor;
|
|
557
|
-
eventBusManager;
|
|
558
|
-
pushNotificationStore;
|
|
559
|
-
pushNotificationSender;
|
|
560
|
-
extendedAgentCardProvider;
|
|
561
|
-
constructor(agentCard, taskStore, agentExecutor, eventBusManager = new DefaultExecutionEventBusManager(), pushNotificationStore, pushNotificationSender, extendedAgentCardProvider) {
|
|
562
|
-
this.agentCard = agentCard;
|
|
563
|
-
this.taskStore = taskStore;
|
|
564
|
-
this.agentExecutor = agentExecutor;
|
|
565
|
-
this.eventBusManager = eventBusManager;
|
|
566
|
-
this.extendedAgentCardProvider = extendedAgentCardProvider;
|
|
567
|
-
if (agentCard.capabilities.pushNotifications) {
|
|
568
|
-
this.pushNotificationStore = pushNotificationStore || new InMemoryPushNotificationStore();
|
|
569
|
-
this.pushNotificationSender = pushNotificationSender || new DefaultPushNotificationSender(this.pushNotificationStore);
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
async getAgentCard() {
|
|
573
|
-
return this.agentCard;
|
|
574
|
-
}
|
|
575
|
-
async getAuthenticatedExtendedAgentCard(context) {
|
|
576
|
-
if (!this.agentCard.supportsAuthenticatedExtendedCard) {
|
|
577
|
-
throw A2AError.unsupportedOperation("Agent does not support authenticated extended card.");
|
|
578
|
-
}
|
|
579
|
-
if (!this.extendedAgentCardProvider) {
|
|
580
|
-
throw A2AError.authenticatedExtendedCardNotConfigured();
|
|
581
|
-
}
|
|
582
|
-
if (typeof this.extendedAgentCardProvider === "function") {
|
|
583
|
-
return this.extendedAgentCardProvider(context);
|
|
584
|
-
}
|
|
585
|
-
if (context?.user?.isAuthenticated) {
|
|
586
|
-
return this.extendedAgentCardProvider;
|
|
587
|
-
}
|
|
588
|
-
return this.agentCard;
|
|
589
|
-
}
|
|
590
|
-
async _createRequestContext(incomingMessage, context) {
|
|
591
|
-
let task;
|
|
592
|
-
let referenceTasks;
|
|
593
|
-
if (incomingMessage.taskId) {
|
|
594
|
-
task = await this.taskStore.load(incomingMessage.taskId);
|
|
595
|
-
if (!task) {
|
|
596
|
-
throw A2AError.taskNotFound(incomingMessage.taskId);
|
|
597
|
-
}
|
|
598
|
-
if (terminalStates.includes(task.status.state)) {
|
|
599
|
-
throw A2AError.invalidRequest(
|
|
600
|
-
`Task ${task.id} is in a terminal state (${task.status.state}) and cannot be modified.`
|
|
601
|
-
);
|
|
602
|
-
}
|
|
603
|
-
task.history = [...task.history || [], incomingMessage];
|
|
604
|
-
await this.taskStore.save(task);
|
|
605
|
-
}
|
|
606
|
-
const taskId = incomingMessage.taskId || (0, import_uuid.v4)();
|
|
607
|
-
if (incomingMessage.referenceTaskIds && incomingMessage.referenceTaskIds.length > 0) {
|
|
608
|
-
referenceTasks = [];
|
|
609
|
-
for (const refId of incomingMessage.referenceTaskIds) {
|
|
610
|
-
const refTask = await this.taskStore.load(refId);
|
|
611
|
-
if (refTask) {
|
|
612
|
-
referenceTasks.push(refTask);
|
|
613
|
-
} else {
|
|
614
|
-
console.warn(`Reference task ${refId} not found.`);
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
const contextId = incomingMessage.contextId || task?.contextId || (0, import_uuid.v4)();
|
|
619
|
-
if (context?.requestedExtensions) {
|
|
620
|
-
const agentCard = await this.getAgentCard();
|
|
621
|
-
const exposedExtensions = new Set(
|
|
622
|
-
agentCard.capabilities.extensions?.map((ext) => ext.uri) || []
|
|
623
|
-
);
|
|
624
|
-
const validExtensions = new Set(
|
|
625
|
-
Array.from(context.requestedExtensions).filter(
|
|
626
|
-
(extension) => exposedExtensions.has(extension)
|
|
627
|
-
)
|
|
628
|
-
);
|
|
629
|
-
context = new ServerCallContext(validExtensions, context.user);
|
|
630
|
-
}
|
|
631
|
-
const messageForContext = {
|
|
632
|
-
...incomingMessage,
|
|
633
|
-
contextId,
|
|
634
|
-
taskId
|
|
635
|
-
};
|
|
636
|
-
return new RequestContext(messageForContext, taskId, contextId, task, referenceTasks, context);
|
|
637
|
-
}
|
|
638
|
-
async _processEvents(taskId, resultManager, eventQueue, options) {
|
|
639
|
-
let firstResultSent = false;
|
|
640
|
-
try {
|
|
641
|
-
for await (const event of eventQueue.events()) {
|
|
642
|
-
await resultManager.processEvent(event);
|
|
643
|
-
try {
|
|
644
|
-
await this._sendPushNotificationIfNeeded(event);
|
|
645
|
-
} catch (error) {
|
|
646
|
-
console.error(`Error sending push notification: ${error}`);
|
|
647
|
-
}
|
|
648
|
-
if (options?.firstResultResolver && !firstResultSent) {
|
|
649
|
-
let firstResult;
|
|
650
|
-
if (event.kind === "message") {
|
|
651
|
-
firstResult = event;
|
|
652
|
-
} else {
|
|
653
|
-
firstResult = resultManager.getCurrentTask();
|
|
654
|
-
}
|
|
655
|
-
if (firstResult) {
|
|
656
|
-
options.firstResultResolver(firstResult);
|
|
657
|
-
firstResultSent = true;
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
if (options?.firstResultRejector && !firstResultSent) {
|
|
662
|
-
options.firstResultRejector(
|
|
663
|
-
A2AError.internalError("Execution finished before a message or task was produced.")
|
|
664
|
-
);
|
|
665
|
-
}
|
|
666
|
-
} catch (error) {
|
|
667
|
-
console.error(`Event processing loop failed for task ${taskId}:`, error);
|
|
668
|
-
this._handleProcessingError(
|
|
669
|
-
error,
|
|
670
|
-
resultManager,
|
|
671
|
-
firstResultSent,
|
|
672
|
-
taskId,
|
|
673
|
-
options?.firstResultRejector
|
|
674
|
-
);
|
|
675
|
-
} finally {
|
|
676
|
-
this.eventBusManager.cleanupByTaskId(taskId);
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
async sendMessage(params, context) {
|
|
680
|
-
const incomingMessage = params.message;
|
|
681
|
-
if (!incomingMessage.messageId) {
|
|
682
|
-
throw A2AError.invalidParams("message.messageId is required.");
|
|
683
|
-
}
|
|
684
|
-
const isBlocking = params.configuration?.blocking !== false;
|
|
685
|
-
const resultManager = new ResultManager(this.taskStore);
|
|
686
|
-
resultManager.setContext(incomingMessage);
|
|
687
|
-
const requestContext = await this._createRequestContext(incomingMessage, context);
|
|
688
|
-
const taskId = requestContext.taskId;
|
|
689
|
-
const finalMessageForAgent = requestContext.userMessage;
|
|
690
|
-
if (params.configuration?.pushNotificationConfig && this.agentCard.capabilities.pushNotifications) {
|
|
691
|
-
await this.pushNotificationStore?.save(taskId, params.configuration.pushNotificationConfig);
|
|
692
|
-
}
|
|
693
|
-
const eventBus = this.eventBusManager.createOrGetByTaskId(taskId);
|
|
694
|
-
const eventQueue = new ExecutionEventQueue(eventBus);
|
|
695
|
-
this.agentExecutor.execute(requestContext, eventBus).catch((err) => {
|
|
696
|
-
console.error(`Agent execution failed for message ${finalMessageForAgent.messageId}:`, err);
|
|
697
|
-
const errorTask = {
|
|
698
|
-
id: requestContext.task?.id || (0, import_uuid.v4)(),
|
|
699
|
-
// Use existing task ID or generate new
|
|
700
|
-
contextId: finalMessageForAgent.contextId,
|
|
701
|
-
status: {
|
|
702
|
-
state: "failed",
|
|
703
|
-
message: {
|
|
704
|
-
kind: "message",
|
|
705
|
-
role: "agent",
|
|
706
|
-
messageId: (0, import_uuid.v4)(),
|
|
707
|
-
parts: [{ kind: "text", text: `Agent execution error: ${err.message}` }],
|
|
708
|
-
taskId: requestContext.task?.id,
|
|
709
|
-
contextId: finalMessageForAgent.contextId
|
|
710
|
-
},
|
|
711
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
712
|
-
},
|
|
713
|
-
history: requestContext.task?.history ? [...requestContext.task.history] : [],
|
|
714
|
-
kind: "task"
|
|
715
|
-
};
|
|
716
|
-
if (finalMessageForAgent) {
|
|
717
|
-
if (!errorTask.history?.find((m) => m.messageId === finalMessageForAgent.messageId)) {
|
|
718
|
-
errorTask.history?.push(finalMessageForAgent);
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
eventBus.publish(errorTask);
|
|
722
|
-
eventBus.publish({
|
|
723
|
-
// And publish a final status update
|
|
724
|
-
kind: "status-update",
|
|
725
|
-
taskId: errorTask.id,
|
|
726
|
-
contextId: errorTask.contextId,
|
|
727
|
-
status: errorTask.status,
|
|
728
|
-
final: true
|
|
729
|
-
});
|
|
730
|
-
eventBus.finished();
|
|
731
|
-
});
|
|
732
|
-
if (isBlocking) {
|
|
733
|
-
await this._processEvents(taskId, resultManager, eventQueue);
|
|
734
|
-
const finalResult = resultManager.getFinalResult();
|
|
735
|
-
if (!finalResult) {
|
|
736
|
-
throw A2AError.internalError(
|
|
737
|
-
"Agent execution finished without a result, and no task context found."
|
|
738
|
-
);
|
|
739
|
-
}
|
|
740
|
-
return finalResult;
|
|
741
|
-
} else {
|
|
742
|
-
return new Promise((resolve, reject) => {
|
|
743
|
-
this._processEvents(taskId, resultManager, eventQueue, {
|
|
744
|
-
firstResultResolver: resolve,
|
|
745
|
-
firstResultRejector: reject
|
|
746
|
-
});
|
|
747
|
-
});
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
async *sendMessageStream(params, context) {
|
|
751
|
-
const incomingMessage = params.message;
|
|
752
|
-
if (!incomingMessage.messageId) {
|
|
753
|
-
throw A2AError.invalidParams("message.messageId is required for streaming.");
|
|
754
|
-
}
|
|
755
|
-
const resultManager = new ResultManager(this.taskStore);
|
|
756
|
-
resultManager.setContext(incomingMessage);
|
|
757
|
-
const requestContext = await this._createRequestContext(incomingMessage, context);
|
|
758
|
-
const taskId = requestContext.taskId;
|
|
759
|
-
const finalMessageForAgent = requestContext.userMessage;
|
|
760
|
-
const eventBus = this.eventBusManager.createOrGetByTaskId(taskId);
|
|
761
|
-
const eventQueue = new ExecutionEventQueue(eventBus);
|
|
762
|
-
if (params.configuration?.pushNotificationConfig && this.agentCard.capabilities.pushNotifications) {
|
|
763
|
-
await this.pushNotificationStore?.save(taskId, params.configuration.pushNotificationConfig);
|
|
764
|
-
}
|
|
765
|
-
this.agentExecutor.execute(requestContext, eventBus).catch((err) => {
|
|
766
|
-
console.error(
|
|
767
|
-
`Agent execution failed for stream message ${finalMessageForAgent.messageId}:`,
|
|
768
|
-
err
|
|
769
|
-
);
|
|
770
|
-
const errorTaskStatus = {
|
|
771
|
-
kind: "status-update",
|
|
772
|
-
taskId: requestContext.task?.id || (0, import_uuid.v4)(),
|
|
773
|
-
// Use existing or a placeholder
|
|
774
|
-
contextId: finalMessageForAgent.contextId,
|
|
775
|
-
status: {
|
|
776
|
-
state: "failed",
|
|
777
|
-
message: {
|
|
778
|
-
kind: "message",
|
|
779
|
-
role: "agent",
|
|
780
|
-
messageId: (0, import_uuid.v4)(),
|
|
781
|
-
parts: [{ kind: "text", text: `Agent execution error: ${err.message}` }],
|
|
782
|
-
taskId: requestContext.task?.id,
|
|
783
|
-
contextId: finalMessageForAgent.contextId
|
|
784
|
-
},
|
|
785
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
786
|
-
},
|
|
787
|
-
final: true
|
|
788
|
-
// This will terminate the stream for the client
|
|
789
|
-
};
|
|
790
|
-
eventBus.publish(errorTaskStatus);
|
|
791
|
-
});
|
|
792
|
-
try {
|
|
793
|
-
for await (const event of eventQueue.events()) {
|
|
794
|
-
await resultManager.processEvent(event);
|
|
795
|
-
await this._sendPushNotificationIfNeeded(event);
|
|
796
|
-
yield event;
|
|
797
|
-
}
|
|
798
|
-
} finally {
|
|
799
|
-
this.eventBusManager.cleanupByTaskId(taskId);
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
async getTask(params, _context) {
|
|
803
|
-
const task = await this.taskStore.load(params.id);
|
|
804
|
-
if (!task) {
|
|
805
|
-
throw A2AError.taskNotFound(params.id);
|
|
806
|
-
}
|
|
807
|
-
if (params.historyLength !== void 0 && params.historyLength >= 0) {
|
|
808
|
-
if (task.history) {
|
|
809
|
-
task.history = task.history.slice(-params.historyLength);
|
|
810
|
-
}
|
|
811
|
-
} else {
|
|
812
|
-
task.history = [];
|
|
813
|
-
}
|
|
814
|
-
return task;
|
|
815
|
-
}
|
|
816
|
-
async cancelTask(params, _context) {
|
|
817
|
-
const task = await this.taskStore.load(params.id);
|
|
818
|
-
if (!task) {
|
|
819
|
-
throw A2AError.taskNotFound(params.id);
|
|
820
|
-
}
|
|
821
|
-
const nonCancelableStates = ["completed", "failed", "canceled", "rejected"];
|
|
822
|
-
if (nonCancelableStates.includes(task.status.state)) {
|
|
823
|
-
throw A2AError.taskNotCancelable(params.id);
|
|
824
|
-
}
|
|
825
|
-
const eventBus = this.eventBusManager.getByTaskId(params.id);
|
|
826
|
-
if (eventBus) {
|
|
827
|
-
const eventQueue = new ExecutionEventQueue(eventBus);
|
|
828
|
-
await this.agentExecutor.cancelTask(params.id, eventBus);
|
|
829
|
-
await this._processEvents(params.id, new ResultManager(this.taskStore), eventQueue);
|
|
830
|
-
} else {
|
|
831
|
-
task.status = {
|
|
832
|
-
state: "canceled",
|
|
833
|
-
message: {
|
|
834
|
-
// Optional: Add a system message indicating cancellation
|
|
835
|
-
kind: "message",
|
|
836
|
-
role: "agent",
|
|
837
|
-
messageId: (0, import_uuid.v4)(),
|
|
838
|
-
parts: [{ kind: "text", text: "Task cancellation requested by user." }],
|
|
839
|
-
taskId: task.id,
|
|
840
|
-
contextId: task.contextId
|
|
841
|
-
},
|
|
842
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
843
|
-
};
|
|
844
|
-
task.history = [...task.history || [], task.status.message];
|
|
845
|
-
await this.taskStore.save(task);
|
|
846
|
-
}
|
|
847
|
-
const latestTask = await this.taskStore.load(params.id);
|
|
848
|
-
if (!latestTask) {
|
|
849
|
-
throw A2AError.internalError(`Task ${params.id} not found after cancellation.`);
|
|
850
|
-
}
|
|
851
|
-
if (latestTask.status.state != "canceled") {
|
|
852
|
-
throw A2AError.taskNotCancelable(params.id);
|
|
853
|
-
}
|
|
854
|
-
return latestTask;
|
|
855
|
-
}
|
|
856
|
-
async setTaskPushNotificationConfig(params, _context) {
|
|
857
|
-
if (!this.agentCard.capabilities.pushNotifications) {
|
|
858
|
-
throw A2AError.pushNotificationNotSupported();
|
|
859
|
-
}
|
|
860
|
-
const task = await this.taskStore.load(params.taskId);
|
|
861
|
-
if (!task) {
|
|
862
|
-
throw A2AError.taskNotFound(params.taskId);
|
|
863
|
-
}
|
|
864
|
-
const { taskId, pushNotificationConfig } = params;
|
|
865
|
-
if (!pushNotificationConfig.id) {
|
|
866
|
-
pushNotificationConfig.id = taskId;
|
|
867
|
-
}
|
|
868
|
-
await this.pushNotificationStore?.save(taskId, pushNotificationConfig);
|
|
869
|
-
return params;
|
|
870
|
-
}
|
|
871
|
-
async getTaskPushNotificationConfig(params, _context) {
|
|
872
|
-
if (!this.agentCard.capabilities.pushNotifications) {
|
|
873
|
-
throw A2AError.pushNotificationNotSupported();
|
|
874
|
-
}
|
|
875
|
-
const task = await this.taskStore.load(params.id);
|
|
876
|
-
if (!task) {
|
|
877
|
-
throw A2AError.taskNotFound(params.id);
|
|
878
|
-
}
|
|
879
|
-
const configs = await this.pushNotificationStore?.load(params.id) || [];
|
|
880
|
-
if (configs.length === 0) {
|
|
881
|
-
throw A2AError.internalError(`Push notification config not found for task ${params.id}.`);
|
|
882
|
-
}
|
|
883
|
-
let configId;
|
|
884
|
-
if ("pushNotificationConfigId" in params && params.pushNotificationConfigId) {
|
|
885
|
-
configId = params.pushNotificationConfigId;
|
|
886
|
-
} else {
|
|
887
|
-
configId = params.id;
|
|
888
|
-
}
|
|
889
|
-
const config = configs.find((c) => c.id === configId);
|
|
890
|
-
if (!config) {
|
|
891
|
-
throw A2AError.internalError(
|
|
892
|
-
`Push notification config with id '${configId}' not found for task ${params.id}.`
|
|
893
|
-
);
|
|
894
|
-
}
|
|
895
|
-
return { taskId: params.id, pushNotificationConfig: config };
|
|
896
|
-
}
|
|
897
|
-
async listTaskPushNotificationConfigs(params, _context) {
|
|
898
|
-
if (!this.agentCard.capabilities.pushNotifications) {
|
|
899
|
-
throw A2AError.pushNotificationNotSupported();
|
|
900
|
-
}
|
|
901
|
-
const task = await this.taskStore.load(params.id);
|
|
902
|
-
if (!task) {
|
|
903
|
-
throw A2AError.taskNotFound(params.id);
|
|
904
|
-
}
|
|
905
|
-
const configs = await this.pushNotificationStore?.load(params.id) || [];
|
|
906
|
-
return configs.map((config) => ({
|
|
907
|
-
taskId: params.id,
|
|
908
|
-
pushNotificationConfig: config
|
|
909
|
-
}));
|
|
910
|
-
}
|
|
911
|
-
async deleteTaskPushNotificationConfig(params, _context) {
|
|
912
|
-
if (!this.agentCard.capabilities.pushNotifications) {
|
|
913
|
-
throw A2AError.pushNotificationNotSupported();
|
|
914
|
-
}
|
|
915
|
-
const task = await this.taskStore.load(params.id);
|
|
916
|
-
if (!task) {
|
|
917
|
-
throw A2AError.taskNotFound(params.id);
|
|
918
|
-
}
|
|
919
|
-
const { id: taskId, pushNotificationConfigId } = params;
|
|
920
|
-
await this.pushNotificationStore?.delete(taskId, pushNotificationConfigId);
|
|
921
|
-
}
|
|
922
|
-
async *resubscribe(params, _context) {
|
|
923
|
-
if (!this.agentCard.capabilities.streaming) {
|
|
924
|
-
throw A2AError.unsupportedOperation("Streaming (and thus resubscription) is not supported.");
|
|
925
|
-
}
|
|
926
|
-
const task = await this.taskStore.load(params.id);
|
|
927
|
-
if (!task) {
|
|
928
|
-
throw A2AError.taskNotFound(params.id);
|
|
929
|
-
}
|
|
930
|
-
yield task;
|
|
931
|
-
const finalStates = ["completed", "failed", "canceled", "rejected"];
|
|
932
|
-
if (finalStates.includes(task.status.state)) {
|
|
933
|
-
return;
|
|
934
|
-
}
|
|
935
|
-
const eventBus = this.eventBusManager.getByTaskId(params.id);
|
|
936
|
-
if (!eventBus) {
|
|
937
|
-
console.warn(`Resubscribe: No active event bus for task ${params.id}.`);
|
|
938
|
-
return;
|
|
939
|
-
}
|
|
940
|
-
const eventQueue = new ExecutionEventQueue(eventBus);
|
|
941
|
-
try {
|
|
942
|
-
for await (const event of eventQueue.events()) {
|
|
943
|
-
if (event.kind === "status-update" && event.taskId === params.id) {
|
|
944
|
-
yield event;
|
|
945
|
-
} else if (event.kind === "artifact-update" && event.taskId === params.id) {
|
|
946
|
-
yield event;
|
|
947
|
-
} else if (event.kind === "task" && event.id === params.id) {
|
|
948
|
-
yield event;
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
} finally {
|
|
952
|
-
eventQueue.stop();
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
async _sendPushNotificationIfNeeded(event) {
|
|
956
|
-
if (!this.agentCard.capabilities.pushNotifications) {
|
|
957
|
-
return;
|
|
958
|
-
}
|
|
959
|
-
let taskId = "";
|
|
960
|
-
if (event.kind == "task") {
|
|
961
|
-
const task2 = event;
|
|
962
|
-
taskId = task2.id;
|
|
963
|
-
} else {
|
|
964
|
-
taskId = event.taskId;
|
|
965
|
-
}
|
|
966
|
-
if (!taskId) {
|
|
967
|
-
console.error(`Task ID not found for event ${event.kind}.`);
|
|
968
|
-
return;
|
|
969
|
-
}
|
|
970
|
-
const task = await this.taskStore.load(taskId);
|
|
971
|
-
if (!task) {
|
|
972
|
-
console.error(`Task ${taskId} not found.`);
|
|
973
|
-
return;
|
|
974
|
-
}
|
|
975
|
-
this.pushNotificationSender?.send(task);
|
|
976
|
-
}
|
|
977
|
-
async _handleProcessingError(error, resultManager, firstResultSent, taskId, firstResultRejector) {
|
|
978
|
-
if (firstResultRejector && !firstResultSent) {
|
|
979
|
-
firstResultRejector(error);
|
|
980
|
-
return;
|
|
981
|
-
}
|
|
982
|
-
if (!firstResultRejector) {
|
|
983
|
-
throw error;
|
|
984
|
-
}
|
|
985
|
-
const currentTask = resultManager.getCurrentTask();
|
|
986
|
-
const errorMessage = error instanceof Error && error.message || "Unknown error";
|
|
987
|
-
if (currentTask) {
|
|
988
|
-
const statusUpdateFailed = {
|
|
989
|
-
taskId: currentTask.id,
|
|
990
|
-
contextId: currentTask.contextId,
|
|
991
|
-
status: {
|
|
992
|
-
state: "failed",
|
|
993
|
-
message: {
|
|
994
|
-
kind: "message",
|
|
995
|
-
role: "agent",
|
|
996
|
-
messageId: (0, import_uuid.v4)(),
|
|
997
|
-
parts: [{ kind: "text", text: `Event processing loop failed: ${errorMessage}` }],
|
|
998
|
-
taskId: currentTask.id,
|
|
999
|
-
contextId: currentTask.contextId
|
|
1000
|
-
},
|
|
1001
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1002
|
-
},
|
|
1003
|
-
kind: "status-update",
|
|
1004
|
-
final: true
|
|
1005
|
-
};
|
|
1006
|
-
try {
|
|
1007
|
-
await resultManager.processEvent(statusUpdateFailed);
|
|
1008
|
-
} catch (error2) {
|
|
1009
|
-
console.error(
|
|
1010
|
-
`Event processing loop failed for task ${taskId}: ${error2 instanceof Error && error2.message || "Unknown error"}`
|
|
1011
|
-
);
|
|
1012
|
-
}
|
|
1013
|
-
} else {
|
|
1014
|
-
console.error(`Event processing loop failed for task ${taskId}: ${errorMessage}`);
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
};
|
|
1018
|
-
|
|
1019
|
-
// src/server/store.ts
|
|
1020
|
-
var InMemoryTaskStore = class {
|
|
1021
|
-
store = /* @__PURE__ */ new Map();
|
|
1022
|
-
async load(taskId) {
|
|
1023
|
-
const entry = this.store.get(taskId);
|
|
1024
|
-
return entry ? { ...entry } : void 0;
|
|
1025
|
-
}
|
|
1026
|
-
async save(task) {
|
|
1027
|
-
this.store.set(task.id, { ...task });
|
|
1028
|
-
}
|
|
1029
|
-
};
|
|
1030
|
-
|
|
1031
|
-
// src/server/transports/jsonrpc_transport_handler.ts
|
|
1032
|
-
var JsonRpcTransportHandler = class {
|
|
1033
|
-
requestHandler;
|
|
1034
|
-
constructor(requestHandler) {
|
|
1035
|
-
this.requestHandler = requestHandler;
|
|
1036
|
-
}
|
|
1037
|
-
/**
|
|
1038
|
-
* Handles an incoming JSON-RPC request.
|
|
1039
|
-
* For streaming methods, it returns an AsyncGenerator of JSONRPCResult.
|
|
1040
|
-
* For non-streaming methods, it returns a Promise of a single JSONRPCMessage (Result or ErrorResponse).
|
|
1041
|
-
*/
|
|
1042
|
-
async handle(requestBody, context) {
|
|
1043
|
-
let rpcRequest;
|
|
1044
|
-
try {
|
|
1045
|
-
if (typeof requestBody === "string") {
|
|
1046
|
-
rpcRequest = JSON.parse(requestBody);
|
|
1047
|
-
} else if (typeof requestBody === "object" && requestBody !== null) {
|
|
1048
|
-
rpcRequest = requestBody;
|
|
1049
|
-
} else {
|
|
1050
|
-
throw A2AError.parseError("Invalid request body type.");
|
|
1051
|
-
}
|
|
1052
|
-
if (!this.isRequestValid(rpcRequest)) {
|
|
1053
|
-
throw A2AError.invalidRequest("Invalid JSON-RPC Request.");
|
|
1054
|
-
}
|
|
1055
|
-
} catch (error) {
|
|
1056
|
-
const a2aError = error instanceof A2AError ? error : A2AError.parseError(
|
|
1057
|
-
error instanceof SyntaxError && error.message || "Failed to parse JSON request."
|
|
1058
|
-
);
|
|
1059
|
-
return {
|
|
1060
|
-
jsonrpc: "2.0",
|
|
1061
|
-
id: rpcRequest?.id !== void 0 ? rpcRequest.id : null,
|
|
1062
|
-
error: a2aError.toJSONRPCError()
|
|
1063
|
-
};
|
|
1064
|
-
}
|
|
1065
|
-
const { method, id: requestId = null } = rpcRequest;
|
|
1066
|
-
try {
|
|
1067
|
-
if (method !== "agent/getAuthenticatedExtendedCard" && !this.paramsAreValid(rpcRequest.params)) {
|
|
1068
|
-
throw A2AError.invalidParams(`Invalid method parameters.`);
|
|
1069
|
-
}
|
|
1070
|
-
if (method === "message/stream" || method === "tasks/resubscribe") {
|
|
1071
|
-
const params = rpcRequest.params;
|
|
1072
|
-
const agentCard = await this.requestHandler.getAgentCard();
|
|
1073
|
-
if (!agentCard.capabilities.streaming) {
|
|
1074
|
-
throw A2AError.unsupportedOperation(`Method ${method} requires streaming capability.`);
|
|
1075
|
-
}
|
|
1076
|
-
const agentEventStream = method === "message/stream" ? this.requestHandler.sendMessageStream(params, context) : this.requestHandler.resubscribe(params, context);
|
|
1077
|
-
return (async function* jsonRpcEventStream() {
|
|
1078
|
-
try {
|
|
1079
|
-
for await (const event of agentEventStream) {
|
|
1080
|
-
yield {
|
|
1081
|
-
jsonrpc: "2.0",
|
|
1082
|
-
id: requestId,
|
|
1083
|
-
// Use the original request ID for all streamed responses
|
|
1084
|
-
result: event
|
|
1085
|
-
};
|
|
1086
|
-
}
|
|
1087
|
-
} catch (streamError) {
|
|
1088
|
-
console.error(
|
|
1089
|
-
`Error in agent event stream for ${method} (request ${requestId}):`,
|
|
1090
|
-
streamError
|
|
1091
|
-
);
|
|
1092
|
-
throw streamError;
|
|
1093
|
-
}
|
|
1094
|
-
})();
|
|
1095
|
-
} else {
|
|
1096
|
-
let result;
|
|
1097
|
-
switch (method) {
|
|
1098
|
-
case "message/send":
|
|
1099
|
-
result = await this.requestHandler.sendMessage(rpcRequest.params, context);
|
|
1100
|
-
break;
|
|
1101
|
-
case "tasks/get":
|
|
1102
|
-
result = await this.requestHandler.getTask(rpcRequest.params, context);
|
|
1103
|
-
break;
|
|
1104
|
-
case "tasks/cancel":
|
|
1105
|
-
result = await this.requestHandler.cancelTask(rpcRequest.params, context);
|
|
1106
|
-
break;
|
|
1107
|
-
case "tasks/pushNotificationConfig/set":
|
|
1108
|
-
result = await this.requestHandler.setTaskPushNotificationConfig(
|
|
1109
|
-
rpcRequest.params,
|
|
1110
|
-
context
|
|
1111
|
-
);
|
|
1112
|
-
break;
|
|
1113
|
-
case "tasks/pushNotificationConfig/get":
|
|
1114
|
-
result = await this.requestHandler.getTaskPushNotificationConfig(
|
|
1115
|
-
rpcRequest.params,
|
|
1116
|
-
context
|
|
1117
|
-
);
|
|
1118
|
-
break;
|
|
1119
|
-
case "tasks/pushNotificationConfig/delete":
|
|
1120
|
-
await this.requestHandler.deleteTaskPushNotificationConfig(rpcRequest.params, context);
|
|
1121
|
-
result = null;
|
|
1122
|
-
break;
|
|
1123
|
-
case "tasks/pushNotificationConfig/list":
|
|
1124
|
-
result = await this.requestHandler.listTaskPushNotificationConfigs(
|
|
1125
|
-
rpcRequest.params,
|
|
1126
|
-
context
|
|
1127
|
-
);
|
|
1128
|
-
break;
|
|
1129
|
-
case "agent/getAuthenticatedExtendedCard":
|
|
1130
|
-
result = await this.requestHandler.getAuthenticatedExtendedAgentCard(context);
|
|
1131
|
-
break;
|
|
1132
|
-
default:
|
|
1133
|
-
throw A2AError.methodNotFound(method);
|
|
1134
|
-
}
|
|
1135
|
-
return {
|
|
1136
|
-
jsonrpc: "2.0",
|
|
1137
|
-
id: requestId,
|
|
1138
|
-
result
|
|
1139
|
-
};
|
|
1140
|
-
}
|
|
1141
|
-
} catch (error) {
|
|
1142
|
-
let a2aError;
|
|
1143
|
-
if (error instanceof A2AError) {
|
|
1144
|
-
a2aError = error;
|
|
1145
|
-
} else {
|
|
1146
|
-
a2aError = A2AError.internalError(
|
|
1147
|
-
error instanceof Error && error.message || "An unexpected error occurred."
|
|
1148
|
-
);
|
|
1149
|
-
}
|
|
1150
|
-
return {
|
|
1151
|
-
jsonrpc: "2.0",
|
|
1152
|
-
id: requestId,
|
|
1153
|
-
error: a2aError.toJSONRPCError()
|
|
1154
|
-
};
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
// Validates the basic structure of a JSON-RPC request
|
|
1158
|
-
isRequestValid(rpcRequest) {
|
|
1159
|
-
if (rpcRequest.jsonrpc !== "2.0") {
|
|
1160
|
-
return false;
|
|
1161
|
-
}
|
|
1162
|
-
if ("id" in rpcRequest) {
|
|
1163
|
-
const id = rpcRequest.id;
|
|
1164
|
-
const isString = typeof id === "string";
|
|
1165
|
-
const isInteger = typeof id === "number" && Number.isInteger(id);
|
|
1166
|
-
const isNull = id === null;
|
|
1167
|
-
if (!isString && !isInteger && !isNull) {
|
|
1168
|
-
return false;
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
|
-
if (!rpcRequest.method || typeof rpcRequest.method !== "string") {
|
|
1172
|
-
return false;
|
|
1173
|
-
}
|
|
1174
|
-
return true;
|
|
1175
|
-
}
|
|
1176
|
-
// Validates that params is an object with non-empty string keys
|
|
1177
|
-
paramsAreValid(params) {
|
|
1178
|
-
if (typeof params !== "object" || params === null || Array.isArray(params)) {
|
|
1179
|
-
return false;
|
|
1180
|
-
}
|
|
1181
|
-
for (const key of Object.keys(params)) {
|
|
1182
|
-
if (key === "") {
|
|
1183
|
-
return false;
|
|
1184
|
-
}
|
|
1185
|
-
}
|
|
1186
|
-
return true;
|
|
1187
|
-
}
|
|
1188
|
-
};
|
|
1189
|
-
|
|
1190
|
-
// src/errors.ts
|
|
1191
|
-
var TaskNotFoundError = class extends Error {
|
|
1192
|
-
constructor(message) {
|
|
1193
|
-
super(message ?? "Task not found");
|
|
1194
|
-
this.name = "TaskNotFoundError";
|
|
1195
|
-
}
|
|
1196
|
-
};
|
|
1197
|
-
var TaskNotCancelableError = class extends Error {
|
|
1198
|
-
constructor(message) {
|
|
1199
|
-
super(message ?? "Task cannot be canceled");
|
|
1200
|
-
this.name = "TaskNotCancelableError";
|
|
1201
|
-
}
|
|
1202
|
-
};
|
|
1203
|
-
var PushNotificationNotSupportedError = class extends Error {
|
|
1204
|
-
constructor(message) {
|
|
1205
|
-
super(message ?? "Push Notification is not supported");
|
|
1206
|
-
this.name = "PushNotificationNotSupportedError";
|
|
1207
|
-
}
|
|
1208
|
-
};
|
|
1209
|
-
var UnsupportedOperationError = class extends Error {
|
|
1210
|
-
constructor(message) {
|
|
1211
|
-
super(message ?? "This operation is not supported");
|
|
1212
|
-
this.name = "UnsupportedOperationError";
|
|
1213
|
-
}
|
|
1214
|
-
};
|
|
1215
|
-
var ContentTypeNotSupportedError = class extends Error {
|
|
1216
|
-
constructor(message) {
|
|
1217
|
-
super(message ?? "Incompatible content types");
|
|
1218
|
-
this.name = "ContentTypeNotSupportedError";
|
|
1219
|
-
}
|
|
1220
|
-
};
|
|
1221
|
-
var InvalidAgentResponseError = class extends Error {
|
|
1222
|
-
constructor(message) {
|
|
1223
|
-
super(message ?? "Invalid agent response type");
|
|
1224
|
-
this.name = "InvalidAgentResponseError";
|
|
1225
|
-
}
|
|
1226
|
-
};
|
|
1227
|
-
var AuthenticatedExtendedCardNotConfiguredError = class extends Error {
|
|
1228
|
-
constructor(message) {
|
|
1229
|
-
super(message ?? "Authenticated Extended Card not configured");
|
|
1230
|
-
this.name = "AuthenticatedExtendedCardNotConfiguredError";
|
|
1231
|
-
}
|
|
1232
|
-
};
|
|
1233
|
-
|
|
1234
|
-
// src/client/transports/json_rpc_transport.ts
|
|
1235
|
-
var JsonRpcTransport = class _JsonRpcTransport {
|
|
1236
|
-
customFetchImpl;
|
|
1237
|
-
endpoint;
|
|
1238
|
-
requestIdCounter = 1;
|
|
1239
|
-
constructor(options) {
|
|
1240
|
-
this.endpoint = options.endpoint;
|
|
1241
|
-
this.customFetchImpl = options.fetchImpl;
|
|
1242
|
-
}
|
|
1243
|
-
async sendMessage(params, options, idOverride) {
|
|
1244
|
-
const rpcResponse = await this._sendRpcRequest(
|
|
1245
|
-
"message/send",
|
|
1246
|
-
params,
|
|
1247
|
-
idOverride,
|
|
1248
|
-
options
|
|
1249
|
-
);
|
|
1250
|
-
return rpcResponse.result;
|
|
1251
|
-
}
|
|
1252
|
-
async *sendMessageStream(params, options) {
|
|
1253
|
-
yield* this._sendStreamingRequest("message/stream", params, options);
|
|
1254
|
-
}
|
|
1255
|
-
async setTaskPushNotificationConfig(params, options, idOverride) {
|
|
1256
|
-
const rpcResponse = await this._sendRpcRequest("tasks/pushNotificationConfig/set", params, idOverride, options);
|
|
1257
|
-
return rpcResponse.result;
|
|
1258
|
-
}
|
|
1259
|
-
async getTaskPushNotificationConfig(params, options, idOverride) {
|
|
1260
|
-
const rpcResponse = await this._sendRpcRequest("tasks/pushNotificationConfig/get", params, idOverride, options);
|
|
1261
|
-
return rpcResponse.result;
|
|
1262
|
-
}
|
|
1263
|
-
async listTaskPushNotificationConfig(params, options, idOverride) {
|
|
1264
|
-
const rpcResponse = await this._sendRpcRequest("tasks/pushNotificationConfig/list", params, idOverride, options);
|
|
1265
|
-
return rpcResponse.result;
|
|
1266
|
-
}
|
|
1267
|
-
async deleteTaskPushNotificationConfig(params, options, idOverride) {
|
|
1268
|
-
await this._sendRpcRequest("tasks/pushNotificationConfig/delete", params, idOverride, options);
|
|
1269
|
-
}
|
|
1270
|
-
async getTask(params, options, idOverride) {
|
|
1271
|
-
const rpcResponse = await this._sendRpcRequest(
|
|
1272
|
-
"tasks/get",
|
|
1273
|
-
params,
|
|
1274
|
-
idOverride,
|
|
1275
|
-
options
|
|
1276
|
-
);
|
|
1277
|
-
return rpcResponse.result;
|
|
1278
|
-
}
|
|
1279
|
-
async cancelTask(params, options, idOverride) {
|
|
1280
|
-
const rpcResponse = await this._sendRpcRequest(
|
|
1281
|
-
"tasks/cancel",
|
|
1282
|
-
params,
|
|
1283
|
-
idOverride,
|
|
1284
|
-
options
|
|
1285
|
-
);
|
|
1286
|
-
return rpcResponse.result;
|
|
1287
|
-
}
|
|
1288
|
-
async *resubscribeTask(params, options) {
|
|
1289
|
-
yield* this._sendStreamingRequest("tasks/resubscribe", params, options);
|
|
1290
|
-
}
|
|
1291
|
-
async callExtensionMethod(method, params, idOverride, options) {
|
|
1292
|
-
return await this._sendRpcRequest(
|
|
1293
|
-
method,
|
|
1294
|
-
params,
|
|
1295
|
-
idOverride,
|
|
1296
|
-
options
|
|
1297
|
-
);
|
|
1298
|
-
}
|
|
1299
|
-
_fetch(...args) {
|
|
1300
|
-
if (this.customFetchImpl) {
|
|
1301
|
-
return this.customFetchImpl(...args);
|
|
1302
|
-
}
|
|
1303
|
-
if (typeof fetch === "function") {
|
|
1304
|
-
return fetch(...args);
|
|
1305
|
-
}
|
|
1306
|
-
throw new Error(
|
|
1307
|
-
"A `fetch` implementation was not provided and is not available in the global scope. Please provide a `fetchImpl` in the A2ATransportOptions. "
|
|
1308
|
-
);
|
|
1309
|
-
}
|
|
1310
|
-
async _sendRpcRequest(method, params, idOverride, options) {
|
|
1311
|
-
const requestId = idOverride ?? this.requestIdCounter++;
|
|
1312
|
-
const rpcRequest = {
|
|
1313
|
-
jsonrpc: "2.0",
|
|
1314
|
-
method,
|
|
1315
|
-
params,
|
|
1316
|
-
id: requestId
|
|
1317
|
-
};
|
|
1318
|
-
const httpResponse = await this._fetchRpc(rpcRequest, "application/json", options?.signal);
|
|
1319
|
-
if (!httpResponse.ok) {
|
|
1320
|
-
let errorBodyText = "(empty or non-JSON response)";
|
|
1321
|
-
let errorJson;
|
|
1322
|
-
try {
|
|
1323
|
-
errorBodyText = await httpResponse.text();
|
|
1324
|
-
errorJson = JSON.parse(errorBodyText);
|
|
1325
|
-
} catch (e) {
|
|
1326
|
-
throw new Error(
|
|
1327
|
-
`HTTP error for ${method}! Status: ${httpResponse.status} ${httpResponse.statusText}. Response: ${errorBodyText}`,
|
|
1328
|
-
{ cause: e }
|
|
1329
|
-
);
|
|
1330
|
-
}
|
|
1331
|
-
if (errorJson.jsonrpc && errorJson.error) {
|
|
1332
|
-
throw _JsonRpcTransport.mapToError(errorJson);
|
|
1333
|
-
} else {
|
|
1334
|
-
throw new Error(
|
|
1335
|
-
`HTTP error for ${method}! Status: ${httpResponse.status} ${httpResponse.statusText}. Response: ${errorBodyText}`
|
|
1336
|
-
);
|
|
1337
|
-
}
|
|
1338
|
-
}
|
|
1339
|
-
const rpcResponse = await httpResponse.json();
|
|
1340
|
-
if (rpcResponse.id !== requestId) {
|
|
1341
|
-
console.error(
|
|
1342
|
-
`CRITICAL: RPC response ID mismatch for method ${method}. Expected ${requestId}, got ${rpcResponse.id}.`
|
|
1343
|
-
);
|
|
1344
|
-
}
|
|
1345
|
-
if ("error" in rpcResponse) {
|
|
1346
|
-
throw _JsonRpcTransport.mapToError(rpcResponse);
|
|
1347
|
-
}
|
|
1348
|
-
return rpcResponse;
|
|
1349
|
-
}
|
|
1350
|
-
async _fetchRpc(rpcRequest, acceptHeader = "application/json", signal) {
|
|
1351
|
-
const requestInit = {
|
|
1352
|
-
method: "POST",
|
|
1353
|
-
headers: {
|
|
1354
|
-
"Content-Type": "application/json",
|
|
1355
|
-
Accept: acceptHeader
|
|
1356
|
-
},
|
|
1357
|
-
body: JSON.stringify(rpcRequest),
|
|
1358
|
-
signal
|
|
1359
|
-
};
|
|
1360
|
-
return this._fetch(this.endpoint, requestInit);
|
|
1361
|
-
}
|
|
1362
|
-
async *_sendStreamingRequest(method, params, options) {
|
|
1363
|
-
const clientRequestId = this.requestIdCounter++;
|
|
1364
|
-
const rpcRequest = {
|
|
1365
|
-
jsonrpc: "2.0",
|
|
1366
|
-
method,
|
|
1367
|
-
params,
|
|
1368
|
-
id: clientRequestId
|
|
1369
|
-
};
|
|
1370
|
-
const response = await this._fetchRpc(rpcRequest, "text/event-stream", options?.signal);
|
|
1371
|
-
if (!response.ok) {
|
|
1372
|
-
let errorBody = "";
|
|
1373
|
-
let errorJson;
|
|
1374
|
-
try {
|
|
1375
|
-
errorBody = await response.text();
|
|
1376
|
-
errorJson = JSON.parse(errorBody);
|
|
1377
|
-
} catch (e) {
|
|
1378
|
-
throw new Error(
|
|
1379
|
-
`HTTP error establishing stream for ${method}: ${response.status} ${response.statusText}. Response: ${errorBody || "(empty)"}`,
|
|
1380
|
-
{ cause: e }
|
|
1381
|
-
);
|
|
1382
|
-
}
|
|
1383
|
-
if (errorJson.error) {
|
|
1384
|
-
throw new Error(
|
|
1385
|
-
`HTTP error establishing stream for ${method}: ${response.status} ${response.statusText}. RPC Error: ${errorJson.error.message} (Code: ${errorJson.error.code})`
|
|
1386
|
-
);
|
|
1387
|
-
}
|
|
1388
|
-
throw new Error(
|
|
1389
|
-
`HTTP error establishing stream for ${method}: ${response.status} ${response.statusText}`
|
|
1390
|
-
);
|
|
1391
|
-
}
|
|
1392
|
-
if (!response.headers.get("Content-Type")?.startsWith("text/event-stream")) {
|
|
1393
|
-
throw new Error(
|
|
1394
|
-
`Invalid response Content-Type for SSE stream for ${method}. Expected 'text/event-stream'.`
|
|
1395
|
-
);
|
|
1396
|
-
}
|
|
1397
|
-
yield* this._parseA2ASseStream(response, clientRequestId);
|
|
1398
|
-
}
|
|
1399
|
-
async *_parseA2ASseStream(response, originalRequestId) {
|
|
1400
|
-
if (!response.body) {
|
|
1401
|
-
throw new Error("SSE response body is undefined. Cannot read stream.");
|
|
1402
|
-
}
|
|
1403
|
-
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
|
|
1404
|
-
let buffer = "";
|
|
1405
|
-
let eventDataBuffer = "";
|
|
1406
|
-
try {
|
|
1407
|
-
while (true) {
|
|
1408
|
-
const { done, value } = await reader.read();
|
|
1409
|
-
if (done) {
|
|
1410
|
-
if (eventDataBuffer.trim()) {
|
|
1411
|
-
const result = this._processSseEventData(
|
|
1412
|
-
eventDataBuffer,
|
|
1413
|
-
originalRequestId
|
|
1414
|
-
);
|
|
1415
|
-
yield result;
|
|
1416
|
-
}
|
|
1417
|
-
break;
|
|
1418
|
-
}
|
|
1419
|
-
buffer += value;
|
|
1420
|
-
let lineEndIndex;
|
|
1421
|
-
while ((lineEndIndex = buffer.indexOf("\n")) >= 0) {
|
|
1422
|
-
const line = buffer.substring(0, lineEndIndex).trim();
|
|
1423
|
-
buffer = buffer.substring(lineEndIndex + 1);
|
|
1424
|
-
if (line === "") {
|
|
1425
|
-
if (eventDataBuffer) {
|
|
1426
|
-
const result = this._processSseEventData(
|
|
1427
|
-
eventDataBuffer,
|
|
1428
|
-
originalRequestId
|
|
1429
|
-
);
|
|
1430
|
-
yield result;
|
|
1431
|
-
eventDataBuffer = "";
|
|
1432
|
-
}
|
|
1433
|
-
} else if (line.startsWith("data:")) {
|
|
1434
|
-
eventDataBuffer += line.substring(5).trimStart() + "\n";
|
|
1435
|
-
}
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
} catch (error) {
|
|
1439
|
-
console.error(
|
|
1440
|
-
"Error reading or parsing SSE stream:",
|
|
1441
|
-
error instanceof Error && error.message || "Error unknown"
|
|
1442
|
-
);
|
|
1443
|
-
throw error;
|
|
1444
|
-
} finally {
|
|
1445
|
-
reader.releaseLock();
|
|
1446
|
-
}
|
|
1447
|
-
}
|
|
1448
|
-
_processSseEventData(jsonData, originalRequestId) {
|
|
1449
|
-
if (!jsonData.trim()) {
|
|
1450
|
-
throw new Error("Attempted to process empty SSE event data.");
|
|
1451
|
-
}
|
|
1452
|
-
try {
|
|
1453
|
-
const sseJsonRpcResponse = JSON.parse(jsonData.replace(/\n$/, ""));
|
|
1454
|
-
const a2aStreamResponse = sseJsonRpcResponse;
|
|
1455
|
-
if (a2aStreamResponse.id !== originalRequestId) {
|
|
1456
|
-
console.warn(
|
|
1457
|
-
`SSE Event's JSON-RPC response ID mismatch. Client request ID: ${originalRequestId}, event response ID: ${a2aStreamResponse.id}.`
|
|
1458
|
-
);
|
|
1459
|
-
}
|
|
1460
|
-
if ("error" in a2aStreamResponse) {
|
|
1461
|
-
const err = a2aStreamResponse.error;
|
|
1462
|
-
throw new Error(
|
|
1463
|
-
`SSE event contained an error: ${err.message} (Code: ${err.code}) Data: ${JSON.stringify(err.data || {})}`
|
|
1464
|
-
);
|
|
1465
|
-
}
|
|
1466
|
-
if (!("result" in a2aStreamResponse) || typeof a2aStreamResponse.result === "undefined") {
|
|
1467
|
-
throw new Error(`SSE event JSON-RPC response is missing 'result' field. Data: ${jsonData}`);
|
|
1468
|
-
}
|
|
1469
|
-
return a2aStreamResponse.result;
|
|
1470
|
-
} catch (e) {
|
|
1471
|
-
if (e instanceof Error && (e.message.startsWith("SSE event contained an error") || e.message.startsWith("SSE event JSON-RPC response is missing 'result' field"))) {
|
|
1472
|
-
throw e;
|
|
1473
|
-
}
|
|
1474
|
-
console.error(
|
|
1475
|
-
"Failed to parse SSE event data string or unexpected JSON-RPC structure:",
|
|
1476
|
-
jsonData,
|
|
1477
|
-
e
|
|
1478
|
-
);
|
|
1479
|
-
throw new Error(
|
|
1480
|
-
`Failed to parse SSE event data: "${jsonData.substring(0, 100)}...". Original error: ${e instanceof Error && e.message || "Unknown error"}`
|
|
1481
|
-
);
|
|
1482
|
-
}
|
|
1483
|
-
}
|
|
1484
|
-
static mapToError(response) {
|
|
1485
|
-
switch (response.error.code) {
|
|
1486
|
-
case -32001:
|
|
1487
|
-
return new TaskNotFoundJSONRPCError(response);
|
|
1488
|
-
case -32002:
|
|
1489
|
-
return new TaskNotCancelableJSONRPCError(response);
|
|
1490
|
-
case -32003:
|
|
1491
|
-
return new PushNotificationNotSupportedJSONRPCError(response);
|
|
1492
|
-
case -32004:
|
|
1493
|
-
return new UnsupportedOperationJSONRPCError(response);
|
|
1494
|
-
case -32005:
|
|
1495
|
-
return new ContentTypeNotSupportedJSONRPCError(response);
|
|
1496
|
-
case -32006:
|
|
1497
|
-
return new InvalidAgentResponseJSONRPCError(response);
|
|
1498
|
-
case -32007:
|
|
1499
|
-
return new AuthenticatedExtendedCardNotConfiguredJSONRPCError(response);
|
|
1500
|
-
default:
|
|
1501
|
-
return new JSONRPCTransportError(response);
|
|
1502
|
-
}
|
|
1503
|
-
}
|
|
1504
|
-
};
|
|
1505
|
-
var JSONRPCTransportError = class extends Error {
|
|
1506
|
-
constructor(errorResponse) {
|
|
1507
|
-
super(
|
|
1508
|
-
`JSON-RPC error: ${errorResponse.error.message} (Code: ${errorResponse.error.code}) Data: ${JSON.stringify(errorResponse.error.data || {})}`
|
|
1509
|
-
);
|
|
1510
|
-
this.errorResponse = errorResponse;
|
|
1511
|
-
}
|
|
1512
|
-
};
|
|
1513
|
-
var TaskNotFoundJSONRPCError = class extends TaskNotFoundError {
|
|
1514
|
-
constructor(errorResponse) {
|
|
1515
|
-
super();
|
|
1516
|
-
this.errorResponse = errorResponse;
|
|
1517
|
-
}
|
|
1518
|
-
};
|
|
1519
|
-
var TaskNotCancelableJSONRPCError = class extends TaskNotCancelableError {
|
|
1520
|
-
constructor(errorResponse) {
|
|
1521
|
-
super();
|
|
1522
|
-
this.errorResponse = errorResponse;
|
|
1523
|
-
}
|
|
1524
|
-
};
|
|
1525
|
-
var PushNotificationNotSupportedJSONRPCError = class extends PushNotificationNotSupportedError {
|
|
1526
|
-
constructor(errorResponse) {
|
|
1527
|
-
super();
|
|
1528
|
-
this.errorResponse = errorResponse;
|
|
1529
|
-
}
|
|
1530
|
-
};
|
|
1531
|
-
var UnsupportedOperationJSONRPCError = class extends UnsupportedOperationError {
|
|
1532
|
-
constructor(errorResponse) {
|
|
1533
|
-
super();
|
|
1534
|
-
this.errorResponse = errorResponse;
|
|
1535
|
-
}
|
|
1536
|
-
};
|
|
1537
|
-
var ContentTypeNotSupportedJSONRPCError = class extends ContentTypeNotSupportedError {
|
|
1538
|
-
constructor(errorResponse) {
|
|
1539
|
-
super();
|
|
1540
|
-
this.errorResponse = errorResponse;
|
|
1541
|
-
}
|
|
1542
|
-
};
|
|
1543
|
-
var InvalidAgentResponseJSONRPCError = class extends InvalidAgentResponseError {
|
|
1544
|
-
constructor(errorResponse) {
|
|
1545
|
-
super();
|
|
1546
|
-
this.errorResponse = errorResponse;
|
|
1547
|
-
}
|
|
1548
|
-
};
|
|
1549
|
-
var AuthenticatedExtendedCardNotConfiguredJSONRPCError = class extends AuthenticatedExtendedCardNotConfiguredError {
|
|
1550
|
-
constructor(errorResponse) {
|
|
1551
|
-
super();
|
|
1552
|
-
this.errorResponse = errorResponse;
|
|
1553
|
-
}
|
|
1554
|
-
};
|
|
1555
|
-
|
|
1556
|
-
// src/client/client.ts
|
|
1557
|
-
var A2AClient = class _A2AClient {
|
|
1558
|
-
static emptyOptions = void 0;
|
|
1559
|
-
agentCardPromise;
|
|
1560
|
-
customFetchImpl;
|
|
1561
|
-
serviceEndpointUrl;
|
|
1562
|
-
// To be populated from AgentCard after fetchin
|
|
1563
|
-
// A2AClient is built around JSON-RPC types, so it will only support JSON-RPC transport, new client with transport agnostic interface is going to be created for multi-transport.
|
|
1564
|
-
// New transport abstraction isn't going to expose individual transport specific fields, so to keep returning JSON-RPC IDs here for compatibility,
|
|
1565
|
-
// keep counter here and pass it to JsonRpcTransport via an optional idOverride parameter (which is not visible via transport-agnostic A2ATransport interface).
|
|
1566
|
-
transport;
|
|
1567
|
-
requestIdCounter = 1;
|
|
1568
|
-
/**
|
|
1569
|
-
* Constructs an A2AClient instance from an AgentCard.
|
|
1570
|
-
* @param agentCard The AgentCard object.
|
|
1571
|
-
* @param options Optional. The options for the A2AClient including the fetch/auth implementation.
|
|
1572
|
-
*/
|
|
1573
|
-
constructor(agentCard, options) {
|
|
1574
|
-
this.customFetchImpl = options?.fetchImpl;
|
|
1575
|
-
if (typeof agentCard === "string") {
|
|
1576
|
-
console.warn(
|
|
1577
|
-
"Warning: Constructing A2AClient with a URL is deprecated. Please use A2AClient.fromCardUrl() instead."
|
|
1578
|
-
);
|
|
1579
|
-
this.agentCardPromise = this._fetchAndCacheAgentCard(agentCard, options?.agentCardPath);
|
|
1580
|
-
} else {
|
|
1581
|
-
if (!agentCard.url) {
|
|
1582
|
-
throw new Error(
|
|
1583
|
-
"Provided Agent Card does not contain a valid 'url' for the service endpoint."
|
|
1584
|
-
);
|
|
1585
|
-
}
|
|
1586
|
-
this.serviceEndpointUrl = agentCard.url;
|
|
1587
|
-
this.agentCardPromise = Promise.resolve(agentCard);
|
|
1588
|
-
}
|
|
1589
|
-
}
|
|
1590
|
-
/**
|
|
1591
|
-
* Dynamically resolves the fetch implementation to use for requests.
|
|
1592
|
-
* Prefers a custom implementation if provided, otherwise falls back to the global fetch.
|
|
1593
|
-
* @returns The fetch implementation.
|
|
1594
|
-
* @param args Arguments to pass to the fetch implementation.
|
|
1595
|
-
* @throws If no fetch implementation is available.
|
|
1596
|
-
*/
|
|
1597
|
-
_fetch(...args) {
|
|
1598
|
-
if (this.customFetchImpl) {
|
|
1599
|
-
return this.customFetchImpl(...args);
|
|
1600
|
-
}
|
|
1601
|
-
if (typeof fetch === "function") {
|
|
1602
|
-
return fetch(...args);
|
|
1603
|
-
}
|
|
1604
|
-
throw new Error(
|
|
1605
|
-
"A `fetch` implementation was not provided and is not available in the global scope. Please provide a `fetchImpl` in the A2AClientOptions. For earlier Node.js versions (pre-v18), you can use a library like `node-fetch`."
|
|
1606
|
-
);
|
|
1607
|
-
}
|
|
1608
|
-
/**
|
|
1609
|
-
* Creates an A2AClient instance by fetching the AgentCard from a URL then constructing the A2AClient.
|
|
1610
|
-
* @param agentCardUrl The URL of the agent card.
|
|
1611
|
-
* @param options Optional. The options for the A2AClient including the fetch/auth implementation.
|
|
1612
|
-
* @returns A Promise that resolves to a new A2AClient instance.
|
|
1613
|
-
*/
|
|
1614
|
-
static async fromCardUrl(agentCardUrl, options) {
|
|
1615
|
-
const fetchImpl = options?.fetchImpl;
|
|
1616
|
-
const requestInit = {
|
|
1617
|
-
headers: { Accept: "application/json" }
|
|
1618
|
-
};
|
|
1619
|
-
let response;
|
|
1620
|
-
if (fetchImpl) {
|
|
1621
|
-
response = await fetchImpl(agentCardUrl, requestInit);
|
|
1622
|
-
} else if (typeof fetch === "function") {
|
|
1623
|
-
response = await fetch(agentCardUrl, requestInit);
|
|
1624
|
-
} else {
|
|
1625
|
-
throw new Error(
|
|
1626
|
-
"A `fetch` implementation was not provided and is not available in the global scope. Please provide a `fetchImpl` in the A2AClientOptions. For earlier Node.js versions (pre-v18), you can use a library like `node-fetch`."
|
|
1627
|
-
);
|
|
1628
|
-
}
|
|
1629
|
-
if (!response.ok) {
|
|
1630
|
-
throw new Error(
|
|
1631
|
-
`Failed to fetch Agent Card from ${agentCardUrl}: ${response.status} ${response.statusText}`
|
|
1632
|
-
);
|
|
1633
|
-
}
|
|
1634
|
-
let agentCard;
|
|
1635
|
-
try {
|
|
1636
|
-
agentCard = await response.json();
|
|
1637
|
-
} catch (error) {
|
|
1638
|
-
console.error("Failed to parse Agent Card JSON:", error);
|
|
1639
|
-
throw new Error(
|
|
1640
|
-
`Failed to parse Agent Card JSON from ${agentCardUrl}. Original error: ${error.message}`
|
|
1641
|
-
);
|
|
1642
|
-
}
|
|
1643
|
-
return new _A2AClient(agentCard, options);
|
|
1644
|
-
}
|
|
1645
|
-
/**
|
|
1646
|
-
* Sends a message to the agent.
|
|
1647
|
-
* The behavior (blocking/non-blocking) and push notification configuration
|
|
1648
|
-
* are specified within the `params.configuration` object.
|
|
1649
|
-
* Optionally, `params.message.contextId` or `params.message.taskId` can be provided.
|
|
1650
|
-
* @param params The parameters for sending the message, including the message content and configuration.
|
|
1651
|
-
* @returns A Promise resolving to SendMessageResponse, which can be a Message, Task, or an error.
|
|
1652
|
-
*/
|
|
1653
|
-
async sendMessage(params) {
|
|
1654
|
-
return await this.invokeJsonRpc(
|
|
1655
|
-
(t, p, id) => t.sendMessage(p, _A2AClient.emptyOptions, id),
|
|
1656
|
-
params
|
|
1657
|
-
);
|
|
1658
|
-
}
|
|
32
|
+
// src/extensions.ts
|
|
33
|
+
var Extensions = {
|
|
1659
34
|
/**
|
|
1660
|
-
*
|
|
1661
|
-
*
|
|
1662
|
-
* Optionally, `params.message.contextId` or `params.message.taskId` can be provided.
|
|
1663
|
-
* Requires the agent to support streaming (`capabilities.streaming: true` in AgentCard).
|
|
1664
|
-
* @param params The parameters for sending the message.
|
|
1665
|
-
* @returns An AsyncGenerator yielding A2AStreamEventData (Message, Task, TaskStatusUpdateEvent, or TaskArtifactUpdateEvent).
|
|
1666
|
-
* The generator throws an error if streaming is not supported or if an HTTP/SSE error occurs.
|
|
35
|
+
* Creates new {@link Extensions} from `current` and `additional`.
|
|
36
|
+
* If `current` already contains `additional` it is returned unmodified.
|
|
1667
37
|
*/
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
throw new Error(
|
|
1672
|
-
"Agent does not support streaming (AgentCard.capabilities.streaming is not true)."
|
|
1673
|
-
);
|
|
38
|
+
createFrom: (current, additional) => {
|
|
39
|
+
if (current?.includes(additional)) {
|
|
40
|
+
return current;
|
|
1674
41
|
}
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
}
|
|
42
|
+
return [...current ?? [], additional];
|
|
43
|
+
},
|
|
1678
44
|
/**
|
|
1679
|
-
*
|
|
1680
|
-
*
|
|
1681
|
-
*
|
|
1682
|
-
* @returns A Promise resolving to SetTaskPushNotificationConfigResponse.
|
|
45
|
+
* Creates {@link Extensions} from comma separated extensions identifiers as per
|
|
46
|
+
* https://a2a-protocol.org/latest/specification/#326-service-parameters.
|
|
47
|
+
* Parses the output of `toServiceParameter`.
|
|
1683
48
|
*/
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
throw new Error(
|
|
1688
|
-
"Agent does not support push notifications (AgentCard.capabilities.pushNotifications is not true)."
|
|
1689
|
-
);
|
|
49
|
+
parseServiceParameter: (value) => {
|
|
50
|
+
if (!value) {
|
|
51
|
+
return [];
|
|
1690
52
|
}
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
/**
|
|
1694
|
-
* Gets the push notification configuration for a given task.
|
|
1695
|
-
* @param params Parameters containing the taskId.
|
|
1696
|
-
* @returns A Promise resolving to GetTaskPushNotificationConfigResponse.
|
|
1697
|
-
*/
|
|
1698
|
-
async getTaskPushNotificationConfig(params) {
|
|
1699
|
-
return await this.invokeJsonRpc(
|
|
1700
|
-
(t, p, id) => t.getTaskPushNotificationConfig(p, _A2AClient.emptyOptions, id),
|
|
1701
|
-
params
|
|
53
|
+
const unique = new Set(
|
|
54
|
+
value.split(",").map((ext) => ext.trim()).filter((ext) => ext.length > 0)
|
|
1702
55
|
);
|
|
1703
|
-
|
|
56
|
+
return Array.from(unique);
|
|
57
|
+
},
|
|
1704
58
|
/**
|
|
1705
|
-
*
|
|
1706
|
-
*
|
|
1707
|
-
* @returns A Promise resolving to ListTaskPushNotificationConfigResponse.
|
|
59
|
+
* Converts {@link Extensions} to comma separated extensions identifiers as per
|
|
60
|
+
* https://a2a-protocol.org/latest/specification/#326-service-parameters.
|
|
1708
61
|
*/
|
|
1709
|
-
|
|
1710
|
-
return
|
|
1711
|
-
}
|
|
1712
|
-
/**
|
|
1713
|
-
* Deletes the push notification configuration for a given task.
|
|
1714
|
-
* @param params Parameters containing the taskId and push notification configuration ID.
|
|
1715
|
-
* @returns A Promise resolving to DeleteTaskPushNotificationConfigResponse.
|
|
1716
|
-
*/
|
|
1717
|
-
async deleteTaskPushNotificationConfig(params) {
|
|
1718
|
-
return await this.invokeJsonRpc((t, p, id) => t.deleteTaskPushNotificationConfig(p, _A2AClient.emptyOptions, id), params);
|
|
1719
|
-
}
|
|
1720
|
-
/**
|
|
1721
|
-
* Retrieves a task by its ID.
|
|
1722
|
-
* @param params Parameters containing the taskId and optional historyLength.
|
|
1723
|
-
* @returns A Promise resolving to GetTaskResponse, which contains the Task object or an error.
|
|
1724
|
-
*/
|
|
1725
|
-
async getTask(params) {
|
|
1726
|
-
return await this.invokeJsonRpc(
|
|
1727
|
-
(t, p, id) => t.getTask(p, _A2AClient.emptyOptions, id),
|
|
1728
|
-
params
|
|
1729
|
-
);
|
|
1730
|
-
}
|
|
1731
|
-
/**
|
|
1732
|
-
* Cancels a task by its ID.
|
|
1733
|
-
* @param params Parameters containing the taskId.
|
|
1734
|
-
* @returns A Promise resolving to CancelTaskResponse, which contains the updated Task object or an error.
|
|
1735
|
-
*/
|
|
1736
|
-
async cancelTask(params) {
|
|
1737
|
-
return await this.invokeJsonRpc(
|
|
1738
|
-
(t, p, id) => t.cancelTask(p, _A2AClient.emptyOptions, id),
|
|
1739
|
-
params
|
|
1740
|
-
);
|
|
1741
|
-
}
|
|
1742
|
-
/**
|
|
1743
|
-
* @template TExtensionParams The type of parameters for the custom extension method.
|
|
1744
|
-
* @template TExtensionResponse The type of response expected from the custom extension method.
|
|
1745
|
-
* This should extend JSONRPCResponse. This ensures the extension response is still a valid A2A response.
|
|
1746
|
-
* @param method Custom JSON-RPC method defined in the AgentCard's extensions.
|
|
1747
|
-
* @param params Extension paramters defined in the AgentCard's extensions.
|
|
1748
|
-
* @returns A Promise that resolves to the RPC response.
|
|
1749
|
-
*/
|
|
1750
|
-
async callExtensionMethod(method, params) {
|
|
1751
|
-
const transport = await this._getOrCreateTransport();
|
|
1752
|
-
try {
|
|
1753
|
-
return await transport.callExtensionMethod(
|
|
1754
|
-
method,
|
|
1755
|
-
params,
|
|
1756
|
-
this.requestIdCounter++
|
|
1757
|
-
);
|
|
1758
|
-
} catch (e) {
|
|
1759
|
-
const errorResponse = extractJSONRPCError(e);
|
|
1760
|
-
if (errorResponse) {
|
|
1761
|
-
return errorResponse;
|
|
1762
|
-
}
|
|
1763
|
-
throw e;
|
|
1764
|
-
}
|
|
1765
|
-
}
|
|
1766
|
-
/**
|
|
1767
|
-
* Resubscribes to a task's event stream using Server-Sent Events (SSE).
|
|
1768
|
-
* This is used if a previous SSE connection for an active task was broken.
|
|
1769
|
-
* Requires the agent to support streaming (`capabilities.streaming: true` in AgentCard).
|
|
1770
|
-
* @param params Parameters containing the taskId.
|
|
1771
|
-
* @returns An AsyncGenerator yielding A2AStreamEventData (Message, Task, TaskStatusUpdateEvent, or TaskArtifactUpdateEvent).
|
|
1772
|
-
*/
|
|
1773
|
-
async *resubscribeTask(params) {
|
|
1774
|
-
const agentCard = await this.agentCardPromise;
|
|
1775
|
-
if (!agentCard.capabilities?.streaming) {
|
|
1776
|
-
throw new Error("Agent does not support streaming (required for tasks/resubscribe).");
|
|
1777
|
-
}
|
|
1778
|
-
const transport = await this._getOrCreateTransport();
|
|
1779
|
-
yield* transport.resubscribeTask(params);
|
|
1780
|
-
}
|
|
1781
|
-
////////////////////////////////////////////////////////////////////////////////
|
|
1782
|
-
// Functions used to support old A2AClient Constructor to be deprecated soon
|
|
1783
|
-
// TODOs:
|
|
1784
|
-
// * remove `agentCardPromise`, and just use agentCard initialized
|
|
1785
|
-
// * _getServiceEndpoint can be made synchronous or deleted and accessed via
|
|
1786
|
-
// agentCard.url
|
|
1787
|
-
// * getAgentCard changed to this.agentCard
|
|
1788
|
-
// * delete resolveAgentCardUrl(), _fetchAndCacheAgentCard(),
|
|
1789
|
-
// agentCardPath from A2AClientOptions
|
|
1790
|
-
// * delete _getOrCreateTransport
|
|
1791
|
-
////////////////////////////////////////////////////////////////////////////////
|
|
1792
|
-
async _getOrCreateTransport() {
|
|
1793
|
-
if (this.transport) {
|
|
1794
|
-
return this.transport;
|
|
1795
|
-
}
|
|
1796
|
-
const endpoint = await this._getServiceEndpoint();
|
|
1797
|
-
this.transport = new JsonRpcTransport({ fetchImpl: this.customFetchImpl, endpoint });
|
|
1798
|
-
return this.transport;
|
|
1799
|
-
}
|
|
1800
|
-
/**
|
|
1801
|
-
* Fetches the Agent Card from the agent's well-known URI and caches its service endpoint URL.
|
|
1802
|
-
* This method is called by the constructor.
|
|
1803
|
-
* @param agentBaseUrl The base URL of the A2A agent (e.g., https://agent.example.com)
|
|
1804
|
-
* @param agentCardPath path to the agent card, defaults to .well-known/agent-card.json
|
|
1805
|
-
* @returns A Promise that resolves to the AgentCard.
|
|
1806
|
-
*/
|
|
1807
|
-
async _fetchAndCacheAgentCard(agentBaseUrl, agentCardPath) {
|
|
1808
|
-
try {
|
|
1809
|
-
const agentCardUrl = this.resolveAgentCardUrl(agentBaseUrl, agentCardPath);
|
|
1810
|
-
const response = await this._fetch(agentCardUrl, {
|
|
1811
|
-
headers: { Accept: "application/json" }
|
|
1812
|
-
});
|
|
1813
|
-
if (!response.ok) {
|
|
1814
|
-
throw new Error(
|
|
1815
|
-
`Failed to fetch Agent Card from ${agentCardUrl}: ${response.status} ${response.statusText}`
|
|
1816
|
-
);
|
|
1817
|
-
}
|
|
1818
|
-
const agentCard = await response.json();
|
|
1819
|
-
if (!agentCard.url) {
|
|
1820
|
-
throw new Error(
|
|
1821
|
-
"Fetched Agent Card does not contain a valid 'url' for the service endpoint."
|
|
1822
|
-
);
|
|
1823
|
-
}
|
|
1824
|
-
this.serviceEndpointUrl = agentCard.url;
|
|
1825
|
-
return agentCard;
|
|
1826
|
-
} catch (error) {
|
|
1827
|
-
console.error("Error fetching or parsing Agent Card:", error);
|
|
1828
|
-
throw error;
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1831
|
-
/**
|
|
1832
|
-
* Retrieves the Agent Card.
|
|
1833
|
-
* If an `agentBaseUrl` is provided, it fetches the card from that specific URL.
|
|
1834
|
-
* Otherwise, it returns the card fetched and cached during client construction.
|
|
1835
|
-
* @param agentBaseUrl Optional. The base URL of the agent to fetch the card from.
|
|
1836
|
-
* @param agentCardPath path to the agent card, defaults to .well-known/agent-card.json
|
|
1837
|
-
* If provided, this will fetch a new card, not use the cached one from the constructor's URL.
|
|
1838
|
-
* @returns A Promise that resolves to the AgentCard.
|
|
1839
|
-
*/
|
|
1840
|
-
async getAgentCard(agentBaseUrl, agentCardPath) {
|
|
1841
|
-
if (agentBaseUrl) {
|
|
1842
|
-
const agentCardUrl = this.resolveAgentCardUrl(agentBaseUrl, agentCardPath);
|
|
1843
|
-
const response = await this._fetch(agentCardUrl, {
|
|
1844
|
-
headers: { Accept: "application/json" }
|
|
1845
|
-
});
|
|
1846
|
-
if (!response.ok) {
|
|
1847
|
-
throw new Error(
|
|
1848
|
-
`Failed to fetch Agent Card from ${agentCardUrl}: ${response.status} ${response.statusText}`
|
|
1849
|
-
);
|
|
1850
|
-
}
|
|
1851
|
-
return await response.json();
|
|
1852
|
-
}
|
|
1853
|
-
return this.agentCardPromise;
|
|
1854
|
-
}
|
|
1855
|
-
/**
|
|
1856
|
-
* Determines the agent card URL based on the agent URL.
|
|
1857
|
-
* @param agentBaseUrl The agent URL.
|
|
1858
|
-
* @param agentCardPath Optional relative path to the agent card, defaults to .well-known/agent-card.json
|
|
1859
|
-
*/
|
|
1860
|
-
resolveAgentCardUrl(agentBaseUrl, agentCardPath = AGENT_CARD_PATH) {
|
|
1861
|
-
return `${agentBaseUrl.replace(/\/$/, "")}/${agentCardPath.replace(/^\//, "")}`;
|
|
1862
|
-
}
|
|
1863
|
-
/**
|
|
1864
|
-
* Gets the RPC service endpoint URL. Ensures the agent card has been fetched first.
|
|
1865
|
-
* @returns A Promise that resolves to the service endpoint URL string.
|
|
1866
|
-
*/
|
|
1867
|
-
async _getServiceEndpoint() {
|
|
1868
|
-
if (this.serviceEndpointUrl) {
|
|
1869
|
-
return this.serviceEndpointUrl;
|
|
1870
|
-
}
|
|
1871
|
-
await this.agentCardPromise;
|
|
1872
|
-
if (!this.serviceEndpointUrl) {
|
|
1873
|
-
throw new Error(
|
|
1874
|
-
"Agent Card URL for RPC endpoint is not available. Fetching might have failed."
|
|
1875
|
-
);
|
|
1876
|
-
}
|
|
1877
|
-
return this.serviceEndpointUrl;
|
|
1878
|
-
}
|
|
1879
|
-
async invokeJsonRpc(caller, params) {
|
|
1880
|
-
const transport = await this._getOrCreateTransport();
|
|
1881
|
-
const requestId = this.requestIdCounter++;
|
|
1882
|
-
try {
|
|
1883
|
-
const result = await caller(transport, params, requestId);
|
|
1884
|
-
return {
|
|
1885
|
-
id: requestId,
|
|
1886
|
-
jsonrpc: "2.0",
|
|
1887
|
-
result: result ?? null
|
|
1888
|
-
// JSON-RPC requires result property on success, it will be null for "void" methods.
|
|
1889
|
-
};
|
|
1890
|
-
} catch (e) {
|
|
1891
|
-
const errorResponse = extractJSONRPCError(e);
|
|
1892
|
-
if (errorResponse) {
|
|
1893
|
-
return errorResponse;
|
|
1894
|
-
}
|
|
1895
|
-
throw e;
|
|
1896
|
-
}
|
|
1897
|
-
}
|
|
1898
|
-
};
|
|
1899
|
-
function extractJSONRPCError(error) {
|
|
1900
|
-
if (error instanceof Object && "errorResponse" in error && error.errorResponse instanceof Object && "jsonrpc" in error.errorResponse && error.errorResponse.jsonrpc === "2.0" && "error" in error.errorResponse && error.errorResponse.error !== null) {
|
|
1901
|
-
return error.errorResponse;
|
|
1902
|
-
} else {
|
|
1903
|
-
return void 0;
|
|
1904
|
-
}
|
|
1905
|
-
}
|
|
1906
|
-
|
|
1907
|
-
// src/client/auth-handler.ts
|
|
1908
|
-
function createAuthenticatingFetchWithRetry(fetchImpl, authHandler) {
|
|
1909
|
-
async function authFetch(url, init) {
|
|
1910
|
-
const authHeaders = await authHandler.headers() || {};
|
|
1911
|
-
const mergedInit = {
|
|
1912
|
-
...init || {},
|
|
1913
|
-
headers: {
|
|
1914
|
-
...authHeaders,
|
|
1915
|
-
...init?.headers || {}
|
|
1916
|
-
}
|
|
1917
|
-
};
|
|
1918
|
-
let response = await fetchImpl(url, mergedInit);
|
|
1919
|
-
const updatedHeaders = await authHandler.shouldRetryWithHeaders(mergedInit, response);
|
|
1920
|
-
if (updatedHeaders) {
|
|
1921
|
-
const retryInit = {
|
|
1922
|
-
...init || {},
|
|
1923
|
-
headers: {
|
|
1924
|
-
...updatedHeaders,
|
|
1925
|
-
...init?.headers || {}
|
|
1926
|
-
}
|
|
1927
|
-
};
|
|
1928
|
-
response = await fetchImpl(url, retryInit);
|
|
1929
|
-
if (response.ok && authHandler.onSuccessfulRetry) {
|
|
1930
|
-
await authHandler.onSuccessfulRetry(updatedHeaders);
|
|
1931
|
-
}
|
|
1932
|
-
}
|
|
1933
|
-
return response;
|
|
1934
|
-
}
|
|
1935
|
-
Object.setPrototypeOf(authFetch, Object.getPrototypeOf(fetchImpl));
|
|
1936
|
-
Object.defineProperties(authFetch, Object.getOwnPropertyDescriptors(fetchImpl));
|
|
1937
|
-
return authFetch;
|
|
1938
|
-
}
|
|
1939
|
-
|
|
1940
|
-
// src/server/express/a2a_express_app.ts
|
|
1941
|
-
var import_express3 = __toESM(require("express"));
|
|
1942
|
-
|
|
1943
|
-
// src/server/express/json_rpc_handler.ts
|
|
1944
|
-
var import_express = __toESM(require("express"));
|
|
1945
|
-
|
|
1946
|
-
// src/server/authentication/user.ts
|
|
1947
|
-
var UnauthenticatedUser = class {
|
|
1948
|
-
get isAuthenticated() {
|
|
1949
|
-
return false;
|
|
1950
|
-
}
|
|
1951
|
-
get userName() {
|
|
1952
|
-
return "";
|
|
1953
|
-
}
|
|
1954
|
-
};
|
|
1955
|
-
|
|
1956
|
-
// src/server/express/json_rpc_handler.ts
|
|
1957
|
-
function jsonRpcHandler(options) {
|
|
1958
|
-
const jsonRpcTransportHandler = new JsonRpcTransportHandler(options.requestHandler);
|
|
1959
|
-
const router = import_express.default.Router();
|
|
1960
|
-
router.use(import_express.default.json(), jsonErrorHandler);
|
|
1961
|
-
router.post("/", async (req, res) => {
|
|
1962
|
-
try {
|
|
1963
|
-
const user = await options.userBuilder(req);
|
|
1964
|
-
const context = new ServerCallContext(
|
|
1965
|
-
getRequestedExtensions(req.header(HTTP_EXTENSION_HEADER)),
|
|
1966
|
-
user ?? new UnauthenticatedUser()
|
|
1967
|
-
);
|
|
1968
|
-
const rpcResponseOrStream = await jsonRpcTransportHandler.handle(req.body, context);
|
|
1969
|
-
if (context.activatedExtensions) {
|
|
1970
|
-
res.setHeader(HTTP_EXTENSION_HEADER, Array.from(context.activatedExtensions));
|
|
1971
|
-
}
|
|
1972
|
-
if (typeof rpcResponseOrStream?.[Symbol.asyncIterator] === "function") {
|
|
1973
|
-
const stream = rpcResponseOrStream;
|
|
1974
|
-
res.setHeader("Content-Type", "text/event-stream");
|
|
1975
|
-
res.setHeader("Cache-Control", "no-cache");
|
|
1976
|
-
res.setHeader("Connection", "keep-alive");
|
|
1977
|
-
res.flushHeaders();
|
|
1978
|
-
try {
|
|
1979
|
-
for await (const event of stream) {
|
|
1980
|
-
res.write(`id: ${(/* @__PURE__ */ new Date()).getTime()}
|
|
1981
|
-
`);
|
|
1982
|
-
res.write(`data: ${JSON.stringify(event)}
|
|
1983
|
-
|
|
1984
|
-
`);
|
|
1985
|
-
}
|
|
1986
|
-
} catch (streamError) {
|
|
1987
|
-
console.error(`Error during SSE streaming (request ${req.body?.id}):`, streamError);
|
|
1988
|
-
let a2aError;
|
|
1989
|
-
if (streamError instanceof A2AError) {
|
|
1990
|
-
a2aError = streamError;
|
|
1991
|
-
} else {
|
|
1992
|
-
a2aError = A2AError.internalError(
|
|
1993
|
-
streamError instanceof Error && streamError.message || "Streaming error."
|
|
1994
|
-
);
|
|
1995
|
-
}
|
|
1996
|
-
const errorResponse = {
|
|
1997
|
-
jsonrpc: "2.0",
|
|
1998
|
-
id: req.body?.id || null,
|
|
1999
|
-
// Use original request ID if available
|
|
2000
|
-
error: a2aError.toJSONRPCError()
|
|
2001
|
-
};
|
|
2002
|
-
if (!res.headersSent) {
|
|
2003
|
-
res.status(500).json(errorResponse);
|
|
2004
|
-
} else {
|
|
2005
|
-
res.write(`id: ${(/* @__PURE__ */ new Date()).getTime()}
|
|
2006
|
-
`);
|
|
2007
|
-
res.write(`event: error
|
|
2008
|
-
`);
|
|
2009
|
-
res.write(`data: ${JSON.stringify(errorResponse)}
|
|
2010
|
-
|
|
2011
|
-
`);
|
|
2012
|
-
}
|
|
2013
|
-
} finally {
|
|
2014
|
-
if (!res.writableEnded) {
|
|
2015
|
-
res.end();
|
|
2016
|
-
}
|
|
2017
|
-
}
|
|
2018
|
-
} else {
|
|
2019
|
-
const rpcResponse = rpcResponseOrStream;
|
|
2020
|
-
res.status(200).json(rpcResponse);
|
|
2021
|
-
}
|
|
2022
|
-
} catch (error) {
|
|
2023
|
-
console.error("Unhandled error in JSON-RPC POST handler:", error);
|
|
2024
|
-
const a2aError = error instanceof A2AError ? error : A2AError.internalError("General processing error.");
|
|
2025
|
-
const errorResponse = {
|
|
2026
|
-
jsonrpc: "2.0",
|
|
2027
|
-
id: req.body?.id || null,
|
|
2028
|
-
error: a2aError.toJSONRPCError()
|
|
2029
|
-
};
|
|
2030
|
-
if (!res.headersSent) {
|
|
2031
|
-
res.status(500).json(errorResponse);
|
|
2032
|
-
} else if (!res.writableEnded) {
|
|
2033
|
-
res.end();
|
|
2034
|
-
}
|
|
2035
|
-
}
|
|
2036
|
-
});
|
|
2037
|
-
return router;
|
|
2038
|
-
}
|
|
2039
|
-
var jsonErrorHandler = (err, _req, res, next) => {
|
|
2040
|
-
if (err instanceof SyntaxError && "body" in err) {
|
|
2041
|
-
const a2aError = A2AError.parseError("Invalid JSON payload.");
|
|
2042
|
-
const errorResponse = {
|
|
2043
|
-
jsonrpc: "2.0",
|
|
2044
|
-
id: null,
|
|
2045
|
-
error: a2aError.toJSONRPCError()
|
|
2046
|
-
};
|
|
2047
|
-
return res.status(400).json(errorResponse);
|
|
2048
|
-
}
|
|
2049
|
-
next(err);
|
|
2050
|
-
};
|
|
2051
|
-
|
|
2052
|
-
// src/server/express/agent_card_handler.ts
|
|
2053
|
-
var import_express2 = __toESM(require("express"));
|
|
2054
|
-
function agentCardHandler(options) {
|
|
2055
|
-
const router = import_express2.default.Router();
|
|
2056
|
-
const provider = typeof options.agentCardProvider === "function" ? options.agentCardProvider : options.agentCardProvider.getAgentCard.bind(options.agentCardProvider);
|
|
2057
|
-
router.get("/", async (_req, res) => {
|
|
2058
|
-
try {
|
|
2059
|
-
const agentCard = await provider();
|
|
2060
|
-
res.json(agentCard);
|
|
2061
|
-
} catch (error) {
|
|
2062
|
-
console.error("Error fetching agent card:", error);
|
|
2063
|
-
res.status(500).json({ error: "Failed to retrieve agent card" });
|
|
2064
|
-
}
|
|
2065
|
-
});
|
|
2066
|
-
return router;
|
|
2067
|
-
}
|
|
2068
|
-
|
|
2069
|
-
// src/server/express/common.ts
|
|
2070
|
-
var UserBuilder = {
|
|
2071
|
-
NoAuthentication: () => Promise.resolve(new UnauthenticatedUser())
|
|
2072
|
-
};
|
|
2073
|
-
|
|
2074
|
-
// src/server/express/a2a_express_app.ts
|
|
2075
|
-
var A2AExpressApp = class {
|
|
2076
|
-
requestHandler;
|
|
2077
|
-
userBuilder;
|
|
2078
|
-
constructor(requestHandler, userBuilder = UserBuilder.NoAuthentication) {
|
|
2079
|
-
this.requestHandler = requestHandler;
|
|
2080
|
-
this.userBuilder = userBuilder;
|
|
2081
|
-
}
|
|
2082
|
-
/**
|
|
2083
|
-
* Adds A2A routes to an existing Express app.
|
|
2084
|
-
* @param app Optional existing Express app.
|
|
2085
|
-
* @param baseUrl The base URL for A2A endpoints (e.g., "/a2a/api").
|
|
2086
|
-
* @param middlewares Optional array of Express middlewares to apply to the A2A routes.
|
|
2087
|
-
* @param agentCardPath Optional custom path for the agent card endpoint (defaults to .well-known/agent-card.json).
|
|
2088
|
-
* @returns The Express app with A2A routes.
|
|
2089
|
-
*/
|
|
2090
|
-
setupRoutes(app, baseUrl = "", middlewares, agentCardPath = AGENT_CARD_PATH) {
|
|
2091
|
-
const router = import_express3.default.Router();
|
|
2092
|
-
router.use(import_express3.default.json(), jsonErrorHandler);
|
|
2093
|
-
if (middlewares && middlewares.length > 0) {
|
|
2094
|
-
router.use(middlewares);
|
|
2095
|
-
}
|
|
2096
|
-
router.use(
|
|
2097
|
-
jsonRpcHandler({
|
|
2098
|
-
requestHandler: this.requestHandler,
|
|
2099
|
-
userBuilder: this.userBuilder
|
|
2100
|
-
})
|
|
2101
|
-
);
|
|
2102
|
-
router.use(`/${agentCardPath}`, agentCardHandler({ agentCardProvider: this.requestHandler }));
|
|
2103
|
-
app.use(baseUrl, router);
|
|
2104
|
-
return app;
|
|
62
|
+
toServiceParameter: (value) => {
|
|
63
|
+
return value.join(",");
|
|
2105
64
|
}
|
|
2106
65
|
};
|
|
2107
66
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2108
67
|
0 && (module.exports = {
|
|
2109
|
-
A2AClient,
|
|
2110
|
-
A2AError,
|
|
2111
|
-
A2AExpressApp,
|
|
2112
68
|
AGENT_CARD_PATH,
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
DefaultRequestHandler,
|
|
2116
|
-
ExecutionEventQueue,
|
|
2117
|
-
InMemoryTaskStore,
|
|
2118
|
-
JsonRpcTransportHandler,
|
|
2119
|
-
RequestContext,
|
|
2120
|
-
ResultManager,
|
|
2121
|
-
createAuthenticatingFetchWithRetry,
|
|
2122
|
-
getCurrentTimestamp,
|
|
2123
|
-
getRequestedExtensions,
|
|
2124
|
-
isArtifactUpdate,
|
|
2125
|
-
isObject,
|
|
2126
|
-
isTaskStatusUpdate
|
|
69
|
+
Extensions,
|
|
70
|
+
HTTP_EXTENSION_HEADER
|
|
2127
71
|
});
|