@assistant-ui/react-a2a 0.2.36 → 0.2.38
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 +42 -44
- package/dist/A2AClient.d.ts.map +1 -1
- package/dist/A2AClient.js +43 -7
- package/dist/A2AClient.js.map +1 -1
- package/dist/A2AThreadRuntimeCore.d.ts +98 -86
- package/dist/A2AThreadRuntimeCore.d.ts.map +1 -1
- package/dist/A2AThreadRuntimeCore.js +204 -17
- package/dist/A2AThreadRuntimeCore.js.map +1 -1
- package/dist/a2aExtras.d.ts +1 -3
- package/dist/a2aExtras.d.ts.map +1 -1
- package/dist/conversions.d.ts +18 -17
- package/dist/conversions.d.ts.map +1 -1
- package/dist/conversions.js +24 -2
- package/dist/conversions.js.map +1 -1
- package/dist/hooks.d.ts +3 -6
- package/dist/hooks.d.ts.map +1 -1
- package/dist/index.d.ts +9 -6
- package/dist/index.d.ts.map +1 -0
- package/dist/types.d.ts +207 -209
- package/dist/types.d.ts.map +1 -1
- package/dist/useA2ARuntime.d.ts +2 -4
- package/dist/useA2ARuntime.d.ts.map +1 -1
- package/dist/useA2ARuntime.js +8 -0
- package/dist/useA2ARuntime.js.map +1 -1
- package/package.json +6 -5
- package/src/A2AClient.test.ts +41 -0
- package/src/A2AClient.ts +50 -6
- package/src/A2AThreadRuntimeCore.test.ts +887 -9
- package/src/A2AThreadRuntimeCore.ts +347 -18
- package/src/conversions.test.ts +116 -0
- package/src/conversions.ts +48 -1
- package/src/useA2ARuntime.test.tsx +245 -0
- package/src/useA2ARuntime.ts +14 -0
- package/src/useA2ARuntime.voice.test.tsx +370 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react-a2a",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.38",
|
|
4
4
|
"description": "A2A (Agent-to-Agent) v1.0 protocol adapter for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"a2a",
|
|
@@ -29,9 +29,10 @@
|
|
|
29
29
|
],
|
|
30
30
|
"sideEffects": false,
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@assistant-ui/core": "^0.3.
|
|
33
|
-
"@assistant-ui/
|
|
34
|
-
"assistant-
|
|
32
|
+
"@assistant-ui/core": "^0.3.21",
|
|
33
|
+
"@assistant-ui/react-generative-ui": "^0.0.21",
|
|
34
|
+
"@assistant-ui/store": "^0.3.15",
|
|
35
|
+
"assistant-stream": "^0.3.45"
|
|
35
36
|
},
|
|
36
37
|
"peerDependencies": {
|
|
37
38
|
"@types/react": "*",
|
|
@@ -43,7 +44,7 @@
|
|
|
43
44
|
}
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
47
|
+
"@assistant-ui/x-buildutils": "0.0.29",
|
|
47
48
|
"@testing-library/dom": "^10.4.2",
|
|
48
49
|
"@testing-library/react": "^16.3.3",
|
|
49
50
|
"@types/react": "^19.3.0",
|
package/src/A2AClient.test.ts
CHANGED
|
@@ -160,6 +160,47 @@ describe("A2AClient", () => {
|
|
|
160
160
|
const [, init] = fetchMock.mock.calls[0]!;
|
|
161
161
|
expect(init.headers.Authorization).toBe("Bearer tok123");
|
|
162
162
|
});
|
|
163
|
+
|
|
164
|
+
it("aborts a request while dynamic headers are pending", async () => {
|
|
165
|
+
const dynamicClient = new A2AClient({
|
|
166
|
+
baseUrl: "https://agent.test",
|
|
167
|
+
headers: () => new Promise<Record<string, string>>(() => {}),
|
|
168
|
+
});
|
|
169
|
+
const controller = new AbortController();
|
|
170
|
+
const reason = new Error("cancelled");
|
|
171
|
+
|
|
172
|
+
const request = dynamicClient.sendMessage(
|
|
173
|
+
userMessage,
|
|
174
|
+
undefined,
|
|
175
|
+
undefined,
|
|
176
|
+
controller.signal,
|
|
177
|
+
);
|
|
178
|
+
controller.abort(reason);
|
|
179
|
+
|
|
180
|
+
await expect(request).rejects.toBe(reason);
|
|
181
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("aborts a stream while dynamic headers are pending", async () => {
|
|
185
|
+
const dynamicClient = new A2AClient({
|
|
186
|
+
baseUrl: "https://agent.test",
|
|
187
|
+
headers: () => new Promise<Record<string, string>>(() => {}),
|
|
188
|
+
});
|
|
189
|
+
const controller = new AbortController();
|
|
190
|
+
const reason = new Error("cancelled");
|
|
191
|
+
const stream = dynamicClient.streamMessage(
|
|
192
|
+
userMessage,
|
|
193
|
+
undefined,
|
|
194
|
+
undefined,
|
|
195
|
+
controller.signal,
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
const next = stream.next();
|
|
199
|
+
controller.abort(reason);
|
|
200
|
+
|
|
201
|
+
await expect(next).rejects.toBe(reason);
|
|
202
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
203
|
+
});
|
|
163
204
|
});
|
|
164
205
|
|
|
165
206
|
describe("fetchOptions", () => {
|
package/src/A2AClient.ts
CHANGED
|
@@ -680,6 +680,49 @@ function signalInit(signal?: AbortSignal): RequestInit {
|
|
|
680
680
|
return signal ? { signal } : {};
|
|
681
681
|
}
|
|
682
682
|
|
|
683
|
+
const getAbortReason = (signal: AbortSignal): unknown => {
|
|
684
|
+
if (signal.reason !== undefined) return signal.reason;
|
|
685
|
+
const error = new Error("The operation was aborted");
|
|
686
|
+
error.name = "AbortError";
|
|
687
|
+
return error;
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
const raceWithAbortSignal = <T>(
|
|
691
|
+
signal: AbortSignal | undefined,
|
|
692
|
+
operation: () => T | PromiseLike<T>,
|
|
693
|
+
): Promise<T> => {
|
|
694
|
+
if (!signal) return Promise.resolve().then(operation);
|
|
695
|
+
if (signal.aborted) return Promise.reject(getAbortReason(signal));
|
|
696
|
+
|
|
697
|
+
return new Promise<T>((resolve, reject) => {
|
|
698
|
+
let settled = false;
|
|
699
|
+
const cleanup = () => signal.removeEventListener("abort", handleAbort);
|
|
700
|
+
const resolveOnce = (value: T) => {
|
|
701
|
+
if (settled) return;
|
|
702
|
+
settled = true;
|
|
703
|
+
cleanup();
|
|
704
|
+
resolve(value);
|
|
705
|
+
};
|
|
706
|
+
const rejectOnce = (error: unknown) => {
|
|
707
|
+
if (settled) return;
|
|
708
|
+
settled = true;
|
|
709
|
+
cleanup();
|
|
710
|
+
reject(error);
|
|
711
|
+
};
|
|
712
|
+
const handleAbort = () => rejectOnce(getAbortReason(signal));
|
|
713
|
+
|
|
714
|
+
signal.addEventListener("abort", handleAbort, { once: true });
|
|
715
|
+
let result: T | PromiseLike<T>;
|
|
716
|
+
try {
|
|
717
|
+
result = operation();
|
|
718
|
+
} catch (error) {
|
|
719
|
+
rejectOnce(error);
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
Promise.resolve(result).then(resolveOnce, rejectOnce);
|
|
723
|
+
});
|
|
724
|
+
};
|
|
725
|
+
|
|
683
726
|
const SKIPPED_FRAME_SNIPPET_LENGTH = 120;
|
|
684
727
|
|
|
685
728
|
function describeSkippedFrame(data: string, reason: string): string {
|
|
@@ -728,10 +771,11 @@ export class A2AClient {
|
|
|
728
771
|
|
|
729
772
|
private async getHeaders(
|
|
730
773
|
includeContentType = true,
|
|
774
|
+
signal?: AbortSignal,
|
|
731
775
|
): Promise<Record<string, string>> {
|
|
732
776
|
const custom =
|
|
733
777
|
typeof this.headersFn === "function"
|
|
734
|
-
? await this.headersFn
|
|
778
|
+
? await raceWithAbortSignal(signal, this.headersFn)
|
|
735
779
|
: this.headersFn;
|
|
736
780
|
const headers: Record<string, string> = {
|
|
737
781
|
Accept: "application/a2a+json, application/json",
|
|
@@ -777,7 +821,7 @@ export class A2AClient {
|
|
|
777
821
|
options: RequestInit = {},
|
|
778
822
|
): Promise<T> {
|
|
779
823
|
const isGet = !options.method || options.method.toUpperCase() === "GET";
|
|
780
|
-
const headers = await this.getHeaders(!isGet);
|
|
824
|
+
const headers = await this.getHeaders(!isGet, options.signal ?? undefined);
|
|
781
825
|
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
782
826
|
...this.fetchOptions,
|
|
783
827
|
...options,
|
|
@@ -809,7 +853,7 @@ export class A2AClient {
|
|
|
809
853
|
// --- Agent Card ---
|
|
810
854
|
|
|
811
855
|
async getAgentCard(signal?: AbortSignal): Promise<A2AAgentCard> {
|
|
812
|
-
const headers = await this.getHeaders(false); // GET: no Content-Type
|
|
856
|
+
const headers = await this.getHeaders(false, signal); // GET: no Content-Type
|
|
813
857
|
const url = `${this.baseUrl}/.well-known/agent-card.json`;
|
|
814
858
|
const response = await fetch(url, {
|
|
815
859
|
...this.fetchOptions,
|
|
@@ -863,7 +907,7 @@ export class A2AClient {
|
|
|
863
907
|
metadata?: Record<string, unknown>,
|
|
864
908
|
signal?: AbortSignal,
|
|
865
909
|
): AsyncGenerator<A2AStreamEvent> {
|
|
866
|
-
const headers = await this.getHeaders(true);
|
|
910
|
+
const headers = await this.getHeaders(true, signal);
|
|
867
911
|
headers.Accept = "text/event-stream";
|
|
868
912
|
|
|
869
913
|
const body: Record<string, unknown> = {
|
|
@@ -954,7 +998,7 @@ export class A2AClient {
|
|
|
954
998
|
taskId: string,
|
|
955
999
|
signal?: AbortSignal,
|
|
956
1000
|
): AsyncGenerator<A2AStreamEvent> {
|
|
957
|
-
const headers = await this.getHeaders(false); // GET: no Content-Type
|
|
1001
|
+
const headers = await this.getHeaders(false, signal); // GET: no Content-Type
|
|
958
1002
|
headers.Accept = "text/event-stream";
|
|
959
1003
|
|
|
960
1004
|
const response = await fetch(
|
|
@@ -1032,7 +1076,7 @@ export class A2AClient {
|
|
|
1032
1076
|
signal?: AbortSignal,
|
|
1033
1077
|
): Promise<void> {
|
|
1034
1078
|
const isGet = false;
|
|
1035
|
-
const headers = await this.getHeaders(!isGet);
|
|
1079
|
+
const headers = await this.getHeaders(!isGet, signal);
|
|
1036
1080
|
const response = await fetch(
|
|
1037
1081
|
`${this.baseUrl}${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs/${encodeURIComponent(configId)}`,
|
|
1038
1082
|
{
|