@iblai/web-utils 1.8.0 → 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.js CHANGED
@@ -802,6 +802,15 @@ const TOOLS = {
802
802
  GOOGLE_SLIDES: "google-slides",
803
803
  GOOGLE_DOCUMENT: "google-docs",
804
804
  };
805
+ // Maps WebSocket tool_call type names to human-readable UI labels
806
+ const TOOL_NAME_MAP = {
807
+ web_search_call: "Searching the web",
808
+ vector_search: "Searching knowledge base",
809
+ calculator: "Calculating",
810
+ file_reader: "Reading file",
811
+ code_executor: "Running code",
812
+ wikipedia: "Searching Wikipedia",
813
+ };
805
814
  const REQUIRED_ACTIONS_FOR_GROUPS = {
806
815
  NOTIFICATIONS: "Ibl.Notifications/Notification/action",
807
816
  };
@@ -10755,41 +10764,41 @@ async function initServiceWorker(basePath = "") {
10755
10764
  return getServiceWorkerStatus();
10756
10765
  }
10757
10766
 
10758
- const CHECK_NETWORK_STATUS_COMMAND = 'check_network_status';
10767
+ const CHECK_NETWORK_STATUS_COMMAND = "check_network_status";
10759
10768
  // CRITICAL: Eagerly import Tauri API to bundle it inline instead of lazy-loading
10760
10769
  // webpackMode: "eager" prevents creating a separate chunk (7427.js)
10761
10770
  // This ensures the code is in the main bundle and always available offline
10762
10771
  let tauriInvoke = null;
10763
- if (typeof window !== 'undefined' && isTauri()) {
10772
+ if (typeof window !== "undefined" && isTauri()) {
10764
10773
  import(/* webpackMode: "eager" */ '@tauri-apps/api/core')
10765
10774
  .then((tauriCore) => {
10766
10775
  tauriInvoke = tauriCore.invoke;
10767
- console.log('[ServiceWorkerProvider] Tauri API loaded eagerly (bundled inline)');
10776
+ console.log("[ServiceWorkerProvider] Tauri API loaded eagerly (bundled inline)");
10768
10777
  })
10769
10778
  .catch((e) => {
10770
- console.warn('[ServiceWorkerProvider] Failed to load Tauri API:', e);
10779
+ console.warn("[ServiceWorkerProvider] Failed to load Tauri API:", e);
10771
10780
  });
10772
10781
  }
10773
10782
  // localStorage key set by the Tauri offline shell
10774
- const TAURI_OFFLINE_MODE_KEY = 'tauri_offline_mode';
10783
+ const TAURI_OFFLINE_MODE_KEY = "tauri_offline_mode";
10775
10784
  /**
10776
10785
  * Check if the shell has marked us as offline
10777
10786
  */
10778
10787
  function isShellOfflineMode() {
10779
- if (typeof window === 'undefined' ||
10780
- typeof (localStorage === null || localStorage === void 0 ? void 0 : localStorage.getItem) !== 'function')
10788
+ if (typeof window === "undefined" ||
10789
+ typeof (localStorage === null || localStorage === void 0 ? void 0 : localStorage.getItem) !== "function")
10781
10790
  return false;
10782
- return localStorage.getItem(TAURI_OFFLINE_MODE_KEY) === 'true';
10791
+ return localStorage.getItem(TAURI_OFFLINE_MODE_KEY) === "true";
10783
10792
  }
10784
10793
  const ServiceWorkerContext = React.createContext(null);
10785
10794
  function useServiceWorker() {
10786
10795
  const context = React.useContext(ServiceWorkerContext);
10787
10796
  if (!context) {
10788
- throw new Error('useServiceWorker must be used within ServiceWorkerProvider');
10797
+ throw new Error("useServiceWorker must be used within ServiceWorkerProvider");
10789
10798
  }
10790
10799
  return context;
10791
10800
  }
10792
- function ServiceWorkerProvider({ children, basePath = '', }) {
10801
+ function ServiceWorkerProvider({ children, basePath = "", }) {
10793
10802
  // Get initial status, considering the shell's offline flag
10794
10803
  const [status, setStatus] = React.useState(() => {
10795
10804
  const baseStatus = getServiceWorkerStatus();
@@ -10806,7 +10815,7 @@ function ServiceWorkerProvider({ children, basePath = '', }) {
10806
10815
  // Skip service worker registration in Tauri offline mode
10807
10816
  // The offline server handles all requests, SW is not needed and will fail to register
10808
10817
  if (isTauriApp && shellOffline) {
10809
- console.log('[ServiceWorkerProvider] Skipping SW registration - Tauri offline mode');
10818
+ console.log("[ServiceWorkerProvider] Skipping SW registration - Tauri offline mode");
10810
10819
  setStatus({
10811
10820
  isSupported: false,
10812
10821
  isRegistered: false,
@@ -10837,11 +10846,11 @@ function ServiceWorkerProvider({ children, basePath = '', }) {
10837
10846
  });
10838
10847
  // Subscribe to updates
10839
10848
  const unsubscribeUpdate = onUpdate(() => {
10840
- sonner.toast.info('App update available', {
10841
- description: 'A new version is ready. Click to update.',
10849
+ sonner.toast.info("App update available", {
10850
+ description: "A new version is ready. Click to update.",
10842
10851
  duration: 10000,
10843
10852
  action: {
10844
- label: 'Update',
10853
+ label: "Update",
10845
10854
  onClick: () => {
10846
10855
  skipWaiting();
10847
10856
  },
@@ -10868,7 +10877,7 @@ function ServiceWorkerProvider({ children, basePath = '', }) {
10868
10877
  const isOnline = (await tauriInvoke(CHECK_NETWORK_STATUS_COMMAND));
10869
10878
  setStatus((prev) => {
10870
10879
  if (prev.isOnline !== isOnline) {
10871
- console.log('[ServiceWorkerProvider] Network status changed:', isOnline ? 'online' : 'offline');
10880
+ console.log("[ServiceWorkerProvider] Network status changed:", isOnline ? "online" : "offline");
10872
10881
  // Notify service worker of the change
10873
10882
  setOfflineStatus(!isOnline);
10874
10883
  return { ...prev, isOnline };
@@ -10881,10 +10890,10 @@ function ServiceWorkerProvider({ children, basePath = '', }) {
10881
10890
  // If we're offline and the Tauri chunk can't load, just skip network checks
10882
10891
  // The error object has a 'name' property for ChunkLoadError
10883
10892
  if (error &&
10884
- typeof error === 'object' &&
10885
- 'name' in error &&
10886
- error.name === 'ChunkLoadError') {
10887
- console.warn('[ServiceWorkerProvider] Tauri API chunk not available (offline), disabling network checks');
10893
+ typeof error === "object" &&
10894
+ "name" in error &&
10895
+ error.name === "ChunkLoadError") {
10896
+ console.warn("[ServiceWorkerProvider] Tauri API chunk not available (offline), disabling network checks");
10888
10897
  // Clear the interval to stop trying
10889
10898
  if (networkCheckInterval) {
10890
10899
  clearInterval(networkCheckInterval);
@@ -10892,7 +10901,7 @@ function ServiceWorkerProvider({ children, basePath = '', }) {
10892
10901
  }
10893
10902
  }
10894
10903
  else {
10895
- console.error('[ServiceWorkerProvider] Failed to check network status:', error);
10904
+ console.error("[ServiceWorkerProvider] Failed to check network status:", error);
10896
10905
  }
10897
10906
  }
10898
10907
  };
@@ -10902,7 +10911,7 @@ function ServiceWorkerProvider({ children, basePath = '', }) {
10902
10911
  networkCheckInterval = setInterval(checkNetworkStatus, 3000);
10903
10912
  // Also listen to browser offline/online events for immediate feedback
10904
10913
  offlineHandler = () => {
10905
- console.log('[ServiceWorkerProvider] Browser offline event fired');
10914
+ console.log("[ServiceWorkerProvider] Browser offline event fired");
10906
10915
  setStatus((prev) => {
10907
10916
  if (prev.isOnline) {
10908
10917
  setOfflineStatus(true);
@@ -10914,12 +10923,12 @@ function ServiceWorkerProvider({ children, basePath = '', }) {
10914
10923
  setTimeout(checkNetworkStatus, 500);
10915
10924
  };
10916
10925
  onlineHandler = () => {
10917
- console.log('[ServiceWorkerProvider] Browser online event fired');
10926
+ console.log("[ServiceWorkerProvider] Browser online event fired");
10918
10927
  // Verify with Tauri before marking as online
10919
10928
  checkNetworkStatus();
10920
10929
  };
10921
- window.addEventListener('offline', offlineHandler);
10922
- window.addEventListener('online', onlineHandler);
10930
+ window.addEventListener("offline", offlineHandler);
10931
+ window.addEventListener("online", onlineHandler);
10923
10932
  }
10924
10933
  return () => {
10925
10934
  unsubscribeUpdate();
@@ -10928,10 +10937,10 @@ function ServiceWorkerProvider({ children, basePath = '', }) {
10928
10937
  clearInterval(networkCheckInterval);
10929
10938
  }
10930
10939
  if (offlineHandler) {
10931
- window.removeEventListener('offline', offlineHandler);
10940
+ window.removeEventListener("offline", offlineHandler);
10932
10941
  }
10933
10942
  if (onlineHandler) {
10934
- window.removeEventListener('online', onlineHandler);
10943
+ window.removeEventListener("online", onlineHandler);
10935
10944
  }
10936
10945
  };
10937
10946
  }, [basePath]);
@@ -10940,8 +10949,8 @@ function ServiceWorkerProvider({ children, basePath = '', }) {
10940
10949
  }, []);
10941
10950
  const clearCache = React.useCallback(() => {
10942
10951
  clearAllCaches();
10943
- sonner.toast.success('Cache cleared', {
10944
- description: 'All cached data has been cleared.',
10952
+ sonner.toast.success("Cache cleared", {
10953
+ description: "All cached data has been cleared.",
10945
10954
  });
10946
10955
  }, []);
10947
10956
  const refreshCacheStatus = React.useCallback(async () => {
@@ -10973,13 +10982,13 @@ function ServiceWorkerProvider({ children, basePath = '', }) {
10973
10982
  catch (error) {
10974
10983
  // CRITICAL: Catch chunk load errors and fall back to navigator.onLine
10975
10984
  if (error &&
10976
- typeof error === 'object' &&
10977
- 'name' in error &&
10978
- error.name === 'ChunkLoadError') {
10979
- console.warn('[ServiceWorkerProvider] Tauri API chunk not available, using navigator.onLine');
10985
+ typeof error === "object" &&
10986
+ "name" in error &&
10987
+ error.name === "ChunkLoadError") {
10988
+ console.warn("[ServiceWorkerProvider] Tauri API chunk not available, using navigator.onLine");
10980
10989
  return navigator.onLine;
10981
10990
  }
10982
- console.error('[ServiceWorkerProvider] Failed to check network status:', error);
10991
+ console.error("[ServiceWorkerProvider] Failed to check network status:", error);
10983
10992
  return navigator.onLine; // Fallback to browser API
10984
10993
  }
10985
10994
  }, []);
@@ -11006,6 +11015,9 @@ const initialState$5 = {
11006
11015
  currentStreamingMessage: {
11007
11016
  id: "",
11008
11017
  content: "",
11018
+ reasoningContent: "",
11019
+ toolCalls: [],
11020
+ isReasoning: false,
11009
11021
  },
11010
11022
  currentStreamingArtifact: null,
11011
11023
  activeTab: "chat",
@@ -11029,6 +11041,9 @@ const initialState$5 = {
11029
11041
  // Buffering state initialized
11030
11042
  streamingArtifactContentBuffer: "",
11031
11043
  lastArtifactContentFlushTime: 0,
11044
+ // Reasoning buffering state initialized
11045
+ streamingReasoningContentBuffer: "",
11046
+ lastReasoningContentFlushTime: 0,
11032
11047
  metadata: {
11033
11048
  edxCourseId: "",
11034
11049
  edxUsageId: "",
@@ -11058,6 +11073,8 @@ const chatSlice = createSlice({
11058
11073
  content: state.currentStreamingMessage.content,
11059
11074
  timestamp: new Date().toISOString(),
11060
11075
  visible: true,
11076
+ reasoningContent: state.currentStreamingMessage.reasoningContent,
11077
+ toolCalls: state.currentStreamingMessage.toolCalls,
11061
11078
  };
11062
11079
  state.chats = {
11063
11080
  ...state.chats,
@@ -11068,6 +11085,8 @@ const chatSlice = createSlice({
11068
11085
  const temp = {
11069
11086
  ...lastMessageInActiveTabMessages,
11070
11087
  content: state.currentStreamingMessage.content,
11088
+ reasoningContent: state.currentStreamingMessage.reasoningContent,
11089
+ toolCalls: state.currentStreamingMessage.toolCalls,
11071
11090
  };
11072
11091
  state.chats = {
11073
11092
  ...state.chats,
@@ -11106,8 +11125,58 @@ const chatSlice = createSlice({
11106
11125
  setCurrentStreamingMessage: (state, action) => {
11107
11126
  state.currentStreamingMessage = action.payload;
11108
11127
  },
11128
+ // Partial update that only sets id/content without resetting reasoning/tool data
11129
+ setStreamingMessageIdAndContent: (state, action) => {
11130
+ state.currentStreamingMessage.id = action.payload.id;
11131
+ state.currentStreamingMessage.content = action.payload.content;
11132
+ },
11109
11133
  resetCurrentStreamingMessage: (state) => {
11110
- state.currentStreamingMessage = { id: "", content: "" };
11134
+ state.currentStreamingMessage = {
11135
+ id: "",
11136
+ content: "",
11137
+ reasoningContent: "",
11138
+ toolCalls: [],
11139
+ isReasoning: false,
11140
+ };
11141
+ // Reset reasoning buffer as well
11142
+ state.streamingReasoningContentBuffer = "";
11143
+ state.lastReasoningContentFlushTime = 0;
11144
+ },
11145
+ // Buffer reasoning tokens, flush at threshold
11146
+ appendReasoningContent: (state, action) => {
11147
+ // Add to buffer
11148
+ state.streamingReasoningContentBuffer += action.payload;
11149
+ // Check if we should flush (threshold reached)
11150
+ const shouldFlush = state.streamingReasoningContentBuffer.length >=
11151
+ STREAMING_CONTENT_BUFFER_THRESHOLD;
11152
+ if (shouldFlush) {
11153
+ // Flush buffer to actual content
11154
+ state.currentStreamingMessage.reasoningContent +=
11155
+ state.streamingReasoningContentBuffer;
11156
+ state.streamingReasoningContentBuffer = "";
11157
+ state.lastReasoningContentFlushTime = Date.now();
11158
+ }
11159
+ },
11160
+ // Force-flush buffered reasoning to currentStreamingMessage
11161
+ flushReasoningBuffer: (state, _action) => {
11162
+ if (state.streamingReasoningContentBuffer.length > 0) {
11163
+ state.currentStreamingMessage.reasoningContent +=
11164
+ state.streamingReasoningContentBuffer;
11165
+ state.streamingReasoningContentBuffer = "";
11166
+ state.lastReasoningContentFlushTime = Date.now();
11167
+ }
11168
+ },
11169
+ // Set isReasoning flag without touching other fields
11170
+ setIsReasoning: (state, action) => {
11171
+ state.currentStreamingMessage.isReasoning = action.payload;
11172
+ },
11173
+ // Sets isReasoning = false (triggers UI collapse)
11174
+ setReasoningComplete: (state, _action) => {
11175
+ state.currentStreamingMessage.isReasoning = false;
11176
+ },
11177
+ // Appends tool call entry
11178
+ addToolCall: (state, action) => {
11179
+ state.currentStreamingMessage.toolCalls.push(action.payload);
11111
11180
  },
11112
11181
  updateStreamingMessageId: (state, action) => {
11113
11182
  const oldId = state.currentStreamingMessage.id;
@@ -11265,11 +11334,15 @@ const chatSlice = createSlice({
11265
11334
  }
11266
11335
  const activeMessages = state.chats[state.activeTab];
11267
11336
  const lastMessage = activeMessages[activeMessages.length - 1];
11337
+ const toolCalls = state.currentStreamingMessage.toolCalls || [];
11338
+ const reasoningContent = state.currentStreamingMessage.reasoningContent || "";
11268
11339
  if (!lastMessage || lastMessage.id !== streamingId) {
11269
11340
  const temp = {
11270
11341
  id: streamingId,
11271
11342
  role: "assistant",
11272
11343
  content: state.currentStreamingMessage.content,
11344
+ reasoningContent,
11345
+ toolCalls,
11273
11346
  timestamp: new Date().toISOString(),
11274
11347
  visible: true,
11275
11348
  };
@@ -11278,6 +11351,19 @@ const chatSlice = createSlice({
11278
11351
  [state.activeTab]: [...activeMessages, temp],
11279
11352
  };
11280
11353
  }
11354
+ else {
11355
+ const updatedMessages = [...activeMessages];
11356
+ updatedMessages[activeMessages.length - 1] = {
11357
+ ...lastMessage,
11358
+ content: state.currentStreamingMessage.content,
11359
+ reasoningContent,
11360
+ toolCalls,
11361
+ };
11362
+ state.chats = {
11363
+ ...state.chats,
11364
+ [state.activeTab]: updatedMessages,
11365
+ };
11366
+ }
11281
11367
  },
11282
11368
  upsertStreamingArtifactVersionOnLastMessage: (state, action) => {
11283
11369
  const { artifactId, title, content, fileExtension, sessionId, versionNumber = 1, username, metadata, isPartial, } = action.payload;
@@ -11480,6 +11566,16 @@ const selectStreamingArtifactFullContent = (state) => {
11480
11566
  return null;
11481
11567
  return artifact.content + buffer;
11482
11568
  };
11569
+ // NEW: Get the full streaming reasoning content including buffer
11570
+ const selectStreamingReasoningContent = (state) => {
11571
+ const reasoningContent = state.chatSliceShared.currentStreamingMessage.reasoningContent;
11572
+ const buffer = state.chatSliceShared.streamingReasoningContentBuffer;
11573
+ return reasoningContent + buffer;
11574
+ };
11575
+ // NEW: Get isReasoning flag
11576
+ const selectIsReasoning = (state) => state.chatSliceShared.currentStreamingMessage.isReasoning;
11577
+ // NEW: Get streaming tool calls
11578
+ const selectStreamingToolCalls = (state) => state.chatSliceShared.currentStreamingMessage.toolCalls;
11483
11579
 
11484
11580
  class TimeTracker {
11485
11581
  constructor(config) {
@@ -12265,6 +12361,8 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
12265
12361
  const currentStreamingMessage = React.useRef({
12266
12362
  id: null,
12267
12363
  content: "",
12364
+ reasoningContent: "",
12365
+ toolCalls: [],
12268
12366
  });
12269
12367
  // Track artifact state during streaming
12270
12368
  const currentArtifact = React.useRef(null);
@@ -12278,6 +12376,14 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
12278
12376
  lastFlushTime: 0,
12279
12377
  flushTimer: null,
12280
12378
  });
12379
+ // NEW: Content buffer for batching reasoning streaming updates
12380
+ const reasoningContentBuffer = React.useRef({
12381
+ content: "",
12382
+ lastFlushTime: 0,
12383
+ flushTimer: null,
12384
+ });
12385
+ // NEW: Track if we've seen reasoning tokens to detect transition to response
12386
+ const hasSeenReasoning = React.useRef(false);
12281
12387
  const stopGenerationSocket = React.useRef(null);
12282
12388
  const isInitialConnection = React.useRef(true);
12283
12389
  const connectionAttempts = React.useRef(0);
@@ -12958,6 +13064,22 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
12958
13064
  }
12959
13065
  return;
12960
13066
  }
13067
+ // NEW: Handle top-level tool_call messages (separate message type)
13068
+ if (messageData.type === "tool_call" && messageData.value) {
13069
+ const toolCallValue = messageData.value;
13070
+ // Dispatch tool call to Redux
13071
+ dispatch(chatActions.addToolCall({
13072
+ id: toolCallValue.id || "",
13073
+ name: toolCallValue.name || "",
13074
+ input: toolCallValue.tool_input,
13075
+ log: toolCallValue.log || "",
13076
+ result: toolCallValue.result || "",
13077
+ }));
13078
+ // Ensure message exists in array and update it with latest data
13079
+ dispatch(chatActions.ensureStreamingAssistantMessage(undefined));
13080
+ // Early return since tool_call messages don't have data/eos fields
13081
+ return;
13082
+ }
12961
13083
  // Handle start of new streaming session
12962
13084
  if (messageData.generation_id &&
12963
13085
  !messageData.data &&
@@ -12970,6 +13092,21 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
12970
13092
  id: messageData.generation_id,
12971
13093
  content: "",
12972
13094
  };
13095
+ // Reset Redux streaming message with all fields
13096
+ dispatch(chatActions.setCurrentStreamingMessage({
13097
+ id: messageData.generation_id,
13098
+ content: "",
13099
+ reasoningContent: "",
13100
+ toolCalls: [],
13101
+ isReasoning: false,
13102
+ }));
13103
+ // NEW: Reset reasoning refs and buffer for new generation
13104
+ hasSeenReasoning.current = false;
13105
+ reasoningContentBuffer.current = {
13106
+ content: "",
13107
+ lastFlushTime: 0,
13108
+ flushTimer: null,
13109
+ };
12973
13110
  onStreamingMessageUpdate === null || onStreamingMessageUpdate === void 0 ? void 0 : onStreamingMessageUpdate(currentStreamingMessage.current);
12974
13111
  onStatusChange("streaming");
12975
13112
  onStreamingChange(true);
@@ -12992,7 +13129,69 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
12992
13129
  // Handle streaming message content (chat text)
12993
13130
  if ((messageData === null || messageData === void 0 ? void 0 : messageData.type) !== "chat_messages" &&
12994
13131
  (messageData.data !== undefined || messageData.eos !== undefined)) {
13132
+ // NEW: Handle annotations (reasoning tokens) BEFORE processing data
13133
+ if (messageData.annotations &&
13134
+ Array.isArray(messageData.annotations)) {
13135
+ for (const annotation of messageData.annotations) {
13136
+ if (annotation.type === "reasoning" && annotation.text) {
13137
+ // Mark that we've seen reasoning
13138
+ hasSeenReasoning.current = true;
13139
+ // Set isReasoning to true without overwriting toolCalls
13140
+ dispatch(chatActions.setIsReasoning(true));
13141
+ // Ensure message exists in array immediately so reasoning section appears
13142
+ dispatch(chatActions.ensureStreamingAssistantMessage(undefined));
13143
+ // Buffer reasoning content
13144
+ reasoningContentBuffer.current.content += annotation.text;
13145
+ // Check if we should flush (threshold or timer)
13146
+ const shouldFlush = reasoningContentBuffer.current.content.length >=
13147
+ STREAMING_CONTENT_BUFFER_THRESHOLD;
13148
+ const now = Date.now();
13149
+ const timeSinceLastFlush = now - reasoningContentBuffer.current.lastFlushTime;
13150
+ if (shouldFlush ||
13151
+ timeSinceLastFlush >= STREAMING_CONTENT_FLUSH_INTERVAL) {
13152
+ // Flush buffer to Redux
13153
+ dispatch(chatActions.appendReasoningContent(reasoningContentBuffer.current.content));
13154
+ reasoningContentBuffer.current.content = "";
13155
+ reasoningContentBuffer.current.lastFlushTime = now;
13156
+ // Ensure message exists in array and update it with latest data
13157
+ dispatch(chatActions.ensureStreamingAssistantMessage(undefined));
13158
+ }
13159
+ else {
13160
+ // Set up timer to flush if not already set
13161
+ if (!reasoningContentBuffer.current.flushTimer) {
13162
+ reasoningContentBuffer.current.flushTimer = setTimeout(() => {
13163
+ if (reasoningContentBuffer.current.content.length > 0) {
13164
+ dispatch(chatActions.appendReasoningContent(reasoningContentBuffer.current.content));
13165
+ reasoningContentBuffer.current.content = "";
13166
+ reasoningContentBuffer.current.lastFlushTime =
13167
+ Date.now();
13168
+ // Ensure message exists in array and update it with latest data
13169
+ dispatch(chatActions.ensureStreamingAssistantMessage(undefined));
13170
+ }
13171
+ reasoningContentBuffer.current.flushTimer = null;
13172
+ }, STREAMING_CONTENT_FLUSH_INTERVAL);
13173
+ }
13174
+ }
13175
+ }
13176
+ }
13177
+ }
12995
13178
  const messageText = messageData.data || "";
13179
+ // NEW: Detect reasoning→response transition (first non-empty data after reasoning)
13180
+ if (messageText && hasSeenReasoning.current) {
13181
+ // Flush remaining reasoning buffer
13182
+ if (reasoningContentBuffer.current.content.length > 0) {
13183
+ dispatch(chatActions.appendReasoningContent(reasoningContentBuffer.current.content));
13184
+ reasoningContentBuffer.current.content = "";
13185
+ }
13186
+ // Flush reasoning buffer in Redux
13187
+ dispatch(chatActions.flushReasoningBuffer(undefined));
13188
+ // Mark reasoning as complete
13189
+ dispatch(chatActions.setReasoningComplete(undefined));
13190
+ // Update message in array with final reasoning state
13191
+ dispatch(chatActions.ensureStreamingAssistantMessage(undefined));
13192
+ // Reset flag so we only do this once
13193
+ hasSeenReasoning.current = false;
13194
+ }
12996
13195
  // If we have content to add, update the in-memory streaming message and Redux
12997
13196
  if (messageText && currentStreamingMessage.current) {
12998
13197
  console.log("[ws-message] Streaming content chunk:", {
@@ -13006,13 +13205,28 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
13006
13205
  content: currentStreamingMessage.current.content + messageText,
13007
13206
  };
13008
13207
  onStreamingMessageUpdate === null || onStreamingMessageUpdate === void 0 ? void 0 : onStreamingMessageUpdate(currentStreamingMessage.current);
13009
- // Update or add message in Redux store
13208
+ // Update or add message in Redux store - use ensureStreamingAssistantMessage for consistency
13010
13209
  if ((_w = currentStreamingMessage.current) === null || _w === void 0 ? void 0 : _w.id) {
13011
- // TODO: Investigate why `appendMessageToActiveTab` is consuming this action payload
13012
- dispatch(chatActions.appendMessageToActiveTab(undefined));
13210
+ dispatch(chatActions.ensureStreamingAssistantMessage(undefined));
13013
13211
  }
13014
13212
  }
13015
13213
  if (messageData.eos) {
13214
+ // NEW: Flush any remaining reasoning buffer on EOS
13215
+ if (reasoningContentBuffer.current.content.length > 0) {
13216
+ dispatch(chatActions.appendReasoningContent(reasoningContentBuffer.current.content));
13217
+ reasoningContentBuffer.current.content = "";
13218
+ }
13219
+ // Flush reasoning buffer in Redux
13220
+ dispatch(chatActions.flushReasoningBuffer(undefined));
13221
+ // Mark reasoning as complete
13222
+ dispatch(chatActions.setReasoningComplete(undefined));
13223
+ // Update message in array with final state
13224
+ dispatch(chatActions.ensureStreamingAssistantMessage(undefined));
13225
+ // Clear any pending flush timer
13226
+ if (reasoningContentBuffer.current.flushTimer) {
13227
+ clearTimeout(reasoningContentBuffer.current.flushTimer);
13228
+ reasoningContentBuffer.current.flushTimer = null;
13229
+ }
13016
13230
  // Handle end of stream
13017
13231
  const hasPendingArtifact = !!pendingArtifactVersion.current;
13018
13232
  const hasMessageContent = !!((_x = currentStreamingMessage.current) === null || _x === void 0 ? void 0 : _x.content) &&
@@ -13627,7 +13841,13 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
13627
13841
  dispatch(chatActions.setStatus(status));
13628
13842
  };
13629
13843
  const onStreamingMessageUpdate = (message) => {
13630
- dispatch(chatActions.setCurrentStreamingMessage(message));
13844
+ // Use setStreamingMessageIdAndContent to avoid resetting reasoning/tool state
13845
+ if (message.id) {
13846
+ dispatch(chatActions.setStreamingMessageIdAndContent({
13847
+ id: message.id,
13848
+ content: message.content,
13849
+ }));
13850
+ }
13631
13851
  };
13632
13852
  const { sendMessage, stopGenerating, ws, isConnected, messageQueue, resetConnection, } = useChat({
13633
13853
  wsUrl,
@@ -13709,7 +13929,7 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
13709
13929
  try {
13710
13930
  previousChats =
13711
13931
  ((_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) => {
13712
- var _a, _b, _c;
13932
+ var _a, _b, _c, _d, _e, _f;
13713
13933
  const artifactVersions = (_a = result.artifact_versions) === null || _a === void 0 ? void 0 : _a.map((av) => {
13714
13934
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
13715
13935
  return ({
@@ -13748,6 +13968,30 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
13748
13968
  fileSize: file.file_size,
13749
13969
  uploadUrl: file.url,
13750
13970
  }))) || [];
13971
+ // Extract additional_kwargs - check both direct and nested paths
13972
+ const additionalKwargs = result.additional_kwargs ||
13973
+ ((_d = (_c = result.message) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d.additional_kwargs);
13974
+ // Extract reasoning content from additional_kwargs
13975
+ const reasoningSummaries = (_e = additionalKwargs === null || additionalKwargs === void 0 ? void 0 : additionalKwargs.reasoning) === null || _e === void 0 ? void 0 : _e.summary;
13976
+ const reasoningContent = Array.isArray(reasoningSummaries)
13977
+ ? reasoningSummaries
13978
+ .map((s) => s.text)
13979
+ .filter(Boolean)
13980
+ .join("\n\n")
13981
+ : undefined;
13982
+ // Extract tool calls from additional_kwargs
13983
+ const toolOutputs = additionalKwargs === null || additionalKwargs === void 0 ? void 0 : additionalKwargs.tool_outputs;
13984
+ const toolCalls = Array.isArray(toolOutputs)
13985
+ ? toolOutputs.map((tool) => {
13986
+ var _a;
13987
+ return ({
13988
+ id: tool.id || "",
13989
+ name: tool.type || "",
13990
+ log: ((_a = tool.action) === null || _a === void 0 ? void 0 : _a.query) || "",
13991
+ result: tool.status || "",
13992
+ });
13993
+ })
13994
+ : undefined;
13751
13995
  return {
13752
13996
  ...result,
13753
13997
  role: result.type === "human" ? "user" : "assistant",
@@ -13755,17 +13999,45 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
13755
13999
  id: result.id || new Date().getTime(),
13756
14000
  content: typeof result.content === "object" &&
13757
14001
  result.content.length > 0
13758
- ? (_c = result.content.find((item) => item.text)) === null || _c === void 0 ? void 0 : _c.text
14002
+ ? (_f = result.content.find((item) => item.text)) === null || _f === void 0 ? void 0 : _f.text
13759
14003
  : result.content,
13760
14004
  // Add transformed artifact versions
13761
14005
  artifactVersions: artifactVersions || undefined,
13762
14006
  fileAttachments: fileAttachments.length > 0 ? fileAttachments : undefined,
14007
+ reasoningContent: reasoningContent || undefined,
14008
+ toolCalls: toolCalls && toolCalls.length > 0 ? toolCalls : undefined,
13763
14009
  };
13764
14010
  }).reverse()) || [];
13765
14011
  }
13766
14012
  catch (error) {
13767
14013
  console.error(JSON.stringify(error));
13768
14014
  }
14015
+ // Preserve reasoningContent and toolCalls from existing messages
14016
+ // when the session API re-fetch doesn't include additional_kwargs.
14017
+ const existingMessages = chats[activeTab] || [];
14018
+ if (existingMessages.length > 0 && previousChats.length > 0) {
14019
+ const existingByContent = new Map();
14020
+ for (const m of existingMessages) {
14021
+ if (m.reasoningContent || (m.toolCalls && m.toolCalls.length > 0)) {
14022
+ existingByContent.set(m.content, m);
14023
+ }
14024
+ }
14025
+ if (existingByContent.size > 0) {
14026
+ for (const msg of previousChats) {
14027
+ const existing = existingByContent.get(msg.content);
14028
+ if (existing) {
14029
+ if (!msg.reasoningContent && existing.reasoningContent) {
14030
+ msg.reasoningContent = existing.reasoningContent;
14031
+ }
14032
+ if ((!msg.toolCalls || msg.toolCalls.length === 0) &&
14033
+ existing.toolCalls &&
14034
+ existing.toolCalls.length > 0) {
14035
+ msg.toolCalls = existing.toolCalls;
14036
+ }
14037
+ }
14038
+ }
14039
+ }
14040
+ }
13769
14041
  dispatch(chatActions.setNewMessages(previousChats));
13770
14042
  }
13771
14043
  catch (error) {
@@ -17943,6 +18215,7 @@ exports.ServiceWorkerProvider = ServiceWorkerProvider;
17943
18215
  exports.SubscriptionFlow = SubscriptionFlow;
17944
18216
  exports.SubscriptionFlowV2 = SubscriptionFlowV2;
17945
18217
  exports.TOOLS = TOOLS;
18218
+ exports.TOOL_NAME_MAP = TOOL_NAME_MAP;
17946
18219
  exports.TenantContext = TenantContext;
17947
18220
  exports.TenantContextProvider = TenantContextProvider;
17948
18221
  exports.TenantProvider = TenantProvider;
@@ -18047,6 +18320,7 @@ exports.selectEnableChatActionsPopup = selectEnableChatActionsPopup;
18047
18320
  exports.selectIframeContext = selectIframeContext;
18048
18321
  exports.selectIsError = selectIsError;
18049
18322
  exports.selectIsPending = selectIsPending;
18323
+ exports.selectIsReasoning = selectIsReasoning;
18050
18324
  exports.selectIsStopped = selectIsStopped;
18051
18325
  exports.selectIsTyping = selectIsTyping;
18052
18326
  exports.selectLastArtifactContentFlushTime = selectLastArtifactContentFlushTime;
@@ -18061,6 +18335,8 @@ exports.selectStatus = selectStatus;
18061
18335
  exports.selectStreaming = selectStreaming;
18062
18336
  exports.selectStreamingArtifactContentBuffer = selectStreamingArtifactContentBuffer;
18063
18337
  exports.selectStreamingArtifactFullContent = selectStreamingArtifactFullContent;
18338
+ exports.selectStreamingReasoningContent = selectStreamingReasoningContent;
18339
+ exports.selectStreamingToolCalls = selectStreamingToolCalls;
18064
18340
  exports.selectToken = selectToken;
18065
18341
  exports.selectTokenEnabled = selectTokenEnabled;
18066
18342
  exports.selectTools = selectTools;