@assistant-ui/react-a2a 0.2.23 → 0.2.25
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.js +15 -2
- package/dist/A2AClient.js.map +1 -1
- package/dist/a2aExtras.d.ts +1 -1
- package/dist/a2aExtras.d.ts.map +1 -1
- package/dist/a2aExtras.js +1 -1
- package/dist/a2aExtras.js.map +1 -1
- package/dist/conversions.d.ts +5 -1
- package/dist/conversions.d.ts.map +1 -1
- package/dist/conversions.js +15 -1
- package/dist/conversions.js.map +1 -1
- package/dist/useA2ARuntime.js +1 -1
- package/package.json +5 -5
- package/src/A2AClient.test.ts +63 -0
- package/src/A2AClient.ts +16 -2
- package/src/a2aExtras.ts +1 -1
- package/src/conversions.test.ts +81 -1
- package/src/conversions.ts +21 -2
package/dist/A2AClient.js
CHANGED
|
@@ -325,10 +325,19 @@ var A2AClient = class {
|
|
|
325
325
|
return null;
|
|
326
326
|
}
|
|
327
327
|
};
|
|
328
|
+
let shouldCancel = true;
|
|
328
329
|
try {
|
|
329
330
|
while (true) {
|
|
330
|
-
|
|
331
|
+
let result;
|
|
332
|
+
try {
|
|
333
|
+
result = await reader.read();
|
|
334
|
+
} catch (error) {
|
|
335
|
+
shouldCancel = false;
|
|
336
|
+
throw error;
|
|
337
|
+
}
|
|
338
|
+
const { done, value } = result;
|
|
331
339
|
if (done) {
|
|
340
|
+
shouldCancel = false;
|
|
332
341
|
for (const event of sseDecoder.push(decoder.decode())) {
|
|
333
342
|
const parsed = readEvent(event);
|
|
334
343
|
if (parsed) yield parsed;
|
|
@@ -341,7 +350,11 @@ var A2AClient = class {
|
|
|
341
350
|
}
|
|
342
351
|
}
|
|
343
352
|
} finally {
|
|
344
|
-
|
|
353
|
+
try {
|
|
354
|
+
if (shouldCancel) await reader.cancel().catch(() => void 0);
|
|
355
|
+
} finally {
|
|
356
|
+
reader.releaseLock();
|
|
357
|
+
}
|
|
345
358
|
}
|
|
346
359
|
}
|
|
347
360
|
};
|
package/dist/A2AClient.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"A2AClient.js","names":[],"sources":["../src/A2AClient.ts"],"sourcesContent":["import { SSEEventDecoder, type SSEEvent } from \"assistant-stream/utils\";\nimport { isRecord } from \"@assistant-ui/core/internal\";\nimport 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\nconst TASK_STATES: ReadonlySet<string> = new Set(\n Object.keys({\n unspecified: true,\n submitted: true,\n working: true,\n completed: true,\n failed: true,\n canceled: true,\n input_required: true,\n rejected: true,\n auth_required: true,\n } satisfies Record<A2ATaskState, true>),\n);\n\nconst isTaskState = (value: unknown): value is A2ATaskState =>\n typeof value === \"string\" && TASK_STATES.has(value);\n\nconst isTask = (value: unknown): value is A2ATask =>\n isRecord(value) &&\n typeof value.id === \"string\" &&\n value.id.length > 0 &&\n isRecord(value.status) &&\n isTaskState(value.status.state);\n\nconst isMessage = (value: unknown): value is A2AMessage =>\n isRecord(value) &&\n typeof value.messageId === \"string\" &&\n value.messageId.length > 0 &&\n typeof value.role === \"string\" &&\n value.role.length > 0 &&\n Array.isArray(value.parts) &&\n value.parts.every(isRecord);\n\nconst isStringArray = (value: unknown): value is string[] =>\n Array.isArray(value) && value.every((item) => typeof item === \"string\");\n\nconst invalidAgentCard = (): never => {\n throw new Error(\n \"Invalid A2A agent card response: expected a valid agent card payload.\",\n );\n};\n\nconst parseCardString = (value: unknown): string =>\n value == null ? \"\" : typeof value === \"string\" ? value : invalidAgentCard();\n\nconst parseCardStringArray = (value: unknown): string[] =>\n value == null ? [] : isStringArray(value) ? value : invalidAgentCard();\n\nconst parseCardRecordArray = (value: unknown): Record<string, unknown>[] =>\n value == null\n ? []\n : Array.isArray(value) && value.every(isRecord)\n ? (value as Record<string, unknown>[])\n : invalidAgentCard();\n\nconst parseCardRecord = (value: unknown): Record<string, unknown> =>\n value == null ? {} : isRecord(value) ? value : invalidAgentCard();\n\n// Proto3 JSON parsing treats omitted and null fields as defaults, so a valid\n// card may arrive without its empty lists, strings, or capabilities. Fill\n// those per the proto3 JSON mapping rules; a payload without a name or with a\n// present field of the wrong type rejects.\nconst parseAgentCardResponse = (value: unknown): A2AAgentCard => {\n if (\n !isRecord(value) ||\n typeof value.name !== \"string\" ||\n value.name.length === 0\n ) {\n return invalidAgentCard();\n }\n\n return {\n ...value,\n name: value.name,\n description: parseCardString(value.description),\n version: parseCardString(value.version),\n supportedInterfaces: parseCardRecordArray(value.supportedInterfaces).map(\n (entry) => ({\n ...entry,\n url: parseCardString(entry.url),\n protocolBinding: parseCardString(entry.protocolBinding),\n protocolVersion: parseCardString(entry.protocolVersion),\n }),\n ),\n capabilities: parseCardRecord(value.capabilities),\n defaultInputModes: parseCardStringArray(value.defaultInputModes),\n defaultOutputModes: parseCardStringArray(value.defaultOutputModes),\n skills: parseCardRecordArray(value.skills).map((entry) => ({\n ...entry,\n id: parseCardString(entry.id),\n name: parseCardString(entry.name),\n description: parseCardString(entry.description),\n tags: parseCardStringArray(entry.tags),\n })),\n } as A2AAgentCard;\n};\n\nconst parseSendMessageResponse = (value: unknown): A2ATask | A2AMessage => {\n if (isRecord(value)) {\n const candidate = value.task ?? value.message ?? value;\n if (isTask(candidate) || isMessage(candidate)) return candidate;\n }\n\n throw new Error(\n \"Invalid A2A message:send response: expected a valid task or message payload.\",\n );\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 parseAgentCardResponse(normalizeKeys(json));\n }\n\n async getExtendedAgentCard(signal?: AbortSignal): Promise<A2AAgentCard> {\n const result = await this.fetchJSON<unknown>(\n `${this.getBasePath()}/extendedAgentCard`,\n signalInit(signal),\n );\n return parseAgentCardResponse(result);\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<unknown>(\n `${this.getBasePath()}/message:send`,\n {\n method: \"POST\",\n body: JSON.stringify(body),\n ...signalInit(signal),\n },\n );\n\n return parseSendMessageResponse(result);\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 contentType = response.headers.get(\"Content-Type\");\n const mediaType = contentType?.split(\";\", 1)[0]?.trim().toLowerCase();\n if (mediaType !== \"text/event-stream\") {\n const received = contentType\n ? `\"${contentType}\"`\n : \"no Content-Type header\";\n throw new Error(\n `Expected A2A stream response Content-Type \"text/event-stream\", received ${received}`,\n );\n }\n\n const reader = response.body?.getReader();\n if (!reader) throw new Error(\"No response body\");\n\n const decoder = new TextDecoder();\n const sseDecoder = new SSEEventDecoder();\n\n const readEvent = (event: SSEEvent): A2AStreamEvent | null => {\n try {\n let parsed = JSON.parse(event.data);\n\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 return discriminateStreamResponse(normalized);\n } catch {\n return null;\n }\n };\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n for (const event of sseDecoder.push(decoder.decode())) {\n const parsed = readEvent(event);\n if (parsed) yield parsed;\n }\n break;\n }\n\n for (const event of sseDecoder.push(\n decoder.decode(value, { stream: true }),\n )) {\n const parsed = readEvent(event);\n if (parsed) yield parsed;\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 { SSEEventDecoder, type SSEEvent } from \"assistant-stream/utils\";\nimport { isRecord } from \"@assistant-ui/core/internal\";\nimport 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\nconst TASK_STATES: ReadonlySet<string> = new Set(\n Object.keys({\n unspecified: true,\n submitted: true,\n working: true,\n completed: true,\n failed: true,\n canceled: true,\n input_required: true,\n rejected: true,\n auth_required: true,\n } satisfies Record<A2ATaskState, true>),\n);\n\nconst isTaskState = (value: unknown): value is A2ATaskState =>\n typeof value === \"string\" && TASK_STATES.has(value);\n\nconst isTask = (value: unknown): value is A2ATask =>\n isRecord(value) &&\n typeof value.id === \"string\" &&\n value.id.length > 0 &&\n isRecord(value.status) &&\n isTaskState(value.status.state);\n\nconst isMessage = (value: unknown): value is A2AMessage =>\n isRecord(value) &&\n typeof value.messageId === \"string\" &&\n value.messageId.length > 0 &&\n typeof value.role === \"string\" &&\n value.role.length > 0 &&\n Array.isArray(value.parts) &&\n value.parts.every(isRecord);\n\nconst isStringArray = (value: unknown): value is string[] =>\n Array.isArray(value) && value.every((item) => typeof item === \"string\");\n\nconst invalidAgentCard = (): never => {\n throw new Error(\n \"Invalid A2A agent card response: expected a valid agent card payload.\",\n );\n};\n\nconst parseCardString = (value: unknown): string =>\n value == null ? \"\" : typeof value === \"string\" ? value : invalidAgentCard();\n\nconst parseCardStringArray = (value: unknown): string[] =>\n value == null ? [] : isStringArray(value) ? value : invalidAgentCard();\n\nconst parseCardRecordArray = (value: unknown): Record<string, unknown>[] =>\n value == null\n ? []\n : Array.isArray(value) && value.every(isRecord)\n ? (value as Record<string, unknown>[])\n : invalidAgentCard();\n\nconst parseCardRecord = (value: unknown): Record<string, unknown> =>\n value == null ? {} : isRecord(value) ? value : invalidAgentCard();\n\n// Proto3 JSON parsing treats omitted and null fields as defaults, so a valid\n// card may arrive without its empty lists, strings, or capabilities. Fill\n// those per the proto3 JSON mapping rules; a payload without a name or with a\n// present field of the wrong type rejects.\nconst parseAgentCardResponse = (value: unknown): A2AAgentCard => {\n if (\n !isRecord(value) ||\n typeof value.name !== \"string\" ||\n value.name.length === 0\n ) {\n return invalidAgentCard();\n }\n\n return {\n ...value,\n name: value.name,\n description: parseCardString(value.description),\n version: parseCardString(value.version),\n supportedInterfaces: parseCardRecordArray(value.supportedInterfaces).map(\n (entry) => ({\n ...entry,\n url: parseCardString(entry.url),\n protocolBinding: parseCardString(entry.protocolBinding),\n protocolVersion: parseCardString(entry.protocolVersion),\n }),\n ),\n capabilities: parseCardRecord(value.capabilities),\n defaultInputModes: parseCardStringArray(value.defaultInputModes),\n defaultOutputModes: parseCardStringArray(value.defaultOutputModes),\n skills: parseCardRecordArray(value.skills).map((entry) => ({\n ...entry,\n id: parseCardString(entry.id),\n name: parseCardString(entry.name),\n description: parseCardString(entry.description),\n tags: parseCardStringArray(entry.tags),\n })),\n } as A2AAgentCard;\n};\n\nconst parseSendMessageResponse = (value: unknown): A2ATask | A2AMessage => {\n if (isRecord(value)) {\n const candidate = value.task ?? value.message ?? value;\n if (isTask(candidate) || isMessage(candidate)) return candidate;\n }\n\n throw new Error(\n \"Invalid A2A message:send response: expected a valid task or message payload.\",\n );\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 parseAgentCardResponse(normalizeKeys(json));\n }\n\n async getExtendedAgentCard(signal?: AbortSignal): Promise<A2AAgentCard> {\n const result = await this.fetchJSON<unknown>(\n `${this.getBasePath()}/extendedAgentCard`,\n signalInit(signal),\n );\n return parseAgentCardResponse(result);\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<unknown>(\n `${this.getBasePath()}/message:send`,\n {\n method: \"POST\",\n body: JSON.stringify(body),\n ...signalInit(signal),\n },\n );\n\n return parseSendMessageResponse(result);\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 contentType = response.headers.get(\"Content-Type\");\n const mediaType = contentType?.split(\";\", 1)[0]?.trim().toLowerCase();\n if (mediaType !== \"text/event-stream\") {\n const received = contentType\n ? `\"${contentType}\"`\n : \"no Content-Type header\";\n throw new Error(\n `Expected A2A stream response Content-Type \"text/event-stream\", received ${received}`,\n );\n }\n\n const reader = response.body?.getReader();\n if (!reader) throw new Error(\"No response body\");\n\n const decoder = new TextDecoder();\n const sseDecoder = new SSEEventDecoder();\n\n const readEvent = (event: SSEEvent): A2AStreamEvent | null => {\n try {\n let parsed = JSON.parse(event.data);\n\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 return discriminateStreamResponse(normalized);\n } catch {\n return null;\n }\n };\n\n let shouldCancel = true;\n try {\n while (true) {\n let result: ReadableStreamReadResult<Uint8Array>;\n try {\n result = await reader.read();\n } catch (error) {\n shouldCancel = false;\n throw error;\n }\n\n const { done, value } = result;\n if (done) {\n shouldCancel = false;\n for (const event of sseDecoder.push(decoder.decode())) {\n const parsed = readEvent(event);\n if (parsed) yield parsed;\n }\n break;\n }\n\n for (const event of sseDecoder.push(\n decoder.decode(value, { stream: true }),\n )) {\n const parsed = readEvent(event);\n if (parsed) yield parsed;\n }\n }\n } finally {\n try {\n if (shouldCancel) await reader.cancel().catch(() => undefined);\n } finally {\n reader.releaseLock();\n }\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/a2aExtras.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { A2AExtras } from "./types.js";
|
|
2
2
|
//#region src/a2aExtras.d.ts
|
|
3
|
-
declare const a2aExtras: import("@assistant-ui/core/
|
|
3
|
+
declare const a2aExtras: import("@assistant-ui/core/react").RuntimeExtras<A2AExtras>;
|
|
4
4
|
//#endregion
|
|
5
5
|
export { a2aExtras };
|
|
6
6
|
//# sourceMappingURL=a2aExtras.d.ts.map
|
package/dist/a2aExtras.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"a2aExtras.d.ts","names":[],"sources":["../src/a2aExtras.ts"],"mappings":";;cAGa,
|
|
1
|
+
{"version":3,"file":"a2aExtras.d.ts","names":[],"sources":["../src/a2aExtras.ts"],"mappings":";;cAGa,8CAAS,cAAA"}
|
package/dist/a2aExtras.js
CHANGED
package/dist/a2aExtras.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"a2aExtras.js","names":[],"sources":["../src/a2aExtras.ts"],"sourcesContent":["import { createRuntimeExtras } from \"@assistant-ui/core/
|
|
1
|
+
{"version":3,"file":"a2aExtras.js","names":[],"sources":["../src/a2aExtras.ts"],"sourcesContent":["import { createRuntimeExtras } from \"@assistant-ui/core/react\";\nimport type { A2AExtras } from \"./types\";\n\nexport const a2aExtras = createRuntimeExtras<A2AExtras>(\"useA2ARuntime\");\n"],"mappings":";;AAGA,MAAa,YAAY,oBAA+B,eAAe"}
|
package/dist/conversions.d.ts
CHANGED
|
@@ -10,9 +10,13 @@ declare function contentPartsToA2AParts(content: ReadonlyArray<{
|
|
|
10
10
|
type: string;
|
|
11
11
|
text?: string | undefined;
|
|
12
12
|
image?: string | undefined;
|
|
13
|
-
data?:
|
|
13
|
+
data?: unknown;
|
|
14
14
|
mimeType?: string | undefined;
|
|
15
15
|
filename?: string | undefined;
|
|
16
|
+
audio?: {
|
|
17
|
+
data: string;
|
|
18
|
+
format: string;
|
|
19
|
+
} | undefined;
|
|
16
20
|
}>, fallbackMimeType?: string): A2APart[];
|
|
17
21
|
declare function a2aMessageToContent(message: A2AMessage): ThreadAssistantMessage["content"];
|
|
18
22
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversions.d.ts","names":[],"sources":["../src/conversions.ts"],"mappings":";;;iBAUgB,iBACd,MAAM,UACL;iBA+Ba,kBACd,OAAO,YACN;iBAgBa,oBAAoB,OAAO;iBAI3B,uBAAuB,OAAO;iBAI9B,yBAAyB,OAAO,eAAe;iBAoB/C,uBACd,SAAS;EACP;EACA;EACA;EACA;EACA;EACA;
|
|
1
|
+
{"version":3,"file":"conversions.d.ts","names":[],"sources":["../src/conversions.ts"],"mappings":";;;iBAUgB,iBACd,MAAM,UACL;iBA+Ba,kBACd,OAAO,YACN;iBAgBa,oBAAoB,OAAO;iBAI3B,uBAAuB,OAAO;iBAI9B,yBAAyB,OAAO,eAAe;iBAoB/C,uBACd,SAAS;EACP;EACA;EACA;EACA;EACA;EACA;EACA;IAAU;IAAc;;IAE1B,4BACC;iBAuEa,oBACd,SAAS,aACR"}
|
package/dist/conversions.js
CHANGED
|
@@ -98,7 +98,7 @@ function contentPartsToA2AParts(content, fallbackMimeType) {
|
|
|
98
98
|
};
|
|
99
99
|
}
|
|
100
100
|
case "file": {
|
|
101
|
-
if (!part.data) return null;
|
|
101
|
+
if (typeof part.data !== "string" || !part.data) return null;
|
|
102
102
|
const declaredMimeType = part.mimeType || fallbackMimeType;
|
|
103
103
|
if (httpUrlPattern.test(part.data)) return {
|
|
104
104
|
url: part.data,
|
|
@@ -111,12 +111,26 @@ function contentPartsToA2AParts(content, fallbackMimeType) {
|
|
|
111
111
|
mediaType: parsed.mimeType,
|
|
112
112
|
...part.filename && { filename: part.filename }
|
|
113
113
|
};
|
|
114
|
+
if (/^data:/i.test(part.data)) return {
|
|
115
|
+
url: part.data,
|
|
116
|
+
...declaredMimeType && { mediaType: declaredMimeType },
|
|
117
|
+
...part.filename && { filename: part.filename }
|
|
118
|
+
};
|
|
114
119
|
return {
|
|
115
120
|
raw: part.data,
|
|
116
121
|
...declaredMimeType && { mediaType: declaredMimeType },
|
|
117
122
|
...part.filename && { filename: part.filename }
|
|
118
123
|
};
|
|
119
124
|
}
|
|
125
|
+
case "audio":
|
|
126
|
+
if (!part.audio) return null;
|
|
127
|
+
return {
|
|
128
|
+
raw: parseDataUrl(part.audio.data)?.data ?? part.audio.data,
|
|
129
|
+
mediaType: `audio/${part.audio.format}`
|
|
130
|
+
};
|
|
131
|
+
case "data":
|
|
132
|
+
if (part.data === void 0) return null;
|
|
133
|
+
return { data: part.data };
|
|
120
134
|
default: return null;
|
|
121
135
|
}
|
|
122
136
|
}).filter((p) => p !== null);
|
package/dist/conversions.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversions.js","names":[],"sources":["../src/conversions.ts"],"sourcesContent":["\"use client\";\n\nimport type { MessageStatus, ThreadAssistantMessage } from \"@assistant-ui/core\";\nimport { httpUrlPattern, parseDataUrl } from \"@assistant-ui/core/internal\";\nimport type { A2AMessage, A2APart, A2ATaskState } from \"./types\";\n\nfunction isImageMediaType(mediaType?: string): boolean {\n return !!mediaType && mediaType.startsWith(\"image/\");\n}\n\nexport function a2aPartToContent(\n part: A2APart,\n): ThreadAssistantMessage[\"content\"][number] {\n if (part.text !== undefined) {\n return { type: \"text\", text: part.text };\n }\n if (part.url !== undefined) {\n if (isImageMediaType(part.mediaType)) {\n return { type: \"image\", image: part.url };\n }\n return {\n type: \"text\",\n text: part.filename ? `[${part.filename}](${part.url})` : part.url,\n };\n }\n if (part.raw !== undefined) {\n if (isImageMediaType(part.mediaType)) {\n return {\n type: \"image\",\n image: `data:${part.mediaType};base64,${part.raw}`,\n };\n }\n return {\n type: \"text\",\n text: `[File: ${part.filename ?? \"download\"}]`,\n };\n }\n if (part.data !== undefined) {\n return { type: \"text\", text: JSON.stringify(part.data, null, 2) };\n }\n return { type: \"text\", text: \"\" };\n}\n\nexport function a2aPartsToContent(\n parts: A2APart[],\n): ThreadAssistantMessage[\"content\"] {\n return parts.map(a2aPartToContent);\n}\n\nconst TERMINAL_STATES = new Set<A2ATaskState>([\n \"completed\",\n \"failed\",\n \"canceled\",\n \"rejected\",\n]);\n\nconst INTERRUPTED_STATES = new Set<A2ATaskState>([\n \"input_required\",\n \"auth_required\",\n]);\n\nexport function isTerminalTaskState(state: A2ATaskState): boolean {\n return TERMINAL_STATES.has(state);\n}\n\nexport function isInterruptedTaskState(state: A2ATaskState): boolean {\n return INTERRUPTED_STATES.has(state);\n}\n\nexport function taskStateToMessageStatus(state: A2ATaskState): MessageStatus {\n switch (state) {\n case \"submitted\":\n case \"working\":\n return { type: \"running\" };\n case \"completed\":\n return { type: \"complete\", reason: \"stop\" };\n case \"failed\":\n case \"rejected\":\n return { type: \"incomplete\", reason: \"error\" };\n case \"canceled\":\n return { type: \"incomplete\", reason: \"cancelled\" };\n case \"input_required\":\n case \"auth_required\":\n return { type: \"requires-action\", reason: \"interrupt\" };\n default:\n return { type: \"running\" };\n }\n}\n\nexport function contentPartsToA2AParts(\n content: ReadonlyArray<{\n type: string;\n text?: string | undefined;\n image?: string | undefined;\n data?: string | undefined;\n
|
|
1
|
+
{"version":3,"file":"conversions.js","names":[],"sources":["../src/conversions.ts"],"sourcesContent":["\"use client\";\n\nimport type { MessageStatus, ThreadAssistantMessage } from \"@assistant-ui/core\";\nimport { httpUrlPattern, parseDataUrl } from \"@assistant-ui/core/internal\";\nimport type { A2AMessage, A2APart, A2ATaskState } from \"./types\";\n\nfunction isImageMediaType(mediaType?: string): boolean {\n return !!mediaType && mediaType.startsWith(\"image/\");\n}\n\nexport function a2aPartToContent(\n part: A2APart,\n): ThreadAssistantMessage[\"content\"][number] {\n if (part.text !== undefined) {\n return { type: \"text\", text: part.text };\n }\n if (part.url !== undefined) {\n if (isImageMediaType(part.mediaType)) {\n return { type: \"image\", image: part.url };\n }\n return {\n type: \"text\",\n text: part.filename ? `[${part.filename}](${part.url})` : part.url,\n };\n }\n if (part.raw !== undefined) {\n if (isImageMediaType(part.mediaType)) {\n return {\n type: \"image\",\n image: `data:${part.mediaType};base64,${part.raw}`,\n };\n }\n return {\n type: \"text\",\n text: `[File: ${part.filename ?? \"download\"}]`,\n };\n }\n if (part.data !== undefined) {\n return { type: \"text\", text: JSON.stringify(part.data, null, 2) };\n }\n return { type: \"text\", text: \"\" };\n}\n\nexport function a2aPartsToContent(\n parts: A2APart[],\n): ThreadAssistantMessage[\"content\"] {\n return parts.map(a2aPartToContent);\n}\n\nconst TERMINAL_STATES = new Set<A2ATaskState>([\n \"completed\",\n \"failed\",\n \"canceled\",\n \"rejected\",\n]);\n\nconst INTERRUPTED_STATES = new Set<A2ATaskState>([\n \"input_required\",\n \"auth_required\",\n]);\n\nexport function isTerminalTaskState(state: A2ATaskState): boolean {\n return TERMINAL_STATES.has(state);\n}\n\nexport function isInterruptedTaskState(state: A2ATaskState): boolean {\n return INTERRUPTED_STATES.has(state);\n}\n\nexport function taskStateToMessageStatus(state: A2ATaskState): MessageStatus {\n switch (state) {\n case \"submitted\":\n case \"working\":\n return { type: \"running\" };\n case \"completed\":\n return { type: \"complete\", reason: \"stop\" };\n case \"failed\":\n case \"rejected\":\n return { type: \"incomplete\", reason: \"error\" };\n case \"canceled\":\n return { type: \"incomplete\", reason: \"cancelled\" };\n case \"input_required\":\n case \"auth_required\":\n return { type: \"requires-action\", reason: \"interrupt\" };\n default:\n return { type: \"running\" };\n }\n}\n\nexport function contentPartsToA2AParts(\n content: ReadonlyArray<{\n type: string;\n text?: string | undefined;\n image?: string | undefined;\n data?: unknown;\n mimeType?: string | undefined;\n filename?: string | undefined;\n audio?: { data: string; format: string } | undefined;\n }>,\n fallbackMimeType?: string,\n): A2APart[] {\n return content\n .map((part): A2APart | null => {\n switch (part.type) {\n case \"text\":\n return { text: part.text ?? \"\" };\n case \"image\": {\n if (!part.image) return null;\n const parsed = parseDataUrl(part.image);\n if (parsed) {\n return {\n raw: parsed.data,\n mediaType: parsed.mimeType,\n ...(part.filename && { filename: part.filename }),\n };\n }\n return {\n url: part.image,\n ...(fallbackMimeType && { mediaType: fallbackMimeType }),\n ...(part.filename && { filename: part.filename }),\n };\n }\n case \"file\": {\n if (typeof part.data !== \"string\" || !part.data) return null;\n const declaredMimeType = part.mimeType || fallbackMimeType;\n if (httpUrlPattern.test(part.data)) {\n return {\n url: part.data,\n ...(declaredMimeType && { mediaType: declaredMimeType }),\n ...(part.filename && { filename: part.filename }),\n };\n }\n const parsed = parseDataUrl(part.data);\n if (parsed) {\n return {\n raw: parsed.data,\n mediaType: parsed.mimeType,\n ...(part.filename && { filename: part.filename }),\n };\n }\n if (/^data:/i.test(part.data)) {\n return {\n url: part.data,\n ...(declaredMimeType && { mediaType: declaredMimeType }),\n ...(part.filename && { filename: part.filename }),\n };\n }\n return {\n raw: part.data,\n ...(declaredMimeType && { mediaType: declaredMimeType }),\n ...(part.filename && { filename: part.filename }),\n };\n }\n case \"audio\": {\n if (!part.audio) return null;\n return {\n raw: parseDataUrl(part.audio.data)?.data ?? part.audio.data,\n mediaType: `audio/${part.audio.format}`,\n };\n }\n case \"data\": {\n if (part.data === undefined) return null;\n return { data: part.data };\n }\n default:\n return null;\n }\n })\n .filter((p): p is A2APart => p !== null);\n}\n\nexport function a2aMessageToContent(\n message: A2AMessage,\n): ThreadAssistantMessage[\"content\"] {\n return a2aPartsToContent(message.parts);\n}\n"],"mappings":"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"}
|
package/dist/useA2ARuntime.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { A2AClient } from "./A2AClient.js";
|
|
3
3
|
import { A2AThreadRuntimeCore } from "./A2AThreadRuntimeCore.js";
|
|
4
4
|
import { a2aExtras } from "./a2aExtras.js";
|
|
5
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
6
5
|
import { useExternalStoreRuntime, useExternalStoreSharedOptions, useRuntimeAdapters } from "@assistant-ui/core/react";
|
|
6
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
7
7
|
//#region src/useA2ARuntime.ts
|
|
8
8
|
const serializeManagedClientOptions = (options) => {
|
|
9
9
|
const fetchOptions = Object.fromEntries(Object.entries(options.fetchOptions ?? {}).sort(([a], [b]) => a.localeCompare(b)));
|
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.25",
|
|
4
4
|
"description": "A2A (Agent-to-Agent) v1.0 protocol adapter for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"a2a",
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
],
|
|
30
30
|
"sideEffects": false,
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@assistant-ui/core": "^0.2
|
|
33
|
-
"@assistant-ui/store": "^0.2
|
|
34
|
-
"assistant-stream": "^0.3.
|
|
32
|
+
"@assistant-ui/core": "^0.3.2",
|
|
33
|
+
"@assistant-ui/store": "^0.3.2",
|
|
34
|
+
"assistant-stream": "^0.3.31"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"@types/react": "*",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"react": "^19.2.8",
|
|
51
51
|
"react-dom": "^19.2.8",
|
|
52
52
|
"vitest": "^4.1.10",
|
|
53
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
53
|
+
"@assistant-ui/x-buildutils": "0.0.20"
|
|
54
54
|
},
|
|
55
55
|
"publishConfig": {
|
|
56
56
|
"access": "public",
|
package/src/A2AClient.test.ts
CHANGED
|
@@ -931,6 +931,69 @@ describe("A2AClient", () => {
|
|
|
931
931
|
expect(evt.event.status.message?.role).toBe("agent");
|
|
932
932
|
});
|
|
933
933
|
|
|
934
|
+
it("cancels the response body when iteration stops early", async () => {
|
|
935
|
+
const sseData = JSON.stringify({
|
|
936
|
+
status_update: {
|
|
937
|
+
task_id: "t1",
|
|
938
|
+
context_id: "ctx-1",
|
|
939
|
+
status: { state: "TASK_STATE_WORKING" },
|
|
940
|
+
},
|
|
941
|
+
});
|
|
942
|
+
const cancel = vi.fn();
|
|
943
|
+
const encoder = new TextEncoder();
|
|
944
|
+
const body = new ReadableStream<Uint8Array>({
|
|
945
|
+
start(controller) {
|
|
946
|
+
controller.enqueue(encoder.encode(`data: ${sseData}\n\n`));
|
|
947
|
+
},
|
|
948
|
+
cancel,
|
|
949
|
+
});
|
|
950
|
+
|
|
951
|
+
fetchMock.mockResolvedValue(
|
|
952
|
+
new Response(body, {
|
|
953
|
+
headers: { "Content-Type": "text/event-stream" },
|
|
954
|
+
}),
|
|
955
|
+
);
|
|
956
|
+
|
|
957
|
+
for await (const _event of client.streamMessage(userMessage)) {
|
|
958
|
+
break;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
expect(cancel).toHaveBeenCalledOnce();
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
it("does not surface cancellation errors on early exit", async () => {
|
|
965
|
+
const sseData = JSON.stringify({
|
|
966
|
+
status_update: {
|
|
967
|
+
task_id: "t1",
|
|
968
|
+
context_id: "ctx-1",
|
|
969
|
+
status: { state: "TASK_STATE_WORKING" },
|
|
970
|
+
},
|
|
971
|
+
});
|
|
972
|
+
const encoder = new TextEncoder();
|
|
973
|
+
let controller!: ReadableStreamDefaultController<Uint8Array>;
|
|
974
|
+
const body = new ReadableStream<Uint8Array>({
|
|
975
|
+
start(streamController) {
|
|
976
|
+
controller = streamController;
|
|
977
|
+
controller.enqueue(encoder.encode(`data: ${sseData}\n\n`));
|
|
978
|
+
},
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
fetchMock.mockResolvedValue(
|
|
982
|
+
new Response(body, {
|
|
983
|
+
headers: { "Content-Type": "text/event-stream" },
|
|
984
|
+
}),
|
|
985
|
+
);
|
|
986
|
+
|
|
987
|
+
const consume = async () => {
|
|
988
|
+
for await (const _event of client.streamMessage(userMessage)) {
|
|
989
|
+
controller.error(new Error("stream failed"));
|
|
990
|
+
break;
|
|
991
|
+
}
|
|
992
|
+
};
|
|
993
|
+
|
|
994
|
+
await expect(consume()).resolves.toBeUndefined();
|
|
995
|
+
});
|
|
996
|
+
|
|
934
997
|
it("parses CRLF-delimited SSE events", async () => {
|
|
935
998
|
const sseData = JSON.stringify({
|
|
936
999
|
status_update: {
|
package/src/A2AClient.ts
CHANGED
|
@@ -637,10 +637,20 @@ export class A2AClient {
|
|
|
637
637
|
}
|
|
638
638
|
};
|
|
639
639
|
|
|
640
|
+
let shouldCancel = true;
|
|
640
641
|
try {
|
|
641
642
|
while (true) {
|
|
642
|
-
|
|
643
|
+
let result: ReadableStreamReadResult<Uint8Array>;
|
|
644
|
+
try {
|
|
645
|
+
result = await reader.read();
|
|
646
|
+
} catch (error) {
|
|
647
|
+
shouldCancel = false;
|
|
648
|
+
throw error;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
const { done, value } = result;
|
|
643
652
|
if (done) {
|
|
653
|
+
shouldCancel = false;
|
|
644
654
|
for (const event of sseDecoder.push(decoder.decode())) {
|
|
645
655
|
const parsed = readEvent(event);
|
|
646
656
|
if (parsed) yield parsed;
|
|
@@ -656,7 +666,11 @@ export class A2AClient {
|
|
|
656
666
|
}
|
|
657
667
|
}
|
|
658
668
|
} finally {
|
|
659
|
-
|
|
669
|
+
try {
|
|
670
|
+
if (shouldCancel) await reader.cancel().catch(() => undefined);
|
|
671
|
+
} finally {
|
|
672
|
+
reader.releaseLock();
|
|
673
|
+
}
|
|
660
674
|
}
|
|
661
675
|
}
|
|
662
676
|
}
|
package/src/a2aExtras.ts
CHANGED
package/src/conversions.test.ts
CHANGED
|
@@ -366,10 +366,90 @@ describe("contentPartsToA2AParts", () => {
|
|
|
366
366
|
expect(result).toEqual([]);
|
|
367
367
|
});
|
|
368
368
|
|
|
369
|
+
it("skips file parts with non-string data", () => {
|
|
370
|
+
const result = contentPartsToA2AParts([
|
|
371
|
+
{ type: "file", data: { nested: true }, mimeType: "application/pdf" },
|
|
372
|
+
]);
|
|
373
|
+
expect(result).toEqual([]);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it("passes non-base64 data URLs through as URLs for file parts", () => {
|
|
377
|
+
const result = contentPartsToA2AParts([
|
|
378
|
+
{ type: "file", data: "data:text/plain,hello", mimeType: "text/plain" },
|
|
379
|
+
]);
|
|
380
|
+
expect(result).toEqual([
|
|
381
|
+
{ url: "data:text/plain,hello", mediaType: "text/plain" },
|
|
382
|
+
]);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it("passes uppercase-scheme non-base64 data URLs through as URLs for file parts", () => {
|
|
386
|
+
const result = contentPartsToA2AParts([
|
|
387
|
+
{ type: "file", data: "DATA:text/plain,hello", mimeType: "text/plain" },
|
|
388
|
+
]);
|
|
389
|
+
expect(result).toEqual([
|
|
390
|
+
{ url: "DATA:text/plain,hello", mediaType: "text/plain" },
|
|
391
|
+
]);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it("converts zero-byte data URLs in file parts to empty raw bytes", () => {
|
|
395
|
+
const result = contentPartsToA2AParts([
|
|
396
|
+
{ type: "file", data: "data:application/pdf;base64,", mimeType: "" },
|
|
397
|
+
]);
|
|
398
|
+
expect(result).toEqual([{ raw: "", mediaType: "application/pdf" }]);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it("converts zero-byte data URLs in image parts to empty raw bytes", () => {
|
|
402
|
+
const result = contentPartsToA2AParts([
|
|
403
|
+
{ type: "image", image: "data:image/png;base64," },
|
|
404
|
+
]);
|
|
405
|
+
expect(result).toEqual([{ raw: "", mediaType: "image/png" }]);
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
it("converts audio parts to raw bytes with the format MIME type", () => {
|
|
409
|
+
const result = contentPartsToA2AParts([
|
|
410
|
+
{ type: "audio", audio: { data: "c291bmQ=", format: "mp3" } },
|
|
411
|
+
]);
|
|
412
|
+
expect(result).toEqual([{ raw: "c291bmQ=", mediaType: "audio/mp3" }]);
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
it("strips data URL envelopes from audio payloads and keeps the format MIME type", () => {
|
|
416
|
+
const result = contentPartsToA2AParts([
|
|
417
|
+
{
|
|
418
|
+
type: "audio",
|
|
419
|
+
audio: { data: "data:audio/mpeg;base64,c291bmQ=", format: "mp3" },
|
|
420
|
+
},
|
|
421
|
+
]);
|
|
422
|
+
expect(result).toEqual([{ raw: "c291bmQ=", mediaType: "audio/mp3" }]);
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it("skips audio parts with no payload", () => {
|
|
426
|
+
const result = contentPartsToA2AParts([{ type: "audio" }]);
|
|
427
|
+
expect(result).toEqual([]);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it("converts data parts to the a2a data field", () => {
|
|
431
|
+
const result = contentPartsToA2AParts([
|
|
432
|
+
{ type: "data", data: { chart: "bar", values: [1, 2] } },
|
|
433
|
+
]);
|
|
434
|
+
expect(result).toEqual([{ data: { chart: "bar", values: [1, 2] } }]);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it("keeps falsy but defined data part payloads", () => {
|
|
438
|
+
const result = contentPartsToA2AParts([
|
|
439
|
+
{ type: "data", data: null },
|
|
440
|
+
{ type: "data", data: 0 },
|
|
441
|
+
]);
|
|
442
|
+
expect(result).toEqual([{ data: null }, { data: 0 }]);
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it("skips data parts with an undefined payload", () => {
|
|
446
|
+
const result = contentPartsToA2AParts([{ type: "data" }]);
|
|
447
|
+
expect(result).toEqual([]);
|
|
448
|
+
});
|
|
449
|
+
|
|
369
450
|
it("skips unknown part types", () => {
|
|
370
451
|
const result = contentPartsToA2AParts([
|
|
371
452
|
{ type: "text", text: "hi" },
|
|
372
|
-
{ type: "audio" },
|
|
373
453
|
{ type: "video" },
|
|
374
454
|
]);
|
|
375
455
|
expect(result).toEqual([{ text: "hi" }]);
|
package/src/conversions.ts
CHANGED
|
@@ -92,9 +92,10 @@ export function contentPartsToA2AParts(
|
|
|
92
92
|
type: string;
|
|
93
93
|
text?: string | undefined;
|
|
94
94
|
image?: string | undefined;
|
|
95
|
-
data?:
|
|
95
|
+
data?: unknown;
|
|
96
96
|
mimeType?: string | undefined;
|
|
97
97
|
filename?: string | undefined;
|
|
98
|
+
audio?: { data: string; format: string } | undefined;
|
|
98
99
|
}>,
|
|
99
100
|
fallbackMimeType?: string,
|
|
100
101
|
): A2APart[] {
|
|
@@ -120,7 +121,7 @@ export function contentPartsToA2AParts(
|
|
|
120
121
|
};
|
|
121
122
|
}
|
|
122
123
|
case "file": {
|
|
123
|
-
if (!part.data) return null;
|
|
124
|
+
if (typeof part.data !== "string" || !part.data) return null;
|
|
124
125
|
const declaredMimeType = part.mimeType || fallbackMimeType;
|
|
125
126
|
if (httpUrlPattern.test(part.data)) {
|
|
126
127
|
return {
|
|
@@ -137,12 +138,30 @@ export function contentPartsToA2AParts(
|
|
|
137
138
|
...(part.filename && { filename: part.filename }),
|
|
138
139
|
};
|
|
139
140
|
}
|
|
141
|
+
if (/^data:/i.test(part.data)) {
|
|
142
|
+
return {
|
|
143
|
+
url: part.data,
|
|
144
|
+
...(declaredMimeType && { mediaType: declaredMimeType }),
|
|
145
|
+
...(part.filename && { filename: part.filename }),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
140
148
|
return {
|
|
141
149
|
raw: part.data,
|
|
142
150
|
...(declaredMimeType && { mediaType: declaredMimeType }),
|
|
143
151
|
...(part.filename && { filename: part.filename }),
|
|
144
152
|
};
|
|
145
153
|
}
|
|
154
|
+
case "audio": {
|
|
155
|
+
if (!part.audio) return null;
|
|
156
|
+
return {
|
|
157
|
+
raw: parseDataUrl(part.audio.data)?.data ?? part.audio.data,
|
|
158
|
+
mediaType: `audio/${part.audio.format}`,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
case "data": {
|
|
162
|
+
if (part.data === undefined) return null;
|
|
163
|
+
return { data: part.data };
|
|
164
|
+
}
|
|
146
165
|
default:
|
|
147
166
|
return null;
|
|
148
167
|
}
|