@assistant-ui/react-a2a 0.2.33 → 0.2.34
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.map +1 -1
- package/dist/A2AClient.js +27 -10
- package/dist/A2AClient.js.map +1 -1
- package/dist/useA2ARuntime.js +5 -0
- package/dist/useA2ARuntime.js.map +1 -1
- package/package.json +5 -5
- package/src/A2AClient.test.ts +131 -0
- package/src/A2AClient.ts +54 -10
- package/src/useA2ARuntime.test.tsx +104 -1
- package/src/useA2ARuntime.ts +5 -0
package/dist/A2AClient.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"A2AClient.d.ts","names":[],"sources":["../src/A2AClient.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"A2AClient.d.ts","names":[],"sources":["../src/A2AClient.ts"],"mappings":";;KAmBY;EACV;;EAEA;;EAEA;EACA,UACI,gCACO,yBAAyB,QAAQ;;EAG5C;;EAEA,eACI,KAAK;;cAIE,iBAAiB;EAC5B;EACA;EACA;EAEY,YAAA,MAAM;;cA2oBP;UACH;UACA;UACA;UACA;UACA;UAIA;EAII,YAAA,SAAS;UAkBb;UAIM;UAqBA;UAyBA;EAoCR,aAAa,SAAS,cAAc,QAAQ;EAe5C,qBAAqB,SAAS,cAAc,QAAQ;EAUpD,YACJ,SAAS,YACT,gBAAgB,6BAChB,WAAW,yBACX,SAAS,cACR,QAAQ,UAAU;EAmBd,cACL,SAAS,YACT,gBAAgB,6BAChB,WAAW,yBACX,SAAS,cACR,eAAe;EA6BZ,QACJ,gBACA,wBACA,SAAS,cACR,QAAQ;EAcL,UACJ,UAAU,qBACV,SAAS,cACR,QAAQ;EAqBL,WACJ,gBACA,WAAW,yBACX,SAAS,cACR,QAAQ;EAaJ,gBACL,gBACA,SAAS,cACR,eAAe;EAqBZ,iCACJ,QAAQ,+BACR,SAAS,cACR,QAAQ;EAiBL,8BACJ,gBACA,kBACA,SAAS,cACR,QAAQ;EAWL,gCACJ,gBACA;IAAY;IAAmB;KAC/B,SAAS,cACR,QAAQ;EAaL,iCACJ,gBACA,kBACA,SAAS,cACR;UAmBY"}
|
package/dist/A2AClient.js
CHANGED
|
@@ -29,12 +29,25 @@ const JSONRPC_STATE_MAP = {
|
|
|
29
29
|
"auth-required": "auth_required",
|
|
30
30
|
unknown: "unspecified"
|
|
31
31
|
};
|
|
32
|
+
const PART_STRING_FIELDS = [
|
|
33
|
+
"text",
|
|
34
|
+
"raw",
|
|
35
|
+
"url",
|
|
36
|
+
"filename",
|
|
37
|
+
"mediaType"
|
|
38
|
+
];
|
|
39
|
+
const NULLABLE_PART_FIELDS = [...PART_STRING_FIELDS, "metadata"];
|
|
40
|
+
function normalizePartNulls(part) {
|
|
41
|
+
const normalized = { ...part };
|
|
42
|
+
for (const field of NULLABLE_PART_FIELDS) if (normalized[field] === null) delete normalized[field];
|
|
43
|
+
return normalized;
|
|
44
|
+
}
|
|
32
45
|
function normalizeParts(value) {
|
|
33
46
|
return value.map((raw) => {
|
|
34
47
|
const part = normalizeKeys(raw, false);
|
|
35
48
|
if (part === null || typeof part !== "object" || Array.isArray(part)) return part;
|
|
36
|
-
const record = part;
|
|
37
|
-
if (record.kind === void 0) return
|
|
49
|
+
const record = normalizePartNulls(part);
|
|
50
|
+
if (record.kind === void 0) return record;
|
|
38
51
|
const { kind, ...rest } = record;
|
|
39
52
|
const file = rest.file;
|
|
40
53
|
if (kind !== "file" || file === null || typeof file !== "object") return rest;
|
|
@@ -42,10 +55,10 @@ function normalizeParts(value) {
|
|
|
42
55
|
const nested = file;
|
|
43
56
|
return {
|
|
44
57
|
...others,
|
|
45
|
-
...nested.uri
|
|
46
|
-
...nested.bytes
|
|
47
|
-
...nested.mimeType
|
|
48
|
-
...nested.name
|
|
58
|
+
...nested.uri != null ? { url: nested.uri } : {},
|
|
59
|
+
...nested.bytes != null ? { raw: nested.bytes } : {},
|
|
60
|
+
...nested.mimeType != null ? { mediaType: nested.mimeType } : {},
|
|
61
|
+
...nested.name != null ? { filename: nested.name } : {}
|
|
49
62
|
};
|
|
50
63
|
});
|
|
51
64
|
}
|
|
@@ -166,8 +179,12 @@ const ROLES = new Set(Object.keys({
|
|
|
166
179
|
}));
|
|
167
180
|
const isRole = (value) => typeof value === "string" && ROLES.has(value);
|
|
168
181
|
const hasOptionalStringIds = (value, keys) => keys.every((key) => value[key] == null || typeof value[key] === "string");
|
|
169
|
-
const
|
|
170
|
-
const
|
|
182
|
+
const hasOptionalStringFields = (value, keys) => keys.every((key) => value[key] === void 0 || typeof value[key] === "string");
|
|
183
|
+
const isPart = (value) => isRecord(value) && hasOptionalStringFields(value, PART_STRING_FIELDS) && (value.metadata === void 0 || isRecord(value.metadata));
|
|
184
|
+
const hasValidNestedParts = (value) => !isRecord(value) || !Array.isArray(value.parts) || value.parts.every(isPart);
|
|
185
|
+
const hasValidNestedPartsInCollection = (value) => !Array.isArray(value) || value.every(hasValidNestedParts);
|
|
186
|
+
const isTask = (value) => isRecord(value) && typeof value.id === "string" && value.id.length > 0 && hasOptionalStringIds(value, ["contextId"]) && isRecord(value.status) && isTaskState(value.status.state) && hasValidNestedParts(value.status.message) && hasValidNestedPartsInCollection(value.artifacts) && hasValidNestedPartsInCollection(value.history);
|
|
187
|
+
const isMessage = (value) => isRecord(value) && typeof value.messageId === "string" && value.messageId.length > 0 && hasOptionalStringIds(value, ["contextId", "taskId"]) && isRole(value.role) && Array.isArray(value.parts) && value.parts.every(isPart);
|
|
171
188
|
const hasStringIds = (value, keys) => keys.every((key) => typeof value[key] === "string");
|
|
172
189
|
const toWrappedTaskStatus = (value) => {
|
|
173
190
|
if (!isRecord(value)) return null;
|
|
@@ -198,7 +215,7 @@ const toWrappedMessage = (value) => {
|
|
|
198
215
|
};
|
|
199
216
|
return isMessage(message) && hasStringIds(message, ["contextId", "taskId"]) ? message : null;
|
|
200
217
|
};
|
|
201
|
-
const isStatusUpdate = (value, allowEmptyTaskId = false) => isRecord(value) && typeof value.taskId === "string" && (allowEmptyTaskId || value.taskId.length > 0) && hasOptionalStringIds(value, ["contextId"]) && isRecord(value.status) && isTaskState(value.status.state);
|
|
218
|
+
const isStatusUpdate = (value, allowEmptyTaskId = false) => isRecord(value) && typeof value.taskId === "string" && (allowEmptyTaskId || value.taskId.length > 0) && hasOptionalStringIds(value, ["contextId"]) && isRecord(value.status) && isTaskState(value.status.state) && hasValidNestedParts(value.status.message);
|
|
202
219
|
const toWrappedStatusUpdate = (value) => {
|
|
203
220
|
if (!isRecord(value) || !isRecord(value.status)) return null;
|
|
204
221
|
const statusUpdate = {
|
|
@@ -209,7 +226,7 @@ const toWrappedStatusUpdate = (value) => {
|
|
|
209
226
|
};
|
|
210
227
|
return isStatusUpdate(statusUpdate, true) && hasStringIds(statusUpdate, ["taskId", "contextId"]) ? statusUpdate : null;
|
|
211
228
|
};
|
|
212
|
-
const isArtifact = (value) => isRecord(value) && typeof value.artifactId === "string" && Array.isArray(value.parts) && value.parts.every(
|
|
229
|
+
const isArtifact = (value) => isRecord(value) && typeof value.artifactId === "string" && Array.isArray(value.parts) && value.parts.every(isPart);
|
|
213
230
|
const isArtifactUpdate = (value) => isRecord(value) && hasOptionalStringIds(value, ["contextId", "taskId"]) && isArtifact(value.artifact);
|
|
214
231
|
const toWrappedArtifact = (value) => {
|
|
215
232
|
if (!isRecord(value)) return null;
|
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\nconst JSONRPC_STATE_MAP: Record<string, string> = {\n \"input-required\": \"input_required\",\n \"auth-required\": \"auth_required\",\n unknown: \"unspecified\",\n};\n\n// JSON-RPC file parts nest the payload under `file`; the internal A2APart is\n// flat, so the nested fields map onto url/raw/mediaType/filename.\nfunction normalizeParts(value: unknown[]): unknown[] {\n return value.map((raw) => {\n const part = normalizeKeys(raw, false);\n if (part === null || typeof part !== \"object\" || Array.isArray(part))\n return part;\n const record = part as Record<string, unknown>;\n if (record.kind === undefined) return part;\n const { kind, ...rest } = record;\n const file = rest.file;\n if (kind !== \"file\" || file === null || typeof file !== \"object\")\n return rest;\n const { file: _file, ...others } = rest;\n const nested = file as Record<string, unknown>;\n return {\n ...others,\n ...(nested.uri !== undefined ? { url: nested.uri } : {}),\n ...(nested.bytes !== undefined ? { raw: nested.bytes } : {}),\n ...(nested.mimeType !== undefined ? { mediaType: nested.mimeType } : {}),\n ...(nested.name !== undefined ? { filename: nested.name } : {}),\n };\n });\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 (camelKey === \"state\" && typeof value === \"string\") {\n // Proto-style (TASK_STATE_WORKING) and the JSON-RPC state names map\n // onto the internal snake_case states; anything unrecognized is\n // preserved verbatim.\n if (value.startsWith(\"TASK_STATE_\")) {\n result[camelKey] = value.slice(11).toLowerCase();\n } else {\n result[camelKey] = JSONRPC_STATE_MAP[value] ?? value;\n }\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 = normalizeParts(value);\n } else if (camelKey === \"parts\" && Array.isArray(value)) {\n // dedup: \"content\" was already mapped to parts above; don't overwrite\n if (!(\"parts\" in result)) result.parts = normalizeParts(value);\n } else if (camelKey !== \"parts\" || !(\"parts\" in result)) {\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) {\n const task = toWrappedTask(data.task);\n if (task) return { type: \"task\", task };\n }\n if (\"message\" in data) {\n const message = toWrappedMessage(data.message);\n if (message) return { type: \"message\", message };\n }\n if (\"statusUpdate\" in data) {\n const statusUpdate = toWrappedStatusUpdate(data.statusUpdate);\n if (statusUpdate) {\n return {\n type: \"statusUpdate\",\n event: statusUpdate as A2AStreamEvent extends {\n type: \"statusUpdate\";\n event: infer E;\n }\n ? E\n : never,\n };\n }\n }\n if (\"artifactUpdate\" in data) {\n const artifactUpdate = toWrappedArtifactUpdate(data.artifactUpdate);\n if (artifactUpdate) {\n return {\n type: \"artifactUpdate\",\n event: artifactUpdate as A2AStreamEvent extends {\n type: \"artifactUpdate\";\n event: infer E;\n }\n ? E\n : never,\n };\n }\n }\n // JSON-RPC streaming results are the event itself, flat, discriminated by\n // `kind` (per the A2A JSON-RPC schema), rather than wrapped in a\n // REST-style single-key envelope. The field sets cannot collide with the\n // wrapper keys above, so this is a pure fallthrough.\n const { kind, ...flat } = data;\n switch (kind) {\n case \"task\":\n if (!isTask(flat)) break;\n return { type: \"task\", task: flat };\n case \"message\":\n if (!isMessage(flat)) break;\n return { type: \"message\", message: flat };\n case \"status-update\": {\n if (!isStatusUpdate(flat)) break;\n const { final: _final, ...event } = flat;\n return {\n type: \"statusUpdate\",\n event: event as unknown as A2AStreamEvent extends {\n type: \"statusUpdate\";\n event: infer E;\n }\n ? E\n : never,\n };\n }\n case \"artifact-update\":\n if (!isArtifactUpdate(flat)) break;\n return {\n type: \"artifactUpdate\",\n event: flat as unknown 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 ROLES: ReadonlySet<string> = new Set(\n Object.keys({\n unspecified: true,\n user: true,\n agent: true,\n } satisfies Record<A2ARole, true>),\n);\n\nconst isRole = (value: unknown): value is A2ARole =>\n typeof value === \"string\" && ROLES.has(value);\n\n// Ids reach task state and the next request body unchecked by anything\n// downstream, so an id that is present and not null must be a string.\n// An omitted or null id keeps the acceptance each path already had.\nconst hasOptionalStringIds = (\n value: Record<string, unknown>,\n keys: readonly string[],\n): boolean =>\n keys.every((key) => value[key] == null || typeof value[key] === \"string\");\n\nconst isTask = (value: unknown): value is A2ATask =>\n isRecord(value) &&\n typeof value.id === \"string\" &&\n value.id.length > 0 &&\n hasOptionalStringIds(value, [\"contextId\"]) &&\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 hasOptionalStringIds(value, [\"contextId\", \"taskId\"]) &&\n isRole(value.role) &&\n Array.isArray(value.parts) &&\n value.parts.every(isRecord);\n\n// Legacy wrappers use ProtoJSON, where omitted and null fields decode to proto\n// defaults. Normalize those defaults before enforcing semantic requirements.\n// Filling a null id with the ProtoJSON default does not make it a string, and\n// the shape guards below check the fields they name rather than the ids. The\n// runtime reads these straight into task state and the next request body.\nconst hasStringIds = (\n value: Record<string, unknown>,\n keys: readonly string[],\n): boolean => keys.every((key) => typeof value[key] === \"string\");\n\nconst toWrappedTaskStatus = (\n value: unknown,\n): Record<string, unknown> | null => {\n if (!isRecord(value)) return null;\n return {\n ...value,\n state: value.state == null ? \"unspecified\" : value.state,\n };\n};\n\nconst toWrappedTask = (value: unknown): A2ATask | null => {\n if (!isRecord(value)) return null;\n const status = toWrappedTaskStatus(value.status);\n if (!status) return null;\n\n const task = {\n ...value,\n contextId: value.contextId == null ? \"\" : value.contextId,\n status,\n };\n return isTask(task) && hasStringIds(task, [\"contextId\"]) ? task : null;\n};\n\nconst toWrappedMessage = (value: unknown): A2AMessage | null => {\n if (!isRecord(value)) return null;\n\n const message = {\n ...value,\n contextId: value.contextId == null ? \"\" : value.contextId,\n taskId: value.taskId == null ? \"\" : value.taskId,\n role: value.role == null ? \"unspecified\" : value.role,\n parts: value.parts == null ? [] : value.parts,\n };\n return isMessage(message) && hasStringIds(message, [\"contextId\", \"taskId\"])\n ? message\n : null;\n};\n\nconst isStatusUpdate = (\n value: unknown,\n allowEmptyTaskId = false,\n): value is Record<string, unknown> =>\n isRecord(value) &&\n typeof value.taskId === \"string\" &&\n (allowEmptyTaskId || value.taskId.length > 0) &&\n hasOptionalStringIds(value, [\"contextId\"]) &&\n isRecord(value.status) &&\n isTaskState(value.status.state);\n\nconst toWrappedStatusUpdate = (\n value: unknown,\n): Record<string, unknown> | null => {\n if (!isRecord(value) || !isRecord(value.status)) return null;\n\n const statusUpdate = {\n ...value,\n taskId: value.taskId == null ? \"\" : value.taskId,\n contextId: value.contextId == null ? \"\" : value.contextId,\n status: toWrappedTaskStatus(value.status),\n };\n return isStatusUpdate(statusUpdate, true) &&\n hasStringIds(statusUpdate, [\"taskId\", \"contextId\"])\n ? statusUpdate\n : null;\n};\n\nconst isArtifact = (value: unknown): value is Record<string, unknown> =>\n isRecord(value) &&\n typeof value.artifactId === \"string\" &&\n Array.isArray(value.parts) &&\n value.parts.every(isRecord);\n\nconst isArtifactUpdate = (value: unknown): value is Record<string, unknown> =>\n isRecord(value) &&\n hasOptionalStringIds(value, [\"contextId\", \"taskId\"]) &&\n isArtifact(value.artifact);\n\nconst toWrappedArtifact = (value: unknown): Record<string, unknown> | null => {\n if (!isRecord(value)) return null;\n\n const artifact = {\n ...value,\n artifactId: value.artifactId == null ? \"\" : value.artifactId,\n parts: value.parts == null ? [] : value.parts,\n };\n return isArtifact(artifact) ? artifact : null;\n};\n\nconst toWrappedArtifactUpdate = (\n value: unknown,\n): Record<string, unknown> | null => {\n if (!isRecord(value)) return null;\n const artifact = toWrappedArtifact(value.artifact);\n if (!artifact) return null;\n\n const artifactUpdate = {\n ...value,\n taskId: value.taskId == null ? \"\" : value.taskId,\n contextId: value.contextId == null ? \"\" : value.contextId,\n artifact,\n };\n return isArtifactUpdate(artifactUpdate) &&\n hasStringIds(artifactUpdate, [\"taskId\", \"contextId\"])\n ? artifactUpdate\n : null;\n};\n\nconst isStringArray = (value: unknown): value is string[] =>\n Array.isArray(value) && value.every((item) => typeof item === \"string\");\n\nconst toJsonRpcError = (error: unknown): A2AError => {\n const rpcError = error as { code?: number; message?: string; data?: unknown };\n return new A2AError({\n code: rpcError.code ?? -1,\n status: \"JSONRPC_ERROR\",\n message: rpcError.message ?? \"A2A JSON-RPC error\",\n details:\n rpcError.data === undefined\n ? undefined\n : Array.isArray(rpcError.data)\n ? rpcError.data\n : [rpcError.data],\n });\n};\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\nconst parseTaskResponse = (\n value: unknown,\n operation: \"tasks:get\" | \"tasks:cancel\",\n): A2ATask => {\n if (isTask(value)) return value;\n\n throw new Error(\n `Invalid A2A ${operation} response: expected a valid task payload.`,\n );\n};\n\nconst isNonNegativeInteger = (value: unknown): value is number =>\n typeof value === \"number\" && Number.isInteger(value) && value >= 0;\n\nconst invalidListTasksResponse = (): never => {\n throw new Error(\n \"Invalid A2A tasks:list response: expected a valid task list payload.\",\n );\n};\n\nconst parseListTasksResponse = (value: unknown): A2AListTasksResponse => {\n if (!isRecord(value)) return invalidListTasksResponse();\n\n const tasks = value.tasks ?? [];\n if (!Array.isArray(tasks) || !tasks.every(isTask)) {\n return invalidListTasksResponse();\n }\n\n const { nextPageToken, pageSize, totalSize } = value;\n if (\n (nextPageToken != null && typeof nextPageToken !== \"string\") ||\n (pageSize != null && !isNonNegativeInteger(pageSize)) ||\n (totalSize != null && !isNonNegativeInteger(totalSize))\n ) {\n return invalidListTasksResponse();\n }\n\n return {\n ...value,\n tasks,\n nextPageToken: nextPageToken ?? \"\",\n pageSize: pageSize ?? 0,\n totalSize: totalSize ?? 0,\n };\n};\n\nconst invalidPushNotificationConfigResponse =\n (\n operation: \"pushNotificationConfigs:create\" | \"pushNotificationConfigs:get\",\n ) =>\n (): never => {\n throw new Error(\n `Invalid A2A ${operation} response: expected a valid push notification config payload.`,\n );\n };\n\nconst invalidListPushNotificationConfigsResponse = (): never => {\n throw new Error(\n \"Invalid A2A pushNotificationConfigs:list response: expected a valid push notification config list payload.\",\n );\n};\n\nconst parseOptionalString = (\n value: unknown,\n invalid: () => never,\n): string | undefined =>\n value == null ? undefined : typeof value === \"string\" ? value : invalid();\n\nconst parseTaskPushNotificationConfigResponse = (\n value: unknown,\n invalid: () => never,\n): A2ATaskPushNotificationConfig => {\n if (\n !isRecord(value) ||\n typeof value.url !== \"string\" ||\n value.url.length === 0\n ) {\n return invalid();\n }\n\n const { tenant, id, taskId, url, token, authentication, ...extra } = value;\n let normalizedAuthentication: A2ATaskPushNotificationConfig[\"authentication\"];\n if (authentication != null) {\n if (!isRecord(authentication)) return invalid();\n const { scheme, credentials, ...authenticationExtra } = authentication;\n normalizedAuthentication = {\n ...authenticationExtra,\n scheme: parseOptionalString(scheme, invalid) ?? \"\",\n ...(credentials == null\n ? {}\n : { credentials: parseOptionalString(credentials, invalid) }),\n };\n }\n\n return {\n ...extra,\n ...(tenant == null ? {} : { tenant: parseOptionalString(tenant, invalid) }),\n ...(id == null ? {} : { id: parseOptionalString(id, invalid) }),\n ...(taskId == null ? {} : { taskId: parseOptionalString(taskId, invalid) }),\n url,\n ...(token == null ? {} : { token: parseOptionalString(token, invalid) }),\n ...(normalizedAuthentication === undefined\n ? {}\n : { authentication: normalizedAuthentication }),\n };\n};\n\nconst parseListTaskPushNotificationConfigsResponse = (\n value: unknown,\n): A2AListTaskPushNotificationConfigsResponse => {\n if (!isRecord(value)) return invalidListPushNotificationConfigsResponse();\n\n const { configs: rawConfigs, nextPageToken, ...extra } = value;\n if (rawConfigs != null && !Array.isArray(rawConfigs)) {\n return invalidListPushNotificationConfigsResponse();\n }\n\n return {\n ...extra,\n configs: (rawConfigs ?? []).map((config) =>\n parseTaskPushNotificationConfigResponse(\n config,\n invalidListPushNotificationConfigsResponse,\n ),\n ),\n ...(nextPageToken == null\n ? {}\n : {\n nextPageToken: parseOptionalString(\n nextPageToken,\n invalidListPushNotificationConfigsResponse,\n ),\n }),\n };\n};\n\nfunction signalInit(signal?: AbortSignal): RequestInit {\n return signal ? { signal } : {};\n}\n\nconst SKIPPED_FRAME_SNIPPET_LENGTH = 120;\n\nfunction describeSkippedFrame(data: string, reason: string): string {\n const collapsed = data.replace(/\\s+/g, \" \");\n const snippet =\n collapsed.length > SKIPPED_FRAME_SNIPPET_LENGTH\n ? `${collapsed.slice(0, SKIPPED_FRAME_SNIPPET_LENGTH)}…`\n : collapsed;\n return `${reason} (frame: ${snippet})`;\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 if (json && typeof json === \"object\" && \"jsonrpc\" in json) {\n if (\"error\" in json && json.error) {\n throw toJsonRpcError(json.error);\n }\n if (\"result\" in json) {\n const result = normalizeKeys(json.result);\n if (isRecord(result) && typeof result.kind === \"string\") {\n const { kind: _kind, ...rest } = result;\n return rest as T;\n }\n return result as T;\n }\n }\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 return 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 const result = await this.fetchJSON<unknown>(\n `${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}${qs ? `?${qs}` : \"\"}`,\n signalInit(signal),\n );\n return parseTaskResponse(result, \"tasks:get\");\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 const result = await this.fetchJSON<unknown>(\n `${this.getBasePath()}/tasks${qs ? `?${qs}` : \"\"}`,\n signalInit(signal),\n );\n return parseListTasksResponse(result);\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 const result = await this.fetchJSON<unknown>(\n `${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}:cancel`,\n {\n method: \"POST\",\n body: JSON.stringify(body),\n ...signalInit(signal),\n },\n );\n return parseTaskResponse(result, \"tasks:cancel\");\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 const result = await this.fetchJSON<unknown>(\n `${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs`,\n {\n method: \"POST\",\n body: JSON.stringify(config),\n ...signalInit(signal),\n },\n );\n return parseTaskPushNotificationConfigResponse(\n result,\n invalidPushNotificationConfigResponse(\"pushNotificationConfigs:create\"),\n );\n }\n\n async getTaskPushNotificationConfig(\n taskId: string,\n configId: string,\n signal?: AbortSignal,\n ): Promise<A2ATaskPushNotificationConfig> {\n const result = await this.fetchJSON<unknown>(\n `${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs/${encodeURIComponent(configId)}`,\n signalInit(signal),\n );\n return parseTaskPushNotificationConfigResponse(\n result,\n invalidPushNotificationConfigResponse(\"pushNotificationConfigs:get\"),\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 const result = await this.fetchJSON<unknown>(\n `${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs${qs ? `?${qs}` : \"\"}`,\n signalInit(signal),\n );\n return parseListTaskPushNotificationConfigsResponse(result);\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(\n response: Response,\n ): AsyncGenerator<A2AStreamEvent, string | undefined> {\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 void response.body?.cancel().catch(() => undefined);\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 let firstSkipReason: string | undefined;\n const noteSkip = (data: string, reason: string) => {\n firstSkipReason ??= describeSkippedFrame(data, reason);\n };\n\n const readEvent = (event: SSEEvent): A2AStreamEvent | null => {\n try {\n let parsed = JSON.parse(event.data);\n\n if (parsed && typeof parsed === \"object\" && \"jsonrpc\" in parsed) {\n if (\"error\" in parsed && parsed.error) {\n throw toJsonRpcError(parsed.error);\n }\n if (\"result\" in parsed) {\n parsed = parsed.result;\n }\n }\n\n const normalized = normalizeKeys(parsed) as Record<string, unknown>;\n const streamEvent = discriminateStreamResponse(normalized);\n if (!streamEvent) noteSkip(event.data, \"unrecognized event shape\");\n return streamEvent;\n } catch (error) {\n if (error instanceof A2AError) throw error;\n noteSkip(\n event.data,\n error instanceof Error ? error.message : String(error),\n );\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 return firstSkipReason;\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 A2APart,\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\nconst JSONRPC_STATE_MAP: Record<string, string> = {\n \"input-required\": \"input_required\",\n \"auth-required\": \"auth_required\",\n unknown: \"unspecified\",\n};\n\nconst PART_STRING_FIELDS = [\n \"text\",\n \"raw\",\n \"url\",\n \"filename\",\n \"mediaType\",\n] as const;\n\nconst NULLABLE_PART_FIELDS = [...PART_STRING_FIELDS, \"metadata\"] as const;\n\nfunction normalizePartNulls(\n part: Record<string, unknown>,\n): Record<string, unknown> {\n const normalized = { ...part };\n for (const field of NULLABLE_PART_FIELDS) {\n if (normalized[field] === null) delete normalized[field];\n }\n return normalized;\n}\n\n// JSON-RPC file parts nest the payload under `file`; the internal A2APart is\n// flat, so the nested fields map onto url/raw/mediaType/filename.\nfunction normalizeParts(value: unknown[]): unknown[] {\n return value.map((raw) => {\n const part = normalizeKeys(raw, false);\n if (part === null || typeof part !== \"object\" || Array.isArray(part))\n return part;\n const record = normalizePartNulls(part as Record<string, unknown>);\n if (record.kind === undefined) return record;\n const { kind, ...rest } = record;\n const file = rest.file;\n if (kind !== \"file\" || file === null || typeof file !== \"object\")\n return rest;\n const { file: _file, ...others } = rest;\n const nested = file as Record<string, unknown>;\n return {\n ...others,\n ...(nested.uri != null ? { url: nested.uri } : {}),\n ...(nested.bytes != null ? { raw: nested.bytes } : {}),\n ...(nested.mimeType != null ? { mediaType: nested.mimeType } : {}),\n ...(nested.name != null ? { filename: nested.name } : {}),\n };\n });\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 (camelKey === \"state\" && typeof value === \"string\") {\n // Proto-style (TASK_STATE_WORKING) and the JSON-RPC state names map\n // onto the internal snake_case states; anything unrecognized is\n // preserved verbatim.\n if (value.startsWith(\"TASK_STATE_\")) {\n result[camelKey] = value.slice(11).toLowerCase();\n } else {\n result[camelKey] = JSONRPC_STATE_MAP[value] ?? value;\n }\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 = normalizeParts(value);\n } else if (camelKey === \"parts\" && Array.isArray(value)) {\n // dedup: \"content\" was already mapped to parts above; don't overwrite\n if (!(\"parts\" in result)) result.parts = normalizeParts(value);\n } else if (camelKey !== \"parts\" || !(\"parts\" in result)) {\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) {\n const task = toWrappedTask(data.task);\n if (task) return { type: \"task\", task };\n }\n if (\"message\" in data) {\n const message = toWrappedMessage(data.message);\n if (message) return { type: \"message\", message };\n }\n if (\"statusUpdate\" in data) {\n const statusUpdate = toWrappedStatusUpdate(data.statusUpdate);\n if (statusUpdate) {\n return {\n type: \"statusUpdate\",\n event: statusUpdate as A2AStreamEvent extends {\n type: \"statusUpdate\";\n event: infer E;\n }\n ? E\n : never,\n };\n }\n }\n if (\"artifactUpdate\" in data) {\n const artifactUpdate = toWrappedArtifactUpdate(data.artifactUpdate);\n if (artifactUpdate) {\n return {\n type: \"artifactUpdate\",\n event: artifactUpdate as A2AStreamEvent extends {\n type: \"artifactUpdate\";\n event: infer E;\n }\n ? E\n : never,\n };\n }\n }\n // JSON-RPC streaming results are the event itself, flat, discriminated by\n // `kind` (per the A2A JSON-RPC schema), rather than wrapped in a\n // REST-style single-key envelope. The field sets cannot collide with the\n // wrapper keys above, so this is a pure fallthrough.\n const { kind, ...flat } = data;\n switch (kind) {\n case \"task\":\n if (!isTask(flat)) break;\n return { type: \"task\", task: flat };\n case \"message\":\n if (!isMessage(flat)) break;\n return { type: \"message\", message: flat };\n case \"status-update\": {\n if (!isStatusUpdate(flat)) break;\n const { final: _final, ...event } = flat;\n return {\n type: \"statusUpdate\",\n event: event as unknown as A2AStreamEvent extends {\n type: \"statusUpdate\";\n event: infer E;\n }\n ? E\n : never,\n };\n }\n case \"artifact-update\":\n if (!isArtifactUpdate(flat)) break;\n return {\n type: \"artifactUpdate\",\n event: flat as unknown 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 ROLES: ReadonlySet<string> = new Set(\n Object.keys({\n unspecified: true,\n user: true,\n agent: true,\n } satisfies Record<A2ARole, true>),\n);\n\nconst isRole = (value: unknown): value is A2ARole =>\n typeof value === \"string\" && ROLES.has(value);\n\n// Ids reach task state and the next request body unchecked by anything\n// downstream, so an id that is present and not null must be a string.\n// An omitted or null id keeps the acceptance each path already had.\nconst hasOptionalStringIds = (\n value: Record<string, unknown>,\n keys: readonly string[],\n): boolean =>\n keys.every((key) => value[key] == null || typeof value[key] === \"string\");\n\nconst hasOptionalStringFields = (\n value: Record<string, unknown>,\n keys: readonly string[],\n): boolean =>\n keys.every(\n (key) => value[key] === undefined || typeof value[key] === \"string\",\n );\n\nconst isPart = (value: unknown): value is A2APart =>\n isRecord(value) &&\n hasOptionalStringFields(value, PART_STRING_FIELDS) &&\n (value.metadata === undefined || isRecord(value.metadata));\n\nconst hasValidNestedParts = (value: unknown): boolean =>\n !isRecord(value) || !Array.isArray(value.parts) || value.parts.every(isPart);\n\nconst hasValidNestedPartsInCollection = (value: unknown): boolean =>\n !Array.isArray(value) || value.every(hasValidNestedParts);\n\nconst isTask = (value: unknown): value is A2ATask =>\n isRecord(value) &&\n typeof value.id === \"string\" &&\n value.id.length > 0 &&\n hasOptionalStringIds(value, [\"contextId\"]) &&\n isRecord(value.status) &&\n isTaskState(value.status.state) &&\n hasValidNestedParts(value.status.message) &&\n hasValidNestedPartsInCollection(value.artifacts) &&\n hasValidNestedPartsInCollection(value.history);\n\nconst isMessage = (value: unknown): value is A2AMessage =>\n isRecord(value) &&\n typeof value.messageId === \"string\" &&\n value.messageId.length > 0 &&\n hasOptionalStringIds(value, [\"contextId\", \"taskId\"]) &&\n isRole(value.role) &&\n Array.isArray(value.parts) &&\n value.parts.every(isPart);\n\n// Legacy wrappers use ProtoJSON, where omitted and null fields decode to proto\n// defaults. Normalize those defaults before enforcing semantic requirements.\n// Filling a null id with the ProtoJSON default does not make it a string, and\n// the shape guards below check the fields they name rather than the ids. The\n// runtime reads these straight into task state and the next request body.\nconst hasStringIds = (\n value: Record<string, unknown>,\n keys: readonly string[],\n): boolean => keys.every((key) => typeof value[key] === \"string\");\n\nconst toWrappedTaskStatus = (\n value: unknown,\n): Record<string, unknown> | null => {\n if (!isRecord(value)) return null;\n return {\n ...value,\n state: value.state == null ? \"unspecified\" : value.state,\n };\n};\n\nconst toWrappedTask = (value: unknown): A2ATask | null => {\n if (!isRecord(value)) return null;\n const status = toWrappedTaskStatus(value.status);\n if (!status) return null;\n\n const task = {\n ...value,\n contextId: value.contextId == null ? \"\" : value.contextId,\n status,\n };\n return isTask(task) && hasStringIds(task, [\"contextId\"]) ? task : null;\n};\n\nconst toWrappedMessage = (value: unknown): A2AMessage | null => {\n if (!isRecord(value)) return null;\n\n const message = {\n ...value,\n contextId: value.contextId == null ? \"\" : value.contextId,\n taskId: value.taskId == null ? \"\" : value.taskId,\n role: value.role == null ? \"unspecified\" : value.role,\n parts: value.parts == null ? [] : value.parts,\n };\n return isMessage(message) && hasStringIds(message, [\"contextId\", \"taskId\"])\n ? message\n : null;\n};\n\nconst isStatusUpdate = (\n value: unknown,\n allowEmptyTaskId = false,\n): value is Record<string, unknown> =>\n isRecord(value) &&\n typeof value.taskId === \"string\" &&\n (allowEmptyTaskId || value.taskId.length > 0) &&\n hasOptionalStringIds(value, [\"contextId\"]) &&\n isRecord(value.status) &&\n isTaskState(value.status.state) &&\n hasValidNestedParts(value.status.message);\n\nconst toWrappedStatusUpdate = (\n value: unknown,\n): Record<string, unknown> | null => {\n if (!isRecord(value) || !isRecord(value.status)) return null;\n\n const statusUpdate = {\n ...value,\n taskId: value.taskId == null ? \"\" : value.taskId,\n contextId: value.contextId == null ? \"\" : value.contextId,\n status: toWrappedTaskStatus(value.status),\n };\n return isStatusUpdate(statusUpdate, true) &&\n hasStringIds(statusUpdate, [\"taskId\", \"contextId\"])\n ? statusUpdate\n : null;\n};\n\nconst isArtifact = (value: unknown): value is Record<string, unknown> =>\n isRecord(value) &&\n typeof value.artifactId === \"string\" &&\n Array.isArray(value.parts) &&\n value.parts.every(isPart);\n\nconst isArtifactUpdate = (value: unknown): value is Record<string, unknown> =>\n isRecord(value) &&\n hasOptionalStringIds(value, [\"contextId\", \"taskId\"]) &&\n isArtifact(value.artifact);\n\nconst toWrappedArtifact = (value: unknown): Record<string, unknown> | null => {\n if (!isRecord(value)) return null;\n\n const artifact = {\n ...value,\n artifactId: value.artifactId == null ? \"\" : value.artifactId,\n parts: value.parts == null ? [] : value.parts,\n };\n return isArtifact(artifact) ? artifact : null;\n};\n\nconst toWrappedArtifactUpdate = (\n value: unknown,\n): Record<string, unknown> | null => {\n if (!isRecord(value)) return null;\n const artifact = toWrappedArtifact(value.artifact);\n if (!artifact) return null;\n\n const artifactUpdate = {\n ...value,\n taskId: value.taskId == null ? \"\" : value.taskId,\n contextId: value.contextId == null ? \"\" : value.contextId,\n artifact,\n };\n return isArtifactUpdate(artifactUpdate) &&\n hasStringIds(artifactUpdate, [\"taskId\", \"contextId\"])\n ? artifactUpdate\n : null;\n};\n\nconst isStringArray = (value: unknown): value is string[] =>\n Array.isArray(value) && value.every((item) => typeof item === \"string\");\n\nconst toJsonRpcError = (error: unknown): A2AError => {\n const rpcError = error as { code?: number; message?: string; data?: unknown };\n return new A2AError({\n code: rpcError.code ?? -1,\n status: \"JSONRPC_ERROR\",\n message: rpcError.message ?? \"A2A JSON-RPC error\",\n details:\n rpcError.data === undefined\n ? undefined\n : Array.isArray(rpcError.data)\n ? rpcError.data\n : [rpcError.data],\n });\n};\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\nconst parseTaskResponse = (\n value: unknown,\n operation: \"tasks:get\" | \"tasks:cancel\",\n): A2ATask => {\n if (isTask(value)) return value;\n\n throw new Error(\n `Invalid A2A ${operation} response: expected a valid task payload.`,\n );\n};\n\nconst isNonNegativeInteger = (value: unknown): value is number =>\n typeof value === \"number\" && Number.isInteger(value) && value >= 0;\n\nconst invalidListTasksResponse = (): never => {\n throw new Error(\n \"Invalid A2A tasks:list response: expected a valid task list payload.\",\n );\n};\n\nconst parseListTasksResponse = (value: unknown): A2AListTasksResponse => {\n if (!isRecord(value)) return invalidListTasksResponse();\n\n const tasks = value.tasks ?? [];\n if (!Array.isArray(tasks) || !tasks.every(isTask)) {\n return invalidListTasksResponse();\n }\n\n const { nextPageToken, pageSize, totalSize } = value;\n if (\n (nextPageToken != null && typeof nextPageToken !== \"string\") ||\n (pageSize != null && !isNonNegativeInteger(pageSize)) ||\n (totalSize != null && !isNonNegativeInteger(totalSize))\n ) {\n return invalidListTasksResponse();\n }\n\n return {\n ...value,\n tasks,\n nextPageToken: nextPageToken ?? \"\",\n pageSize: pageSize ?? 0,\n totalSize: totalSize ?? 0,\n };\n};\n\nconst invalidPushNotificationConfigResponse =\n (\n operation: \"pushNotificationConfigs:create\" | \"pushNotificationConfigs:get\",\n ) =>\n (): never => {\n throw new Error(\n `Invalid A2A ${operation} response: expected a valid push notification config payload.`,\n );\n };\n\nconst invalidListPushNotificationConfigsResponse = (): never => {\n throw new Error(\n \"Invalid A2A pushNotificationConfigs:list response: expected a valid push notification config list payload.\",\n );\n};\n\nconst parseOptionalString = (\n value: unknown,\n invalid: () => never,\n): string | undefined =>\n value == null ? undefined : typeof value === \"string\" ? value : invalid();\n\nconst parseTaskPushNotificationConfigResponse = (\n value: unknown,\n invalid: () => never,\n): A2ATaskPushNotificationConfig => {\n if (\n !isRecord(value) ||\n typeof value.url !== \"string\" ||\n value.url.length === 0\n ) {\n return invalid();\n }\n\n const { tenant, id, taskId, url, token, authentication, ...extra } = value;\n let normalizedAuthentication: A2ATaskPushNotificationConfig[\"authentication\"];\n if (authentication != null) {\n if (!isRecord(authentication)) return invalid();\n const { scheme, credentials, ...authenticationExtra } = authentication;\n normalizedAuthentication = {\n ...authenticationExtra,\n scheme: parseOptionalString(scheme, invalid) ?? \"\",\n ...(credentials == null\n ? {}\n : { credentials: parseOptionalString(credentials, invalid) }),\n };\n }\n\n return {\n ...extra,\n ...(tenant == null ? {} : { tenant: parseOptionalString(tenant, invalid) }),\n ...(id == null ? {} : { id: parseOptionalString(id, invalid) }),\n ...(taskId == null ? {} : { taskId: parseOptionalString(taskId, invalid) }),\n url,\n ...(token == null ? {} : { token: parseOptionalString(token, invalid) }),\n ...(normalizedAuthentication === undefined\n ? {}\n : { authentication: normalizedAuthentication }),\n };\n};\n\nconst parseListTaskPushNotificationConfigsResponse = (\n value: unknown,\n): A2AListTaskPushNotificationConfigsResponse => {\n if (!isRecord(value)) return invalidListPushNotificationConfigsResponse();\n\n const { configs: rawConfigs, nextPageToken, ...extra } = value;\n if (rawConfigs != null && !Array.isArray(rawConfigs)) {\n return invalidListPushNotificationConfigsResponse();\n }\n\n return {\n ...extra,\n configs: (rawConfigs ?? []).map((config) =>\n parseTaskPushNotificationConfigResponse(\n config,\n invalidListPushNotificationConfigsResponse,\n ),\n ),\n ...(nextPageToken == null\n ? {}\n : {\n nextPageToken: parseOptionalString(\n nextPageToken,\n invalidListPushNotificationConfigsResponse,\n ),\n }),\n };\n};\n\nfunction signalInit(signal?: AbortSignal): RequestInit {\n return signal ? { signal } : {};\n}\n\nconst SKIPPED_FRAME_SNIPPET_LENGTH = 120;\n\nfunction describeSkippedFrame(data: string, reason: string): string {\n const collapsed = data.replace(/\\s+/g, \" \");\n const snippet =\n collapsed.length > SKIPPED_FRAME_SNIPPET_LENGTH\n ? `${collapsed.slice(0, SKIPPED_FRAME_SNIPPET_LENGTH)}…`\n : collapsed;\n return `${reason} (frame: ${snippet})`;\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 if (json && typeof json === \"object\" && \"jsonrpc\" in json) {\n if (\"error\" in json && json.error) {\n throw toJsonRpcError(json.error);\n }\n if (\"result\" in json) {\n const result = normalizeKeys(json.result);\n if (isRecord(result) && typeof result.kind === \"string\") {\n const { kind: _kind, ...rest } = result;\n return rest as T;\n }\n return result as T;\n }\n }\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 return 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 const result = await this.fetchJSON<unknown>(\n `${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}${qs ? `?${qs}` : \"\"}`,\n signalInit(signal),\n );\n return parseTaskResponse(result, \"tasks:get\");\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 const result = await this.fetchJSON<unknown>(\n `${this.getBasePath()}/tasks${qs ? `?${qs}` : \"\"}`,\n signalInit(signal),\n );\n return parseListTasksResponse(result);\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 const result = await this.fetchJSON<unknown>(\n `${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}:cancel`,\n {\n method: \"POST\",\n body: JSON.stringify(body),\n ...signalInit(signal),\n },\n );\n return parseTaskResponse(result, \"tasks:cancel\");\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 const result = await this.fetchJSON<unknown>(\n `${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs`,\n {\n method: \"POST\",\n body: JSON.stringify(config),\n ...signalInit(signal),\n },\n );\n return parseTaskPushNotificationConfigResponse(\n result,\n invalidPushNotificationConfigResponse(\"pushNotificationConfigs:create\"),\n );\n }\n\n async getTaskPushNotificationConfig(\n taskId: string,\n configId: string,\n signal?: AbortSignal,\n ): Promise<A2ATaskPushNotificationConfig> {\n const result = await this.fetchJSON<unknown>(\n `${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs/${encodeURIComponent(configId)}`,\n signalInit(signal),\n );\n return parseTaskPushNotificationConfigResponse(\n result,\n invalidPushNotificationConfigResponse(\"pushNotificationConfigs:get\"),\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 const result = await this.fetchJSON<unknown>(\n `${this.getBasePath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs${qs ? `?${qs}` : \"\"}`,\n signalInit(signal),\n );\n return parseListTaskPushNotificationConfigsResponse(result);\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(\n response: Response,\n ): AsyncGenerator<A2AStreamEvent, string | undefined> {\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 void response.body?.cancel().catch(() => undefined);\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 let firstSkipReason: string | undefined;\n const noteSkip = (data: string, reason: string) => {\n firstSkipReason ??= describeSkippedFrame(data, reason);\n };\n\n const readEvent = (event: SSEEvent): A2AStreamEvent | null => {\n try {\n let parsed = JSON.parse(event.data);\n\n if (parsed && typeof parsed === \"object\" && \"jsonrpc\" in parsed) {\n if (\"error\" in parsed && parsed.error) {\n throw toJsonRpcError(parsed.error);\n }\n if (\"result\" in parsed) {\n parsed = parsed.result;\n }\n }\n\n const normalized = normalizeKeys(parsed) as Record<string, unknown>;\n const streamEvent = discriminateStreamResponse(normalized);\n if (!streamEvent) noteSkip(event.data, \"unrecognized event shape\");\n return streamEvent;\n } catch (error) {\n if (error instanceof A2AError) throw error;\n noteSkip(\n event.data,\n error instanceof Error ? error.message : String(error),\n );\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 return firstSkipReason;\n }\n}\n"],"mappings":"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"}
|
package/dist/useA2ARuntime.js
CHANGED
|
@@ -64,18 +64,23 @@ function useA2ARuntime(options) {
|
|
|
64
64
|
useEffect(() => {
|
|
65
65
|
core.updateOptions(coreOptions);
|
|
66
66
|
});
|
|
67
|
+
const threadSwitchGenerationRef = useRef(0);
|
|
67
68
|
const threadList = useMemo(() => {
|
|
68
69
|
if (!threadListAdapter) return void 0;
|
|
69
70
|
const { onSwitchToNewThread, onSwitchToThread } = threadListAdapter;
|
|
70
71
|
return {
|
|
71
72
|
threadId: threadListAdapter.threadId,
|
|
72
73
|
onSwitchToNewThread: onSwitchToNewThread ? async () => {
|
|
74
|
+
const generation = ++threadSwitchGenerationRef.current;
|
|
73
75
|
await onSwitchToNewThread();
|
|
76
|
+
if (generation !== threadSwitchGenerationRef.current) return;
|
|
74
77
|
core.applyExternalMessages([]);
|
|
75
78
|
core.resetContext();
|
|
76
79
|
} : void 0,
|
|
77
80
|
onSwitchToThread: onSwitchToThread ? async (threadId) => {
|
|
81
|
+
const generation = ++threadSwitchGenerationRef.current;
|
|
78
82
|
const result = await onSwitchToThread(threadId);
|
|
83
|
+
if (generation !== threadSwitchGenerationRef.current) return;
|
|
79
84
|
core.applyExternalMessages(result.messages);
|
|
80
85
|
core.resetContext();
|
|
81
86
|
} : void 0
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useA2ARuntime.js","names":[],"sources":["../src/useA2ARuntime.ts"],"sourcesContent":["\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useInsertionEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport {\n useExternalStoreRuntime,\n useExternalStoreSharedOptions,\n useRuntimeAdapters,\n} from \"@assistant-ui/core/react\";\nimport type {\n AssistantRuntime,\n AppendMessage,\n ExternalStoreAdapter,\n ThreadMessage,\n} from \"@assistant-ui/core\";\nimport { A2AClient, type A2AClientOptions } from \"./A2AClient\";\nimport { A2AThreadRuntimeCore } from \"./A2AThreadRuntimeCore\";\nimport { a2aExtras } from \"./a2aExtras\";\nimport type { UseA2ARuntimeOptions } from \"./types\";\n\ntype ManagedA2AClientOptions = Omit<A2AClientOptions, \"headers\">;\n\nconst serializeManagedClientOptions = (\n options: ManagedA2AClientOptions,\n): string => {\n const fetchOptions = Object.fromEntries(\n Object.entries(options.fetchOptions ?? {}).sort(([a], [b]) =>\n a.localeCompare(b),\n ),\n );\n\n return JSON.stringify({\n ...options,\n fetchOptions,\n });\n};\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 const headersRef = useRef(options.headers);\n useInsertionEffect(() => {\n headersRef.current = options.headers;\n });\n const resolveHeaders = useCallback(() => {\n const headers = headersRef.current;\n return typeof headers === \"function\" ? headers() : (headers ?? {});\n }, []);\n\n const managedClientOptionsKey = options.client\n ? null\n : options.baseUrl\n ? serializeManagedClientOptions({\n baseUrl: options.baseUrl,\n basePath: options.basePath,\n tenant: options.tenant,\n extensions: options.extensions,\n fetchOptions: options.fetchOptions,\n })\n : null;\n\n const client = useMemo(() => {\n if (options.client) return options.client;\n if (!managedClientOptionsKey) {\n throw new Error(\"useA2ARuntime requires either `client` or `baseUrl`\");\n }\n\n return new A2AClient({\n ...(JSON.parse(managedClientOptionsKey) as ManagedA2AClientOptions),\n headers: resolveHeaders,\n });\n }, [managedClientOptionsKey, options.client, resolveHeaders]);\n\n const coreOptions = {\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 const coreOptionsRef = useRef(coreOptions);\n coreOptionsRef.current = coreOptions;\n\n const core = useMemo(\n () =>\n new A2AThreadRuntimeCore({\n ...coreOptionsRef.current,\n client,\n notifyUpdate,\n }),\n [client, notifyUpdate],\n );\n\n useEffect(() => {\n core.updateOptions(coreOptions);\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 // Apply first so the abort inside resetContext finds an already\n // cleared repository and cannot persist the old thread's partial\n // assistant message.\n core.applyExternalMessages([]);\n core.resetContext();\n }\n : undefined,\n onSwitchToThread: onSwitchToThread\n ? async (threadId: string) => {\n const result = await onSwitchToThread(threadId);\n core.applyExternalMessages(result.messages);\n core.resetContext();\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 messageRepository: core.getMessageRepository(),\n isRunning: core.isRunning(),\n extras: a2aExtras.provide({\n task: core.getTask(),\n artifacts: core.getArtifacts(),\n agentCard: core.getAgentCard(),\n }),\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":";;;;;;;AA4BA,MAAM,iCACJ,YACW;CACX,MAAM,eAAe,OAAO,YAC1B,OAAO,QAAQ,QAAQ,gBAAgB,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OACrD,EAAE,cAAc,CAAC,CACnB,CACF;CAEA,OAAO,KAAK,UAAU;EACpB,GAAG;EACH;CACF,CAAC;AACH;AAEA,SAAgB,cAAc,SAAiD;CAC7E,MAAM,CAAC,UAAU,cAAc,SAAS,CAAC;CACzC,MAAM,eAAe,kBAAkB,YAAY,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;CACnE,MAAM,kBAAkB,mBAAmB;CAC3C,MAAM,iBAAiB,QAAQ,UAAU,WAAW,iBAAiB;CACrE,MAAM,oBAAoB,QAAQ,UAAU;CAE5C,MAAM,aAAa,OAAO,QAAQ,OAAO;CACzC,yBAAyB;EACvB,WAAW,UAAU,QAAQ;CAC/B,CAAC;CACD,MAAM,iBAAiB,kBAAkB;EACvC,MAAM,UAAU,WAAW;EAC3B,OAAO,OAAO,YAAY,aAAa,QAAQ,IAAK,WAAW,CAAC;CAClE,GAAG,CAAC,CAAC;CAEL,MAAM,0BAA0B,QAAQ,SACpC,OACA,QAAQ,UACN,8BAA8B;EAC5B,SAAS,QAAQ;EACjB,UAAU,QAAQ;EAClB,QAAQ,QAAQ;EAChB,YAAY,QAAQ;EACpB,cAAc,QAAQ;CACxB,CAAC,IACD;CAEN,MAAM,SAAS,cAAc;EAC3B,IAAI,QAAQ,QAAQ,OAAO,QAAQ;EACnC,IAAI,CAAC,yBACH,MAAM,IAAI,MAAM,qDAAqD;EAGvE,OAAO,IAAI,UAAU;GACnB,GAAI,KAAK,MAAM,uBAAuB;GACtC,SAAS;EACX,CAAC;CACH,GAAG;EAAC;EAAyB,QAAQ;EAAQ;CAAc,CAAC;CAE5D,MAAM,cAAc;EAClB;EACA,WAAW,QAAQ;EACnB,eAAe,QAAQ;EACvB,GAAI,QAAQ,WAAW,EAAE,SAAS,QAAQ,QAAQ;EAClD,GAAI,QAAQ,YAAY,EAAE,UAAU,QAAQ,SAAS;EACrD,GAAI,QAAQ,sBAAsB,EAChC,oBAAoB,QAAQ,mBAC9B;EACA,GAAI,kBAAkB,EAAE,SAAS,eAAe;CAClD;CACA,MAAM,iBAAiB,OAAO,WAAW;CACzC,eAAe,UAAU;CAEzB,MAAM,OAAO,cAET,IAAI,qBAAqB;EACvB,GAAG,eAAe;EAClB;EACA;CACF,CAAC,GACH,CAAC,QAAQ,YAAY,CACvB;CAEA,gBAAgB;EACd,KAAK,cAAc,WAAW;CAChC,CAAC;CAGD,MAAM,aAAa,cAAc;EAC/B,IAAI,CAAC,mBAAmB,OAAO,KAAA;EAE/B,MAAM,EAAE,qBAAqB,qBAAqB;EAElD,OAAO;GACL,UAAU,kBAAkB;GAC5B,qBAAqB,sBACjB,YAAY;IACV,MAAM,oBAAoB;
|
|
1
|
+
{"version":3,"file":"useA2ARuntime.js","names":[],"sources":["../src/useA2ARuntime.ts"],"sourcesContent":["\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useInsertionEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport {\n useExternalStoreRuntime,\n useExternalStoreSharedOptions,\n useRuntimeAdapters,\n} from \"@assistant-ui/core/react\";\nimport type {\n AssistantRuntime,\n AppendMessage,\n ExternalStoreAdapter,\n ThreadMessage,\n} from \"@assistant-ui/core\";\nimport { A2AClient, type A2AClientOptions } from \"./A2AClient\";\nimport { A2AThreadRuntimeCore } from \"./A2AThreadRuntimeCore\";\nimport { a2aExtras } from \"./a2aExtras\";\nimport type { UseA2ARuntimeOptions } from \"./types\";\n\ntype ManagedA2AClientOptions = Omit<A2AClientOptions, \"headers\">;\n\nconst serializeManagedClientOptions = (\n options: ManagedA2AClientOptions,\n): string => {\n const fetchOptions = Object.fromEntries(\n Object.entries(options.fetchOptions ?? {}).sort(([a], [b]) =>\n a.localeCompare(b),\n ),\n );\n\n return JSON.stringify({\n ...options,\n fetchOptions,\n });\n};\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 const headersRef = useRef(options.headers);\n useInsertionEffect(() => {\n headersRef.current = options.headers;\n });\n const resolveHeaders = useCallback(() => {\n const headers = headersRef.current;\n return typeof headers === \"function\" ? headers() : (headers ?? {});\n }, []);\n\n const managedClientOptionsKey = options.client\n ? null\n : options.baseUrl\n ? serializeManagedClientOptions({\n baseUrl: options.baseUrl,\n basePath: options.basePath,\n tenant: options.tenant,\n extensions: options.extensions,\n fetchOptions: options.fetchOptions,\n })\n : null;\n\n const client = useMemo(() => {\n if (options.client) return options.client;\n if (!managedClientOptionsKey) {\n throw new Error(\"useA2ARuntime requires either `client` or `baseUrl`\");\n }\n\n return new A2AClient({\n ...(JSON.parse(managedClientOptionsKey) as ManagedA2AClientOptions),\n headers: resolveHeaders,\n });\n }, [managedClientOptionsKey, options.client, resolveHeaders]);\n\n const coreOptions = {\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 const coreOptionsRef = useRef(coreOptions);\n coreOptionsRef.current = coreOptions;\n\n const core = useMemo(\n () =>\n new A2AThreadRuntimeCore({\n ...coreOptionsRef.current,\n client,\n notifyUpdate,\n }),\n [client, notifyUpdate],\n );\n\n useEffect(() => {\n core.updateOptions(coreOptions);\n });\n\n // Thread list\n const threadSwitchGenerationRef = useRef(0);\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 const generation = ++threadSwitchGenerationRef.current;\n await onSwitchToNewThread();\n if (generation !== threadSwitchGenerationRef.current) return;\n // Apply first so the abort inside resetContext finds an already\n // cleared repository and cannot persist the old thread's partial\n // assistant message.\n core.applyExternalMessages([]);\n core.resetContext();\n }\n : undefined,\n onSwitchToThread: onSwitchToThread\n ? async (threadId: string) => {\n const generation = ++threadSwitchGenerationRef.current;\n const result = await onSwitchToThread(threadId);\n if (generation !== threadSwitchGenerationRef.current) return;\n core.applyExternalMessages(result.messages);\n core.resetContext();\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 messageRepository: core.getMessageRepository(),\n isRunning: core.isRunning(),\n extras: a2aExtras.provide({\n task: core.getTask(),\n artifacts: core.getArtifacts(),\n agentCard: core.getAgentCard(),\n }),\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.34",
|
|
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.3.
|
|
33
|
-
"@assistant-ui/store": "^0.3.
|
|
34
|
-
"assistant-stream": "^0.3.
|
|
32
|
+
"@assistant-ui/core": "^0.3.18",
|
|
33
|
+
"@assistant-ui/store": "^0.3.13",
|
|
34
|
+
"assistant-stream": "^0.3.42"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"@types/react": "*",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
46
|
+
"@assistant-ui/x-buildutils": "0.0.26",
|
|
47
47
|
"@testing-library/dom": "^10.4.1",
|
|
48
48
|
"@testing-library/react": "^16.3.3",
|
|
49
49
|
"@types/react": "^19.2.18",
|
package/src/A2AClient.test.ts
CHANGED
|
@@ -479,6 +479,83 @@ describe("A2AClient", () => {
|
|
|
479
479
|
);
|
|
480
480
|
});
|
|
481
481
|
|
|
482
|
+
it.each([
|
|
483
|
+
["text", { invalid: true }],
|
|
484
|
+
["raw", 42],
|
|
485
|
+
["url", false],
|
|
486
|
+
["filename", []],
|
|
487
|
+
["mediaType", {}],
|
|
488
|
+
["metadata", []],
|
|
489
|
+
])("rejects a message part with malformed %s", async (field, value) => {
|
|
490
|
+
fetchMock.mockResolvedValue(
|
|
491
|
+
mockFetchResponse({
|
|
492
|
+
message: {
|
|
493
|
+
messageId: "m2",
|
|
494
|
+
role: "agent",
|
|
495
|
+
parts: [{ [field]: value }],
|
|
496
|
+
},
|
|
497
|
+
}),
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
await expect(client.sendMessage(userMessage)).rejects.toThrow(
|
|
501
|
+
"Invalid A2A message:send response: expected a valid task or message payload.",
|
|
502
|
+
);
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
it("normalizes null optional message-part fields", async () => {
|
|
506
|
+
fetchMock.mockResolvedValue(
|
|
507
|
+
mockFetchResponse({
|
|
508
|
+
message: {
|
|
509
|
+
messageId: "m2",
|
|
510
|
+
role: "agent",
|
|
511
|
+
parts: [
|
|
512
|
+
{
|
|
513
|
+
text: null,
|
|
514
|
+
raw: null,
|
|
515
|
+
url: null,
|
|
516
|
+
data: null,
|
|
517
|
+
metadata: null,
|
|
518
|
+
filename: null,
|
|
519
|
+
mediaType: null,
|
|
520
|
+
},
|
|
521
|
+
],
|
|
522
|
+
},
|
|
523
|
+
}),
|
|
524
|
+
);
|
|
525
|
+
|
|
526
|
+
const result = await client.sendMessage(userMessage);
|
|
527
|
+
|
|
528
|
+
expect((result as A2AMessage).parts).toEqual([{ data: null }]);
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
it.each([
|
|
532
|
+
[
|
|
533
|
+
"status message",
|
|
534
|
+
{
|
|
535
|
+
status: {
|
|
536
|
+
state: "completed",
|
|
537
|
+
message: { parts: [{ text: { invalid: true } }] },
|
|
538
|
+
},
|
|
539
|
+
},
|
|
540
|
+
],
|
|
541
|
+
["history message", { history: [{ parts: [{ raw: 42 }] }] }],
|
|
542
|
+
["artifact", { artifacts: [{ parts: [{ metadata: [] }] }] }],
|
|
543
|
+
])("rejects a task with malformed %s parts", async (_name, nested) => {
|
|
544
|
+
fetchMock.mockResolvedValue(
|
|
545
|
+
mockFetchResponse({
|
|
546
|
+
task: {
|
|
547
|
+
id: "t1",
|
|
548
|
+
status: { state: "completed" },
|
|
549
|
+
...nested,
|
|
550
|
+
},
|
|
551
|
+
}),
|
|
552
|
+
);
|
|
553
|
+
|
|
554
|
+
await expect(client.sendMessage(userMessage)).rejects.toThrow(
|
|
555
|
+
"Invalid A2A message:send response: expected a valid task or message payload.",
|
|
556
|
+
);
|
|
557
|
+
});
|
|
558
|
+
|
|
482
559
|
it.each([
|
|
483
560
|
["task", { id: "t1", status: { state: "completed" } }],
|
|
484
561
|
[
|
|
@@ -1072,6 +1149,60 @@ describe("A2AClient", () => {
|
|
|
1072
1149
|
}
|
|
1073
1150
|
});
|
|
1074
1151
|
|
|
1152
|
+
it("drops wrapped messages and artifact updates with malformed parts", async () => {
|
|
1153
|
+
const frames = [
|
|
1154
|
+
{
|
|
1155
|
+
message: {
|
|
1156
|
+
message_id: "m1",
|
|
1157
|
+
role: "ROLE_AGENT",
|
|
1158
|
+
parts: [{ text: { invalid: true } }],
|
|
1159
|
+
},
|
|
1160
|
+
},
|
|
1161
|
+
{
|
|
1162
|
+
artifact_update: {
|
|
1163
|
+
task_id: "t1",
|
|
1164
|
+
context_id: "c1",
|
|
1165
|
+
artifact: {
|
|
1166
|
+
artifact_id: "a1",
|
|
1167
|
+
parts: [{ url: 42 }],
|
|
1168
|
+
},
|
|
1169
|
+
},
|
|
1170
|
+
},
|
|
1171
|
+
{
|
|
1172
|
+
status_update: {
|
|
1173
|
+
task_id: "t1",
|
|
1174
|
+
context_id: "c1",
|
|
1175
|
+
status: {
|
|
1176
|
+
state: "TASK_STATE_WORKING",
|
|
1177
|
+
message: { parts: [{ text: { invalid: true } }] },
|
|
1178
|
+
},
|
|
1179
|
+
},
|
|
1180
|
+
},
|
|
1181
|
+
{
|
|
1182
|
+
task: {
|
|
1183
|
+
id: "t1",
|
|
1184
|
+
status: {
|
|
1185
|
+
state: "TASK_STATE_WORKING",
|
|
1186
|
+
message: { parts: [{ text: { invalid: true } }] },
|
|
1187
|
+
},
|
|
1188
|
+
},
|
|
1189
|
+
},
|
|
1190
|
+
];
|
|
1191
|
+
|
|
1192
|
+
for (const frame of frames) {
|
|
1193
|
+
fetchMock.mockResolvedValue(
|
|
1194
|
+
mockSSEResponse([`data: ${JSON.stringify(frame)}`, "", ""]),
|
|
1195
|
+
);
|
|
1196
|
+
|
|
1197
|
+
const events: A2AStreamEvent[] = [];
|
|
1198
|
+
for await (const event of client.streamMessage(userMessage)) {
|
|
1199
|
+
events.push(event);
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
expect(events).toEqual([]);
|
|
1203
|
+
}
|
|
1204
|
+
});
|
|
1205
|
+
|
|
1075
1206
|
it("drops a wrapped status update whose contextId is not a string", async () => {
|
|
1076
1207
|
const sseData = JSON.stringify({
|
|
1077
1208
|
status_update: {
|
package/src/A2AClient.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
A2AListTasksRequest,
|
|
8
8
|
A2AListTasksResponse,
|
|
9
9
|
A2AMessage,
|
|
10
|
+
A2APart,
|
|
10
11
|
A2ARole,
|
|
11
12
|
A2ASendMessageConfiguration,
|
|
12
13
|
A2AStreamEvent,
|
|
@@ -69,6 +70,26 @@ const JSONRPC_STATE_MAP: Record<string, string> = {
|
|
|
69
70
|
unknown: "unspecified",
|
|
70
71
|
};
|
|
71
72
|
|
|
73
|
+
const PART_STRING_FIELDS = [
|
|
74
|
+
"text",
|
|
75
|
+
"raw",
|
|
76
|
+
"url",
|
|
77
|
+
"filename",
|
|
78
|
+
"mediaType",
|
|
79
|
+
] as const;
|
|
80
|
+
|
|
81
|
+
const NULLABLE_PART_FIELDS = [...PART_STRING_FIELDS, "metadata"] as const;
|
|
82
|
+
|
|
83
|
+
function normalizePartNulls(
|
|
84
|
+
part: Record<string, unknown>,
|
|
85
|
+
): Record<string, unknown> {
|
|
86
|
+
const normalized = { ...part };
|
|
87
|
+
for (const field of NULLABLE_PART_FIELDS) {
|
|
88
|
+
if (normalized[field] === null) delete normalized[field];
|
|
89
|
+
}
|
|
90
|
+
return normalized;
|
|
91
|
+
}
|
|
92
|
+
|
|
72
93
|
// JSON-RPC file parts nest the payload under `file`; the internal A2APart is
|
|
73
94
|
// flat, so the nested fields map onto url/raw/mediaType/filename.
|
|
74
95
|
function normalizeParts(value: unknown[]): unknown[] {
|
|
@@ -76,8 +97,8 @@ function normalizeParts(value: unknown[]): unknown[] {
|
|
|
76
97
|
const part = normalizeKeys(raw, false);
|
|
77
98
|
if (part === null || typeof part !== "object" || Array.isArray(part))
|
|
78
99
|
return part;
|
|
79
|
-
const record = part as Record<string, unknown
|
|
80
|
-
if (record.kind === undefined) return
|
|
100
|
+
const record = normalizePartNulls(part as Record<string, unknown>);
|
|
101
|
+
if (record.kind === undefined) return record;
|
|
81
102
|
const { kind, ...rest } = record;
|
|
82
103
|
const file = rest.file;
|
|
83
104
|
if (kind !== "file" || file === null || typeof file !== "object")
|
|
@@ -86,10 +107,10 @@ function normalizeParts(value: unknown[]): unknown[] {
|
|
|
86
107
|
const nested = file as Record<string, unknown>;
|
|
87
108
|
return {
|
|
88
109
|
...others,
|
|
89
|
-
...(nested.uri
|
|
90
|
-
...(nested.bytes
|
|
91
|
-
...(nested.mimeType
|
|
92
|
-
...(nested.name
|
|
110
|
+
...(nested.uri != null ? { url: nested.uri } : {}),
|
|
111
|
+
...(nested.bytes != null ? { raw: nested.bytes } : {}),
|
|
112
|
+
...(nested.mimeType != null ? { mediaType: nested.mimeType } : {}),
|
|
113
|
+
...(nested.name != null ? { filename: nested.name } : {}),
|
|
93
114
|
};
|
|
94
115
|
});
|
|
95
116
|
}
|
|
@@ -272,13 +293,35 @@ const hasOptionalStringIds = (
|
|
|
272
293
|
): boolean =>
|
|
273
294
|
keys.every((key) => value[key] == null || typeof value[key] === "string");
|
|
274
295
|
|
|
296
|
+
const hasOptionalStringFields = (
|
|
297
|
+
value: Record<string, unknown>,
|
|
298
|
+
keys: readonly string[],
|
|
299
|
+
): boolean =>
|
|
300
|
+
keys.every(
|
|
301
|
+
(key) => value[key] === undefined || typeof value[key] === "string",
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
const isPart = (value: unknown): value is A2APart =>
|
|
305
|
+
isRecord(value) &&
|
|
306
|
+
hasOptionalStringFields(value, PART_STRING_FIELDS) &&
|
|
307
|
+
(value.metadata === undefined || isRecord(value.metadata));
|
|
308
|
+
|
|
309
|
+
const hasValidNestedParts = (value: unknown): boolean =>
|
|
310
|
+
!isRecord(value) || !Array.isArray(value.parts) || value.parts.every(isPart);
|
|
311
|
+
|
|
312
|
+
const hasValidNestedPartsInCollection = (value: unknown): boolean =>
|
|
313
|
+
!Array.isArray(value) || value.every(hasValidNestedParts);
|
|
314
|
+
|
|
275
315
|
const isTask = (value: unknown): value is A2ATask =>
|
|
276
316
|
isRecord(value) &&
|
|
277
317
|
typeof value.id === "string" &&
|
|
278
318
|
value.id.length > 0 &&
|
|
279
319
|
hasOptionalStringIds(value, ["contextId"]) &&
|
|
280
320
|
isRecord(value.status) &&
|
|
281
|
-
isTaskState(value.status.state)
|
|
321
|
+
isTaskState(value.status.state) &&
|
|
322
|
+
hasValidNestedParts(value.status.message) &&
|
|
323
|
+
hasValidNestedPartsInCollection(value.artifacts) &&
|
|
324
|
+
hasValidNestedPartsInCollection(value.history);
|
|
282
325
|
|
|
283
326
|
const isMessage = (value: unknown): value is A2AMessage =>
|
|
284
327
|
isRecord(value) &&
|
|
@@ -287,7 +330,7 @@ const isMessage = (value: unknown): value is A2AMessage =>
|
|
|
287
330
|
hasOptionalStringIds(value, ["contextId", "taskId"]) &&
|
|
288
331
|
isRole(value.role) &&
|
|
289
332
|
Array.isArray(value.parts) &&
|
|
290
|
-
value.parts.every(
|
|
333
|
+
value.parts.every(isPart);
|
|
291
334
|
|
|
292
335
|
// Legacy wrappers use ProtoJSON, where omitted and null fields decode to proto
|
|
293
336
|
// defaults. Normalize those defaults before enforcing semantic requirements.
|
|
@@ -346,7 +389,8 @@ const isStatusUpdate = (
|
|
|
346
389
|
(allowEmptyTaskId || value.taskId.length > 0) &&
|
|
347
390
|
hasOptionalStringIds(value, ["contextId"]) &&
|
|
348
391
|
isRecord(value.status) &&
|
|
349
|
-
isTaskState(value.status.state)
|
|
392
|
+
isTaskState(value.status.state) &&
|
|
393
|
+
hasValidNestedParts(value.status.message);
|
|
350
394
|
|
|
351
395
|
const toWrappedStatusUpdate = (
|
|
352
396
|
value: unknown,
|
|
@@ -369,7 +413,7 @@ const isArtifact = (value: unknown): value is Record<string, unknown> =>
|
|
|
369
413
|
isRecord(value) &&
|
|
370
414
|
typeof value.artifactId === "string" &&
|
|
371
415
|
Array.isArray(value.parts) &&
|
|
372
|
-
value.parts.every(
|
|
416
|
+
value.parts.every(isPart);
|
|
373
417
|
|
|
374
418
|
const isArtifactUpdate = (value: unknown): value is Record<string, unknown> =>
|
|
375
419
|
isRecord(value) &&
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
// @vitest-environment jsdom
|
|
2
2
|
|
|
3
3
|
import { act, renderHook, waitFor } from "@testing-library/react";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
startTransition,
|
|
6
|
+
Suspense,
|
|
7
|
+
useState,
|
|
8
|
+
type PropsWithChildren,
|
|
9
|
+
} from "react";
|
|
5
10
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
11
|
+
import type { ThreadMessage } from "@assistant-ui/core";
|
|
6
12
|
import type { A2AClient } from "./A2AClient";
|
|
7
13
|
import type { A2AStreamEvent } from "./types";
|
|
8
14
|
import { useA2ARuntime } from "./useA2ARuntime";
|
|
@@ -84,6 +90,15 @@ const createFetchMock = () =>
|
|
|
84
90
|
);
|
|
85
91
|
});
|
|
86
92
|
|
|
93
|
+
const createThreadMessage = (id: string): ThreadMessage => ({
|
|
94
|
+
id,
|
|
95
|
+
role: "user",
|
|
96
|
+
content: [{ type: "text", text: id }],
|
|
97
|
+
attachments: [],
|
|
98
|
+
createdAt: new Date(0),
|
|
99
|
+
metadata: { custom: {} },
|
|
100
|
+
});
|
|
101
|
+
|
|
87
102
|
afterEach(() => {
|
|
88
103
|
vi.unstubAllGlobals();
|
|
89
104
|
});
|
|
@@ -277,4 +292,92 @@ describe("useA2ARuntime", () => {
|
|
|
277
292
|
Authorization: "Bearer workspace-a",
|
|
278
293
|
});
|
|
279
294
|
});
|
|
295
|
+
|
|
296
|
+
it("ignores an older thread load after a newer selection", async () => {
|
|
297
|
+
const { client } = createMockClient();
|
|
298
|
+
let resolveFirst!: (value: { messages: ThreadMessage[] }) => void;
|
|
299
|
+
let resolveSecond!: (value: { messages: ThreadMessage[] }) => void;
|
|
300
|
+
const first = new Promise<{ messages: ThreadMessage[] }>((resolve) => {
|
|
301
|
+
resolveFirst = resolve;
|
|
302
|
+
});
|
|
303
|
+
const second = new Promise<{ messages: ThreadMessage[] }>((resolve) => {
|
|
304
|
+
resolveSecond = resolve;
|
|
305
|
+
});
|
|
306
|
+
const { result } = renderHook(() => {
|
|
307
|
+
const [threadId, setThreadId] = useState("initial");
|
|
308
|
+
return useA2ARuntime({
|
|
309
|
+
client,
|
|
310
|
+
adapters: {
|
|
311
|
+
threadList: {
|
|
312
|
+
threadId,
|
|
313
|
+
onSwitchToThread: async (nextThreadId) => {
|
|
314
|
+
setThreadId(nextThreadId);
|
|
315
|
+
return nextThreadId === "thread-a" ? first : second;
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
let switchA!: Promise<void>;
|
|
323
|
+
let switchB!: Promise<void>;
|
|
324
|
+
act(() => {
|
|
325
|
+
switchA = result.current.threads.switchToThread("thread-a");
|
|
326
|
+
switchB = result.current.threads.switchToThread("thread-b");
|
|
327
|
+
});
|
|
328
|
+
expect(result.current.threads.getState().mainThreadId).toBe("thread-b");
|
|
329
|
+
|
|
330
|
+
await act(async () => {
|
|
331
|
+
resolveSecond({ messages: [createThreadMessage("thread-b")] });
|
|
332
|
+
await switchB;
|
|
333
|
+
});
|
|
334
|
+
await act(async () => {
|
|
335
|
+
resolveFirst({ messages: [createThreadMessage("thread-a")] });
|
|
336
|
+
await switchA;
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
expect(result.current.thread.getState().messages.map((m) => m.id)).toEqual([
|
|
340
|
+
"thread-b",
|
|
341
|
+
]);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it("ignores a thread load superseded by a new thread", async () => {
|
|
345
|
+
const { client } = createMockClient();
|
|
346
|
+
let resolveLoad!: (value: { messages: ThreadMessage[] }) => void;
|
|
347
|
+
const load = new Promise<{ messages: ThreadMessage[] }>((resolve) => {
|
|
348
|
+
resolveLoad = resolve;
|
|
349
|
+
});
|
|
350
|
+
const { result } = renderHook(() => {
|
|
351
|
+
const [threadId, setThreadId] = useState("initial");
|
|
352
|
+
return useA2ARuntime({
|
|
353
|
+
client,
|
|
354
|
+
adapters: {
|
|
355
|
+
threadList: {
|
|
356
|
+
threadId,
|
|
357
|
+
onSwitchToThread: async (nextThreadId) => {
|
|
358
|
+
setThreadId(nextThreadId);
|
|
359
|
+
return load;
|
|
360
|
+
},
|
|
361
|
+
onSwitchToNewThread: async () => {
|
|
362
|
+
setThreadId("thread-new");
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
let staleSwitch!: Promise<void>;
|
|
370
|
+
act(() => {
|
|
371
|
+
staleSwitch = result.current.threads.switchToThread("thread-a");
|
|
372
|
+
});
|
|
373
|
+
await act(async () => {
|
|
374
|
+
await result.current.threads.switchToNewThread();
|
|
375
|
+
});
|
|
376
|
+
await act(async () => {
|
|
377
|
+
resolveLoad({ messages: [createThreadMessage("thread-a")] });
|
|
378
|
+
await staleSwitch;
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
expect(result.current.thread.getState().messages).toEqual([]);
|
|
382
|
+
});
|
|
280
383
|
});
|
package/src/useA2ARuntime.ts
CHANGED
|
@@ -110,6 +110,7 @@ export function useA2ARuntime(options: UseA2ARuntimeOptions): AssistantRuntime {
|
|
|
110
110
|
});
|
|
111
111
|
|
|
112
112
|
// Thread list
|
|
113
|
+
const threadSwitchGenerationRef = useRef(0);
|
|
113
114
|
const threadList = useMemo(() => {
|
|
114
115
|
if (!threadListAdapter) return undefined;
|
|
115
116
|
|
|
@@ -119,7 +120,9 @@ export function useA2ARuntime(options: UseA2ARuntimeOptions): AssistantRuntime {
|
|
|
119
120
|
threadId: threadListAdapter.threadId,
|
|
120
121
|
onSwitchToNewThread: onSwitchToNewThread
|
|
121
122
|
? async () => {
|
|
123
|
+
const generation = ++threadSwitchGenerationRef.current;
|
|
122
124
|
await onSwitchToNewThread();
|
|
125
|
+
if (generation !== threadSwitchGenerationRef.current) return;
|
|
123
126
|
// Apply first so the abort inside resetContext finds an already
|
|
124
127
|
// cleared repository and cannot persist the old thread's partial
|
|
125
128
|
// assistant message.
|
|
@@ -129,7 +132,9 @@ export function useA2ARuntime(options: UseA2ARuntimeOptions): AssistantRuntime {
|
|
|
129
132
|
: undefined,
|
|
130
133
|
onSwitchToThread: onSwitchToThread
|
|
131
134
|
? async (threadId: string) => {
|
|
135
|
+
const generation = ++threadSwitchGenerationRef.current;
|
|
132
136
|
const result = await onSwitchToThread(threadId);
|
|
137
|
+
if (generation !== threadSwitchGenerationRef.current) return;
|
|
133
138
|
core.applyExternalMessages(result.messages);
|
|
134
139
|
core.resetContext();
|
|
135
140
|
}
|