@meetsmore-oss/use-ai-client 1.4.0 → 1.5.0

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,13 +1,13 @@
1
1
  import {
2
2
  generateChatId,
3
3
  generateMessageId
4
- } from "./chunk-QTQR7MAU.js";
4
+ } from "./chunk-STF3H6F5.js";
5
5
 
6
6
  // src/useAI.ts
7
- import { useState as useState11, useEffect as useEffect10, useRef as useRef11, useCallback as useCallback10, useMemo as useMemo6 } from "react";
7
+ import { useState as useState12, useEffect as useEffect11, useRef as useRef12, useCallback as useCallback11, useMemo as useMemo6 } from "react";
8
8
 
9
9
  // src/providers/useAIProvider.tsx
10
- import { createContext as createContext4, useContext as useContext4, useState as useState10, useEffect as useEffect9, useCallback as useCallback9, useRef as useRef9 } from "react";
10
+ import { createContext as createContext4, useContext as useContext4, useState as useState11, useEffect as useEffect10, useCallback as useCallback10, useRef as useRef10 } from "react";
11
11
 
12
12
  // src/types.ts
13
13
  import { EventType, ErrorCode } from "@meetsmore-oss/use-ai-core";
@@ -104,6 +104,11 @@ var defaultStrings = {
104
104
  RATE_LIMITED: "Too many requests. Please wait a moment before trying again.",
105
105
  /** Error for unknown/unexpected errors */
106
106
  UNKNOWN_ERROR: "An unexpected error occurred. Please try again."
107
+ },
108
+ // Tool execution status
109
+ toolExecution: {
110
+ /** Fallback messages when no tool title is provided (one randomly selected) */
111
+ fallbackMessages: ["Working", "Processing", "Thinking"]
107
112
  }
108
113
  };
109
114
  var StringsContext = createContext(defaultStrings);
@@ -1571,6 +1576,71 @@ function useDropdownState(options = {}) {
1571
1576
 
1572
1577
  // src/components/UseAIChatPanel.tsx
1573
1578
  import { Fragment, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1579
+ function FeedbackButton({ type, isSelected, onClick, selectedColor, unselectedColor }) {
1580
+ const buttonRef = useRef4(null);
1581
+ const handleClick = () => {
1582
+ if (!isSelected && buttonRef.current) {
1583
+ buttonRef.current.style.transform = "scale(1.3)";
1584
+ setTimeout(() => {
1585
+ if (buttonRef.current) {
1586
+ buttonRef.current.style.transform = "scale(1)";
1587
+ }
1588
+ }, 150);
1589
+ }
1590
+ onClick();
1591
+ };
1592
+ const thumbsUpPath = "M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3";
1593
+ const thumbsDownPath = "M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17";
1594
+ return /* @__PURE__ */ jsx10(
1595
+ "button",
1596
+ {
1597
+ ref: buttonRef,
1598
+ "data-testid": `feedback-${type}`,
1599
+ onClick: handleClick,
1600
+ title: type === "upvote" ? "Good response" : "Poor response",
1601
+ style: {
1602
+ background: "transparent",
1603
+ border: "none",
1604
+ padding: "4px",
1605
+ cursor: "pointer",
1606
+ color: isSelected ? selectedColor : unselectedColor,
1607
+ opacity: isSelected ? 1 : 0.5,
1608
+ transition: "all 0.15s",
1609
+ display: "flex",
1610
+ alignItems: "center",
1611
+ justifyContent: "center",
1612
+ borderRadius: "4px",
1613
+ transform: "scale(1)"
1614
+ },
1615
+ onMouseEnter: (e) => {
1616
+ if (!isSelected) {
1617
+ e.currentTarget.style.opacity = "0.8";
1618
+ e.currentTarget.style.color = selectedColor;
1619
+ }
1620
+ },
1621
+ onMouseLeave: (e) => {
1622
+ if (!isSelected) {
1623
+ e.currentTarget.style.opacity = "0.5";
1624
+ e.currentTarget.style.color = unselectedColor;
1625
+ }
1626
+ },
1627
+ children: /* @__PURE__ */ jsx10(
1628
+ "svg",
1629
+ {
1630
+ width: "14",
1631
+ height: "14",
1632
+ viewBox: "0 0 24 24",
1633
+ fill: isSelected ? "currentColor" : "none",
1634
+ stroke: "currentColor",
1635
+ strokeWidth: "2",
1636
+ strokeLinecap: "round",
1637
+ strokeLinejoin: "round",
1638
+ children: /* @__PURE__ */ jsx10("path", { d: type === "upvote" ? thumbsUpPath : thumbsDownPath })
1639
+ }
1640
+ )
1641
+ }
1642
+ );
1643
+ }
1574
1644
  function getTextContent(content) {
1575
1645
  if (typeof content === "string") {
1576
1646
  return content;
@@ -1603,7 +1673,10 @@ function UseAIChatPanel({
1603
1673
  onSaveCommand,
1604
1674
  onRenameCommand,
1605
1675
  onDeleteCommand,
1606
- closeButton
1676
+ closeButton,
1677
+ executingTool,
1678
+ feedbackEnabled,
1679
+ onFeedback
1607
1680
  }) {
1608
1681
  const strings = useStrings();
1609
1682
  const theme = useTheme();
@@ -2228,6 +2301,46 @@ function UseAIChatPanel({
2228
2301
  ]
2229
2302
  }
2230
2303
  ),
2304
+ message.role === "assistant" && message.traceId && feedbackEnabled && onFeedback && /* @__PURE__ */ jsxs7(
2305
+ "div",
2306
+ {
2307
+ "data-testid": "feedback-buttons",
2308
+ style: {
2309
+ display: "flex",
2310
+ gap: "4px",
2311
+ marginTop: "4px",
2312
+ padding: "0 4px"
2313
+ },
2314
+ children: [
2315
+ /* @__PURE__ */ jsx10(
2316
+ FeedbackButton,
2317
+ {
2318
+ type: "upvote",
2319
+ isSelected: message.feedback === "upvote",
2320
+ onClick: () => {
2321
+ const newFeedback = message.feedback === "upvote" ? null : "upvote";
2322
+ onFeedback(message.id, message.traceId, newFeedback);
2323
+ },
2324
+ selectedColor: theme.primaryColor,
2325
+ unselectedColor: theme.secondaryTextColor
2326
+ }
2327
+ ),
2328
+ /* @__PURE__ */ jsx10(
2329
+ FeedbackButton,
2330
+ {
2331
+ type: "downvote",
2332
+ isSelected: message.feedback === "downvote",
2333
+ onClick: () => {
2334
+ const newFeedback = message.feedback === "downvote" ? null : "downvote";
2335
+ onFeedback(message.id, message.traceId, newFeedback);
2336
+ },
2337
+ selectedColor: theme.errorTextColor,
2338
+ unselectedColor: theme.secondaryTextColor
2339
+ }
2340
+ )
2341
+ ]
2342
+ }
2343
+ ),
2231
2344
  /* @__PURE__ */ jsx10(
2232
2345
  "div",
2233
2346
  {
@@ -2289,10 +2402,7 @@ function UseAIChatPanel({
2289
2402
  } }) })
2290
2403
  ] }),
2291
2404
  fileProcessing.progress == null && /* @__PURE__ */ jsx10("span", { className: "dots", style: { marginLeft: "4px" }, children: "..." })
2292
- ] }) : /* @__PURE__ */ jsxs7(Fragment, { children: [
2293
- /* @__PURE__ */ jsx10("span", { style: { opacity: 0.6 }, children: strings.input.thinking }),
2294
- /* @__PURE__ */ jsx10("span", { className: "dots", style: { marginLeft: "4px" }, children: "..." })
2295
- ] })
2405
+ ] }) : /* @__PURE__ */ jsx10("span", { className: "dots", style: { opacity: 0.6 }, children: "..." })
2296
2406
  }
2297
2407
  )
2298
2408
  }
@@ -2379,7 +2489,7 @@ function UseAIChatPanel({
2379
2489
  value: input,
2380
2490
  onChange: handleInputChange,
2381
2491
  onKeyDown: handleKeyDown,
2382
- placeholder: connected ? strings.input.placeholder : strings.input.connectingPlaceholder,
2492
+ placeholder: !connected ? strings.input.connectingPlaceholder : loading ? `${executingTool?.displayText ?? strings.input.thinking}...` : strings.input.placeholder,
2383
2493
  disabled: !connected || loading,
2384
2494
  rows: 1,
2385
2495
  style: {
@@ -2641,7 +2751,10 @@ function UseAIChat({ floating = false }) {
2641
2751
  commands: ctx.commands.list,
2642
2752
  onSaveCommand: ctx.commands.save,
2643
2753
  onRenameCommand: ctx.commands.rename,
2644
- onDeleteCommand: ctx.commands.delete
2754
+ onDeleteCommand: ctx.commands.delete,
2755
+ executingTool: ctx.executingTool,
2756
+ feedbackEnabled: ctx.feedback?.enabled,
2757
+ onFeedback: ctx.feedback?.submit
2645
2758
  };
2646
2759
  if (floating) {
2647
2760
  return /* @__PURE__ */ jsx12(
@@ -2702,6 +2815,9 @@ var UseAIClient = class {
2702
2815
  _currentAssistantToolCalls = [];
2703
2816
  // Tool call assembly
2704
2817
  currentToolCalls = /* @__PURE__ */ new Map();
2818
+ // Feedback tracking
2819
+ _langfuseEnabled = false;
2820
+ langfuseConfigHandlers = /* @__PURE__ */ new Set();
2705
2821
  /**
2706
2822
  * Establishes a Socket.IO connection to the server.
2707
2823
  * Connection state changes are notified via onConnectionStateChange().
@@ -2744,6 +2860,11 @@ var UseAIClient = class {
2744
2860
  this._defaultAgent = data.defaultAgent;
2745
2861
  this.agentsChangeHandlers.forEach((handler) => handler(data.agents, data.defaultAgent));
2746
2862
  });
2863
+ this.socket.on("config", (data) => {
2864
+ console.log("[Client] Received server config:", data);
2865
+ this._langfuseEnabled = data.langfuseEnabled ?? false;
2866
+ this.langfuseConfigHandlers.forEach((handler) => handler(this._langfuseEnabled));
2867
+ });
2747
2868
  this.socket.on("connect_error", (error) => {
2748
2869
  console.warn("[UseAI] Connection error:", error.message);
2749
2870
  });
@@ -3160,6 +3281,41 @@ var UseAIClient = class {
3160
3281
  isConnected() {
3161
3282
  return this.socket !== null && this.socket.connected;
3162
3283
  }
3284
+ /**
3285
+ * Subscribes to Langfuse config changes.
3286
+ *
3287
+ * @param handler - Callback function receiving langfuse enabled status
3288
+ * @returns Cleanup function to unsubscribe
3289
+ */
3290
+ onLangfuseConfigChange(handler) {
3291
+ this.langfuseConfigHandlers.add(handler);
3292
+ handler(this._langfuseEnabled);
3293
+ return () => {
3294
+ this.langfuseConfigHandlers.delete(handler);
3295
+ };
3296
+ }
3297
+ /**
3298
+ * Submits feedback for an assistant message.
3299
+ * Sends feedback to the server, which forwards it to Langfuse.
3300
+ *
3301
+ * @param messageId - The client-side message ID
3302
+ * @param traceId - The Langfuse trace ID (runId from RUN_FINISHED)
3303
+ * @param feedback - 'upvote' for positive, 'downvote' for negative, null to remove
3304
+ */
3305
+ submitFeedback(messageId, traceId, feedback) {
3306
+ if (!this.socket?.connected) {
3307
+ console.warn("[UseAI] Cannot submit feedback: not connected");
3308
+ return;
3309
+ }
3310
+ if (!this._langfuseEnabled) {
3311
+ console.warn("[UseAI] Cannot submit feedback: Langfuse not enabled on server");
3312
+ return;
3313
+ }
3314
+ this.send({
3315
+ type: "message_feedback",
3316
+ data: { messageId, traceId, feedback }
3317
+ });
3318
+ }
3163
3319
  };
3164
3320
 
3165
3321
  // src/defineTool.ts
@@ -3199,8 +3355,8 @@ function defineTool(description, schemaOrFn, fnOrOptions, options) {
3199
3355
  description,
3200
3356
  parameters
3201
3357
  };
3202
- if (this._options.confirmationRequired) {
3203
- toolDef.confirmationRequired = true;
3358
+ if (this._options.annotations) {
3359
+ toolDef.annotations = this._options.annotations;
3204
3360
  }
3205
3361
  return toolDef;
3206
3362
  },
@@ -3398,7 +3554,9 @@ function transformMessagesToUI(storageMessages) {
3398
3554
  role: msg.role,
3399
3555
  content: msg.content,
3400
3556
  timestamp: msg.createdAt,
3401
- displayMode: msg.displayMode
3557
+ displayMode: msg.displayMode,
3558
+ traceId: msg.traceId,
3559
+ feedback: msg.feedback
3402
3560
  }));
3403
3561
  }
3404
3562
  function transformMessagesToClientFormat(uiMessages) {
@@ -3414,6 +3572,8 @@ function transformMessagesToClientFormat(uiMessages) {
3414
3572
  function useChatManagement({
3415
3573
  repository,
3416
3574
  clientRef,
3575
+ messages,
3576
+ setMessages,
3417
3577
  onSendMessage,
3418
3578
  setOpen,
3419
3579
  connected,
@@ -3421,7 +3581,6 @@ function useChatManagement({
3421
3581
  }) {
3422
3582
  const [currentChatId, setCurrentChatId] = useState5(null);
3423
3583
  const [pendingChatId, setPendingChatId] = useState5(null);
3424
- const [messages, setMessages] = useState5([]);
3425
3584
  const currentChatIdSnapshot = useRef5(null);
3426
3585
  const pendingChatIdSnapshot = useRef5(null);
3427
3586
  useEffect5(() => {
@@ -3540,7 +3699,7 @@ function useChatManagement({
3540
3699
  console.error("[ChatManagement] Chat not found:", chatId);
3541
3700
  return false;
3542
3701
  }
3543
- const { generateMessageId: generateMessageId2 } = await import("./types-64CH2HXY.js");
3702
+ const { generateMessageId: generateMessageId2 } = await import("./types-GWPQMSYT.js");
3544
3703
  chat.messages.push({
3545
3704
  id: generateMessageId2(),
3546
3705
  role: "user",
@@ -3562,7 +3721,7 @@ function useChatManagement({
3562
3721
  return false;
3563
3722
  }
3564
3723
  }, [repository, reloadMessages]);
3565
- const saveAIResponse = useCallback4(async (content, displayMode) => {
3724
+ const saveAIResponse = useCallback4(async (content, displayMode, traceId) => {
3566
3725
  const currentChatIdValue = currentChatIdSnapshot.current;
3567
3726
  const pendingChatIdValue = pendingChatIdSnapshot.current;
3568
3727
  const displayedChatId2 = pendingChatIdValue || currentChatIdValue;
@@ -3576,13 +3735,14 @@ function useChatManagement({
3576
3735
  console.error("[ChatManagement] Chat not found:", currentChatIdValue);
3577
3736
  return;
3578
3737
  }
3579
- const { generateMessageId: generateMessageId2 } = await import("./types-64CH2HXY.js");
3738
+ const { generateMessageId: generateMessageId2 } = await import("./types-GWPQMSYT.js");
3580
3739
  chat.messages.push({
3581
3740
  id: generateMessageId2(),
3582
3741
  role: "assistant",
3583
3742
  content,
3584
3743
  createdAt: /* @__PURE__ */ new Date(),
3585
- displayMode
3744
+ displayMode,
3745
+ traceId
3586
3746
  });
3587
3747
  if (!chat.title) {
3588
3748
  const firstUserMessage = chat.messages.find((msg) => msg.role === "user");
@@ -3700,7 +3860,6 @@ function useChatManagement({
3700
3860
  return {
3701
3861
  currentChatId,
3702
3862
  pendingChatId,
3703
- messages,
3704
3863
  displayedChatId,
3705
3864
  createNewChat,
3706
3865
  loadChat,
@@ -4094,6 +4253,68 @@ function usePromptState({
4094
4253
  };
4095
4254
  }
4096
4255
 
4256
+ // src/hooks/useFeedback.ts
4257
+ import { useState as useState10, useEffect as useEffect9, useRef as useRef9, useCallback as useCallback9 } from "react";
4258
+ function useFeedback({
4259
+ clientRef,
4260
+ repository,
4261
+ getDisplayedChatId,
4262
+ setMessages
4263
+ }) {
4264
+ const [enabled, setEnabled] = useState10(false);
4265
+ const enabledRef = useRef9(false);
4266
+ useEffect9(() => {
4267
+ enabledRef.current = enabled;
4268
+ }, [enabled]);
4269
+ useEffect9(() => {
4270
+ const client = clientRef.current;
4271
+ if (!client) return;
4272
+ const unsubscribe = client.onLangfuseConfigChange((isEnabled) => {
4273
+ setEnabled(isEnabled);
4274
+ });
4275
+ return unsubscribe;
4276
+ }, [clientRef.current]);
4277
+ const updateFeedbackInStorage = useCallback9(async (messageId, feedback) => {
4278
+ const displayedChatId = getDisplayedChatId();
4279
+ if (!displayedChatId) {
4280
+ console.warn("[useFeedback] No chat ID, cannot update feedback");
4281
+ return;
4282
+ }
4283
+ try {
4284
+ const chat = await repository.loadChat(displayedChatId);
4285
+ if (!chat) {
4286
+ console.error("[useFeedback] Chat not found:", displayedChatId);
4287
+ return;
4288
+ }
4289
+ const message = chat.messages.find((msg) => msg.id === messageId);
4290
+ if (message) {
4291
+ message.feedback = feedback;
4292
+ await repository.saveChat(chat);
4293
+ setMessages(
4294
+ (prevMessages) => prevMessages.map(
4295
+ (msg) => msg.id === messageId ? { ...msg, feedback } : msg
4296
+ )
4297
+ );
4298
+ } else {
4299
+ console.warn("[useFeedback] Message not found:", messageId);
4300
+ }
4301
+ } catch (error) {
4302
+ console.error("[useFeedback] Failed to update feedback:", error);
4303
+ }
4304
+ }, [repository, getDisplayedChatId, setMessages]);
4305
+ const submitFeedback = useCallback9((messageId, traceId, feedback) => {
4306
+ updateFeedbackInStorage(messageId, feedback);
4307
+ const client = clientRef.current;
4308
+ if (client && enabledRef.current) {
4309
+ client.submitFeedback(messageId, traceId, feedback);
4310
+ }
4311
+ }, [clientRef, updateFeedbackInStorage]);
4312
+ return {
4313
+ enabled,
4314
+ submitFeedback
4315
+ };
4316
+ }
4317
+
4097
4318
  // src/providers/useAIProvider.tsx
4098
4319
  import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
4099
4320
  var __UseAIContext = createContext4(null);
@@ -4175,21 +4396,24 @@ function UseAIProvider({
4175
4396
  const fileUploadConfig = fileUploadConfigProp === false ? void 0 : fileUploadConfigProp ?? DEFAULT_FILE_UPLOAD_CONFIG;
4176
4397
  const theme = { ...defaultTheme, ...customTheme };
4177
4398
  const strings = { ...defaultStrings, ...customStrings };
4178
- const [connected, setConnected] = useState10(false);
4179
- const [isChatOpen, setIsChatOpen] = useState10(false);
4180
- const [loading, setLoading] = useState10(false);
4181
- const [fileProcessingState, setFileProcessingState] = useState10(null);
4182
- const handleSetChatOpen = useCallback9((open) => {
4399
+ const [connected, setConnected] = useState11(false);
4400
+ const [isChatOpen, setIsChatOpen] = useState11(false);
4401
+ const [loading, setLoading] = useState11(false);
4402
+ const [messages, setMessages] = useState11([]);
4403
+ const [fileProcessingState, setFileProcessingState] = useState11(null);
4404
+ const handleSetChatOpen = useCallback10((open) => {
4183
4405
  setIsChatOpen(open);
4184
4406
  onOpenChange?.(open);
4185
4407
  }, [onOpenChange]);
4186
- const [streamingText, setStreamingText] = useState10("");
4187
- const streamingChatIdRef = useRef9(null);
4188
- const clientRef = useRef9(null);
4189
- const repositoryRef = useRef9(
4408
+ const [streamingText, setStreamingText] = useState11("");
4409
+ const streamingChatIdRef = useRef10(null);
4410
+ const [executingTool, setExecutingTool] = useState11(null);
4411
+ const executingToolFallbackRef = useRef10(null);
4412
+ const clientRef = useRef10(null);
4413
+ const repositoryRef = useRef10(
4190
4414
  chatRepository || new LocalStorageChatRepository()
4191
4415
  );
4192
- const handleSendMessageRef = useRef9(null);
4416
+ const handleSendMessageRef = useRef10(null);
4193
4417
  const {
4194
4418
  registerTools,
4195
4419
  unregisterTools,
@@ -4211,7 +4435,7 @@ function UseAIProvider({
4211
4435
  clientRef,
4212
4436
  connected
4213
4437
  });
4214
- const stableSendMessage = useCallback9(async (message, attachments) => {
4438
+ const stableSendMessage = useCallback10(async (message, attachments) => {
4215
4439
  if (handleSendMessageRef.current) {
4216
4440
  await handleSendMessageRef.current(message, attachments);
4217
4441
  }
@@ -4219,6 +4443,8 @@ function UseAIProvider({
4219
4443
  const chatManagement = useChatManagement({
4220
4444
  repository: repositoryRef.current,
4221
4445
  clientRef,
4446
+ messages,
4447
+ setMessages,
4222
4448
  onSendMessage: stableSendMessage,
4223
4449
  setOpen: handleSetChatOpen,
4224
4450
  connected,
@@ -4227,7 +4453,6 @@ function UseAIProvider({
4227
4453
  const {
4228
4454
  currentChatId,
4229
4455
  pendingChatId,
4230
- messages,
4231
4456
  displayedChatId,
4232
4457
  createNewChat,
4233
4458
  loadChat,
@@ -4241,6 +4466,12 @@ function UseAIProvider({
4241
4466
  getCurrentChat,
4242
4467
  updateMetadata
4243
4468
  } = chatManagement;
4469
+ const feedback = useFeedback({
4470
+ clientRef,
4471
+ repository: repositoryRef.current,
4472
+ getDisplayedChatId: () => displayedChatId,
4473
+ setMessages
4474
+ });
4244
4475
  const {
4245
4476
  availableAgents,
4246
4477
  defaultAgent,
@@ -4254,7 +4485,7 @@ function UseAIProvider({
4254
4485
  renameCommand,
4255
4486
  deleteCommand
4256
4487
  } = useCommandManagement({ repository: commandRepository });
4257
- useEffect9(() => {
4488
+ useEffect10(() => {
4258
4489
  console.log("[UseAIProvider] Initializing client with serverUrl:", serverUrl);
4259
4490
  const client = new UseAIClient(serverUrl);
4260
4491
  if (mcpHeadersProvider) {
@@ -4267,9 +4498,19 @@ function UseAIProvider({
4267
4498
  console.log("[UseAIProvider] Connecting...");
4268
4499
  client.connect();
4269
4500
  const unsubscribe = client.onEvent("globalChat", async (event) => {
4270
- if (event.type === EventType.TOOL_CALL_END) {
4501
+ if (event.type === EventType.TOOL_CALL_START) {
4502
+ const e = event;
4503
+ const tool = aggregatedToolsRef.current[e.toolCallName];
4504
+ const title = e.annotations?.title ?? tool?._options?.annotations?.title ?? null;
4505
+ if (!title) {
4506
+ const fallbacks = strings.toolExecution.fallbackMessages;
4507
+ executingToolFallbackRef.current = fallbacks[Math.floor(Math.random() * fallbacks.length)];
4508
+ }
4509
+ setExecutingTool({ toolCallId: e.toolCallId, title });
4510
+ } else if (event.type === EventType.TOOL_CALL_END) {
4271
4511
  const toolCallEnd = event;
4272
4512
  const toolCallId = toolCallEnd.toolCallId;
4513
+ setExecutingTool((prev) => prev?.toolCallId === toolCallId ? null : prev);
4273
4514
  const toolCallData = client["currentToolCalls"].get(toolCallId);
4274
4515
  if (!toolCallData) {
4275
4516
  console.error(`[Provider] Tool call ${toolCallId} not found`);
@@ -4319,14 +4560,16 @@ function UseAIProvider({
4319
4560
  const contentEvent = event;
4320
4561
  setStreamingText((prev) => prev + contentEvent.delta);
4321
4562
  } else if (event.type === EventType.TEXT_MESSAGE_END) {
4563
+ setStreamingText("");
4564
+ streamingChatIdRef.current = null;
4565
+ } else if (event.type === EventType.RUN_FINISHED) {
4322
4566
  const content = client.currentMessageContent;
4323
4567
  if (content) {
4324
- console.log("[Provider] Received text message:", content.substring(0, 100));
4325
- saveAIResponse(content);
4326
- setStreamingText("");
4327
- streamingChatIdRef.current = null;
4328
- setLoading(false);
4568
+ const finishedEvent = event;
4569
+ const traceId = finishedEvent.runId;
4570
+ saveAIResponse(content, void 0, traceId);
4329
4571
  }
4572
+ setLoading(false);
4330
4573
  } else if (event.type === EventType.RUN_ERROR) {
4331
4574
  const errorEvent = event;
4332
4575
  const errorCode = errorEvent.message;
@@ -4345,15 +4588,15 @@ function UseAIProvider({
4345
4588
  client.disconnect();
4346
4589
  };
4347
4590
  }, [serverUrl]);
4348
- useEffect9(() => {
4591
+ useEffect10(() => {
4349
4592
  const client = clientRef.current;
4350
4593
  if (!client) return;
4351
4594
  if (mcpHeadersProvider) {
4352
4595
  client.setMcpHeadersProvider(mcpHeadersProvider);
4353
4596
  }
4354
4597
  }, [mcpHeadersProvider]);
4355
- const lastRegisteredToolsRef = useRef9("");
4356
- useEffect9(() => {
4598
+ const lastRegisteredToolsRef = useRef10("");
4599
+ useEffect10(() => {
4357
4600
  const client = clientRef.current;
4358
4601
  if (!client || !client.isConnected() || !hasTools) return;
4359
4602
  const toolKeys = Object.keys(aggregatedTools).sort().join(",");
@@ -4371,7 +4614,7 @@ function UseAIProvider({
4371
4614
  console.error("Failed to register tools:", err);
4372
4615
  }
4373
4616
  }, [hasTools, aggregatedTools, connected]);
4374
- const handleSendMessage = useCallback9(async (message, attachments) => {
4617
+ const handleSendMessage = useCallback10(async (message, attachments) => {
4375
4618
  if (!clientRef.current) return;
4376
4619
  setStreamingText("");
4377
4620
  const activatedChatId = activatePendingChat();
@@ -4467,6 +4710,9 @@ function UseAIProvider({
4467
4710
  }
4468
4711
  };
4469
4712
  const effectiveStreamingText = streamingChatIdRef.current === displayedChatId ? streamingText : "";
4713
+ const executingToolDisplay = executingTool ? {
4714
+ displayText: executingTool.title ?? executingToolFallbackRef.current ?? strings.toolExecution.fallbackMessages[0]
4715
+ } : null;
4470
4716
  const chatUIContextValue = {
4471
4717
  connected,
4472
4718
  loading,
@@ -4499,6 +4745,11 @@ function UseAIProvider({
4499
4745
  ui: {
4500
4746
  isOpen: isChatOpen,
4501
4747
  setOpen: handleSetChatOpen
4748
+ },
4749
+ executingTool: executingToolDisplay,
4750
+ feedback: {
4751
+ enabled: feedback.enabled,
4752
+ submit: feedback.submitFeedback
4502
4753
  }
4503
4754
  };
4504
4755
  const isUIDisabled = CustomButton === null || CustomChat === null;
@@ -4525,7 +4776,10 @@ function UseAIProvider({
4525
4776
  commands,
4526
4777
  onSaveCommand: saveCommand,
4527
4778
  onRenameCommand: renameCommand,
4528
- onDeleteCommand: deleteCommand
4779
+ onDeleteCommand: deleteCommand,
4780
+ executingTool: executingToolDisplay,
4781
+ feedbackEnabled: feedback.enabled,
4782
+ onFeedback: feedback.submitFeedback
4529
4783
  };
4530
4784
  const renderDefaultChat = () => {
4531
4785
  if (isUIDisabled) return null;
@@ -4589,11 +4843,11 @@ function useAIContext() {
4589
4843
  }
4590
4844
 
4591
4845
  // src/hooks/useStableTools.ts
4592
- import { useRef as useRef10 } from "react";
4846
+ import { useRef as useRef11 } from "react";
4593
4847
  function useStableTools(tools) {
4594
- const latestToolsRef = useRef10({});
4595
- const stableToolsRef = useRef10({});
4596
- const prevToolNamesRef = useRef10("");
4848
+ const latestToolsRef = useRef11({});
4849
+ const stableToolsRef = useRef11({});
4850
+ const prevToolNamesRef = useRef11("");
4597
4851
  if (!tools) {
4598
4852
  latestToolsRef.current = {};
4599
4853
  return void 0;
@@ -4663,25 +4917,25 @@ function useAI(options = {}) {
4663
4917
  const { connected, tools, client, prompts } = useAIContext();
4664
4918
  const { register: registerTools, unregister: unregisterTools } = tools;
4665
4919
  const { update: updatePrompt, registerWaiter, unregisterWaiter } = prompts;
4666
- const [response, setResponse] = useState11(null);
4667
- const [loading, setLoading] = useState11(false);
4668
- const [error, setError] = useState11(null);
4669
- const hookId = useRef11(`useAI-${Math.random().toString(36).substr(2, 9)}`);
4670
- const toolsRef = useRef11({});
4671
- const componentRef = useRef11(null);
4672
- const promptChangeResolvers = useRef11([]);
4920
+ const [response, setResponse] = useState12(null);
4921
+ const [loading, setLoading] = useState12(false);
4922
+ const [error, setError] = useState12(null);
4923
+ const hookId = useRef12(`useAI-${Math.random().toString(36).substr(2, 9)}`);
4924
+ const toolsRef = useRef12({});
4925
+ const componentRef = useRef12(null);
4926
+ const promptChangeResolvers = useRef12([]);
4673
4927
  const stableTools = useStableTools(options.tools);
4674
4928
  const toolsKey = useMemo6(() => {
4675
4929
  if (!options.tools) return "";
4676
4930
  return Object.keys(options.tools).sort().join(",");
4677
4931
  }, [options.tools]);
4678
4932
  const memoizedSuggestions = useMemo6(() => options.suggestions, [options.suggestions]);
4679
- useEffect10(() => {
4933
+ useEffect11(() => {
4680
4934
  if (componentRef.current) {
4681
4935
  componentRef.current.setAttribute("data-useai-context", "true");
4682
4936
  }
4683
4937
  }, []);
4684
- const waitForPromptChange = useCallback10(() => {
4938
+ const waitForPromptChange = useCallback11(() => {
4685
4939
  return new Promise((resolve) => {
4686
4940
  const timeoutMs = 100;
4687
4941
  const timeoutId = setTimeout(() => {
@@ -4698,14 +4952,14 @@ function useAI(options = {}) {
4698
4952
  promptChangeResolvers.current.push(resolveAndCleanup);
4699
4953
  });
4700
4954
  }, []);
4701
- useEffect10(() => {
4955
+ useEffect11(() => {
4702
4956
  if (!enabled || options.invisible) return;
4703
4957
  registerWaiter(hookId.current, waitForPromptChange);
4704
4958
  return () => {
4705
4959
  unregisterWaiter(hookId.current);
4706
4960
  };
4707
4961
  }, [enabled, options.invisible, registerWaiter, unregisterWaiter, waitForPromptChange]);
4708
- useEffect10(() => {
4962
+ useEffect11(() => {
4709
4963
  if (!enabled) return;
4710
4964
  updatePrompt(hookId.current, options.prompt, memoizedSuggestions);
4711
4965
  if (promptChangeResolvers.current.length > 0) {
@@ -4713,15 +4967,15 @@ function useAI(options = {}) {
4713
4967
  promptChangeResolvers.current = [];
4714
4968
  }
4715
4969
  }, [enabled, options.prompt, memoizedSuggestions, updatePrompt]);
4716
- const updatePromptRef = useRef11(updatePrompt);
4970
+ const updatePromptRef = useRef12(updatePrompt);
4717
4971
  updatePromptRef.current = updatePrompt;
4718
- useEffect10(() => {
4972
+ useEffect11(() => {
4719
4973
  const id = hookId.current;
4720
4974
  return () => {
4721
4975
  updatePromptRef.current(id, void 0, void 0);
4722
4976
  };
4723
4977
  }, []);
4724
- useEffect10(() => {
4978
+ useEffect11(() => {
4725
4979
  if (!enabled) return;
4726
4980
  if (stableTools) {
4727
4981
  const componentId = options.id || componentRef.current?.id;
@@ -4735,7 +4989,7 @@ function useAI(options = {}) {
4735
4989
  }
4736
4990
  };
4737
4991
  }, [enabled, toolsKey, stableTools, options.id, options.invisible, registerTools, unregisterTools]);
4738
- useEffect10(() => {
4992
+ useEffect11(() => {
4739
4993
  if (!enabled || !client) return;
4740
4994
  const unsubscribe = client.onEvent(hookId.current, (event) => {
4741
4995
  handleAGUIEvent(event);
@@ -4744,7 +4998,7 @@ function useAI(options = {}) {
4744
4998
  unsubscribe();
4745
4999
  };
4746
5000
  }, [enabled, client]);
4747
- const handleAGUIEvent = useCallback10(async (event) => {
5001
+ const handleAGUIEvent = useCallback11(async (event) => {
4748
5002
  switch (event.type) {
4749
5003
  case EventType.TEXT_MESSAGE_END: {
4750
5004
  const content = client?.currentMessageContent;
@@ -4764,7 +5018,7 @@ function useAI(options = {}) {
4764
5018
  }
4765
5019
  }
4766
5020
  }, [client, options.onError]);
4767
- const generate = useCallback10(async (prompt) => {
5021
+ const generate = useCallback11(async (prompt) => {
4768
5022
  if (!enabled) {
4769
5023
  const error2 = new Error("AI features are disabled");
4770
5024
  setError(error2);
@@ -4800,17 +5054,17 @@ function useAI(options = {}) {
4800
5054
  }
4801
5055
 
4802
5056
  // src/useAIWorkflow.ts
4803
- import { useState as useState12, useCallback as useCallback11, useRef as useRef12, useEffect as useEffect11 } from "react";
5057
+ import { useState as useState13, useCallback as useCallback12, useRef as useRef13, useEffect as useEffect12 } from "react";
4804
5058
  import { EventType as EventType3 } from "@meetsmore-oss/use-ai-core";
4805
5059
  import { v4 as uuidv43 } from "uuid";
4806
5060
  function useAIWorkflow(runner, workflowId) {
4807
5061
  const { connected, client } = useAIContext();
4808
- const [status, setStatus] = useState12("idle");
4809
- const [text, setText] = useState12(null);
4810
- const [error, setError] = useState12(null);
4811
- const currentWorkflowRef = useRef12(null);
4812
- const eventListenerIdRef = useRef12(`useAIWorkflow-${Math.random().toString(36).substr(2, 9)}`);
4813
- const handleWorkflowEvent = useCallback11(async (event) => {
5062
+ const [status, setStatus] = useState13("idle");
5063
+ const [text, setText] = useState13(null);
5064
+ const [error, setError] = useState13(null);
5065
+ const currentWorkflowRef = useRef13(null);
5066
+ const eventListenerIdRef = useRef13(`useAIWorkflow-${Math.random().toString(36).substr(2, 9)}`);
5067
+ const handleWorkflowEvent = useCallback12(async (event) => {
4814
5068
  const currentWorkflow = currentWorkflowRef.current;
4815
5069
  if (!currentWorkflow) return;
4816
5070
  if (event.type === EventType3.RUN_STARTED) {
@@ -4895,14 +5149,14 @@ function useAIWorkflow(runner, workflowId) {
4895
5149
  }
4896
5150
  }
4897
5151
  }, [client]);
4898
- useEffect11(() => {
5152
+ useEffect12(() => {
4899
5153
  if (!client) return;
4900
5154
  const unsubscribe = client.onEvent(eventListenerIdRef.current, handleWorkflowEvent);
4901
5155
  return () => {
4902
5156
  unsubscribe();
4903
5157
  };
4904
5158
  }, [client, handleWorkflowEvent]);
4905
- const trigger = useCallback11(async (options) => {
5159
+ const trigger = useCallback12(async (options) => {
4906
5160
  if (!client?.isConnected()) {
4907
5161
  const err = new Error("Not connected to server");
4908
5162
  setError(err);
@@ -4991,6 +5245,7 @@ export {
4991
5245
  useChatManagement,
4992
5246
  useCommandManagement,
4993
5247
  useDropdownState,
5248
+ useFeedback,
4994
5249
  useFileUpload,
4995
5250
  usePromptState,
4996
5251
  useSlashCommands,