@tanstack/ai 0.58.0 → 0.61.0

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 (144) hide show
  1. package/dist/esm/activities/chat/adapter.d.ts +9 -0
  2. package/dist/esm/activities/chat/adapter.js +1 -0
  3. package/dist/esm/activities/chat/adapter.js.map +1 -1
  4. package/dist/esm/activities/chat/agents/define-agent.d.ts +81 -0
  5. package/dist/esm/activities/chat/agents/define-agent.js +34 -0
  6. package/dist/esm/activities/chat/agents/define-agent.js.map +1 -0
  7. package/dist/esm/activities/chat/agents/route.d.ts +53 -0
  8. package/dist/esm/activities/chat/agents/route.js +59 -0
  9. package/dist/esm/activities/chat/agents/route.js.map +1 -0
  10. package/dist/esm/activities/chat/agents/spawn.d.ts +124 -0
  11. package/dist/esm/activities/chat/agents/spawn.js +490 -0
  12. package/dist/esm/activities/chat/agents/spawn.js.map +1 -0
  13. package/dist/esm/activities/chat/agents/turn.d.ts +36 -0
  14. package/dist/esm/activities/chat/agents/turn.js +78 -0
  15. package/dist/esm/activities/chat/agents/turn.js.map +1 -0
  16. package/dist/esm/activities/chat/index.d.ts +13 -3
  17. package/dist/esm/activities/chat/index.js +462 -26
  18. package/dist/esm/activities/chat/index.js.map +1 -1
  19. package/dist/esm/activities/chat/messages.d.ts +7 -1
  20. package/dist/esm/activities/chat/messages.js +99 -19
  21. package/dist/esm/activities/chat/messages.js.map +1 -1
  22. package/dist/esm/activities/chat/middleware/run-store.d.ts +43 -7
  23. package/dist/esm/activities/chat/middleware/run-store.js +8 -1
  24. package/dist/esm/activities/chat/middleware/run-store.js.map +1 -1
  25. package/dist/esm/activities/chat/middleware/types.d.ts +47 -1
  26. package/dist/esm/activities/chat/middleware/types.js.map +1 -1
  27. package/dist/esm/activities/chat/stream/message-updaters.js +9 -2
  28. package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
  29. package/dist/esm/activities/chat/stream/processor.d.ts +46 -1
  30. package/dist/esm/activities/chat/stream/processor.js +294 -18
  31. package/dist/esm/activities/chat/stream/processor.js.map +1 -1
  32. package/dist/esm/activities/chat/tools/tool-calls.d.ts +17 -3
  33. package/dist/esm/activities/chat/tools/tool-calls.js +56 -5
  34. package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
  35. package/dist/esm/activities/embed/adapter.d.ts +7 -0
  36. package/dist/esm/activities/embed/adapter.js +1 -0
  37. package/dist/esm/activities/embed/adapter.js.map +1 -1
  38. package/dist/esm/activities/embed/index.js +2 -0
  39. package/dist/esm/activities/embed/index.js.map +1 -1
  40. package/dist/esm/activities/files/adapter.d.ts +97 -0
  41. package/dist/esm/activities/files/adapter.js +45 -0
  42. package/dist/esm/activities/files/adapter.js.map +1 -0
  43. package/dist/esm/activities/files/index.d.ts +66 -0
  44. package/dist/esm/activities/files/index.js +78 -0
  45. package/dist/esm/activities/files/index.js.map +1 -0
  46. package/dist/esm/activities/generateAudio/index.js +1 -1
  47. package/dist/esm/activities/generateImage/adapter.d.ts +8 -0
  48. package/dist/esm/activities/generateImage/adapter.js +1 -0
  49. package/dist/esm/activities/generateImage/adapter.js.map +1 -1
  50. package/dist/esm/activities/generateImage/index.js +3 -1
  51. package/dist/esm/activities/generateImage/index.js.map +1 -1
  52. package/dist/esm/activities/generateLiveVideo/index.js +1 -1
  53. package/dist/esm/activities/generateSpeech/index.js +1 -1
  54. package/dist/esm/activities/generateTranscription/index.js +1 -1
  55. package/dist/esm/activities/generateVideo/adapter.d.ts +8 -0
  56. package/dist/esm/activities/generateVideo/adapter.js +1 -0
  57. package/dist/esm/activities/generateVideo/adapter.js.map +1 -1
  58. package/dist/esm/activities/generateVideo/index.js +3 -0
  59. package/dist/esm/activities/generateVideo/index.js.map +1 -1
  60. package/dist/esm/activities/generateVoice/index.js +1 -1
  61. package/dist/esm/activities/generateWorld/adapter.d.ts +4 -2
  62. package/dist/esm/activities/generateWorld/adapter.js.map +1 -1
  63. package/dist/esm/activities/generateWorld/index.d.ts +4 -3
  64. package/dist/esm/activities/generateWorld/index.js +6 -5
  65. package/dist/esm/activities/generateWorld/index.js.map +1 -1
  66. package/dist/esm/activities/index.d.ts +9 -3
  67. package/dist/esm/activities/index.js +17 -13
  68. package/dist/esm/activities/summarize/chat-stream-summarize.d.ts +2 -0
  69. package/dist/esm/activities/summarize/chat-stream-summarize.js +8 -8
  70. package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
  71. package/dist/esm/activities/summarize/index.js +1 -1
  72. package/dist/esm/client.d.ts +7 -36
  73. package/dist/esm/client.js +5 -37
  74. package/dist/esm/client.js.map +1 -1
  75. package/dist/esm/index.d.ts +8 -2
  76. package/dist/esm/index.js +9 -4
  77. package/dist/esm/middlewares/content-guard.js.map +1 -1
  78. package/dist/esm/types.d.ts +179 -98
  79. package/dist/esm/utilities/adapter-yield-chunk.d.ts +5 -1
  80. package/dist/esm/utilities/ag-ui-usage.d.ts +9 -9
  81. package/dist/esm/utilities/ag-ui-usage.js +66 -3
  82. package/dist/esm/utilities/ag-ui-usage.js.map +1 -1
  83. package/dist/esm/utilities/ag-ui-wire.js +90 -13
  84. package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
  85. package/dist/esm/utilities/content-source.d.ts +60 -0
  86. package/dist/esm/utilities/content-source.js +85 -0
  87. package/dist/esm/utilities/content-source.js.map +1 -0
  88. package/dist/esm/utilities/normalize-stream-chunk.js +7 -2
  89. package/dist/esm/utilities/normalize-stream-chunk.js.map +1 -1
  90. package/dist/esm/utilities/provider-executed.d.ts +7 -0
  91. package/dist/esm/utilities/provider-executed.js +10 -1
  92. package/dist/esm/utilities/provider-executed.js.map +1 -1
  93. package/dist/esm/utilities/spec-event-keys.js +13 -8
  94. package/dist/esm/utilities/spec-event-keys.js.map +1 -1
  95. package/dist/esm/utilities/subagent-wire.d.ts +36 -0
  96. package/dist/esm/utilities/subagent-wire.js +131 -0
  97. package/dist/esm/utilities/subagent-wire.js.map +1 -0
  98. package/dist/esm/utilities/tool-result.d.ts +12 -2
  99. package/dist/esm/utilities/tool-result.js +23 -3
  100. package/dist/esm/utilities/tool-result.js.map +1 -1
  101. package/package.json +3 -3
  102. package/skills/ai-core/adapter-configuration/SKILL.md +62 -0
  103. package/skills/ai-core/adapter-configuration/references/grok-adapter.md +1 -1
  104. package/skills/ai-core/chat-experience/SKILL.md +14 -0
  105. package/skills/ai-core/media-generation/SKILL.md +10 -2
  106. package/skills/ai-core/middleware/SKILL.md +7 -4
  107. package/src/activities/chat/adapter.ts +10 -0
  108. package/src/activities/chat/agents/define-agent.ts +121 -0
  109. package/src/activities/chat/agents/route.ts +115 -0
  110. package/src/activities/chat/agents/spawn.ts +806 -0
  111. package/src/activities/chat/agents/turn.ts +151 -0
  112. package/src/activities/chat/index.ts +734 -30
  113. package/src/activities/chat/messages.ts +137 -16
  114. package/src/activities/chat/middleware/run-store.ts +56 -7
  115. package/src/activities/chat/middleware/types.ts +47 -0
  116. package/src/activities/chat/stream/message-updaters.ts +24 -2
  117. package/src/activities/chat/stream/processor.ts +452 -30
  118. package/src/activities/chat/tools/tool-calls.ts +83 -11
  119. package/src/activities/embed/adapter.ts +7 -0
  120. package/src/activities/embed/index.ts +5 -0
  121. package/src/activities/files/adapter.ts +120 -0
  122. package/src/activities/files/index.ts +113 -0
  123. package/src/activities/generateImage/adapter.ts +8 -0
  124. package/src/activities/generateImage/index.ts +4 -0
  125. package/src/activities/generateVideo/adapter.ts +8 -0
  126. package/src/activities/generateVideo/index.ts +7 -0
  127. package/src/activities/generateWorld/adapter.ts +4 -2
  128. package/src/activities/generateWorld/index.ts +7 -6
  129. package/src/activities/index.ts +40 -1
  130. package/src/activities/summarize/chat-stream-summarize.ts +22 -12
  131. package/src/client.ts +29 -35
  132. package/src/index.ts +39 -0
  133. package/src/middlewares/content-guard.ts +7 -5
  134. package/src/types.ts +226 -103
  135. package/src/utilities/adapter-yield-chunk.ts +10 -2
  136. package/src/utilities/ag-ui-usage.test.ts +38 -0
  137. package/src/utilities/ag-ui-usage.ts +98 -11
  138. package/src/utilities/ag-ui-wire.ts +134 -16
  139. package/src/utilities/content-source.ts +138 -0
  140. package/src/utilities/normalize-stream-chunk.ts +10 -2
  141. package/src/utilities/provider-executed.ts +13 -0
  142. package/src/utilities/spec-event-keys.ts +34 -7
  143. package/src/utilities/subagent-wire.ts +184 -0
  144. package/src/utilities/tool-result.ts +38 -2
@@ -1,9 +1,11 @@
1
+ import { EventType } from "../../../types.js";
1
2
  import { mergeMetadata, tanstackMetadata } from "../../../utilities/merge-metadata.js";
2
3
  import { runErrorEventToError } from "../../../utilities/errors.js";
3
4
  import { INTERRUPT_BINDING_METADATA_KEY } from "../../../interrupt-resume.js";
4
- import { normalizeToolResult } from "../../../utilities/tool-result.js";
5
- import { isProviderExecutedToolCall } from "../../../utilities/provider-executed.js";
6
- import { aguiSnapshotMessageToUIMessage, coerceCreatedAt, generateMessageId, uiMessageToModelMessages } from "../messages.js";
5
+ import { isAssistantSegmentOf, isProviderExecutedToolCall } from "../../../utilities/provider-executed.js";
6
+ import { normalizeToolResult, toolResultErrorText } from "../../../utilities/tool-result.js";
7
+ import { splitSubagentWire } from "../../../utilities/subagent-wire.js";
8
+ import { aguiContentToContentParts, aguiSnapshotMessageToUIMessage, coerceCreatedAt, generateMessageId, uiMessageToModelMessages } from "../messages.js";
7
9
  import { getChunkRunId } from "../../../utilities/chunk-ids.js";
8
10
  import { defaultJSONParser } from "./json-parser.js";
9
11
  import { appendStructuredOutputDelta, completeStructuredOutputPart, errorStructuredOutputPart, updateTextPart, updateThinkingPart, updateToolCallApproval, updateToolCallApprovalResponse, updateToolCallPart, updateToolCallWithOutput, updateToolResultPart } from "./message-updaters.js";
@@ -67,13 +69,17 @@ var StreamProcessor = class StreamProcessor {
67
69
  structuredMessageIds = /* @__PURE__ */ new Set();
68
70
  structuredOutputUpdateBatches = /* @__PURE__ */ new Map();
69
71
  activeRuns = /* @__PURE__ */ new Set();
72
+ childProcessors = /* @__PURE__ */ new Map();
73
+ childToolCalls = /* @__PURE__ */ new Map();
70
74
  finishReason = null;
71
75
  hasError = false;
72
76
  isDone = false;
73
77
  streamEndEmitted = false;
74
78
  recording = null;
75
79
  recordingStartTime = 0;
80
+ subagentRunId;
76
81
  constructor(options = {}) {
82
+ this.subagentRunId = options.subagentRunId;
77
83
  this.chunkStrategy = options.chunkStrategy || new ImmediateStrategy();
78
84
  this.events = options.events || {};
79
85
  this.jsonParser = options.jsonParser || defaultJSONParser;
@@ -192,6 +198,11 @@ var StreamProcessor = class StreamProcessor {
192
198
  addToolResult(toolCallId, output, error) {
193
199
  const messageWithToolCall = this.messages.find((msg) => msg.parts.some((p) => p.type === "tool-call" && p.id === toolCallId));
194
200
  if (!messageWithToolCall) {
201
+ const owner = this.childOwningToolCall((part) => part.id === toolCallId);
202
+ if (owner !== void 0) {
203
+ this.updateChild(owner, (child) => child.addToolResult(toolCallId, output, error));
204
+ return;
205
+ }
195
206
  console.warn(`[StreamProcessor] Could not find message with tool call ${toolCallId}`);
196
207
  return;
197
208
  }
@@ -206,6 +217,11 @@ var StreamProcessor = class StreamProcessor {
206
217
  * Add an approval response (called by client after handling onApprovalRequest)
207
218
  */
208
219
  addToolApprovalResponse(approvalId, approved) {
220
+ const owner = this.childOwningToolCall((part) => part.approval?.id === approvalId);
221
+ if (owner !== void 0) {
222
+ this.updateChild(owner, (child) => child.addToolApprovalResponse(approvalId, approved));
223
+ return;
224
+ }
209
225
  this.messages = updateToolCallApprovalResponse(this.messages, approvalId, approved);
210
226
  this.emitMessagesChange();
211
227
  }
@@ -259,6 +275,8 @@ var StreamProcessor = class StreamProcessor {
259
275
  this.structuredMessageIds.clear();
260
276
  this.structuredOutputUpdateBatches.clear();
261
277
  this.pendingManualMessageId = null;
278
+ this.childProcessors.clear();
279
+ this.childToolCalls.clear();
262
280
  this.emitMessagesChange();
263
281
  }
264
282
  /**
@@ -287,7 +305,17 @@ var StreamProcessor = class StreamProcessor {
287
305
  timestamp: Date.now(),
288
306
  index: this.recording.chunks.length
289
307
  });
308
+ if (this.routeToChild(chunk)) return;
290
309
  switch (chunk.type) {
310
+ case "SUBAGENT_STARTED":
311
+ this.handleSubagentStartedEvent(chunk);
312
+ break;
313
+ case "SUBAGENT_FINISHED":
314
+ this.handleSubagentFinishedEvent(chunk);
315
+ break;
316
+ case "SUBAGENT_ERROR":
317
+ this.handleSubagentErrorEvent(chunk);
318
+ break;
291
319
  case "TEXT_MESSAGE_START":
292
320
  this.handleTextMessageStartEvent(chunk);
293
321
  break;
@@ -497,6 +525,164 @@ var StreamProcessor = class StreamProcessor {
497
525
  } : msg);
498
526
  this.emitMessagesChange();
499
527
  }
528
+ findSubagentPart(subagentRunId) {
529
+ for (const message of this.messages) for (const part of message.parts) if (part.type === "subagent" && part.subagent.id === subagentRunId) return {
530
+ message,
531
+ part
532
+ };
533
+ }
534
+ /**
535
+ * The direct child whose card holds `id`: the child itself, or a nested
536
+ * child inside its messages.
537
+ */
538
+ childOwning(id) {
539
+ const holds = (messages) => messages.some((message) => message.parts.some((part) => part.type === "subagent" && (part.subagent.id === id || holds(part.subagent.messages))));
540
+ for (const message of this.messages) for (const part of message.parts) {
541
+ if (part.type !== "subagent") continue;
542
+ if (part.subagent.id === id || holds(part.subagent.messages)) return part.subagent.id;
543
+ }
544
+ }
545
+ /** The direct child whose messages hold this tool call. */
546
+ childOwningToolCall(matches) {
547
+ const holds = (messages) => messages.some((message) => message.parts.some((part) => part.type === "tool-call" && matches(part) || part.type === "subagent" && holds(part.subagent.messages)));
548
+ for (const message of this.messages) for (const part of message.parts) if (part.type === "subagent" && holds(part.subagent.messages)) return part.subagent.id;
549
+ }
550
+ /**
551
+ * The processor for a direct child. The card's messages are the source of
552
+ * truth. The processor re-reads them when something else replaced them.
553
+ */
554
+ childProcessor(subagentRunId) {
555
+ const found = this.findSubagentPart(subagentRunId);
556
+ if (!found) return void 0;
557
+ const messages = found.part.subagent.messages;
558
+ const existing = this.childProcessors.get(subagentRunId);
559
+ if (existing) {
560
+ if (existing.getMessages() !== messages) existing.messages = messages;
561
+ return existing;
562
+ }
563
+ const child = new StreamProcessor({
564
+ subagentRunId,
565
+ initialMessages: messages,
566
+ events: {
567
+ onToolCall: (args) => this.events.onToolCall?.(args),
568
+ onApprovalRequest: (args) => this.events.onApprovalRequest?.(args),
569
+ onCustomEvent: (name, value, context) => this.events.onCustomEvent?.(name, value, context)
570
+ }
571
+ });
572
+ this.childProcessors.set(subagentRunId, child);
573
+ return child;
574
+ }
575
+ /** Run `update` on a direct child, then copy its messages onto the card. */
576
+ updateChild(subagentRunId, update) {
577
+ const child = this.childProcessor(subagentRunId);
578
+ if (!child) return;
579
+ update(child);
580
+ const messages = child.getMessages();
581
+ this.patchSubagent(subagentRunId, (subagent) => {
582
+ subagent.messages = messages;
583
+ });
584
+ }
585
+ handleSubagentStartedEvent(chunk) {
586
+ if (chunk.type !== "SUBAGENT_STARTED") return;
587
+ if (this.findSubagentPart(chunk.subagentRunId)) {
588
+ this.childProcessors.delete(chunk.subagentRunId);
589
+ this.patchSubagent(chunk.subagentRunId, (subagent) => {
590
+ subagent.status = "running";
591
+ delete subagent.interruptIds;
592
+ delete subagent.error;
593
+ if (chunk.metadata !== void 0) subagent.metadata = chunk.metadata;
594
+ });
595
+ return;
596
+ }
597
+ const { messageId } = this.ensureAssistantMessage();
598
+ const part = {
599
+ type: "subagent",
600
+ subagent: {
601
+ id: chunk.subagentRunId,
602
+ name: chunk.name,
603
+ ...chunk.description !== void 0 && { description: chunk.description },
604
+ status: "running",
605
+ ...chunk.parentSubagentRunId !== void 0 && { parentSubagentRunId: chunk.parentSubagentRunId },
606
+ ...chunk.parentToolCallId !== void 0 && { parentToolCallId: chunk.parentToolCallId },
607
+ ...chunk.metadata !== void 0 && { metadata: chunk.metadata },
608
+ messages: []
609
+ }
610
+ };
611
+ this.messages = this.messages.map((message) => message.id === messageId ? {
612
+ ...message,
613
+ parts: [...message.parts, part]
614
+ } : message);
615
+ this.emitMessagesChange();
616
+ }
617
+ handleSubagentFinishedEvent(chunk) {
618
+ if (chunk.type !== "SUBAGENT_FINISHED") return;
619
+ this.updateChild(chunk.subagentRunId, (child) => child.finalizeStream());
620
+ this.patchSubagent(chunk.subagentRunId, (subagent) => {
621
+ if (subagent.status === "error") return;
622
+ if (chunk.outcome?.type === "suspended") {
623
+ subagent.status = "suspended";
624
+ subagent.interruptIds = chunk.outcome.interruptIds ?? [];
625
+ return;
626
+ }
627
+ subagent.status = "finished";
628
+ });
629
+ }
630
+ handleSubagentErrorEvent(chunk) {
631
+ if (chunk.type !== "SUBAGENT_ERROR") return;
632
+ this.updateChild(chunk.subagentRunId, (child) => child.finalizeStream());
633
+ this.patchSubagent(chunk.subagentRunId, (subagent) => {
634
+ subagent.status = "error";
635
+ subagent.error = {
636
+ message: chunk.message,
637
+ ...chunk.code !== void 0 && { code: chunk.code }
638
+ };
639
+ });
640
+ }
641
+ patchSubagent(subagentRunId, patch) {
642
+ this.messages = this.messages.map((message) => {
643
+ const index = message.parts.findIndex((part) => part.type === "subagent" && part.subagent.id === subagentRunId);
644
+ if (index === -1) return message;
645
+ const part = message.parts[index];
646
+ if (!part || part.type !== "subagent") return message;
647
+ const next = {
648
+ type: "subagent",
649
+ subagent: { ...part.subagent }
650
+ };
651
+ patch(next.subagent);
652
+ const parts = [...message.parts];
653
+ parts[index] = next;
654
+ return {
655
+ ...message,
656
+ parts
657
+ };
658
+ });
659
+ this.emitMessagesChange();
660
+ }
661
+ /**
662
+ * Send a child's chunk to that child's processor, so the card keeps text,
663
+ * reasoning, tool calls, results, and nested children. A tool event without
664
+ * `subagentRunId` follows its `TOOL_CALL_START`.
665
+ */
666
+ routeToChild(chunk) {
667
+ let id;
668
+ if (chunk.type === "SUBAGENT_STARTED") id = chunk.parentSubagentRunId;
669
+ else if (chunk.type === "SUBAGENT_FINISHED" || chunk.type === "SUBAGENT_ERROR") {
670
+ if (this.findSubagentPart(chunk.subagentRunId)) return false;
671
+ id = chunk.subagentRunId;
672
+ } else if ("subagentRunId" in chunk && chunk.subagentRunId) id = chunk.subagentRunId;
673
+ else if ("toolCallId" in chunk && typeof chunk.toolCallId === "string") id = this.childToolCalls.get(chunk.toolCallId);
674
+ if (id === void 0) return false;
675
+ const owner = this.childOwning(id);
676
+ if (owner === void 0) {
677
+ if (id === this.subagentRunId) return false;
678
+ console.warn(`[StreamProcessor] Dropped a chunk for unknown subagent ${id}`);
679
+ return true;
680
+ }
681
+ if (this.findSubagentPart(owner)?.part.subagent.status === "error") return true;
682
+ if (chunk.type === "TOOL_CALL_START") this.childToolCalls.set(chunk.toolCallId, owner);
683
+ this.updateChild(owner, (child) => child.processChunk(chunk));
684
+ return true;
685
+ }
500
686
  /**
501
687
  * Handle TEXT_MESSAGE_START event
502
688
  */
@@ -579,8 +765,9 @@ var StreamProcessor = class StreamProcessor {
579
765
  this.resetStreamState();
580
766
  const prevMessages = this.messages;
581
767
  const prevById = new Map(prevMessages.map((msg) => [msg.id, msg]));
582
- const normalized = this.mergeReasoningFanOut(chunk.messages.map(aguiSnapshotMessageToUIMessage));
583
- this.messages = this.reconcileSnapshotToolCalls(normalized, prevMessages).map((msg) => {
768
+ const { top, groups } = splitSubagentWire(chunk.messages);
769
+ const normalized = this.mergeReasoningFanOut(top.map(aguiSnapshotMessageToUIMessage));
770
+ const reconciled = this.reconcileSnapshotToolCalls(normalized, prevMessages).map((msg) => {
584
771
  if (msg.metadata != null) return msg;
585
772
  const prev = prevById.get(msg.id);
586
773
  if (prev?.metadata == null) return msg;
@@ -589,9 +776,76 @@ var StreamProcessor = class StreamProcessor {
589
776
  metadata: prev.metadata
590
777
  };
591
778
  });
779
+ this.messages = this.attachSnapshotSubagents(reconciled, top, groups, prevMessages);
592
780
  this.emitMessagesChange();
593
781
  }
594
782
  /**
783
+ * Put subagent cards back on a snapshot. Child wire messages (tagged with
784
+ * `subagentRunId`) become a card on the nearest assistant message before
785
+ * them, or a new assistant message when there is none. A card that the
786
+ * snapshot does not carry stays on its message, because a server snapshot
787
+ * can hold the parent history only.
788
+ */
789
+ attachSnapshotSubagents(messages, top, groups, prevMessages) {
790
+ const cards = [];
791
+ const children = /* @__PURE__ */ new Map();
792
+ for (const group of groups) {
793
+ const child = new StreamProcessor({ subagentRunId: group.id });
794
+ child.processChunk({
795
+ type: EventType.MESSAGES_SNAPSHOT,
796
+ messages: group.messages,
797
+ timestamp: Date.now()
798
+ });
799
+ children.set(group.id, child);
800
+ const { placeholder: _placeholder, ...info } = group.info;
801
+ cards.push({
802
+ hostId: top.slice(0, group.hostIndex + 1).findLast((message) => message.role === "assistant")?.id,
803
+ part: {
804
+ type: "subagent",
805
+ subagent: {
806
+ ...info,
807
+ id: group.id,
808
+ messages: child.getMessages()
809
+ }
810
+ },
811
+ carried: false
812
+ });
813
+ }
814
+ const inSnapshot = new Set(groups.map((group) => group.id));
815
+ for (const message of prevMessages) for (const part of message.parts) {
816
+ if (part.type !== "subagent" || inSnapshot.has(part.subagent.id)) continue;
817
+ cards.push({
818
+ hostId: message.id,
819
+ ...part.subagent.parentToolCallId !== void 0 && { toolCallId: part.subagent.parentToolCallId },
820
+ part,
821
+ carried: true
822
+ });
823
+ const kept = this.childProcessors.get(part.subagent.id);
824
+ if (kept) children.set(part.subagent.id, kept);
825
+ }
826
+ this.childProcessors.clear();
827
+ for (const [id, child] of children) this.childProcessors.set(id, child);
828
+ let out = messages;
829
+ for (const card of cards) {
830
+ let index = out.findIndex((message) => message.id === card.hostId);
831
+ if (index === -1 && card.toolCallId !== void 0) index = out.findIndex((message) => message.parts.some((part) => part.type === "tool-call" && part.id === card.toolCallId));
832
+ if (index === -1 && !card.carried) index = out.findLastIndex((message) => message.role === "assistant");
833
+ if (index === -1) {
834
+ if (!card.carried) out = [...out, {
835
+ id: generateMessageId(),
836
+ role: "assistant",
837
+ parts: [card.part]
838
+ }];
839
+ continue;
840
+ }
841
+ out = out.map((message, position) => position !== index || message.parts.some((part) => part.type === "subagent" && part.subagent.id === card.part.subagent.id) ? message : {
842
+ ...message,
843
+ parts: [...message.parts, card.part]
844
+ });
845
+ }
846
+ return out;
847
+ }
848
+ /**
595
849
  * Reconcile a freshly normalized snapshot with the pre-snapshot message
596
850
  * state so unreconstructable tool-call metadata is preserved.
597
851
  *
@@ -636,16 +890,23 @@ var StreamProcessor = class StreamProcessor {
636
890
  pending.push(msg);
637
891
  continue;
638
892
  }
893
+ let next = msg;
639
894
  if (msg.role === "assistant" && pending.length > 0 && !isToolResultOnly(msg)) {
640
- out.push({
895
+ next = {
641
896
  ...msg,
642
897
  parts: [...pending.flatMap(thinkingParts), ...msg.parts]
643
- });
898
+ };
644
899
  pending = [];
900
+ } else flushPending();
901
+ const prev = out.at(-1);
902
+ if (prev?.role === "assistant" && next.role === "assistant" && isAssistantSegmentOf(next.id, prev.id)) {
903
+ out[out.length - 1] = {
904
+ ...prev,
905
+ parts: [...prev.parts, ...next.parts]
906
+ };
645
907
  continue;
646
908
  }
647
- flushPending();
648
- out.push(msg);
909
+ out.push(next);
649
910
  }
650
911
  flushPending();
651
912
  return out;
@@ -711,7 +972,7 @@ var StreamProcessor = class StreamProcessor {
711
972
  } catch {
712
973
  output = result.content;
713
974
  }
714
- const errorText = result.state === "error" ? this.extractToolResultError(output) : void 0;
975
+ const errorText = result.state === "error" ? toolResultErrorText(output) : void 0;
715
976
  next = {
716
977
  ...next,
717
978
  output: errorText ? { error: errorText } : output,
@@ -884,10 +1145,6 @@ var StreamProcessor = class StreamProcessor {
884
1145
  }
885
1146
  }
886
1147
  }
887
- extractToolResultError(output) {
888
- if (output && typeof output === "object" && "error" in output && typeof output.error === "string") return output.error;
889
- return typeof output === "string" ? output : "Tool execution failed";
890
- }
891
1148
  /**
892
1149
  * Handle TOOL_CALL_RESULT event (AG-UI spec).
893
1150
  *
@@ -901,13 +1158,13 @@ var StreamProcessor = class StreamProcessor {
901
1158
  const isOutputError = chunk.state === "output-error" || tanstackMetadata(chunk)?.state === "output-error";
902
1159
  let output;
903
1160
  try {
904
- output = JSON.parse(chunk.content);
1161
+ output = typeof chunk.content === "string" ? JSON.parse(chunk.content) : chunk.content;
905
1162
  } catch {
906
1163
  output = chunk.content;
907
1164
  }
908
1165
  this.messages = updateToolCallWithOutput(this.messages, chunk.toolCallId, output, isOutputError ? "error" : void 0);
909
1166
  const resultState = isOutputError ? "error" : "complete";
910
- this.messages = updateToolResultPart(this.messages, messageId, chunk.toolCallId, chunk.content, resultState, resultState === "error" ? this.extractToolResultError(output) : void 0);
1167
+ this.messages = updateToolResultPart(this.messages, messageId, chunk.toolCallId, aguiContentToContentParts(chunk.content), resultState, resultState === "error" ? toolResultErrorText(output) : void 0);
911
1168
  this.emitMessagesChange();
912
1169
  }
913
1170
  /**
@@ -941,21 +1198,38 @@ var StreamProcessor = class StreamProcessor {
941
1198
  this.finalizeStream();
942
1199
  }
943
1200
  }
944
- handleInterrupts(interrupts) {
1201
+ /**
1202
+ * Apply interrupt state to tool-call parts, then fire the client events.
1203
+ * A child's interrupt updates the child's card. `emit` is false there, so
1204
+ * the events fire once, from the top processor.
1205
+ */
1206
+ handleInterrupts(interrupts, emit = true) {
945
1207
  const hasGeneric = interruptBatchHasGeneric(interrupts);
946
1208
  for (const interrupt of interrupts) {
947
1209
  const metadata = interrupt.metadata && typeof interrupt.metadata === "object" ? interrupt.metadata : {};
948
1210
  const kind = typeof metadata.kind === "string" ? metadata.kind : void 0;
949
1211
  const toolCallId = interrupt.toolCallId;
950
1212
  if (!toolCallId) continue;
1213
+ const owner = interrupt.subagentRunId !== void 0 ? this.childOwning(interrupt.subagentRunId) : this.childOwningToolCall((part) => part.id === toolCallId);
1214
+ if (owner !== void 0) this.updateChild(owner, (child) => child.handleInterrupts([interrupt], false));
951
1215
  const toolName = typeof metadata.toolName === "string" ? metadata.toolName : this.findToolCallName(toolCallId);
952
1216
  const input = Object.hasOwn(metadata, "input") ? metadata.input : {};
953
1217
  if (kind === "approval" || interrupt.reason === "approval_required") {
1218
+ if (owner !== void 0) {
1219
+ if (emit) this.events.onApprovalRequest?.({
1220
+ toolCallId,
1221
+ toolName,
1222
+ input,
1223
+ approvalId: interrupt.id
1224
+ });
1225
+ continue;
1226
+ }
954
1227
  const resolvedMessageId = this.getActiveAssistantMessageId() ?? this.toolCallToMessage.get(toolCallId) ?? this.messages.find((m) => m.role === "assistant" && m.parts.some((p) => p.type === "tool-call" && p.id === toolCallId))?.id;
955
1228
  if (resolvedMessageId) {
956
1229
  this.messages = updateToolCallApproval(this.messages, resolvedMessageId, toolCallId, interrupt.id);
957
1230
  this.emitMessagesChange();
958
1231
  }
1232
+ if (!emit) continue;
959
1233
  this.events.onApprovalRequest?.({
960
1234
  toolCallId,
961
1235
  toolName,
@@ -965,7 +1239,7 @@ var StreamProcessor = class StreamProcessor {
965
1239
  continue;
966
1240
  }
967
1241
  if (kind === "client_tool" || interrupt.reason === "client_tool_input") {
968
- if (hasGeneric) continue;
1242
+ if (hasGeneric || !emit) continue;
969
1243
  this.events.onToolCall?.({
970
1244
  toolCallId,
971
1245
  toolName,
@@ -1473,6 +1747,8 @@ var StreamProcessor = class StreamProcessor {
1473
1747
  reset() {
1474
1748
  this.resetStreamState();
1475
1749
  this.messages = [];
1750
+ this.childProcessors.clear();
1751
+ this.childToolCalls.clear();
1476
1752
  }
1477
1753
  /**
1478
1754
  * Check if a message contains only whitespace text and no other meaningful parts