impact-chatbot 2.3.84 → 2.3.86

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.cjs.js CHANGED
@@ -675,8 +675,11 @@ const handleMessageLike = async (question, liked, setLoadingState, displaySnackM
675
675
  try {
676
676
  let request;
677
677
  if (currentMode === "agent") {
678
+ // Use the session stored on this specific message. The live sessionId is
679
+ // reset once a flow completes / awaits follow-up, so relying on it would
680
+ // send an empty session_id or the session of a newer chat.
678
681
  const agentPayload = {
679
- session_id: sessionId,
682
+ session_id: activeMessage?.chatSessionId || activeMessage?.sessionId || sessionId,
680
683
  liked,
681
684
  };
682
685
  request = await likeDislikeCommentForAgent(agentPayload, baseUrl);
@@ -1439,31 +1442,50 @@ const ThinkinHeaderInfo = (props) => {
1439
1442
  });
1440
1443
  const { streamStartTime, isStreamCompleted, finalElapsedSeconds, thinkingHeaderMessage } = thinkingContext;
1441
1444
  const [elapsed, setElapsed] = React.useState(0);
1445
+ const [frozenText, setFrozenText] = React.useState(null);
1442
1446
  const intervalRef = React.useRef(null);
1443
1447
  // Once this instance sees "completed", freeze it permanently so a future
1444
1448
  // chat's streamStartTime doesn't cause it to start ticking again.
1445
1449
  const frozenRef = React.useRef(false);
1446
1450
  const frozenTextRef = React.useRef(null);
1447
- const streamStartTimeRef = React.useRef(null); // Track streamStartTime changes to unfreeze
1451
+ // The streamStartTime this instance latched onto. Used to detect when a
1452
+ // different stream has taken over (e.g. the user asked a new question while
1453
+ // this message's step form was still waiting for input).
1454
+ const ownStartTimeRef = React.useRef(null);
1455
+ /**
1456
+ * Freezes this instance permanently on a final label and elapsed value.
1457
+ */
1458
+ const freeze = (text, seconds) => {
1459
+ frozenRef.current = true;
1460
+ frozenTextRef.current = text;
1461
+ setFrozenText(text);
1462
+ setElapsed(seconds);
1463
+ };
1448
1464
  React.useEffect(() => {
1449
1465
  // If already frozen (stream completed), NEVER unfreeze.
1450
1466
  // This prevents a new chat's streamStartTime from restarting this timer.
1451
1467
  if (frozenRef.current)
1452
1468
  return;
1453
- // Track streamStartTime changes for non-frozen instances
1454
- if (streamStartTimeRef.current !== streamStartTime) {
1455
- streamStartTimeRef.current = streamStartTime;
1469
+ // Adopt the first non-null streamStartTime we see as this instance's own
1470
+ if (ownStartTimeRef.current === null && streamStartTime) {
1471
+ ownStartTimeRef.current = streamStartTime;
1456
1472
  }
1473
+ const ownStartTime = ownStartTimeRef.current;
1457
1474
  // Clear any existing interval
1458
1475
  if (intervalRef.current) {
1459
1476
  clearInterval(intervalRef.current);
1460
1477
  intervalRef.current = null;
1461
1478
  }
1462
1479
  if (isStreamCompleted && typeof finalElapsedSeconds === 'number') {
1463
- // Freeze this instance it will never tick again
1464
- frozenRef.current = true;
1465
- frozenTextRef.current = `Completed in ${formatElapsedTime(finalElapsedSeconds)}`;
1466
- setElapsed(finalElapsedSeconds);
1480
+ // Freeze this instance - it will never tick again
1481
+ freeze(`Completed in ${formatElapsedTime(finalElapsedSeconds)}`, finalElapsedSeconds);
1482
+ }
1483
+ else if (ownStartTime !== null && streamStartTime !== ownStartTime) {
1484
+ // A different stream has started (new question) while this one never
1485
+ // completed - typically an abandoned step form. Freeze on this
1486
+ // instance's OWN elapsed time instead of following the new stream.
1487
+ const ownElapsed = Math.floor((Date.now() - ownStartTime) / 1000);
1488
+ freeze(`Completed in ${formatElapsedTime(ownElapsed)}`, ownElapsed);
1467
1489
  }
1468
1490
  else if (streamStartTime && !isStreamCompleted) {
1469
1491
  // Live timer — update every second
@@ -1484,8 +1506,8 @@ const ThinkinHeaderInfo = (props) => {
1484
1506
  }, [streamStartTime, isStreamCompleted, finalElapsedSeconds]);
1485
1507
  // Determine display text
1486
1508
  let displayText = thinkingHeaderMessage || '';
1487
- if (frozenRef.current && frozenTextRef.current) {
1488
- displayText = frozenTextRef.current;
1509
+ if (frozenText || (frozenRef.current && frozenTextRef.current)) {
1510
+ displayText = frozenText || frozenTextRef.current;
1489
1511
  }
1490
1512
  else if (streamStartTime && !isStreamCompleted) {
1491
1513
  displayText = `Working for ${formatElapsedTime(elapsed)}`;
@@ -2479,6 +2501,20 @@ const useChatFlow = (chatDataRef, setLoader, setFlowType, setScreenName, setUser
2479
2501
  }
2480
2502
  if (data.flow_type === "agent" && data.actionType === "direct") {
2481
2503
  data.baseUrl = baseUrl;
2504
+ // Reset the timer before the request goes out (same as index.jsx
2505
+ // handleSendMessage). Without this the new ThinkinHeaderInfo mounts while
2506
+ // the previous response's "completed" state is still in Redux and freezes
2507
+ // itself on that stale elapsed time.
2508
+ // streamStartTime stays null here: processStream sets it, so a question
2509
+ // has exactly one start time (two would make the new header look like a
2510
+ // superseded one and freeze it at 0m:00s).
2511
+ dispatch(smartBotActions.setThinkingContext({
2512
+ thinkingContent: "",
2513
+ thinkingHeaderMessage: "Working for 0m:00s",
2514
+ streamStartTime: null,
2515
+ isStreamCompleted: false,
2516
+ finalElapsedSeconds: null,
2517
+ }));
2482
2518
  prepareDataAndSendToAgent(data, false, {
2483
2519
  chatbotContext: chatbotContext,
2484
2520
  setChatbotContext: smartBotActions.setChatbotContext,
@@ -5337,6 +5373,13 @@ const sseevent = (message, messageToStoreRef) => {
5337
5373
  ? parsedData.chat_id
5338
5374
  : "";
5339
5375
  messageToStoreRef.current.sessionId = parsedData?.session_id;
5376
+ // Sticky copy of the session id this message belongs to. sessionId gets
5377
+ // cleared on completed/follow-up so the next user message starts a fresh
5378
+ // session, but message level actions (like/dislike) still need to refer
5379
+ // to the session that actually produced this response.
5380
+ if (parsedData?.session_id) {
5381
+ messageToStoreRef.current.chatSessionId = parsedData.session_id;
5382
+ }
5340
5383
  }
5341
5384
  if (messageToStoreRef.current.currentMode === "navigation" &&
5342
5385
  parsedData?.session_id) {
@@ -8685,7 +8728,7 @@ const StreamedContent = ({ botData, botProps }) => {
8685
8728
  const retryTimerRef = React.useRef(null); // Interval ref for countdown
8686
8729
  const retryCountdownRef = React.useRef(0); // Non-state countdown for interval callback
8687
8730
  const thinkingContentRef = React.useRef("");
8688
- reactRedux.useSelector((state) => {
8731
+ const thinkingContext = reactRedux.useSelector((state) => {
8689
8732
  return state.smartBotReducer.thinkingContext;
8690
8733
  });
8691
8734
  const stepRef = React.useRef(_isAbortedRemount
@@ -8708,7 +8751,10 @@ const StreamedContent = ({ botData, botProps }) => {
8708
8751
  const thinkingStartTimeRef = React.useRef(null);
8709
8752
  const thinkingTimeFinalRef = React.useRef(0);
8710
8753
  const thinkingHeaderMessageRef = React.useRef("Working for 0m:00s");
8711
- const streamStartTimeRef = React.useRef(null);
8754
+ // Inherit the in-flight start time so a remount (e.g. tab switch, where
8755
+ // processStream is not re-run) keeps the original one instead of resetting.
8756
+ // Null for a fresh question - processStream sets it.
8757
+ const streamStartTimeRef = React.useRef(thinkingContext?.isStreamCompleted ? null : thinkingContext?.streamStartTime || null);
8712
8758
  const thinkingDoneRef = React.useRef(false);
8713
8759
  const thinkingStartedRef = React.useRef(false);
8714
8760
  const setThinkingContentRef = React.useRef(setThinkingContent); // Store current setThinkingContent function
@@ -9683,7 +9729,12 @@ const StreamedContent = ({ botData, botProps }) => {
9683
9729
  },
9684
9730
  };
9685
9731
  const hasStepFormWidgets = !isEmpty(stepFormDataMapRef.current);
9686
- processResponse(response, botData.inputBody, currentMode, botData.utilityObject.customChatConfig, {
9732
+ // response.session_id is intentionally blank once the flow completes, so
9733
+ // keep the retained session separately and stamp it on the stored message.
9734
+ const messageChatSessionId = messageToStoreRef.current.chatSessionId ||
9735
+ messageToStoreRef.current.sessionId ||
9736
+ "";
9737
+ Promise.resolve(processResponse(response, botData.inputBody, currentMode, botData.utilityObject.customChatConfig, {
9687
9738
  newChatData: chatDataInfoRef,
9688
9739
  isTabEnabled: true,
9689
9740
  steps: stepRef.current.map(s => ({ ...s })),
@@ -9691,7 +9742,15 @@ const StreamedContent = ({ botData, botProps }) => {
9691
9742
  questions: [...questionsRef.current],
9692
9743
  questionsStepsMap: { ...questionsStepsMapRef.current },
9693
9744
  stepFormDataMap: { ...stepFormDataMapRef.current },
9694
- }, activeConversationId);
9745
+ }, activeConversationId)).then(() => {
9746
+ if (!messageChatSessionId)
9747
+ return;
9748
+ const storedMessages = chatDataInfoRef.current?.[currentMode]?.conversations?.[activeConversationId]?.messages;
9749
+ if (storedMessages?.length) {
9750
+ storedMessages[storedMessages.length - 1].chatSessionId =
9751
+ messageChatSessionId;
9752
+ }
9753
+ });
9695
9754
  // [
9696
9755
  // {
9697
9756
  // header: "Finding relevant information",
@@ -15855,10 +15914,14 @@ const SmartBot = (props) => {
15855
15914
  answerMode: answerMode,
15856
15915
  };
15857
15916
  // if(!isEmpty(userInput)) {
15917
+ // Clear the previous response's completed state so the incoming
15918
+ // ThinkinHeaderInfo doesn't freeze on it. streamStartTime stays null
15919
+ // here: processStream sets it, so a question has exactly one start
15920
+ // time (two would make the new header look like a superseded one).
15858
15921
  dispatch(smartBotActions.setThinkingContext({
15859
15922
  thinkingContent: "",
15860
15923
  thinkingHeaderMessage: "Working for 0m:00s",
15861
- streamStartTime: Date.now(),
15924
+ streamStartTime: null,
15862
15925
  isStreamCompleted: false,
15863
15926
  finalElapsedSeconds: null,
15864
15927
  }));