@memori.ai/memori-react 8.38.8 → 8.40.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.
Files changed (45) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/components/MemoriWidget/MemoriWidget.js +401 -87
  3. package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
  4. package/dist/helpers/credits.d.ts +5 -2
  5. package/dist/helpers/credits.js +5 -1
  6. package/dist/helpers/credits.js.map +1 -1
  7. package/dist/helpers/nats/getNatsConfig.d.ts +5 -0
  8. package/dist/helpers/nats/getNatsConfig.js +29 -0
  9. package/dist/helpers/nats/getNatsConfig.js.map +1 -0
  10. package/dist/helpers/nats/useNats.d.ts +12 -0
  11. package/dist/helpers/nats/useNats.js +72 -0
  12. package/dist/helpers/nats/useNats.js.map +1 -0
  13. package/dist/helpers/nats/useNatsSession.d.ts +27 -0
  14. package/dist/helpers/nats/useNatsSession.js +108 -0
  15. package/dist/helpers/nats/useNatsSession.js.map +1 -0
  16. package/dist/version.d.ts +1 -1
  17. package/dist/version.js +1 -1
  18. package/esm/components/MemoriWidget/MemoriWidget.js +401 -87
  19. package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
  20. package/esm/helpers/credits.d.ts +5 -2
  21. package/esm/helpers/credits.js +5 -1
  22. package/esm/helpers/credits.js.map +1 -1
  23. package/esm/helpers/nats/getNatsConfig.d.ts +5 -0
  24. package/esm/helpers/nats/getNatsConfig.js +25 -0
  25. package/esm/helpers/nats/getNatsConfig.js.map +1 -0
  26. package/esm/helpers/nats/useNats.d.ts +12 -0
  27. package/esm/helpers/nats/useNats.js +68 -0
  28. package/esm/helpers/nats/useNats.js.map +1 -0
  29. package/esm/helpers/nats/useNatsSession.d.ts +27 -0
  30. package/esm/helpers/nats/useNatsSession.js +103 -0
  31. package/esm/helpers/nats/useNatsSession.js.map +1 -0
  32. package/esm/version.d.ts +1 -1
  33. package/esm/version.js +1 -1
  34. package/package.json +3 -2
  35. package/src/components/MemoriWidget/MemoriWidget.tsx +546 -140
  36. package/src/components/StartPanel/StartPanel.stories.tsx +21 -0
  37. package/src/components/StartPanel/StartPanel.test.tsx +66 -1
  38. package/src/components/StartPanel/__snapshots__/StartPanel.test.tsx.snap +156 -0
  39. package/src/components/layouts/layouts.stories.tsx +28 -34
  40. package/src/helpers/credits.ts +16 -1
  41. package/src/helpers/nats/getNatsConfig.ts +69 -0
  42. package/src/helpers/nats/useNats.ts +122 -0
  43. package/src/helpers/nats/useNatsSession.ts +210 -0
  44. package/src/index.stories.tsx +19 -3
  45. package/src/version.ts +1 -1
@@ -38,6 +38,7 @@ import { sanitizeText } from '../../helpers/sanitizer';
38
38
  import { useTTS } from '../../helpers/tts/useTTS';
39
39
  import ChatHistoryDrawer from '../ChatHistoryDrawer/ChatHistory';
40
40
  import { useSTT } from '../../helpers/stt/useSTT';
41
+ import { useNats } from '../../helpers/nats/useNats';
41
42
  const getMemoriState = (integrationId) => {
42
43
  var _a, _b, _c, _d, _f;
43
44
  let widget = integrationId
@@ -65,6 +66,11 @@ const NULL_PLACE_SPEC = {
65
66
  longitude: null,
66
67
  uncertaintyKm: null,
67
68
  };
69
+ const ENTER_TEXT_NATS_TIMEOUT_MS = 120000;
70
+ function readCorrelationID(response) {
71
+ const value = response.correlationID;
72
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
73
+ }
68
74
  const typeMessage = (message, waitForPrevious = true, hidden = false, typingText, useLoaderTextAsMsg = false, hasBatchQueued = false) => {
69
75
  const e = new CustomEvent('MemoriTextEntered', {
70
76
  detail: {
@@ -169,7 +175,7 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
169
175
  setIsClient(true);
170
176
  }, []);
171
177
  const client = memoriApiClient(apiURL, engineURL);
172
- const { initSession, deleteSession, postTextEnteredEvent, postPlaceChangedEvent, postDateChangedEvent, postTagChangedEvent, getSession, getExpertReferences, getSessionChatLogs, } = client;
178
+ const { initSession, deleteSession, postEnterTextAsync, postTextEnteredEvent, postPlaceChangedEvent, postDateChangedEvent, postTagChangedEvent, getSession, getExpertReferences, getSessionChatLogs, } = client;
173
179
  const [instruct, setInstruct] = useState(false);
174
180
  const [enableFocusChatInput, setEnableFocusChatInput] = useState(true);
175
181
  const [loginToken, setLoginToken] = useState((_a = additionalInfo === null || additionalInfo === void 0 ? void 0 : additionalInfo.loginToken) !== null && _a !== void 0 ? _a : authToken);
@@ -199,7 +205,6 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
199
205
  }, [loginToken, user === null || user === void 0 ? void 0 : user.userID]);
200
206
  const [showLoginDrawer, setShowLoginDrawer] = useState(false);
201
207
  const [clickedStart, setClickedStart] = useState(false);
202
- const [gotErrorInOpening, setGotErrorInOpening] = useState(false);
203
208
  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()) ||
204
209
  ((_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());
205
210
  const integrationConfig = (integration === null || integration === void 0 ? void 0 : integration.customData)
@@ -235,6 +240,8 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
235
240
  const [loading, setLoading] = useState(false);
236
241
  const [memoriTyping, setMemoriTyping] = useState(false);
237
242
  const [typingText, setTypingText] = useState();
243
+ const pendingEnterTextRef = useRef(new Map());
244
+ const bufferedNatsResponsesRef = useRef(new Map());
238
245
  const layoutName = typeof layout === 'string'
239
246
  ? layout
240
247
  : typeof (integrationConfig === null || integrationConfig === void 0 ? void 0 : integrationConfig.layout) === 'string'
@@ -321,14 +328,12 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
321
328
  if (!venue)
322
329
  return undefined;
323
330
  const place = {};
324
- if (venue.latitude != null &&
325
- venue.longitude != null) {
331
+ if (venue.latitude != null && venue.longitude != null) {
326
332
  place.latitude = venue.latitude;
327
333
  place.longitude = venue.longitude;
328
334
  if (venue.placeName)
329
335
  place.placeName = venue.placeName;
330
- if (venue.uncertainty != null &&
331
- venue.uncertainty > 0)
336
+ if (venue.uncertainty != null && venue.uncertainty > 0)
332
337
  place.uncertaintyKm = venue.uncertainty;
333
338
  }
334
339
  else if (venue.placeName) {
@@ -385,7 +390,7 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
385
390
  };
386
391
  const [chatLogID, setChatLogID] = useState(undefined);
387
392
  const sendMessage = async (text, media, newSessionId, translate = true, translatedText, hidden = false, typingText, useLoaderTextAsMsg = false, hasBatchQueued = false) => {
388
- var _a, _b, _c, _d, _f, _g;
393
+ var _a, _b, _c, _d;
389
394
  const sessionID = newSessionId ||
390
395
  sessionId ||
391
396
  ((_a = window.getMemoriState()) === null || _a === void 0 ? void 0 : _a.sessionID);
@@ -439,12 +444,16 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
439
444
  ? !!newSessionId && newSessionId !== sessionId
440
445
  : !!newSessionId,
441
446
  });
442
- setMemoriTyping(true);
443
- setTypingText(typingText);
444
447
  let gotError = false;
445
448
  try {
446
449
  const placeSpec = getPlaceSpecForEnterText(position);
447
- const { currentState, ...response } = await postTextEnteredEvent({
450
+ console.debug('[EnterText] sendMessage: posting', {
451
+ sessionId: sessionID,
452
+ textLength: msg.length,
453
+ hasBatchQueued,
454
+ typingText,
455
+ });
456
+ const response = await postEnterTextAsync({
448
457
  sessionId: sessionID,
449
458
  text: msg,
450
459
  ...(memori.needsDateTime && {
@@ -452,57 +461,35 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
452
461
  }),
453
462
  ...(placeSpec !== undefined && { place: placeSpec }),
454
463
  });
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
- }
464
+ console.debug('[EnterText] sendMessage: HTTP response', {
465
+ resultCode: response.resultCode,
466
+ correlationID: readCorrelationID(response),
467
+ resultMessage: response.resultMessage,
468
+ });
469
+ const correlationID = readCorrelationID(response);
470
+ if (response.resultCode === 0 && correlationID) {
471
+ registerPendingEnterText(correlationID, {
472
+ msg,
473
+ typingText,
474
+ useLoaderTextAsMsg,
475
+ hasBatchQueued,
476
+ });
477
+ console.info('[EnterText] sendMessage: accepted, showing typing indicator', {
478
+ correlationID: correlationID,
479
+ typingText,
480
+ });
481
+ setMemoriTyping(true);
482
+ setTypingText(typingText);
483
+ }
484
+ else if (response.resultCode === 0) {
485
+ console.error('[EnterText] sendMessage: HTTP 200 but missing correlationID — cannot match NATS response', response);
499
486
  }
500
487
  else if (response.resultCode === 404) {
501
488
  setHistory(h => [...h.slice(0, h.length - 1)]);
502
489
  reopenSession(true, memoriPwd || memori.secretToken, memoriTokens, undefined, undefined, {
503
490
  LANG: userLang,
504
491
  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()) || '',
492
+ ROUTE: ((_d = (_c = window.location.pathname) === null || _c === void 0 ? void 0 : _c.split('/')) === null || _d === void 0 ? void 0 : _d.pop()) || '',
506
493
  ...(initialContextVars || {}),
507
494
  }, initialQuestion, undefined, undefined, undefined, undefined, true).then(state => {
508
495
  console.info('session timeout');
@@ -532,16 +519,11 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
532
519
  }
533
520
  }
534
521
  catch (error) {
535
- console.log('error', error);
536
- console.error(error);
522
+ console.error('[EnterText] sendMessage: request failed', error);
537
523
  gotError = true;
538
524
  setTypingText(undefined);
539
525
  setMemoriTyping(false);
540
526
  }
541
- if (!hasBatchQueued) {
542
- setTypingText(undefined);
543
- setMemoriTyping(false);
544
- }
545
527
  };
546
528
  const translateDialogState = async (state, userLang, msg, avoidPushingMessage = false) => {
547
529
  var _a, _b, _c, _d, _f, _g;
@@ -763,6 +745,9 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
763
745
  setAuthModalState('password');
764
746
  return;
765
747
  }
748
+ if (!(await checkCredits({ notify: true }))) {
749
+ return;
750
+ }
766
751
  setLoading(true);
767
752
  try {
768
753
  let referral;
@@ -806,9 +791,8 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
806
791
  else if (session === null || session === void 0 ? void 0 : session.resultMessage.startsWith('This Memori is aged restricted')) {
807
792
  console.warn(session);
808
793
  toast.error(t('underageTwinSession', { age: minAge }));
809
- setGotErrorInOpening(true);
810
794
  }
811
- else if ((session === null || session === void 0 ? void 0 : session.resultCode) === 403) {
795
+ else if ((session === null || session === void 0 ? void 0 : session.resultCode) === 403 && memori.privacyType !== 'PUBLIC') {
812
796
  setMemoriPwd(undefined);
813
797
  setAuthModalState('password');
814
798
  return session;
@@ -818,7 +802,6 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
818
802
  toast.error(tst => (_jsxs("div", { children: [_jsx("p", { children: t(getErrori18nKey(session === null || session === void 0 ? void 0 : session.resultCode)) }), _jsx(Button, { outlined: true, padded: false, onClick: () => toast.dismiss(tst.id), icon: _jsx(CloseIcon, {}), children: t('close') })] })), {
819
803
  duration: Infinity,
820
804
  });
821
- setGotErrorInOpening(true);
822
805
  return session;
823
806
  }
824
807
  }
@@ -845,6 +828,10 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
845
828
  setAuthModalState('password');
846
829
  return;
847
830
  }
831
+ if (!(await checkCredits({ notify: true }))) {
832
+ setLoading(false);
833
+ return null;
834
+ }
848
835
  let referral;
849
836
  try {
850
837
  referral = (() => {
@@ -938,9 +925,9 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
938
925
  else if (response === null || response === void 0 ? void 0 : response.resultMessage.startsWith('This Memori is aged restricted')) {
939
926
  console.error('[REOPEN_SESSION] Age restriction error:', response);
940
927
  toast.error(t('underageTwinSession', { age: minAge }));
941
- setGotErrorInOpening(true);
942
928
  }
943
- else if ((response === null || response === void 0 ? void 0 : response.resultCode) === 403) {
929
+ else if ((response === null || response === void 0 ? void 0 : response.resultCode) === 403 &&
930
+ memori.privacyType !== 'PUBLIC') {
944
931
  console.error('[REOPEN_SESSION] Authentication error');
945
932
  setMemoriPwd(undefined);
946
933
  setAuthModalState('password');
@@ -948,7 +935,6 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
948
935
  else {
949
936
  console.error('[REOPEN_SESSION] Other error:', response);
950
937
  toast.error(t(getErrori18nKey(response.resultCode)));
951
- setGotErrorInOpening(true);
952
938
  }
953
939
  }
954
940
  catch (err) {
@@ -1155,6 +1141,277 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
1155
1141
  setHasUserActivatedSpeak,
1156
1142
  speakerMuted,
1157
1143
  ]);
1144
+ const processEnterTextDialogResponse = useCallback((event, pending) => {
1145
+ var _a, _b;
1146
+ console.debug('[EnterText] processDialogResponse', {
1147
+ correlationID: event.correlationID,
1148
+ resultCode: event.resultCode,
1149
+ hasCurrentState: !!event.currentState,
1150
+ hasBatchQueued: pending.hasBatchQueued,
1151
+ });
1152
+ const { msg, typingText: pendingTypingText, useLoaderTextAsMsg, } = pending;
1153
+ const currentState = event.currentState;
1154
+ if (event.resultCode !== 0 || !currentState) {
1155
+ if (event.resultCode === 500 && event.resultMessage) {
1156
+ console.warn('[EnterText] processDialogResponse: server error', {
1157
+ correlationID: event.correlationID,
1158
+ resultMessage: event.resultMessage,
1159
+ });
1160
+ setHistory(h => [
1161
+ ...h,
1162
+ {
1163
+ text: 'Error: ' + event.resultMessage,
1164
+ emitter: 'system',
1165
+ fromUser: false,
1166
+ initial: false,
1167
+ contextVars: {},
1168
+ date: new Date().toISOString(),
1169
+ },
1170
+ ]);
1171
+ }
1172
+ else if (event.resultCode !== 0) {
1173
+ console.warn('[SEND_MESSAGE/NATS]', event);
1174
+ }
1175
+ return;
1176
+ }
1177
+ if (!msg) {
1178
+ console.debug('[EnterText] processDialogResponse: no msg in pending, skipping');
1179
+ return;
1180
+ }
1181
+ setChatLogID(undefined);
1182
+ const emission = useLoaderTextAsMsg && pendingTypingText
1183
+ ? pendingTypingText
1184
+ : (_a = currentState.emission) !== null && _a !== void 0 ? _a : currentDialogState === null || currentDialogState === void 0 ? void 0 : currentDialogState.emission;
1185
+ console.debug('[EnterText] processDialogResponse: rendering emission', {
1186
+ correlationID: event.correlationID,
1187
+ emissionPreview: emission === null || emission === void 0 ? void 0 : emission.slice(0, 80),
1188
+ state: currentState.state,
1189
+ });
1190
+ if (userLang.toLowerCase() !== language.toLowerCase() &&
1191
+ emission &&
1192
+ isMultilanguageEnabled) {
1193
+ currentState.emission = emission;
1194
+ translateDialogState(currentState, userLang, msg).then(ts => {
1195
+ const text = ts.translatedEmission || ts.emission;
1196
+ if (text && shouldPlayAudio(text)) {
1197
+ handleSpeak(text);
1198
+ }
1199
+ });
1200
+ }
1201
+ else {
1202
+ setCurrentDialogState({
1203
+ ...currentState,
1204
+ emission,
1205
+ });
1206
+ if (emission) {
1207
+ pushMessage({
1208
+ text: emission,
1209
+ emitter: currentState.emitter,
1210
+ media: (_b = currentState.emittedMedia) !== null && _b !== void 0 ? _b : currentState.media,
1211
+ llmUsage: currentState.llmUsage,
1212
+ fromUser: false,
1213
+ questionAnswered: msg,
1214
+ generatedByAI: !!currentState.completion,
1215
+ contextVars: currentState.contextVars,
1216
+ date: currentState.currentDate,
1217
+ placeName: currentState.currentPlaceName,
1218
+ placeLatitude: currentState.currentLatitude,
1219
+ placeLongitude: currentState.currentLongitude,
1220
+ placeUncertaintyKm: currentState.currentUncertaintyKm,
1221
+ tag: currentState.currentTag,
1222
+ memoryTags: currentState.memoryTags,
1223
+ });
1224
+ if (emission && shouldPlayAudio(emission)) {
1225
+ handleSpeak(emission);
1226
+ }
1227
+ }
1228
+ }
1229
+ }, [
1230
+ userLang,
1231
+ language,
1232
+ isMultilanguageEnabled,
1233
+ currentDialogState === null || currentDialogState === void 0 ? void 0 : currentDialogState.emission,
1234
+ translateDialogState,
1235
+ handleSpeak,
1236
+ shouldPlayAudio,
1237
+ ]);
1238
+ const clearEnterTextPending = useCallback((correlationID, pending) => {
1239
+ var _a;
1240
+ if (pending.natsTimeoutId) {
1241
+ clearTimeout(pending.natsTimeoutId);
1242
+ }
1243
+ if ((_a = pending.waitForResponse) === null || _a === void 0 ? void 0 : _a.timeoutId) {
1244
+ clearTimeout(pending.waitForResponse.timeoutId);
1245
+ }
1246
+ pendingEnterTextRef.current.delete(correlationID);
1247
+ }, []);
1248
+ const deliverEnterTextNatsError = useCallback((event) => {
1249
+ var _a, _b, _c;
1250
+ const correlationID = event.correlationID;
1251
+ const errorText = event.errorMessage
1252
+ ? `Error: ${event.errorMessage}`
1253
+ : event.errorCode
1254
+ ? `Error: ${event.errorCode}`
1255
+ : 'Error: An unexpected error occurred';
1256
+ console.error('[EnterText] NATS error event', {
1257
+ correlationID,
1258
+ errorCode: event.errorCode,
1259
+ errorMessage: event.errorMessage,
1260
+ });
1261
+ pushMessage({
1262
+ text: errorText,
1263
+ emitter: 'system',
1264
+ fromUser: false,
1265
+ initial: false,
1266
+ contextVars: {},
1267
+ date: new Date().toISOString(),
1268
+ });
1269
+ if (correlationID) {
1270
+ const pending = pendingEnterTextRef.current.get(correlationID);
1271
+ if (pending) {
1272
+ clearEnterTextPending(correlationID, pending);
1273
+ (_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')));
1274
+ }
1275
+ }
1276
+ setMemoriTyping(false);
1277
+ setTypingText(undefined);
1278
+ }, [clearEnterTextPending]);
1279
+ const deliverEnterTextNatsResponse = useCallback((correlationID, event) => {
1280
+ const pending = pendingEnterTextRef.current.get(correlationID);
1281
+ if (!pending) {
1282
+ const pendingCorrelationIDs = [...pendingEnterTextRef.current.keys()];
1283
+ console.warn('[EnterText] NATS response buffered (no matching pending)', {
1284
+ receivedCorrelationID: correlationID,
1285
+ resultCode: event.resultCode,
1286
+ pendingCorrelationIDs,
1287
+ hint: pendingCorrelationIDs.length > 0
1288
+ ? 'Use one of pendingCorrelationIDs in your nats pub correlation_id'
1289
+ : 'Send a message in the widget first, then copy correlationID from HTTP response logs',
1290
+ });
1291
+ bufferedNatsResponsesRef.current.set(correlationID, event);
1292
+ return;
1293
+ }
1294
+ clearEnterTextPending(correlationID, pending);
1295
+ if (pending.waitForResponse) {
1296
+ console.info('[EnterText] NATS response delivered to waiter', {
1297
+ correlationID,
1298
+ resultCode: event.resultCode,
1299
+ });
1300
+ pending.waitForResponse.resolve(event);
1301
+ setMemoriTyping(false);
1302
+ setTypingText(undefined);
1303
+ return;
1304
+ }
1305
+ processEnterTextDialogResponse(event, pending);
1306
+ if (!pending.hasBatchQueued) {
1307
+ console.info('[EnterText] typing indicator cleared', { correlationID });
1308
+ setMemoriTyping(false);
1309
+ setTypingText(undefined);
1310
+ }
1311
+ else {
1312
+ console.debug('[EnterText] typing kept (batch queued)', {
1313
+ correlationID,
1314
+ });
1315
+ }
1316
+ }, [processEnterTextDialogResponse, clearEnterTextPending]);
1317
+ const registerPendingEnterText = useCallback((correlationID, pending) => {
1318
+ const buffered = bufferedNatsResponsesRef.current.get(correlationID);
1319
+ if (buffered) {
1320
+ console.info('[EnterText] replaying buffered NATS response', {
1321
+ correlationID,
1322
+ waitForResponse: !!pending.waitForResponse,
1323
+ });
1324
+ bufferedNatsResponsesRef.current.delete(correlationID);
1325
+ pendingEnterTextRef.current.set(correlationID, pending);
1326
+ deliverEnterTextNatsResponse(correlationID, buffered);
1327
+ return;
1328
+ }
1329
+ if (!pending.waitForResponse && !pending.natsTimeoutId) {
1330
+ pending.natsTimeoutId = setTimeout(() => {
1331
+ var _a;
1332
+ const current = pendingEnterTextRef.current.get(correlationID);
1333
+ if (!current)
1334
+ return;
1335
+ clearEnterTextPending(correlationID, current);
1336
+ console.error('[EnterText] NATS response timeout', {
1337
+ correlationID,
1338
+ timeoutMs: ENTER_TEXT_NATS_TIMEOUT_MS,
1339
+ });
1340
+ if (!current.hasBatchQueued) {
1341
+ setMemoriTyping(false);
1342
+ setTypingText(undefined);
1343
+ }
1344
+ (_a = current.waitForResponse) === null || _a === void 0 ? void 0 : _a.reject(new Error('NATS enter-text response timeout'));
1345
+ }, ENTER_TEXT_NATS_TIMEOUT_MS);
1346
+ }
1347
+ console.debug('[EnterText] pending registered', {
1348
+ correlationID,
1349
+ waitForResponse: !!pending.waitForResponse,
1350
+ hasBatchQueued: pending.hasBatchQueued,
1351
+ });
1352
+ pendingEnterTextRef.current.set(correlationID, pending);
1353
+ }, [deliverEnterTextNatsResponse, clearEnterTextPending]);
1354
+ const waitForEnterTextNatsResponse = useCallback((correlationID, timeoutMs = 120000) => new Promise((resolve, reject) => {
1355
+ console.debug('[EnterText] waiting for NATS response', {
1356
+ correlationID,
1357
+ timeoutMs,
1358
+ });
1359
+ const timeoutId = setTimeout(() => {
1360
+ const current = pendingEnterTextRef.current.get(correlationID);
1361
+ if (current) {
1362
+ clearEnterTextPending(correlationID, current);
1363
+ }
1364
+ console.error('[EnterText] NATS response timeout', {
1365
+ correlationID,
1366
+ timeoutMs,
1367
+ });
1368
+ reject(new Error('NATS enter-text response timeout'));
1369
+ }, timeoutMs);
1370
+ registerPendingEnterText(correlationID, {
1371
+ waitForResponse: {
1372
+ resolve: event => {
1373
+ clearTimeout(timeoutId);
1374
+ resolve(event);
1375
+ },
1376
+ reject: error => {
1377
+ clearTimeout(timeoutId);
1378
+ reject(error);
1379
+ },
1380
+ timeoutId,
1381
+ },
1382
+ });
1383
+ }), [registerPendingEnterText, clearEnterTextPending]);
1384
+ useNats({
1385
+ baseUrl,
1386
+ sessionId,
1387
+ onProgress: useCallback((event) => {
1388
+ console.debug('[EnterText] NATS progress', {
1389
+ correlationID: event.correlationID,
1390
+ step: event.currentStep,
1391
+ finalStep: event.finalStep,
1392
+ message: event.message,
1393
+ });
1394
+ if (event.message) {
1395
+ setTypingText(event.message);
1396
+ }
1397
+ }, []),
1398
+ onDialogResponse: useCallback((event) => {
1399
+ const correlationID = event.correlationID;
1400
+ console.debug('[EnterText] NATS dialog.text_entered_response received', {
1401
+ correlationID,
1402
+ resultCode: event.resultCode,
1403
+ requestID: event.requestID,
1404
+ });
1405
+ if (!correlationID) {
1406
+ console.warn('[EnterText] dialog_text_entered_response without correlationID', event);
1407
+ setMemoriTyping(false);
1408
+ setTypingText(undefined);
1409
+ return;
1410
+ }
1411
+ deliverEnterTextNatsResponse(correlationID, event);
1412
+ }, [deliverEnterTextNatsResponse]),
1413
+ onError: deliverEnterTextNatsError,
1414
+ });
1158
1415
  const focusChatInput = () => {
1159
1416
  let textarea = document.querySelector('#chat-fieldset textarea');
1160
1417
  if (textarea && enableFocusChatInput) {
@@ -1349,7 +1606,7 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
1349
1606
  };
1350
1607
  }, [sessionId, userLang, disableTextEnteredEvents]);
1351
1608
  const onClickStart = useCallback(async (session, initialSessionExpired = false, chatLog, targetSessionID) => {
1352
- var _a, _b, _c, _d, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
1609
+ var _a, _b, _c, _d, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
1353
1610
  const sessionID = chatLog ? undefined : (session === null || session === void 0 ? void 0 : session.sessionID) || sessionId;
1354
1611
  const dialogState = chatLog
1355
1612
  ? undefined
@@ -1366,23 +1623,26 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
1366
1623
  setShowPositionDrawer(true);
1367
1624
  return;
1368
1625
  }
1626
+ if (!(await checkCredits({ notify: true }))) {
1627
+ setClickedStart(false);
1628
+ setLoading(false);
1629
+ return;
1630
+ }
1369
1631
  if (!sessionID && !!minAge && !birth) {
1370
1632
  setShowAgeVerification(true);
1371
1633
  setClickedStart(false);
1372
1634
  }
1373
- else if ((!sessionID &&
1635
+ else if (!sessionID &&
1374
1636
  memori.privacyType !== 'PUBLIC' &&
1375
1637
  !memori.secretToken &&
1376
1638
  !memoriPwd &&
1377
- !memoriTokens) ||
1378
- (!sessionID && gotErrorInOpening)) {
1639
+ !memoriTokens) {
1379
1640
  setAuthModalState('password');
1380
1641
  setClickedStart(false);
1381
1642
  return;
1382
1643
  }
1383
1644
  else if (!sessionID || initialSessionExpired) {
1384
1645
  setClickedStart(false);
1385
- setGotErrorInOpening(false);
1386
1646
  const session = await fetchSession({
1387
1647
  memoriID: memori.engineMemoriID,
1388
1648
  password: secret || memoriPwd || memori.secretToken,
@@ -1466,7 +1726,6 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
1466
1726
  const { currentState, ...response } = await getSession(sessionID);
1467
1727
  if (response.resultCode !== 0 || !currentState) {
1468
1728
  const { chatLogs } = await getSessionChatLogs(sessionID, sessionID);
1469
- setGotErrorInOpening(true);
1470
1729
  setSessionId(undefined);
1471
1730
  setClickedStart(false);
1472
1731
  await onClickStart(undefined, true, chatLogs === null || chatLogs === void 0 ? void 0 : chatLogs[0]);
@@ -1570,9 +1829,11 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
1570
1829
  console.log('[onClickStart] Starting with initial question');
1571
1830
  translatedMessages = [];
1572
1831
  setHistory([]);
1573
- setMemoriTyping(true);
1574
1832
  const placeSpec = getPlaceSpecForEnterText(position);
1575
- const response = await postTextEnteredEvent({
1833
+ console.debug('[EnterText] onClickStart: posting initial question', {
1834
+ sessionId: sessionID,
1835
+ });
1836
+ const response = await postEnterTextAsync({
1576
1837
  sessionId: sessionID,
1577
1838
  text: initialQuestion,
1578
1839
  ...(memori.needsDateTime && {
@@ -1580,6 +1841,10 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
1580
1841
  }),
1581
1842
  ...(placeSpec !== undefined && { place: placeSpec }),
1582
1843
  });
1844
+ console.debug('[EnterText] onClickStart: HTTP response', {
1845
+ resultCode: response.resultCode,
1846
+ correlationID: readCorrelationID(response),
1847
+ });
1583
1848
  if (response.resultCode === 500 && response.resultMessage) {
1584
1849
  setHistory(h => [
1585
1850
  ...h,
@@ -1592,10 +1857,33 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
1592
1857
  date: new Date().toISOString(),
1593
1858
  },
1594
1859
  ]);
1595
- setMemoriTyping(false);
1596
1860
  return;
1597
1861
  }
1598
- await translateAndSpeak((_w = response.currentState) !== null && _w !== void 0 ? _w : currentState, userLang, undefined, false);
1862
+ const onClickStartCorrelationID = readCorrelationID(response);
1863
+ if (response.resultCode === 0 && onClickStartCorrelationID) {
1864
+ console.info('[EnterText] onClickStart: accepted, showing typing indicator', {
1865
+ correlationID: onClickStartCorrelationID,
1866
+ });
1867
+ setMemoriTyping(true);
1868
+ try {
1869
+ const natsEvent = await waitForEnterTextNatsResponse(onClickStartCorrelationID);
1870
+ console.info('[EnterText] onClickStart: NATS response received', {
1871
+ correlationID: onClickStartCorrelationID,
1872
+ resultCode: natsEvent.resultCode,
1873
+ });
1874
+ if (natsEvent.resultCode === 0 && natsEvent.currentState) {
1875
+ await translateAndSpeak(natsEvent.currentState, userLang, undefined, false);
1876
+ }
1877
+ }
1878
+ catch (e) {
1879
+ console.error('[EnterText] onClickStart: NATS wait failed', e);
1880
+ setMemoriTyping(false);
1881
+ setTypingText(undefined);
1882
+ }
1883
+ }
1884
+ else if (response.resultCode === 0) {
1885
+ console.error('[EnterText] onClickStart: HTTP 200 but missing correlationID', response);
1886
+ }
1599
1887
  }
1600
1888
  }
1601
1889
  }
@@ -1666,9 +1954,24 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
1666
1954
  (user === null || user === void 0 ? void 0 : user.pAndCUAccepted);
1667
1955
  const needsCredits = tenant === null || tenant === void 0 ? void 0 : tenant.billingDelegation;
1668
1956
  const [hasEnoughCredits, setHasEnoughCredits] = useState(true);
1669
- const checkCredits = useCallback(async () => {
1957
+ const handleNotEnoughCredits = useCallback(() => {
1958
+ setHasEnoughCredits(false);
1959
+ setAuthModalState(null);
1960
+ toast.error(t('notEnoughCredits'));
1961
+ }, [t]);
1962
+ const checkCredits = useCallback(async (options) => {
1670
1963
  if (!(tenant === null || tenant === void 0 ? void 0 : tenant.billingDelegation))
1671
- return;
1964
+ return true;
1965
+ if (!ownerUserID && !ownerUserName) {
1966
+ console.warn('Cannot verify credits: missing owner identifier');
1967
+ if (options === null || options === void 0 ? void 0 : options.notify) {
1968
+ handleNotEnoughCredits();
1969
+ }
1970
+ else {
1971
+ setHasEnoughCredits(false);
1972
+ }
1973
+ return false;
1974
+ }
1672
1975
  try {
1673
1976
  const resp = await getCredits({
1674
1977
  operation: deepThoughtEnabled
@@ -1681,22 +1984,38 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
1681
1984
  });
1682
1985
  if (resp.enough) {
1683
1986
  setHasEnoughCredits(true);
1987
+ return true;
1684
1988
  }
1685
1989
  else {
1686
- setHasEnoughCredits(false);
1687
1990
  console.warn('Not enough credits. Required:', resp.required);
1991
+ if (options === null || options === void 0 ? void 0 : options.notify) {
1992
+ handleNotEnoughCredits();
1993
+ }
1994
+ else {
1995
+ setHasEnoughCredits(false);
1996
+ }
1997
+ return false;
1688
1998
  }
1689
1999
  }
1690
2000
  catch (e) {
1691
2001
  let err = e;
1692
2002
  console.debug(err);
2003
+ return true;
1693
2004
  }
1694
- }, [tenant === null || tenant === void 0 ? void 0 : tenant.billingDelegation, deepThoughtEnabled]);
2005
+ }, [
2006
+ baseUrl,
2007
+ deepThoughtEnabled,
2008
+ handleNotEnoughCredits,
2009
+ ownerUserID,
2010
+ ownerUserName,
2011
+ tenant === null || tenant === void 0 ? void 0 : tenant.billingDelegation,
2012
+ tenantID,
2013
+ ]);
1695
2014
  useEffect(() => {
1696
2015
  if (tenant === null || tenant === void 0 ? void 0 : tenant.billingDelegation) {
1697
2016
  checkCredits();
1698
2017
  }
1699
- }, [tenant === null || tenant === void 0 ? void 0 : tenant.billingDelegation, deepThoughtEnabled]);
2018
+ }, [tenant === null || tenant === void 0 ? void 0 : tenant.billingDelegation, deepThoughtEnabled, checkCredits]);
1700
2019
  useEffect(() => {
1701
2020
  if (__WEBCOMPONENT__)
1702
2021
  return;
@@ -1942,10 +2261,6 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
1942
2261
  }
1943
2262
  })
1944
2263
  .catch(error => {
1945
- if (!(error instanceof Error) ||
1946
- error.message !== 'AUTH_FAILED') {
1947
- setGotErrorInOpening(true);
1948
- }
1949
2264
  throw error;
1950
2265
  });
1951
2266
  }, minimumNumberOfRecoveryTokens: (_18 = memori === null || memori === void 0 ? void 0 : memori.minimumNumberOfRecoveryTokens) !== null && _18 !== void 0 ? _18 : 1 })), isClient && (_jsx(AgeVerificationModal, { visible: showAgeVerification, minAge: minAge, onClose: birthDate => {
@@ -1966,7 +2281,6 @@ const MemoriWidget = ({ memori, memoriConfigs, ownerUserID, ownerUserName, tenan
1966
2281
  })
1967
2282
  .catch(() => {
1968
2283
  setShowAgeVerification(false);
1969
- setGotErrorInOpening(true);
1970
2284
  });
1971
2285
  }
1972
2286
  else {