@iblai/web-utils 1.7.3 → 1.8.1

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.esm.js CHANGED
@@ -782,6 +782,15 @@ const TOOLS = {
782
782
  GOOGLE_SLIDES: "google-slides",
783
783
  GOOGLE_DOCUMENT: "google-docs",
784
784
  };
785
+ // Maps WebSocket tool_call type names to human-readable UI labels
786
+ const TOOL_NAME_MAP = {
787
+ web_search_call: "Searching the web",
788
+ vector_search: "Searching knowledge base",
789
+ calculator: "Calculating",
790
+ file_reader: "Reading file",
791
+ code_executor: "Running code",
792
+ wikipedia: "Searching Wikipedia",
793
+ };
785
794
  const REQUIRED_ACTIONS_FOR_GROUPS = {
786
795
  NOTIFICATIONS: "Ibl.Notifications/Notification/action",
787
796
  };
@@ -1161,17 +1170,19 @@ async function redirectToAuthSpa(options) {
1161
1170
  logoutTimestamp: "ibl_logout_timestamp",
1162
1171
  loginTimestamp: "ibl_login_timestamp",
1163
1172
  tenantSwitching: "ibl_tenant_switching",
1164
- }, hasNonExpiredAuthToken, isOffline, preserveTokenKey, authRedirectProxy, isNativeApp, } = options;
1165
- console.log("[redirectToAuthSpa] starting redirect to auth spa", redirectTo, platformKey, logout, saveRedirect);
1173
+ }, hasNonExpiredAuthToken, isOffline, preserveTokenKey, authRedirectProxy, isNativeApp, forceRedirect = false, } = options;
1174
+ console.log("[redirectToAuthSpa] starting redirect to auth spa", redirectTo, platformKey, logout, saveRedirect, forceRedirect);
1166
1175
  // Skip if a tenant switch is already in progress
1167
- if (cookieNames.tenantSwitching &&
1176
+ if (!forceRedirect &&
1177
+ cookieNames.tenantSwitching &&
1168
1178
  document.cookie.includes(cookieNames.tenantSwitching)) {
1169
1179
  console.log("[redirectToAuthSpa] Tenant switch in progress, skipping redirect");
1170
1180
  return;
1171
1181
  }
1172
1182
  // Skip if a login occurred after the last logout (login takes precedence)
1173
1183
  // but only if this app actually has a valid auth token
1174
- if (hasNonExpiredAuthToken &&
1184
+ if (!forceRedirect &&
1185
+ hasNonExpiredAuthToken &&
1175
1186
  cookieNames.loginTimestamp &&
1176
1187
  cookieNames.logoutTimestamp) {
1177
1188
  const loginTs = getCookieValue(cookieNames.loginTimestamp);
@@ -1183,7 +1194,8 @@ async function redirectToAuthSpa(options) {
1183
1194
  hasValidToken,
1184
1195
  loginAhead: loginTs && logoutTs ? Number(loginTs) > Number(logoutTs) : false,
1185
1196
  });
1186
- if (hasValidToken &&
1197
+ if (!forceRedirect &&
1198
+ hasValidToken &&
1187
1199
  loginTs &&
1188
1200
  logoutTs &&
1189
1201
  Number(loginTs) > Number(logoutTs)) {
@@ -10732,41 +10744,41 @@ async function initServiceWorker(basePath = "") {
10732
10744
  return getServiceWorkerStatus();
10733
10745
  }
10734
10746
 
10735
- const CHECK_NETWORK_STATUS_COMMAND = 'check_network_status';
10747
+ const CHECK_NETWORK_STATUS_COMMAND = "check_network_status";
10736
10748
  // CRITICAL: Eagerly import Tauri API to bundle it inline instead of lazy-loading
10737
10749
  // webpackMode: "eager" prevents creating a separate chunk (7427.js)
10738
10750
  // This ensures the code is in the main bundle and always available offline
10739
10751
  let tauriInvoke = null;
10740
- if (typeof window !== 'undefined' && isTauri()) {
10752
+ if (typeof window !== "undefined" && isTauri()) {
10741
10753
  import(/* webpackMode: "eager" */ '@tauri-apps/api/core')
10742
10754
  .then((tauriCore) => {
10743
10755
  tauriInvoke = tauriCore.invoke;
10744
- console.log('[ServiceWorkerProvider] Tauri API loaded eagerly (bundled inline)');
10756
+ console.log("[ServiceWorkerProvider] Tauri API loaded eagerly (bundled inline)");
10745
10757
  })
10746
10758
  .catch((e) => {
10747
- console.warn('[ServiceWorkerProvider] Failed to load Tauri API:', e);
10759
+ console.warn("[ServiceWorkerProvider] Failed to load Tauri API:", e);
10748
10760
  });
10749
10761
  }
10750
10762
  // localStorage key set by the Tauri offline shell
10751
- const TAURI_OFFLINE_MODE_KEY = 'tauri_offline_mode';
10763
+ const TAURI_OFFLINE_MODE_KEY = "tauri_offline_mode";
10752
10764
  /**
10753
10765
  * Check if the shell has marked us as offline
10754
10766
  */
10755
10767
  function isShellOfflineMode() {
10756
- if (typeof window === 'undefined' ||
10757
- typeof (localStorage === null || localStorage === void 0 ? void 0 : localStorage.getItem) !== 'function')
10768
+ if (typeof window === "undefined" ||
10769
+ typeof (localStorage === null || localStorage === void 0 ? void 0 : localStorage.getItem) !== "function")
10758
10770
  return false;
10759
- return localStorage.getItem(TAURI_OFFLINE_MODE_KEY) === 'true';
10771
+ return localStorage.getItem(TAURI_OFFLINE_MODE_KEY) === "true";
10760
10772
  }
10761
10773
  const ServiceWorkerContext = createContext(null);
10762
10774
  function useServiceWorker() {
10763
10775
  const context = useContext(ServiceWorkerContext);
10764
10776
  if (!context) {
10765
- throw new Error('useServiceWorker must be used within ServiceWorkerProvider');
10777
+ throw new Error("useServiceWorker must be used within ServiceWorkerProvider");
10766
10778
  }
10767
10779
  return context;
10768
10780
  }
10769
- function ServiceWorkerProvider({ children, basePath = '', }) {
10781
+ function ServiceWorkerProvider({ children, basePath = "", }) {
10770
10782
  // Get initial status, considering the shell's offline flag
10771
10783
  const [status, setStatus] = useState(() => {
10772
10784
  const baseStatus = getServiceWorkerStatus();
@@ -10783,7 +10795,7 @@ function ServiceWorkerProvider({ children, basePath = '', }) {
10783
10795
  // Skip service worker registration in Tauri offline mode
10784
10796
  // The offline server handles all requests, SW is not needed and will fail to register
10785
10797
  if (isTauriApp && shellOffline) {
10786
- console.log('[ServiceWorkerProvider] Skipping SW registration - Tauri offline mode');
10798
+ console.log("[ServiceWorkerProvider] Skipping SW registration - Tauri offline mode");
10787
10799
  setStatus({
10788
10800
  isSupported: false,
10789
10801
  isRegistered: false,
@@ -10814,11 +10826,11 @@ function ServiceWorkerProvider({ children, basePath = '', }) {
10814
10826
  });
10815
10827
  // Subscribe to updates
10816
10828
  const unsubscribeUpdate = onUpdate(() => {
10817
- toast.info('App update available', {
10818
- description: 'A new version is ready. Click to update.',
10829
+ toast.info("App update available", {
10830
+ description: "A new version is ready. Click to update.",
10819
10831
  duration: 10000,
10820
10832
  action: {
10821
- label: 'Update',
10833
+ label: "Update",
10822
10834
  onClick: () => {
10823
10835
  skipWaiting();
10824
10836
  },
@@ -10845,7 +10857,7 @@ function ServiceWorkerProvider({ children, basePath = '', }) {
10845
10857
  const isOnline = (await tauriInvoke(CHECK_NETWORK_STATUS_COMMAND));
10846
10858
  setStatus((prev) => {
10847
10859
  if (prev.isOnline !== isOnline) {
10848
- console.log('[ServiceWorkerProvider] Network status changed:', isOnline ? 'online' : 'offline');
10860
+ console.log("[ServiceWorkerProvider] Network status changed:", isOnline ? "online" : "offline");
10849
10861
  // Notify service worker of the change
10850
10862
  setOfflineStatus(!isOnline);
10851
10863
  return { ...prev, isOnline };
@@ -10858,10 +10870,10 @@ function ServiceWorkerProvider({ children, basePath = '', }) {
10858
10870
  // If we're offline and the Tauri chunk can't load, just skip network checks
10859
10871
  // The error object has a 'name' property for ChunkLoadError
10860
10872
  if (error &&
10861
- typeof error === 'object' &&
10862
- 'name' in error &&
10863
- error.name === 'ChunkLoadError') {
10864
- console.warn('[ServiceWorkerProvider] Tauri API chunk not available (offline), disabling network checks');
10873
+ typeof error === "object" &&
10874
+ "name" in error &&
10875
+ error.name === "ChunkLoadError") {
10876
+ console.warn("[ServiceWorkerProvider] Tauri API chunk not available (offline), disabling network checks");
10865
10877
  // Clear the interval to stop trying
10866
10878
  if (networkCheckInterval) {
10867
10879
  clearInterval(networkCheckInterval);
@@ -10869,7 +10881,7 @@ function ServiceWorkerProvider({ children, basePath = '', }) {
10869
10881
  }
10870
10882
  }
10871
10883
  else {
10872
- console.error('[ServiceWorkerProvider] Failed to check network status:', error);
10884
+ console.error("[ServiceWorkerProvider] Failed to check network status:", error);
10873
10885
  }
10874
10886
  }
10875
10887
  };
@@ -10879,7 +10891,7 @@ function ServiceWorkerProvider({ children, basePath = '', }) {
10879
10891
  networkCheckInterval = setInterval(checkNetworkStatus, 3000);
10880
10892
  // Also listen to browser offline/online events for immediate feedback
10881
10893
  offlineHandler = () => {
10882
- console.log('[ServiceWorkerProvider] Browser offline event fired');
10894
+ console.log("[ServiceWorkerProvider] Browser offline event fired");
10883
10895
  setStatus((prev) => {
10884
10896
  if (prev.isOnline) {
10885
10897
  setOfflineStatus(true);
@@ -10891,12 +10903,12 @@ function ServiceWorkerProvider({ children, basePath = '', }) {
10891
10903
  setTimeout(checkNetworkStatus, 500);
10892
10904
  };
10893
10905
  onlineHandler = () => {
10894
- console.log('[ServiceWorkerProvider] Browser online event fired');
10906
+ console.log("[ServiceWorkerProvider] Browser online event fired");
10895
10907
  // Verify with Tauri before marking as online
10896
10908
  checkNetworkStatus();
10897
10909
  };
10898
- window.addEventListener('offline', offlineHandler);
10899
- window.addEventListener('online', onlineHandler);
10910
+ window.addEventListener("offline", offlineHandler);
10911
+ window.addEventListener("online", onlineHandler);
10900
10912
  }
10901
10913
  return () => {
10902
10914
  unsubscribeUpdate();
@@ -10905,10 +10917,10 @@ function ServiceWorkerProvider({ children, basePath = '', }) {
10905
10917
  clearInterval(networkCheckInterval);
10906
10918
  }
10907
10919
  if (offlineHandler) {
10908
- window.removeEventListener('offline', offlineHandler);
10920
+ window.removeEventListener("offline", offlineHandler);
10909
10921
  }
10910
10922
  if (onlineHandler) {
10911
- window.removeEventListener('online', onlineHandler);
10923
+ window.removeEventListener("online", onlineHandler);
10912
10924
  }
10913
10925
  };
10914
10926
  }, [basePath]);
@@ -10917,8 +10929,8 @@ function ServiceWorkerProvider({ children, basePath = '', }) {
10917
10929
  }, []);
10918
10930
  const clearCache = useCallback(() => {
10919
10931
  clearAllCaches();
10920
- toast.success('Cache cleared', {
10921
- description: 'All cached data has been cleared.',
10932
+ toast.success("Cache cleared", {
10933
+ description: "All cached data has been cleared.",
10922
10934
  });
10923
10935
  }, []);
10924
10936
  const refreshCacheStatus = useCallback(async () => {
@@ -10950,13 +10962,13 @@ function ServiceWorkerProvider({ children, basePath = '', }) {
10950
10962
  catch (error) {
10951
10963
  // CRITICAL: Catch chunk load errors and fall back to navigator.onLine
10952
10964
  if (error &&
10953
- typeof error === 'object' &&
10954
- 'name' in error &&
10955
- error.name === 'ChunkLoadError') {
10956
- console.warn('[ServiceWorkerProvider] Tauri API chunk not available, using navigator.onLine');
10965
+ typeof error === "object" &&
10966
+ "name" in error &&
10967
+ error.name === "ChunkLoadError") {
10968
+ console.warn("[ServiceWorkerProvider] Tauri API chunk not available, using navigator.onLine");
10957
10969
  return navigator.onLine;
10958
10970
  }
10959
- console.error('[ServiceWorkerProvider] Failed to check network status:', error);
10971
+ console.error("[ServiceWorkerProvider] Failed to check network status:", error);
10960
10972
  return navigator.onLine; // Fallback to browser API
10961
10973
  }
10962
10974
  }, []);
@@ -10983,6 +10995,9 @@ const initialState$5 = {
10983
10995
  currentStreamingMessage: {
10984
10996
  id: "",
10985
10997
  content: "",
10998
+ reasoningContent: "",
10999
+ toolCalls: [],
11000
+ isReasoning: false,
10986
11001
  },
10987
11002
  currentStreamingArtifact: null,
10988
11003
  activeTab: "chat",
@@ -11006,6 +11021,9 @@ const initialState$5 = {
11006
11021
  // Buffering state initialized
11007
11022
  streamingArtifactContentBuffer: "",
11008
11023
  lastArtifactContentFlushTime: 0,
11024
+ // Reasoning buffering state initialized
11025
+ streamingReasoningContentBuffer: "",
11026
+ lastReasoningContentFlushTime: 0,
11009
11027
  metadata: {
11010
11028
  edxCourseId: "",
11011
11029
  edxUsageId: "",
@@ -11035,6 +11053,8 @@ const chatSlice = createSlice({
11035
11053
  content: state.currentStreamingMessage.content,
11036
11054
  timestamp: new Date().toISOString(),
11037
11055
  visible: true,
11056
+ reasoningContent: state.currentStreamingMessage.reasoningContent,
11057
+ toolCalls: state.currentStreamingMessage.toolCalls,
11038
11058
  };
11039
11059
  state.chats = {
11040
11060
  ...state.chats,
@@ -11045,6 +11065,8 @@ const chatSlice = createSlice({
11045
11065
  const temp = {
11046
11066
  ...lastMessageInActiveTabMessages,
11047
11067
  content: state.currentStreamingMessage.content,
11068
+ reasoningContent: state.currentStreamingMessage.reasoningContent,
11069
+ toolCalls: state.currentStreamingMessage.toolCalls,
11048
11070
  };
11049
11071
  state.chats = {
11050
11072
  ...state.chats,
@@ -11083,8 +11105,58 @@ const chatSlice = createSlice({
11083
11105
  setCurrentStreamingMessage: (state, action) => {
11084
11106
  state.currentStreamingMessage = action.payload;
11085
11107
  },
11108
+ // Partial update that only sets id/content without resetting reasoning/tool data
11109
+ setStreamingMessageIdAndContent: (state, action) => {
11110
+ state.currentStreamingMessage.id = action.payload.id;
11111
+ state.currentStreamingMessage.content = action.payload.content;
11112
+ },
11086
11113
  resetCurrentStreamingMessage: (state) => {
11087
- state.currentStreamingMessage = { id: "", content: "" };
11114
+ state.currentStreamingMessage = {
11115
+ id: "",
11116
+ content: "",
11117
+ reasoningContent: "",
11118
+ toolCalls: [],
11119
+ isReasoning: false,
11120
+ };
11121
+ // Reset reasoning buffer as well
11122
+ state.streamingReasoningContentBuffer = "";
11123
+ state.lastReasoningContentFlushTime = 0;
11124
+ },
11125
+ // Buffer reasoning tokens, flush at threshold
11126
+ appendReasoningContent: (state, action) => {
11127
+ // Add to buffer
11128
+ state.streamingReasoningContentBuffer += action.payload;
11129
+ // Check if we should flush (threshold reached)
11130
+ const shouldFlush = state.streamingReasoningContentBuffer.length >=
11131
+ STREAMING_CONTENT_BUFFER_THRESHOLD;
11132
+ if (shouldFlush) {
11133
+ // Flush buffer to actual content
11134
+ state.currentStreamingMessage.reasoningContent +=
11135
+ state.streamingReasoningContentBuffer;
11136
+ state.streamingReasoningContentBuffer = "";
11137
+ state.lastReasoningContentFlushTime = Date.now();
11138
+ }
11139
+ },
11140
+ // Force-flush buffered reasoning to currentStreamingMessage
11141
+ flushReasoningBuffer: (state, _action) => {
11142
+ if (state.streamingReasoningContentBuffer.length > 0) {
11143
+ state.currentStreamingMessage.reasoningContent +=
11144
+ state.streamingReasoningContentBuffer;
11145
+ state.streamingReasoningContentBuffer = "";
11146
+ state.lastReasoningContentFlushTime = Date.now();
11147
+ }
11148
+ },
11149
+ // Set isReasoning flag without touching other fields
11150
+ setIsReasoning: (state, action) => {
11151
+ state.currentStreamingMessage.isReasoning = action.payload;
11152
+ },
11153
+ // Sets isReasoning = false (triggers UI collapse)
11154
+ setReasoningComplete: (state, _action) => {
11155
+ state.currentStreamingMessage.isReasoning = false;
11156
+ },
11157
+ // Appends tool call entry
11158
+ addToolCall: (state, action) => {
11159
+ state.currentStreamingMessage.toolCalls.push(action.payload);
11088
11160
  },
11089
11161
  updateStreamingMessageId: (state, action) => {
11090
11162
  const oldId = state.currentStreamingMessage.id;
@@ -11242,11 +11314,15 @@ const chatSlice = createSlice({
11242
11314
  }
11243
11315
  const activeMessages = state.chats[state.activeTab];
11244
11316
  const lastMessage = activeMessages[activeMessages.length - 1];
11317
+ const toolCalls = state.currentStreamingMessage.toolCalls || [];
11318
+ const reasoningContent = state.currentStreamingMessage.reasoningContent || "";
11245
11319
  if (!lastMessage || lastMessage.id !== streamingId) {
11246
11320
  const temp = {
11247
11321
  id: streamingId,
11248
11322
  role: "assistant",
11249
11323
  content: state.currentStreamingMessage.content,
11324
+ reasoningContent,
11325
+ toolCalls,
11250
11326
  timestamp: new Date().toISOString(),
11251
11327
  visible: true,
11252
11328
  };
@@ -11255,6 +11331,19 @@ const chatSlice = createSlice({
11255
11331
  [state.activeTab]: [...activeMessages, temp],
11256
11332
  };
11257
11333
  }
11334
+ else {
11335
+ const updatedMessages = [...activeMessages];
11336
+ updatedMessages[activeMessages.length - 1] = {
11337
+ ...lastMessage,
11338
+ content: state.currentStreamingMessage.content,
11339
+ reasoningContent,
11340
+ toolCalls,
11341
+ };
11342
+ state.chats = {
11343
+ ...state.chats,
11344
+ [state.activeTab]: updatedMessages,
11345
+ };
11346
+ }
11258
11347
  },
11259
11348
  upsertStreamingArtifactVersionOnLastMessage: (state, action) => {
11260
11349
  const { artifactId, title, content, fileExtension, sessionId, versionNumber = 1, username, metadata, isPartial, } = action.payload;
@@ -11457,6 +11546,16 @@ const selectStreamingArtifactFullContent = (state) => {
11457
11546
  return null;
11458
11547
  return artifact.content + buffer;
11459
11548
  };
11549
+ // NEW: Get the full streaming reasoning content including buffer
11550
+ const selectStreamingReasoningContent = (state) => {
11551
+ const reasoningContent = state.chatSliceShared.currentStreamingMessage.reasoningContent;
11552
+ const buffer = state.chatSliceShared.streamingReasoningContentBuffer;
11553
+ return reasoningContent + buffer;
11554
+ };
11555
+ // NEW: Get isReasoning flag
11556
+ const selectIsReasoning = (state) => state.chatSliceShared.currentStreamingMessage.isReasoning;
11557
+ // NEW: Get streaming tool calls
11558
+ const selectStreamingToolCalls = (state) => state.chatSliceShared.currentStreamingMessage.toolCalls;
11460
11559
 
11461
11560
  class TimeTracker {
11462
11561
  constructor(config) {
@@ -12242,6 +12341,8 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
12242
12341
  const currentStreamingMessage = useRef({
12243
12342
  id: null,
12244
12343
  content: "",
12344
+ reasoningContent: "",
12345
+ toolCalls: [],
12245
12346
  });
12246
12347
  // Track artifact state during streaming
12247
12348
  const currentArtifact = useRef(null);
@@ -12255,6 +12356,14 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
12255
12356
  lastFlushTime: 0,
12256
12357
  flushTimer: null,
12257
12358
  });
12359
+ // NEW: Content buffer for batching reasoning streaming updates
12360
+ const reasoningContentBuffer = useRef({
12361
+ content: "",
12362
+ lastFlushTime: 0,
12363
+ flushTimer: null,
12364
+ });
12365
+ // NEW: Track if we've seen reasoning tokens to detect transition to response
12366
+ const hasSeenReasoning = useRef(false);
12258
12367
  const stopGenerationSocket = useRef(null);
12259
12368
  const isInitialConnection = useRef(true);
12260
12369
  const connectionAttempts = useRef(0);
@@ -12935,6 +13044,22 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
12935
13044
  }
12936
13045
  return;
12937
13046
  }
13047
+ // NEW: Handle top-level tool_call messages (separate message type)
13048
+ if (messageData.type === "tool_call" && messageData.value) {
13049
+ const toolCallValue = messageData.value;
13050
+ // Dispatch tool call to Redux
13051
+ dispatch(chatActions.addToolCall({
13052
+ id: toolCallValue.id || "",
13053
+ name: toolCallValue.name || "",
13054
+ input: toolCallValue.tool_input,
13055
+ log: toolCallValue.log || "",
13056
+ result: toolCallValue.result || "",
13057
+ }));
13058
+ // Ensure message exists in array and update it with latest data
13059
+ dispatch(chatActions.ensureStreamingAssistantMessage(undefined));
13060
+ // Early return since tool_call messages don't have data/eos fields
13061
+ return;
13062
+ }
12938
13063
  // Handle start of new streaming session
12939
13064
  if (messageData.generation_id &&
12940
13065
  !messageData.data &&
@@ -12947,6 +13072,21 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
12947
13072
  id: messageData.generation_id,
12948
13073
  content: "",
12949
13074
  };
13075
+ // Reset Redux streaming message with all fields
13076
+ dispatch(chatActions.setCurrentStreamingMessage({
13077
+ id: messageData.generation_id,
13078
+ content: "",
13079
+ reasoningContent: "",
13080
+ toolCalls: [],
13081
+ isReasoning: false,
13082
+ }));
13083
+ // NEW: Reset reasoning refs and buffer for new generation
13084
+ hasSeenReasoning.current = false;
13085
+ reasoningContentBuffer.current = {
13086
+ content: "",
13087
+ lastFlushTime: 0,
13088
+ flushTimer: null,
13089
+ };
12950
13090
  onStreamingMessageUpdate === null || onStreamingMessageUpdate === void 0 ? void 0 : onStreamingMessageUpdate(currentStreamingMessage.current);
12951
13091
  onStatusChange("streaming");
12952
13092
  onStreamingChange(true);
@@ -12969,7 +13109,69 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
12969
13109
  // Handle streaming message content (chat text)
12970
13110
  if ((messageData === null || messageData === void 0 ? void 0 : messageData.type) !== "chat_messages" &&
12971
13111
  (messageData.data !== undefined || messageData.eos !== undefined)) {
13112
+ // NEW: Handle annotations (reasoning tokens) BEFORE processing data
13113
+ if (messageData.annotations &&
13114
+ Array.isArray(messageData.annotations)) {
13115
+ for (const annotation of messageData.annotations) {
13116
+ if (annotation.type === "reasoning" && annotation.text) {
13117
+ // Mark that we've seen reasoning
13118
+ hasSeenReasoning.current = true;
13119
+ // Set isReasoning to true without overwriting toolCalls
13120
+ dispatch(chatActions.setIsReasoning(true));
13121
+ // Ensure message exists in array immediately so reasoning section appears
13122
+ dispatch(chatActions.ensureStreamingAssistantMessage(undefined));
13123
+ // Buffer reasoning content
13124
+ reasoningContentBuffer.current.content += annotation.text;
13125
+ // Check if we should flush (threshold or timer)
13126
+ const shouldFlush = reasoningContentBuffer.current.content.length >=
13127
+ STREAMING_CONTENT_BUFFER_THRESHOLD;
13128
+ const now = Date.now();
13129
+ const timeSinceLastFlush = now - reasoningContentBuffer.current.lastFlushTime;
13130
+ if (shouldFlush ||
13131
+ timeSinceLastFlush >= STREAMING_CONTENT_FLUSH_INTERVAL) {
13132
+ // Flush buffer to Redux
13133
+ dispatch(chatActions.appendReasoningContent(reasoningContentBuffer.current.content));
13134
+ reasoningContentBuffer.current.content = "";
13135
+ reasoningContentBuffer.current.lastFlushTime = now;
13136
+ // Ensure message exists in array and update it with latest data
13137
+ dispatch(chatActions.ensureStreamingAssistantMessage(undefined));
13138
+ }
13139
+ else {
13140
+ // Set up timer to flush if not already set
13141
+ if (!reasoningContentBuffer.current.flushTimer) {
13142
+ reasoningContentBuffer.current.flushTimer = setTimeout(() => {
13143
+ if (reasoningContentBuffer.current.content.length > 0) {
13144
+ dispatch(chatActions.appendReasoningContent(reasoningContentBuffer.current.content));
13145
+ reasoningContentBuffer.current.content = "";
13146
+ reasoningContentBuffer.current.lastFlushTime =
13147
+ Date.now();
13148
+ // Ensure message exists in array and update it with latest data
13149
+ dispatch(chatActions.ensureStreamingAssistantMessage(undefined));
13150
+ }
13151
+ reasoningContentBuffer.current.flushTimer = null;
13152
+ }, STREAMING_CONTENT_FLUSH_INTERVAL);
13153
+ }
13154
+ }
13155
+ }
13156
+ }
13157
+ }
12972
13158
  const messageText = messageData.data || "";
13159
+ // NEW: Detect reasoning→response transition (first non-empty data after reasoning)
13160
+ if (messageText && hasSeenReasoning.current) {
13161
+ // Flush remaining reasoning buffer
13162
+ if (reasoningContentBuffer.current.content.length > 0) {
13163
+ dispatch(chatActions.appendReasoningContent(reasoningContentBuffer.current.content));
13164
+ reasoningContentBuffer.current.content = "";
13165
+ }
13166
+ // Flush reasoning buffer in Redux
13167
+ dispatch(chatActions.flushReasoningBuffer(undefined));
13168
+ // Mark reasoning as complete
13169
+ dispatch(chatActions.setReasoningComplete(undefined));
13170
+ // Update message in array with final reasoning state
13171
+ dispatch(chatActions.ensureStreamingAssistantMessage(undefined));
13172
+ // Reset flag so we only do this once
13173
+ hasSeenReasoning.current = false;
13174
+ }
12973
13175
  // If we have content to add, update the in-memory streaming message and Redux
12974
13176
  if (messageText && currentStreamingMessage.current) {
12975
13177
  console.log("[ws-message] Streaming content chunk:", {
@@ -12983,13 +13185,28 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
12983
13185
  content: currentStreamingMessage.current.content + messageText,
12984
13186
  };
12985
13187
  onStreamingMessageUpdate === null || onStreamingMessageUpdate === void 0 ? void 0 : onStreamingMessageUpdate(currentStreamingMessage.current);
12986
- // Update or add message in Redux store
13188
+ // Update or add message in Redux store - use ensureStreamingAssistantMessage for consistency
12987
13189
  if ((_w = currentStreamingMessage.current) === null || _w === void 0 ? void 0 : _w.id) {
12988
- // TODO: Investigate why `appendMessageToActiveTab` is consuming this action payload
12989
- dispatch(chatActions.appendMessageToActiveTab(undefined));
13190
+ dispatch(chatActions.ensureStreamingAssistantMessage(undefined));
12990
13191
  }
12991
13192
  }
12992
13193
  if (messageData.eos) {
13194
+ // NEW: Flush any remaining reasoning buffer on EOS
13195
+ if (reasoningContentBuffer.current.content.length > 0) {
13196
+ dispatch(chatActions.appendReasoningContent(reasoningContentBuffer.current.content));
13197
+ reasoningContentBuffer.current.content = "";
13198
+ }
13199
+ // Flush reasoning buffer in Redux
13200
+ dispatch(chatActions.flushReasoningBuffer(undefined));
13201
+ // Mark reasoning as complete
13202
+ dispatch(chatActions.setReasoningComplete(undefined));
13203
+ // Update message in array with final state
13204
+ dispatch(chatActions.ensureStreamingAssistantMessage(undefined));
13205
+ // Clear any pending flush timer
13206
+ if (reasoningContentBuffer.current.flushTimer) {
13207
+ clearTimeout(reasoningContentBuffer.current.flushTimer);
13208
+ reasoningContentBuffer.current.flushTimer = null;
13209
+ }
12993
13210
  // Handle end of stream
12994
13211
  const hasPendingArtifact = !!pendingArtifactVersion.current;
12995
13212
  const hasMessageContent = !!((_x = currentStreamingMessage.current) === null || _x === void 0 ? void 0 : _x.content) &&
@@ -13604,7 +13821,13 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
13604
13821
  dispatch(chatActions.setStatus(status));
13605
13822
  };
13606
13823
  const onStreamingMessageUpdate = (message) => {
13607
- dispatch(chatActions.setCurrentStreamingMessage(message));
13824
+ // Use setStreamingMessageIdAndContent to avoid resetting reasoning/tool state
13825
+ if (message.id) {
13826
+ dispatch(chatActions.setStreamingMessageIdAndContent({
13827
+ id: message.id,
13828
+ content: message.content,
13829
+ }));
13830
+ }
13608
13831
  };
13609
13832
  const { sendMessage, stopGenerating, ws, isConnected, messageQueue, resetConnection, } = useChat({
13610
13833
  wsUrl,
@@ -13686,7 +13909,7 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
13686
13909
  try {
13687
13910
  previousChats =
13688
13911
  ((_d = (_c = data === null || data === void 0 ? void 0 : data.data) === null || _c === void 0 ? void 0 : _c.results) === null || _d === void 0 ? void 0 : _d.map((result) => {
13689
- var _a, _b, _c;
13912
+ var _a, _b, _c, _d, _e, _f;
13690
13913
  const artifactVersions = (_a = result.artifact_versions) === null || _a === void 0 ? void 0 : _a.map((av) => {
13691
13914
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
13692
13915
  return ({
@@ -13725,6 +13948,30 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
13725
13948
  fileSize: file.file_size,
13726
13949
  uploadUrl: file.url,
13727
13950
  }))) || [];
13951
+ // Extract additional_kwargs - check both direct and nested paths
13952
+ const additionalKwargs = result.additional_kwargs ||
13953
+ ((_d = (_c = result.message) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d.additional_kwargs);
13954
+ // Extract reasoning content from additional_kwargs
13955
+ const reasoningSummaries = (_e = additionalKwargs === null || additionalKwargs === void 0 ? void 0 : additionalKwargs.reasoning) === null || _e === void 0 ? void 0 : _e.summary;
13956
+ const reasoningContent = Array.isArray(reasoningSummaries)
13957
+ ? reasoningSummaries
13958
+ .map((s) => s.text)
13959
+ .filter(Boolean)
13960
+ .join("\n\n")
13961
+ : undefined;
13962
+ // Extract tool calls from additional_kwargs
13963
+ const toolOutputs = additionalKwargs === null || additionalKwargs === void 0 ? void 0 : additionalKwargs.tool_outputs;
13964
+ const toolCalls = Array.isArray(toolOutputs)
13965
+ ? toolOutputs.map((tool) => {
13966
+ var _a;
13967
+ return ({
13968
+ id: tool.id || "",
13969
+ name: tool.type || "",
13970
+ log: ((_a = tool.action) === null || _a === void 0 ? void 0 : _a.query) || "",
13971
+ result: tool.status || "",
13972
+ });
13973
+ })
13974
+ : undefined;
13728
13975
  return {
13729
13976
  ...result,
13730
13977
  role: result.type === "human" ? "user" : "assistant",
@@ -13732,17 +13979,45 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
13732
13979
  id: result.id || new Date().getTime(),
13733
13980
  content: typeof result.content === "object" &&
13734
13981
  result.content.length > 0
13735
- ? (_c = result.content.find((item) => item.text)) === null || _c === void 0 ? void 0 : _c.text
13982
+ ? (_f = result.content.find((item) => item.text)) === null || _f === void 0 ? void 0 : _f.text
13736
13983
  : result.content,
13737
13984
  // Add transformed artifact versions
13738
13985
  artifactVersions: artifactVersions || undefined,
13739
13986
  fileAttachments: fileAttachments.length > 0 ? fileAttachments : undefined,
13987
+ reasoningContent: reasoningContent || undefined,
13988
+ toolCalls: toolCalls && toolCalls.length > 0 ? toolCalls : undefined,
13740
13989
  };
13741
13990
  }).reverse()) || [];
13742
13991
  }
13743
13992
  catch (error) {
13744
13993
  console.error(JSON.stringify(error));
13745
13994
  }
13995
+ // Preserve reasoningContent and toolCalls from existing messages
13996
+ // when the session API re-fetch doesn't include additional_kwargs.
13997
+ const existingMessages = chats[activeTab] || [];
13998
+ if (existingMessages.length > 0 && previousChats.length > 0) {
13999
+ const existingByContent = new Map();
14000
+ for (const m of existingMessages) {
14001
+ if (m.reasoningContent || (m.toolCalls && m.toolCalls.length > 0)) {
14002
+ existingByContent.set(m.content, m);
14003
+ }
14004
+ }
14005
+ if (existingByContent.size > 0) {
14006
+ for (const msg of previousChats) {
14007
+ const existing = existingByContent.get(msg.content);
14008
+ if (existing) {
14009
+ if (!msg.reasoningContent && existing.reasoningContent) {
14010
+ msg.reasoningContent = existing.reasoningContent;
14011
+ }
14012
+ if ((!msg.toolCalls || msg.toolCalls.length === 0) &&
14013
+ existing.toolCalls &&
14014
+ existing.toolCalls.length > 0) {
14015
+ msg.toolCalls = existing.toolCalls;
14016
+ }
14017
+ }
14018
+ }
14019
+ }
14020
+ }
13746
14021
  dispatch(chatActions.setNewMessages(previousChats));
13747
14022
  }
13748
14023
  catch (error) {
@@ -17893,5 +18168,5 @@ const checkRbacPermission = (rbacPermissions, rbacResource, enableRBAC = true) =
17893
18168
  return checkRbacPermissionInternal(rbacPermissions, rbacResource);
17894
18169
  };
17895
18170
 
17896
- export { ALPHANUMERIC_32_REGEX, ANONYMOUS_USERNAME, AuthContext, AuthContextProvider, AuthProvider, CHAT_AREA_SIZE, CacheKeys, DEFAULT_DISCLAIMER_CONTENT, LOCAL_STORAGE_KEYS, MAX_INITIAL_WEBSOCKET_CONNECTION_ATTEMPTS, MENTOR_CHAT_DOCUMENTS_EXTENSIONS, MENTOR_VISIBILITY, MENTOR_VISIBILITY_VALUES, METADATAS, MentorProvider, REQUIRED_ACTIONS_FOR_GROUPS, RemoteEvents, STREAMING_CONTENT_BUFFER_THRESHOLD, STREAMING_CONTENT_FLUSH_INTERVAL, SUBSCRIPTION_MESSAGES, SUBSCRIPTION_PACKAGES, SUBSCRIPTION_PACKAGES_V2, SUBSCRIPTION_TRIGGERS, SUBSCRIPTION_V2_TRIGGERS, ServiceWorkerProvider, SubscriptionFlow, SubscriptionFlowV2, TOOLS, TenantContext, TenantContextProvider, TenantProvider, TimeTracker, WithFormPermissions, WithPermissions, addFiles, addMessage, addProtocolToUrl, advancedTabs, advancedTabsProperties, appleRestrictionReducer, appleRestrictionSlice, chatActions, chatInputSlice, chatInputSliceActions, chatInputSliceReducer, chatInputSliceSelectors, chatSliceReducerShared, checkModelAvailable, checkOllamaHealth, checkRbacPermission, clearAllCaches, clearApiCache, clearAuthCookies, clearCookies, clearCurrentTenantCookie, clearFiles, clearMessages, combineCSVData, convertToOllamaMessages, createFileReference, createMultipleFileReferences, csvDataToText, defaultSessionIds, deleteCookie, deleteCookieOnAllDomains, enableChatActionsPopup, eventBus, fetchWithCache, filesReducer, filesSlice, formatRelativeTime, getAuthSpaJoinUrl, getCacheStatus, getCachedApiResponse, getCookieValue, getDomainParts, getFileInfo, getInitials, getLocalLLMSystemPrompt, getNextNavigation, getParentDomain, getPlatform, getPlatformKey, getServiceWorkerStatus, getStoredUserName, getTimeAgo, getUserEmail, getUserName, handleLogout, hostChatReducer, hostChatSlice, initServiceWorker, isAlphaNumeric32, isExpo, isFileAccepted, isInIframe, isJSON, isLoggedIn, isNode, isReactNative, isSafariBrowser, isServiceWorkerSupported, isStripeActivated, isTauri, isTauriOfflineMode, isWeb$2 as isWeb, loadMetadataConfig, markdownToPlainText, monetizationSlice, onStatusChange, onUpdate, parseCSV, preCacheMentorData, rbacReducer, redirectToAuthSpa, redirectToAuthSpaJoinTenant, registerServiceWorker, removeFile, requestPresignedUrl, safeRequire, selectActiveChatMessages, selectActiveTab, selectArtifactsEnabled, selectAttachedFiles, selectChats, selectCurrentStreamingArtifact, selectCurrentStreamingMessage, selectDocumentFilter, selectEnableChatActionsPopup, selectIframeContext, selectIsError, selectIsPending, selectIsStopped, selectIsTyping, selectLastArtifactContentFlushTime, selectMetadata, selectNumberOfActiveChatMessages, selectRbacPermissions, selectSessionId, selectSessionIds, selectShouldStartNewChat, selectShowingSharedChat, selectStatus, selectStreaming, selectStreamingArtifactContentBuffer, selectStreamingArtifactFullContent, selectToken, selectTokenEnabled, selectTools, sendMessageToParentWebsite, setAccessCheckResponse, setAdvancedDisplayMonetizationCheckoutModal, setCachedApiResponse, setCookieForAuth, setDisplayMonetizationCheckoutModal, setError402Detected, setFreeTrialUsageOptions, setOfflineStatus, setOpenAppleRestrictionModal, setOpenPricingModal, setPricingModalData, setSubscriptionStatus, setTauriMode, setTopBannerOptions, setupNetworkListeners, showMonetizationCheckoutModal, skipWaiting, streamOllamaChat, subscriptionReducer, subscriptionSlice, syncAuthToCookies, tenantKeySchema, tenantSchema, topBannerReducer, topBannerSlice, translatePrompt, unregisterServiceWorker, updateFileMetadata, updateFileProgress, updateFileRetryCount, updateFileStatus, updateFileUrl, updateFileUrlFromWebSocket, updateRbacPermissions, uploadToS3, use402ErrorCheck, useAccessingPublicRoute, useAdvancedChat, useAuthContext, useAuthProvider, useAxdToken, useCachedSessionId, useChat, useChatFileUpload, useCurrentTenant, useDayJs, useDmToken, useEmbedMode, useEventCallback, useEventListener, useExternalPricingPlan, useFileDragDrop, useIsAdmin, useIsomorphicLayoutEffect, useLocalStorage, useMentorSettings, useMentorTools, useModelFileUploadCapabilities, useOS, useProfileImageUpload, useResponsive, useServiceWorker, useShowAttachment, useShowFreeTrialDialog, useShowVoiceCall, useShowVoiceRecorder, useStripeUpgrade, useSubscriptionHandler, useSubscriptionHandlerV2, useTenantContext, useTenantMetadata, useTimeTracker, useTimeTrackerNative, useTimer, useUserAgreement, useUserData, useUserProfileUpdate, useUserTenants, useUsername, useVisitingTenant, useVoiceChat, useWelcome as useWelcomeMessage, userDataSchema, validateFile };
18171
+ export { ALPHANUMERIC_32_REGEX, ANONYMOUS_USERNAME, AuthContext, AuthContextProvider, AuthProvider, CHAT_AREA_SIZE, CacheKeys, DEFAULT_DISCLAIMER_CONTENT, LOCAL_STORAGE_KEYS, MAX_INITIAL_WEBSOCKET_CONNECTION_ATTEMPTS, MENTOR_CHAT_DOCUMENTS_EXTENSIONS, MENTOR_VISIBILITY, MENTOR_VISIBILITY_VALUES, METADATAS, MentorProvider, REQUIRED_ACTIONS_FOR_GROUPS, RemoteEvents, STREAMING_CONTENT_BUFFER_THRESHOLD, STREAMING_CONTENT_FLUSH_INTERVAL, SUBSCRIPTION_MESSAGES, SUBSCRIPTION_PACKAGES, SUBSCRIPTION_PACKAGES_V2, SUBSCRIPTION_TRIGGERS, SUBSCRIPTION_V2_TRIGGERS, ServiceWorkerProvider, SubscriptionFlow, SubscriptionFlowV2, TOOLS, TOOL_NAME_MAP, TenantContext, TenantContextProvider, TenantProvider, TimeTracker, WithFormPermissions, WithPermissions, addFiles, addMessage, addProtocolToUrl, advancedTabs, advancedTabsProperties, appleRestrictionReducer, appleRestrictionSlice, chatActions, chatInputSlice, chatInputSliceActions, chatInputSliceReducer, chatInputSliceSelectors, chatSliceReducerShared, checkModelAvailable, checkOllamaHealth, checkRbacPermission, clearAllCaches, clearApiCache, clearAuthCookies, clearCookies, clearCurrentTenantCookie, clearFiles, clearMessages, combineCSVData, convertToOllamaMessages, createFileReference, createMultipleFileReferences, csvDataToText, defaultSessionIds, deleteCookie, deleteCookieOnAllDomains, enableChatActionsPopup, eventBus, fetchWithCache, filesReducer, filesSlice, formatRelativeTime, getAuthSpaJoinUrl, getCacheStatus, getCachedApiResponse, getCookieValue, getDomainParts, getFileInfo, getInitials, getLocalLLMSystemPrompt, getNextNavigation, getParentDomain, getPlatform, getPlatformKey, getServiceWorkerStatus, getStoredUserName, getTimeAgo, getUserEmail, getUserName, handleLogout, hostChatReducer, hostChatSlice, initServiceWorker, isAlphaNumeric32, isExpo, isFileAccepted, isInIframe, isJSON, isLoggedIn, isNode, isReactNative, isSafariBrowser, isServiceWorkerSupported, isStripeActivated, isTauri, isTauriOfflineMode, isWeb$2 as isWeb, loadMetadataConfig, markdownToPlainText, monetizationSlice, onStatusChange, onUpdate, parseCSV, preCacheMentorData, rbacReducer, redirectToAuthSpa, redirectToAuthSpaJoinTenant, registerServiceWorker, removeFile, requestPresignedUrl, safeRequire, selectActiveChatMessages, selectActiveTab, selectArtifactsEnabled, selectAttachedFiles, selectChats, selectCurrentStreamingArtifact, selectCurrentStreamingMessage, selectDocumentFilter, selectEnableChatActionsPopup, selectIframeContext, selectIsError, selectIsPending, selectIsReasoning, selectIsStopped, selectIsTyping, selectLastArtifactContentFlushTime, selectMetadata, selectNumberOfActiveChatMessages, selectRbacPermissions, selectSessionId, selectSessionIds, selectShouldStartNewChat, selectShowingSharedChat, selectStatus, selectStreaming, selectStreamingArtifactContentBuffer, selectStreamingArtifactFullContent, selectStreamingReasoningContent, selectStreamingToolCalls, selectToken, selectTokenEnabled, selectTools, sendMessageToParentWebsite, setAccessCheckResponse, setAdvancedDisplayMonetizationCheckoutModal, setCachedApiResponse, setCookieForAuth, setDisplayMonetizationCheckoutModal, setError402Detected, setFreeTrialUsageOptions, setOfflineStatus, setOpenAppleRestrictionModal, setOpenPricingModal, setPricingModalData, setSubscriptionStatus, setTauriMode, setTopBannerOptions, setupNetworkListeners, showMonetizationCheckoutModal, skipWaiting, streamOllamaChat, subscriptionReducer, subscriptionSlice, syncAuthToCookies, tenantKeySchema, tenantSchema, topBannerReducer, topBannerSlice, translatePrompt, unregisterServiceWorker, updateFileMetadata, updateFileProgress, updateFileRetryCount, updateFileStatus, updateFileUrl, updateFileUrlFromWebSocket, updateRbacPermissions, uploadToS3, use402ErrorCheck, useAccessingPublicRoute, useAdvancedChat, useAuthContext, useAuthProvider, useAxdToken, useCachedSessionId, useChat, useChatFileUpload, useCurrentTenant, useDayJs, useDmToken, useEmbedMode, useEventCallback, useEventListener, useExternalPricingPlan, useFileDragDrop, useIsAdmin, useIsomorphicLayoutEffect, useLocalStorage, useMentorSettings, useMentorTools, useModelFileUploadCapabilities, useOS, useProfileImageUpload, useResponsive, useServiceWorker, useShowAttachment, useShowFreeTrialDialog, useShowVoiceCall, useShowVoiceRecorder, useStripeUpgrade, useSubscriptionHandler, useSubscriptionHandlerV2, useTenantContext, useTenantMetadata, useTimeTracker, useTimeTrackerNative, useTimer, useUserAgreement, useUserData, useUserProfileUpdate, useUserTenants, useUsername, useVisitingTenant, useVoiceChat, useWelcome as useWelcomeMessage, userDataSchema, validateFile };
17897
18172
  //# sourceMappingURL=index.esm.js.map