@assistant-ui/react-a2a 0.2.15 → 0.2.17

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/src/A2AClient.ts CHANGED
@@ -26,6 +26,10 @@ export type A2AClientOptions = {
26
26
  | undefined;
27
27
  /** A2A extension URIs to negotiate. Sent as A2A-Extensions header. */
28
28
  extensions?: string[] | undefined;
29
+ /** Extra fetch options applied to every request. */
30
+ fetchOptions?:
31
+ | Omit<RequestInit, "headers" | "body" | "method" | "signal">
32
+ | undefined;
29
33
  };
30
34
 
31
35
  export class A2AError extends Error {
@@ -87,10 +91,10 @@ function normalizeKeys(obj: unknown, opaque = false): unknown {
87
91
  ) {
88
92
  result[camelKey] = value.slice(5).toLowerCase();
89
93
  } else if (camelKey === "content" && Array.isArray(value)) {
90
- // v1.0 proto uses "content" for message/artifact parts; map to internal "parts"
94
+ // v0.3 servers used "content" for message/artifact parts; normalize to "parts" for backward compat
91
95
  result.parts = normalizeKeys(value, false);
92
96
  } else if (camelKey !== "parts" || !("parts" in result)) {
93
- // skip "parts" if "content" already mapped it (prefer content over parts)
97
+ // dedup: "content" was already mapped to parts above; don't overwrite
94
98
  result[camelKey] = isOpaqueChild ? value : normalizeKeys(value, false);
95
99
  }
96
100
  }
@@ -111,8 +115,7 @@ function toWireTaskState(state: A2ATaskState): string {
111
115
  }
112
116
 
113
117
  function toWireMessage(msg: A2AMessage): unknown {
114
- const { parts, ...rest } = msg;
115
- return { ...rest, role: toWireRole(msg.role), content: parts };
118
+ return { ...msg, role: toWireRole(msg.role) };
116
119
  }
117
120
 
118
121
  function discriminateStreamResponse(
@@ -158,6 +161,10 @@ export class A2AClient {
158
161
  private basePath: string;
159
162
  private tenant: string | undefined;
160
163
  private extensionUris: string[] | undefined;
164
+ private fetchOptions: Omit<
165
+ RequestInit,
166
+ "headers" | "body" | "method" | "signal"
167
+ >;
161
168
  private headersFn:
162
169
  | Record<string, string>
163
170
  | (() => Record<string, string> | Promise<Record<string, string>>);
@@ -169,6 +176,14 @@ export class A2AClient {
169
176
  : "";
170
177
  this.tenant = options.tenant;
171
178
  this.extensionUris = options.extensions;
179
+ const {
180
+ headers: _h,
181
+ body: _b,
182
+ method: _m,
183
+ signal: _s,
184
+ ...safeFetchOptions
185
+ } = (options.fetchOptions ?? {}) as RequestInit;
186
+ this.fetchOptions = safeFetchOptions;
172
187
  this.headersFn = options.headers ?? {};
173
188
  }
174
189
 
@@ -229,6 +244,7 @@ export class A2AClient {
229
244
  const isGet = !options.method || options.method.toUpperCase() === "GET";
230
245
  const headers = await this.getHeaders(!isGet);
231
246
  const response = await fetch(`${this.baseUrl}${path}`, {
247
+ ...this.fetchOptions,
232
248
  ...options,
233
249
  headers: {
234
250
  ...headers,
@@ -247,7 +263,11 @@ export class A2AClient {
247
263
  async getAgentCard(signal?: AbortSignal): Promise<A2AAgentCard> {
248
264
  const headers = await this.getHeaders(false); // GET: no Content-Type
249
265
  const url = `${this.baseUrl}/.well-known/agent-card.json`;
250
- const response = await fetch(url, { headers, ...signalInit(signal) });
266
+ const response = await fetch(url, {
267
+ ...this.fetchOptions,
268
+ headers,
269
+ ...signalInit(signal),
270
+ });
251
271
  if (!response.ok) {
252
272
  await this.throwResponseError(response);
253
273
  }
@@ -310,6 +330,7 @@ export class A2AClient {
310
330
  const response = await fetch(
311
331
  `${this.baseUrl}${this.getBasePath()}/message:stream`,
312
332
  {
333
+ ...this.fetchOptions,
313
334
  method: "POST",
314
335
  headers,
315
336
  body: JSON.stringify(body),
@@ -390,7 +411,11 @@ export class A2AClient {
390
411
 
391
412
  const response = await fetch(
392
413
  `${this.baseUrl}${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}:subscribe`,
393
- { headers, ...signalInit(signal) },
414
+ {
415
+ ...this.fetchOptions,
416
+ headers,
417
+ ...signalInit(signal),
418
+ },
394
419
  );
395
420
  if (!response.ok) {
396
421
  await this.throwResponseError(response);
@@ -453,7 +478,12 @@ export class A2AClient {
453
478
  const headers = await this.getHeaders(!isGet);
454
479
  const response = await fetch(
455
480
  `${this.baseUrl}${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs/${encodeURIComponent(configId)}`,
456
- { method: "DELETE", headers, ...signalInit(signal) },
481
+ {
482
+ ...this.fetchOptions,
483
+ method: "DELETE",
484
+ headers,
485
+ ...signalInit(signal),
486
+ },
457
487
  );
458
488
  if (!response.ok) {
459
489
  await this.throwResponseError(response);
@@ -37,7 +37,7 @@ function createUserAppendMessage(text: string): AppendMessage {
37
37
  parentId: null,
38
38
  role: "user",
39
39
  content: [{ type: "text", text }],
40
- };
40
+ } as unknown as AppendMessage;
41
41
  }
42
42
 
43
43
  function statusUpdateEvent(state: string, text?: string): A2AStreamEvent {
@@ -90,7 +90,7 @@ describe("A2AThreadRuntimeCore", () => {
90
90
  ) {
91
91
  return new A2AThreadRuntimeCore({
92
92
  client: createMockClient(clientOverrides),
93
- notifyUpdate,
93
+ notifyUpdate: notifyUpdate as unknown as () => void,
94
94
  ...coreOverrides,
95
95
  });
96
96
  }
@@ -369,7 +369,7 @@ describe("A2AThreadRuntimeCore", () => {
369
369
  }),
370
370
  }),
371
371
  onArtifactComplete,
372
- notifyUpdate,
372
+ notifyUpdate: notifyUpdate as unknown as () => void,
373
373
  });
374
374
 
375
375
  await core.append(createUserAppendMessage("Go"));
@@ -588,7 +588,7 @@ describe("A2AThreadRuntimeCore", () => {
588
588
  })),
589
589
  }),
590
590
  onError,
591
- notifyUpdate,
591
+ notifyUpdate: notifyUpdate as unknown as () => void,
592
592
  });
593
593
 
594
594
  await expect(core.append(createUserAppendMessage("Go"))).rejects.toThrow(
@@ -680,7 +680,7 @@ describe("A2AThreadRuntimeCore", () => {
680
680
  createdAt: new Date(),
681
681
  content: [{ type: "text", text: "External" }],
682
682
  status: { type: "complete", reason: "stop" },
683
- } as ThreadMessage,
683
+ } as unknown as ThreadMessage,
684
684
  ];
685
685
 
686
686
  core.applyExternalMessages(msgs);
@@ -200,14 +200,12 @@ describe("taskStateToMessageStatus", () => {
200
200
  });
201
201
 
202
202
  describe("isTerminalTaskState", () => {
203
- it.each([
204
- "completed",
205
- "failed",
206
- "canceled",
207
- "rejected",
208
- ] as A2ATaskState[])("returns true for %s", (state) => {
209
- expect(isTerminalTaskState(state)).toBe(true);
210
- });
203
+ it.each(["completed", "failed", "canceled", "rejected"] as A2ATaskState[])(
204
+ "returns true for %s",
205
+ (state) => {
206
+ expect(isTerminalTaskState(state)).toBe(true);
207
+ },
208
+ );
211
209
 
212
210
  it.each([
213
211
  "submitted",
@@ -221,12 +219,12 @@ describe("isTerminalTaskState", () => {
221
219
  });
222
220
 
223
221
  describe("isInterruptedTaskState", () => {
224
- it.each([
225
- "input_required",
226
- "auth_required",
227
- ] as A2ATaskState[])("returns true for %s", (state) => {
228
- expect(isInterruptedTaskState(state)).toBe(true);
229
- });
222
+ it.each(["input_required", "auth_required"] as A2ATaskState[])(
223
+ "returns true for %s",
224
+ (state) => {
225
+ expect(isInterruptedTaskState(state)).toBe(true);
226
+ },
227
+ );
230
228
 
231
229
  it.each([
232
230
  "submitted",
@@ -1,17 +1,20 @@
1
- /// <reference types="@assistant-ui/core/store" />
2
1
  "use client";
3
2
 
4
3
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
5
4
  import {
6
5
  useExternalStoreRuntime,
6
+ useExternalStoreSharedOptions,
7
7
  useRuntimeAdapters,
8
8
  } from "@assistant-ui/core/react";
9
9
  import type {
10
10
  AssistantRuntime,
11
11
  AppendMessage,
12
12
  AttachmentAdapter,
13
+ DictationAdapter,
13
14
  ExternalStoreAdapter,
15
+ ExternalStoreSharedOptions,
14
16
  FeedbackAdapter,
17
+ RealtimeVoiceAdapter,
15
18
  SpeechSynthesisAdapter,
16
19
  ThreadHistoryAdapter,
17
20
  ThreadMessage,
@@ -76,7 +79,7 @@ export type UseA2AThreadListAdapter = {
76
79
 
77
80
  // --- Options ---
78
81
 
79
- export type UseA2ARuntimeOptions = {
82
+ export type UseA2ARuntimeOptions = ExternalStoreSharedOptions & {
80
83
  /** Pre-built A2A client instance. Provide this OR baseUrl. */
81
84
  client?: A2AClient;
82
85
  /** Base URL of the A2A server. Used to create a client if `client` is not provided. */
@@ -89,6 +92,8 @@ export type UseA2ARuntimeOptions = {
89
92
  headers?: A2AClientOptions["headers"];
90
93
  /** A2A extension URIs to negotiate. Only used with baseUrl. */
91
94
  extensions?: string[];
95
+ /** Extra fetch options (e.g. `{ credentials: 'include' }`). Only used with `baseUrl`. */
96
+ fetchOptions?: A2AClientOptions["fetchOptions"];
92
97
 
93
98
  /** Initial context ID for the conversation. */
94
99
  contextId?: string;
@@ -105,6 +110,8 @@ export type UseA2ARuntimeOptions = {
105
110
  adapters?: {
106
111
  attachments?: AttachmentAdapter;
107
112
  speech?: SpeechSynthesisAdapter;
113
+ dictation?: DictationAdapter;
114
+ voice?: RealtimeVoiceAdapter;
108
115
  feedback?: FeedbackAdapter;
109
116
  history?: ThreadHistoryAdapter;
110
117
  threadList?: UseA2AThreadListAdapter;
@@ -132,6 +139,7 @@ export function useA2ARuntime(options: UseA2ARuntimeOptions): AssistantRuntime {
132
139
  tenant: options.tenant,
133
140
  headers: options.headers,
134
141
  extensions: options.extensions,
142
+ fetchOptions: options.fetchOptions,
135
143
  });
136
144
  } else {
137
145
  throw new Error("useA2ARuntime requires either `client` or `baseUrl`");
@@ -198,6 +206,8 @@ export function useA2ARuntime(options: UseA2ARuntimeOptions): AssistantRuntime {
198
206
  () => ({
199
207
  attachments: adapters?.attachments ?? runtimeAdapters?.attachments,
200
208
  speech: adapters?.speech,
209
+ dictation: adapters?.dictation,
210
+ voice: adapters?.voice,
201
211
  feedback: adapters?.feedback,
202
212
  threadList,
203
213
  }),
@@ -205,10 +215,12 @@ export function useA2ARuntime(options: UseA2ARuntimeOptions): AssistantRuntime {
205
215
  );
206
216
 
207
217
  // Build store adapter
218
+ const shared = useExternalStoreSharedOptions(options);
208
219
  const store = useMemo(() => {
209
220
  void _version;
210
221
 
211
222
  return {
223
+ ...shared,
212
224
  isLoading: core.isLoading,
213
225
  messages: core.getMessages(),
214
226
  isRunning: core.isRunning(),
@@ -228,7 +240,7 @@ export function useA2ARuntime(options: UseA2ARuntimeOptions): AssistantRuntime {
228
240
  core.applyExternalMessages(messages),
229
241
  adapters: adapterAdapters,
230
242
  } satisfies ExternalStoreAdapter<ThreadMessage>;
231
- }, [adapterAdapters, core, _version]);
243
+ }, [adapterAdapters, core, _version, shared]);
232
244
 
233
245
  const runtime = useExternalStoreRuntime(store);
234
246
 
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,aAAa,EACb,UAAU,EACV,eAAe,EACf,eAAe,GAChB,2BAAwB;AACzB,YAAY,EACV,oBAAoB,EACpB,uBAAuB,GACxB,2BAAwB;AAGzB,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,uBAAoB;AAClD,YAAY,EAAE,gBAAgB,EAAE,uBAAoB;AAGpD,YAAY,EACV,OAAO,EACP,OAAO,EACP,UAAU,EACV,YAAY,EACZ,aAAa,EACb,WAAW,EACX,OAAO,EACP,cAAc,EACd,wBAAwB,EACxB,0BAA0B,EAC1B,qBAAqB,EACrB,6BAA6B,EAC7B,0CAA0C,EAC1C,2BAA2B,EAC3B,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,EACZ,uBAAuB,EACvB,yBAAyB,EACzB,6BAA6B,EAC7B,6BAA6B,EAC7B,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,EACpB,aAAa,EACb,uBAAuB,EACvB,8BAA8B,EAC9B,0BAA0B,EAC1B,iBAAiB,EACjB,sBAAsB,EACtB,qBAAqB,EACrB,aAAa,EACb,oBAAoB,EACpB,iBAAiB,EACjB,YAAY,GACb,mBAAgB;AACjB,OAAO,EAAE,oBAAoB,EAAE,mBAAgB;AAG/C,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,wBAAwB,EACxB,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,GACvB,yBAAsB"}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY;AACZ,OAAO,EACL,aAAa,EACb,UAAU,EACV,eAAe,EACf,eAAe,GAChB,2BAAwB;AAMzB,SAAS;AACT,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,uBAAoB;AAyClD,OAAO,EAAE,oBAAoB,EAAE,mBAAgB;AAE/C,4CAA4C;AAC5C,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,wBAAwB,EACxB,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,GACvB,yBAAsB"}