@tanstack/ai 0.47.2 → 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 (105) 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 +126 -31
  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 +197 -110
  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 +59 -54
  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 +12 -5
  30. package/dist/esm/middlewares/otel.js.map +1 -1
  31. package/dist/esm/stream-to-response.js +13 -6
  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/errors.d.ts +9 -0
  53. package/dist/esm/utilities/errors.js +13 -1
  54. package/dist/esm/utilities/errors.js.map +1 -1
  55. package/dist/esm/utilities/merge-metadata.d.ts +14 -0
  56. package/dist/esm/utilities/merge-metadata.js +43 -0
  57. package/dist/esm/utilities/merge-metadata.js.map +1 -0
  58. package/dist/esm/utilities/normalize-stream-chunk.d.ts +3 -0
  59. package/dist/esm/utilities/normalize-stream-chunk.js +100 -0
  60. package/dist/esm/utilities/normalize-stream-chunk.js.map +1 -0
  61. package/dist/esm/utilities/reasoning-encrypted-value.d.ts +8 -0
  62. package/dist/esm/utilities/reasoning-encrypted-value.js +16 -0
  63. package/dist/esm/utilities/reasoning-encrypted-value.js.map +1 -0
  64. package/dist/esm/utilities/restore-inbound-chunk.d.ts +15 -0
  65. package/dist/esm/utilities/restore-inbound-chunk.js +43 -0
  66. package/dist/esm/utilities/restore-inbound-chunk.js.map +1 -0
  67. package/dist/esm/utilities/spec-event-keys.d.ts +2 -0
  68. package/dist/esm/utilities/spec-event-keys.js +56 -0
  69. package/dist/esm/utilities/spec-event-keys.js.map +1 -0
  70. package/dist/esm/utilities/structured-output-events.d.ts +3 -3
  71. package/dist/esm/utilities/structured-output-events.js.map +1 -1
  72. package/package.json +3 -3
  73. package/src/activities/chat/adapter.ts +4 -4
  74. package/src/activities/chat/index.ts +398 -400
  75. package/src/activities/chat/messages.ts +183 -36
  76. package/src/activities/chat/stream/processor.ts +313 -239
  77. package/src/activities/chat/tools/tool-calls.ts +23 -22
  78. package/src/activities/generateVideo/index.ts +7 -6
  79. package/src/activities/stream-generation-result.ts +8 -12
  80. package/src/activities/summarize/chat-stream-summarize.ts +98 -70
  81. package/src/adapter-internals.ts +2 -0
  82. package/src/client.ts +13 -0
  83. package/src/index.ts +6 -0
  84. package/src/middlewares/otel.ts +16 -8
  85. package/src/stream-to-response.ts +16 -4
  86. package/src/stream-to-websocket.ts +3 -1
  87. package/src/strip-to-spec-middleware.ts +48 -24
  88. package/src/types.ts +109 -393
  89. package/src/utilities/adapter-yield-chunk.ts +30 -0
  90. package/src/utilities/ag-ui-usage.test.ts +194 -0
  91. package/src/utilities/ag-ui-usage.ts +148 -0
  92. package/src/utilities/ag-ui-wire.ts +149 -31
  93. package/src/utilities/chat-params.ts +22 -39
  94. package/src/utilities/chunk-ids.ts +24 -0
  95. package/src/utilities/errors.ts +23 -0
  96. package/src/utilities/merge-metadata.test.ts +117 -0
  97. package/src/utilities/merge-metadata.ts +59 -0
  98. package/src/utilities/normalize-stream-chunk.test.ts +423 -0
  99. package/src/utilities/normalize-stream-chunk.ts +186 -0
  100. package/src/utilities/reasoning-encrypted-value.ts +18 -0
  101. package/src/utilities/restore-inbound-chunk.test.ts +133 -0
  102. package/src/utilities/restore-inbound-chunk.ts +72 -0
  103. package/src/utilities/spec-event-keys.test.ts +34 -0
  104. package/src/utilities/spec-event-keys.ts +74 -0
  105. package/src/utilities/structured-output-events.ts +3 -3
@@ -1,7 +1,10 @@
1
+ import { mergeMetadata, tanstackMetadata } from "../../../utilities/merge-metadata.js";
2
+ import { runErrorEventToError } from "../../../utilities/errors.js";
1
3
  import { INTERRUPT_BINDING_METADATA_KEY } from "../../../interrupt-resume.js";
2
4
  import { normalizeToolResult } from "../../../utilities/tool-result.js";
3
5
  import { isProviderExecutedToolCall } from "../../../utilities/provider-executed.js";
4
6
  import { aguiSnapshotMessageToUIMessage, generateMessageId, uiMessageToModelMessages } from "../messages.js";
7
+ import { getChunkRunId } from "../../../utilities/chunk-ids.js";
5
8
  import { defaultJSONParser } from "./json-parser.js";
6
9
  import { appendStructuredOutputDelta, completeStructuredOutputPart, errorStructuredOutputPart, updateTextPart, updateThinkingPart, updateToolCallApproval, updateToolCallApprovalResponse, updateToolCallPart, updateToolCallWithOutput, updateToolResultPart } from "./message-updaters.js";
7
10
  import { ImmediateStrategy } from "./strategies.js";
@@ -89,6 +92,7 @@ var StreamProcessor = class StreamProcessor {
89
92
  *
90
93
  * @param content - The message content (string or array of content parts)
91
94
  * @param id - Optional custom message ID (generated if not provided)
95
+ * @param metadata - Optional AG-UI metadata bag
92
96
  * @returns The created UIMessage
93
97
  *
94
98
  * @example
@@ -106,7 +110,7 @@ var StreamProcessor = class StreamProcessor {
106
110
  * processor.addUserMessage('Hello!', 'custom-id-123')
107
111
  * ```
108
112
  */
109
- addUserMessage(content, id) {
113
+ addUserMessage(content, id, metadata) {
110
114
  const parts = typeof content === "string" ? [{
111
115
  type: "text",
112
116
  content
@@ -117,7 +121,8 @@ var StreamProcessor = class StreamProcessor {
117
121
  id: id ?? generateMessageId(),
118
122
  role: "user",
119
123
  parts,
120
- createdAt: /* @__PURE__ */ new Date()
124
+ createdAt: /* @__PURE__ */ new Date(),
125
+ ...metadata != null ? { metadata } : {}
121
126
  };
122
127
  this.messages = [...this.messages, userMessage];
123
128
  this.emitMessagesChange();
@@ -298,6 +303,9 @@ var StreamProcessor = class StreamProcessor {
298
303
  case "REASONING_MESSAGE_CONTENT":
299
304
  this.handleReasoningMessageContentEvent(chunk);
300
305
  break;
306
+ case "REASONING_ENCRYPTED_VALUE":
307
+ this.handleReasoningEncryptedValueEvent(chunk);
308
+ break;
301
309
  case "TOOL_CALL_RESULT":
302
310
  this.handleToolCallResultEvent(chunk);
303
311
  break;
@@ -422,6 +430,33 @@ var StreamProcessor = class StreamProcessor {
422
430
  };
423
431
  }
424
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
+ /**
425
460
  * Handle TEXT_MESSAGE_START event
426
461
  */
427
462
  handleTextMessageStartEvent(chunk) {
@@ -443,11 +478,13 @@ var StreamProcessor = class StreamProcessor {
443
478
  }
444
479
  this.activeMessageIds.delete(pendingId);
445
480
  this.activeMessageIds.add(messageId);
481
+ for (const [toolCallId, mappedMessageId] of this.toolCallToMessage) if (mappedMessageId === pendingId) this.toolCallToMessage.set(toolCallId, messageId);
446
482
  }
447
483
  if (!this.messageStates.has(messageId)) {
448
484
  this.createMessageState(messageId, uiRole);
449
485
  this.activeMessageIds.add(messageId);
450
486
  }
487
+ this.mergeMessageMetadata(messageId, chunk.metadata);
451
488
  this.emitMessagesChange();
452
489
  return;
453
490
  }
@@ -461,6 +498,7 @@ var StreamProcessor = class StreamProcessor {
461
498
  existingState.lastEmittedText = "";
462
499
  existingState.hasToolCallsSinceTextStart = false;
463
500
  }
501
+ this.mergeMessageMetadata(messageId, chunk.metadata);
464
502
  return;
465
503
  }
466
504
  const newMessage = {
@@ -472,6 +510,7 @@ var StreamProcessor = class StreamProcessor {
472
510
  this.messages = [...this.messages, newMessage];
473
511
  this.createMessageState(messageId, uiRole);
474
512
  this.activeMessageIds.add(messageId);
513
+ this.mergeMessageMetadata(messageId, chunk.metadata);
475
514
  this.events.onStreamStart?.();
476
515
  this.emitMessagesChange();
477
516
  }
@@ -480,11 +519,11 @@ var StreamProcessor = class StreamProcessor {
480
519
  */
481
520
  handleTextMessageEndEvent(chunk) {
482
521
  const { messageId } = chunk;
522
+ this.mergeMessageMetadata(messageId, chunk.metadata);
483
523
  const state = this.getMessageState(messageId);
484
524
  if (!state) return;
485
525
  if (state.isComplete) return;
486
526
  if (state.currentSegmentText !== state.lastEmittedText) this.emitTextUpdateForMessage(messageId);
487
- this.completeAllToolCallsForMessage(messageId);
488
527
  }
489
528
  /**
490
529
  * Handle MESSAGES_SNAPSHOT event
@@ -492,8 +531,17 @@ var StreamProcessor = class StreamProcessor {
492
531
  handleMessagesSnapshotEvent(chunk) {
493
532
  this.resetStreamState();
494
533
  const prevMessages = this.messages;
495
- const normalized = chunk.messages.map(aguiSnapshotMessageToUIMessage);
496
- 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
+ });
497
545
  this.emitMessagesChange();
498
546
  }
499
547
  /**
@@ -519,6 +567,42 @@ var StreamProcessor = class StreamProcessor {
519
567
  * matching `tool-call` (and prefer pre-snapshot complete/output when the
520
568
  * snapshot is poorer) so server tools keep the same UI shape as client tools.
521
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
+ }
522
606
  reconcileSnapshotToolCalls(snapshot, prevMessages) {
523
607
  const prevToolCalls = /* @__PURE__ */ new Map();
524
608
  for (const msg of prevMessages) for (const part of msg.parts) if (part.type === "tool-call") prevToolCalls.set(part.id, part);
@@ -607,15 +691,9 @@ var StreamProcessor = class StreamProcessor {
607
691
  */
608
692
  handleTextMessageContentEvent(chunk) {
609
693
  const { messageId, state } = this.ensureAssistantMessage(chunk.messageId);
610
- this.completeAllToolCallsForMessage(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.
800
+ * Handle TOOL_CALL_END event — arguments are finalized (input-complete).
801
+ * Tool output arrives on TOOL_CALL_RESULT, not on this event.
729
802
  *
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.
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.
734
808
  *
735
- * If `input` is provided, it overrides the accumulated string parse as the
736
- * canonical parsed arguments.
737
- *
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);
@@ -889,21 +949,17 @@ var StreamProcessor = class StreamProcessor {
889
949
  this.emitStructuredOutputChange(messageId, "error");
890
950
  this.emitMessagesChange();
891
951
  }
892
- const error = new Error(errorMessage);
893
- const code = chunk.code ?? chunk.error?.code;
894
- if (code !== void 0) Object.assign(error, { code });
895
- if (chunk.rawEvent !== void 0) Object.assign(error, { rawEvent: chunk.rawEvent });
896
- this.events.onError?.(error);
952
+ this.events.onError?.(runErrorEventToError(chunk));
897
953
  }
898
954
  /**
899
955
  * Handle STEP_STARTED event (for thinking/reasoning content).
900
956
  *
901
- * Records the stepId so that subsequent STEP_FINISHED deltas accumulate
957
+ * Records the stepId so later REASONING_MESSAGE_CONTENT deltas accumulate
902
958
  * into their own ThinkingPart. Does not create a message — the message
903
- * is lazily created when the first STEP_FINISHED content arrives.
959
+ * is lazily created when the first REASONING_MESSAGE_CONTENT arrives.
904
960
  */
905
961
  handleStepStartedEvent(chunk) {
906
- const stepId = chunk.stepId ?? generateMessageId();
962
+ const stepId = chunk.stepName || generateMessageId();
907
963
  const activeId = this.getActiveAssistantMessageId();
908
964
  if (activeId) {
909
965
  const state = this.getMessageState(activeId);
@@ -920,48 +976,25 @@ var StreamProcessor = class StreamProcessor {
920
976
  this.pendingThinkingStepId = stepId;
921
977
  }
922
978
  /**
923
- * Handle STEP_FINISHED event (for thinking/reasoning content).
979
+ * Handle STEP_FINISHED event.
924
980
  *
925
- * Accumulates delta into the current thinking step's content and updates
926
- * the corresponding ThinkingPart in the UIMessage.
927
- *
928
- * @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.
929
985
  */
930
986
  handleStepFinishedEvent(chunk) {
987
+ const extra = chunk;
988
+ const signature = extra.signature;
989
+ if (!signature) return;
931
990
  const { messageId, state } = this.ensureAssistantMessage(this.getActiveAssistantMessageId() ?? void 0);
932
- if (state.hasSeenReasoningEvents) {
933
- if (chunk.signature) {
934
- const stepId = state.currentThinkingStepId ?? chunk.stepId;
935
- if (!stepId) return;
936
- const thinking = state.thinkingSteps.get(stepId);
937
- if (thinking !== void 0) {
938
- state.thinkingStepSignatures.set(stepId, chunk.signature);
939
- this.messages = updateThinkingPart(this.messages, messageId, stepId, thinking, chunk.signature);
940
- this.emitMessagesChange();
941
- }
942
- }
943
- return;
944
- }
945
- this.consumePendingThinkingStep(state);
946
- const stepId = state.currentThinkingStepId ?? chunk.stepId ?? generateMessageId();
947
- if (!state.thinkingSteps.has(stepId)) {
948
- state.thinkingSteps.set(stepId, "");
949
- state.thinkingStepOrder.push(stepId);
950
- state.currentThinkingStepId = stepId;
951
- }
952
- const previous = state.thinkingSteps.get(stepId) ?? "";
953
- let nextThinking = previous;
954
- if (chunk.delta && chunk.delta !== "") nextThinking = previous + chunk.delta;
955
- else if (chunk.content && chunk.content !== "") {
956
- if (chunk.content.startsWith(previous)) nextThinking = chunk.content;
957
- else if (previous.startsWith(chunk.content)) nextThinking = previous;
958
- else nextThinking = previous + chunk.content;
959
- }
960
- state.thinkingSteps.set(stepId, nextThinking);
961
- if (chunk.signature) state.thinkingStepSignatures.set(stepId, chunk.signature);
962
- 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);
963
997
  this.emitMessagesChange();
964
- this.events.onThinkingUpdate?.(messageId, stepId, nextThinking);
965
998
  }
966
999
  /**
967
1000
  * Handle REASONING_MESSAGE_CONTENT event (AG-UI reasoning protocol).
@@ -987,6 +1020,58 @@ var StreamProcessor = class StreamProcessor {
987
1020
  this.events.onThinkingUpdate?.(messageId, stepId, nextThinking);
988
1021
  }
989
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
+ /**
990
1075
  * Handle CUSTOM event.
991
1076
  *
992
1077
  * Handles custom events consumed by the processor:
@@ -1081,12 +1166,8 @@ var StreamProcessor = class StreamProcessor {
1081
1166
  /**
1082
1167
  * Detect if an incoming content chunk represents a NEW text segment
1083
1168
  */
1084
- isNewTextSegment(chunk, previous) {
1085
- if (chunk.content !== void 0) {
1086
- if (chunk.content.length < previous.length) return true;
1087
- if (!chunk.content.startsWith(previous) && !previous.startsWith(chunk.content)) return true;
1088
- }
1089
- return false;
1169
+ isNewTextSegment(_chunk, _previous) {
1170
+ return true;
1090
1171
  }
1091
1172
  /**
1092
1173
  * Complete all tool calls across all active messages — safety net for stream termination.
@@ -1118,7 +1199,13 @@ var StreamProcessor = class StreamProcessor {
1118
1199
  */
1119
1200
  completeToolCall(messageId, _index, toolCall) {
1120
1201
  toolCall.state = "input-complete";
1121
- toolCall.parsedArguments = this.jsonParser.parse(toolCall.arguments);
1202
+ let strictParseSucceeded = false;
1203
+ try {
1204
+ toolCall.parsedArguments = JSON.parse(toolCall.arguments);
1205
+ strictParseSucceeded = true;
1206
+ } catch {
1207
+ toolCall.parsedArguments = void 0;
1208
+ }
1122
1209
  if (this.isToolCallPartErrored(toolCall.id)) return;
1123
1210
  if (this.isToolCallPartAwaitingUserAction(toolCall.id)) return;
1124
1211
  this.messages = updateToolCallPart(this.messages, messageId, {
@@ -1126,7 +1213,7 @@ var StreamProcessor = class StreamProcessor {
1126
1213
  name: toolCall.name,
1127
1214
  arguments: toolCall.arguments,
1128
1215
  state: "input-complete",
1129
- ...toolCall.parsedArguments !== void 0 && { input: toolCall.parsedArguments },
1216
+ ...strictParseSucceeded && { input: toolCall.parsedArguments },
1130
1217
  ...toolCall.metadata !== void 0 && { metadata: toolCall.metadata }
1131
1218
  });
1132
1219
  this.emitMessagesChange();