@assistant-ui/react-a2a 0.1.0

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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/dist/A2AMessageAccumulator.d.ts +16 -0
  4. package/dist/A2AMessageAccumulator.d.ts.map +1 -0
  5. package/dist/A2AMessageAccumulator.js +35 -0
  6. package/dist/A2AMessageAccumulator.js.map +1 -0
  7. package/dist/appendA2AChunk.d.ts +3 -0
  8. package/dist/appendA2AChunk.d.ts.map +1 -0
  9. package/dist/appendA2AChunk.js +90 -0
  10. package/dist/appendA2AChunk.js.map +1 -0
  11. package/dist/convertA2AMessages.d.ts +64 -0
  12. package/dist/convertA2AMessages.d.ts.map +1 -0
  13. package/dist/convertA2AMessages.js +93 -0
  14. package/dist/convertA2AMessages.js.map +1 -0
  15. package/dist/index.d.ts +7 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +8 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/testUtils.d.ts +4 -0
  20. package/dist/testUtils.d.ts.map +1 -0
  21. package/dist/testUtils.js +10 -0
  22. package/dist/testUtils.js.map +1 -0
  23. package/dist/types.d.ts +94 -0
  24. package/dist/types.d.ts.map +1 -0
  25. package/dist/types.js +14 -0
  26. package/dist/types.js.map +1 -0
  27. package/dist/useA2AMessages.d.ts +25 -0
  28. package/dist/useA2AMessages.d.ts.map +1 -0
  29. package/dist/useA2AMessages.js +134 -0
  30. package/dist/useA2AMessages.js.map +1 -0
  31. package/dist/useA2ARuntime.d.ts +55 -0
  32. package/dist/useA2ARuntime.d.ts.map +1 -0
  33. package/dist/useA2ARuntime.js +215 -0
  34. package/dist/useA2ARuntime.js.map +1 -0
  35. package/package.json +68 -0
  36. package/src/A2AMessageAccumulator.ts +48 -0
  37. package/src/appendA2AChunk.ts +121 -0
  38. package/src/convertA2AMessages.ts +108 -0
  39. package/src/index.ts +6 -0
  40. package/src/testUtils.ts +11 -0
  41. package/src/types.ts +114 -0
  42. package/src/useA2AMessages.ts +180 -0
  43. package/src/useA2ARuntime.ts +331 -0
@@ -0,0 +1,134 @@
1
+ // src/useA2AMessages.ts
2
+ import { useState, useCallback, useRef, useMemo } from "react";
3
+ import { v4 as uuidv4 } from "uuid";
4
+ import { A2AMessageAccumulator } from "./A2AMessageAccumulator.js";
5
+ import {
6
+ A2AKnownEventTypes
7
+ } from "./types.js";
8
+ var DEFAULT_APPEND_MESSAGE = (_, curr) => curr;
9
+ var useA2AMessages = ({
10
+ stream,
11
+ appendMessage = DEFAULT_APPEND_MESSAGE,
12
+ eventHandlers
13
+ }) => {
14
+ const [messages, setMessages] = useState([]);
15
+ const [taskState, setTaskState] = useState();
16
+ const [artifacts, setArtifacts] = useState([]);
17
+ const abortControllerRef = useRef(null);
18
+ const { onTaskUpdate, onArtifacts, onError, onStateUpdate, onCustomEvent } = useMemo(() => eventHandlers ?? {}, [eventHandlers]);
19
+ const sendMessage = useCallback(
20
+ async (newMessages, config) => {
21
+ const newMessagesWithId = newMessages.map(
22
+ (m) => m.id ? m : { ...m, id: uuidv4() }
23
+ );
24
+ const accumulator = new A2AMessageAccumulator({
25
+ initialMessages: messages,
26
+ appendMessage
27
+ });
28
+ setMessages(accumulator.addMessages(newMessagesWithId));
29
+ const abortController = new AbortController();
30
+ abortControllerRef.current = abortController;
31
+ const response = await stream(newMessagesWithId, {
32
+ ...config,
33
+ abortSignal: abortController.signal
34
+ });
35
+ for await (const event of response) {
36
+ switch (event.event) {
37
+ case A2AKnownEventTypes.TaskUpdate:
38
+ const taskData = event.data;
39
+ setTaskState(taskData);
40
+ onTaskUpdate?.(taskData);
41
+ break;
42
+ case A2AKnownEventTypes.TaskComplete:
43
+ const { messages: taskMessages, artifacts: taskArtifacts } = event.data;
44
+ if (taskMessages) {
45
+ setMessages(accumulator.addMessages(taskMessages));
46
+ }
47
+ if (taskArtifacts) {
48
+ setArtifacts(taskArtifacts);
49
+ onArtifacts?.(taskArtifacts);
50
+ }
51
+ setTaskState(void 0);
52
+ break;
53
+ case A2AKnownEventTypes.TaskFailed:
54
+ onError?.(event.data);
55
+ if (taskState) {
56
+ setTaskState({
57
+ ...taskState,
58
+ state: "failed",
59
+ message: event.data?.message
60
+ });
61
+ }
62
+ break;
63
+ case A2AKnownEventTypes.Artifacts:
64
+ const artifactData = event.data;
65
+ setArtifacts(artifactData);
66
+ onArtifacts?.(artifactData);
67
+ break;
68
+ case A2AKnownEventTypes.StateUpdate:
69
+ onStateUpdate?.(event.data);
70
+ break;
71
+ case A2AKnownEventTypes.Error:
72
+ onError?.(event.data);
73
+ const messages2 = accumulator.getMessages();
74
+ const lastAssistantMessage = messages2.findLast(
75
+ (m) => m != null && "role" in m && m.role === "assistant" && m.id != null
76
+ );
77
+ if (lastAssistantMessage) {
78
+ const errorMessage = {
79
+ ...lastAssistantMessage,
80
+ status: {
81
+ type: "incomplete",
82
+ reason: "error",
83
+ error: event.data
84
+ }
85
+ };
86
+ setMessages(accumulator.addMessages([errorMessage]));
87
+ }
88
+ break;
89
+ default:
90
+ if (onCustomEvent) {
91
+ onCustomEvent(event.event, event.data);
92
+ } else {
93
+ console.warn(
94
+ "Unhandled A2A event received:",
95
+ event.event,
96
+ event.data
97
+ );
98
+ }
99
+ break;
100
+ }
101
+ }
102
+ },
103
+ [
104
+ messages,
105
+ appendMessage,
106
+ stream,
107
+ onTaskUpdate,
108
+ onArtifacts,
109
+ onError,
110
+ onStateUpdate,
111
+ onCustomEvent,
112
+ taskState
113
+ ]
114
+ );
115
+ const cancel = useCallback(() => {
116
+ if (abortControllerRef.current) {
117
+ abortControllerRef.current.abort();
118
+ }
119
+ }, [abortControllerRef]);
120
+ return {
121
+ messages,
122
+ artifacts,
123
+ taskState,
124
+ sendMessage,
125
+ cancel,
126
+ setMessages,
127
+ setArtifacts,
128
+ setTaskState
129
+ };
130
+ };
131
+ export {
132
+ useA2AMessages
133
+ };
134
+ //# sourceMappingURL=useA2AMessages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/useA2AMessages.ts"],"sourcesContent":["import { useState, useCallback, useRef, useMemo } from \"react\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport { A2AMessageAccumulator } from \"./A2AMessageAccumulator\";\nimport {\n A2AEvent,\n A2AKnownEventTypes,\n A2ATaskState,\n A2AArtifact,\n A2ASendMessageConfig,\n A2AStreamCallback,\n OnTaskUpdateEventCallback,\n OnArtifactsEventCallback,\n OnErrorEventCallback,\n OnStateUpdateEventCallback,\n OnCustomEventCallback,\n} from \"./types\";\n\nexport type A2AMessagesEvent<_TMessage> = A2AEvent;\n\nconst DEFAULT_APPEND_MESSAGE = <TMessage>(\n _: TMessage | undefined,\n curr: TMessage,\n) => curr;\n\nexport const useA2AMessages = <TMessage extends { id?: string }>({\n stream,\n appendMessage = DEFAULT_APPEND_MESSAGE,\n eventHandlers,\n}: {\n stream: A2AStreamCallback<TMessage>;\n appendMessage?: (prev: TMessage | undefined, curr: TMessage) => TMessage;\n eventHandlers?: {\n onTaskUpdate?: OnTaskUpdateEventCallback;\n onArtifacts?: OnArtifactsEventCallback;\n onError?: OnErrorEventCallback;\n onStateUpdate?: OnStateUpdateEventCallback;\n onCustomEvent?: OnCustomEventCallback;\n };\n}) => {\n const [messages, setMessages] = useState<TMessage[]>([]);\n const [taskState, setTaskState] = useState<A2ATaskState | undefined>();\n const [artifacts, setArtifacts] = useState<A2AArtifact[]>([]);\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const { onTaskUpdate, onArtifacts, onError, onStateUpdate, onCustomEvent } =\n useMemo(() => eventHandlers ?? {}, [eventHandlers]);\n\n const sendMessage = useCallback(\n async (newMessages: TMessage[], config: A2ASendMessageConfig) => {\n // ensure all messages have an ID\n const newMessagesWithId = newMessages.map((m) =>\n m.id ? m : { ...m, id: uuidv4() },\n );\n\n const accumulator = new A2AMessageAccumulator({\n initialMessages: messages,\n appendMessage,\n });\n setMessages(accumulator.addMessages(newMessagesWithId));\n\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n const response = await stream(newMessagesWithId, {\n ...config,\n abortSignal: abortController.signal,\n });\n\n for await (const event of response) {\n switch (event.event) {\n case A2AKnownEventTypes.TaskUpdate:\n const taskData = event.data as A2ATaskState;\n setTaskState(taskData);\n onTaskUpdate?.(taskData);\n break;\n\n case A2AKnownEventTypes.TaskComplete:\n // Extract messages and artifacts from completed task\n const { messages: taskMessages, artifacts: taskArtifacts } =\n event.data;\n if (taskMessages) {\n setMessages(accumulator.addMessages(taskMessages));\n }\n if (taskArtifacts) {\n setArtifacts(taskArtifacts);\n onArtifacts?.(taskArtifacts);\n }\n // Clear task state on completion\n setTaskState(undefined);\n break;\n\n case A2AKnownEventTypes.TaskFailed:\n onError?.(event.data);\n // Update task state to failed\n if (taskState) {\n setTaskState({\n ...taskState,\n state: \"failed\",\n message: event.data?.message,\n });\n }\n break;\n\n case A2AKnownEventTypes.Artifacts:\n const artifactData = event.data as A2AArtifact[];\n setArtifacts(artifactData);\n onArtifacts?.(artifactData);\n break;\n\n case A2AKnownEventTypes.StateUpdate:\n onStateUpdate?.(event.data);\n break;\n\n case A2AKnownEventTypes.Error:\n onError?.(event.data);\n // Update the last assistant message with error status if available\n const messages = accumulator.getMessages();\n const lastAssistantMessage = messages.findLast(\n (m): m is TMessage & { role: string; id: string } =>\n m != null &&\n \"role\" in m &&\n m.role === \"assistant\" &&\n m.id != null,\n );\n if (lastAssistantMessage) {\n const errorMessage = {\n ...lastAssistantMessage,\n status: {\n type: \"incomplete\" as const,\n reason: \"error\" as const,\n error: event.data,\n },\n };\n setMessages(accumulator.addMessages([errorMessage]));\n }\n break;\n\n default:\n if (onCustomEvent) {\n onCustomEvent(event.event, event.data);\n } else {\n console.warn(\n \"Unhandled A2A event received:\",\n event.event,\n event.data,\n );\n }\n break;\n }\n }\n },\n [\n messages,\n appendMessage,\n stream,\n onTaskUpdate,\n onArtifacts,\n onError,\n onStateUpdate,\n onCustomEvent,\n taskState,\n ],\n );\n\n const cancel = useCallback(() => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n }, [abortControllerRef]);\n\n return {\n messages,\n artifacts,\n taskState,\n sendMessage,\n cancel,\n setMessages,\n setArtifacts,\n setTaskState,\n };\n};\n"],"mappings":"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","names":["messages"]}
@@ -0,0 +1,55 @@
1
+ import { A2AMessage, A2AArtifact, A2ATaskState, A2ASendMessageConfig, A2AStreamCallback, OnTaskUpdateEventCallback, OnArtifactsEventCallback, OnErrorEventCallback, OnStateUpdateEventCallback, OnCustomEventCallback } from "./types";
2
+ import { AttachmentAdapter } from "@assistant-ui/react";
3
+ import { FeedbackAdapter } from "@assistant-ui/react";
4
+ import { SpeechSynthesisAdapter } from "@assistant-ui/react";
5
+ export declare const useA2ATaskState: () => A2ATaskState | undefined;
6
+ export declare const useA2AArtifacts: () => A2AArtifact[];
7
+ export declare const useA2ASend: () => (messages: A2AMessage[], config: A2ASendMessageConfig) => Promise<void>;
8
+ export declare const useA2ARuntime: ({ autoCancelPendingToolCalls, adapters: { attachments, feedback, speech }, unstable_allowCancellation, stream, contextId, onSwitchToNewThread, onSwitchToThread, eventHandlers, }: {
9
+ /**
10
+ * @deprecated For thread management use `useCloudThreadListRuntime` instead. This option will be removed in a future version.
11
+ */
12
+ contextId?: string | undefined;
13
+ autoCancelPendingToolCalls?: boolean | undefined;
14
+ unstable_allowCancellation?: boolean | undefined;
15
+ stream: A2AStreamCallback<A2AMessage>;
16
+ /**
17
+ * @deprecated For thread management use `useCloudThreadListRuntime` instead. This option will be removed in a future version.
18
+ */
19
+ onSwitchToNewThread?: () => Promise<void> | void;
20
+ onSwitchToThread?: (contextId: string) => Promise<{
21
+ messages: A2AMessage[];
22
+ artifacts?: A2AArtifact[];
23
+ }>;
24
+ adapters?: {
25
+ attachments?: AttachmentAdapter;
26
+ speech?: SpeechSynthesisAdapter;
27
+ feedback?: FeedbackAdapter;
28
+ } | undefined;
29
+ /**
30
+ * Event handlers for various A2A stream events
31
+ */
32
+ eventHandlers?: {
33
+ /**
34
+ * Called when task updates are received from the A2A stream
35
+ */
36
+ onTaskUpdate?: OnTaskUpdateEventCallback;
37
+ /**
38
+ * Called when artifacts are received from the A2A stream
39
+ */
40
+ onArtifacts?: OnArtifactsEventCallback;
41
+ /**
42
+ * Called when errors occur during A2A stream processing
43
+ */
44
+ onError?: OnErrorEventCallback;
45
+ /**
46
+ * Called when state updates are received from the A2A stream
47
+ */
48
+ onStateUpdate?: OnStateUpdateEventCallback;
49
+ /**
50
+ * Called when custom events are received from the A2A stream
51
+ */
52
+ onCustomEvent?: OnCustomEventCallback;
53
+ } | undefined;
54
+ }) => import("@assistant-ui/react").AssistantRuntime;
55
+ //# sourceMappingURL=useA2ARuntime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useA2ARuntime.d.ts","sourceRoot":"","sources":["../src/useA2ARuntime.ts"],"names":[],"mappings":"AACA,OAAO,EACL,UAAU,EAEV,WAAW,EACX,YAAY,EACZ,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,wBAAwB,EACxB,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACtB,MAAM,SAAS,CAAC;AASjB,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGxD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AA4E7D,eAAO,MAAM,eAAe,gCAG3B,CAAC;AAEF,eAAO,MAAM,eAAe,qBAG3B,CAAC;AAEF,eAAO,MAAM,UAAU,mBA5BJ,UAAU,EAAE,UAAU,oBAAoB,KAAK,OAAO,CAAC,IAAI,CA+B7E,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,mLAS3B;IACD;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,0BAA0B,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjD,0BAA0B,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjD,MAAM,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC;IACtC;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACjD,gBAAgB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;QAChD,QAAQ,EAAE,UAAU,EAAE,CAAC;QACvB,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC;KAC3B,CAAC,CAAC;IACH,QAAQ,CAAC,EACL;QACE,WAAW,CAAC,EAAE,iBAAiB,CAAC;QAChC,MAAM,CAAC,EAAE,sBAAsB,CAAC;QAChC,QAAQ,CAAC,EAAE,eAAe,CAAC;KAC5B,GACD,SAAS,CAAC;IACd;;OAEG;IACH,aAAa,CAAC,EACV;QACE;;WAEG;QACH,YAAY,CAAC,EAAE,yBAAyB,CAAC;QACzC;;WAEG;QACH,WAAW,CAAC,EAAE,wBAAwB,CAAC;QACvC;;WAEG;QACH,OAAO,CAAC,EAAE,oBAAoB,CAAC;QAC/B;;WAEG;QACH,aAAa,CAAC,EAAE,0BAA0B,CAAC;QAC3C;;WAEG;QACH,aAAa,CAAC,EAAE,qBAAqB,CAAC;KACvC,GACD,SAAS,CAAC;CACf,mDA0JA,CAAC"}
@@ -0,0 +1,215 @@
1
+ // src/useA2ARuntime.ts
2
+ import { useEffect, useRef, useState } from "react";
3
+ import {
4
+ useExternalMessageConverter,
5
+ useExternalStoreRuntime,
6
+ useThread,
7
+ useThreadListItemRuntime
8
+ } from "@assistant-ui/react";
9
+ import { convertA2AMessage } from "./convertA2AMessages.js";
10
+ import { useA2AMessages } from "./useA2AMessages.js";
11
+ import { appendA2AChunk } from "./appendA2AChunk.js";
12
+ var getPendingToolCalls = (messages) => {
13
+ const pendingToolCalls = /* @__PURE__ */ new Map();
14
+ for (const message of messages) {
15
+ if (message.role === "assistant") {
16
+ for (const toolCall of message.tool_calls ?? []) {
17
+ pendingToolCalls.set(toolCall.id, toolCall);
18
+ }
19
+ }
20
+ if (message.role === "tool") {
21
+ pendingToolCalls.delete(message.tool_call_id);
22
+ }
23
+ }
24
+ return [...pendingToolCalls.values()];
25
+ };
26
+ var getMessageContent = (msg) => {
27
+ const allContent = [
28
+ ...msg.content,
29
+ ...msg.attachments?.flatMap((a) => a.content) ?? []
30
+ ];
31
+ const content = allContent.map((part) => {
32
+ const type = part.type;
33
+ switch (type) {
34
+ case "text":
35
+ return { type: "text", text: part.text };
36
+ case "image":
37
+ return { type: "image_url", image_url: { url: part.image } };
38
+ case "tool-call":
39
+ throw new Error("Tool call appends are not supported.");
40
+ default:
41
+ const _exhaustiveCheck = type;
42
+ throw new Error(
43
+ `Unsupported append message part type: ${_exhaustiveCheck}`
44
+ );
45
+ }
46
+ });
47
+ if (content.length === 1 && content[0]?.type === "text") {
48
+ return content[0].text ?? "";
49
+ }
50
+ return content;
51
+ };
52
+ var symbolA2ARuntimeExtras = Symbol("a2a-runtime-extras");
53
+ var asA2ARuntimeExtras = (extras) => {
54
+ if (typeof extras !== "object" || extras == null || !(symbolA2ARuntimeExtras in extras))
55
+ throw new Error(
56
+ "This method can only be called when you are using useA2ARuntime"
57
+ );
58
+ return extras;
59
+ };
60
+ var useA2ATaskState = () => {
61
+ const { taskState } = useThread((t) => asA2ARuntimeExtras(t.extras));
62
+ return taskState;
63
+ };
64
+ var useA2AArtifacts = () => {
65
+ const { artifacts } = useThread((t) => asA2ARuntimeExtras(t.extras));
66
+ return artifacts;
67
+ };
68
+ var useA2ASend = () => {
69
+ const { send } = useThread((t) => asA2ARuntimeExtras(t.extras));
70
+ return send;
71
+ };
72
+ var useA2ARuntime = ({
73
+ autoCancelPendingToolCalls,
74
+ adapters: { attachments, feedback, speech } = {},
75
+ unstable_allowCancellation,
76
+ stream,
77
+ contextId,
78
+ onSwitchToNewThread,
79
+ onSwitchToThread,
80
+ eventHandlers
81
+ }) => {
82
+ const {
83
+ taskState,
84
+ artifacts,
85
+ setArtifacts,
86
+ messages,
87
+ sendMessage,
88
+ cancel,
89
+ setMessages
90
+ } = useA2AMessages({
91
+ appendMessage: appendA2AChunk,
92
+ stream,
93
+ ...eventHandlers && { eventHandlers }
94
+ });
95
+ const [isRunning, setIsRunning] = useState(false);
96
+ const handleSendMessage = async (messages2, config) => {
97
+ try {
98
+ setIsRunning(true);
99
+ await sendMessage(messages2, config);
100
+ } catch (error) {
101
+ console.error("Error streaming A2A messages:", error);
102
+ } finally {
103
+ setIsRunning(false);
104
+ }
105
+ };
106
+ const threadMessages = useExternalMessageConverter({
107
+ callback: convertA2AMessage,
108
+ messages,
109
+ isRunning
110
+ });
111
+ const switchToThread = !onSwitchToThread ? void 0 : async (externalId) => {
112
+ const { messages: messages2, artifacts: artifacts2 } = await onSwitchToThread(externalId);
113
+ setMessages(messages2);
114
+ if (artifacts2) {
115
+ setArtifacts(artifacts2);
116
+ }
117
+ };
118
+ const threadList = {
119
+ threadId: contextId,
120
+ onSwitchToNewThread: !onSwitchToNewThread ? void 0 : async () => {
121
+ await onSwitchToNewThread();
122
+ setMessages([]);
123
+ setArtifacts([]);
124
+ },
125
+ onSwitchToThread: switchToThread
126
+ };
127
+ const loadingRef = useRef(false);
128
+ const threadListItemRuntime = useThreadListItemRuntime({ optional: true });
129
+ useEffect(() => {
130
+ if (!threadListItemRuntime || !switchToThread || loadingRef.current) return;
131
+ const externalId = threadListItemRuntime.getState().externalId;
132
+ if (externalId) {
133
+ loadingRef.current = true;
134
+ switchToThread(externalId).finally(() => {
135
+ loadingRef.current = false;
136
+ });
137
+ }
138
+ }, []);
139
+ return useExternalStoreRuntime({
140
+ isRunning,
141
+ messages: threadMessages,
142
+ adapters: {
143
+ attachments,
144
+ feedback,
145
+ speech,
146
+ threadList
147
+ },
148
+ extras: {
149
+ [symbolA2ARuntimeExtras]: true,
150
+ taskState,
151
+ artifacts,
152
+ send: handleSendMessage
153
+ },
154
+ onNew: (msg) => {
155
+ const cancellations = autoCancelPendingToolCalls !== false ? getPendingToolCalls(messages).map(
156
+ (t) => ({
157
+ role: "tool",
158
+ tool_call_id: t.id,
159
+ content: JSON.stringify({ cancelled: true }),
160
+ status: {
161
+ type: "incomplete",
162
+ reason: "cancelled"
163
+ }
164
+ })
165
+ ) : [];
166
+ const config = {};
167
+ if (contextId !== void 0) config.contextId = contextId;
168
+ if (msg.runConfig !== void 0) config.runConfig = msg.runConfig;
169
+ return handleSendMessage(
170
+ [
171
+ ...cancellations,
172
+ {
173
+ role: "user",
174
+ content: getMessageContent(msg)
175
+ }
176
+ ],
177
+ config
178
+ );
179
+ },
180
+ onAddToolResult: async ({
181
+ toolCallId,
182
+ toolName: _toolName,
183
+ result,
184
+ isError,
185
+ artifact
186
+ }) => {
187
+ const message = {
188
+ role: "tool",
189
+ tool_call_id: toolCallId,
190
+ content: JSON.stringify(result),
191
+ status: isError ? { type: "incomplete", reason: "error" } : { type: "complete", reason: "stop" }
192
+ };
193
+ if (artifact) {
194
+ message.artifacts = [artifact];
195
+ }
196
+ const config = {};
197
+ if (contextId !== void 0) config.contextId = contextId;
198
+ await handleSendMessage(
199
+ [message],
200
+ // TODO reuse runconfig here!
201
+ config
202
+ );
203
+ },
204
+ onCancel: unstable_allowCancellation ? async () => {
205
+ cancel();
206
+ } : void 0
207
+ });
208
+ };
209
+ export {
210
+ useA2AArtifacts,
211
+ useA2ARuntime,
212
+ useA2ASend,
213
+ useA2ATaskState
214
+ };
215
+ //# sourceMappingURL=useA2ARuntime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/useA2ARuntime.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport {\n A2AMessage,\n A2AToolCall,\n A2AArtifact,\n A2ATaskState,\n A2ASendMessageConfig,\n A2AStreamCallback,\n OnTaskUpdateEventCallback,\n OnArtifactsEventCallback,\n OnErrorEventCallback,\n OnStateUpdateEventCallback,\n OnCustomEventCallback,\n} from \"./types\";\nimport {\n useExternalMessageConverter,\n useExternalStoreRuntime,\n useThread,\n useThreadListItemRuntime,\n} from \"@assistant-ui/react\";\nimport { convertA2AMessage } from \"./convertA2AMessages\";\nimport { useA2AMessages } from \"./useA2AMessages\";\nimport { AttachmentAdapter } from \"@assistant-ui/react\";\nimport { AppendMessage } from \"@assistant-ui/react\";\nimport { ExternalStoreAdapter } from \"@assistant-ui/react\";\nimport { FeedbackAdapter } from \"@assistant-ui/react\";\nimport { SpeechSynthesisAdapter } from \"@assistant-ui/react\";\nimport { appendA2AChunk } from \"./appendA2AChunk\";\n\nconst getPendingToolCalls = (messages: A2AMessage[]) => {\n const pendingToolCalls = new Map<string, A2AToolCall>();\n for (const message of messages) {\n if (message.role === \"assistant\") {\n for (const toolCall of message.tool_calls ?? []) {\n pendingToolCalls.set(toolCall.id, toolCall);\n }\n }\n if (message.role === \"tool\") {\n pendingToolCalls.delete(message.tool_call_id!);\n }\n }\n\n return [...pendingToolCalls.values()];\n};\n\nconst getMessageContent = (msg: AppendMessage) => {\n const allContent = [\n ...msg.content,\n ...(msg.attachments?.flatMap((a) => a.content) ?? []),\n ];\n const content = allContent.map((part) => {\n const type = part.type;\n switch (type) {\n case \"text\":\n return { type: \"text\" as const, text: part.text };\n case \"image\":\n return { type: \"image_url\" as const, image_url: { url: part.image } };\n\n case \"tool-call\":\n throw new Error(\"Tool call appends are not supported.\");\n\n default:\n const _exhaustiveCheck:\n | \"reasoning\"\n | \"source\"\n | \"file\"\n | \"audio\"\n | \"data\" = type;\n throw new Error(\n `Unsupported append message part type: ${_exhaustiveCheck}`,\n );\n }\n });\n\n if (content.length === 1 && content[0]?.type === \"text\") {\n return content[0].text ?? \"\";\n }\n\n return content;\n};\n\nconst symbolA2ARuntimeExtras = Symbol(\"a2a-runtime-extras\");\ntype A2ARuntimeExtras = {\n [symbolA2ARuntimeExtras]: true;\n send: (messages: A2AMessage[], config: A2ASendMessageConfig) => Promise<void>;\n taskState: A2ATaskState | undefined;\n artifacts: A2AArtifact[];\n};\n\nconst asA2ARuntimeExtras = (extras: unknown): A2ARuntimeExtras => {\n if (\n typeof extras !== \"object\" ||\n extras == null ||\n !(symbolA2ARuntimeExtras in extras)\n )\n throw new Error(\n \"This method can only be called when you are using useA2ARuntime\",\n );\n\n return extras as A2ARuntimeExtras;\n};\n\nexport const useA2ATaskState = () => {\n const { taskState } = useThread((t) => asA2ARuntimeExtras(t.extras));\n return taskState;\n};\n\nexport const useA2AArtifacts = () => {\n const { artifacts } = useThread((t) => asA2ARuntimeExtras(t.extras));\n return artifacts;\n};\n\nexport const useA2ASend = () => {\n const { send } = useThread((t) => asA2ARuntimeExtras(t.extras));\n return send;\n};\n\nexport const useA2ARuntime = ({\n autoCancelPendingToolCalls,\n adapters: { attachments, feedback, speech } = {},\n unstable_allowCancellation,\n stream,\n contextId,\n onSwitchToNewThread,\n onSwitchToThread,\n eventHandlers,\n}: {\n /**\n * @deprecated For thread management use `useCloudThreadListRuntime` instead. This option will be removed in a future version.\n */\n contextId?: string | undefined;\n autoCancelPendingToolCalls?: boolean | undefined;\n unstable_allowCancellation?: boolean | undefined;\n stream: A2AStreamCallback<A2AMessage>;\n /**\n * @deprecated For thread management use `useCloudThreadListRuntime` instead. This option will be removed in a future version.\n */\n onSwitchToNewThread?: () => Promise<void> | void;\n onSwitchToThread?: (contextId: string) => Promise<{\n messages: A2AMessage[];\n artifacts?: A2AArtifact[];\n }>;\n adapters?:\n | {\n attachments?: AttachmentAdapter;\n speech?: SpeechSynthesisAdapter;\n feedback?: FeedbackAdapter;\n }\n | undefined;\n /**\n * Event handlers for various A2A stream events\n */\n eventHandlers?:\n | {\n /**\n * Called when task updates are received from the A2A stream\n */\n onTaskUpdate?: OnTaskUpdateEventCallback;\n /**\n * Called when artifacts are received from the A2A stream\n */\n onArtifacts?: OnArtifactsEventCallback;\n /**\n * Called when errors occur during A2A stream processing\n */\n onError?: OnErrorEventCallback;\n /**\n * Called when state updates are received from the A2A stream\n */\n onStateUpdate?: OnStateUpdateEventCallback;\n /**\n * Called when custom events are received from the A2A stream\n */\n onCustomEvent?: OnCustomEventCallback;\n }\n | undefined;\n}) => {\n const {\n taskState,\n artifacts,\n setArtifacts,\n messages,\n sendMessage,\n cancel,\n setMessages,\n } = useA2AMessages({\n appendMessage: appendA2AChunk,\n stream,\n ...(eventHandlers && { eventHandlers }),\n });\n\n const [isRunning, setIsRunning] = useState(false);\n const handleSendMessage = async (\n messages: A2AMessage[],\n config: A2ASendMessageConfig,\n ) => {\n try {\n setIsRunning(true);\n await sendMessage(messages, config);\n } catch (error) {\n console.error(\"Error streaming A2A messages:\", error);\n } finally {\n setIsRunning(false);\n }\n };\n\n const threadMessages = useExternalMessageConverter({\n callback: convertA2AMessage,\n messages,\n isRunning,\n });\n\n const switchToThread = !onSwitchToThread\n ? undefined\n : async (externalId: string) => {\n const { messages, artifacts } = await onSwitchToThread(externalId);\n setMessages(messages);\n if (artifacts) {\n setArtifacts(artifacts);\n }\n };\n\n const threadList: NonNullable<\n ExternalStoreAdapter[\"adapters\"]\n >[\"threadList\"] = {\n threadId: contextId,\n onSwitchToNewThread: !onSwitchToNewThread\n ? undefined\n : async () => {\n await onSwitchToNewThread();\n setMessages([]);\n setArtifacts([]);\n },\n onSwitchToThread: switchToThread,\n };\n\n const loadingRef = useRef(false);\n const threadListItemRuntime = useThreadListItemRuntime({ optional: true });\n useEffect(() => {\n if (!threadListItemRuntime || !switchToThread || loadingRef.current) return;\n\n const externalId = threadListItemRuntime.getState().externalId;\n if (externalId) {\n loadingRef.current = true;\n switchToThread(externalId).finally(() => {\n loadingRef.current = false;\n });\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return useExternalStoreRuntime({\n isRunning,\n messages: threadMessages,\n adapters: {\n attachments,\n feedback,\n speech,\n threadList,\n },\n extras: {\n [symbolA2ARuntimeExtras]: true,\n taskState,\n artifacts,\n send: handleSendMessage,\n } satisfies A2ARuntimeExtras,\n onNew: (msg) => {\n const cancellations =\n autoCancelPendingToolCalls !== false\n ? getPendingToolCalls(messages).map(\n (t) =>\n ({\n role: \"tool\",\n tool_call_id: t.id,\n content: JSON.stringify({ cancelled: true }),\n status: {\n type: \"incomplete\",\n reason: \"cancelled\",\n },\n }) satisfies A2AMessage & { role: \"tool\" },\n )\n : [];\n\n const config: A2ASendMessageConfig = {};\n if (contextId !== undefined) config.contextId = contextId;\n if (msg.runConfig !== undefined) config.runConfig = msg.runConfig;\n return handleSendMessage(\n [\n ...cancellations,\n {\n role: \"user\",\n content: getMessageContent(msg),\n },\n ],\n config,\n );\n },\n onAddToolResult: async ({\n toolCallId,\n toolName: _toolName,\n result,\n isError,\n artifact,\n }) => {\n // TODO parallel human in the loop calls\n const message: A2AMessage = {\n role: \"tool\",\n tool_call_id: toolCallId,\n content: JSON.stringify(result),\n status: isError\n ? { type: \"incomplete\", reason: \"error\" }\n : { type: \"complete\", reason: \"stop\" },\n };\n if (artifact) {\n message.artifacts = [artifact] as A2AArtifact[];\n }\n const config: A2ASendMessageConfig = {};\n if (contextId !== undefined) config.contextId = contextId;\n await handleSendMessage(\n [message],\n // TODO reuse runconfig here!\n config,\n );\n },\n onCancel: unstable_allowCancellation\n ? async () => {\n cancel();\n }\n : undefined,\n });\n};\n"],"mappings":"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","names":["messages","artifacts"]}
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@assistant-ui/react-a2a",
3
+ "version": "0.1.0",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "default": "./dist/index.js"
10
+ }
11
+ },
12
+ "main": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "files": [
15
+ "dist",
16
+ "src",
17
+ "README.md"
18
+ ],
19
+ "sideEffects": false,
20
+ "dependencies": {
21
+ "assistant-stream": "^0.2.23",
22
+ "uuid": "^11.1.0",
23
+ "zod": "^4.0.17"
24
+ },
25
+ "peerDependencies": {
26
+ "@assistant-ui/react": "^0.10.50",
27
+ "@types/react": "*",
28
+ "react": "^18 || ^19 || ^19.0.0-rc"
29
+ },
30
+ "peerDependenciesMeta": {
31
+ "@types/react": {
32
+ "optional": true
33
+ }
34
+ },
35
+ "devDependencies": {
36
+ "@testing-library/dom": "^10.4.1",
37
+ "@testing-library/react": "^16.3.0",
38
+ "@types/node": "^24.3.0",
39
+ "@types/react": "^19.1.10",
40
+ "@types/uuid": "^10.0.0",
41
+ "eslint": "^9",
42
+ "eslint-config-next": "15.4.6",
43
+ "jsdom": "^26.1.0",
44
+ "react": "19.1.1",
45
+ "tsx": "^4.20.4",
46
+ "vitest": "^3.2.4",
47
+ "@assistant-ui/react": "0.11.39",
48
+ "@assistant-ui/x-buildutils": "0.0.1"
49
+ },
50
+ "publishConfig": {
51
+ "access": "public",
52
+ "provenance": true
53
+ },
54
+ "homepage": "https://www.assistant-ui.com/",
55
+ "repository": {
56
+ "type": "git",
57
+ "url": "https://github.com/assistant-ui/assistant-ui/tree/main/packages/react-a2a"
58
+ },
59
+ "bugs": {
60
+ "url": "https://github.com/assistant-ui/assistant-ui/issues"
61
+ },
62
+ "scripts": {
63
+ "build": "tsx scripts/build.mts",
64
+ "lint": "eslint .",
65
+ "test": "vitest run --passWithNoTests",
66
+ "test:watch": "vitest"
67
+ }
68
+ }
@@ -0,0 +1,48 @@
1
+ import { v4 as uuidv4 } from "uuid";
2
+
3
+ export type A2AStateAccumulatorConfig<TMessage> = {
4
+ initialMessages?: TMessage[];
5
+ appendMessage?: (prev: TMessage | undefined, curr: TMessage) => TMessage;
6
+ };
7
+
8
+ export class A2AMessageAccumulator<TMessage extends { id?: string }> {
9
+ private messagesMap = new Map<string, TMessage>();
10
+ private appendMessage: (
11
+ prev: TMessage | undefined,
12
+ curr: TMessage,
13
+ ) => TMessage;
14
+
15
+ constructor({
16
+ initialMessages = [],
17
+ appendMessage = ((_: TMessage | undefined, curr: TMessage) => curr) as (
18
+ prev: TMessage | undefined,
19
+ curr: TMessage,
20
+ ) => TMessage,
21
+ }: A2AStateAccumulatorConfig<TMessage> = {}) {
22
+ this.appendMessage = appendMessage;
23
+ this.addMessages(initialMessages);
24
+ }
25
+
26
+ private ensureMessageId(message: TMessage): TMessage {
27
+ return message.id ? message : { ...message, id: uuidv4() };
28
+ }
29
+
30
+ public addMessages(newMessages: TMessage[]) {
31
+ if (newMessages.length === 0) return this.getMessages();
32
+
33
+ for (const message of newMessages.map(this.ensureMessageId)) {
34
+ const messageId = message.id!; // ensureMessageId guarantees id exists
35
+ const previous = this.messagesMap.get(messageId);
36
+ this.messagesMap.set(messageId, this.appendMessage(previous, message));
37
+ }
38
+ return this.getMessages();
39
+ }
40
+
41
+ public getMessages(): TMessage[] {
42
+ return [...this.messagesMap.values()];
43
+ }
44
+
45
+ public clear() {
46
+ this.messagesMap.clear();
47
+ }
48
+ }