@assistant-ui/react-a2a 0.2.36 → 0.2.38

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,91 +1,103 @@
1
- import { A2AAgentCard, A2AArtifact, A2ASendMessageConfiguration, A2ATask } from "./types.js";
2
- import { A2AClient } from "./A2AClient.js";
3
- import { AppendMessage, AssistantRuntime, ExportedMessageRepository, ThreadHistoryAdapter, ThreadMessage } from "@assistant-ui/core";
4
- //#region src/A2AThreadRuntimeCore.d.ts
1
+ import type { AppendMessage, AssistantRuntime, ExportedMessageRepository, ThreadHistoryAdapter, ThreadMessage, Unstable_RecordToolInteractionOptions } from "@assistant-ui/core";
2
+ import type { A2AClient } from "./A2AClient.js";
3
+ import type { A2AArtifact, A2AAgentCard, A2ASendMessageConfiguration, A2ATask } from "./types.js";
5
4
  export type A2AThreadRuntimeCoreOptions = {
6
- client: A2AClient;
7
- contextId?: string | undefined;
8
- configuration?: A2ASendMessageConfiguration | undefined;
9
- onError?: ((error: Error) => void) | undefined;
10
- onCancel?: (() => void) | undefined;
11
- onArtifactComplete?: ((artifact: A2AArtifact) => void) | undefined;
12
- history?: ThreadHistoryAdapter | undefined;
13
- notifyUpdate: () => void;
5
+ client: A2AClient;
6
+ contextId?: string | undefined;
7
+ configuration?: A2ASendMessageConfiguration | undefined;
8
+ onError?: ((error: Error) => void) | undefined;
9
+ onCancel?: (() => void) | undefined;
10
+ onArtifactComplete?: ((artifact: A2AArtifact) => void) | undefined;
11
+ history?: ThreadHistoryAdapter | undefined;
12
+ notifyUpdate: () => void;
14
13
  };
15
14
  export declare class A2AThreadRuntimeCore {
16
- private client;
17
- private contextId;
18
- private configuration;
19
- private onError;
20
- private onCancel;
21
- private onArtifactComplete;
22
- private history;
23
- private readonly notifyUpdate;
24
- private runtime;
25
- private readonly session;
26
- private isRunningFlag;
27
- private abortController;
28
- private runGeneration;
29
- private pendingError;
30
- private currentTask;
31
- private currentArtifacts;
32
- private agentCardValue;
33
- private readonly assistantHistoryParents;
34
- private readonly recordedHistoryIds;
35
- private _isLoading;
36
- private _loadPromise;
37
- private _historyLoadGeneration;
38
- private _loadRequested;
39
- private _agentCardPromise;
40
- private _agentCardRetryAfter;
41
- private _agentCardRetryDelay;
42
- private _agentCardDiscoveryFailed;
43
- private lastOptionsContextId;
44
- constructor(options: A2AThreadRuntimeCoreOptions);
45
- updateOptions(options: Omit<A2AThreadRuntimeCoreOptions, "notifyUpdate">): void;
46
- /** Thread-boundary reset: applyExternalMessages alone also serves branch
47
- * switches, deletes, and cancel resyncs, which must keep the live context. */
48
- resetContext(): void;
49
- attachRuntime(runtime: AssistantRuntime): void;
50
- detachRuntime(): void;
51
- getRuntime(): AssistantRuntime | undefined;
52
- getMessages(): readonly ThreadMessage[];
53
- getMessageRepository(): ExportedMessageRepository;
54
- getTask(): A2ATask | undefined;
55
- getArtifacts(): readonly A2AArtifact[];
56
- getAgentCard(): A2AAgentCard | undefined;
57
- isRunning(): boolean;
58
- get isLoading(): boolean;
59
- private loadAgentCard;
60
- private waitForAgentCard;
61
- __internal_load(): Promise<void>;
62
- append(message: AppendMessage): Promise<void>;
63
- edit(message: AppendMessage): Promise<void>;
64
- reload(parentId: string | null, _config?: {
65
- runConfig?: Record<string, unknown>;
66
- }): Promise<void>;
67
- cancel(): Promise<void>;
68
- private appendLinearChain;
69
- private finalizeExternalApply;
70
- applyExternalMessages(messages: readonly ThreadMessage[]): void;
71
- private startRun;
72
- private runStreaming;
73
- private runSync;
74
- private handleStreamEvent;
75
- private handleStatusUpdate;
76
- private handleArtifactUpdate;
77
- private handleMessage;
78
- private handleTaskSnapshot;
79
- private insertAssistantPlaceholder;
80
- private updateAssistantContent;
81
- private updateAssistantStatus;
82
- private getAssistantStatus;
83
- private setRunning;
84
- private finishRun;
85
- private recordHistoryEntry;
86
- private markPendingAssistantHistory;
87
- private persistAssistantHistory;
88
- private appendHistoryItem;
15
+ private client;
16
+ private contextId;
17
+ private configuration;
18
+ private onError;
19
+ private onCancel;
20
+ private onArtifactComplete;
21
+ private history;
22
+ private readonly notifyUpdate;
23
+ private runtime;
24
+ private readonly session;
25
+ private isRunningFlag;
26
+ private abortController;
27
+ private runGeneration;
28
+ private pendingError;
29
+ private currentTask;
30
+ private currentArtifacts;
31
+ private a2uiState;
32
+ private readonly a2uiMessageIds;
33
+ private agentCardValue;
34
+ private readonly assistantHistoryParents;
35
+ private readonly recordedHistoryIds;
36
+ private readonly historyWrites;
37
+ private _isLoading;
38
+ private _loadPromise;
39
+ private _historyLoadGeneration;
40
+ private _loadRequested;
41
+ private _agentCardPromise;
42
+ private _agentCardRetryAfter;
43
+ private _agentCardRetryDelay;
44
+ private _agentCardDiscoveryFailed;
45
+ private lastOptionsContextId;
46
+ constructor(options: A2AThreadRuntimeCoreOptions);
47
+ updateOptions(options: Omit<A2AThreadRuntimeCoreOptions, "notifyUpdate">): void;
48
+ /** Thread-boundary reset: applyExternalMessages alone also serves branch
49
+ * switches, deletes, and cancel resyncs, which must keep the live context. */
50
+ resetContext(): void;
51
+ attachRuntime(runtime: AssistantRuntime): void;
52
+ detachRuntime(): void;
53
+ getRuntime(): AssistantRuntime | undefined;
54
+ getMessages(): readonly ThreadMessage[];
55
+ getMessageRepository(): ExportedMessageRepository;
56
+ recordToolInteraction({ messageId, toolCallId, interaction, }: Unstable_RecordToolInteractionOptions): Promise<void>;
57
+ getTask(): A2ATask | undefined;
58
+ getArtifacts(): readonly A2AArtifact[];
59
+ getAgentCard(): A2AAgentCard | undefined;
60
+ isRunning(): boolean;
61
+ get isLoading(): boolean;
62
+ private loadAgentCard;
63
+ private waitForAgentCard;
64
+ __internal_load(): Promise<void>;
65
+ append(message: AppendMessage): Promise<void>;
66
+ appendVoiceTranscript(message: ThreadMessage): void;
67
+ edit(message: AppendMessage): Promise<void>;
68
+ reload(parentId: string | null, _config?: {
69
+ runConfig?: Record<string, unknown>;
70
+ }): Promise<void>;
71
+ cancel(): Promise<void>;
72
+ private appendLinearChain;
73
+ private finalizeExternalApply;
74
+ applyExternalMessages(messages: readonly ThreadMessage[]): void;
75
+ private startRun;
76
+ private runStreaming;
77
+ private runSync;
78
+ private handleStreamEvent;
79
+ private handleStatusUpdate;
80
+ private handleArtifactUpdate;
81
+ private handleMessage;
82
+ private handleTaskSnapshot;
83
+ private insertAssistantPlaceholder;
84
+ private updateAssistantContent;
85
+ private applyA2uiMessage;
86
+ private applyA2uiParts;
87
+ private a2uiSurfaceParts;
88
+ private withA2uiSurfaces;
89
+ private withPreservedToolInteractions;
90
+ private rebuildAssistantA2uiSurfaces;
91
+ private updateAssistantArtifacts;
92
+ private updateAssistantStatus;
93
+ private getAssistantStatus;
94
+ private isPersistableAssistantStatus;
95
+ private setRunning;
96
+ private finishRun;
97
+ private recordHistoryEntry;
98
+ private markPendingAssistantHistory;
99
+ private persistAssistantHistory;
100
+ private appendHistoryItem;
101
+ private chainHistoryWrite;
89
102
  }
90
- //#endregion
91
103
  //# sourceMappingURL=A2AThreadRuntimeCore.d.ts.map
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"A2AThreadRuntimeCore.d.ts","sourceRoot":"","sources":["../src/A2AThreadRuntimeCore.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,16 @@
1
1
  "use client";
2
- import { a2aMessageToContent, isTerminalTaskState, taskStateToMessageStatus, threadMessageToA2AMessage } from "./conversions.js";
3
- import { createMessageRepositorySession, invokeUserCallback } from "@assistant-ui/core/internal";
2
+ import { a2aMessageToContent, a2uiPartsToOperations, isTerminalTaskState, taskStateToMessageStatus, threadMessageToA2AMessage } from "./conversions.js";
3
+ import { appendToolInteraction, createMessageRepositorySession, invokeUserCallback } from "@assistant-ui/core/internal";
4
4
  import { fromThreadMessageLike, generateId } from "@assistant-ui/core";
5
+ import { applyA2uiOperations, convertSurfaceToUISpec, surfaceToOperations } from "@assistant-ui/react-generative-ui/a2ui";
5
6
  //#region src/A2AThreadRuntimeCore.ts
6
7
  const INITIAL_AGENT_CARD_RETRY_DELAY_MS = 5e3;
7
8
  const MAX_AGENT_CARD_RETRY_DELAY_MS = 3e5;
9
+ const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
10
+ const storedArtifact = (artifact) => ({
11
+ ...artifact,
12
+ parts: artifact.parts.map(({ raw: _raw, ...part }) => part)
13
+ });
8
14
  const FALLBACK_USER_STATUS = {
9
15
  type: "complete",
10
16
  reason: "unknown"
@@ -35,9 +41,12 @@ var A2AThreadRuntimeCore = class {
35
41
  pendingError = null;
36
42
  currentTask;
37
43
  currentArtifacts = [];
44
+ a2uiState = /* @__PURE__ */ new Map();
45
+ a2uiMessageIds = /* @__PURE__ */ new Set();
38
46
  agentCardValue;
39
47
  assistantHistoryParents = /* @__PURE__ */ new Map();
40
48
  recordedHistoryIds = /* @__PURE__ */ new Set();
49
+ historyWrites = /* @__PURE__ */ new Map();
41
50
  _isLoading = false;
42
51
  _loadPromise;
43
52
  _historyLoadGeneration = 0;
@@ -103,6 +112,26 @@ var A2AThreadRuntimeCore = class {
103
112
  getMessageRepository() {
104
113
  return this.session.export();
105
114
  }
115
+ async recordToolInteraction({ messageId, toolCallId, interaction }) {
116
+ const message = this.session.tryGetMessage(messageId)?.message;
117
+ if (!message) throw new Error("Tried to record a tool interaction on a non-existing message");
118
+ if (message.role !== "assistant") throw new Error("Tried to record a tool interaction on a non-assistant message");
119
+ const target = message.content.find((part) => part.type === "tool-call" && part.toolCallId === toolCallId);
120
+ if (!target || target.type !== "tool-call") throw new Error("Tried to record a tool interaction on a non-existing tool call");
121
+ if (this.session.updateMessage(messageId, (current) => {
122
+ if (current.role !== "assistant") return current;
123
+ return {
124
+ ...current,
125
+ content: current.content.map((part) => part.type === "tool-call" && part.toolCallId === toolCallId ? {
126
+ ...part,
127
+ unstable_interactions: appendToolInteraction(part.unstable_interactions, interaction)
128
+ } : part)
129
+ };
130
+ })) {
131
+ this.notifyUpdate();
132
+ this.persistAssistantHistory(messageId);
133
+ }
134
+ }
106
135
  getTask() {
107
136
  return this.currentTask;
108
137
  }
@@ -159,7 +188,7 @@ var A2AThreadRuntimeCore = class {
159
188
  this._isLoading = true;
160
189
  const generation = this._historyLoadGeneration;
161
190
  const historyPromise = this.history.load();
162
- this._loadPromise = Promise.all([historyPromise, agentCardPromise]).then(([repo]) => {
191
+ this._loadPromise = historyPromise.then((repo) => {
163
192
  if (generation !== this._historyLoadGeneration) return;
164
193
  if (repo) {
165
194
  this.session.applyExternalMessageRepository(repo);
@@ -187,6 +216,13 @@ var A2AThreadRuntimeCore = class {
187
216
  if (!startRun) return;
188
217
  await this.startRun(threadMessage);
189
218
  }
219
+ appendVoiceTranscript(message) {
220
+ const parentId = this.session.headId;
221
+ this.session.addOrUpdateMessage(parentId, message);
222
+ this.session.switchToBranch(message.id);
223
+ this.notifyUpdate();
224
+ this.recordHistoryEntry(parentId, message);
225
+ }
190
226
  async edit(message) {
191
227
  await this.append(message);
192
228
  }
@@ -226,6 +262,8 @@ var A2AThreadRuntimeCore = class {
226
262
  for (const { message } of this.getMessageRepository().messages) this.recordedHistoryIds.add(message.id);
227
263
  this.currentTask = void 0;
228
264
  this.currentArtifacts = [];
265
+ this.a2uiState = /* @__PURE__ */ new Map();
266
+ this.a2uiMessageIds.clear();
229
267
  this.notifyUpdate();
230
268
  }
231
269
  applyExternalMessages(messages) {
@@ -267,6 +305,8 @@ var A2AThreadRuntimeCore = class {
267
305
  });
268
306
  if (this.currentTask && isTerminalTaskState(this.currentTask.status.state)) this.currentTask = void 0;
269
307
  this.currentArtifacts = [];
308
+ this.a2uiState = /* @__PURE__ */ new Map();
309
+ this.a2uiMessageIds.clear();
270
310
  const assistantParentId = userThreadMessage.id;
271
311
  const assistantId = this.insertAssistantPlaceholder(assistantParentId);
272
312
  this.markPendingAssistantHistory(assistantId, assistantParentId);
@@ -343,7 +383,7 @@ var A2AThreadRuntimeCore = class {
343
383
  this.handleStatusUpdate(assistantId, event.event);
344
384
  break;
345
385
  case "artifactUpdate":
346
- this.handleArtifactUpdate(event.event);
386
+ this.handleArtifactUpdate(assistantId, event.event);
347
387
  break;
348
388
  case "message":
349
389
  this.handleMessage(assistantId, event.message);
@@ -363,6 +403,7 @@ var A2AThreadRuntimeCore = class {
363
403
  };
364
404
  if (event.contextId) this.contextId = event.contextId;
365
405
  if (event.status.message) {
406
+ this.applyA2uiMessage(event.status.message);
366
407
  const content = a2aMessageToContent(event.status.message);
367
408
  this.updateAssistantContent(assistantId, content);
368
409
  }
@@ -370,7 +411,7 @@ var A2AThreadRuntimeCore = class {
370
411
  this.updateAssistantStatus(assistantId, status);
371
412
  this.notifyUpdate();
372
413
  }
373
- handleArtifactUpdate(event) {
414
+ handleArtifactUpdate(assistantId, event) {
374
415
  const { append, lastChunk } = event;
375
416
  const artifact = normalizeArtifact(event.artifact);
376
417
  const existingIdx = this.currentArtifacts.findIndex((a) => a.artifactId === artifact.artifactId);
@@ -397,11 +438,15 @@ var A2AThreadRuntimeCore = class {
397
438
  updated = artifact;
398
439
  this.currentArtifacts = [...this.currentArtifacts, updated];
399
440
  }
441
+ this.applyA2uiParts(artifact.parts);
442
+ this.updateAssistantArtifacts(assistantId);
443
+ this.rebuildAssistantA2uiSurfaces(assistantId);
400
444
  if (lastChunk) invokeRuntimeCallback("onArtifactComplete", this.onArtifactComplete, updated);
401
445
  this.notifyUpdate();
402
446
  }
403
447
  handleMessage(assistantId, message) {
404
448
  if (message.role !== "agent") return;
449
+ this.applyA2uiMessage(message);
405
450
  const content = a2aMessageToContent(message);
406
451
  this.updateAssistantContent(assistantId, content);
407
452
  this.notifyUpdate();
@@ -415,11 +460,21 @@ var A2AThreadRuntimeCore = class {
415
460
  ...history === void 0 ? {} : { history }
416
461
  };
417
462
  if (task.contextId) this.contextId = task.contextId;
418
- if (artifacts) this.currentArtifacts = artifacts;
463
+ const isCompleteSnapshot = history !== void 0 && artifacts !== void 0;
464
+ const artifactsToApply = isCompleteSnapshot ? artifacts : artifacts?.filter((artifact) => !this.currentArtifacts.some(({ artifactId }) => artifactId === artifact.artifactId));
465
+ if (isCompleteSnapshot) {
466
+ this.currentArtifacts = artifacts;
467
+ this.a2uiState = /* @__PURE__ */ new Map();
468
+ this.a2uiMessageIds.clear();
469
+ } else if (artifactsToApply) this.currentArtifacts = [...this.currentArtifacts, ...artifactsToApply];
470
+ for (const message of history ?? []) if (message.role === "agent") this.applyA2uiMessage(message, true);
471
+ for (const artifact of artifactsToApply ?? []) this.applyA2uiParts(artifact.parts);
472
+ if (task.status.message) this.applyA2uiMessage(task.status.message, true);
473
+ if (isCompleteSnapshot || artifactsToApply?.length) this.updateAssistantArtifacts(assistantId);
419
474
  if (task.status.message) {
420
475
  const content = a2aMessageToContent(task.status.message);
421
476
  this.updateAssistantContent(assistantId, content);
422
- }
477
+ } else this.rebuildAssistantA2uiSurfaces(assistantId);
423
478
  const status = taskStateToMessageStatus(task.status.state);
424
479
  this.updateAssistantStatus(assistantId, status);
425
480
  this.notifyUpdate();
@@ -450,10 +505,86 @@ var A2AThreadRuntimeCore = class {
450
505
  if (message.role !== "assistant") return message;
451
506
  return {
452
507
  ...message,
453
- content
508
+ content: this.withA2uiSurfaces(content, message.content)
454
509
  };
455
510
  });
456
511
  }
512
+ applyA2uiMessage(message, replay = false) {
513
+ if (message.messageId) {
514
+ if (replay && this.a2uiMessageIds.has(message.messageId)) return;
515
+ this.a2uiMessageIds.add(message.messageId);
516
+ }
517
+ this.applyA2uiParts(message.parts);
518
+ }
519
+ applyA2uiParts(parts) {
520
+ const operations = a2uiPartsToOperations(parts);
521
+ if (operations.length === 0) return;
522
+ this.a2uiState = applyA2uiOperations(this.a2uiState, operations).state;
523
+ }
524
+ a2uiSurfaceParts() {
525
+ const parts = [];
526
+ for (const [surfaceId, surface] of this.a2uiState) {
527
+ const { spec } = convertSurfaceToUISpec(surface);
528
+ if (!spec) continue;
529
+ parts.push({
530
+ type: "tool-call",
531
+ toolCallId: `a2ui:${surfaceId}`,
532
+ toolName: "present",
533
+ args: spec,
534
+ argsText: JSON.stringify(spec),
535
+ result: {},
536
+ artifact: { a2ui: surfaceToOperations(surface) }
537
+ });
538
+ }
539
+ return parts;
540
+ }
541
+ withA2uiSurfaces(content, previousContent = content) {
542
+ const preserved = content.filter((part) => !(part.type === "tool-call" && part.toolCallId.startsWith("a2ui:")));
543
+ const interactionSource = [...previousContent, ...content];
544
+ return this.withPreservedToolInteractions(interactionSource, [...preserved, ...this.a2uiSurfaceParts()]);
545
+ }
546
+ withPreservedToolInteractions(previousContent, content) {
547
+ const interactions = /* @__PURE__ */ new Map();
548
+ for (const part of previousContent) if (part.type === "tool-call" && part.unstable_interactions !== void 0) interactions.set(part.toolCallId, part.unstable_interactions);
549
+ if (interactions.size === 0) return content;
550
+ return content.map((part) => {
551
+ if (part.type !== "tool-call" || part.unstable_interactions !== void 0) return part;
552
+ const unstable_interactions = interactions.get(part.toolCallId);
553
+ return unstable_interactions === void 0 ? part : {
554
+ ...part,
555
+ unstable_interactions
556
+ };
557
+ });
558
+ }
559
+ rebuildAssistantA2uiSurfaces(messageId) {
560
+ if (this.session.updateMessage(messageId, (message) => {
561
+ if (message.role !== "assistant") return message;
562
+ return {
563
+ ...message,
564
+ content: this.withA2uiSurfaces(message.content)
565
+ };
566
+ })) this.notifyUpdate();
567
+ }
568
+ updateAssistantArtifacts(messageId) {
569
+ if (this.session.updateMessage(messageId, (message) => {
570
+ if (message.role !== "assistant") return message;
571
+ const custom = message.metadata.custom;
572
+ const a2a = isRecord$1(custom.a2a) ? custom.a2a : {};
573
+ return {
574
+ ...message,
575
+ metadata: {
576
+ ...message.metadata,
577
+ custom: {
578
+ ...custom,
579
+ a2a: {
580
+ ...a2a,
581
+ artifacts: this.currentArtifacts.map(storedArtifact)
582
+ }
583
+ }
584
+ }
585
+ };
586
+ })) this.notifyUpdate();
587
+ }
457
588
  updateAssistantStatus(messageId, status) {
458
589
  if (this.session.updateMessage(messageId, (message) => {
459
590
  if (message.role !== "assistant") return message;
@@ -463,7 +594,7 @@ var A2AThreadRuntimeCore = class {
463
594
  };
464
595
  })) {
465
596
  this.notifyUpdate();
466
- if (status.type === "complete" || status.type === "incomplete") this.persistAssistantHistory(messageId);
597
+ if (this.isPersistableAssistantStatus(status)) this.persistAssistantHistory(messageId);
467
598
  }
468
599
  }
469
600
  getAssistantStatus(messageId) {
@@ -471,6 +602,9 @@ var A2AThreadRuntimeCore = class {
471
602
  if (msg?.role !== "assistant") return void 0;
472
603
  return msg.status;
473
604
  }
605
+ isPersistableAssistantStatus(status) {
606
+ return status.type === "complete" || status.type === "incomplete" || status.type === "requires-action" && status.reason === "interrupt";
607
+ }
474
608
  setRunning(running) {
475
609
  this.isRunningFlag = running;
476
610
  this.notifyUpdate();
@@ -481,7 +615,9 @@ var A2AThreadRuntimeCore = class {
481
615
  this.setRunning(false);
482
616
  }
483
617
  recordHistoryEntry(parentId, message) {
484
- this.appendHistoryItem(parentId, message);
618
+ this.appendHistoryItem(parentId, message)?.catch((error) => {
619
+ console.error("[react-a2a] failed to append history entry", error);
620
+ });
485
621
  }
486
622
  markPendingAssistantHistory(messageId, parentId) {
487
623
  if (!this.history) return;
@@ -489,23 +625,74 @@ var A2AThreadRuntimeCore = class {
489
625
  }
490
626
  persistAssistantHistory(messageId) {
491
627
  if (!this.history) return;
628
+ const messageData = this.session.tryGetMessage(messageId);
492
629
  const parentId = this.assistantHistoryParents.get(messageId);
493
- if (parentId === void 0) return;
494
- const message = this.session.tryGetMessage(messageId)?.message;
630
+ if (parentId === void 0 && !this.recordedHistoryIds.has(messageId)) return;
631
+ const resolvedParentId = parentId === void 0 ? messageData?.parentId : parentId;
632
+ if (resolvedParentId === void 0) return;
633
+ const message = messageData?.message;
495
634
  if (!message || message.role !== "assistant") return;
496
- if (message.status?.type !== "complete" && message.status?.type !== "incomplete") return;
497
- this.assistantHistoryParents.delete(messageId);
498
- this.appendHistoryItem(parentId, message);
635
+ if (!this.isPersistableAssistantStatus(message.status)) return;
636
+ const isPausing = message.status.type === "requires-action";
637
+ if (isPausing && !this.history.update) return;
638
+ if (this.recordedHistoryIds.has(messageId)) {
639
+ if (this.history.update) {
640
+ const update = this.history.update.bind(this.history);
641
+ const write = this.chainHistoryWrite(messageId, () => update({
642
+ parentId: resolvedParentId,
643
+ message
644
+ }));
645
+ if (!isPausing) this.assistantHistoryParents.delete(messageId);
646
+ write.then(() => {
647
+ this.recordedHistoryIds.add(messageId);
648
+ }, (error) => {
649
+ const pending = this.historyWrites.get(messageId);
650
+ if (pending === void 0 || pending === write) this.assistantHistoryParents.set(messageId, resolvedParentId);
651
+ console.error("[react-a2a] failed to update history entry", error);
652
+ });
653
+ return;
654
+ }
655
+ if (!isPausing) this.assistantHistoryParents.delete(messageId);
656
+ return;
657
+ }
658
+ const write = this.appendHistoryItem(resolvedParentId, message);
659
+ if (!write) return;
660
+ if (!isPausing) this.assistantHistoryParents.delete(messageId);
661
+ write.catch((error) => {
662
+ const pending = this.historyWrites.get(messageId);
663
+ if (pending === void 0 || pending === write) this.assistantHistoryParents.set(messageId, resolvedParentId);
664
+ console.error("[react-a2a] failed to append history entry", error);
665
+ });
499
666
  }
500
667
  appendHistoryItem(parentId, message) {
501
668
  if (!this.history || this.recordedHistoryIds.has(message.id)) return;
502
669
  this.recordedHistoryIds.add(message.id);
503
- this.history.append({
670
+ const append = this.history.append.bind(this.history);
671
+ const write = this.chainHistoryWrite(message.id, () => append({
504
672
  parentId,
505
673
  message
506
- }).catch(() => {
674
+ }));
675
+ write.catch(() => {
507
676
  this.recordedHistoryIds.delete(message.id);
508
677
  });
678
+ return write;
679
+ }
680
+ chainHistoryWrite(id, write) {
681
+ const pending = this.historyWrites.get(id);
682
+ let next;
683
+ if (pending) next = pending.then(write, write);
684
+ else try {
685
+ next = Promise.resolve(write());
686
+ } catch (error) {
687
+ next = Promise.reject(error);
688
+ }
689
+ this.historyWrites.set(id, next);
690
+ next.then(() => {
691
+ if (this.historyWrites.get(id) === next) this.historyWrites.delete(id);
692
+ }, () => {
693
+ if (this.historyWrites.get(id) === next) this.historyWrites.delete(id);
694
+ });
695
+ return next;
509
696
  }
510
697
  };
511
698
  //#endregion