@memori.ai/memori-react 8.39.0 → 8.40.1

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 (49) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/components/MemoriWidget/MemoriWidget.d.ts +2 -1
  3. package/dist/components/MemoriWidget/MemoriWidget.js +450 -146
  4. package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
  5. package/dist/helpers/credits.d.ts +3 -2
  6. package/dist/helpers/credits.js +4 -3
  7. package/dist/helpers/credits.js.map +1 -1
  8. package/dist/helpers/nats/getNatsConfig.d.ts +5 -0
  9. package/dist/helpers/nats/getNatsConfig.js +29 -0
  10. package/dist/helpers/nats/getNatsConfig.js.map +1 -0
  11. package/dist/helpers/nats/useNats.d.ts +12 -0
  12. package/dist/helpers/nats/useNats.js +72 -0
  13. package/dist/helpers/nats/useNats.js.map +1 -0
  14. package/dist/helpers/nats/useNatsSession.d.ts +27 -0
  15. package/dist/helpers/nats/useNatsSession.js +108 -0
  16. package/dist/helpers/nats/useNatsSession.js.map +1 -0
  17. package/dist/index.js +1 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/version.d.ts +1 -1
  20. package/dist/version.js +1 -1
  21. package/esm/components/MemoriWidget/MemoriWidget.d.ts +2 -1
  22. package/esm/components/MemoriWidget/MemoriWidget.js +453 -147
  23. package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
  24. package/esm/helpers/credits.d.ts +3 -2
  25. package/esm/helpers/credits.js +4 -3
  26. package/esm/helpers/credits.js.map +1 -1
  27. package/esm/helpers/nats/getNatsConfig.d.ts +5 -0
  28. package/esm/helpers/nats/getNatsConfig.js +25 -0
  29. package/esm/helpers/nats/getNatsConfig.js.map +1 -0
  30. package/esm/helpers/nats/useNats.d.ts +12 -0
  31. package/esm/helpers/nats/useNats.js +68 -0
  32. package/esm/helpers/nats/useNats.js.map +1 -0
  33. package/esm/helpers/nats/useNatsSession.d.ts +27 -0
  34. package/esm/helpers/nats/useNatsSession.js +103 -0
  35. package/esm/helpers/nats/useNatsSession.js.map +1 -0
  36. package/esm/index.js +1 -1
  37. package/esm/index.js.map +1 -1
  38. package/esm/version.d.ts +1 -1
  39. package/esm/version.js +1 -1
  40. package/package.json +3 -2
  41. package/src/components/MemoriWidget/MemoriWidget.tsx +543 -149
  42. package/src/components/layouts/layouts.stories.tsx +29 -35
  43. package/src/helpers/credits.ts +6 -3
  44. package/src/helpers/nats/getNatsConfig.ts +69 -0
  45. package/src/helpers/nats/useNats.ts +122 -0
  46. package/src/helpers/nats/useNatsSession.ts +210 -0
  47. package/src/index.stories.tsx +19 -3
  48. package/src/index.tsx +1 -0
  49. package/src/version.ts +1 -1
@@ -41,6 +41,7 @@ const sanitizer_1 = require("../../helpers/sanitizer");
41
41
  const useTTS_1 = require("../../helpers/tts/useTTS");
42
42
  const ChatHistory_1 = tslib_1.__importDefault(require("../ChatHistoryDrawer/ChatHistory"));
43
43
  const useSTT_1 = require("../../helpers/stt/useSTT");
44
+ const useNats_1 = require("../../helpers/nats/useNats");
44
45
  const getMemoriState = (integrationId) => {
45
46
  var _a, _b, _c, _d, _f;
46
47
  let widget = integrationId
@@ -68,6 +69,11 @@ const NULL_PLACE_SPEC = {
68
69
  longitude: null,
69
70
  uncertaintyKm: null,
70
71
  };
72
+ const ENTER_TEXT_NATS_TIMEOUT_MS = 120000;
73
+ function readCorrelationID(response) {
74
+ const value = response.correlationID;
75
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
76
+ }
71
77
  const typeMessage = (message, waitForPrevious = true, hidden = false, typingText, useLoaderTextAsMsg = false, hasBatchQueued = false) => {
72
78
  const e = new CustomEvent('MemoriTextEntered', {
73
79
  detail: {
@@ -164,7 +170,7 @@ window.typeBatchMessages = typeBatchMessages;
164
170
  let audioContext;
165
171
  let memoriPassword;
166
172
  let userToken;
167
- const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang, uiLang, spokenLang, multilingual, integration, layout, customLayout, showShare, preview = false, embed = false, showCopyButton = true, showTranslationOriginal = false, showInputs = true, showDates = false, showContextPerLine = false, showMessageConsumption = false, showSettings, showTypingText = false, showClear = false, showLogin = false, showUpload, showOnlyLastMessages, showChatHistory, showReasoning, avatar3dHidden, height = '100vh', secret, baseUrl = 'https://aisuru-staging.aclambda.online', apiURL = 'https://backend-staging.memori.ai', engineURL = 'https://engine-staging.memori.ai', initialContextVars, initialQuestion, ttsProvider, ogImage, sessionID: initialSessionID, tenant, personification, authToken, enableAudio, defaultSpeakerActive = true, disableTextEnteredEvents = false, onStateChange, additionalInfo, additionalSettings, customMediaRenderer, userAvatar, __WEBCOMPONENT__ = false, useMathFormatting = false, autoStart = false, applyVarsToRoot = false, showFunctionCache = false, maxTotalMessagePayload, maxTextareaCharacters, }) => {
173
+ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenantID, memoriLang, uiLang, spokenLang, multilingual, integration, layout, customLayout, showShare, preview = false, embed = false, showCopyButton = true, showTranslationOriginal = false, showInputs = true, showDates = false, showContextPerLine = false, showMessageConsumption = false, showSettings, showTypingText = false, showClear = false, showLogin = false, showUpload, showOnlyLastMessages, showChatHistory, showReasoning, avatar3dHidden, height = '100vh', secret, baseUrl = 'https://aisuru-staging.aclambda.online', apiURL = 'https://backend-staging.memori.ai', engineURL = 'https://engine-staging.memori.ai', initialContextVars, initialQuestion, ttsProvider, ogImage, sessionID: initialSessionID, tenant, personification, authToken, enableAudio, defaultSpeakerActive = true, disableTextEnteredEvents = false, onStateChange, additionalInfo, additionalSettings, customMediaRenderer, userAvatar, __WEBCOMPONENT__ = false, useMathFormatting = false, autoStart = false, applyVarsToRoot = false, showFunctionCache = false, maxTotalMessagePayload, maxTextareaCharacters, }) => {
168
174
  var _a, _b, _c, _d, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18;
169
175
  const { t, i18n } = (0, react_i18next_1.useTranslation)();
170
176
  const [isClient, setIsClient] = (0, react_1.useState)(false);
@@ -172,7 +178,7 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
172
178
  setIsClient(true);
173
179
  }, []);
174
180
  const client = (0, memori_api_client_1.default)(apiURL, engineURL);
175
- const { initSession, deleteSession, postTextEnteredEvent, postPlaceChangedEvent, postDateChangedEvent, postTagChangedEvent, getSession, getExpertReferences, getSessionChatLogs, } = client;
181
+ const { initSession, deleteSession, postEnterTextAsync, postTextEnteredEvent, postPlaceChangedEvent, postDateChangedEvent, postTagChangedEvent, getSession, getExpertReferences, getSessionChatLogs, } = client;
176
182
  const [instruct, setInstruct] = (0, react_1.useState)(false);
177
183
  const [enableFocusChatInput, setEnableFocusChatInput] = (0, react_1.useState)(true);
178
184
  const [loginToken, setLoginToken] = (0, react_1.useState)((_a = additionalInfo === null || additionalInfo === void 0 ? void 0 : additionalInfo.loginToken) !== null && _a !== void 0 ? _a : authToken);
@@ -202,6 +208,7 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
202
208
  }, [loginToken, user === null || user === void 0 ? void 0 : user.userID]);
203
209
  const [showLoginDrawer, setShowLoginDrawer] = (0, react_1.useState)(false);
204
210
  const [clickedStart, setClickedStart] = (0, react_1.useState)(false);
211
+ const sessionStartingRef = (0, react_1.useRef)(false);
205
212
  const language = ((_d = (_c = (_b = memori.culture) === null || _b === void 0 ? void 0 : _b.split('-')) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.toUpperCase()) ||
206
213
  ((_j = (_h = (_g = (_f = memoriConfigs === null || memoriConfigs === void 0 ? void 0 : memoriConfigs.find(c => c.memoriConfigID === memori.memoriConfigurationID)) === null || _f === void 0 ? void 0 : _f.culture) === null || _g === void 0 ? void 0 : _g.split('-')) === null || _h === void 0 ? void 0 : _h[0]) === null || _j === void 0 ? void 0 : _j.toUpperCase());
207
214
  const integrationConfig = (integration === null || integration === void 0 ? void 0 : integration.customData)
@@ -237,6 +244,8 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
237
244
  const [loading, setLoading] = (0, react_1.useState)(false);
238
245
  const [memoriTyping, setMemoriTyping] = (0, react_1.useState)(false);
239
246
  const [typingText, setTypingText] = (0, react_1.useState)();
247
+ const pendingEnterTextRef = (0, react_1.useRef)(new Map());
248
+ const bufferedNatsResponsesRef = (0, react_1.useRef)(new Map());
240
249
  const layoutName = typeof layout === 'string'
241
250
  ? layout
242
251
  : typeof (integrationConfig === null || integrationConfig === void 0 ? void 0 : integrationConfig.layout) === 'string'
@@ -385,7 +394,7 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
385
394
  };
386
395
  const [chatLogID, setChatLogID] = (0, react_1.useState)(undefined);
387
396
  const sendMessage = async (text, media, newSessionId, translate = true, translatedText, hidden = false, typingText, useLoaderTextAsMsg = false, hasBatchQueued = false) => {
388
- var _a, _b, _c, _d, _f, _g;
397
+ var _a, _b, _c, _d;
389
398
  const sessionID = newSessionId ||
390
399
  sessionId ||
391
400
  ((_a = window.getMemoriState()) === null || _a === void 0 ? void 0 : _a.sessionID);
@@ -439,12 +448,16 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
439
448
  ? !!newSessionId && newSessionId !== sessionId
440
449
  : !!newSessionId,
441
450
  });
442
- setMemoriTyping(true);
443
- setTypingText(typingText);
444
451
  let gotError = false;
445
452
  try {
446
453
  const placeSpec = getPlaceSpecForEnterText(position);
447
- const { currentState, ...response } = await postTextEnteredEvent({
454
+ console.debug('[EnterText] sendMessage: posting', {
455
+ sessionId: sessionID,
456
+ textLength: msg.length,
457
+ hasBatchQueued,
458
+ typingText,
459
+ });
460
+ const response = await postEnterTextAsync({
448
461
  sessionId: sessionID,
449
462
  text: msg,
450
463
  ...(memori.needsDateTime && {
@@ -452,57 +465,35 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
452
465
  }),
453
466
  ...(placeSpec !== undefined && { place: placeSpec }),
454
467
  });
455
- if (response.resultCode === 0 && currentState) {
456
- setChatLogID(undefined);
457
- const emission = useLoaderTextAsMsg && typingText
458
- ? typingText
459
- : (_c = currentState.emission) !== null && _c !== void 0 ? _c : currentDialogState === null || currentDialogState === void 0 ? void 0 : currentDialogState.emission;
460
- if (userLang.toLowerCase() !== language.toLowerCase() &&
461
- emission &&
462
- isMultilanguageEnabled) {
463
- currentState.emission = emission;
464
- translateDialogState(currentState, userLang, msg).then(ts => {
465
- let text = ts.translatedEmission || ts.emission;
466
- if (text && shouldPlayAudio(text)) {
467
- handleSpeak(text);
468
- }
469
- });
470
- }
471
- else {
472
- setCurrentDialogState({
473
- ...currentState,
474
- emission,
475
- });
476
- if (emission) {
477
- pushMessage({
478
- text: emission,
479
- emitter: currentState.emitter,
480
- media: (_d = currentState.emittedMedia) !== null && _d !== void 0 ? _d : currentState.media,
481
- llmUsage: currentState.llmUsage,
482
- fromUser: false,
483
- questionAnswered: msg,
484
- generatedByAI: !!currentState.completion,
485
- contextVars: currentState.contextVars,
486
- date: currentState.currentDate,
487
- placeName: currentState.currentPlaceName,
488
- placeLatitude: currentState.currentLatitude,
489
- placeLongitude: currentState.currentLongitude,
490
- placeUncertaintyKm: currentState.currentUncertaintyKm,
491
- tag: currentState.currentTag,
492
- memoryTags: currentState.memoryTags,
493
- });
494
- if (emission && shouldPlayAudio(emission)) {
495
- handleSpeak(emission);
496
- }
497
- }
498
- }
468
+ console.debug('[EnterText] sendMessage: HTTP response', {
469
+ resultCode: response.resultCode,
470
+ correlationID: readCorrelationID(response),
471
+ resultMessage: response.resultMessage,
472
+ });
473
+ const correlationID = readCorrelationID(response);
474
+ if (response.resultCode === 0 && correlationID) {
475
+ registerPendingEnterText(correlationID, {
476
+ msg,
477
+ typingText,
478
+ useLoaderTextAsMsg,
479
+ hasBatchQueued,
480
+ });
481
+ console.info('[EnterText] sendMessage: accepted, showing typing indicator', {
482
+ correlationID: correlationID,
483
+ typingText,
484
+ });
485
+ setMemoriTyping(true);
486
+ setTypingText(typingText);
487
+ }
488
+ else if (response.resultCode === 0) {
489
+ console.error('[EnterText] sendMessage: HTTP 200 but missing correlationID — cannot match NATS response', response);
499
490
  }
500
491
  else if (response.resultCode === 404) {
501
492
  setHistory(h => [...h.slice(0, h.length - 1)]);
502
493
  reopenSession(true, memoriPwd || memori.secretToken, memoriTokens, undefined, undefined, {
503
494
  LANG: userLang,
504
495
  PATHNAME: window.location.pathname,
505
- ROUTE: ((_g = (_f = window.location.pathname) === null || _f === void 0 ? void 0 : _f.split('/')) === null || _g === void 0 ? void 0 : _g.pop()) || '',
496
+ ROUTE: ((_d = (_c = window.location.pathname) === null || _c === void 0 ? void 0 : _c.split('/')) === null || _d === void 0 ? void 0 : _d.pop()) || '',
506
497
  ...(initialContextVars || {}),
507
498
  }, initialQuestion, undefined, undefined, undefined, undefined, true).then(state => {
508
499
  console.info('session timeout');
@@ -532,16 +523,11 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
532
523
  }
533
524
  }
534
525
  catch (error) {
535
- console.log('error', error);
536
- console.error(error);
526
+ console.error('[EnterText] sendMessage: request failed', error);
537
527
  gotError = true;
538
528
  setTypingText(undefined);
539
529
  setMemoriTyping(false);
540
530
  }
541
- if (!hasBatchQueued) {
542
- setTypingText(undefined);
543
- setMemoriTyping(false);
544
- }
545
531
  };
546
532
  const translateDialogState = async (state, userLang, msg, avoidPushingMessage = false) => {
547
533
  var _a, _b, _c, _d, _f, _g;
@@ -763,7 +749,7 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
763
749
  setAuthModalState('password');
764
750
  return;
765
751
  }
766
- if (!(await checkCredits({ notify: true, goBack: true }))) {
752
+ if (!(await checkCredits({ notify: true }))) {
767
753
  return;
768
754
  }
769
755
  setLoading(true);
@@ -846,7 +832,7 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
846
832
  setAuthModalState('password');
847
833
  return;
848
834
  }
849
- if (!(await checkCredits({ notify: true, goBack: true }))) {
835
+ if (!(await checkCredits({ notify: true }))) {
850
836
  setLoading(false);
851
837
  return null;
852
838
  }
@@ -1061,6 +1047,7 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
1061
1047
  return () => {
1062
1048
  setHasUserActivatedSpeak(false);
1063
1049
  setClickedStart(false);
1050
+ sessionStartingRef.current = false;
1064
1051
  clearInteractionTimeout();
1065
1052
  timeoutRef.current = undefined;
1066
1053
  };
@@ -1159,6 +1146,277 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
1159
1146
  setHasUserActivatedSpeak,
1160
1147
  speakerMuted,
1161
1148
  ]);
1149
+ const processEnterTextDialogResponse = (0, react_1.useCallback)((event, pending) => {
1150
+ var _a, _b;
1151
+ console.debug('[EnterText] processDialogResponse', {
1152
+ correlationID: event.correlationID,
1153
+ resultCode: event.resultCode,
1154
+ hasCurrentState: !!event.currentState,
1155
+ hasBatchQueued: pending.hasBatchQueued,
1156
+ });
1157
+ const { msg, typingText: pendingTypingText, useLoaderTextAsMsg, } = pending;
1158
+ const currentState = event.currentState;
1159
+ if (event.resultCode !== 0 || !currentState) {
1160
+ if (event.resultCode === 500 && event.resultMessage) {
1161
+ console.warn('[EnterText] processDialogResponse: server error', {
1162
+ correlationID: event.correlationID,
1163
+ resultMessage: event.resultMessage,
1164
+ });
1165
+ setHistory(h => [
1166
+ ...h,
1167
+ {
1168
+ text: 'Error: ' + event.resultMessage,
1169
+ emitter: 'system',
1170
+ fromUser: false,
1171
+ initial: false,
1172
+ contextVars: {},
1173
+ date: new Date().toISOString(),
1174
+ },
1175
+ ]);
1176
+ }
1177
+ else if (event.resultCode !== 0) {
1178
+ console.warn('[SEND_MESSAGE/NATS]', event);
1179
+ }
1180
+ return;
1181
+ }
1182
+ if (!msg) {
1183
+ console.debug('[EnterText] processDialogResponse: no msg in pending, skipping');
1184
+ return;
1185
+ }
1186
+ setChatLogID(undefined);
1187
+ const emission = useLoaderTextAsMsg && pendingTypingText
1188
+ ? pendingTypingText
1189
+ : (_a = currentState.emission) !== null && _a !== void 0 ? _a : currentDialogState === null || currentDialogState === void 0 ? void 0 : currentDialogState.emission;
1190
+ console.debug('[EnterText] processDialogResponse: rendering emission', {
1191
+ correlationID: event.correlationID,
1192
+ emissionPreview: emission === null || emission === void 0 ? void 0 : emission.slice(0, 80),
1193
+ state: currentState.state,
1194
+ });
1195
+ if (userLang.toLowerCase() !== language.toLowerCase() &&
1196
+ emission &&
1197
+ isMultilanguageEnabled) {
1198
+ currentState.emission = emission;
1199
+ translateDialogState(currentState, userLang, msg).then(ts => {
1200
+ const text = ts.translatedEmission || ts.emission;
1201
+ if (text && shouldPlayAudio(text)) {
1202
+ handleSpeak(text);
1203
+ }
1204
+ });
1205
+ }
1206
+ else {
1207
+ setCurrentDialogState({
1208
+ ...currentState,
1209
+ emission,
1210
+ });
1211
+ if (emission) {
1212
+ pushMessage({
1213
+ text: emission,
1214
+ emitter: currentState.emitter,
1215
+ media: (_b = currentState.emittedMedia) !== null && _b !== void 0 ? _b : currentState.media,
1216
+ llmUsage: currentState.llmUsage,
1217
+ fromUser: false,
1218
+ questionAnswered: msg,
1219
+ generatedByAI: !!currentState.completion,
1220
+ contextVars: currentState.contextVars,
1221
+ date: currentState.currentDate,
1222
+ placeName: currentState.currentPlaceName,
1223
+ placeLatitude: currentState.currentLatitude,
1224
+ placeLongitude: currentState.currentLongitude,
1225
+ placeUncertaintyKm: currentState.currentUncertaintyKm,
1226
+ tag: currentState.currentTag,
1227
+ memoryTags: currentState.memoryTags,
1228
+ });
1229
+ if (emission && shouldPlayAudio(emission)) {
1230
+ handleSpeak(emission);
1231
+ }
1232
+ }
1233
+ }
1234
+ }, [
1235
+ userLang,
1236
+ language,
1237
+ isMultilanguageEnabled,
1238
+ currentDialogState === null || currentDialogState === void 0 ? void 0 : currentDialogState.emission,
1239
+ translateDialogState,
1240
+ handleSpeak,
1241
+ shouldPlayAudio,
1242
+ ]);
1243
+ const clearEnterTextPending = (0, react_1.useCallback)((correlationID, pending) => {
1244
+ var _a;
1245
+ if (pending.natsTimeoutId) {
1246
+ clearTimeout(pending.natsTimeoutId);
1247
+ }
1248
+ if ((_a = pending.waitForResponse) === null || _a === void 0 ? void 0 : _a.timeoutId) {
1249
+ clearTimeout(pending.waitForResponse.timeoutId);
1250
+ }
1251
+ pendingEnterTextRef.current.delete(correlationID);
1252
+ }, []);
1253
+ const deliverEnterTextNatsError = (0, react_1.useCallback)((event) => {
1254
+ var _a, _b, _c;
1255
+ const correlationID = event.correlationID;
1256
+ const errorText = event.errorMessage
1257
+ ? `Error: ${event.errorMessage}`
1258
+ : event.errorCode
1259
+ ? `Error: ${event.errorCode}`
1260
+ : 'Error: An unexpected error occurred';
1261
+ console.error('[EnterText] NATS error event', {
1262
+ correlationID,
1263
+ errorCode: event.errorCode,
1264
+ errorMessage: event.errorMessage,
1265
+ });
1266
+ pushMessage({
1267
+ text: errorText,
1268
+ emitter: 'system',
1269
+ fromUser: false,
1270
+ initial: false,
1271
+ contextVars: {},
1272
+ date: new Date().toISOString(),
1273
+ });
1274
+ if (correlationID) {
1275
+ const pending = pendingEnterTextRef.current.get(correlationID);
1276
+ if (pending) {
1277
+ clearEnterTextPending(correlationID, pending);
1278
+ (_a = pending.waitForResponse) === null || _a === void 0 ? void 0 : _a.reject(new Error((_b = event.errorMessage) !== null && _b !== void 0 ? _b : String((_c = event.errorCode) !== null && _c !== void 0 ? _c : 'NATS error')));
1279
+ }
1280
+ }
1281
+ setMemoriTyping(false);
1282
+ setTypingText(undefined);
1283
+ }, [clearEnterTextPending]);
1284
+ const deliverEnterTextNatsResponse = (0, react_1.useCallback)((correlationID, event) => {
1285
+ const pending = pendingEnterTextRef.current.get(correlationID);
1286
+ if (!pending) {
1287
+ const pendingCorrelationIDs = [...pendingEnterTextRef.current.keys()];
1288
+ console.warn('[EnterText] NATS response buffered (no matching pending)', {
1289
+ receivedCorrelationID: correlationID,
1290
+ resultCode: event.resultCode,
1291
+ pendingCorrelationIDs,
1292
+ hint: pendingCorrelationIDs.length > 0
1293
+ ? 'Use one of pendingCorrelationIDs in your nats pub correlation_id'
1294
+ : 'Send a message in the widget first, then copy correlationID from HTTP response logs',
1295
+ });
1296
+ bufferedNatsResponsesRef.current.set(correlationID, event);
1297
+ return;
1298
+ }
1299
+ clearEnterTextPending(correlationID, pending);
1300
+ if (pending.waitForResponse) {
1301
+ console.info('[EnterText] NATS response delivered to waiter', {
1302
+ correlationID,
1303
+ resultCode: event.resultCode,
1304
+ });
1305
+ pending.waitForResponse.resolve(event);
1306
+ setMemoriTyping(false);
1307
+ setTypingText(undefined);
1308
+ return;
1309
+ }
1310
+ processEnterTextDialogResponse(event, pending);
1311
+ if (!pending.hasBatchQueued) {
1312
+ console.info('[EnterText] typing indicator cleared', { correlationID });
1313
+ setMemoriTyping(false);
1314
+ setTypingText(undefined);
1315
+ }
1316
+ else {
1317
+ console.debug('[EnterText] typing kept (batch queued)', {
1318
+ correlationID,
1319
+ });
1320
+ }
1321
+ }, [processEnterTextDialogResponse, clearEnterTextPending]);
1322
+ const registerPendingEnterText = (0, react_1.useCallback)((correlationID, pending) => {
1323
+ const buffered = bufferedNatsResponsesRef.current.get(correlationID);
1324
+ if (buffered) {
1325
+ console.info('[EnterText] replaying buffered NATS response', {
1326
+ correlationID,
1327
+ waitForResponse: !!pending.waitForResponse,
1328
+ });
1329
+ bufferedNatsResponsesRef.current.delete(correlationID);
1330
+ pendingEnterTextRef.current.set(correlationID, pending);
1331
+ deliverEnterTextNatsResponse(correlationID, buffered);
1332
+ return;
1333
+ }
1334
+ if (!pending.waitForResponse && !pending.natsTimeoutId) {
1335
+ pending.natsTimeoutId = setTimeout(() => {
1336
+ var _a;
1337
+ const current = pendingEnterTextRef.current.get(correlationID);
1338
+ if (!current)
1339
+ return;
1340
+ clearEnterTextPending(correlationID, current);
1341
+ console.error('[EnterText] NATS response timeout', {
1342
+ correlationID,
1343
+ timeoutMs: ENTER_TEXT_NATS_TIMEOUT_MS,
1344
+ });
1345
+ if (!current.hasBatchQueued) {
1346
+ setMemoriTyping(false);
1347
+ setTypingText(undefined);
1348
+ }
1349
+ (_a = current.waitForResponse) === null || _a === void 0 ? void 0 : _a.reject(new Error('NATS enter-text response timeout'));
1350
+ }, ENTER_TEXT_NATS_TIMEOUT_MS);
1351
+ }
1352
+ console.debug('[EnterText] pending registered', {
1353
+ correlationID,
1354
+ waitForResponse: !!pending.waitForResponse,
1355
+ hasBatchQueued: pending.hasBatchQueued,
1356
+ });
1357
+ pendingEnterTextRef.current.set(correlationID, pending);
1358
+ }, [deliverEnterTextNatsResponse, clearEnterTextPending]);
1359
+ const waitForEnterTextNatsResponse = (0, react_1.useCallback)((correlationID, timeoutMs = 120000) => new Promise((resolve, reject) => {
1360
+ console.debug('[EnterText] waiting for NATS response', {
1361
+ correlationID,
1362
+ timeoutMs,
1363
+ });
1364
+ const timeoutId = setTimeout(() => {
1365
+ const current = pendingEnterTextRef.current.get(correlationID);
1366
+ if (current) {
1367
+ clearEnterTextPending(correlationID, current);
1368
+ }
1369
+ console.error('[EnterText] NATS response timeout', {
1370
+ correlationID,
1371
+ timeoutMs,
1372
+ });
1373
+ reject(new Error('NATS enter-text response timeout'));
1374
+ }, timeoutMs);
1375
+ registerPendingEnterText(correlationID, {
1376
+ waitForResponse: {
1377
+ resolve: event => {
1378
+ clearTimeout(timeoutId);
1379
+ resolve(event);
1380
+ },
1381
+ reject: error => {
1382
+ clearTimeout(timeoutId);
1383
+ reject(error);
1384
+ },
1385
+ timeoutId,
1386
+ },
1387
+ });
1388
+ }), [registerPendingEnterText, clearEnterTextPending]);
1389
+ (0, useNats_1.useNats)({
1390
+ baseUrl,
1391
+ sessionId,
1392
+ onProgress: (0, react_1.useCallback)((event) => {
1393
+ console.debug('[EnterText] NATS progress', {
1394
+ correlationID: event.correlationID,
1395
+ step: event.currentStep,
1396
+ finalStep: event.finalStep,
1397
+ message: event.message,
1398
+ });
1399
+ if (event.message) {
1400
+ setTypingText(event.message);
1401
+ }
1402
+ }, []),
1403
+ onDialogResponse: (0, react_1.useCallback)((event) => {
1404
+ const correlationID = event.correlationID;
1405
+ console.debug('[EnterText] NATS dialog.text_entered_response received', {
1406
+ correlationID,
1407
+ resultCode: event.resultCode,
1408
+ requestID: event.requestID,
1409
+ });
1410
+ if (!correlationID) {
1411
+ console.warn('[EnterText] dialog_text_entered_response without correlationID', event);
1412
+ setMemoriTyping(false);
1413
+ setTypingText(undefined);
1414
+ return;
1415
+ }
1416
+ deliverEnterTextNatsResponse(correlationID, event);
1417
+ }, [deliverEnterTextNatsResponse]),
1418
+ onError: deliverEnterTextNatsError,
1419
+ });
1162
1420
  const focusChatInput = () => {
1163
1421
  let textarea = document.querySelector('#chat-fieldset textarea');
1164
1422
  if (textarea && enableFocusChatInput) {
@@ -1353,7 +1611,7 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
1353
1611
  };
1354
1612
  }, [sessionId, userLang, disableTextEnteredEvents]);
1355
1613
  const onClickStart = (0, react_1.useCallback)(async (session, initialSessionExpired = false, chatLog, targetSessionID) => {
1356
- var _a, _b, _c, _d, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
1614
+ var _a, _b, _c, _d, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
1357
1615
  const sessionID = chatLog ? undefined : (session === null || session === void 0 ? void 0 : session.sessionID) || sessionId;
1358
1616
  const dialogState = chatLog
1359
1617
  ? undefined
@@ -1370,7 +1628,7 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
1370
1628
  setShowPositionDrawer(true);
1371
1629
  return;
1372
1630
  }
1373
- if (!(await checkCredits({ notify: true, goBack: true }))) {
1631
+ if (!(await checkCredits({ notify: true }))) {
1374
1632
  setClickedStart(false);
1375
1633
  setLoading(false);
1376
1634
  return;
@@ -1378,6 +1636,7 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
1378
1636
  if (!sessionID && !!minAge && !birth) {
1379
1637
  setShowAgeVerification(true);
1380
1638
  setClickedStart(false);
1639
+ return;
1381
1640
  }
1382
1641
  else if (!sessionID &&
1383
1642
  memori.privacyType !== 'PUBLIC' &&
@@ -1389,82 +1648,95 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
1389
1648
  return;
1390
1649
  }
1391
1650
  else if (!sessionID || initialSessionExpired) {
1392
- setClickedStart(false);
1393
- const session = await fetchSession({
1394
- memoriID: memori.engineMemoriID,
1395
- password: secret || memoriPwd || memori.secretToken,
1396
- tag: personification === null || personification === void 0 ? void 0 : personification.tag,
1397
- pin: personification === null || personification === void 0 ? void 0 : personification.pin,
1398
- continueFromChatLogID: chatLog === null || chatLog === void 0 ? void 0 : chatLog.chatLogID,
1399
- initialContextVars: {
1400
- LANG: userLang,
1401
- PATHNAME: (_a = window.location.pathname) === null || _a === void 0 ? void 0 : _a.toUpperCase(),
1402
- ROUTE: ((_d = (_c = (_b = window.location.pathname) === null || _b === void 0 ? void 0 : _b.split('/')) === null || _c === void 0 ? void 0 : _c.pop()) === null || _d === void 0 ? void 0 : _d.toUpperCase()) || '',
1403
- ...((!chatLog
1404
- ? initialContextVars
1405
- : chatLog.lines[chatLog.lines.length - 1].contextVars) || {}),
1406
- },
1407
- initialQuestion: chatLog ? undefined : initialQuestion,
1408
- birthDate: birth,
1409
- additionalInfo: {
1410
- ...(additionalInfo || {}),
1411
- loginToken: (_g = (_f = userToken !== null && userToken !== void 0 ? userToken : loginToken) !== null && _f !== void 0 ? _f : additionalInfo === null || additionalInfo === void 0 ? void 0 : additionalInfo.loginToken) !== null && _g !== void 0 ? _g : authToken,
1412
- language: ((_k = userLang !== null && userLang !== void 0 ? userLang : (_j = (_h = memori.culture) === null || _h === void 0 ? void 0 : _h.split('-')) === null || _j === void 0 ? void 0 : _j[0]) !== null && _k !== void 0 ? _k : 'IT').toLowerCase(),
1413
- timeZoneOffset: new Date().getTimezoneOffset().toString(),
1414
- },
1415
- });
1416
- if (session === null || session === void 0 ? void 0 : session.dialogState) {
1417
- if (!chatLog) {
1418
- setHistory([]);
1419
- await translateAndSpeak(session.dialogState, userLang);
1420
- setHasUserActivatedSpeak(true);
1421
- }
1422
- else {
1423
- const messages = chatLog.lines.map((l, i) => {
1424
- var _a, _b;
1425
- return ({
1426
- text: l.text,
1427
- media: (_b = (_a = l.media) === null || _a === void 0 ? void 0 : _a.filter(m => constants_1.allowedMediaTypes.includes(m.mimeType))) === null || _b === void 0 ? void 0 : _b.map(m => ({
1428
- mediumID: `${i}-${m.mimeType}`,
1429
- ...m,
1430
- })),
1431
- fromUser: l.inbound,
1432
- llmUsage: l.llmUsage,
1433
- timestamp: l.timestamp,
1434
- emitter: l.emitter,
1435
- initial: i === 0,
1436
- });
1437
- });
1438
- translatedMessages = messages !== null && messages !== void 0 ? messages : [];
1439
- if (language.toUpperCase() !== userLang.toUpperCase() &&
1440
- isMultilanguageEnabled) {
1441
- try {
1442
- translatedMessages = await Promise.all(messages.map(async (m) => {
1443
- if ('originalText' in m && m.originalText) {
1444
- return m;
1445
- }
1446
- return {
1651
+ if (sessionStartingRef.current) {
1652
+ return;
1653
+ }
1654
+ sessionStartingRef.current = true;
1655
+ try {
1656
+ const session = await fetchSession({
1657
+ memoriID: memori.engineMemoriID,
1658
+ password: secret || memoriPwd || memori.secretToken,
1659
+ tag: personification === null || personification === void 0 ? void 0 : personification.tag,
1660
+ pin: personification === null || personification === void 0 ? void 0 : personification.pin,
1661
+ continueFromChatLogID: chatLog === null || chatLog === void 0 ? void 0 : chatLog.chatLogID,
1662
+ initialContextVars: {
1663
+ LANG: userLang,
1664
+ PATHNAME: (_a = window.location.pathname) === null || _a === void 0 ? void 0 : _a.toUpperCase(),
1665
+ ROUTE: ((_d = (_c = (_b = window.location.pathname) === null || _b === void 0 ? void 0 : _b.split('/')) === null || _c === void 0 ? void 0 : _c.pop()) === null || _d === void 0 ? void 0 : _d.toUpperCase()) ||
1666
+ '',
1667
+ ...((!chatLog
1668
+ ? initialContextVars
1669
+ : chatLog.lines[chatLog.lines.length - 1].contextVars) || {}),
1670
+ },
1671
+ initialQuestion: chatLog ? undefined : initialQuestion,
1672
+ birthDate: birth,
1673
+ additionalInfo: {
1674
+ ...(additionalInfo || {}),
1675
+ loginToken: (_g = (_f = userToken !== null && userToken !== void 0 ? userToken : loginToken) !== null && _f !== void 0 ? _f : additionalInfo === null || additionalInfo === void 0 ? void 0 : additionalInfo.loginToken) !== null && _g !== void 0 ? _g : authToken,
1676
+ language: ((_k = userLang !== null && userLang !== void 0 ? userLang : (_j = (_h = memori.culture) === null || _h === void 0 ? void 0 : _h.split('-')) === null || _j === void 0 ? void 0 : _j[0]) !== null && _k !== void 0 ? _k : 'IT').toLowerCase(),
1677
+ timeZoneOffset: new Date().getTimezoneOffset().toString(),
1678
+ },
1679
+ });
1680
+ if (session === null || session === void 0 ? void 0 : session.dialogState) {
1681
+ if (!chatLog) {
1682
+ setHistory([]);
1683
+ await translateAndSpeak(session.dialogState, userLang);
1684
+ setHasUserActivatedSpeak(true);
1685
+ setClickedStart(false);
1686
+ }
1687
+ else {
1688
+ const messages = chatLog.lines.map((l, i) => {
1689
+ var _a, _b;
1690
+ return ({
1691
+ text: l.text,
1692
+ media: (_b = (_a = l.media) === null || _a === void 0 ? void 0 : _a.filter(m => constants_1.allowedMediaTypes.includes(m.mimeType))) === null || _b === void 0 ? void 0 : _b.map(m => ({
1693
+ mediumID: `${i}-${m.mimeType}`,
1447
1694
  ...m,
1448
- originalText: m.text,
1449
- text: (await (0, translations_1.getTranslation)(m.text, userLang, language, baseUrl)).text,
1450
- };
1451
- }));
1452
- }
1453
- catch (e) {
1454
- console.error('[onClickStart] Error translating messages:', e);
1695
+ })),
1696
+ fromUser: l.inbound,
1697
+ llmUsage: l.llmUsage,
1698
+ timestamp: l.timestamp,
1699
+ emitter: l.emitter,
1700
+ initial: i === 0,
1701
+ });
1702
+ });
1703
+ translatedMessages = messages !== null && messages !== void 0 ? messages : [];
1704
+ if (language.toUpperCase() !== userLang.toUpperCase() &&
1705
+ isMultilanguageEnabled) {
1706
+ try {
1707
+ translatedMessages = await Promise.all(messages.map(async (m) => {
1708
+ if ('originalText' in m && m.originalText) {
1709
+ return m;
1710
+ }
1711
+ return {
1712
+ ...m,
1713
+ originalText: m.text,
1714
+ text: (await (0, translations_1.getTranslation)(m.text, userLang, language, baseUrl)).text,
1715
+ };
1716
+ }));
1717
+ }
1718
+ catch (e) {
1719
+ console.error('[onClickStart] Error translating messages:', e);
1720
+ }
1455
1721
  }
1722
+ setHistory(translatedMessages);
1723
+ translateDialogState(session.dialogState, userLang, undefined, true).finally(() => {
1724
+ setHasUserActivatedSpeak(true);
1725
+ setClickedStart(false);
1726
+ });
1456
1727
  }
1457
- setHistory(translatedMessages);
1458
- translateDialogState(session.dialogState, userLang, undefined, true).finally(() => {
1459
- setHasUserActivatedSpeak(true);
1460
- });
1728
+ }
1729
+ else if ((session === null || session === void 0 ? void 0 : session.resultCode) === 0) {
1730
+ sessionStartingRef.current = false;
1731
+ await onClickStart(session || undefined);
1732
+ }
1733
+ else {
1734
+ setLoading(false);
1735
+ setClickedStart(false);
1461
1736
  }
1462
1737
  }
1463
- else if ((session === null || session === void 0 ? void 0 : session.resultCode) === 0) {
1464
- await onClickStart(session || undefined);
1465
- }
1466
- else {
1467
- setLoading(false);
1738
+ finally {
1739
+ sessionStartingRef.current = false;
1468
1740
  }
1469
1741
  return;
1470
1742
  }
@@ -1501,6 +1773,7 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
1501
1773
  ...(initialContextVars || {}),
1502
1774
  }, initialQuestion, birth).then(() => {
1503
1775
  setHasUserActivatedSpeak(true);
1776
+ setClickedStart(false);
1504
1777
  });
1505
1778
  }
1506
1779
  }
@@ -1576,9 +1849,11 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
1576
1849
  console.log('[onClickStart] Starting with initial question');
1577
1850
  translatedMessages = [];
1578
1851
  setHistory([]);
1579
- setMemoriTyping(true);
1580
1852
  const placeSpec = getPlaceSpecForEnterText(position);
1581
- const response = await postTextEnteredEvent({
1853
+ console.debug('[EnterText] onClickStart: posting initial question', {
1854
+ sessionId: sessionID,
1855
+ });
1856
+ const response = await postEnterTextAsync({
1582
1857
  sessionId: sessionID,
1583
1858
  text: initialQuestion,
1584
1859
  ...(memori.needsDateTime && {
@@ -1586,6 +1861,10 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
1586
1861
  }),
1587
1862
  ...(placeSpec !== undefined && { place: placeSpec }),
1588
1863
  });
1864
+ console.debug('[EnterText] onClickStart: HTTP response', {
1865
+ resultCode: response.resultCode,
1866
+ correlationID: readCorrelationID(response),
1867
+ });
1589
1868
  if (response.resultCode === 500 && response.resultMessage) {
1590
1869
  setHistory(h => [
1591
1870
  ...h,
@@ -1598,10 +1877,33 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
1598
1877
  date: new Date().toISOString(),
1599
1878
  },
1600
1879
  ]);
1601
- setMemoriTyping(false);
1602
1880
  return;
1603
1881
  }
1604
- await translateAndSpeak((_w = response.currentState) !== null && _w !== void 0 ? _w : currentState, userLang, undefined, false);
1882
+ const onClickStartCorrelationID = readCorrelationID(response);
1883
+ if (response.resultCode === 0 && onClickStartCorrelationID) {
1884
+ console.info('[EnterText] onClickStart: accepted, showing typing indicator', {
1885
+ correlationID: onClickStartCorrelationID,
1886
+ });
1887
+ setMemoriTyping(true);
1888
+ try {
1889
+ const natsEvent = await waitForEnterTextNatsResponse(onClickStartCorrelationID);
1890
+ console.info('[EnterText] onClickStart: NATS response received', {
1891
+ correlationID: onClickStartCorrelationID,
1892
+ resultCode: natsEvent.resultCode,
1893
+ });
1894
+ if (natsEvent.resultCode === 0 && natsEvent.currentState) {
1895
+ await translateAndSpeak(natsEvent.currentState, userLang, undefined, false);
1896
+ }
1897
+ }
1898
+ catch (e) {
1899
+ console.error('[EnterText] onClickStart: NATS wait failed', e);
1900
+ setMemoriTyping(false);
1901
+ setTypingText(undefined);
1902
+ }
1903
+ }
1904
+ else if (response.resultCode === 0) {
1905
+ console.error('[EnterText] onClickStart: HTTP 200 but missing correlationID', response);
1906
+ }
1605
1907
  }
1606
1908
  }
1607
1909
  }
@@ -1611,10 +1913,13 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
1611
1913
  }
1612
1914
  }, [memoriPwd, memori, memoriTokens, birthDate, sessionId, userLang, position]);
1613
1915
  (0, react_1.useEffect)(() => {
1614
- if (!clickedStart && autoStart && selectedLayout !== 'HIDDEN_CHAT') {
1916
+ if (!clickedStart &&
1917
+ !sessionStartingRef.current &&
1918
+ autoStart &&
1919
+ selectedLayout !== 'HIDDEN_CHAT') {
1615
1920
  onClickStart();
1616
1921
  }
1617
- }, [clickedStart, autoStart, selectedLayout]);
1922
+ }, [clickedStart, autoStart, selectedLayout, sessionId]);
1618
1923
  (0, react_1.useEffect)(() => {
1619
1924
  const targetNode = document.querySelector(`memori-client[memoriname="${memori.name}"]`) ||
1620
1925
  document.querySelector(`memori-client[memoriid="${memori.memoriID}"]`) ||
@@ -1672,21 +1977,18 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
1672
1977
  (user === null || user === void 0 ? void 0 : user.pAndCUAccepted);
1673
1978
  const needsCredits = tenant === null || tenant === void 0 ? void 0 : tenant.billingDelegation;
1674
1979
  const [hasEnoughCredits, setHasEnoughCredits] = (0, react_1.useState)(true);
1675
- const handleNotEnoughCredits = (0, react_1.useCallback)((goBack = false) => {
1980
+ const handleNotEnoughCredits = (0, react_1.useCallback)(() => {
1676
1981
  setHasEnoughCredits(false);
1677
1982
  setAuthModalState(null);
1678
1983
  react_hot_toast_1.default.error(t('notEnoughCredits'));
1679
- if (goBack && window.history.length > 1) {
1680
- window.history.back();
1681
- }
1682
1984
  }, [t]);
1683
1985
  const checkCredits = (0, react_1.useCallback)(async (options) => {
1684
1986
  if (!(tenant === null || tenant === void 0 ? void 0 : tenant.billingDelegation))
1685
1987
  return true;
1686
- if (!ownerUserID) {
1687
- console.warn('Cannot verify credits: missing ownerUserID');
1988
+ if (!ownerUserID && !ownerUserName) {
1989
+ console.warn('Cannot verify credits: missing owner identifier');
1688
1990
  if (options === null || options === void 0 ? void 0 : options.notify) {
1689
- handleNotEnoughCredits(!!options.goBack);
1991
+ handleNotEnoughCredits();
1690
1992
  }
1691
1993
  else {
1692
1994
  setHasEnoughCredits(false);
@@ -1700,6 +2002,7 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
1700
2002
  : 'session_creation',
1701
2003
  baseUrl: baseUrl,
1702
2004
  userID: ownerUserID,
2005
+ userName: ownerUserName,
1703
2006
  tenant: tenantID,
1704
2007
  });
1705
2008
  if (resp.enough) {
@@ -1709,7 +2012,7 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
1709
2012
  else {
1710
2013
  console.warn('Not enough credits. Required:', resp.required);
1711
2014
  if (options === null || options === void 0 ? void 0 : options.notify) {
1712
- handleNotEnoughCredits(!!options.goBack);
2015
+ handleNotEnoughCredits();
1713
2016
  }
1714
2017
  else {
1715
2018
  setHasEnoughCredits(false);
@@ -1727,6 +2030,7 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, tenantID, memoriLang
1727
2030
  deepThoughtEnabled,
1728
2031
  handleNotEnoughCredits,
1729
2032
  ownerUserID,
2033
+ ownerUserName,
1730
2034
  tenant === null || tenant === void 0 ? void 0 : tenant.billingDelegation,
1731
2035
  tenantID,
1732
2036
  ]);