@assistant-ui/react-a2a 0.2.16 → 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/dist/A2AClient.d.ts +3 -1
- package/dist/A2AClient.d.ts.map +1 -1
- package/dist/A2AClient.js +10 -4
- package/dist/A2AClient.js.map +1 -1
- package/dist/A2AThreadRuntimeCore.d.ts.map +1 -1
- package/dist/useA2ARuntime.d.ts +6 -4
- package/dist/useA2ARuntime.d.ts.map +1 -1
- package/dist/useA2ARuntime.js +9 -3
- package/dist/useA2ARuntime.js.map +1 -1
- package/package.json +4 -4
- package/src/A2AClient.test.ts +115 -6
- package/src/A2AClient.ts +37 -7
- package/src/A2AThreadRuntimeCore.test.ts +5 -5
- package/src/conversions.test.ts +12 -14
- package/src/useA2ARuntime.ts +15 -3
package/dist/A2AClient.d.ts
CHANGED
|
@@ -6,7 +6,8 @@ type A2AClientOptions = {
|
|
|
6
6
|
basePath?: string | undefined; /** Optional tenant ID for multi-tenant servers. */
|
|
7
7
|
tenant?: string | undefined;
|
|
8
8
|
headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>) | undefined; /** A2A extension URIs to negotiate. Sent as A2A-Extensions header. */
|
|
9
|
-
extensions?: string[] | undefined;
|
|
9
|
+
extensions?: string[] | undefined; /** Extra fetch options applied to every request. */
|
|
10
|
+
fetchOptions?: Omit<RequestInit, "headers" | "body" | "method" | "signal"> | undefined;
|
|
10
11
|
};
|
|
11
12
|
declare class A2AError extends Error {
|
|
12
13
|
code: number;
|
|
@@ -19,6 +20,7 @@ declare class A2AClient {
|
|
|
19
20
|
private basePath;
|
|
20
21
|
private tenant;
|
|
21
22
|
private extensionUris;
|
|
23
|
+
private fetchOptions;
|
|
22
24
|
private headersFn;
|
|
23
25
|
constructor(options: A2AClientOptions);
|
|
24
26
|
private getBasePath;
|
package/dist/A2AClient.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"A2AClient.d.ts","names":[],"sources":["../src/A2AClient.ts"],"mappings":";;;KAgBY,gBAAA;EACV,OAAA,UADU;EAGV,QAAA;EAEA,MAAA;EACA,OAAA,GACI,MAAA,0BACO,MAAA,mBAAyB,OAAA,CAAQ,MAAA,gCAAA;EAG5C,UAAA;AAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"A2AClient.d.ts","names":[],"sources":["../src/A2AClient.ts"],"mappings":";;;KAgBY,gBAAA;EACV,OAAA,UADU;EAGV,QAAA;EAEA,MAAA;EACA,OAAA,GACI,MAAA,0BACO,MAAA,mBAAyB,OAAA,CAAQ,MAAA,gCAAA;EAG5C,UAAA,yBAGS;EADT,YAAA,GACI,IAAA,CAAK,WAAA;AAAA;AAAA,cAIE,QAAA,SAAiB,KAAK;EACjC,IAAA;EACA,MAAA;EACA,OAAA;cAEY,IAAA,EAAM,YAAA;AAAA;AAAA,cAuHP,SAAA;EAAA,QACH,OAAA;EAAA,QACA,QAAA;EAAA,QACA,MAAA;EAAA,QACA,aAAA;EAAA,QACA,YAAA;EAAA,QAIA,SAAA;cAII,OAAA,EAAS,gBAAA;EAAA,QAkBb,WAAA;EAAA,QAIM,UAAA;EAAA,QAqBA,kBAAA;EAAA,QAyBA,SAAA;EAuBR,YAAA,CAAa,MAAA,GAAS,WAAA,GAAc,OAAA,CAAQ,YAAA;EAe5C,oBAAA,CAAqB,MAAA,GAAS,WAAA,GAAc,OAAA,CAAQ,YAAA;EASpD,WAAA,CACJ,OAAA,EAAS,UAAA,EACT,aAAA,GAAgB,2BAAA,EAChB,QAAA,GAAW,MAAA,mBACX,MAAA,GAAS,WAAA,GACR,OAAA,CAAQ,OAAA,GAAU,UAAA;EAuBd,aAAA,CACL,OAAA,EAAS,UAAA,EACT,aAAA,GAAgB,2BAAA,EAChB,QAAA,GAAW,MAAA,mBACX,MAAA,GAAS,WAAA,GACR,cAAA,CAAe,cAAA;EA6BZ,OAAA,CACJ,MAAA,UACA,aAAA,WACA,MAAA,GAAS,WAAA,GACR,OAAA,CAAQ,OAAA;EAaL,SAAA,CACJ,OAAA,GAAU,mBAAA,EACV,MAAA,GAAS,WAAA,GACR,OAAA,CAAQ,oBAAA;EAoBL,UAAA,CACJ,MAAA,UACA,QAAA,GAAW,MAAA,mBACX,MAAA,GAAS,WAAA,GACR,OAAA,CAAQ,OAAA;EAYJ,eAAA,CACL,MAAA,UACA,MAAA,GAAS,WAAA,GACR,cAAA,CAAe,cAAA;EAqBZ,gCAAA,CACJ,MAAA,EAAQ,6BAAA,EACR,MAAA,GAAS,WAAA,GACR,OAAA,CAAQ,6BAAA;EAaL,6BAAA,CACJ,MAAA,UACA,QAAA,UACA,MAAA,GAAS,WAAA,GACR,OAAA,CAAQ,6BAAA;EAOL,+BAAA,CACJ,MAAA,UACA,OAAA;IAAY,QAAA;IAAmB,SAAA;EAAA,GAC/B,MAAA,GAAS,WAAA,GACR,OAAA,CAAQ,0CAAA;EAYL,gCAAA,CACJ,MAAA,UACA,QAAA,UACA,MAAA,GAAS,WAAA,GACR,OAAA;EAAA,QAmBY,QAAA;AAAA"}
|
package/dist/A2AClient.js
CHANGED
|
@@ -51,11 +51,9 @@ function toWireTaskState(state) {
|
|
|
51
51
|
return `TASK_STATE_${state.toUpperCase()}`;
|
|
52
52
|
}
|
|
53
53
|
function toWireMessage(msg) {
|
|
54
|
-
const { parts, ...rest } = msg;
|
|
55
54
|
return {
|
|
56
|
-
...
|
|
57
|
-
role: toWireRole(msg.role)
|
|
58
|
-
content: parts
|
|
55
|
+
...msg,
|
|
56
|
+
role: toWireRole(msg.role)
|
|
59
57
|
};
|
|
60
58
|
}
|
|
61
59
|
function discriminateStreamResponse(data) {
|
|
@@ -85,12 +83,15 @@ var A2AClient = class {
|
|
|
85
83
|
basePath;
|
|
86
84
|
tenant;
|
|
87
85
|
extensionUris;
|
|
86
|
+
fetchOptions;
|
|
88
87
|
headersFn;
|
|
89
88
|
constructor(options) {
|
|
90
89
|
this.baseUrl = options.baseUrl.replace(/\/$/, "");
|
|
91
90
|
this.basePath = options.basePath ? `/${options.basePath.replace(/^\/|\/$/g, "")}` : "";
|
|
92
91
|
this.tenant = options.tenant;
|
|
93
92
|
this.extensionUris = options.extensions;
|
|
93
|
+
const { headers: _h, body: _b, method: _m, signal: _s, ...safeFetchOptions } = options.fetchOptions ?? {};
|
|
94
|
+
this.fetchOptions = safeFetchOptions;
|
|
94
95
|
this.headersFn = options.headers ?? {};
|
|
95
96
|
}
|
|
96
97
|
getBasePath() {
|
|
@@ -130,6 +131,7 @@ var A2AClient = class {
|
|
|
130
131
|
const isGet = !options.method || options.method.toUpperCase() === "GET";
|
|
131
132
|
const headers = await this.getHeaders(!isGet);
|
|
132
133
|
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
134
|
+
...this.fetchOptions,
|
|
133
135
|
...options,
|
|
134
136
|
headers: {
|
|
135
137
|
...headers,
|
|
@@ -143,6 +145,7 @@ var A2AClient = class {
|
|
|
143
145
|
const headers = await this.getHeaders(false);
|
|
144
146
|
const url = `${this.baseUrl}/.well-known/agent-card.json`;
|
|
145
147
|
const response = await fetch(url, {
|
|
148
|
+
...this.fetchOptions,
|
|
146
149
|
headers,
|
|
147
150
|
...signalInit(signal)
|
|
148
151
|
});
|
|
@@ -172,6 +175,7 @@ var A2AClient = class {
|
|
|
172
175
|
if (configuration) body.configuration = configuration;
|
|
173
176
|
if (metadata) body.metadata = metadata;
|
|
174
177
|
const response = await fetch(`${this.baseUrl}${this.getBasePath()}/message:stream`, {
|
|
178
|
+
...this.fetchOptions,
|
|
175
179
|
method: "POST",
|
|
176
180
|
headers,
|
|
177
181
|
body: JSON.stringify(body),
|
|
@@ -210,6 +214,7 @@ var A2AClient = class {
|
|
|
210
214
|
const headers = await this.getHeaders(false);
|
|
211
215
|
headers.Accept = "text/event-stream";
|
|
212
216
|
const response = await fetch(`${this.baseUrl}${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}:subscribe`, {
|
|
217
|
+
...this.fetchOptions,
|
|
213
218
|
headers,
|
|
214
219
|
...signalInit(signal)
|
|
215
220
|
});
|
|
@@ -238,6 +243,7 @@ var A2AClient = class {
|
|
|
238
243
|
async deleteTaskPushNotificationConfig(taskId, configId, signal) {
|
|
239
244
|
const headers = await this.getHeaders(true);
|
|
240
245
|
const response = await fetch(`${this.baseUrl}${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs/${encodeURIComponent(configId)}`, {
|
|
246
|
+
...this.fetchOptions,
|
|
241
247
|
method: "DELETE",
|
|
242
248
|
headers,
|
|
243
249
|
...signalInit(signal)
|
package/dist/A2AClient.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"A2AClient.js","names":[],"sources":["../src/A2AClient.ts"],"sourcesContent":["import type {\n A2AAgentCard,\n A2AErrorInfo,\n A2AListTaskPushNotificationConfigsResponse,\n A2AListTasksRequest,\n A2AListTasksResponse,\n A2AMessage,\n A2ARole,\n A2ASendMessageConfiguration,\n A2AStreamEvent,\n A2ATask,\n A2ATaskPushNotificationConfig,\n A2ATaskState,\n} from \"./types\";\nimport { A2A_PROTOCOL_VERSION } from \"./types\";\n\nexport type A2AClientOptions = {\n baseUrl: string;\n /** Optional path prefix for all API endpoints (e.g. \"/v1\"). Does not affect agent card discovery. */\n basePath?: string | undefined;\n /** Optional tenant ID for multi-tenant servers. */\n tenant?: string | undefined;\n headers?:\n | Record<string, string>\n | (() => Record<string, string> | Promise<Record<string, string>>)\n | undefined;\n /** A2A extension URIs to negotiate. Sent as A2A-Extensions header. */\n extensions?: string[] | undefined;\n};\n\nexport class A2AError extends Error {\n code: number;\n status: string;\n details: unknown[] | undefined;\n\n constructor(info: A2AErrorInfo) {\n super(info.message);\n this.name = \"A2AError\";\n this.code = info.code;\n this.status = info.status;\n this.details = info.details;\n }\n}\n\n// Incoming key normalization: snake_case → camelCase, plus ProtoJSON enum normalization.\nfunction toCamelCase(key: string): string {\n return key.replace(/_([a-z])/g, (_, c: string) => c.toUpperCase());\n}\n\n// Fields whose values are opaque user data (google.protobuf.Struct / Value).\n// Keys inside these objects must NOT be camelCased or have enum normalization applied.\nconst OPAQUE_FIELDS = new Set([\n \"metadata\",\n \"data\",\n \"params\",\n \"forwardedProps\",\n \"scopes\",\n]);\n\nfunction normalizeKeys(obj: unknown, opaque = false): unknown {\n if (Array.isArray(obj)) return obj.map((v) => normalizeKeys(v, opaque));\n if (obj !== null && typeof obj === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n // Inside opaque fields: preserve keys and values as-is (only recurse arrays/objects structurally)\n if (opaque) {\n result[key] =\n typeof value === \"object\" && value !== null\n ? normalizeKeys(value, true)\n : value;\n continue;\n }\n\n const camelKey = toCamelCase(key);\n const isOpaqueChild = OPAQUE_FIELDS.has(camelKey);\n\n if (\n camelKey === \"state\" &&\n typeof value === \"string\" &&\n value.startsWith(\"TASK_STATE_\")\n ) {\n result[camelKey] = value.slice(11).toLowerCase();\n } else if (\n camelKey === \"role\" &&\n typeof value === \"string\" &&\n value.startsWith(\"ROLE_\")\n ) {\n result[camelKey] = value.slice(5).toLowerCase();\n } else if (camelKey === \"content\" && Array.isArray(value)) {\n // v1.0 proto uses \"content\" for message/artifact parts; map to internal \"parts\"\n result.parts = normalizeKeys(value, false);\n } else if (camelKey !== \"parts\" || !(\"parts\" in result)) {\n // skip \"parts\" if \"content\" already mapped it (prefer content over parts)\n result[camelKey] = isOpaqueChild ? value : normalizeKeys(value, false);\n }\n }\n return result;\n }\n return obj;\n}\n\n// Outgoing enum conversion (v1.0 ProtoJSON format)\nfunction toWireRole(role: A2ARole): string {\n if (role === \"user\") return \"ROLE_USER\";\n if (role === \"agent\") return \"ROLE_AGENT\";\n return \"ROLE_UNSPECIFIED\";\n}\n\nfunction toWireTaskState(state: A2ATaskState): string {\n return `TASK_STATE_${state.toUpperCase()}`;\n}\n\nfunction toWireMessage(msg: A2AMessage): unknown {\n const { parts, ...rest } = msg;\n return { ...rest, role: toWireRole(msg.role), content: parts };\n}\n\nfunction discriminateStreamResponse(\n data: Record<string, unknown>,\n): A2AStreamEvent | null {\n if (\"task\" in data && data.task) {\n return { type: \"task\", task: data.task as A2ATask };\n }\n if (\"message\" in data && data.message) {\n return { type: \"message\", message: data.message as A2AMessage };\n }\n if (\"statusUpdate\" in data && data.statusUpdate) {\n return {\n type: \"statusUpdate\",\n event: data.statusUpdate as A2AStreamEvent extends {\n type: \"statusUpdate\";\n event: infer E;\n }\n ? E\n : never,\n };\n }\n if (\"artifactUpdate\" in data && data.artifactUpdate) {\n return {\n type: \"artifactUpdate\",\n event: data.artifactUpdate as A2AStreamEvent extends {\n type: \"artifactUpdate\";\n event: infer E;\n }\n ? E\n : never,\n };\n }\n return null;\n}\n\nfunction signalInit(signal?: AbortSignal): RequestInit {\n return signal ? { signal } : {};\n}\n\nexport class A2AClient {\n private baseUrl: string;\n private basePath: string;\n private tenant: string | undefined;\n private extensionUris: string[] | undefined;\n private headersFn:\n | Record<string, string>\n | (() => Record<string, string> | Promise<Record<string, string>>);\n\n constructor(options: A2AClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/$/, \"\");\n this.basePath = options.basePath\n ? `/${options.basePath.replace(/^\\/|\\/$/g, \"\")}`\n : \"\";\n this.tenant = options.tenant;\n this.extensionUris = options.extensions;\n this.headersFn = options.headers ?? {};\n }\n\n private getBasePath(): string {\n return `${this.basePath}${this.tenant ? `/${encodeURIComponent(this.tenant)}` : \"\"}`;\n }\n\n private async getHeaders(\n includeContentType = true,\n ): Promise<Record<string, string>> {\n const custom =\n typeof this.headersFn === \"function\"\n ? await this.headersFn()\n : this.headersFn;\n const headers: Record<string, string> = {\n Accept: \"application/a2a+json, application/json\",\n \"A2A-Version\": A2A_PROTOCOL_VERSION,\n ...custom,\n };\n if (includeContentType) {\n headers[\"Content-Type\"] = \"application/a2a+json\";\n }\n if (this.extensionUris?.length) {\n headers[\"A2A-Extensions\"] = this.extensionUris.join(\", \");\n }\n return headers;\n }\n\n private async throwResponseError(response: Response): Promise<never> {\n let errorBody: unknown;\n try {\n errorBody = await response.json();\n } catch {\n // no parseable body\n }\n\n if (errorBody && typeof errorBody === \"object\" && \"error\" in errorBody) {\n const err = (errorBody as Record<string, any>).error;\n throw new A2AError({\n code: err.code ?? response.status,\n status: err.status ?? response.statusText,\n message: err.message ?? `A2A request failed: ${response.status}`,\n details: err.details,\n });\n }\n\n throw new A2AError({\n code: response.status,\n status: response.statusText,\n message: `A2A request failed: ${response.status} ${response.statusText}`,\n });\n }\n\n private async fetchJSON<T>(\n path: string,\n options: RequestInit = {},\n ): Promise<T> {\n const isGet = !options.method || options.method.toUpperCase() === \"GET\";\n const headers = await this.getHeaders(!isGet);\n const response = await fetch(`${this.baseUrl}${path}`, {\n ...options,\n headers: {\n ...headers,\n ...(options.headers as Record<string, string>),\n },\n });\n if (!response.ok) {\n await this.throwResponseError(response);\n }\n const json = await response.json();\n return normalizeKeys(json) as T;\n }\n\n // --- Agent Card ---\n\n async getAgentCard(signal?: AbortSignal): Promise<A2AAgentCard> {\n const headers = await this.getHeaders(false); // GET: no Content-Type\n const url = `${this.baseUrl}/.well-known/agent-card.json`;\n const response = await fetch(url, { headers, ...signalInit(signal) });\n if (!response.ok) {\n await this.throwResponseError(response);\n }\n const json = await response.json();\n return normalizeKeys(json) as A2AAgentCard;\n }\n\n async getExtendedAgentCard(signal?: AbortSignal): Promise<A2AAgentCard> {\n return this.fetchJSON<A2AAgentCard>(\n `${this.getBasePath()}/extendedAgentCard`,\n signalInit(signal),\n );\n }\n\n // --- Message ---\n\n async sendMessage(\n message: A2AMessage,\n configuration?: A2ASendMessageConfiguration,\n metadata?: Record<string, unknown>,\n signal?: AbortSignal,\n ): Promise<A2ATask | A2AMessage> {\n const body: Record<string, unknown> = {\n message: toWireMessage(message),\n };\n if (configuration) body.configuration = configuration;\n if (metadata) body.metadata = metadata;\n\n const result = await this.fetchJSON<Record<string, unknown>>(\n `${this.getBasePath()}/message:send`,\n {\n method: \"POST\",\n body: JSON.stringify(body),\n ...signalInit(signal),\n },\n );\n\n // Unwrap SendMessageResponse: {task: Task} | {message: Message}\n if (\"task\" in result && result.task) return result.task as A2ATask;\n if (\"message\" in result && result.message)\n return result.message as A2AMessage;\n return result as unknown as A2ATask | A2AMessage;\n }\n\n async *streamMessage(\n message: A2AMessage,\n configuration?: A2ASendMessageConfiguration,\n metadata?: Record<string, unknown>,\n signal?: AbortSignal,\n ): AsyncGenerator<A2AStreamEvent> {\n const headers = await this.getHeaders(true);\n headers.Accept = \"text/event-stream\";\n\n const body: Record<string, unknown> = {\n message: toWireMessage(message),\n };\n if (configuration) body.configuration = configuration;\n if (metadata) body.metadata = metadata;\n\n const response = await fetch(\n `${this.baseUrl}${this.getBasePath()}/message:stream`,\n {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n ...signalInit(signal),\n },\n );\n if (!response.ok) {\n await this.throwResponseError(response);\n }\n\n yield* this.parseSSE(response);\n }\n\n // --- Tasks ---\n\n async getTask(\n taskId: string,\n historyLength?: number,\n signal?: AbortSignal,\n ): Promise<A2ATask> {\n const params = new URLSearchParams();\n if (historyLength !== undefined) {\n // Proto field name for HTTP transcoding query params\n params.set(\"history_length\", String(historyLength));\n }\n const qs = params.toString();\n return this.fetchJSON<A2ATask>(\n `${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}${qs ? `?${qs}` : \"\"}`,\n signalInit(signal),\n );\n }\n\n async listTasks(\n request?: A2AListTasksRequest,\n signal?: AbortSignal,\n ): Promise<A2AListTasksResponse> {\n const params = new URLSearchParams();\n if (request?.contextId) params.set(\"context_id\", request.contextId);\n if (request?.status) params.set(\"status\", toWireTaskState(request.status));\n if (request?.pageSize !== undefined)\n params.set(\"page_size\", String(request.pageSize));\n if (request?.pageToken) params.set(\"page_token\", request.pageToken);\n if (request?.historyLength !== undefined)\n params.set(\"history_length\", String(request.historyLength));\n if (request?.statusTimestampAfter)\n params.set(\"status_timestamp_after\", request.statusTimestampAfter);\n if (request?.includeArtifacts !== undefined)\n params.set(\"include_artifacts\", String(request.includeArtifacts));\n const qs = params.toString();\n return this.fetchJSON<A2AListTasksResponse>(\n `${this.getBasePath()}/tasks${qs ? `?${qs}` : \"\"}`,\n signalInit(signal),\n );\n }\n\n async cancelTask(\n taskId: string,\n metadata?: Record<string, unknown>,\n signal?: AbortSignal,\n ): Promise<A2ATask> {\n const body = metadata ? { metadata } : {};\n return this.fetchJSON<A2ATask>(\n `${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}:cancel`,\n {\n method: \"POST\",\n body: JSON.stringify(body),\n ...signalInit(signal),\n },\n );\n }\n\n async *subscribeToTask(\n taskId: string,\n signal?: AbortSignal,\n ): AsyncGenerator<A2AStreamEvent> {\n const headers = await this.getHeaders(false); // GET: no Content-Type\n headers.Accept = \"text/event-stream\";\n\n const response = await fetch(\n `${this.baseUrl}${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}:subscribe`,\n { headers, ...signalInit(signal) },\n );\n if (!response.ok) {\n await this.throwResponseError(response);\n }\n\n yield* this.parseSSE(response);\n }\n\n // --- Push Notification Configs ---\n\n async createTaskPushNotificationConfig(\n config: A2ATaskPushNotificationConfig,\n signal?: AbortSignal,\n ): Promise<A2ATaskPushNotificationConfig> {\n const taskId = config.taskId;\n if (!taskId) throw new Error(\"taskId is required\");\n return this.fetchJSON<A2ATaskPushNotificationConfig>(\n `${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs`,\n {\n method: \"POST\",\n body: JSON.stringify(config),\n ...signalInit(signal),\n },\n );\n }\n\n async getTaskPushNotificationConfig(\n taskId: string,\n configId: string,\n signal?: AbortSignal,\n ): Promise<A2ATaskPushNotificationConfig> {\n return this.fetchJSON<A2ATaskPushNotificationConfig>(\n `${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs/${encodeURIComponent(configId)}`,\n signalInit(signal),\n );\n }\n\n async listTaskPushNotificationConfigs(\n taskId: string,\n options?: { pageSize?: number; pageToken?: string },\n signal?: AbortSignal,\n ): Promise<A2AListTaskPushNotificationConfigsResponse> {\n const params = new URLSearchParams();\n if (options?.pageSize !== undefined)\n params.set(\"page_size\", String(options.pageSize));\n if (options?.pageToken) params.set(\"page_token\", options.pageToken);\n const qs = params.toString();\n return this.fetchJSON<A2AListTaskPushNotificationConfigsResponse>(\n `${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs${qs ? `?${qs}` : \"\"}`,\n signalInit(signal),\n );\n }\n\n async deleteTaskPushNotificationConfig(\n taskId: string,\n configId: string,\n signal?: AbortSignal,\n ): Promise<void> {\n const isGet = false;\n const headers = await this.getHeaders(!isGet);\n const response = await fetch(\n `${this.baseUrl}${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs/${encodeURIComponent(configId)}`,\n { method: \"DELETE\", headers, ...signalInit(signal) },\n );\n if (!response.ok) {\n await this.throwResponseError(response);\n }\n }\n\n // --- SSE Parsing ---\n\n private async *parseSSE(response: Response): AsyncGenerator<A2AStreamEvent> {\n const reader = response.body?.getReader();\n if (!reader) throw new Error(\"No response body\");\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n let eventEnd: number = buffer.indexOf(\"\\n\\n\");\n while (eventEnd !== -1) {\n const eventText = buffer.slice(0, eventEnd);\n buffer = buffer.slice(eventEnd + 2);\n\n const dataLines: string[] = [];\n\n for (const line of eventText.split(\"\\n\")) {\n const trimmed = line.replace(/\\r$/, \"\");\n if (trimmed.startsWith(\"data:\")) {\n dataLines.push(trimmed.slice(5).trim());\n }\n // event:, id:, retry: lines are parsed but not used —\n // we discriminate event type from the JSON payload.\n }\n\n if (dataLines.length === 0) continue;\n\n try {\n let parsed = JSON.parse(dataLines.join(\"\\n\"));\n\n // Unwrap JSON-RPC envelope if present\n if (\n parsed &&\n typeof parsed === \"object\" &&\n \"jsonrpc\" in parsed &&\n \"result\" in parsed\n ) {\n parsed = parsed.result;\n }\n\n const normalized = normalizeKeys(parsed) as Record<string, unknown>;\n const event = discriminateStreamResponse(normalized);\n if (event) yield event;\n } catch {\n // Skip malformed events\n }\n eventEnd = buffer.indexOf(\"\\n\\n\");\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"A2AClient.js","names":[],"sources":["../src/A2AClient.ts"],"sourcesContent":["import type {\n A2AAgentCard,\n A2AErrorInfo,\n A2AListTaskPushNotificationConfigsResponse,\n A2AListTasksRequest,\n A2AListTasksResponse,\n A2AMessage,\n A2ARole,\n A2ASendMessageConfiguration,\n A2AStreamEvent,\n A2ATask,\n A2ATaskPushNotificationConfig,\n A2ATaskState,\n} from \"./types\";\nimport { A2A_PROTOCOL_VERSION } from \"./types\";\n\nexport type A2AClientOptions = {\n baseUrl: string;\n /** Optional path prefix for all API endpoints (e.g. \"/v1\"). Does not affect agent card discovery. */\n basePath?: string | undefined;\n /** Optional tenant ID for multi-tenant servers. */\n tenant?: string | undefined;\n headers?:\n | Record<string, string>\n | (() => Record<string, string> | Promise<Record<string, string>>)\n | undefined;\n /** A2A extension URIs to negotiate. Sent as A2A-Extensions header. */\n extensions?: string[] | undefined;\n /** Extra fetch options applied to every request. */\n fetchOptions?:\n | Omit<RequestInit, \"headers\" | \"body\" | \"method\" | \"signal\">\n | undefined;\n};\n\nexport class A2AError extends Error {\n code: number;\n status: string;\n details: unknown[] | undefined;\n\n constructor(info: A2AErrorInfo) {\n super(info.message);\n this.name = \"A2AError\";\n this.code = info.code;\n this.status = info.status;\n this.details = info.details;\n }\n}\n\n// Incoming key normalization: snake_case → camelCase, plus ProtoJSON enum normalization.\nfunction toCamelCase(key: string): string {\n return key.replace(/_([a-z])/g, (_, c: string) => c.toUpperCase());\n}\n\n// Fields whose values are opaque user data (google.protobuf.Struct / Value).\n// Keys inside these objects must NOT be camelCased or have enum normalization applied.\nconst OPAQUE_FIELDS = new Set([\n \"metadata\",\n \"data\",\n \"params\",\n \"forwardedProps\",\n \"scopes\",\n]);\n\nfunction normalizeKeys(obj: unknown, opaque = false): unknown {\n if (Array.isArray(obj)) return obj.map((v) => normalizeKeys(v, opaque));\n if (obj !== null && typeof obj === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n // Inside opaque fields: preserve keys and values as-is (only recurse arrays/objects structurally)\n if (opaque) {\n result[key] =\n typeof value === \"object\" && value !== null\n ? normalizeKeys(value, true)\n : value;\n continue;\n }\n\n const camelKey = toCamelCase(key);\n const isOpaqueChild = OPAQUE_FIELDS.has(camelKey);\n\n if (\n camelKey === \"state\" &&\n typeof value === \"string\" &&\n value.startsWith(\"TASK_STATE_\")\n ) {\n result[camelKey] = value.slice(11).toLowerCase();\n } else if (\n camelKey === \"role\" &&\n typeof value === \"string\" &&\n value.startsWith(\"ROLE_\")\n ) {\n result[camelKey] = value.slice(5).toLowerCase();\n } else if (camelKey === \"content\" && Array.isArray(value)) {\n // v0.3 servers used \"content\" for message/artifact parts; normalize to \"parts\" for backward compat\n result.parts = normalizeKeys(value, false);\n } else if (camelKey !== \"parts\" || !(\"parts\" in result)) {\n // dedup: \"content\" was already mapped to parts above; don't overwrite\n result[camelKey] = isOpaqueChild ? value : normalizeKeys(value, false);\n }\n }\n return result;\n }\n return obj;\n}\n\n// Outgoing enum conversion (v1.0 ProtoJSON format)\nfunction toWireRole(role: A2ARole): string {\n if (role === \"user\") return \"ROLE_USER\";\n if (role === \"agent\") return \"ROLE_AGENT\";\n return \"ROLE_UNSPECIFIED\";\n}\n\nfunction toWireTaskState(state: A2ATaskState): string {\n return `TASK_STATE_${state.toUpperCase()}`;\n}\n\nfunction toWireMessage(msg: A2AMessage): unknown {\n return { ...msg, role: toWireRole(msg.role) };\n}\n\nfunction discriminateStreamResponse(\n data: Record<string, unknown>,\n): A2AStreamEvent | null {\n if (\"task\" in data && data.task) {\n return { type: \"task\", task: data.task as A2ATask };\n }\n if (\"message\" in data && data.message) {\n return { type: \"message\", message: data.message as A2AMessage };\n }\n if (\"statusUpdate\" in data && data.statusUpdate) {\n return {\n type: \"statusUpdate\",\n event: data.statusUpdate as A2AStreamEvent extends {\n type: \"statusUpdate\";\n event: infer E;\n }\n ? E\n : never,\n };\n }\n if (\"artifactUpdate\" in data && data.artifactUpdate) {\n return {\n type: \"artifactUpdate\",\n event: data.artifactUpdate as A2AStreamEvent extends {\n type: \"artifactUpdate\";\n event: infer E;\n }\n ? E\n : never,\n };\n }\n return null;\n}\n\nfunction signalInit(signal?: AbortSignal): RequestInit {\n return signal ? { signal } : {};\n}\n\nexport class A2AClient {\n private baseUrl: string;\n private basePath: string;\n private tenant: string | undefined;\n private extensionUris: string[] | undefined;\n private fetchOptions: Omit<\n RequestInit,\n \"headers\" | \"body\" | \"method\" | \"signal\"\n >;\n private headersFn:\n | Record<string, string>\n | (() => Record<string, string> | Promise<Record<string, string>>);\n\n constructor(options: A2AClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/$/, \"\");\n this.basePath = options.basePath\n ? `/${options.basePath.replace(/^\\/|\\/$/g, \"\")}`\n : \"\";\n this.tenant = options.tenant;\n this.extensionUris = options.extensions;\n const {\n headers: _h,\n body: _b,\n method: _m,\n signal: _s,\n ...safeFetchOptions\n } = (options.fetchOptions ?? {}) as RequestInit;\n this.fetchOptions = safeFetchOptions;\n this.headersFn = options.headers ?? {};\n }\n\n private getBasePath(): string {\n return `${this.basePath}${this.tenant ? `/${encodeURIComponent(this.tenant)}` : \"\"}`;\n }\n\n private async getHeaders(\n includeContentType = true,\n ): Promise<Record<string, string>> {\n const custom =\n typeof this.headersFn === \"function\"\n ? await this.headersFn()\n : this.headersFn;\n const headers: Record<string, string> = {\n Accept: \"application/a2a+json, application/json\",\n \"A2A-Version\": A2A_PROTOCOL_VERSION,\n ...custom,\n };\n if (includeContentType) {\n headers[\"Content-Type\"] = \"application/a2a+json\";\n }\n if (this.extensionUris?.length) {\n headers[\"A2A-Extensions\"] = this.extensionUris.join(\", \");\n }\n return headers;\n }\n\n private async throwResponseError(response: Response): Promise<never> {\n let errorBody: unknown;\n try {\n errorBody = await response.json();\n } catch {\n // no parseable body\n }\n\n if (errorBody && typeof errorBody === \"object\" && \"error\" in errorBody) {\n const err = (errorBody as Record<string, any>).error;\n throw new A2AError({\n code: err.code ?? response.status,\n status: err.status ?? response.statusText,\n message: err.message ?? `A2A request failed: ${response.status}`,\n details: err.details,\n });\n }\n\n throw new A2AError({\n code: response.status,\n status: response.statusText,\n message: `A2A request failed: ${response.status} ${response.statusText}`,\n });\n }\n\n private async fetchJSON<T>(\n path: string,\n options: RequestInit = {},\n ): Promise<T> {\n const isGet = !options.method || options.method.toUpperCase() === \"GET\";\n const headers = await this.getHeaders(!isGet);\n const response = await fetch(`${this.baseUrl}${path}`, {\n ...this.fetchOptions,\n ...options,\n headers: {\n ...headers,\n ...(options.headers as Record<string, string>),\n },\n });\n if (!response.ok) {\n await this.throwResponseError(response);\n }\n const json = await response.json();\n return normalizeKeys(json) as T;\n }\n\n // --- Agent Card ---\n\n async getAgentCard(signal?: AbortSignal): Promise<A2AAgentCard> {\n const headers = await this.getHeaders(false); // GET: no Content-Type\n const url = `${this.baseUrl}/.well-known/agent-card.json`;\n const response = await fetch(url, {\n ...this.fetchOptions,\n headers,\n ...signalInit(signal),\n });\n if (!response.ok) {\n await this.throwResponseError(response);\n }\n const json = await response.json();\n return normalizeKeys(json) as A2AAgentCard;\n }\n\n async getExtendedAgentCard(signal?: AbortSignal): Promise<A2AAgentCard> {\n return this.fetchJSON<A2AAgentCard>(\n `${this.getBasePath()}/extendedAgentCard`,\n signalInit(signal),\n );\n }\n\n // --- Message ---\n\n async sendMessage(\n message: A2AMessage,\n configuration?: A2ASendMessageConfiguration,\n metadata?: Record<string, unknown>,\n signal?: AbortSignal,\n ): Promise<A2ATask | A2AMessage> {\n const body: Record<string, unknown> = {\n message: toWireMessage(message),\n };\n if (configuration) body.configuration = configuration;\n if (metadata) body.metadata = metadata;\n\n const result = await this.fetchJSON<Record<string, unknown>>(\n `${this.getBasePath()}/message:send`,\n {\n method: \"POST\",\n body: JSON.stringify(body),\n ...signalInit(signal),\n },\n );\n\n // Unwrap SendMessageResponse: {task: Task} | {message: Message}\n if (\"task\" in result && result.task) return result.task as A2ATask;\n if (\"message\" in result && result.message)\n return result.message as A2AMessage;\n return result as unknown as A2ATask | A2AMessage;\n }\n\n async *streamMessage(\n message: A2AMessage,\n configuration?: A2ASendMessageConfiguration,\n metadata?: Record<string, unknown>,\n signal?: AbortSignal,\n ): AsyncGenerator<A2AStreamEvent> {\n const headers = await this.getHeaders(true);\n headers.Accept = \"text/event-stream\";\n\n const body: Record<string, unknown> = {\n message: toWireMessage(message),\n };\n if (configuration) body.configuration = configuration;\n if (metadata) body.metadata = metadata;\n\n const response = await fetch(\n `${this.baseUrl}${this.getBasePath()}/message:stream`,\n {\n ...this.fetchOptions,\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n ...signalInit(signal),\n },\n );\n if (!response.ok) {\n await this.throwResponseError(response);\n }\n\n yield* this.parseSSE(response);\n }\n\n // --- Tasks ---\n\n async getTask(\n taskId: string,\n historyLength?: number,\n signal?: AbortSignal,\n ): Promise<A2ATask> {\n const params = new URLSearchParams();\n if (historyLength !== undefined) {\n // Proto field name for HTTP transcoding query params\n params.set(\"history_length\", String(historyLength));\n }\n const qs = params.toString();\n return this.fetchJSON<A2ATask>(\n `${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}${qs ? `?${qs}` : \"\"}`,\n signalInit(signal),\n );\n }\n\n async listTasks(\n request?: A2AListTasksRequest,\n signal?: AbortSignal,\n ): Promise<A2AListTasksResponse> {\n const params = new URLSearchParams();\n if (request?.contextId) params.set(\"context_id\", request.contextId);\n if (request?.status) params.set(\"status\", toWireTaskState(request.status));\n if (request?.pageSize !== undefined)\n params.set(\"page_size\", String(request.pageSize));\n if (request?.pageToken) params.set(\"page_token\", request.pageToken);\n if (request?.historyLength !== undefined)\n params.set(\"history_length\", String(request.historyLength));\n if (request?.statusTimestampAfter)\n params.set(\"status_timestamp_after\", request.statusTimestampAfter);\n if (request?.includeArtifacts !== undefined)\n params.set(\"include_artifacts\", String(request.includeArtifacts));\n const qs = params.toString();\n return this.fetchJSON<A2AListTasksResponse>(\n `${this.getBasePath()}/tasks${qs ? `?${qs}` : \"\"}`,\n signalInit(signal),\n );\n }\n\n async cancelTask(\n taskId: string,\n metadata?: Record<string, unknown>,\n signal?: AbortSignal,\n ): Promise<A2ATask> {\n const body = metadata ? { metadata } : {};\n return this.fetchJSON<A2ATask>(\n `${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}:cancel`,\n {\n method: \"POST\",\n body: JSON.stringify(body),\n ...signalInit(signal),\n },\n );\n }\n\n async *subscribeToTask(\n taskId: string,\n signal?: AbortSignal,\n ): AsyncGenerator<A2AStreamEvent> {\n const headers = await this.getHeaders(false); // GET: no Content-Type\n headers.Accept = \"text/event-stream\";\n\n const response = await fetch(\n `${this.baseUrl}${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}:subscribe`,\n {\n ...this.fetchOptions,\n headers,\n ...signalInit(signal),\n },\n );\n if (!response.ok) {\n await this.throwResponseError(response);\n }\n\n yield* this.parseSSE(response);\n }\n\n // --- Push Notification Configs ---\n\n async createTaskPushNotificationConfig(\n config: A2ATaskPushNotificationConfig,\n signal?: AbortSignal,\n ): Promise<A2ATaskPushNotificationConfig> {\n const taskId = config.taskId;\n if (!taskId) throw new Error(\"taskId is required\");\n return this.fetchJSON<A2ATaskPushNotificationConfig>(\n `${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs`,\n {\n method: \"POST\",\n body: JSON.stringify(config),\n ...signalInit(signal),\n },\n );\n }\n\n async getTaskPushNotificationConfig(\n taskId: string,\n configId: string,\n signal?: AbortSignal,\n ): Promise<A2ATaskPushNotificationConfig> {\n return this.fetchJSON<A2ATaskPushNotificationConfig>(\n `${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs/${encodeURIComponent(configId)}`,\n signalInit(signal),\n );\n }\n\n async listTaskPushNotificationConfigs(\n taskId: string,\n options?: { pageSize?: number; pageToken?: string },\n signal?: AbortSignal,\n ): Promise<A2AListTaskPushNotificationConfigsResponse> {\n const params = new URLSearchParams();\n if (options?.pageSize !== undefined)\n params.set(\"page_size\", String(options.pageSize));\n if (options?.pageToken) params.set(\"page_token\", options.pageToken);\n const qs = params.toString();\n return this.fetchJSON<A2AListTaskPushNotificationConfigsResponse>(\n `${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs${qs ? `?${qs}` : \"\"}`,\n signalInit(signal),\n );\n }\n\n async deleteTaskPushNotificationConfig(\n taskId: string,\n configId: string,\n signal?: AbortSignal,\n ): Promise<void> {\n const isGet = false;\n const headers = await this.getHeaders(!isGet);\n const response = await fetch(\n `${this.baseUrl}${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs/${encodeURIComponent(configId)}`,\n {\n ...this.fetchOptions,\n method: \"DELETE\",\n headers,\n ...signalInit(signal),\n },\n );\n if (!response.ok) {\n await this.throwResponseError(response);\n }\n }\n\n // --- SSE Parsing ---\n\n private async *parseSSE(response: Response): AsyncGenerator<A2AStreamEvent> {\n const reader = response.body?.getReader();\n if (!reader) throw new Error(\"No response body\");\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n let eventEnd: number = buffer.indexOf(\"\\n\\n\");\n while (eventEnd !== -1) {\n const eventText = buffer.slice(0, eventEnd);\n buffer = buffer.slice(eventEnd + 2);\n\n const dataLines: string[] = [];\n\n for (const line of eventText.split(\"\\n\")) {\n const trimmed = line.replace(/\\r$/, \"\");\n if (trimmed.startsWith(\"data:\")) {\n dataLines.push(trimmed.slice(5).trim());\n }\n // event:, id:, retry: lines are parsed but not used —\n // we discriminate event type from the JSON payload.\n }\n\n if (dataLines.length === 0) continue;\n\n try {\n let parsed = JSON.parse(dataLines.join(\"\\n\"));\n\n // Unwrap JSON-RPC envelope if present\n if (\n parsed &&\n typeof parsed === \"object\" &&\n \"jsonrpc\" in parsed &&\n \"result\" in parsed\n ) {\n parsed = parsed.result;\n }\n\n const normalized = normalizeKeys(parsed) as Record<string, unknown>;\n const event = discriminateStreamResponse(normalized);\n if (event) yield event;\n } catch {\n // Skip malformed events\n }\n eventEnd = buffer.indexOf(\"\\n\\n\");\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n}\n"],"mappings":";;AAkCA,IAAa,WAAb,cAA8B,MAAM;CAClC;CACA;CACA;CAEA,YAAY,MAAoB;EAC9B,MAAM,KAAK,OAAO;EAClB,KAAK,OAAO;EACZ,KAAK,OAAO,KAAK;EACjB,KAAK,SAAS,KAAK;EACnB,KAAK,UAAU,KAAK;CACtB;AACF;AAGA,SAAS,YAAY,KAAqB;CACxC,OAAO,IAAI,QAAQ,cAAc,GAAG,MAAc,EAAE,YAAY,CAAC;AACnE;AAIA,MAAM,gBAAgB,IAAI,IAAI;CAC5B;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAAS,cAAc,KAAc,SAAS,OAAgB;CAC5D,IAAI,MAAM,QAAQ,GAAG,GAAG,OAAO,IAAI,KAAK,MAAM,cAAc,GAAG,MAAM,CAAC;CACtE,IAAI,QAAQ,QAAQ,OAAO,QAAQ,UAAU;EAC3C,MAAM,SAAkC,CAAC;EACzC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,GAA8B,GAAG;GAEzE,IAAI,QAAQ;IACV,OAAO,OACL,OAAO,UAAU,YAAY,UAAU,OACnC,cAAc,OAAO,IAAI,IACzB;IACN;GACF;GAEA,MAAM,WAAW,YAAY,GAAG;GAChC,MAAM,gBAAgB,cAAc,IAAI,QAAQ;GAEhD,IACE,aAAa,WACb,OAAO,UAAU,YACjB,MAAM,WAAW,aAAa,GAE9B,OAAO,YAAY,MAAM,MAAM,EAAE,EAAE,YAAY;QAC1C,IACL,aAAa,UACb,OAAO,UAAU,YACjB,MAAM,WAAW,OAAO,GAExB,OAAO,YAAY,MAAM,MAAM,CAAC,EAAE,YAAY;QACzC,IAAI,aAAa,aAAa,MAAM,QAAQ,KAAK,GAEtD,OAAO,QAAQ,cAAc,OAAO,KAAK;QACpC,IAAI,aAAa,WAAW,EAAE,WAAW,SAE9C,OAAO,YAAY,gBAAgB,QAAQ,cAAc,OAAO,KAAK;EAEzE;EACA,OAAO;CACT;CACA,OAAO;AACT;AAGA,SAAS,WAAW,MAAuB;CACzC,IAAI,SAAS,QAAQ,OAAO;CAC5B,IAAI,SAAS,SAAS,OAAO;CAC7B,OAAO;AACT;AAEA,SAAS,gBAAgB,OAA6B;CACpD,OAAO,cAAc,MAAM,YAAY;AACzC;AAEA,SAAS,cAAc,KAA0B;CAC/C,OAAO;EAAE,GAAG;EAAK,MAAM,WAAW,IAAI,IAAI;CAAE;AAC9C;AAEA,SAAS,2BACP,MACuB;CACvB,IAAI,UAAU,QAAQ,KAAK,MACzB,OAAO;EAAE,MAAM;EAAQ,MAAM,KAAK;CAAgB;CAEpD,IAAI,aAAa,QAAQ,KAAK,SAC5B,OAAO;EAAE,MAAM;EAAW,SAAS,KAAK;CAAsB;CAEhE,IAAI,kBAAkB,QAAQ,KAAK,cACjC,OAAO;EACL,MAAM;EACN,OAAO,KAAK;CAMd;CAEF,IAAI,oBAAoB,QAAQ,KAAK,gBACnC,OAAO;EACL,MAAM;EACN,OAAO,KAAK;CAMd;CAEF,OAAO;AACT;AAEA,SAAS,WAAW,QAAmC;CACrD,OAAO,SAAS,EAAE,OAAO,IAAI,CAAC;AAChC;AAEA,IAAa,YAAb,MAAuB;CACrB;CACA;CACA;CACA;CACA;CAIA;CAIA,YAAY,SAA2B;EACrC,KAAK,UAAU,QAAQ,QAAQ,QAAQ,OAAO,EAAE;EAChD,KAAK,WAAW,QAAQ,WACpB,IAAI,QAAQ,SAAS,QAAQ,YAAY,EAAE,MAC3C;EACJ,KAAK,SAAS,QAAQ;EACtB,KAAK,gBAAgB,QAAQ;EAC7B,MAAM,EACJ,SAAS,IACT,MAAM,IACN,QAAQ,IACR,QAAQ,IACR,GAAG,qBACA,QAAQ,gBAAgB,CAAC;EAC9B,KAAK,eAAe;EACpB,KAAK,YAAY,QAAQ,WAAW,CAAC;CACvC;CAEA,cAA8B;EAC5B,OAAO,GAAG,KAAK,WAAW,KAAK,SAAS,IAAI,mBAAmB,KAAK,MAAM,MAAM;CAClF;CAEA,MAAc,WACZ,qBAAqB,MACY;EAKjC,MAAM,UAAkC;GACtC,QAAQ;GACR,eAAA;GACA,GANA,OAAO,KAAK,cAAc,aACtB,MAAM,KAAK,UAAU,IACrB,KAAK;EAKX;EACA,IAAI,oBACF,QAAQ,kBAAkB;EAE5B,IAAI,KAAK,eAAe,QACtB,QAAQ,oBAAoB,KAAK,cAAc,KAAK,IAAI;EAE1D,OAAO;CACT;CAEA,MAAc,mBAAmB,UAAoC;EACnE,IAAI;EACJ,IAAI;GACF,YAAY,MAAM,SAAS,KAAK;EAClC,QAAQ,CAER;EAEA,IAAI,aAAa,OAAO,cAAc,YAAY,WAAW,WAAW;GACtE,MAAM,MAAO,UAAkC;GAC/C,MAAM,IAAI,SAAS;IACjB,MAAM,IAAI,QAAQ,SAAS;IAC3B,QAAQ,IAAI,UAAU,SAAS;IAC/B,SAAS,IAAI,WAAW,uBAAuB,SAAS;IACxD,SAAS,IAAI;GACf,CAAC;EACH;EAEA,MAAM,IAAI,SAAS;GACjB,MAAM,SAAS;GACf,QAAQ,SAAS;GACjB,SAAS,uBAAuB,SAAS,OAAO,GAAG,SAAS;EAC9D,CAAC;CACH;CAEA,MAAc,UACZ,MACA,UAAuB,CAAC,GACZ;EACZ,MAAM,QAAQ,CAAC,QAAQ,UAAU,QAAQ,OAAO,YAAY,MAAM;EAClE,MAAM,UAAU,MAAM,KAAK,WAAW,CAAC,KAAK;EAC5C,MAAM,WAAW,MAAM,MAAM,GAAG,KAAK,UAAU,QAAQ;GACrD,GAAG,KAAK;GACR,GAAG;GACH,SAAS;IACP,GAAG;IACH,GAAI,QAAQ;GACd;EACF,CAAC;EACD,IAAI,CAAC,SAAS,IACZ,MAAM,KAAK,mBAAmB,QAAQ;EAGxC,OAAO,cAAc,MADF,SAAS,KAAK,CACR;CAC3B;CAIA,MAAM,aAAa,QAA6C;EAC9D,MAAM,UAAU,MAAM,KAAK,WAAW,KAAK;EAC3C,MAAM,MAAM,GAAG,KAAK,QAAQ;EAC5B,MAAM,WAAW,MAAM,MAAM,KAAK;GAChC,GAAG,KAAK;GACR;GACA,GAAG,WAAW,MAAM;EACtB,CAAC;EACD,IAAI,CAAC,SAAS,IACZ,MAAM,KAAK,mBAAmB,QAAQ;EAGxC,OAAO,cAAc,MADF,SAAS,KAAK,CACR;CAC3B;CAEA,MAAM,qBAAqB,QAA6C;EACtE,OAAO,KAAK,UACV,GAAG,KAAK,YAAY,EAAE,qBACtB,WAAW,MAAM,CACnB;CACF;CAIA,MAAM,YACJ,SACA,eACA,UACA,QAC+B;EAC/B,MAAM,OAAgC,EACpC,SAAS,cAAc,OAAO,EAChC;EACA,IAAI,eAAe,KAAK,gBAAgB;EACxC,IAAI,UAAU,KAAK,WAAW;EAE9B,MAAM,SAAS,MAAM,KAAK,UACxB,GAAG,KAAK,YAAY,EAAE,gBACtB;GACE,QAAQ;GACR,MAAM,KAAK,UAAU,IAAI;GACzB,GAAG,WAAW,MAAM;EACtB,CACF;EAGA,IAAI,UAAU,UAAU,OAAO,MAAM,OAAO,OAAO;EACnD,IAAI,aAAa,UAAU,OAAO,SAChC,OAAO,OAAO;EAChB,OAAO;CACT;CAEA,OAAO,cACL,SACA,eACA,UACA,QACgC;EAChC,MAAM,UAAU,MAAM,KAAK,WAAW,IAAI;EAC1C,QAAQ,SAAS;EAEjB,MAAM,OAAgC,EACpC,SAAS,cAAc,OAAO,EAChC;EACA,IAAI,eAAe,KAAK,gBAAgB;EACxC,IAAI,UAAU,KAAK,WAAW;EAE9B,MAAM,WAAW,MAAM,MACrB,GAAG,KAAK,UAAU,KAAK,YAAY,EAAE,kBACrC;GACE,GAAG,KAAK;GACR,QAAQ;GACR;GACA,MAAM,KAAK,UAAU,IAAI;GACzB,GAAG,WAAW,MAAM;EACtB,CACF;EACA,IAAI,CAAC,SAAS,IACZ,MAAM,KAAK,mBAAmB,QAAQ;EAGxC,OAAO,KAAK,SAAS,QAAQ;CAC/B;CAIA,MAAM,QACJ,QACA,eACA,QACkB;EAClB,MAAM,SAAS,IAAI,gBAAgB;EACnC,IAAI,kBAAkB,KAAA,GAEpB,OAAO,IAAI,kBAAkB,OAAO,aAAa,CAAC;EAEpD,MAAM,KAAK,OAAO,SAAS;EAC3B,OAAO,KAAK,UACV,GAAG,KAAK,YAAY,EAAE,SAAS,mBAAmB,MAAM,IAAI,KAAK,IAAI,OAAO,MAC5E,WAAW,MAAM,CACnB;CACF;CAEA,MAAM,UACJ,SACA,QAC+B;EAC/B,MAAM,SAAS,IAAI,gBAAgB;EACnC,IAAI,SAAS,WAAW,OAAO,IAAI,cAAc,QAAQ,SAAS;EAClE,IAAI,SAAS,QAAQ,OAAO,IAAI,UAAU,gBAAgB,QAAQ,MAAM,CAAC;EACzE,IAAI,SAAS,aAAa,KAAA,GACxB,OAAO,IAAI,aAAa,OAAO,QAAQ,QAAQ,CAAC;EAClD,IAAI,SAAS,WAAW,OAAO,IAAI,cAAc,QAAQ,SAAS;EAClE,IAAI,SAAS,kBAAkB,KAAA,GAC7B,OAAO,IAAI,kBAAkB,OAAO,QAAQ,aAAa,CAAC;EAC5D,IAAI,SAAS,sBACX,OAAO,IAAI,0BAA0B,QAAQ,oBAAoB;EACnE,IAAI,SAAS,qBAAqB,KAAA,GAChC,OAAO,IAAI,qBAAqB,OAAO,QAAQ,gBAAgB,CAAC;EAClE,MAAM,KAAK,OAAO,SAAS;EAC3B,OAAO,KAAK,UACV,GAAG,KAAK,YAAY,EAAE,QAAQ,KAAK,IAAI,OAAO,MAC9C,WAAW,MAAM,CACnB;CACF;CAEA,MAAM,WACJ,QACA,UACA,QACkB;EAClB,MAAM,OAAO,WAAW,EAAE,SAAS,IAAI,CAAC;EACxC,OAAO,KAAK,UACV,GAAG,KAAK,YAAY,EAAE,SAAS,mBAAmB,MAAM,EAAE,UAC1D;GACE,QAAQ;GACR,MAAM,KAAK,UAAU,IAAI;GACzB,GAAG,WAAW,MAAM;EACtB,CACF;CACF;CAEA,OAAO,gBACL,QACA,QACgC;EAChC,MAAM,UAAU,MAAM,KAAK,WAAW,KAAK;EAC3C,QAAQ,SAAS;EAEjB,MAAM,WAAW,MAAM,MACrB,GAAG,KAAK,UAAU,KAAK,YAAY,EAAE,SAAS,mBAAmB,MAAM,EAAE,aACzE;GACE,GAAG,KAAK;GACR;GACA,GAAG,WAAW,MAAM;EACtB,CACF;EACA,IAAI,CAAC,SAAS,IACZ,MAAM,KAAK,mBAAmB,QAAQ;EAGxC,OAAO,KAAK,SAAS,QAAQ;CAC/B;CAIA,MAAM,iCACJ,QACA,QACwC;EACxC,MAAM,SAAS,OAAO;EACtB,IAAI,CAAC,QAAQ,MAAM,IAAI,MAAM,oBAAoB;EACjD,OAAO,KAAK,UACV,GAAG,KAAK,YAAY,EAAE,SAAS,mBAAmB,MAAM,EAAE,2BAC1D;GACE,QAAQ;GACR,MAAM,KAAK,UAAU,MAAM;GAC3B,GAAG,WAAW,MAAM;EACtB,CACF;CACF;CAEA,MAAM,8BACJ,QACA,UACA,QACwC;EACxC,OAAO,KAAK,UACV,GAAG,KAAK,YAAY,EAAE,SAAS,mBAAmB,MAAM,EAAE,2BAA2B,mBAAmB,QAAQ,KAChH,WAAW,MAAM,CACnB;CACF;CAEA,MAAM,gCACJ,QACA,SACA,QACqD;EACrD,MAAM,SAAS,IAAI,gBAAgB;EACnC,IAAI,SAAS,aAAa,KAAA,GACxB,OAAO,IAAI,aAAa,OAAO,QAAQ,QAAQ,CAAC;EAClD,IAAI,SAAS,WAAW,OAAO,IAAI,cAAc,QAAQ,SAAS;EAClE,MAAM,KAAK,OAAO,SAAS;EAC3B,OAAO,KAAK,UACV,GAAG,KAAK,YAAY,EAAE,SAAS,mBAAmB,MAAM,EAAE,0BAA0B,KAAK,IAAI,OAAO,MACpG,WAAW,MAAM,CACnB;CACF;CAEA,MAAM,iCACJ,QACA,UACA,QACe;EAEf,MAAM,UAAU,MAAM,KAAK,WAAW,IAAM;EAC5C,MAAM,WAAW,MAAM,MACrB,GAAG,KAAK,UAAU,KAAK,YAAY,EAAE,SAAS,mBAAmB,MAAM,EAAE,2BAA2B,mBAAmB,QAAQ,KAC/H;GACE,GAAG,KAAK;GACR,QAAQ;GACR;GACA,GAAG,WAAW,MAAM;EACtB,CACF;EACA,IAAI,CAAC,SAAS,IACZ,MAAM,KAAK,mBAAmB,QAAQ;CAE1C;CAIA,OAAe,SAAS,UAAoD;EAC1E,MAAM,SAAS,SAAS,MAAM,UAAU;EACxC,IAAI,CAAC,QAAQ,MAAM,IAAI,MAAM,kBAAkB;EAE/C,MAAM,UAAU,IAAI,YAAY;EAChC,IAAI,SAAS;EAEb,IAAI;GACF,OAAO,MAAM;IACX,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,KAAK;IAC1C,IAAI,MAAM;IAEV,UAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;IAEhD,IAAI,WAAmB,OAAO,QAAQ,MAAM;IAC5C,OAAO,aAAa,IAAI;KACtB,MAAM,YAAY,OAAO,MAAM,GAAG,QAAQ;KAC1C,SAAS,OAAO,MAAM,WAAW,CAAC;KAElC,MAAM,YAAsB,CAAC;KAE7B,KAAK,MAAM,QAAQ,UAAU,MAAM,IAAI,GAAG;MACxC,MAAM,UAAU,KAAK,QAAQ,OAAO,EAAE;MACtC,IAAI,QAAQ,WAAW,OAAO,GAC5B,UAAU,KAAK,QAAQ,MAAM,CAAC,EAAE,KAAK,CAAC;KAI1C;KAEA,IAAI,UAAU,WAAW,GAAG;KAE5B,IAAI;MACF,IAAI,SAAS,KAAK,MAAM,UAAU,KAAK,IAAI,CAAC;MAG5C,IACE,UACA,OAAO,WAAW,YAClB,aAAa,UACb,YAAY,QAEZ,SAAS,OAAO;MAIlB,MAAM,QAAQ,2BADK,cAAc,MACiB,CAAC;MACnD,IAAI,OAAO,MAAM;KACnB,QAAQ,CAER;KACA,WAAW,OAAO,QAAQ,MAAM;IAClC;GACF;EACF,UAAU;GACR,OAAO,YAAY;EACrB;CACF;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"A2AThreadRuntimeCore.d.ts","names":[],"sources":["../src/A2AThreadRuntimeCore.ts"],"mappings":";;;;;KA6BY,2BAAA;EACV,MAAA,EAAQ,SAAA;EACR,SAAA;EACA,aAAA,GAAgB,2BAAA;EAChB,OAAA,KAAY,KAAA,EAAO,KAAA;EACnB,QAAA;EACA,kBAAA,KAAuB,QAAA,EAAU,WAAA;EACjC,OAAA,GAAU,oBAAA;EACV,YAAA;AAAA;AAAA,cAQW,oBAAA;EAAA,QACH,MAAA;EAAA,QACA,SAAA;EAAA,QACA,aAAA;EAAA,QACA,OAAA;EAAA,QACA,QAAA;EAAA,QACA,kBAAA;EAAA,QACA,OAAA;EAAA,iBACS,YAAA;EAAA,QAET,OAAA;EAAA,QACA,QAAA;EAAA,QACA,aAAA;EAAA,QACA,eAAA;EAAA,QACA,YAAA;EAAA,QAGA,WAAA;EAAA,QACA,gBAAA;EAAA,QACA,cAAA;EAAA,iBAGS,uBAAA;EAAA,iBACA,kBAAA;EAAA,QACT,UAAA;EAAA,QACA,YAAA;cAEI,OAAA,EAAS,2BAAA;EAWrB,aAAA,CAAc,OAAA,EAAS,IAAA,CAAK,2BAAA;EAU5B,aAAA,CAAc,OAAA,EAAS,gBAAA;EAIvB,aAAA
|
|
1
|
+
{"version":3,"file":"A2AThreadRuntimeCore.d.ts","names":[],"sources":["../src/A2AThreadRuntimeCore.ts"],"mappings":";;;;;KA6BY,2BAAA;EACV,MAAA,EAAQ,SAAA;EACR,SAAA;EACA,aAAA,GAAgB,2BAAA;EAChB,OAAA,KAAY,KAAA,EAAO,KAAA;EACnB,QAAA;EACA,kBAAA,KAAuB,QAAA,EAAU,WAAA;EACjC,OAAA,GAAU,oBAAA;EACV,YAAA;AAAA;AAAA,cAQW,oBAAA;EAAA,QACH,MAAA;EAAA,QACA,SAAA;EAAA,QACA,aAAA;EAAA,QACA,OAAA;EAAA,QACA,QAAA;EAAA,QACA,kBAAA;EAAA,QACA,OAAA;EAAA,iBACS,YAAA;EAAA,QAET,OAAA;EAAA,QACA,QAAA;EAAA,QACA,aAAA;EAAA,QACA,eAAA;EAAA,QACA,YAAA;EAAA,QAGA,WAAA;EAAA,QACA,gBAAA;EAAA,QACA,cAAA;EAAA,iBAGS,uBAAA;EAAA,iBACA,kBAAA;EAAA,QACT,UAAA;EAAA,QACA,YAAA;cAEI,OAAA,EAAS,2BAAA;EAWrB,aAAA,CAAc,OAAA,EAAS,IAAA,CAAK,2BAAA;EAU5B,aAAA,CAAc,OAAA,EAAS,gBAAA;EAIvB,aAAA;EASA,UAAA,IAAc,gBAAA;EAId,WAAA,aAAwB,aAAA;EAIxB,OAAA,IAAW,OAAA;EAIX,YAAA,aAAyB,WAAA;EAIzB,YAAA,IAAgB,YAAA;EAIhB,SAAA;EAAA,IAII,SAAA;EAIJ,eAAA,IAAmB,OAAA;EAgCb,MAAA,CAAO,OAAA,EAAS,aAAA,GAAgB,OAAA;EAsBhC,IAAA,CAAK,OAAA,EAAS,aAAA,GAAgB,OAAA;EAI9B,MAAA,CACJ,QAAA,iBACA,OAAA;IAAW,SAAA,GAAY,MAAA;EAAA,IACtB,OAAA;EAaG,MAAA,IAAU,OAAA;EAiBhB,qBAAA,CAAsB,QAAA,WAAmB,aAAA;EAAA,QAe3B,QAAA;EAAA,QAwEA,YAAA;EAAA,QA4BA,OAAA;EAAA,QA4BN,iBAAA;EAAA,QAiBA,kBAAA;EAAA,QA6BA,oBAAA;EAAA,QAqCA,aAAA;EAAA,QAQA,kBAAA;EAAA,QAuBA,yBAAA;EAAA,QAiCA,0BAAA;EAAA,QAqBA,sBAAA;EAAA,QAWA,qBAAA;EAAA,QAgBA,kBAAA;EAAA,QASA,UAAA;EAAA,QAKA,SAAA;EAAA,QAOA,SAAA;EAAA,QAcA,kBAAA;EAAA,QAIA,2BAAA;EAAA,QAQA,uBAAA;EAAA,QAeA,iBAAA;AAAA"}
|
package/dist/useA2ARuntime.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
/// <reference types="@assistant-ui/core/store" />
|
|
2
1
|
import { A2AAgentCard, A2AArtifact, A2ASendMessageConfiguration, A2ATask } from "./types.js";
|
|
3
2
|
import { A2AClient, A2AClientOptions } from "./A2AClient.js";
|
|
4
|
-
import { AssistantRuntime, AttachmentAdapter, FeedbackAdapter, SpeechSynthesisAdapter, ThreadHistoryAdapter, ThreadMessage } from "@assistant-ui/core";
|
|
3
|
+
import { AssistantRuntime, AttachmentAdapter, DictationAdapter, ExternalStoreSharedOptions, FeedbackAdapter, RealtimeVoiceAdapter, SpeechSynthesisAdapter, ThreadHistoryAdapter, ThreadMessage } from "@assistant-ui/core";
|
|
5
4
|
|
|
6
5
|
//#region src/useA2ARuntime.d.ts
|
|
7
6
|
declare const useA2ATask: () => A2ATask | undefined;
|
|
@@ -14,13 +13,14 @@ type UseA2AThreadListAdapter = {
|
|
|
14
13
|
messages: readonly ThreadMessage[];
|
|
15
14
|
}>;
|
|
16
15
|
};
|
|
17
|
-
type UseA2ARuntimeOptions = {
|
|
16
|
+
type UseA2ARuntimeOptions = ExternalStoreSharedOptions & {
|
|
18
17
|
/** Pre-built A2A client instance. Provide this OR baseUrl. */client?: A2AClient; /** Base URL of the A2A server. Used to create a client if `client` is not provided. */
|
|
19
18
|
baseUrl?: string; /** Optional path prefix for all API endpoints (e.g. "/v1"). Does not affect agent card discovery. Only used with baseUrl. */
|
|
20
19
|
basePath?: string; /** Optional tenant ID for multi-tenant servers. Only used with baseUrl. */
|
|
21
20
|
tenant?: string; /** Headers for the A2A client (only used with baseUrl). */
|
|
22
21
|
headers?: A2AClientOptions["headers"]; /** A2A extension URIs to negotiate. Only used with baseUrl. */
|
|
23
|
-
extensions?: string[]; /**
|
|
22
|
+
extensions?: string[]; /** Extra fetch options (e.g. `{ credentials: 'include' }`). Only used with `baseUrl`. */
|
|
23
|
+
fetchOptions?: A2AClientOptions["fetchOptions"]; /** Initial context ID for the conversation. */
|
|
24
24
|
contextId?: string; /** Default send message configuration. */
|
|
25
25
|
configuration?: A2ASendMessageConfiguration; /** Called when an error occurs. */
|
|
26
26
|
onError?: (error: Error) => void; /** Called when a run is cancelled. */
|
|
@@ -29,6 +29,8 @@ type UseA2ARuntimeOptions = {
|
|
|
29
29
|
adapters?: {
|
|
30
30
|
attachments?: AttachmentAdapter;
|
|
31
31
|
speech?: SpeechSynthesisAdapter;
|
|
32
|
+
dictation?: DictationAdapter;
|
|
33
|
+
voice?: RealtimeVoiceAdapter;
|
|
32
34
|
feedback?: FeedbackAdapter;
|
|
33
35
|
history?: ThreadHistoryAdapter;
|
|
34
36
|
threadList?: UseA2AThreadListAdapter;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useA2ARuntime.d.ts","names":[],"sources":["../src/useA2ARuntime.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"useA2ARuntime.d.ts","names":[],"sources":["../src/useA2ARuntime.ts"],"mappings":";;;;;cAyDa,UAAA,QAAU,OAAA;AAAA,cAIV,eAAA,iBAAe,WAAA;AAAA,cAIf,eAAA,QAAe,YAAA;AAAA,KAMhB,uBAAA;EACV,QAAA;EACA,mBAAA,SAA4B,OAAA;EAC5B,gBAAA,IAAoB,QAAA,aAAqB,OAAA;IACvC,QAAA,WAAmB,aAAA;EAAA;AAAA;AAAA,KAMX,oBAAA,GAAuB,0BAAA;EAhBtB,8DAkBX,MAAA,GAAS,SAAA;EAET,OAAA,WApB0B;EAsB1B,QAAA,WAhBiC;EAkBjC,MAAA,WAhB4B;EAkB5B,OAAA,GAAU,gBAAA,aAjB+B;EAmBzC,UAAA,aAnBgD;EAqBhD,YAAA,GAAe,gBAAA,kBAtBf;EAyBA,SAAA,WAxBA;EA0BA,aAAA,GAAgB,2BAAA,EA1ByB;EA6BzC,OAAA,IAAW,KAAA,EAAO,KAAA,WA5BG;EA8BrB,QAAA,eA9BkC;EAgClC,kBAAA,IAAsB,QAAA,EAJC,WAAA;EAMvB,QAAA;IACE,WAAA,GAAc,iBAAA;IACd,MAAA,GAAS,sBAAA;IACT,SAAA,GAAY,gBAAA;IACZ,KAAA,GAAQ,oBAAA;IACR,QAAA,GAAW,eAAA;IACX,OAAA,GAAU,oBAAA;IACV,UAAA,GAAa,uBAAA;EAAA;AAAA;AAAA,iBAMD,aAAA,CAAc,OAAA,EAAS,oBAAA,GAAuB,gBAAgB"}
|
package/dist/useA2ARuntime.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { A2AClient } from "./A2AClient.js";
|
|
3
3
|
import { A2AThreadRuntimeCore } from "./A2AThreadRuntimeCore.js";
|
|
4
4
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
5
|
-
import { useExternalStoreRuntime, useRuntimeAdapters } from "@assistant-ui/core/react";
|
|
5
|
+
import { useExternalStoreRuntime, useExternalStoreSharedOptions, useRuntimeAdapters } from "@assistant-ui/core/react";
|
|
6
6
|
import { useAuiState } from "@assistant-ui/store";
|
|
7
7
|
//#region src/useA2ARuntime.ts
|
|
8
8
|
const symbolA2AExtras = Symbol("a2a-extras");
|
|
@@ -32,7 +32,8 @@ function useA2ARuntime(options) {
|
|
|
32
32
|
basePath: options.basePath,
|
|
33
33
|
tenant: options.tenant,
|
|
34
34
|
headers: options.headers,
|
|
35
|
-
extensions: options.extensions
|
|
35
|
+
extensions: options.extensions,
|
|
36
|
+
fetchOptions: options.fetchOptions
|
|
36
37
|
});
|
|
37
38
|
else throw new Error("useA2ARuntime requires either `client` or `baseUrl`");
|
|
38
39
|
const client = clientRef.current;
|
|
@@ -76,6 +77,8 @@ function useA2ARuntime(options) {
|
|
|
76
77
|
const adapterAdapters = useMemo(() => ({
|
|
77
78
|
attachments: adapters?.attachments ?? runtimeAdapters?.attachments,
|
|
78
79
|
speech: adapters?.speech,
|
|
80
|
+
dictation: adapters?.dictation,
|
|
81
|
+
voice: adapters?.voice,
|
|
79
82
|
feedback: adapters?.feedback,
|
|
80
83
|
threadList
|
|
81
84
|
}), [
|
|
@@ -83,8 +86,10 @@ function useA2ARuntime(options) {
|
|
|
83
86
|
runtimeAdapters,
|
|
84
87
|
threadList
|
|
85
88
|
]);
|
|
89
|
+
const shared = useExternalStoreSharedOptions(options);
|
|
86
90
|
const runtime = useExternalStoreRuntime(useMemo(() => {
|
|
87
91
|
return {
|
|
92
|
+
...shared,
|
|
88
93
|
isLoading: core.isLoading,
|
|
89
94
|
messages: core.getMessages(),
|
|
90
95
|
isRunning: core.isRunning(),
|
|
@@ -105,7 +110,8 @@ function useA2ARuntime(options) {
|
|
|
105
110
|
}, [
|
|
106
111
|
adapterAdapters,
|
|
107
112
|
core,
|
|
108
|
-
_version
|
|
113
|
+
_version,
|
|
114
|
+
shared
|
|
109
115
|
]));
|
|
110
116
|
useEffect(() => {
|
|
111
117
|
core.attachRuntime(runtime);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useA2ARuntime.js","names":[],"sources":["../src/useA2ARuntime.ts"],"sourcesContent":["/// <reference types=\"@assistant-ui/core/store\" />\n\"use client\";\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport {\n useExternalStoreRuntime,\n useRuntimeAdapters,\n} from \"@assistant-ui/core/react\";\nimport type {\n AssistantRuntime,\n AppendMessage,\n AttachmentAdapter,\n ExternalStoreAdapter,\n FeedbackAdapter,\n SpeechSynthesisAdapter,\n ThreadHistoryAdapter,\n ThreadMessage,\n} from \"@assistant-ui/core\";\nimport { useAuiState } from \"@assistant-ui/store\";\nimport { A2AClient } from \"./A2AClient\";\nimport type { A2AClientOptions } from \"./A2AClient\";\nimport { A2AThreadRuntimeCore } from \"./A2AThreadRuntimeCore\";\nimport type {\n A2AArtifact,\n A2AAgentCard,\n A2ASendMessageConfiguration,\n A2ATask,\n} from \"./types\";\n\n// --- Extras symbol for A2A-specific state ---\n\nconst symbolA2AExtras = Symbol(\"a2a-extras\");\n\ntype A2AExtras = {\n [symbolA2AExtras]: true;\n task: A2ATask | undefined;\n artifacts: readonly A2AArtifact[];\n agentCard: A2AAgentCard | undefined;\n};\n\nconst asA2AExtras = (extras: unknown): A2AExtras => {\n if (\n typeof extras !== \"object\" ||\n extras == null ||\n !(symbolA2AExtras in extras)\n )\n throw new Error(\n \"This hook can only be used inside a useA2ARuntime provider\",\n );\n return extras as A2AExtras;\n};\n\n// --- Public hooks for A2A state ---\n\nexport const useA2ATask = () => {\n return useAuiState((s) => asA2AExtras(s.thread.extras).task);\n};\n\nexport const useA2AArtifacts = () => {\n return useAuiState((s) => asA2AExtras(s.thread.extras).artifacts);\n};\n\nexport const useA2AAgentCard = () => {\n return useAuiState((s) => asA2AExtras(s.thread.extras).agentCard);\n};\n\n// --- Thread list adapter type ---\n\nexport type UseA2AThreadListAdapter = {\n threadId?: string;\n onSwitchToNewThread?: () => Promise<void> | void;\n onSwitchToThread?: (threadId: string) => Promise<{\n messages: readonly ThreadMessage[];\n }>;\n};\n\n// --- Options ---\n\nexport type UseA2ARuntimeOptions = {\n /** Pre-built A2A client instance. Provide this OR baseUrl. */\n client?: A2AClient;\n /** Base URL of the A2A server. Used to create a client if `client` is not provided. */\n baseUrl?: string;\n /** Optional path prefix for all API endpoints (e.g. \"/v1\"). Does not affect agent card discovery. Only used with baseUrl. */\n basePath?: string;\n /** Optional tenant ID for multi-tenant servers. Only used with baseUrl. */\n tenant?: string;\n /** Headers for the A2A client (only used with baseUrl). */\n headers?: A2AClientOptions[\"headers\"];\n /** A2A extension URIs to negotiate. Only used with baseUrl. */\n extensions?: string[];\n\n /** Initial context ID for the conversation. */\n contextId?: string;\n /** Default send message configuration. */\n configuration?: A2ASendMessageConfiguration;\n\n /** Called when an error occurs. */\n onError?: (error: Error) => void;\n /** Called when a run is cancelled. */\n onCancel?: () => void;\n /** Called when an artifact is fully received (lastChunk). */\n onArtifactComplete?: (artifact: import(\"./types\").A2AArtifact) => void;\n\n adapters?: {\n attachments?: AttachmentAdapter;\n speech?: SpeechSynthesisAdapter;\n feedback?: FeedbackAdapter;\n history?: ThreadHistoryAdapter;\n threadList?: UseA2AThreadListAdapter;\n };\n};\n\n// --- Main hook ---\n\nexport function useA2ARuntime(options: UseA2ARuntimeOptions): AssistantRuntime {\n const [_version, setVersion] = useState(0);\n const notifyUpdate = useCallback(() => setVersion((v) => v + 1), []);\n const runtimeAdapters = useRuntimeAdapters();\n const historyAdapter = options.adapters?.history ?? runtimeAdapters?.history;\n const threadListAdapter = options.adapters?.threadList;\n\n // Create or reuse client\n const clientRef = useRef<A2AClient | null>(null);\n if (!clientRef.current) {\n if (options.client) {\n clientRef.current = options.client;\n } else if (options.baseUrl) {\n clientRef.current = new A2AClient({\n baseUrl: options.baseUrl,\n basePath: options.basePath,\n tenant: options.tenant,\n headers: options.headers,\n extensions: options.extensions,\n });\n } else {\n throw new Error(\"useA2ARuntime requires either `client` or `baseUrl`\");\n }\n }\n const client = clientRef.current;\n\n // Create or reuse core\n const coreRef = useRef<A2AThreadRuntimeCore | null>(null);\n if (!coreRef.current) {\n coreRef.current = new A2AThreadRuntimeCore({\n client,\n contextId: options.contextId,\n configuration: options.configuration,\n ...(options.onError && { onError: options.onError }),\n ...(options.onCancel && { onCancel: options.onCancel }),\n ...(options.onArtifactComplete && {\n onArtifactComplete: options.onArtifactComplete,\n }),\n ...(historyAdapter && { history: historyAdapter }),\n notifyUpdate,\n });\n }\n\n const core = coreRef.current;\n core.updateOptions({\n client,\n contextId: options.contextId,\n configuration: options.configuration,\n ...(options.onError && { onError: options.onError }),\n ...(options.onCancel && { onCancel: options.onCancel }),\n ...(options.onArtifactComplete && {\n onArtifactComplete: options.onArtifactComplete,\n }),\n ...(historyAdapter && { history: historyAdapter }),\n });\n\n // Thread list\n const threadList = useMemo(() => {\n if (!threadListAdapter) return undefined;\n\n const { onSwitchToNewThread, onSwitchToThread } = threadListAdapter;\n\n return {\n threadId: threadListAdapter.threadId,\n onSwitchToNewThread: onSwitchToNewThread\n ? async () => {\n await onSwitchToNewThread();\n core.applyExternalMessages([]);\n }\n : undefined,\n onSwitchToThread: onSwitchToThread\n ? async (threadId: string) => {\n const result = await onSwitchToThread(threadId);\n core.applyExternalMessages(result.messages);\n }\n : undefined,\n };\n }, [threadListAdapter, core]);\n\n // Adapters\n const adapters = options.adapters;\n const adapterAdapters = useMemo(\n () => ({\n attachments: adapters?.attachments ?? runtimeAdapters?.attachments,\n speech: adapters?.speech,\n feedback: adapters?.feedback,\n threadList,\n }),\n [adapters, runtimeAdapters, threadList],\n );\n\n // Build store adapter\n const store = useMemo(() => {\n void _version;\n\n return {\n isLoading: core.isLoading,\n messages: core.getMessages(),\n isRunning: core.isRunning(),\n extras: {\n [symbolA2AExtras]: true,\n task: core.getTask(),\n artifacts: core.getArtifacts(),\n agentCard: core.getAgentCard(),\n } satisfies A2AExtras,\n onNew: (message: AppendMessage) => core.append(message),\n onEdit: (message: AppendMessage) => core.edit(message),\n onReload: (parentId: string | null) => core.reload(parentId),\n onCancel: () => core.cancel(),\n setMessages: (messages: readonly ThreadMessage[]) =>\n core.applyExternalMessages(messages),\n onImport: (messages: readonly ThreadMessage[]) =>\n core.applyExternalMessages(messages),\n adapters: adapterAdapters,\n } satisfies ExternalStoreAdapter<ThreadMessage>;\n }, [adapterAdapters, core, _version]);\n\n const runtime = useExternalStoreRuntime(store);\n\n useEffect(() => {\n core.attachRuntime(runtime);\n return () => {\n core.detachRuntime();\n };\n }, [core, runtime]);\n\n useEffect(() => {\n core.__internal_load();\n }, [core]);\n\n return runtime;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useA2ARuntime.js","names":[],"sources":["../src/useA2ARuntime.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport {\n useExternalStoreRuntime,\n useExternalStoreSharedOptions,\n useRuntimeAdapters,\n} from \"@assistant-ui/core/react\";\nimport type {\n AssistantRuntime,\n AppendMessage,\n AttachmentAdapter,\n DictationAdapter,\n ExternalStoreAdapter,\n ExternalStoreSharedOptions,\n FeedbackAdapter,\n RealtimeVoiceAdapter,\n SpeechSynthesisAdapter,\n ThreadHistoryAdapter,\n ThreadMessage,\n} from \"@assistant-ui/core\";\nimport { useAuiState } from \"@assistant-ui/store\";\nimport { A2AClient } from \"./A2AClient\";\nimport type { A2AClientOptions } from \"./A2AClient\";\nimport { A2AThreadRuntimeCore } from \"./A2AThreadRuntimeCore\";\nimport type {\n A2AArtifact,\n A2AAgentCard,\n A2ASendMessageConfiguration,\n A2ATask,\n} from \"./types\";\n\n// --- Extras symbol for A2A-specific state ---\n\nconst symbolA2AExtras = Symbol(\"a2a-extras\");\n\ntype A2AExtras = {\n [symbolA2AExtras]: true;\n task: A2ATask | undefined;\n artifacts: readonly A2AArtifact[];\n agentCard: A2AAgentCard | undefined;\n};\n\nconst asA2AExtras = (extras: unknown): A2AExtras => {\n if (\n typeof extras !== \"object\" ||\n extras == null ||\n !(symbolA2AExtras in extras)\n )\n throw new Error(\n \"This hook can only be used inside a useA2ARuntime provider\",\n );\n return extras as A2AExtras;\n};\n\n// --- Public hooks for A2A state ---\n\nexport const useA2ATask = () => {\n return useAuiState((s) => asA2AExtras(s.thread.extras).task);\n};\n\nexport const useA2AArtifacts = () => {\n return useAuiState((s) => asA2AExtras(s.thread.extras).artifacts);\n};\n\nexport const useA2AAgentCard = () => {\n return useAuiState((s) => asA2AExtras(s.thread.extras).agentCard);\n};\n\n// --- Thread list adapter type ---\n\nexport type UseA2AThreadListAdapter = {\n threadId?: string;\n onSwitchToNewThread?: () => Promise<void> | void;\n onSwitchToThread?: (threadId: string) => Promise<{\n messages: readonly ThreadMessage[];\n }>;\n};\n\n// --- Options ---\n\nexport type UseA2ARuntimeOptions = ExternalStoreSharedOptions & {\n /** Pre-built A2A client instance. Provide this OR baseUrl. */\n client?: A2AClient;\n /** Base URL of the A2A server. Used to create a client if `client` is not provided. */\n baseUrl?: string;\n /** Optional path prefix for all API endpoints (e.g. \"/v1\"). Does not affect agent card discovery. Only used with baseUrl. */\n basePath?: string;\n /** Optional tenant ID for multi-tenant servers. Only used with baseUrl. */\n tenant?: string;\n /** Headers for the A2A client (only used with baseUrl). */\n headers?: A2AClientOptions[\"headers\"];\n /** A2A extension URIs to negotiate. Only used with baseUrl. */\n extensions?: string[];\n /** Extra fetch options (e.g. `{ credentials: 'include' }`). Only used with `baseUrl`. */\n fetchOptions?: A2AClientOptions[\"fetchOptions\"];\n\n /** Initial context ID for the conversation. */\n contextId?: string;\n /** Default send message configuration. */\n configuration?: A2ASendMessageConfiguration;\n\n /** Called when an error occurs. */\n onError?: (error: Error) => void;\n /** Called when a run is cancelled. */\n onCancel?: () => void;\n /** Called when an artifact is fully received (lastChunk). */\n onArtifactComplete?: (artifact: import(\"./types\").A2AArtifact) => void;\n\n adapters?: {\n attachments?: AttachmentAdapter;\n speech?: SpeechSynthesisAdapter;\n dictation?: DictationAdapter;\n voice?: RealtimeVoiceAdapter;\n feedback?: FeedbackAdapter;\n history?: ThreadHistoryAdapter;\n threadList?: UseA2AThreadListAdapter;\n };\n};\n\n// --- Main hook ---\n\nexport function useA2ARuntime(options: UseA2ARuntimeOptions): AssistantRuntime {\n const [_version, setVersion] = useState(0);\n const notifyUpdate = useCallback(() => setVersion((v) => v + 1), []);\n const runtimeAdapters = useRuntimeAdapters();\n const historyAdapter = options.adapters?.history ?? runtimeAdapters?.history;\n const threadListAdapter = options.adapters?.threadList;\n\n // Create or reuse client\n const clientRef = useRef<A2AClient | null>(null);\n if (!clientRef.current) {\n if (options.client) {\n clientRef.current = options.client;\n } else if (options.baseUrl) {\n clientRef.current = new A2AClient({\n baseUrl: options.baseUrl,\n basePath: options.basePath,\n tenant: options.tenant,\n headers: options.headers,\n extensions: options.extensions,\n fetchOptions: options.fetchOptions,\n });\n } else {\n throw new Error(\"useA2ARuntime requires either `client` or `baseUrl`\");\n }\n }\n const client = clientRef.current;\n\n // Create or reuse core\n const coreRef = useRef<A2AThreadRuntimeCore | null>(null);\n if (!coreRef.current) {\n coreRef.current = new A2AThreadRuntimeCore({\n client,\n contextId: options.contextId,\n configuration: options.configuration,\n ...(options.onError && { onError: options.onError }),\n ...(options.onCancel && { onCancel: options.onCancel }),\n ...(options.onArtifactComplete && {\n onArtifactComplete: options.onArtifactComplete,\n }),\n ...(historyAdapter && { history: historyAdapter }),\n notifyUpdate,\n });\n }\n\n const core = coreRef.current;\n core.updateOptions({\n client,\n contextId: options.contextId,\n configuration: options.configuration,\n ...(options.onError && { onError: options.onError }),\n ...(options.onCancel && { onCancel: options.onCancel }),\n ...(options.onArtifactComplete && {\n onArtifactComplete: options.onArtifactComplete,\n }),\n ...(historyAdapter && { history: historyAdapter }),\n });\n\n // Thread list\n const threadList = useMemo(() => {\n if (!threadListAdapter) return undefined;\n\n const { onSwitchToNewThread, onSwitchToThread } = threadListAdapter;\n\n return {\n threadId: threadListAdapter.threadId,\n onSwitchToNewThread: onSwitchToNewThread\n ? async () => {\n await onSwitchToNewThread();\n core.applyExternalMessages([]);\n }\n : undefined,\n onSwitchToThread: onSwitchToThread\n ? async (threadId: string) => {\n const result = await onSwitchToThread(threadId);\n core.applyExternalMessages(result.messages);\n }\n : undefined,\n };\n }, [threadListAdapter, core]);\n\n // Adapters\n const adapters = options.adapters;\n const adapterAdapters = useMemo(\n () => ({\n attachments: adapters?.attachments ?? runtimeAdapters?.attachments,\n speech: adapters?.speech,\n dictation: adapters?.dictation,\n voice: adapters?.voice,\n feedback: adapters?.feedback,\n threadList,\n }),\n [adapters, runtimeAdapters, threadList],\n );\n\n // Build store adapter\n const shared = useExternalStoreSharedOptions(options);\n const store = useMemo(() => {\n void _version;\n\n return {\n ...shared,\n isLoading: core.isLoading,\n messages: core.getMessages(),\n isRunning: core.isRunning(),\n extras: {\n [symbolA2AExtras]: true,\n task: core.getTask(),\n artifacts: core.getArtifacts(),\n agentCard: core.getAgentCard(),\n } satisfies A2AExtras,\n onNew: (message: AppendMessage) => core.append(message),\n onEdit: (message: AppendMessage) => core.edit(message),\n onReload: (parentId: string | null) => core.reload(parentId),\n onCancel: () => core.cancel(),\n setMessages: (messages: readonly ThreadMessage[]) =>\n core.applyExternalMessages(messages),\n onImport: (messages: readonly ThreadMessage[]) =>\n core.applyExternalMessages(messages),\n adapters: adapterAdapters,\n } satisfies ExternalStoreAdapter<ThreadMessage>;\n }, [adapterAdapters, core, _version, shared]);\n\n const runtime = useExternalStoreRuntime(store);\n\n useEffect(() => {\n core.attachRuntime(runtime);\n return () => {\n core.detachRuntime();\n };\n }, [core, runtime]);\n\n useEffect(() => {\n core.__internal_load();\n }, [core]);\n\n return runtime;\n}\n"],"mappings":"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"}
|
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.17",
|
|
4
4
|
"description": "A2A (Agent-to-Agent) v1.0 protocol adapter for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"a2a",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
],
|
|
30
30
|
"sideEffects": false,
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@assistant-ui/core": "^0.2.
|
|
33
|
-
"@assistant-ui/store": "^0.2.
|
|
32
|
+
"@assistant-ui/core": "^0.2.8",
|
|
33
|
+
"@assistant-ui/store": "^0.2.13"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"@types/react": "*",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"@types/react": "^19.2.15",
|
|
46
46
|
"react": "^19.2.6",
|
|
47
47
|
"vitest": "^4.1.7",
|
|
48
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
48
|
+
"@assistant-ui/x-buildutils": "0.0.10"
|
|
49
49
|
},
|
|
50
50
|
"publishConfig": {
|
|
51
51
|
"access": "public",
|
package/src/A2AClient.test.ts
CHANGED
|
@@ -148,6 +148,115 @@ describe("A2AClient", () => {
|
|
|
148
148
|
});
|
|
149
149
|
});
|
|
150
150
|
|
|
151
|
+
describe("fetchOptions", () => {
|
|
152
|
+
it("applies fetchOptions to all request types", async () => {
|
|
153
|
+
const fetchOptionsClient = new A2AClient({
|
|
154
|
+
baseUrl: "https://agent.test",
|
|
155
|
+
fetchOptions: { credentials: "include" },
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
fetchMock
|
|
159
|
+
.mockResolvedValueOnce(
|
|
160
|
+
mockFetchResponse({
|
|
161
|
+
task: { id: "t1", status: { state: "completed" } },
|
|
162
|
+
}),
|
|
163
|
+
)
|
|
164
|
+
.mockResolvedValueOnce(
|
|
165
|
+
mockFetchResponse({
|
|
166
|
+
name: "Test Agent",
|
|
167
|
+
description: "A test",
|
|
168
|
+
version: "1.0",
|
|
169
|
+
supportedInterfaces: [],
|
|
170
|
+
capabilities: {},
|
|
171
|
+
defaultInputModes: [],
|
|
172
|
+
defaultOutputModes: [],
|
|
173
|
+
skills: [],
|
|
174
|
+
}),
|
|
175
|
+
)
|
|
176
|
+
.mockResolvedValueOnce(
|
|
177
|
+
mockSSEResponse([
|
|
178
|
+
`data: ${JSON.stringify({ status_update: { task_id: "t1", context_id: "ctx-1", status: { state: "TASK_STATE_WORKING" } } })}`,
|
|
179
|
+
"",
|
|
180
|
+
"",
|
|
181
|
+
]),
|
|
182
|
+
)
|
|
183
|
+
.mockResolvedValueOnce(
|
|
184
|
+
mockSSEResponse([
|
|
185
|
+
`data: ${JSON.stringify({ status_update: { task_id: "t1", context_id: "ctx-1", status: { state: "TASK_STATE_WORKING" } } })}`,
|
|
186
|
+
"",
|
|
187
|
+
"",
|
|
188
|
+
]),
|
|
189
|
+
)
|
|
190
|
+
.mockResolvedValueOnce({
|
|
191
|
+
ok: true,
|
|
192
|
+
status: 204,
|
|
193
|
+
statusText: "No Content",
|
|
194
|
+
headers: new Headers(),
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
await fetchOptionsClient.sendMessage(userMessage);
|
|
198
|
+
await fetchOptionsClient.getAgentCard();
|
|
199
|
+
for await (const _ of fetchOptionsClient.streamMessage(userMessage)) {
|
|
200
|
+
void _;
|
|
201
|
+
}
|
|
202
|
+
for await (const _ of fetchOptionsClient.subscribeToTask("t1")) {
|
|
203
|
+
void _;
|
|
204
|
+
}
|
|
205
|
+
await fetchOptionsClient.deleteTaskPushNotificationConfig("t1", "pnc-1");
|
|
206
|
+
|
|
207
|
+
for (const [, init] of fetchMock.mock.calls) {
|
|
208
|
+
expect(init.credentials).toBe("include");
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it("strips internally-managed fields even when smuggled via cast", async () => {
|
|
213
|
+
const smuggledSignal = new AbortController().signal;
|
|
214
|
+
const fetchOptionsClient = new A2AClient({
|
|
215
|
+
baseUrl: "https://agent.test",
|
|
216
|
+
fetchOptions: {
|
|
217
|
+
method: "GET",
|
|
218
|
+
headers: { "X-Injected": "1" },
|
|
219
|
+
body: "smuggled",
|
|
220
|
+
signal: smuggledSignal,
|
|
221
|
+
} as RequestInit,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
fetchMock
|
|
225
|
+
.mockResolvedValueOnce(
|
|
226
|
+
mockFetchResponse({
|
|
227
|
+
task: { id: "t1", status: { state: "completed" } },
|
|
228
|
+
}),
|
|
229
|
+
)
|
|
230
|
+
.mockResolvedValueOnce(
|
|
231
|
+
mockFetchResponse({
|
|
232
|
+
name: "Test",
|
|
233
|
+
description: "desc",
|
|
234
|
+
version: "1.0",
|
|
235
|
+
supportedInterfaces: [],
|
|
236
|
+
capabilities: {},
|
|
237
|
+
defaultInputModes: [],
|
|
238
|
+
defaultOutputModes: [],
|
|
239
|
+
skills: [],
|
|
240
|
+
}),
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
await fetchOptionsClient.sendMessage(userMessage);
|
|
244
|
+
await fetchOptionsClient.getAgentCard();
|
|
245
|
+
|
|
246
|
+
for (const [, init] of fetchMock.mock.calls) {
|
|
247
|
+
expect(init.headers["X-Injected"]).toBeUndefined();
|
|
248
|
+
expect(init.body).not.toBe("smuggled");
|
|
249
|
+
expect(init.signal).not.toBe(smuggledSignal);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
expect(fetchMock.mock.calls[0]![1].method).toBe("POST");
|
|
253
|
+
expect(fetchMock.mock.calls[0]![1].headers["Content-Type"]).toBe(
|
|
254
|
+
"application/a2a+json",
|
|
255
|
+
);
|
|
256
|
+
expect(fetchMock.mock.calls[1]![1].method).toBeUndefined();
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
|
|
151
260
|
// --- Tenant ---
|
|
152
261
|
|
|
153
262
|
describe("tenant", () => {
|
|
@@ -275,7 +384,7 @@ describe("A2AClient", () => {
|
|
|
275
384
|
expect(body.message.messageId).toBe("msg-1");
|
|
276
385
|
});
|
|
277
386
|
|
|
278
|
-
it("sends '
|
|
387
|
+
it("sends 'parts' per A2A v1.0 spec (gRPC + JSON-RPC unified in a2aproject/A2A#1100)", async () => {
|
|
279
388
|
fetchMock.mockResolvedValue(
|
|
280
389
|
mockFetchResponse({
|
|
281
390
|
task: { id: "t1", status: { state: "completed" } },
|
|
@@ -285,8 +394,8 @@ describe("A2AClient", () => {
|
|
|
285
394
|
await client.sendMessage(userMessage);
|
|
286
395
|
|
|
287
396
|
const body = JSON.parse(fetchMock.mock.calls[0]![1].body);
|
|
288
|
-
expect(body.message.
|
|
289
|
-
expect(body.message.
|
|
397
|
+
expect(body.message.parts).toEqual([{ text: "Hello" }]);
|
|
398
|
+
expect(body.message.content).toBeUndefined();
|
|
290
399
|
});
|
|
291
400
|
|
|
292
401
|
it("unwraps task from SendMessageResponse", async () => {
|
|
@@ -320,7 +429,7 @@ describe("A2AClient", () => {
|
|
|
320
429
|
expect((result as any).role).toBe("agent");
|
|
321
430
|
});
|
|
322
431
|
|
|
323
|
-
it("normalizes 'content' array from
|
|
432
|
+
it("normalizes 'content' array from v0.3 server response to internal 'parts'", async () => {
|
|
324
433
|
fetchMock.mockResolvedValue(
|
|
325
434
|
mockFetchResponse({
|
|
326
435
|
message: {
|
|
@@ -778,7 +887,7 @@ describe("A2AClient", () => {
|
|
|
778
887
|
expect(events).toHaveLength(2);
|
|
779
888
|
});
|
|
780
889
|
|
|
781
|
-
it("normalizes
|
|
890
|
+
it("normalizes 'content' array from v0.3 server response to 'parts' in SSE artifact update events", async () => {
|
|
782
891
|
const sseData = JSON.stringify({
|
|
783
892
|
artifact_update: {
|
|
784
893
|
task_id: "t1",
|
|
@@ -810,7 +919,7 @@ describe("A2AClient", () => {
|
|
|
810
919
|
expect((evt.event.artifact as any).content).toBeUndefined();
|
|
811
920
|
});
|
|
812
921
|
|
|
813
|
-
it("normalizes
|
|
922
|
+
it("normalizes 'content' array from v0.3 server response to 'parts' in SSE status update messages", async () => {
|
|
814
923
|
const sseData = JSON.stringify({
|
|
815
924
|
status_update: {
|
|
816
925
|
task_id: "t1",
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
|
|
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, {
|
|
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
|
-
{
|
|
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
|
-
{
|
|
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);
|
package/src/conversions.test.ts
CHANGED
|
@@ -200,14 +200,12 @@ describe("taskStateToMessageStatus", () => {
|
|
|
200
200
|
});
|
|
201
201
|
|
|
202
202
|
describe("isTerminalTaskState", () => {
|
|
203
|
-
it.each([
|
|
204
|
-
"
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
"
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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",
|
package/src/useA2ARuntime.ts
CHANGED
|
@@ -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
|
|