@meetsmore-oss/use-ai-client 1.5.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4,13 +4,13 @@ import {
4
4
  } from "./chunk-STF3H6F5.js";
5
5
 
6
6
  // src/useAI.ts
7
- import { useState as useState12, useEffect as useEffect11, useRef as useRef12, useCallback as useCallback11, useMemo as useMemo6 } from "react";
7
+ import { useState as useState14, useEffect as useEffect11, useRef as useRef13, useCallback as useCallback12, useMemo as useMemo6 } from "react";
8
8
 
9
9
  // src/providers/useAIProvider.tsx
10
- import { createContext as createContext4, useContext as useContext4, useState as useState11, useEffect as useEffect10, useCallback as useCallback10, useRef as useRef10 } from "react";
10
+ import { createContext as createContext4, useContext as useContext4, useState as useState13, useEffect as useEffect10, useCallback as useCallback11, useRef as useRef11 } from "react";
11
11
 
12
12
  // src/types.ts
13
- import { EventType, ErrorCode } from "@meetsmore-oss/use-ai-core";
13
+ import { EventType, ErrorCode, TOOL_APPROVAL_REQUEST } from "@meetsmore-oss/use-ai-core";
14
14
 
15
15
  // src/theme/strings.ts
16
16
  import { createContext, useContext } from "react";
@@ -109,6 +109,25 @@ var defaultStrings = {
109
109
  toolExecution: {
110
110
  /** Fallback messages when no tool title is provided (one randomly selected) */
111
111
  fallbackMessages: ["Working", "Processing", "Thinking"]
112
+ },
113
+ // Tool approval dialog
114
+ toolApproval: {
115
+ /** Title shown in the approval dialog */
116
+ title: "Confirmation Required",
117
+ /** Message shown in the approval dialog. {toolName} is replaced with tool name. */
118
+ message: '"{toolName}" is waiting for your approval.',
119
+ /** Message shown when multiple tools are awaiting approval. {count} is replaced with number. */
120
+ batchMessage: "{count} actions are waiting for your approval.",
121
+ /** Label for approve button */
122
+ approve: "Allow",
123
+ /** Label for approve all button (batch mode) */
124
+ approveAll: "Allow All",
125
+ /** Label for reject button */
126
+ reject: "Deny",
127
+ /** Label for reject all button (batch mode) */
128
+ rejectAll: "Deny All",
129
+ /** Label for showing tool arguments */
130
+ showDetails: "Show details"
112
131
  }
113
132
  };
114
133
  var StringsContext = createContext(defaultStrings);
@@ -255,7 +274,7 @@ function UseAIFloatingButton({
255
274
  }
256
275
 
257
276
  // src/components/UseAIChatPanel.tsx
258
- import { useState as useState4, useRef as useRef4, useEffect as useEffect4 } from "react";
277
+ import { useState as useState5, useRef as useRef4, useEffect as useEffect4 } from "react";
259
278
 
260
279
  // src/components/MarkdownContent.tsx
261
280
  import ReactMarkdown from "react-markdown";
@@ -1177,30 +1196,30 @@ function matchesMimeType(mimeType, pattern) {
1177
1196
  const regex = new RegExp(`^${regexPattern}$`);
1178
1197
  return regex.test(mimeType);
1179
1198
  }
1180
- function findTransformer(mimeType, transformers) {
1199
+ function findTransformerPattern(mimeType, transformers) {
1181
1200
  if (!transformers) {
1182
1201
  return void 0;
1183
1202
  }
1184
- let bestMatch;
1203
+ let bestKey;
1185
1204
  let bestIsExact = false;
1186
1205
  let bestLength = -1;
1187
- for (const [pattern, transformer] of Object.entries(transformers)) {
1206
+ for (const pattern of Object.keys(transformers)) {
1188
1207
  if (!matchesMimeType(mimeType, pattern)) {
1189
1208
  continue;
1190
1209
  }
1191
1210
  const isExact = !pattern.includes("*");
1192
1211
  if (isExact && !bestIsExact) {
1193
- bestMatch = transformer;
1212
+ bestKey = pattern;
1194
1213
  bestIsExact = true;
1195
1214
  bestLength = pattern.length;
1196
1215
  continue;
1197
1216
  }
1198
1217
  if (isExact === bestIsExact && pattern.length > bestLength) {
1199
- bestMatch = transformer;
1218
+ bestKey = pattern;
1200
1219
  bestLength = pattern.length;
1201
1220
  }
1202
1221
  }
1203
- return bestMatch;
1222
+ return bestKey;
1204
1223
  }
1205
1224
 
1206
1225
  // src/fileUpload/EmbedFileUploadBackend.ts
@@ -1231,75 +1250,104 @@ var EmbedFileUploadBackend = class {
1231
1250
  };
1232
1251
 
1233
1252
  // src/fileUpload/processAttachments.ts
1253
+ function groupBy(items, keyFn) {
1254
+ const map = /* @__PURE__ */ new Map();
1255
+ for (const item of items) {
1256
+ const key = keyFn(item);
1257
+ const list = map.get(key);
1258
+ list ? list.push(item) : map.set(key, [item]);
1259
+ }
1260
+ return map;
1261
+ }
1234
1262
  var transformationCache = /* @__PURE__ */ new Map();
1235
1263
  function getFileCacheKey(file) {
1236
1264
  return `${file.name}:${file.size}:${file.lastModified}`;
1237
1265
  }
1238
- async function getTransformedContent(file, transformer, context, onProgress) {
1239
- const cacheKey = getFileCacheKey(file);
1266
+ async function hashGroupCacheKey(files) {
1267
+ const raw = files.map(getFileCacheKey).join(", ");
1268
+ const data = new TextEncoder().encode(raw);
1269
+ const hash = await crypto.subtle.digest("SHA-256", data);
1270
+ return Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("");
1271
+ }
1272
+ async function getTransformedContent(files, transformer, context, onProgress) {
1273
+ if (files.length === 0) {
1274
+ return [];
1275
+ }
1276
+ const cacheKey = await hashGroupCacheKey(files);
1240
1277
  const cached = transformationCache.get(cacheKey);
1241
- if (cached !== void 0) {
1278
+ if (cached) {
1242
1279
  return cached;
1243
1280
  }
1244
- const result = await transformer.transform(file, context, onProgress);
1245
- transformationCache.set(cacheKey, result);
1246
- return result;
1281
+ const results = await transformer.transform(files, context, onProgress);
1282
+ transformationCache.set(cacheKey, results);
1283
+ return results;
1284
+ }
1285
+ async function toContentPart(attachment, backend) {
1286
+ if (attachment.transformedContent !== void 0) {
1287
+ return {
1288
+ type: "transformed_file",
1289
+ text: attachment.transformedContent,
1290
+ originalFile: {
1291
+ name: attachment.file.name,
1292
+ mimeType: attachment.file.type,
1293
+ size: attachment.file.size
1294
+ }
1295
+ };
1296
+ }
1297
+ const url = await backend.prepareForSend(attachment.file);
1298
+ if (attachment.file.type.startsWith("image/")) {
1299
+ return { type: "image", url };
1300
+ }
1301
+ return {
1302
+ type: "file",
1303
+ url,
1304
+ mimeType: attachment.file.type,
1305
+ name: attachment.file.name
1306
+ };
1247
1307
  }
1248
1308
  async function processAttachments(attachments, config) {
1249
1309
  const { getCurrentChat, backend = new EmbedFileUploadBackend(), transformers = {}, onFileProgress } = config;
1250
1310
  const contentParts = [];
1251
1311
  const chat = await getCurrentChat();
1252
1312
  const context = { chat };
1253
- for (const attachment of attachments) {
1313
+ const groups = groupBy(
1314
+ attachments,
1315
+ (attachment) => attachment.transformedContent !== void 0 ? null : findTransformerPattern(attachment.file.type, transformers) ?? null
1316
+ );
1317
+ for (const [key, groupAttachments] of groups) {
1318
+ if (key === null) {
1319
+ const parts = await Promise.all(
1320
+ groupAttachments.map((a) => toContentPart(a, backend))
1321
+ );
1322
+ contentParts.push(...parts);
1323
+ continue;
1324
+ }
1325
+ const transformer = transformers[key];
1326
+ const files = groupAttachments.map((a) => a.file);
1327
+ groupAttachments.forEach((a) => onFileProgress?.(a.id, { status: "processing" }));
1254
1328
  try {
1255
- if (attachment.transformedContent !== void 0) {
1256
- contentParts.push({
1257
- type: "transformed_file",
1258
- text: attachment.transformedContent,
1259
- originalFile: {
1260
- name: attachment.file.name,
1261
- mimeType: attachment.file.type,
1262
- size: attachment.file.size
1263
- }
1264
- });
1265
- continue;
1266
- }
1267
- const transformer = findTransformer(attachment.file.type, transformers);
1268
- if (transformer) {
1269
- onFileProgress?.(attachment.id, { status: "processing" });
1270
- const transformedText = await getTransformedContent(
1271
- attachment.file,
1272
- transformer,
1273
- context,
1274
- (progress) => {
1275
- onFileProgress?.(attachment.id, { status: "processing", progress });
1276
- }
1277
- );
1329
+ const results = await getTransformedContent(
1330
+ files,
1331
+ transformer,
1332
+ context,
1333
+ (progress) => {
1334
+ groupAttachments.forEach((a) => onFileProgress?.(a.id, { status: "processing", progress }));
1335
+ }
1336
+ );
1337
+ results.forEach((text, i) => {
1278
1338
  contentParts.push({
1279
1339
  type: "transformed_file",
1280
- text: transformedText,
1340
+ text,
1281
1341
  originalFile: {
1282
- name: attachment.file.name,
1283
- mimeType: attachment.file.type,
1284
- size: attachment.file.size
1342
+ name: files[i].name,
1343
+ mimeType: files[i].type,
1344
+ size: files[i].size
1285
1345
  }
1286
1346
  });
1287
- onFileProgress?.(attachment.id, { status: "done" });
1288
- } else {
1289
- const url = await backend.prepareForSend(attachment.file);
1290
- if (attachment.file.type.startsWith("image/")) {
1291
- contentParts.push({ type: "image", url });
1292
- } else {
1293
- contentParts.push({
1294
- type: "file",
1295
- url,
1296
- mimeType: attachment.file.type,
1297
- name: attachment.file.name
1298
- });
1299
- }
1300
- }
1347
+ onFileProgress?.(groupAttachments[i].id, { status: "done" });
1348
+ });
1301
1349
  } catch (error) {
1302
- onFileProgress?.(attachment.id, { status: "error" });
1350
+ groupAttachments.forEach((a) => onFileProgress?.(a.id, { status: "error" }));
1303
1351
  throw error;
1304
1352
  }
1305
1353
  }
@@ -1370,12 +1418,14 @@ function useFileUpload({
1370
1418
  setFileError(null);
1371
1419
  setProcessingState(/* @__PURE__ */ new Map());
1372
1420
  }, [resetDependency]);
1373
- const runTransformer = useCallback2(async (attachmentId, file, transformer) => {
1421
+ const runTransformer = useCallback2(async (attachmentId, file, transformerKey) => {
1422
+ const transformer = transformers?.[transformerKey];
1423
+ if (!transformer) return;
1374
1424
  setProcessingState((prev) => new Map(prev).set(attachmentId, { status: "processing" }));
1375
1425
  try {
1376
1426
  const chat = await getCurrentChat();
1377
1427
  const context = { chat };
1378
- const transformedContent = await getTransformedContent(file, transformer, context, (progress) => {
1428
+ const [transformedContent] = await getTransformedContent([file], transformer, context, (progress) => {
1379
1429
  setProcessingState((prev) => new Map(prev).set(attachmentId, {
1380
1430
  status: "processing",
1381
1431
  progress
@@ -1389,7 +1439,7 @@ function useFileUpload({
1389
1439
  console.error(`[useFileUpload] Transformation failed for ${file.name}:`, error);
1390
1440
  setProcessingState((prev) => new Map(prev).set(attachmentId, { status: "error" }));
1391
1441
  }
1392
- }, [getCurrentChat]);
1442
+ }, [getCurrentChat, transformers]);
1393
1443
  const handleFiles = useCallback2(async (files) => {
1394
1444
  const fileArray = Array.from(files);
1395
1445
  for (const file of fileArray) {
@@ -1411,9 +1461,9 @@ function useFileUpload({
1411
1461
  preview
1412
1462
  };
1413
1463
  setAttachments((prev) => [...prev, attachment]);
1414
- const transformer = findTransformer(file.type, transformers);
1415
- if (transformer) {
1416
- runTransformer(attachmentId, file, transformer);
1464
+ const transformerKey = findTransformerPattern(file.type, transformers);
1465
+ if (transformerKey) {
1466
+ runTransformer(attachmentId, file, transformerKey);
1417
1467
  }
1418
1468
  }
1419
1469
  }, [maxFileSize, acceptedTypes, strings, transformers, runTransformer]);
@@ -1574,8 +1624,210 @@ function useDropdownState(options = {}) {
1574
1624
  };
1575
1625
  }
1576
1626
 
1627
+ // src/components/ToolApprovalDialog.tsx
1628
+ import { useState as useState4 } from "react";
1629
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1630
+ function ToolApprovalDialog({
1631
+ toolCallName,
1632
+ toolCallArgs,
1633
+ annotations,
1634
+ toolCount = 1,
1635
+ pendingTools = [],
1636
+ onApprove,
1637
+ onReject,
1638
+ theme,
1639
+ strings
1640
+ }) {
1641
+ const [showDetails, setShowDetails] = useState4(false);
1642
+ const displayName = annotations?.title || toolCallName;
1643
+ const isBatch = toolCount > 1;
1644
+ const message = isBatch ? strings.toolApproval.batchMessage?.replace("{count}", String(toolCount)) ?? `${toolCount} actions are waiting for your approval` : strings.toolApproval.message.replace("{toolName}", displayName);
1645
+ const getToolDisplayName = (tool) => tool.annotations?.title || tool.toolCallName;
1646
+ return /* @__PURE__ */ jsxs7(
1647
+ "div",
1648
+ {
1649
+ "data-testid": "tool-approval-dialog",
1650
+ style: {
1651
+ border: `2px solid ${theme.primaryColor}`,
1652
+ borderRadius: "12px",
1653
+ background: theme.backgroundColor,
1654
+ overflow: "hidden"
1655
+ },
1656
+ children: [
1657
+ /* @__PURE__ */ jsxs7(
1658
+ "div",
1659
+ {
1660
+ style: {
1661
+ padding: "12px 14px",
1662
+ borderBottom: `1px solid ${theme.borderColor}`,
1663
+ background: theme.assistantMessageBackground
1664
+ },
1665
+ children: [
1666
+ /* @__PURE__ */ jsxs7("div", { style: {
1667
+ display: "flex",
1668
+ alignItems: "center",
1669
+ gap: "8px",
1670
+ marginBottom: "4px"
1671
+ }, children: [
1672
+ /* @__PURE__ */ jsxs7("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: theme.primaryColor, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1673
+ /* @__PURE__ */ jsx10("path", { d: "M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" }),
1674
+ /* @__PURE__ */ jsx10("line", { x1: "12", y1: "9", x2: "12", y2: "13" }),
1675
+ /* @__PURE__ */ jsx10("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
1676
+ ] }),
1677
+ /* @__PURE__ */ jsx10("span", { style: {
1678
+ fontWeight: 600,
1679
+ fontSize: "14px",
1680
+ color: theme.textColor
1681
+ }, children: strings.toolApproval.title })
1682
+ ] }),
1683
+ /* @__PURE__ */ jsx10("div", { style: {
1684
+ fontSize: "13px",
1685
+ color: theme.secondaryTextColor,
1686
+ paddingLeft: "24px"
1687
+ }, children: message })
1688
+ ]
1689
+ }
1690
+ ),
1691
+ /* @__PURE__ */ jsxs7("div", { style: { padding: "8px 14px" }, children: [
1692
+ /* @__PURE__ */ jsxs7(
1693
+ "button",
1694
+ {
1695
+ onClick: () => setShowDetails(!showDetails),
1696
+ style: {
1697
+ background: "transparent",
1698
+ border: "none",
1699
+ padding: 0,
1700
+ cursor: "pointer",
1701
+ fontSize: "12px",
1702
+ color: theme.secondaryTextColor,
1703
+ display: "flex",
1704
+ alignItems: "center",
1705
+ gap: "4px"
1706
+ },
1707
+ children: [
1708
+ /* @__PURE__ */ jsx10(
1709
+ "svg",
1710
+ {
1711
+ width: "10",
1712
+ height: "10",
1713
+ viewBox: "0 0 12 12",
1714
+ fill: "none",
1715
+ style: {
1716
+ transform: showDetails ? "rotate(90deg)" : "rotate(0deg)",
1717
+ transition: "transform 0.15s"
1718
+ },
1719
+ children: /* @__PURE__ */ jsx10("path", { d: "M4 2L8 6L4 10", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
1720
+ }
1721
+ ),
1722
+ strings.toolApproval.showDetails
1723
+ ]
1724
+ }
1725
+ ),
1726
+ showDetails && /* @__PURE__ */ jsx10("div", { style: {
1727
+ marginTop: "6px",
1728
+ display: "flex",
1729
+ flexDirection: "column",
1730
+ gap: "8px",
1731
+ maxHeight: "200px",
1732
+ overflow: "auto"
1733
+ }, children: (pendingTools.length > 0 ? pendingTools : [{
1734
+ toolCallId: "single",
1735
+ toolCallName,
1736
+ toolCallArgs,
1737
+ annotations
1738
+ }]).map((tool, index) => /* @__PURE__ */ jsxs7(
1739
+ "div",
1740
+ {
1741
+ style: {
1742
+ padding: "8px",
1743
+ background: theme.hoverBackground,
1744
+ borderRadius: "6px"
1745
+ },
1746
+ children: [
1747
+ /* @__PURE__ */ jsx10("div", { style: {
1748
+ fontSize: "12px",
1749
+ fontWeight: 500,
1750
+ color: theme.textColor,
1751
+ marginBottom: "4px"
1752
+ }, children: getToolDisplayName(tool) }),
1753
+ /* @__PURE__ */ jsx10("pre", { style: {
1754
+ margin: 0,
1755
+ fontSize: "11px",
1756
+ overflow: "auto",
1757
+ color: theme.secondaryTextColor,
1758
+ whiteSpace: "pre-wrap",
1759
+ wordBreak: "break-word"
1760
+ }, children: JSON.stringify(tool.toolCallArgs, null, 2) })
1761
+ ]
1762
+ },
1763
+ tool.toolCallId
1764
+ )) })
1765
+ ] }),
1766
+ /* @__PURE__ */ jsxs7("div", { style: {
1767
+ display: "flex",
1768
+ gap: "8px",
1769
+ padding: "8px 14px 12px"
1770
+ }, children: [
1771
+ /* @__PURE__ */ jsx10(
1772
+ "button",
1773
+ {
1774
+ "data-testid": "approve-tool-button",
1775
+ onClick: onApprove,
1776
+ style: {
1777
+ flex: 1,
1778
+ padding: "8px 16px",
1779
+ borderRadius: "8px",
1780
+ border: "none",
1781
+ background: theme.primaryGradient,
1782
+ color: "white",
1783
+ fontWeight: 500,
1784
+ cursor: "pointer",
1785
+ fontSize: "13px",
1786
+ transition: "opacity 0.15s"
1787
+ },
1788
+ onMouseEnter: (e) => {
1789
+ e.currentTarget.style.opacity = "0.9";
1790
+ },
1791
+ onMouseLeave: (e) => {
1792
+ e.currentTarget.style.opacity = "1";
1793
+ },
1794
+ children: isBatch ? strings.toolApproval.approveAll ?? "Approve All" : strings.toolApproval.approve
1795
+ }
1796
+ ),
1797
+ /* @__PURE__ */ jsx10(
1798
+ "button",
1799
+ {
1800
+ "data-testid": "reject-tool-button",
1801
+ onClick: () => onReject(),
1802
+ style: {
1803
+ flex: 1,
1804
+ padding: "8px 16px",
1805
+ borderRadius: "8px",
1806
+ border: `1px solid ${theme.borderColor}`,
1807
+ background: "transparent",
1808
+ color: theme.textColor,
1809
+ fontWeight: 500,
1810
+ cursor: "pointer",
1811
+ fontSize: "13px",
1812
+ transition: "all 0.15s"
1813
+ },
1814
+ onMouseEnter: (e) => {
1815
+ e.currentTarget.style.background = theme.hoverBackground;
1816
+ },
1817
+ onMouseLeave: (e) => {
1818
+ e.currentTarget.style.background = "transparent";
1819
+ },
1820
+ children: isBatch ? strings.toolApproval.rejectAll ?? "Reject All" : strings.toolApproval.reject
1821
+ }
1822
+ )
1823
+ ] })
1824
+ ]
1825
+ }
1826
+ );
1827
+ }
1828
+
1577
1829
  // src/components/UseAIChatPanel.tsx
1578
- import { Fragment, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1830
+ import { Fragment, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
1579
1831
  function FeedbackButton({ type, isSelected, onClick, selectedColor, unselectedColor }) {
1580
1832
  const buttonRef = useRef4(null);
1581
1833
  const handleClick = () => {
@@ -1591,7 +1843,7 @@ function FeedbackButton({ type, isSelected, onClick, selectedColor, unselectedCo
1591
1843
  };
1592
1844
  const thumbsUpPath = "M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3";
1593
1845
  const thumbsDownPath = "M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17";
1594
- return /* @__PURE__ */ jsx10(
1846
+ return /* @__PURE__ */ jsx11(
1595
1847
  "button",
1596
1848
  {
1597
1849
  ref: buttonRef,
@@ -1624,7 +1876,7 @@ function FeedbackButton({ type, isSelected, onClick, selectedColor, unselectedCo
1624
1876
  e.currentTarget.style.color = unselectedColor;
1625
1877
  }
1626
1878
  },
1627
- children: /* @__PURE__ */ jsx10(
1879
+ children: /* @__PURE__ */ jsx11(
1628
1880
  "svg",
1629
1881
  {
1630
1882
  width: "14",
@@ -1635,7 +1887,7 @@ function FeedbackButton({ type, isSelected, onClick, selectedColor, unselectedCo
1635
1887
  strokeWidth: "2",
1636
1888
  strokeLinecap: "round",
1637
1889
  strokeLinejoin: "round",
1638
- children: /* @__PURE__ */ jsx10("path", { d: type === "upvote" ? thumbsUpPath : thumbsDownPath })
1890
+ children: /* @__PURE__ */ jsx11("path", { d: type === "upvote" ? thumbsUpPath : thumbsDownPath })
1639
1891
  }
1640
1892
  )
1641
1893
  }
@@ -1676,18 +1928,21 @@ function UseAIChatPanel({
1676
1928
  closeButton,
1677
1929
  executingTool,
1678
1930
  feedbackEnabled,
1679
- onFeedback
1931
+ onFeedback,
1932
+ pendingApprovals = [],
1933
+ onApproveToolCall,
1934
+ onRejectToolCall
1680
1935
  }) {
1681
1936
  const strings = useStrings();
1682
1937
  const theme = useTheme();
1683
- const [input, setInput] = useState4("");
1938
+ const [input, setInput] = useState5("");
1684
1939
  const chatHistoryDropdown = useDropdownState();
1685
1940
  const agentDropdown = useDropdownState();
1686
- const [chatHistory, setChatHistory] = useState4([]);
1941
+ const [chatHistory, setChatHistory] = useState5([]);
1687
1942
  const messagesEndRef = useRef4(null);
1688
- const [displayedSuggestions, setDisplayedSuggestions] = useState4([]);
1943
+ const [displayedSuggestions, setDisplayedSuggestions] = useState5([]);
1689
1944
  const textareaRef = useRef4(null);
1690
- const [hoveredMessageId, setHoveredMessageId] = useState4(null);
1945
+ const [hoveredMessageId, setHoveredMessageId] = useState5(null);
1691
1946
  const {
1692
1947
  attachments,
1693
1948
  fileError,
@@ -1774,7 +2029,7 @@ function UseAIChatPanel({
1774
2029
  chatHistoryDropdown.close();
1775
2030
  }
1776
2031
  };
1777
- return /* @__PURE__ */ jsxs7(
2032
+ return /* @__PURE__ */ jsxs8(
1778
2033
  "div",
1779
2034
  {
1780
2035
  onClick: () => {
@@ -1792,7 +2047,7 @@ function UseAIChatPanel({
1792
2047
  },
1793
2048
  children: [
1794
2049
  DropZoneOverlay,
1795
- /* @__PURE__ */ jsxs7(
2050
+ /* @__PURE__ */ jsxs8(
1796
2051
  "div",
1797
2052
  {
1798
2053
  style: {
@@ -1805,7 +2060,7 @@ function UseAIChatPanel({
1805
2060
  gap: "12px"
1806
2061
  },
1807
2062
  children: [
1808
- /* @__PURE__ */ jsx10("div", { style: { flex: 1, minWidth: 0, position: "relative" }, children: onListChats ? /* @__PURE__ */ jsxs7(
2063
+ /* @__PURE__ */ jsx11("div", { style: { flex: 1, minWidth: 0, position: "relative" }, children: onListChats ? /* @__PURE__ */ jsxs8(
1809
2064
  "button",
1810
2065
  {
1811
2066
  "data-testid": "chat-history-dropdown-button",
@@ -1838,7 +2093,7 @@ function UseAIChatPanel({
1838
2093
  e.currentTarget.style.background = "transparent";
1839
2094
  },
1840
2095
  children: [
1841
- /* @__PURE__ */ jsx10("span", { style: {
2096
+ /* @__PURE__ */ jsx11("span", { style: {
1842
2097
  overflow: "hidden",
1843
2098
  textOverflow: "ellipsis",
1844
2099
  whiteSpace: "nowrap",
@@ -1855,13 +2110,13 @@ function UseAIChatPanel({
1855
2110
  }
1856
2111
  return strings.header.newChat;
1857
2112
  })() }),
1858
- /* @__PURE__ */ jsx10("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx10("path", { d: "M3 4.5L6 7.5L9 4.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
2113
+ /* @__PURE__ */ jsx11("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx11("path", { d: "M3 4.5L6 7.5L9 4.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
1859
2114
  ]
1860
2115
  }
1861
- ) : /* @__PURE__ */ jsx10("div", { style: { fontSize: "14px", fontWeight: "600", color: theme.textColor, padding: "6px 8px" }, children: strings.header.aiAssistant }) }),
1862
- /* @__PURE__ */ jsxs7("div", { style: { display: "flex", alignItems: "center", gap: "4px" }, children: [
1863
- availableAgents && availableAgents.length > 1 && onAgentChange && /* @__PURE__ */ jsxs7("div", { style: { position: "relative" }, children: [
1864
- /* @__PURE__ */ jsxs7(
2116
+ ) : /* @__PURE__ */ jsx11("div", { style: { fontSize: "14px", fontWeight: "600", color: theme.textColor, padding: "6px 8px" }, children: strings.header.aiAssistant }) }),
2117
+ /* @__PURE__ */ jsxs8("div", { style: { display: "flex", alignItems: "center", gap: "4px" }, children: [
2118
+ availableAgents && availableAgents.length > 1 && onAgentChange && /* @__PURE__ */ jsxs8("div", { style: { position: "relative" }, children: [
2119
+ /* @__PURE__ */ jsxs8(
1865
2120
  "button",
1866
2121
  {
1867
2122
  "data-testid": "agent-selector",
@@ -1890,7 +2145,7 @@ function UseAIChatPanel({
1890
2145
  },
1891
2146
  title: "Select AI model",
1892
2147
  children: [
1893
- /* @__PURE__ */ jsx10("span", { style: {
2148
+ /* @__PURE__ */ jsx11("span", { style: {
1894
2149
  overflow: "hidden",
1895
2150
  textOverflow: "ellipsis",
1896
2151
  whiteSpace: "nowrap",
@@ -1899,11 +2154,11 @@ function UseAIChatPanel({
1899
2154
  const agent = availableAgents.find((a) => a.id === (selectedAgent ?? defaultAgent));
1900
2155
  return agent?.name || "AI";
1901
2156
  })() }),
1902
- /* @__PURE__ */ jsx10("svg", { width: "10", height: "10", viewBox: "0 0 12 12", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx10("path", { d: "M3 4.5L6 7.5L9 4.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
2157
+ /* @__PURE__ */ jsx11("svg", { width: "10", height: "10", viewBox: "0 0 12 12", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx11("path", { d: "M3 4.5L6 7.5L9 4.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
1903
2158
  ]
1904
2159
  }
1905
2160
  ),
1906
- agentDropdown.isOpen && /* @__PURE__ */ jsx10(
2161
+ agentDropdown.isOpen && /* @__PURE__ */ jsx11(
1907
2162
  "div",
1908
2163
  {
1909
2164
  style: {
@@ -1923,7 +2178,7 @@ function UseAIChatPanel({
1923
2178
  },
1924
2179
  children: availableAgents.map((agent) => {
1925
2180
  const isSelected = agent.id === (selectedAgent ?? defaultAgent);
1926
- return /* @__PURE__ */ jsxs7(
2181
+ return /* @__PURE__ */ jsxs8(
1927
2182
  "div",
1928
2183
  {
1929
2184
  "data-testid": "agent-option",
@@ -1953,13 +2208,13 @@ function UseAIChatPanel({
1953
2208
  }
1954
2209
  },
1955
2210
  children: [
1956
- /* @__PURE__ */ jsxs7("div", { style: { flex: 1, minWidth: 0 }, children: [
1957
- /* @__PURE__ */ jsx10("div", { style: {
2211
+ /* @__PURE__ */ jsxs8("div", { style: { flex: 1, minWidth: 0 }, children: [
2212
+ /* @__PURE__ */ jsx11("div", { style: {
1958
2213
  fontSize: "13px",
1959
2214
  fontWeight: isSelected ? "600" : "500",
1960
2215
  color: isSelected ? theme.primaryColor : theme.textColor
1961
2216
  }, children: agent.name }),
1962
- agent.annotation && /* @__PURE__ */ jsx10("div", { style: {
2217
+ agent.annotation && /* @__PURE__ */ jsx11("div", { style: {
1963
2218
  fontSize: "11px",
1964
2219
  color: theme.secondaryTextColor,
1965
2220
  marginTop: "2px",
@@ -1968,7 +2223,7 @@ function UseAIChatPanel({
1968
2223
  whiteSpace: "nowrap"
1969
2224
  }, children: agent.annotation })
1970
2225
  ] }),
1971
- isSelected && /* @__PURE__ */ jsx10("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx10("path", { d: "M2 7L5.5 10.5L12 4", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
2226
+ isSelected && /* @__PURE__ */ jsx11("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx11("path", { d: "M2 7L5.5 10.5L12 4", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
1972
2227
  ]
1973
2228
  },
1974
2229
  agent.id
@@ -1977,7 +2232,7 @@ function UseAIChatPanel({
1977
2232
  }
1978
2233
  )
1979
2234
  ] }),
1980
- onNewChat && /* @__PURE__ */ jsx10(
2235
+ onNewChat && /* @__PURE__ */ jsx11(
1981
2236
  "button",
1982
2237
  {
1983
2238
  "data-testid": "new-chat-button",
@@ -2004,10 +2259,10 @@ function UseAIChatPanel({
2004
2259
  e.currentTarget.style.color = theme.secondaryTextColor;
2005
2260
  },
2006
2261
  title: strings.header.newChat,
2007
- children: /* @__PURE__ */ jsx10("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx10("path", { d: "M8 3.5V12.5M3.5 8H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) })
2262
+ 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" }) })
2008
2263
  }
2009
2264
  ),
2010
- onDeleteChat && messages.length > 0 && /* @__PURE__ */ jsx10(
2265
+ onDeleteChat && messages.length > 0 && /* @__PURE__ */ jsx11(
2011
2266
  "button",
2012
2267
  {
2013
2268
  "data-testid": "delete-chat-button",
@@ -2031,7 +2286,7 @@ function UseAIChatPanel({
2031
2286
  e.currentTarget.style.color = theme.secondaryTextColor;
2032
2287
  },
2033
2288
  title: strings.header.deleteChat,
2034
- children: /* @__PURE__ */ jsx10("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx10("path", { d: "M2 4H14M6.5 7V11M9.5 7V11M3 4L4 13C4 13.5304 4.21071 14.0391 4.58579 14.4142C4.96086 14.7893 5.46957 15 6 15H10C10.5304 15 11.0391 14.7893 11.4142 14.4142C11.7893 14.0391 12 13.5304 12 13L13 4M5.5 4V2.5C5.5 2.23478 5.60536 1.98043 5.79289 1.79289C5.98043 1.60536 6.23478 1.5 6.5 1.5H9.5C9.76522 1.5 10.0196 1.60536 10.2071 1.79289C10.3946 1.98043 10.5 2.23478 10.5 2.5V4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
2289
+ children: /* @__PURE__ */ jsx11("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx11("path", { d: "M2 4H14M6.5 7V11M9.5 7V11M3 4L4 13C4 13.5304 4.21071 14.0391 4.58579 14.4142C4.96086 14.7893 5.46957 15 6 15H10C10.5304 15 11.0391 14.7893 11.4142 14.4142C11.7893 14.0391 12 13.5304 12 13L13 4M5.5 4V2.5C5.5 2.23478 5.60536 1.98043 5.79289 1.79289C5.98043 1.60536 6.23478 1.5 6.5 1.5H9.5C9.76522 1.5 10.0196 1.60536 10.2071 1.79289C10.3946 1.98043 10.5 2.23478 10.5 2.5V4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
2035
2290
  }
2036
2291
  ),
2037
2292
  closeButton
@@ -2039,7 +2294,7 @@ function UseAIChatPanel({
2039
2294
  ]
2040
2295
  }
2041
2296
  ),
2042
- chatHistoryDropdown.isOpen && onListChats && /* @__PURE__ */ jsx10(
2297
+ chatHistoryDropdown.isOpen && onListChats && /* @__PURE__ */ jsx11(
2043
2298
  "div",
2044
2299
  {
2045
2300
  style: {
@@ -2056,7 +2311,7 @@ function UseAIChatPanel({
2056
2311
  flexDirection: "column",
2057
2312
  overflow: "hidden"
2058
2313
  },
2059
- children: /* @__PURE__ */ jsx10(
2314
+ children: /* @__PURE__ */ jsx11(
2060
2315
  "div",
2061
2316
  {
2062
2317
  style: {
@@ -2064,7 +2319,7 @@ function UseAIChatPanel({
2064
2319
  overflowY: "auto",
2065
2320
  padding: "8px"
2066
2321
  },
2067
- children: chatHistory.length === 0 ? /* @__PURE__ */ jsx10(
2322
+ children: chatHistory.length === 0 ? /* @__PURE__ */ jsx11(
2068
2323
  "div",
2069
2324
  {
2070
2325
  style: {
@@ -2073,9 +2328,9 @@ function UseAIChatPanel({
2073
2328
  padding: "32px 16px",
2074
2329
  fontSize: "13px"
2075
2330
  },
2076
- children: /* @__PURE__ */ jsx10("p", { style: { margin: 0 }, children: strings.chatHistory.noChatHistory })
2331
+ children: /* @__PURE__ */ jsx11("p", { style: { margin: 0 }, children: strings.chatHistory.noChatHistory })
2077
2332
  }
2078
- ) : chatHistory.map((chat) => /* @__PURE__ */ jsxs7(
2333
+ ) : chatHistory.map((chat) => /* @__PURE__ */ jsxs8(
2079
2334
  "div",
2080
2335
  {
2081
2336
  "data-testid": "chat-history-item",
@@ -2099,10 +2354,10 @@ function UseAIChatPanel({
2099
2354
  }
2100
2355
  },
2101
2356
  children: [
2102
- /* @__PURE__ */ jsx10("div", { style: { fontSize: "13px", fontWeight: "500", color: theme.textColor, marginBottom: "4px", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: chat.title || strings.header.newChat }),
2103
- /* @__PURE__ */ jsxs7("div", { style: { fontSize: "11px", color: theme.secondaryTextColor }, children: [
2357
+ /* @__PURE__ */ jsx11("div", { style: { fontSize: "13px", fontWeight: "500", color: theme.textColor, marginBottom: "4px", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: chat.title || strings.header.newChat }),
2358
+ /* @__PURE__ */ jsxs8("div", { style: { fontSize: "11px", color: theme.secondaryTextColor }, children: [
2104
2359
  new Date(chat.updatedAt).toLocaleDateString([], { month: "short", day: "numeric" }),
2105
- currentChatId === chat.id && /* @__PURE__ */ jsxs7("span", { style: {
2360
+ currentChatId === chat.id && /* @__PURE__ */ jsxs8("span", { style: {
2106
2361
  marginLeft: "8px",
2107
2362
  color: theme.primaryColor,
2108
2363
  fontWeight: "600"
@@ -2121,7 +2376,7 @@ function UseAIChatPanel({
2121
2376
  ),
2122
2377
  chatHistoryDropdown.Backdrop,
2123
2378
  agentDropdown.Backdrop,
2124
- /* @__PURE__ */ jsxs7(
2379
+ /* @__PURE__ */ jsxs8(
2125
2380
  "div",
2126
2381
  {
2127
2382
  style: {
@@ -2133,7 +2388,7 @@ function UseAIChatPanel({
2133
2388
  gap: "12px"
2134
2389
  },
2135
2390
  children: [
2136
- messages.length === 0 && /* @__PURE__ */ jsxs7(
2391
+ messages.length === 0 && /* @__PURE__ */ jsxs8(
2137
2392
  "div",
2138
2393
  {
2139
2394
  style: {
@@ -2144,12 +2399,12 @@ function UseAIChatPanel({
2144
2399
  gap: "20px"
2145
2400
  },
2146
2401
  children: [
2147
- /* @__PURE__ */ jsxs7("div", { style: { textAlign: "center", color: theme.secondaryTextColor, fontSize: "14px" }, children: [
2148
- /* @__PURE__ */ jsx10("p", { style: { margin: 0, fontSize: "32px", marginBottom: "12px" }, children: "\u{1F4AC}" }),
2149
- /* @__PURE__ */ jsx10("p", { style: { margin: 0 }, children: strings.emptyChat.startConversation }),
2150
- /* @__PURE__ */ jsx10("p", { style: { margin: "8px 0 0", fontSize: "12px" }, children: strings.emptyChat.askMeToHelp })
2402
+ /* @__PURE__ */ jsxs8("div", { style: { textAlign: "center", color: theme.secondaryTextColor, fontSize: "14px" }, children: [
2403
+ /* @__PURE__ */ jsx11("p", { style: { margin: 0, fontSize: "32px", marginBottom: "12px" }, children: "\u{1F4AC}" }),
2404
+ /* @__PURE__ */ jsx11("p", { style: { margin: 0 }, children: strings.emptyChat.startConversation }),
2405
+ /* @__PURE__ */ jsx11("p", { style: { margin: "8px 0 0", fontSize: "12px" }, children: strings.emptyChat.askMeToHelp })
2151
2406
  ] }),
2152
- displayedSuggestions.length > 0 && /* @__PURE__ */ jsx10(
2407
+ displayedSuggestions.length > 0 && /* @__PURE__ */ jsx11(
2153
2408
  "div",
2154
2409
  {
2155
2410
  style: {
@@ -2159,7 +2414,7 @@ function UseAIChatPanel({
2159
2414
  width: "100%",
2160
2415
  maxWidth: "320px"
2161
2416
  },
2162
- children: displayedSuggestions.map((suggestion, index) => /* @__PURE__ */ jsx10(
2417
+ children: displayedSuggestions.map((suggestion, index) => /* @__PURE__ */ jsx11(
2163
2418
  "button",
2164
2419
  {
2165
2420
  "data-testid": "chat-suggestion-button",
@@ -2203,7 +2458,7 @@ function UseAIChatPanel({
2203
2458
  ]
2204
2459
  }
2205
2460
  ),
2206
- messages.map((message) => /* @__PURE__ */ jsxs7(
2461
+ messages.map((message) => /* @__PURE__ */ jsxs8(
2207
2462
  "div",
2208
2463
  {
2209
2464
  "data-testid": `chat-message-${message.role}`,
@@ -2216,7 +2471,7 @@ function UseAIChatPanel({
2216
2471
  onMouseEnter: () => message.role === "user" && setHoveredMessageId(message.id),
2217
2472
  onMouseLeave: () => setHoveredMessageId(null),
2218
2473
  children: [
2219
- /* @__PURE__ */ jsxs7(
2474
+ /* @__PURE__ */ jsxs8(
2220
2475
  "div",
2221
2476
  {
2222
2477
  style: {
@@ -2224,7 +2479,7 @@ function UseAIChatPanel({
2224
2479
  maxWidth: "80%"
2225
2480
  },
2226
2481
  children: [
2227
- message.role === "user" && hoveredMessageId === message.id && onSaveCommand && !slashCommands.isSavingCommand(message.id) && /* @__PURE__ */ jsx10(
2482
+ message.role === "user" && hoveredMessageId === message.id && onSaveCommand && !slashCommands.isSavingCommand(message.id) && /* @__PURE__ */ jsx11(
2228
2483
  "button",
2229
2484
  {
2230
2485
  "data-testid": "save-command-button",
@@ -2260,14 +2515,14 @@ function UseAIChatPanel({
2260
2515
  e.currentTarget.style.transform = "scale(1)";
2261
2516
  e.currentTarget.style.boxShadow = "0 2px 6px rgba(0, 0, 0, 0.15)";
2262
2517
  },
2263
- children: /* @__PURE__ */ jsxs7("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2264
- /* @__PURE__ */ jsx10("path", { d: "M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" }),
2265
- /* @__PURE__ */ jsx10("polyline", { points: "17 21 17 13 7 13 7 21" }),
2266
- /* @__PURE__ */ jsx10("polyline", { points: "7 3 7 8 15 8" })
2518
+ children: /* @__PURE__ */ jsxs8("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2519
+ /* @__PURE__ */ jsx11("path", { d: "M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" }),
2520
+ /* @__PURE__ */ jsx11("polyline", { points: "17 21 17 13 7 13 7 21" }),
2521
+ /* @__PURE__ */ jsx11("polyline", { points: "7 3 7 8 15 8" })
2267
2522
  ] })
2268
2523
  }
2269
2524
  ),
2270
- /* @__PURE__ */ jsxs7(
2525
+ /* @__PURE__ */ jsxs8(
2271
2526
  "div",
2272
2527
  {
2273
2528
  "data-testid": "chat-message-content",
@@ -2282,7 +2537,7 @@ function UseAIChatPanel({
2282
2537
  wordWrap: "break-word"
2283
2538
  },
2284
2539
  children: [
2285
- message.role === "user" && hasFileContent(message.content) && /* @__PURE__ */ jsx10("div", { style: { display: "flex", flexWrap: "wrap", gap: "6px", marginBottom: "8px" }, children: message.content.filter((part) => part.type === "file").map((part, idx) => /* @__PURE__ */ jsx10(
2540
+ message.role === "user" && hasFileContent(message.content) && /* @__PURE__ */ jsx11("div", { style: { display: "flex", flexWrap: "wrap", gap: "6px", marginBottom: "8px" }, children: message.content.filter((part) => part.type === "file").map((part, idx) => /* @__PURE__ */ jsx11(
2286
2541
  FilePlaceholder,
2287
2542
  {
2288
2543
  name: part.file.name,
@@ -2290,7 +2545,7 @@ function UseAIChatPanel({
2290
2545
  },
2291
2546
  idx
2292
2547
  )) }),
2293
- message.role === "assistant" ? /* @__PURE__ */ jsx10(MarkdownContent, { content: getTextContent(message.content) }) : getTextContent(message.content)
2548
+ message.role === "assistant" ? /* @__PURE__ */ jsx11(MarkdownContent, { content: getTextContent(message.content) }) : getTextContent(message.content)
2294
2549
  ]
2295
2550
  }
2296
2551
  ),
@@ -2301,7 +2556,7 @@ function UseAIChatPanel({
2301
2556
  ]
2302
2557
  }
2303
2558
  ),
2304
- message.role === "assistant" && message.traceId && feedbackEnabled && onFeedback && /* @__PURE__ */ jsxs7(
2559
+ message.role === "assistant" && message.traceId && feedbackEnabled && onFeedback && /* @__PURE__ */ jsxs8(
2305
2560
  "div",
2306
2561
  {
2307
2562
  "data-testid": "feedback-buttons",
@@ -2312,7 +2567,7 @@ function UseAIChatPanel({
2312
2567
  padding: "0 4px"
2313
2568
  },
2314
2569
  children: [
2315
- /* @__PURE__ */ jsx10(
2570
+ /* @__PURE__ */ jsx11(
2316
2571
  FeedbackButton,
2317
2572
  {
2318
2573
  type: "upvote",
@@ -2325,7 +2580,7 @@ function UseAIChatPanel({
2325
2580
  unselectedColor: theme.secondaryTextColor
2326
2581
  }
2327
2582
  ),
2328
- /* @__PURE__ */ jsx10(
2583
+ /* @__PURE__ */ jsx11(
2329
2584
  FeedbackButton,
2330
2585
  {
2331
2586
  type: "downvote",
@@ -2341,7 +2596,7 @@ function UseAIChatPanel({
2341
2596
  ]
2342
2597
  }
2343
2598
  ),
2344
- /* @__PURE__ */ jsx10(
2599
+ /* @__PURE__ */ jsx11(
2345
2600
  "div",
2346
2601
  {
2347
2602
  style: {
@@ -2360,14 +2615,14 @@ function UseAIChatPanel({
2360
2615
  },
2361
2616
  message.id
2362
2617
  )),
2363
- loading && /* @__PURE__ */ jsx10(
2618
+ loading && /* @__PURE__ */ jsx11(
2364
2619
  "div",
2365
2620
  {
2366
2621
  style: {
2367
2622
  display: "flex",
2368
2623
  alignItems: "flex-start"
2369
2624
  },
2370
- children: /* @__PURE__ */ jsx10(
2625
+ children: /* @__PURE__ */ jsx11(
2371
2626
  "div",
2372
2627
  {
2373
2628
  className: "markdown-content",
@@ -2380,20 +2635,20 @@ function UseAIChatPanel({
2380
2635
  color: theme.textColor,
2381
2636
  maxWidth: "80%"
2382
2637
  },
2383
- children: streamingText ? /* @__PURE__ */ jsx10(MarkdownContent, { content: streamingText }) : fileProcessing && fileProcessing.status === "processing" ? /* @__PURE__ */ jsxs7("div", { children: [
2384
- /* @__PURE__ */ jsx10("span", { style: { opacity: 0.6 }, children: strings.input.processingFile }),
2385
- fileProcessing.progress != null && /* @__PURE__ */ jsxs7(Fragment, { children: [
2386
- /* @__PURE__ */ jsxs7("span", { style: { opacity: 0.6, marginLeft: "4px" }, children: [
2638
+ children: streamingText ? /* @__PURE__ */ jsx11(MarkdownContent, { content: streamingText }) : fileProcessing && fileProcessing.status === "processing" ? /* @__PURE__ */ jsxs8("div", { children: [
2639
+ /* @__PURE__ */ jsx11("span", { style: { opacity: 0.6 }, children: strings.input.processingFile }),
2640
+ fileProcessing.progress != null && /* @__PURE__ */ jsxs8(Fragment, { children: [
2641
+ /* @__PURE__ */ jsxs8("span", { style: { opacity: 0.6, marginLeft: "4px" }, children: [
2387
2642
  Math.round(fileProcessing.progress),
2388
2643
  "%"
2389
2644
  ] }),
2390
- /* @__PURE__ */ jsx10("div", { style: {
2645
+ /* @__PURE__ */ jsx11("div", { style: {
2391
2646
  marginTop: "6px",
2392
2647
  height: "4px",
2393
2648
  borderRadius: "2px",
2394
2649
  background: theme.borderColor,
2395
2650
  overflow: "hidden"
2396
- }, children: /* @__PURE__ */ jsx10("div", { style: {
2651
+ }, children: /* @__PURE__ */ jsx11("div", { style: {
2397
2652
  height: "100%",
2398
2653
  width: `${fileProcessing.progress}%`,
2399
2654
  borderRadius: "2px",
@@ -2401,17 +2656,17 @@ function UseAIChatPanel({
2401
2656
  transition: "width 0.3s ease"
2402
2657
  } }) })
2403
2658
  ] }),
2404
- fileProcessing.progress == null && /* @__PURE__ */ jsx10("span", { className: "dots", style: { marginLeft: "4px" }, children: "..." })
2405
- ] }) : /* @__PURE__ */ jsx10("span", { className: "dots", style: { opacity: 0.6 }, children: "..." })
2659
+ fileProcessing.progress == null && /* @__PURE__ */ jsx11("span", { className: "dots", style: { marginLeft: "4px" }, children: "..." })
2660
+ ] }) : /* @__PURE__ */ jsx11("span", { className: "dots", style: { opacity: 0.6 }, children: "..." })
2406
2661
  }
2407
2662
  )
2408
2663
  }
2409
2664
  ),
2410
- /* @__PURE__ */ jsx10("div", { ref: messagesEndRef })
2665
+ /* @__PURE__ */ jsx11("div", { ref: messagesEndRef })
2411
2666
  ]
2412
2667
  }
2413
2668
  ),
2414
- /* @__PURE__ */ jsxs7(
2669
+ /* @__PURE__ */ jsxs8(
2415
2670
  "div",
2416
2671
  {
2417
2672
  style: {
@@ -2419,7 +2674,7 @@ function UseAIChatPanel({
2419
2674
  borderTop: `1px solid ${theme.borderColor}`
2420
2675
  },
2421
2676
  children: [
2422
- fileError && /* @__PURE__ */ jsx10(
2677
+ fileError && /* @__PURE__ */ jsx11(
2423
2678
  "div",
2424
2679
  {
2425
2680
  "data-testid": "file-error",
@@ -2434,7 +2689,7 @@ function UseAIChatPanel({
2434
2689
  children: fileError
2435
2690
  }
2436
2691
  ),
2437
- attachments.length > 0 && /* @__PURE__ */ jsx10(
2692
+ attachments.length > 0 && /* @__PURE__ */ jsx11(
2438
2693
  "div",
2439
2694
  {
2440
2695
  "data-testid": "file-attachments",
@@ -2444,7 +2699,7 @@ function UseAIChatPanel({
2444
2699
  gap: "8px",
2445
2700
  marginBottom: "8px"
2446
2701
  },
2447
- children: attachments.map((attachment) => /* @__PURE__ */ jsx10(
2702
+ children: attachments.map((attachment) => /* @__PURE__ */ jsx11(
2448
2703
  FileChip,
2449
2704
  {
2450
2705
  attachment,
@@ -2456,143 +2711,159 @@ function UseAIChatPanel({
2456
2711
  ))
2457
2712
  }
2458
2713
  ),
2459
- /* @__PURE__ */ jsxs7(
2460
- "div",
2714
+ /* @__PURE__ */ jsx11(
2715
+ "input",
2461
2716
  {
2462
- style: {
2463
- border: `1px solid ${theme.borderColor}`,
2464
- borderRadius: "12px",
2465
- background: theme.backgroundColor,
2466
- overflow: "hidden",
2467
- position: "relative"
2468
- },
2469
- children: [
2470
- slashCommands.AutocompleteComponent,
2471
- /* @__PURE__ */ jsx10(
2472
- "input",
2473
- {
2474
- ref: fileInputRef,
2475
- type: "file",
2476
- multiple: true,
2477
- "data-testid": "file-input",
2478
- style: { display: "none" },
2479
- onChange: handleFileInputChange,
2480
- accept: acceptedTypes?.join(",")
2481
- }
2482
- ),
2483
- /* @__PURE__ */ jsx10(
2484
- "textarea",
2485
- {
2486
- ref: textareaRef,
2487
- "data-testid": "chat-input",
2488
- className: "chat-input",
2489
- value: input,
2490
- onChange: handleInputChange,
2491
- onKeyDown: handleKeyDown,
2492
- placeholder: !connected ? strings.input.connectingPlaceholder : loading ? `${executingTool?.displayText ?? strings.input.thinking}...` : strings.input.placeholder,
2493
- disabled: !connected || loading,
2494
- rows: 1,
2495
- style: {
2496
- width: "100%",
2497
- padding: "10px 14px 6px",
2498
- border: "none",
2499
- fontSize: "14px",
2500
- lineHeight: "1.4",
2501
- resize: "none",
2502
- maxHeight: `${maxTextareaHeight}px`,
2503
- fontFamily: "inherit",
2504
- outline: "none",
2505
- background: "transparent",
2506
- overflowY: "auto",
2507
- boxSizing: "border-box"
2508
- }
2509
- }
2510
- ),
2511
- /* @__PURE__ */ jsxs7(
2512
- "div",
2513
- {
2514
- style: {
2515
- display: "flex",
2516
- alignItems: "center",
2517
- justifyContent: "space-between",
2518
- padding: "4px 8px"
2519
- },
2520
- children: [
2521
- /* @__PURE__ */ jsx10("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: fileUploadEnabled && /* @__PURE__ */ jsx10(
2522
- "button",
2523
- {
2524
- "data-testid": "file-picker-button",
2525
- onClick: openFilePicker,
2526
- disabled: !connected || loading,
2527
- style: {
2528
- padding: "4px",
2529
- background: "transparent",
2530
- border: `1px solid ${theme.borderColor}`,
2531
- borderRadius: "50%",
2532
- cursor: connected && !loading ? "pointer" : "not-allowed",
2533
- color: theme.secondaryTextColor,
2534
- display: "flex",
2535
- alignItems: "center",
2536
- justifyContent: "center",
2537
- width: "28px",
2538
- height: "28px",
2539
- transition: "all 0.15s",
2540
- opacity: connected && !loading ? 1 : 0.5
2541
- },
2542
- onMouseEnter: (e) => {
2543
- if (connected && !loading) {
2544
- e.currentTarget.style.color = theme.primaryColor;
2545
- e.currentTarget.style.borderColor = theme.primaryColor;
2546
- }
2547
- },
2548
- onMouseLeave: (e) => {
2549
- e.currentTarget.style.color = theme.secondaryTextColor;
2550
- e.currentTarget.style.borderColor = theme.borderColor;
2551
- },
2552
- title: strings.fileUpload.attachFiles,
2553
- children: /* @__PURE__ */ jsxs7("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
2554
- /* @__PURE__ */ jsx10("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
2555
- /* @__PURE__ */ jsx10("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
2556
- ] })
2557
- }
2558
- ) }),
2559
- /* @__PURE__ */ jsx10(
2560
- "button",
2561
- {
2562
- "data-testid": "chat-send-button",
2563
- className: "chat-send-button",
2564
- onClick: handleSend,
2565
- disabled: !connected || loading || !input.trim() && attachments.length === 0,
2566
- style: {
2567
- padding: "6px",
2568
- background: connected && !loading && (input.trim() || attachments.length > 0) ? theme.primaryGradient : theme.buttonDisabledBackground,
2569
- color: connected && !loading && (input.trim() || attachments.length > 0) ? "white" : theme.secondaryTextColor,
2570
- border: "none",
2571
- borderRadius: "50%",
2572
- cursor: connected && !loading && (input.trim() || attachments.length > 0) ? "pointer" : "not-allowed",
2573
- display: "flex",
2574
- alignItems: "center",
2575
- justifyContent: "center",
2576
- width: "32px",
2577
- height: "32px",
2578
- transition: "all 0.2s"
2579
- },
2580
- children: /* @__PURE__ */ jsxs7("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
2581
- /* @__PURE__ */ jsx10("line", { x1: "12", y1: "19", x2: "12", y2: "5" }),
2582
- /* @__PURE__ */ jsx10("polyline", { points: "5 12 12 5 19 12" })
2583
- ] })
2584
- }
2585
- )
2586
- ]
2587
- }
2588
- )
2589
- ]
2717
+ ref: fileInputRef,
2718
+ type: "file",
2719
+ multiple: true,
2720
+ "data-testid": "file-input",
2721
+ style: { display: "none" },
2722
+ onChange: handleFileInputChange,
2723
+ accept: acceptedTypes?.join(",")
2724
+ }
2725
+ ),
2726
+ pendingApprovals.length > 0 && onApproveToolCall && onRejectToolCall ? /* @__PURE__ */ jsx11(
2727
+ ToolApprovalDialog,
2728
+ {
2729
+ toolCallName: pendingApprovals[0].toolCallName,
2730
+ toolCallArgs: pendingApprovals[0].toolCallArgs,
2731
+ annotations: pendingApprovals[0].annotations,
2732
+ toolCount: pendingApprovals.length,
2733
+ pendingTools: pendingApprovals,
2734
+ onApprove: onApproveToolCall,
2735
+ onReject: onRejectToolCall,
2736
+ theme,
2737
+ strings
2590
2738
  }
2739
+ ) : (
2740
+ /* Input container - single border around everything */
2741
+ /* @__PURE__ */ jsxs8(
2742
+ "div",
2743
+ {
2744
+ style: {
2745
+ border: `1px solid ${theme.borderColor}`,
2746
+ borderRadius: "12px",
2747
+ background: theme.backgroundColor,
2748
+ overflow: "hidden",
2749
+ position: "relative"
2750
+ },
2751
+ children: [
2752
+ slashCommands.AutocompleteComponent,
2753
+ /* @__PURE__ */ jsx11(
2754
+ "textarea",
2755
+ {
2756
+ ref: textareaRef,
2757
+ "data-testid": "chat-input",
2758
+ className: "chat-input",
2759
+ value: input,
2760
+ onChange: handleInputChange,
2761
+ onKeyDown: handleKeyDown,
2762
+ placeholder: !connected ? strings.input.connectingPlaceholder : loading ? `${executingTool?.displayText ?? strings.input.thinking}...` : strings.input.placeholder,
2763
+ disabled: !connected || loading || pendingApprovals.length > 0,
2764
+ rows: 1,
2765
+ style: {
2766
+ width: "100%",
2767
+ padding: "10px 14px 6px",
2768
+ border: "none",
2769
+ fontSize: "14px",
2770
+ lineHeight: "1.4",
2771
+ resize: "none",
2772
+ maxHeight: `${maxTextareaHeight}px`,
2773
+ fontFamily: "inherit",
2774
+ outline: "none",
2775
+ background: "transparent",
2776
+ overflowY: "auto",
2777
+ boxSizing: "border-box"
2778
+ }
2779
+ }
2780
+ ),
2781
+ /* @__PURE__ */ jsxs8(
2782
+ "div",
2783
+ {
2784
+ style: {
2785
+ display: "flex",
2786
+ alignItems: "center",
2787
+ justifyContent: "space-between",
2788
+ padding: "4px 8px"
2789
+ },
2790
+ children: [
2791
+ /* @__PURE__ */ jsx11("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: fileUploadEnabled && /* @__PURE__ */ jsx11(
2792
+ "button",
2793
+ {
2794
+ "data-testid": "file-picker-button",
2795
+ onClick: openFilePicker,
2796
+ disabled: !connected || loading || pendingApprovals.length > 0,
2797
+ style: {
2798
+ padding: "4px",
2799
+ background: "transparent",
2800
+ border: `1px solid ${theme.borderColor}`,
2801
+ borderRadius: "50%",
2802
+ cursor: connected && !loading && pendingApprovals.length === 0 ? "pointer" : "not-allowed",
2803
+ color: theme.secondaryTextColor,
2804
+ display: "flex",
2805
+ alignItems: "center",
2806
+ justifyContent: "center",
2807
+ width: "28px",
2808
+ height: "28px",
2809
+ transition: "all 0.15s",
2810
+ opacity: connected && !loading && pendingApprovals.length === 0 ? 1 : 0.5
2811
+ },
2812
+ onMouseEnter: (e) => {
2813
+ if (connected && !loading && pendingApprovals.length === 0) {
2814
+ e.currentTarget.style.color = theme.primaryColor;
2815
+ e.currentTarget.style.borderColor = theme.primaryColor;
2816
+ }
2817
+ },
2818
+ onMouseLeave: (e) => {
2819
+ e.currentTarget.style.color = theme.secondaryTextColor;
2820
+ e.currentTarget.style.borderColor = theme.borderColor;
2821
+ },
2822
+ title: strings.fileUpload.attachFiles,
2823
+ children: /* @__PURE__ */ jsxs8("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
2824
+ /* @__PURE__ */ jsx11("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
2825
+ /* @__PURE__ */ jsx11("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
2826
+ ] })
2827
+ }
2828
+ ) }),
2829
+ /* @__PURE__ */ jsx11(
2830
+ "button",
2831
+ {
2832
+ "data-testid": "chat-send-button",
2833
+ className: "chat-send-button",
2834
+ onClick: handleSend,
2835
+ disabled: !connected || loading || pendingApprovals.length > 0 || !input.trim() && attachments.length === 0,
2836
+ style: {
2837
+ padding: "6px",
2838
+ background: connected && !loading && pendingApprovals.length === 0 && (input.trim() || attachments.length > 0) ? theme.primaryGradient : theme.buttonDisabledBackground,
2839
+ color: connected && !loading && pendingApprovals.length === 0 && (input.trim() || attachments.length > 0) ? "white" : theme.secondaryTextColor,
2840
+ border: "none",
2841
+ borderRadius: "50%",
2842
+ cursor: connected && !loading && pendingApprovals.length === 0 && (input.trim() || attachments.length > 0) ? "pointer" : "not-allowed",
2843
+ display: "flex",
2844
+ alignItems: "center",
2845
+ justifyContent: "center",
2846
+ width: "32px",
2847
+ height: "32px",
2848
+ transition: "all 0.2s"
2849
+ },
2850
+ children: /* @__PURE__ */ jsxs8("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
2851
+ /* @__PURE__ */ jsx11("line", { x1: "12", y1: "19", x2: "12", y2: "5" }),
2852
+ /* @__PURE__ */ jsx11("polyline", { points: "5 12 12 5 19 12" })
2853
+ ] })
2854
+ }
2855
+ )
2856
+ ]
2857
+ }
2858
+ )
2859
+ ]
2860
+ }
2861
+ )
2591
2862
  )
2592
2863
  ]
2593
2864
  }
2594
2865
  ),
2595
- /* @__PURE__ */ jsx10("style", { children: `
2866
+ /* @__PURE__ */ jsx11("style", { children: `
2596
2867
  /* Markdown content styles */
2597
2868
  .markdown-content > :first-child {
2598
2869
  margin-top: 0 !important;
@@ -2617,7 +2888,7 @@ function UseAIChatPanel({
2617
2888
  }
2618
2889
 
2619
2890
  // src/components/UseAIFloatingChatWrapper.tsx
2620
- import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
2891
+ import { Fragment as Fragment2, jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
2621
2892
  function UseAIFloatingChatWrapper({
2622
2893
  isOpen,
2623
2894
  onClose,
@@ -2625,8 +2896,8 @@ function UseAIFloatingChatWrapper({
2625
2896
  }) {
2626
2897
  const theme = useTheme();
2627
2898
  if (!isOpen) return null;
2628
- return /* @__PURE__ */ jsxs8(Fragment2, { children: [
2629
- /* @__PURE__ */ jsx11(
2899
+ return /* @__PURE__ */ jsxs9(Fragment2, { children: [
2900
+ /* @__PURE__ */ jsx12(
2630
2901
  "div",
2631
2902
  {
2632
2903
  style: {
@@ -2642,7 +2913,7 @@ function UseAIFloatingChatWrapper({
2642
2913
  onClick: onClose
2643
2914
  }
2644
2915
  ),
2645
- /* @__PURE__ */ jsx11(
2916
+ /* @__PURE__ */ jsx12(
2646
2917
  "div",
2647
2918
  {
2648
2919
  style: {
@@ -2661,7 +2932,7 @@ function UseAIFloatingChatWrapper({
2661
2932
  children
2662
2933
  }
2663
2934
  ),
2664
- /* @__PURE__ */ jsx11("style", { children: `
2935
+ /* @__PURE__ */ jsx12("style", { children: `
2665
2936
  @keyframes fadeIn {
2666
2937
  from { opacity: 0; }
2667
2938
  to { opacity: 1; }
@@ -2681,7 +2952,7 @@ function UseAIFloatingChatWrapper({
2681
2952
  }
2682
2953
  function CloseButton({ onClick }) {
2683
2954
  const theme = useTheme();
2684
- return /* @__PURE__ */ jsx11(
2955
+ return /* @__PURE__ */ jsx12(
2685
2956
  "button",
2686
2957
  {
2687
2958
  "data-testid": "chat-close-button",
@@ -2716,7 +2987,7 @@ function CloseButton({ onClick }) {
2716
2987
 
2717
2988
  // src/components/UseAIChat.tsx
2718
2989
  import { createContext as createContext3, useContext as useContext3 } from "react";
2719
- import { jsx as jsx12 } from "react/jsx-runtime";
2990
+ import { jsx as jsx13 } from "react/jsx-runtime";
2720
2991
  var __UseAIChatContext = createContext3(null);
2721
2992
  function useChatUIContext() {
2722
2993
  const context = useContext3(__UseAIChatContext);
@@ -2752,27 +3023,30 @@ function UseAIChat({ floating = false }) {
2752
3023
  onSaveCommand: ctx.commands.save,
2753
3024
  onRenameCommand: ctx.commands.rename,
2754
3025
  onDeleteCommand: ctx.commands.delete,
2755
- executingTool: ctx.executingTool,
3026
+ executingTool: ctx.tools.executing,
2756
3027
  feedbackEnabled: ctx.feedback?.enabled,
2757
- onFeedback: ctx.feedback?.submit
3028
+ onFeedback: ctx.feedback?.submit,
3029
+ pendingApprovals: ctx.tools.pending.tools,
3030
+ onApproveToolCall: ctx.tools.pending.tools.length > 0 ? ctx.tools.pending.approveAll : void 0,
3031
+ onRejectToolCall: ctx.tools.pending.tools.length > 0 ? ctx.tools.pending.rejectAll : void 0
2758
3032
  };
2759
3033
  if (floating) {
2760
- return /* @__PURE__ */ jsx12(
3034
+ return /* @__PURE__ */ jsx13(
2761
3035
  UseAIFloatingChatWrapper,
2762
3036
  {
2763
3037
  isOpen: ctx.ui.isOpen,
2764
3038
  onClose: () => ctx.ui.setOpen(false),
2765
- children: /* @__PURE__ */ jsx12(
3039
+ children: /* @__PURE__ */ jsx13(
2766
3040
  UseAIChatPanel,
2767
3041
  {
2768
3042
  ...chatPanelProps,
2769
- closeButton: /* @__PURE__ */ jsx12(CloseButton, { onClick: () => ctx.ui.setOpen(false) })
3043
+ closeButton: /* @__PURE__ */ jsx13(CloseButton, { onClick: () => ctx.ui.setOpen(false) })
2770
3044
  }
2771
3045
  )
2772
3046
  }
2773
3047
  );
2774
3048
  }
2775
- return /* @__PURE__ */ jsx12(UseAIChatPanel, { ...chatPanelProps });
3049
+ return /* @__PURE__ */ jsx13(UseAIChatPanel, { ...chatPanelProps });
2776
3050
  }
2777
3051
 
2778
3052
  // src/client.ts
@@ -2798,8 +3072,6 @@ var UseAIClient = class {
2798
3072
  _tools = [];
2799
3073
  _messages = [];
2800
3074
  _state = null;
2801
- // MCP headers provider
2802
- mcpHeadersProvider;
2803
3075
  // Agent selection
2804
3076
  _availableAgents = [];
2805
3077
  _defaultAgent = null;
@@ -2958,22 +3230,15 @@ var UseAIClient = class {
2958
3230
  updateState(state) {
2959
3231
  this._state = state;
2960
3232
  }
2961
- /**
2962
- * Sets the MCP headers provider.
2963
- * The provider will be called each time a message is sent to get fresh headers.
2964
- *
2965
- * @param provider - Function that returns MCP headers configuration
2966
- */
2967
- setMcpHeadersProvider(provider) {
2968
- this.mcpHeadersProvider = provider;
2969
- }
2970
3233
  /**
2971
3234
  * Sends a user prompt to the AI.
2972
3235
  *
2973
3236
  * @param prompt - The user's prompt/question (text part)
2974
3237
  * @param multimodalContent - Optional multimodal content (text, images, files)
3238
+ * @param forwardedProps - Optional props to forward to the server (e.g., telemetryMetadata, mcpHeaders).
3239
+ * Internally merged with other forwardedProps.
2975
3240
  */
2976
- async sendPrompt(prompt, multimodalContent) {
3241
+ async sendPrompt(prompt, multimodalContent, forwardedProps) {
2977
3242
  let messageContent = prompt;
2978
3243
  if (multimodalContent && multimodalContent.length > 0) {
2979
3244
  messageContent = multimodalContent.map((part) => {
@@ -3003,14 +3268,6 @@ var UseAIClient = class {
3003
3268
  // Type cast needed for Message type compatibility
3004
3269
  };
3005
3270
  this._messages.push(userMessage);
3006
- let mcpHeaders;
3007
- if (this.mcpHeadersProvider) {
3008
- try {
3009
- mcpHeaders = await this.mcpHeadersProvider();
3010
- } catch (error) {
3011
- console.error("[UseAIClient] Failed to get MCP headers:", error);
3012
- }
3013
- }
3014
3271
  const runInput = {
3015
3272
  threadId: this.threadId,
3016
3273
  // Use getter to ensure non-null
@@ -3019,13 +3276,14 @@ var UseAIClient = class {
3019
3276
  tools: this._tools.map((t) => ({
3020
3277
  name: t.name,
3021
3278
  description: t.description,
3022
- parameters: t.parameters
3279
+ parameters: t.parameters,
3280
+ annotations: t.annotations
3023
3281
  })),
3024
3282
  state: this._state,
3025
3283
  context: [],
3026
3284
  forwardedProps: {
3027
- ...mcpHeaders ? { mcpHeaders } : {},
3028
- ...this._selectedAgent ? { agent: this._selectedAgent } : {}
3285
+ ...this._selectedAgent ? { agent: this._selectedAgent } : {},
3286
+ ...forwardedProps || {}
3029
3287
  }
3030
3288
  };
3031
3289
  this.send({
@@ -3062,6 +3320,24 @@ var UseAIClient = class {
3062
3320
  this._messages.push(toolResultMsg);
3063
3321
  this.send(toolResultMessage);
3064
3322
  }
3323
+ /**
3324
+ * Sends a tool approval response back to the server.
3325
+ *
3326
+ * @param toolCallId - The ID of the tool call being approved/rejected
3327
+ * @param approved - Whether the tool execution is approved
3328
+ * @param reason - Optional reason for rejection (shown to AI)
3329
+ */
3330
+ sendToolApprovalResponse(toolCallId, approved, reason) {
3331
+ const message = {
3332
+ type: "tool_approval_response",
3333
+ data: {
3334
+ toolCallId,
3335
+ approved,
3336
+ reason
3337
+ }
3338
+ };
3339
+ this.send(message);
3340
+ }
3065
3341
  /**
3066
3342
  * Retrieves accumulated tool call data for a specific tool call ID.
3067
3343
  * Used to get the complete tool name and arguments after they've been streamed
@@ -3534,7 +3810,7 @@ var LocalStorageChatRepository = class {
3534
3810
  };
3535
3811
 
3536
3812
  // src/hooks/useChatManagement.ts
3537
- import { useState as useState5, useCallback as useCallback4, useRef as useRef5, useEffect as useEffect5 } from "react";
3813
+ import { useState as useState6, useCallback as useCallback4, useRef as useRef5, useEffect as useEffect5 } from "react";
3538
3814
  var CHAT_TITLE_MAX_LENGTH = 50;
3539
3815
  function deepEquals(a, b) {
3540
3816
  return JSON.stringify(a) === JSON.stringify(b);
@@ -3577,10 +3853,11 @@ function useChatManagement({
3577
3853
  onSendMessage,
3578
3854
  setOpen,
3579
3855
  connected,
3580
- loading
3856
+ loading,
3857
+ hasPendingApproval
3581
3858
  }) {
3582
- const [currentChatId, setCurrentChatId] = useState5(null);
3583
- const [pendingChatId, setPendingChatId] = useState5(null);
3859
+ const [currentChatId, setCurrentChatId] = useState6(null);
3860
+ const [pendingChatId, setPendingChatId] = useState6(null);
3584
3861
  const currentChatIdSnapshot = useRef5(null);
3585
3862
  const pendingChatIdSnapshot = useRef5(null);
3586
3863
  useEffect5(() => {
@@ -3799,6 +4076,10 @@ function useChatManagement({
3799
4076
  useEffect5(() => {
3800
4077
  loadingRef.current = loading;
3801
4078
  }, [loading]);
4079
+ const hasPendingApprovalRef = useRef5(hasPendingApproval);
4080
+ useEffect5(() => {
4081
+ hasPendingApprovalRef.current = hasPendingApproval;
4082
+ }, [hasPendingApproval]);
3802
4083
  const processMessageQueue = useCallback4(async () => {
3803
4084
  if (isProcessingQueueRef.current || pendingMessagesRef.current.length === 0 || !onSendMessage) {
3804
4085
  return;
@@ -3806,7 +4087,7 @@ function useChatManagement({
3806
4087
  isProcessingQueueRef.current = true;
3807
4088
  while (pendingMessagesRef.current.length > 0) {
3808
4089
  const { message, options } = pendingMessagesRef.current.shift();
3809
- const { newChat = false, attachments = [], openChat = true, metadata } = options ?? {};
4090
+ const { newChat = false, attachments = [], openChat = true, metadata, forwardedProps } = options ?? {};
3810
4091
  if (newChat) {
3811
4092
  await createNewChat({ metadata });
3812
4093
  }
@@ -3828,21 +4109,21 @@ function useChatManagement({
3828
4109
  };
3829
4110
  })
3830
4111
  );
3831
- await onSendMessage(message, fileAttachments.length > 0 ? fileAttachments : void 0);
4112
+ await onSendMessage(message, fileAttachments.length > 0 ? fileAttachments : void 0, forwardedProps);
3832
4113
  if (openChat && setOpen) {
3833
4114
  setOpen(true);
3834
4115
  }
3835
4116
  await new Promise((resolve) => {
3836
- const checkLoading = () => {
4117
+ const checkReady = () => {
3837
4118
  setTimeout(() => {
3838
- if (!loadingRef.current) {
4119
+ if (!loadingRef.current && !hasPendingApprovalRef.current) {
3839
4120
  resolve();
3840
4121
  } else {
3841
- checkLoading();
4122
+ checkReady();
3842
4123
  }
3843
4124
  }, 100);
3844
4125
  };
3845
- checkLoading();
4126
+ checkReady();
3846
4127
  });
3847
4128
  }
3848
4129
  isProcessingQueueRef.current = false;
@@ -3879,7 +4160,7 @@ function useChatManagement({
3879
4160
  }
3880
4161
 
3881
4162
  // src/hooks/useAgentSelection.ts
3882
- import { useState as useState6, useCallback as useCallback5, useEffect as useEffect6, useMemo as useMemo3 } from "react";
4163
+ import { useState as useState7, useCallback as useCallback5, useEffect as useEffect6, useMemo as useMemo3 } from "react";
3883
4164
  function filterAgents(serverAgents, defaultAgentId, visibleAgentIds) {
3884
4165
  const getDefaultAgentFallback = () => {
3885
4166
  const defaultAgentInfo = serverAgents.find((a) => a.id === defaultAgentId);
@@ -3905,9 +4186,9 @@ function useAgentSelection({
3905
4186
  connected,
3906
4187
  visibleAgentIds
3907
4188
  }) {
3908
- const [serverAgents, setServerAgents] = useState6([]);
3909
- const [defaultAgent, setDefaultAgent] = useState6(null);
3910
- const [selectedAgent, setSelectedAgent] = useState6(null);
4189
+ const [serverAgents, setServerAgents] = useState7([]);
4190
+ const [defaultAgent, setDefaultAgent] = useState7(null);
4191
+ const [selectedAgent, setSelectedAgent] = useState7(null);
3911
4192
  const availableAgents = useMemo3(
3912
4193
  () => filterAgents(serverAgents, defaultAgent, visibleAgentIds),
3913
4194
  [serverAgents, defaultAgent, visibleAgentIds]
@@ -3944,7 +4225,7 @@ function useAgentSelection({
3944
4225
  }
3945
4226
 
3946
4227
  // src/hooks/useCommandManagement.ts
3947
- import { useState as useState7, useCallback as useCallback6, useRef as useRef6, useEffect as useEffect7 } from "react";
4228
+ import { useState as useState8, useCallback as useCallback6, useRef as useRef6, useEffect as useEffect7 } from "react";
3948
4229
 
3949
4230
  // src/commands/LocalStorageCommandRepository.ts
3950
4231
  var STORAGE_KEY_PREFIX2 = "use-ai:command:";
@@ -4077,7 +4358,7 @@ function useCommandManagement({
4077
4358
  const repositoryRef = useRef6(
4078
4359
  repository || new LocalStorageCommandRepository()
4079
4360
  );
4080
- const [commands, setCommands] = useState7([]);
4361
+ const [commands, setCommands] = useState8([]);
4081
4362
  const refreshCommands = useCallback6(async () => {
4082
4363
  try {
4083
4364
  const cmdList = await repositoryRef.current.listCommands();
@@ -4119,10 +4400,10 @@ function useCommandManagement({
4119
4400
  }
4120
4401
 
4121
4402
  // src/hooks/useToolRegistry.ts
4122
- import { useState as useState8, useCallback as useCallback7, useRef as useRef7, useMemo as useMemo4 } from "react";
4403
+ import { useState as useState9, useCallback as useCallback7, useRef as useRef7, useMemo as useMemo4 } from "react";
4123
4404
  function useToolRegistry() {
4124
4405
  const toolRegistryRef = useRef7(/* @__PURE__ */ new Map());
4125
- const [toolRegistryVersion, setToolRegistryVersion] = useState8(0);
4406
+ const [toolRegistryVersion, setToolRegistryVersion] = useState9(0);
4126
4407
  const toolOwnershipRef = useRef7(/* @__PURE__ */ new Map());
4127
4408
  const invisibleRef = useRef7(/* @__PURE__ */ new Set());
4128
4409
  const registerTools = useCallback7((id, tools, options) => {
@@ -4182,7 +4463,7 @@ function useToolRegistry() {
4182
4463
  }
4183
4464
 
4184
4465
  // src/hooks/usePromptState.ts
4185
- import { useState as useState9, useCallback as useCallback8, useRef as useRef8, useMemo as useMemo5, useEffect as useEffect8 } from "react";
4466
+ import { useState as useState10, useCallback as useCallback8, useRef as useRef8, useMemo as useMemo5, useEffect as useEffect8 } from "react";
4186
4467
  function usePromptState({
4187
4468
  systemPrompt,
4188
4469
  clientRef,
@@ -4191,7 +4472,7 @@ function usePromptState({
4191
4472
  const promptsRef = useRef8(/* @__PURE__ */ new Map());
4192
4473
  const suggestionsRef = useRef8(/* @__PURE__ */ new Map());
4193
4474
  const waitersRef = useRef8(/* @__PURE__ */ new Map());
4194
- const [suggestionsVersion, setSuggestionsVersion] = useState9(0);
4475
+ const [suggestionsVersion, setSuggestionsVersion] = useState10(0);
4195
4476
  const buildStateFromPrompts = useCallback8(() => {
4196
4477
  const promptParts = [];
4197
4478
  if (systemPrompt) {
@@ -4254,14 +4535,14 @@ function usePromptState({
4254
4535
  }
4255
4536
 
4256
4537
  // src/hooks/useFeedback.ts
4257
- import { useState as useState10, useEffect as useEffect9, useRef as useRef9, useCallback as useCallback9 } from "react";
4538
+ import { useState as useState11, useEffect as useEffect9, useRef as useRef9, useCallback as useCallback9 } from "react";
4258
4539
  function useFeedback({
4259
4540
  clientRef,
4260
4541
  repository,
4261
4542
  getDisplayedChatId,
4262
4543
  setMessages
4263
4544
  }) {
4264
- const [enabled, setEnabled] = useState10(false);
4545
+ const [enabled, setEnabled] = useState11(false);
4265
4546
  const enabledRef = useRef9(false);
4266
4547
  useEffect9(() => {
4267
4548
  enabledRef.current = enabled;
@@ -4315,8 +4596,120 @@ function useFeedback({
4315
4596
  };
4316
4597
  }
4317
4598
 
4599
+ // src/hooks/useToolExecution.ts
4600
+ import { useState as useState12, useCallback as useCallback10, useRef as useRef10 } from "react";
4601
+ function useToolExecution({
4602
+ clientRef,
4603
+ aggregatedToolsRef,
4604
+ toolOwnershipRef,
4605
+ promptsRef,
4606
+ isInvisible,
4607
+ getWaiter
4608
+ }) {
4609
+ const [pendingApprovals, setPendingApprovals] = useState12([]);
4610
+ const pendingApprovalToolCallsRef = useRef10(/* @__PURE__ */ new Map());
4611
+ const handleApprovalRequest = useCallback10((event) => {
4612
+ console.log("[useToolExecution] Tool approval requested:", event.toolCallName, event.toolCallId);
4613
+ setPendingApprovals((prev) => [
4614
+ ...prev,
4615
+ {
4616
+ toolCallId: event.toolCallId,
4617
+ toolCallName: event.toolCallName,
4618
+ toolCallArgs: event.toolCallArgs,
4619
+ annotations: event.annotations
4620
+ }
4621
+ ]);
4622
+ }, []);
4623
+ const executeToolCall = useCallback10(async (toolCallId, name, input) => {
4624
+ const client = clientRef.current;
4625
+ if (!client) {
4626
+ console.error("[useToolExecution] No client available for tool execution");
4627
+ return;
4628
+ }
4629
+ try {
4630
+ const ownerId = toolOwnershipRef.current.get(name);
4631
+ console.log(`[useToolExecution] Tool "${name}" owned by component:`, ownerId);
4632
+ console.log("[useToolExecution] Executing tool...");
4633
+ const result = await executeDefinedTool(aggregatedToolsRef.current, name, input);
4634
+ const isErrorResult = result && typeof result === "object" && ("error" in result || result.success === false);
4635
+ const ownerIsInvisible = ownerId ? isInvisible(ownerId) : false;
4636
+ if (ownerId && !isErrorResult && !ownerIsInvisible) {
4637
+ const waiter = getWaiter(ownerId);
4638
+ if (waiter) {
4639
+ console.log(`[useToolExecution] Waiting for prompt change from ${ownerId}...`);
4640
+ await waiter();
4641
+ console.log("[useToolExecution] Prompt change wait complete");
4642
+ }
4643
+ } else if (isErrorResult) {
4644
+ console.log("[useToolExecution] Tool returned error, skipping prompt wait");
4645
+ } else if (ownerIsInvisible) {
4646
+ console.log("[useToolExecution] Component is invisible, skipping prompt wait");
4647
+ }
4648
+ let updatedState = null;
4649
+ if (ownerId) {
4650
+ const prompt = promptsRef.current.get(ownerId);
4651
+ if (prompt) {
4652
+ updatedState = { context: prompt };
4653
+ console.log(`[useToolExecution] Updated state from ${ownerId}`);
4654
+ }
4655
+ }
4656
+ client.sendToolResponse(toolCallId, result, updatedState);
4657
+ } catch (err) {
4658
+ console.error("Tool execution error:", err);
4659
+ client.sendToolResponse(toolCallId, {
4660
+ error: err instanceof Error ? err.message : "Unknown error"
4661
+ });
4662
+ }
4663
+ }, [clientRef, aggregatedToolsRef, toolOwnershipRef, promptsRef, isInvisible, getWaiter]);
4664
+ const storePendingToolCall = useCallback10((toolCallId, name, input, toolCallData) => {
4665
+ console.log(`[useToolExecution] Storing pending tool call "${name}" for approval`);
4666
+ pendingApprovalToolCallsRef.current.set(toolCallId, { name, input, toolCallData });
4667
+ }, []);
4668
+ const executePendingToolAfterApproval = useCallback10(async (toolCallId) => {
4669
+ const pendingTool = pendingApprovalToolCallsRef.current.get(toolCallId);
4670
+ if (!pendingTool) {
4671
+ console.warn(`[useToolExecution] No pending tool found for ${toolCallId}`);
4672
+ return;
4673
+ }
4674
+ pendingApprovalToolCallsRef.current.delete(toolCallId);
4675
+ await executeToolCall(toolCallId, pendingTool.name, pendingTool.input);
4676
+ }, [executeToolCall]);
4677
+ const approveAll = useCallback10(async () => {
4678
+ if (!clientRef.current) return;
4679
+ console.log("[useToolExecution] Approving all tool calls:", pendingApprovals.length);
4680
+ const pendingTools = [...pendingApprovals];
4681
+ for (const pending of pendingTools) {
4682
+ clientRef.current.sendToolApprovalResponse(pending.toolCallId, true);
4683
+ }
4684
+ setPendingApprovals([]);
4685
+ for (const tool of pendingTools) {
4686
+ await executePendingToolAfterApproval(tool.toolCallId);
4687
+ }
4688
+ }, [clientRef, pendingApprovals, executePendingToolAfterApproval]);
4689
+ const rejectAll = useCallback10((reason) => {
4690
+ if (!clientRef.current) return;
4691
+ console.log("[useToolExecution] Rejecting all tool calls:", pendingApprovals.length, reason);
4692
+ const pendingTools = [...pendingApprovals];
4693
+ for (const pending of pendingTools) {
4694
+ clientRef.current.sendToolApprovalResponse(pending.toolCallId, false, reason);
4695
+ }
4696
+ setPendingApprovals([]);
4697
+ for (const tool of pendingTools) {
4698
+ pendingApprovalToolCallsRef.current.delete(tool.toolCallId);
4699
+ }
4700
+ }, [clientRef, pendingApprovals]);
4701
+ return {
4702
+ pendingApprovals,
4703
+ handleApprovalRequest,
4704
+ executeToolCall,
4705
+ storePendingToolCall,
4706
+ approveAll,
4707
+ rejectAll
4708
+ };
4709
+ }
4710
+
4318
4711
  // src/providers/useAIProvider.tsx
4319
- import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
4712
+ import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
4320
4713
  var __UseAIContext = createContext4(null);
4321
4714
  var hasWarnedAboutMissingProvider = false;
4322
4715
  var noOpContextValue = {
@@ -4384,7 +4777,7 @@ function UseAIProvider({
4384
4777
  CustomButton,
4385
4778
  CustomChat,
4386
4779
  chatRepository,
4387
- mcpHeadersProvider,
4780
+ forwardedPropsProvider,
4388
4781
  fileUploadConfig: fileUploadConfigProp,
4389
4782
  commandRepository,
4390
4783
  renderChat = true,
@@ -4396,24 +4789,24 @@ function UseAIProvider({
4396
4789
  const fileUploadConfig = fileUploadConfigProp === false ? void 0 : fileUploadConfigProp ?? DEFAULT_FILE_UPLOAD_CONFIG;
4397
4790
  const theme = { ...defaultTheme, ...customTheme };
4398
4791
  const strings = { ...defaultStrings, ...customStrings };
4399
- const [connected, setConnected] = useState11(false);
4400
- const [isChatOpen, setIsChatOpen] = useState11(false);
4401
- const [loading, setLoading] = useState11(false);
4402
- const [messages, setMessages] = useState11([]);
4403
- const [fileProcessingState, setFileProcessingState] = useState11(null);
4404
- const handleSetChatOpen = useCallback10((open) => {
4792
+ const [connected, setConnected] = useState13(false);
4793
+ const [isChatOpen, setIsChatOpen] = useState13(false);
4794
+ const [loading, setLoading] = useState13(false);
4795
+ const [messages, setMessages] = useState13([]);
4796
+ const [fileProcessingState, setFileProcessingState] = useState13(null);
4797
+ const handleSetChatOpen = useCallback11((open) => {
4405
4798
  setIsChatOpen(open);
4406
4799
  onOpenChange?.(open);
4407
4800
  }, [onOpenChange]);
4408
- const [streamingText, setStreamingText] = useState11("");
4409
- const streamingChatIdRef = useRef10(null);
4410
- const [executingTool, setExecutingTool] = useState11(null);
4411
- const executingToolFallbackRef = useRef10(null);
4412
- const clientRef = useRef10(null);
4413
- const repositoryRef = useRef10(
4801
+ const [streamingText, setStreamingText] = useState13("");
4802
+ const streamingChatIdRef = useRef11(null);
4803
+ const [executingTool, setExecutingTool] = useState13(null);
4804
+ const executingToolFallbackRef = useRef11(null);
4805
+ const clientRef = useRef11(null);
4806
+ const repositoryRef = useRef11(
4414
4807
  chatRepository || new LocalStorageChatRepository()
4415
4808
  );
4416
- const handleSendMessageRef = useRef10(null);
4809
+ const handleSendMessageRef = useRef11(null);
4417
4810
  const {
4418
4811
  registerTools,
4419
4812
  unregisterTools,
@@ -4435,11 +4828,19 @@ function UseAIProvider({
4435
4828
  clientRef,
4436
4829
  connected
4437
4830
  });
4438
- const stableSendMessage = useCallback10(async (message, attachments) => {
4831
+ const stableSendMessage = useCallback11(async (message, attachments, forwardedProps) => {
4439
4832
  if (handleSendMessageRef.current) {
4440
- await handleSendMessageRef.current(message, attachments);
4833
+ await handleSendMessageRef.current(message, attachments, forwardedProps);
4441
4834
  }
4442
4835
  }, []);
4836
+ const toolExecution = useToolExecution({
4837
+ clientRef,
4838
+ aggregatedToolsRef,
4839
+ toolOwnershipRef,
4840
+ promptsRef,
4841
+ isInvisible,
4842
+ getWaiter
4843
+ });
4443
4844
  const chatManagement = useChatManagement({
4444
4845
  repository: repositoryRef.current,
4445
4846
  clientRef,
@@ -4448,7 +4849,8 @@ function UseAIProvider({
4448
4849
  onSendMessage: stableSendMessage,
4449
4850
  setOpen: handleSetChatOpen,
4450
4851
  connected,
4451
- loading
4852
+ loading,
4853
+ hasPendingApproval: toolExecution.pendingApprovals.length > 0
4452
4854
  });
4453
4855
  const {
4454
4856
  currentChatId,
@@ -4488,9 +4890,6 @@ function UseAIProvider({
4488
4890
  useEffect10(() => {
4489
4891
  console.log("[UseAIProvider] Initializing client with serverUrl:", serverUrl);
4490
4892
  const client = new UseAIClient(serverUrl);
4491
- if (mcpHeadersProvider) {
4492
- client.setMcpHeadersProvider(mcpHeadersProvider);
4493
- }
4494
4893
  const unsubscribeConnection = client.onConnectionStateChange((isConnected) => {
4495
4894
  console.log("[UseAIProvider] Connection state changed:", isConnected);
4496
4895
  setConnected(isConnected);
@@ -4518,44 +4917,20 @@ function UseAIProvider({
4518
4917
  }
4519
4918
  const name = toolCallData.name;
4520
4919
  const input = JSON.parse(toolCallData.args);
4521
- if (!aggregatedToolsRef.current[name]) {
4920
+ const tool = aggregatedToolsRef.current[name];
4921
+ if (!tool) {
4522
4922
  console.log(`[Provider] Tool "${name}" not found in useAI tools, skipping (likely a workflow tool)`);
4523
4923
  return;
4524
4924
  }
4525
- try {
4526
- const ownerId = toolOwnershipRef.current.get(name);
4527
- console.log(`[useAI] Tool "${name}" owned by component:`, ownerId);
4528
- console.log("[useAI] Executing tool...");
4529
- const result = await executeDefinedTool(aggregatedToolsRef.current, name, input);
4530
- const isErrorResult = result && typeof result === "object" && ("error" in result || result.success === false);
4531
- const ownerIsInvisible = ownerId ? isInvisible(ownerId) : false;
4532
- if (ownerId && !isErrorResult && !ownerIsInvisible) {
4533
- const waiter = getWaiter(ownerId);
4534
- if (waiter) {
4535
- console.log(`[useAI] Waiting for prompt change from ${ownerId}...`);
4536
- await waiter();
4537
- console.log("[useAI] Prompt change wait complete");
4538
- }
4539
- } else if (isErrorResult) {
4540
- console.log("[useAI] Tool returned error, skipping prompt wait");
4541
- } else if (ownerIsInvisible) {
4542
- console.log("[useAI] Component is invisible, skipping prompt wait");
4543
- }
4544
- let updatedState = null;
4545
- if (ownerId) {
4546
- const prompt = promptsRef.current.get(ownerId);
4547
- if (prompt) {
4548
- updatedState = { context: prompt };
4549
- console.log(`[useAI] Updated state from ${ownerId}`);
4550
- }
4551
- }
4552
- client.sendToolResponse(toolCallId, result, updatedState);
4553
- } catch (err) {
4554
- console.error("Tool execution error:", err);
4555
- client.sendToolResponse(toolCallId, {
4556
- error: err instanceof Error ? err.message : "Unknown error"
4557
- });
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;
4558
4929
  }
4930
+ await toolExecution.executeToolCall(toolCallId, name, input);
4931
+ } else if (event.type === TOOL_APPROVAL_REQUEST) {
4932
+ const e = event;
4933
+ toolExecution.handleApprovalRequest(e);
4559
4934
  } else if (event.type === EventType.TEXT_MESSAGE_CONTENT) {
4560
4935
  const contentEvent = event;
4561
4936
  setStreamingText((prev) => prev + contentEvent.delta);
@@ -4588,14 +4963,7 @@ function UseAIProvider({
4588
4963
  client.disconnect();
4589
4964
  };
4590
4965
  }, [serverUrl]);
4591
- useEffect10(() => {
4592
- const client = clientRef.current;
4593
- if (!client) return;
4594
- if (mcpHeadersProvider) {
4595
- client.setMcpHeadersProvider(mcpHeadersProvider);
4596
- }
4597
- }, [mcpHeadersProvider]);
4598
- const lastRegisteredToolsRef = useRef10("");
4966
+ const lastRegisteredToolsRef = useRef11("");
4599
4967
  useEffect10(() => {
4600
4968
  const client = clientRef.current;
4601
4969
  if (!client || !client.isConnected() || !hasTools) return;
@@ -4614,7 +4982,7 @@ function UseAIProvider({
4614
4982
  console.error("Failed to register tools:", err);
4615
4983
  }
4616
4984
  }, [hasTools, aggregatedTools, connected]);
4617
- const handleSendMessage = useCallback10(async (message, attachments) => {
4985
+ const handleSendMessage = useCallback11(async (message, attachments, messageForwardedProps) => {
4618
4986
  if (!clientRef.current) return;
4619
4987
  setStreamingText("");
4620
4988
  const activatedChatId = activatePendingChat();
@@ -4668,8 +5036,18 @@ function UseAIProvider({
4668
5036
  }
4669
5037
  setLoading(true);
4670
5038
  }
4671
- await clientRef.current.sendPrompt(message, multimodalContent);
4672
- }, [activatePendingChat, currentChatId, saveUserMessage, fileUploadConfig, getCurrentChat]);
5039
+ const providerResult = forwardedPropsProvider ? forwardedPropsProvider() : {};
5040
+ const providerProps = providerResult instanceof Promise ? await providerResult : providerResult;
5041
+ const mergedForwardedProps = {
5042
+ ...providerProps,
5043
+ ...messageForwardedProps
5044
+ };
5045
+ await clientRef.current.sendPrompt(
5046
+ message,
5047
+ multimodalContent,
5048
+ Object.keys(mergedForwardedProps).length > 0 ? mergedForwardedProps : void 0
5049
+ );
5050
+ }, [activatePendingChat, currentChatId, saveUserMessage, fileUploadConfig, getCurrentChat, forwardedPropsProvider]);
4673
5051
  handleSendMessageRef.current = handleSendMessage;
4674
5052
  const value = {
4675
5053
  serverUrl,
@@ -4746,7 +5124,14 @@ function UseAIProvider({
4746
5124
  isOpen: isChatOpen,
4747
5125
  setOpen: handleSetChatOpen
4748
5126
  },
4749
- executingTool: executingToolDisplay,
5127
+ tools: {
5128
+ executing: executingToolDisplay,
5129
+ pending: {
5130
+ tools: toolExecution.pendingApprovals,
5131
+ approveAll: toolExecution.approveAll,
5132
+ rejectAll: toolExecution.rejectAll
5133
+ }
5134
+ },
4750
5135
  feedback: {
4751
5136
  enabled: feedback.enabled,
4752
5137
  submit: feedback.submitFeedback
@@ -4779,21 +5164,24 @@ function UseAIProvider({
4779
5164
  onDeleteCommand: deleteCommand,
4780
5165
  executingTool: executingToolDisplay,
4781
5166
  feedbackEnabled: feedback.enabled,
4782
- onFeedback: feedback.submitFeedback
5167
+ onFeedback: feedback.submitFeedback,
5168
+ pendingApprovals: toolExecution.pendingApprovals,
5169
+ onApproveToolCall: toolExecution.pendingApprovals.length > 0 ? toolExecution.approveAll : void 0,
5170
+ onRejectToolCall: toolExecution.pendingApprovals.length > 0 ? toolExecution.rejectAll : void 0
4783
5171
  };
4784
5172
  const renderDefaultChat = () => {
4785
5173
  if (isUIDisabled) return null;
4786
- return /* @__PURE__ */ jsx13(UseAIFloatingChatWrapper, { isOpen: isChatOpen, onClose: () => handleSetChatOpen(false), children: /* @__PURE__ */ jsx13(
5174
+ return /* @__PURE__ */ jsx14(UseAIFloatingChatWrapper, { isOpen: isChatOpen, onClose: () => handleSetChatOpen(false), children: /* @__PURE__ */ jsx14(
4787
5175
  UseAIChatPanel,
4788
5176
  {
4789
5177
  ...chatPanelProps,
4790
- closeButton: /* @__PURE__ */ jsx13(CloseButton, { onClick: () => handleSetChatOpen(false) })
5178
+ closeButton: /* @__PURE__ */ jsx14(CloseButton, { onClick: () => handleSetChatOpen(false) })
4791
5179
  }
4792
5180
  ) });
4793
5181
  };
4794
5182
  const renderCustomChat = () => {
4795
5183
  if (!CustomChat) return null;
4796
- return /* @__PURE__ */ jsx13(
5184
+ return /* @__PURE__ */ jsx14(
4797
5185
  CustomChat,
4798
5186
  {
4799
5187
  isOpen: isChatOpen,
@@ -4812,8 +5200,8 @@ function UseAIProvider({
4812
5200
  };
4813
5201
  const renderBuiltInChat = () => {
4814
5202
  if (!renderChat) return null;
4815
- return /* @__PURE__ */ jsxs9(Fragment3, { children: [
4816
- ButtonComponent && /* @__PURE__ */ jsx13(
5203
+ return /* @__PURE__ */ jsxs10(Fragment3, { children: [
5204
+ ButtonComponent && /* @__PURE__ */ jsx14(
4817
5205
  ButtonComponent,
4818
5206
  {
4819
5207
  onClick: () => handleSetChatOpen(true),
@@ -4823,7 +5211,7 @@ function UseAIProvider({
4823
5211
  hasCustomChat ? renderCustomChat() : renderDefaultChat()
4824
5212
  ] });
4825
5213
  };
4826
- return /* @__PURE__ */ jsx13(ThemeContext.Provider, { value: theme, children: /* @__PURE__ */ jsx13(StringsContext.Provider, { value: strings, children: /* @__PURE__ */ jsx13(__UseAIContext.Provider, { value, children: /* @__PURE__ */ jsxs9(__UseAIChatContext.Provider, { value: chatUIContextValue, children: [
5214
+ return /* @__PURE__ */ jsx14(ThemeContext.Provider, { value: theme, children: /* @__PURE__ */ jsx14(StringsContext.Provider, { value: strings, children: /* @__PURE__ */ jsx14(__UseAIContext.Provider, { value, children: /* @__PURE__ */ jsxs10(__UseAIChatContext.Provider, { value: chatUIContextValue, children: [
4827
5215
  children,
4828
5216
  renderBuiltInChat()
4829
5217
  ] }) }) }) });
@@ -4843,11 +5231,11 @@ function useAIContext() {
4843
5231
  }
4844
5232
 
4845
5233
  // src/hooks/useStableTools.ts
4846
- import { useRef as useRef11 } from "react";
5234
+ import { useRef as useRef12 } from "react";
4847
5235
  function useStableTools(tools) {
4848
- const latestToolsRef = useRef11({});
4849
- const stableToolsRef = useRef11({});
4850
- const prevToolNamesRef = useRef11("");
5236
+ const latestToolsRef = useRef12({});
5237
+ const stableToolsRef = useRef12({});
5238
+ const prevToolNamesRef = useRef12("");
4851
5239
  if (!tools) {
4852
5240
  latestToolsRef.current = {};
4853
5241
  return void 0;
@@ -4917,13 +5305,13 @@ function useAI(options = {}) {
4917
5305
  const { connected, tools, client, prompts } = useAIContext();
4918
5306
  const { register: registerTools, unregister: unregisterTools } = tools;
4919
5307
  const { update: updatePrompt, registerWaiter, unregisterWaiter } = prompts;
4920
- const [response, setResponse] = useState12(null);
4921
- const [loading, setLoading] = useState12(false);
4922
- const [error, setError] = useState12(null);
4923
- const hookId = useRef12(`useAI-${Math.random().toString(36).substr(2, 9)}`);
4924
- const toolsRef = useRef12({});
4925
- const componentRef = useRef12(null);
4926
- const promptChangeResolvers = useRef12([]);
5308
+ const [response, setResponse] = useState14(null);
5309
+ const [loading, setLoading] = useState14(false);
5310
+ const [error, setError] = useState14(null);
5311
+ const hookId = useRef13(`useAI-${Math.random().toString(36).substr(2, 9)}`);
5312
+ const toolsRef = useRef13({});
5313
+ const componentRef = useRef13(null);
5314
+ const promptChangeResolvers = useRef13([]);
4927
5315
  const stableTools = useStableTools(options.tools);
4928
5316
  const toolsKey = useMemo6(() => {
4929
5317
  if (!options.tools) return "";
@@ -4935,7 +5323,7 @@ function useAI(options = {}) {
4935
5323
  componentRef.current.setAttribute("data-useai-context", "true");
4936
5324
  }
4937
5325
  }, []);
4938
- const waitForPromptChange = useCallback11(() => {
5326
+ const waitForPromptChange = useCallback12(() => {
4939
5327
  return new Promise((resolve) => {
4940
5328
  const timeoutMs = 100;
4941
5329
  const timeoutId = setTimeout(() => {
@@ -4967,7 +5355,7 @@ function useAI(options = {}) {
4967
5355
  promptChangeResolvers.current = [];
4968
5356
  }
4969
5357
  }, [enabled, options.prompt, memoizedSuggestions, updatePrompt]);
4970
- const updatePromptRef = useRef12(updatePrompt);
5358
+ const updatePromptRef = useRef13(updatePrompt);
4971
5359
  updatePromptRef.current = updatePrompt;
4972
5360
  useEffect11(() => {
4973
5361
  const id = hookId.current;
@@ -4998,7 +5386,7 @@ function useAI(options = {}) {
4998
5386
  unsubscribe();
4999
5387
  };
5000
5388
  }, [enabled, client]);
5001
- const handleAGUIEvent = useCallback11(async (event) => {
5389
+ const handleAGUIEvent = useCallback12(async (event) => {
5002
5390
  switch (event.type) {
5003
5391
  case EventType.TEXT_MESSAGE_END: {
5004
5392
  const content = client?.currentMessageContent;
@@ -5018,7 +5406,7 @@ function useAI(options = {}) {
5018
5406
  }
5019
5407
  }
5020
5408
  }, [client, options.onError]);
5021
- const generate = useCallback11(async (prompt) => {
5409
+ const generate = useCallback12(async (prompt) => {
5022
5410
  if (!enabled) {
5023
5411
  const error2 = new Error("AI features are disabled");
5024
5412
  setError(error2);
@@ -5054,17 +5442,17 @@ function useAI(options = {}) {
5054
5442
  }
5055
5443
 
5056
5444
  // src/useAIWorkflow.ts
5057
- import { useState as useState13, useCallback as useCallback12, useRef as useRef13, useEffect as useEffect12 } from "react";
5445
+ import { useState as useState15, useCallback as useCallback13, useRef as useRef14, useEffect as useEffect12 } from "react";
5058
5446
  import { EventType as EventType3 } from "@meetsmore-oss/use-ai-core";
5059
5447
  import { v4 as uuidv43 } from "uuid";
5060
5448
  function useAIWorkflow(runner, workflowId) {
5061
5449
  const { connected, client } = useAIContext();
5062
- const [status, setStatus] = useState13("idle");
5063
- const [text, setText] = useState13(null);
5064
- const [error, setError] = useState13(null);
5065
- const currentWorkflowRef = useRef13(null);
5066
- const eventListenerIdRef = useRef13(`useAIWorkflow-${Math.random().toString(36).substr(2, 9)}`);
5067
- const handleWorkflowEvent = useCallback12(async (event) => {
5450
+ const [status, setStatus] = useState15("idle");
5451
+ const [text, setText] = useState15(null);
5452
+ const [error, setError] = useState15(null);
5453
+ const currentWorkflowRef = useRef14(null);
5454
+ const eventListenerIdRef = useRef14(`useAIWorkflow-${Math.random().toString(36).substr(2, 9)}`);
5455
+ const handleWorkflowEvent = useCallback13(async (event) => {
5068
5456
  const currentWorkflow = currentWorkflowRef.current;
5069
5457
  if (!currentWorkflow) return;
5070
5458
  if (event.type === EventType3.RUN_STARTED) {
@@ -5156,7 +5544,7 @@ function useAIWorkflow(runner, workflowId) {
5156
5544
  unsubscribe();
5157
5545
  };
5158
5546
  }, [client, handleWorkflowEvent]);
5159
- const trigger = useCallback12(async (options) => {
5547
+ const trigger = useCallback13(async (options) => {
5160
5548
  if (!client?.isConnected()) {
5161
5549
  const err = new Error("Not connected to server");
5162
5550
  setError(err);
@@ -5232,7 +5620,7 @@ export {
5232
5620
  defaultTheme,
5233
5621
  defineTool,
5234
5622
  executeDefinedTool,
5235
- findTransformer,
5623
+ findTransformerPattern,
5236
5624
  generateChatId,
5237
5625
  generateCommandId,
5238
5626
  generateMessageId,