@meetsmore-oss/use-ai-client 1.8.0 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundled.js +4021 -1276
- package/dist/bundled.js.map +1 -1
- package/dist/index.d.ts +594 -544
- package/dist/index.js +239 -95
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/dist/chunk-STF3H6F5.js +0 -13
- package/dist/chunk-STF3H6F5.js.map +0 -1
- package/dist/chunk-UM4UCU4W.js +0 -46
- package/dist/chunk-UM4UCU4W.js.map +0 -1
- package/dist/types-GWPQMSYT.js +0 -9
- package/dist/types-GWPQMSYT.js.map +0 -1
- package/dist/types-RJZTRF3U.js +0 -9
- package/dist/types-RJZTRF3U.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
generateChatId,
|
|
3
|
-
generateMessageId
|
|
4
|
-
} from "./chunk-STF3H6F5.js";
|
|
5
|
-
|
|
6
1
|
// src/useAI.ts
|
|
7
2
|
import { useState as useState14, useEffect as useEffect12, useLayoutEffect, useRef as useRef14, useCallback as useCallback13, useMemo as useMemo6 } from "react";
|
|
8
3
|
|
|
@@ -273,13 +268,23 @@ function UseAIFloatingButton({
|
|
|
273
268
|
// src/components/UseAIChatPanel.tsx
|
|
274
269
|
import { useState as useState5, useRef as useRef4, useEffect as useEffect4 } from "react";
|
|
275
270
|
|
|
271
|
+
// src/utils/messageContent.ts
|
|
272
|
+
function getTextFromContent(content) {
|
|
273
|
+
if (typeof content === "string") {
|
|
274
|
+
return content;
|
|
275
|
+
}
|
|
276
|
+
return content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
277
|
+
}
|
|
278
|
+
|
|
276
279
|
// src/components/MarkdownContent.tsx
|
|
277
280
|
import ReactMarkdown from "react-markdown";
|
|
281
|
+
import remarkGfm from "remark-gfm";
|
|
278
282
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
279
283
|
function MarkdownContent({ content }) {
|
|
280
284
|
return /* @__PURE__ */ jsx2(
|
|
281
285
|
ReactMarkdown,
|
|
282
286
|
{
|
|
287
|
+
remarkPlugins: [remarkGfm],
|
|
283
288
|
components: {
|
|
284
289
|
// Override default element rendering for better chat styling
|
|
285
290
|
p: ({ children }) => /* @__PURE__ */ jsx2("p", { style: { margin: "0 0 0.5em 0" }, children }),
|
|
@@ -394,6 +399,23 @@ function MarkdownContent({ content }) {
|
|
|
394
399
|
},
|
|
395
400
|
children
|
|
396
401
|
}
|
|
402
|
+
),
|
|
403
|
+
// Render images as links to prevent automatic HTTP requests.
|
|
404
|
+
// <img> tags fire GET requests on render, which could be exploited
|
|
405
|
+
// via prompt injection to exfiltrate sensitive data through URLs.
|
|
406
|
+
img: ({ src, alt }) => /* @__PURE__ */ jsx2(
|
|
407
|
+
"a",
|
|
408
|
+
{
|
|
409
|
+
href: src,
|
|
410
|
+
target: "_blank",
|
|
411
|
+
rel: "noopener noreferrer",
|
|
412
|
+
style: {
|
|
413
|
+
color: "inherit",
|
|
414
|
+
textDecoration: "underline",
|
|
415
|
+
textUnderlineOffset: "2px"
|
|
416
|
+
},
|
|
417
|
+
children: alt || "Image"
|
|
418
|
+
}
|
|
397
419
|
)
|
|
398
420
|
},
|
|
399
421
|
children: content
|
|
@@ -1638,7 +1660,8 @@ function ToolApprovalDialog({
|
|
|
1638
1660
|
const [showDetails, setShowDetails] = useState4(false);
|
|
1639
1661
|
const displayName = annotations?.title || toolCallName;
|
|
1640
1662
|
const isBatch = toolCount > 1;
|
|
1641
|
-
const
|
|
1663
|
+
const runtimeMessage = pendingTools.find((t) => t.message)?.message;
|
|
1664
|
+
const message = runtimeMessage ? runtimeMessage : isBatch ? strings.toolApproval.batchMessage?.replace("{count}", String(toolCount)) ?? `${toolCount} actions are waiting for your approval` : strings.toolApproval.message.replace("{toolName}", displayName);
|
|
1642
1665
|
const getToolDisplayName = (tool) => tool.annotations?.title || tool.toolCallName;
|
|
1643
1666
|
return /* @__PURE__ */ jsxs7(
|
|
1644
1667
|
"div",
|
|
@@ -1890,12 +1913,6 @@ function FeedbackButton({ type, isSelected, onClick, selectedColor, unselectedCo
|
|
|
1890
1913
|
}
|
|
1891
1914
|
);
|
|
1892
1915
|
}
|
|
1893
|
-
function getTextContent(content) {
|
|
1894
|
-
if (typeof content === "string") {
|
|
1895
|
-
return content;
|
|
1896
|
-
}
|
|
1897
|
-
return content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
1898
|
-
}
|
|
1899
1916
|
function hasFileContent(content) {
|
|
1900
1917
|
return Array.isArray(content) && content.some((part) => part.type === "file");
|
|
1901
1918
|
}
|
|
@@ -1932,6 +1949,11 @@ function UseAIChatPanel({
|
|
|
1932
1949
|
}) {
|
|
1933
1950
|
const strings = useStrings();
|
|
1934
1951
|
const theme = useTheme();
|
|
1952
|
+
const displayMessages = messages.filter((m) => {
|
|
1953
|
+
if (m.role === "tool") return false;
|
|
1954
|
+
if (m.role === "assistant" && m.toolCalls && m.toolCalls.length > 0 && !getTextFromContent(m.content)) return false;
|
|
1955
|
+
return true;
|
|
1956
|
+
});
|
|
1935
1957
|
const [input, setInput] = useState5("");
|
|
1936
1958
|
const chatHistoryDropdown = useDropdownState();
|
|
1937
1959
|
const agentDropdown = useDropdownState();
|
|
@@ -2097,10 +2119,10 @@ function UseAIChatPanel({
|
|
|
2097
2119
|
flex: 1,
|
|
2098
2120
|
minWidth: 0
|
|
2099
2121
|
}, children: (() => {
|
|
2100
|
-
if (
|
|
2101
|
-
const firstUserMsg =
|
|
2122
|
+
if (displayMessages.length > 0) {
|
|
2123
|
+
const firstUserMsg = displayMessages.find((m) => m.role === "user");
|
|
2102
2124
|
if (firstUserMsg) {
|
|
2103
|
-
const textContent =
|
|
2125
|
+
const textContent = getTextFromContent(firstUserMsg.content);
|
|
2104
2126
|
const maxLength = 30;
|
|
2105
2127
|
return textContent.length > maxLength ? textContent.substring(0, maxLength) + "..." : textContent || strings.header.newChat;
|
|
2106
2128
|
}
|
|
@@ -2259,7 +2281,7 @@ function UseAIChatPanel({
|
|
|
2259
2281
|
children: /* @__PURE__ */ jsx11("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx11("path", { d: "M8 3.5V12.5M3.5 8H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) })
|
|
2260
2282
|
}
|
|
2261
2283
|
),
|
|
2262
|
-
onDeleteChat &&
|
|
2284
|
+
onDeleteChat && displayMessages.length > 0 && /* @__PURE__ */ jsx11(
|
|
2263
2285
|
"button",
|
|
2264
2286
|
{
|
|
2265
2287
|
"data-testid": "delete-chat-button",
|
|
@@ -2385,7 +2407,7 @@ function UseAIChatPanel({
|
|
|
2385
2407
|
gap: "12px"
|
|
2386
2408
|
},
|
|
2387
2409
|
children: [
|
|
2388
|
-
|
|
2410
|
+
displayMessages.length === 0 && /* @__PURE__ */ jsxs8(
|
|
2389
2411
|
"div",
|
|
2390
2412
|
{
|
|
2391
2413
|
style: {
|
|
@@ -2455,7 +2477,7 @@ function UseAIChatPanel({
|
|
|
2455
2477
|
]
|
|
2456
2478
|
}
|
|
2457
2479
|
),
|
|
2458
|
-
|
|
2480
|
+
displayMessages.map((message) => /* @__PURE__ */ jsxs8(
|
|
2459
2481
|
"div",
|
|
2460
2482
|
{
|
|
2461
2483
|
"data-testid": `chat-message-${message.role}`,
|
|
@@ -2482,7 +2504,7 @@ function UseAIChatPanel({
|
|
|
2482
2504
|
"data-testid": "save-command-button",
|
|
2483
2505
|
onClick: (e) => {
|
|
2484
2506
|
e.stopPropagation();
|
|
2485
|
-
const messageText =
|
|
2507
|
+
const messageText = getTextFromContent(message.content);
|
|
2486
2508
|
slashCommands.startSavingCommand(message.id, messageText);
|
|
2487
2509
|
},
|
|
2488
2510
|
title: "Save as slash command",
|
|
@@ -2542,13 +2564,13 @@ function UseAIChatPanel({
|
|
|
2542
2564
|
},
|
|
2543
2565
|
idx
|
|
2544
2566
|
)) }),
|
|
2545
|
-
message.role === "assistant" ? /* @__PURE__ */ jsx11(MarkdownContent, { content:
|
|
2567
|
+
message.role === "assistant" ? /* @__PURE__ */ jsx11(MarkdownContent, { content: getTextFromContent(message.content) }) : getTextFromContent(message.content)
|
|
2546
2568
|
]
|
|
2547
2569
|
}
|
|
2548
2570
|
),
|
|
2549
2571
|
slashCommands.renderInlineSaveUI({
|
|
2550
2572
|
messageId: message.id,
|
|
2551
|
-
messageText:
|
|
2573
|
+
messageText: getTextFromContent(message.content)
|
|
2552
2574
|
})
|
|
2553
2575
|
]
|
|
2554
2576
|
}
|
|
@@ -2602,7 +2624,7 @@ function UseAIChatPanel({
|
|
|
2602
2624
|
marginTop: "4px",
|
|
2603
2625
|
padding: "0 4px"
|
|
2604
2626
|
},
|
|
2605
|
-
children: message.
|
|
2627
|
+
children: message.createdAt.toLocaleTimeString([], {
|
|
2606
2628
|
hour: "2-digit",
|
|
2607
2629
|
minute: "2-digit"
|
|
2608
2630
|
})
|
|
@@ -3082,6 +3104,8 @@ var UseAIClient = class {
|
|
|
3082
3104
|
// Assistant message assembly (for tracking full conversation history)
|
|
3083
3105
|
_currentAssistantMessage = null;
|
|
3084
3106
|
_currentAssistantToolCalls = [];
|
|
3107
|
+
// Tool results collected during a turn, pushed to _messages in correct order at RUN_FINISHED
|
|
3108
|
+
_pendingToolResults = [];
|
|
3085
3109
|
// Tool call assembly
|
|
3086
3110
|
currentToolCalls = /* @__PURE__ */ new Map();
|
|
3087
3111
|
// Feedback tracking
|
|
@@ -3150,6 +3174,7 @@ var UseAIClient = class {
|
|
|
3150
3174
|
content: ""
|
|
3151
3175
|
};
|
|
3152
3176
|
this._currentAssistantToolCalls = [];
|
|
3177
|
+
this._pendingToolResults = [];
|
|
3153
3178
|
}
|
|
3154
3179
|
if (event.type === EventType.TEXT_MESSAGE_START) {
|
|
3155
3180
|
const e = event;
|
|
@@ -3190,17 +3215,32 @@ var UseAIClient = class {
|
|
|
3190
3215
|
}
|
|
3191
3216
|
} else if (event.type === EventType.RUN_FINISHED) {
|
|
3192
3217
|
if (this._currentAssistantMessage) {
|
|
3193
|
-
const assistantMessage = {
|
|
3194
|
-
id: this._currentAssistantMessage.id,
|
|
3195
|
-
role: "assistant",
|
|
3196
|
-
content: this._currentAssistantMessage.content || ""
|
|
3197
|
-
};
|
|
3198
3218
|
if (this._currentAssistantToolCalls.length > 0) {
|
|
3199
|
-
|
|
3219
|
+
const toolCallMessage = {
|
|
3220
|
+
id: uuidv42(),
|
|
3221
|
+
role: "assistant",
|
|
3222
|
+
content: "",
|
|
3223
|
+
toolCalls: this._currentAssistantToolCalls
|
|
3224
|
+
};
|
|
3225
|
+
this._messages.push(toolCallMessage);
|
|
3226
|
+
this._messages.push(...this._pendingToolResults);
|
|
3227
|
+
const textMessage = {
|
|
3228
|
+
id: this._currentAssistantMessage.id,
|
|
3229
|
+
role: "assistant",
|
|
3230
|
+
content: this._currentAssistantMessage.content || ""
|
|
3231
|
+
};
|
|
3232
|
+
this._messages.push(textMessage);
|
|
3233
|
+
} else {
|
|
3234
|
+
const assistantMessage = {
|
|
3235
|
+
id: this._currentAssistantMessage.id,
|
|
3236
|
+
role: "assistant",
|
|
3237
|
+
content: this._currentAssistantMessage.content || ""
|
|
3238
|
+
};
|
|
3239
|
+
this._messages.push(assistantMessage);
|
|
3200
3240
|
}
|
|
3201
|
-
this._messages.push(assistantMessage);
|
|
3202
3241
|
this._currentAssistantMessage = null;
|
|
3203
3242
|
this._currentAssistantToolCalls = [];
|
|
3243
|
+
this._pendingToolResults = [];
|
|
3204
3244
|
}
|
|
3205
3245
|
}
|
|
3206
3246
|
this.eventHandlers.forEach((handler) => handler(event));
|
|
@@ -3320,7 +3360,7 @@ var UseAIClient = class {
|
|
|
3320
3360
|
content: toolResultMessage.data.content,
|
|
3321
3361
|
toolCallId
|
|
3322
3362
|
};
|
|
3323
|
-
this.
|
|
3363
|
+
this._pendingToolResults.push(toolResultMsg);
|
|
3324
3364
|
this.send(toolResultMessage);
|
|
3325
3365
|
}
|
|
3326
3366
|
/**
|
|
@@ -3339,6 +3379,14 @@ var UseAIClient = class {
|
|
|
3339
3379
|
reason
|
|
3340
3380
|
}
|
|
3341
3381
|
};
|
|
3382
|
+
if (!approved) {
|
|
3383
|
+
this._pendingToolResults.push({
|
|
3384
|
+
id: uuidv42(),
|
|
3385
|
+
role: "tool",
|
|
3386
|
+
content: JSON.stringify({ rejected: true, reason: reason || "User rejected tool execution" }),
|
|
3387
|
+
toolCallId
|
|
3388
|
+
});
|
|
3389
|
+
}
|
|
3342
3390
|
this.send(message);
|
|
3343
3391
|
}
|
|
3344
3392
|
/**
|
|
@@ -3535,6 +3583,7 @@ var UseAIClient = class {
|
|
|
3535
3583
|
this.currentToolCalls.clear();
|
|
3536
3584
|
this._currentAssistantMessage = null;
|
|
3537
3585
|
this._currentAssistantToolCalls = [];
|
|
3586
|
+
this._pendingToolResults = [];
|
|
3538
3587
|
}
|
|
3539
3588
|
send(message) {
|
|
3540
3589
|
if (this.socket && this.socket.connected) {
|
|
@@ -3605,7 +3654,8 @@ function defineTool(description, schemaOrFn, fnOrOptions, options) {
|
|
|
3605
3654
|
let actualFn;
|
|
3606
3655
|
let actualOptions;
|
|
3607
3656
|
if (isNoParamFunction) {
|
|
3608
|
-
|
|
3657
|
+
const noParamFn = schemaOrFn;
|
|
3658
|
+
actualFn = (_input, ctx) => noParamFn(ctx);
|
|
3609
3659
|
actualOptions = fnOrOptions || {};
|
|
3610
3660
|
} else {
|
|
3611
3661
|
actualFn = fnOrOptions;
|
|
@@ -3639,21 +3689,29 @@ function defineTool(description, schemaOrFn, fnOrOptions, options) {
|
|
|
3639
3689
|
}
|
|
3640
3690
|
return toolDef;
|
|
3641
3691
|
},
|
|
3642
|
-
async _execute(input) {
|
|
3692
|
+
async _execute(input, ctx) {
|
|
3643
3693
|
const validated = this._zodSchema.parse(input);
|
|
3644
|
-
return await actualFn(validated);
|
|
3694
|
+
return await actualFn(validated, ctx);
|
|
3645
3695
|
}
|
|
3646
3696
|
};
|
|
3647
3697
|
}
|
|
3648
3698
|
function convertToolsToDefinitions(tools) {
|
|
3649
3699
|
return Object.entries(tools).map(([name, tool]) => tool._toToolDefinition(name));
|
|
3650
3700
|
}
|
|
3651
|
-
async function executeDefinedTool(tools, toolName, input) {
|
|
3701
|
+
async function executeDefinedTool(tools, toolName, input, ctx) {
|
|
3652
3702
|
const tool = tools[toolName];
|
|
3653
3703
|
if (!tool) {
|
|
3654
3704
|
throw new Error(`Tool "${toolName}" not found`);
|
|
3655
3705
|
}
|
|
3656
|
-
return await tool._execute(input);
|
|
3706
|
+
return await tool._execute(input, ctx);
|
|
3707
|
+
}
|
|
3708
|
+
|
|
3709
|
+
// src/providers/chatRepository/types.ts
|
|
3710
|
+
function generateChatId() {
|
|
3711
|
+
return `chat_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
3712
|
+
}
|
|
3713
|
+
function generateMessageId() {
|
|
3714
|
+
return `msg_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
3657
3715
|
}
|
|
3658
3716
|
|
|
3659
3717
|
// src/providers/chatRepository/LocalStorageChatRepository.ts
|
|
@@ -3821,31 +3879,31 @@ function deepEquals(a, b) {
|
|
|
3821
3879
|
function generateChatTitle(message) {
|
|
3822
3880
|
return message.length > CHAT_TITLE_MAX_LENGTH ? message.substring(0, CHAT_TITLE_MAX_LENGTH) + "..." : message;
|
|
3823
3881
|
}
|
|
3824
|
-
function
|
|
3825
|
-
|
|
3826
|
-
return content;
|
|
3827
|
-
}
|
|
3828
|
-
return content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
3829
|
-
}
|
|
3830
|
-
function transformMessagesToUI(storageMessages) {
|
|
3831
|
-
return storageMessages.map((msg) => ({
|
|
3832
|
-
id: msg.id,
|
|
3833
|
-
role: msg.role,
|
|
3834
|
-
content: msg.content,
|
|
3835
|
-
timestamp: msg.createdAt,
|
|
3836
|
-
displayMode: msg.displayMode,
|
|
3837
|
-
traceId: msg.traceId,
|
|
3838
|
-
feedback: msg.feedback
|
|
3839
|
-
}));
|
|
3840
|
-
}
|
|
3841
|
-
function transformMessagesToClientFormat(uiMessages) {
|
|
3842
|
-
return uiMessages.map((msg) => {
|
|
3882
|
+
function transformMessagesToClientFormat(persistedMessages) {
|
|
3883
|
+
return persistedMessages.map((msg) => {
|
|
3843
3884
|
const textContent = getTextFromContent(msg.content);
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3885
|
+
switch (msg.role) {
|
|
3886
|
+
case "tool":
|
|
3887
|
+
return {
|
|
3888
|
+
id: msg.id,
|
|
3889
|
+
role: "tool",
|
|
3890
|
+
content: textContent,
|
|
3891
|
+
toolCallId: msg.toolCallId || ""
|
|
3892
|
+
};
|
|
3893
|
+
case "assistant":
|
|
3894
|
+
return {
|
|
3895
|
+
id: msg.id,
|
|
3896
|
+
role: "assistant",
|
|
3897
|
+
content: textContent,
|
|
3898
|
+
...msg.toolCalls && msg.toolCalls.length > 0 ? { toolCalls: msg.toolCalls } : {}
|
|
3899
|
+
};
|
|
3900
|
+
case "user":
|
|
3901
|
+
return {
|
|
3902
|
+
id: msg.id,
|
|
3903
|
+
role: "user",
|
|
3904
|
+
content: textContent
|
|
3905
|
+
};
|
|
3906
|
+
}
|
|
3849
3907
|
});
|
|
3850
3908
|
}
|
|
3851
3909
|
function useChatManagement({
|
|
@@ -3869,9 +3927,8 @@ function useChatManagement({
|
|
|
3869
3927
|
try {
|
|
3870
3928
|
const chat = await repository.loadChat(chatId);
|
|
3871
3929
|
if (chat) {
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
return loadedMessages;
|
|
3930
|
+
console.log("[ChatManagement] Loaded", chat.messages.length, "messages from storage for chat:", chatId);
|
|
3931
|
+
return chat.messages;
|
|
3875
3932
|
} else {
|
|
3876
3933
|
console.log("[ChatManagement] Chat not found in storage:", chatId);
|
|
3877
3934
|
return [];
|
|
@@ -3967,7 +4024,7 @@ function useChatManagement({
|
|
|
3967
4024
|
setCurrentChatId(pendingChatId);
|
|
3968
4025
|
setPendingChatId(null);
|
|
3969
4026
|
return pendingChatId;
|
|
3970
|
-
}, [pendingChatId,
|
|
4027
|
+
}, [pendingChatId, clientRef, messages]);
|
|
3971
4028
|
const saveUserMessage = useCallback4(async (chatId, content) => {
|
|
3972
4029
|
try {
|
|
3973
4030
|
const chat = await repository.loadChat(chatId);
|
|
@@ -3975,13 +4032,13 @@ function useChatManagement({
|
|
|
3975
4032
|
console.error("[ChatManagement] Chat not found:", chatId);
|
|
3976
4033
|
return false;
|
|
3977
4034
|
}
|
|
3978
|
-
const
|
|
3979
|
-
|
|
3980
|
-
id: generateMessageId2(),
|
|
4035
|
+
const newMessage = {
|
|
4036
|
+
id: generateMessageId(),
|
|
3981
4037
|
role: "user",
|
|
3982
4038
|
content,
|
|
3983
4039
|
createdAt: /* @__PURE__ */ new Date()
|
|
3984
|
-
}
|
|
4040
|
+
};
|
|
4041
|
+
chat.messages.push(newMessage);
|
|
3985
4042
|
if (!chat.title) {
|
|
3986
4043
|
const text = getTextFromContent(content);
|
|
3987
4044
|
if (text) {
|
|
@@ -3990,14 +4047,14 @@ function useChatManagement({
|
|
|
3990
4047
|
}
|
|
3991
4048
|
await repository.saveChat(chat);
|
|
3992
4049
|
console.log("[ChatManagement] Saved user message to storage");
|
|
3993
|
-
|
|
4050
|
+
setMessages((prev) => [...prev, newMessage]);
|
|
3994
4051
|
return true;
|
|
3995
4052
|
} catch (error) {
|
|
3996
4053
|
console.error("[ChatManagement] Failed to save user message:", error);
|
|
3997
4054
|
return false;
|
|
3998
4055
|
}
|
|
3999
|
-
}, [repository,
|
|
4000
|
-
const saveAIResponse = useCallback4(async (content, displayMode, traceId) => {
|
|
4056
|
+
}, [repository, setMessages]);
|
|
4057
|
+
const saveAIResponse = useCallback4(async (content, displayMode, traceId, turnMessages) => {
|
|
4001
4058
|
const currentChatIdValue = currentChatIdSnapshot.current;
|
|
4002
4059
|
const pendingChatIdValue = pendingChatIdSnapshot.current;
|
|
4003
4060
|
const displayedChatId2 = pendingChatIdValue || currentChatIdValue;
|
|
@@ -4011,15 +4068,18 @@ function useChatManagement({
|
|
|
4011
4068
|
console.error("[ChatManagement] Chat not found:", currentChatIdValue);
|
|
4012
4069
|
return;
|
|
4013
4070
|
}
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4071
|
+
if (turnMessages && turnMessages.length > 0) {
|
|
4072
|
+
chat.messages.push(...turnMessages);
|
|
4073
|
+
}
|
|
4074
|
+
const finalMessage = {
|
|
4075
|
+
id: generateMessageId(),
|
|
4017
4076
|
role: "assistant",
|
|
4018
4077
|
content,
|
|
4019
4078
|
createdAt: /* @__PURE__ */ new Date(),
|
|
4020
4079
|
displayMode,
|
|
4021
4080
|
traceId
|
|
4022
|
-
}
|
|
4081
|
+
};
|
|
4082
|
+
chat.messages.push(finalMessage);
|
|
4023
4083
|
if (!chat.title) {
|
|
4024
4084
|
const firstUserMessage = chat.messages.find((msg) => msg.role === "user");
|
|
4025
4085
|
if (firstUserMessage) {
|
|
@@ -4032,12 +4092,16 @@ function useChatManagement({
|
|
|
4032
4092
|
await repository.saveChat(chat);
|
|
4033
4093
|
console.log("[ChatManagement] Saved AI response to storage for chatId:", currentChatIdValue);
|
|
4034
4094
|
if (displayedChatId2 === currentChatIdValue) {
|
|
4035
|
-
|
|
4095
|
+
const newMessages = [
|
|
4096
|
+
...turnMessages ?? [],
|
|
4097
|
+
finalMessage
|
|
4098
|
+
];
|
|
4099
|
+
setMessages((prev) => [...prev, ...newMessages]);
|
|
4036
4100
|
}
|
|
4037
4101
|
} catch (error) {
|
|
4038
4102
|
console.error("[ChatManagement] Failed to save AI response:", error);
|
|
4039
4103
|
}
|
|
4040
|
-
}, [repository,
|
|
4104
|
+
}, [repository, setMessages]);
|
|
4041
4105
|
const initializedRef = useRef5(false);
|
|
4042
4106
|
useEffect5(() => {
|
|
4043
4107
|
if (currentChatId === null && pendingChatId === null && !initializedRef.current) {
|
|
@@ -4068,6 +4132,26 @@ function useChatManagement({
|
|
|
4068
4132
|
})();
|
|
4069
4133
|
}
|
|
4070
4134
|
}, [currentChatId, pendingChatId, createNewChat, repository, loadChatMessages, clientRef, setMessages]);
|
|
4135
|
+
const connectionPhaseRef = useRef5("initial");
|
|
4136
|
+
useEffect5(() => {
|
|
4137
|
+
if (connected) {
|
|
4138
|
+
const isReconnect = connectionPhaseRef.current === "disconnected";
|
|
4139
|
+
connectionPhaseRef.current = "connected";
|
|
4140
|
+
if (isReconnect && currentChatId && clientRef.current) {
|
|
4141
|
+
(async () => {
|
|
4142
|
+
const loadedMessages = await loadChatMessages(currentChatId);
|
|
4143
|
+
if (loadedMessages.length > 0) {
|
|
4144
|
+
clientRef.current?.loadMessages(transformMessagesToClientFormat(loadedMessages));
|
|
4145
|
+
console.log("[ChatManagement] Reloaded", loadedMessages.length, "messages from storage on reconnect");
|
|
4146
|
+
}
|
|
4147
|
+
})();
|
|
4148
|
+
}
|
|
4149
|
+
} else {
|
|
4150
|
+
if (connectionPhaseRef.current === "connected") {
|
|
4151
|
+
connectionPhaseRef.current = "disconnected";
|
|
4152
|
+
}
|
|
4153
|
+
}
|
|
4154
|
+
}, [connected, currentChatId, loadChatMessages, clientRef]);
|
|
4071
4155
|
const displayedChatId = pendingChatId || currentChatId;
|
|
4072
4156
|
return {
|
|
4073
4157
|
currentChatId,
|
|
@@ -4344,6 +4428,7 @@ function useToolSystem({
|
|
|
4344
4428
|
const waitersRef = useRef7(/* @__PURE__ */ new Map());
|
|
4345
4429
|
const [pendingApprovals, setPendingApprovals] = useState9([]);
|
|
4346
4430
|
const pendingApprovalToolCallsRef = useRef7(/* @__PURE__ */ new Map());
|
|
4431
|
+
const runtimeApprovalResolversRef = useRef7(/* @__PURE__ */ new Map());
|
|
4347
4432
|
const registerTools = useCallback7((id, tools, options) => {
|
|
4348
4433
|
const existingTools = toolRegistryRef.current.get(id);
|
|
4349
4434
|
toolRegistryRef.current.set(id, tools);
|
|
@@ -4453,7 +4538,9 @@ function useToolSystem({
|
|
|
4453
4538
|
toolCallId: event.toolCallId,
|
|
4454
4539
|
toolCallName: event.toolCallName,
|
|
4455
4540
|
toolCallArgs: event.toolCallArgs,
|
|
4456
|
-
annotations: event.annotations
|
|
4541
|
+
annotations: event.annotations,
|
|
4542
|
+
message: event.message,
|
|
4543
|
+
metadata: event.metadata
|
|
4457
4544
|
}
|
|
4458
4545
|
]);
|
|
4459
4546
|
}, []);
|
|
@@ -4466,8 +4553,26 @@ function useToolSystem({
|
|
|
4466
4553
|
try {
|
|
4467
4554
|
const ownerId = toolOwnershipRef.current.get(name);
|
|
4468
4555
|
console.log(`[useToolSystem] Tool "${name}" owned by component:`, ownerId);
|
|
4556
|
+
const ctx = {
|
|
4557
|
+
requestApproval: ({ message, metadata }) => {
|
|
4558
|
+
return new Promise((resolve) => {
|
|
4559
|
+
const approvalId = `${toolCallId}-runtime-${Date.now()}`;
|
|
4560
|
+
runtimeApprovalResolversRef.current.set(approvalId, resolve);
|
|
4561
|
+
setPendingApprovals((prev) => [
|
|
4562
|
+
...prev,
|
|
4563
|
+
{
|
|
4564
|
+
toolCallId: approvalId,
|
|
4565
|
+
toolCallName: name,
|
|
4566
|
+
toolCallArgs: input || {},
|
|
4567
|
+
message,
|
|
4568
|
+
metadata
|
|
4569
|
+
}
|
|
4570
|
+
]);
|
|
4571
|
+
});
|
|
4572
|
+
}
|
|
4573
|
+
};
|
|
4469
4574
|
console.log("[useToolSystem] Executing tool...");
|
|
4470
|
-
const result = await executeDefinedTool(aggregatedToolsRef.current, name, input);
|
|
4575
|
+
const result = await executeDefinedTool(aggregatedToolsRef.current, name, input, ctx);
|
|
4471
4576
|
const isErrorResult = result && typeof result === "object" && ("error" in result || result.success === false);
|
|
4472
4577
|
const ownerIsInvisible = ownerId ? isInvisible(ownerId) : false;
|
|
4473
4578
|
if (ownerId && !isErrorResult && !ownerIsInvisible) {
|
|
@@ -4512,24 +4617,32 @@ function useToolSystem({
|
|
|
4512
4617
|
if (!clientRef.current) return;
|
|
4513
4618
|
console.log("[useToolSystem] Approving all tool calls:", pendingApprovals.length);
|
|
4514
4619
|
const pendingTools = [...pendingApprovals];
|
|
4515
|
-
for (const pending of pendingTools) {
|
|
4516
|
-
clientRef.current.sendToolApprovalResponse(pending.toolCallId, true);
|
|
4517
|
-
}
|
|
4518
4620
|
setPendingApprovals([]);
|
|
4519
|
-
for (const
|
|
4520
|
-
|
|
4621
|
+
for (const pending of pendingTools) {
|
|
4622
|
+
const runtimeResolver = runtimeApprovalResolversRef.current.get(pending.toolCallId);
|
|
4623
|
+
if (runtimeResolver) {
|
|
4624
|
+
runtimeApprovalResolversRef.current.delete(pending.toolCallId);
|
|
4625
|
+
runtimeResolver({ approved: true });
|
|
4626
|
+
} else {
|
|
4627
|
+
clientRef.current.sendToolApprovalResponse(pending.toolCallId, true);
|
|
4628
|
+
await executePendingToolAfterApproval(pending.toolCallId);
|
|
4629
|
+
}
|
|
4521
4630
|
}
|
|
4522
4631
|
}, [clientRef, pendingApprovals, executePendingToolAfterApproval]);
|
|
4523
4632
|
const rejectAll = useCallback7((reason) => {
|
|
4524
4633
|
if (!clientRef.current) return;
|
|
4525
4634
|
console.log("[useToolSystem] Rejecting all tool calls:", pendingApprovals.length, reason);
|
|
4526
4635
|
const pendingTools = [...pendingApprovals];
|
|
4527
|
-
for (const pending of pendingTools) {
|
|
4528
|
-
clientRef.current.sendToolApprovalResponse(pending.toolCallId, false, reason);
|
|
4529
|
-
}
|
|
4530
4636
|
setPendingApprovals([]);
|
|
4531
|
-
for (const
|
|
4532
|
-
|
|
4637
|
+
for (const pending of pendingTools) {
|
|
4638
|
+
const runtimeResolver = runtimeApprovalResolversRef.current.get(pending.toolCallId);
|
|
4639
|
+
if (runtimeResolver) {
|
|
4640
|
+
runtimeApprovalResolversRef.current.delete(pending.toolCallId);
|
|
4641
|
+
runtimeResolver({ approved: false, reason });
|
|
4642
|
+
} else {
|
|
4643
|
+
clientRef.current.sendToolApprovalResponse(pending.toolCallId, false, reason);
|
|
4644
|
+
pendingApprovalToolCallsRef.current.delete(pending.toolCallId);
|
|
4645
|
+
}
|
|
4533
4646
|
}
|
|
4534
4647
|
}, [clientRef, pendingApprovals]);
|
|
4535
4648
|
return {
|
|
@@ -4683,6 +4796,30 @@ import { useState as useState12, useCallback as useCallback10, useRef as useRef1
|
|
|
4683
4796
|
import { EventType as EventType2, ErrorCode, TOOL_APPROVAL_REQUEST } from "@meetsmore-oss/use-ai-core";
|
|
4684
4797
|
|
|
4685
4798
|
// src/hooks/useServerEvents.ts
|
|
4799
|
+
function extractTurnMessages(messages, startIndex) {
|
|
4800
|
+
const turnSlice = messages.slice(startIndex);
|
|
4801
|
+
const result = [];
|
|
4802
|
+
for (const msg of turnSlice) {
|
|
4803
|
+
if (msg.role === "assistant" && "toolCalls" in msg && msg.toolCalls) {
|
|
4804
|
+
result.push({
|
|
4805
|
+
id: msg.id,
|
|
4806
|
+
role: "assistant",
|
|
4807
|
+
content: "",
|
|
4808
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
4809
|
+
toolCalls: msg.toolCalls
|
|
4810
|
+
});
|
|
4811
|
+
} else if (msg.role === "tool") {
|
|
4812
|
+
result.push({
|
|
4813
|
+
id: msg.id,
|
|
4814
|
+
role: "tool",
|
|
4815
|
+
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
|
|
4816
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
4817
|
+
toolCallId: "toolCallId" in msg && msg.toolCallId ? msg.toolCallId : void 0
|
|
4818
|
+
});
|
|
4819
|
+
}
|
|
4820
|
+
}
|
|
4821
|
+
return result;
|
|
4822
|
+
}
|
|
4686
4823
|
function useServerEvents({
|
|
4687
4824
|
toolSystem,
|
|
4688
4825
|
saveAIResponse,
|
|
@@ -4691,6 +4828,7 @@ function useServerEvents({
|
|
|
4691
4828
|
const [loading, setLoading] = useState12(false);
|
|
4692
4829
|
const [streamingText, setStreamingText] = useState12("");
|
|
4693
4830
|
const streamingChatIdRef = useRef10(null);
|
|
4831
|
+
const messageCountAtRunStartRef = useRef10(0);
|
|
4694
4832
|
const [executingToolRaw, setExecutingTool] = useState12(null);
|
|
4695
4833
|
const executingToolFallbackRef = useRef10(null);
|
|
4696
4834
|
const clearStreamingText = useCallback10(() => {
|
|
@@ -4705,7 +4843,9 @@ function useServerEvents({
|
|
|
4705
4843
|
const handleServerEvent = useCallback10(async (client, event) => {
|
|
4706
4844
|
const ts = toolSystemRef.current;
|
|
4707
4845
|
const strs = stringsRef.current;
|
|
4708
|
-
if (event.type === EventType2.
|
|
4846
|
+
if (event.type === EventType2.RUN_STARTED) {
|
|
4847
|
+
messageCountAtRunStartRef.current = client.messages.length;
|
|
4848
|
+
} else if (event.type === EventType2.TOOL_CALL_START) {
|
|
4709
4849
|
const e = event;
|
|
4710
4850
|
const tool = ts.aggregatedToolsRef.current[e.toolCallName];
|
|
4711
4851
|
const title = e.annotations?.title ?? tool?._options?.annotations?.title ?? null;
|
|
@@ -4750,7 +4890,8 @@ function useServerEvents({
|
|
|
4750
4890
|
if (content) {
|
|
4751
4891
|
const finishedEvent = event;
|
|
4752
4892
|
const traceId = finishedEvent.runId;
|
|
4753
|
-
|
|
4893
|
+
const turnMessages = extractTurnMessages(client.messages, messageCountAtRunStartRef.current);
|
|
4894
|
+
saveAIResponseRef.current(content, void 0, traceId, turnMessages);
|
|
4754
4895
|
}
|
|
4755
4896
|
setLoading(false);
|
|
4756
4897
|
} else if (event.type === EventType2.RUN_ERROR) {
|
|
@@ -5326,19 +5467,19 @@ function useStableTools(tools) {
|
|
|
5326
5467
|
return stableToolsRef.current;
|
|
5327
5468
|
}
|
|
5328
5469
|
function createStableToolWrapper(name, tool, latestToolsRef) {
|
|
5329
|
-
const stableHandler = (input) => {
|
|
5470
|
+
const stableHandler = (input, ctx) => {
|
|
5330
5471
|
const currentTool = latestToolsRef.current[name];
|
|
5331
5472
|
if (!currentTool) {
|
|
5332
5473
|
throw new Error(`Tool "${name}" no longer exists`);
|
|
5333
5474
|
}
|
|
5334
|
-
return currentTool.fn(input);
|
|
5475
|
+
return currentTool.fn(input, ctx);
|
|
5335
5476
|
};
|
|
5336
|
-
const stableExecute = async (input) => {
|
|
5477
|
+
const stableExecute = async (input, ctx) => {
|
|
5337
5478
|
const currentTool = latestToolsRef.current[name];
|
|
5338
5479
|
if (!currentTool) {
|
|
5339
5480
|
throw new Error(`Tool "${name}" no longer exists`);
|
|
5340
5481
|
}
|
|
5341
|
-
return await currentTool._execute(input);
|
|
5482
|
+
return await currentTool._execute(input, ctx);
|
|
5342
5483
|
};
|
|
5343
5484
|
return {
|
|
5344
5485
|
description: tool.description,
|
|
@@ -5546,7 +5687,10 @@ function useAIWorkflow(runner, workflowId) {
|
|
|
5546
5687
|
console.log(`[useAIWorkflow] Executing tool: ${toolName}`, toolArgs);
|
|
5547
5688
|
console.log(`[useAIWorkflow] Available tools:`, Object.keys(currentWorkflow.tools));
|
|
5548
5689
|
try {
|
|
5549
|
-
const
|
|
5690
|
+
const noopCtx = {
|
|
5691
|
+
requestApproval: async () => ({ approved: true })
|
|
5692
|
+
};
|
|
5693
|
+
const result = await executeDefinedTool(currentWorkflow.tools, toolName, toolArgs, noopCtx);
|
|
5550
5694
|
currentWorkflow.toolCalls.push({
|
|
5551
5695
|
toolName,
|
|
5552
5696
|
args: toolArgs,
|