@meetsmore-oss/use-ai-client 1.7.0 → 1.9.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/bundled.js +18421 -18312
- package/dist/bundled.js.map +1 -1
- package/dist/index.d.ts +259 -145
- package/dist/index.js +533 -436
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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
|
|
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
|
|
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";
|
|
@@ -1641,7 +1638,8 @@ function ToolApprovalDialog({
|
|
|
1641
1638
|
const [showDetails, setShowDetails] = useState4(false);
|
|
1642
1639
|
const displayName = annotations?.title || toolCallName;
|
|
1643
1640
|
const isBatch = toolCount > 1;
|
|
1644
|
-
const
|
|
1641
|
+
const runtimeMessage = pendingTools.find((t) => t.message)?.message;
|
|
1642
|
+
const message = runtimeMessage ? runtimeMessage : isBatch ? strings.toolApproval.batchMessage?.replace("{count}", String(toolCount)) ?? `${toolCount} actions are waiting for your approval` : strings.toolApproval.message.replace("{toolName}", displayName);
|
|
1645
1643
|
const getToolDisplayName = (tool) => tool.annotations?.title || tool.toolCallName;
|
|
1646
1644
|
return /* @__PURE__ */ jsxs7(
|
|
1647
1645
|
"div",
|
|
@@ -3051,7 +3049,7 @@ function UseAIChat({ floating = false }) {
|
|
|
3051
3049
|
|
|
3052
3050
|
// src/client.ts
|
|
3053
3051
|
import { io } from "socket.io-client";
|
|
3054
|
-
import { EventType
|
|
3052
|
+
import { EventType } from "@meetsmore-oss/use-ai-core";
|
|
3055
3053
|
import { v4 as uuidv42 } from "uuid";
|
|
3056
3054
|
var UseAIClient = class {
|
|
3057
3055
|
/**
|
|
@@ -3146,7 +3144,7 @@ var UseAIClient = class {
|
|
|
3146
3144
|
});
|
|
3147
3145
|
}
|
|
3148
3146
|
handleEvent(event) {
|
|
3149
|
-
if (event.type ===
|
|
3147
|
+
if (event.type === EventType.RUN_STARTED) {
|
|
3150
3148
|
this._currentAssistantMessage = {
|
|
3151
3149
|
id: uuidv42(),
|
|
3152
3150
|
role: "assistant",
|
|
@@ -3154,31 +3152,31 @@ var UseAIClient = class {
|
|
|
3154
3152
|
};
|
|
3155
3153
|
this._currentAssistantToolCalls = [];
|
|
3156
3154
|
}
|
|
3157
|
-
if (event.type ===
|
|
3155
|
+
if (event.type === EventType.TEXT_MESSAGE_START) {
|
|
3158
3156
|
const e = event;
|
|
3159
3157
|
this._currentMessageId = e.messageId;
|
|
3160
3158
|
this._currentMessageContent = "";
|
|
3161
|
-
} else if (event.type ===
|
|
3159
|
+
} else if (event.type === EventType.TEXT_MESSAGE_CONTENT) {
|
|
3162
3160
|
const e = event;
|
|
3163
3161
|
this._currentMessageContent += e.delta;
|
|
3164
|
-
} else if (event.type ===
|
|
3162
|
+
} else if (event.type === EventType.TEXT_MESSAGE_END) {
|
|
3165
3163
|
if (this._currentAssistantMessage) {
|
|
3166
3164
|
this._currentAssistantMessage.content = this._currentMessageContent;
|
|
3167
3165
|
}
|
|
3168
3166
|
this._currentMessageId = null;
|
|
3169
|
-
} else if (event.type ===
|
|
3167
|
+
} else if (event.type === EventType.TOOL_CALL_START) {
|
|
3170
3168
|
const e = event;
|
|
3171
3169
|
this.currentToolCalls.set(e.toolCallId, {
|
|
3172
3170
|
name: e.toolCallName,
|
|
3173
3171
|
args: ""
|
|
3174
3172
|
});
|
|
3175
|
-
} else if (event.type ===
|
|
3173
|
+
} else if (event.type === EventType.TOOL_CALL_ARGS) {
|
|
3176
3174
|
const e = event;
|
|
3177
3175
|
const toolCall = this.currentToolCalls.get(e.toolCallId);
|
|
3178
3176
|
if (toolCall) {
|
|
3179
3177
|
toolCall.args += e.delta;
|
|
3180
3178
|
}
|
|
3181
|
-
} else if (event.type ===
|
|
3179
|
+
} else if (event.type === EventType.TOOL_CALL_END) {
|
|
3182
3180
|
const e = event;
|
|
3183
3181
|
const toolCall = this.currentToolCalls.get(e.toolCallId);
|
|
3184
3182
|
if (toolCall) {
|
|
@@ -3191,7 +3189,7 @@ var UseAIClient = class {
|
|
|
3191
3189
|
}
|
|
3192
3190
|
});
|
|
3193
3191
|
}
|
|
3194
|
-
} else if (event.type ===
|
|
3192
|
+
} else if (event.type === EventType.RUN_FINISHED) {
|
|
3195
3193
|
if (this._currentAssistantMessage) {
|
|
3196
3194
|
const assistantMessage = {
|
|
3197
3195
|
id: this._currentAssistantMessage.id,
|
|
@@ -3308,7 +3306,13 @@ var UseAIClient = class {
|
|
|
3308
3306
|
messageId: uuidv42(),
|
|
3309
3307
|
toolCallId,
|
|
3310
3308
|
content: JSON.stringify(result),
|
|
3311
|
-
role: "tool"
|
|
3309
|
+
role: "tool",
|
|
3310
|
+
// use-ai extension: include current tools and state for mid-run updates
|
|
3311
|
+
// (e.g., when navigation causes new components to mount)
|
|
3312
|
+
forwardedProps: {
|
|
3313
|
+
tools: this._tools,
|
|
3314
|
+
state: this._state
|
|
3315
|
+
}
|
|
3312
3316
|
}
|
|
3313
3317
|
};
|
|
3314
3318
|
const toolResultMsg = {
|
|
@@ -3371,7 +3375,7 @@ var UseAIClient = class {
|
|
|
3371
3375
|
*/
|
|
3372
3376
|
onTextMessage(handler) {
|
|
3373
3377
|
return this.onEvent("text-message-handler", (event) => {
|
|
3374
|
-
if (event.type ===
|
|
3378
|
+
if (event.type === EventType.TEXT_MESSAGE_END && this._currentMessageContent) {
|
|
3375
3379
|
handler(this._currentMessageContent);
|
|
3376
3380
|
}
|
|
3377
3381
|
});
|
|
@@ -3385,7 +3389,7 @@ var UseAIClient = class {
|
|
|
3385
3389
|
*/
|
|
3386
3390
|
onToolCall(handler) {
|
|
3387
3391
|
return this.onEvent("tool-call-handler", (event) => {
|
|
3388
|
-
if (event.type ===
|
|
3392
|
+
if (event.type === EventType.TOOL_CALL_END) {
|
|
3389
3393
|
const e = event;
|
|
3390
3394
|
const toolCall = this.currentToolCalls.get(e.toolCallId);
|
|
3391
3395
|
if (toolCall) {
|
|
@@ -3602,7 +3606,8 @@ function defineTool(description, schemaOrFn, fnOrOptions, options) {
|
|
|
3602
3606
|
let actualFn;
|
|
3603
3607
|
let actualOptions;
|
|
3604
3608
|
if (isNoParamFunction) {
|
|
3605
|
-
|
|
3609
|
+
const noParamFn = schemaOrFn;
|
|
3610
|
+
actualFn = (_input, ctx) => noParamFn(ctx);
|
|
3606
3611
|
actualOptions = fnOrOptions || {};
|
|
3607
3612
|
} else {
|
|
3608
3613
|
actualFn = fnOrOptions;
|
|
@@ -3636,21 +3641,21 @@ function defineTool(description, schemaOrFn, fnOrOptions, options) {
|
|
|
3636
3641
|
}
|
|
3637
3642
|
return toolDef;
|
|
3638
3643
|
},
|
|
3639
|
-
async _execute(input) {
|
|
3644
|
+
async _execute(input, ctx) {
|
|
3640
3645
|
const validated = this._zodSchema.parse(input);
|
|
3641
|
-
return await actualFn(validated);
|
|
3646
|
+
return await actualFn(validated, ctx);
|
|
3642
3647
|
}
|
|
3643
3648
|
};
|
|
3644
3649
|
}
|
|
3645
3650
|
function convertToolsToDefinitions(tools) {
|
|
3646
3651
|
return Object.entries(tools).map(([name, tool]) => tool._toToolDefinition(name));
|
|
3647
3652
|
}
|
|
3648
|
-
async function executeDefinedTool(tools, toolName, input) {
|
|
3653
|
+
async function executeDefinedTool(tools, toolName, input, ctx) {
|
|
3649
3654
|
const tool = tools[toolName];
|
|
3650
3655
|
if (!tool) {
|
|
3651
3656
|
throw new Error(`Tool "${toolName}" not found`);
|
|
3652
3657
|
}
|
|
3653
|
-
return await tool._execute(input);
|
|
3658
|
+
return await tool._execute(input, ctx);
|
|
3654
3659
|
}
|
|
3655
3660
|
|
|
3656
3661
|
// src/providers/chatRepository/LocalStorageChatRepository.ts
|
|
@@ -3850,11 +3855,7 @@ function useChatManagement({
|
|
|
3850
3855
|
clientRef,
|
|
3851
3856
|
messages,
|
|
3852
3857
|
setMessages,
|
|
3853
|
-
|
|
3854
|
-
setOpen,
|
|
3855
|
-
connected,
|
|
3856
|
-
loading,
|
|
3857
|
-
hasPendingApproval
|
|
3858
|
+
connected
|
|
3858
3859
|
}) {
|
|
3859
3860
|
const [currentChatId, setCurrentChatId] = useState6(null);
|
|
3860
3861
|
const [pendingChatId, setPendingChatId] = useState6(null);
|
|
@@ -3885,7 +3886,7 @@ function useChatManagement({
|
|
|
3885
3886
|
const reloadMessages = useCallback4(async (chatId) => {
|
|
3886
3887
|
const loadedMessages = await loadChatMessages(chatId);
|
|
3887
3888
|
setMessages(loadedMessages);
|
|
3888
|
-
}, [loadChatMessages]);
|
|
3889
|
+
}, [loadChatMessages, setMessages]);
|
|
3889
3890
|
const createNewChat = useCallback4(async (options) => {
|
|
3890
3891
|
console.log("[ChatManagement] createNewChat called - currentChatId:", currentChatId, "pendingChatId:", pendingChatId, "messages.length:", messages.length);
|
|
3891
3892
|
if (pendingChatId && messages.length === 0) {
|
|
@@ -3906,7 +3907,7 @@ function useChatManagement({
|
|
|
3906
3907
|
}
|
|
3907
3908
|
console.log("[ChatManagement] Created pending chat:", chatId, "(will activate on first message)");
|
|
3908
3909
|
return chatId;
|
|
3909
|
-
}, [currentChatId, pendingChatId, messages, repository, clientRef]);
|
|
3910
|
+
}, [currentChatId, pendingChatId, messages, repository, clientRef, setMessages]);
|
|
3910
3911
|
const loadChat = useCallback4(async (chatId) => {
|
|
3911
3912
|
setPendingChatId(chatId);
|
|
3912
3913
|
await reloadMessages(chatId);
|
|
@@ -3927,7 +3928,7 @@ function useChatManagement({
|
|
|
3927
3928
|
setMessages([]);
|
|
3928
3929
|
}
|
|
3929
3930
|
console.log("[ChatManagement] Deleted chat:", chatId);
|
|
3930
|
-
}, [currentChatId, pendingChatId, repository]);
|
|
3931
|
+
}, [currentChatId, pendingChatId, repository, setMessages]);
|
|
3931
3932
|
const listChats = useCallback4(async () => {
|
|
3932
3933
|
return await repository.listChats();
|
|
3933
3934
|
}, [repository]);
|
|
@@ -3941,7 +3942,7 @@ function useChatManagement({
|
|
|
3941
3942
|
console.log("[ChatManagement] Cleared current chat:", currentChatId);
|
|
3942
3943
|
}
|
|
3943
3944
|
}
|
|
3944
|
-
}, [currentChatId, repository]);
|
|
3945
|
+
}, [currentChatId, repository, setMessages]);
|
|
3945
3946
|
const getCurrentChat = useCallback4(async () => {
|
|
3946
3947
|
const chatId = pendingChatId || currentChatId;
|
|
3947
3948
|
if (!chatId) return null;
|
|
@@ -4068,76 +4069,8 @@ function useChatManagement({
|
|
|
4068
4069
|
}
|
|
4069
4070
|
})();
|
|
4070
4071
|
}
|
|
4071
|
-
}, [currentChatId, pendingChatId, createNewChat, repository, loadChatMessages, clientRef]);
|
|
4072
|
+
}, [currentChatId, pendingChatId, createNewChat, repository, loadChatMessages, clientRef, setMessages]);
|
|
4072
4073
|
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
4074
|
return {
|
|
4142
4075
|
currentChatId,
|
|
4143
4076
|
pendingChatId,
|
|
@@ -4151,7 +4084,6 @@ function useChatManagement({
|
|
|
4151
4084
|
saveUserMessage,
|
|
4152
4085
|
saveAIResponse,
|
|
4153
4086
|
reloadMessages,
|
|
4154
|
-
sendMessage,
|
|
4155
4087
|
getCurrentChat,
|
|
4156
4088
|
updateMetadata,
|
|
4157
4089
|
currentChatIdSnapshot,
|
|
@@ -4399,16 +4331,26 @@ function useCommandManagement({
|
|
|
4399
4331
|
};
|
|
4400
4332
|
}
|
|
4401
4333
|
|
|
4402
|
-
// src/hooks/
|
|
4334
|
+
// src/hooks/useToolSystem.ts
|
|
4403
4335
|
import { useState as useState9, useCallback as useCallback7, useRef as useRef7, useMemo as useMemo4 } from "react";
|
|
4404
|
-
function
|
|
4336
|
+
function useToolSystem({
|
|
4337
|
+
clientRef,
|
|
4338
|
+
buildState
|
|
4339
|
+
}) {
|
|
4405
4340
|
const toolRegistryRef = useRef7(/* @__PURE__ */ new Map());
|
|
4406
4341
|
const [toolRegistryVersion, setToolRegistryVersion] = useState9(0);
|
|
4407
4342
|
const toolOwnershipRef = useRef7(/* @__PURE__ */ new Map());
|
|
4408
4343
|
const invisibleRef = useRef7(/* @__PURE__ */ new Set());
|
|
4344
|
+
const readyStateRef = useRef7(/* @__PURE__ */ new Map());
|
|
4345
|
+
const readyListenersRef = useRef7(/* @__PURE__ */ new Set());
|
|
4346
|
+
const waitersRef = useRef7(/* @__PURE__ */ new Map());
|
|
4347
|
+
const [pendingApprovals, setPendingApprovals] = useState9([]);
|
|
4348
|
+
const pendingApprovalToolCallsRef = useRef7(/* @__PURE__ */ new Map());
|
|
4349
|
+
const runtimeApprovalResolversRef = useRef7(/* @__PURE__ */ new Map());
|
|
4409
4350
|
const registerTools = useCallback7((id, tools, options) => {
|
|
4410
4351
|
const existingTools = toolRegistryRef.current.get(id);
|
|
4411
4352
|
toolRegistryRef.current.set(id, tools);
|
|
4353
|
+
readyStateRef.current.set(id, false);
|
|
4412
4354
|
if (existingTools) {
|
|
4413
4355
|
const existingKeys = Object.keys(existingTools).sort().join(",");
|
|
4414
4356
|
const newKeys = Object.keys(tools).sort().join(",");
|
|
@@ -4427,6 +4369,13 @@ function useToolRegistry() {
|
|
|
4427
4369
|
invisibleRef.current.delete(id);
|
|
4428
4370
|
}
|
|
4429
4371
|
}, []);
|
|
4372
|
+
const signalReady = useCallback7((id) => {
|
|
4373
|
+
if (!toolRegistryRef.current.has(id)) {
|
|
4374
|
+
return;
|
|
4375
|
+
}
|
|
4376
|
+
readyStateRef.current.set(id, true);
|
|
4377
|
+
readyListenersRef.current.forEach((listener) => listener());
|
|
4378
|
+
}, []);
|
|
4430
4379
|
const unregisterTools = useCallback7((id) => {
|
|
4431
4380
|
const tools = toolRegistryRef.current.get(id);
|
|
4432
4381
|
if (tools) {
|
|
@@ -4435,8 +4384,10 @@ function useToolRegistry() {
|
|
|
4435
4384
|
});
|
|
4436
4385
|
}
|
|
4437
4386
|
toolRegistryRef.current.delete(id);
|
|
4387
|
+
readyStateRef.current.delete(id);
|
|
4438
4388
|
setToolRegistryVersion((v) => v + 1);
|
|
4439
4389
|
invisibleRef.current.delete(id);
|
|
4390
|
+
readyListenersRef.current.forEach((listener) => listener());
|
|
4440
4391
|
}, []);
|
|
4441
4392
|
const isInvisible = useCallback7((id) => {
|
|
4442
4393
|
return invisibleRef.current.has(id);
|
|
@@ -4451,14 +4402,187 @@ function useToolRegistry() {
|
|
|
4451
4402
|
const hasTools = toolRegistryRef.current.size > 0;
|
|
4452
4403
|
const aggregatedToolsRef = useRef7(aggregatedTools);
|
|
4453
4404
|
aggregatedToolsRef.current = aggregatedTools;
|
|
4405
|
+
const waitForToolsToStabilize = useCallback7(async () => {
|
|
4406
|
+
const maxWaitMs = 500;
|
|
4407
|
+
const checkAllReady = () => {
|
|
4408
|
+
if (readyStateRef.current.size === 0) return true;
|
|
4409
|
+
for (const ready of readyStateRef.current.values()) {
|
|
4410
|
+
if (!ready) return false;
|
|
4411
|
+
}
|
|
4412
|
+
return true;
|
|
4413
|
+
};
|
|
4414
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
4415
|
+
if (checkAllReady()) {
|
|
4416
|
+
return;
|
|
4417
|
+
}
|
|
4418
|
+
return new Promise((resolve) => {
|
|
4419
|
+
let safetyTimeout = null;
|
|
4420
|
+
let resolved = false;
|
|
4421
|
+
const cleanup = () => {
|
|
4422
|
+
if (resolved) return;
|
|
4423
|
+
resolved = true;
|
|
4424
|
+
if (safetyTimeout) clearTimeout(safetyTimeout);
|
|
4425
|
+
readyListenersRef.current.delete(onReadyChange);
|
|
4426
|
+
};
|
|
4427
|
+
const onReadyChange = () => {
|
|
4428
|
+
if (resolved) return;
|
|
4429
|
+
if (checkAllReady()) {
|
|
4430
|
+
cleanup();
|
|
4431
|
+
resolve();
|
|
4432
|
+
}
|
|
4433
|
+
};
|
|
4434
|
+
safetyTimeout = setTimeout(() => {
|
|
4435
|
+
cleanup();
|
|
4436
|
+
resolve();
|
|
4437
|
+
}, maxWaitMs);
|
|
4438
|
+
readyListenersRef.current.add(onReadyChange);
|
|
4439
|
+
onReadyChange();
|
|
4440
|
+
});
|
|
4441
|
+
}, []);
|
|
4442
|
+
const registerWaiter = useCallback7((id, waiter) => {
|
|
4443
|
+
waitersRef.current.set(id, waiter);
|
|
4444
|
+
}, []);
|
|
4445
|
+
const unregisterWaiter = useCallback7((id) => {
|
|
4446
|
+
waitersRef.current.delete(id);
|
|
4447
|
+
}, []);
|
|
4448
|
+
const getWaiter = useCallback7((id) => {
|
|
4449
|
+
return waitersRef.current.get(id);
|
|
4450
|
+
}, []);
|
|
4451
|
+
const handleApprovalRequest = useCallback7((event) => {
|
|
4452
|
+
console.log("[useToolSystem] Tool approval requested:", event.toolCallName, event.toolCallId);
|
|
4453
|
+
setPendingApprovals((prev) => [
|
|
4454
|
+
...prev,
|
|
4455
|
+
{
|
|
4456
|
+
toolCallId: event.toolCallId,
|
|
4457
|
+
toolCallName: event.toolCallName,
|
|
4458
|
+
toolCallArgs: event.toolCallArgs,
|
|
4459
|
+
annotations: event.annotations,
|
|
4460
|
+
message: event.message,
|
|
4461
|
+
metadata: event.metadata
|
|
4462
|
+
}
|
|
4463
|
+
]);
|
|
4464
|
+
}, []);
|
|
4465
|
+
const executeToolCall = useCallback7(async (toolCallId, name, input) => {
|
|
4466
|
+
const client = clientRef.current;
|
|
4467
|
+
if (!client) {
|
|
4468
|
+
console.error("[useToolSystem] No client available for tool execution");
|
|
4469
|
+
return;
|
|
4470
|
+
}
|
|
4471
|
+
try {
|
|
4472
|
+
const ownerId = toolOwnershipRef.current.get(name);
|
|
4473
|
+
console.log(`[useToolSystem] Tool "${name}" owned by component:`, ownerId);
|
|
4474
|
+
const ctx = {
|
|
4475
|
+
requestApproval: ({ message, metadata }) => {
|
|
4476
|
+
return new Promise((resolve) => {
|
|
4477
|
+
const approvalId = `${toolCallId}-runtime-${Date.now()}`;
|
|
4478
|
+
runtimeApprovalResolversRef.current.set(approvalId, resolve);
|
|
4479
|
+
setPendingApprovals((prev) => [
|
|
4480
|
+
...prev,
|
|
4481
|
+
{
|
|
4482
|
+
toolCallId: approvalId,
|
|
4483
|
+
toolCallName: name,
|
|
4484
|
+
toolCallArgs: input || {},
|
|
4485
|
+
message,
|
|
4486
|
+
metadata
|
|
4487
|
+
}
|
|
4488
|
+
]);
|
|
4489
|
+
});
|
|
4490
|
+
}
|
|
4491
|
+
};
|
|
4492
|
+
console.log("[useToolSystem] Executing tool...");
|
|
4493
|
+
const result = await executeDefinedTool(aggregatedToolsRef.current, name, input, ctx);
|
|
4494
|
+
const isErrorResult = result && typeof result === "object" && ("error" in result || result.success === false);
|
|
4495
|
+
const ownerIsInvisible = ownerId ? isInvisible(ownerId) : false;
|
|
4496
|
+
if (ownerId && !isErrorResult && !ownerIsInvisible) {
|
|
4497
|
+
const waiter = getWaiter(ownerId);
|
|
4498
|
+
if (waiter) {
|
|
4499
|
+
console.log(`[useToolSystem] Waiting for prompt change from ${ownerId}...`);
|
|
4500
|
+
await waiter();
|
|
4501
|
+
console.log("[useToolSystem] Prompt change wait complete");
|
|
4502
|
+
}
|
|
4503
|
+
} else if (isErrorResult) {
|
|
4504
|
+
console.log("[useToolSystem] Tool returned error, skipping prompt wait");
|
|
4505
|
+
} else if (ownerIsInvisible) {
|
|
4506
|
+
console.log("[useToolSystem] Component is invisible, skipping prompt wait");
|
|
4507
|
+
}
|
|
4508
|
+
console.log("[useToolSystem] Waiting for tools to stabilize...");
|
|
4509
|
+
await waitForToolsToStabilize();
|
|
4510
|
+
console.log("[useToolSystem] Tools stabilized");
|
|
4511
|
+
const updatedState = buildState();
|
|
4512
|
+
console.log(`[useToolSystem] Updated state (aggregated from all hooks)`);
|
|
4513
|
+
client.sendToolResponse(toolCallId, result, updatedState);
|
|
4514
|
+
} catch (err) {
|
|
4515
|
+
console.error("Tool execution error:", err);
|
|
4516
|
+
client.sendToolResponse(toolCallId, {
|
|
4517
|
+
error: err instanceof Error ? err.message : "Unknown error"
|
|
4518
|
+
});
|
|
4519
|
+
}
|
|
4520
|
+
}, [clientRef, isInvisible, getWaiter, waitForToolsToStabilize, buildState]);
|
|
4521
|
+
const storePendingToolCall = useCallback7((toolCallId, name, input, toolCallData) => {
|
|
4522
|
+
console.log(`[useToolSystem] Storing pending tool call "${name}" for approval`);
|
|
4523
|
+
pendingApprovalToolCallsRef.current.set(toolCallId, { name, input, toolCallData });
|
|
4524
|
+
}, []);
|
|
4525
|
+
const executePendingToolAfterApproval = useCallback7(async (toolCallId) => {
|
|
4526
|
+
const pendingTool = pendingApprovalToolCallsRef.current.get(toolCallId);
|
|
4527
|
+
if (!pendingTool) {
|
|
4528
|
+
console.warn(`[useToolSystem] No pending tool found for ${toolCallId}`);
|
|
4529
|
+
return;
|
|
4530
|
+
}
|
|
4531
|
+
pendingApprovalToolCallsRef.current.delete(toolCallId);
|
|
4532
|
+
await executeToolCall(toolCallId, pendingTool.name, pendingTool.input);
|
|
4533
|
+
}, [executeToolCall]);
|
|
4534
|
+
const approveAll = useCallback7(async () => {
|
|
4535
|
+
if (!clientRef.current) return;
|
|
4536
|
+
console.log("[useToolSystem] Approving all tool calls:", pendingApprovals.length);
|
|
4537
|
+
const pendingTools = [...pendingApprovals];
|
|
4538
|
+
setPendingApprovals([]);
|
|
4539
|
+
for (const pending of pendingTools) {
|
|
4540
|
+
const runtimeResolver = runtimeApprovalResolversRef.current.get(pending.toolCallId);
|
|
4541
|
+
if (runtimeResolver) {
|
|
4542
|
+
runtimeApprovalResolversRef.current.delete(pending.toolCallId);
|
|
4543
|
+
runtimeResolver({ approved: true });
|
|
4544
|
+
} else {
|
|
4545
|
+
clientRef.current.sendToolApprovalResponse(pending.toolCallId, true);
|
|
4546
|
+
await executePendingToolAfterApproval(pending.toolCallId);
|
|
4547
|
+
}
|
|
4548
|
+
}
|
|
4549
|
+
}, [clientRef, pendingApprovals, executePendingToolAfterApproval]);
|
|
4550
|
+
const rejectAll = useCallback7((reason) => {
|
|
4551
|
+
if (!clientRef.current) return;
|
|
4552
|
+
console.log("[useToolSystem] Rejecting all tool calls:", pendingApprovals.length, reason);
|
|
4553
|
+
const pendingTools = [...pendingApprovals];
|
|
4554
|
+
setPendingApprovals([]);
|
|
4555
|
+
for (const pending of pendingTools) {
|
|
4556
|
+
const runtimeResolver = runtimeApprovalResolversRef.current.get(pending.toolCallId);
|
|
4557
|
+
if (runtimeResolver) {
|
|
4558
|
+
runtimeApprovalResolversRef.current.delete(pending.toolCallId);
|
|
4559
|
+
runtimeResolver({ approved: false, reason });
|
|
4560
|
+
} else {
|
|
4561
|
+
clientRef.current.sendToolApprovalResponse(pending.toolCallId, false, reason);
|
|
4562
|
+
pendingApprovalToolCallsRef.current.delete(pending.toolCallId);
|
|
4563
|
+
}
|
|
4564
|
+
}
|
|
4565
|
+
}, [clientRef, pendingApprovals]);
|
|
4454
4566
|
return {
|
|
4567
|
+
// Registry
|
|
4455
4568
|
registerTools,
|
|
4456
4569
|
unregisterTools,
|
|
4457
4570
|
isInvisible,
|
|
4458
4571
|
aggregatedTools,
|
|
4459
4572
|
hasTools,
|
|
4460
4573
|
aggregatedToolsRef,
|
|
4461
|
-
|
|
4574
|
+
signalReady,
|
|
4575
|
+
toolRegistryVersion,
|
|
4576
|
+
// Waiters
|
|
4577
|
+
registerWaiter,
|
|
4578
|
+
unregisterWaiter,
|
|
4579
|
+
// Execution
|
|
4580
|
+
pendingApprovals,
|
|
4581
|
+
handleApprovalRequest,
|
|
4582
|
+
executeToolCall,
|
|
4583
|
+
storePendingToolCall,
|
|
4584
|
+
approveAll,
|
|
4585
|
+
rejectAll
|
|
4462
4586
|
};
|
|
4463
4587
|
}
|
|
4464
4588
|
|
|
@@ -4471,7 +4595,6 @@ function usePromptState({
|
|
|
4471
4595
|
}) {
|
|
4472
4596
|
const promptsRef = useRef8(/* @__PURE__ */ new Map());
|
|
4473
4597
|
const suggestionsRef = useRef8(/* @__PURE__ */ new Map());
|
|
4474
|
-
const waitersRef = useRef8(/* @__PURE__ */ new Map());
|
|
4475
4598
|
const [suggestionsVersion, setSuggestionsVersion] = useState10(0);
|
|
4476
4599
|
const buildStateFromPrompts = useCallback8(() => {
|
|
4477
4600
|
const promptParts = [];
|
|
@@ -4508,15 +4631,6 @@ function usePromptState({
|
|
|
4508
4631
|
clientRef.current.updateState(buildStateFromPrompts());
|
|
4509
4632
|
}
|
|
4510
4633
|
}, [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
4634
|
const aggregatedSuggestions = useMemo5(() => {
|
|
4521
4635
|
const allSuggestions = [];
|
|
4522
4636
|
suggestionsRef.current.forEach((suggestions) => {
|
|
@@ -4526,11 +4640,8 @@ function usePromptState({
|
|
|
4526
4640
|
}, [suggestionsVersion]);
|
|
4527
4641
|
return {
|
|
4528
4642
|
updatePrompt,
|
|
4529
|
-
registerWaiter,
|
|
4530
|
-
unregisterWaiter,
|
|
4531
|
-
getWaiter,
|
|
4532
4643
|
aggregatedSuggestions,
|
|
4533
|
-
|
|
4644
|
+
buildStateFromPrompts
|
|
4534
4645
|
};
|
|
4535
4646
|
}
|
|
4536
4647
|
|
|
@@ -4596,116 +4707,190 @@ function useFeedback({
|
|
|
4596
4707
|
};
|
|
4597
4708
|
}
|
|
4598
4709
|
|
|
4599
|
-
// src/hooks/
|
|
4710
|
+
// src/hooks/useServerEvents.ts
|
|
4600
4711
|
import { useState as useState12, useCallback as useCallback10, useRef as useRef10 } from "react";
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4712
|
+
|
|
4713
|
+
// src/types.ts
|
|
4714
|
+
import { EventType as EventType2, ErrorCode, TOOL_APPROVAL_REQUEST } from "@meetsmore-oss/use-ai-core";
|
|
4715
|
+
|
|
4716
|
+
// src/hooks/useServerEvents.ts
|
|
4717
|
+
function useServerEvents({
|
|
4718
|
+
toolSystem,
|
|
4719
|
+
saveAIResponse,
|
|
4720
|
+
strings
|
|
4608
4721
|
}) {
|
|
4609
|
-
const [
|
|
4610
|
-
const
|
|
4611
|
-
const
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4722
|
+
const [loading, setLoading] = useState12(false);
|
|
4723
|
+
const [streamingText, setStreamingText] = useState12("");
|
|
4724
|
+
const streamingChatIdRef = useRef10(null);
|
|
4725
|
+
const [executingToolRaw, setExecutingTool] = useState12(null);
|
|
4726
|
+
const executingToolFallbackRef = useRef10(null);
|
|
4727
|
+
const clearStreamingText = useCallback10(() => {
|
|
4728
|
+
setStreamingText("");
|
|
4729
|
+
}, []);
|
|
4730
|
+
const toolSystemRef = useRef10(toolSystem);
|
|
4731
|
+
toolSystemRef.current = toolSystem;
|
|
4732
|
+
const saveAIResponseRef = useRef10(saveAIResponse);
|
|
4733
|
+
saveAIResponseRef.current = saveAIResponse;
|
|
4734
|
+
const stringsRef = useRef10(strings);
|
|
4735
|
+
stringsRef.current = strings;
|
|
4736
|
+
const handleServerEvent = useCallback10(async (client, event) => {
|
|
4737
|
+
const ts = toolSystemRef.current;
|
|
4738
|
+
const strs = stringsRef.current;
|
|
4739
|
+
if (event.type === EventType2.TOOL_CALL_START) {
|
|
4740
|
+
const e = event;
|
|
4741
|
+
const tool = ts.aggregatedToolsRef.current[e.toolCallName];
|
|
4742
|
+
const title = e.annotations?.title ?? tool?._options?.annotations?.title ?? null;
|
|
4743
|
+
if (!title) {
|
|
4744
|
+
const fallbacks = strs.toolExecution.fallbackMessages;
|
|
4745
|
+
executingToolFallbackRef.current = fallbacks[Math.floor(Math.random() * fallbacks.length)];
|
|
4620
4746
|
}
|
|
4621
|
-
|
|
4747
|
+
setExecutingTool({ toolCallId: e.toolCallId, title });
|
|
4748
|
+
} else if (event.type === EventType2.TOOL_CALL_END) {
|
|
4749
|
+
const toolCallEnd = event;
|
|
4750
|
+
const toolCallId = toolCallEnd.toolCallId;
|
|
4751
|
+
setExecutingTool((prev) => prev?.toolCallId === toolCallId ? null : prev);
|
|
4752
|
+
const toolCallData = client.currentToolCalls.get(toolCallId);
|
|
4753
|
+
if (!toolCallData) {
|
|
4754
|
+
console.error(`[ServerEvents] Tool call ${toolCallId} not found`);
|
|
4755
|
+
return;
|
|
4756
|
+
}
|
|
4757
|
+
const name = toolCallData.name;
|
|
4758
|
+
const input = JSON.parse(toolCallData.args);
|
|
4759
|
+
const tool = ts.aggregatedToolsRef.current[name];
|
|
4760
|
+
if (!tool) {
|
|
4761
|
+
console.log(`[ServerEvents] Tool "${name}" not found in useAI tools, skipping (likely a workflow tool)`);
|
|
4762
|
+
return;
|
|
4763
|
+
}
|
|
4764
|
+
if (tool._options?.annotations?.destructiveHint === true) {
|
|
4765
|
+
console.log(`[ServerEvents] Tool "${name}" requires approval, deferring execution`);
|
|
4766
|
+
ts.storePendingToolCall(toolCallId, name, input, toolCallData);
|
|
4767
|
+
return;
|
|
4768
|
+
}
|
|
4769
|
+
await ts.executeToolCall(toolCallId, name, input);
|
|
4770
|
+
} else if (event.type === TOOL_APPROVAL_REQUEST) {
|
|
4771
|
+
const e = event;
|
|
4772
|
+
ts.handleApprovalRequest(e);
|
|
4773
|
+
} else if (event.type === EventType2.TEXT_MESSAGE_CONTENT) {
|
|
4774
|
+
const contentEvent = event;
|
|
4775
|
+
setStreamingText((prev) => prev + contentEvent.delta);
|
|
4776
|
+
} else if (event.type === EventType2.TEXT_MESSAGE_END) {
|
|
4777
|
+
setStreamingText("");
|
|
4778
|
+
streamingChatIdRef.current = null;
|
|
4779
|
+
} else if (event.type === EventType2.RUN_FINISHED) {
|
|
4780
|
+
const content = client.currentMessageContent;
|
|
4781
|
+
if (content) {
|
|
4782
|
+
const finishedEvent = event;
|
|
4783
|
+
const traceId = finishedEvent.runId;
|
|
4784
|
+
saveAIResponseRef.current(content, void 0, traceId);
|
|
4785
|
+
}
|
|
4786
|
+
setLoading(false);
|
|
4787
|
+
} else if (event.type === EventType2.RUN_ERROR) {
|
|
4788
|
+
const errorEvent = event;
|
|
4789
|
+
const errorCode = errorEvent.message;
|
|
4790
|
+
console.error("[ServerEvents] Run error:", errorCode);
|
|
4791
|
+
const userMessage = strs.errors[errorCode] || strs.errors[ErrorCode.UNKNOWN_ERROR];
|
|
4792
|
+
saveAIResponseRef.current(userMessage, "error");
|
|
4793
|
+
setStreamingText("");
|
|
4794
|
+
streamingChatIdRef.current = null;
|
|
4795
|
+
setLoading(false);
|
|
4796
|
+
}
|
|
4622
4797
|
}, []);
|
|
4623
|
-
const
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4798
|
+
const executingTool = executingToolRaw ? {
|
|
4799
|
+
displayText: executingToolRaw.title ?? executingToolFallbackRef.current ?? strings.toolExecution.fallbackMessages[0]
|
|
4800
|
+
} : null;
|
|
4801
|
+
return {
|
|
4802
|
+
loading,
|
|
4803
|
+
setLoading,
|
|
4804
|
+
streamingText,
|
|
4805
|
+
clearStreamingText,
|
|
4806
|
+
executingTool,
|
|
4807
|
+
streamingChatIdRef,
|
|
4808
|
+
handleServerEvent
|
|
4809
|
+
};
|
|
4810
|
+
}
|
|
4811
|
+
|
|
4812
|
+
// src/hooks/useMessageQueue.ts
|
|
4813
|
+
import { useCallback as useCallback11, useRef as useRef11, useEffect as useEffect10 } from "react";
|
|
4814
|
+
function useMessageQueue({
|
|
4815
|
+
sendFn,
|
|
4816
|
+
createNewChat,
|
|
4817
|
+
setOpen,
|
|
4818
|
+
connected,
|
|
4819
|
+
loading,
|
|
4820
|
+
hasPendingApproval
|
|
4821
|
+
}) {
|
|
4822
|
+
const pendingMessagesRef = useRef11([]);
|
|
4823
|
+
const isProcessingQueueRef = useRef11(false);
|
|
4824
|
+
const sendFnRef = useRef11(sendFn);
|
|
4825
|
+
sendFnRef.current = sendFn;
|
|
4826
|
+
const createNewChatRef = useRef11(createNewChat);
|
|
4827
|
+
createNewChatRef.current = createNewChat;
|
|
4828
|
+
const setOpenRef = useRef11(setOpen);
|
|
4829
|
+
setOpenRef.current = setOpen;
|
|
4830
|
+
const loadingRef = useRef11(loading);
|
|
4831
|
+
useEffect10(() => {
|
|
4832
|
+
loadingRef.current = loading;
|
|
4833
|
+
}, [loading]);
|
|
4834
|
+
const hasPendingApprovalRef = useRef11(hasPendingApproval);
|
|
4835
|
+
useEffect10(() => {
|
|
4836
|
+
hasPendingApprovalRef.current = hasPendingApproval;
|
|
4837
|
+
}, [hasPendingApproval]);
|
|
4838
|
+
const processMessageQueue = useCallback11(async () => {
|
|
4839
|
+
if (isProcessingQueueRef.current || pendingMessagesRef.current.length === 0) {
|
|
4627
4840
|
return;
|
|
4628
4841
|
}
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
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");
|
|
4842
|
+
isProcessingQueueRef.current = true;
|
|
4843
|
+
while (pendingMessagesRef.current.length > 0) {
|
|
4844
|
+
const { message, options } = pendingMessagesRef.current.shift();
|
|
4845
|
+
const { newChat = false, attachments = [], openChat = true, metadata, forwardedProps } = options ?? {};
|
|
4846
|
+
if (newChat) {
|
|
4847
|
+
await createNewChatRef.current({ metadata });
|
|
4647
4848
|
}
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4849
|
+
const fileAttachments = await Promise.all(
|
|
4850
|
+
attachments.map(async (file) => {
|
|
4851
|
+
let preview;
|
|
4852
|
+
if (file.type.startsWith("image/")) {
|
|
4853
|
+
preview = await new Promise((resolve) => {
|
|
4854
|
+
const reader = new FileReader();
|
|
4855
|
+
reader.onload = () => resolve(typeof reader.result === "string" ? reader.result : void 0);
|
|
4856
|
+
reader.onerror = () => resolve(void 0);
|
|
4857
|
+
reader.readAsDataURL(file);
|
|
4858
|
+
});
|
|
4859
|
+
}
|
|
4860
|
+
return {
|
|
4861
|
+
id: crypto.randomUUID(),
|
|
4862
|
+
file,
|
|
4863
|
+
preview
|
|
4864
|
+
};
|
|
4865
|
+
})
|
|
4866
|
+
);
|
|
4867
|
+
await sendFnRef.current(message, fileAttachments.length > 0 ? fileAttachments : void 0, forwardedProps);
|
|
4868
|
+
if (openChat && setOpenRef.current) {
|
|
4869
|
+
setOpenRef.current(true);
|
|
4655
4870
|
}
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4871
|
+
await new Promise((resolve) => {
|
|
4872
|
+
const checkReady = () => {
|
|
4873
|
+
setTimeout(() => {
|
|
4874
|
+
if (!loadingRef.current && !hasPendingApprovalRef.current) {
|
|
4875
|
+
resolve();
|
|
4876
|
+
} else {
|
|
4877
|
+
checkReady();
|
|
4878
|
+
}
|
|
4879
|
+
}, 100);
|
|
4880
|
+
};
|
|
4881
|
+
checkReady();
|
|
4661
4882
|
});
|
|
4662
4883
|
}
|
|
4663
|
-
|
|
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 });
|
|
4884
|
+
isProcessingQueueRef.current = false;
|
|
4667
4885
|
}, []);
|
|
4668
|
-
const
|
|
4669
|
-
|
|
4670
|
-
|
|
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);
|
|
4886
|
+
const sendMessage = useCallback11(async (message, options) => {
|
|
4887
|
+
if (!connected) {
|
|
4888
|
+
throw new Error("Not connected to UseAI server");
|
|
4699
4889
|
}
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
executeToolCall,
|
|
4705
|
-
storePendingToolCall,
|
|
4706
|
-
approveAll,
|
|
4707
|
-
rejectAll
|
|
4708
|
-
};
|
|
4890
|
+
pendingMessagesRef.current.push({ message, options });
|
|
4891
|
+
await processMessageQueue();
|
|
4892
|
+
}, [connected, processMessageQueue]);
|
|
4893
|
+
return { sendMessage };
|
|
4709
4894
|
}
|
|
4710
4895
|
|
|
4711
4896
|
// src/providers/useAIProvider.tsx
|
|
@@ -4720,16 +4905,18 @@ var noOpContextValue = {
|
|
|
4720
4905
|
register: () => {
|
|
4721
4906
|
},
|
|
4722
4907
|
unregister: () => {
|
|
4723
|
-
}
|
|
4724
|
-
|
|
4725
|
-
prompts: {
|
|
4726
|
-
update: () => {
|
|
4908
|
+
},
|
|
4909
|
+
signalReady: () => {
|
|
4727
4910
|
},
|
|
4728
4911
|
registerWaiter: () => {
|
|
4729
4912
|
},
|
|
4730
4913
|
unregisterWaiter: () => {
|
|
4731
4914
|
}
|
|
4732
4915
|
},
|
|
4916
|
+
prompts: {
|
|
4917
|
+
update: () => {
|
|
4918
|
+
}
|
|
4919
|
+
},
|
|
4733
4920
|
chat: {
|
|
4734
4921
|
currentId: null,
|
|
4735
4922
|
create: async () => "",
|
|
@@ -4791,87 +4978,41 @@ function UseAIProvider({
|
|
|
4791
4978
|
const strings = { ...defaultStrings, ...customStrings };
|
|
4792
4979
|
const [connected, setConnected] = useState13(false);
|
|
4793
4980
|
const [isChatOpen, setIsChatOpen] = useState13(false);
|
|
4794
|
-
const [loading, setLoading] = useState13(false);
|
|
4795
4981
|
const [messages, setMessages] = useState13([]);
|
|
4796
4982
|
const [fileProcessingState, setFileProcessingState] = useState13(null);
|
|
4797
|
-
const handleSetChatOpen =
|
|
4983
|
+
const handleSetChatOpen = useCallback12((open) => {
|
|
4798
4984
|
setIsChatOpen(open);
|
|
4799
4985
|
onOpenChange?.(open);
|
|
4800
4986
|
}, [onOpenChange]);
|
|
4801
|
-
const
|
|
4802
|
-
const
|
|
4803
|
-
const [executingTool, setExecutingTool] = useState13(null);
|
|
4804
|
-
const executingToolFallbackRef = useRef11(null);
|
|
4805
|
-
const clientRef = useRef11(null);
|
|
4806
|
-
const repositoryRef = useRef11(
|
|
4987
|
+
const clientRef = useRef12(null);
|
|
4988
|
+
const repositoryRef = useRef12(
|
|
4807
4989
|
chatRepository || new LocalStorageChatRepository()
|
|
4808
4990
|
);
|
|
4809
|
-
const
|
|
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({
|
|
4991
|
+
const promptState = usePromptState({
|
|
4827
4992
|
systemPrompt,
|
|
4828
4993
|
clientRef,
|
|
4829
4994
|
connected
|
|
4830
4995
|
});
|
|
4831
|
-
const
|
|
4832
|
-
if (handleSendMessageRef.current) {
|
|
4833
|
-
await handleSendMessageRef.current(message, attachments, forwardedProps);
|
|
4834
|
-
}
|
|
4835
|
-
}, []);
|
|
4836
|
-
const toolExecution = useToolExecution({
|
|
4996
|
+
const toolSystem = useToolSystem({
|
|
4837
4997
|
clientRef,
|
|
4838
|
-
|
|
4839
|
-
toolOwnershipRef,
|
|
4840
|
-
promptsRef,
|
|
4841
|
-
isInvisible,
|
|
4842
|
-
getWaiter
|
|
4998
|
+
buildState: promptState.buildStateFromPrompts
|
|
4843
4999
|
});
|
|
4844
5000
|
const chatManagement = useChatManagement({
|
|
4845
5001
|
repository: repositoryRef.current,
|
|
4846
5002
|
clientRef,
|
|
4847
5003
|
messages,
|
|
4848
5004
|
setMessages,
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
5005
|
+
connected
|
|
5006
|
+
});
|
|
5007
|
+
const serverEvents = useServerEvents({
|
|
5008
|
+
toolSystem,
|
|
5009
|
+
saveAIResponse: chatManagement.saveAIResponse,
|
|
5010
|
+
strings
|
|
4854
5011
|
});
|
|
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
5012
|
const feedback = useFeedback({
|
|
4872
5013
|
clientRef,
|
|
4873
5014
|
repository: repositoryRef.current,
|
|
4874
|
-
getDisplayedChatId: () => displayedChatId,
|
|
5015
|
+
getDisplayedChatId: () => chatManagement.displayedChatId,
|
|
4875
5016
|
setMessages
|
|
4876
5017
|
});
|
|
4877
5018
|
const {
|
|
@@ -4887,7 +5028,9 @@ function UseAIProvider({
|
|
|
4887
5028
|
renameCommand,
|
|
4888
5029
|
deleteCommand
|
|
4889
5030
|
} = useCommandManagement({ repository: commandRepository });
|
|
4890
|
-
|
|
5031
|
+
const handleServerEventRef = useRef12(serverEvents.handleServerEvent);
|
|
5032
|
+
handleServerEventRef.current = serverEvents.handleServerEvent;
|
|
5033
|
+
useEffect11(() => {
|
|
4891
5034
|
console.log("[UseAIProvider] Initializing client with serverUrl:", serverUrl);
|
|
4892
5035
|
const client = new UseAIClient(serverUrl);
|
|
4893
5036
|
const unsubscribeConnection = client.onConnectionStateChange((isConnected) => {
|
|
@@ -4897,64 +5040,7 @@ function UseAIProvider({
|
|
|
4897
5040
|
console.log("[UseAIProvider] Connecting...");
|
|
4898
5041
|
client.connect();
|
|
4899
5042
|
const unsubscribe = client.onEvent("globalChat", async (event) => {
|
|
4900
|
-
|
|
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
|
-
}
|
|
5043
|
+
await handleServerEventRef.current(client, event);
|
|
4958
5044
|
});
|
|
4959
5045
|
clientRef.current = client;
|
|
4960
5046
|
return () => {
|
|
@@ -4963,11 +5049,11 @@ function UseAIProvider({
|
|
|
4963
5049
|
client.disconnect();
|
|
4964
5050
|
};
|
|
4965
5051
|
}, [serverUrl]);
|
|
4966
|
-
const lastRegisteredToolsRef =
|
|
4967
|
-
|
|
5052
|
+
const lastRegisteredToolsRef = useRef12("");
|
|
5053
|
+
useEffect11(() => {
|
|
4968
5054
|
const client = clientRef.current;
|
|
4969
|
-
if (!client || !client.isConnected() || !hasTools) return;
|
|
4970
|
-
const toolKeys = Object.keys(aggregatedTools).sort().join(",");
|
|
5055
|
+
if (!client || !client.isConnected() || !toolSystem.hasTools) return;
|
|
5056
|
+
const toolKeys = Object.keys(toolSystem.aggregatedTools).sort().join(",");
|
|
4971
5057
|
if (toolKeys === lastRegisteredToolsRef.current) {
|
|
4972
5058
|
console.log("[Provider] Skipping re-registration, tools unchanged");
|
|
4973
5059
|
return;
|
|
@@ -4975,19 +5061,19 @@ function UseAIProvider({
|
|
|
4975
5061
|
lastRegisteredToolsRef.current = toolKeys;
|
|
4976
5062
|
console.log("[Provider] Registering tools:", toolKeys);
|
|
4977
5063
|
try {
|
|
4978
|
-
const toolDefinitions = convertToolsToDefinitions(aggregatedTools);
|
|
5064
|
+
const toolDefinitions = convertToolsToDefinitions(toolSystem.aggregatedTools);
|
|
4979
5065
|
console.log(`[Provider] Registering ${toolDefinitions.length} tools`);
|
|
4980
5066
|
client.registerTools(toolDefinitions);
|
|
4981
5067
|
} catch (err) {
|
|
4982
5068
|
console.error("Failed to register tools:", err);
|
|
4983
5069
|
}
|
|
4984
|
-
}, [hasTools, aggregatedTools, connected]);
|
|
4985
|
-
const handleSendMessage =
|
|
5070
|
+
}, [toolSystem.hasTools, toolSystem.aggregatedTools, connected]);
|
|
5071
|
+
const handleSendMessage = useCallback12(async (message, attachments, messageForwardedProps) => {
|
|
4986
5072
|
if (!clientRef.current) return;
|
|
4987
|
-
|
|
4988
|
-
const activatedChatId = activatePendingChat();
|
|
4989
|
-
const activeChatId = activatedChatId || currentChatId;
|
|
4990
|
-
streamingChatIdRef.current = activeChatId;
|
|
5073
|
+
serverEvents.clearStreamingText();
|
|
5074
|
+
const activatedChatId = chatManagement.activatePendingChat();
|
|
5075
|
+
const activeChatId = activatedChatId || chatManagement.currentChatId;
|
|
5076
|
+
serverEvents.streamingChatIdRef.current = activeChatId;
|
|
4991
5077
|
let persistedContent = message;
|
|
4992
5078
|
let multimodalContent;
|
|
4993
5079
|
if (attachments && attachments.length > 0) {
|
|
@@ -5007,12 +5093,12 @@ function UseAIProvider({
|
|
|
5007
5093
|
}
|
|
5008
5094
|
persistedContent = persistedParts;
|
|
5009
5095
|
if (activeChatId) {
|
|
5010
|
-
await saveUserMessage(activeChatId, persistedContent);
|
|
5096
|
+
await chatManagement.saveUserMessage(activeChatId, persistedContent);
|
|
5011
5097
|
}
|
|
5012
|
-
setLoading(true);
|
|
5098
|
+
serverEvents.setLoading(true);
|
|
5013
5099
|
try {
|
|
5014
5100
|
const fileContent = await processAttachments(attachments, {
|
|
5015
|
-
getCurrentChat,
|
|
5101
|
+
getCurrentChat: chatManagement.getCurrentChat,
|
|
5016
5102
|
backend: fileUploadConfig?.backend,
|
|
5017
5103
|
transformers: fileUploadConfig?.transformers,
|
|
5018
5104
|
onFileProgress: (_fileId, state) => {
|
|
@@ -5025,16 +5111,16 @@ function UseAIProvider({
|
|
|
5025
5111
|
}
|
|
5026
5112
|
multimodalContent.push(...fileContent);
|
|
5027
5113
|
} catch (error) {
|
|
5028
|
-
setLoading(false);
|
|
5114
|
+
serverEvents.setLoading(false);
|
|
5029
5115
|
throw error;
|
|
5030
5116
|
} finally {
|
|
5031
5117
|
setFileProcessingState(null);
|
|
5032
5118
|
}
|
|
5033
5119
|
} else {
|
|
5034
5120
|
if (activeChatId) {
|
|
5035
|
-
await saveUserMessage(activeChatId, persistedContent);
|
|
5121
|
+
await chatManagement.saveUserMessage(activeChatId, persistedContent);
|
|
5036
5122
|
}
|
|
5037
|
-
setLoading(true);
|
|
5123
|
+
serverEvents.setLoading(true);
|
|
5038
5124
|
}
|
|
5039
5125
|
const providerResult = forwardedPropsProvider ? forwardedPropsProvider() : {};
|
|
5040
5126
|
const providerProps = providerResult instanceof Promise ? await providerResult : providerResult;
|
|
@@ -5047,31 +5133,39 @@ function UseAIProvider({
|
|
|
5047
5133
|
multimodalContent,
|
|
5048
5134
|
Object.keys(mergedForwardedProps).length > 0 ? mergedForwardedProps : void 0
|
|
5049
5135
|
);
|
|
5050
|
-
}, [
|
|
5051
|
-
|
|
5136
|
+
}, [chatManagement, serverEvents, fileUploadConfig, forwardedPropsProvider]);
|
|
5137
|
+
const messageQueue = useMessageQueue({
|
|
5138
|
+
sendFn: handleSendMessage,
|
|
5139
|
+
createNewChat: chatManagement.createNewChat,
|
|
5140
|
+
setOpen: handleSetChatOpen,
|
|
5141
|
+
connected,
|
|
5142
|
+
loading: serverEvents.loading,
|
|
5143
|
+
hasPendingApproval: toolSystem.pendingApprovals.length > 0
|
|
5144
|
+
});
|
|
5052
5145
|
const value = {
|
|
5053
5146
|
serverUrl,
|
|
5054
5147
|
connected,
|
|
5055
5148
|
client: clientRef.current,
|
|
5056
5149
|
tools: {
|
|
5057
|
-
register: registerTools,
|
|
5058
|
-
unregister: unregisterTools
|
|
5150
|
+
register: toolSystem.registerTools,
|
|
5151
|
+
unregister: toolSystem.unregisterTools,
|
|
5152
|
+
signalReady: toolSystem.signalReady,
|
|
5153
|
+
registerWaiter: toolSystem.registerWaiter,
|
|
5154
|
+
unregisterWaiter: toolSystem.unregisterWaiter
|
|
5059
5155
|
},
|
|
5060
5156
|
prompts: {
|
|
5061
|
-
update: updatePrompt
|
|
5062
|
-
registerWaiter,
|
|
5063
|
-
unregisterWaiter
|
|
5157
|
+
update: promptState.updatePrompt
|
|
5064
5158
|
},
|
|
5065
5159
|
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
|
|
5160
|
+
currentId: chatManagement.currentChatId,
|
|
5161
|
+
create: chatManagement.createNewChat,
|
|
5162
|
+
load: chatManagement.loadChat,
|
|
5163
|
+
delete: chatManagement.deleteChat,
|
|
5164
|
+
list: chatManagement.listChats,
|
|
5165
|
+
clear: chatManagement.clearCurrentChat,
|
|
5166
|
+
sendMessage: messageQueue.sendMessage,
|
|
5167
|
+
get: chatManagement.getCurrentChat,
|
|
5168
|
+
updateMetadata: chatManagement.updateMetadata
|
|
5075
5169
|
},
|
|
5076
5170
|
agents: {
|
|
5077
5171
|
available: availableAgents,
|
|
@@ -5087,26 +5181,23 @@ function UseAIProvider({
|
|
|
5087
5181
|
delete: deleteCommand
|
|
5088
5182
|
}
|
|
5089
5183
|
};
|
|
5090
|
-
const effectiveStreamingText = streamingChatIdRef.current === displayedChatId ? streamingText : "";
|
|
5091
|
-
const executingToolDisplay = executingTool ? {
|
|
5092
|
-
displayText: executingTool.title ?? executingToolFallbackRef.current ?? strings.toolExecution.fallbackMessages[0]
|
|
5093
|
-
} : null;
|
|
5184
|
+
const effectiveStreamingText = serverEvents.streamingChatIdRef.current === chatManagement.displayedChatId ? serverEvents.streamingText : "";
|
|
5094
5185
|
const chatUIContextValue = {
|
|
5095
5186
|
connected,
|
|
5096
|
-
loading,
|
|
5187
|
+
loading: serverEvents.loading,
|
|
5097
5188
|
sendMessage: handleSendMessage,
|
|
5098
5189
|
messages,
|
|
5099
5190
|
streamingText: effectiveStreamingText,
|
|
5100
|
-
suggestions: aggregatedSuggestions,
|
|
5191
|
+
suggestions: promptState.aggregatedSuggestions,
|
|
5101
5192
|
fileUploadConfig,
|
|
5102
5193
|
fileProcessing: fileProcessingState,
|
|
5103
5194
|
history: {
|
|
5104
|
-
currentId: displayedChatId,
|
|
5105
|
-
create: createNewChat,
|
|
5106
|
-
load: loadChat,
|
|
5107
|
-
delete: deleteChat,
|
|
5108
|
-
list: listChats,
|
|
5109
|
-
get: getCurrentChat
|
|
5195
|
+
currentId: chatManagement.displayedChatId,
|
|
5196
|
+
create: chatManagement.createNewChat,
|
|
5197
|
+
load: chatManagement.loadChat,
|
|
5198
|
+
delete: chatManagement.deleteChat,
|
|
5199
|
+
list: chatManagement.listChats,
|
|
5200
|
+
get: chatManagement.getCurrentChat
|
|
5110
5201
|
},
|
|
5111
5202
|
agents: {
|
|
5112
5203
|
available: availableAgents,
|
|
@@ -5125,11 +5216,11 @@ function UseAIProvider({
|
|
|
5125
5216
|
setOpen: handleSetChatOpen
|
|
5126
5217
|
},
|
|
5127
5218
|
tools: {
|
|
5128
|
-
executing:
|
|
5219
|
+
executing: serverEvents.executingTool,
|
|
5129
5220
|
pending: {
|
|
5130
|
-
tools:
|
|
5131
|
-
approveAll:
|
|
5132
|
-
rejectAll:
|
|
5221
|
+
tools: toolSystem.pendingApprovals,
|
|
5222
|
+
approveAll: toolSystem.approveAll,
|
|
5223
|
+
rejectAll: toolSystem.rejectAll
|
|
5133
5224
|
}
|
|
5134
5225
|
},
|
|
5135
5226
|
feedback: {
|
|
@@ -5143,15 +5234,15 @@ function UseAIProvider({
|
|
|
5143
5234
|
const chatPanelProps = {
|
|
5144
5235
|
onSendMessage: handleSendMessage,
|
|
5145
5236
|
messages,
|
|
5146
|
-
loading,
|
|
5237
|
+
loading: serverEvents.loading,
|
|
5147
5238
|
connected,
|
|
5148
5239
|
streamingText: effectiveStreamingText,
|
|
5149
|
-
currentChatId: displayedChatId,
|
|
5150
|
-
onNewChat: createNewChat,
|
|
5151
|
-
onLoadChat: loadChat,
|
|
5152
|
-
onDeleteChat: deleteChat,
|
|
5153
|
-
onListChats: listChats,
|
|
5154
|
-
suggestions: aggregatedSuggestions,
|
|
5240
|
+
currentChatId: chatManagement.displayedChatId,
|
|
5241
|
+
onNewChat: chatManagement.createNewChat,
|
|
5242
|
+
onLoadChat: chatManagement.loadChat,
|
|
5243
|
+
onDeleteChat: chatManagement.deleteChat,
|
|
5244
|
+
onListChats: chatManagement.listChats,
|
|
5245
|
+
suggestions: promptState.aggregatedSuggestions,
|
|
5155
5246
|
availableAgents,
|
|
5156
5247
|
defaultAgent,
|
|
5157
5248
|
selectedAgent,
|
|
@@ -5162,12 +5253,12 @@ function UseAIProvider({
|
|
|
5162
5253
|
onSaveCommand: saveCommand,
|
|
5163
5254
|
onRenameCommand: renameCommand,
|
|
5164
5255
|
onDeleteCommand: deleteCommand,
|
|
5165
|
-
executingTool:
|
|
5256
|
+
executingTool: serverEvents.executingTool,
|
|
5166
5257
|
feedbackEnabled: feedback.enabled,
|
|
5167
5258
|
onFeedback: feedback.submitFeedback,
|
|
5168
|
-
pendingApprovals:
|
|
5169
|
-
onApproveToolCall:
|
|
5170
|
-
onRejectToolCall:
|
|
5259
|
+
pendingApprovals: toolSystem.pendingApprovals,
|
|
5260
|
+
onApproveToolCall: toolSystem.pendingApprovals.length > 0 ? toolSystem.approveAll : void 0,
|
|
5261
|
+
onRejectToolCall: toolSystem.pendingApprovals.length > 0 ? toolSystem.rejectAll : void 0
|
|
5171
5262
|
};
|
|
5172
5263
|
const renderDefaultChat = () => {
|
|
5173
5264
|
if (isUIDisabled) return null;
|
|
@@ -5188,9 +5279,9 @@ function UseAIProvider({
|
|
|
5188
5279
|
onClose: () => handleSetChatOpen(false),
|
|
5189
5280
|
onSendMessage: handleSendMessage,
|
|
5190
5281
|
messages,
|
|
5191
|
-
loading,
|
|
5282
|
+
loading: serverEvents.loading,
|
|
5192
5283
|
connected,
|
|
5193
|
-
suggestions: aggregatedSuggestions,
|
|
5284
|
+
suggestions: promptState.aggregatedSuggestions,
|
|
5194
5285
|
availableAgents,
|
|
5195
5286
|
defaultAgent,
|
|
5196
5287
|
selectedAgent,
|
|
@@ -5231,11 +5322,11 @@ function useAIContext() {
|
|
|
5231
5322
|
}
|
|
5232
5323
|
|
|
5233
5324
|
// src/hooks/useStableTools.ts
|
|
5234
|
-
import { useRef as
|
|
5325
|
+
import { useRef as useRef13 } from "react";
|
|
5235
5326
|
function useStableTools(tools) {
|
|
5236
|
-
const latestToolsRef =
|
|
5237
|
-
const stableToolsRef =
|
|
5238
|
-
const prevToolNamesRef =
|
|
5327
|
+
const latestToolsRef = useRef13({});
|
|
5328
|
+
const stableToolsRef = useRef13({});
|
|
5329
|
+
const prevToolNamesRef = useRef13("");
|
|
5239
5330
|
if (!tools) {
|
|
5240
5331
|
latestToolsRef.current = {};
|
|
5241
5332
|
return void 0;
|
|
@@ -5266,19 +5357,19 @@ function useStableTools(tools) {
|
|
|
5266
5357
|
return stableToolsRef.current;
|
|
5267
5358
|
}
|
|
5268
5359
|
function createStableToolWrapper(name, tool, latestToolsRef) {
|
|
5269
|
-
const stableHandler = (input) => {
|
|
5360
|
+
const stableHandler = (input, ctx) => {
|
|
5270
5361
|
const currentTool = latestToolsRef.current[name];
|
|
5271
5362
|
if (!currentTool) {
|
|
5272
5363
|
throw new Error(`Tool "${name}" no longer exists`);
|
|
5273
5364
|
}
|
|
5274
|
-
return currentTool.fn(input);
|
|
5365
|
+
return currentTool.fn(input, ctx);
|
|
5275
5366
|
};
|
|
5276
|
-
const stableExecute = async (input) => {
|
|
5367
|
+
const stableExecute = async (input, ctx) => {
|
|
5277
5368
|
const currentTool = latestToolsRef.current[name];
|
|
5278
5369
|
if (!currentTool) {
|
|
5279
5370
|
throw new Error(`Tool "${name}" no longer exists`);
|
|
5280
5371
|
}
|
|
5281
|
-
return await currentTool._execute(input);
|
|
5372
|
+
return await currentTool._execute(input, ctx);
|
|
5282
5373
|
};
|
|
5283
5374
|
return {
|
|
5284
5375
|
description: tool.description,
|
|
@@ -5303,27 +5394,27 @@ function namespaceTools(tools, namespace) {
|
|
|
5303
5394
|
function useAI(options = {}) {
|
|
5304
5395
|
const { enabled = true } = options;
|
|
5305
5396
|
const { connected, tools, client, prompts } = useAIContext();
|
|
5306
|
-
const { register: registerTools, unregister: unregisterTools } = tools;
|
|
5307
|
-
const { update: updatePrompt
|
|
5397
|
+
const { register: registerTools, unregister: unregisterTools, signalReady, registerWaiter, unregisterWaiter } = tools;
|
|
5398
|
+
const { update: updatePrompt } = prompts;
|
|
5308
5399
|
const [response, setResponse] = useState14(null);
|
|
5309
5400
|
const [loading, setLoading] = useState14(false);
|
|
5310
5401
|
const [error, setError] = useState14(null);
|
|
5311
|
-
const hookId =
|
|
5312
|
-
const toolsRef =
|
|
5313
|
-
const componentRef =
|
|
5314
|
-
const promptChangeResolvers =
|
|
5402
|
+
const hookId = useRef14(`useAI-${Math.random().toString(36).substr(2, 9)}`);
|
|
5403
|
+
const toolsRef = useRef14({});
|
|
5404
|
+
const componentRef = useRef14(null);
|
|
5405
|
+
const promptChangeResolvers = useRef14([]);
|
|
5315
5406
|
const stableTools = useStableTools(options.tools);
|
|
5316
5407
|
const toolsKey = useMemo6(() => {
|
|
5317
5408
|
if (!options.tools) return "";
|
|
5318
5409
|
return Object.keys(options.tools).sort().join(",");
|
|
5319
5410
|
}, [options.tools]);
|
|
5320
5411
|
const memoizedSuggestions = useMemo6(() => options.suggestions, [options.suggestions]);
|
|
5321
|
-
|
|
5412
|
+
useEffect12(() => {
|
|
5322
5413
|
if (componentRef.current) {
|
|
5323
5414
|
componentRef.current.setAttribute("data-useai-context", "true");
|
|
5324
5415
|
}
|
|
5325
5416
|
}, []);
|
|
5326
|
-
const waitForPromptChange =
|
|
5417
|
+
const waitForPromptChange = useCallback13(() => {
|
|
5327
5418
|
return new Promise((resolve) => {
|
|
5328
5419
|
const timeoutMs = 100;
|
|
5329
5420
|
const timeoutId = setTimeout(() => {
|
|
@@ -5340,14 +5431,14 @@ function useAI(options = {}) {
|
|
|
5340
5431
|
promptChangeResolvers.current.push(resolveAndCleanup);
|
|
5341
5432
|
});
|
|
5342
5433
|
}, []);
|
|
5343
|
-
|
|
5434
|
+
useEffect12(() => {
|
|
5344
5435
|
if (!enabled || options.invisible) return;
|
|
5345
5436
|
registerWaiter(hookId.current, waitForPromptChange);
|
|
5346
5437
|
return () => {
|
|
5347
5438
|
unregisterWaiter(hookId.current);
|
|
5348
5439
|
};
|
|
5349
5440
|
}, [enabled, options.invisible, registerWaiter, unregisterWaiter, waitForPromptChange]);
|
|
5350
|
-
|
|
5441
|
+
useEffect12(() => {
|
|
5351
5442
|
if (!enabled) return;
|
|
5352
5443
|
updatePrompt(hookId.current, options.prompt, memoizedSuggestions);
|
|
5353
5444
|
if (promptChangeResolvers.current.length > 0) {
|
|
@@ -5355,29 +5446,30 @@ function useAI(options = {}) {
|
|
|
5355
5446
|
promptChangeResolvers.current = [];
|
|
5356
5447
|
}
|
|
5357
5448
|
}, [enabled, options.prompt, memoizedSuggestions, updatePrompt]);
|
|
5358
|
-
const updatePromptRef =
|
|
5449
|
+
const updatePromptRef = useRef14(updatePrompt);
|
|
5359
5450
|
updatePromptRef.current = updatePrompt;
|
|
5360
|
-
|
|
5451
|
+
useEffect12(() => {
|
|
5361
5452
|
const id = hookId.current;
|
|
5362
5453
|
return () => {
|
|
5363
5454
|
updatePromptRef.current(id, void 0, void 0);
|
|
5364
5455
|
};
|
|
5365
5456
|
}, []);
|
|
5366
|
-
|
|
5457
|
+
useLayoutEffect(() => {
|
|
5367
5458
|
if (!enabled) return;
|
|
5368
5459
|
if (stableTools) {
|
|
5369
5460
|
const componentId = options.id || componentRef.current?.id;
|
|
5370
5461
|
const toolsToRegister = componentId ? namespaceTools(stableTools, componentId) : stableTools;
|
|
5371
5462
|
registerTools(hookId.current, toolsToRegister, { invisible: options.invisible });
|
|
5372
5463
|
toolsRef.current = toolsToRegister;
|
|
5464
|
+
signalReady(hookId.current);
|
|
5373
5465
|
}
|
|
5374
5466
|
return () => {
|
|
5375
5467
|
if (stableTools) {
|
|
5376
5468
|
unregisterTools(hookId.current);
|
|
5377
5469
|
}
|
|
5378
5470
|
};
|
|
5379
|
-
}, [enabled, toolsKey, stableTools, options.id, options.invisible, registerTools, unregisterTools]);
|
|
5380
|
-
|
|
5471
|
+
}, [enabled, toolsKey, stableTools, options.id, options.invisible, registerTools, unregisterTools, signalReady]);
|
|
5472
|
+
useEffect12(() => {
|
|
5381
5473
|
if (!enabled || !client) return;
|
|
5382
5474
|
const unsubscribe = client.onEvent(hookId.current, (event) => {
|
|
5383
5475
|
handleAGUIEvent(event);
|
|
@@ -5386,9 +5478,9 @@ function useAI(options = {}) {
|
|
|
5386
5478
|
unsubscribe();
|
|
5387
5479
|
};
|
|
5388
5480
|
}, [enabled, client]);
|
|
5389
|
-
const handleAGUIEvent =
|
|
5481
|
+
const handleAGUIEvent = useCallback13(async (event) => {
|
|
5390
5482
|
switch (event.type) {
|
|
5391
|
-
case
|
|
5483
|
+
case EventType2.TEXT_MESSAGE_END: {
|
|
5392
5484
|
const content = client?.currentMessageContent;
|
|
5393
5485
|
if (content) {
|
|
5394
5486
|
setResponse(content);
|
|
@@ -5396,7 +5488,7 @@ function useAI(options = {}) {
|
|
|
5396
5488
|
}
|
|
5397
5489
|
break;
|
|
5398
5490
|
}
|
|
5399
|
-
case
|
|
5491
|
+
case EventType2.RUN_ERROR: {
|
|
5400
5492
|
const errorEvent = event;
|
|
5401
5493
|
const error2 = new Error(errorEvent.message);
|
|
5402
5494
|
setError(error2);
|
|
@@ -5406,7 +5498,7 @@ function useAI(options = {}) {
|
|
|
5406
5498
|
}
|
|
5407
5499
|
}
|
|
5408
5500
|
}, [client, options.onError]);
|
|
5409
|
-
const generate =
|
|
5501
|
+
const generate = useCallback13(async (prompt) => {
|
|
5410
5502
|
if (!enabled) {
|
|
5411
5503
|
const error2 = new Error("AI features are disabled");
|
|
5412
5504
|
setError(error2);
|
|
@@ -5442,7 +5534,7 @@ function useAI(options = {}) {
|
|
|
5442
5534
|
}
|
|
5443
5535
|
|
|
5444
5536
|
// src/useAIWorkflow.ts
|
|
5445
|
-
import { useState as useState15, useCallback as
|
|
5537
|
+
import { useState as useState15, useCallback as useCallback14, useRef as useRef15, useEffect as useEffect13 } from "react";
|
|
5446
5538
|
import { EventType as EventType3 } from "@meetsmore-oss/use-ai-core";
|
|
5447
5539
|
import { v4 as uuidv43 } from "uuid";
|
|
5448
5540
|
function useAIWorkflow(runner, workflowId) {
|
|
@@ -5450,9 +5542,9 @@ function useAIWorkflow(runner, workflowId) {
|
|
|
5450
5542
|
const [status, setStatus] = useState15("idle");
|
|
5451
5543
|
const [text, setText] = useState15(null);
|
|
5452
5544
|
const [error, setError] = useState15(null);
|
|
5453
|
-
const currentWorkflowRef =
|
|
5454
|
-
const eventListenerIdRef =
|
|
5455
|
-
const handleWorkflowEvent =
|
|
5545
|
+
const currentWorkflowRef = useRef15(null);
|
|
5546
|
+
const eventListenerIdRef = useRef15(`useAIWorkflow-${Math.random().toString(36).substr(2, 9)}`);
|
|
5547
|
+
const handleWorkflowEvent = useCallback14(async (event) => {
|
|
5456
5548
|
const currentWorkflow = currentWorkflowRef.current;
|
|
5457
5549
|
if (!currentWorkflow) return;
|
|
5458
5550
|
if (event.type === EventType3.RUN_STARTED) {
|
|
@@ -5485,7 +5577,10 @@ function useAIWorkflow(runner, workflowId) {
|
|
|
5485
5577
|
console.log(`[useAIWorkflow] Executing tool: ${toolName}`, toolArgs);
|
|
5486
5578
|
console.log(`[useAIWorkflow] Available tools:`, Object.keys(currentWorkflow.tools));
|
|
5487
5579
|
try {
|
|
5488
|
-
const
|
|
5580
|
+
const noopCtx = {
|
|
5581
|
+
requestApproval: async () => ({ approved: true })
|
|
5582
|
+
};
|
|
5583
|
+
const result = await executeDefinedTool(currentWorkflow.tools, toolName, toolArgs, noopCtx);
|
|
5489
5584
|
currentWorkflow.toolCalls.push({
|
|
5490
5585
|
toolName,
|
|
5491
5586
|
args: toolArgs,
|
|
@@ -5537,14 +5632,14 @@ function useAIWorkflow(runner, workflowId) {
|
|
|
5537
5632
|
}
|
|
5538
5633
|
}
|
|
5539
5634
|
}, [client]);
|
|
5540
|
-
|
|
5635
|
+
useEffect13(() => {
|
|
5541
5636
|
if (!client) return;
|
|
5542
5637
|
const unsubscribe = client.onEvent(eventListenerIdRef.current, handleWorkflowEvent);
|
|
5543
5638
|
return () => {
|
|
5544
5639
|
unsubscribe();
|
|
5545
5640
|
};
|
|
5546
5641
|
}, [client, handleWorkflowEvent]);
|
|
5547
|
-
const trigger =
|
|
5642
|
+
const trigger = useCallback14(async (options) => {
|
|
5548
5643
|
if (!client?.isConnected()) {
|
|
5549
5644
|
const err = new Error("Not connected to server");
|
|
5550
5645
|
setError(err);
|
|
@@ -5635,12 +5730,14 @@ export {
|
|
|
5635
5730
|
useDropdownState,
|
|
5636
5731
|
useFeedback,
|
|
5637
5732
|
useFileUpload,
|
|
5733
|
+
useMessageQueue,
|
|
5638
5734
|
usePromptState,
|
|
5735
|
+
useServerEvents,
|
|
5639
5736
|
useSlashCommands,
|
|
5640
5737
|
useStableTools,
|
|
5641
5738
|
useStrings,
|
|
5642
5739
|
useTheme,
|
|
5643
|
-
|
|
5740
|
+
useToolSystem,
|
|
5644
5741
|
validateCommandName,
|
|
5645
5742
|
z2 as z
|
|
5646
5743
|
};
|