@eshal-bot/chat-widget 0.1.98 → 0.1.100

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.
@@ -1 +1 @@
1
- export{S as default}from"./chat-widget-index-C4KMbduJ.esm.js";import"react";import"react-dom/client";import"react-dom";import"react/jsx-runtime";
1
+ export{S as default}from"./chat-widget-index-BOARafNH.esm.js";import"react";import"react-dom/client";import"react-dom";import"react/jsx-runtime";
@@ -7880,6 +7880,13 @@
7880
7880
  TEXT_CHUNK: 'text_chunk',
7881
7881
  PROCESSING_END: 'processing_end',
7882
7882
  COMPLETE: 'complete',
7883
+ // EV-2872: suggestion chips delivered AFTER `complete` on the same
7884
+ // stream (the backend no longer blocks the composer unlock on chip
7885
+ // generation). Carries a `prompts` array; attaches to the last
7886
+ // assistant message. Wire value matches @eshal/shared's
7887
+ // StreamEventType.FOLLOW_UP_PROMPTS (the frontend's ChatService.ts
7888
+ // already handled it).
7889
+ FOLLOW_UP_PROMPTS: 'follow_up_prompts',
7883
7890
  ERROR: 'error',
7884
7891
  HUMAN_DECISION_REQUIRED: 'human_decision_required'
7885
7892
  };
@@ -8322,6 +8329,26 @@
8322
8329
  updateFinalMessage(setMessages, finalContent, metadata);
8323
8330
  break;
8324
8331
  }
8332
+ case StreamEventType.FOLLOW_UP_PROMPTS:
8333
+ {
8334
+ // EV-2872: trailing chips chunk - arrives after COMPLETE. Attach to
8335
+ // the finalized assistant message (same shape updateFinalMessage
8336
+ // stores from the complete chunk).
8337
+ const latePrompts = Array.isArray(streamEvent.prompts) ? streamEvent.prompts : [];
8338
+ if (latePrompts.length > 0) {
8339
+ setMessages(prev => {
8340
+ const updated = [...(prev || [])];
8341
+ const lastMsg = updated[updated.length - 1];
8342
+ if (lastMsg && lastMsg.role === 'assistant') {
8343
+ updated[updated.length - 1] = _objectSpread2(_objectSpread2({}, lastMsg), {}, {
8344
+ prompts: normalizePrompts(latePrompts)
8345
+ });
8346
+ }
8347
+ return updated;
8348
+ });
8349
+ }
8350
+ break;
8351
+ }
8325
8352
  case StreamEventType.ERROR:
8326
8353
  addErrorMessage(setMessages, streamEvent.error || 'Unknown error occurred');
8327
8354
  break;
@@ -8464,6 +8491,32 @@
8464
8491
  continue;
8465
8492
  }
8466
8493
 
8494
+ // EV-2872: trailing chips chunk. The backend now emits `complete`
8495
+ // WITHOUT waiting for suggestion-chip generation (the composer
8496
+ // unlocks at last-token time) and delivers the chips afterwards
8497
+ // as their own {type:'follow_up_prompts'} chunk on the same
8498
+ // stream. Attach them to the already-finalized assistant
8499
+ // message. Turns where chip generation timed out simply never
8500
+ // send this chunk.
8501
+ if (parsed.type === 'follow_up_prompts') {
8502
+ const latePrompts = Array.isArray(parsed.prompts) ? parsed.prompts : [];
8503
+ if (latePrompts.length > 0) {
8504
+ setMessages(prev => {
8505
+ const updated = [...(prev || [])];
8506
+ const lastMsg = updated[updated.length - 1];
8507
+ if (lastMsg && lastMsg.role === 'assistant') {
8508
+ // Same storage shape updateFinalMessage uses on the
8509
+ // complete chunk: message.prompts, normalized.
8510
+ updated[updated.length - 1] = _objectSpread2(_objectSpread2({}, lastMsg), {}, {
8511
+ prompts: normalizePrompts(latePrompts)
8512
+ });
8513
+ }
8514
+ return updated;
8515
+ });
8516
+ }
8517
+ continue;
8518
+ }
8519
+
8467
8520
  // Handle text_chunk events
8468
8521
  if (parsed.type === 'text_chunk') {
8469
8522
  var _parsed$choices2;
@@ -17169,7 +17222,11 @@
17169
17222
  },
17170
17223
  children: suggestions.map((p, index) => /*#__PURE__*/jsxRuntimeExports.jsx("button", {
17171
17224
  type: "button",
17172
- onClick: () => onSuggestionClick(p.submitText || p.text || p),
17225
+ onClick: () =>
17226
+ // EV-2842: pass the prompt object + position so the click
17227
+ // handler can record telemetry; extra args are ignored by
17228
+ // handlers that only read the submit value.
17229
+ onSuggestionClick(p.submitText || p.text || p, p, index),
17173
17230
  dir: "auto",
17174
17231
  className: "inline-flex items-center rounded-full px-4 py-1.5 whitespace-nowrap flex-shrink-0 border-0 transition-transform duration-150 active:scale-95",
17175
17232
  style: {
@@ -18852,9 +18909,48 @@
18852
18909
  var _panelRef$current;
18853
18910
  (_panelRef$current = panelRef.current) === null || _panelRef$current === void 0 || _panelRef$current.focus();
18854
18911
  }, []);
18855
- const stableOnPromptSuggestionClick = reactExports.useCallback(text => {
18912
+
18913
+ // EV-2842 - chip click telemetry. Fire-and-forget POST to the
18914
+ // HMAC-verified support proxy (same auth pattern as the CSAT calls
18915
+ // below). A telemetry failure must never block or delay submitting
18916
+ // the chip's text, so this is never awaited and swallows everything.
18917
+ const reportChipClick = reactExports.useCallback(async (submitValue, prompt, index) => {
18918
+ try {
18919
+ if (!apiBaseUrl) return;
18920
+ const headers = {
18921
+ "Content-Type": "application/json"
18922
+ };
18923
+ if (typeof getAuthToken === "function") {
18924
+ const token = await getAuthToken();
18925
+ if (token) headers["Authorization"] = "Bearer ".concat(token);
18926
+ }
18927
+ await fetch("".concat(apiBaseUrl.replace(/\/$/, ""), "/api/v1/support/chip-click"), {
18928
+ method: "POST",
18929
+ headers,
18930
+ credentials: "include",
18931
+ keepalive: true,
18932
+ body: JSON.stringify({
18933
+ conversationId,
18934
+ chipText: prompt && prompt.text || String(submitValue !== null && submitValue !== void 0 ? submitValue : ""),
18935
+ submitText: String(submitValue !== null && submitValue !== void 0 ? submitValue : ""),
18936
+ position: Number.isInteger(index) ? index : undefined,
18937
+ source: "widget"
18938
+ })
18939
+ });
18940
+ } catch (_unused) {
18941
+ // Telemetry only - never surface to the user.
18942
+ }
18943
+ }, [apiBaseUrl, getAuthToken, conversationId]);
18944
+ const stableOnPromptSuggestionClick = reactExports.useCallback((text, prompt, index) => {
18945
+ // EV-2842: record the chip click (not awaited). Only fires when a
18946
+ // prompt OBJECT rode along - i.e. a real auto-prompt chip row -
18947
+ // so A2UI buttons / onboarding handlers that call this with just
18948
+ // a string are not miscounted as chip clicks.
18949
+ if (prompt && typeof prompt === "object") {
18950
+ reportChipClick(text, prompt, index);
18951
+ }
18856
18952
  if (onPromptSuggestionClick) onPromptSuggestionClick(text);else onDirectSend === null || onDirectSend === void 0 || onDirectSend(text);
18857
- }, [onPromptSuggestionClick, onDirectSend]);
18953
+ }, [onPromptSuggestionClick, onDirectSend, reportChipClick]);
18858
18954
 
18859
18955
  // ─── Resizable widget ────────────────────────────────────────────
18860
18956
  const anchorRight = launcherPosition.includes("right");
@@ -18866,7 +18962,7 @@
18866
18962
  if (!raw) return null;
18867
18963
  const parsed = JSON.parse(raw);
18868
18964
  if (typeof (parsed === null || parsed === void 0 ? void 0 : parsed.width) === "number" && typeof (parsed === null || parsed === void 0 ? void 0 : parsed.height) === "number" && parsed.width >= _WIDGET_MIN_W && parsed.height >= _WIDGET_MIN_H) return parsed;
18869
- } catch (_unused) {/* fall through */}
18965
+ } catch (_unused2) {/* fall through */}
18870
18966
  return null;
18871
18967
  });
18872
18968
  const resizeAxisRef = reactExports.useRef(null);
@@ -18914,7 +19010,7 @@
18914
19010
  if (!widgetSize) return;
18915
19011
  try {
18916
19012
  window.localStorage.setItem(_WIDGET_SIZE_KEY, JSON.stringify(widgetSize));
18917
- } catch (_unused2) {/* ignore */}
19013
+ } catch (_unused3) {/* ignore */}
18918
19014
  }, [widgetSize]);
18919
19015
  const handleResizeStart = (axis, e) => {
18920
19016
  e.preventDefault();
@@ -18942,7 +19038,7 @@
18942
19038
  try {
18943
19039
  var _getSession$csatSubmi, _getSession;
18944
19040
  return (_getSession$csatSubmi = (_getSession = getSession(organizationId)) === null || _getSession === void 0 ? void 0 : _getSession.csatSubmitted) !== null && _getSession$csatSubmi !== void 0 ? _getSession$csatSubmi : false;
18945
- } catch (_unused3) {
19041
+ } catch (_unused4) {
18946
19042
  return false;
18947
19043
  }
18948
19044
  });
@@ -18961,7 +19057,7 @@
18961
19057
  const submitted = (_getSession$csatSubmi2 = (_getSession2 = getSession(organizationId)) === null || _getSession2 === void 0 ? void 0 : _getSession2.csatSubmitted) !== null && _getSession$csatSubmi2 !== void 0 ? _getSession$csatSubmi2 : false;
18962
19058
  setCsatSubmitted(submitted);
18963
19059
  if (submitted) setShowCsat(false);
18964
- } catch (_unused4) {}
19060
+ } catch (_unused5) {}
18965
19061
  }, [conversationId]);
18966
19062
 
18967
19063
  // Auto-scroll hook