@assistant-ui/react-ag-ui 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +2 -0
  2. package/dist/runtime/AgUiThreadController.d.ts +11 -0
  3. package/dist/runtime/AgUiThreadController.d.ts.map +1 -1
  4. package/dist/runtime/AgUiThreadController.js +212 -22
  5. package/dist/runtime/AgUiThreadController.js.map +1 -1
  6. package/dist/runtime/adapter/conversions.d.ts +9 -1
  7. package/dist/runtime/adapter/conversions.d.ts.map +1 -1
  8. package/dist/runtime/adapter/conversions.js +184 -9
  9. package/dist/runtime/adapter/conversions.js.map +1 -1
  10. package/dist/runtime/adapter/run-aggregator.d.ts +1 -0
  11. package/dist/runtime/adapter/run-aggregator.d.ts.map +1 -1
  12. package/dist/runtime/adapter/run-aggregator.js +34 -12
  13. package/dist/runtime/adapter/run-aggregator.js.map +1 -1
  14. package/dist/runtime/adapter/subscriber.d.ts +0 -1
  15. package/dist/runtime/adapter/subscriber.d.ts.map +1 -1
  16. package/dist/runtime/adapter/subscriber.js +1 -8
  17. package/dist/runtime/adapter/subscriber.js.map +1 -1
  18. package/dist/runtime/event-parser.d.ts.map +1 -1
  19. package/dist/runtime/event-parser.js +1 -0
  20. package/dist/runtime/event-parser.js.map +1 -1
  21. package/dist/runtime/types.d.ts +2 -0
  22. package/dist/runtime/types.d.ts.map +1 -1
  23. package/dist/runtime/types.js +2 -1
  24. package/dist/runtime/types.js.map +1 -1
  25. package/package.json +5 -5
  26. package/src/runtime/AgUiThreadController.disconnect.test.ts +914 -0
  27. package/src/runtime/AgUiThreadController.interrupt-history.test.ts +392 -0
  28. package/src/runtime/AgUiThreadController.replay.test.ts +8 -1
  29. package/src/runtime/AgUiThreadController.snapshot-head-move.test.ts +167 -0
  30. package/src/runtime/AgUiThreadController.subagent-snapshot.test.ts +854 -0
  31. package/src/runtime/AgUiThreadController.test.ts +40 -0
  32. package/src/runtime/AgUiThreadController.ts +459 -27
  33. package/src/runtime/adapter/conversions.content.test.ts +129 -0
  34. package/src/runtime/adapter/conversions.subagent-messages.test.ts +550 -0
  35. package/src/runtime/adapter/conversions.ts +331 -20
  36. package/src/runtime/adapter/run-aggregator.metadata.test.ts +151 -0
  37. package/src/runtime/adapter/run-aggregator.ts +52 -9
  38. package/src/runtime/adapter/subscriber.test.ts +1 -1
  39. package/src/runtime/adapter/subscriber.ts +1 -6
  40. package/src/runtime/event-parser.ts +3 -0
  41. package/src/runtime/types.ts +5 -0
package/README.md CHANGED
@@ -37,6 +37,8 @@ export function Provider({ children }: { children: React.ReactNode }) {
37
37
 
38
38
  An AG-UI backend that runs subagents (the agents-as-tools pattern) emits `SUBAGENT_STARTED` / `SUBAGENT_FINISHED` / `SUBAGENT_ERROR` plus a `subagentRunId` on the events each subagent produces. This adapter groups that activity into one nested assistant message per subagent run and attaches it to the spawning tool call as `ToolCallMessagePart.messages`, joined on `SUBAGENT_STARTED.parentToolCallId`. `PartPrimitive.Messages` renders it without extra wiring, and the subagent's name, description, result, and error code ride on that message's `agui` metadata.
39
39
 
40
+ A `MESSAGES_SNAPSHOT` whose messages carry `subagentRunId` adds to the same nested messages instead of rendering them a second time in the parent thread: a run's messages attach to the tool call its `SUBAGENT_STARTED.parentToolCallId` named in this session, merged with what streamed live. A run that was never announced this session, and its messages, stay in the parent thread. The live grouping only reaches spawning calls of the current run, so a subagent resumed in a later run still streams into the parent thread, and after that run's snapshot can show both nested and in the parent thread.
41
+
40
42
  A subagent that names no reachable spawning call (`parentToolCallId` is optional, may name a call this run never saw, or two runs may name each other) has nowhere to nest, so its output renders in the parent thread instead of being dropped. This matches the downgrade the protocol's own pre-subagent compatibility middleware performs.
41
43
 
42
44
  A subagent's frontend-executed tool calls work the same way the root agent's do: a call nested on `ToolCallMessagePart.messages` is reachable by `getPendingToolCalls()`, resolves through `addToolResult`, and its result rides the resume as a `tool` record on the spawning assistant record, the same flattened wire shape these calls had before subagent attribution.
@@ -44,6 +44,7 @@ export declare class AgUiThreadController {
44
44
  private activeRunAgent;
45
45
  private history;
46
46
  private readonly assistantHistoryParents;
47
+ private readonly subagentParentToolCallIds;
47
48
  private readonly snapshotHistoryIds;
48
49
  private readonly persistedHistoryIds;
49
50
  private readonly historyWrites;
@@ -156,6 +157,15 @@ export declare class AgUiThreadController {
156
157
  private preserveToolInteractions;
157
158
  private mergeSnapshotAssistantContent;
158
159
  private reconcileSnapshotNestedContent;
160
+ /**
161
+ * Merges one tool call's nested transcripts message by message. The incoming
162
+ * copy is the base for tool calls and reasoning; resolved results and calls
163
+ * only the prior copy has carry over, and the prior text wins when it extends
164
+ * the incoming text. Importing a snapshot keeps prior messages it lacks, a
165
+ * prior status that is running or incomplete, and the prior status when the
166
+ * snapshot waits on calls the merged content has already resolved.
167
+ */
168
+ private mergeNestedTranscripts;
159
169
  private mergeAssistantMetadata;
160
170
  private handleEvent;
161
171
  private updateActiveAssistantState;
@@ -167,6 +177,7 @@ export declare class AgUiThreadController {
167
177
  private isPersistableStatus;
168
178
  private recordHistoryEntry;
169
179
  private markPendingAssistantHistory;
180
+ private persistSettledAssistant;
170
181
  private persistAssistantHistory;
171
182
  private appendHistoryItem;
172
183
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AgUiThreadController.d.ts","sourceRoot":"","sources":["../../src/runtime/AgUiThreadController.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AgUiThreadController.d.ts","sourceRoot":"","sources":["../../src/runtime/AgUiThreadController.ts"],"names":[],"mappings":"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"}
@@ -3,8 +3,8 @@ import { createAgUiThreadState, reduceAgUiThreadState } from "./agUiThreadState.
3
3
  import { readMcpAppResourceUri, toolContentText } from "./mcp-tool-result.js";
4
4
  import { AG_UI_METADATA_NAMESPACE } from "./types.js";
5
5
  import { buildToolApprovalResume, preserveToolApprovalDecision, projectAgUiToolApprovals, withSettledToolApprovals, withToolApprovalDecision } from "./adapter/tool-approval.js";
6
+ import { fromAgUiSnapshotMessages, toAgUiMessages, toAgUiTools, withAgUiPartMessageMetadata } from "./adapter/conversions.js";
6
7
  import { RunAggregator, isPlainObject, tryParseJSON } from "./adapter/run-aggregator.js";
7
- import { fromAgUiMessages, toAgUiMessages, toAgUiTools } from "./adapter/conversions.js";
8
8
  import { createAgUiSubscriber } from "./adapter/subscriber.js";
9
9
  import { createReplayContentMerger, groupReplayToolContent, indexReplayToolOwners, preserveToolResult } from "./adapter/replay-content.js";
10
10
  import { fromThreadMessageLike, generateId, isMcpAppUri } from "@assistant-ui/core";
@@ -57,6 +57,42 @@ const FALLBACK_USER_STATUS = {
57
57
  type: "complete",
58
58
  reason: "unknown"
59
59
  };
60
+ const textOf = (content) => content.flatMap((part) => part.type === "text" ? [part.text] : []).join("");
61
+ /**
62
+ * Swaps `content`'s text parts, in order, for `source`'s. A text part `source`
63
+ * has beyond `content`'s text slots goes after the nearest earlier part of
64
+ * `source` that `content` also has: a tool call by id, another part by its
65
+ * type and position among parts of that type. With none, it goes first.
66
+ */
67
+ const replaceTextParts = (content, source) => {
68
+ const texts = source.filter((part) => part.type === "text");
69
+ const result = [];
70
+ let used = 0;
71
+ for (const part of content) if (part.type !== "text") result.push(part);
72
+ else if (used < texts.length) result.push(texts[used++]);
73
+ const positionIn = (parts, part) => parts.filter((other) => other.type === part.type).indexOf(part);
74
+ for (const text of texts.slice(used)) {
75
+ let anchor = -1;
76
+ for (let index = source.indexOf(text) - 1; index >= 0 && anchor < 0; index--) {
77
+ const before = source[index];
78
+ if (before.type === "tool-call") anchor = result.findIndex((part) => part.type === "tool-call" && part.toolCallId === before.toolCallId);
79
+ else if (before.type === "text") anchor = result.indexOf(before);
80
+ else {
81
+ const match = result.filter((part) => part.type === before.type)[positionIn(source, before)];
82
+ anchor = match ? result.indexOf(match) : -1;
83
+ }
84
+ }
85
+ result.splice(anchor + 1, 0, text);
86
+ }
87
+ return result;
88
+ };
89
+ const settleNestedContent = (content, status) => mapToolCallPartsDeep(content, (part) => part.messages ? {
90
+ ...part,
91
+ messages: part.messages.map((message) => message.role === "assistant" && message.status?.type === "running" ? {
92
+ ...message,
93
+ status
94
+ } : message)
95
+ } : part).content;
60
96
  const invokeRuntimeCallback = (name, callback, ...args) => {
61
97
  invokeUserCallback("react-ag-ui", name, callback, ...args);
62
98
  };
@@ -107,6 +143,7 @@ var AgUiThreadController = class {
107
143
  activeRunAgent = null;
108
144
  history;
109
145
  assistantHistoryParents = /* @__PURE__ */ new Map();
146
+ subagentParentToolCallIds = /* @__PURE__ */ new Map();
110
147
  snapshotHistoryIds = /* @__PURE__ */ new Set();
111
148
  persistedHistoryIds = /* @__PURE__ */ new Set();
112
149
  historyWrites = createHistoryWriteChain();
@@ -188,6 +225,7 @@ var AgUiThreadController = class {
188
225
  if (!repo) return;
189
226
  this.session.applyExternalMessageRepository(repo);
190
227
  this.assistantHistoryParents.clear();
228
+ this.subagentParentToolCallIds.clear();
191
229
  this.snapshotHistoryIds.clear();
192
230
  this.persistedHistoryIds.clear();
193
231
  for (const { message } of repo.messages) this.persistedHistoryIds.add(message.id);
@@ -555,7 +593,10 @@ var AgUiThreadController = class {
555
593
  custom: newCustom
556
594
  }
557
595
  };
558
- })) this.publishUpdate();
596
+ })) {
597
+ this.publishUpdate();
598
+ this.persistSettledAssistant(messageId);
599
+ }
559
600
  }
560
601
  findMessageIdForToolCall(toolCallId) {
561
602
  let fallbackMessageId;
@@ -609,9 +650,31 @@ var AgUiThreadController = class {
609
650
  };
610
651
  });
611
652
  if (!matchedToolCall) return message;
653
+ const settled = mapToolCallPartsDeep(content, (part) => {
654
+ if (!part.messages) return part;
655
+ let changed = false;
656
+ const messages = part.messages.map((nested) => {
657
+ if (nested.role !== "assistant" || nested.status.type !== "requires-action" || nested.status.reason !== "tool-calls") return nested;
658
+ const listed = listedPendingToolCallIds(nested);
659
+ if (Array.from(iterateToolCallParts(nested.content)).some((call) => !isResolvedToolCall(call) && (!listed || listed.has(call.toolCallId)))) return nested;
660
+ changed = true;
661
+ return {
662
+ ...nested,
663
+ status: {
664
+ type: "complete",
665
+ reason: "unknown"
666
+ },
667
+ metadata: withoutPendingToolCallIds(nested.metadata)
668
+ };
669
+ });
670
+ return changed ? {
671
+ ...part,
672
+ messages
673
+ } : part;
674
+ }).content;
612
675
  return {
613
676
  ...assistant,
614
- content
677
+ content: settled
615
678
  };
616
679
  })) return;
617
680
  this.publishUpdate();
@@ -724,7 +787,8 @@ var AgUiThreadController = class {
724
787
  metadata: withoutPendingToolCallIds(current.metadata)
725
788
  } : current)) return false;
726
789
  this.publishUpdate();
727
- this.persistAssistantHistory(messageId);
790
+ if (this.history?.update) this.persistSettledAssistant(messageId);
791
+ else this.persistAssistantHistory(messageId);
728
792
  return true;
729
793
  }
730
794
  startResumeRun(messageId) {
@@ -827,6 +891,7 @@ var AgUiThreadController = class {
827
891
  const controller = this.abortController;
828
892
  const activeRunAgent = this.activeRunAgent;
829
893
  this.dispatch({ type: "resetThread" });
894
+ this.subagentParentToolCallIds.clear();
830
895
  if (controller) {
831
896
  this.abortController = null;
832
897
  this.activeRunAgent = null;
@@ -892,6 +957,8 @@ var AgUiThreadController = class {
892
957
  let frameOwnerIds = /* @__PURE__ */ new Set();
893
958
  let restrictedPendingToolCallIds;
894
959
  let settlingRun = false;
960
+ let latestSnapshot;
961
+ let snapshotToolCallIds = /* @__PURE__ */ new Set();
895
962
  const ensureAssistant = (allowRecreate = false) => {
896
963
  const cached = assistantMessageId;
897
964
  if (cached !== void 0 && this.session.tryGetMessage(cached)) return cached;
@@ -1097,7 +1164,45 @@ var AgUiThreadController = class {
1097
1164
  showThinking: this.showThinking,
1098
1165
  joinStrategy,
1099
1166
  logger: this.logger,
1100
- emit: applyUpdate,
1167
+ emit: (update) => {
1168
+ const currentSnapshotAssistant = assistantMessageId === void 0 ? void 0 : this.session.tryGetMessage(assistantMessageId)?.message;
1169
+ const listedSnapshotToolCallIds = currentSnapshotAssistant?.role === "assistant" ? listedPendingToolCallIds(currentSnapshotAssistant) : void 0;
1170
+ const hasPendingSnapshotToolCall = latestSnapshot?.id === assistantMessageId && currentSnapshotAssistant?.role === "assistant" && Array.from(iterateToolCallParts(currentSnapshotAssistant.content)).some((part) => !isResolvedToolCall(part) && (!listedSnapshotToolCallIds || listedSnapshotToolCallIds.has(part.toolCallId)) && (!restrictedPendingToolCallIds || restrictedPendingToolCallIds.has(part.toolCallId)));
1171
+ const reconciledUpdate = settlingRun && update.status?.type === "complete" && hasPendingSnapshotToolCall ? {
1172
+ ...update,
1173
+ status: {
1174
+ type: "requires-action",
1175
+ reason: "tool-calls"
1176
+ },
1177
+ ...restrictedPendingToolCallIds ? { metadata: {
1178
+ ...update.metadata,
1179
+ custom: {
1180
+ ...update.metadata?.custom,
1181
+ [AG_UI_METADATA_NAMESPACE]: {
1182
+ ...update.metadata?.custom?.[AG_UI_METADATA_NAMESPACE],
1183
+ pendingToolCallIds: [...restrictedPendingToolCallIds]
1184
+ }
1185
+ }
1186
+ } } : {}
1187
+ } : update;
1188
+ if (settlingRun && latestSnapshot && latestSnapshot.id === assistantMessageId && reconciledUpdate.status && reconciledUpdate.status.type !== "running" && reconciledUpdate.status.type !== "requires-action") {
1189
+ const status = reconciledUpdate.status;
1190
+ const current = this.session.tryGetMessage(assistantMessageId)?.message;
1191
+ const content = settleNestedContent(current?.role === "assistant" ? current.content : latestSnapshot.content, status);
1192
+ applyUpdate({
1193
+ ...reconciledUpdate,
1194
+ content
1195
+ });
1196
+ } else if (latestSnapshot && latestSnapshot.id === assistantMessageId && reconciledUpdate.content !== void 0) {
1197
+ const content = this.mergeSnapshotAssistantContent(latestSnapshot.content, reconciledUpdate.content, reconciledUpdate.content.some((part) => part.type === "reasoning"), reconciledUpdate.content.some(isActivityPart), snapshotToolCallIds);
1198
+ applyUpdate({
1199
+ ...reconciledUpdate,
1200
+ content
1201
+ });
1202
+ } else applyUpdate(reconciledUpdate);
1203
+ const current = assistantMessageId ? this.session.tryGetMessage(assistantMessageId)?.message : void 0;
1204
+ latestSnapshot = !settlingRun && current?.role === "assistant" && current.id === latestSnapshot?.id ? current : void 0;
1205
+ },
1101
1206
  onServerMessageId: (serverId) => {
1102
1207
  adoptServerMessageId(serverId, false);
1103
1208
  },
@@ -1147,6 +1252,10 @@ var AgUiThreadController = class {
1147
1252
  }
1148
1253
  }
1149
1254
  assistantMessageId = nextAssistantMessageId;
1255
+ const imported = assistantMessageId ? this.session.tryGetMessage(assistantMessageId)?.message : void 0;
1256
+ const source = event.messages.find((message) => message?.role === "assistant" && message.id === imported?.id);
1257
+ latestSnapshot = imported?.role === "assistant" && source ? imported : void 0;
1258
+ snapshotToolCallIds = new Set(Array.isArray(source?.toolCalls) ? source.toolCalls.map((call) => call.id).filter((id) => typeof id === "string") : []);
1150
1259
  if (!replayEnded && assistantMessageId === void 0) {
1151
1260
  const tail = this.session.getMessages().at(-1);
1152
1261
  snapshotAssistantId = tail?.role === "assistant" ? tail.id : void 0;
@@ -1194,7 +1303,6 @@ var AgUiThreadController = class {
1194
1303
  const input = this.buildRunInput(runId, normalizedRunConfig, historicalMessages, resume);
1195
1304
  const subscriber = createAgUiSubscriber({
1196
1305
  dispatch,
1197
- runId,
1198
1306
  logger: this.logger,
1199
1307
  onRunFailed: (error) => {
1200
1308
  if (abortSignal.aborted) return;
@@ -1205,7 +1313,7 @@ var AgUiThreadController = class {
1205
1313
  try {
1206
1314
  runAgentInstance.messages = input.messages;
1207
1315
  runAgentInstance.threadId = input.threadId;
1208
- runAgentInstance.state = input.state ?? null;
1316
+ runAgentInstance.state = input.state;
1209
1317
  } catch {}
1210
1318
  await runAgentInstance.runAgent.bind(runAgentInstance)(input, subscriber, { signal: abortSignal });
1211
1319
  }
@@ -1220,6 +1328,19 @@ var AgUiThreadController = class {
1220
1328
  pendingError = err;
1221
1329
  }
1222
1330
  } finally {
1331
+ const current = assistantMessageId ? this.session.tryGetMessage(assistantMessageId)?.message : void 0;
1332
+ if (this.abortController === abortController && current?.role === "assistant" && current.status?.type === "running") {
1333
+ const status = {
1334
+ type: "incomplete",
1335
+ reason: "error",
1336
+ error: "The connection ended before the answer finished."
1337
+ };
1338
+ settleHeldMessages(status);
1339
+ applyUpdate({
1340
+ status,
1341
+ content: settleNestedContent(current.content, status)
1342
+ });
1343
+ }
1223
1344
  this.finishRun(abortController);
1224
1345
  }
1225
1346
  if (pendingError) {
@@ -1342,7 +1463,7 @@ var AgUiThreadController = class {
1342
1463
  return {
1343
1464
  threadId,
1344
1465
  runId,
1345
- state: this.state.stateSnapshot ?? null,
1466
+ state: this.state.stateSnapshot === void 0 ? {} : this.state.stateSnapshot,
1346
1467
  messages,
1347
1468
  tools: toAgUiTools(context?.tools),
1348
1469
  context: [...context?.system ? [{
@@ -1535,10 +1656,10 @@ var AgUiThreadController = class {
1535
1656
  });
1536
1657
  return changed ? content : next;
1537
1658
  }
1538
- mergeSnapshotAssistantContent(previous, next, snapshotHasReasoning, snapshotHasActivity) {
1539
- const shouldKeep = (part) => part.type === "reasoning" ? this.showThinking && !snapshotHasReasoning : isActivityPart(part) && !snapshotHasActivity;
1659
+ mergeSnapshotAssistantContent(previous, next, snapshotHasReasoning, snapshotHasActivity, keepMissingToolCalls = /* @__PURE__ */ new Set(), keepPriorTranscripts = false) {
1660
+ const shouldKeep = (part) => part.type === "tool-call" ? keepMissingToolCalls.has(part.toolCallId) && !next.some((incoming) => incoming.type === "tool-call" && incoming.toolCallId === part.toolCallId) : part.type === "reasoning" ? this.showThinking && !snapshotHasReasoning : part.type === "data" && !isActivityPart(part) ? !next.some((incoming) => incoming.type === "data" && incoming.name === part.name) : isActivityPart(part) && !snapshotHasActivity;
1540
1661
  const kept = previous.filter(shouldKeep);
1541
- const merged = this.reconcileSnapshotNestedContent(previous, this.preserveToolInteractions(previous, next));
1662
+ const merged = this.reconcileSnapshotNestedContent(previous, this.preserveToolInteractions(previous, next), keepPriorTranscripts);
1542
1663
  if (kept.length === 0) return merged;
1543
1664
  const mergedByType = /* @__PURE__ */ new Map();
1544
1665
  const mergedTools = /* @__PURE__ */ new Map();
@@ -1570,7 +1691,7 @@ var AgUiThreadController = class {
1570
1691
  result.push(...insertions[merged.length]);
1571
1692
  return result;
1572
1693
  }
1573
- reconcileSnapshotNestedContent(previous, next) {
1694
+ reconcileSnapshotNestedContent(previous, next, keepPriorTranscripts) {
1574
1695
  const nestedIds = /* @__PURE__ */ new Set();
1575
1696
  for (const part of previous) {
1576
1697
  if (part.type !== "tool-call" || !part.messages) continue;
@@ -1600,11 +1721,49 @@ var AgUiThreadController = class {
1600
1721
  if (part.type === "tool-call" && flattened.has(part.toolCallId)) return [];
1601
1722
  if (part.type !== "tool-call") return [part];
1602
1723
  const prior = previousCalls.get(part.toolCallId);
1603
- return prior?.messages === void 0 ? [part] : [{
1724
+ if (prior?.messages === void 0) return [part];
1725
+ if (part.messages === void 0) return [{
1604
1726
  ...part,
1605
1727
  messages: prior.messages
1606
1728
  }];
1729
+ if (part.messages.length === 0) return [part];
1730
+ return [{
1731
+ ...part,
1732
+ messages: this.mergeNestedTranscripts(prior.messages, part.messages, keepPriorTranscripts)
1733
+ }];
1734
+ });
1735
+ }
1736
+ /**
1737
+ * Merges one tool call's nested transcripts message by message. The incoming
1738
+ * copy is the base for tool calls and reasoning; resolved results and calls
1739
+ * only the prior copy has carry over, and the prior text wins when it extends
1740
+ * the incoming text. Importing a snapshot keeps prior messages it lacks, a
1741
+ * prior status that is running or incomplete, and the prior status when the
1742
+ * snapshot waits on calls the merged content has already resolved.
1743
+ */
1744
+ mergeNestedTranscripts(prior, next, importingSnapshot) {
1745
+ const priorById = new Map(prior.map((message) => [message.id, message]));
1746
+ const merged = next.map((message) => {
1747
+ const previous = priorById.get(message.id);
1748
+ if (previous?.role !== "assistant" || message.role !== "assistant") return message;
1749
+ let content = this.mergeSnapshotAssistantContent(previous.content, this.preserveToolResults(previous.content, message.content), message.content.some((part) => part.type === "reasoning"), message.content.some(isActivityPart), new Set(Array.from(iterateToolCallParts(previous.content), (call) => call.toolCallId)), importingSnapshot);
1750
+ const priorText = textOf(previous.content);
1751
+ const incomingText = textOf(message.content);
1752
+ if (priorText.length > incomingText.length && priorText.startsWith(incomingText)) content = replaceTextParts(content, previous.content);
1753
+ const status = !importingSnapshot ? message.status : previous.status.type === "running" || previous.status.type === "incomplete" || message.status.type === "requires-action" && message.status.reason === "tool-calls" && !Array.from(iterateToolCallParts(content)).some((call) => !isResolvedToolCall(call)) ? previous.status : message.status;
1754
+ return {
1755
+ ...message,
1756
+ status,
1757
+ content,
1758
+ metadata: {
1759
+ ...message.metadata,
1760
+ custom: mergeAgUiNamespace(previous.metadata.custom, message.metadata.custom)
1761
+ }
1762
+ };
1607
1763
  });
1764
+ if (!importingSnapshot) return merged;
1765
+ const mergedById = new Map(merged.map((message) => [message.id, message]));
1766
+ return [...prior.map((message) => mergedById.get(message.id) ?? message), ...merged.filter((message) => !priorById.has(message.id))];
1608
1767
  }
1609
1768
  mergeAssistantMetadata(current, incoming) {
1610
1769
  const annotations = incoming.unstable_annotations ? [...current.unstable_annotations, ...incoming.unstable_annotations] : current.unstable_annotations;
@@ -1621,6 +1780,7 @@ var AgUiThreadController = class {
1621
1780
  };
1622
1781
  }
1623
1782
  handleEvent(aggregator, event, activeAssistantId, joinStrategy, runStartMessageIds, hasRunUserBoundary, reconcileSnapshot) {
1783
+ if (event.type === "SUBAGENT_STARTED" && !this.subagentParentToolCallIds.has(event.subagentRunId)) this.subagentParentToolCallIds.set(event.subagentRunId, event.parentToolCallId);
1624
1784
  switch (event.type) {
1625
1785
  case "STATE_SNAPSHOT":
1626
1786
  this.dispatch({
@@ -1643,12 +1803,26 @@ var AgUiThreadController = class {
1643
1803
  }
1644
1804
  break;
1645
1805
  case "MESSAGES_SNAPSHOT": {
1806
+ aggregator.handle(event);
1646
1807
  const previousHeadId = this.session.headId;
1647
1808
  const activeAssistant = activeAssistantId === void 0 ? void 0 : this.session.tryGetMessage(activeAssistantId)?.message;
1648
1809
  const hasActiveText = activeAssistant?.role === "assistant" && activeAssistant.content.some((part) => part.type === "text");
1649
1810
  this.importMessagesSnapshot(event.messages, activeAssistantId, joinStrategy, runStartMessageIds, hasRunUserBoundary, reconcileSnapshot);
1650
1811
  const headId = this.session.headId;
1651
- if (hasActiveText && headId !== previousHeadId) activeAssistantId = (headId === null ? void 0 : this.session.tryGetMessage(headId)?.message)?.role === "assistant" ? headId ?? void 0 : void 0;
1812
+ const head = headId === null ? void 0 : this.session.tryGetMessage(headId)?.message;
1813
+ const hasNewPendingToolCallAnswer = head?.role === "assistant" && !runStartMessageIds.has(head.id) && head.status?.type === "requires-action" && head.status.reason === "tool-calls";
1814
+ if (headId !== previousHeadId && (hasActiveText || hasNewPendingToolCallAnswer)) {
1815
+ const nextActiveAssistantId = head?.role === "assistant" ? headId ?? void 0 : void 0;
1816
+ if (activeAssistantId !== nextActiveAssistantId && activeAssistantId) {
1817
+ const previous = this.session.tryGetMessage(activeAssistantId)?.message;
1818
+ if (previous?.role === "assistant" && previous.status?.type === "running") this.updateAssistantMessage(activeAssistantId, {
1819
+ status: FALLBACK_USER_STATUS,
1820
+ content: settleNestedContent(previous.content, FALLBACK_USER_STATUS)
1821
+ });
1822
+ }
1823
+ activeAssistantId = nextActiveAssistantId;
1824
+ if (hasNewPendingToolCallAnswer && head) this.updateAssistantMessage(head.id, { status: { type: "running" } });
1825
+ }
1652
1826
  this.updateActiveAssistantState(activeAssistantId);
1653
1827
  break;
1654
1828
  }
@@ -1710,7 +1884,8 @@ var AgUiThreadController = class {
1710
1884
  text: modelText
1711
1885
  }] } : {},
1712
1886
  ...part.isError === void 0 ? typeof event.mcpResult?.isError === "boolean" ? { isError: event.mcpResult.isError } : event.role === "tool" ? { isError: false } : {} : {},
1713
- ...event.messageId ? { unstable_toolMessageId: event.messageId } : {}
1887
+ ...event.messageId ? { unstable_toolMessageId: event.messageId } : {},
1888
+ ...event.metadata !== void 0 ? { providerMetadata: withAgUiPartMessageMetadata(part.providerMetadata, event.metadata) } : {}
1714
1889
  };
1715
1890
  const { modelContent: _modelContent, ...partWithoutModelContent } = part;
1716
1891
  return {
@@ -1722,7 +1897,8 @@ var AgUiThreadController = class {
1722
1897
  }] } : {},
1723
1898
  ...typeof event.mcpResult?.isError === "boolean" ? { isError: event.mcpResult.isError } : event.role === "tool" ? { isError: false } : {},
1724
1899
  ...part.mcp === void 0 && mcpAppUri !== void 0 ? { mcp: { app: { resourceUri: mcpAppUri } } } : {},
1725
- ...event.messageId ? { unstable_toolMessageId: event.messageId } : {}
1900
+ ...event.messageId ? { unstable_toolMessageId: event.messageId } : {},
1901
+ ...event.metadata !== void 0 ? { providerMetadata: withAgUiPartMessageMetadata(part.providerMetadata, event.metadata) } : {}
1726
1902
  };
1727
1903
  });
1728
1904
  if (!matchedToolCall) return message;
@@ -1732,7 +1908,8 @@ var AgUiThreadController = class {
1732
1908
  };
1733
1909
  })) return;
1734
1910
  this.publishUpdate();
1735
- this.maybeCompleteAfterToolResults(messageId);
1911
+ if (this.maybeCompleteAfterToolResults(messageId)) return;
1912
+ this.persistSettledAssistant(messageId);
1736
1913
  }
1737
1914
  applyCrossRunActivitySnapshot(messageId, toolCallId, event) {
1738
1915
  const resourceUri = event.content["resourceUri"];
@@ -1771,7 +1948,8 @@ var AgUiThreadController = class {
1771
1948
  };
1772
1949
  })) return;
1773
1950
  this.publishUpdate();
1774
- this.maybeCompleteAfterToolResults(messageId);
1951
+ if (this.maybeCompleteAfterToolResults(messageId)) return;
1952
+ this.persistSettledAssistant(messageId);
1775
1953
  }
1776
1954
  importMessagesSnapshot(rawMessages, activeAssistantId, joinStrategy, runStartMessageIds, hasRunUserBoundary, reconcileSnapshot) {
1777
1955
  try {
@@ -1779,10 +1957,12 @@ var AgUiThreadController = class {
1779
1957
  const activeMessage = activeAssistantItem?.message;
1780
1958
  const activeAssistantParentId = activeAssistantItem?.parentId;
1781
1959
  const activeAssistant = activeMessage?.role === "assistant" ? activeMessage : void 0;
1782
- const normalized = fromAgUiMessages(rawMessages, { showThinking: this.showThinking });
1960
+ const normalized = fromAgUiSnapshotMessages(rawMessages, { showThinking: this.showThinking }, this.subagentParentToolCallIds);
1783
1961
  const converted = [];
1784
1962
  for (const message of normalized) try {
1785
- converted.push(fromThreadMessageLike(message, generateId(), FALLBACK_USER_STATUS));
1963
+ const previous = message.id ? this.session.tryGetMessage(message.id)?.message : void 0;
1964
+ const fallbackStatus = previous?.role === "assistant" && previous.status?.type === "running" ? previous.status : FALLBACK_USER_STATUS;
1965
+ converted.push(fromThreadMessageLike(message, generateId(), fallbackStatus));
1786
1966
  } catch (error) {
1787
1967
  this.logger.error?.("[agui] failed to import message from snapshot", error);
1788
1968
  }
@@ -1794,9 +1974,11 @@ var AgUiThreadController = class {
1794
1974
  if (convertedMessage.role === "assistant") {
1795
1975
  const previous = existing?.role === "assistant" ? existing : void 0;
1796
1976
  const snapshot = reconcileSnapshot?.(convertedMessage, previous) ?? convertedMessage;
1977
+ const preservesRunningStatus = convertedMessage.id === activeAssistantId && previous?.status?.type === "running" && snapshot.status?.type === "requires-action" && snapshot.status.reason === "tool-calls";
1797
1978
  converted[index] = previous ? {
1798
1979
  ...snapshot,
1799
- content: this.mergeSnapshotAssistantContent(previous.content, snapshot.content, snapshotHasReasoning, snapshotHasActivity)
1980
+ ...preservesRunningStatus ? { status: previous.status } : {},
1981
+ content: this.mergeSnapshotAssistantContent(previous.content, snapshot.content, snapshotHasReasoning, snapshotHasActivity, /* @__PURE__ */ new Set(), true)
1800
1982
  } : snapshot;
1801
1983
  }
1802
1984
  }
@@ -1881,6 +2063,14 @@ var AgUiThreadController = class {
1881
2063
  if (!this.history) return;
1882
2064
  this.assistantHistoryParents.set(messageId, parentId);
1883
2065
  }
2066
+ persistSettledAssistant(messageId) {
2067
+ const item = this.session.tryGetMessage(messageId);
2068
+ if (!this.history?.update || !item) return;
2069
+ if (!this.isTerminalStatus(item.message.status)) return;
2070
+ if (!this.persistedHistoryIds.has(messageId) && this.historyWrites.pending(messageId) === void 0) return;
2071
+ this.markPendingAssistantHistory(messageId, item.parentId);
2072
+ this.persistAssistantHistory(messageId);
2073
+ }
1884
2074
  persistAssistantHistory(messageId) {
1885
2075
  const history = this.history;
1886
2076
  if (!history) return;
@@ -1891,7 +2081,7 @@ var AgUiThreadController = class {
1891
2081
  if (!this.isPersistableStatus(message.status)) return;
1892
2082
  const wasPersisted = this.persistedHistoryIds.has(messageId);
1893
2083
  const update = history.update;
1894
- if (update !== void 0 && (wasPersisted || this.snapshotHistoryIds.has(messageId))) {
2084
+ if (update !== void 0 && (wasPersisted || this.snapshotHistoryIds.has(messageId) || this.historyWrites.pending(messageId) !== void 0)) {
1895
2085
  const write = this.historyWrites.chain(messageId, () => update.call(history, {
1896
2086
  parentId,
1897
2087
  message