@tanstack/ai 0.47.3 → 0.48.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 (101) hide show
  1. package/README.md +2 -1
  2. package/dist/esm/activities/chat/adapter.d.ts +5 -4
  3. package/dist/esm/activities/chat/adapter.js.map +1 -1
  4. package/dist/esm/activities/chat/index.d.ts +4 -7
  5. package/dist/esm/activities/chat/index.js +201 -243
  6. package/dist/esm/activities/chat/index.js.map +1 -1
  7. package/dist/esm/activities/chat/messages.js +115 -25
  8. package/dist/esm/activities/chat/messages.js.map +1 -1
  9. package/dist/esm/activities/chat/stream/processor.d.ts +38 -17
  10. package/dist/esm/activities/chat/stream/processor.js +186 -101
  11. package/dist/esm/activities/chat/stream/processor.js.map +1 -1
  12. package/dist/esm/activities/chat/tools/tool-calls.d.ts +3 -2
  13. package/dist/esm/activities/chat/tools/tool-calls.js +15 -10
  14. package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
  15. package/dist/esm/activities/generateVideo/index.js +6 -6
  16. package/dist/esm/activities/generateVideo/index.js.map +1 -1
  17. package/dist/esm/activities/stream-generation-result.js +7 -8
  18. package/dist/esm/activities/stream-generation-result.js.map +1 -1
  19. package/dist/esm/activities/summarize/chat-stream-summarize.d.ts +2 -1
  20. package/dist/esm/activities/summarize/chat-stream-summarize.js +56 -53
  21. package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
  22. package/dist/esm/adapter-internals.d.ts +2 -0
  23. package/dist/esm/adapter-internals.js +3 -1
  24. package/dist/esm/client.d.ts +8 -1
  25. package/dist/esm/client.js +7 -2
  26. package/dist/esm/client.js.map +1 -1
  27. package/dist/esm/index.d.ts +6 -0
  28. package/dist/esm/index.js +6 -2
  29. package/dist/esm/middlewares/otel.js +11 -4
  30. package/dist/esm/middlewares/otel.js.map +1 -1
  31. package/dist/esm/stream-to-response.js +8 -5
  32. package/dist/esm/stream-to-response.js.map +1 -1
  33. package/dist/esm/stream-to-websocket.js +4 -2
  34. package/dist/esm/stream-to-websocket.js.map +1 -1
  35. package/dist/esm/strip-to-spec-middleware.d.ts +10 -13
  36. package/dist/esm/strip-to-spec-middleware.js +24 -22
  37. package/dist/esm/strip-to-spec-middleware.js.map +1 -1
  38. package/dist/esm/types.d.ts +82 -305
  39. package/dist/esm/utilities/adapter-yield-chunk.d.ts +31 -0
  40. package/dist/esm/utilities/ag-ui-usage.d.ts +24 -0
  41. package/dist/esm/utilities/ag-ui-usage.js +66 -0
  42. package/dist/esm/utilities/ag-ui-usage.js.map +1 -0
  43. package/dist/esm/utilities/ag-ui-wire.d.ts +14 -7
  44. package/dist/esm/utilities/ag-ui-wire.js +71 -30
  45. package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
  46. package/dist/esm/utilities/chat-params.d.ts +3 -3
  47. package/dist/esm/utilities/chat-params.js +10 -30
  48. package/dist/esm/utilities/chat-params.js.map +1 -1
  49. package/dist/esm/utilities/chunk-ids.d.ts +5 -0
  50. package/dist/esm/utilities/chunk-ids.js +25 -0
  51. package/dist/esm/utilities/chunk-ids.js.map +1 -0
  52. package/dist/esm/utilities/merge-metadata.d.ts +14 -0
  53. package/dist/esm/utilities/merge-metadata.js +43 -0
  54. package/dist/esm/utilities/merge-metadata.js.map +1 -0
  55. package/dist/esm/utilities/normalize-stream-chunk.d.ts +3 -0
  56. package/dist/esm/utilities/normalize-stream-chunk.js +100 -0
  57. package/dist/esm/utilities/normalize-stream-chunk.js.map +1 -0
  58. package/dist/esm/utilities/reasoning-encrypted-value.d.ts +8 -0
  59. package/dist/esm/utilities/reasoning-encrypted-value.js +16 -0
  60. package/dist/esm/utilities/reasoning-encrypted-value.js.map +1 -0
  61. package/dist/esm/utilities/restore-inbound-chunk.d.ts +15 -0
  62. package/dist/esm/utilities/restore-inbound-chunk.js +43 -0
  63. package/dist/esm/utilities/restore-inbound-chunk.js.map +1 -0
  64. package/dist/esm/utilities/spec-event-keys.d.ts +2 -0
  65. package/dist/esm/utilities/spec-event-keys.js +56 -0
  66. package/dist/esm/utilities/spec-event-keys.js.map +1 -0
  67. package/dist/esm/utilities/structured-output-events.d.ts +3 -3
  68. package/dist/esm/utilities/structured-output-events.js.map +1 -1
  69. package/package.json +2 -2
  70. package/src/activities/chat/adapter.ts +4 -4
  71. package/src/activities/chat/index.ts +398 -400
  72. package/src/activities/chat/messages.ts +174 -34
  73. package/src/activities/chat/stream/processor.ts +289 -218
  74. package/src/activities/chat/tools/tool-calls.ts +23 -22
  75. package/src/activities/generateVideo/index.ts +7 -6
  76. package/src/activities/stream-generation-result.ts +8 -12
  77. package/src/activities/summarize/chat-stream-summarize.ts +94 -70
  78. package/src/adapter-internals.ts +2 -0
  79. package/src/client.ts +13 -0
  80. package/src/index.ts +6 -0
  81. package/src/middlewares/otel.ts +16 -8
  82. package/src/stream-to-response.ts +11 -4
  83. package/src/stream-to-websocket.ts +3 -1
  84. package/src/strip-to-spec-middleware.ts +48 -24
  85. package/src/types.ts +109 -393
  86. package/src/utilities/adapter-yield-chunk.ts +30 -0
  87. package/src/utilities/ag-ui-usage.test.ts +194 -0
  88. package/src/utilities/ag-ui-usage.ts +148 -0
  89. package/src/utilities/ag-ui-wire.ts +149 -31
  90. package/src/utilities/chat-params.ts +22 -39
  91. package/src/utilities/chunk-ids.ts +24 -0
  92. package/src/utilities/merge-metadata.test.ts +117 -0
  93. package/src/utilities/merge-metadata.ts +59 -0
  94. package/src/utilities/normalize-stream-chunk.test.ts +423 -0
  95. package/src/utilities/normalize-stream-chunk.ts +186 -0
  96. package/src/utilities/reasoning-encrypted-value.ts +18 -0
  97. package/src/utilities/restore-inbound-chunk.test.ts +133 -0
  98. package/src/utilities/restore-inbound-chunk.ts +72 -0
  99. package/src/utilities/spec-event-keys.test.ts +34 -0
  100. package/src/utilities/spec-event-keys.ts +74 -0
  101. package/src/utilities/structured-output-events.ts +3 -3
@@ -1,8 +1,10 @@
1
+ import { mergeMetadata, tanstackMetadata } from "../../../utilities/merge-metadata.js";
1
2
  import { runErrorEventToError } from "../../../utilities/errors.js";
2
3
  import { INTERRUPT_BINDING_METADATA_KEY } from "../../../interrupt-resume.js";
3
4
  import { normalizeToolResult } from "../../../utilities/tool-result.js";
4
5
  import { isProviderExecutedToolCall } from "../../../utilities/provider-executed.js";
5
6
  import { aguiSnapshotMessageToUIMessage, generateMessageId, uiMessageToModelMessages } from "../messages.js";
7
+ import { getChunkRunId } from "../../../utilities/chunk-ids.js";
6
8
  import { defaultJSONParser } from "./json-parser.js";
7
9
  import { appendStructuredOutputDelta, completeStructuredOutputPart, errorStructuredOutputPart, updateTextPart, updateThinkingPart, updateToolCallApproval, updateToolCallApprovalResponse, updateToolCallPart, updateToolCallWithOutput, updateToolResultPart } from "./message-updaters.js";
8
10
  import { ImmediateStrategy } from "./strategies.js";
@@ -90,6 +92,7 @@ var StreamProcessor = class StreamProcessor {
90
92
  *
91
93
  * @param content - The message content (string or array of content parts)
92
94
  * @param id - Optional custom message ID (generated if not provided)
95
+ * @param metadata - Optional AG-UI metadata bag
93
96
  * @returns The created UIMessage
94
97
  *
95
98
  * @example
@@ -107,7 +110,7 @@ var StreamProcessor = class StreamProcessor {
107
110
  * processor.addUserMessage('Hello!', 'custom-id-123')
108
111
  * ```
109
112
  */
110
- addUserMessage(content, id) {
113
+ addUserMessage(content, id, metadata) {
111
114
  const parts = typeof content === "string" ? [{
112
115
  type: "text",
113
116
  content
@@ -118,7 +121,8 @@ var StreamProcessor = class StreamProcessor {
118
121
  id: id ?? generateMessageId(),
119
122
  role: "user",
120
123
  parts,
121
- createdAt: /* @__PURE__ */ new Date()
124
+ createdAt: /* @__PURE__ */ new Date(),
125
+ ...metadata != null ? { metadata } : {}
122
126
  };
123
127
  this.messages = [...this.messages, userMessage];
124
128
  this.emitMessagesChange();
@@ -299,6 +303,9 @@ var StreamProcessor = class StreamProcessor {
299
303
  case "REASONING_MESSAGE_CONTENT":
300
304
  this.handleReasoningMessageContentEvent(chunk);
301
305
  break;
306
+ case "REASONING_ENCRYPTED_VALUE":
307
+ this.handleReasoningEncryptedValueEvent(chunk);
308
+ break;
302
309
  case "TOOL_CALL_RESULT":
303
310
  this.handleToolCallResultEvent(chunk);
304
311
  break;
@@ -423,6 +430,33 @@ var StreamProcessor = class StreamProcessor {
423
430
  };
424
431
  }
425
432
  /**
433
+ * Merge event metadata onto a UIMessage. `tanstack` is deep-merged so a
434
+ * later delta does not wipe `tanstack.model`. High-frequency leftover
435
+ * keys (`content`, `args`) never stamp onto the message.
436
+ * Rebuilds `createdAt` when `tanstack.createdAt` is an ISO string.
437
+ */
438
+ mergeMessageMetadata(messageId, incoming) {
439
+ if (incoming == null || typeof incoming !== "object" || Array.isArray(incoming)) return;
440
+ const message = this.messages.find((msg) => msg.id === messageId);
441
+ if (!message) return;
442
+ const incomingRecord = incoming;
443
+ const incomingTanstack = tanstackMetadata(incomingRecord);
444
+ const toMerge = incomingTanstack != null && ("content" in incomingTanstack || "args" in incomingTanstack) ? {
445
+ ...incomingRecord,
446
+ tanstack: Object.fromEntries(Object.entries(incomingTanstack).filter(([key]) => key !== "content" && key !== "args"))
447
+ } : incomingRecord;
448
+ const metadata = mergeMetadata(message.metadata, toMerge);
449
+ const createdAtRaw = tanstackMetadata(incomingRecord)?.createdAt;
450
+ const createdAt = typeof createdAtRaw === "string" ? new Date(createdAtRaw) : void 0;
451
+ const createdAtValid = createdAt !== void 0 && !Number.isNaN(createdAt.getTime());
452
+ this.messages = this.messages.map((msg) => msg.id === messageId ? {
453
+ ...msg,
454
+ ...metadata !== void 0 ? { metadata } : {},
455
+ ...createdAtValid ? { createdAt } : {}
456
+ } : msg);
457
+ this.emitMessagesChange();
458
+ }
459
+ /**
426
460
  * Handle TEXT_MESSAGE_START event
427
461
  */
428
462
  handleTextMessageStartEvent(chunk) {
@@ -450,6 +484,7 @@ var StreamProcessor = class StreamProcessor {
450
484
  this.createMessageState(messageId, uiRole);
451
485
  this.activeMessageIds.add(messageId);
452
486
  }
487
+ this.mergeMessageMetadata(messageId, chunk.metadata);
453
488
  this.emitMessagesChange();
454
489
  return;
455
490
  }
@@ -463,6 +498,7 @@ var StreamProcessor = class StreamProcessor {
463
498
  existingState.lastEmittedText = "";
464
499
  existingState.hasToolCallsSinceTextStart = false;
465
500
  }
501
+ this.mergeMessageMetadata(messageId, chunk.metadata);
466
502
  return;
467
503
  }
468
504
  const newMessage = {
@@ -474,6 +510,7 @@ var StreamProcessor = class StreamProcessor {
474
510
  this.messages = [...this.messages, newMessage];
475
511
  this.createMessageState(messageId, uiRole);
476
512
  this.activeMessageIds.add(messageId);
513
+ this.mergeMessageMetadata(messageId, chunk.metadata);
477
514
  this.events.onStreamStart?.();
478
515
  this.emitMessagesChange();
479
516
  }
@@ -482,6 +519,7 @@ var StreamProcessor = class StreamProcessor {
482
519
  */
483
520
  handleTextMessageEndEvent(chunk) {
484
521
  const { messageId } = chunk;
522
+ this.mergeMessageMetadata(messageId, chunk.metadata);
485
523
  const state = this.getMessageState(messageId);
486
524
  if (!state) return;
487
525
  if (state.isComplete) return;
@@ -493,8 +531,17 @@ var StreamProcessor = class StreamProcessor {
493
531
  handleMessagesSnapshotEvent(chunk) {
494
532
  this.resetStreamState();
495
533
  const prevMessages = this.messages;
496
- const normalized = chunk.messages.map(aguiSnapshotMessageToUIMessage);
497
- this.messages = this.reconcileSnapshotToolCalls(normalized, prevMessages);
534
+ const prevById = new Map(prevMessages.map((msg) => [msg.id, msg]));
535
+ const normalized = this.mergeReasoningFanOut(chunk.messages.map(aguiSnapshotMessageToUIMessage));
536
+ this.messages = this.reconcileSnapshotToolCalls(normalized, prevMessages).map((msg) => {
537
+ if (msg.metadata != null) return msg;
538
+ const prev = prevById.get(msg.id);
539
+ if (prev?.metadata == null) return msg;
540
+ return {
541
+ ...msg,
542
+ metadata: prev.metadata
543
+ };
544
+ });
498
545
  this.emitMessagesChange();
499
546
  }
500
547
  /**
@@ -520,6 +567,42 @@ var StreamProcessor = class StreamProcessor {
520
567
  * matching `tool-call` (and prefer pre-snapshot complete/output when the
521
568
  * snapshot is poorer) so server tools keep the same UI shape as client tools.
522
569
  */
570
+ /**
571
+ * Wire order is reasoning fan-outs, then the assistant anchor.
572
+ * Snapshot conversion turns each reasoning row into its own assistant
573
+ * message. Fold leading thinking-only messages into the next real
574
+ * assistant. Do not fold into a tool-result-only message (`role: 'tool'`
575
+ * on the wire). `reconcileSnapshotToolCalls` anchors those results.
576
+ */
577
+ mergeReasoningFanOut(messages) {
578
+ const out = [];
579
+ let pending = [];
580
+ const thinkingParts = (msg) => msg.parts.filter((part) => part.type === "thinking");
581
+ const isThinkingOnly = (msg) => msg.role === "assistant" && msg.parts.length > 0 && msg.parts.every((part) => part.type === "thinking");
582
+ const isToolResultOnly = (msg) => msg.role === "assistant" && msg.parts.length === 1 && msg.parts[0]?.type === "tool-result";
583
+ const flushPending = () => {
584
+ out.push(...pending);
585
+ pending = [];
586
+ };
587
+ for (const msg of messages) {
588
+ if (isThinkingOnly(msg)) {
589
+ pending.push(msg);
590
+ continue;
591
+ }
592
+ if (msg.role === "assistant" && pending.length > 0 && !isToolResultOnly(msg)) {
593
+ out.push({
594
+ ...msg,
595
+ parts: [...pending.flatMap(thinkingParts), ...msg.parts]
596
+ });
597
+ pending = [];
598
+ continue;
599
+ }
600
+ flushPending();
601
+ out.push(msg);
602
+ }
603
+ flushPending();
604
+ return out;
605
+ }
523
606
  reconcileSnapshotToolCalls(snapshot, prevMessages) {
524
607
  const prevToolCalls = /* @__PURE__ */ new Map();
525
608
  for (const msg of prevMessages) for (const part of msg.parts) if (part.type === "tool-call") prevToolCalls.set(part.id, part);
@@ -608,14 +691,9 @@ var StreamProcessor = class StreamProcessor {
608
691
  */
609
692
  handleTextMessageContentEvent(chunk) {
610
693
  const { messageId, state } = this.ensureAssistantMessage(chunk.messageId);
694
+ this.mergeMessageMetadata(messageId, chunk.metadata);
611
695
  if (this.structuredMessageIds.has(messageId)) {
612
- let delta = chunk.delta || "";
613
- if (delta === "" && chunk.content !== void 0 && chunk.content !== "") {
614
- const existingRaw = (this.messages.find((m) => m.id === messageId)?.parts.find((p) => p.type === "structured-output") ?? { raw: "" }).raw;
615
- if (chunk.content.startsWith(existingRaw)) delta = chunk.content.slice(existingRaw.length);
616
- else if (existingRaw.startsWith(chunk.content)) delta = "";
617
- else delta = chunk.content;
618
- }
696
+ const delta = chunk.delta || "";
619
697
  if (delta !== "") {
620
698
  this.messages = appendStructuredOutputDelta(this.messages, messageId, delta);
621
699
  state.totalTextContent += delta;
@@ -632,18 +710,12 @@ var StreamProcessor = class StreamProcessor {
632
710
  state.hasToolCallsSinceTextStart = false;
633
711
  }
634
712
  const currentText = state.currentSegmentText;
635
- let nextText = currentText;
636
713
  const delta = chunk.delta || "";
637
- if (delta !== "") nextText = currentText + delta;
638
- else if (chunk.content !== void 0 && chunk.content !== "") {
639
- if (chunk.content.startsWith(currentText)) nextText = chunk.content;
640
- else if (currentText.startsWith(chunk.content)) nextText = currentText;
641
- else nextText = currentText + chunk.content;
642
- }
714
+ const nextText = delta !== "" ? currentText + delta : currentText;
643
715
  const textDelta = nextText.slice(currentText.length);
644
716
  state.currentSegmentText = nextText;
645
717
  state.totalTextContent += textDelta;
646
- const chunkPortion = chunk.delta || chunk.content || "";
718
+ const chunkPortion = chunk.delta || "";
647
719
  if (this.chunkStrategy.shouldEmit(chunkPortion, state.currentSegmentText) && state.currentSegmentText !== state.lastEmittedText) this.emitTextUpdateForMessage(messageId);
648
720
  }
649
721
  /**
@@ -666,7 +738,7 @@ var StreamProcessor = class StreamProcessor {
666
738
  const toolCallId = chunk.toolCallId;
667
739
  if (!state.toolCalls.get(toolCallId)) {
668
740
  const initialState = "awaiting-input";
669
- const toolName = chunk.toolCallName ?? chunk.toolName;
741
+ const toolName = chunk.toolCallName;
670
742
  const chunkMetadata = chunk.metadata;
671
743
  const newToolCall = {
672
744
  id: chunk.toolCallId,
@@ -674,7 +746,7 @@ var StreamProcessor = class StreamProcessor {
674
746
  arguments: "",
675
747
  state: initialState,
676
748
  parsedArguments: void 0,
677
- index: chunk.index ?? state.toolCalls.size,
749
+ index: state.toolCalls.size,
678
750
  ...chunkMetadata !== void 0 && { metadata: chunkMetadata }
679
751
  };
680
752
  state.toolCalls.set(toolCallId, newToolCall);
@@ -725,17 +797,15 @@ var StreamProcessor = class StreamProcessor {
725
797
  this.events.onToolCallStateChange?.(messageId, existingToolCall.id, existingToolCall.state, existingToolCall.arguments);
726
798
  }
727
799
  /**
728
- * Handle TOOL_CALL_END event — authoritative signal that a tool call's input is finalized.
729
- *
730
- * This event has a DUAL ROLE:
731
- * - Without `result`: Signals arguments are done (from adapter). Transitions to input-complete.
732
- * - With `result`: Signals tool was executed and result is available (from TextEngine).
733
- * Creates both output on the tool-call part AND a tool-result part.
800
+ * Handle TOOL_CALL_END event — arguments are finalized (input-complete).
801
+ * Tool output arrives on TOOL_CALL_RESULT, not on this event.
734
802
  *
735
- * If `input` is provided, it overrides the accumulated string parse as the
736
- * canonical parsed arguments.
803
+ * If TOOL_CALL_END carries parsed `input`, use it as the canonical arguments:
804
+ * back-fill the accumulated string when no TOOL_CALL_ARGS deltas were seen
805
+ * (adapters that deliver the whole input on END — e.g. Anthropic
806
+ * server_tool_use / web_search — issue #839) and override the rendered part's
807
+ * `input` with the canonical value.
737
808
  *
738
- * @see docs/chat-architecture.md#tool-results-and-the-tool_call_end-dual-role — Full explanation
739
809
  * @see docs/chat-architecture.md#single-shot-tool-call-response — End-to-end flow
740
810
  */
741
811
  handleToolCallEndEvent(chunk) {
@@ -743,39 +813,27 @@ var StreamProcessor = class StreamProcessor {
743
813
  if (!messageId) return;
744
814
  const msgState = this.getMessageState(messageId);
745
815
  if (!msgState) return;
816
+ const input = chunk.input !== void 0 ? chunk.input : tanstackMetadata(chunk)?.input;
746
817
  const existingToolCall = msgState.toolCalls.get(chunk.toolCallId);
747
818
  if (existingToolCall && existingToolCall.state !== "input-complete") {
748
- if (chunk.input !== void 0 && !existingToolCall.arguments) try {
749
- existingToolCall.arguments = JSON.stringify(chunk.input);
819
+ if (input !== void 0 && !existingToolCall.arguments) try {
820
+ existingToolCall.arguments = JSON.stringify(input);
750
821
  } catch {}
751
822
  const index = msgState.toolCallOrder.indexOf(chunk.toolCallId);
752
823
  this.completeToolCall(messageId, index, existingToolCall);
753
- if (chunk.input !== void 0) {
754
- existingToolCall.parsedArguments = chunk.input;
824
+ if (input !== void 0) {
825
+ existingToolCall.parsedArguments = input;
755
826
  this.messages = updateToolCallPart(this.messages, messageId, {
756
827
  id: existingToolCall.id,
757
828
  name: existingToolCall.name,
758
829
  arguments: existingToolCall.arguments,
759
830
  state: "input-complete",
760
- input: chunk.input,
831
+ input,
761
832
  ...existingToolCall.metadata !== void 0 && { metadata: existingToolCall.metadata }
762
833
  });
763
834
  this.emitMessagesChange();
764
835
  }
765
836
  }
766
- if (chunk.result) {
767
- let output;
768
- if (Array.isArray(chunk.result)) output = chunk.result;
769
- else try {
770
- output = JSON.parse(chunk.result);
771
- } catch {
772
- output = chunk.result;
773
- }
774
- this.messages = updateToolCallWithOutput(this.messages, chunk.toolCallId, output, chunk.state === "output-error" ? "error" : void 0);
775
- const resultState = chunk.state === "output-error" ? "error" : "complete";
776
- this.messages = updateToolResultPart(this.messages, messageId, chunk.toolCallId, chunk.result, resultState, resultState === "error" ? this.extractToolResultError(output) : void 0);
777
- this.emitMessagesChange();
778
- }
779
837
  }
780
838
  extractToolResultError(output) {
781
839
  if (output && typeof output === "object" && "error" in output && typeof output.error === "string") return output.error;
@@ -789,16 +847,17 @@ var StreamProcessor = class StreamProcessor {
789
847
  * This is the spec-compliant path for delivering tool results to the client.
790
848
  */
791
849
  handleToolCallResultEvent(chunk) {
792
- const messageId = this.toolCallToMessage.get(chunk.toolCallId);
850
+ const messageId = this.toolCallToMessage.get(chunk.toolCallId) ?? this.messages.find((m) => m.parts.some((p) => p.type === "tool-call" && p.id === chunk.toolCallId))?.id;
793
851
  if (!messageId) return;
852
+ const isOutputError = chunk.state === "output-error" || tanstackMetadata(chunk)?.state === "output-error";
794
853
  let output;
795
854
  try {
796
855
  output = JSON.parse(chunk.content);
797
856
  } catch {
798
857
  output = chunk.content;
799
858
  }
800
- this.messages = updateToolCallWithOutput(this.messages, chunk.toolCallId, output, chunk.state === "output-error" ? "error" : void 0);
801
- const resultState = chunk.state === "output-error" ? "error" : "complete";
859
+ this.messages = updateToolCallWithOutput(this.messages, chunk.toolCallId, output, isOutputError ? "error" : void 0);
860
+ const resultState = isOutputError ? "error" : "complete";
802
861
  this.messages = updateToolResultPart(this.messages, messageId, chunk.toolCallId, chunk.content, resultState, resultState === "error" ? this.extractToolResultError(output) : void 0);
803
862
  this.emitMessagesChange();
804
863
  }
@@ -822,7 +881,8 @@ var StreamProcessor = class StreamProcessor {
822
881
  * @see docs/chat-architecture.md#adapter-contract — Why RUN_FINISHED is mandatory
823
882
  */
824
883
  handleRunFinishedEvent(chunk) {
825
- this.finishReason = chunk.finishReason ?? null;
884
+ const extra = chunk;
885
+ this.finishReason = extra.finishReason !== void 0 ? extra.finishReason : tanstackMetadata(chunk)?.finishReason ?? null;
826
886
  this.activeRuns.delete(chunk.runId);
827
887
  if (chunk.outcome?.type === "interrupt") this.handleInterrupts(chunk.outcome.interrupts);
828
888
  if (this.activeRuns.size === 0) {
@@ -876,12 +936,12 @@ var StreamProcessor = class StreamProcessor {
876
936
  */
877
937
  handleRunErrorEvent(chunk) {
878
938
  this.hasError = true;
879
- const runId = "runId" in chunk && typeof chunk.runId === "string" ? chunk.runId : void 0;
939
+ const runId = getChunkRunId(chunk);
880
940
  if (runId) this.activeRuns.delete(runId);
881
941
  else this.activeRuns.clear();
882
942
  const { messageId } = this.ensureAssistantMessage();
883
- const errorMessage = chunk.message || chunk.error?.message || "An error occurred";
884
- if (!chunk.message && !chunk.error?.message) console.error("[StreamProcessor] RUN_ERROR with no message; original chunk:", chunk);
943
+ const errorMessage = chunk.message || "An error occurred";
944
+ if (!chunk.message) console.error("[StreamProcessor] RUN_ERROR with no message; original chunk:", chunk);
885
945
  if (this.structuredMessageIds.has(messageId)) {
886
946
  this.flushStructuredOutputUpdate(messageId);
887
947
  this.messages = errorStructuredOutputPart(this.messages, messageId, errorMessage);
@@ -894,12 +954,12 @@ var StreamProcessor = class StreamProcessor {
894
954
  /**
895
955
  * Handle STEP_STARTED event (for thinking/reasoning content).
896
956
  *
897
- * Records the stepId so that subsequent STEP_FINISHED deltas accumulate
957
+ * Records the stepId so later REASONING_MESSAGE_CONTENT deltas accumulate
898
958
  * into their own ThinkingPart. Does not create a message — the message
899
- * is lazily created when the first STEP_FINISHED content arrives.
959
+ * is lazily created when the first REASONING_MESSAGE_CONTENT arrives.
900
960
  */
901
961
  handleStepStartedEvent(chunk) {
902
- const stepId = chunk.stepId ?? generateMessageId();
962
+ const stepId = chunk.stepName || generateMessageId();
903
963
  const activeId = this.getActiveAssistantMessageId();
904
964
  if (activeId) {
905
965
  const state = this.getMessageState(activeId);
@@ -916,48 +976,25 @@ var StreamProcessor = class StreamProcessor {
916
976
  this.pendingThinkingStepId = stepId;
917
977
  }
918
978
  /**
919
- * Handle STEP_FINISHED event (for thinking/reasoning content).
920
- *
921
- * Accumulates delta into the current thinking step's content and updates
922
- * the corresponding ThinkingPart in the UIMessage.
979
+ * Handle STEP_FINISHED event.
923
980
  *
924
- * @see docs/chat-architecture.md#thinkingreasoning-content — Thinking flow
981
+ * Thinking *content* comes from REASONING_MESSAGE_CONTENT, not STEP_FINISHED.
982
+ * But some adapters (e.g. BytePlus thinking-summary) carry the provider
983
+ * signature blob ONLY on the STEP_FINISHED event, so still extract that here
984
+ * and attach it to the thinking step the reasoning events already built.
925
985
  */
926
986
  handleStepFinishedEvent(chunk) {
987
+ const extra = chunk;
988
+ const signature = extra.signature;
989
+ if (!signature) return;
927
990
  const { messageId, state } = this.ensureAssistantMessage(this.getActiveAssistantMessageId() ?? void 0);
928
- if (state.hasSeenReasoningEvents) {
929
- if (chunk.signature) {
930
- const stepId = state.currentThinkingStepId ?? chunk.stepId;
931
- if (!stepId) return;
932
- const thinking = state.thinkingSteps.get(stepId);
933
- if (thinking !== void 0) {
934
- state.thinkingStepSignatures.set(stepId, chunk.signature);
935
- this.messages = updateThinkingPart(this.messages, messageId, stepId, thinking, chunk.signature);
936
- this.emitMessagesChange();
937
- }
938
- }
939
- return;
940
- }
941
- this.consumePendingThinkingStep(state);
942
- const stepId = state.currentThinkingStepId ?? chunk.stepId ?? generateMessageId();
943
- if (!state.thinkingSteps.has(stepId)) {
944
- state.thinkingSteps.set(stepId, "");
945
- state.thinkingStepOrder.push(stepId);
946
- state.currentThinkingStepId = stepId;
947
- }
948
- const previous = state.thinkingSteps.get(stepId) ?? "";
949
- let nextThinking = previous;
950
- if (chunk.delta && chunk.delta !== "") nextThinking = previous + chunk.delta;
951
- else if (chunk.content && chunk.content !== "") {
952
- if (chunk.content.startsWith(previous)) nextThinking = chunk.content;
953
- else if (previous.startsWith(chunk.content)) nextThinking = previous;
954
- else nextThinking = previous + chunk.content;
955
- }
956
- state.thinkingSteps.set(stepId, nextThinking);
957
- if (chunk.signature) state.thinkingStepSignatures.set(stepId, chunk.signature);
958
- this.messages = updateThinkingPart(this.messages, messageId, stepId, nextThinking, state.thinkingStepSignatures.get(stepId));
991
+ const stepId = state.currentThinkingStepId ?? extra.stepId;
992
+ if (!stepId) return;
993
+ const thinking = state.thinkingSteps.get(stepId);
994
+ if (thinking === void 0) return;
995
+ state.thinkingStepSignatures.set(stepId, signature);
996
+ this.messages = updateThinkingPart(this.messages, messageId, stepId, thinking, signature);
959
997
  this.emitMessagesChange();
960
- this.events.onThinkingUpdate?.(messageId, stepId, nextThinking);
961
998
  }
962
999
  /**
963
1000
  * Handle REASONING_MESSAGE_CONTENT event (AG-UI reasoning protocol).
@@ -983,6 +1020,58 @@ var StreamProcessor = class StreamProcessor {
983
1020
  this.events.onThinkingUpdate?.(messageId, stepId, nextThinking);
984
1021
  }
985
1022
  /**
1023
+ * Attach a provider signature blob from REASONING_ENCRYPTED_VALUE.
1024
+ * `subtype: 'message'` updates ThinkingPart.signature.
1025
+ * `subtype: 'tool-call'` stores Gemini thoughtSignature on the tool-call part.
1026
+ */
1027
+ handleReasoningEncryptedValueEvent(chunk) {
1028
+ const encryptedValue = chunk.encryptedValue;
1029
+ if (typeof encryptedValue !== "string" || encryptedValue === "") return;
1030
+ if (chunk.subtype === "tool-call") {
1031
+ this.attachToolCallSignature(chunk.entityId, encryptedValue);
1032
+ return;
1033
+ }
1034
+ const { messageId, state } = this.ensureAssistantMessage(this.getActiveAssistantMessageId() ?? void 0);
1035
+ const stepId = state.currentThinkingStepId ?? chunk.entityId;
1036
+ state.thinkingStepSignatures.set(stepId, encryptedValue);
1037
+ const content = state.thinkingSteps.get(stepId) ?? "";
1038
+ if (!state.thinkingSteps.has(stepId)) {
1039
+ state.thinkingSteps.set(stepId, content);
1040
+ state.thinkingStepOrder.push(stepId);
1041
+ }
1042
+ this.messages = updateThinkingPart(this.messages, messageId, stepId, content, encryptedValue);
1043
+ this.emitMessagesChange();
1044
+ }
1045
+ attachToolCallSignature(toolCallId, thoughtSignature) {
1046
+ this.messages = this.messages.map((msg) => {
1047
+ let changed = false;
1048
+ const parts = msg.parts.map((part) => {
1049
+ if (part.type !== "tool-call" || part.id !== toolCallId) return part;
1050
+ changed = true;
1051
+ return {
1052
+ ...part,
1053
+ metadata: {
1054
+ ...part.metadata != null && typeof part.metadata === "object" ? part.metadata : {},
1055
+ thoughtSignature
1056
+ }
1057
+ };
1058
+ });
1059
+ return changed ? {
1060
+ ...msg,
1061
+ parts
1062
+ } : msg;
1063
+ });
1064
+ for (const state of this.messageStates.values()) {
1065
+ const call = state.toolCalls.get(toolCallId);
1066
+ if (!call) continue;
1067
+ call.metadata = {
1068
+ ...call.metadata ?? {},
1069
+ thoughtSignature
1070
+ };
1071
+ }
1072
+ this.emitMessagesChange();
1073
+ }
1074
+ /**
986
1075
  * Handle CUSTOM event.
987
1076
  *
988
1077
  * Handles custom events consumed by the processor:
@@ -1077,12 +1166,8 @@ var StreamProcessor = class StreamProcessor {
1077
1166
  /**
1078
1167
  * Detect if an incoming content chunk represents a NEW text segment
1079
1168
  */
1080
- isNewTextSegment(chunk, previous) {
1081
- if (chunk.content !== void 0) {
1082
- if (chunk.content.length < previous.length) return true;
1083
- if (!chunk.content.startsWith(previous) && !previous.startsWith(chunk.content)) return true;
1084
- }
1085
- return false;
1169
+ isNewTextSegment(_chunk, _previous) {
1170
+ return true;
1086
1171
  }
1087
1172
  /**
1088
1173
  * Complete all tool calls across all active messages — safety net for stream termination.