@paymanai/payman-typescript-ask-sdk 1.2.4 → 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -144,6 +144,8 @@ type ChatConfig = {
144
144
  api: APIConfig;
145
145
  /** Workflow name */
146
146
  workflowName: string;
147
+ /** User ID for chatStore and activeStreamStore — required for context store messages management. Set to undefined on logout to clear stored messages. */
148
+ userId?: string;
147
149
  /** Workflow version */
148
150
  workflowVersion?: number;
149
151
  /** Stage/Environment */
@@ -194,6 +196,10 @@ type ChatConfig = {
194
196
  isChatDisabled?: boolean;
195
197
  /** Custom component to render when chat is disabled. If not provided, default disabled UI will be shown */
196
198
  disabledComponent?: React.ReactNode;
199
+ /** Pre-populate the chat with messages (e.g. loaded conversation history) */
200
+ initialMessages?: MessageDisplay[];
201
+ /** Resume an existing session by providing its ID */
202
+ initialSessionId?: string;
197
203
  };
198
204
  type ChatCallbacks = {
199
205
  /** Called when a message is sent (before API call) */
@@ -222,6 +228,8 @@ type UseChatReturn = {
222
228
  messages: MessageDisplay[];
223
229
  sendMessage: (userMessage: string) => Promise<void>;
224
230
  clearMessages: () => void;
231
+ /** Prepend older messages to the top of the list (e.g. loaded from history pagination) */
232
+ prependMessages: (messages: MessageDisplay[]) => void;
225
233
  cancelStream: () => void;
226
234
  resetSession: () => void;
227
235
  getSessionId: () => string | undefined;
package/dist/index.d.ts CHANGED
@@ -144,6 +144,8 @@ type ChatConfig = {
144
144
  api: APIConfig;
145
145
  /** Workflow name */
146
146
  workflowName: string;
147
+ /** User ID for chatStore and activeStreamStore — required for context store messages management. Set to undefined on logout to clear stored messages. */
148
+ userId?: string;
147
149
  /** Workflow version */
148
150
  workflowVersion?: number;
149
151
  /** Stage/Environment */
@@ -194,6 +196,10 @@ type ChatConfig = {
194
196
  isChatDisabled?: boolean;
195
197
  /** Custom component to render when chat is disabled. If not provided, default disabled UI will be shown */
196
198
  disabledComponent?: React.ReactNode;
199
+ /** Pre-populate the chat with messages (e.g. loaded conversation history) */
200
+ initialMessages?: MessageDisplay[];
201
+ /** Resume an existing session by providing its ID */
202
+ initialSessionId?: string;
197
203
  };
198
204
  type ChatCallbacks = {
199
205
  /** Called when a message is sent (before API call) */
@@ -222,6 +228,8 @@ type UseChatReturn = {
222
228
  messages: MessageDisplay[];
223
229
  sendMessage: (userMessage: string) => Promise<void>;
224
230
  clearMessages: () => void;
231
+ /** Prepend older messages to the top of the list (e.g. loaded from history pagination) */
232
+ prependMessages: (messages: MessageDisplay[]) => void;
225
233
  cancelStream: () => void;
226
234
  resetSession: () => void;
227
235
  getSessionId: () => string | undefined;
package/dist/index.js CHANGED
@@ -614,6 +614,84 @@ async function cancelUserAction(config, userActionId) {
614
614
  async function resendUserAction(config, userActionId) {
615
615
  return sendUserActionRequest(config, userActionId, "resend");
616
616
  }
617
+
618
+ // src/utils/chatStore.ts
619
+ var memoryStore = /* @__PURE__ */ new Map();
620
+ var chatStore = {
621
+ get(key) {
622
+ return memoryStore.get(key) ?? [];
623
+ },
624
+ set(key, messages) {
625
+ memoryStore.set(key, messages);
626
+ },
627
+ delete(key) {
628
+ memoryStore.delete(key);
629
+ }
630
+ };
631
+
632
+ // src/utils/activeStreamStore.ts
633
+ var streams = /* @__PURE__ */ new Map();
634
+ var activeStreamStore = {
635
+ has(key) {
636
+ return streams.has(key);
637
+ },
638
+ get(key) {
639
+ const entry = streams.get(key);
640
+ if (!entry) return null;
641
+ return { messages: entry.messages, isWaiting: entry.isWaiting };
642
+ },
643
+ // Called before startStream — registers the controller and initial messages
644
+ start(key, abortController, initialMessages) {
645
+ const existing = streams.get(key);
646
+ streams.set(key, {
647
+ messages: initialMessages,
648
+ isWaiting: true,
649
+ abortController,
650
+ listeners: existing?.listeners ?? /* @__PURE__ */ new Set()
651
+ });
652
+ },
653
+ // Called by the stream on every event — applies the same updater pattern React uses
654
+ applyMessages(key, updater) {
655
+ const entry = streams.get(key);
656
+ if (!entry) return;
657
+ const next = typeof updater === "function" ? updater(entry.messages) : updater;
658
+ entry.messages = next;
659
+ entry.listeners.forEach((l) => l(next, entry.isWaiting));
660
+ },
661
+ setWaiting(key, waiting) {
662
+ const entry = streams.get(key);
663
+ if (!entry) return;
664
+ entry.isWaiting = waiting;
665
+ entry.listeners.forEach((l) => l(entry.messages, waiting));
666
+ },
667
+ // Called when stream completes — persists to chatStore and cleans up
668
+ complete(key) {
669
+ const entry = streams.get(key);
670
+ if (!entry) return;
671
+ entry.isWaiting = false;
672
+ entry.listeners.forEach((l) => l(entry.messages, false));
673
+ const toSave = entry.messages.filter((m) => !m.isStreaming);
674
+ if (toSave.length > 0) chatStore.set(key, toSave);
675
+ streams.delete(key);
676
+ },
677
+ // Subscribe — returns unsubscribe fn. Component calls this on mount, cleanup on unmount.
678
+ subscribe(key, listener) {
679
+ const entry = streams.get(key);
680
+ if (!entry) return () => {
681
+ };
682
+ entry.listeners.add(listener);
683
+ return () => {
684
+ streams.get(key)?.listeners.delete(listener);
685
+ };
686
+ },
687
+ // Explicit user cancel — aborts the controller and removes the entry
688
+ abort(key) {
689
+ const entry = streams.get(key);
690
+ if (!entry) return;
691
+ entry.abortController.abort();
692
+ streams.delete(key);
693
+ }
694
+ };
617
695
  function useStreamManager(config, callbacks, setMessages, setIsWaitingForResponse) {
618
696
  const abortControllerRef = react.useRef(null);
619
697
  const configRef = react.useRef(config);
@@ -621,9 +699,9 @@ function useStreamManager(config, callbacks, setMessages, setIsWaitingForRespons
621
699
  const callbacksRef = react.useRef(callbacks);
622
700
  callbacksRef.current = callbacks;
623
701
  const startStream = react.useCallback(
624
- async (userMessage, streamingId, sessionId) => {
702
+ async (userMessage, streamingId, sessionId, externalAbortController) => {
625
703
  abortControllerRef.current?.abort();
626
- const abortController = new AbortController();
704
+ const abortController = externalAbortController ?? new AbortController();
627
705
  abortControllerRef.current = abortController;
628
706
  const state = {
629
707
  accumulatedContent: "",
@@ -827,13 +905,43 @@ function useStreamManager(config, callbacks, setMessages, setIsWaitingForRespons
827
905
 
828
906
  // src/hooks/useChat.ts
829
907
  function useChat(config, callbacks = {}) {
830
- const [messages, setMessages] = react.useState([]);
908
+ const [messages, setMessages] = react.useState(() => {
909
+ if (config.userId) return chatStore.get(config.userId);
910
+ return config.initialMessages ?? [];
911
+ });
831
912
  const [isWaitingForResponse, setIsWaitingForResponse] = react.useState(false);
832
- const sessionIdRef = react.useRef(void 0);
913
+ const sessionIdRef = react.useRef(
914
+ config.userId ? chatStore.get(config.userId).find((m) => m.sessionId)?.sessionId ?? config.initialSessionId ?? void 0 : config.initialSessionId ?? void 0
915
+ );
916
+ const prevUserIdRef = react.useRef(config.userId);
833
917
  const callbacksRef = react.useRef(callbacks);
834
918
  callbacksRef.current = callbacks;
835
919
  const configRef = react.useRef(config);
836
920
  configRef.current = config;
921
+ const messagesRef = react.useRef(messages);
922
+ messagesRef.current = messages;
923
+ const storeAwareSetMessages = react.useCallback(
924
+ (updater) => {
925
+ const { userId } = configRef.current;
926
+ if (userId && activeStreamStore.has(userId)) {
927
+ activeStreamStore.applyMessages(userId, updater);
928
+ }
929
+ setMessages(updater);
930
+ },
931
+ // eslint-disable-next-line react-hooks/exhaustive-deps
932
+ []
933
+ );
934
+ const storeAwareSetIsWaiting = react.useCallback(
935
+ (waiting) => {
936
+ const { userId } = configRef.current;
937
+ if (userId && activeStreamStore.has(userId)) {
938
+ activeStreamStore.setWaiting(userId, waiting);
939
+ }
940
+ setIsWaitingForResponse(waiting);
941
+ },
942
+ // eslint-disable-next-line react-hooks/exhaustive-deps
943
+ []
944
+ );
837
945
  const [userActionState, setUserActionState] = react.useState({
838
946
  request: null,
839
947
  result: null,
@@ -876,8 +984,8 @@ function useChat(config, callbacks = {}) {
876
984
  const { startStream, cancelStream: cancelStreamManager, abortControllerRef } = useStreamManager(
877
985
  config,
878
986
  wrappedCallbacks,
879
- setMessages,
880
- setIsWaitingForResponse
987
+ storeAwareSetMessages,
988
+ storeAwareSetIsWaiting
881
989
  );
882
990
  const sendMessage = react.useCallback(
883
991
  async (userMessage) => {
@@ -914,11 +1022,21 @@ function useChat(config, callbacks = {}) {
914
1022
  currentMessage: void 0
915
1023
  };
916
1024
  setMessages((prev) => [...prev, streamingMsg]);
1025
+ const abortController = new AbortController();
1026
+ const { userId } = configRef.current;
1027
+ if (userId) {
1028
+ const initialMessages = [...messagesRef.current, userMsg, streamingMsg];
1029
+ activeStreamStore.start(userId, abortController, initialMessages);
1030
+ }
917
1031
  const newSessionId = await startStream(
918
1032
  userMessage,
919
1033
  streamingId,
920
- sessionIdRef.current
1034
+ sessionIdRef.current,
1035
+ abortController
921
1036
  );
1037
+ if (userId) {
1038
+ activeStreamStore.complete(userId);
1039
+ }
922
1040
  if (newSessionId && newSessionId !== sessionIdRef.current) {
923
1041
  sessionIdRef.current = newSessionId;
924
1042
  }
@@ -926,9 +1044,18 @@ function useChat(config, callbacks = {}) {
926
1044
  [startStream]
927
1045
  );
928
1046
  const clearMessages = react.useCallback(() => {
1047
+ if (configRef.current.userId) {
1048
+ chatStore.delete(configRef.current.userId);
1049
+ }
929
1050
  setMessages([]);
930
1051
  }, []);
1052
+ const prependMessages = react.useCallback((msgs) => {
1053
+ setMessages((prev) => [...msgs, ...prev]);
1054
+ }, []);
931
1055
  const cancelStream = react.useCallback(() => {
1056
+ if (configRef.current.userId) {
1057
+ activeStreamStore.abort(configRef.current.userId);
1058
+ }
932
1059
  cancelStreamManager();
933
1060
  setIsWaitingForResponse(false);
934
1061
  setUserActionState((prev) => ({ ...prev, request: null, result: null }));
@@ -948,6 +1075,10 @@ function useChat(config, callbacks = {}) {
948
1075
  );
949
1076
  }, [cancelStreamManager]);
950
1077
  const resetSession = react.useCallback(() => {
1078
+ if (configRef.current.userId) {
1079
+ activeStreamStore.abort(configRef.current.userId);
1080
+ chatStore.delete(configRef.current.userId);
1081
+ }
951
1082
  setMessages([]);
952
1083
  sessionIdRef.current = void 0;
953
1084
  abortControllerRef.current?.abort();
@@ -1010,10 +1141,48 @@ function useChat(config, callbacks = {}) {
1010
1141
  throw error;
1011
1142
  }
1012
1143
  }, []);
1144
+ react.useEffect(() => {
1145
+ const { userId } = config;
1146
+ if (!userId) return;
1147
+ const unsubscribe = activeStreamStore.subscribe(userId, (msgs, isWaiting) => {
1148
+ setMessages(msgs);
1149
+ setIsWaitingForResponse(isWaiting);
1150
+ });
1151
+ const active = activeStreamStore.get(userId);
1152
+ if (active) {
1153
+ setMessages(active.messages);
1154
+ setIsWaitingForResponse(active.isWaiting);
1155
+ }
1156
+ return unsubscribe;
1157
+ }, []);
1158
+ react.useEffect(() => {
1159
+ if (!config.userId) return;
1160
+ const toSave = messages.filter((m) => !m.isStreaming);
1161
+ if (toSave.length > 0) {
1162
+ chatStore.set(config.userId, toSave);
1163
+ }
1164
+ }, [messages, config.userId]);
1165
+ react.useEffect(() => {
1166
+ const prevUserId = prevUserIdRef.current;
1167
+ prevUserIdRef.current = config.userId;
1168
+ if (prevUserId === config.userId) return;
1169
+ if (prevUserId && !config.userId) {
1170
+ chatStore.delete(prevUserId);
1171
+ setMessages([]);
1172
+ sessionIdRef.current = void 0;
1173
+ setIsWaitingForResponse(false);
1174
+ setUserActionState({ request: null, result: null, clearOtpTrigger: 0 });
1175
+ } else if (config.userId) {
1176
+ const stored = chatStore.get(config.userId);
1177
+ setMessages(stored);
1178
+ sessionIdRef.current = stored.find((m) => m.sessionId)?.sessionId;
1179
+ }
1180
+ }, [config.userId]);
1013
1181
  return {
1014
1182
  messages,
1015
1183
  sendMessage,
1016
1184
  clearMessages,
1185
+ prependMessages,
1017
1186
  cancelStream,
1018
1187
  resetSession,
1019
1188
  getSessionId,