@meetsmore-oss/use-ai-client 1.8.0 → 1.9.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
@@ -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
@@ -1638,7 +1660,8 @@ function ToolApprovalDialog({
1638
1660
  const [showDetails, setShowDetails] = useState4(false);
1639
1661
  const displayName = annotations?.title || toolCallName;
1640
1662
  const isBatch = toolCount > 1;
1641
- const message = isBatch ? strings.toolApproval.batchMessage?.replace("{count}", String(toolCount)) ?? `${toolCount} actions are waiting for your approval` : strings.toolApproval.message.replace("{toolName}", displayName);
1663
+ const runtimeMessage = pendingTools.find((t) => t.message)?.message;
1664
+ const message = runtimeMessage ? runtimeMessage : isBatch ? strings.toolApproval.batchMessage?.replace("{count}", String(toolCount)) ?? `${toolCount} actions are waiting for your approval` : strings.toolApproval.message.replace("{toolName}", displayName);
1642
1665
  const getToolDisplayName = (tool) => tool.annotations?.title || tool.toolCallName;
1643
1666
  return /* @__PURE__ */ jsxs7(
1644
1667
  "div",
@@ -1890,12 +1913,6 @@ function FeedbackButton({ type, isSelected, onClick, selectedColor, unselectedCo
1890
1913
  }
1891
1914
  );
1892
1915
  }
1893
- function getTextContent(content) {
1894
- if (typeof content === "string") {
1895
- return content;
1896
- }
1897
- return content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
1898
- }
1899
1916
  function hasFileContent(content) {
1900
1917
  return Array.isArray(content) && content.some((part) => part.type === "file");
1901
1918
  }
@@ -1932,6 +1949,11 @@ function UseAIChatPanel({
1932
1949
  }) {
1933
1950
  const strings = useStrings();
1934
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
+ });
1935
1957
  const [input, setInput] = useState5("");
1936
1958
  const chatHistoryDropdown = useDropdownState();
1937
1959
  const agentDropdown = useDropdownState();
@@ -2097,10 +2119,10 @@ function UseAIChatPanel({
2097
2119
  flex: 1,
2098
2120
  minWidth: 0
2099
2121
  }, children: (() => {
2100
- if (messages.length > 0) {
2101
- const firstUserMsg = messages.find((m) => m.role === "user");
2122
+ if (displayMessages.length > 0) {
2123
+ const firstUserMsg = displayMessages.find((m) => m.role === "user");
2102
2124
  if (firstUserMsg) {
2103
- const textContent = getTextContent(firstUserMsg.content);
2125
+ const textContent = getTextFromContent(firstUserMsg.content);
2104
2126
  const maxLength = 30;
2105
2127
  return textContent.length > maxLength ? textContent.substring(0, maxLength) + "..." : textContent || strings.header.newChat;
2106
2128
  }
@@ -2259,7 +2281,7 @@ function UseAIChatPanel({
2259
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" }) })
2260
2282
  }
2261
2283
  ),
2262
- onDeleteChat && messages.length > 0 && /* @__PURE__ */ jsx11(
2284
+ onDeleteChat && displayMessages.length > 0 && /* @__PURE__ */ jsx11(
2263
2285
  "button",
2264
2286
  {
2265
2287
  "data-testid": "delete-chat-button",
@@ -2385,7 +2407,7 @@ function UseAIChatPanel({
2385
2407
  gap: "12px"
2386
2408
  },
2387
2409
  children: [
2388
- messages.length === 0 && /* @__PURE__ */ jsxs8(
2410
+ displayMessages.length === 0 && /* @__PURE__ */ jsxs8(
2389
2411
  "div",
2390
2412
  {
2391
2413
  style: {
@@ -2455,7 +2477,7 @@ function UseAIChatPanel({
2455
2477
  ]
2456
2478
  }
2457
2479
  ),
2458
- messages.map((message) => /* @__PURE__ */ jsxs8(
2480
+ displayMessages.map((message) => /* @__PURE__ */ jsxs8(
2459
2481
  "div",
2460
2482
  {
2461
2483
  "data-testid": `chat-message-${message.role}`,
@@ -2482,7 +2504,7 @@ function UseAIChatPanel({
2482
2504
  "data-testid": "save-command-button",
2483
2505
  onClick: (e) => {
2484
2506
  e.stopPropagation();
2485
- const messageText = getTextContent(message.content);
2507
+ const messageText = getTextFromContent(message.content);
2486
2508
  slashCommands.startSavingCommand(message.id, messageText);
2487
2509
  },
2488
2510
  title: "Save as slash command",
@@ -2542,13 +2564,13 @@ function UseAIChatPanel({
2542
2564
  },
2543
2565
  idx
2544
2566
  )) }),
2545
- 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)
2546
2568
  ]
2547
2569
  }
2548
2570
  ),
2549
2571
  slashCommands.renderInlineSaveUI({
2550
2572
  messageId: message.id,
2551
- messageText: getTextContent(message.content)
2573
+ messageText: getTextFromContent(message.content)
2552
2574
  })
2553
2575
  ]
2554
2576
  }
@@ -2602,7 +2624,7 @@ function UseAIChatPanel({
2602
2624
  marginTop: "4px",
2603
2625
  padding: "0 4px"
2604
2626
  },
2605
- children: message.timestamp.toLocaleTimeString([], {
2627
+ children: message.createdAt.toLocaleTimeString([], {
2606
2628
  hour: "2-digit",
2607
2629
  minute: "2-digit"
2608
2630
  })
@@ -3082,6 +3104,8 @@ var UseAIClient = class {
3082
3104
  // Assistant message assembly (for tracking full conversation history)
3083
3105
  _currentAssistantMessage = null;
3084
3106
  _currentAssistantToolCalls = [];
3107
+ // Tool results collected during a turn, pushed to _messages in correct order at RUN_FINISHED
3108
+ _pendingToolResults = [];
3085
3109
  // Tool call assembly
3086
3110
  currentToolCalls = /* @__PURE__ */ new Map();
3087
3111
  // Feedback tracking
@@ -3150,6 +3174,7 @@ var UseAIClient = class {
3150
3174
  content: ""
3151
3175
  };
3152
3176
  this._currentAssistantToolCalls = [];
3177
+ this._pendingToolResults = [];
3153
3178
  }
3154
3179
  if (event.type === EventType.TEXT_MESSAGE_START) {
3155
3180
  const e = event;
@@ -3190,17 +3215,32 @@ var UseAIClient = class {
3190
3215
  }
3191
3216
  } else if (event.type === EventType.RUN_FINISHED) {
3192
3217
  if (this._currentAssistantMessage) {
3193
- const assistantMessage = {
3194
- id: this._currentAssistantMessage.id,
3195
- role: "assistant",
3196
- content: this._currentAssistantMessage.content || ""
3197
- };
3198
3218
  if (this._currentAssistantToolCalls.length > 0) {
3199
- 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);
3200
3240
  }
3201
- this._messages.push(assistantMessage);
3202
3241
  this._currentAssistantMessage = null;
3203
3242
  this._currentAssistantToolCalls = [];
3243
+ this._pendingToolResults = [];
3204
3244
  }
3205
3245
  }
3206
3246
  this.eventHandlers.forEach((handler) => handler(event));
@@ -3320,7 +3360,7 @@ var UseAIClient = class {
3320
3360
  content: toolResultMessage.data.content,
3321
3361
  toolCallId
3322
3362
  };
3323
- this._messages.push(toolResultMsg);
3363
+ this._pendingToolResults.push(toolResultMsg);
3324
3364
  this.send(toolResultMessage);
3325
3365
  }
3326
3366
  /**
@@ -3339,6 +3379,14 @@ var UseAIClient = class {
3339
3379
  reason
3340
3380
  }
3341
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
+ }
3342
3390
  this.send(message);
3343
3391
  }
3344
3392
  /**
@@ -3535,6 +3583,7 @@ var UseAIClient = class {
3535
3583
  this.currentToolCalls.clear();
3536
3584
  this._currentAssistantMessage = null;
3537
3585
  this._currentAssistantToolCalls = [];
3586
+ this._pendingToolResults = [];
3538
3587
  }
3539
3588
  send(message) {
3540
3589
  if (this.socket && this.socket.connected) {
@@ -3605,7 +3654,8 @@ function defineTool(description, schemaOrFn, fnOrOptions, options) {
3605
3654
  let actualFn;
3606
3655
  let actualOptions;
3607
3656
  if (isNoParamFunction) {
3608
- actualFn = schemaOrFn;
3657
+ const noParamFn = schemaOrFn;
3658
+ actualFn = (_input, ctx) => noParamFn(ctx);
3609
3659
  actualOptions = fnOrOptions || {};
3610
3660
  } else {
3611
3661
  actualFn = fnOrOptions;
@@ -3639,21 +3689,29 @@ function defineTool(description, schemaOrFn, fnOrOptions, options) {
3639
3689
  }
3640
3690
  return toolDef;
3641
3691
  },
3642
- async _execute(input) {
3692
+ async _execute(input, ctx) {
3643
3693
  const validated = this._zodSchema.parse(input);
3644
- return await actualFn(validated);
3694
+ return await actualFn(validated, ctx);
3645
3695
  }
3646
3696
  };
3647
3697
  }
3648
3698
  function convertToolsToDefinitions(tools) {
3649
3699
  return Object.entries(tools).map(([name, tool]) => tool._toToolDefinition(name));
3650
3700
  }
3651
- async function executeDefinedTool(tools, toolName, input) {
3701
+ async function executeDefinedTool(tools, toolName, input, ctx) {
3652
3702
  const tool = tools[toolName];
3653
3703
  if (!tool) {
3654
3704
  throw new Error(`Tool "${toolName}" not found`);
3655
3705
  }
3656
- return await tool._execute(input);
3706
+ return await tool._execute(input, ctx);
3707
+ }
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)}`;
3657
3715
  }
3658
3716
 
3659
3717
  // src/providers/chatRepository/LocalStorageChatRepository.ts
@@ -3821,31 +3879,31 @@ function deepEquals(a, b) {
3821
3879
  function generateChatTitle(message) {
3822
3880
  return message.length > CHAT_TITLE_MAX_LENGTH ? message.substring(0, CHAT_TITLE_MAX_LENGTH) + "..." : message;
3823
3881
  }
3824
- function getTextFromContent(content) {
3825
- if (typeof content === "string") {
3826
- return content;
3827
- }
3828
- return content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
3829
- }
3830
- function transformMessagesToUI(storageMessages) {
3831
- return storageMessages.map((msg) => ({
3832
- id: msg.id,
3833
- role: msg.role,
3834
- content: msg.content,
3835
- timestamp: msg.createdAt,
3836
- displayMode: msg.displayMode,
3837
- traceId: msg.traceId,
3838
- feedback: msg.feedback
3839
- }));
3840
- }
3841
- function transformMessagesToClientFormat(uiMessages) {
3842
- return uiMessages.map((msg) => {
3882
+ function transformMessagesToClientFormat(persistedMessages) {
3883
+ return persistedMessages.map((msg) => {
3843
3884
  const textContent = getTextFromContent(msg.content);
3844
- return {
3845
- id: msg.id,
3846
- role: msg.role,
3847
- content: textContent
3848
- };
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
+ }
3849
3907
  });
3850
3908
  }
3851
3909
  function useChatManagement({
@@ -3869,9 +3927,8 @@ function useChatManagement({
3869
3927
  try {
3870
3928
  const chat = await repository.loadChat(chatId);
3871
3929
  if (chat) {
3872
- const loadedMessages = transformMessagesToUI(chat.messages);
3873
- console.log("[ChatManagement] Loaded", loadedMessages.length, "messages from storage for chat:", chatId);
3874
- return loadedMessages;
3930
+ console.log("[ChatManagement] Loaded", chat.messages.length, "messages from storage for chat:", chatId);
3931
+ return chat.messages;
3875
3932
  } else {
3876
3933
  console.log("[ChatManagement] Chat not found in storage:", chatId);
3877
3934
  return [];
@@ -3967,7 +4024,7 @@ function useChatManagement({
3967
4024
  setCurrentChatId(pendingChatId);
3968
4025
  setPendingChatId(null);
3969
4026
  return pendingChatId;
3970
- }, [pendingChatId, messages, clientRef]);
4027
+ }, [pendingChatId, clientRef, messages]);
3971
4028
  const saveUserMessage = useCallback4(async (chatId, content) => {
3972
4029
  try {
3973
4030
  const chat = await repository.loadChat(chatId);
@@ -3975,13 +4032,13 @@ function useChatManagement({
3975
4032
  console.error("[ChatManagement] Chat not found:", chatId);
3976
4033
  return false;
3977
4034
  }
3978
- const { generateMessageId: generateMessageId2 } = await import("./types-GWPQMSYT.js");
3979
- chat.messages.push({
3980
- id: generateMessageId2(),
4035
+ const newMessage = {
4036
+ id: generateMessageId(),
3981
4037
  role: "user",
3982
4038
  content,
3983
4039
  createdAt: /* @__PURE__ */ new Date()
3984
- });
4040
+ };
4041
+ chat.messages.push(newMessage);
3985
4042
  if (!chat.title) {
3986
4043
  const text = getTextFromContent(content);
3987
4044
  if (text) {
@@ -3990,14 +4047,14 @@ function useChatManagement({
3990
4047
  }
3991
4048
  await repository.saveChat(chat);
3992
4049
  console.log("[ChatManagement] Saved user message to storage");
3993
- await reloadMessages(chatId);
4050
+ setMessages((prev) => [...prev, newMessage]);
3994
4051
  return true;
3995
4052
  } catch (error) {
3996
4053
  console.error("[ChatManagement] Failed to save user message:", error);
3997
4054
  return false;
3998
4055
  }
3999
- }, [repository, reloadMessages]);
4000
- const saveAIResponse = useCallback4(async (content, displayMode, traceId) => {
4056
+ }, [repository, setMessages]);
4057
+ const saveAIResponse = useCallback4(async (content, displayMode, traceId, turnMessages) => {
4001
4058
  const currentChatIdValue = currentChatIdSnapshot.current;
4002
4059
  const pendingChatIdValue = pendingChatIdSnapshot.current;
4003
4060
  const displayedChatId2 = pendingChatIdValue || currentChatIdValue;
@@ -4011,15 +4068,18 @@ function useChatManagement({
4011
4068
  console.error("[ChatManagement] Chat not found:", currentChatIdValue);
4012
4069
  return;
4013
4070
  }
4014
- const { generateMessageId: generateMessageId2 } = await import("./types-GWPQMSYT.js");
4015
- chat.messages.push({
4016
- id: generateMessageId2(),
4071
+ if (turnMessages && turnMessages.length > 0) {
4072
+ chat.messages.push(...turnMessages);
4073
+ }
4074
+ const finalMessage = {
4075
+ id: generateMessageId(),
4017
4076
  role: "assistant",
4018
4077
  content,
4019
4078
  createdAt: /* @__PURE__ */ new Date(),
4020
4079
  displayMode,
4021
4080
  traceId
4022
- });
4081
+ };
4082
+ chat.messages.push(finalMessage);
4023
4083
  if (!chat.title) {
4024
4084
  const firstUserMessage = chat.messages.find((msg) => msg.role === "user");
4025
4085
  if (firstUserMessage) {
@@ -4032,12 +4092,16 @@ function useChatManagement({
4032
4092
  await repository.saveChat(chat);
4033
4093
  console.log("[ChatManagement] Saved AI response to storage for chatId:", currentChatIdValue);
4034
4094
  if (displayedChatId2 === currentChatIdValue) {
4035
- await reloadMessages(currentChatIdValue);
4095
+ const newMessages = [
4096
+ ...turnMessages ?? [],
4097
+ finalMessage
4098
+ ];
4099
+ setMessages((prev) => [...prev, ...newMessages]);
4036
4100
  }
4037
4101
  } catch (error) {
4038
4102
  console.error("[ChatManagement] Failed to save AI response:", error);
4039
4103
  }
4040
- }, [repository, reloadMessages]);
4104
+ }, [repository, setMessages]);
4041
4105
  const initializedRef = useRef5(false);
4042
4106
  useEffect5(() => {
4043
4107
  if (currentChatId === null && pendingChatId === null && !initializedRef.current) {
@@ -4068,6 +4132,26 @@ function useChatManagement({
4068
4132
  })();
4069
4133
  }
4070
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]);
4071
4155
  const displayedChatId = pendingChatId || currentChatId;
4072
4156
  return {
4073
4157
  currentChatId,
@@ -4344,6 +4428,7 @@ function useToolSystem({
4344
4428
  const waitersRef = useRef7(/* @__PURE__ */ new Map());
4345
4429
  const [pendingApprovals, setPendingApprovals] = useState9([]);
4346
4430
  const pendingApprovalToolCallsRef = useRef7(/* @__PURE__ */ new Map());
4431
+ const runtimeApprovalResolversRef = useRef7(/* @__PURE__ */ new Map());
4347
4432
  const registerTools = useCallback7((id, tools, options) => {
4348
4433
  const existingTools = toolRegistryRef.current.get(id);
4349
4434
  toolRegistryRef.current.set(id, tools);
@@ -4453,7 +4538,9 @@ function useToolSystem({
4453
4538
  toolCallId: event.toolCallId,
4454
4539
  toolCallName: event.toolCallName,
4455
4540
  toolCallArgs: event.toolCallArgs,
4456
- annotations: event.annotations
4541
+ annotations: event.annotations,
4542
+ message: event.message,
4543
+ metadata: event.metadata
4457
4544
  }
4458
4545
  ]);
4459
4546
  }, []);
@@ -4466,8 +4553,26 @@ function useToolSystem({
4466
4553
  try {
4467
4554
  const ownerId = toolOwnershipRef.current.get(name);
4468
4555
  console.log(`[useToolSystem] Tool "${name}" owned by component:`, ownerId);
4556
+ const ctx = {
4557
+ requestApproval: ({ message, metadata }) => {
4558
+ return new Promise((resolve) => {
4559
+ const approvalId = `${toolCallId}-runtime-${Date.now()}`;
4560
+ runtimeApprovalResolversRef.current.set(approvalId, resolve);
4561
+ setPendingApprovals((prev) => [
4562
+ ...prev,
4563
+ {
4564
+ toolCallId: approvalId,
4565
+ toolCallName: name,
4566
+ toolCallArgs: input || {},
4567
+ message,
4568
+ metadata
4569
+ }
4570
+ ]);
4571
+ });
4572
+ }
4573
+ };
4469
4574
  console.log("[useToolSystem] Executing tool...");
4470
- const result = await executeDefinedTool(aggregatedToolsRef.current, name, input);
4575
+ const result = await executeDefinedTool(aggregatedToolsRef.current, name, input, ctx);
4471
4576
  const isErrorResult = result && typeof result === "object" && ("error" in result || result.success === false);
4472
4577
  const ownerIsInvisible = ownerId ? isInvisible(ownerId) : false;
4473
4578
  if (ownerId && !isErrorResult && !ownerIsInvisible) {
@@ -4512,24 +4617,32 @@ function useToolSystem({
4512
4617
  if (!clientRef.current) return;
4513
4618
  console.log("[useToolSystem] Approving all tool calls:", pendingApprovals.length);
4514
4619
  const pendingTools = [...pendingApprovals];
4515
- for (const pending of pendingTools) {
4516
- clientRef.current.sendToolApprovalResponse(pending.toolCallId, true);
4517
- }
4518
4620
  setPendingApprovals([]);
4519
- for (const tool of pendingTools) {
4520
- await executePendingToolAfterApproval(tool.toolCallId);
4621
+ for (const pending of pendingTools) {
4622
+ const runtimeResolver = runtimeApprovalResolversRef.current.get(pending.toolCallId);
4623
+ if (runtimeResolver) {
4624
+ runtimeApprovalResolversRef.current.delete(pending.toolCallId);
4625
+ runtimeResolver({ approved: true });
4626
+ } else {
4627
+ clientRef.current.sendToolApprovalResponse(pending.toolCallId, true);
4628
+ await executePendingToolAfterApproval(pending.toolCallId);
4629
+ }
4521
4630
  }
4522
4631
  }, [clientRef, pendingApprovals, executePendingToolAfterApproval]);
4523
4632
  const rejectAll = useCallback7((reason) => {
4524
4633
  if (!clientRef.current) return;
4525
4634
  console.log("[useToolSystem] Rejecting all tool calls:", pendingApprovals.length, reason);
4526
4635
  const pendingTools = [...pendingApprovals];
4527
- for (const pending of pendingTools) {
4528
- clientRef.current.sendToolApprovalResponse(pending.toolCallId, false, reason);
4529
- }
4530
4636
  setPendingApprovals([]);
4531
- for (const tool of pendingTools) {
4532
- pendingApprovalToolCallsRef.current.delete(tool.toolCallId);
4637
+ for (const pending of pendingTools) {
4638
+ const runtimeResolver = runtimeApprovalResolversRef.current.get(pending.toolCallId);
4639
+ if (runtimeResolver) {
4640
+ runtimeApprovalResolversRef.current.delete(pending.toolCallId);
4641
+ runtimeResolver({ approved: false, reason });
4642
+ } else {
4643
+ clientRef.current.sendToolApprovalResponse(pending.toolCallId, false, reason);
4644
+ pendingApprovalToolCallsRef.current.delete(pending.toolCallId);
4645
+ }
4533
4646
  }
4534
4647
  }, [clientRef, pendingApprovals]);
4535
4648
  return {
@@ -4683,6 +4796,30 @@ import { useState as useState12, useCallback as useCallback10, useRef as useRef1
4683
4796
  import { EventType as EventType2, ErrorCode, TOOL_APPROVAL_REQUEST } from "@meetsmore-oss/use-ai-core";
4684
4797
 
4685
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
+ }
4686
4823
  function useServerEvents({
4687
4824
  toolSystem,
4688
4825
  saveAIResponse,
@@ -4691,6 +4828,7 @@ function useServerEvents({
4691
4828
  const [loading, setLoading] = useState12(false);
4692
4829
  const [streamingText, setStreamingText] = useState12("");
4693
4830
  const streamingChatIdRef = useRef10(null);
4831
+ const messageCountAtRunStartRef = useRef10(0);
4694
4832
  const [executingToolRaw, setExecutingTool] = useState12(null);
4695
4833
  const executingToolFallbackRef = useRef10(null);
4696
4834
  const clearStreamingText = useCallback10(() => {
@@ -4705,7 +4843,9 @@ function useServerEvents({
4705
4843
  const handleServerEvent = useCallback10(async (client, event) => {
4706
4844
  const ts = toolSystemRef.current;
4707
4845
  const strs = stringsRef.current;
4708
- 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) {
4709
4849
  const e = event;
4710
4850
  const tool = ts.aggregatedToolsRef.current[e.toolCallName];
4711
4851
  const title = e.annotations?.title ?? tool?._options?.annotations?.title ?? null;
@@ -4750,7 +4890,8 @@ function useServerEvents({
4750
4890
  if (content) {
4751
4891
  const finishedEvent = event;
4752
4892
  const traceId = finishedEvent.runId;
4753
- saveAIResponseRef.current(content, void 0, traceId);
4893
+ const turnMessages = extractTurnMessages(client.messages, messageCountAtRunStartRef.current);
4894
+ saveAIResponseRef.current(content, void 0, traceId, turnMessages);
4754
4895
  }
4755
4896
  setLoading(false);
4756
4897
  } else if (event.type === EventType2.RUN_ERROR) {
@@ -5326,19 +5467,19 @@ function useStableTools(tools) {
5326
5467
  return stableToolsRef.current;
5327
5468
  }
5328
5469
  function createStableToolWrapper(name, tool, latestToolsRef) {
5329
- const stableHandler = (input) => {
5470
+ const stableHandler = (input, ctx) => {
5330
5471
  const currentTool = latestToolsRef.current[name];
5331
5472
  if (!currentTool) {
5332
5473
  throw new Error(`Tool "${name}" no longer exists`);
5333
5474
  }
5334
- return currentTool.fn(input);
5475
+ return currentTool.fn(input, ctx);
5335
5476
  };
5336
- const stableExecute = async (input) => {
5477
+ const stableExecute = async (input, ctx) => {
5337
5478
  const currentTool = latestToolsRef.current[name];
5338
5479
  if (!currentTool) {
5339
5480
  throw new Error(`Tool "${name}" no longer exists`);
5340
5481
  }
5341
- return await currentTool._execute(input);
5482
+ return await currentTool._execute(input, ctx);
5342
5483
  };
5343
5484
  return {
5344
5485
  description: tool.description,
@@ -5546,7 +5687,10 @@ function useAIWorkflow(runner, workflowId) {
5546
5687
  console.log(`[useAIWorkflow] Executing tool: ${toolName}`, toolArgs);
5547
5688
  console.log(`[useAIWorkflow] Available tools:`, Object.keys(currentWorkflow.tools));
5548
5689
  try {
5549
- const result = await executeDefinedTool(currentWorkflow.tools, toolName, toolArgs);
5690
+ const noopCtx = {
5691
+ requestApproval: async () => ({ approved: true })
5692
+ };
5693
+ const result = await executeDefinedTool(currentWorkflow.tools, toolName, toolArgs, noopCtx);
5550
5694
  currentWorkflow.toolCalls.push({
5551
5695
  toolName,
5552
5696
  args: toolArgs,