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

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
@@ -162,6 +162,8 @@ function getEventMessage(event) {
162
162
  return event.workerName ? `${event.workerName} thinking...` : "Thinking...";
163
163
  case "INTENT_THINKING_CONT":
164
164
  return event.message || "";
165
+ case "KEEP_ALIVE":
166
+ return "";
165
167
  default:
166
168
  return eventType;
167
169
  }
@@ -198,6 +200,9 @@ function completeLastInProgressStep(steps) {
198
200
  }
199
201
  function processStreamEvent(event, state) {
200
202
  const eventType = event.eventType;
203
+ if (typeof eventType === "string" && eventType.toUpperCase() === "KEEP_ALIVE") {
204
+ return state;
205
+ }
201
206
  const message = getEventMessage(event);
202
207
  if (eventType !== "INTENT_THINKING" && eventType !== "INTENT_THINKING_CONT") {
203
208
  if (state.currentThinkingStepId) {
@@ -614,6 +619,84 @@ async function cancelUserAction(config, userActionId) {
614
619
  async function resendUserAction(config, userActionId) {
615
620
  return sendUserActionRequest(config, userActionId, "resend");
616
621
  }
622
+
623
+ // src/utils/chatStore.ts
624
+ var memoryStore = /* @__PURE__ */ new Map();
625
+ var chatStore = {
626
+ get(key) {
627
+ return memoryStore.get(key) ?? [];
628
+ },
629
+ set(key, messages) {
630
+ memoryStore.set(key, messages);
631
+ },
632
+ delete(key) {
633
+ memoryStore.delete(key);
634
+ }
635
+ };
636
+
637
+ // src/utils/activeStreamStore.ts
638
+ var streams = /* @__PURE__ */ new Map();
639
+ var activeStreamStore = {
640
+ has(key) {
641
+ return streams.has(key);
642
+ },
643
+ get(key) {
644
+ const entry = streams.get(key);
645
+ if (!entry) return null;
646
+ return { messages: entry.messages, isWaiting: entry.isWaiting };
647
+ },
648
+ // Called before startStream — registers the controller and initial messages
649
+ start(key, abortController, initialMessages) {
650
+ const existing = streams.get(key);
651
+ streams.set(key, {
652
+ messages: initialMessages,
653
+ isWaiting: true,
654
+ abortController,
655
+ listeners: existing?.listeners ?? /* @__PURE__ */ new Set()
656
+ });
657
+ },
658
+ // Called by the stream on every event — applies the same updater pattern React uses
659
+ applyMessages(key, updater) {
660
+ const entry = streams.get(key);
661
+ if (!entry) return;
662
+ const next = typeof updater === "function" ? updater(entry.messages) : updater;
663
+ entry.messages = next;
664
+ entry.listeners.forEach((l) => l(next, entry.isWaiting));
665
+ },
666
+ setWaiting(key, waiting) {
667
+ const entry = streams.get(key);
668
+ if (!entry) return;
669
+ entry.isWaiting = waiting;
670
+ entry.listeners.forEach((l) => l(entry.messages, waiting));
671
+ },
672
+ // Called when stream completes — persists to chatStore and cleans up
673
+ complete(key) {
674
+ const entry = streams.get(key);
675
+ if (!entry) return;
676
+ entry.isWaiting = false;
677
+ entry.listeners.forEach((l) => l(entry.messages, false));
678
+ const toSave = entry.messages.filter((m) => !m.isStreaming);
679
+ if (toSave.length > 0) chatStore.set(key, toSave);
680
+ streams.delete(key);
681
+ },
682
+ // Subscribe — returns unsubscribe fn. Component calls this on mount, cleanup on unmount.
683
+ subscribe(key, listener) {
684
+ const entry = streams.get(key);
685
+ if (!entry) return () => {
686
+ };
687
+ entry.listeners.add(listener);
688
+ return () => {
689
+ streams.get(key)?.listeners.delete(listener);
690
+ };
691
+ },
692
+ // Explicit user cancel — aborts the controller and removes the entry
693
+ abort(key) {
694
+ const entry = streams.get(key);
695
+ if (!entry) return;
696
+ entry.abortController.abort();
697
+ streams.delete(key);
698
+ }
699
+ };
617
700
  function useStreamManager(config, callbacks, setMessages, setIsWaitingForResponse) {
618
701
  const abortControllerRef = react.useRef(null);
619
702
  const configRef = react.useRef(config);
@@ -621,9 +704,9 @@ function useStreamManager(config, callbacks, setMessages, setIsWaitingForRespons
621
704
  const callbacksRef = react.useRef(callbacks);
622
705
  callbacksRef.current = callbacks;
623
706
  const startStream = react.useCallback(
624
- async (userMessage, streamingId, sessionId) => {
707
+ async (userMessage, streamingId, sessionId, externalAbortController) => {
625
708
  abortControllerRef.current?.abort();
626
- const abortController = new AbortController();
709
+ const abortController = externalAbortController ?? new AbortController();
627
710
  abortControllerRef.current = abortController;
628
711
  const state = {
629
712
  accumulatedContent: "",
@@ -702,6 +785,11 @@ function useStreamManager(config, callbacks, setMessages, setIsWaitingForRespons
702
785
  if (abortController.signal.aborted) {
703
786
  return;
704
787
  }
788
+ if (typeof event.eventType === "string" && event.eventType.toUpperCase() === "KEEP_ALIVE") {
789
+ if (event.executionId) state.executionId = event.executionId;
790
+ if (event.sessionId) state.currentSessionId = event.sessionId;
791
+ return;
792
+ }
705
793
  if (event.executionId) state.executionId = event.executionId;
706
794
  if (event.sessionId) state.currentSessionId = event.sessionId;
707
795
  const activeThinkingLengthBeforeProcess = state.activeThinkingText?.length ?? 0;
@@ -827,13 +915,43 @@ function useStreamManager(config, callbacks, setMessages, setIsWaitingForRespons
827
915
 
828
916
  // src/hooks/useChat.ts
829
917
  function useChat(config, callbacks = {}) {
830
- const [messages, setMessages] = react.useState([]);
918
+ const [messages, setMessages] = react.useState(() => {
919
+ if (config.userId) return chatStore.get(config.userId);
920
+ return config.initialMessages ?? [];
921
+ });
831
922
  const [isWaitingForResponse, setIsWaitingForResponse] = react.useState(false);
832
- const sessionIdRef = react.useRef(void 0);
923
+ const sessionIdRef = react.useRef(
924
+ config.userId ? chatStore.get(config.userId).find((m) => m.sessionId)?.sessionId ?? config.initialSessionId ?? void 0 : config.initialSessionId ?? void 0
925
+ );
926
+ const prevUserIdRef = react.useRef(config.userId);
833
927
  const callbacksRef = react.useRef(callbacks);
834
928
  callbacksRef.current = callbacks;
835
929
  const configRef = react.useRef(config);
836
930
  configRef.current = config;
931
+ const messagesRef = react.useRef(messages);
932
+ messagesRef.current = messages;
933
+ const storeAwareSetMessages = react.useCallback(
934
+ (updater) => {
935
+ const { userId } = configRef.current;
936
+ if (userId && activeStreamStore.has(userId)) {
937
+ activeStreamStore.applyMessages(userId, updater);
938
+ }
939
+ setMessages(updater);
940
+ },
941
+ // eslint-disable-next-line react-hooks/exhaustive-deps
942
+ []
943
+ );
944
+ const storeAwareSetIsWaiting = react.useCallback(
945
+ (waiting) => {
946
+ const { userId } = configRef.current;
947
+ if (userId && activeStreamStore.has(userId)) {
948
+ activeStreamStore.setWaiting(userId, waiting);
949
+ }
950
+ setIsWaitingForResponse(waiting);
951
+ },
952
+ // eslint-disable-next-line react-hooks/exhaustive-deps
953
+ []
954
+ );
837
955
  const [userActionState, setUserActionState] = react.useState({
838
956
  request: null,
839
957
  result: null,
@@ -876,8 +994,8 @@ function useChat(config, callbacks = {}) {
876
994
  const { startStream, cancelStream: cancelStreamManager, abortControllerRef } = useStreamManager(
877
995
  config,
878
996
  wrappedCallbacks,
879
- setMessages,
880
- setIsWaitingForResponse
997
+ storeAwareSetMessages,
998
+ storeAwareSetIsWaiting
881
999
  );
882
1000
  const sendMessage = react.useCallback(
883
1001
  async (userMessage) => {
@@ -914,11 +1032,21 @@ function useChat(config, callbacks = {}) {
914
1032
  currentMessage: void 0
915
1033
  };
916
1034
  setMessages((prev) => [...prev, streamingMsg]);
1035
+ const abortController = new AbortController();
1036
+ const { userId } = configRef.current;
1037
+ if (userId) {
1038
+ const initialMessages = [...messagesRef.current, userMsg, streamingMsg];
1039
+ activeStreamStore.start(userId, abortController, initialMessages);
1040
+ }
917
1041
  const newSessionId = await startStream(
918
1042
  userMessage,
919
1043
  streamingId,
920
- sessionIdRef.current
1044
+ sessionIdRef.current,
1045
+ abortController
921
1046
  );
1047
+ if (userId) {
1048
+ activeStreamStore.complete(userId);
1049
+ }
922
1050
  if (newSessionId && newSessionId !== sessionIdRef.current) {
923
1051
  sessionIdRef.current = newSessionId;
924
1052
  }
@@ -926,9 +1054,18 @@ function useChat(config, callbacks = {}) {
926
1054
  [startStream]
927
1055
  );
928
1056
  const clearMessages = react.useCallback(() => {
1057
+ if (configRef.current.userId) {
1058
+ chatStore.delete(configRef.current.userId);
1059
+ }
929
1060
  setMessages([]);
930
1061
  }, []);
1062
+ const prependMessages = react.useCallback((msgs) => {
1063
+ setMessages((prev) => [...msgs, ...prev]);
1064
+ }, []);
931
1065
  const cancelStream = react.useCallback(() => {
1066
+ if (configRef.current.userId) {
1067
+ activeStreamStore.abort(configRef.current.userId);
1068
+ }
932
1069
  cancelStreamManager();
933
1070
  setIsWaitingForResponse(false);
934
1071
  setUserActionState((prev) => ({ ...prev, request: null, result: null }));
@@ -948,6 +1085,10 @@ function useChat(config, callbacks = {}) {
948
1085
  );
949
1086
  }, [cancelStreamManager]);
950
1087
  const resetSession = react.useCallback(() => {
1088
+ if (configRef.current.userId) {
1089
+ activeStreamStore.abort(configRef.current.userId);
1090
+ chatStore.delete(configRef.current.userId);
1091
+ }
951
1092
  setMessages([]);
952
1093
  sessionIdRef.current = void 0;
953
1094
  abortControllerRef.current?.abort();
@@ -1010,10 +1151,48 @@ function useChat(config, callbacks = {}) {
1010
1151
  throw error;
1011
1152
  }
1012
1153
  }, []);
1154
+ react.useEffect(() => {
1155
+ const { userId } = config;
1156
+ if (!userId) return;
1157
+ const unsubscribe = activeStreamStore.subscribe(userId, (msgs, isWaiting) => {
1158
+ setMessages(msgs);
1159
+ setIsWaitingForResponse(isWaiting);
1160
+ });
1161
+ const active = activeStreamStore.get(userId);
1162
+ if (active) {
1163
+ setMessages(active.messages);
1164
+ setIsWaitingForResponse(active.isWaiting);
1165
+ }
1166
+ return unsubscribe;
1167
+ }, []);
1168
+ react.useEffect(() => {
1169
+ if (!config.userId) return;
1170
+ const toSave = messages.filter((m) => !m.isStreaming);
1171
+ if (toSave.length > 0) {
1172
+ chatStore.set(config.userId, toSave);
1173
+ }
1174
+ }, [messages, config.userId]);
1175
+ react.useEffect(() => {
1176
+ const prevUserId = prevUserIdRef.current;
1177
+ prevUserIdRef.current = config.userId;
1178
+ if (prevUserId === config.userId) return;
1179
+ if (prevUserId && !config.userId) {
1180
+ chatStore.delete(prevUserId);
1181
+ setMessages([]);
1182
+ sessionIdRef.current = void 0;
1183
+ setIsWaitingForResponse(false);
1184
+ setUserActionState({ request: null, result: null, clearOtpTrigger: 0 });
1185
+ } else if (config.userId) {
1186
+ const stored = chatStore.get(config.userId);
1187
+ setMessages(stored);
1188
+ sessionIdRef.current = stored.find((m) => m.sessionId)?.sessionId;
1189
+ }
1190
+ }, [config.userId]);
1013
1191
  return {
1014
1192
  messages,
1015
1193
  sendMessage,
1016
1194
  clearMessages,
1195
+ prependMessages,
1017
1196
  cancelStream,
1018
1197
  resetSession,
1019
1198
  getSessionId,