@assistant-ui/react-a2a 0.2.35 → 0.2.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/A2AClient.d.ts +3 -4
- package/dist/A2AClient.d.ts.map +1 -1
- package/dist/A2AThreadRuntimeCore.d.ts +4 -3
- package/dist/A2AThreadRuntimeCore.d.ts.map +1 -1
- package/dist/A2AThreadRuntimeCore.js +14 -3
- package/dist/A2AThreadRuntimeCore.js.map +1 -1
- package/dist/a2aExtras.d.ts +1 -2
- package/dist/a2aExtras.d.ts.map +1 -1
- package/dist/conversions.d.ts +8 -9
- package/dist/conversions.d.ts.map +1 -1
- package/dist/hooks.d.ts +3 -4
- package/dist/hooks.d.ts.map +1 -1
- package/dist/types.d.ts +39 -40
- package/dist/types.d.ts.map +1 -1
- package/dist/useA2ARuntime.d.ts +1 -2
- package/dist/useA2ARuntime.d.ts.map +1 -1
- package/package.json +11 -10
- package/src/A2AThreadRuntimeCore.test.ts +247 -1
- package/src/A2AThreadRuntimeCore.ts +25 -3
- package/src/useA2ARuntime.test.tsx +91 -1
package/dist/A2AClient.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { A2AAgentCard, A2AErrorInfo, A2AListTaskPushNotificationConfigsResponse, A2AListTasksRequest, A2AListTasksResponse, A2AMessage, A2ASendMessageConfiguration, A2AStreamEvent, A2ATask, A2ATaskPushNotificationConfig } from "./types.js";
|
|
2
2
|
//#region src/A2AClient.d.ts
|
|
3
|
-
type A2AClientOptions = {
|
|
3
|
+
export type A2AClientOptions = {
|
|
4
4
|
baseUrl: string;
|
|
5
5
|
/** Optional path prefix for all API endpoints (e.g. "/v1"). Does not affect agent card discovery. */
|
|
6
6
|
basePath?: string | undefined;
|
|
@@ -12,13 +12,13 @@ type A2AClientOptions = {
|
|
|
12
12
|
/** Extra fetch options applied to every request. */
|
|
13
13
|
fetchOptions?: Omit<RequestInit, "headers" | "body" | "method" | "signal"> | undefined;
|
|
14
14
|
};
|
|
15
|
-
declare class A2AError extends Error {
|
|
15
|
+
export declare class A2AError extends Error {
|
|
16
16
|
code: number;
|
|
17
17
|
status: string;
|
|
18
18
|
details: unknown[] | undefined;
|
|
19
19
|
constructor(info: A2AErrorInfo);
|
|
20
20
|
}
|
|
21
|
-
declare class A2AClient {
|
|
21
|
+
export declare class A2AClient {
|
|
22
22
|
private baseUrl;
|
|
23
23
|
private basePath;
|
|
24
24
|
private tenant;
|
|
@@ -48,5 +48,4 @@ declare class A2AClient {
|
|
|
48
48
|
private parseSSE;
|
|
49
49
|
}
|
|
50
50
|
//#endregion
|
|
51
|
-
export { A2AClient, A2AClientOptions, A2AError };
|
|
52
51
|
//# sourceMappingURL=A2AClient.d.ts.map
|
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":";;YAmBY;EACV;;EAEA;;EAEA;EACA,UACI,gCACO,yBAAyB,QAAQ;;EAG5C;;EAEA,eACI,KAAK;;qBAIE,iBAAiB;EAC5B;EACA;EACA;EAEY,YAAA,MAAM;;qBA2oBP;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"}
|
|
@@ -2,7 +2,7 @@ import { A2AAgentCard, A2AArtifact, A2ASendMessageConfiguration, A2ATask } from
|
|
|
2
2
|
import { A2AClient } from "./A2AClient.js";
|
|
3
3
|
import { AppendMessage, AssistantRuntime, ExportedMessageRepository, ThreadHistoryAdapter, ThreadMessage } from "@assistant-ui/core";
|
|
4
4
|
//#region src/A2AThreadRuntimeCore.d.ts
|
|
5
|
-
type A2AThreadRuntimeCoreOptions = {
|
|
5
|
+
export type A2AThreadRuntimeCoreOptions = {
|
|
6
6
|
client: A2AClient;
|
|
7
7
|
contextId?: string | undefined;
|
|
8
8
|
configuration?: A2ASendMessageConfiguration | undefined;
|
|
@@ -12,7 +12,7 @@ type A2AThreadRuntimeCoreOptions = {
|
|
|
12
12
|
history?: ThreadHistoryAdapter | undefined;
|
|
13
13
|
notifyUpdate: () => void;
|
|
14
14
|
};
|
|
15
|
-
declare class A2AThreadRuntimeCore {
|
|
15
|
+
export declare class A2AThreadRuntimeCore {
|
|
16
16
|
private client;
|
|
17
17
|
private contextId;
|
|
18
18
|
private configuration;
|
|
@@ -25,6 +25,7 @@ declare class A2AThreadRuntimeCore {
|
|
|
25
25
|
private readonly session;
|
|
26
26
|
private isRunningFlag;
|
|
27
27
|
private abortController;
|
|
28
|
+
private runGeneration;
|
|
28
29
|
private pendingError;
|
|
29
30
|
private currentTask;
|
|
30
31
|
private currentArtifacts;
|
|
@@ -33,6 +34,7 @@ declare class A2AThreadRuntimeCore {
|
|
|
33
34
|
private readonly recordedHistoryIds;
|
|
34
35
|
private _isLoading;
|
|
35
36
|
private _loadPromise;
|
|
37
|
+
private _historyLoadGeneration;
|
|
36
38
|
private _loadRequested;
|
|
37
39
|
private _agentCardPromise;
|
|
38
40
|
private _agentCardRetryAfter;
|
|
@@ -86,5 +88,4 @@ declare class A2AThreadRuntimeCore {
|
|
|
86
88
|
private appendHistoryItem;
|
|
87
89
|
}
|
|
88
90
|
//#endregion
|
|
89
|
-
export { A2AThreadRuntimeCore, A2AThreadRuntimeCoreOptions };
|
|
90
91
|
//# sourceMappingURL=A2AThreadRuntimeCore.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"A2AThreadRuntimeCore.d.ts","names":[],"sources":["../src/A2AThreadRuntimeCore.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"A2AThreadRuntimeCore.d.ts","names":[],"sources":["../src/A2AThreadRuntimeCore.ts"],"mappings":";;;;YAsCY;EACV,QAAQ;EACR;EACA,gBAAgB;EAChB,YAAY,OAAO;EACnB;EACA,uBAAuB,UAAU;EACjC,UAAU;EACV;;qBAyBW;UACH;UACA;UACA;UACA;UACA;UACA;UACA;mBACS;UAET;mBACS;UACT;UACA;UACA;UACA;UAGA;UACA;UACA;mBAGS;mBACA;UACT;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UAEA;EAEI,YAAA,SAAS;EAYrB,cAAc,SAAS,KAAK;;;EA8B5B;EAgBA,cAAc,SAAS;EAIvB;EASA,cAAc;EAId,wBAAwB;EAIxB,wBAAwB;EAIxB,WAAW;EAIX,yBAAyB;EAIzB,gBAAgB;EAIhB;MAII;UAII;UAwBM;EAoBd,mBAAmB;EAsCb,OAAO,SAAS,gBAAgB;EAuBhC,KAAK,SAAS,gBAAgB;EAI9B,OACJ,yBACA;IAAW,YAAY;MACtB;EAaG,UAAU;UA6BR;UAgBA;EAWR,sBAAsB,mBAAmB;UAmC3B;UAiFA;UA4CA;UA4BN;UAiBA;UA6BA;UA0CA;UAQA;UAuCA;UAsBA;UAUA;UAaA;UAQA;UAKA;UAQA;UAIA;UAQA;UAeA"}
|
|
@@ -31,6 +31,7 @@ var A2AThreadRuntimeCore = class {
|
|
|
31
31
|
session = createMessageRepositorySession();
|
|
32
32
|
isRunningFlag = false;
|
|
33
33
|
abortController = null;
|
|
34
|
+
runGeneration = 0;
|
|
34
35
|
pendingError = null;
|
|
35
36
|
currentTask;
|
|
36
37
|
currentArtifacts = [];
|
|
@@ -39,6 +40,7 @@ var A2AThreadRuntimeCore = class {
|
|
|
39
40
|
recordedHistoryIds = /* @__PURE__ */ new Set();
|
|
40
41
|
_isLoading = false;
|
|
41
42
|
_loadPromise;
|
|
43
|
+
_historyLoadGeneration = 0;
|
|
42
44
|
_loadRequested = false;
|
|
43
45
|
_agentCardPromise;
|
|
44
46
|
_agentCardRetryAfter = 0;
|
|
@@ -73,6 +75,8 @@ var A2AThreadRuntimeCore = class {
|
|
|
73
75
|
/** Thread-boundary reset: applyExternalMessages alone also serves branch
|
|
74
76
|
* switches, deletes, and cancel resyncs, which must keep the live context. */
|
|
75
77
|
resetContext() {
|
|
78
|
+
this._historyLoadGeneration++;
|
|
79
|
+
this._isLoading = false;
|
|
76
80
|
const controller = this.abortController;
|
|
77
81
|
this.contextId = this.lastOptionsContextId;
|
|
78
82
|
if (controller) {
|
|
@@ -153,15 +157,19 @@ var A2AThreadRuntimeCore = class {
|
|
|
153
157
|
if (this._loadPromise) return this._loadPromise;
|
|
154
158
|
if (!this.history) return agentCardPromise;
|
|
155
159
|
this._isLoading = true;
|
|
160
|
+
const generation = this._historyLoadGeneration;
|
|
156
161
|
const historyPromise = this.history.load();
|
|
157
162
|
this._loadPromise = Promise.all([historyPromise, agentCardPromise]).then(([repo]) => {
|
|
163
|
+
if (generation !== this._historyLoadGeneration) return;
|
|
158
164
|
if (repo) {
|
|
159
165
|
this.session.applyExternalMessageRepository(repo);
|
|
160
166
|
this.finalizeExternalApply();
|
|
161
167
|
}
|
|
162
168
|
}).catch((error) => {
|
|
169
|
+
if (generation !== this._historyLoadGeneration) return;
|
|
163
170
|
invokeRuntimeCallback("onError", this.onError, error instanceof Error ? error : new Error(String(error)));
|
|
164
171
|
}).finally(() => {
|
|
172
|
+
if (generation !== this._historyLoadGeneration) return;
|
|
165
173
|
this._isLoading = false;
|
|
166
174
|
this.notifyUpdate();
|
|
167
175
|
});
|
|
@@ -191,10 +199,12 @@ var A2AThreadRuntimeCore = class {
|
|
|
191
199
|
}
|
|
192
200
|
async cancel() {
|
|
193
201
|
if (!this.abortController) return;
|
|
202
|
+
const task = this.currentTask;
|
|
203
|
+
const generation = this.runGeneration;
|
|
194
204
|
this.abortController.abort();
|
|
195
|
-
if (
|
|
196
|
-
const updated = await this.client.cancelTask(
|
|
197
|
-
this.currentTask = updated;
|
|
205
|
+
if (task?.id) try {
|
|
206
|
+
const updated = await this.client.cancelTask(task.id);
|
|
207
|
+
if (this.currentTask === task && this.runGeneration === generation) this.currentTask = updated;
|
|
198
208
|
} catch {}
|
|
199
209
|
}
|
|
200
210
|
appendLinearChain(messages) {
|
|
@@ -246,6 +256,7 @@ var A2AThreadRuntimeCore = class {
|
|
|
246
256
|
this.finalizeExternalApply();
|
|
247
257
|
}
|
|
248
258
|
async startRun(userThreadMessage) {
|
|
259
|
+
this.runGeneration++;
|
|
249
260
|
if (this.abortController) {
|
|
250
261
|
this.abortController.abort();
|
|
251
262
|
this.abortController = null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"A2AThreadRuntimeCore.js","names":[],"sources":["../src/A2AThreadRuntimeCore.ts"],"sourcesContent":["\"use client\";\n\nimport { generateId, fromThreadMessageLike } from \"@assistant-ui/core\";\nimport type {\n AppendMessage,\n AssistantRuntime,\n ExportedMessageRepository,\n MessageStatus,\n ThreadAssistantMessage,\n ThreadHistoryAdapter,\n ThreadMessage,\n} from \"@assistant-ui/core\";\nimport {\n createMessageRepositorySession,\n invokeUserCallback,\n} from \"@assistant-ui/core/internal\";\nimport type { A2AClient } from \"./A2AClient\";\nimport type {\n A2AArtifact,\n A2AAgentCard,\n A2AMessage,\n A2ASendMessageConfiguration,\n A2AStreamEvent,\n A2ATask,\n A2ATaskArtifactUpdateEvent,\n A2ATaskStatusUpdateEvent,\n} from \"./types\";\n\nimport {\n a2aMessageToContent,\n isTerminalTaskState,\n threadMessageToA2AMessage,\n taskStateToMessageStatus,\n} from \"./conversions\";\n\nconst INITIAL_AGENT_CARD_RETRY_DELAY_MS = 5_000;\nconst MAX_AGENT_CARD_RETRY_DELAY_MS = 5 * 60_000;\n\nexport type A2AThreadRuntimeCoreOptions = {\n client: A2AClient;\n contextId?: string | undefined;\n configuration?: A2ASendMessageConfiguration | undefined;\n onError?: ((error: Error) => void) | undefined;\n onCancel?: (() => void) | undefined;\n onArtifactComplete?: ((artifact: A2AArtifact) => void) | undefined;\n history?: ThreadHistoryAdapter | undefined;\n notifyUpdate: () => void;\n};\n\nconst FALLBACK_USER_STATUS = {\n type: \"complete\",\n reason: \"unknown\",\n} as const;\n\ntype A2ARuntimeCallbackName = \"onError\" | \"onCancel\" | \"onArtifactComplete\";\n\nconst invokeRuntimeCallback = <TArgs extends readonly unknown[]>(\n name: A2ARuntimeCallbackName,\n callback: ((...args: TArgs) => unknown) | undefined,\n ...args: TArgs\n): void => {\n void invokeUserCallback(\"react-a2a\", name, callback, ...args);\n};\n\nfunction normalizeArtifact(artifact: A2AArtifact): A2AArtifact {\n return {\n ...artifact,\n parts: Array.isArray(artifact.parts) ? artifact.parts : [],\n };\n}\n\nexport class A2AThreadRuntimeCore {\n private client: A2AClient;\n private contextId: string | undefined;\n private configuration: A2ASendMessageConfiguration | undefined;\n private onError: ((error: Error) => void) | undefined;\n private onCancel: (() => void) | undefined;\n private onArtifactComplete: ((artifact: A2AArtifact) => void) | undefined;\n private history: ThreadHistoryAdapter | undefined;\n private readonly notifyUpdate: () => void;\n\n private runtime: AssistantRuntime | undefined;\n private readonly session = createMessageRepositorySession();\n private isRunningFlag = false;\n private abortController: AbortController | null = null;\n private pendingError: Error | null = null;\n\n // A2A-specific state\n private currentTask: A2ATask | undefined;\n private currentArtifacts: A2AArtifact[] = [];\n private agentCardValue: A2AAgentCard | undefined;\n\n // History tracking\n private readonly assistantHistoryParents = new Map<string, string | null>();\n private readonly recordedHistoryIds = new Set<string>();\n private _isLoading = false;\n private _loadPromise: Promise<void> | undefined;\n private _loadRequested = false;\n private _agentCardPromise: Promise<void> | undefined;\n private _agentCardRetryAfter = 0;\n private _agentCardRetryDelay = INITIAL_AGENT_CARD_RETRY_DELAY_MS;\n private _agentCardDiscoveryFailed = false;\n\n private lastOptionsContextId: string | undefined;\n\n constructor(options: A2AThreadRuntimeCoreOptions) {\n this.client = options.client;\n this.contextId = options.contextId;\n this.lastOptionsContextId = options.contextId;\n this.configuration = options.configuration;\n this.onError = options.onError;\n this.onCancel = options.onCancel;\n this.onArtifactComplete = options.onArtifactComplete;\n this.history = options.history;\n this.notifyUpdate = options.notifyUpdate;\n }\n\n updateOptions(options: Omit<A2AThreadRuntimeCoreOptions, \"notifyUpdate\">) {\n this.client = options.client;\n // The hook re-applies options on every render, including renders caused\n // by this core's own notifyUpdate. The option only seeds the context: a\n // re-render with the same value must not clobber a server-assigned\n // contextId learned from the stream.\n if (options.contextId !== this.lastOptionsContextId) {\n this.contextId = options.contextId;\n this.lastOptionsContextId = options.contextId;\n }\n this.configuration = options.configuration;\n this.onError = options.onError;\n this.onCancel = options.onCancel;\n this.onArtifactComplete = options.onArtifactComplete;\n const previousHistory = this.history;\n this.history = options.history;\n\n if (\n this._loadRequested &&\n !this._loadPromise &&\n !previousHistory &&\n options.history &&\n this.session.getMessages().length === 0\n ) {\n void this.__internal_load();\n }\n }\n\n /** Thread-boundary reset: applyExternalMessages alone also serves branch\n * switches, deletes, and cancel resyncs, which must keep the live context. */\n resetContext(): void {\n // Restore the seed before aborting: an onCancel callback that starts a\n // new run must not pick up the old thread's context, and its controller\n // must not be discarded.\n const controller = this.abortController;\n this.contextId = this.lastOptionsContextId;\n if (controller) {\n controller.abort();\n if (this.abortController === controller) {\n this.abortController = null;\n }\n }\n }\n\n attachRuntime(runtime: AssistantRuntime) {\n this.runtime = runtime;\n }\n\n detachRuntime() {\n this.runtime = undefined;\n // Abort in-flight requests on unmount\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = null;\n }\n }\n\n getRuntime(): AssistantRuntime | undefined {\n return this.runtime;\n }\n\n getMessages(): readonly ThreadMessage[] {\n return this.session.getMessages();\n }\n\n getMessageRepository(): ExportedMessageRepository {\n return this.session.export();\n }\n\n getTask(): A2ATask | undefined {\n return this.currentTask;\n }\n\n getArtifacts(): readonly A2AArtifact[] {\n return this.currentArtifacts;\n }\n\n getAgentCard(): A2AAgentCard | undefined {\n return this.agentCardValue;\n }\n\n isRunning(): boolean {\n return this.isRunningFlag;\n }\n\n get isLoading(): boolean {\n return this._isLoading;\n }\n\n private loadAgentCard(): Promise<void> {\n if (Date.now() < this._agentCardRetryAfter) return Promise.resolve();\n\n this._agentCardPromise ??= this.client.getAgentCard().then(\n (agentCard) => {\n this.agentCardValue = agentCard;\n this._agentCardRetryAfter = 0;\n this._agentCardRetryDelay = INITIAL_AGENT_CARD_RETRY_DELAY_MS;\n this._agentCardDiscoveryFailed = false;\n this.notifyUpdate();\n },\n () => {\n this._agentCardDiscoveryFailed = true;\n this._agentCardRetryAfter = Date.now() + this._agentCardRetryDelay;\n this._agentCardRetryDelay = Math.min(\n this._agentCardRetryDelay * 2,\n MAX_AGENT_CARD_RETRY_DELAY_MS,\n );\n this._agentCardPromise = undefined;\n },\n );\n return this._agentCardPromise;\n }\n\n private async waitForAgentCard(signal: AbortSignal): Promise<boolean> {\n const shouldWait = !this._agentCardDiscoveryFailed;\n const load = this.loadAgentCard();\n if (signal.aborted) return false;\n if (!shouldWait) return true;\n\n let onAbort!: () => void;\n const abort = new Promise<void>((resolve) => {\n onAbort = resolve;\n signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n\n try {\n await Promise.race([load, abort]);\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n }\n return !signal.aborted;\n }\n\n __internal_load(): Promise<void> {\n this._loadRequested = true;\n const agentCardPromise = this.loadAgentCard();\n\n if (this._loadPromise) return this._loadPromise;\n if (!this.history) return agentCardPromise;\n\n this._isLoading = true;\n\n const historyPromise = this.history.load();\n\n this._loadPromise = Promise.all([historyPromise, agentCardPromise])\n .then(([repo]) => {\n if (repo) {\n this.session.applyExternalMessageRepository(repo);\n this.finalizeExternalApply();\n }\n })\n .catch((error) => {\n invokeRuntimeCallback(\n \"onError\",\n this.onError,\n error instanceof Error ? error : new Error(String(error)),\n );\n })\n .finally(() => {\n this._isLoading = false;\n this.notifyUpdate();\n });\n\n this.notifyUpdate();\n return this._loadPromise;\n }\n\n async append(message: AppendMessage): Promise<void> {\n const startRun = message.startRun ?? message.role === \"user\";\n\n const threadMessage = fromThreadMessageLike(\n message as any,\n generateId(),\n FALLBACK_USER_STATUS,\n );\n const parentId =\n message.parentId === null\n ? null\n : message.parentId && this.session.hasMessage(message.parentId)\n ? message.parentId\n : this.session.headId;\n this.session.addOrUpdateMessage(parentId, threadMessage);\n this.session.switchToBranch(threadMessage.id);\n this.notifyUpdate();\n this.recordHistoryEntry(parentId, threadMessage);\n\n if (!startRun) return;\n await this.startRun(threadMessage);\n }\n\n async edit(message: AppendMessage): Promise<void> {\n await this.append(message);\n }\n\n async reload(\n parentId: string | null,\n _config: { runConfig?: Record<string, unknown> } = {},\n ): Promise<void> {\n const messages =\n parentId === null\n ? []\n : (this.session.tryGetMessages(parentId) ?? this.getMessages());\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i]!.role === \"user\") {\n await this.startRun(messages[i]!);\n return;\n }\n }\n }\n\n async cancel(): Promise<void> {\n if (!this.abortController) return;\n\n // Abort locally first so the stream stops immediately\n this.abortController.abort();\n\n // Then try to cancel the task on the server\n if (this.currentTask?.id) {\n try {\n const updated = await this.client.cancelTask(this.currentTask.id);\n this.currentTask = updated;\n } catch {\n // Server cancel failed; local abort already handled\n }\n }\n }\n\n private appendLinearChain(messages: readonly ThreadMessage[]): string | null {\n let parentId: string | null = null;\n let lastId: string | null = null;\n const seen = new Set<string>();\n\n for (const message of messages) {\n if (seen.has(message.id)) continue;\n seen.add(message.id);\n this.session.addOrUpdateMessage(parentId, message);\n parentId = message.id;\n lastId = message.id;\n }\n\n return lastId;\n }\n\n private finalizeExternalApply(): void {\n this.assistantHistoryParents.clear();\n this.recordedHistoryIds.clear();\n for (const { message } of this.getMessageRepository().messages) {\n this.recordedHistoryIds.add(message.id);\n }\n this.currentTask = undefined;\n this.currentArtifacts = [];\n this.notifyUpdate();\n }\n\n applyExternalMessages(messages: readonly ThreadMessage[]): void {\n if (messages.length === 0) {\n this.session.clear();\n } else {\n let expectedParentId: string | null = null;\n let lastAppliedId: string | null = null;\n let hardReplace = false;\n const seen = new Set<string>();\n\n for (const message of messages) {\n if (seen.has(message.id)) continue;\n seen.add(message.id);\n const existing = this.session.tryGetMessage(message.id);\n if (existing && existing.parentId !== expectedParentId) {\n hardReplace = true;\n break;\n }\n this.session.addOrUpdateMessage(expectedParentId, message);\n expectedParentId = message.id;\n lastAppliedId = message.id;\n }\n\n if (hardReplace) {\n this.session.clear();\n lastAppliedId = this.appendLinearChain(messages);\n }\n\n this.session.resetHead(lastAppliedId);\n }\n\n this.finalizeExternalApply();\n }\n\n // --- Run logic ---\n\n private async startRun(userThreadMessage: ThreadMessage): Promise<void> {\n // Cancel any in-progress run before starting a new one\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = null;\n }\n\n const a2aMessage = threadMessageToA2AMessage(userThreadMessage, {\n contextId: this.contextId,\n taskId:\n this.currentTask?.id &&\n !isTerminalTaskState(this.currentTask.status.state)\n ? this.currentTask.id\n : undefined,\n });\n\n // Clear task if previous task reached terminal state\n if (\n this.currentTask &&\n isTerminalTaskState(this.currentTask.status.state)\n ) {\n this.currentTask = undefined;\n }\n\n this.currentArtifacts = [];\n\n const assistantParentId = userThreadMessage.id;\n const assistantId = this.insertAssistantPlaceholder(assistantParentId);\n this.markPendingAssistantHistory(assistantId, assistantParentId);\n\n const abortController = new AbortController();\n this.abortController = abortController;\n\n abortController.signal.addEventListener(\n \"abort\",\n () => {\n this.updateAssistantStatus(assistantId, {\n type: \"incomplete\",\n reason: \"cancelled\",\n });\n this.finishRun(abortController);\n invokeRuntimeCallback(\"onCancel\", this.onCancel);\n },\n { once: true },\n );\n\n this.setRunning(true);\n\n try {\n if (!(await this.waitForAgentCard(abortController.signal))) return;\n\n const supportsStreaming =\n this.agentCardValue?.capabilities?.streaming !== false;\n if (supportsStreaming) {\n await this.runStreaming(a2aMessage, assistantId, abortController);\n } else {\n await this.runSync(a2aMessage, assistantId, abortController);\n }\n } catch (error) {\n if (!abortController.signal.aborted) {\n const err = error instanceof Error ? error : new Error(String(error));\n this.updateAssistantStatus(assistantId, {\n type: \"incomplete\",\n reason: \"error\",\n });\n invokeRuntimeCallback(\"onError\", this.onError, err);\n this.pendingError = this.pendingError ?? err;\n }\n } finally {\n this.finishRun(abortController);\n }\n\n if (this.pendingError) {\n const err = this.pendingError;\n this.pendingError = null;\n throw err;\n }\n }\n\n private async runStreaming(\n a2aMessage: A2AMessage,\n assistantId: string,\n abortController: AbortController,\n ): Promise<void> {\n const stream = this.client.streamMessage(\n a2aMessage,\n this.configuration,\n undefined, // metadata\n abortController.signal,\n );\n\n let receivedEvent = false;\n let skipReason: string | undefined;\n const diagnosedStream = (async function* () {\n const reason = yield* stream;\n if (typeof reason === \"string\") skipReason = reason;\n })();\n\n for await (const event of diagnosedStream) {\n if (abortController.signal.aborted) break;\n receivedEvent = true;\n this.handleStreamEvent(assistantId, event);\n }\n\n if (!abortController.signal.aborted) {\n if (!receivedEvent) {\n throw new Error(\n skipReason\n ? `A2A message stream ended without any events. First skipped frame: ${skipReason}`\n : \"A2A message stream ended without any events.\",\n );\n }\n\n const lastStatus = this.getAssistantStatus(assistantId);\n if (lastStatus?.type === \"running\") {\n this.updateAssistantStatus(assistantId, {\n type: \"complete\",\n reason: \"stop\",\n });\n }\n }\n }\n\n private async runSync(\n a2aMessage: A2AMessage,\n assistantId: string,\n abortController: AbortController,\n ): Promise<void> {\n const result = await this.client.sendMessage(\n a2aMessage,\n this.configuration,\n undefined, // metadata\n abortController.signal,\n );\n\n if (abortController.signal.aborted) return;\n\n // Result is either A2ATask or A2AMessage\n if (\"id\" in result && \"status\" in result) {\n // It's a Task\n this.handleTaskSnapshot(assistantId, result as A2ATask);\n } else if (\"messageId\" in result && \"parts\" in result) {\n // It's a Message\n this.handleMessage(assistantId, result as A2AMessage);\n this.updateAssistantStatus(assistantId, {\n type: \"complete\",\n reason: \"stop\",\n });\n }\n }\n\n private handleStreamEvent(assistantId: string, event: A2AStreamEvent) {\n switch (event.type) {\n case \"statusUpdate\":\n this.handleStatusUpdate(assistantId, event.event);\n break;\n case \"artifactUpdate\":\n this.handleArtifactUpdate(event.event);\n break;\n case \"message\":\n this.handleMessage(assistantId, event.message);\n break;\n case \"task\":\n this.handleTaskSnapshot(assistantId, event.task);\n break;\n }\n }\n\n private handleStatusUpdate(\n assistantId: string,\n event: A2ATaskStatusUpdateEvent,\n ) {\n if (!this.currentTask) {\n this.currentTask = {\n id: event.taskId,\n contextId: event.contextId,\n status: event.status,\n };\n } else {\n this.currentTask = { ...this.currentTask, status: event.status };\n }\n\n if (event.contextId) {\n this.contextId = event.contextId;\n }\n\n if (event.status.message) {\n const content = a2aMessageToContent(event.status.message);\n this.updateAssistantContent(assistantId, content);\n }\n\n const status = taskStateToMessageStatus(event.status.state);\n this.updateAssistantStatus(assistantId, status);\n\n this.notifyUpdate();\n }\n\n private handleArtifactUpdate(event: A2ATaskArtifactUpdateEvent) {\n const { append, lastChunk } = event;\n const artifact = normalizeArtifact(event.artifact);\n const existingIdx = this.currentArtifacts.findIndex(\n (a) => a.artifactId === artifact.artifactId,\n );\n\n let updated: A2AArtifact;\n if (existingIdx >= 0 && append) {\n const existing = this.currentArtifacts[existingIdx]!;\n updated = {\n ...existing,\n parts: [...existing.parts, ...artifact.parts],\n };\n this.currentArtifacts = [\n ...this.currentArtifacts.slice(0, existingIdx),\n updated,\n ...this.currentArtifacts.slice(existingIdx + 1),\n ];\n } else if (existingIdx >= 0) {\n updated = artifact;\n this.currentArtifacts = [\n ...this.currentArtifacts.slice(0, existingIdx),\n updated,\n ...this.currentArtifacts.slice(existingIdx + 1),\n ];\n } else {\n updated = artifact;\n this.currentArtifacts = [...this.currentArtifacts, updated];\n }\n\n if (lastChunk) {\n invokeRuntimeCallback(\n \"onArtifactComplete\",\n this.onArtifactComplete,\n updated,\n );\n }\n\n this.notifyUpdate();\n }\n\n private handleMessage(assistantId: string, message: A2AMessage) {\n if (message.role !== \"agent\") return;\n\n const content = a2aMessageToContent(message);\n this.updateAssistantContent(assistantId, content);\n this.notifyUpdate();\n }\n\n private handleTaskSnapshot(assistantId: string, task: A2ATask) {\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 this.currentTask = {\n ...task,\n ...(artifacts === undefined ? {} : { artifacts }),\n ...(history === undefined ? {} : { history }),\n };\n\n if (task.contextId) {\n this.contextId = task.contextId;\n }\n if (artifacts) {\n this.currentArtifacts = artifacts;\n }\n\n if (task.status.message) {\n const content = a2aMessageToContent(task.status.message);\n this.updateAssistantContent(assistantId, content);\n }\n\n const status = taskStateToMessageStatus(task.status.state);\n this.updateAssistantStatus(assistantId, status);\n\n this.notifyUpdate();\n }\n\n // --- Message helpers ---\n\n private insertAssistantPlaceholder(parentId: string): string {\n const id = generateId();\n const assistant: ThreadAssistantMessage = {\n id,\n role: \"assistant\",\n createdAt: new Date(),\n status: { type: \"running\" },\n content: [],\n metadata: {\n unstable_state: null,\n unstable_annotations: [],\n unstable_data: [],\n steps: [],\n custom: {},\n },\n };\n this.session.addOrUpdateMessage(parentId, assistant);\n this.session.switchToBranch(id);\n this.notifyUpdate();\n return id;\n }\n\n private updateAssistantContent(\n messageId: string,\n content: ThreadAssistantMessage[\"content\"],\n ) {\n this.session.updateMessage(messageId, (message) => {\n if (message.role !== \"assistant\") return message;\n return { ...message, content };\n });\n }\n\n private updateAssistantStatus(messageId: string, status: MessageStatus) {\n const touched = this.session.updateMessage(messageId, (message) => {\n if (message.role !== \"assistant\") return message;\n return { ...message, status };\n });\n if (touched) {\n this.notifyUpdate();\n if (status.type === \"complete\" || status.type === \"incomplete\") {\n this.persistAssistantHistory(messageId);\n }\n }\n }\n\n private getAssistantStatus(messageId: string): MessageStatus | undefined {\n const msg = this.session.tryGetMessage(messageId)?.message;\n if (msg?.role !== \"assistant\") return undefined;\n return msg.status;\n }\n\n // --- Lifecycle helpers ---\n\n private setRunning(running: boolean) {\n this.isRunningFlag = running;\n this.notifyUpdate();\n }\n\n private finishRun(controller: AbortController | null) {\n if (this.abortController !== controller) return;\n this.abortController = null;\n this.setRunning(false);\n }\n\n // --- History persistence ---\n\n private recordHistoryEntry(parentId: string | null, message: ThreadMessage) {\n this.appendHistoryItem(parentId, message);\n }\n\n private markPendingAssistantHistory(\n messageId: string,\n parentId: string | null,\n ) {\n if (!this.history) return;\n this.assistantHistoryParents.set(messageId, parentId);\n }\n\n private persistAssistantHistory(messageId: string) {\n if (!this.history) return;\n const parentId = this.assistantHistoryParents.get(messageId);\n if (parentId === undefined) return;\n const message = this.session.tryGetMessage(messageId)?.message;\n if (!message || message.role !== \"assistant\") return;\n if (\n message.status?.type !== \"complete\" &&\n message.status?.type !== \"incomplete\"\n )\n return;\n this.assistantHistoryParents.delete(messageId);\n this.appendHistoryItem(parentId, message);\n }\n\n private appendHistoryItem(parentId: string | null, message: ThreadMessage) {\n if (!this.history || this.recordedHistoryIds.has(message.id)) return;\n this.recordedHistoryIds.add(message.id);\n void this.history.append({ parentId, message }).catch(() => {\n this.recordedHistoryIds.delete(message.id);\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"A2AThreadRuntimeCore.js","names":[],"sources":["../src/A2AThreadRuntimeCore.ts"],"sourcesContent":["\"use client\";\n\nimport { generateId, fromThreadMessageLike } from \"@assistant-ui/core\";\nimport type {\n AppendMessage,\n AssistantRuntime,\n ExportedMessageRepository,\n MessageStatus,\n ThreadAssistantMessage,\n ThreadHistoryAdapter,\n ThreadMessage,\n} from \"@assistant-ui/core\";\nimport {\n createMessageRepositorySession,\n invokeUserCallback,\n} from \"@assistant-ui/core/internal\";\nimport type { A2AClient } from \"./A2AClient\";\nimport type {\n A2AArtifact,\n A2AAgentCard,\n A2AMessage,\n A2ASendMessageConfiguration,\n A2AStreamEvent,\n A2ATask,\n A2ATaskArtifactUpdateEvent,\n A2ATaskStatusUpdateEvent,\n} from \"./types\";\n\nimport {\n a2aMessageToContent,\n isTerminalTaskState,\n threadMessageToA2AMessage,\n taskStateToMessageStatus,\n} from \"./conversions\";\n\nconst INITIAL_AGENT_CARD_RETRY_DELAY_MS = 5_000;\nconst MAX_AGENT_CARD_RETRY_DELAY_MS = 5 * 60_000;\n\nexport type A2AThreadRuntimeCoreOptions = {\n client: A2AClient;\n contextId?: string | undefined;\n configuration?: A2ASendMessageConfiguration | undefined;\n onError?: ((error: Error) => void) | undefined;\n onCancel?: (() => void) | undefined;\n onArtifactComplete?: ((artifact: A2AArtifact) => void) | undefined;\n history?: ThreadHistoryAdapter | undefined;\n notifyUpdate: () => void;\n};\n\nconst FALLBACK_USER_STATUS = {\n type: \"complete\",\n reason: \"unknown\",\n} as const;\n\ntype A2ARuntimeCallbackName = \"onError\" | \"onCancel\" | \"onArtifactComplete\";\n\nconst invokeRuntimeCallback = <TArgs extends readonly unknown[]>(\n name: A2ARuntimeCallbackName,\n callback: ((...args: TArgs) => unknown) | undefined,\n ...args: TArgs\n): void => {\n void invokeUserCallback(\"react-a2a\", name, callback, ...args);\n};\n\nfunction normalizeArtifact(artifact: A2AArtifact): A2AArtifact {\n return {\n ...artifact,\n parts: Array.isArray(artifact.parts) ? artifact.parts : [],\n };\n}\n\nexport class A2AThreadRuntimeCore {\n private client: A2AClient;\n private contextId: string | undefined;\n private configuration: A2ASendMessageConfiguration | undefined;\n private onError: ((error: Error) => void) | undefined;\n private onCancel: (() => void) | undefined;\n private onArtifactComplete: ((artifact: A2AArtifact) => void) | undefined;\n private history: ThreadHistoryAdapter | undefined;\n private readonly notifyUpdate: () => void;\n\n private runtime: AssistantRuntime | undefined;\n private readonly session = createMessageRepositorySession();\n private isRunningFlag = false;\n private abortController: AbortController | null = null;\n private runGeneration = 0;\n private pendingError: Error | null = null;\n\n // A2A-specific state\n private currentTask: A2ATask | undefined;\n private currentArtifacts: A2AArtifact[] = [];\n private agentCardValue: A2AAgentCard | undefined;\n\n // History tracking\n private readonly assistantHistoryParents = new Map<string, string | null>();\n private readonly recordedHistoryIds = new Set<string>();\n private _isLoading = false;\n private _loadPromise: Promise<void> | undefined;\n private _historyLoadGeneration = 0;\n private _loadRequested = false;\n private _agentCardPromise: Promise<void> | undefined;\n private _agentCardRetryAfter = 0;\n private _agentCardRetryDelay = INITIAL_AGENT_CARD_RETRY_DELAY_MS;\n private _agentCardDiscoveryFailed = false;\n\n private lastOptionsContextId: string | undefined;\n\n constructor(options: A2AThreadRuntimeCoreOptions) {\n this.client = options.client;\n this.contextId = options.contextId;\n this.lastOptionsContextId = options.contextId;\n this.configuration = options.configuration;\n this.onError = options.onError;\n this.onCancel = options.onCancel;\n this.onArtifactComplete = options.onArtifactComplete;\n this.history = options.history;\n this.notifyUpdate = options.notifyUpdate;\n }\n\n updateOptions(options: Omit<A2AThreadRuntimeCoreOptions, \"notifyUpdate\">) {\n this.client = options.client;\n // The hook re-applies options on every render, including renders caused\n // by this core's own notifyUpdate. The option only seeds the context: a\n // re-render with the same value must not clobber a server-assigned\n // contextId learned from the stream.\n if (options.contextId !== this.lastOptionsContextId) {\n this.contextId = options.contextId;\n this.lastOptionsContextId = options.contextId;\n }\n this.configuration = options.configuration;\n this.onError = options.onError;\n this.onCancel = options.onCancel;\n this.onArtifactComplete = options.onArtifactComplete;\n const previousHistory = this.history;\n this.history = options.history;\n\n if (\n this._loadRequested &&\n !this._loadPromise &&\n !previousHistory &&\n options.history &&\n this.session.getMessages().length === 0\n ) {\n void this.__internal_load();\n }\n }\n\n /** Thread-boundary reset: applyExternalMessages alone also serves branch\n * switches, deletes, and cancel resyncs, which must keep the live context. */\n resetContext(): void {\n this._historyLoadGeneration++;\n this._isLoading = false;\n // Restore the seed before aborting: an onCancel callback that starts a\n // new run must not pick up the old thread's context, and its controller\n // must not be discarded.\n const controller = this.abortController;\n this.contextId = this.lastOptionsContextId;\n if (controller) {\n controller.abort();\n if (this.abortController === controller) {\n this.abortController = null;\n }\n }\n }\n\n attachRuntime(runtime: AssistantRuntime) {\n this.runtime = runtime;\n }\n\n detachRuntime() {\n this.runtime = undefined;\n // Abort in-flight requests on unmount\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = null;\n }\n }\n\n getRuntime(): AssistantRuntime | undefined {\n return this.runtime;\n }\n\n getMessages(): readonly ThreadMessage[] {\n return this.session.getMessages();\n }\n\n getMessageRepository(): ExportedMessageRepository {\n return this.session.export();\n }\n\n getTask(): A2ATask | undefined {\n return this.currentTask;\n }\n\n getArtifacts(): readonly A2AArtifact[] {\n return this.currentArtifacts;\n }\n\n getAgentCard(): A2AAgentCard | undefined {\n return this.agentCardValue;\n }\n\n isRunning(): boolean {\n return this.isRunningFlag;\n }\n\n get isLoading(): boolean {\n return this._isLoading;\n }\n\n private loadAgentCard(): Promise<void> {\n if (Date.now() < this._agentCardRetryAfter) return Promise.resolve();\n\n this._agentCardPromise ??= this.client.getAgentCard().then(\n (agentCard) => {\n this.agentCardValue = agentCard;\n this._agentCardRetryAfter = 0;\n this._agentCardRetryDelay = INITIAL_AGENT_CARD_RETRY_DELAY_MS;\n this._agentCardDiscoveryFailed = false;\n this.notifyUpdate();\n },\n () => {\n this._agentCardDiscoveryFailed = true;\n this._agentCardRetryAfter = Date.now() + this._agentCardRetryDelay;\n this._agentCardRetryDelay = Math.min(\n this._agentCardRetryDelay * 2,\n MAX_AGENT_CARD_RETRY_DELAY_MS,\n );\n this._agentCardPromise = undefined;\n },\n );\n return this._agentCardPromise;\n }\n\n private async waitForAgentCard(signal: AbortSignal): Promise<boolean> {\n const shouldWait = !this._agentCardDiscoveryFailed;\n const load = this.loadAgentCard();\n if (signal.aborted) return false;\n if (!shouldWait) return true;\n\n let onAbort!: () => void;\n const abort = new Promise<void>((resolve) => {\n onAbort = resolve;\n signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n\n try {\n await Promise.race([load, abort]);\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n }\n return !signal.aborted;\n }\n\n __internal_load(): Promise<void> {\n this._loadRequested = true;\n const agentCardPromise = this.loadAgentCard();\n\n if (this._loadPromise) return this._loadPromise;\n if (!this.history) return agentCardPromise;\n\n this._isLoading = true;\n\n const generation = this._historyLoadGeneration;\n const historyPromise = this.history.load();\n\n this._loadPromise = Promise.all([historyPromise, agentCardPromise])\n .then(([repo]) => {\n if (generation !== this._historyLoadGeneration) return;\n if (repo) {\n this.session.applyExternalMessageRepository(repo);\n this.finalizeExternalApply();\n }\n })\n .catch((error) => {\n if (generation !== this._historyLoadGeneration) return;\n invokeRuntimeCallback(\n \"onError\",\n this.onError,\n error instanceof Error ? error : new Error(String(error)),\n );\n })\n .finally(() => {\n if (generation !== this._historyLoadGeneration) return;\n this._isLoading = false;\n this.notifyUpdate();\n });\n\n this.notifyUpdate();\n return this._loadPromise;\n }\n\n async append(message: AppendMessage): Promise<void> {\n const startRun = message.startRun ?? message.role === \"user\";\n\n const threadMessage = fromThreadMessageLike(\n message as any,\n generateId(),\n FALLBACK_USER_STATUS,\n );\n const parentId =\n message.parentId === null\n ? null\n : message.parentId && this.session.hasMessage(message.parentId)\n ? message.parentId\n : this.session.headId;\n this.session.addOrUpdateMessage(parentId, threadMessage);\n this.session.switchToBranch(threadMessage.id);\n this.notifyUpdate();\n this.recordHistoryEntry(parentId, threadMessage);\n\n if (!startRun) return;\n await this.startRun(threadMessage);\n }\n\n async edit(message: AppendMessage): Promise<void> {\n await this.append(message);\n }\n\n async reload(\n parentId: string | null,\n _config: { runConfig?: Record<string, unknown> } = {},\n ): Promise<void> {\n const messages =\n parentId === null\n ? []\n : (this.session.tryGetMessages(parentId) ?? this.getMessages());\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i]!.role === \"user\") {\n await this.startRun(messages[i]!);\n return;\n }\n }\n }\n\n async cancel(): Promise<void> {\n if (!this.abortController) return;\n\n // Read the server target before aborting: the abort listener runs the\n // onCancel callback synchronously, which may clear the thread and with it\n // the task this cancellation is for, or start a new run.\n const task = this.currentTask;\n const generation = this.runGeneration;\n\n // Abort locally first so the stream stops immediately\n this.abortController.abort();\n\n // Then try to cancel the task on the server\n if (task?.id) {\n try {\n const updated = await this.client.cancelTask(task.id);\n // Only apply the response while nothing newer exists. A newer snapshot\n // or a cleared thread replaces the task object; a follow-up run that\n // has not emitted yet keeps it, so the run generation is what rules\n // that case out.\n if (this.currentTask === task && this.runGeneration === generation) {\n this.currentTask = updated;\n }\n } catch {\n // Server cancel failed; local abort already handled\n }\n }\n }\n\n private appendLinearChain(messages: readonly ThreadMessage[]): string | null {\n let parentId: string | null = null;\n let lastId: string | null = null;\n const seen = new Set<string>();\n\n for (const message of messages) {\n if (seen.has(message.id)) continue;\n seen.add(message.id);\n this.session.addOrUpdateMessage(parentId, message);\n parentId = message.id;\n lastId = message.id;\n }\n\n return lastId;\n }\n\n private finalizeExternalApply(): void {\n this.assistantHistoryParents.clear();\n this.recordedHistoryIds.clear();\n for (const { message } of this.getMessageRepository().messages) {\n this.recordedHistoryIds.add(message.id);\n }\n this.currentTask = undefined;\n this.currentArtifacts = [];\n this.notifyUpdate();\n }\n\n applyExternalMessages(messages: readonly ThreadMessage[]): void {\n if (messages.length === 0) {\n this.session.clear();\n } else {\n let expectedParentId: string | null = null;\n let lastAppliedId: string | null = null;\n let hardReplace = false;\n const seen = new Set<string>();\n\n for (const message of messages) {\n if (seen.has(message.id)) continue;\n seen.add(message.id);\n const existing = this.session.tryGetMessage(message.id);\n if (existing && existing.parentId !== expectedParentId) {\n hardReplace = true;\n break;\n }\n this.session.addOrUpdateMessage(expectedParentId, message);\n expectedParentId = message.id;\n lastAppliedId = message.id;\n }\n\n if (hardReplace) {\n this.session.clear();\n lastAppliedId = this.appendLinearChain(messages);\n }\n\n this.session.resetHead(lastAppliedId);\n }\n\n this.finalizeExternalApply();\n }\n\n // --- Run logic ---\n\n private async startRun(userThreadMessage: ThreadMessage): Promise<void> {\n this.runGeneration++;\n\n // Cancel any in-progress run before starting a new one\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = null;\n }\n\n const a2aMessage = threadMessageToA2AMessage(userThreadMessage, {\n contextId: this.contextId,\n taskId:\n this.currentTask?.id &&\n !isTerminalTaskState(this.currentTask.status.state)\n ? this.currentTask.id\n : undefined,\n });\n\n // Clear task if previous task reached terminal state\n if (\n this.currentTask &&\n isTerminalTaskState(this.currentTask.status.state)\n ) {\n this.currentTask = undefined;\n }\n\n this.currentArtifacts = [];\n\n const assistantParentId = userThreadMessage.id;\n const assistantId = this.insertAssistantPlaceholder(assistantParentId);\n this.markPendingAssistantHistory(assistantId, assistantParentId);\n\n const abortController = new AbortController();\n this.abortController = abortController;\n\n abortController.signal.addEventListener(\n \"abort\",\n () => {\n this.updateAssistantStatus(assistantId, {\n type: \"incomplete\",\n reason: \"cancelled\",\n });\n this.finishRun(abortController);\n invokeRuntimeCallback(\"onCancel\", this.onCancel);\n },\n { once: true },\n );\n\n this.setRunning(true);\n\n try {\n if (!(await this.waitForAgentCard(abortController.signal))) return;\n\n const supportsStreaming =\n this.agentCardValue?.capabilities?.streaming !== false;\n if (supportsStreaming) {\n await this.runStreaming(a2aMessage, assistantId, abortController);\n } else {\n await this.runSync(a2aMessage, assistantId, abortController);\n }\n } catch (error) {\n if (!abortController.signal.aborted) {\n const err = error instanceof Error ? error : new Error(String(error));\n this.updateAssistantStatus(assistantId, {\n type: \"incomplete\",\n reason: \"error\",\n });\n invokeRuntimeCallback(\"onError\", this.onError, err);\n this.pendingError = this.pendingError ?? err;\n }\n } finally {\n this.finishRun(abortController);\n }\n\n if (this.pendingError) {\n const err = this.pendingError;\n this.pendingError = null;\n throw err;\n }\n }\n\n private async runStreaming(\n a2aMessage: A2AMessage,\n assistantId: string,\n abortController: AbortController,\n ): Promise<void> {\n const stream = this.client.streamMessage(\n a2aMessage,\n this.configuration,\n undefined, // metadata\n abortController.signal,\n );\n\n let receivedEvent = false;\n let skipReason: string | undefined;\n const diagnosedStream = (async function* () {\n const reason = yield* stream;\n if (typeof reason === \"string\") skipReason = reason;\n })();\n\n for await (const event of diagnosedStream) {\n if (abortController.signal.aborted) break;\n receivedEvent = true;\n this.handleStreamEvent(assistantId, event);\n }\n\n if (!abortController.signal.aborted) {\n if (!receivedEvent) {\n throw new Error(\n skipReason\n ? `A2A message stream ended without any events. First skipped frame: ${skipReason}`\n : \"A2A message stream ended without any events.\",\n );\n }\n\n const lastStatus = this.getAssistantStatus(assistantId);\n if (lastStatus?.type === \"running\") {\n this.updateAssistantStatus(assistantId, {\n type: \"complete\",\n reason: \"stop\",\n });\n }\n }\n }\n\n private async runSync(\n a2aMessage: A2AMessage,\n assistantId: string,\n abortController: AbortController,\n ): Promise<void> {\n const result = await this.client.sendMessage(\n a2aMessage,\n this.configuration,\n undefined, // metadata\n abortController.signal,\n );\n\n if (abortController.signal.aborted) return;\n\n // Result is either A2ATask or A2AMessage\n if (\"id\" in result && \"status\" in result) {\n // It's a Task\n this.handleTaskSnapshot(assistantId, result as A2ATask);\n } else if (\"messageId\" in result && \"parts\" in result) {\n // It's a Message\n this.handleMessage(assistantId, result as A2AMessage);\n this.updateAssistantStatus(assistantId, {\n type: \"complete\",\n reason: \"stop\",\n });\n }\n }\n\n private handleStreamEvent(assistantId: string, event: A2AStreamEvent) {\n switch (event.type) {\n case \"statusUpdate\":\n this.handleStatusUpdate(assistantId, event.event);\n break;\n case \"artifactUpdate\":\n this.handleArtifactUpdate(event.event);\n break;\n case \"message\":\n this.handleMessage(assistantId, event.message);\n break;\n case \"task\":\n this.handleTaskSnapshot(assistantId, event.task);\n break;\n }\n }\n\n private handleStatusUpdate(\n assistantId: string,\n event: A2ATaskStatusUpdateEvent,\n ) {\n if (!this.currentTask) {\n this.currentTask = {\n id: event.taskId,\n contextId: event.contextId,\n status: event.status,\n };\n } else {\n this.currentTask = { ...this.currentTask, status: event.status };\n }\n\n if (event.contextId) {\n this.contextId = event.contextId;\n }\n\n if (event.status.message) {\n const content = a2aMessageToContent(event.status.message);\n this.updateAssistantContent(assistantId, content);\n }\n\n const status = taskStateToMessageStatus(event.status.state);\n this.updateAssistantStatus(assistantId, status);\n\n this.notifyUpdate();\n }\n\n private handleArtifactUpdate(event: A2ATaskArtifactUpdateEvent) {\n const { append, lastChunk } = event;\n const artifact = normalizeArtifact(event.artifact);\n const existingIdx = this.currentArtifacts.findIndex(\n (a) => a.artifactId === artifact.artifactId,\n );\n\n let updated: A2AArtifact;\n if (existingIdx >= 0 && append) {\n const existing = this.currentArtifacts[existingIdx]!;\n updated = {\n ...existing,\n parts: [...existing.parts, ...artifact.parts],\n };\n this.currentArtifacts = [\n ...this.currentArtifacts.slice(0, existingIdx),\n updated,\n ...this.currentArtifacts.slice(existingIdx + 1),\n ];\n } else if (existingIdx >= 0) {\n updated = artifact;\n this.currentArtifacts = [\n ...this.currentArtifacts.slice(0, existingIdx),\n updated,\n ...this.currentArtifacts.slice(existingIdx + 1),\n ];\n } else {\n updated = artifact;\n this.currentArtifacts = [...this.currentArtifacts, updated];\n }\n\n if (lastChunk) {\n invokeRuntimeCallback(\n \"onArtifactComplete\",\n this.onArtifactComplete,\n updated,\n );\n }\n\n this.notifyUpdate();\n }\n\n private handleMessage(assistantId: string, message: A2AMessage) {\n if (message.role !== \"agent\") return;\n\n const content = a2aMessageToContent(message);\n this.updateAssistantContent(assistantId, content);\n this.notifyUpdate();\n }\n\n private handleTaskSnapshot(assistantId: string, task: A2ATask) {\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 this.currentTask = {\n ...task,\n ...(artifacts === undefined ? {} : { artifacts }),\n ...(history === undefined ? {} : { history }),\n };\n\n if (task.contextId) {\n this.contextId = task.contextId;\n }\n if (artifacts) {\n this.currentArtifacts = artifacts;\n }\n\n if (task.status.message) {\n const content = a2aMessageToContent(task.status.message);\n this.updateAssistantContent(assistantId, content);\n }\n\n const status = taskStateToMessageStatus(task.status.state);\n this.updateAssistantStatus(assistantId, status);\n\n this.notifyUpdate();\n }\n\n // --- Message helpers ---\n\n private insertAssistantPlaceholder(parentId: string): string {\n const id = generateId();\n const assistant: ThreadAssistantMessage = {\n id,\n role: \"assistant\",\n createdAt: new Date(),\n status: { type: \"running\" },\n content: [],\n metadata: {\n unstable_state: null,\n unstable_annotations: [],\n unstable_data: [],\n steps: [],\n custom: {},\n },\n };\n this.session.addOrUpdateMessage(parentId, assistant);\n this.session.switchToBranch(id);\n this.notifyUpdate();\n return id;\n }\n\n private updateAssistantContent(\n messageId: string,\n content: ThreadAssistantMessage[\"content\"],\n ) {\n this.session.updateMessage(messageId, (message) => {\n if (message.role !== \"assistant\") return message;\n return { ...message, content };\n });\n }\n\n private updateAssistantStatus(messageId: string, status: MessageStatus) {\n const touched = this.session.updateMessage(messageId, (message) => {\n if (message.role !== \"assistant\") return message;\n return { ...message, status };\n });\n if (touched) {\n this.notifyUpdate();\n if (status.type === \"complete\" || status.type === \"incomplete\") {\n this.persistAssistantHistory(messageId);\n }\n }\n }\n\n private getAssistantStatus(messageId: string): MessageStatus | undefined {\n const msg = this.session.tryGetMessage(messageId)?.message;\n if (msg?.role !== \"assistant\") return undefined;\n return msg.status;\n }\n\n // --- Lifecycle helpers ---\n\n private setRunning(running: boolean) {\n this.isRunningFlag = running;\n this.notifyUpdate();\n }\n\n private finishRun(controller: AbortController | null) {\n if (this.abortController !== controller) return;\n this.abortController = null;\n this.setRunning(false);\n }\n\n // --- History persistence ---\n\n private recordHistoryEntry(parentId: string | null, message: ThreadMessage) {\n this.appendHistoryItem(parentId, message);\n }\n\n private markPendingAssistantHistory(\n messageId: string,\n parentId: string | null,\n ) {\n if (!this.history) return;\n this.assistantHistoryParents.set(messageId, parentId);\n }\n\n private persistAssistantHistory(messageId: string) {\n if (!this.history) return;\n const parentId = this.assistantHistoryParents.get(messageId);\n if (parentId === undefined) return;\n const message = this.session.tryGetMessage(messageId)?.message;\n if (!message || message.role !== \"assistant\") return;\n if (\n message.status?.type !== \"complete\" &&\n message.status?.type !== \"incomplete\"\n )\n return;\n this.assistantHistoryParents.delete(messageId);\n this.appendHistoryItem(parentId, message);\n }\n\n private appendHistoryItem(parentId: string | null, message: ThreadMessage) {\n if (!this.history || this.recordedHistoryIds.has(message.id)) return;\n this.recordedHistoryIds.add(message.id);\n void this.history.append({ parentId, message }).catch(() => {\n this.recordedHistoryIds.delete(message.id);\n });\n }\n}\n"],"mappings":"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"}
|
package/dist/a2aExtras.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { A2AExtras } from "./types.js";
|
|
2
2
|
//#region src/a2aExtras.d.ts
|
|
3
|
-
declare const a2aExtras: import("@assistant-ui/core/react").RuntimeExtras<A2AExtras>;
|
|
3
|
+
export declare const a2aExtras: import("@assistant-ui/core/react").RuntimeExtras<A2AExtras>;
|
|
4
4
|
//#endregion
|
|
5
|
-
export { a2aExtras };
|
|
6
5
|
//# sourceMappingURL=a2aExtras.d.ts.map
|
package/dist/a2aExtras.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"a2aExtras.d.ts","names":[],"sources":["../src/a2aExtras.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"a2aExtras.d.ts","names":[],"sources":["../src/a2aExtras.ts"],"mappings":";;qBAGa,8CAAS,cAAA"}
|
package/dist/conversions.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { A2AMessage, A2APart, A2ATaskState } from "./types.js";
|
|
2
2
|
import { MessageStatus, ThreadAssistantMessage, ThreadMessage } from "@assistant-ui/core";
|
|
3
3
|
//#region src/conversions.d.ts
|
|
4
|
-
declare function a2aPartToContent(part: A2APart): ThreadAssistantMessage["content"][number];
|
|
5
|
-
declare function a2aPartsToContent(parts: A2APart[]): ThreadAssistantMessage["content"];
|
|
6
|
-
declare function isTerminalTaskState(state: A2ATaskState): boolean;
|
|
7
|
-
declare function isInterruptedTaskState(state: A2ATaskState): boolean;
|
|
8
|
-
declare function taskStateToMessageStatus(state: A2ATaskState): MessageStatus;
|
|
9
|
-
declare function contentPartsToA2AParts(content: ReadonlyArray<{
|
|
4
|
+
export declare function a2aPartToContent(part: A2APart): ThreadAssistantMessage["content"][number];
|
|
5
|
+
export declare function a2aPartsToContent(parts: A2APart[]): ThreadAssistantMessage["content"];
|
|
6
|
+
export declare function isTerminalTaskState(state: A2ATaskState): boolean;
|
|
7
|
+
export declare function isInterruptedTaskState(state: A2ATaskState): boolean;
|
|
8
|
+
export declare function taskStateToMessageStatus(state: A2ATaskState): MessageStatus;
|
|
9
|
+
export declare function contentPartsToA2AParts(content: ReadonlyArray<{
|
|
10
10
|
type: string;
|
|
11
11
|
text?: string | undefined;
|
|
12
12
|
image?: string | undefined;
|
|
@@ -19,11 +19,10 @@ declare function contentPartsToA2AParts(content: ReadonlyArray<{
|
|
|
19
19
|
format: string;
|
|
20
20
|
} | undefined;
|
|
21
21
|
}>, fallbackMimeType?: string): A2APart[];
|
|
22
|
-
declare function a2aMessageToContent(message: A2AMessage): ThreadAssistantMessage["content"];
|
|
23
|
-
declare function threadMessageToA2AMessage(message: ThreadMessage, options?: {
|
|
22
|
+
export declare function a2aMessageToContent(message: A2AMessage): ThreadAssistantMessage["content"];
|
|
23
|
+
export declare function threadMessageToA2AMessage(message: ThreadMessage, options?: {
|
|
24
24
|
contextId?: string | undefined;
|
|
25
25
|
taskId?: string | undefined;
|
|
26
26
|
}): A2AMessage;
|
|
27
27
|
//#endregion
|
|
28
|
-
export { a2aMessageToContent, a2aPartToContent, a2aPartsToContent, contentPartsToA2AParts, isInterruptedTaskState, isTerminalTaskState, taskStateToMessageStatus, threadMessageToA2AMessage };
|
|
29
28
|
//# sourceMappingURL=conversions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversions.d.ts","names":[],"sources":["../src/conversions.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"conversions.d.ts","names":[],"sources":["../src/conversions.ts"],"mappings":";;;wBAiBgB,iBACd,MAAM,UACL;wBAyCa,kBACd,OAAO,YACN;wBAgBa,oBAAoB,OAAO;wBAI3B,uBAAuB,OAAO;wBAI9B,yBAAyB,OAAO,eAAe;wBAoB/C,uBACd,SAAS;EACP;EACA;EACA;EACA;EACA;EACA;EACA;EACA;IAAU;IAAc;;IAE1B,4BACC;wBA4Ea,oBACd,SAAS,aACR;wBAIa,0BACd,SAAS,eACT;EACE;EACA;IAED"}
|
package/dist/hooks.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { A2AAgentCard, A2AArtifact, A2ATask } from "./types.js";
|
|
2
2
|
//#region src/hooks.d.ts
|
|
3
3
|
/** The current A2A task, if a run is in progress. */
|
|
4
|
-
declare const useA2ATask: () => A2ATask | undefined;
|
|
4
|
+
export declare const useA2ATask: () => A2ATask | undefined;
|
|
5
5
|
/** The artifacts accumulated for the current A2A run. */
|
|
6
|
-
declare const useA2AArtifacts: () => readonly A2AArtifact[];
|
|
6
|
+
export declare const useA2AArtifacts: () => readonly A2AArtifact[];
|
|
7
7
|
/** The discovered A2A agent card. */
|
|
8
|
-
declare const useA2AAgentCard: () => A2AAgentCard | undefined;
|
|
8
|
+
export declare const useA2AAgentCard: () => A2AAgentCard | undefined;
|
|
9
9
|
//#endregion
|
|
10
|
-
export { useA2AAgentCard, useA2AArtifacts, useA2ATask };
|
|
11
10
|
//# sourceMappingURL=hooks.d.ts.map
|
package/dist/hooks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","names":[],"sources":["../src/hooks.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","names":[],"sources":["../src/hooks.ts"],"mappings":";;;qBAKa,kBAAA;;qBAGA,gCAAA;;qBAGA,uBAAA"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { A2AClient, A2AClientOptions } from "./A2AClient.js";
|
|
2
2
|
import { AttachmentAdapter, DictationAdapter, ExternalStoreSharedOptions, FeedbackAdapter, RealtimeVoiceAdapter, SpeechSynthesisAdapter, ThreadHistoryAdapter, ThreadMessage } from "@assistant-ui/core";
|
|
3
3
|
//#region src/types.d.ts
|
|
4
|
-
declare const A2A_PROTOCOL_VERSION = "1.0";
|
|
5
|
-
type A2ARole = "unspecified" | "user" | "agent";
|
|
6
|
-
type A2APart = {
|
|
4
|
+
export declare const A2A_PROTOCOL_VERSION = "1.0";
|
|
5
|
+
export type A2ARole = "unspecified" | "user" | "agent";
|
|
6
|
+
export type A2APart = {
|
|
7
7
|
text?: string | undefined;
|
|
8
8
|
raw?: string | undefined;
|
|
9
9
|
url?: string | undefined;
|
|
@@ -12,7 +12,7 @@ type A2APart = {
|
|
|
12
12
|
filename?: string | undefined;
|
|
13
13
|
mediaType?: string | undefined;
|
|
14
14
|
};
|
|
15
|
-
type A2AMessage = {
|
|
15
|
+
export type A2AMessage = {
|
|
16
16
|
messageId: string;
|
|
17
17
|
contextId?: string | undefined;
|
|
18
18
|
taskId?: string | undefined;
|
|
@@ -22,13 +22,13 @@ type A2AMessage = {
|
|
|
22
22
|
extensions?: string[] | undefined;
|
|
23
23
|
referenceTaskIds?: string[] | undefined;
|
|
24
24
|
};
|
|
25
|
-
type A2ATaskState = "unspecified" | "submitted" | "working" | "completed" | "failed" | "canceled" | "input_required" | "rejected" | "auth_required";
|
|
26
|
-
type A2ATaskStatus = {
|
|
25
|
+
export type A2ATaskState = "unspecified" | "submitted" | "working" | "completed" | "failed" | "canceled" | "input_required" | "rejected" | "auth_required";
|
|
26
|
+
export type A2ATaskStatus = {
|
|
27
27
|
state: A2ATaskState;
|
|
28
28
|
message?: A2AMessage | undefined;
|
|
29
29
|
timestamp?: string | undefined;
|
|
30
30
|
};
|
|
31
|
-
type A2AArtifact = {
|
|
31
|
+
export type A2AArtifact = {
|
|
32
32
|
artifactId: string;
|
|
33
33
|
name?: string | undefined;
|
|
34
34
|
description?: string | undefined;
|
|
@@ -36,7 +36,7 @@ type A2AArtifact = {
|
|
|
36
36
|
metadata?: Record<string, unknown> | undefined;
|
|
37
37
|
extensions?: string[] | undefined;
|
|
38
38
|
};
|
|
39
|
-
type A2ATask = {
|
|
39
|
+
export type A2ATask = {
|
|
40
40
|
id: string;
|
|
41
41
|
contextId?: string | undefined;
|
|
42
42
|
status: A2ATaskStatus;
|
|
@@ -44,13 +44,13 @@ type A2ATask = {
|
|
|
44
44
|
history?: A2AMessage[] | undefined;
|
|
45
45
|
metadata?: Record<string, unknown> | undefined;
|
|
46
46
|
};
|
|
47
|
-
type A2ATaskStatusUpdateEvent = {
|
|
47
|
+
export type A2ATaskStatusUpdateEvent = {
|
|
48
48
|
taskId: string;
|
|
49
49
|
contextId: string;
|
|
50
50
|
status: A2ATaskStatus;
|
|
51
51
|
metadata?: Record<string, unknown> | undefined;
|
|
52
52
|
};
|
|
53
|
-
type A2ATaskArtifactUpdateEvent = {
|
|
53
|
+
export type A2ATaskArtifactUpdateEvent = {
|
|
54
54
|
taskId: string;
|
|
55
55
|
contextId: string;
|
|
56
56
|
artifact: A2AArtifact;
|
|
@@ -58,7 +58,7 @@ type A2ATaskArtifactUpdateEvent = {
|
|
|
58
58
|
lastChunk?: boolean | undefined;
|
|
59
59
|
metadata?: Record<string, unknown> | undefined;
|
|
60
60
|
};
|
|
61
|
-
type A2AStreamEvent = {
|
|
61
|
+
export type A2AStreamEvent = {
|
|
62
62
|
type: "task";
|
|
63
63
|
task: A2ATask;
|
|
64
64
|
} | {
|
|
@@ -71,11 +71,11 @@ type A2AStreamEvent = {
|
|
|
71
71
|
type: "artifactUpdate";
|
|
72
72
|
event: A2ATaskArtifactUpdateEvent;
|
|
73
73
|
};
|
|
74
|
-
type A2AAuthenticationInfo = {
|
|
74
|
+
export type A2AAuthenticationInfo = {
|
|
75
75
|
scheme: string;
|
|
76
76
|
credentials?: string | undefined;
|
|
77
77
|
};
|
|
78
|
-
type A2ATaskPushNotificationConfig = {
|
|
78
|
+
export type A2ATaskPushNotificationConfig = {
|
|
79
79
|
tenant?: string | undefined;
|
|
80
80
|
id?: string | undefined;
|
|
81
81
|
taskId?: string | undefined;
|
|
@@ -83,17 +83,17 @@ type A2ATaskPushNotificationConfig = {
|
|
|
83
83
|
token?: string | undefined;
|
|
84
84
|
authentication?: A2AAuthenticationInfo | undefined;
|
|
85
85
|
};
|
|
86
|
-
type A2AListTaskPushNotificationConfigsResponse = {
|
|
86
|
+
export type A2AListTaskPushNotificationConfigsResponse = {
|
|
87
87
|
configs: A2ATaskPushNotificationConfig[];
|
|
88
88
|
nextPageToken?: string | undefined;
|
|
89
89
|
};
|
|
90
|
-
type A2ASendMessageConfiguration = {
|
|
90
|
+
export type A2ASendMessageConfiguration = {
|
|
91
91
|
acceptedOutputModes?: string[] | undefined;
|
|
92
92
|
taskPushNotificationConfig?: A2ATaskPushNotificationConfig | undefined;
|
|
93
93
|
historyLength?: number | undefined;
|
|
94
94
|
returnImmediately?: boolean | undefined;
|
|
95
95
|
};
|
|
96
|
-
type A2AListTasksRequest = {
|
|
96
|
+
export type A2AListTasksRequest = {
|
|
97
97
|
contextId?: string | undefined;
|
|
98
98
|
status?: A2ATaskState | undefined;
|
|
99
99
|
pageSize?: number | undefined;
|
|
@@ -102,59 +102,59 @@ type A2AListTasksRequest = {
|
|
|
102
102
|
statusTimestampAfter?: string | undefined;
|
|
103
103
|
includeArtifacts?: boolean | undefined;
|
|
104
104
|
};
|
|
105
|
-
type A2AListTasksResponse = {
|
|
105
|
+
export type A2AListTasksResponse = {
|
|
106
106
|
tasks: A2ATask[];
|
|
107
107
|
nextPageToken: string;
|
|
108
108
|
pageSize: number;
|
|
109
109
|
totalSize: number;
|
|
110
110
|
};
|
|
111
|
-
type A2AErrorInfo = {
|
|
111
|
+
export type A2AErrorInfo = {
|
|
112
112
|
code: number;
|
|
113
113
|
status: string;
|
|
114
114
|
message: string;
|
|
115
115
|
details?: unknown[] | undefined;
|
|
116
116
|
};
|
|
117
|
-
type A2AApiKeySecurityScheme = {
|
|
117
|
+
export type A2AApiKeySecurityScheme = {
|
|
118
118
|
description?: string | undefined;
|
|
119
119
|
location: string;
|
|
120
120
|
name: string;
|
|
121
121
|
};
|
|
122
|
-
type A2AHttpAuthSecurityScheme = {
|
|
122
|
+
export type A2AHttpAuthSecurityScheme = {
|
|
123
123
|
description?: string | undefined;
|
|
124
124
|
scheme: string;
|
|
125
125
|
bearerFormat?: string | undefined;
|
|
126
126
|
};
|
|
127
|
-
type A2AAuthorizationCodeOAuthFlow = {
|
|
127
|
+
export type A2AAuthorizationCodeOAuthFlow = {
|
|
128
128
|
authorizationUrl: string;
|
|
129
129
|
tokenUrl: string;
|
|
130
130
|
refreshUrl?: string | undefined;
|
|
131
131
|
scopes: Record<string, string>;
|
|
132
132
|
pkceRequired?: boolean | undefined;
|
|
133
133
|
};
|
|
134
|
-
type A2AClientCredentialsOAuthFlow = {
|
|
134
|
+
export type A2AClientCredentialsOAuthFlow = {
|
|
135
135
|
tokenUrl: string;
|
|
136
136
|
refreshUrl?: string | undefined;
|
|
137
137
|
scopes: Record<string, string>;
|
|
138
138
|
};
|
|
139
|
-
type A2ADeviceCodeOAuthFlow = {
|
|
139
|
+
export type A2ADeviceCodeOAuthFlow = {
|
|
140
140
|
deviceAuthorizationUrl: string;
|
|
141
141
|
tokenUrl: string;
|
|
142
142
|
refreshUrl?: string | undefined;
|
|
143
143
|
scopes: Record<string, string>;
|
|
144
144
|
};
|
|
145
145
|
/** @deprecated Use Authorization Code + PKCE instead. */
|
|
146
|
-
type A2AImplicitOAuthFlow = {
|
|
146
|
+
export type A2AImplicitOAuthFlow = {
|
|
147
147
|
authorizationUrl?: string | undefined;
|
|
148
148
|
refreshUrl?: string | undefined;
|
|
149
149
|
scopes?: Record<string, string> | undefined;
|
|
150
150
|
};
|
|
151
151
|
/** @deprecated Use Authorization Code + PKCE or Device Code. */
|
|
152
|
-
type A2APasswordOAuthFlow = {
|
|
152
|
+
export type A2APasswordOAuthFlow = {
|
|
153
153
|
tokenUrl?: string | undefined;
|
|
154
154
|
refreshUrl?: string | undefined;
|
|
155
155
|
scopes?: Record<string, string> | undefined;
|
|
156
156
|
};
|
|
157
|
-
type A2AOAuthFlows = {
|
|
157
|
+
export type A2AOAuthFlows = {
|
|
158
158
|
authorizationCode?: A2AAuthorizationCodeOAuthFlow | undefined;
|
|
159
159
|
clientCredentials?: A2AClientCredentialsOAuthFlow | undefined;
|
|
160
160
|
/** @deprecated */
|
|
@@ -163,36 +163,36 @@ type A2AOAuthFlows = {
|
|
|
163
163
|
password?: A2APasswordOAuthFlow | undefined;
|
|
164
164
|
deviceCode?: A2ADeviceCodeOAuthFlow | undefined;
|
|
165
165
|
};
|
|
166
|
-
type A2AOAuth2SecurityScheme = {
|
|
166
|
+
export type A2AOAuth2SecurityScheme = {
|
|
167
167
|
description?: string | undefined;
|
|
168
168
|
flows: A2AOAuthFlows;
|
|
169
169
|
oauth2MetadataUrl?: string | undefined;
|
|
170
170
|
};
|
|
171
|
-
type A2AOpenIdConnectSecurityScheme = {
|
|
171
|
+
export type A2AOpenIdConnectSecurityScheme = {
|
|
172
172
|
description?: string | undefined;
|
|
173
173
|
openIdConnectUrl: string;
|
|
174
174
|
};
|
|
175
|
-
type A2AMutualTlsSecurityScheme = {
|
|
175
|
+
export type A2AMutualTlsSecurityScheme = {
|
|
176
176
|
description?: string | undefined;
|
|
177
177
|
};
|
|
178
|
-
type A2ASecurityScheme = {
|
|
178
|
+
export type A2ASecurityScheme = {
|
|
179
179
|
apiKeySecurityScheme?: A2AApiKeySecurityScheme | undefined;
|
|
180
180
|
httpAuthSecurityScheme?: A2AHttpAuthSecurityScheme | undefined;
|
|
181
181
|
oauth2SecurityScheme?: A2AOAuth2SecurityScheme | undefined;
|
|
182
182
|
openIdConnectSecurityScheme?: A2AOpenIdConnectSecurityScheme | undefined;
|
|
183
183
|
mtlsSecurityScheme?: A2AMutualTlsSecurityScheme | undefined;
|
|
184
184
|
};
|
|
185
|
-
type A2ASecurityRequirement = {
|
|
185
|
+
export type A2ASecurityRequirement = {
|
|
186
186
|
schemes: Record<string, {
|
|
187
187
|
list: string[];
|
|
188
188
|
}>;
|
|
189
189
|
};
|
|
190
|
-
type A2AAgentCardSignature = {
|
|
190
|
+
export type A2AAgentCardSignature = {
|
|
191
191
|
protected: string;
|
|
192
192
|
signature: string;
|
|
193
193
|
header?: Record<string, unknown> | undefined;
|
|
194
194
|
};
|
|
195
|
-
type A2AAgentSkill = {
|
|
195
|
+
export type A2AAgentSkill = {
|
|
196
196
|
id: string;
|
|
197
197
|
name: string;
|
|
198
198
|
description: string;
|
|
@@ -202,7 +202,7 @@ type A2AAgentSkill = {
|
|
|
202
202
|
outputModes?: string[] | undefined;
|
|
203
203
|
securityRequirements?: A2ASecurityRequirement[] | undefined;
|
|
204
204
|
};
|
|
205
|
-
type A2AAgentCapabilities = {
|
|
205
|
+
export type A2AAgentCapabilities = {
|
|
206
206
|
streaming?: boolean | undefined;
|
|
207
207
|
pushNotifications?: boolean | undefined;
|
|
208
208
|
extensions?: Array<{
|
|
@@ -213,13 +213,13 @@ type A2AAgentCapabilities = {
|
|
|
213
213
|
}> | undefined;
|
|
214
214
|
extendedAgentCard?: boolean | undefined;
|
|
215
215
|
};
|
|
216
|
-
type A2AAgentInterface = {
|
|
216
|
+
export type A2AAgentInterface = {
|
|
217
217
|
url: string;
|
|
218
218
|
protocolBinding: string;
|
|
219
219
|
protocolVersion: string;
|
|
220
220
|
tenant?: string | undefined;
|
|
221
221
|
};
|
|
222
|
-
type A2AAgentCard = {
|
|
222
|
+
export type A2AAgentCard = {
|
|
223
223
|
name: string;
|
|
224
224
|
description: string;
|
|
225
225
|
supportedInterfaces: A2AAgentInterface[];
|
|
@@ -239,19 +239,19 @@ type A2AAgentCard = {
|
|
|
239
239
|
iconUrl?: string | undefined;
|
|
240
240
|
};
|
|
241
241
|
/** Private state `useA2ARuntime` exposes through `thread.extras`. */
|
|
242
|
-
type A2AExtras = {
|
|
242
|
+
export type A2AExtras = {
|
|
243
243
|
task: A2ATask | undefined;
|
|
244
244
|
artifacts: readonly A2AArtifact[];
|
|
245
245
|
agentCard: A2AAgentCard | undefined;
|
|
246
246
|
};
|
|
247
|
-
type UseA2AThreadListAdapter = {
|
|
247
|
+
export type UseA2AThreadListAdapter = {
|
|
248
248
|
threadId?: string;
|
|
249
249
|
onSwitchToNewThread?: () => Promise<void> | void;
|
|
250
250
|
onSwitchToThread?: (threadId: string) => Promise<{
|
|
251
251
|
messages: readonly ThreadMessage[];
|
|
252
252
|
}>;
|
|
253
253
|
};
|
|
254
|
-
type UseA2ARuntimeOptions = ExternalStoreSharedOptions & {
|
|
254
|
+
export type UseA2ARuntimeOptions = ExternalStoreSharedOptions & {
|
|
255
255
|
/** Pre-built A2A client instance. Provide this OR baseUrl. */
|
|
256
256
|
client?: A2AClient;
|
|
257
257
|
/** Base URL of the A2A server. Used to create a client if `client` is not provided. */
|
|
@@ -287,5 +287,4 @@ type UseA2ARuntimeOptions = ExternalStoreSharedOptions & {
|
|
|
287
287
|
};
|
|
288
288
|
};
|
|
289
289
|
//#endregion
|
|
290
|
-
export { A2AAgentCapabilities, A2AAgentCard, A2AAgentCardSignature, A2AAgentInterface, A2AAgentSkill, A2AApiKeySecurityScheme, A2AArtifact, A2AAuthenticationInfo, A2AAuthorizationCodeOAuthFlow, A2AClientCredentialsOAuthFlow, A2ADeviceCodeOAuthFlow, A2AErrorInfo, A2AExtras, A2AHttpAuthSecurityScheme, A2AImplicitOAuthFlow, A2AListTaskPushNotificationConfigsResponse, A2AListTasksRequest, A2AListTasksResponse, A2AMessage, A2AMutualTlsSecurityScheme, A2AOAuth2SecurityScheme, A2AOAuthFlows, A2AOpenIdConnectSecurityScheme, A2APart, A2APasswordOAuthFlow, A2ARole, A2ASecurityRequirement, A2ASecurityScheme, A2ASendMessageConfiguration, A2AStreamEvent, A2ATask, A2ATaskArtifactUpdateEvent, A2ATaskPushNotificationConfig, A2ATaskState, A2ATaskStatus, A2ATaskStatusUpdateEvent, A2A_PROTOCOL_VERSION, UseA2ARuntimeOptions, UseA2AThreadListAdapter };
|
|
291
290
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"mappings":";;;qBAgBa;YAED;YAEA;EACV;EACA;EACA;EACA;EAEA,WAAW;EACX;EACA;;YAGU;EACV;EACA;EACA;EACA,MAAM;EACN,OAAO;EACP,WAAW;EACX;EACA;;YAGU;YAWA;EACV,OAAO;EACP,UAAU;EACV;;YAGU;EACV;EACA;EACA;EACA,OAAO;EACP,WAAW;EACX;;YAGU;EACV;EACA;EACA,QAAQ;EACR,YAAY;EACZ,UAAU;EACV,WAAW;;YAID;EACV;EACA;EACA,QAAQ;EACR,WAAW;;YAGD;EACV;EACA;EACA,UAAU;EACV;EACA;EACA,WAAW;;YAGD;EACN;EAAc,MAAM;;EACpB;EAAiB,SAAS;;EAC1B;EAAsB,OAAO;;EAC7B;EAAwB,OAAO;;YAEzB;EACV;EACA;;YAGU;EACV;EACA;EACA;EACA;EACA;EACA,iBAAiB;;YAGP;EACV,SAAS;EACT;;YAGU;EACV;EACA,6BAA6B;EAC7B;EACA;;YAGU;EACV;EACA,SAAS;EACT;EACA;EACA;EACA;EACA;;YAGU;EACV,OAAO;EACP;EACA;EACA;;YAIU;EACV;EACA;EACA;EACA;;YAGU;EACV;EACA;EACA;;YAGU;EACV;EACA;EACA;;YAGU;EACV;EACA;EACA;EACA,QAAQ;EACR;;YAGU;EACV;EACA;EACA,QAAQ;;YAGE;EACV;EACA;EACA;EACA,QAAQ;;;YAIE;EACV;EACA;EACA,SAAS;;;YAIC;EACV;EACA;EACA,SAAS;;YAGC;EACV,oBAAoB;EACpB,oBAAoB;;EAEpB,WAAW;;EAEX,WAAW;EACX,aAAa;;YAGH;EACV;EACA,OAAO;EACP;;YAGU;EACV;EACA;;YAGU;EACV;;YAGU;EACV,uBAAuB;EACvB,yBAAyB;EACzB,uBAAuB;EACvB,8BAA8B;EAC9B,qBAAqB;;YAGX;EACV,SAAS;IAAiB;;;YAIhB;EACV;EACA;EACA,SAAS;;YAGC;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA,uBAAuB;;YAGb;EACV;EACA;EACA,aACI;IACE;IACA;IACA;IACA,SAAS;;EAGf;;YAGU;EACV;EACA;EACA;EACA;;YAGU;EACV;EACA;EACA,qBAAqB;EACrB;IAEM;IACA;;EAGN;EACA;EACA,cAAc;EACd,kBAAkB,eAAe;EACjC,uBAAuB;EACvB;EACA;EACA,QAAQ;EACR,aAAa;EACb;;;YAIU;EACV,MAAM;EACN,oBAAoB;EACpB,WAAW;;YAGD;EACV;EACA,4BAA4B;EAC5B,oBAAoB,qBAAqB;IACvC,mBAAmB;;;YAIX,uBAAuB;;EAEjC,SAAS;;EAET;;EAEA;;EAEA;;EAEA,UAAU;;EAEV;;EAEA,eAAe;;EAGf;;EAEA,gBAAgB;;EAGhB,WAAW,OAAO;;EAElB;;EAEA,sBAAsB,UAAU;EAEhC;IACE,cAAc;IACd,SAAS;IACT,YAAY;IACZ,QAAQ;IACR,WAAW;IACX,UAAU;IACV,aAAa"}
|
package/dist/useA2ARuntime.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { UseA2ARuntimeOptions } from "./types.js";
|
|
2
2
|
import { AssistantRuntime } from "@assistant-ui/core";
|
|
3
3
|
//#region src/useA2ARuntime.d.ts
|
|
4
|
-
declare function useA2ARuntime(options: UseA2ARuntimeOptions): AssistantRuntime;
|
|
4
|
+
export declare function useA2ARuntime(options: UseA2ARuntimeOptions): AssistantRuntime;
|
|
5
5
|
//#endregion
|
|
6
|
-
export { useA2ARuntime };
|
|
7
6
|
//# sourceMappingURL=useA2ARuntime.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useA2ARuntime.d.ts","names":[],"sources":["../src/useA2ARuntime.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"useA2ARuntime.d.ts","names":[],"sources":["../src/useA2ARuntime.ts"],"mappings":";;;wBA2CgB,cAAc,SAAS,uBAAuB"}
|
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.36",
|
|
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.20",
|
|
33
|
+
"@assistant-ui/store": "^0.3.14",
|
|
34
|
+
"assistant-stream": "^0.3.44"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"@types/react": "*",
|
|
@@ -43,14 +43,14 @@
|
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
47
|
-
"@testing-library/dom": "^10.4.
|
|
46
|
+
"@assistant-ui/x-buildutils": "0.0.28",
|
|
47
|
+
"@testing-library/dom": "^10.4.2",
|
|
48
48
|
"@testing-library/react": "^16.3.3",
|
|
49
|
-
"@types/react": "^19.
|
|
49
|
+
"@types/react": "^19.3.0",
|
|
50
50
|
"jsdom": "^30.0.1",
|
|
51
|
-
"react": "^19.
|
|
52
|
-
"react-dom": "^19.
|
|
53
|
-
"vitest": "^5.0.
|
|
51
|
+
"react": "^19.3.0",
|
|
52
|
+
"react-dom": "^19.3.0",
|
|
53
|
+
"vitest": "^5.0.1"
|
|
54
54
|
},
|
|
55
55
|
"publishConfig": {
|
|
56
56
|
"access": "public",
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
"scripts": {
|
|
69
69
|
"build": "aui-build",
|
|
70
70
|
"test": "vitest run",
|
|
71
|
+
"typecheck": "tsc --noEmit",
|
|
71
72
|
"test:watch": "vitest"
|
|
72
73
|
}
|
|
73
74
|
}
|
|
@@ -7,7 +7,11 @@ import type {
|
|
|
7
7
|
A2AStreamEvent,
|
|
8
8
|
A2ATask,
|
|
9
9
|
} from "./types";
|
|
10
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
AppendMessage,
|
|
12
|
+
ExportedMessageRepository,
|
|
13
|
+
ThreadMessage,
|
|
14
|
+
} from "@assistant-ui/core";
|
|
11
15
|
|
|
12
16
|
// --- Mock client factory ---
|
|
13
17
|
|
|
@@ -269,6 +273,118 @@ describe("A2AThreadRuntimeCore", () => {
|
|
|
269
273
|
});
|
|
270
274
|
|
|
271
275
|
describe("history loading", () => {
|
|
276
|
+
it("keeps initial history loading across same-thread message resyncs", async () => {
|
|
277
|
+
let resolve!: (repo: ExportedMessageRepository) => void;
|
|
278
|
+
const pending = new Promise<ExportedMessageRepository>((res) => {
|
|
279
|
+
resolve = res;
|
|
280
|
+
});
|
|
281
|
+
const core = createCore(
|
|
282
|
+
{},
|
|
283
|
+
{ history: { load: () => pending, append: async () => {} } },
|
|
284
|
+
);
|
|
285
|
+
const loading = core.__internal_load();
|
|
286
|
+
core.applyExternalMessages([]);
|
|
287
|
+
const wasLoading = core.isLoading;
|
|
288
|
+
const restored = createHistoryMessage(
|
|
289
|
+
"restored",
|
|
290
|
+
"user",
|
|
291
|
+
"Saved history",
|
|
292
|
+
);
|
|
293
|
+
resolve({
|
|
294
|
+
headId: restored.id,
|
|
295
|
+
messages: [{ parentId: null, message: restored }],
|
|
296
|
+
});
|
|
297
|
+
await loading;
|
|
298
|
+
expect(wasLoading).toBe(true);
|
|
299
|
+
expect(core.getMessages()).toEqual([restored]);
|
|
300
|
+
expect(core.isLoading).toBe(false);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it.each(["resolve", "reject"])(
|
|
304
|
+
"ignores an initial history load that later %s after a thread switch",
|
|
305
|
+
async (outcome) => {
|
|
306
|
+
let resolve!: (repo: ExportedMessageRepository) => void;
|
|
307
|
+
let reject!: (error: Error) => void;
|
|
308
|
+
const pending = new Promise<ExportedMessageRepository>((res, rej) => {
|
|
309
|
+
resolve = res;
|
|
310
|
+
reject = rej;
|
|
311
|
+
});
|
|
312
|
+
const onError = vi.fn();
|
|
313
|
+
const history = {
|
|
314
|
+
load: vi.fn(() => pending),
|
|
315
|
+
append: vi.fn().mockResolvedValue(undefined),
|
|
316
|
+
};
|
|
317
|
+
const core = createCore({}, { history, onError });
|
|
318
|
+
const loading = core.__internal_load();
|
|
319
|
+
const replacement = createHistoryMessage(
|
|
320
|
+
"selected",
|
|
321
|
+
"user",
|
|
322
|
+
"Selected thread",
|
|
323
|
+
);
|
|
324
|
+
core.applyExternalMessages([replacement]);
|
|
325
|
+
core.resetContext();
|
|
326
|
+
|
|
327
|
+
if (outcome === "resolve") {
|
|
328
|
+
const previous = createHistoryMessage(
|
|
329
|
+
"previous",
|
|
330
|
+
"user",
|
|
331
|
+
"Previous thread",
|
|
332
|
+
);
|
|
333
|
+
resolve({
|
|
334
|
+
headId: previous.id,
|
|
335
|
+
messages: [{ parentId: null, message: previous }],
|
|
336
|
+
});
|
|
337
|
+
} else {
|
|
338
|
+
reject(new Error("Old history failed"));
|
|
339
|
+
}
|
|
340
|
+
await loading;
|
|
341
|
+
|
|
342
|
+
expect(core.getMessages()).toEqual([replacement]);
|
|
343
|
+
expect(core.isLoading).toBe(false);
|
|
344
|
+
expect(onError).not.toHaveBeenCalled();
|
|
345
|
+
await core.__internal_load();
|
|
346
|
+
expect(history.load).toHaveBeenCalledTimes(1);
|
|
347
|
+
},
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
it("ends initial loading immediately when switching threads", async () => {
|
|
351
|
+
let resolve!: (repo: ExportedMessageRepository) => void;
|
|
352
|
+
const pending = new Promise<ExportedMessageRepository>((res) => {
|
|
353
|
+
resolve = res;
|
|
354
|
+
});
|
|
355
|
+
const core = createCore(
|
|
356
|
+
{},
|
|
357
|
+
{ history: { load: () => pending, append: async () => {} } },
|
|
358
|
+
);
|
|
359
|
+
const loading = core.__internal_load();
|
|
360
|
+
expect(core.isLoading).toBe(true);
|
|
361
|
+
core.applyExternalMessages([]);
|
|
362
|
+
core.resetContext();
|
|
363
|
+
const updatesAfterReplacement = notifyUpdate.mock.calls.length;
|
|
364
|
+
expect(core.isLoading).toBe(false);
|
|
365
|
+
resolve({ messages: [] });
|
|
366
|
+
await loading;
|
|
367
|
+
expect(notifyUpdate).toHaveBeenCalledTimes(updatesAfterReplacement);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it("does not import old history when agent-card discovery settles after a thread switch", async () => {
|
|
371
|
+
let resolveCard!: (card: A2AAgentCard) => void;
|
|
372
|
+
const card = new Promise<A2AAgentCard>((resolve) => {
|
|
373
|
+
resolveCard = resolve;
|
|
374
|
+
});
|
|
375
|
+
const { history } = createBranchedHistory();
|
|
376
|
+
const core = createCore({ getAgentCard: () => card }, { history });
|
|
377
|
+
const loading = core.__internal_load();
|
|
378
|
+
await Promise.resolve();
|
|
379
|
+
core.applyExternalMessages([]);
|
|
380
|
+
core.resetContext();
|
|
381
|
+
resolveCard({ name: "Agent" } as A2AAgentCard);
|
|
382
|
+
await loading;
|
|
383
|
+
expect(core.getMessages()).toEqual([]);
|
|
384
|
+
expect(core.getAgentCard()?.name).toBe("Agent");
|
|
385
|
+
expect(core.isLoading).toBe(false);
|
|
386
|
+
});
|
|
387
|
+
|
|
272
388
|
it("preserves sibling branches and selects the persisted head", async () => {
|
|
273
389
|
const { user, firstAssistant, secondAssistant, history } =
|
|
274
390
|
createBranchedHistory();
|
|
@@ -1402,6 +1518,136 @@ describe("A2AThreadRuntimeCore", () => {
|
|
|
1402
1518
|
expect(cancelTask).not.toHaveBeenCalled();
|
|
1403
1519
|
});
|
|
1404
1520
|
|
|
1521
|
+
it("ignores a cancellation response that resolves after a newer task snapshot", async () => {
|
|
1522
|
+
let resolveCancel!: (task: A2ATask) => void;
|
|
1523
|
+
const cancelTask = vi.fn().mockReturnValue(
|
|
1524
|
+
new Promise<A2ATask>((resolve) => {
|
|
1525
|
+
resolveCancel = resolve;
|
|
1526
|
+
}),
|
|
1527
|
+
);
|
|
1528
|
+
const streamMessage = vi.fn().mockImplementation(async function* (
|
|
1529
|
+
_msg: any,
|
|
1530
|
+
_cfg: any,
|
|
1531
|
+
_meta: any,
|
|
1532
|
+
signal: AbortSignal,
|
|
1533
|
+
) {
|
|
1534
|
+
if (streamMessage.mock.calls.length === 1) {
|
|
1535
|
+
yield statusUpdateEvent("working");
|
|
1536
|
+
await new Promise<void>((resolve) => {
|
|
1537
|
+
if (signal.aborted) resolve();
|
|
1538
|
+
else
|
|
1539
|
+
signal.addEventListener("abort", () => resolve(), {
|
|
1540
|
+
once: true,
|
|
1541
|
+
});
|
|
1542
|
+
});
|
|
1543
|
+
return;
|
|
1544
|
+
}
|
|
1545
|
+
yield statusUpdateEvent("working", "Second answer");
|
|
1546
|
+
});
|
|
1547
|
+
const core = createCore({ cancelTask, streamMessage });
|
|
1548
|
+
|
|
1549
|
+
const firstRun = core.append(createUserAppendMessage("First"));
|
|
1550
|
+
await vi.waitFor(() => expect(core.getTask()?.id).toBe("t1"));
|
|
1551
|
+
|
|
1552
|
+
const cancelPromise = core.cancel();
|
|
1553
|
+
await firstRun;
|
|
1554
|
+
|
|
1555
|
+
await core.append(createUserAppendMessage("Second"));
|
|
1556
|
+
expect(core.getTask()?.status.state).toBe("working");
|
|
1557
|
+
|
|
1558
|
+
resolveCancel({ id: "t1", status: { state: "canceled" } });
|
|
1559
|
+
await cancelPromise;
|
|
1560
|
+
|
|
1561
|
+
expect(cancelTask).toHaveBeenCalledExactlyOnceWith("t1");
|
|
1562
|
+
expect(core.getTask()).toMatchObject({
|
|
1563
|
+
id: "t1",
|
|
1564
|
+
status: { state: "working" },
|
|
1565
|
+
});
|
|
1566
|
+
});
|
|
1567
|
+
|
|
1568
|
+
it("ignores a cancellation response that resolves after a newer run starts", async () => {
|
|
1569
|
+
let resolveCancel!: (task: A2ATask) => void;
|
|
1570
|
+
const cancelTask = vi.fn().mockReturnValue(
|
|
1571
|
+
new Promise<A2ATask>((resolve) => {
|
|
1572
|
+
resolveCancel = resolve;
|
|
1573
|
+
}),
|
|
1574
|
+
);
|
|
1575
|
+
// The follow-up run never emits, so it leaves the non-terminal task
|
|
1576
|
+
// object from the first run in place: only the run itself is newer.
|
|
1577
|
+
const streamMessage = vi.fn().mockImplementation(async function* (
|
|
1578
|
+
_msg: any,
|
|
1579
|
+
_cfg: any,
|
|
1580
|
+
_meta: any,
|
|
1581
|
+
signal: AbortSignal,
|
|
1582
|
+
) {
|
|
1583
|
+
if (streamMessage.mock.calls.length === 1) {
|
|
1584
|
+
yield statusUpdateEvent("working");
|
|
1585
|
+
}
|
|
1586
|
+
await new Promise<void>((resolve) => {
|
|
1587
|
+
if (signal.aborted) resolve();
|
|
1588
|
+
else
|
|
1589
|
+
signal.addEventListener("abort", () => resolve(), { once: true });
|
|
1590
|
+
});
|
|
1591
|
+
});
|
|
1592
|
+
const core = createCore({ cancelTask, streamMessage });
|
|
1593
|
+
|
|
1594
|
+
const firstRun = core.append(createUserAppendMessage("First"));
|
|
1595
|
+
await vi.waitFor(() => expect(core.getTask()?.id).toBe("t1"));
|
|
1596
|
+
|
|
1597
|
+
const cancelPromise = core.cancel();
|
|
1598
|
+
await firstRun;
|
|
1599
|
+
|
|
1600
|
+
void core.append(createUserAppendMessage("Second"));
|
|
1601
|
+
await vi.waitFor(() => expect(streamMessage).toHaveBeenCalledTimes(2));
|
|
1602
|
+
expect(core.isRunning()).toBe(true);
|
|
1603
|
+
|
|
1604
|
+
resolveCancel({ id: "t1", status: { state: "canceled" } });
|
|
1605
|
+
await cancelPromise;
|
|
1606
|
+
|
|
1607
|
+
expect(core.getTask()?.status.state).toBe("working");
|
|
1608
|
+
});
|
|
1609
|
+
|
|
1610
|
+
it("still cancels the server task when onCancel clears the thread", async () => {
|
|
1611
|
+
let resolveCancel!: (task: A2ATask) => void;
|
|
1612
|
+
const cancelTask = vi.fn().mockReturnValue(
|
|
1613
|
+
new Promise<A2ATask>((resolve) => {
|
|
1614
|
+
resolveCancel = resolve;
|
|
1615
|
+
}),
|
|
1616
|
+
);
|
|
1617
|
+
const streamMessage = vi.fn().mockImplementation(async function* (
|
|
1618
|
+
_msg: any,
|
|
1619
|
+
_cfg: any,
|
|
1620
|
+
_meta: any,
|
|
1621
|
+
signal: AbortSignal,
|
|
1622
|
+
) {
|
|
1623
|
+
yield statusUpdateEvent("working");
|
|
1624
|
+
await new Promise<void>((resolve) => {
|
|
1625
|
+
if (signal.aborted) resolve();
|
|
1626
|
+
else
|
|
1627
|
+
signal.addEventListener("abort", () => resolve(), { once: true });
|
|
1628
|
+
});
|
|
1629
|
+
});
|
|
1630
|
+
let core!: A2AThreadRuntimeCore;
|
|
1631
|
+
core = createCore(
|
|
1632
|
+
{ cancelTask, streamMessage },
|
|
1633
|
+
{ onCancel: () => core.applyExternalMessages([]) },
|
|
1634
|
+
);
|
|
1635
|
+
|
|
1636
|
+
const runPromise = core.append(createUserAppendMessage("Go"));
|
|
1637
|
+
await vi.waitFor(() => expect(core.getTask()?.id).toBe("t1"));
|
|
1638
|
+
|
|
1639
|
+
const cancelPromise = core.cancel();
|
|
1640
|
+
await runPromise;
|
|
1641
|
+
|
|
1642
|
+
expect(cancelTask).toHaveBeenCalledExactlyOnceWith("t1");
|
|
1643
|
+
|
|
1644
|
+
resolveCancel({ id: "t1", status: { state: "canceled" } });
|
|
1645
|
+
await cancelPromise;
|
|
1646
|
+
|
|
1647
|
+
expect(core.getTask()).toBeUndefined();
|
|
1648
|
+
expect(core.getMessages()).toEqual([]);
|
|
1649
|
+
});
|
|
1650
|
+
|
|
1405
1651
|
it.each(["throws", "rejects"] as const)(
|
|
1406
1652
|
"isolates onCancel callbacks that %s",
|
|
1407
1653
|
async (failureMode) => {
|
|
@@ -83,6 +83,7 @@ export class A2AThreadRuntimeCore {
|
|
|
83
83
|
private readonly session = createMessageRepositorySession();
|
|
84
84
|
private isRunningFlag = false;
|
|
85
85
|
private abortController: AbortController | null = null;
|
|
86
|
+
private runGeneration = 0;
|
|
86
87
|
private pendingError: Error | null = null;
|
|
87
88
|
|
|
88
89
|
// A2A-specific state
|
|
@@ -95,6 +96,7 @@ export class A2AThreadRuntimeCore {
|
|
|
95
96
|
private readonly recordedHistoryIds = new Set<string>();
|
|
96
97
|
private _isLoading = false;
|
|
97
98
|
private _loadPromise: Promise<void> | undefined;
|
|
99
|
+
private _historyLoadGeneration = 0;
|
|
98
100
|
private _loadRequested = false;
|
|
99
101
|
private _agentCardPromise: Promise<void> | undefined;
|
|
100
102
|
private _agentCardRetryAfter = 0;
|
|
@@ -146,6 +148,8 @@ export class A2AThreadRuntimeCore {
|
|
|
146
148
|
/** Thread-boundary reset: applyExternalMessages alone also serves branch
|
|
147
149
|
* switches, deletes, and cancel resyncs, which must keep the live context. */
|
|
148
150
|
resetContext(): void {
|
|
151
|
+
this._historyLoadGeneration++;
|
|
152
|
+
this._isLoading = false;
|
|
149
153
|
// Restore the seed before aborting: an onCancel callback that starts a
|
|
150
154
|
// new run must not pick up the old thread's context, and its controller
|
|
151
155
|
// must not be discarded.
|
|
@@ -257,16 +261,19 @@ export class A2AThreadRuntimeCore {
|
|
|
257
261
|
|
|
258
262
|
this._isLoading = true;
|
|
259
263
|
|
|
264
|
+
const generation = this._historyLoadGeneration;
|
|
260
265
|
const historyPromise = this.history.load();
|
|
261
266
|
|
|
262
267
|
this._loadPromise = Promise.all([historyPromise, agentCardPromise])
|
|
263
268
|
.then(([repo]) => {
|
|
269
|
+
if (generation !== this._historyLoadGeneration) return;
|
|
264
270
|
if (repo) {
|
|
265
271
|
this.session.applyExternalMessageRepository(repo);
|
|
266
272
|
this.finalizeExternalApply();
|
|
267
273
|
}
|
|
268
274
|
})
|
|
269
275
|
.catch((error) => {
|
|
276
|
+
if (generation !== this._historyLoadGeneration) return;
|
|
270
277
|
invokeRuntimeCallback(
|
|
271
278
|
"onError",
|
|
272
279
|
this.onError,
|
|
@@ -274,6 +281,7 @@ export class A2AThreadRuntimeCore {
|
|
|
274
281
|
);
|
|
275
282
|
})
|
|
276
283
|
.finally(() => {
|
|
284
|
+
if (generation !== this._historyLoadGeneration) return;
|
|
277
285
|
this._isLoading = false;
|
|
278
286
|
this.notifyUpdate();
|
|
279
287
|
});
|
|
@@ -328,14 +336,26 @@ export class A2AThreadRuntimeCore {
|
|
|
328
336
|
async cancel(): Promise<void> {
|
|
329
337
|
if (!this.abortController) return;
|
|
330
338
|
|
|
339
|
+
// Read the server target before aborting: the abort listener runs the
|
|
340
|
+
// onCancel callback synchronously, which may clear the thread and with it
|
|
341
|
+
// the task this cancellation is for, or start a new run.
|
|
342
|
+
const task = this.currentTask;
|
|
343
|
+
const generation = this.runGeneration;
|
|
344
|
+
|
|
331
345
|
// Abort locally first so the stream stops immediately
|
|
332
346
|
this.abortController.abort();
|
|
333
347
|
|
|
334
348
|
// Then try to cancel the task on the server
|
|
335
|
-
if (
|
|
349
|
+
if (task?.id) {
|
|
336
350
|
try {
|
|
337
|
-
const updated = await this.client.cancelTask(
|
|
338
|
-
|
|
351
|
+
const updated = await this.client.cancelTask(task.id);
|
|
352
|
+
// Only apply the response while nothing newer exists. A newer snapshot
|
|
353
|
+
// or a cleared thread replaces the task object; a follow-up run that
|
|
354
|
+
// has not emitted yet keeps it, so the run generation is what rules
|
|
355
|
+
// that case out.
|
|
356
|
+
if (this.currentTask === task && this.runGeneration === generation) {
|
|
357
|
+
this.currentTask = updated;
|
|
358
|
+
}
|
|
339
359
|
} catch {
|
|
340
360
|
// Server cancel failed; local abort already handled
|
|
341
361
|
}
|
|
@@ -405,6 +425,8 @@ export class A2AThreadRuntimeCore {
|
|
|
405
425
|
// --- Run logic ---
|
|
406
426
|
|
|
407
427
|
private async startRun(userThreadMessage: ThreadMessage): Promise<void> {
|
|
428
|
+
this.runGeneration++;
|
|
429
|
+
|
|
408
430
|
// Cancel any in-progress run before starting a new one
|
|
409
431
|
if (this.abortController) {
|
|
410
432
|
this.abortController.abort();
|
|
@@ -8,7 +8,10 @@ import {
|
|
|
8
8
|
type PropsWithChildren,
|
|
9
9
|
} from "react";
|
|
10
10
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
11
|
-
import type {
|
|
11
|
+
import type {
|
|
12
|
+
ExportedMessageRepository,
|
|
13
|
+
ThreadMessage,
|
|
14
|
+
} from "@assistant-ui/core";
|
|
12
15
|
import type { A2AClient } from "./A2AClient";
|
|
13
16
|
import type { A2AStreamEvent } from "./types";
|
|
14
17
|
import { useA2ARuntime } from "./useA2ARuntime";
|
|
@@ -357,6 +360,93 @@ describe("useA2ARuntime", () => {
|
|
|
357
360
|
]);
|
|
358
361
|
});
|
|
359
362
|
|
|
363
|
+
it("keeps pending history when a run is cancelled in the same thread", async () => {
|
|
364
|
+
const { client, streamMessage } = createMockClient(true);
|
|
365
|
+
let resolve!: (repo: ExportedMessageRepository) => void;
|
|
366
|
+
const pending = new Promise<ExportedMessageRepository>((res) => {
|
|
367
|
+
resolve = res;
|
|
368
|
+
});
|
|
369
|
+
const { result } = renderHook(() =>
|
|
370
|
+
useA2ARuntime({
|
|
371
|
+
client,
|
|
372
|
+
adapters: { history: { load: () => pending, append: async () => {} } },
|
|
373
|
+
}),
|
|
374
|
+
);
|
|
375
|
+
act(() => {
|
|
376
|
+
result.current.thread.append("Hello");
|
|
377
|
+
});
|
|
378
|
+
await waitFor(() => expect(streamMessage).toHaveBeenCalledOnce());
|
|
379
|
+
await act(async () => {
|
|
380
|
+
result.current.thread.cancelRun();
|
|
381
|
+
await new Promise((done) => setTimeout(done, 0));
|
|
382
|
+
});
|
|
383
|
+
const wasLoading = result.current.thread.getState().isLoading;
|
|
384
|
+
const restored = createThreadMessage("restored");
|
|
385
|
+
await act(async () => {
|
|
386
|
+
resolve({
|
|
387
|
+
headId: restored.id,
|
|
388
|
+
messages: [{ parentId: null, message: restored }],
|
|
389
|
+
});
|
|
390
|
+
await pending;
|
|
391
|
+
});
|
|
392
|
+
expect(wasLoading).toBe(true);
|
|
393
|
+
expect(result.current.thread.getState().messages).toEqual([restored]);
|
|
394
|
+
expect(result.current.thread.getState().isLoading).toBe(false);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it.each(["existing", "new"])(
|
|
398
|
+
"keeps the selected %s thread when initial history finishes later",
|
|
399
|
+
async (target) => {
|
|
400
|
+
const { client } = createMockClient();
|
|
401
|
+
let resolveHistory!: (repo: ExportedMessageRepository) => void;
|
|
402
|
+
const pending = new Promise<ExportedMessageRepository>((resolve) => {
|
|
403
|
+
resolveHistory = resolve;
|
|
404
|
+
});
|
|
405
|
+
const history = { load: () => pending, append: async () => {} };
|
|
406
|
+
const { result } = renderHook(() => {
|
|
407
|
+
const [threadId, setThreadId] = useState("thread-a");
|
|
408
|
+
return useA2ARuntime({
|
|
409
|
+
client,
|
|
410
|
+
adapters: {
|
|
411
|
+
history,
|
|
412
|
+
threadList: {
|
|
413
|
+
threadId,
|
|
414
|
+
onSwitchToThread: async (id) => {
|
|
415
|
+
setThreadId(id);
|
|
416
|
+
return { messages: [createThreadMessage("message-b")] };
|
|
417
|
+
},
|
|
418
|
+
onSwitchToNewThread: async () => {
|
|
419
|
+
setThreadId("thread-new");
|
|
420
|
+
},
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
await act(async () => {
|
|
427
|
+
if (target === "existing")
|
|
428
|
+
await result.current.threads.switchToThread("thread-b");
|
|
429
|
+
else await result.current.threads.switchToNewThread();
|
|
430
|
+
});
|
|
431
|
+
const selectedId = result.current.threads.getState().mainThreadId;
|
|
432
|
+
const selectedMessages = result.current.thread.getState().messages;
|
|
433
|
+
await act(async () => {
|
|
434
|
+
resolveHistory({
|
|
435
|
+
headId: "message-a",
|
|
436
|
+
messages: [
|
|
437
|
+
{ parentId: null, message: createThreadMessage("message-a") },
|
|
438
|
+
],
|
|
439
|
+
});
|
|
440
|
+
await pending;
|
|
441
|
+
});
|
|
442
|
+
expect(result.current.threads.getState().mainThreadId).toBe(selectedId);
|
|
443
|
+
expect(result.current.thread.getState().messages).toEqual(
|
|
444
|
+
selectedMessages,
|
|
445
|
+
);
|
|
446
|
+
expect(result.current.thread.getState().isLoading).toBe(false);
|
|
447
|
+
},
|
|
448
|
+
);
|
|
449
|
+
|
|
360
450
|
it("ignores a thread load superseded by a new thread", async () => {
|
|
361
451
|
const { client } = createMockClient();
|
|
362
452
|
let resolveLoad!: (value: { messages: ThreadMessage[] }) => void;
|