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