@meetsmore-oss/use-ai-client 1.6.0 → 1.8.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
@@ -4,13 +4,10 @@ import {
4
4
  } from "./chunk-STF3H6F5.js";
5
5
 
6
6
  // src/useAI.ts
7
- import { useState as useState14, useEffect as useEffect11, useRef as useRef13, useCallback as useCallback12, useMemo as useMemo6 } from "react";
7
+ import { useState as useState14, useEffect as useEffect12, useLayoutEffect, useRef as useRef14, useCallback as useCallback13, useMemo as useMemo6 } from "react";
8
8
 
9
9
  // src/providers/useAIProvider.tsx
10
- import { createContext as createContext4, useContext as useContext4, useState as useState13, useEffect as useEffect10, useCallback as useCallback11, useRef as useRef11 } from "react";
11
-
12
- // src/types.ts
13
- import { EventType, ErrorCode, TOOL_APPROVAL_REQUEST } from "@meetsmore-oss/use-ai-core";
10
+ import { createContext as createContext4, useContext as useContext4, useState as useState13, useEffect as useEffect11, useCallback as useCallback12, useRef as useRef12 } from "react";
14
11
 
15
12
  // src/theme/strings.ts
16
13
  import { createContext, useContext } from "react";
@@ -3051,7 +3048,7 @@ function UseAIChat({ floating = false }) {
3051
3048
 
3052
3049
  // src/client.ts
3053
3050
  import { io } from "socket.io-client";
3054
- import { EventType as EventType2 } from "@meetsmore-oss/use-ai-core";
3051
+ import { EventType } from "@meetsmore-oss/use-ai-core";
3055
3052
  import { v4 as uuidv42 } from "uuid";
3056
3053
  var UseAIClient = class {
3057
3054
  /**
@@ -3072,8 +3069,6 @@ var UseAIClient = class {
3072
3069
  _tools = [];
3073
3070
  _messages = [];
3074
3071
  _state = null;
3075
- // MCP headers provider
3076
- mcpHeadersProvider;
3077
3072
  // Agent selection
3078
3073
  _availableAgents = [];
3079
3074
  _defaultAgent = null;
@@ -3148,7 +3143,7 @@ var UseAIClient = class {
3148
3143
  });
3149
3144
  }
3150
3145
  handleEvent(event) {
3151
- if (event.type === EventType2.RUN_STARTED) {
3146
+ if (event.type === EventType.RUN_STARTED) {
3152
3147
  this._currentAssistantMessage = {
3153
3148
  id: uuidv42(),
3154
3149
  role: "assistant",
@@ -3156,31 +3151,31 @@ var UseAIClient = class {
3156
3151
  };
3157
3152
  this._currentAssistantToolCalls = [];
3158
3153
  }
3159
- if (event.type === EventType2.TEXT_MESSAGE_START) {
3154
+ if (event.type === EventType.TEXT_MESSAGE_START) {
3160
3155
  const e = event;
3161
3156
  this._currentMessageId = e.messageId;
3162
3157
  this._currentMessageContent = "";
3163
- } else if (event.type === EventType2.TEXT_MESSAGE_CONTENT) {
3158
+ } else if (event.type === EventType.TEXT_MESSAGE_CONTENT) {
3164
3159
  const e = event;
3165
3160
  this._currentMessageContent += e.delta;
3166
- } else if (event.type === EventType2.TEXT_MESSAGE_END) {
3161
+ } else if (event.type === EventType.TEXT_MESSAGE_END) {
3167
3162
  if (this._currentAssistantMessage) {
3168
3163
  this._currentAssistantMessage.content = this._currentMessageContent;
3169
3164
  }
3170
3165
  this._currentMessageId = null;
3171
- } else if (event.type === EventType2.TOOL_CALL_START) {
3166
+ } else if (event.type === EventType.TOOL_CALL_START) {
3172
3167
  const e = event;
3173
3168
  this.currentToolCalls.set(e.toolCallId, {
3174
3169
  name: e.toolCallName,
3175
3170
  args: ""
3176
3171
  });
3177
- } else if (event.type === EventType2.TOOL_CALL_ARGS) {
3172
+ } else if (event.type === EventType.TOOL_CALL_ARGS) {
3178
3173
  const e = event;
3179
3174
  const toolCall = this.currentToolCalls.get(e.toolCallId);
3180
3175
  if (toolCall) {
3181
3176
  toolCall.args += e.delta;
3182
3177
  }
3183
- } else if (event.type === EventType2.TOOL_CALL_END) {
3178
+ } else if (event.type === EventType.TOOL_CALL_END) {
3184
3179
  const e = event;
3185
3180
  const toolCall = this.currentToolCalls.get(e.toolCallId);
3186
3181
  if (toolCall) {
@@ -3193,7 +3188,7 @@ var UseAIClient = class {
3193
3188
  }
3194
3189
  });
3195
3190
  }
3196
- } else if (event.type === EventType2.RUN_FINISHED) {
3191
+ } else if (event.type === EventType.RUN_FINISHED) {
3197
3192
  if (this._currentAssistantMessage) {
3198
3193
  const assistantMessage = {
3199
3194
  id: this._currentAssistantMessage.id,
@@ -3232,22 +3227,15 @@ var UseAIClient = class {
3232
3227
  updateState(state) {
3233
3228
  this._state = state;
3234
3229
  }
3235
- /**
3236
- * Sets the MCP headers provider.
3237
- * The provider will be called each time a message is sent to get fresh headers.
3238
- *
3239
- * @param provider - Function that returns MCP headers configuration
3240
- */
3241
- setMcpHeadersProvider(provider) {
3242
- this.mcpHeadersProvider = provider;
3243
- }
3244
3230
  /**
3245
3231
  * Sends a user prompt to the AI.
3246
3232
  *
3247
3233
  * @param prompt - The user's prompt/question (text part)
3248
3234
  * @param multimodalContent - Optional multimodal content (text, images, files)
3235
+ * @param forwardedProps - Optional props to forward to the server (e.g., telemetryMetadata, mcpHeaders).
3236
+ * Internally merged with other forwardedProps.
3249
3237
  */
3250
- async sendPrompt(prompt, multimodalContent) {
3238
+ async sendPrompt(prompt, multimodalContent, forwardedProps) {
3251
3239
  let messageContent = prompt;
3252
3240
  if (multimodalContent && multimodalContent.length > 0) {
3253
3241
  messageContent = multimodalContent.map((part) => {
@@ -3277,14 +3265,6 @@ var UseAIClient = class {
3277
3265
  // Type cast needed for Message type compatibility
3278
3266
  };
3279
3267
  this._messages.push(userMessage);
3280
- let mcpHeaders;
3281
- if (this.mcpHeadersProvider) {
3282
- try {
3283
- mcpHeaders = await this.mcpHeadersProvider();
3284
- } catch (error) {
3285
- console.error("[UseAIClient] Failed to get MCP headers:", error);
3286
- }
3287
- }
3288
3268
  const runInput = {
3289
3269
  threadId: this.threadId,
3290
3270
  // Use getter to ensure non-null
@@ -3299,8 +3279,8 @@ var UseAIClient = class {
3299
3279
  state: this._state,
3300
3280
  context: [],
3301
3281
  forwardedProps: {
3302
- ...mcpHeaders ? { mcpHeaders } : {},
3303
- ...this._selectedAgent ? { agent: this._selectedAgent } : {}
3282
+ ...this._selectedAgent ? { agent: this._selectedAgent } : {},
3283
+ ...forwardedProps || {}
3304
3284
  }
3305
3285
  };
3306
3286
  this.send({
@@ -3325,7 +3305,13 @@ var UseAIClient = class {
3325
3305
  messageId: uuidv42(),
3326
3306
  toolCallId,
3327
3307
  content: JSON.stringify(result),
3328
- role: "tool"
3308
+ role: "tool",
3309
+ // use-ai extension: include current tools and state for mid-run updates
3310
+ // (e.g., when navigation causes new components to mount)
3311
+ forwardedProps: {
3312
+ tools: this._tools,
3313
+ state: this._state
3314
+ }
3329
3315
  }
3330
3316
  };
3331
3317
  const toolResultMsg = {
@@ -3388,7 +3374,7 @@ var UseAIClient = class {
3388
3374
  */
3389
3375
  onTextMessage(handler) {
3390
3376
  return this.onEvent("text-message-handler", (event) => {
3391
- if (event.type === EventType2.TEXT_MESSAGE_END && this._currentMessageContent) {
3377
+ if (event.type === EventType.TEXT_MESSAGE_END && this._currentMessageContent) {
3392
3378
  handler(this._currentMessageContent);
3393
3379
  }
3394
3380
  });
@@ -3402,7 +3388,7 @@ var UseAIClient = class {
3402
3388
  */
3403
3389
  onToolCall(handler) {
3404
3390
  return this.onEvent("tool-call-handler", (event) => {
3405
- if (event.type === EventType2.TOOL_CALL_END) {
3391
+ if (event.type === EventType.TOOL_CALL_END) {
3406
3392
  const e = event;
3407
3393
  const toolCall = this.currentToolCalls.get(e.toolCallId);
3408
3394
  if (toolCall) {
@@ -3867,11 +3853,7 @@ function useChatManagement({
3867
3853
  clientRef,
3868
3854
  messages,
3869
3855
  setMessages,
3870
- onSendMessage,
3871
- setOpen,
3872
- connected,
3873
- loading,
3874
- hasPendingApproval
3856
+ connected
3875
3857
  }) {
3876
3858
  const [currentChatId, setCurrentChatId] = useState6(null);
3877
3859
  const [pendingChatId, setPendingChatId] = useState6(null);
@@ -3902,7 +3884,7 @@ function useChatManagement({
3902
3884
  const reloadMessages = useCallback4(async (chatId) => {
3903
3885
  const loadedMessages = await loadChatMessages(chatId);
3904
3886
  setMessages(loadedMessages);
3905
- }, [loadChatMessages]);
3887
+ }, [loadChatMessages, setMessages]);
3906
3888
  const createNewChat = useCallback4(async (options) => {
3907
3889
  console.log("[ChatManagement] createNewChat called - currentChatId:", currentChatId, "pendingChatId:", pendingChatId, "messages.length:", messages.length);
3908
3890
  if (pendingChatId && messages.length === 0) {
@@ -3923,7 +3905,7 @@ function useChatManagement({
3923
3905
  }
3924
3906
  console.log("[ChatManagement] Created pending chat:", chatId, "(will activate on first message)");
3925
3907
  return chatId;
3926
- }, [currentChatId, pendingChatId, messages, repository, clientRef]);
3908
+ }, [currentChatId, pendingChatId, messages, repository, clientRef, setMessages]);
3927
3909
  const loadChat = useCallback4(async (chatId) => {
3928
3910
  setPendingChatId(chatId);
3929
3911
  await reloadMessages(chatId);
@@ -3944,7 +3926,7 @@ function useChatManagement({
3944
3926
  setMessages([]);
3945
3927
  }
3946
3928
  console.log("[ChatManagement] Deleted chat:", chatId);
3947
- }, [currentChatId, pendingChatId, repository]);
3929
+ }, [currentChatId, pendingChatId, repository, setMessages]);
3948
3930
  const listChats = useCallback4(async () => {
3949
3931
  return await repository.listChats();
3950
3932
  }, [repository]);
@@ -3958,7 +3940,7 @@ function useChatManagement({
3958
3940
  console.log("[ChatManagement] Cleared current chat:", currentChatId);
3959
3941
  }
3960
3942
  }
3961
- }, [currentChatId, repository]);
3943
+ }, [currentChatId, repository, setMessages]);
3962
3944
  const getCurrentChat = useCallback4(async () => {
3963
3945
  const chatId = pendingChatId || currentChatId;
3964
3946
  if (!chatId) return null;
@@ -4085,76 +4067,8 @@ function useChatManagement({
4085
4067
  }
4086
4068
  })();
4087
4069
  }
4088
- }, [currentChatId, pendingChatId, createNewChat, repository, loadChatMessages, clientRef]);
4070
+ }, [currentChatId, pendingChatId, createNewChat, repository, loadChatMessages, clientRef, setMessages]);
4089
4071
  const displayedChatId = pendingChatId || currentChatId;
4090
- const pendingMessagesRef = useRef5([]);
4091
- const isProcessingQueueRef = useRef5(false);
4092
- const loadingRef = useRef5(loading);
4093
- useEffect5(() => {
4094
- loadingRef.current = loading;
4095
- }, [loading]);
4096
- const hasPendingApprovalRef = useRef5(hasPendingApproval);
4097
- useEffect5(() => {
4098
- hasPendingApprovalRef.current = hasPendingApproval;
4099
- }, [hasPendingApproval]);
4100
- const processMessageQueue = useCallback4(async () => {
4101
- if (isProcessingQueueRef.current || pendingMessagesRef.current.length === 0 || !onSendMessage) {
4102
- return;
4103
- }
4104
- isProcessingQueueRef.current = true;
4105
- while (pendingMessagesRef.current.length > 0) {
4106
- const { message, options } = pendingMessagesRef.current.shift();
4107
- const { newChat = false, attachments = [], openChat = true, metadata } = options ?? {};
4108
- if (newChat) {
4109
- await createNewChat({ metadata });
4110
- }
4111
- const fileAttachments = await Promise.all(
4112
- attachments.map(async (file) => {
4113
- let preview;
4114
- if (file.type.startsWith("image/")) {
4115
- preview = await new Promise((resolve) => {
4116
- const reader = new FileReader();
4117
- reader.onload = () => resolve(typeof reader.result === "string" ? reader.result : void 0);
4118
- reader.onerror = () => resolve(void 0);
4119
- reader.readAsDataURL(file);
4120
- });
4121
- }
4122
- return {
4123
- id: crypto.randomUUID(),
4124
- file,
4125
- preview
4126
- };
4127
- })
4128
- );
4129
- await onSendMessage(message, fileAttachments.length > 0 ? fileAttachments : void 0);
4130
- if (openChat && setOpen) {
4131
- setOpen(true);
4132
- }
4133
- await new Promise((resolve) => {
4134
- const checkReady = () => {
4135
- setTimeout(() => {
4136
- if (!loadingRef.current && !hasPendingApprovalRef.current) {
4137
- resolve();
4138
- } else {
4139
- checkReady();
4140
- }
4141
- }, 100);
4142
- };
4143
- checkReady();
4144
- });
4145
- }
4146
- isProcessingQueueRef.current = false;
4147
- }, [onSendMessage, createNewChat, setOpen]);
4148
- const sendMessage = useCallback4(async (message, options) => {
4149
- if (!onSendMessage) {
4150
- throw new Error("sendMessage is not available (onSendMessage callback not provided)");
4151
- }
4152
- if (!connected) {
4153
- throw new Error("Not connected to UseAI server");
4154
- }
4155
- pendingMessagesRef.current.push({ message, options });
4156
- await processMessageQueue();
4157
- }, [onSendMessage, connected, processMessageQueue]);
4158
4072
  return {
4159
4073
  currentChatId,
4160
4074
  pendingChatId,
@@ -4168,7 +4082,6 @@ function useChatManagement({
4168
4082
  saveUserMessage,
4169
4083
  saveAIResponse,
4170
4084
  reloadMessages,
4171
- sendMessage,
4172
4085
  getCurrentChat,
4173
4086
  updateMetadata,
4174
4087
  currentChatIdSnapshot,
@@ -4416,16 +4329,25 @@ function useCommandManagement({
4416
4329
  };
4417
4330
  }
4418
4331
 
4419
- // src/hooks/useToolRegistry.ts
4332
+ // src/hooks/useToolSystem.ts
4420
4333
  import { useState as useState9, useCallback as useCallback7, useRef as useRef7, useMemo as useMemo4 } from "react";
4421
- function useToolRegistry() {
4334
+ function useToolSystem({
4335
+ clientRef,
4336
+ buildState
4337
+ }) {
4422
4338
  const toolRegistryRef = useRef7(/* @__PURE__ */ new Map());
4423
4339
  const [toolRegistryVersion, setToolRegistryVersion] = useState9(0);
4424
4340
  const toolOwnershipRef = useRef7(/* @__PURE__ */ new Map());
4425
4341
  const invisibleRef = useRef7(/* @__PURE__ */ new Set());
4342
+ const readyStateRef = useRef7(/* @__PURE__ */ new Map());
4343
+ const readyListenersRef = useRef7(/* @__PURE__ */ new Set());
4344
+ const waitersRef = useRef7(/* @__PURE__ */ new Map());
4345
+ const [pendingApprovals, setPendingApprovals] = useState9([]);
4346
+ const pendingApprovalToolCallsRef = useRef7(/* @__PURE__ */ new Map());
4426
4347
  const registerTools = useCallback7((id, tools, options) => {
4427
4348
  const existingTools = toolRegistryRef.current.get(id);
4428
4349
  toolRegistryRef.current.set(id, tools);
4350
+ readyStateRef.current.set(id, false);
4429
4351
  if (existingTools) {
4430
4352
  const existingKeys = Object.keys(existingTools).sort().join(",");
4431
4353
  const newKeys = Object.keys(tools).sort().join(",");
@@ -4444,6 +4366,13 @@ function useToolRegistry() {
4444
4366
  invisibleRef.current.delete(id);
4445
4367
  }
4446
4368
  }, []);
4369
+ const signalReady = useCallback7((id) => {
4370
+ if (!toolRegistryRef.current.has(id)) {
4371
+ return;
4372
+ }
4373
+ readyStateRef.current.set(id, true);
4374
+ readyListenersRef.current.forEach((listener) => listener());
4375
+ }, []);
4447
4376
  const unregisterTools = useCallback7((id) => {
4448
4377
  const tools = toolRegistryRef.current.get(id);
4449
4378
  if (tools) {
@@ -4452,8 +4381,10 @@ function useToolRegistry() {
4452
4381
  });
4453
4382
  }
4454
4383
  toolRegistryRef.current.delete(id);
4384
+ readyStateRef.current.delete(id);
4455
4385
  setToolRegistryVersion((v) => v + 1);
4456
4386
  invisibleRef.current.delete(id);
4387
+ readyListenersRef.current.forEach((listener) => listener());
4457
4388
  }, []);
4458
4389
  const isInvisible = useCallback7((id) => {
4459
4390
  return invisibleRef.current.has(id);
@@ -4468,14 +4399,159 @@ function useToolRegistry() {
4468
4399
  const hasTools = toolRegistryRef.current.size > 0;
4469
4400
  const aggregatedToolsRef = useRef7(aggregatedTools);
4470
4401
  aggregatedToolsRef.current = aggregatedTools;
4402
+ const waitForToolsToStabilize = useCallback7(async () => {
4403
+ const maxWaitMs = 500;
4404
+ const checkAllReady = () => {
4405
+ if (readyStateRef.current.size === 0) return true;
4406
+ for (const ready of readyStateRef.current.values()) {
4407
+ if (!ready) return false;
4408
+ }
4409
+ return true;
4410
+ };
4411
+ await new Promise((resolve) => setTimeout(resolve, 0));
4412
+ if (checkAllReady()) {
4413
+ return;
4414
+ }
4415
+ return new Promise((resolve) => {
4416
+ let safetyTimeout = null;
4417
+ let resolved = false;
4418
+ const cleanup = () => {
4419
+ if (resolved) return;
4420
+ resolved = true;
4421
+ if (safetyTimeout) clearTimeout(safetyTimeout);
4422
+ readyListenersRef.current.delete(onReadyChange);
4423
+ };
4424
+ const onReadyChange = () => {
4425
+ if (resolved) return;
4426
+ if (checkAllReady()) {
4427
+ cleanup();
4428
+ resolve();
4429
+ }
4430
+ };
4431
+ safetyTimeout = setTimeout(() => {
4432
+ cleanup();
4433
+ resolve();
4434
+ }, maxWaitMs);
4435
+ readyListenersRef.current.add(onReadyChange);
4436
+ onReadyChange();
4437
+ });
4438
+ }, []);
4439
+ const registerWaiter = useCallback7((id, waiter) => {
4440
+ waitersRef.current.set(id, waiter);
4441
+ }, []);
4442
+ const unregisterWaiter = useCallback7((id) => {
4443
+ waitersRef.current.delete(id);
4444
+ }, []);
4445
+ const getWaiter = useCallback7((id) => {
4446
+ return waitersRef.current.get(id);
4447
+ }, []);
4448
+ const handleApprovalRequest = useCallback7((event) => {
4449
+ console.log("[useToolSystem] Tool approval requested:", event.toolCallName, event.toolCallId);
4450
+ setPendingApprovals((prev) => [
4451
+ ...prev,
4452
+ {
4453
+ toolCallId: event.toolCallId,
4454
+ toolCallName: event.toolCallName,
4455
+ toolCallArgs: event.toolCallArgs,
4456
+ annotations: event.annotations
4457
+ }
4458
+ ]);
4459
+ }, []);
4460
+ const executeToolCall = useCallback7(async (toolCallId, name, input) => {
4461
+ const client = clientRef.current;
4462
+ if (!client) {
4463
+ console.error("[useToolSystem] No client available for tool execution");
4464
+ return;
4465
+ }
4466
+ try {
4467
+ const ownerId = toolOwnershipRef.current.get(name);
4468
+ console.log(`[useToolSystem] Tool "${name}" owned by component:`, ownerId);
4469
+ console.log("[useToolSystem] Executing tool...");
4470
+ const result = await executeDefinedTool(aggregatedToolsRef.current, name, input);
4471
+ const isErrorResult = result && typeof result === "object" && ("error" in result || result.success === false);
4472
+ const ownerIsInvisible = ownerId ? isInvisible(ownerId) : false;
4473
+ if (ownerId && !isErrorResult && !ownerIsInvisible) {
4474
+ const waiter = getWaiter(ownerId);
4475
+ if (waiter) {
4476
+ console.log(`[useToolSystem] Waiting for prompt change from ${ownerId}...`);
4477
+ await waiter();
4478
+ console.log("[useToolSystem] Prompt change wait complete");
4479
+ }
4480
+ } else if (isErrorResult) {
4481
+ console.log("[useToolSystem] Tool returned error, skipping prompt wait");
4482
+ } else if (ownerIsInvisible) {
4483
+ console.log("[useToolSystem] Component is invisible, skipping prompt wait");
4484
+ }
4485
+ console.log("[useToolSystem] Waiting for tools to stabilize...");
4486
+ await waitForToolsToStabilize();
4487
+ console.log("[useToolSystem] Tools stabilized");
4488
+ const updatedState = buildState();
4489
+ console.log(`[useToolSystem] Updated state (aggregated from all hooks)`);
4490
+ client.sendToolResponse(toolCallId, result, updatedState);
4491
+ } catch (err) {
4492
+ console.error("Tool execution error:", err);
4493
+ client.sendToolResponse(toolCallId, {
4494
+ error: err instanceof Error ? err.message : "Unknown error"
4495
+ });
4496
+ }
4497
+ }, [clientRef, isInvisible, getWaiter, waitForToolsToStabilize, buildState]);
4498
+ const storePendingToolCall = useCallback7((toolCallId, name, input, toolCallData) => {
4499
+ console.log(`[useToolSystem] Storing pending tool call "${name}" for approval`);
4500
+ pendingApprovalToolCallsRef.current.set(toolCallId, { name, input, toolCallData });
4501
+ }, []);
4502
+ const executePendingToolAfterApproval = useCallback7(async (toolCallId) => {
4503
+ const pendingTool = pendingApprovalToolCallsRef.current.get(toolCallId);
4504
+ if (!pendingTool) {
4505
+ console.warn(`[useToolSystem] No pending tool found for ${toolCallId}`);
4506
+ return;
4507
+ }
4508
+ pendingApprovalToolCallsRef.current.delete(toolCallId);
4509
+ await executeToolCall(toolCallId, pendingTool.name, pendingTool.input);
4510
+ }, [executeToolCall]);
4511
+ const approveAll = useCallback7(async () => {
4512
+ if (!clientRef.current) return;
4513
+ console.log("[useToolSystem] Approving all tool calls:", pendingApprovals.length);
4514
+ const pendingTools = [...pendingApprovals];
4515
+ for (const pending of pendingTools) {
4516
+ clientRef.current.sendToolApprovalResponse(pending.toolCallId, true);
4517
+ }
4518
+ setPendingApprovals([]);
4519
+ for (const tool of pendingTools) {
4520
+ await executePendingToolAfterApproval(tool.toolCallId);
4521
+ }
4522
+ }, [clientRef, pendingApprovals, executePendingToolAfterApproval]);
4523
+ const rejectAll = useCallback7((reason) => {
4524
+ if (!clientRef.current) return;
4525
+ console.log("[useToolSystem] Rejecting all tool calls:", pendingApprovals.length, reason);
4526
+ const pendingTools = [...pendingApprovals];
4527
+ for (const pending of pendingTools) {
4528
+ clientRef.current.sendToolApprovalResponse(pending.toolCallId, false, reason);
4529
+ }
4530
+ setPendingApprovals([]);
4531
+ for (const tool of pendingTools) {
4532
+ pendingApprovalToolCallsRef.current.delete(tool.toolCallId);
4533
+ }
4534
+ }, [clientRef, pendingApprovals]);
4471
4535
  return {
4536
+ // Registry
4472
4537
  registerTools,
4473
4538
  unregisterTools,
4474
4539
  isInvisible,
4475
4540
  aggregatedTools,
4476
4541
  hasTools,
4477
4542
  aggregatedToolsRef,
4478
- toolOwnershipRef
4543
+ signalReady,
4544
+ toolRegistryVersion,
4545
+ // Waiters
4546
+ registerWaiter,
4547
+ unregisterWaiter,
4548
+ // Execution
4549
+ pendingApprovals,
4550
+ handleApprovalRequest,
4551
+ executeToolCall,
4552
+ storePendingToolCall,
4553
+ approveAll,
4554
+ rejectAll
4479
4555
  };
4480
4556
  }
4481
4557
 
@@ -4488,7 +4564,6 @@ function usePromptState({
4488
4564
  }) {
4489
4565
  const promptsRef = useRef8(/* @__PURE__ */ new Map());
4490
4566
  const suggestionsRef = useRef8(/* @__PURE__ */ new Map());
4491
- const waitersRef = useRef8(/* @__PURE__ */ new Map());
4492
4567
  const [suggestionsVersion, setSuggestionsVersion] = useState10(0);
4493
4568
  const buildStateFromPrompts = useCallback8(() => {
4494
4569
  const promptParts = [];
@@ -4525,15 +4600,6 @@ function usePromptState({
4525
4600
  clientRef.current.updateState(buildStateFromPrompts());
4526
4601
  }
4527
4602
  }, [buildStateFromPrompts, clientRef, connected]);
4528
- const registerWaiter = useCallback8((id, waiter) => {
4529
- waitersRef.current.set(id, waiter);
4530
- }, []);
4531
- const unregisterWaiter = useCallback8((id) => {
4532
- waitersRef.current.delete(id);
4533
- }, []);
4534
- const getWaiter = useCallback8((id) => {
4535
- return waitersRef.current.get(id);
4536
- }, []);
4537
4603
  const aggregatedSuggestions = useMemo5(() => {
4538
4604
  const allSuggestions = [];
4539
4605
  suggestionsRef.current.forEach((suggestions) => {
@@ -4543,11 +4609,8 @@ function usePromptState({
4543
4609
  }, [suggestionsVersion]);
4544
4610
  return {
4545
4611
  updatePrompt,
4546
- registerWaiter,
4547
- unregisterWaiter,
4548
- getWaiter,
4549
4612
  aggregatedSuggestions,
4550
- promptsRef
4613
+ buildStateFromPrompts
4551
4614
  };
4552
4615
  }
4553
4616
 
@@ -4613,116 +4676,190 @@ function useFeedback({
4613
4676
  };
4614
4677
  }
4615
4678
 
4616
- // src/hooks/useToolExecution.ts
4679
+ // src/hooks/useServerEvents.ts
4617
4680
  import { useState as useState12, useCallback as useCallback10, useRef as useRef10 } from "react";
4618
- function useToolExecution({
4619
- clientRef,
4620
- aggregatedToolsRef,
4621
- toolOwnershipRef,
4622
- promptsRef,
4623
- isInvisible,
4624
- getWaiter
4681
+
4682
+ // src/types.ts
4683
+ import { EventType as EventType2, ErrorCode, TOOL_APPROVAL_REQUEST } from "@meetsmore-oss/use-ai-core";
4684
+
4685
+ // src/hooks/useServerEvents.ts
4686
+ function useServerEvents({
4687
+ toolSystem,
4688
+ saveAIResponse,
4689
+ strings
4625
4690
  }) {
4626
- const [pendingApprovals, setPendingApprovals] = useState12([]);
4627
- const pendingApprovalToolCallsRef = useRef10(/* @__PURE__ */ new Map());
4628
- const handleApprovalRequest = useCallback10((event) => {
4629
- console.log("[useToolExecution] Tool approval requested:", event.toolCallName, event.toolCallId);
4630
- setPendingApprovals((prev) => [
4631
- ...prev,
4632
- {
4633
- toolCallId: event.toolCallId,
4634
- toolCallName: event.toolCallName,
4635
- toolCallArgs: event.toolCallArgs,
4636
- annotations: event.annotations
4691
+ const [loading, setLoading] = useState12(false);
4692
+ const [streamingText, setStreamingText] = useState12("");
4693
+ const streamingChatIdRef = useRef10(null);
4694
+ const [executingToolRaw, setExecutingTool] = useState12(null);
4695
+ const executingToolFallbackRef = useRef10(null);
4696
+ const clearStreamingText = useCallback10(() => {
4697
+ setStreamingText("");
4698
+ }, []);
4699
+ const toolSystemRef = useRef10(toolSystem);
4700
+ toolSystemRef.current = toolSystem;
4701
+ const saveAIResponseRef = useRef10(saveAIResponse);
4702
+ saveAIResponseRef.current = saveAIResponse;
4703
+ const stringsRef = useRef10(strings);
4704
+ stringsRef.current = strings;
4705
+ const handleServerEvent = useCallback10(async (client, event) => {
4706
+ const ts = toolSystemRef.current;
4707
+ const strs = stringsRef.current;
4708
+ if (event.type === EventType2.TOOL_CALL_START) {
4709
+ const e = event;
4710
+ const tool = ts.aggregatedToolsRef.current[e.toolCallName];
4711
+ const title = e.annotations?.title ?? tool?._options?.annotations?.title ?? null;
4712
+ if (!title) {
4713
+ const fallbacks = strs.toolExecution.fallbackMessages;
4714
+ executingToolFallbackRef.current = fallbacks[Math.floor(Math.random() * fallbacks.length)];
4637
4715
  }
4638
- ]);
4716
+ setExecutingTool({ toolCallId: e.toolCallId, title });
4717
+ } else if (event.type === EventType2.TOOL_CALL_END) {
4718
+ const toolCallEnd = event;
4719
+ const toolCallId = toolCallEnd.toolCallId;
4720
+ setExecutingTool((prev) => prev?.toolCallId === toolCallId ? null : prev);
4721
+ const toolCallData = client.currentToolCalls.get(toolCallId);
4722
+ if (!toolCallData) {
4723
+ console.error(`[ServerEvents] Tool call ${toolCallId} not found`);
4724
+ return;
4725
+ }
4726
+ const name = toolCallData.name;
4727
+ const input = JSON.parse(toolCallData.args);
4728
+ const tool = ts.aggregatedToolsRef.current[name];
4729
+ if (!tool) {
4730
+ console.log(`[ServerEvents] Tool "${name}" not found in useAI tools, skipping (likely a workflow tool)`);
4731
+ return;
4732
+ }
4733
+ if (tool._options?.annotations?.destructiveHint === true) {
4734
+ console.log(`[ServerEvents] Tool "${name}" requires approval, deferring execution`);
4735
+ ts.storePendingToolCall(toolCallId, name, input, toolCallData);
4736
+ return;
4737
+ }
4738
+ await ts.executeToolCall(toolCallId, name, input);
4739
+ } else if (event.type === TOOL_APPROVAL_REQUEST) {
4740
+ const e = event;
4741
+ ts.handleApprovalRequest(e);
4742
+ } else if (event.type === EventType2.TEXT_MESSAGE_CONTENT) {
4743
+ const contentEvent = event;
4744
+ setStreamingText((prev) => prev + contentEvent.delta);
4745
+ } else if (event.type === EventType2.TEXT_MESSAGE_END) {
4746
+ setStreamingText("");
4747
+ streamingChatIdRef.current = null;
4748
+ } else if (event.type === EventType2.RUN_FINISHED) {
4749
+ const content = client.currentMessageContent;
4750
+ if (content) {
4751
+ const finishedEvent = event;
4752
+ const traceId = finishedEvent.runId;
4753
+ saveAIResponseRef.current(content, void 0, traceId);
4754
+ }
4755
+ setLoading(false);
4756
+ } else if (event.type === EventType2.RUN_ERROR) {
4757
+ const errorEvent = event;
4758
+ const errorCode = errorEvent.message;
4759
+ console.error("[ServerEvents] Run error:", errorCode);
4760
+ const userMessage = strs.errors[errorCode] || strs.errors[ErrorCode.UNKNOWN_ERROR];
4761
+ saveAIResponseRef.current(userMessage, "error");
4762
+ setStreamingText("");
4763
+ streamingChatIdRef.current = null;
4764
+ setLoading(false);
4765
+ }
4639
4766
  }, []);
4640
- const executeToolCall = useCallback10(async (toolCallId, name, input) => {
4641
- const client = clientRef.current;
4642
- if (!client) {
4643
- console.error("[useToolExecution] No client available for tool execution");
4767
+ const executingTool = executingToolRaw ? {
4768
+ displayText: executingToolRaw.title ?? executingToolFallbackRef.current ?? strings.toolExecution.fallbackMessages[0]
4769
+ } : null;
4770
+ return {
4771
+ loading,
4772
+ setLoading,
4773
+ streamingText,
4774
+ clearStreamingText,
4775
+ executingTool,
4776
+ streamingChatIdRef,
4777
+ handleServerEvent
4778
+ };
4779
+ }
4780
+
4781
+ // src/hooks/useMessageQueue.ts
4782
+ import { useCallback as useCallback11, useRef as useRef11, useEffect as useEffect10 } from "react";
4783
+ function useMessageQueue({
4784
+ sendFn,
4785
+ createNewChat,
4786
+ setOpen,
4787
+ connected,
4788
+ loading,
4789
+ hasPendingApproval
4790
+ }) {
4791
+ const pendingMessagesRef = useRef11([]);
4792
+ const isProcessingQueueRef = useRef11(false);
4793
+ const sendFnRef = useRef11(sendFn);
4794
+ sendFnRef.current = sendFn;
4795
+ const createNewChatRef = useRef11(createNewChat);
4796
+ createNewChatRef.current = createNewChat;
4797
+ const setOpenRef = useRef11(setOpen);
4798
+ setOpenRef.current = setOpen;
4799
+ const loadingRef = useRef11(loading);
4800
+ useEffect10(() => {
4801
+ loadingRef.current = loading;
4802
+ }, [loading]);
4803
+ const hasPendingApprovalRef = useRef11(hasPendingApproval);
4804
+ useEffect10(() => {
4805
+ hasPendingApprovalRef.current = hasPendingApproval;
4806
+ }, [hasPendingApproval]);
4807
+ const processMessageQueue = useCallback11(async () => {
4808
+ if (isProcessingQueueRef.current || pendingMessagesRef.current.length === 0) {
4644
4809
  return;
4645
4810
  }
4646
- try {
4647
- const ownerId = toolOwnershipRef.current.get(name);
4648
- console.log(`[useToolExecution] Tool "${name}" owned by component:`, ownerId);
4649
- console.log("[useToolExecution] Executing tool...");
4650
- const result = await executeDefinedTool(aggregatedToolsRef.current, name, input);
4651
- const isErrorResult = result && typeof result === "object" && ("error" in result || result.success === false);
4652
- const ownerIsInvisible = ownerId ? isInvisible(ownerId) : false;
4653
- if (ownerId && !isErrorResult && !ownerIsInvisible) {
4654
- const waiter = getWaiter(ownerId);
4655
- if (waiter) {
4656
- console.log(`[useToolExecution] Waiting for prompt change from ${ownerId}...`);
4657
- await waiter();
4658
- console.log("[useToolExecution] Prompt change wait complete");
4659
- }
4660
- } else if (isErrorResult) {
4661
- console.log("[useToolExecution] Tool returned error, skipping prompt wait");
4662
- } else if (ownerIsInvisible) {
4663
- console.log("[useToolExecution] Component is invisible, skipping prompt wait");
4811
+ isProcessingQueueRef.current = true;
4812
+ while (pendingMessagesRef.current.length > 0) {
4813
+ const { message, options } = pendingMessagesRef.current.shift();
4814
+ const { newChat = false, attachments = [], openChat = true, metadata, forwardedProps } = options ?? {};
4815
+ if (newChat) {
4816
+ await createNewChatRef.current({ metadata });
4664
4817
  }
4665
- let updatedState = null;
4666
- if (ownerId) {
4667
- const prompt = promptsRef.current.get(ownerId);
4668
- if (prompt) {
4669
- updatedState = { context: prompt };
4670
- console.log(`[useToolExecution] Updated state from ${ownerId}`);
4671
- }
4818
+ const fileAttachments = await Promise.all(
4819
+ attachments.map(async (file) => {
4820
+ let preview;
4821
+ if (file.type.startsWith("image/")) {
4822
+ preview = await new Promise((resolve) => {
4823
+ const reader = new FileReader();
4824
+ reader.onload = () => resolve(typeof reader.result === "string" ? reader.result : void 0);
4825
+ reader.onerror = () => resolve(void 0);
4826
+ reader.readAsDataURL(file);
4827
+ });
4828
+ }
4829
+ return {
4830
+ id: crypto.randomUUID(),
4831
+ file,
4832
+ preview
4833
+ };
4834
+ })
4835
+ );
4836
+ await sendFnRef.current(message, fileAttachments.length > 0 ? fileAttachments : void 0, forwardedProps);
4837
+ if (openChat && setOpenRef.current) {
4838
+ setOpenRef.current(true);
4672
4839
  }
4673
- client.sendToolResponse(toolCallId, result, updatedState);
4674
- } catch (err) {
4675
- console.error("Tool execution error:", err);
4676
- client.sendToolResponse(toolCallId, {
4677
- error: err instanceof Error ? err.message : "Unknown error"
4840
+ await new Promise((resolve) => {
4841
+ const checkReady = () => {
4842
+ setTimeout(() => {
4843
+ if (!loadingRef.current && !hasPendingApprovalRef.current) {
4844
+ resolve();
4845
+ } else {
4846
+ checkReady();
4847
+ }
4848
+ }, 100);
4849
+ };
4850
+ checkReady();
4678
4851
  });
4679
4852
  }
4680
- }, [clientRef, aggregatedToolsRef, toolOwnershipRef, promptsRef, isInvisible, getWaiter]);
4681
- const storePendingToolCall = useCallback10((toolCallId, name, input, toolCallData) => {
4682
- console.log(`[useToolExecution] Storing pending tool call "${name}" for approval`);
4683
- pendingApprovalToolCallsRef.current.set(toolCallId, { name, input, toolCallData });
4853
+ isProcessingQueueRef.current = false;
4684
4854
  }, []);
4685
- const executePendingToolAfterApproval = useCallback10(async (toolCallId) => {
4686
- const pendingTool = pendingApprovalToolCallsRef.current.get(toolCallId);
4687
- if (!pendingTool) {
4688
- console.warn(`[useToolExecution] No pending tool found for ${toolCallId}`);
4689
- return;
4690
- }
4691
- pendingApprovalToolCallsRef.current.delete(toolCallId);
4692
- await executeToolCall(toolCallId, pendingTool.name, pendingTool.input);
4693
- }, [executeToolCall]);
4694
- const approveAll = useCallback10(async () => {
4695
- if (!clientRef.current) return;
4696
- console.log("[useToolExecution] Approving all tool calls:", pendingApprovals.length);
4697
- const pendingTools = [...pendingApprovals];
4698
- for (const pending of pendingTools) {
4699
- clientRef.current.sendToolApprovalResponse(pending.toolCallId, true);
4700
- }
4701
- setPendingApprovals([]);
4702
- for (const tool of pendingTools) {
4703
- await executePendingToolAfterApproval(tool.toolCallId);
4704
- }
4705
- }, [clientRef, pendingApprovals, executePendingToolAfterApproval]);
4706
- const rejectAll = useCallback10((reason) => {
4707
- if (!clientRef.current) return;
4708
- console.log("[useToolExecution] Rejecting all tool calls:", pendingApprovals.length, reason);
4709
- const pendingTools = [...pendingApprovals];
4710
- for (const pending of pendingTools) {
4711
- clientRef.current.sendToolApprovalResponse(pending.toolCallId, false, reason);
4712
- }
4713
- setPendingApprovals([]);
4714
- for (const tool of pendingTools) {
4715
- pendingApprovalToolCallsRef.current.delete(tool.toolCallId);
4855
+ const sendMessage = useCallback11(async (message, options) => {
4856
+ if (!connected) {
4857
+ throw new Error("Not connected to UseAI server");
4716
4858
  }
4717
- }, [clientRef, pendingApprovals]);
4718
- return {
4719
- pendingApprovals,
4720
- handleApprovalRequest,
4721
- executeToolCall,
4722
- storePendingToolCall,
4723
- approveAll,
4724
- rejectAll
4725
- };
4859
+ pendingMessagesRef.current.push({ message, options });
4860
+ await processMessageQueue();
4861
+ }, [connected, processMessageQueue]);
4862
+ return { sendMessage };
4726
4863
  }
4727
4864
 
4728
4865
  // src/providers/useAIProvider.tsx
@@ -4737,16 +4874,18 @@ var noOpContextValue = {
4737
4874
  register: () => {
4738
4875
  },
4739
4876
  unregister: () => {
4740
- }
4741
- },
4742
- prompts: {
4743
- update: () => {
4877
+ },
4878
+ signalReady: () => {
4744
4879
  },
4745
4880
  registerWaiter: () => {
4746
4881
  },
4747
4882
  unregisterWaiter: () => {
4748
4883
  }
4749
4884
  },
4885
+ prompts: {
4886
+ update: () => {
4887
+ }
4888
+ },
4750
4889
  chat: {
4751
4890
  currentId: null,
4752
4891
  create: async () => "",
@@ -4794,7 +4933,7 @@ function UseAIProvider({
4794
4933
  CustomButton,
4795
4934
  CustomChat,
4796
4935
  chatRepository,
4797
- mcpHeadersProvider,
4936
+ forwardedPropsProvider,
4798
4937
  fileUploadConfig: fileUploadConfigProp,
4799
4938
  commandRepository,
4800
4939
  renderChat = true,
@@ -4808,87 +4947,41 @@ function UseAIProvider({
4808
4947
  const strings = { ...defaultStrings, ...customStrings };
4809
4948
  const [connected, setConnected] = useState13(false);
4810
4949
  const [isChatOpen, setIsChatOpen] = useState13(false);
4811
- const [loading, setLoading] = useState13(false);
4812
4950
  const [messages, setMessages] = useState13([]);
4813
4951
  const [fileProcessingState, setFileProcessingState] = useState13(null);
4814
- const handleSetChatOpen = useCallback11((open) => {
4952
+ const handleSetChatOpen = useCallback12((open) => {
4815
4953
  setIsChatOpen(open);
4816
4954
  onOpenChange?.(open);
4817
4955
  }, [onOpenChange]);
4818
- const [streamingText, setStreamingText] = useState13("");
4819
- const streamingChatIdRef = useRef11(null);
4820
- const [executingTool, setExecutingTool] = useState13(null);
4821
- const executingToolFallbackRef = useRef11(null);
4822
- const clientRef = useRef11(null);
4823
- const repositoryRef = useRef11(
4956
+ const clientRef = useRef12(null);
4957
+ const repositoryRef = useRef12(
4824
4958
  chatRepository || new LocalStorageChatRepository()
4825
4959
  );
4826
- const handleSendMessageRef = useRef11(null);
4827
- const {
4828
- registerTools,
4829
- unregisterTools,
4830
- isInvisible,
4831
- aggregatedTools,
4832
- hasTools,
4833
- aggregatedToolsRef,
4834
- toolOwnershipRef
4835
- } = useToolRegistry();
4836
- const {
4837
- updatePrompt,
4838
- registerWaiter,
4839
- unregisterWaiter,
4840
- getWaiter,
4841
- aggregatedSuggestions,
4842
- promptsRef
4843
- } = usePromptState({
4960
+ const promptState = usePromptState({
4844
4961
  systemPrompt,
4845
4962
  clientRef,
4846
4963
  connected
4847
4964
  });
4848
- const stableSendMessage = useCallback11(async (message, attachments) => {
4849
- if (handleSendMessageRef.current) {
4850
- await handleSendMessageRef.current(message, attachments);
4851
- }
4852
- }, []);
4853
- const toolExecution = useToolExecution({
4965
+ const toolSystem = useToolSystem({
4854
4966
  clientRef,
4855
- aggregatedToolsRef,
4856
- toolOwnershipRef,
4857
- promptsRef,
4858
- isInvisible,
4859
- getWaiter
4967
+ buildState: promptState.buildStateFromPrompts
4860
4968
  });
4861
4969
  const chatManagement = useChatManagement({
4862
4970
  repository: repositoryRef.current,
4863
4971
  clientRef,
4864
4972
  messages,
4865
4973
  setMessages,
4866
- onSendMessage: stableSendMessage,
4867
- setOpen: handleSetChatOpen,
4868
- connected,
4869
- loading,
4870
- hasPendingApproval: toolExecution.pendingApprovals.length > 0
4974
+ connected
4975
+ });
4976
+ const serverEvents = useServerEvents({
4977
+ toolSystem,
4978
+ saveAIResponse: chatManagement.saveAIResponse,
4979
+ strings
4871
4980
  });
4872
- const {
4873
- currentChatId,
4874
- pendingChatId,
4875
- displayedChatId,
4876
- createNewChat,
4877
- loadChat,
4878
- deleteChat,
4879
- listChats,
4880
- clearCurrentChat,
4881
- activatePendingChat,
4882
- saveUserMessage,
4883
- saveAIResponse,
4884
- sendMessage,
4885
- getCurrentChat,
4886
- updateMetadata
4887
- } = chatManagement;
4888
4981
  const feedback = useFeedback({
4889
4982
  clientRef,
4890
4983
  repository: repositoryRef.current,
4891
- getDisplayedChatId: () => displayedChatId,
4984
+ getDisplayedChatId: () => chatManagement.displayedChatId,
4892
4985
  setMessages
4893
4986
  });
4894
4987
  const {
@@ -4904,12 +4997,11 @@ function UseAIProvider({
4904
4997
  renameCommand,
4905
4998
  deleteCommand
4906
4999
  } = useCommandManagement({ repository: commandRepository });
4907
- useEffect10(() => {
5000
+ const handleServerEventRef = useRef12(serverEvents.handleServerEvent);
5001
+ handleServerEventRef.current = serverEvents.handleServerEvent;
5002
+ useEffect11(() => {
4908
5003
  console.log("[UseAIProvider] Initializing client with serverUrl:", serverUrl);
4909
5004
  const client = new UseAIClient(serverUrl);
4910
- if (mcpHeadersProvider) {
4911
- client.setMcpHeadersProvider(mcpHeadersProvider);
4912
- }
4913
5005
  const unsubscribeConnection = client.onConnectionStateChange((isConnected) => {
4914
5006
  console.log("[UseAIProvider] Connection state changed:", isConnected);
4915
5007
  setConnected(isConnected);
@@ -4917,64 +5009,7 @@ function UseAIProvider({
4917
5009
  console.log("[UseAIProvider] Connecting...");
4918
5010
  client.connect();
4919
5011
  const unsubscribe = client.onEvent("globalChat", async (event) => {
4920
- if (event.type === EventType.TOOL_CALL_START) {
4921
- const e = event;
4922
- const tool = aggregatedToolsRef.current[e.toolCallName];
4923
- const title = e.annotations?.title ?? tool?._options?.annotations?.title ?? null;
4924
- if (!title) {
4925
- const fallbacks = strings.toolExecution.fallbackMessages;
4926
- executingToolFallbackRef.current = fallbacks[Math.floor(Math.random() * fallbacks.length)];
4927
- }
4928
- setExecutingTool({ toolCallId: e.toolCallId, title });
4929
- } else if (event.type === EventType.TOOL_CALL_END) {
4930
- const toolCallEnd = event;
4931
- const toolCallId = toolCallEnd.toolCallId;
4932
- setExecutingTool((prev) => prev?.toolCallId === toolCallId ? null : prev);
4933
- const toolCallData = client["currentToolCalls"].get(toolCallId);
4934
- if (!toolCallData) {
4935
- console.error(`[Provider] Tool call ${toolCallId} not found`);
4936
- return;
4937
- }
4938
- const name = toolCallData.name;
4939
- const input = JSON.parse(toolCallData.args);
4940
- const tool = aggregatedToolsRef.current[name];
4941
- if (!tool) {
4942
- console.log(`[Provider] Tool "${name}" not found in useAI tools, skipping (likely a workflow tool)`);
4943
- return;
4944
- }
4945
- if (tool._options?.annotations?.destructiveHint === true) {
4946
- console.log(`[Provider] Tool "${name}" requires approval, deferring execution`);
4947
- toolExecution.storePendingToolCall(toolCallId, name, input, toolCallData);
4948
- return;
4949
- }
4950
- await toolExecution.executeToolCall(toolCallId, name, input);
4951
- } else if (event.type === TOOL_APPROVAL_REQUEST) {
4952
- const e = event;
4953
- toolExecution.handleApprovalRequest(e);
4954
- } else if (event.type === EventType.TEXT_MESSAGE_CONTENT) {
4955
- const contentEvent = event;
4956
- setStreamingText((prev) => prev + contentEvent.delta);
4957
- } else if (event.type === EventType.TEXT_MESSAGE_END) {
4958
- setStreamingText("");
4959
- streamingChatIdRef.current = null;
4960
- } else if (event.type === EventType.RUN_FINISHED) {
4961
- const content = client.currentMessageContent;
4962
- if (content) {
4963
- const finishedEvent = event;
4964
- const traceId = finishedEvent.runId;
4965
- saveAIResponse(content, void 0, traceId);
4966
- }
4967
- setLoading(false);
4968
- } else if (event.type === EventType.RUN_ERROR) {
4969
- const errorEvent = event;
4970
- const errorCode = errorEvent.message;
4971
- console.error("[Provider] Run error:", errorCode);
4972
- const userMessage = strings.errors[errorCode] || strings.errors[ErrorCode.UNKNOWN_ERROR];
4973
- saveAIResponse(userMessage, "error");
4974
- setStreamingText("");
4975
- streamingChatIdRef.current = null;
4976
- setLoading(false);
4977
- }
5012
+ await handleServerEventRef.current(client, event);
4978
5013
  });
4979
5014
  clientRef.current = client;
4980
5015
  return () => {
@@ -4983,18 +5018,11 @@ function UseAIProvider({
4983
5018
  client.disconnect();
4984
5019
  };
4985
5020
  }, [serverUrl]);
4986
- useEffect10(() => {
4987
- const client = clientRef.current;
4988
- if (!client) return;
4989
- if (mcpHeadersProvider) {
4990
- client.setMcpHeadersProvider(mcpHeadersProvider);
4991
- }
4992
- }, [mcpHeadersProvider]);
4993
- const lastRegisteredToolsRef = useRef11("");
4994
- useEffect10(() => {
5021
+ const lastRegisteredToolsRef = useRef12("");
5022
+ useEffect11(() => {
4995
5023
  const client = clientRef.current;
4996
- if (!client || !client.isConnected() || !hasTools) return;
4997
- const toolKeys = Object.keys(aggregatedTools).sort().join(",");
5024
+ if (!client || !client.isConnected() || !toolSystem.hasTools) return;
5025
+ const toolKeys = Object.keys(toolSystem.aggregatedTools).sort().join(",");
4998
5026
  if (toolKeys === lastRegisteredToolsRef.current) {
4999
5027
  console.log("[Provider] Skipping re-registration, tools unchanged");
5000
5028
  return;
@@ -5002,19 +5030,19 @@ function UseAIProvider({
5002
5030
  lastRegisteredToolsRef.current = toolKeys;
5003
5031
  console.log("[Provider] Registering tools:", toolKeys);
5004
5032
  try {
5005
- const toolDefinitions = convertToolsToDefinitions(aggregatedTools);
5033
+ const toolDefinitions = convertToolsToDefinitions(toolSystem.aggregatedTools);
5006
5034
  console.log(`[Provider] Registering ${toolDefinitions.length} tools`);
5007
5035
  client.registerTools(toolDefinitions);
5008
5036
  } catch (err) {
5009
5037
  console.error("Failed to register tools:", err);
5010
5038
  }
5011
- }, [hasTools, aggregatedTools, connected]);
5012
- const handleSendMessage = useCallback11(async (message, attachments) => {
5039
+ }, [toolSystem.hasTools, toolSystem.aggregatedTools, connected]);
5040
+ const handleSendMessage = useCallback12(async (message, attachments, messageForwardedProps) => {
5013
5041
  if (!clientRef.current) return;
5014
- setStreamingText("");
5015
- const activatedChatId = activatePendingChat();
5016
- const activeChatId = activatedChatId || currentChatId;
5017
- streamingChatIdRef.current = activeChatId;
5042
+ serverEvents.clearStreamingText();
5043
+ const activatedChatId = chatManagement.activatePendingChat();
5044
+ const activeChatId = activatedChatId || chatManagement.currentChatId;
5045
+ serverEvents.streamingChatIdRef.current = activeChatId;
5018
5046
  let persistedContent = message;
5019
5047
  let multimodalContent;
5020
5048
  if (attachments && attachments.length > 0) {
@@ -5034,12 +5062,12 @@ function UseAIProvider({
5034
5062
  }
5035
5063
  persistedContent = persistedParts;
5036
5064
  if (activeChatId) {
5037
- await saveUserMessage(activeChatId, persistedContent);
5065
+ await chatManagement.saveUserMessage(activeChatId, persistedContent);
5038
5066
  }
5039
- setLoading(true);
5067
+ serverEvents.setLoading(true);
5040
5068
  try {
5041
5069
  const fileContent = await processAttachments(attachments, {
5042
- getCurrentChat,
5070
+ getCurrentChat: chatManagement.getCurrentChat,
5043
5071
  backend: fileUploadConfig?.backend,
5044
5072
  transformers: fileUploadConfig?.transformers,
5045
5073
  onFileProgress: (_fileId, state) => {
@@ -5052,43 +5080,61 @@ function UseAIProvider({
5052
5080
  }
5053
5081
  multimodalContent.push(...fileContent);
5054
5082
  } catch (error) {
5055
- setLoading(false);
5083
+ serverEvents.setLoading(false);
5056
5084
  throw error;
5057
5085
  } finally {
5058
5086
  setFileProcessingState(null);
5059
5087
  }
5060
5088
  } else {
5061
5089
  if (activeChatId) {
5062
- await saveUserMessage(activeChatId, persistedContent);
5090
+ await chatManagement.saveUserMessage(activeChatId, persistedContent);
5063
5091
  }
5064
- setLoading(true);
5092
+ serverEvents.setLoading(true);
5065
5093
  }
5066
- await clientRef.current.sendPrompt(message, multimodalContent);
5067
- }, [activatePendingChat, currentChatId, saveUserMessage, fileUploadConfig, getCurrentChat]);
5068
- handleSendMessageRef.current = handleSendMessage;
5094
+ const providerResult = forwardedPropsProvider ? forwardedPropsProvider() : {};
5095
+ const providerProps = providerResult instanceof Promise ? await providerResult : providerResult;
5096
+ const mergedForwardedProps = {
5097
+ ...providerProps,
5098
+ ...messageForwardedProps
5099
+ };
5100
+ await clientRef.current.sendPrompt(
5101
+ message,
5102
+ multimodalContent,
5103
+ Object.keys(mergedForwardedProps).length > 0 ? mergedForwardedProps : void 0
5104
+ );
5105
+ }, [chatManagement, serverEvents, fileUploadConfig, forwardedPropsProvider]);
5106
+ const messageQueue = useMessageQueue({
5107
+ sendFn: handleSendMessage,
5108
+ createNewChat: chatManagement.createNewChat,
5109
+ setOpen: handleSetChatOpen,
5110
+ connected,
5111
+ loading: serverEvents.loading,
5112
+ hasPendingApproval: toolSystem.pendingApprovals.length > 0
5113
+ });
5069
5114
  const value = {
5070
5115
  serverUrl,
5071
5116
  connected,
5072
5117
  client: clientRef.current,
5073
5118
  tools: {
5074
- register: registerTools,
5075
- unregister: unregisterTools
5119
+ register: toolSystem.registerTools,
5120
+ unregister: toolSystem.unregisterTools,
5121
+ signalReady: toolSystem.signalReady,
5122
+ registerWaiter: toolSystem.registerWaiter,
5123
+ unregisterWaiter: toolSystem.unregisterWaiter
5076
5124
  },
5077
5125
  prompts: {
5078
- update: updatePrompt,
5079
- registerWaiter,
5080
- unregisterWaiter
5126
+ update: promptState.updatePrompt
5081
5127
  },
5082
5128
  chat: {
5083
- currentId: currentChatId,
5084
- create: createNewChat,
5085
- load: loadChat,
5086
- delete: deleteChat,
5087
- list: listChats,
5088
- clear: clearCurrentChat,
5089
- sendMessage,
5090
- get: getCurrentChat,
5091
- updateMetadata
5129
+ currentId: chatManagement.currentChatId,
5130
+ create: chatManagement.createNewChat,
5131
+ load: chatManagement.loadChat,
5132
+ delete: chatManagement.deleteChat,
5133
+ list: chatManagement.listChats,
5134
+ clear: chatManagement.clearCurrentChat,
5135
+ sendMessage: messageQueue.sendMessage,
5136
+ get: chatManagement.getCurrentChat,
5137
+ updateMetadata: chatManagement.updateMetadata
5092
5138
  },
5093
5139
  agents: {
5094
5140
  available: availableAgents,
@@ -5104,26 +5150,23 @@ function UseAIProvider({
5104
5150
  delete: deleteCommand
5105
5151
  }
5106
5152
  };
5107
- const effectiveStreamingText = streamingChatIdRef.current === displayedChatId ? streamingText : "";
5108
- const executingToolDisplay = executingTool ? {
5109
- displayText: executingTool.title ?? executingToolFallbackRef.current ?? strings.toolExecution.fallbackMessages[0]
5110
- } : null;
5153
+ const effectiveStreamingText = serverEvents.streamingChatIdRef.current === chatManagement.displayedChatId ? serverEvents.streamingText : "";
5111
5154
  const chatUIContextValue = {
5112
5155
  connected,
5113
- loading,
5156
+ loading: serverEvents.loading,
5114
5157
  sendMessage: handleSendMessage,
5115
5158
  messages,
5116
5159
  streamingText: effectiveStreamingText,
5117
- suggestions: aggregatedSuggestions,
5160
+ suggestions: promptState.aggregatedSuggestions,
5118
5161
  fileUploadConfig,
5119
5162
  fileProcessing: fileProcessingState,
5120
5163
  history: {
5121
- currentId: displayedChatId,
5122
- create: createNewChat,
5123
- load: loadChat,
5124
- delete: deleteChat,
5125
- list: listChats,
5126
- get: getCurrentChat
5164
+ currentId: chatManagement.displayedChatId,
5165
+ create: chatManagement.createNewChat,
5166
+ load: chatManagement.loadChat,
5167
+ delete: chatManagement.deleteChat,
5168
+ list: chatManagement.listChats,
5169
+ get: chatManagement.getCurrentChat
5127
5170
  },
5128
5171
  agents: {
5129
5172
  available: availableAgents,
@@ -5142,11 +5185,11 @@ function UseAIProvider({
5142
5185
  setOpen: handleSetChatOpen
5143
5186
  },
5144
5187
  tools: {
5145
- executing: executingToolDisplay,
5188
+ executing: serverEvents.executingTool,
5146
5189
  pending: {
5147
- tools: toolExecution.pendingApprovals,
5148
- approveAll: toolExecution.approveAll,
5149
- rejectAll: toolExecution.rejectAll
5190
+ tools: toolSystem.pendingApprovals,
5191
+ approveAll: toolSystem.approveAll,
5192
+ rejectAll: toolSystem.rejectAll
5150
5193
  }
5151
5194
  },
5152
5195
  feedback: {
@@ -5160,15 +5203,15 @@ function UseAIProvider({
5160
5203
  const chatPanelProps = {
5161
5204
  onSendMessage: handleSendMessage,
5162
5205
  messages,
5163
- loading,
5206
+ loading: serverEvents.loading,
5164
5207
  connected,
5165
5208
  streamingText: effectiveStreamingText,
5166
- currentChatId: displayedChatId,
5167
- onNewChat: createNewChat,
5168
- onLoadChat: loadChat,
5169
- onDeleteChat: deleteChat,
5170
- onListChats: listChats,
5171
- suggestions: aggregatedSuggestions,
5209
+ currentChatId: chatManagement.displayedChatId,
5210
+ onNewChat: chatManagement.createNewChat,
5211
+ onLoadChat: chatManagement.loadChat,
5212
+ onDeleteChat: chatManagement.deleteChat,
5213
+ onListChats: chatManagement.listChats,
5214
+ suggestions: promptState.aggregatedSuggestions,
5172
5215
  availableAgents,
5173
5216
  defaultAgent,
5174
5217
  selectedAgent,
@@ -5179,12 +5222,12 @@ function UseAIProvider({
5179
5222
  onSaveCommand: saveCommand,
5180
5223
  onRenameCommand: renameCommand,
5181
5224
  onDeleteCommand: deleteCommand,
5182
- executingTool: executingToolDisplay,
5225
+ executingTool: serverEvents.executingTool,
5183
5226
  feedbackEnabled: feedback.enabled,
5184
5227
  onFeedback: feedback.submitFeedback,
5185
- pendingApprovals: toolExecution.pendingApprovals,
5186
- onApproveToolCall: toolExecution.pendingApprovals.length > 0 ? toolExecution.approveAll : void 0,
5187
- onRejectToolCall: toolExecution.pendingApprovals.length > 0 ? toolExecution.rejectAll : void 0
5228
+ pendingApprovals: toolSystem.pendingApprovals,
5229
+ onApproveToolCall: toolSystem.pendingApprovals.length > 0 ? toolSystem.approveAll : void 0,
5230
+ onRejectToolCall: toolSystem.pendingApprovals.length > 0 ? toolSystem.rejectAll : void 0
5188
5231
  };
5189
5232
  const renderDefaultChat = () => {
5190
5233
  if (isUIDisabled) return null;
@@ -5205,9 +5248,9 @@ function UseAIProvider({
5205
5248
  onClose: () => handleSetChatOpen(false),
5206
5249
  onSendMessage: handleSendMessage,
5207
5250
  messages,
5208
- loading,
5251
+ loading: serverEvents.loading,
5209
5252
  connected,
5210
- suggestions: aggregatedSuggestions,
5253
+ suggestions: promptState.aggregatedSuggestions,
5211
5254
  availableAgents,
5212
5255
  defaultAgent,
5213
5256
  selectedAgent,
@@ -5248,11 +5291,11 @@ function useAIContext() {
5248
5291
  }
5249
5292
 
5250
5293
  // src/hooks/useStableTools.ts
5251
- import { useRef as useRef12 } from "react";
5294
+ import { useRef as useRef13 } from "react";
5252
5295
  function useStableTools(tools) {
5253
- const latestToolsRef = useRef12({});
5254
- const stableToolsRef = useRef12({});
5255
- const prevToolNamesRef = useRef12("");
5296
+ const latestToolsRef = useRef13({});
5297
+ const stableToolsRef = useRef13({});
5298
+ const prevToolNamesRef = useRef13("");
5256
5299
  if (!tools) {
5257
5300
  latestToolsRef.current = {};
5258
5301
  return void 0;
@@ -5320,27 +5363,27 @@ function namespaceTools(tools, namespace) {
5320
5363
  function useAI(options = {}) {
5321
5364
  const { enabled = true } = options;
5322
5365
  const { connected, tools, client, prompts } = useAIContext();
5323
- const { register: registerTools, unregister: unregisterTools } = tools;
5324
- const { update: updatePrompt, registerWaiter, unregisterWaiter } = prompts;
5366
+ const { register: registerTools, unregister: unregisterTools, signalReady, registerWaiter, unregisterWaiter } = tools;
5367
+ const { update: updatePrompt } = prompts;
5325
5368
  const [response, setResponse] = useState14(null);
5326
5369
  const [loading, setLoading] = useState14(false);
5327
5370
  const [error, setError] = useState14(null);
5328
- const hookId = useRef13(`useAI-${Math.random().toString(36).substr(2, 9)}`);
5329
- const toolsRef = useRef13({});
5330
- const componentRef = useRef13(null);
5331
- const promptChangeResolvers = useRef13([]);
5371
+ const hookId = useRef14(`useAI-${Math.random().toString(36).substr(2, 9)}`);
5372
+ const toolsRef = useRef14({});
5373
+ const componentRef = useRef14(null);
5374
+ const promptChangeResolvers = useRef14([]);
5332
5375
  const stableTools = useStableTools(options.tools);
5333
5376
  const toolsKey = useMemo6(() => {
5334
5377
  if (!options.tools) return "";
5335
5378
  return Object.keys(options.tools).sort().join(",");
5336
5379
  }, [options.tools]);
5337
5380
  const memoizedSuggestions = useMemo6(() => options.suggestions, [options.suggestions]);
5338
- useEffect11(() => {
5381
+ useEffect12(() => {
5339
5382
  if (componentRef.current) {
5340
5383
  componentRef.current.setAttribute("data-useai-context", "true");
5341
5384
  }
5342
5385
  }, []);
5343
- const waitForPromptChange = useCallback12(() => {
5386
+ const waitForPromptChange = useCallback13(() => {
5344
5387
  return new Promise((resolve) => {
5345
5388
  const timeoutMs = 100;
5346
5389
  const timeoutId = setTimeout(() => {
@@ -5357,14 +5400,14 @@ function useAI(options = {}) {
5357
5400
  promptChangeResolvers.current.push(resolveAndCleanup);
5358
5401
  });
5359
5402
  }, []);
5360
- useEffect11(() => {
5403
+ useEffect12(() => {
5361
5404
  if (!enabled || options.invisible) return;
5362
5405
  registerWaiter(hookId.current, waitForPromptChange);
5363
5406
  return () => {
5364
5407
  unregisterWaiter(hookId.current);
5365
5408
  };
5366
5409
  }, [enabled, options.invisible, registerWaiter, unregisterWaiter, waitForPromptChange]);
5367
- useEffect11(() => {
5410
+ useEffect12(() => {
5368
5411
  if (!enabled) return;
5369
5412
  updatePrompt(hookId.current, options.prompt, memoizedSuggestions);
5370
5413
  if (promptChangeResolvers.current.length > 0) {
@@ -5372,29 +5415,30 @@ function useAI(options = {}) {
5372
5415
  promptChangeResolvers.current = [];
5373
5416
  }
5374
5417
  }, [enabled, options.prompt, memoizedSuggestions, updatePrompt]);
5375
- const updatePromptRef = useRef13(updatePrompt);
5418
+ const updatePromptRef = useRef14(updatePrompt);
5376
5419
  updatePromptRef.current = updatePrompt;
5377
- useEffect11(() => {
5420
+ useEffect12(() => {
5378
5421
  const id = hookId.current;
5379
5422
  return () => {
5380
5423
  updatePromptRef.current(id, void 0, void 0);
5381
5424
  };
5382
5425
  }, []);
5383
- useEffect11(() => {
5426
+ useLayoutEffect(() => {
5384
5427
  if (!enabled) return;
5385
5428
  if (stableTools) {
5386
5429
  const componentId = options.id || componentRef.current?.id;
5387
5430
  const toolsToRegister = componentId ? namespaceTools(stableTools, componentId) : stableTools;
5388
5431
  registerTools(hookId.current, toolsToRegister, { invisible: options.invisible });
5389
5432
  toolsRef.current = toolsToRegister;
5433
+ signalReady(hookId.current);
5390
5434
  }
5391
5435
  return () => {
5392
5436
  if (stableTools) {
5393
5437
  unregisterTools(hookId.current);
5394
5438
  }
5395
5439
  };
5396
- }, [enabled, toolsKey, stableTools, options.id, options.invisible, registerTools, unregisterTools]);
5397
- useEffect11(() => {
5440
+ }, [enabled, toolsKey, stableTools, options.id, options.invisible, registerTools, unregisterTools, signalReady]);
5441
+ useEffect12(() => {
5398
5442
  if (!enabled || !client) return;
5399
5443
  const unsubscribe = client.onEvent(hookId.current, (event) => {
5400
5444
  handleAGUIEvent(event);
@@ -5403,9 +5447,9 @@ function useAI(options = {}) {
5403
5447
  unsubscribe();
5404
5448
  };
5405
5449
  }, [enabled, client]);
5406
- const handleAGUIEvent = useCallback12(async (event) => {
5450
+ const handleAGUIEvent = useCallback13(async (event) => {
5407
5451
  switch (event.type) {
5408
- case EventType.TEXT_MESSAGE_END: {
5452
+ case EventType2.TEXT_MESSAGE_END: {
5409
5453
  const content = client?.currentMessageContent;
5410
5454
  if (content) {
5411
5455
  setResponse(content);
@@ -5413,7 +5457,7 @@ function useAI(options = {}) {
5413
5457
  }
5414
5458
  break;
5415
5459
  }
5416
- case EventType.RUN_ERROR: {
5460
+ case EventType2.RUN_ERROR: {
5417
5461
  const errorEvent = event;
5418
5462
  const error2 = new Error(errorEvent.message);
5419
5463
  setError(error2);
@@ -5423,7 +5467,7 @@ function useAI(options = {}) {
5423
5467
  }
5424
5468
  }
5425
5469
  }, [client, options.onError]);
5426
- const generate = useCallback12(async (prompt) => {
5470
+ const generate = useCallback13(async (prompt) => {
5427
5471
  if (!enabled) {
5428
5472
  const error2 = new Error("AI features are disabled");
5429
5473
  setError(error2);
@@ -5459,7 +5503,7 @@ function useAI(options = {}) {
5459
5503
  }
5460
5504
 
5461
5505
  // src/useAIWorkflow.ts
5462
- import { useState as useState15, useCallback as useCallback13, useRef as useRef14, useEffect as useEffect12 } from "react";
5506
+ import { useState as useState15, useCallback as useCallback14, useRef as useRef15, useEffect as useEffect13 } from "react";
5463
5507
  import { EventType as EventType3 } from "@meetsmore-oss/use-ai-core";
5464
5508
  import { v4 as uuidv43 } from "uuid";
5465
5509
  function useAIWorkflow(runner, workflowId) {
@@ -5467,9 +5511,9 @@ function useAIWorkflow(runner, workflowId) {
5467
5511
  const [status, setStatus] = useState15("idle");
5468
5512
  const [text, setText] = useState15(null);
5469
5513
  const [error, setError] = useState15(null);
5470
- const currentWorkflowRef = useRef14(null);
5471
- const eventListenerIdRef = useRef14(`useAIWorkflow-${Math.random().toString(36).substr(2, 9)}`);
5472
- const handleWorkflowEvent = useCallback13(async (event) => {
5514
+ const currentWorkflowRef = useRef15(null);
5515
+ const eventListenerIdRef = useRef15(`useAIWorkflow-${Math.random().toString(36).substr(2, 9)}`);
5516
+ const handleWorkflowEvent = useCallback14(async (event) => {
5473
5517
  const currentWorkflow = currentWorkflowRef.current;
5474
5518
  if (!currentWorkflow) return;
5475
5519
  if (event.type === EventType3.RUN_STARTED) {
@@ -5554,14 +5598,14 @@ function useAIWorkflow(runner, workflowId) {
5554
5598
  }
5555
5599
  }
5556
5600
  }, [client]);
5557
- useEffect12(() => {
5601
+ useEffect13(() => {
5558
5602
  if (!client) return;
5559
5603
  const unsubscribe = client.onEvent(eventListenerIdRef.current, handleWorkflowEvent);
5560
5604
  return () => {
5561
5605
  unsubscribe();
5562
5606
  };
5563
5607
  }, [client, handleWorkflowEvent]);
5564
- const trigger = useCallback13(async (options) => {
5608
+ const trigger = useCallback14(async (options) => {
5565
5609
  if (!client?.isConnected()) {
5566
5610
  const err = new Error("Not connected to server");
5567
5611
  setError(err);
@@ -5652,12 +5696,14 @@ export {
5652
5696
  useDropdownState,
5653
5697
  useFeedback,
5654
5698
  useFileUpload,
5699
+ useMessageQueue,
5655
5700
  usePromptState,
5701
+ useServerEvents,
5656
5702
  useSlashCommands,
5657
5703
  useStableTools,
5658
5704
  useStrings,
5659
5705
  useTheme,
5660
- useToolRegistry,
5706
+ useToolSystem,
5661
5707
  validateCommandName,
5662
5708
  z2 as z
5663
5709
  };