@assistant-ui/react-a2a 0.2.15 → 0.2.16
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/dist/A2AClient.d.ts +43 -42
- package/dist/A2AClient.d.ts.map +1 -1
- package/dist/A2AClient.js +267 -349
- package/dist/A2AClient.js.map +1 -1
- package/dist/A2AThreadRuntimeCore.d.ts +76 -72
- package/dist/A2AThreadRuntimeCore.d.ts.map +1 -1
- package/dist/A2AThreadRuntimeCore.js +400 -479
- package/dist/A2AThreadRuntimeCore.js.map +1 -1
- package/dist/conversions.d.ts +16 -12
- package/dist/conversions.d.ts.map +1 -1
- package/dist/conversions.js +88 -79
- package/dist/conversions.js.map +1 -1
- package/dist/index.d.ts +5 -8
- package/dist/index.js +5 -8
- package/dist/types.d.ts +229 -228
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +5 -4
- package/dist/types.js.map +1 -1
- package/dist/useA2ARuntime.d.ts +36 -44
- package/dist/useA2ARuntime.d.ts.map +1 -1
- package/dist/useA2ARuntime.js +113 -134
- package/dist/useA2ARuntime.js.map +1 -1
- package/package.json +7 -7
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
package/dist/A2AClient.js
CHANGED
|
@@ -1,366 +1,284 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
1
|
+
import "./types.js";
|
|
2
|
+
//#region src/A2AClient.ts
|
|
3
|
+
var A2AError = class extends Error {
|
|
4
|
+
code;
|
|
5
|
+
status;
|
|
6
|
+
details;
|
|
7
|
+
constructor(info) {
|
|
8
|
+
super(info.message);
|
|
9
|
+
this.name = "A2AError";
|
|
10
|
+
this.code = info.code;
|
|
11
|
+
this.status = info.status;
|
|
12
|
+
this.details = info.details;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
15
|
function toCamelCase(key) {
|
|
16
|
-
|
|
16
|
+
return key.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
17
17
|
}
|
|
18
|
-
// Fields whose values are opaque user data (google.protobuf.Struct / Value).
|
|
19
|
-
// Keys inside these objects must NOT be camelCased or have enum normalization applied.
|
|
20
18
|
const OPAQUE_FIELDS = new Set([
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
"metadata",
|
|
20
|
+
"data",
|
|
21
|
+
"params",
|
|
22
|
+
"forwardedProps",
|
|
23
|
+
"scopes"
|
|
26
24
|
]);
|
|
27
25
|
function normalizeKeys(obj, opaque = false) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
result[camelKey] = value.slice(11).toLowerCase();
|
|
47
|
-
}
|
|
48
|
-
else if (camelKey === "role" &&
|
|
49
|
-
typeof value === "string" &&
|
|
50
|
-
value.startsWith("ROLE_")) {
|
|
51
|
-
result[camelKey] = value.slice(5).toLowerCase();
|
|
52
|
-
}
|
|
53
|
-
else if (camelKey === "content" && Array.isArray(value)) {
|
|
54
|
-
// v1.0 proto uses "content" for message/artifact parts; map to internal "parts"
|
|
55
|
-
result.parts = normalizeKeys(value, false);
|
|
56
|
-
}
|
|
57
|
-
else if (camelKey !== "parts" || !("parts" in result)) {
|
|
58
|
-
// skip "parts" if "content" already mapped it (prefer content over parts)
|
|
59
|
-
result[camelKey] = isOpaqueChild ? value : normalizeKeys(value, false);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return result;
|
|
63
|
-
}
|
|
64
|
-
return obj;
|
|
26
|
+
if (Array.isArray(obj)) return obj.map((v) => normalizeKeys(v, opaque));
|
|
27
|
+
if (obj !== null && typeof obj === "object") {
|
|
28
|
+
const result = {};
|
|
29
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
30
|
+
if (opaque) {
|
|
31
|
+
result[key] = typeof value === "object" && value !== null ? normalizeKeys(value, true) : value;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const camelKey = toCamelCase(key);
|
|
35
|
+
const isOpaqueChild = OPAQUE_FIELDS.has(camelKey);
|
|
36
|
+
if (camelKey === "state" && typeof value === "string" && value.startsWith("TASK_STATE_")) result[camelKey] = value.slice(11).toLowerCase();
|
|
37
|
+
else if (camelKey === "role" && typeof value === "string" && value.startsWith("ROLE_")) result[camelKey] = value.slice(5).toLowerCase();
|
|
38
|
+
else if (camelKey === "content" && Array.isArray(value)) result.parts = normalizeKeys(value, false);
|
|
39
|
+
else if (camelKey !== "parts" || !("parts" in result)) result[camelKey] = isOpaqueChild ? value : normalizeKeys(value, false);
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
return obj;
|
|
65
44
|
}
|
|
66
|
-
// Outgoing enum conversion (v1.0 ProtoJSON format)
|
|
67
45
|
function toWireRole(role) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return "ROLE_AGENT";
|
|
72
|
-
return "ROLE_UNSPECIFIED";
|
|
46
|
+
if (role === "user") return "ROLE_USER";
|
|
47
|
+
if (role === "agent") return "ROLE_AGENT";
|
|
48
|
+
return "ROLE_UNSPECIFIED";
|
|
73
49
|
}
|
|
74
50
|
function toWireTaskState(state) {
|
|
75
|
-
|
|
51
|
+
return `TASK_STATE_${state.toUpperCase()}`;
|
|
76
52
|
}
|
|
77
53
|
function toWireMessage(msg) {
|
|
78
|
-
|
|
79
|
-
|
|
54
|
+
const { parts, ...rest } = msg;
|
|
55
|
+
return {
|
|
56
|
+
...rest,
|
|
57
|
+
role: toWireRole(msg.role),
|
|
58
|
+
content: parts
|
|
59
|
+
};
|
|
80
60
|
}
|
|
81
61
|
function discriminateStreamResponse(data) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
return null;
|
|
62
|
+
if ("task" in data && data.task) return {
|
|
63
|
+
type: "task",
|
|
64
|
+
task: data.task
|
|
65
|
+
};
|
|
66
|
+
if ("message" in data && data.message) return {
|
|
67
|
+
type: "message",
|
|
68
|
+
message: data.message
|
|
69
|
+
};
|
|
70
|
+
if ("statusUpdate" in data && data.statusUpdate) return {
|
|
71
|
+
type: "statusUpdate",
|
|
72
|
+
event: data.statusUpdate
|
|
73
|
+
};
|
|
74
|
+
if ("artifactUpdate" in data && data.artifactUpdate) return {
|
|
75
|
+
type: "artifactUpdate",
|
|
76
|
+
event: data.artifactUpdate
|
|
77
|
+
};
|
|
78
|
+
return null;
|
|
101
79
|
}
|
|
102
80
|
function signalInit(signal) {
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
export class A2AClient {
|
|
106
|
-
baseUrl;
|
|
107
|
-
basePath;
|
|
108
|
-
tenant;
|
|
109
|
-
extensionUris;
|
|
110
|
-
headersFn;
|
|
111
|
-
constructor(options) {
|
|
112
|
-
this.baseUrl = options.baseUrl.replace(/\/$/, "");
|
|
113
|
-
this.basePath = options.basePath
|
|
114
|
-
? `/${options.basePath.replace(/^\/|\/$/g, "")}`
|
|
115
|
-
: "";
|
|
116
|
-
this.tenant = options.tenant;
|
|
117
|
-
this.extensionUris = options.extensions;
|
|
118
|
-
this.headersFn = options.headers ?? {};
|
|
119
|
-
}
|
|
120
|
-
getBasePath() {
|
|
121
|
-
return `${this.basePath}${this.tenant ? `/${encodeURIComponent(this.tenant)}` : ""}`;
|
|
122
|
-
}
|
|
123
|
-
async getHeaders(includeContentType = true) {
|
|
124
|
-
const custom = typeof this.headersFn === "function"
|
|
125
|
-
? await this.headersFn()
|
|
126
|
-
: this.headersFn;
|
|
127
|
-
const headers = {
|
|
128
|
-
Accept: "application/a2a+json, application/json",
|
|
129
|
-
"A2A-Version": A2A_PROTOCOL_VERSION,
|
|
130
|
-
...custom,
|
|
131
|
-
};
|
|
132
|
-
if (includeContentType) {
|
|
133
|
-
headers["Content-Type"] = "application/a2a+json";
|
|
134
|
-
}
|
|
135
|
-
if (this.extensionUris?.length) {
|
|
136
|
-
headers["A2A-Extensions"] = this.extensionUris.join(", ");
|
|
137
|
-
}
|
|
138
|
-
return headers;
|
|
139
|
-
}
|
|
140
|
-
async throwResponseError(response) {
|
|
141
|
-
let errorBody;
|
|
142
|
-
try {
|
|
143
|
-
errorBody = await response.json();
|
|
144
|
-
}
|
|
145
|
-
catch {
|
|
146
|
-
// no parseable body
|
|
147
|
-
}
|
|
148
|
-
if (errorBody && typeof errorBody === "object" && "error" in errorBody) {
|
|
149
|
-
const err = errorBody.error;
|
|
150
|
-
throw new A2AError({
|
|
151
|
-
code: err.code ?? response.status,
|
|
152
|
-
status: err.status ?? response.statusText,
|
|
153
|
-
message: err.message ?? `A2A request failed: ${response.status}`,
|
|
154
|
-
details: err.details,
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
throw new A2AError({
|
|
158
|
-
code: response.status,
|
|
159
|
-
status: response.statusText,
|
|
160
|
-
message: `A2A request failed: ${response.status} ${response.statusText}`,
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
async fetchJSON(path, options = {}) {
|
|
164
|
-
const isGet = !options.method || options.method.toUpperCase() === "GET";
|
|
165
|
-
const headers = await this.getHeaders(!isGet);
|
|
166
|
-
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
167
|
-
...options,
|
|
168
|
-
headers: {
|
|
169
|
-
...headers,
|
|
170
|
-
...options.headers,
|
|
171
|
-
},
|
|
172
|
-
});
|
|
173
|
-
if (!response.ok) {
|
|
174
|
-
await this.throwResponseError(response);
|
|
175
|
-
}
|
|
176
|
-
const json = await response.json();
|
|
177
|
-
return normalizeKeys(json);
|
|
178
|
-
}
|
|
179
|
-
// --- Agent Card ---
|
|
180
|
-
async getAgentCard(signal) {
|
|
181
|
-
const headers = await this.getHeaders(false); // GET: no Content-Type
|
|
182
|
-
const url = `${this.baseUrl}/.well-known/agent-card.json`;
|
|
183
|
-
const response = await fetch(url, { headers, ...signalInit(signal) });
|
|
184
|
-
if (!response.ok) {
|
|
185
|
-
await this.throwResponseError(response);
|
|
186
|
-
}
|
|
187
|
-
const json = await response.json();
|
|
188
|
-
return normalizeKeys(json);
|
|
189
|
-
}
|
|
190
|
-
async getExtendedAgentCard(signal) {
|
|
191
|
-
return this.fetchJSON(`${this.getBasePath()}/extendedAgentCard`, signalInit(signal));
|
|
192
|
-
}
|
|
193
|
-
// --- Message ---
|
|
194
|
-
async sendMessage(message, configuration, metadata, signal) {
|
|
195
|
-
const body = {
|
|
196
|
-
message: toWireMessage(message),
|
|
197
|
-
};
|
|
198
|
-
if (configuration)
|
|
199
|
-
body.configuration = configuration;
|
|
200
|
-
if (metadata)
|
|
201
|
-
body.metadata = metadata;
|
|
202
|
-
const result = await this.fetchJSON(`${this.getBasePath()}/message:send`, {
|
|
203
|
-
method: "POST",
|
|
204
|
-
body: JSON.stringify(body),
|
|
205
|
-
...signalInit(signal),
|
|
206
|
-
});
|
|
207
|
-
// Unwrap SendMessageResponse: {task: Task} | {message: Message}
|
|
208
|
-
if ("task" in result && result.task)
|
|
209
|
-
return result.task;
|
|
210
|
-
if ("message" in result && result.message)
|
|
211
|
-
return result.message;
|
|
212
|
-
return result;
|
|
213
|
-
}
|
|
214
|
-
async *streamMessage(message, configuration, metadata, signal) {
|
|
215
|
-
const headers = await this.getHeaders(true);
|
|
216
|
-
headers.Accept = "text/event-stream";
|
|
217
|
-
const body = {
|
|
218
|
-
message: toWireMessage(message),
|
|
219
|
-
};
|
|
220
|
-
if (configuration)
|
|
221
|
-
body.configuration = configuration;
|
|
222
|
-
if (metadata)
|
|
223
|
-
body.metadata = metadata;
|
|
224
|
-
const response = await fetch(`${this.baseUrl}${this.getBasePath()}/message:stream`, {
|
|
225
|
-
method: "POST",
|
|
226
|
-
headers,
|
|
227
|
-
body: JSON.stringify(body),
|
|
228
|
-
...signalInit(signal),
|
|
229
|
-
});
|
|
230
|
-
if (!response.ok) {
|
|
231
|
-
await this.throwResponseError(response);
|
|
232
|
-
}
|
|
233
|
-
yield* this.parseSSE(response);
|
|
234
|
-
}
|
|
235
|
-
// --- Tasks ---
|
|
236
|
-
async getTask(taskId, historyLength, signal) {
|
|
237
|
-
const params = new URLSearchParams();
|
|
238
|
-
if (historyLength !== undefined) {
|
|
239
|
-
// Proto field name for HTTP transcoding query params
|
|
240
|
-
params.set("history_length", String(historyLength));
|
|
241
|
-
}
|
|
242
|
-
const qs = params.toString();
|
|
243
|
-
return this.fetchJSON(`${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}${qs ? `?${qs}` : ""}`, signalInit(signal));
|
|
244
|
-
}
|
|
245
|
-
async listTasks(request, signal) {
|
|
246
|
-
const params = new URLSearchParams();
|
|
247
|
-
if (request?.contextId)
|
|
248
|
-
params.set("context_id", request.contextId);
|
|
249
|
-
if (request?.status)
|
|
250
|
-
params.set("status", toWireTaskState(request.status));
|
|
251
|
-
if (request?.pageSize !== undefined)
|
|
252
|
-
params.set("page_size", String(request.pageSize));
|
|
253
|
-
if (request?.pageToken)
|
|
254
|
-
params.set("page_token", request.pageToken);
|
|
255
|
-
if (request?.historyLength !== undefined)
|
|
256
|
-
params.set("history_length", String(request.historyLength));
|
|
257
|
-
if (request?.statusTimestampAfter)
|
|
258
|
-
params.set("status_timestamp_after", request.statusTimestampAfter);
|
|
259
|
-
if (request?.includeArtifacts !== undefined)
|
|
260
|
-
params.set("include_artifacts", String(request.includeArtifacts));
|
|
261
|
-
const qs = params.toString();
|
|
262
|
-
return this.fetchJSON(`${this.getBasePath()}/tasks${qs ? `?${qs}` : ""}`, signalInit(signal));
|
|
263
|
-
}
|
|
264
|
-
async cancelTask(taskId, metadata, signal) {
|
|
265
|
-
const body = metadata ? { metadata } : {};
|
|
266
|
-
return this.fetchJSON(`${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}:cancel`, {
|
|
267
|
-
method: "POST",
|
|
268
|
-
body: JSON.stringify(body),
|
|
269
|
-
...signalInit(signal),
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
async *subscribeToTask(taskId, signal) {
|
|
273
|
-
const headers = await this.getHeaders(false); // GET: no Content-Type
|
|
274
|
-
headers.Accept = "text/event-stream";
|
|
275
|
-
const response = await fetch(`${this.baseUrl}${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}:subscribe`, { headers, ...signalInit(signal) });
|
|
276
|
-
if (!response.ok) {
|
|
277
|
-
await this.throwResponseError(response);
|
|
278
|
-
}
|
|
279
|
-
yield* this.parseSSE(response);
|
|
280
|
-
}
|
|
281
|
-
// --- Push Notification Configs ---
|
|
282
|
-
async createTaskPushNotificationConfig(config, signal) {
|
|
283
|
-
const taskId = config.taskId;
|
|
284
|
-
if (!taskId)
|
|
285
|
-
throw new Error("taskId is required");
|
|
286
|
-
return this.fetchJSON(`${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs`, {
|
|
287
|
-
method: "POST",
|
|
288
|
-
body: JSON.stringify(config),
|
|
289
|
-
...signalInit(signal),
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
async getTaskPushNotificationConfig(taskId, configId, signal) {
|
|
293
|
-
return this.fetchJSON(`${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs/${encodeURIComponent(configId)}`, signalInit(signal));
|
|
294
|
-
}
|
|
295
|
-
async listTaskPushNotificationConfigs(taskId, options, signal) {
|
|
296
|
-
const params = new URLSearchParams();
|
|
297
|
-
if (options?.pageSize !== undefined)
|
|
298
|
-
params.set("page_size", String(options.pageSize));
|
|
299
|
-
if (options?.pageToken)
|
|
300
|
-
params.set("page_token", options.pageToken);
|
|
301
|
-
const qs = params.toString();
|
|
302
|
-
return this.fetchJSON(`${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs${qs ? `?${qs}` : ""}`, signalInit(signal));
|
|
303
|
-
}
|
|
304
|
-
async deleteTaskPushNotificationConfig(taskId, configId, signal) {
|
|
305
|
-
const isGet = false;
|
|
306
|
-
const headers = await this.getHeaders(!isGet);
|
|
307
|
-
const response = await fetch(`${this.baseUrl}${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs/${encodeURIComponent(configId)}`, { method: "DELETE", headers, ...signalInit(signal) });
|
|
308
|
-
if (!response.ok) {
|
|
309
|
-
await this.throwResponseError(response);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
// --- SSE Parsing ---
|
|
313
|
-
async *parseSSE(response) {
|
|
314
|
-
const reader = response.body?.getReader();
|
|
315
|
-
if (!reader)
|
|
316
|
-
throw new Error("No response body");
|
|
317
|
-
const decoder = new TextDecoder();
|
|
318
|
-
let buffer = "";
|
|
319
|
-
try {
|
|
320
|
-
while (true) {
|
|
321
|
-
const { done, value } = await reader.read();
|
|
322
|
-
if (done)
|
|
323
|
-
break;
|
|
324
|
-
buffer += decoder.decode(value, { stream: true });
|
|
325
|
-
let eventEnd = buffer.indexOf("\n\n");
|
|
326
|
-
while (eventEnd !== -1) {
|
|
327
|
-
const eventText = buffer.slice(0, eventEnd);
|
|
328
|
-
buffer = buffer.slice(eventEnd + 2);
|
|
329
|
-
const dataLines = [];
|
|
330
|
-
for (const line of eventText.split("\n")) {
|
|
331
|
-
const trimmed = line.replace(/\r$/, "");
|
|
332
|
-
if (trimmed.startsWith("data:")) {
|
|
333
|
-
dataLines.push(trimmed.slice(5).trim());
|
|
334
|
-
}
|
|
335
|
-
// event:, id:, retry: lines are parsed but not used —
|
|
336
|
-
// we discriminate event type from the JSON payload.
|
|
337
|
-
}
|
|
338
|
-
if (dataLines.length === 0)
|
|
339
|
-
continue;
|
|
340
|
-
try {
|
|
341
|
-
let parsed = JSON.parse(dataLines.join("\n"));
|
|
342
|
-
// Unwrap JSON-RPC envelope if present
|
|
343
|
-
if (parsed &&
|
|
344
|
-
typeof parsed === "object" &&
|
|
345
|
-
"jsonrpc" in parsed &&
|
|
346
|
-
"result" in parsed) {
|
|
347
|
-
parsed = parsed.result;
|
|
348
|
-
}
|
|
349
|
-
const normalized = normalizeKeys(parsed);
|
|
350
|
-
const event = discriminateStreamResponse(normalized);
|
|
351
|
-
if (event)
|
|
352
|
-
yield event;
|
|
353
|
-
}
|
|
354
|
-
catch {
|
|
355
|
-
// Skip malformed events
|
|
356
|
-
}
|
|
357
|
-
eventEnd = buffer.indexOf("\n\n");
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
finally {
|
|
362
|
-
reader.releaseLock();
|
|
363
|
-
}
|
|
364
|
-
}
|
|
81
|
+
return signal ? { signal } : {};
|
|
365
82
|
}
|
|
83
|
+
var A2AClient = class {
|
|
84
|
+
baseUrl;
|
|
85
|
+
basePath;
|
|
86
|
+
tenant;
|
|
87
|
+
extensionUris;
|
|
88
|
+
headersFn;
|
|
89
|
+
constructor(options) {
|
|
90
|
+
this.baseUrl = options.baseUrl.replace(/\/$/, "");
|
|
91
|
+
this.basePath = options.basePath ? `/${options.basePath.replace(/^\/|\/$/g, "")}` : "";
|
|
92
|
+
this.tenant = options.tenant;
|
|
93
|
+
this.extensionUris = options.extensions;
|
|
94
|
+
this.headersFn = options.headers ?? {};
|
|
95
|
+
}
|
|
96
|
+
getBasePath() {
|
|
97
|
+
return `${this.basePath}${this.tenant ? `/${encodeURIComponent(this.tenant)}` : ""}`;
|
|
98
|
+
}
|
|
99
|
+
async getHeaders(includeContentType = true) {
|
|
100
|
+
const headers = {
|
|
101
|
+
Accept: "application/a2a+json, application/json",
|
|
102
|
+
"A2A-Version": "1.0",
|
|
103
|
+
...typeof this.headersFn === "function" ? await this.headersFn() : this.headersFn
|
|
104
|
+
};
|
|
105
|
+
if (includeContentType) headers["Content-Type"] = "application/a2a+json";
|
|
106
|
+
if (this.extensionUris?.length) headers["A2A-Extensions"] = this.extensionUris.join(", ");
|
|
107
|
+
return headers;
|
|
108
|
+
}
|
|
109
|
+
async throwResponseError(response) {
|
|
110
|
+
let errorBody;
|
|
111
|
+
try {
|
|
112
|
+
errorBody = await response.json();
|
|
113
|
+
} catch {}
|
|
114
|
+
if (errorBody && typeof errorBody === "object" && "error" in errorBody) {
|
|
115
|
+
const err = errorBody.error;
|
|
116
|
+
throw new A2AError({
|
|
117
|
+
code: err.code ?? response.status,
|
|
118
|
+
status: err.status ?? response.statusText,
|
|
119
|
+
message: err.message ?? `A2A request failed: ${response.status}`,
|
|
120
|
+
details: err.details
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
throw new A2AError({
|
|
124
|
+
code: response.status,
|
|
125
|
+
status: response.statusText,
|
|
126
|
+
message: `A2A request failed: ${response.status} ${response.statusText}`
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
async fetchJSON(path, options = {}) {
|
|
130
|
+
const isGet = !options.method || options.method.toUpperCase() === "GET";
|
|
131
|
+
const headers = await this.getHeaders(!isGet);
|
|
132
|
+
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
133
|
+
...options,
|
|
134
|
+
headers: {
|
|
135
|
+
...headers,
|
|
136
|
+
...options.headers
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
if (!response.ok) await this.throwResponseError(response);
|
|
140
|
+
return normalizeKeys(await response.json());
|
|
141
|
+
}
|
|
142
|
+
async getAgentCard(signal) {
|
|
143
|
+
const headers = await this.getHeaders(false);
|
|
144
|
+
const url = `${this.baseUrl}/.well-known/agent-card.json`;
|
|
145
|
+
const response = await fetch(url, {
|
|
146
|
+
headers,
|
|
147
|
+
...signalInit(signal)
|
|
148
|
+
});
|
|
149
|
+
if (!response.ok) await this.throwResponseError(response);
|
|
150
|
+
return normalizeKeys(await response.json());
|
|
151
|
+
}
|
|
152
|
+
async getExtendedAgentCard(signal) {
|
|
153
|
+
return this.fetchJSON(`${this.getBasePath()}/extendedAgentCard`, signalInit(signal));
|
|
154
|
+
}
|
|
155
|
+
async sendMessage(message, configuration, metadata, signal) {
|
|
156
|
+
const body = { message: toWireMessage(message) };
|
|
157
|
+
if (configuration) body.configuration = configuration;
|
|
158
|
+
if (metadata) body.metadata = metadata;
|
|
159
|
+
const result = await this.fetchJSON(`${this.getBasePath()}/message:send`, {
|
|
160
|
+
method: "POST",
|
|
161
|
+
body: JSON.stringify(body),
|
|
162
|
+
...signalInit(signal)
|
|
163
|
+
});
|
|
164
|
+
if ("task" in result && result.task) return result.task;
|
|
165
|
+
if ("message" in result && result.message) return result.message;
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
async *streamMessage(message, configuration, metadata, signal) {
|
|
169
|
+
const headers = await this.getHeaders(true);
|
|
170
|
+
headers.Accept = "text/event-stream";
|
|
171
|
+
const body = { message: toWireMessage(message) };
|
|
172
|
+
if (configuration) body.configuration = configuration;
|
|
173
|
+
if (metadata) body.metadata = metadata;
|
|
174
|
+
const response = await fetch(`${this.baseUrl}${this.getBasePath()}/message:stream`, {
|
|
175
|
+
method: "POST",
|
|
176
|
+
headers,
|
|
177
|
+
body: JSON.stringify(body),
|
|
178
|
+
...signalInit(signal)
|
|
179
|
+
});
|
|
180
|
+
if (!response.ok) await this.throwResponseError(response);
|
|
181
|
+
yield* this.parseSSE(response);
|
|
182
|
+
}
|
|
183
|
+
async getTask(taskId, historyLength, signal) {
|
|
184
|
+
const params = new URLSearchParams();
|
|
185
|
+
if (historyLength !== void 0) params.set("history_length", String(historyLength));
|
|
186
|
+
const qs = params.toString();
|
|
187
|
+
return this.fetchJSON(`${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}${qs ? `?${qs}` : ""}`, signalInit(signal));
|
|
188
|
+
}
|
|
189
|
+
async listTasks(request, signal) {
|
|
190
|
+
const params = new URLSearchParams();
|
|
191
|
+
if (request?.contextId) params.set("context_id", request.contextId);
|
|
192
|
+
if (request?.status) params.set("status", toWireTaskState(request.status));
|
|
193
|
+
if (request?.pageSize !== void 0) params.set("page_size", String(request.pageSize));
|
|
194
|
+
if (request?.pageToken) params.set("page_token", request.pageToken);
|
|
195
|
+
if (request?.historyLength !== void 0) params.set("history_length", String(request.historyLength));
|
|
196
|
+
if (request?.statusTimestampAfter) params.set("status_timestamp_after", request.statusTimestampAfter);
|
|
197
|
+
if (request?.includeArtifacts !== void 0) params.set("include_artifacts", String(request.includeArtifacts));
|
|
198
|
+
const qs = params.toString();
|
|
199
|
+
return this.fetchJSON(`${this.getBasePath()}/tasks${qs ? `?${qs}` : ""}`, signalInit(signal));
|
|
200
|
+
}
|
|
201
|
+
async cancelTask(taskId, metadata, signal) {
|
|
202
|
+
const body = metadata ? { metadata } : {};
|
|
203
|
+
return this.fetchJSON(`${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}:cancel`, {
|
|
204
|
+
method: "POST",
|
|
205
|
+
body: JSON.stringify(body),
|
|
206
|
+
...signalInit(signal)
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
async *subscribeToTask(taskId, signal) {
|
|
210
|
+
const headers = await this.getHeaders(false);
|
|
211
|
+
headers.Accept = "text/event-stream";
|
|
212
|
+
const response = await fetch(`${this.baseUrl}${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}:subscribe`, {
|
|
213
|
+
headers,
|
|
214
|
+
...signalInit(signal)
|
|
215
|
+
});
|
|
216
|
+
if (!response.ok) await this.throwResponseError(response);
|
|
217
|
+
yield* this.parseSSE(response);
|
|
218
|
+
}
|
|
219
|
+
async createTaskPushNotificationConfig(config, signal) {
|
|
220
|
+
const taskId = config.taskId;
|
|
221
|
+
if (!taskId) throw new Error("taskId is required");
|
|
222
|
+
return this.fetchJSON(`${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs`, {
|
|
223
|
+
method: "POST",
|
|
224
|
+
body: JSON.stringify(config),
|
|
225
|
+
...signalInit(signal)
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
async getTaskPushNotificationConfig(taskId, configId, signal) {
|
|
229
|
+
return this.fetchJSON(`${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs/${encodeURIComponent(configId)}`, signalInit(signal));
|
|
230
|
+
}
|
|
231
|
+
async listTaskPushNotificationConfigs(taskId, options, signal) {
|
|
232
|
+
const params = new URLSearchParams();
|
|
233
|
+
if (options?.pageSize !== void 0) params.set("page_size", String(options.pageSize));
|
|
234
|
+
if (options?.pageToken) params.set("page_token", options.pageToken);
|
|
235
|
+
const qs = params.toString();
|
|
236
|
+
return this.fetchJSON(`${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs${qs ? `?${qs}` : ""}`, signalInit(signal));
|
|
237
|
+
}
|
|
238
|
+
async deleteTaskPushNotificationConfig(taskId, configId, signal) {
|
|
239
|
+
const headers = await this.getHeaders(true);
|
|
240
|
+
const response = await fetch(`${this.baseUrl}${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs/${encodeURIComponent(configId)}`, {
|
|
241
|
+
method: "DELETE",
|
|
242
|
+
headers,
|
|
243
|
+
...signalInit(signal)
|
|
244
|
+
});
|
|
245
|
+
if (!response.ok) await this.throwResponseError(response);
|
|
246
|
+
}
|
|
247
|
+
async *parseSSE(response) {
|
|
248
|
+
const reader = response.body?.getReader();
|
|
249
|
+
if (!reader) throw new Error("No response body");
|
|
250
|
+
const decoder = new TextDecoder();
|
|
251
|
+
let buffer = "";
|
|
252
|
+
try {
|
|
253
|
+
while (true) {
|
|
254
|
+
const { done, value } = await reader.read();
|
|
255
|
+
if (done) break;
|
|
256
|
+
buffer += decoder.decode(value, { stream: true });
|
|
257
|
+
let eventEnd = buffer.indexOf("\n\n");
|
|
258
|
+
while (eventEnd !== -1) {
|
|
259
|
+
const eventText = buffer.slice(0, eventEnd);
|
|
260
|
+
buffer = buffer.slice(eventEnd + 2);
|
|
261
|
+
const dataLines = [];
|
|
262
|
+
for (const line of eventText.split("\n")) {
|
|
263
|
+
const trimmed = line.replace(/\r$/, "");
|
|
264
|
+
if (trimmed.startsWith("data:")) dataLines.push(trimmed.slice(5).trim());
|
|
265
|
+
}
|
|
266
|
+
if (dataLines.length === 0) continue;
|
|
267
|
+
try {
|
|
268
|
+
let parsed = JSON.parse(dataLines.join("\n"));
|
|
269
|
+
if (parsed && typeof parsed === "object" && "jsonrpc" in parsed && "result" in parsed) parsed = parsed.result;
|
|
270
|
+
const event = discriminateStreamResponse(normalizeKeys(parsed));
|
|
271
|
+
if (event) yield event;
|
|
272
|
+
} catch {}
|
|
273
|
+
eventEnd = buffer.indexOf("\n\n");
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
} finally {
|
|
277
|
+
reader.releaseLock();
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
//#endregion
|
|
282
|
+
export { A2AClient, A2AError };
|
|
283
|
+
|
|
366
284
|
//# sourceMappingURL=A2AClient.js.map
|