@microsoft/omnichannel-chat-sdk 1.11.9-main.b0a2bb8 → 1.11.9-main.c03be93

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -44,6 +44,7 @@ Please make sure you have a chat widget configured before using this package or
44
44
  - [Create Chat Adapter](#create-chat-adapter)
45
45
  - [Get Voice & Video Calling](#get-voice--video-calling)
46
46
  - [Get Post Chat Survey Context](#get-post-chat-survey-context)
47
+ - [Get Persistent Chat History](#get-persistent-chat-history)
47
48
  - [Common Scenarios](#common-scenarios)
48
49
  - [Using BotFramework-WebChat](#using-botframework-webchat)
49
50
  - [Escalation to Voice & Video](#escalation-to-voice--video)
@@ -52,6 +53,7 @@ Please make sure you have a chat widget configured before using this package or
52
53
  - [Reconnect to existing Chat](#reconnect-to-existing-chat)
53
54
  - [Authenticated Chat](#authenticated-chat)
54
55
  - [Persistent Chat](#persistent-chat)
56
+ - [Persistent Chat History](#persistent-chat-history)
55
57
  - [Chat Reconnect with Authenticated User](#chat-reconnect-with-authenticated-user)
56
58
  - [Chat Reconnect with Unauthenticated User](#chat-reconnect-with-unauthenticated-user)
57
59
  - [Best Practices for Chat Session Management: Handling Disconnections and Network Instabilit(#best-practices-for-chat-session-management:-handling-disconnections-and-network-instability)
@@ -579,6 +581,24 @@ It gets the participant type that should be used for the survey and both the def
579
581
  const postChatSurveyContext = await chatSDK.getPostChatSurveyContext();
580
582
  ```
581
583
 
584
+ ### Get Persistent Chat History
585
+
586
+ It fetches paginated chat history from previous persistent chat conversations for authenticated users. Returns messages in chronological order along with a pagination token to fetch the next page.
587
+
588
+ > :warning: Requires **persistent chat** to be enabled and the user must be **authenticated**.
589
+
590
+ ```ts
591
+ const optionalParams = {
592
+ pageSize: 25, // Number of messages per page (optional)
593
+ pageToken: '' // Token from a previous response to fetch the next page (optional, omit for first call)
594
+ };
595
+
596
+ const response = await chatSDK.getPersistentChatHistory(optionalParams);
597
+
598
+ // response.chatMessages: PersistentChatHistoryMessage[] — Array of chat messages
599
+ // response.nextPageToken: string | null — Token to pass in the next call; null means no more history
600
+ ```
601
+
582
602
  ### Get Agent Availability
583
603
 
584
604
  It gets information on whether a queue is available, and whether there are agents available in that queue, as well as queue position and average wait time. This call only supports authenticated chat.
@@ -735,6 +755,200 @@ await chatSDK.initialize();
735
755
  // from this point, this acts like a persistent chat
736
756
  ```
737
757
 
758
+ ### Persistent Chat History
759
+
760
+ > See <https://docs.microsoft.com/en-us/dynamics365/customer-service/persistent-chat> on how to set up persistent chat
761
+
762
+ When persistent chat is enabled, you can retrieve the chat history from previous conversations using `getPersistentChatHistory()`. This method returns paginated results, allowing you to load history incrementally (e.g., as the user scrolls up).
763
+
764
+ > :warning: **Prerequisites:**
765
+ > - Persistent chat must be **enabled** in the admin portal (conversation mode set to Persistent Chat) and in the SDK config (`persistentChat.disable: false`)
766
+ > - The user must be **authenticated** (`getAuthToken` must be configured)
767
+ > - `chatSDK.initialize()` and `chatSDK.startChat()` must be called before fetching history
768
+ > - Chat history is available for up to **12 months**
769
+
770
+ #### Page Size
771
+
772
+ The `pageSize` parameter controls how many messages are returned per call.
773
+
774
+ | | Value |
775
+ | --- | --- |
776
+ | **Default** | `50` (used when `pageSize` is omitted) |
777
+ | **Minimum** | `1` |
778
+ | **Maximum** | `100` |
779
+
780
+ If `pageSize` is not provided, the backend defaults to **50**. Values outside the 1–100 range will be rejected with an error.
781
+
782
+ #### How Pagination Works
783
+
784
+ | Call | `pageToken` param | What happens |
785
+ | --- | --- | --- |
786
+ | **First call** | Omit or `undefined` | Fetches the most recent page of messages. The response includes `nextPageToken` if older messages exist. |
787
+ | **Subsequent calls** | Pass the `nextPageToken` from the previous response | Fetches the next (older) page. Again returns `nextPageToken` if more messages remain. |
788
+ | **Last page** | Pass the `nextPageToken` from the previous response | Returns the remaining messages. `nextPageToken` is `null`, indicating there are no more messages in the history. |
789
+
790
+ In short: keep passing the `nextPageToken` from each response into the next call until the backend returns `nextPageToken: null` — that signals the entire history has been retrieved.
791
+
792
+ #### Basic Usage
793
+
794
+ ```ts
795
+ const chatSDKConfig = {
796
+ persistentChat: {
797
+ disable: false,
798
+ tokenUpdateTime: 21600000
799
+ },
800
+ getAuthToken: async () => {
801
+ const response = await fetch("http://contosohelp.com/token");
802
+ if (response.ok) {
803
+ return await response.text();
804
+ }
805
+ else {
806
+ return null
807
+ }
808
+ }
809
+ }
810
+
811
+ const chatSDK = new OmnichannelChatSDK.OmnichannelChatSDK(omnichannelConfig, chatSDKConfig);
812
+ await chatSDK.initialize();
813
+
814
+ await chatSDK.startChat();
815
+
816
+ // Fetch the first page of chat history
817
+ const history = await chatSDK.getPersistentChatHistory({
818
+ pageSize: 25
819
+ });
820
+
821
+ if (history) {
822
+ // Render messages
823
+ history.chatMessages.forEach((message) => {
824
+ console.log(`[${message.createdDateTime}] ${message.content}`);
825
+ });
826
+
827
+ // Check if more history is available
828
+ if (history.nextPageToken) {
829
+ console.log("More history available");
830
+ }
831
+ }
832
+ ```
833
+
834
+ #### Pagination — Loading More History on Scroll Up
835
+
836
+ After the first call returns a `nextPageToken`, you can fetch older pages on demand — for example, each time the customer scrolls up in the chat window. The loop below shows fetching all remaining history, but in practice you would call `getPersistentChatHistory` once per scroll-up action, passing the `nextPageToken` from the previous response.
837
+
838
+ ```ts
839
+ // After the first call (Basic Usage above), save the nextPageToken
840
+ let pageToken: string | undefined = history?.nextPageToken ?? undefined;
841
+
842
+ // Example: fetch the next page when the customer scrolls up
843
+ const loadMoreHistory = async () => {
844
+ if (!pageToken) {
845
+ console.log("No more history to load");
846
+ return;
847
+ }
848
+
849
+ const response = await chatSDK.getPersistentChatHistory({
850
+ pageSize: 25,
851
+ pageToken
852
+ });
853
+
854
+ if (response && response.chatMessages.length > 0) {
855
+ // Render the older messages in the chat UI
856
+ response.chatMessages.forEach((message) => {
857
+ console.log(`[${message.createdDateTime}] ${message.content}`);
858
+ });
859
+
860
+ // Update the token for the next scroll-up action
861
+ pageToken = response.nextPageToken ?? undefined;
862
+ }
863
+ };
864
+
865
+ // Bind to your scroll-up event (e.g., user reaches the top of the chat container)
866
+ chatContainer.addEventListener('scroll', () => {
867
+ if (chatContainer.scrollTop === 0) {
868
+ loadMoreHistory();
869
+ }
870
+ });
871
+ ```
872
+
873
+ Alternatively, to fetch all history at once in a loop:
874
+
875
+ ```ts
876
+ let token: string | undefined = history?.nextPageToken ?? undefined;
877
+ let allMessages = [...(history?.chatMessages ?? [])];
878
+
879
+ while (token) {
880
+ const response = await chatSDK.getPersistentChatHistory({
881
+ pageSize: 25,
882
+ pageToken: token
883
+ });
884
+
885
+ if (response && response.chatMessages.length > 0) {
886
+ allMessages = [...allMessages, ...response.chatMessages];
887
+ token = response.nextPageToken ?? undefined;
888
+ } else {
889
+ break;
890
+ }
891
+ }
892
+
893
+ console.log(`Total messages retrieved: ${allMessages.length}`);
894
+ ```
895
+
896
+ #### Response Shape
897
+
898
+ The response type is exported from the SDK and can be imported for TypeScript usage:
899
+
900
+ ```ts
901
+ import { GetPersistentChatHistoryResponse } from '@microsoft/omnichannel-chat-sdk';
902
+ ```
903
+
904
+ ```ts
905
+ type GetPersistentChatHistoryResponse = {
906
+ chatMessages: PersistentChatHistoryMessage[];
907
+ nextPageToken: string | null; // null when no more pages
908
+ }
909
+
910
+ type PersistentChatHistoryMessage = {
911
+ content: string; // Message text
912
+ contentType: number; // Content type identifier
913
+ createdDateTime: string; // ISO 8601 timestamp
914
+ from: {
915
+ application: { // Sender application info
916
+ displayName: string; // e.g., agent name or bot name
917
+ id: string;
918
+ } | null;
919
+ };
920
+ id: string; // Unique message identifier
921
+ attachments: unknown[]; // File attachments if any
922
+ additionalData: {
923
+ deliveryMode: string; // Delivery channel
924
+ ConversationId: string; // Conversation the message belongs to
925
+ tags: string; // Message tags
926
+ };
927
+ }
928
+ ```
929
+
930
+ #### Error Handling
931
+
932
+ ```ts
933
+ try {
934
+ const history = await chatSDK.getPersistentChatHistory({ pageSize: 25 });
935
+ // Process messages...
936
+ } catch (error) {
937
+ if (error.message === 'ChatSDKNotInitialized') {
938
+ // SDK not initialized — call chatSDK.initialize() first
939
+ }
940
+ if (error.message === 'PersistentChatNotEnabled') {
941
+ // Persistent chat is not enabled for this widget
942
+ }
943
+ if (error.message === 'AuthenticatedUserTokenNotFound') {
944
+ // User is not authenticated — configure getAuthToken
945
+ }
946
+ if (error.message === 'PersistentChatConversationRetrievalFailure') {
947
+ // Server error fetching history — retry or handle gracefully
948
+ }
949
+ }
950
+ ```
951
+
738
952
  ### Chat Reconnect with Authenticated User
739
953
 
740
954
  > See <https://docs.microsoft.com/en-us/dynamics365/customer-service/configure-reconnect-chat?tabs=customerserviceadmincenter#enable-reconnection-to-a-previous-chat-session> on how to set up chat reconnect
@@ -50,6 +50,7 @@ declare class OmnichannelChatSDK {
50
50
  sessionId: string | null;
51
51
  private chatOperationInProgress;
52
52
  private pendingOperations;
53
+ private deferInitialAuth;
53
54
  private unqServicesOrgUrl;
54
55
  private coreServicesOrgUrl;
55
56
  private dynamicsLocationCode;
@@ -170,6 +171,17 @@ declare class OmnichannelChatSDK {
170
171
  sendMessage(message: ChatSDKMessage): Promise<OmnichannelMessage | void>;
171
172
  onNewMessage(onNewMessageCallback: CallableFunction, optionalParams?: OnNewMessageOptionalParams): Promise<void>;
172
173
  sendTypingEvent(): Promise<void>;
174
+ /**
175
+ * Fetches unread message count for the authenticated user.
176
+ * Auth-only — does not require an active chat session.
177
+ */
178
+ getUnreadMessageCount(): Promise<object>;
179
+ /**
180
+ * Sends a read receipt for a specific message.
181
+ * Authenticated: calls MRT (updates NRD + forwards to ACS).
182
+ * Unauthenticated: calls ACS directly.
183
+ */
184
+ sendReadReceipt(messageId: string): Promise<void>;
173
185
  onTypingEvent(onTypingEventCallback: CallableFunction): Promise<void>;
174
186
  onAgentEndSession(onAgentEndSessionCallback: (message: IRawThread | ParticipantsRemovedEvent) => void): Promise<void>;
175
187
  uploadFileAttachment(fileInfo: IFileInfo | File): Promise<UploadFileAttachmentResponse>;
@@ -181,6 +193,9 @@ declare class OmnichannelChatSDK {
181
193
  getVoiceVideoCalling(voiceVideoCallingOptionalParams?: VoiceVideoCallingOptionalParams): Promise<GetVoiceVideoCallingResponse>;
182
194
  getPostChatSurveyContext(): Promise<PostChatContext>;
183
195
  getAgentAvailability(optionalParams?: GetAgentAvailabilityOptionalParams): Promise<GetAgentAvailabilityResponse>;
196
+ authenticateChat(tokenOrProvider: string | (() => Promise<string>), optionalParams?: {
197
+ refreshChatToken?: boolean;
198
+ }): Promise<void>;
184
199
  startPolling(): Promise<void>;
185
200
  stopPolling(): Promise<void>;
186
201
  private populateInitChatOptionalParam;
@@ -194,6 +209,13 @@ declare class OmnichannelChatSDK {
194
209
  private setCallingOptionConfiguration;
195
210
  private setLiveChatVersionConfiguration;
196
211
  private setWidgetSnippetBaseUrl;
212
+ /**
213
+ * Creates diagnostic data for getChatConfig errors using the error classifier
214
+ * @param e The error object
215
+ * @param clientElapsedMs Optional elapsed time in milliseconds from client's perspective
216
+ * @returns Diagnostic data with clientElapsedMs, online status, and cancellation reason
217
+ */
218
+ private createGetChatConfigDiagnosticData;
197
219
  private getChatConfig;
198
220
  private buildConfigurations;
199
221
  private resolveIC3ClientUrl;