@assistant-ui/react-a2a 0.2.46 → 0.2.48

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.
@@ -0,0 +1,56 @@
1
+ import { type A2uiState } from "@assistant-ui/generative-ui/a2ui";
2
+ import type { A2AAgentCard, A2AArtifact, A2AMessage, A2ATask, A2ATaskArtifactUpdateEvent, A2ATaskStatusUpdateEvent } from "./types.js";
3
+ export type A2AThreadState = {
4
+ currentTask: A2ATask | undefined;
5
+ currentArtifacts: A2AArtifact[];
6
+ a2uiState: A2uiState;
7
+ a2uiMessageIds: ReadonlySet<string>;
8
+ agentCard: A2AAgentCard | undefined;
9
+ isRunning: boolean;
10
+ isLoading: boolean;
11
+ loadRequested: boolean;
12
+ contextId: string | undefined;
13
+ };
14
+ export type A2AThreadAction = {
15
+ type: "statusUpdate";
16
+ event: A2ATaskStatusUpdateEvent;
17
+ } | {
18
+ type: "artifactUpdate";
19
+ event: A2ATaskArtifactUpdateEvent;
20
+ } | {
21
+ type: "taskSnapshot";
22
+ task: A2ATask;
23
+ } | {
24
+ type: "a2uiMessage";
25
+ message: A2AMessage;
26
+ replay?: boolean;
27
+ } | {
28
+ type: "prepareRun";
29
+ } | {
30
+ type: "runStarted";
31
+ } | {
32
+ type: "runFinished";
33
+ } | {
34
+ type: "agentCardLoaded";
35
+ agentCard: A2AAgentCard;
36
+ } | {
37
+ type: "loadRequested";
38
+ } | {
39
+ type: "loadStarted";
40
+ } | {
41
+ type: "loadFinished";
42
+ } | {
43
+ type: "contextLearned";
44
+ contextId: string | undefined;
45
+ } | {
46
+ type: "resetContext";
47
+ contextId: string | undefined;
48
+ } | {
49
+ type: "taskReplaced";
50
+ task: A2ATask;
51
+ } | {
52
+ type: "reset";
53
+ };
54
+ export declare const createA2AThreadState: (contextId?: string) => A2AThreadState;
55
+ export declare const reduceA2AThreadState: (state: A2AThreadState, action: A2AThreadAction) => A2AThreadState;
56
+ //# sourceMappingURL=a2aThreadState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a2aThreadState.d.ts","sourceRoot":"","sources":["../src/a2aThreadState.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,SAAS,EACf,MAAM,kCAAkC,CAAC;AAE1C,OAAO,KAAK,EACV,YAAY,EACZ,WAAW,EACX,UAAU,EAEV,OAAO,EACP,0BAA0B,EAC1B,wBAAwB,EACzB,mBAAgB;AAEjB,MAAM,MAAM,cAAc,GAAG;IAC3B,WAAW,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,gBAAgB,EAAE,WAAW,EAAE,CAAC;IAChC,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACpC,SAAS,EAAE,YAAY,GAAG,SAAS,CAAC;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,wBAAwB,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,0BAA0B,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,UAAU,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9D;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,SAAS,EAAE,YAAY,CAAA;CAAE,GACpD;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,GACzB;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtB,eAAO,MAAM,oBAAoB,GAAI,YAAY,MAAM,KAAG,cAUxD,CAAC;AAuCH,eAAO,MAAM,oBAAoB,GAC/B,OAAO,cAAc,EACrB,QAAQ,eAAe,KACtB,cAiJF,CAAC"}
@@ -0,0 +1,152 @@
1
+ import { a2uiPartsToOperations, isTerminalTaskState } from "./conversions.js";
2
+ import { applyA2uiOperations } from "@assistant-ui/generative-ui/a2ui";
3
+ //#region src/a2aThreadState.ts
4
+ const createA2AThreadState = (contextId) => ({
5
+ currentTask: void 0,
6
+ currentArtifacts: [],
7
+ a2uiState: /* @__PURE__ */ new Map(),
8
+ a2uiMessageIds: /* @__PURE__ */ new Set(),
9
+ agentCard: void 0,
10
+ isRunning: false,
11
+ isLoading: false,
12
+ loadRequested: false,
13
+ contextId
14
+ });
15
+ const normalizeArtifact = (artifact) => ({
16
+ ...artifact,
17
+ parts: Array.isArray(artifact.parts) ? artifact.parts : []
18
+ });
19
+ const applyParts = (state, parts) => {
20
+ const operations = a2uiPartsToOperations(parts);
21
+ if (operations.length === 0) return state;
22
+ return {
23
+ ...state,
24
+ a2uiState: applyA2uiOperations(state.a2uiState, operations).state
25
+ };
26
+ };
27
+ const applyMessage = (state, message, replay) => {
28
+ if (message.messageId && replay && state.a2uiMessageIds.has(message.messageId)) return state;
29
+ const next = message.messageId ? {
30
+ ...state,
31
+ a2uiMessageIds: /* @__PURE__ */ new Set([...state.a2uiMessageIds, message.messageId])
32
+ } : state;
33
+ return applyParts(next, message.parts);
34
+ };
35
+ const reduceA2AThreadState = (state, action) => {
36
+ switch (action.type) {
37
+ case "statusUpdate": {
38
+ const { event } = action;
39
+ const currentTask = state.currentTask ? {
40
+ ...state.currentTask,
41
+ status: event.status
42
+ } : {
43
+ id: event.taskId,
44
+ contextId: event.contextId,
45
+ status: event.status
46
+ };
47
+ const next = {
48
+ ...state,
49
+ currentTask,
50
+ contextId: event.contextId || state.contextId
51
+ };
52
+ return event.status.message ? applyMessage(next, event.status.message, false) : next;
53
+ }
54
+ case "artifactUpdate": {
55
+ const { artifact: input, append } = action.event;
56
+ const artifact = normalizeArtifact(input);
57
+ const index = state.currentArtifacts.findIndex((current) => current.artifactId === artifact.artifactId);
58
+ const updated = index >= 0 && append ? {
59
+ ...state.currentArtifacts[index],
60
+ parts: [...state.currentArtifacts[index].parts, ...artifact.parts]
61
+ } : artifact;
62
+ const currentArtifacts = index >= 0 ? [
63
+ ...state.currentArtifacts.slice(0, index),
64
+ updated,
65
+ ...state.currentArtifacts.slice(index + 1)
66
+ ] : [...state.currentArtifacts, updated];
67
+ return applyParts({
68
+ ...state,
69
+ currentArtifacts
70
+ }, artifact.parts);
71
+ }
72
+ case "taskSnapshot": {
73
+ const { task } = action;
74
+ const artifacts = task.artifacts === void 0 ? void 0 : Array.isArray(task.artifacts) ? task.artifacts.map(normalizeArtifact) : [];
75
+ const history = task.history === void 0 ? void 0 : Array.isArray(task.history) ? task.history : [];
76
+ const complete = history !== void 0 && artifacts !== void 0;
77
+ const artifactsToApply = complete ? artifacts : artifacts?.filter((artifact) => !state.currentArtifacts.some(({ artifactId }) => artifactId === artifact.artifactId));
78
+ let next = {
79
+ ...state,
80
+ currentTask: {
81
+ ...task,
82
+ ...artifacts === void 0 ? {} : { artifacts },
83
+ ...history === void 0 ? {} : { history }
84
+ },
85
+ contextId: task.contextId || state.contextId,
86
+ currentArtifacts: complete ? artifacts : artifactsToApply ? [...state.currentArtifacts, ...artifactsToApply] : state.currentArtifacts,
87
+ a2uiState: complete ? /* @__PURE__ */ new Map() : state.a2uiState,
88
+ a2uiMessageIds: complete ? /* @__PURE__ */ new Set() : state.a2uiMessageIds
89
+ };
90
+ for (const message of history ?? []) if (message.role === "agent") next = applyMessage(next, message, true);
91
+ for (const artifact of artifactsToApply ?? []) next = applyParts(next, artifact.parts);
92
+ if (task.status.message) next = applyMessage(next, task.status.message, true);
93
+ return next;
94
+ }
95
+ case "a2uiMessage": return applyMessage(state, action.message, action.replay ?? false);
96
+ case "prepareRun": return {
97
+ ...state,
98
+ currentTask: state.currentTask && isTerminalTaskState(state.currentTask.status.state) ? void 0 : state.currentTask,
99
+ currentArtifacts: [],
100
+ a2uiState: /* @__PURE__ */ new Map(),
101
+ a2uiMessageIds: /* @__PURE__ */ new Set()
102
+ };
103
+ case "runStarted": return state.isRunning ? state : {
104
+ ...state,
105
+ isRunning: true
106
+ };
107
+ case "runFinished": return !state.isRunning ? state : {
108
+ ...state,
109
+ isRunning: false
110
+ };
111
+ case "agentCardLoaded": return state.agentCard === action.agentCard ? state : {
112
+ ...state,
113
+ agentCard: action.agentCard
114
+ };
115
+ case "loadRequested": return state.loadRequested ? state : {
116
+ ...state,
117
+ loadRequested: true
118
+ };
119
+ case "loadStarted": return state.isLoading ? state : {
120
+ ...state,
121
+ isLoading: true
122
+ };
123
+ case "loadFinished": return !state.isLoading ? state : {
124
+ ...state,
125
+ isLoading: false
126
+ };
127
+ case "contextLearned": return state.contextId === action.contextId ? state : {
128
+ ...state,
129
+ contextId: action.contextId
130
+ };
131
+ case "resetContext": return !state.isLoading && state.contextId === action.contextId ? state : {
132
+ ...state,
133
+ isLoading: false,
134
+ contextId: action.contextId
135
+ };
136
+ case "taskReplaced": return state.currentTask === action.task ? state : {
137
+ ...state,
138
+ currentTask: action.task
139
+ };
140
+ case "reset": return {
141
+ ...state,
142
+ currentTask: void 0,
143
+ currentArtifacts: [],
144
+ a2uiState: /* @__PURE__ */ new Map(),
145
+ a2uiMessageIds: /* @__PURE__ */ new Set()
146
+ };
147
+ }
148
+ };
149
+ //#endregion
150
+ export { createA2AThreadState, reduceA2AThreadState };
151
+
152
+ //# sourceMappingURL=a2aThreadState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a2aThreadState.js","names":[],"sources":["../src/a2aThreadState.ts"],"sourcesContent":["import {\n applyA2uiOperations,\n type A2uiState,\n} from \"@assistant-ui/generative-ui/a2ui\";\nimport { a2uiPartsToOperations, isTerminalTaskState } from \"./conversions\";\nimport type {\n A2AAgentCard,\n A2AArtifact,\n A2AMessage,\n A2APart,\n A2ATask,\n A2ATaskArtifactUpdateEvent,\n A2ATaskStatusUpdateEvent,\n} from \"./types\";\n\nexport type A2AThreadState = {\n currentTask: A2ATask | undefined;\n currentArtifacts: A2AArtifact[];\n a2uiState: A2uiState;\n a2uiMessageIds: ReadonlySet<string>;\n agentCard: A2AAgentCard | undefined;\n isRunning: boolean;\n isLoading: boolean;\n loadRequested: boolean;\n contextId: string | undefined;\n};\n\nexport type A2AThreadAction =\n | { type: \"statusUpdate\"; event: A2ATaskStatusUpdateEvent }\n | { type: \"artifactUpdate\"; event: A2ATaskArtifactUpdateEvent }\n | { type: \"taskSnapshot\"; task: A2ATask }\n | { type: \"a2uiMessage\"; message: A2AMessage; replay?: boolean }\n | { type: \"prepareRun\" }\n | { type: \"runStarted\" }\n | { type: \"runFinished\" }\n | { type: \"agentCardLoaded\"; agentCard: A2AAgentCard }\n | { type: \"loadRequested\" }\n | { type: \"loadStarted\" }\n | { type: \"loadFinished\" }\n | { type: \"contextLearned\"; contextId: string | undefined }\n | { type: \"resetContext\"; contextId: string | undefined }\n | { type: \"taskReplaced\"; task: A2ATask }\n | { type: \"reset\" };\n\nexport const createA2AThreadState = (contextId?: string): A2AThreadState => ({\n currentTask: undefined,\n currentArtifacts: [],\n a2uiState: new Map(),\n a2uiMessageIds: new Set(),\n agentCard: undefined,\n isRunning: false,\n isLoading: false,\n loadRequested: false,\n contextId,\n});\n\nconst normalizeArtifact = (artifact: A2AArtifact): A2AArtifact => ({\n ...artifact,\n parts: Array.isArray(artifact.parts) ? artifact.parts : [],\n});\n\nconst applyParts = (\n state: A2AThreadState,\n parts: readonly A2APart[],\n): A2AThreadState => {\n const operations = a2uiPartsToOperations(parts);\n if (operations.length === 0) return state;\n return {\n ...state,\n a2uiState: applyA2uiOperations(state.a2uiState, operations).state,\n };\n};\n\nconst applyMessage = (\n state: A2AThreadState,\n message: A2AMessage,\n replay: boolean,\n): A2AThreadState => {\n if (\n message.messageId &&\n replay &&\n state.a2uiMessageIds.has(message.messageId)\n )\n return state;\n const next = message.messageId\n ? {\n ...state,\n a2uiMessageIds: new Set([...state.a2uiMessageIds, message.messageId]),\n }\n : state;\n return applyParts(next, message.parts);\n};\n\nexport const reduceA2AThreadState = (\n state: A2AThreadState,\n action: A2AThreadAction,\n): A2AThreadState => {\n switch (action.type) {\n case \"statusUpdate\": {\n const { event } = action;\n const currentTask = state.currentTask\n ? { ...state.currentTask, status: event.status }\n : {\n id: event.taskId,\n contextId: event.contextId,\n status: event.status,\n };\n const next = {\n ...state,\n currentTask,\n contextId: event.contextId || state.contextId,\n };\n return event.status.message\n ? applyMessage(next, event.status.message, false)\n : next;\n }\n case \"artifactUpdate\": {\n const { artifact: input, append } = action.event;\n const artifact = normalizeArtifact(input);\n const index = state.currentArtifacts.findIndex(\n (current) => current.artifactId === artifact.artifactId,\n );\n const updated =\n index >= 0 && append\n ? {\n ...state.currentArtifacts[index]!,\n parts: [\n ...state.currentArtifacts[index]!.parts,\n ...artifact.parts,\n ],\n }\n : artifact;\n const currentArtifacts =\n index >= 0\n ? [\n ...state.currentArtifacts.slice(0, index),\n updated,\n ...state.currentArtifacts.slice(index + 1),\n ]\n : [...state.currentArtifacts, updated];\n return applyParts({ ...state, currentArtifacts }, artifact.parts);\n }\n case \"taskSnapshot\": {\n const { task } = action;\n const artifacts =\n task.artifacts === undefined\n ? undefined\n : Array.isArray(task.artifacts)\n ? task.artifacts.map(normalizeArtifact)\n : [];\n const history =\n task.history === undefined\n ? undefined\n : Array.isArray(task.history)\n ? task.history\n : [];\n const complete = history !== undefined && artifacts !== undefined;\n const artifactsToApply = complete\n ? artifacts\n : artifacts?.filter(\n (artifact) =>\n !state.currentArtifacts.some(\n ({ artifactId }) => artifactId === artifact.artifactId,\n ),\n );\n let next: A2AThreadState = {\n ...state,\n currentTask: {\n ...task,\n ...(artifacts === undefined ? {} : { artifacts }),\n ...(history === undefined ? {} : { history }),\n },\n contextId: task.contextId || state.contextId,\n currentArtifacts: complete\n ? artifacts!\n : artifactsToApply\n ? [...state.currentArtifacts, ...artifactsToApply]\n : state.currentArtifacts,\n a2uiState: complete ? new Map() : state.a2uiState,\n a2uiMessageIds: complete ? new Set() : state.a2uiMessageIds,\n };\n for (const message of history ?? []) {\n if (message.role === \"agent\") next = applyMessage(next, message, true);\n }\n for (const artifact of artifactsToApply ?? []) {\n next = applyParts(next, artifact.parts);\n }\n if (task.status.message) {\n next = applyMessage(next, task.status.message, true);\n }\n return next;\n }\n case \"a2uiMessage\":\n return applyMessage(state, action.message, action.replay ?? false);\n case \"prepareRun\":\n return {\n ...state,\n currentTask:\n state.currentTask &&\n isTerminalTaskState(state.currentTask.status.state)\n ? undefined\n : state.currentTask,\n currentArtifacts: [],\n a2uiState: new Map(),\n a2uiMessageIds: new Set(),\n };\n case \"runStarted\":\n return state.isRunning ? state : { ...state, isRunning: true };\n case \"runFinished\":\n return !state.isRunning ? state : { ...state, isRunning: false };\n case \"agentCardLoaded\":\n return state.agentCard === action.agentCard\n ? state\n : { ...state, agentCard: action.agentCard };\n case \"loadRequested\":\n return state.loadRequested ? state : { ...state, loadRequested: true };\n case \"loadStarted\":\n return state.isLoading ? state : { ...state, isLoading: true };\n case \"loadFinished\":\n return !state.isLoading ? state : { ...state, isLoading: false };\n case \"contextLearned\":\n return state.contextId === action.contextId\n ? state\n : { ...state, contextId: action.contextId };\n case \"resetContext\":\n return !state.isLoading && state.contextId === action.contextId\n ? state\n : { ...state, isLoading: false, contextId: action.contextId };\n case \"taskReplaced\":\n return state.currentTask === action.task\n ? state\n : { ...state, currentTask: action.task };\n case \"reset\":\n return {\n ...state,\n currentTask: undefined,\n currentArtifacts: [],\n a2uiState: new Map(),\n a2uiMessageIds: new Set(),\n };\n }\n};\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { A2AClient } from "./A2AClient.js";
3
- import { A2AThreadRuntimeCore } from "./A2AThreadRuntimeCore.js";
3
+ import { A2AThreadController } from "./A2AThreadController.js";
4
4
  import { a2aExtras } from "./a2aExtras.js";
5
5
  import { useExternalStoreRuntime, useExternalStoreSharedOptions, useRuntimeAdapters } from "@assistant-ui/core/react";
6
6
  import { useCallback, useEffect, useInsertionEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
@@ -66,7 +66,7 @@ function useA2ARuntime(options) {
66
66
  };
67
67
  const coreOptionsRef = useRef(coreOptions);
68
68
  coreOptionsRef.current = coreOptions;
69
- const createCore = () => new A2AThreadRuntimeCore({
69
+ const createCore = () => new A2AThreadController({
70
70
  ...coreOptionsRef.current,
71
71
  client
72
72
  });
@@ -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 useSyncExternalStore,\n} from \"react\";\nimport {\n useExternalStoreRuntime,\n useExternalStoreSharedOptions,\n useRuntimeAdapters,\n} from \"@assistant-ui/core/react\";\nimport { useReplaySafeEffect } from \"@assistant-ui/store/internal\";\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 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 createClient = () => {\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 };\n const [pinnedClient, setPinnedClient] = useState(() => ({\n key: managedClientOptionsKey,\n provided: options.client,\n client: createClient(),\n }));\n let currentClient = pinnedClient;\n if (\n pinnedClient.key !== managedClientOptionsKey ||\n pinnedClient.provided !== options.client\n ) {\n currentClient = {\n key: managedClientOptionsKey,\n provided: options.client,\n client: createClient(),\n };\n setPinnedClient(currentClient);\n }\n const client = currentClient.client;\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 createCore = () =>\n new A2AThreadRuntimeCore({\n ...coreOptionsRef.current,\n client,\n });\n const [pinnedCore, setPinnedCore] = useState(() => ({\n client,\n core: createCore(),\n }));\n let currentCore = pinnedCore;\n if (pinnedCore.client !== client) {\n currentCore = { client, core: createCore() };\n setPinnedCore(currentCore);\n }\n const core = currentCore.core;\n const snapshot = useSyncExternalStore(\n core.subscribe,\n core.getSnapshot,\n core.getSnapshot,\n );\n\n useEffect(() => {\n core.updateOptions(coreOptions);\n });\n\n // Thread list\n const threadSwitchGenerationRef = useRef(0);\n const switchingGenerationRef = useRef<number | null>(null);\n const threadList = useMemo(() => {\n if (!threadListAdapter) return undefined;\n\n const { onSwitchToNewThread, onSwitchToThread } = threadListAdapter;\n\n const releaseSwitch = (generation: number) => {\n if (switchingGenerationRef.current === generation) {\n switchingGenerationRef.current = null;\n }\n };\n\n return {\n threadId: threadListAdapter.threadId,\n onSwitchToNewThread: onSwitchToNewThread\n ? async () => {\n const generation = ++threadSwitchGenerationRef.current;\n switchingGenerationRef.current = generation;\n try {\n // Clear before the thread id flips, or the old messages leak\n // into the new thread as a sibling branch.\n core.applyExternalMessages([]);\n core.resetContext();\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 } finally {\n releaseSwitch(generation);\n }\n }\n : undefined,\n onSwitchToThread: onSwitchToThread\n ? async (threadId: string) => {\n const generation = ++threadSwitchGenerationRef.current;\n switchingGenerationRef.current = generation;\n try {\n // Clear before the thread id flips, or the old messages leak\n // into the new thread as a sibling branch.\n core.applyExternalMessages([]);\n core.resetContext();\n const result = await onSwitchToThread(threadId);\n if (generation !== threadSwitchGenerationRef.current) return;\n core.applyExternalMessages([]);\n core.applyExternalMessages(result.messages);\n core.resetContext();\n } finally {\n releaseSwitch(generation);\n }\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 return {\n ...shared,\n isLoading: snapshot.isLoading,\n messageRepository: snapshot.messageRepository,\n isRunning: snapshot.isRunning,\n unstable_persistsHistory: true,\n extras: a2aExtras.provide({\n task: snapshot.task,\n artifacts: snapshot.artifacts,\n agentCard: snapshot.agentCard,\n }),\n onNew: (message: AppendMessage) =>\n switchingGenerationRef.current === null\n ? core.append(message)\n : Promise.resolve(),\n onVoiceTranscript: (message: ThreadMessage) =>\n core.appendVoiceTranscript(message),\n onEdit: (message: AppendMessage) =>\n switchingGenerationRef.current === null\n ? core.edit(message)\n : Promise.resolve(),\n onReload: (parentId: string | null) =>\n switchingGenerationRef.current === null\n ? core.reload(parentId)\n : Promise.resolve(),\n onCancel: () => core.cancel(),\n unstable_onRecordToolInteraction: (options) =>\n core.recordToolInteraction(options),\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, snapshot, shared]);\n\n const runtime = useExternalStoreRuntime(store);\n\n useReplaySafeEffect(() => {\n core.attachRuntime(runtime);\n return () => {\n core.detachRuntime();\n };\n }, [core, runtime]);\n\n useEffect(() => {\n core.__internal_load();\n }, [core]);\n\n return runtime;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useA2ARuntime.js","names":[],"sources":["../src/useA2ARuntime.ts"],"sourcesContent":["\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useInsertionEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport {\n useExternalStoreRuntime,\n useExternalStoreSharedOptions,\n useRuntimeAdapters,\n} from \"@assistant-ui/core/react\";\nimport { useReplaySafeEffect } from \"@assistant-ui/store/internal\";\nimport type {\n AssistantRuntime,\n AppendMessage,\n ExternalStoreAdapter,\n ThreadMessage,\n} from \"@assistant-ui/core\";\nimport { A2AClient, type A2AClientOptions } from \"./A2AClient\";\nimport { A2AThreadController } from \"./A2AThreadController\";\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 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 createClient = () => {\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 };\n const [pinnedClient, setPinnedClient] = useState(() => ({\n key: managedClientOptionsKey,\n provided: options.client,\n client: createClient(),\n }));\n let currentClient = pinnedClient;\n if (\n pinnedClient.key !== managedClientOptionsKey ||\n pinnedClient.provided !== options.client\n ) {\n currentClient = {\n key: managedClientOptionsKey,\n provided: options.client,\n client: createClient(),\n };\n setPinnedClient(currentClient);\n }\n const client = currentClient.client;\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 createCore = () =>\n new A2AThreadController({\n ...coreOptionsRef.current,\n client,\n });\n const [pinnedCore, setPinnedCore] = useState(() => ({\n client,\n core: createCore(),\n }));\n let currentCore = pinnedCore;\n if (pinnedCore.client !== client) {\n currentCore = { client, core: createCore() };\n setPinnedCore(currentCore);\n }\n const core = currentCore.core;\n const snapshot = useSyncExternalStore(\n core.subscribe,\n core.getSnapshot,\n core.getSnapshot,\n );\n\n useEffect(() => {\n core.updateOptions(coreOptions);\n });\n\n // Thread list\n const threadSwitchGenerationRef = useRef(0);\n const switchingGenerationRef = useRef<number | null>(null);\n const threadList = useMemo(() => {\n if (!threadListAdapter) return undefined;\n\n const { onSwitchToNewThread, onSwitchToThread } = threadListAdapter;\n\n const releaseSwitch = (generation: number) => {\n if (switchingGenerationRef.current === generation) {\n switchingGenerationRef.current = null;\n }\n };\n\n return {\n threadId: threadListAdapter.threadId,\n onSwitchToNewThread: onSwitchToNewThread\n ? async () => {\n const generation = ++threadSwitchGenerationRef.current;\n switchingGenerationRef.current = generation;\n try {\n // Clear before the thread id flips, or the old messages leak\n // into the new thread as a sibling branch.\n core.applyExternalMessages([]);\n core.resetContext();\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 } finally {\n releaseSwitch(generation);\n }\n }\n : undefined,\n onSwitchToThread: onSwitchToThread\n ? async (threadId: string) => {\n const generation = ++threadSwitchGenerationRef.current;\n switchingGenerationRef.current = generation;\n try {\n // Clear before the thread id flips, or the old messages leak\n // into the new thread as a sibling branch.\n core.applyExternalMessages([]);\n core.resetContext();\n const result = await onSwitchToThread(threadId);\n if (generation !== threadSwitchGenerationRef.current) return;\n core.applyExternalMessages([]);\n core.applyExternalMessages(result.messages);\n core.resetContext();\n } finally {\n releaseSwitch(generation);\n }\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 return {\n ...shared,\n isLoading: snapshot.isLoading,\n messageRepository: snapshot.messageRepository,\n isRunning: snapshot.isRunning,\n unstable_persistsHistory: true,\n extras: a2aExtras.provide({\n task: snapshot.task,\n artifacts: snapshot.artifacts,\n agentCard: snapshot.agentCard,\n }),\n onNew: (message: AppendMessage) =>\n switchingGenerationRef.current === null\n ? core.append(message)\n : Promise.resolve(),\n onVoiceTranscript: (message: ThreadMessage) =>\n core.appendVoiceTranscript(message),\n onEdit: (message: AppendMessage) =>\n switchingGenerationRef.current === null\n ? core.edit(message)\n : Promise.resolve(),\n onReload: (parentId: string | null) =>\n switchingGenerationRef.current === null\n ? core.reload(parentId)\n : Promise.resolve(),\n onCancel: () => core.cancel(),\n unstable_onRecordToolInteraction: (options) =>\n core.recordToolInteraction(options),\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, snapshot, shared]);\n\n const runtime = useExternalStoreRuntime(store);\n\n useReplaySafeEffect(() => {\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.46",
3
+ "version": "0.2.48",
4
4
  "description": "A2A (Agent-to-Agent) v1.0 protocol adapter for assistant-ui",
5
5
  "keywords": [
6
6
  "a2a",
@@ -29,10 +29,10 @@
29
29
  ],
30
30
  "sideEffects": false,
31
31
  "dependencies": {
32
- "@assistant-ui/core": "^0.3.29",
33
- "@assistant-ui/generative-ui": "^0.0.5",
34
- "@assistant-ui/store": "^0.3.21",
35
- "assistant-stream": "^0.3.52"
32
+ "@assistant-ui/core": "^0.3.31",
33
+ "@assistant-ui/generative-ui": "^0.0.7",
34
+ "@assistant-ui/store": "^0.3.22",
35
+ "assistant-stream": "^0.3.54"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@types/react": "*",
@@ -44,15 +44,15 @@
44
44
  }
45
45
  },
46
46
  "devDependencies": {
47
- "@assistant-ui/x-buildutils": "0.0.30",
47
+ "@assistant-ui/x-buildutils": "0.0.31",
48
48
  "@assistant-ui/x-react18": "0.0.0",
49
49
  "@testing-library/dom": "^10.4.2",
50
50
  "@testing-library/react": "^16.3.3",
51
51
  "@types/react": "^19.3.0",
52
- "jsdom": "^30.1.2",
53
- "react": "^19.3.0",
54
- "react-dom": "^19.3.0",
55
- "vitest": "^5.0.3"
52
+ "jsdom": "^30.1.3",
53
+ "react": "^19.3.1",
54
+ "react-dom": "^19.3.1",
55
+ "vitest": "^5.0.5"
56
56
  },
57
57
  "publishConfig": {
58
58
  "access": "public",
@@ -3,7 +3,7 @@ import {
3
3
  applyA2uiOperations,
4
4
  convertSurfaceToUISpec,
5
5
  } from "@assistant-ui/generative-ui/a2ui";
6
- import { A2AThreadRuntimeCore } from "./A2AThreadRuntimeCore";
6
+ import { A2AThreadController } from "./A2AThreadController";
7
7
  import type { A2AClient } from "./A2AClient";
8
8
  import type {
9
9
  A2AAgentCard,
@@ -154,7 +154,7 @@ const toolInteraction = {
154
154
  payload: { value: "selected" },
155
155
  };
156
156
 
157
- describe("A2AThreadRuntimeCore", () => {
157
+ describe("A2AThreadController", () => {
158
158
  let onUpdate = vi.fn<() => void>();
159
159
 
160
160
  beforeEach(() => {
@@ -169,7 +169,7 @@ describe("A2AThreadRuntimeCore", () => {
169
169
  clientOverrides: Partial<A2AClient> = {},
170
170
  coreOverrides: Record<string, unknown> = {},
171
171
  ) {
172
- const core = new A2AThreadRuntimeCore({
172
+ const core = new A2AThreadController({
173
173
  client: createMockClient(clientOverrides),
174
174
  ...coreOverrides,
175
175
  });
@@ -634,7 +634,7 @@ describe("A2AThreadRuntimeCore", () => {
634
634
  yield statusUpdateEvent("completed", "Answer");
635
635
  });
636
636
  const client = createMockClient({ streamMessage });
637
- const core = new A2AThreadRuntimeCore({
637
+ const core = new A2AThreadController({
638
638
  client,
639
639
  });
640
640
 
@@ -653,7 +653,7 @@ describe("A2AThreadRuntimeCore", () => {
653
653
  yield statusUpdateEvent("completed", "Answer");
654
654
  });
655
655
  const client = createMockClient({ streamMessage });
656
- const core = new A2AThreadRuntimeCore({
656
+ const core = new A2AThreadController({
657
657
  client,
658
658
  });
659
659
 
@@ -706,7 +706,7 @@ describe("A2AThreadRuntimeCore", () => {
706
706
  yield statusUpdateEvent("completed", "Answer");
707
707
  });
708
708
  const client = createMockClient({ streamMessage });
709
- const core = new A2AThreadRuntimeCore({
709
+ const core = new A2AThreadController({
710
710
  client,
711
711
  });
712
712
 
@@ -725,7 +725,7 @@ describe("A2AThreadRuntimeCore", () => {
725
725
  yield statusUpdateEvent("completed", "Answer");
726
726
  });
727
727
  const client = createMockClient({ streamMessage });
728
- const core = new A2AThreadRuntimeCore({
728
+ const core = new A2AThreadController({
729
729
  client,
730
730
  });
731
731
 
@@ -1581,8 +1581,9 @@ describe("A2AThreadRuntimeCore", () => {
1581
1581
 
1582
1582
  const core = createCore({ sendMessage, streamMessage });
1583
1583
  // Simulate agent card with streaming: false
1584
- (core as any).agentCardValue = {
1585
- capabilities: { streaming: false },
1584
+ (core as any).state = {
1585
+ ...(core as any).state,
1586
+ agentCard: { capabilities: { streaming: false } },
1586
1587
  };
1587
1588
 
1588
1589
  await core.append(createUserAppendMessage("Hello"));
@@ -1605,8 +1606,9 @@ describe("A2AThreadRuntimeCore", () => {
1605
1606
  } satisfies A2AMessage);
1606
1607
 
1607
1608
  const core = createCore({ sendMessage });
1608
- (core as any).agentCardValue = {
1609
- capabilities: { streaming: false },
1609
+ (core as any).state = {
1610
+ ...(core as any).state,
1611
+ agentCard: { capabilities: { streaming: false } },
1610
1612
  };
1611
1613
 
1612
1614
  await core.append(createUserAppendMessage("Hello"));
@@ -1763,7 +1765,7 @@ describe("A2AThreadRuntimeCore", () => {
1763
1765
  it("calls onArtifactComplete when lastChunk=true", async () => {
1764
1766
  const onArtifactComplete = vi.fn();
1765
1767
 
1766
- const core = new A2AThreadRuntimeCore({
1768
+ const core = new A2AThreadController({
1767
1769
  client: createMockClient({
1768
1770
  streamMessage: vi.fn().mockImplementation(async function* () {
1769
1771
  yield artifactUpdateEvent("a1", [{ text: "code" }], {
@@ -1781,6 +1783,32 @@ describe("A2AThreadRuntimeCore", () => {
1781
1783
  expect(onArtifactComplete.mock.calls[0]![0].artifactId).toBe("a1");
1782
1784
  });
1783
1785
 
1786
+ it("passes the completed artifact even if a subscriber clears the thread", async () => {
1787
+ const onArtifactComplete = vi.fn();
1788
+
1789
+ const core = new A2AThreadController({
1790
+ client: createMockClient({
1791
+ streamMessage: vi.fn().mockImplementation(async function* () {
1792
+ yield artifactUpdateEvent("a1", [{ text: "code" }], {
1793
+ lastChunk: true,
1794
+ });
1795
+ }),
1796
+ }),
1797
+ onArtifactComplete,
1798
+ });
1799
+ let cleared = false;
1800
+ core.subscribe(() => {
1801
+ if (cleared || core.getArtifacts().length === 0) return;
1802
+ cleared = true;
1803
+ core.applyExternalMessages([]);
1804
+ });
1805
+
1806
+ await core.append(createUserAppendMessage("Go"));
1807
+
1808
+ expect(onArtifactComplete).toHaveBeenCalledTimes(1);
1809
+ expect(onArtifactComplete.mock.calls[0]![0]?.artifactId).toBe("a1");
1810
+ });
1811
+
1784
1812
  it.each(["throws", "rejects"] as const)(
1785
1813
  "continues streaming when onArtifactComplete %s",
1786
1814
  async (failureMode) => {
@@ -2377,7 +2405,7 @@ describe("A2AThreadRuntimeCore", () => {
2377
2405
  signal.addEventListener("abort", () => resolve(), { once: true });
2378
2406
  });
2379
2407
  });
2380
- let core!: A2AThreadRuntimeCore;
2408
+ let core!: A2AThreadController;
2381
2409
  let replacementRun: Promise<void> | undefined;
2382
2410
  let startReplacement = true;
2383
2411
  core = createCore(
@@ -2428,9 +2456,9 @@ describe("A2AThreadRuntimeCore", () => {
2428
2456
  const core = createCore({ cancelTask });
2429
2457
 
2430
2458
  // Manually set task state to simulate a running task
2431
- (core as any).currentTask = {
2432
- id: "t1",
2433
- status: { state: "working" },
2459
+ (core as any).state = {
2460
+ ...(core as any).state,
2461
+ currentTask: { id: "t1", status: { state: "working" } },
2434
2462
  };
2435
2463
  (core as any).abortController = new AbortController();
2436
2464
  onUpdate.mockClear();
@@ -2556,7 +2584,7 @@ describe("A2AThreadRuntimeCore", () => {
2556
2584
  });
2557
2585
  });
2558
2586
  let restarted = false;
2559
- let core!: A2AThreadRuntimeCore;
2587
+ let core!: A2AThreadController;
2560
2588
  core = createCore(
2561
2589
  { streamMessage },
2562
2590
  {
@@ -2621,7 +2649,7 @@ describe("A2AThreadRuntimeCore", () => {
2621
2649
  signal.addEventListener("abort", () => resolve(), { once: true });
2622
2650
  });
2623
2651
  });
2624
- let core!: A2AThreadRuntimeCore;
2652
+ let core!: A2AThreadController;
2625
2653
  core = createCore(
2626
2654
  { cancelTask, streamMessage },
2627
2655
  { onCancel: () => core.applyExternalMessages([]) },
@@ -2704,7 +2732,7 @@ describe("A2AThreadRuntimeCore", () => {
2704
2732
  it("sets error status and re-throws on stream failure", async () => {
2705
2733
  const onError = vi.fn();
2706
2734
 
2707
- const core = new A2AThreadRuntimeCore({
2735
+ const core = new A2AThreadController({
2708
2736
  client: createMockClient({
2709
2737
  streamMessage: vi.fn().mockImplementation(() => ({
2710
2738
  async next() {
@@ -2959,7 +2987,7 @@ describe("outbound message conversion", () => {
2959
2987
  const streamMessage = vi.fn().mockImplementation(async function* () {
2960
2988
  yield statusUpdateEvent("completed", "Done");
2961
2989
  });
2962
- const core = new A2AThreadRuntimeCore({
2990
+ const core = new A2AThreadController({
2963
2991
  client: createMockClient({ streamMessage }),
2964
2992
  });
2965
2993
  return { core, streamMessage };