@xapp/chat-widget 1.100.0 → 1.101.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.
package/dist/index.es.js CHANGED
@@ -4643,8 +4643,8 @@ var MCPChat = /** @class */ (function () {
4643
4643
  // Generic fallback
4644
4644
  return "Something went wrong. Please try again.";
4645
4645
  };
4646
- MCPChat.prototype.dispatchStreamStart = function (messageId) {
4647
- var bot = this.getBot(undefined);
4646
+ MCPChat.prototype.dispatchStreamStart = function (messageId, user) {
4647
+ var bot = this.getBot(user);
4648
4648
  var streamTimestamp = +new Date();
4649
4649
  log("[dispatchStreamStart] Stream started: ".concat(messageId, ", timestamp: ").concat(streamTimestamp));
4650
4650
  this.dispatch({
@@ -4657,30 +4657,25 @@ var MCPChat = /** @class */ (function () {
4657
4657
  },
4658
4658
  });
4659
4659
  };
4660
- MCPChat.prototype.dispatchStreamChunk = function (messageId, text) {
4660
+ MCPChat.prototype.dispatchStreamChunk = function (messageId, text, user) {
4661
4661
  this.dispatch({
4662
4662
  type: "chat",
4663
4663
  detail: {
4664
4664
  type: "chat.stream.chunk",
4665
- user: this.getBot(undefined),
4665
+ user: this.getBot(user),
4666
4666
  messageId: messageId,
4667
4667
  text: text,
4668
4668
  timestamp: +new Date(),
4669
4669
  },
4670
4670
  });
4671
4671
  };
4672
- MCPChat.prototype.dispatchStreamEnd = function (messageId, fullMessage, toolCalls) {
4672
+ MCPChat.prototype.dispatchStreamEnd = function (messageId, fullMessage, options) {
4673
+ if (options === void 0) { options = {}; }
4674
+ var toolCalls = options.toolCalls, user = options.user, preserveChips = options.preserveChips;
4673
4675
  log("Stream ended: ".concat(messageId));
4674
4676
  this.dispatch({
4675
4677
  type: "chat",
4676
- detail: {
4677
- type: "chat.stream.end",
4678
- user: this.getBot(undefined),
4679
- messageId: messageId,
4680
- msg: fullMessage,
4681
- toolCalls: toolCalls,
4682
- timestamp: +new Date(),
4683
- },
4678
+ detail: __assign$1(__assign$1({ type: "chat.stream.end", user: this.getBot(user), messageId: messageId, msg: fullMessage, toolCalls: toolCalls }, (preserveChips && { preserveChips: preserveChips })), { timestamp: +new Date() }),
4684
4679
  });
4685
4680
  };
4686
4681
  MCPChat.prototype.dispatchStreamAbort = function (messageId) {
@@ -4790,6 +4785,19 @@ var MCPChat = /** @class */ (function () {
4790
4785
  MCPChat.prototype.bufferStreamText = function (text) {
4791
4786
  this.streamBuffer += text;
4792
4787
  };
4788
+ /**
4789
+ * Dispatches whatever is buffered right now, without waiting on the throttle timer.
4790
+ *
4791
+ * Used when a bubble has to be closed mid-turn (`message_start`): the split has to be
4792
+ * synchronous, because frames arrive in order on one `onmessage` callback and deferring it
4793
+ * to a microtask would let the next `text` frame land in the bubble we are closing.
4794
+ */
4795
+ MCPChat.prototype.flushStreamBufferSync = function () {
4796
+ if (this.streamBuffer.length > 0 && this.streamingMessageIdForThrottle) {
4797
+ this.dispatchStreamChunk(this.streamingMessageIdForThrottle, this.streamBuffer);
4798
+ this.streamBuffer = "";
4799
+ }
4800
+ };
4793
4801
  /**
4794
4802
  * Flushes any remaining buffered text and stops the throttle timer.
4795
4803
  * Returns a promise that resolves when all buffered text has been dispatched.
@@ -5070,7 +5078,7 @@ var MCPChat = /** @class */ (function () {
5070
5078
  */
5071
5079
  MCPChat.prototype.performSSERequest = function (body) {
5072
5080
  return __awaiter$1(this, void 0, void 0, function () {
5073
- var endpoint, messageId, textChunks, hasError, httpStatusCode, sseError;
5081
+ var endpoint, messageId, bubbleId, bubbleUser, bubbleChunks, hasError, httpStatusCode, sseError;
5074
5082
  var _this = this;
5075
5083
  return __generator$1(this, function (_a) {
5076
5084
  endpoint = this.getEndpoint();
@@ -5083,7 +5091,8 @@ var MCPChat = /** @class */ (function () {
5083
5091
  this.abortController = new AbortController();
5084
5092
  messageId = uuid_1();
5085
5093
  this.currentMessageId = messageId;
5086
- textChunks = [];
5094
+ bubbleId = messageId;
5095
+ bubbleChunks = [];
5087
5096
  hasError = false;
5088
5097
  return [2 /*return*/, new Promise(function (resolve, reject) {
5089
5098
  // WARNING: Do not log headers object - it contains sensitive auth tokens
@@ -5114,6 +5123,7 @@ var MCPChat = /** @class */ (function () {
5114
5123
  });
5115
5124
  }); },
5116
5125
  onmessage: function (event) {
5126
+ var _a;
5117
5127
  // Guard: ignore events from stale/aborted requests
5118
5128
  if (_this.currentMessageId !== messageId) {
5119
5129
  log("[SSE] Ignoring stale event for messageId: ".concat(messageId, ", current: ").concat(_this.currentMessageId));
@@ -5144,6 +5154,10 @@ var MCPChat = /** @class */ (function () {
5144
5154
  if (data.isReplay) {
5145
5155
  _this.isReplaying = true;
5146
5156
  }
5157
+ // A server may name the participant up front.
5158
+ if (data.user) {
5159
+ bubbleUser = data.user;
5160
+ }
5147
5161
  // Store identity from server response
5148
5162
  if (data.userId || data.sessionId) {
5149
5163
  if (data.userId) {
@@ -5163,13 +5177,13 @@ var MCPChat = /** @class */ (function () {
5163
5177
  if (data.isReplay) {
5164
5178
  // Replay connection — resume into the existing partial
5165
5179
  // message instead of creating a duplicate placeholder
5166
- _this.dispatchStreamResume(messageId);
5180
+ _this.dispatchStreamResume(bubbleId);
5167
5181
  }
5168
5182
  else {
5169
5183
  // Normal connection — create new streaming placeholder
5170
- _this.dispatchStreamStart(messageId);
5184
+ _this.dispatchStreamStart(bubbleId, bubbleUser);
5171
5185
  }
5172
- _this.startStreamThrottle(messageId);
5186
+ _this.startStreamThrottle(bubbleId);
5173
5187
  break;
5174
5188
  case "session_expired":
5175
5189
  // Session no longer exists on server - need to start fresh
@@ -5187,14 +5201,40 @@ var MCPChat = /** @class */ (function () {
5187
5201
  _this.initializeNewSession();
5188
5202
  resolve();
5189
5203
  break;
5204
+ case "message_start": {
5205
+ // Close the bubble being streamed and open a new one, optionally
5206
+ // under a different participant. This is the only way a turn can
5207
+ // render as more than one message: the reducer creates a fresh
5208
+ // bubble per `chat.stream.start` and finalizes any still-streaming
5209
+ // one, so a server can make a handoff look like someone joining.
5210
+ // Servers that never send this are unaffected.
5211
+ log("[SSE] message_start \u2014 splitting bubble, user: ".concat((_a = data.user) === null || _a === void 0 ? void 0 : _a.displayName));
5212
+ _this.flushStreamBufferSync();
5213
+ _this.dispatchStreamEnd(bubbleId, { text: bubbleChunks.join("") }, {
5214
+ // Same debug-mode capture as `done`, scoped to this bubble:
5215
+ // stream.end replaces a bubble's toolCalls wholesale, so any
5216
+ // left in currentToolCalls would reappear on the next one.
5217
+ toolCalls: (_this.config.debugMode || _this._debugMode) ? __spreadArray$1([], _this.currentToolCalls, true) : undefined,
5218
+ user: bubbleUser,
5219
+ // Chips belong to the turn; its final end sets them.
5220
+ preserveChips: true,
5221
+ });
5222
+ _this.currentToolCalls = [];
5223
+ bubbleId = uuid_1();
5224
+ bubbleChunks = [];
5225
+ bubbleUser = data.user;
5226
+ _this.dispatchStreamStart(bubbleId, bubbleUser);
5227
+ _this.startStreamThrottle(bubbleId);
5228
+ break;
5229
+ }
5190
5230
  case "text":
5191
5231
  if (data.text) {
5192
- log("[SSE] Text chunk received (".concat(data.text.length, " chars), total chunks: ").concat(textChunks.length + 1, ", isReplay: ").concat(data.isReplay));
5193
- textChunks.push(data.text);
5232
+ log("[SSE] Text chunk received (".concat(data.text.length, " chars), chunks in this bubble: ").concat(bubbleChunks.length + 1, ", isReplay: ").concat(data.isReplay));
5233
+ bubbleChunks.push(data.text);
5194
5234
  if (data.isReplay || _this.isSleeping) {
5195
5235
  // Replayed text or tab hidden — dispatch immediately
5196
5236
  // (no throttle timer running in either case)
5197
- _this.dispatchStreamChunk(messageId, data.text);
5237
+ _this.dispatchStreamChunk(bubbleId, data.text, bubbleUser);
5198
5238
  }
5199
5239
  else {
5200
5240
  // Live text — buffer for throttled dispatch
@@ -5218,7 +5258,7 @@ var MCPChat = /** @class */ (function () {
5218
5258
  _this.currentToolCalls.push(toolCall);
5219
5259
  // Always dispatch tool start for user-friendly progress display
5220
5260
  _this.dispatchToolStart({
5221
- messageId: messageId,
5261
+ messageId: bubbleId,
5222
5262
  toolName: data.toolName,
5223
5263
  toolCallId: data.toolCallId,
5224
5264
  displayName: data.displayName,
@@ -5259,7 +5299,7 @@ var MCPChat = /** @class */ (function () {
5259
5299
  }
5260
5300
  // Always dispatch tool end for user-friendly progress display
5261
5301
  _this.dispatchToolEnd({
5262
- messageId: messageId,
5302
+ messageId: bubbleId,
5263
5303
  toolName: data.toolName,
5264
5304
  toolCallId: data.toolCallId,
5265
5305
  displayName: data.displayName,
@@ -5281,8 +5321,10 @@ var MCPChat = /** @class */ (function () {
5281
5321
  storeIdentity(undefined, data.response.sessionId);
5282
5322
  }
5283
5323
  // Wait for buffer to flush, then finalize
5284
- var accumulatedText = textChunks.join("");
5285
- log("[SSE] Done event received. Total chunks: ".concat(textChunks.length, ", accumulated text length: ").concat(accumulatedText.length));
5324
+ // The bubble's own text, not the whole turn's — a turn split by
5325
+ // `message_start` has already committed its earlier bubbles.
5326
+ var accumulatedText = bubbleChunks.join("");
5327
+ log("[SSE] Done event received. Chunks in this bubble: ".concat(bubbleChunks.length, ", accumulated text length: ").concat(accumulatedText.length));
5286
5328
  log("[SSE] First 100 chars of accumulated: ".concat(accumulatedText.substring(0, 100)));
5287
5329
  // Map suggestions to options (chips) format
5288
5330
  var suggestions = data.response.suggestions;
@@ -5298,8 +5340,14 @@ var MCPChat = /** @class */ (function () {
5298
5340
  if (sources === null || sources === void 0 ? void 0 : sources.length) {
5299
5341
  log("[SSE] Sources: ".concat(sources.length, " items"));
5300
5342
  }
5343
+ // `response.response` is request-scoped. For an unsplit turn it is
5344
+ // the whole answer, and the fallback serves servers that deliver it
5345
+ // only via `done`. Once `message_start` has fired it would copy every
5346
+ // earlier bubble's text into this one, so a split turn's bubbles
5347
+ // must stream their text as `text` frames.
5348
+ var isSplitTurn = bubbleId !== messageId;
5301
5349
  var finalMessage_1 = {
5302
- text: accumulatedText || data.response.response || "",
5350
+ text: accumulatedText || (isSplitTurn ? "" : data.response.response) || "",
5303
5351
  options: options,
5304
5352
  sources: sources,
5305
5353
  displays: data.response.displays,
@@ -5307,7 +5355,7 @@ var MCPChat = /** @class */ (function () {
5307
5355
  // Capture tool calls before flushing (debug mode — config or runtime toggle)
5308
5356
  var toolCalls_1 = (_this.config.debugMode || _this._debugMode) ? __spreadArray$1([], _this.currentToolCalls, true) : undefined;
5309
5357
  _this.flushStreamBuffer().then(function () {
5310
- _this.dispatchStreamEnd(messageId, finalMessage_1, toolCalls_1);
5358
+ _this.dispatchStreamEnd(bubbleId, finalMessage_1, { toolCalls: toolCalls_1, user: bubbleUser });
5311
5359
  _this.currentToolCalls = []; // Clear after dispatch
5312
5360
  resolve();
5313
5361
  });
@@ -5347,7 +5395,7 @@ var MCPChat = /** @class */ (function () {
5347
5395
  log("[MCPChat] SSE error during sleep - will request replay on wakeup");
5348
5396
  _this.needsReplayOnWakeup = true;
5349
5397
  if (_this.streamingMessageIdForThrottle) {
5350
- _this.dispatchStreamAbort(messageId);
5398
+ _this.dispatchStreamAbort(bubbleId);
5351
5399
  }
5352
5400
  _this.stopStreamThrottle();
5353
5401
  }
@@ -37947,8 +37995,11 @@ function update(state, action) {
37947
37995
  var finalMsg_1 = endDetail.msg;
37948
37996
  // Preserve tool calls from either the action or the existing message
37949
37997
  var toolCalls_1 = endDetail.toolCalls || endStreamingChat.toolCalls;
37950
- // Update chips from message options (same as chat.msg handler)
37951
- var chips = ((_w = finalMsg_1 === null || finalMsg_1 === void 0 ? void 0 : finalMsg_1.options) === null || _w === void 0 ? void 0 : _w.length) ? finalMsg_1.options : [];
37998
+ // Update chips from message options (same as chat.msg handler) — unless this closes
37999
+ // an intermediate bubble of a split turn, whose chips arrive with the final end.
38000
+ var chips = endDetail.preserveChips
38001
+ ? state.chips
38002
+ : ((_w = finalMsg_1 === null || finalMsg_1 === void 0 ? void 0 : finalMsg_1.options) === null || _w === void 0 ? void 0 : _w.length) ? finalMsg_1.options : [];
37952
38003
  return __assign$1(__assign$1({}, state), { lastTimestamp: endDetail === null || endDetail === void 0 ? void 0 : endDetail.timestamp, streamingMessageId: undefined, chips: chips, chats: state.chats.map(function (chat, index) {
37953
38004
  if (index === streamingIndex_4 && isChatMsgDetail(chat)) {
37954
38005
  return __assign$1(__assign$1({}, chat), { msg: finalMsg_1, isStreaming: false, wasStreamed: true, toolCalls: toolCalls_1 });