@meetsmore-oss/use-ai-client 1.9.0 → 1.9.2

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
@@ -1,8 +1,3 @@
1
- import {
2
- generateChatId,
3
- generateMessageId
4
- } from "./chunk-STF3H6F5.js";
5
-
6
1
  // src/useAI.ts
7
2
  import { useState as useState14, useEffect as useEffect12, useLayoutEffect, useRef as useRef14, useCallback as useCallback13, useMemo as useMemo6 } from "react";
8
3
 
@@ -273,13 +268,23 @@ function UseAIFloatingButton({
273
268
  // src/components/UseAIChatPanel.tsx
274
269
  import { useState as useState5, useRef as useRef4, useEffect as useEffect4 } from "react";
275
270
 
271
+ // src/utils/messageContent.ts
272
+ function getTextFromContent(content) {
273
+ if (typeof content === "string") {
274
+ return content;
275
+ }
276
+ return content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
277
+ }
278
+
276
279
  // src/components/MarkdownContent.tsx
277
280
  import ReactMarkdown from "react-markdown";
281
+ import remarkGfm from "remark-gfm";
278
282
  import { jsx as jsx2 } from "react/jsx-runtime";
279
283
  function MarkdownContent({ content }) {
280
284
  return /* @__PURE__ */ jsx2(
281
285
  ReactMarkdown,
282
286
  {
287
+ remarkPlugins: [remarkGfm],
283
288
  components: {
284
289
  // Override default element rendering for better chat styling
285
290
  p: ({ children }) => /* @__PURE__ */ jsx2("p", { style: { margin: "0 0 0.5em 0" }, children }),
@@ -394,6 +399,23 @@ function MarkdownContent({ content }) {
394
399
  },
395
400
  children
396
401
  }
402
+ ),
403
+ // Render images as links to prevent automatic HTTP requests.
404
+ // <img> tags fire GET requests on render, which could be exploited
405
+ // via prompt injection to exfiltrate sensitive data through URLs.
406
+ img: ({ src, alt }) => /* @__PURE__ */ jsx2(
407
+ "a",
408
+ {
409
+ href: src,
410
+ target: "_blank",
411
+ rel: "noopener noreferrer",
412
+ style: {
413
+ color: "inherit",
414
+ textDecoration: "underline",
415
+ textUnderlineOffset: "2px"
416
+ },
417
+ children: alt || "Image"
418
+ }
397
419
  )
398
420
  },
399
421
  children: content
@@ -1891,12 +1913,6 @@ function FeedbackButton({ type, isSelected, onClick, selectedColor, unselectedCo
1891
1913
  }
1892
1914
  );
1893
1915
  }
1894
- function getTextContent(content) {
1895
- if (typeof content === "string") {
1896
- return content;
1897
- }
1898
- return content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
1899
- }
1900
1916
  function hasFileContent(content) {
1901
1917
  return Array.isArray(content) && content.some((part) => part.type === "file");
1902
1918
  }
@@ -1933,6 +1949,11 @@ function UseAIChatPanel({
1933
1949
  }) {
1934
1950
  const strings = useStrings();
1935
1951
  const theme = useTheme();
1952
+ const displayMessages = messages.filter((m) => {
1953
+ if (m.role === "tool") return false;
1954
+ if (m.role === "assistant" && m.toolCalls && m.toolCalls.length > 0 && !getTextFromContent(m.content)) return false;
1955
+ return true;
1956
+ });
1936
1957
  const [input, setInput] = useState5("");
1937
1958
  const chatHistoryDropdown = useDropdownState();
1938
1959
  const agentDropdown = useDropdownState();
@@ -2098,10 +2119,10 @@ function UseAIChatPanel({
2098
2119
  flex: 1,
2099
2120
  minWidth: 0
2100
2121
  }, children: (() => {
2101
- if (messages.length > 0) {
2102
- const firstUserMsg = messages.find((m) => m.role === "user");
2122
+ if (displayMessages.length > 0) {
2123
+ const firstUserMsg = displayMessages.find((m) => m.role === "user");
2103
2124
  if (firstUserMsg) {
2104
- const textContent = getTextContent(firstUserMsg.content);
2125
+ const textContent = getTextFromContent(firstUserMsg.content);
2105
2126
  const maxLength = 30;
2106
2127
  return textContent.length > maxLength ? textContent.substring(0, maxLength) + "..." : textContent || strings.header.newChat;
2107
2128
  }
@@ -2260,7 +2281,7 @@ function UseAIChatPanel({
2260
2281
  children: /* @__PURE__ */ jsx11("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx11("path", { d: "M8 3.5V12.5M3.5 8H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) })
2261
2282
  }
2262
2283
  ),
2263
- onDeleteChat && messages.length > 0 && /* @__PURE__ */ jsx11(
2284
+ onDeleteChat && displayMessages.length > 0 && /* @__PURE__ */ jsx11(
2264
2285
  "button",
2265
2286
  {
2266
2287
  "data-testid": "delete-chat-button",
@@ -2386,7 +2407,7 @@ function UseAIChatPanel({
2386
2407
  gap: "12px"
2387
2408
  },
2388
2409
  children: [
2389
- messages.length === 0 && /* @__PURE__ */ jsxs8(
2410
+ displayMessages.length === 0 && /* @__PURE__ */ jsxs8(
2390
2411
  "div",
2391
2412
  {
2392
2413
  style: {
@@ -2456,7 +2477,7 @@ function UseAIChatPanel({
2456
2477
  ]
2457
2478
  }
2458
2479
  ),
2459
- messages.map((message) => /* @__PURE__ */ jsxs8(
2480
+ displayMessages.map((message) => /* @__PURE__ */ jsxs8(
2460
2481
  "div",
2461
2482
  {
2462
2483
  "data-testid": `chat-message-${message.role}`,
@@ -2483,7 +2504,7 @@ function UseAIChatPanel({
2483
2504
  "data-testid": "save-command-button",
2484
2505
  onClick: (e) => {
2485
2506
  e.stopPropagation();
2486
- const messageText = getTextContent(message.content);
2507
+ const messageText = getTextFromContent(message.content);
2487
2508
  slashCommands.startSavingCommand(message.id, messageText);
2488
2509
  },
2489
2510
  title: "Save as slash command",
@@ -2543,13 +2564,13 @@ function UseAIChatPanel({
2543
2564
  },
2544
2565
  idx
2545
2566
  )) }),
2546
- message.role === "assistant" ? /* @__PURE__ */ jsx11(MarkdownContent, { content: getTextContent(message.content) }) : getTextContent(message.content)
2567
+ message.role === "assistant" ? /* @__PURE__ */ jsx11(MarkdownContent, { content: getTextFromContent(message.content) }) : getTextFromContent(message.content)
2547
2568
  ]
2548
2569
  }
2549
2570
  ),
2550
2571
  slashCommands.renderInlineSaveUI({
2551
2572
  messageId: message.id,
2552
- messageText: getTextContent(message.content)
2573
+ messageText: getTextFromContent(message.content)
2553
2574
  })
2554
2575
  ]
2555
2576
  }
@@ -2603,7 +2624,7 @@ function UseAIChatPanel({
2603
2624
  marginTop: "4px",
2604
2625
  padding: "0 4px"
2605
2626
  },
2606
- children: message.timestamp.toLocaleTimeString([], {
2627
+ children: message.createdAt.toLocaleTimeString([], {
2607
2628
  hour: "2-digit",
2608
2629
  minute: "2-digit"
2609
2630
  })
@@ -3083,6 +3104,8 @@ var UseAIClient = class {
3083
3104
  // Assistant message assembly (for tracking full conversation history)
3084
3105
  _currentAssistantMessage = null;
3085
3106
  _currentAssistantToolCalls = [];
3107
+ // Tool results collected during a turn, pushed to _messages in correct order at RUN_FINISHED
3108
+ _pendingToolResults = [];
3086
3109
  // Tool call assembly
3087
3110
  currentToolCalls = /* @__PURE__ */ new Map();
3088
3111
  // Feedback tracking
@@ -3151,6 +3174,7 @@ var UseAIClient = class {
3151
3174
  content: ""
3152
3175
  };
3153
3176
  this._currentAssistantToolCalls = [];
3177
+ this._pendingToolResults = [];
3154
3178
  }
3155
3179
  if (event.type === EventType.TEXT_MESSAGE_START) {
3156
3180
  const e = event;
@@ -3191,17 +3215,32 @@ var UseAIClient = class {
3191
3215
  }
3192
3216
  } else if (event.type === EventType.RUN_FINISHED) {
3193
3217
  if (this._currentAssistantMessage) {
3194
- const assistantMessage = {
3195
- id: this._currentAssistantMessage.id,
3196
- role: "assistant",
3197
- content: this._currentAssistantMessage.content || ""
3198
- };
3199
3218
  if (this._currentAssistantToolCalls.length > 0) {
3200
- assistantMessage.toolCalls = this._currentAssistantToolCalls;
3219
+ const toolCallMessage = {
3220
+ id: uuidv42(),
3221
+ role: "assistant",
3222
+ content: "",
3223
+ toolCalls: this._currentAssistantToolCalls
3224
+ };
3225
+ this._messages.push(toolCallMessage);
3226
+ this._messages.push(...this._pendingToolResults);
3227
+ const textMessage = {
3228
+ id: this._currentAssistantMessage.id,
3229
+ role: "assistant",
3230
+ content: this._currentAssistantMessage.content || ""
3231
+ };
3232
+ this._messages.push(textMessage);
3233
+ } else {
3234
+ const assistantMessage = {
3235
+ id: this._currentAssistantMessage.id,
3236
+ role: "assistant",
3237
+ content: this._currentAssistantMessage.content || ""
3238
+ };
3239
+ this._messages.push(assistantMessage);
3201
3240
  }
3202
- this._messages.push(assistantMessage);
3203
3241
  this._currentAssistantMessage = null;
3204
3242
  this._currentAssistantToolCalls = [];
3243
+ this._pendingToolResults = [];
3205
3244
  }
3206
3245
  }
3207
3246
  this.eventHandlers.forEach((handler) => handler(event));
@@ -3321,7 +3360,7 @@ var UseAIClient = class {
3321
3360
  content: toolResultMessage.data.content,
3322
3361
  toolCallId
3323
3362
  };
3324
- this._messages.push(toolResultMsg);
3363
+ this._pendingToolResults.push(toolResultMsg);
3325
3364
  this.send(toolResultMessage);
3326
3365
  }
3327
3366
  /**
@@ -3340,6 +3379,14 @@ var UseAIClient = class {
3340
3379
  reason
3341
3380
  }
3342
3381
  };
3382
+ if (!approved) {
3383
+ this._pendingToolResults.push({
3384
+ id: uuidv42(),
3385
+ role: "tool",
3386
+ content: JSON.stringify({ rejected: true, reason: reason || "User rejected tool execution" }),
3387
+ toolCallId
3388
+ });
3389
+ }
3343
3390
  this.send(message);
3344
3391
  }
3345
3392
  /**
@@ -3536,6 +3583,7 @@ var UseAIClient = class {
3536
3583
  this.currentToolCalls.clear();
3537
3584
  this._currentAssistantMessage = null;
3538
3585
  this._currentAssistantToolCalls = [];
3586
+ this._pendingToolResults = [];
3539
3587
  }
3540
3588
  send(message) {
3541
3589
  if (this.socket && this.socket.connected) {
@@ -3658,6 +3706,14 @@ async function executeDefinedTool(tools, toolName, input, ctx) {
3658
3706
  return await tool._execute(input, ctx);
3659
3707
  }
3660
3708
 
3709
+ // src/providers/chatRepository/types.ts
3710
+ function generateChatId() {
3711
+ return `chat_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
3712
+ }
3713
+ function generateMessageId() {
3714
+ return `msg_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
3715
+ }
3716
+
3661
3717
  // src/providers/chatRepository/LocalStorageChatRepository.ts
3662
3718
  var STORAGE_KEY_PREFIX = "use-ai:chat:";
3663
3719
  var STORAGE_INDEX_KEY = "use-ai:chat-index";
@@ -3823,31 +3879,31 @@ function deepEquals(a, b) {
3823
3879
  function generateChatTitle(message) {
3824
3880
  return message.length > CHAT_TITLE_MAX_LENGTH ? message.substring(0, CHAT_TITLE_MAX_LENGTH) + "..." : message;
3825
3881
  }
3826
- function getTextFromContent(content) {
3827
- if (typeof content === "string") {
3828
- return content;
3829
- }
3830
- return content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
3831
- }
3832
- function transformMessagesToUI(storageMessages) {
3833
- return storageMessages.map((msg) => ({
3834
- id: msg.id,
3835
- role: msg.role,
3836
- content: msg.content,
3837
- timestamp: msg.createdAt,
3838
- displayMode: msg.displayMode,
3839
- traceId: msg.traceId,
3840
- feedback: msg.feedback
3841
- }));
3842
- }
3843
- function transformMessagesToClientFormat(uiMessages) {
3844
- return uiMessages.map((msg) => {
3882
+ function transformMessagesToClientFormat(persistedMessages) {
3883
+ return persistedMessages.map((msg) => {
3845
3884
  const textContent = getTextFromContent(msg.content);
3846
- return {
3847
- id: msg.id,
3848
- role: msg.role,
3849
- content: textContent
3850
- };
3885
+ switch (msg.role) {
3886
+ case "tool":
3887
+ return {
3888
+ id: msg.id,
3889
+ role: "tool",
3890
+ content: textContent,
3891
+ toolCallId: msg.toolCallId || ""
3892
+ };
3893
+ case "assistant":
3894
+ return {
3895
+ id: msg.id,
3896
+ role: "assistant",
3897
+ content: textContent,
3898
+ ...msg.toolCalls && msg.toolCalls.length > 0 ? { toolCalls: msg.toolCalls } : {}
3899
+ };
3900
+ case "user":
3901
+ return {
3902
+ id: msg.id,
3903
+ role: "user",
3904
+ content: textContent
3905
+ };
3906
+ }
3851
3907
  });
3852
3908
  }
3853
3909
  function useChatManagement({
@@ -3871,9 +3927,8 @@ function useChatManagement({
3871
3927
  try {
3872
3928
  const chat = await repository.loadChat(chatId);
3873
3929
  if (chat) {
3874
- const loadedMessages = transformMessagesToUI(chat.messages);
3875
- console.log("[ChatManagement] Loaded", loadedMessages.length, "messages from storage for chat:", chatId);
3876
- return loadedMessages;
3930
+ console.log("[ChatManagement] Loaded", chat.messages.length, "messages from storage for chat:", chatId);
3931
+ return chat.messages;
3877
3932
  } else {
3878
3933
  console.log("[ChatManagement] Chat not found in storage:", chatId);
3879
3934
  return [];
@@ -3969,7 +4024,7 @@ function useChatManagement({
3969
4024
  setCurrentChatId(pendingChatId);
3970
4025
  setPendingChatId(null);
3971
4026
  return pendingChatId;
3972
- }, [pendingChatId, messages, clientRef]);
4027
+ }, [pendingChatId, clientRef, messages]);
3973
4028
  const saveUserMessage = useCallback4(async (chatId, content) => {
3974
4029
  try {
3975
4030
  const chat = await repository.loadChat(chatId);
@@ -3977,13 +4032,13 @@ function useChatManagement({
3977
4032
  console.error("[ChatManagement] Chat not found:", chatId);
3978
4033
  return false;
3979
4034
  }
3980
- const { generateMessageId: generateMessageId2 } = await import("./types-GWPQMSYT.js");
3981
- chat.messages.push({
3982
- id: generateMessageId2(),
4035
+ const newMessage = {
4036
+ id: generateMessageId(),
3983
4037
  role: "user",
3984
4038
  content,
3985
4039
  createdAt: /* @__PURE__ */ new Date()
3986
- });
4040
+ };
4041
+ chat.messages.push(newMessage);
3987
4042
  if (!chat.title) {
3988
4043
  const text = getTextFromContent(content);
3989
4044
  if (text) {
@@ -3992,14 +4047,14 @@ function useChatManagement({
3992
4047
  }
3993
4048
  await repository.saveChat(chat);
3994
4049
  console.log("[ChatManagement] Saved user message to storage");
3995
- await reloadMessages(chatId);
4050
+ setMessages((prev) => [...prev, newMessage]);
3996
4051
  return true;
3997
4052
  } catch (error) {
3998
4053
  console.error("[ChatManagement] Failed to save user message:", error);
3999
4054
  return false;
4000
4055
  }
4001
- }, [repository, reloadMessages]);
4002
- const saveAIResponse = useCallback4(async (content, displayMode, traceId) => {
4056
+ }, [repository, setMessages]);
4057
+ const saveAIResponse = useCallback4(async (content, displayMode, traceId, turnMessages) => {
4003
4058
  const currentChatIdValue = currentChatIdSnapshot.current;
4004
4059
  const pendingChatIdValue = pendingChatIdSnapshot.current;
4005
4060
  const displayedChatId2 = pendingChatIdValue || currentChatIdValue;
@@ -4013,15 +4068,18 @@ function useChatManagement({
4013
4068
  console.error("[ChatManagement] Chat not found:", currentChatIdValue);
4014
4069
  return;
4015
4070
  }
4016
- const { generateMessageId: generateMessageId2 } = await import("./types-GWPQMSYT.js");
4017
- chat.messages.push({
4018
- id: generateMessageId2(),
4071
+ if (turnMessages && turnMessages.length > 0) {
4072
+ chat.messages.push(...turnMessages);
4073
+ }
4074
+ const finalMessage = {
4075
+ id: generateMessageId(),
4019
4076
  role: "assistant",
4020
4077
  content,
4021
4078
  createdAt: /* @__PURE__ */ new Date(),
4022
4079
  displayMode,
4023
4080
  traceId
4024
- });
4081
+ };
4082
+ chat.messages.push(finalMessage);
4025
4083
  if (!chat.title) {
4026
4084
  const firstUserMessage = chat.messages.find((msg) => msg.role === "user");
4027
4085
  if (firstUserMessage) {
@@ -4034,12 +4092,16 @@ function useChatManagement({
4034
4092
  await repository.saveChat(chat);
4035
4093
  console.log("[ChatManagement] Saved AI response to storage for chatId:", currentChatIdValue);
4036
4094
  if (displayedChatId2 === currentChatIdValue) {
4037
- await reloadMessages(currentChatIdValue);
4095
+ const newMessages = [
4096
+ ...turnMessages ?? [],
4097
+ finalMessage
4098
+ ];
4099
+ setMessages((prev) => [...prev, ...newMessages]);
4038
4100
  }
4039
4101
  } catch (error) {
4040
4102
  console.error("[ChatManagement] Failed to save AI response:", error);
4041
4103
  }
4042
- }, [repository, reloadMessages]);
4104
+ }, [repository, setMessages]);
4043
4105
  const initializedRef = useRef5(false);
4044
4106
  useEffect5(() => {
4045
4107
  if (currentChatId === null && pendingChatId === null && !initializedRef.current) {
@@ -4070,6 +4132,26 @@ function useChatManagement({
4070
4132
  })();
4071
4133
  }
4072
4134
  }, [currentChatId, pendingChatId, createNewChat, repository, loadChatMessages, clientRef, setMessages]);
4135
+ const connectionPhaseRef = useRef5("initial");
4136
+ useEffect5(() => {
4137
+ if (connected) {
4138
+ const isReconnect = connectionPhaseRef.current === "disconnected";
4139
+ connectionPhaseRef.current = "connected";
4140
+ if (isReconnect && currentChatId && clientRef.current) {
4141
+ (async () => {
4142
+ const loadedMessages = await loadChatMessages(currentChatId);
4143
+ if (loadedMessages.length > 0) {
4144
+ clientRef.current?.loadMessages(transformMessagesToClientFormat(loadedMessages));
4145
+ console.log("[ChatManagement] Reloaded", loadedMessages.length, "messages from storage on reconnect");
4146
+ }
4147
+ })();
4148
+ }
4149
+ } else {
4150
+ if (connectionPhaseRef.current === "connected") {
4151
+ connectionPhaseRef.current = "disconnected";
4152
+ }
4153
+ }
4154
+ }, [connected, currentChatId, loadChatMessages, clientRef]);
4073
4155
  const displayedChatId = pendingChatId || currentChatId;
4074
4156
  return {
4075
4157
  currentChatId,
@@ -4714,6 +4796,30 @@ import { useState as useState12, useCallback as useCallback10, useRef as useRef1
4714
4796
  import { EventType as EventType2, ErrorCode, TOOL_APPROVAL_REQUEST } from "@meetsmore-oss/use-ai-core";
4715
4797
 
4716
4798
  // src/hooks/useServerEvents.ts
4799
+ function extractTurnMessages(messages, startIndex) {
4800
+ const turnSlice = messages.slice(startIndex);
4801
+ const result = [];
4802
+ for (const msg of turnSlice) {
4803
+ if (msg.role === "assistant" && "toolCalls" in msg && msg.toolCalls) {
4804
+ result.push({
4805
+ id: msg.id,
4806
+ role: "assistant",
4807
+ content: "",
4808
+ createdAt: /* @__PURE__ */ new Date(),
4809
+ toolCalls: msg.toolCalls
4810
+ });
4811
+ } else if (msg.role === "tool") {
4812
+ result.push({
4813
+ id: msg.id,
4814
+ role: "tool",
4815
+ content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
4816
+ createdAt: /* @__PURE__ */ new Date(),
4817
+ toolCallId: "toolCallId" in msg && msg.toolCallId ? msg.toolCallId : void 0
4818
+ });
4819
+ }
4820
+ }
4821
+ return result;
4822
+ }
4717
4823
  function useServerEvents({
4718
4824
  toolSystem,
4719
4825
  saveAIResponse,
@@ -4722,6 +4828,7 @@ function useServerEvents({
4722
4828
  const [loading, setLoading] = useState12(false);
4723
4829
  const [streamingText, setStreamingText] = useState12("");
4724
4830
  const streamingChatIdRef = useRef10(null);
4831
+ const messageCountAtRunStartRef = useRef10(0);
4725
4832
  const [executingToolRaw, setExecutingTool] = useState12(null);
4726
4833
  const executingToolFallbackRef = useRef10(null);
4727
4834
  const clearStreamingText = useCallback10(() => {
@@ -4736,7 +4843,9 @@ function useServerEvents({
4736
4843
  const handleServerEvent = useCallback10(async (client, event) => {
4737
4844
  const ts = toolSystemRef.current;
4738
4845
  const strs = stringsRef.current;
4739
- if (event.type === EventType2.TOOL_CALL_START) {
4846
+ if (event.type === EventType2.RUN_STARTED) {
4847
+ messageCountAtRunStartRef.current = client.messages.length;
4848
+ } else if (event.type === EventType2.TOOL_CALL_START) {
4740
4849
  const e = event;
4741
4850
  const tool = ts.aggregatedToolsRef.current[e.toolCallName];
4742
4851
  const title = e.annotations?.title ?? tool?._options?.annotations?.title ?? null;
@@ -4781,7 +4890,8 @@ function useServerEvents({
4781
4890
  if (content) {
4782
4891
  const finishedEvent = event;
4783
4892
  const traceId = finishedEvent.runId;
4784
- saveAIResponseRef.current(content, void 0, traceId);
4893
+ const turnMessages = extractTurnMessages(client.messages, messageCountAtRunStartRef.current);
4894
+ saveAIResponseRef.current(content, void 0, traceId, turnMessages);
4785
4895
  }
4786
4896
  setLoading(false);
4787
4897
  } else if (event.type === EventType2.RUN_ERROR) {
@@ -5052,7 +5162,15 @@ function UseAIProvider({
5052
5162
  const lastRegisteredToolsRef = useRef12("");
5053
5163
  useEffect11(() => {
5054
5164
  const client = clientRef.current;
5055
- if (!client || !client.isConnected() || !toolSystem.hasTools) return;
5165
+ if (!client || !client.isConnected()) return;
5166
+ if (!toolSystem.hasTools) {
5167
+ if (lastRegisteredToolsRef.current !== "") {
5168
+ lastRegisteredToolsRef.current = "";
5169
+ client.registerTools([]);
5170
+ console.log("[Provider] All tools unregistered, clearing client tools");
5171
+ }
5172
+ return;
5173
+ }
5056
5174
  const toolKeys = Object.keys(toolSystem.aggregatedTools).sort().join(",");
5057
5175
  if (toolKeys === lastRegisteredToolsRef.current) {
5058
5176
  console.log("[Provider] Skipping re-registration, tools unchanged");