open-chat-studio-widget 0.8.0 → 0.9.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 (35) hide show
  1. package/README.md +1 -0
  2. package/dist/cjs/{index-Cf6K60f1.js → index-DDod9Zyw.js} +3 -3
  3. package/dist/cjs/{index-Cf6K60f1.js.map → index-DDod9Zyw.js.map} +1 -1
  4. package/dist/cjs/loader.cjs.js +2 -2
  5. package/dist/cjs/open-chat-studio-widget.cjs.entry.js +175 -22
  6. package/dist/cjs/open-chat-studio-widget.cjs.entry.js.map +1 -1
  7. package/dist/cjs/open-chat-studio-widget.cjs.js +2 -2
  8. package/dist/cjs/open-chat-studio-widget.entry.cjs.js.map +1 -1
  9. package/dist/collection/components/ocs-chat/ocs-chat.js +82 -4
  10. package/dist/collection/components/ocs-chat/ocs-chat.js.map +1 -1
  11. package/dist/collection/services/chat-session-service.js +88 -18
  12. package/dist/collection/services/chat-session-service.js.map +1 -1
  13. package/dist/collection/services/file-attachment-manager.js +6 -0
  14. package/dist/collection/services/file-attachment-manager.js.map +1 -1
  15. package/dist/components/open-chat-studio-widget.js +175 -21
  16. package/dist/components/open-chat-studio-widget.js.map +1 -1
  17. package/dist/esm/{index-DXf2dIht.js → index-iUBQH9om.js} +3 -3
  18. package/dist/esm/{index-DXf2dIht.js.map → index-iUBQH9om.js.map} +1 -1
  19. package/dist/esm/loader.js +3 -3
  20. package/dist/esm/open-chat-studio-widget.entry.js +175 -22
  21. package/dist/esm/open-chat-studio-widget.entry.js.map +1 -1
  22. package/dist/esm/open-chat-studio-widget.js +3 -3
  23. package/dist/open-chat-studio-widget/open-chat-studio-widget.entry.esm.js.map +1 -1
  24. package/dist/open-chat-studio-widget/open-chat-studio-widget.esm.js +1 -1
  25. package/dist/open-chat-studio-widget/p-9c925476.entry.js +4 -0
  26. package/dist/open-chat-studio-widget/p-9c925476.entry.js.map +1 -0
  27. package/dist/open-chat-studio-widget/{p-DXf2dIht.js → p-iUBQH9om.js} +2 -2
  28. package/dist/open-chat-studio-widget/{p-DXf2dIht.js.map → p-iUBQH9om.js.map} +1 -1
  29. package/dist/types/components/ocs-chat/ocs-chat.d.ts +14 -0
  30. package/dist/types/components.d.ts +8 -0
  31. package/dist/types/services/chat-session-service.d.ts +21 -0
  32. package/dist/types/services/file-attachment-manager.d.ts +2 -0
  33. package/package.json +1 -1
  34. package/dist/open-chat-studio-widget/p-ff47dabf.entry.js +0 -4
  35. package/dist/open-chat-studio-widget/p-ff47dabf.entry.js.map +0 -1
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-Cf6K60f1.js');
3
+ var index = require('./index-DDod9Zyw.js');
4
4
  var appGlobals = require('./app-globals-V2Kpy_OQ.js');
5
5
 
6
6
  const defineCustomElements = async (win, options) => {
7
7
  if (typeof window === 'undefined') return undefined;
8
8
  await appGlobals.globalScripts();
9
- return index.bootstrapLazy([["open-chat-studio-widget.cjs",[[257,"open-chat-studio-widget",{"chatbotId":[1,"chatbot-id"],"apiBaseUrl":[1,"api-base-url"],"buttonText":[1,"button-text"],"iconUrl":[1,"icon-url"],"embedKey":[1,"embed-key"],"buttonShape":[1,"button-shape"],"showButton":[4,"show-button"],"mode":[1],"headerText":[1,"header-text"],"newChatConfirmationMessage":[1,"new-chat-confirmation-message"],"visible":[1028],"position":[1025],"welcomeMessages":[1,"welcome-messages"],"starterQuestions":[1,"starter-questions"],"userId":[1,"user-id"],"userName":[1,"user-name"],"persistentSession":[4,"persistent-session"],"persistentSessionExpire":[2,"persistent-session-expire"],"allowFullScreen":[4,"allow-full-screen"],"allowAttachments":[4,"allow-attachments"],"typingIndicatorText":[1,"typing-indicator-text"],"language":[1],"translationsUrl":[1,"translations-url"],"pageContext":[1040,"page-context"],"versionNumber":[2,"version-number"],"sessionId":[1,"session-id"],"error":[32],"messages":[32],"activeSessionId":[32],"isLoading":[32],"isTyping":[32],"typingProgressMessage":[32],"messageInput":[32],"currentPollTaskId":[32],"isDragging":[32],"dragOffset":[32],"windowPosition":[32],"fullscreenPosition":[32],"parsedWelcomeMessages":[32],"parsedStarterQuestions":[32],"generatedUserId":[32],"isFullscreen":[32],"showNewChatConfirmation":[32],"selectedFiles":[32],"isUploadingFiles":[32],"isButtonDragging":[32],"buttonWasDragged":[32]},null,{"pageContext":["pageContextHandler"],"chatbotId":["chatbotConfigHandler"],"versionNumber":["chatbotConfigHandler"],"visible":["visibilityHandler"]}]]]], options);
9
+ return index.bootstrapLazy([["open-chat-studio-widget.cjs",[[257,"open-chat-studio-widget",{"chatbotId":[1,"chatbot-id"],"apiBaseUrl":[1,"api-base-url"],"buttonText":[1,"button-text"],"iconUrl":[1,"icon-url"],"embedKey":[1,"embed-key"],"buttonShape":[1,"button-shape"],"showButton":[4,"show-button"],"mode":[1],"headerText":[1,"header-text"],"newChatConfirmationMessage":[1,"new-chat-confirmation-message"],"visible":[1028],"position":[1025],"welcomeMessages":[1,"welcome-messages"],"starterQuestions":[1,"starter-questions"],"userId":[1,"user-id"],"userName":[1,"user-name"],"persistentSession":[4,"persistent-session"],"persistentSessionExpire":[2,"persistent-session-expire"],"allowFullScreen":[4,"allow-full-screen"],"allowAttachments":[4,"allow-attachments"],"typingIndicatorText":[1,"typing-indicator-text"],"language":[1],"translationsUrl":[1,"translations-url"],"pageContext":[1040,"page-context"],"versionNumber":[2,"version-number"],"sessionId":[1,"session-id"],"sessionToken":[1,"session-token"],"error":[32],"messages":[32],"activeSessionId":[32],"isLoading":[32],"isTyping":[32],"typingProgressMessage":[32],"messageInput":[32],"currentPollTaskId":[32],"isDragging":[32],"dragOffset":[32],"windowPosition":[32],"fullscreenPosition":[32],"parsedWelcomeMessages":[32],"parsedStarterQuestions":[32],"generatedUserId":[32],"isFullscreen":[32],"showNewChatConfirmation":[32],"selectedFiles":[32],"isUploadingFiles":[32],"isButtonDragging":[32],"buttonWasDragged":[32]},null,{"pageContext":["pageContextHandler"],"chatbotId":["chatbotConfigHandler"],"versionNumber":["chatbotConfigHandler"],"visible":["visibilityHandler"]}]]]], options);
10
10
  };
11
11
 
12
12
  exports.setNonce = index.setNonce;
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-Cf6K60f1.js');
3
+ var index = require('./index-DDod9Zyw.js');
4
4
 
5
5
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
6
6
  const OcsWidgetAvatar = () => {
@@ -4470,6 +4470,8 @@ var ar = {
4470
4470
  "status.starting": "جارٍ بدء المحادثة...",
4471
4471
  "status.typing": "جارٍ تحضير الرد",
4472
4472
  "status.uploading": "جارٍ التحميل",
4473
+ "status.sessionExpired": "انتهت صلاحية جلسة الدردشة. سيتم بدء محادثة جديدة — يُرجى إعادة إرسال رسالتك.",
4474
+ "status.sessionError": "لم تعد جلسة الدردشة هذه متاحة.",
4473
4475
  "modal.newChatTitle": "بدء محادثة جديدة",
4474
4476
  "modal.newChatBody": "بدء محادثة جديدة سيؤدي إلى مسح المحادثة الحالية. هل ترغب بالمتابعة؟",
4475
4477
  "modal.cancel": "إلغاء",
@@ -4502,6 +4504,8 @@ var en = {
4502
4504
  "status.starting": "Starting chat...",
4503
4505
  "status.typing": "Preparing response",
4504
4506
  "status.uploading": "Uploading",
4507
+ "status.sessionExpired": "Your chat session expired. Starting a new chat — please resend your message.",
4508
+ "status.sessionError": "This chat session is no longer available.",
4505
4509
  "modal.newChatTitle": "Start New Chat",
4506
4510
  "modal.newChatBody": "Starting a new chat will clear your current conversation. Continue?",
4507
4511
  "modal.cancel": "Cancel",
@@ -4534,6 +4538,8 @@ var es = {
4534
4538
  "status.starting": "Iniciando chat...",
4535
4539
  "status.typing": "Preparando respuesta",
4536
4540
  "status.uploading": "Subiendo",
4541
+ "status.sessionExpired": "Tu sesión de chat ha expirado. Se iniciará un nuevo chat: vuelve a enviar tu mensaje.",
4542
+ "status.sessionError": "Esta sesión de chat ya no está disponible.",
4537
4543
  "modal.newChatTitle": "Iniciar Nuevo Chat",
4538
4544
  "modal.newChatBody": "Iniciar un nuevo chat borrará tu conversación actual. ¿Continuar?",
4539
4545
  "modal.cancel": "Cancelar",
@@ -4566,6 +4572,8 @@ var fr = {
4566
4572
  "status.starting": "Démarrage du chat...",
4567
4573
  "status.typing": "Préparation de la réponse",
4568
4574
  "status.uploading": "Téléversement",
4575
+ "status.sessionExpired": "Votre session de chat a expiré. Un nouveau chat va démarrer — veuillez renvoyer votre message.",
4576
+ "status.sessionError": "Cette session de chat n'est plus disponible.",
4569
4577
  "modal.newChatTitle": "Nouveau Chat",
4570
4578
  "modal.newChatBody": "Démarrer un nouveau chat effacera votre conversation actuelle. Continuer?",
4571
4579
  "modal.cancel": "Annuler",
@@ -4598,6 +4606,8 @@ var hi = {
4598
4606
  "status.starting": "चैट शुरू हो रही है...",
4599
4607
  "status.typing": "जवाब तैयार किया जा रहा है",
4600
4608
  "status.uploading": "अपलोड हो रहा है",
4609
+ "status.sessionExpired": "आपका चैट सत्र समाप्त हो गया है। एक नई चैट शुरू हो रही है — कृपया अपना संदेश फिर से भेजें।",
4610
+ "status.sessionError": "यह चैट सत्र अब उपलब्ध नहीं है।",
4601
4611
  "modal.newChatTitle": "नई चैट शुरू करें",
4602
4612
  "modal.newChatBody": "नई चैट शुरू करने से आपकी मौजूदा बातचीत हट जाएगी। क्या आप जारी रखना चाहते हैं?",
4603
4613
  "modal.cancel": "रद्द करें",
@@ -4630,6 +4640,8 @@ var it = {
4630
4640
  "status.starting": "Avvio della chat...",
4631
4641
  "status.typing": "Preparazione della risposta",
4632
4642
  "status.uploading": "Caricamento",
4643
+ "status.sessionExpired": "La tua sessione di chat è scaduta. Verrà avviata una nuova chat: invia di nuovo il tuo messaggio.",
4644
+ "status.sessionError": "Questa sessione di chat non è più disponibile.",
4633
4645
  "modal.newChatTitle": "Avvia nuova chat",
4634
4646
  "modal.newChatBody": "Avviare una nuova chat cancellerà la conversazione attuale. Continuare?",
4635
4647
  "modal.cancel": "Annulla",
@@ -4662,6 +4674,8 @@ var pt = {
4662
4674
  "status.starting": "Iniciando chat...",
4663
4675
  "status.typing": "Preparando resposta",
4664
4676
  "status.uploading": "Carregando",
4677
+ "status.sessionExpired": "Sua sessão de chat expirou. Iniciando um novo chat — reenvie sua mensagem.",
4678
+ "status.sessionError": "Esta sessão de chat não está mais disponível.",
4665
4679
  "modal.newChatTitle": "Iniciar novo chat",
4666
4680
  "modal.newChatBody": "Iniciar um novo chat apagará sua conversa atual. Deseja continuar?",
4667
4681
  "modal.cancel": "Cancelar",
@@ -4694,6 +4708,8 @@ var sw = {
4694
4708
  "status.starting": "Inaanzisha gumzo...",
4695
4709
  "status.typing": "Inatayarisha jibu",
4696
4710
  "status.uploading": "Inapakia",
4711
+ "status.sessionExpired": "Kipindi chako cha gumzo kimeisha. Gumzo jipya linaanza — tafadhali tuma tena ujumbe wako.",
4712
+ "status.sessionError": "Kipindi hiki cha gumzo hakipatikani tena.",
4697
4713
  "modal.newChatTitle": "Anza gumzo jipya",
4698
4714
  "modal.newChatBody": "Kuanza gumzo jipya kutafuta gumzo lako la sasa. Je, ungependa kuendelea?",
4699
4715
  "modal.cancel": "Ghairi",
@@ -4726,6 +4742,8 @@ var uk = {
4726
4742
  "status.starting": "Запуск чату...",
4727
4743
  "status.typing": "Готується відповідь",
4728
4744
  "status.uploading": "Завантаження",
4745
+ "status.sessionExpired": "Ваш сеанс чату завершився. Розпочинається новий чат — будь ласка, надішліть повідомлення ще раз.",
4746
+ "status.sessionError": "Цей сеанс чату більше недоступний.",
4729
4747
  "modal.newChatTitle": "Почати новий чат",
4730
4748
  "modal.newChatBody": "Початок нового чату видалить вашу поточну розмову. Продовжити?",
4731
4749
  "modal.cancel": "Скасувати",
@@ -4982,55 +5000,54 @@ function currentDomainMatchesApiBaseUrl(apiBaseUrl) {
4982
5000
  return window.location.origin === apiBase.origin;
4983
5001
  }
4984
5002
 
5003
+ class SessionAccessError extends Error {
5004
+ constructor(status, code, message) {
5005
+ super(message);
5006
+ this.name = 'SessionAccessError';
5007
+ this.status = status;
5008
+ this.code = code;
5009
+ }
5010
+ }
4985
5011
  class ChatSessionService {
4986
5012
  constructor(options) {
4987
5013
  var _a, _b, _c, _d;
4988
5014
  this.apiBaseUrl = options.apiBaseUrl;
4989
5015
  this.embedKey = options.embedKey;
4990
5016
  this.widgetVersion = options.widgetVersion;
5017
+ this.sessionToken = options.sessionToken;
4991
5018
  this.csrfTokenProvider = (_a = options.csrfTokenProvider) !== null && _a !== void 0 ? _a : getCSRFToken;
4992
5019
  this.taskPollingIntervalMs = (_b = options.taskPollingIntervalMs) !== null && _b !== void 0 ? _b : 1000;
4993
5020
  this.taskPollingMaxAttempts = (_c = options.taskPollingMaxAttempts) !== null && _c !== void 0 ? _c : 120;
4994
5021
  this.messagePollingIntervalMs = (_d = options.messagePollingIntervalMs) !== null && _d !== void 0 ? _d : 30000;
4995
5022
  }
4996
5023
  async startSession(requestBody) {
4997
- const response = await fetch(`${this.apiBaseUrl}/api/chat/start/`, {
5024
+ const response = await this.request(`${this.apiBaseUrl}/api/chat/start/`, {
4998
5025
  method: 'POST',
4999
5026
  headers: this.getJsonHeaders(),
5000
5027
  body: JSON.stringify(requestBody),
5001
5028
  });
5002
5029
  if (!response.ok) {
5003
- throw new Error(`Failed to start session: ${response.statusText}`);
5030
+ await this.raiseForStatus(response, 'Failed to start session');
5004
5031
  }
5005
5032
  return response.json();
5006
5033
  }
5007
5034
  async sendMessage(sessionId, payload) {
5008
- const response = await fetch(`${this.apiBaseUrl}/api/chat/${sessionId}/message/`, {
5035
+ const response = await this.request(`${this.apiBaseUrl}/api/chat/${sessionId}/message/`, {
5009
5036
  method: 'POST',
5010
5037
  headers: this.getJsonHeaders(),
5011
5038
  body: JSON.stringify(payload),
5012
5039
  });
5013
5040
  if (!response.ok) {
5014
- throw new Error(`Failed to send message: ${response.statusText}`);
5041
+ await this.raiseForStatus(response, 'Failed to send message');
5015
5042
  }
5016
5043
  return response.json();
5017
5044
  }
5018
5045
  async pollTaskOnce(sessionId, taskId) {
5019
- const response = await fetch(`${this.apiBaseUrl}/api/chat/${sessionId}/${taskId}/poll/`, {
5046
+ const response = await this.request(`${this.apiBaseUrl}/api/chat/${sessionId}/${taskId}/poll/`, {
5020
5047
  headers: this.getCommonHeaders(),
5021
5048
  });
5022
5049
  if (!response.ok) {
5023
- let errorMessage = `Failed to poll task: ${response.statusText}`;
5024
- try {
5025
- const data = (await response.json());
5026
- if (data === null || data === void 0 ? void 0 : data.error) {
5027
- errorMessage = data.error;
5028
- }
5029
- }
5030
- catch (_a) {
5031
- // non-JSON body; keep statusText fallback
5032
- }
5033
- throw new Error(errorMessage);
5050
+ await this.raiseForStatus(response, 'Failed to poll task');
5034
5051
  }
5035
5052
  return response.json();
5036
5053
  }
@@ -5090,11 +5107,11 @@ class ChatSessionService {
5090
5107
  if (since) {
5091
5108
  url.searchParams.set('since', since);
5092
5109
  }
5093
- const response = await fetch(url.toString(), {
5110
+ const response = await this.request(url.toString(), {
5094
5111
  headers: this.getCommonHeaders(),
5095
5112
  });
5096
5113
  if (!response.ok) {
5097
- throw new Error(`Failed to poll messages: ${response.statusText}`);
5114
+ await this.raiseForStatus(response, 'Failed to poll messages');
5098
5115
  }
5099
5116
  return response.json();
5100
5117
  }
@@ -5151,6 +5168,74 @@ class ChatSessionService {
5151
5168
  this.messagePollingTimer = undefined;
5152
5169
  }
5153
5170
  }
5171
+ setSessionToken(token) {
5172
+ this.sessionToken = token;
5173
+ }
5174
+ async request(input, init) {
5175
+ const response = await fetch(input, init);
5176
+ this.checkSunsetHeaders(response);
5177
+ return response;
5178
+ }
5179
+ /**
5180
+ * Log a deprecation warning (RFC 8594 `Deprecation`/`Sunset`/`Link` headers)
5181
+ * when the server reports that this widget version is deprecated. Warns
5182
+ * during the deprecation window and errors once the sunset date has passed.
5183
+ * Logs at most once per level so polling does not flood the console.
5184
+ */
5185
+ checkSunsetHeaders(response) {
5186
+ const headers = response === null || response === void 0 ? void 0 : response.headers;
5187
+ if (!headers || typeof headers.get !== 'function') {
5188
+ return;
5189
+ }
5190
+ if (headers.get('Deprecation') !== 'true') {
5191
+ return;
5192
+ }
5193
+ const sunsetAt = this.parseSunsetDate(headers.get('Sunset'));
5194
+ const pastSunset = sunsetAt !== null && Date.now() >= sunsetAt.getTime();
5195
+ const level = pastSunset ? 'error' : 'warn';
5196
+ if (this.loggedSunsetLevel === level) {
5197
+ return;
5198
+ }
5199
+ this.loggedSunsetLevel = level;
5200
+ const upgradeUrl = this.parseSuccessorUrl(headers.get('Link'));
5201
+ const upgradeSuffix = upgradeUrl ? ` Upgrade: ${upgradeUrl}` : '';
5202
+ const sunsetText = sunsetAt ? sunsetAt.toUTCString() : 'an upcoming date';
5203
+ if (level === 'error') {
5204
+ console.error(`[open-chat-studio-widget] Widget version ${this.widgetVersion} is past its sunset date ` + `(${sunsetText}) and may stop working.${upgradeSuffix}`);
5205
+ }
5206
+ else {
5207
+ console.warn(`[open-chat-studio-widget] Widget version ${this.widgetVersion} is deprecated and will stop ` + `working after ${sunsetText}.${upgradeSuffix}`);
5208
+ }
5209
+ }
5210
+ parseSunsetDate(sunset) {
5211
+ if (!sunset) {
5212
+ return null;
5213
+ }
5214
+ const parsed = new Date(sunset);
5215
+ return Number.isNaN(parsed.getTime()) ? null : parsed;
5216
+ }
5217
+ parseSuccessorUrl(link) {
5218
+ const match = link === null || link === void 0 ? void 0 : link.match(/<([^>]+)>\s*;\s*rel="?successor-version"?/);
5219
+ return match === null || match === void 0 ? void 0 : match[1];
5220
+ }
5221
+ async raiseForStatus(response, fallbackPrefix) {
5222
+ let message = `${fallbackPrefix}: ${response.statusText}`;
5223
+ let code;
5224
+ try {
5225
+ const data = (await response.json());
5226
+ if (data === null || data === void 0 ? void 0 : data.error) {
5227
+ message = data.error;
5228
+ }
5229
+ code = data === null || data === void 0 ? void 0 : data.code;
5230
+ }
5231
+ catch (_a) {
5232
+ // non-JSON body; keep statusText fallback
5233
+ }
5234
+ if (response.status === 403) {
5235
+ throw new SessionAccessError(response.status, code, message);
5236
+ }
5237
+ throw new Error(message);
5238
+ }
5154
5239
  getJsonHeaders() {
5155
5240
  const headers = this.getCommonHeaders();
5156
5241
  headers['Content-Type'] = 'application/json';
@@ -5167,6 +5252,9 @@ class ChatSessionService {
5167
5252
  if (this.embedKey) {
5168
5253
  headers['X-Embed-Key'] = this.embedKey;
5169
5254
  }
5255
+ if (this.sessionToken) {
5256
+ headers['X-Session-Token'] = this.sessionToken;
5257
+ }
5170
5258
  return headers;
5171
5259
  }
5172
5260
  }
@@ -5233,8 +5321,13 @@ class FileAttachmentManager {
5233
5321
  formData.append('participant_name', context.participantName);
5234
5322
  }
5235
5323
  try {
5324
+ const headers = {};
5325
+ if (context.sessionToken) {
5326
+ headers['X-Session-Token'] = context.sessionToken;
5327
+ }
5236
5328
  const response = await fetch(`${context.apiBaseUrl}/api/chat/${context.sessionId}/upload/`, {
5237
5329
  method: 'POST',
5330
+ headers,
5238
5331
  body: formData,
5239
5332
  });
5240
5333
  if (!response.ok) {
@@ -5244,6 +5337,7 @@ class FileAttachmentManager {
5244
5337
  selectedFiles: this.markPendingFilesWithError(existingFiles, errorMessage),
5245
5338
  uploadedIds,
5246
5339
  errorMessage,
5340
+ tokenRejected: response.status === 403,
5247
5341
  };
5248
5342
  }
5249
5343
  const data = await this.safeJson(response);
@@ -5529,13 +5623,15 @@ const OcsChat = class {
5529
5623
  if (this.isSessionBound()) {
5530
5624
  // Bound to an externally-managed session: the host page is the source of truth.
5531
5625
  this.activeSessionId = this.sessionId;
5626
+ this.applySessionToken(this.sessionToken);
5532
5627
  }
5533
5628
  else if (this.persistentSession && this.isLocalStorageAvailable()) {
5534
5629
  // Always try to load existing session if localStorage is available
5535
- const { sessionId, messages } = this.loadSessionFromStorage();
5630
+ const { sessionId, messages, sessionToken } = this.loadSessionFromStorage();
5536
5631
  if (sessionId && messages) {
5537
5632
  this.activeSessionId = sessionId;
5538
5633
  this.messages = messages;
5634
+ this.applySessionToken(sessionToken);
5539
5635
  }
5540
5636
  }
5541
5637
  this.parseWelcomeMessages();
@@ -5584,6 +5680,11 @@ const OcsChat = class {
5584
5680
  this.removeButtonEventListeners();
5585
5681
  window.removeEventListener('resize', this.handleWindowResize);
5586
5682
  }
5683
+ applySessionToken(token) {
5684
+ var _a;
5685
+ this.currentSessionToken = token;
5686
+ (_a = this.chatService) === null || _a === void 0 ? void 0 : _a.setSessionToken(token);
5687
+ }
5587
5688
  getChatService() {
5588
5689
  if (!this.chatService) {
5589
5690
  this.chatService = new ChatSessionService({
@@ -5593,6 +5694,7 @@ const OcsChat = class {
5593
5694
  taskPollingIntervalMs: OcsChat.TASK_POLLING_INTERVAL_MS,
5594
5695
  taskPollingMaxAttempts: OcsChat.TASK_POLLING_MAX_ATTEMPTS,
5595
5696
  messagePollingIntervalMs: OcsChat.MESSAGE_POLLING_INTERVAL_MS,
5697
+ sessionToken: this.currentSessionToken,
5596
5698
  });
5597
5699
  }
5598
5700
  return this.chatService;
@@ -5608,6 +5710,27 @@ const OcsChat = class {
5608
5710
  this.saveSessionToStorage();
5609
5711
  this.scrollToBottom();
5610
5712
  }
5713
+ /**
5714
+ * Recover from a rejected session token (403). Unbound widgets discard the
5715
+ * dead session/token, show a notice, and start fresh on the next send; bound
5716
+ * widgets cannot restart a host-owned session, so they surface an error.
5717
+ */
5718
+ handleSessionAccessError() {
5719
+ this.cleanup();
5720
+ this.isLoading = false;
5721
+ this.isTyping = false;
5722
+ this.isUploadingFiles = false;
5723
+ this.typingProgressMessage = '';
5724
+ if (this.isSessionBound()) {
5725
+ this.addErrorMessage(this.translationManager.get('status.sessionError', 'This chat session is no longer available.'));
5726
+ return;
5727
+ }
5728
+ this.sessionEpoch += 1;
5729
+ this.activeSessionId = undefined;
5730
+ this.applySessionToken(undefined);
5731
+ this.clearSessionStorage();
5732
+ this.addErrorMessage(this.translationManager.get('status.sessionExpired', 'Your chat session expired. Starting a new chat — please resend your message.'));
5733
+ }
5611
5734
  handleError(errorText) {
5612
5735
  // show as system message
5613
5736
  this.addErrorMessage(errorText);
@@ -5680,12 +5803,14 @@ const OcsChat = class {
5680
5803
  this.currentPollTaskId = '';
5681
5804
  }
5682
5805
  async startSession() {
5806
+ var _a;
5683
5807
  const epoch = this.sessionEpoch;
5684
5808
  try {
5685
5809
  this.isLoading = true;
5686
5810
  const userId = this.getOrGenerateUserId();
5687
5811
  const requestBody = {
5688
5812
  chatbot_id: this.chatbotId,
5813
+ use_session_token: true,
5689
5814
  session_data: {
5690
5815
  source: 'widget',
5691
5816
  page_url: window.location.href,
@@ -5702,6 +5827,7 @@ const OcsChat = class {
5702
5827
  if (epoch !== this.sessionEpoch)
5703
5828
  return;
5704
5829
  this.activeSessionId = data.session_id;
5830
+ this.applySessionToken((_a = data.session_token) !== null && _a !== void 0 ? _a : undefined);
5705
5831
  this.saveSessionToStorage();
5706
5832
  this.startMessagePolling();
5707
5833
  }
@@ -5734,6 +5860,10 @@ const OcsChat = class {
5734
5860
  catch (error) {
5735
5861
  if (epoch !== this.sessionEpoch)
5736
5862
  return;
5863
+ if (error instanceof SessionAccessError) {
5864
+ this.handleSessionAccessError();
5865
+ return;
5866
+ }
5737
5867
  console.warn('Failed to load chat history:', error);
5738
5868
  }
5739
5869
  this.startMessagePolling();
@@ -5749,8 +5879,12 @@ const OcsChat = class {
5749
5879
  sessionId: this.activeSessionId,
5750
5880
  participantId: this.getOrGenerateUserId(),
5751
5881
  participantName: this.userName,
5882
+ sessionToken: this.currentSessionToken,
5752
5883
  });
5753
5884
  this.selectedFiles = uploadResult.selectedFiles;
5885
+ if (uploadResult.tokenRejected) {
5886
+ throw new SessionAccessError(403, 'session_token_required', uploadResult.errorMessage || 'Session token rejected');
5887
+ }
5754
5888
  return uploadResult.uploadedIds;
5755
5889
  }
5756
5890
  finally {
@@ -5842,6 +5976,10 @@ const OcsChat = class {
5842
5976
  catch (error) {
5843
5977
  if (epoch !== this.sessionEpoch)
5844
5978
  return;
5979
+ if (error instanceof SessionAccessError) {
5980
+ this.handleSessionAccessError();
5981
+ return;
5982
+ }
5845
5983
  const errorText = error instanceof Error ? error.message : 'Failed to send message';
5846
5984
  this.handleError(errorText);
5847
5985
  }
@@ -6016,8 +6154,12 @@ const OcsChat = class {
6016
6154
  },
6017
6155
  onError: error => {
6018
6156
  this.typingProgressMessage = '';
6019
- this.handleError(error.message);
6020
6157
  this.taskPollingHandle = undefined;
6158
+ if (error instanceof SessionAccessError) {
6159
+ this.handleSessionAccessError();
6160
+ return;
6161
+ }
6162
+ this.handleError(error.message);
6021
6163
  this.startMessagePolling();
6022
6164
  },
6023
6165
  });
@@ -6379,6 +6521,7 @@ const OcsChat = class {
6379
6521
  messages: `ocs-chat-messages-${this.chatbotId}`,
6380
6522
  lastActivity: `ocs-chat-activity-${this.chatbotId}`,
6381
6523
  visible: `ocs-chat-visible-${this.chatbotId}`,
6524
+ sessionToken: `ocs-chat-token-${this.chatbotId}`,
6382
6525
  };
6383
6526
  }
6384
6527
  saveSessionToStorage() {
@@ -6390,6 +6533,12 @@ const OcsChat = class {
6390
6533
  if (this.activeSessionId) {
6391
6534
  localStorage.setItem(keys.sessionId, this.activeSessionId);
6392
6535
  localStorage.setItem(keys.lastActivity, new Date().toISOString());
6536
+ if (this.currentSessionToken) {
6537
+ localStorage.setItem(keys.sessionToken, this.currentSessionToken);
6538
+ }
6539
+ else {
6540
+ localStorage.removeItem(keys.sessionToken);
6541
+ }
6393
6542
  }
6394
6543
  localStorage.setItem(keys.messages, JSON.stringify(this.messages));
6395
6544
  }
@@ -6398,6 +6547,7 @@ const OcsChat = class {
6398
6547
  }
6399
6548
  }
6400
6549
  loadSessionFromStorage() {
6550
+ var _a;
6401
6551
  const keys = this.getStorageKeys();
6402
6552
  try {
6403
6553
  if (this.persistentSessionExpire > 0) {
@@ -6425,7 +6575,8 @@ const OcsChat = class {
6425
6575
  messages = [];
6426
6576
  }
6427
6577
  }
6428
- return { sessionId, messages };
6578
+ const sessionToken = (_a = localStorage.getItem(keys.sessionToken)) !== null && _a !== void 0 ? _a : undefined;
6579
+ return { sessionId, messages, sessionToken };
6429
6580
  }
6430
6581
  catch (error) {
6431
6582
  // fall back to starting a new session
@@ -6497,6 +6648,7 @@ const OcsChat = class {
6497
6648
  localStorage.removeItem(keys.messages);
6498
6649
  localStorage.removeItem(keys.lastActivity);
6499
6650
  localStorage.removeItem(keys.visible);
6651
+ localStorage.removeItem(keys.sessionToken);
6500
6652
  }
6501
6653
  catch (error) {
6502
6654
  console.warn('Failed to clear chat session from localStorage:', error);
@@ -6538,6 +6690,7 @@ const OcsChat = class {
6538
6690
  // A session provided by the host page (session-id prop) cannot be cleared;
6539
6691
  // stay bound to it. Unbound widgets start a new session on the next message.
6540
6692
  this.activeSessionId = this.sessionId;
6693
+ this.applySessionToken(this.isSessionBound() ? this.sessionToken : undefined);
6541
6694
  this.messages = [];
6542
6695
  this.isTyping = false;
6543
6696
  this.currentPollTaskId = '';