@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.
@@ -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
@@ -1 +1 @@
1
- {"version":3,"file":"A2AClient.d.ts","names":[],"sources":["../src/A2AClient.ts"],"mappings":";;KAmBY;EACV;;EAEA;;EAEA;EACA,UACI,gCACO,yBAAyB,QAAQ;;EAG5C;;EAEA,eACI,KAAK;;cAIE,iBAAiB;EAC5B;EACA;EACA;EAEY,YAAA,MAAM;;cA2oBP;UACH;UACA;UACA;UACA;UACA;UAIA;EAII,YAAA,SAAS;UAkBb;UAIM;UAqBA;UAyBA;EAoCR,aAAa,SAAS,cAAc,QAAQ;EAe5C,qBAAqB,SAAS,cAAc,QAAQ;EAUpD,YACJ,SAAS,YACT,gBAAgB,6BAChB,WAAW,yBACX,SAAS,cACR,QAAQ,UAAU;EAmBd,cACL,SAAS,YACT,gBAAgB,6BAChB,WAAW,yBACX,SAAS,cACR,eAAe;EA6BZ,QACJ,gBACA,wBACA,SAAS,cACR,QAAQ;EAcL,UACJ,UAAU,qBACV,SAAS,cACR,QAAQ;EAqBL,WACJ,gBACA,WAAW,yBACX,SAAS,cACR,QAAQ;EAaJ,gBACL,gBACA,SAAS,cACR,eAAe;EAqBZ,iCACJ,QAAQ,+BACR,SAAS,cACR,QAAQ;EAiBL,8BACJ,gBACA,kBACA,SAAS,cACR,QAAQ;EAWL,gCACJ,gBACA;IAAY;IAAmB;KAC/B,SAAS,cACR,QAAQ;EAaL,iCACJ,gBACA,kBACA,SAAS,cACR;UAmBY"}
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":";;;;KAsCY;EACV,QAAQ;EACR;EACA,gBAAgB;EAChB,YAAY,OAAO;EACnB;EACA,uBAAuB,UAAU;EACjC,UAAU;EACV;;cAyBW;UACH;UACA;UACA;UACA;UACA;UACA;UACA;mBACS;UAET;mBACS;UACT;UACA;UACA;UAGA;UACA;UACA;mBAGS;mBACA;UACT;UACA;UACA;UACA;UACA;UACA;UACA;UAEA;EAEI,YAAA,SAAS;EAYrB,cAAc,SAAS,KAAK;;;EA8B5B;EAcA,cAAc,SAAS;EAIvB;EASA,cAAc;EAId,wBAAwB;EAIxB,wBAAwB;EAIxB,WAAW;EAIX,yBAAyB;EAIzB,gBAAgB;EAIhB;MAII;UAII;UAwBM;EAoBd,mBAAmB;EAkCb,OAAO,SAAS,gBAAgB;EAuBhC,KAAK,SAAS,gBAAgB;EAI9B,OACJ,yBACA;IAAW,YAAY;MACtB;EAaG,UAAU;UAiBR;UAgBA;EAWR,sBAAsB,mBAAmB;UAmC3B;UA+EA;UA4CA;UA4BN;UAiBA;UA6BA;UA0CA;UAQA;UAuCA;UAsBA;UAUA;UAaA;UAQA;UAKA;UAQA;UAIA;UAQA;UAeA"}
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 (this.currentTask?.id) try {
196
- const updated = await this.client.cancelTask(this.currentTask.id);
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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"}
@@ -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
@@ -1 +1 @@
1
- {"version":3,"file":"a2aExtras.d.ts","names":[],"sources":["../src/a2aExtras.ts"],"mappings":";;cAGa,8CAAS,cAAA"}
1
+ {"version":3,"file":"a2aExtras.d.ts","names":[],"sources":["../src/a2aExtras.ts"],"mappings":";;qBAGa,8CAAS,cAAA"}
@@ -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":";;;iBAiBgB,iBACd,MAAM,UACL;iBAyCa,kBACd,OAAO,YACN;iBAgBa,oBAAoB,OAAO;iBAI3B,uBAAuB,OAAO;iBAI9B,yBAAyB,OAAO,eAAe;iBAoB/C,uBACd,SAAS;EACP;EACA;EACA;EACA;EACA;EACA;EACA;EACA;IAAU;IAAc;;IAE1B,4BACC;iBA4Ea,oBACd,SAAS,aACR;iBAIa,0BACd,SAAS,eACT;EACE;EACA;IAED"}
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
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.d.ts","names":[],"sources":["../src/hooks.ts"],"mappings":";;;cAKa,kBAAA;;cAGA,gCAAA;;cAGA,uBAAA"}
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
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"mappings":";;;cAgBa;KAED;KAEA;EACV;EACA;EACA;EACA;EAEA,WAAW;EACX;EACA;;KAGU;EACV;EACA;EACA;EACA,MAAM;EACN,OAAO;EACP,WAAW;EACX;EACA;;KAGU;KAWA;EACV,OAAO;EACP,UAAU;EACV;;KAGU;EACV;EACA;EACA;EACA,OAAO;EACP,WAAW;EACX;;KAGU;EACV;EACA;EACA,QAAQ;EACR,YAAY;EACZ,UAAU;EACV,WAAW;;KAID;EACV;EACA;EACA,QAAQ;EACR,WAAW;;KAGD;EACV;EACA;EACA,UAAU;EACV;EACA;EACA,WAAW;;KAGD;EACN;EAAc,MAAM;;EACpB;EAAiB,SAAS;;EAC1B;EAAsB,OAAO;;EAC7B;EAAwB,OAAO;;KAEzB;EACV;EACA;;KAGU;EACV;EACA;EACA;EACA;EACA;EACA,iBAAiB;;KAGP;EACV,SAAS;EACT;;KAGU;EACV;EACA,6BAA6B;EAC7B;EACA;;KAGU;EACV;EACA,SAAS;EACT;EACA;EACA;EACA;EACA;;KAGU;EACV,OAAO;EACP;EACA;EACA;;KAIU;EACV;EACA;EACA;EACA;;KAGU;EACV;EACA;EACA;;KAGU;EACV;EACA;EACA;;KAGU;EACV;EACA;EACA;EACA,QAAQ;EACR;;KAGU;EACV;EACA;EACA,QAAQ;;KAGE;EACV;EACA;EACA;EACA,QAAQ;;;KAIE;EACV;EACA;EACA,SAAS;;;KAIC;EACV;EACA;EACA,SAAS;;KAGC;EACV,oBAAoB;EACpB,oBAAoB;;EAEpB,WAAW;;EAEX,WAAW;EACX,aAAa;;KAGH;EACV;EACA,OAAO;EACP;;KAGU;EACV;EACA;;KAGU;EACV;;KAGU;EACV,uBAAuB;EACvB,yBAAyB;EACzB,uBAAuB;EACvB,8BAA8B;EAC9B,qBAAqB;;KAGX;EACV,SAAS;IAAiB;;;KAIhB;EACV;EACA;EACA,SAAS;;KAGC;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA,uBAAuB;;KAGb;EACV;EACA;EACA,aACI;IACE;IACA;IACA;IACA,SAAS;;EAGf;;KAGU;EACV;EACA;EACA;EACA;;KAGU;EACV;EACA;EACA,qBAAqB;EACrB;IAEM;IACA;;EAGN;EACA;EACA,cAAc;EACd,kBAAkB,eAAe;EACjC,uBAAuB;EACvB;EACA;EACA,QAAQ;EACR,aAAa;EACb;;;KAIU;EACV,MAAM;EACN,oBAAoB;EACpB,WAAW;;KAGD;EACV;EACA,4BAA4B;EAC5B,oBAAoB,qBAAqB;IACvC,mBAAmB;;;KAIX,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"}
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"}
@@ -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":";;;iBA2CgB,cAAc,SAAS,uBAAuB"}
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.35",
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.19",
33
- "@assistant-ui/store": "^0.3.13",
34
- "assistant-stream": "^0.3.43"
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.27",
47
- "@testing-library/dom": "^10.4.1",
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.2.18",
49
+ "@types/react": "^19.3.0",
50
50
  "jsdom": "^30.0.1",
51
- "react": "^19.2.8",
52
- "react-dom": "^19.2.8",
53
- "vitest": "^5.0.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 { AppendMessage, ThreadMessage } from "@assistant-ui/core";
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 (this.currentTask?.id) {
349
+ if (task?.id) {
336
350
  try {
337
- const updated = await this.client.cancelTask(this.currentTask.id);
338
- this.currentTask = updated;
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 { ThreadMessage } from "@assistant-ui/core";
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;