@meetsmore-oss/use-ai-client 1.7.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
  /**
@@ -3146,7 +3143,7 @@ var UseAIClient = class {
3146
3143
  });
3147
3144
  }
3148
3145
  handleEvent(event) {
3149
- if (event.type === EventType2.RUN_STARTED) {
3146
+ if (event.type === EventType.RUN_STARTED) {
3150
3147
  this._currentAssistantMessage = {
3151
3148
  id: uuidv42(),
3152
3149
  role: "assistant",
@@ -3154,31 +3151,31 @@ var UseAIClient = class {
3154
3151
  };
3155
3152
  this._currentAssistantToolCalls = [];
3156
3153
  }
3157
- if (event.type === EventType2.TEXT_MESSAGE_START) {
3154
+ if (event.type === EventType.TEXT_MESSAGE_START) {
3158
3155
  const e = event;
3159
3156
  this._currentMessageId = e.messageId;
3160
3157
  this._currentMessageContent = "";
3161
- } else if (event.type === EventType2.TEXT_MESSAGE_CONTENT) {
3158
+ } else if (event.type === EventType.TEXT_MESSAGE_CONTENT) {
3162
3159
  const e = event;
3163
3160
  this._currentMessageContent += e.delta;
3164
- } else if (event.type === EventType2.TEXT_MESSAGE_END) {
3161
+ } else if (event.type === EventType.TEXT_MESSAGE_END) {
3165
3162
  if (this._currentAssistantMessage) {
3166
3163
  this._currentAssistantMessage.content = this._currentMessageContent;
3167
3164
  }
3168
3165
  this._currentMessageId = null;
3169
- } else if (event.type === EventType2.TOOL_CALL_START) {
3166
+ } else if (event.type === EventType.TOOL_CALL_START) {
3170
3167
  const e = event;
3171
3168
  this.currentToolCalls.set(e.toolCallId, {
3172
3169
  name: e.toolCallName,
3173
3170
  args: ""
3174
3171
  });
3175
- } else if (event.type === EventType2.TOOL_CALL_ARGS) {
3172
+ } else if (event.type === EventType.TOOL_CALL_ARGS) {
3176
3173
  const e = event;
3177
3174
  const toolCall = this.currentToolCalls.get(e.toolCallId);
3178
3175
  if (toolCall) {
3179
3176
  toolCall.args += e.delta;
3180
3177
  }
3181
- } else if (event.type === EventType2.TOOL_CALL_END) {
3178
+ } else if (event.type === EventType.TOOL_CALL_END) {
3182
3179
  const e = event;
3183
3180
  const toolCall = this.currentToolCalls.get(e.toolCallId);
3184
3181
  if (toolCall) {
@@ -3191,7 +3188,7 @@ var UseAIClient = class {
3191
3188
  }
3192
3189
  });
3193
3190
  }
3194
- } else if (event.type === EventType2.RUN_FINISHED) {
3191
+ } else if (event.type === EventType.RUN_FINISHED) {
3195
3192
  if (this._currentAssistantMessage) {
3196
3193
  const assistantMessage = {
3197
3194
  id: this._currentAssistantMessage.id,
@@ -3308,7 +3305,13 @@ var UseAIClient = class {
3308
3305
  messageId: uuidv42(),
3309
3306
  toolCallId,
3310
3307
  content: JSON.stringify(result),
3311
- 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
+ }
3312
3315
  }
3313
3316
  };
3314
3317
  const toolResultMsg = {
@@ -3371,7 +3374,7 @@ var UseAIClient = class {
3371
3374
  */
3372
3375
  onTextMessage(handler) {
3373
3376
  return this.onEvent("text-message-handler", (event) => {
3374
- if (event.type === EventType2.TEXT_MESSAGE_END && this._currentMessageContent) {
3377
+ if (event.type === EventType.TEXT_MESSAGE_END && this._currentMessageContent) {
3375
3378
  handler(this._currentMessageContent);
3376
3379
  }
3377
3380
  });
@@ -3385,7 +3388,7 @@ var UseAIClient = class {
3385
3388
  */
3386
3389
  onToolCall(handler) {
3387
3390
  return this.onEvent("tool-call-handler", (event) => {
3388
- if (event.type === EventType2.TOOL_CALL_END) {
3391
+ if (event.type === EventType.TOOL_CALL_END) {
3389
3392
  const e = event;
3390
3393
  const toolCall = this.currentToolCalls.get(e.toolCallId);
3391
3394
  if (toolCall) {
@@ -3850,11 +3853,7 @@ function useChatManagement({
3850
3853
  clientRef,
3851
3854
  messages,
3852
3855
  setMessages,
3853
- onSendMessage,
3854
- setOpen,
3855
- connected,
3856
- loading,
3857
- hasPendingApproval
3856
+ connected
3858
3857
  }) {
3859
3858
  const [currentChatId, setCurrentChatId] = useState6(null);
3860
3859
  const [pendingChatId, setPendingChatId] = useState6(null);
@@ -3885,7 +3884,7 @@ function useChatManagement({
3885
3884
  const reloadMessages = useCallback4(async (chatId) => {
3886
3885
  const loadedMessages = await loadChatMessages(chatId);
3887
3886
  setMessages(loadedMessages);
3888
- }, [loadChatMessages]);
3887
+ }, [loadChatMessages, setMessages]);
3889
3888
  const createNewChat = useCallback4(async (options) => {
3890
3889
  console.log("[ChatManagement] createNewChat called - currentChatId:", currentChatId, "pendingChatId:", pendingChatId, "messages.length:", messages.length);
3891
3890
  if (pendingChatId && messages.length === 0) {
@@ -3906,7 +3905,7 @@ function useChatManagement({
3906
3905
  }
3907
3906
  console.log("[ChatManagement] Created pending chat:", chatId, "(will activate on first message)");
3908
3907
  return chatId;
3909
- }, [currentChatId, pendingChatId, messages, repository, clientRef]);
3908
+ }, [currentChatId, pendingChatId, messages, repository, clientRef, setMessages]);
3910
3909
  const loadChat = useCallback4(async (chatId) => {
3911
3910
  setPendingChatId(chatId);
3912
3911
  await reloadMessages(chatId);
@@ -3927,7 +3926,7 @@ function useChatManagement({
3927
3926
  setMessages([]);
3928
3927
  }
3929
3928
  console.log("[ChatManagement] Deleted chat:", chatId);
3930
- }, [currentChatId, pendingChatId, repository]);
3929
+ }, [currentChatId, pendingChatId, repository, setMessages]);
3931
3930
  const listChats = useCallback4(async () => {
3932
3931
  return await repository.listChats();
3933
3932
  }, [repository]);
@@ -3941,7 +3940,7 @@ function useChatManagement({
3941
3940
  console.log("[ChatManagement] Cleared current chat:", currentChatId);
3942
3941
  }
3943
3942
  }
3944
- }, [currentChatId, repository]);
3943
+ }, [currentChatId, repository, setMessages]);
3945
3944
  const getCurrentChat = useCallback4(async () => {
3946
3945
  const chatId = pendingChatId || currentChatId;
3947
3946
  if (!chatId) return null;
@@ -4068,76 +4067,8 @@ function useChatManagement({
4068
4067
  }
4069
4068
  })();
4070
4069
  }
4071
- }, [currentChatId, pendingChatId, createNewChat, repository, loadChatMessages, clientRef]);
4070
+ }, [currentChatId, pendingChatId, createNewChat, repository, loadChatMessages, clientRef, setMessages]);
4072
4071
  const displayedChatId = pendingChatId || currentChatId;
4073
- const pendingMessagesRef = useRef5([]);
4074
- const isProcessingQueueRef = useRef5(false);
4075
- const loadingRef = useRef5(loading);
4076
- useEffect5(() => {
4077
- loadingRef.current = loading;
4078
- }, [loading]);
4079
- const hasPendingApprovalRef = useRef5(hasPendingApproval);
4080
- useEffect5(() => {
4081
- hasPendingApprovalRef.current = hasPendingApproval;
4082
- }, [hasPendingApproval]);
4083
- const processMessageQueue = useCallback4(async () => {
4084
- if (isProcessingQueueRef.current || pendingMessagesRef.current.length === 0 || !onSendMessage) {
4085
- return;
4086
- }
4087
- isProcessingQueueRef.current = true;
4088
- while (pendingMessagesRef.current.length > 0) {
4089
- const { message, options } = pendingMessagesRef.current.shift();
4090
- const { newChat = false, attachments = [], openChat = true, metadata, forwardedProps } = options ?? {};
4091
- if (newChat) {
4092
- await createNewChat({ metadata });
4093
- }
4094
- const fileAttachments = await Promise.all(
4095
- attachments.map(async (file) => {
4096
- let preview;
4097
- if (file.type.startsWith("image/")) {
4098
- preview = await new Promise((resolve) => {
4099
- const reader = new FileReader();
4100
- reader.onload = () => resolve(typeof reader.result === "string" ? reader.result : void 0);
4101
- reader.onerror = () => resolve(void 0);
4102
- reader.readAsDataURL(file);
4103
- });
4104
- }
4105
- return {
4106
- id: crypto.randomUUID(),
4107
- file,
4108
- preview
4109
- };
4110
- })
4111
- );
4112
- await onSendMessage(message, fileAttachments.length > 0 ? fileAttachments : void 0, forwardedProps);
4113
- if (openChat && setOpen) {
4114
- setOpen(true);
4115
- }
4116
- await new Promise((resolve) => {
4117
- const checkReady = () => {
4118
- setTimeout(() => {
4119
- if (!loadingRef.current && !hasPendingApprovalRef.current) {
4120
- resolve();
4121
- } else {
4122
- checkReady();
4123
- }
4124
- }, 100);
4125
- };
4126
- checkReady();
4127
- });
4128
- }
4129
- isProcessingQueueRef.current = false;
4130
- }, [onSendMessage, createNewChat, setOpen]);
4131
- const sendMessage = useCallback4(async (message, options) => {
4132
- if (!onSendMessage) {
4133
- throw new Error("sendMessage is not available (onSendMessage callback not provided)");
4134
- }
4135
- if (!connected) {
4136
- throw new Error("Not connected to UseAI server");
4137
- }
4138
- pendingMessagesRef.current.push({ message, options });
4139
- await processMessageQueue();
4140
- }, [onSendMessage, connected, processMessageQueue]);
4141
4072
  return {
4142
4073
  currentChatId,
4143
4074
  pendingChatId,
@@ -4151,7 +4082,6 @@ function useChatManagement({
4151
4082
  saveUserMessage,
4152
4083
  saveAIResponse,
4153
4084
  reloadMessages,
4154
- sendMessage,
4155
4085
  getCurrentChat,
4156
4086
  updateMetadata,
4157
4087
  currentChatIdSnapshot,
@@ -4399,16 +4329,25 @@ function useCommandManagement({
4399
4329
  };
4400
4330
  }
4401
4331
 
4402
- // src/hooks/useToolRegistry.ts
4332
+ // src/hooks/useToolSystem.ts
4403
4333
  import { useState as useState9, useCallback as useCallback7, useRef as useRef7, useMemo as useMemo4 } from "react";
4404
- function useToolRegistry() {
4334
+ function useToolSystem({
4335
+ clientRef,
4336
+ buildState
4337
+ }) {
4405
4338
  const toolRegistryRef = useRef7(/* @__PURE__ */ new Map());
4406
4339
  const [toolRegistryVersion, setToolRegistryVersion] = useState9(0);
4407
4340
  const toolOwnershipRef = useRef7(/* @__PURE__ */ new Map());
4408
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());
4409
4347
  const registerTools = useCallback7((id, tools, options) => {
4410
4348
  const existingTools = toolRegistryRef.current.get(id);
4411
4349
  toolRegistryRef.current.set(id, tools);
4350
+ readyStateRef.current.set(id, false);
4412
4351
  if (existingTools) {
4413
4352
  const existingKeys = Object.keys(existingTools).sort().join(",");
4414
4353
  const newKeys = Object.keys(tools).sort().join(",");
@@ -4427,6 +4366,13 @@ function useToolRegistry() {
4427
4366
  invisibleRef.current.delete(id);
4428
4367
  }
4429
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
+ }, []);
4430
4376
  const unregisterTools = useCallback7((id) => {
4431
4377
  const tools = toolRegistryRef.current.get(id);
4432
4378
  if (tools) {
@@ -4435,8 +4381,10 @@ function useToolRegistry() {
4435
4381
  });
4436
4382
  }
4437
4383
  toolRegistryRef.current.delete(id);
4384
+ readyStateRef.current.delete(id);
4438
4385
  setToolRegistryVersion((v) => v + 1);
4439
4386
  invisibleRef.current.delete(id);
4387
+ readyListenersRef.current.forEach((listener) => listener());
4440
4388
  }, []);
4441
4389
  const isInvisible = useCallback7((id) => {
4442
4390
  return invisibleRef.current.has(id);
@@ -4451,14 +4399,159 @@ function useToolRegistry() {
4451
4399
  const hasTools = toolRegistryRef.current.size > 0;
4452
4400
  const aggregatedToolsRef = useRef7(aggregatedTools);
4453
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]);
4454
4535
  return {
4536
+ // Registry
4455
4537
  registerTools,
4456
4538
  unregisterTools,
4457
4539
  isInvisible,
4458
4540
  aggregatedTools,
4459
4541
  hasTools,
4460
4542
  aggregatedToolsRef,
4461
- 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
4462
4555
  };
4463
4556
  }
4464
4557
 
@@ -4471,7 +4564,6 @@ function usePromptState({
4471
4564
  }) {
4472
4565
  const promptsRef = useRef8(/* @__PURE__ */ new Map());
4473
4566
  const suggestionsRef = useRef8(/* @__PURE__ */ new Map());
4474
- const waitersRef = useRef8(/* @__PURE__ */ new Map());
4475
4567
  const [suggestionsVersion, setSuggestionsVersion] = useState10(0);
4476
4568
  const buildStateFromPrompts = useCallback8(() => {
4477
4569
  const promptParts = [];
@@ -4508,15 +4600,6 @@ function usePromptState({
4508
4600
  clientRef.current.updateState(buildStateFromPrompts());
4509
4601
  }
4510
4602
  }, [buildStateFromPrompts, clientRef, connected]);
4511
- const registerWaiter = useCallback8((id, waiter) => {
4512
- waitersRef.current.set(id, waiter);
4513
- }, []);
4514
- const unregisterWaiter = useCallback8((id) => {
4515
- waitersRef.current.delete(id);
4516
- }, []);
4517
- const getWaiter = useCallback8((id) => {
4518
- return waitersRef.current.get(id);
4519
- }, []);
4520
4603
  const aggregatedSuggestions = useMemo5(() => {
4521
4604
  const allSuggestions = [];
4522
4605
  suggestionsRef.current.forEach((suggestions) => {
@@ -4526,11 +4609,8 @@ function usePromptState({
4526
4609
  }, [suggestionsVersion]);
4527
4610
  return {
4528
4611
  updatePrompt,
4529
- registerWaiter,
4530
- unregisterWaiter,
4531
- getWaiter,
4532
4612
  aggregatedSuggestions,
4533
- promptsRef
4613
+ buildStateFromPrompts
4534
4614
  };
4535
4615
  }
4536
4616
 
@@ -4596,116 +4676,190 @@ function useFeedback({
4596
4676
  };
4597
4677
  }
4598
4678
 
4599
- // src/hooks/useToolExecution.ts
4679
+ // src/hooks/useServerEvents.ts
4600
4680
  import { useState as useState12, useCallback as useCallback10, useRef as useRef10 } from "react";
4601
- function useToolExecution({
4602
- clientRef,
4603
- aggregatedToolsRef,
4604
- toolOwnershipRef,
4605
- promptsRef,
4606
- isInvisible,
4607
- 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
4608
4690
  }) {
4609
- const [pendingApprovals, setPendingApprovals] = useState12([]);
4610
- const pendingApprovalToolCallsRef = useRef10(/* @__PURE__ */ new Map());
4611
- const handleApprovalRequest = useCallback10((event) => {
4612
- console.log("[useToolExecution] Tool approval requested:", event.toolCallName, event.toolCallId);
4613
- setPendingApprovals((prev) => [
4614
- ...prev,
4615
- {
4616
- toolCallId: event.toolCallId,
4617
- toolCallName: event.toolCallName,
4618
- toolCallArgs: event.toolCallArgs,
4619
- 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)];
4620
4715
  }
4621
- ]);
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
+ }
4622
4766
  }, []);
4623
- const executeToolCall = useCallback10(async (toolCallId, name, input) => {
4624
- const client = clientRef.current;
4625
- if (!client) {
4626
- 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) {
4627
4809
  return;
4628
4810
  }
4629
- try {
4630
- const ownerId = toolOwnershipRef.current.get(name);
4631
- console.log(`[useToolExecution] Tool "${name}" owned by component:`, ownerId);
4632
- console.log("[useToolExecution] Executing tool...");
4633
- const result = await executeDefinedTool(aggregatedToolsRef.current, name, input);
4634
- const isErrorResult = result && typeof result === "object" && ("error" in result || result.success === false);
4635
- const ownerIsInvisible = ownerId ? isInvisible(ownerId) : false;
4636
- if (ownerId && !isErrorResult && !ownerIsInvisible) {
4637
- const waiter = getWaiter(ownerId);
4638
- if (waiter) {
4639
- console.log(`[useToolExecution] Waiting for prompt change from ${ownerId}...`);
4640
- await waiter();
4641
- console.log("[useToolExecution] Prompt change wait complete");
4642
- }
4643
- } else if (isErrorResult) {
4644
- console.log("[useToolExecution] Tool returned error, skipping prompt wait");
4645
- } else if (ownerIsInvisible) {
4646
- 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 });
4647
4817
  }
4648
- let updatedState = null;
4649
- if (ownerId) {
4650
- const prompt = promptsRef.current.get(ownerId);
4651
- if (prompt) {
4652
- updatedState = { context: prompt };
4653
- console.log(`[useToolExecution] Updated state from ${ownerId}`);
4654
- }
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);
4655
4839
  }
4656
- client.sendToolResponse(toolCallId, result, updatedState);
4657
- } catch (err) {
4658
- console.error("Tool execution error:", err);
4659
- client.sendToolResponse(toolCallId, {
4660
- 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();
4661
4851
  });
4662
4852
  }
4663
- }, [clientRef, aggregatedToolsRef, toolOwnershipRef, promptsRef, isInvisible, getWaiter]);
4664
- const storePendingToolCall = useCallback10((toolCallId, name, input, toolCallData) => {
4665
- console.log(`[useToolExecution] Storing pending tool call "${name}" for approval`);
4666
- pendingApprovalToolCallsRef.current.set(toolCallId, { name, input, toolCallData });
4853
+ isProcessingQueueRef.current = false;
4667
4854
  }, []);
4668
- const executePendingToolAfterApproval = useCallback10(async (toolCallId) => {
4669
- const pendingTool = pendingApprovalToolCallsRef.current.get(toolCallId);
4670
- if (!pendingTool) {
4671
- console.warn(`[useToolExecution] No pending tool found for ${toolCallId}`);
4672
- return;
4673
- }
4674
- pendingApprovalToolCallsRef.current.delete(toolCallId);
4675
- await executeToolCall(toolCallId, pendingTool.name, pendingTool.input);
4676
- }, [executeToolCall]);
4677
- const approveAll = useCallback10(async () => {
4678
- if (!clientRef.current) return;
4679
- console.log("[useToolExecution] Approving all tool calls:", pendingApprovals.length);
4680
- const pendingTools = [...pendingApprovals];
4681
- for (const pending of pendingTools) {
4682
- clientRef.current.sendToolApprovalResponse(pending.toolCallId, true);
4683
- }
4684
- setPendingApprovals([]);
4685
- for (const tool of pendingTools) {
4686
- await executePendingToolAfterApproval(tool.toolCallId);
4687
- }
4688
- }, [clientRef, pendingApprovals, executePendingToolAfterApproval]);
4689
- const rejectAll = useCallback10((reason) => {
4690
- if (!clientRef.current) return;
4691
- console.log("[useToolExecution] Rejecting all tool calls:", pendingApprovals.length, reason);
4692
- const pendingTools = [...pendingApprovals];
4693
- for (const pending of pendingTools) {
4694
- clientRef.current.sendToolApprovalResponse(pending.toolCallId, false, reason);
4695
- }
4696
- setPendingApprovals([]);
4697
- for (const tool of pendingTools) {
4698
- pendingApprovalToolCallsRef.current.delete(tool.toolCallId);
4855
+ const sendMessage = useCallback11(async (message, options) => {
4856
+ if (!connected) {
4857
+ throw new Error("Not connected to UseAI server");
4699
4858
  }
4700
- }, [clientRef, pendingApprovals]);
4701
- return {
4702
- pendingApprovals,
4703
- handleApprovalRequest,
4704
- executeToolCall,
4705
- storePendingToolCall,
4706
- approveAll,
4707
- rejectAll
4708
- };
4859
+ pendingMessagesRef.current.push({ message, options });
4860
+ await processMessageQueue();
4861
+ }, [connected, processMessageQueue]);
4862
+ return { sendMessage };
4709
4863
  }
4710
4864
 
4711
4865
  // src/providers/useAIProvider.tsx
@@ -4720,16 +4874,18 @@ var noOpContextValue = {
4720
4874
  register: () => {
4721
4875
  },
4722
4876
  unregister: () => {
4723
- }
4724
- },
4725
- prompts: {
4726
- update: () => {
4877
+ },
4878
+ signalReady: () => {
4727
4879
  },
4728
4880
  registerWaiter: () => {
4729
4881
  },
4730
4882
  unregisterWaiter: () => {
4731
4883
  }
4732
4884
  },
4885
+ prompts: {
4886
+ update: () => {
4887
+ }
4888
+ },
4733
4889
  chat: {
4734
4890
  currentId: null,
4735
4891
  create: async () => "",
@@ -4791,87 +4947,41 @@ function UseAIProvider({
4791
4947
  const strings = { ...defaultStrings, ...customStrings };
4792
4948
  const [connected, setConnected] = useState13(false);
4793
4949
  const [isChatOpen, setIsChatOpen] = useState13(false);
4794
- const [loading, setLoading] = useState13(false);
4795
4950
  const [messages, setMessages] = useState13([]);
4796
4951
  const [fileProcessingState, setFileProcessingState] = useState13(null);
4797
- const handleSetChatOpen = useCallback11((open) => {
4952
+ const handleSetChatOpen = useCallback12((open) => {
4798
4953
  setIsChatOpen(open);
4799
4954
  onOpenChange?.(open);
4800
4955
  }, [onOpenChange]);
4801
- const [streamingText, setStreamingText] = useState13("");
4802
- const streamingChatIdRef = useRef11(null);
4803
- const [executingTool, setExecutingTool] = useState13(null);
4804
- const executingToolFallbackRef = useRef11(null);
4805
- const clientRef = useRef11(null);
4806
- const repositoryRef = useRef11(
4956
+ const clientRef = useRef12(null);
4957
+ const repositoryRef = useRef12(
4807
4958
  chatRepository || new LocalStorageChatRepository()
4808
4959
  );
4809
- const handleSendMessageRef = useRef11(null);
4810
- const {
4811
- registerTools,
4812
- unregisterTools,
4813
- isInvisible,
4814
- aggregatedTools,
4815
- hasTools,
4816
- aggregatedToolsRef,
4817
- toolOwnershipRef
4818
- } = useToolRegistry();
4819
- const {
4820
- updatePrompt,
4821
- registerWaiter,
4822
- unregisterWaiter,
4823
- getWaiter,
4824
- aggregatedSuggestions,
4825
- promptsRef
4826
- } = usePromptState({
4960
+ const promptState = usePromptState({
4827
4961
  systemPrompt,
4828
4962
  clientRef,
4829
4963
  connected
4830
4964
  });
4831
- const stableSendMessage = useCallback11(async (message, attachments, forwardedProps) => {
4832
- if (handleSendMessageRef.current) {
4833
- await handleSendMessageRef.current(message, attachments, forwardedProps);
4834
- }
4835
- }, []);
4836
- const toolExecution = useToolExecution({
4965
+ const toolSystem = useToolSystem({
4837
4966
  clientRef,
4838
- aggregatedToolsRef,
4839
- toolOwnershipRef,
4840
- promptsRef,
4841
- isInvisible,
4842
- getWaiter
4967
+ buildState: promptState.buildStateFromPrompts
4843
4968
  });
4844
4969
  const chatManagement = useChatManagement({
4845
4970
  repository: repositoryRef.current,
4846
4971
  clientRef,
4847
4972
  messages,
4848
4973
  setMessages,
4849
- onSendMessage: stableSendMessage,
4850
- setOpen: handleSetChatOpen,
4851
- connected,
4852
- loading,
4853
- hasPendingApproval: toolExecution.pendingApprovals.length > 0
4974
+ connected
4975
+ });
4976
+ const serverEvents = useServerEvents({
4977
+ toolSystem,
4978
+ saveAIResponse: chatManagement.saveAIResponse,
4979
+ strings
4854
4980
  });
4855
- const {
4856
- currentChatId,
4857
- pendingChatId,
4858
- displayedChatId,
4859
- createNewChat,
4860
- loadChat,
4861
- deleteChat,
4862
- listChats,
4863
- clearCurrentChat,
4864
- activatePendingChat,
4865
- saveUserMessage,
4866
- saveAIResponse,
4867
- sendMessage,
4868
- getCurrentChat,
4869
- updateMetadata
4870
- } = chatManagement;
4871
4981
  const feedback = useFeedback({
4872
4982
  clientRef,
4873
4983
  repository: repositoryRef.current,
4874
- getDisplayedChatId: () => displayedChatId,
4984
+ getDisplayedChatId: () => chatManagement.displayedChatId,
4875
4985
  setMessages
4876
4986
  });
4877
4987
  const {
@@ -4887,7 +4997,9 @@ function UseAIProvider({
4887
4997
  renameCommand,
4888
4998
  deleteCommand
4889
4999
  } = useCommandManagement({ repository: commandRepository });
4890
- useEffect10(() => {
5000
+ const handleServerEventRef = useRef12(serverEvents.handleServerEvent);
5001
+ handleServerEventRef.current = serverEvents.handleServerEvent;
5002
+ useEffect11(() => {
4891
5003
  console.log("[UseAIProvider] Initializing client with serverUrl:", serverUrl);
4892
5004
  const client = new UseAIClient(serverUrl);
4893
5005
  const unsubscribeConnection = client.onConnectionStateChange((isConnected) => {
@@ -4897,64 +5009,7 @@ function UseAIProvider({
4897
5009
  console.log("[UseAIProvider] Connecting...");
4898
5010
  client.connect();
4899
5011
  const unsubscribe = client.onEvent("globalChat", async (event) => {
4900
- if (event.type === EventType.TOOL_CALL_START) {
4901
- const e = event;
4902
- const tool = aggregatedToolsRef.current[e.toolCallName];
4903
- const title = e.annotations?.title ?? tool?._options?.annotations?.title ?? null;
4904
- if (!title) {
4905
- const fallbacks = strings.toolExecution.fallbackMessages;
4906
- executingToolFallbackRef.current = fallbacks[Math.floor(Math.random() * fallbacks.length)];
4907
- }
4908
- setExecutingTool({ toolCallId: e.toolCallId, title });
4909
- } else if (event.type === EventType.TOOL_CALL_END) {
4910
- const toolCallEnd = event;
4911
- const toolCallId = toolCallEnd.toolCallId;
4912
- setExecutingTool((prev) => prev?.toolCallId === toolCallId ? null : prev);
4913
- const toolCallData = client["currentToolCalls"].get(toolCallId);
4914
- if (!toolCallData) {
4915
- console.error(`[Provider] Tool call ${toolCallId} not found`);
4916
- return;
4917
- }
4918
- const name = toolCallData.name;
4919
- const input = JSON.parse(toolCallData.args);
4920
- const tool = aggregatedToolsRef.current[name];
4921
- if (!tool) {
4922
- console.log(`[Provider] Tool "${name}" not found in useAI tools, skipping (likely a workflow tool)`);
4923
- return;
4924
- }
4925
- if (tool._options?.annotations?.destructiveHint === true) {
4926
- console.log(`[Provider] Tool "${name}" requires approval, deferring execution`);
4927
- toolExecution.storePendingToolCall(toolCallId, name, input, toolCallData);
4928
- return;
4929
- }
4930
- await toolExecution.executeToolCall(toolCallId, name, input);
4931
- } else if (event.type === TOOL_APPROVAL_REQUEST) {
4932
- const e = event;
4933
- toolExecution.handleApprovalRequest(e);
4934
- } else if (event.type === EventType.TEXT_MESSAGE_CONTENT) {
4935
- const contentEvent = event;
4936
- setStreamingText((prev) => prev + contentEvent.delta);
4937
- } else if (event.type === EventType.TEXT_MESSAGE_END) {
4938
- setStreamingText("");
4939
- streamingChatIdRef.current = null;
4940
- } else if (event.type === EventType.RUN_FINISHED) {
4941
- const content = client.currentMessageContent;
4942
- if (content) {
4943
- const finishedEvent = event;
4944
- const traceId = finishedEvent.runId;
4945
- saveAIResponse(content, void 0, traceId);
4946
- }
4947
- setLoading(false);
4948
- } else if (event.type === EventType.RUN_ERROR) {
4949
- const errorEvent = event;
4950
- const errorCode = errorEvent.message;
4951
- console.error("[Provider] Run error:", errorCode);
4952
- const userMessage = strings.errors[errorCode] || strings.errors[ErrorCode.UNKNOWN_ERROR];
4953
- saveAIResponse(userMessage, "error");
4954
- setStreamingText("");
4955
- streamingChatIdRef.current = null;
4956
- setLoading(false);
4957
- }
5012
+ await handleServerEventRef.current(client, event);
4958
5013
  });
4959
5014
  clientRef.current = client;
4960
5015
  return () => {
@@ -4963,11 +5018,11 @@ function UseAIProvider({
4963
5018
  client.disconnect();
4964
5019
  };
4965
5020
  }, [serverUrl]);
4966
- const lastRegisteredToolsRef = useRef11("");
4967
- useEffect10(() => {
5021
+ const lastRegisteredToolsRef = useRef12("");
5022
+ useEffect11(() => {
4968
5023
  const client = clientRef.current;
4969
- if (!client || !client.isConnected() || !hasTools) return;
4970
- const toolKeys = Object.keys(aggregatedTools).sort().join(",");
5024
+ if (!client || !client.isConnected() || !toolSystem.hasTools) return;
5025
+ const toolKeys = Object.keys(toolSystem.aggregatedTools).sort().join(",");
4971
5026
  if (toolKeys === lastRegisteredToolsRef.current) {
4972
5027
  console.log("[Provider] Skipping re-registration, tools unchanged");
4973
5028
  return;
@@ -4975,19 +5030,19 @@ function UseAIProvider({
4975
5030
  lastRegisteredToolsRef.current = toolKeys;
4976
5031
  console.log("[Provider] Registering tools:", toolKeys);
4977
5032
  try {
4978
- const toolDefinitions = convertToolsToDefinitions(aggregatedTools);
5033
+ const toolDefinitions = convertToolsToDefinitions(toolSystem.aggregatedTools);
4979
5034
  console.log(`[Provider] Registering ${toolDefinitions.length} tools`);
4980
5035
  client.registerTools(toolDefinitions);
4981
5036
  } catch (err) {
4982
5037
  console.error("Failed to register tools:", err);
4983
5038
  }
4984
- }, [hasTools, aggregatedTools, connected]);
4985
- const handleSendMessage = useCallback11(async (message, attachments, messageForwardedProps) => {
5039
+ }, [toolSystem.hasTools, toolSystem.aggregatedTools, connected]);
5040
+ const handleSendMessage = useCallback12(async (message, attachments, messageForwardedProps) => {
4986
5041
  if (!clientRef.current) return;
4987
- setStreamingText("");
4988
- const activatedChatId = activatePendingChat();
4989
- const activeChatId = activatedChatId || currentChatId;
4990
- streamingChatIdRef.current = activeChatId;
5042
+ serverEvents.clearStreamingText();
5043
+ const activatedChatId = chatManagement.activatePendingChat();
5044
+ const activeChatId = activatedChatId || chatManagement.currentChatId;
5045
+ serverEvents.streamingChatIdRef.current = activeChatId;
4991
5046
  let persistedContent = message;
4992
5047
  let multimodalContent;
4993
5048
  if (attachments && attachments.length > 0) {
@@ -5007,12 +5062,12 @@ function UseAIProvider({
5007
5062
  }
5008
5063
  persistedContent = persistedParts;
5009
5064
  if (activeChatId) {
5010
- await saveUserMessage(activeChatId, persistedContent);
5065
+ await chatManagement.saveUserMessage(activeChatId, persistedContent);
5011
5066
  }
5012
- setLoading(true);
5067
+ serverEvents.setLoading(true);
5013
5068
  try {
5014
5069
  const fileContent = await processAttachments(attachments, {
5015
- getCurrentChat,
5070
+ getCurrentChat: chatManagement.getCurrentChat,
5016
5071
  backend: fileUploadConfig?.backend,
5017
5072
  transformers: fileUploadConfig?.transformers,
5018
5073
  onFileProgress: (_fileId, state) => {
@@ -5025,16 +5080,16 @@ function UseAIProvider({
5025
5080
  }
5026
5081
  multimodalContent.push(...fileContent);
5027
5082
  } catch (error) {
5028
- setLoading(false);
5083
+ serverEvents.setLoading(false);
5029
5084
  throw error;
5030
5085
  } finally {
5031
5086
  setFileProcessingState(null);
5032
5087
  }
5033
5088
  } else {
5034
5089
  if (activeChatId) {
5035
- await saveUserMessage(activeChatId, persistedContent);
5090
+ await chatManagement.saveUserMessage(activeChatId, persistedContent);
5036
5091
  }
5037
- setLoading(true);
5092
+ serverEvents.setLoading(true);
5038
5093
  }
5039
5094
  const providerResult = forwardedPropsProvider ? forwardedPropsProvider() : {};
5040
5095
  const providerProps = providerResult instanceof Promise ? await providerResult : providerResult;
@@ -5047,31 +5102,39 @@ function UseAIProvider({
5047
5102
  multimodalContent,
5048
5103
  Object.keys(mergedForwardedProps).length > 0 ? mergedForwardedProps : void 0
5049
5104
  );
5050
- }, [activatePendingChat, currentChatId, saveUserMessage, fileUploadConfig, getCurrentChat, forwardedPropsProvider]);
5051
- handleSendMessageRef.current = handleSendMessage;
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
+ });
5052
5114
  const value = {
5053
5115
  serverUrl,
5054
5116
  connected,
5055
5117
  client: clientRef.current,
5056
5118
  tools: {
5057
- register: registerTools,
5058
- unregister: unregisterTools
5119
+ register: toolSystem.registerTools,
5120
+ unregister: toolSystem.unregisterTools,
5121
+ signalReady: toolSystem.signalReady,
5122
+ registerWaiter: toolSystem.registerWaiter,
5123
+ unregisterWaiter: toolSystem.unregisterWaiter
5059
5124
  },
5060
5125
  prompts: {
5061
- update: updatePrompt,
5062
- registerWaiter,
5063
- unregisterWaiter
5126
+ update: promptState.updatePrompt
5064
5127
  },
5065
5128
  chat: {
5066
- currentId: currentChatId,
5067
- create: createNewChat,
5068
- load: loadChat,
5069
- delete: deleteChat,
5070
- list: listChats,
5071
- clear: clearCurrentChat,
5072
- sendMessage,
5073
- get: getCurrentChat,
5074
- 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
5075
5138
  },
5076
5139
  agents: {
5077
5140
  available: availableAgents,
@@ -5087,26 +5150,23 @@ function UseAIProvider({
5087
5150
  delete: deleteCommand
5088
5151
  }
5089
5152
  };
5090
- const effectiveStreamingText = streamingChatIdRef.current === displayedChatId ? streamingText : "";
5091
- const executingToolDisplay = executingTool ? {
5092
- displayText: executingTool.title ?? executingToolFallbackRef.current ?? strings.toolExecution.fallbackMessages[0]
5093
- } : null;
5153
+ const effectiveStreamingText = serverEvents.streamingChatIdRef.current === chatManagement.displayedChatId ? serverEvents.streamingText : "";
5094
5154
  const chatUIContextValue = {
5095
5155
  connected,
5096
- loading,
5156
+ loading: serverEvents.loading,
5097
5157
  sendMessage: handleSendMessage,
5098
5158
  messages,
5099
5159
  streamingText: effectiveStreamingText,
5100
- suggestions: aggregatedSuggestions,
5160
+ suggestions: promptState.aggregatedSuggestions,
5101
5161
  fileUploadConfig,
5102
5162
  fileProcessing: fileProcessingState,
5103
5163
  history: {
5104
- currentId: displayedChatId,
5105
- create: createNewChat,
5106
- load: loadChat,
5107
- delete: deleteChat,
5108
- list: listChats,
5109
- 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
5110
5170
  },
5111
5171
  agents: {
5112
5172
  available: availableAgents,
@@ -5125,11 +5185,11 @@ function UseAIProvider({
5125
5185
  setOpen: handleSetChatOpen
5126
5186
  },
5127
5187
  tools: {
5128
- executing: executingToolDisplay,
5188
+ executing: serverEvents.executingTool,
5129
5189
  pending: {
5130
- tools: toolExecution.pendingApprovals,
5131
- approveAll: toolExecution.approveAll,
5132
- rejectAll: toolExecution.rejectAll
5190
+ tools: toolSystem.pendingApprovals,
5191
+ approveAll: toolSystem.approveAll,
5192
+ rejectAll: toolSystem.rejectAll
5133
5193
  }
5134
5194
  },
5135
5195
  feedback: {
@@ -5143,15 +5203,15 @@ function UseAIProvider({
5143
5203
  const chatPanelProps = {
5144
5204
  onSendMessage: handleSendMessage,
5145
5205
  messages,
5146
- loading,
5206
+ loading: serverEvents.loading,
5147
5207
  connected,
5148
5208
  streamingText: effectiveStreamingText,
5149
- currentChatId: displayedChatId,
5150
- onNewChat: createNewChat,
5151
- onLoadChat: loadChat,
5152
- onDeleteChat: deleteChat,
5153
- onListChats: listChats,
5154
- 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,
5155
5215
  availableAgents,
5156
5216
  defaultAgent,
5157
5217
  selectedAgent,
@@ -5162,12 +5222,12 @@ function UseAIProvider({
5162
5222
  onSaveCommand: saveCommand,
5163
5223
  onRenameCommand: renameCommand,
5164
5224
  onDeleteCommand: deleteCommand,
5165
- executingTool: executingToolDisplay,
5225
+ executingTool: serverEvents.executingTool,
5166
5226
  feedbackEnabled: feedback.enabled,
5167
5227
  onFeedback: feedback.submitFeedback,
5168
- pendingApprovals: toolExecution.pendingApprovals,
5169
- onApproveToolCall: toolExecution.pendingApprovals.length > 0 ? toolExecution.approveAll : void 0,
5170
- 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
5171
5231
  };
5172
5232
  const renderDefaultChat = () => {
5173
5233
  if (isUIDisabled) return null;
@@ -5188,9 +5248,9 @@ function UseAIProvider({
5188
5248
  onClose: () => handleSetChatOpen(false),
5189
5249
  onSendMessage: handleSendMessage,
5190
5250
  messages,
5191
- loading,
5251
+ loading: serverEvents.loading,
5192
5252
  connected,
5193
- suggestions: aggregatedSuggestions,
5253
+ suggestions: promptState.aggregatedSuggestions,
5194
5254
  availableAgents,
5195
5255
  defaultAgent,
5196
5256
  selectedAgent,
@@ -5231,11 +5291,11 @@ function useAIContext() {
5231
5291
  }
5232
5292
 
5233
5293
  // src/hooks/useStableTools.ts
5234
- import { useRef as useRef12 } from "react";
5294
+ import { useRef as useRef13 } from "react";
5235
5295
  function useStableTools(tools) {
5236
- const latestToolsRef = useRef12({});
5237
- const stableToolsRef = useRef12({});
5238
- const prevToolNamesRef = useRef12("");
5296
+ const latestToolsRef = useRef13({});
5297
+ const stableToolsRef = useRef13({});
5298
+ const prevToolNamesRef = useRef13("");
5239
5299
  if (!tools) {
5240
5300
  latestToolsRef.current = {};
5241
5301
  return void 0;
@@ -5303,27 +5363,27 @@ function namespaceTools(tools, namespace) {
5303
5363
  function useAI(options = {}) {
5304
5364
  const { enabled = true } = options;
5305
5365
  const { connected, tools, client, prompts } = useAIContext();
5306
- const { register: registerTools, unregister: unregisterTools } = tools;
5307
- const { update: updatePrompt, registerWaiter, unregisterWaiter } = prompts;
5366
+ const { register: registerTools, unregister: unregisterTools, signalReady, registerWaiter, unregisterWaiter } = tools;
5367
+ const { update: updatePrompt } = prompts;
5308
5368
  const [response, setResponse] = useState14(null);
5309
5369
  const [loading, setLoading] = useState14(false);
5310
5370
  const [error, setError] = useState14(null);
5311
- const hookId = useRef13(`useAI-${Math.random().toString(36).substr(2, 9)}`);
5312
- const toolsRef = useRef13({});
5313
- const componentRef = useRef13(null);
5314
- 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([]);
5315
5375
  const stableTools = useStableTools(options.tools);
5316
5376
  const toolsKey = useMemo6(() => {
5317
5377
  if (!options.tools) return "";
5318
5378
  return Object.keys(options.tools).sort().join(",");
5319
5379
  }, [options.tools]);
5320
5380
  const memoizedSuggestions = useMemo6(() => options.suggestions, [options.suggestions]);
5321
- useEffect11(() => {
5381
+ useEffect12(() => {
5322
5382
  if (componentRef.current) {
5323
5383
  componentRef.current.setAttribute("data-useai-context", "true");
5324
5384
  }
5325
5385
  }, []);
5326
- const waitForPromptChange = useCallback12(() => {
5386
+ const waitForPromptChange = useCallback13(() => {
5327
5387
  return new Promise((resolve) => {
5328
5388
  const timeoutMs = 100;
5329
5389
  const timeoutId = setTimeout(() => {
@@ -5340,14 +5400,14 @@ function useAI(options = {}) {
5340
5400
  promptChangeResolvers.current.push(resolveAndCleanup);
5341
5401
  });
5342
5402
  }, []);
5343
- useEffect11(() => {
5403
+ useEffect12(() => {
5344
5404
  if (!enabled || options.invisible) return;
5345
5405
  registerWaiter(hookId.current, waitForPromptChange);
5346
5406
  return () => {
5347
5407
  unregisterWaiter(hookId.current);
5348
5408
  };
5349
5409
  }, [enabled, options.invisible, registerWaiter, unregisterWaiter, waitForPromptChange]);
5350
- useEffect11(() => {
5410
+ useEffect12(() => {
5351
5411
  if (!enabled) return;
5352
5412
  updatePrompt(hookId.current, options.prompt, memoizedSuggestions);
5353
5413
  if (promptChangeResolvers.current.length > 0) {
@@ -5355,29 +5415,30 @@ function useAI(options = {}) {
5355
5415
  promptChangeResolvers.current = [];
5356
5416
  }
5357
5417
  }, [enabled, options.prompt, memoizedSuggestions, updatePrompt]);
5358
- const updatePromptRef = useRef13(updatePrompt);
5418
+ const updatePromptRef = useRef14(updatePrompt);
5359
5419
  updatePromptRef.current = updatePrompt;
5360
- useEffect11(() => {
5420
+ useEffect12(() => {
5361
5421
  const id = hookId.current;
5362
5422
  return () => {
5363
5423
  updatePromptRef.current(id, void 0, void 0);
5364
5424
  };
5365
5425
  }, []);
5366
- useEffect11(() => {
5426
+ useLayoutEffect(() => {
5367
5427
  if (!enabled) return;
5368
5428
  if (stableTools) {
5369
5429
  const componentId = options.id || componentRef.current?.id;
5370
5430
  const toolsToRegister = componentId ? namespaceTools(stableTools, componentId) : stableTools;
5371
5431
  registerTools(hookId.current, toolsToRegister, { invisible: options.invisible });
5372
5432
  toolsRef.current = toolsToRegister;
5433
+ signalReady(hookId.current);
5373
5434
  }
5374
5435
  return () => {
5375
5436
  if (stableTools) {
5376
5437
  unregisterTools(hookId.current);
5377
5438
  }
5378
5439
  };
5379
- }, [enabled, toolsKey, stableTools, options.id, options.invisible, registerTools, unregisterTools]);
5380
- useEffect11(() => {
5440
+ }, [enabled, toolsKey, stableTools, options.id, options.invisible, registerTools, unregisterTools, signalReady]);
5441
+ useEffect12(() => {
5381
5442
  if (!enabled || !client) return;
5382
5443
  const unsubscribe = client.onEvent(hookId.current, (event) => {
5383
5444
  handleAGUIEvent(event);
@@ -5386,9 +5447,9 @@ function useAI(options = {}) {
5386
5447
  unsubscribe();
5387
5448
  };
5388
5449
  }, [enabled, client]);
5389
- const handleAGUIEvent = useCallback12(async (event) => {
5450
+ const handleAGUIEvent = useCallback13(async (event) => {
5390
5451
  switch (event.type) {
5391
- case EventType.TEXT_MESSAGE_END: {
5452
+ case EventType2.TEXT_MESSAGE_END: {
5392
5453
  const content = client?.currentMessageContent;
5393
5454
  if (content) {
5394
5455
  setResponse(content);
@@ -5396,7 +5457,7 @@ function useAI(options = {}) {
5396
5457
  }
5397
5458
  break;
5398
5459
  }
5399
- case EventType.RUN_ERROR: {
5460
+ case EventType2.RUN_ERROR: {
5400
5461
  const errorEvent = event;
5401
5462
  const error2 = new Error(errorEvent.message);
5402
5463
  setError(error2);
@@ -5406,7 +5467,7 @@ function useAI(options = {}) {
5406
5467
  }
5407
5468
  }
5408
5469
  }, [client, options.onError]);
5409
- const generate = useCallback12(async (prompt) => {
5470
+ const generate = useCallback13(async (prompt) => {
5410
5471
  if (!enabled) {
5411
5472
  const error2 = new Error("AI features are disabled");
5412
5473
  setError(error2);
@@ -5442,7 +5503,7 @@ function useAI(options = {}) {
5442
5503
  }
5443
5504
 
5444
5505
  // src/useAIWorkflow.ts
5445
- 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";
5446
5507
  import { EventType as EventType3 } from "@meetsmore-oss/use-ai-core";
5447
5508
  import { v4 as uuidv43 } from "uuid";
5448
5509
  function useAIWorkflow(runner, workflowId) {
@@ -5450,9 +5511,9 @@ function useAIWorkflow(runner, workflowId) {
5450
5511
  const [status, setStatus] = useState15("idle");
5451
5512
  const [text, setText] = useState15(null);
5452
5513
  const [error, setError] = useState15(null);
5453
- const currentWorkflowRef = useRef14(null);
5454
- const eventListenerIdRef = useRef14(`useAIWorkflow-${Math.random().toString(36).substr(2, 9)}`);
5455
- 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) => {
5456
5517
  const currentWorkflow = currentWorkflowRef.current;
5457
5518
  if (!currentWorkflow) return;
5458
5519
  if (event.type === EventType3.RUN_STARTED) {
@@ -5537,14 +5598,14 @@ function useAIWorkflow(runner, workflowId) {
5537
5598
  }
5538
5599
  }
5539
5600
  }, [client]);
5540
- useEffect12(() => {
5601
+ useEffect13(() => {
5541
5602
  if (!client) return;
5542
5603
  const unsubscribe = client.onEvent(eventListenerIdRef.current, handleWorkflowEvent);
5543
5604
  return () => {
5544
5605
  unsubscribe();
5545
5606
  };
5546
5607
  }, [client, handleWorkflowEvent]);
5547
- const trigger = useCallback13(async (options) => {
5608
+ const trigger = useCallback14(async (options) => {
5548
5609
  if (!client?.isConnected()) {
5549
5610
  const err = new Error("Not connected to server");
5550
5611
  setError(err);
@@ -5635,12 +5696,14 @@ export {
5635
5696
  useDropdownState,
5636
5697
  useFeedback,
5637
5698
  useFileUpload,
5699
+ useMessageQueue,
5638
5700
  usePromptState,
5701
+ useServerEvents,
5639
5702
  useSlashCommands,
5640
5703
  useStableTools,
5641
5704
  useStrings,
5642
5705
  useTheme,
5643
- useToolRegistry,
5706
+ useToolSystem,
5644
5707
  validateCommandName,
5645
5708
  z2 as z
5646
5709
  };