@burdenoff/fluidgrids-fe-sdk 2026.922.12 → 2026.922.13

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
@@ -42,7 +42,7 @@ import { Icon } from '@iconify/react';
42
42
  import { ErrorBoundary } from '@burdenoff/fe-libs/shared/components';
43
43
  import '@xyflow/react/dist/style.css';
44
44
  import { Button } from '@burdenoff/fe-libs/button';
45
- import { Tabs, TabsList, TabsTrigger, TabsContent, Card, Table, TableHeader, TableRow, TableHead, TableBody, TableCell, Badge, CardHeader, Label, Input, Switch, Dialog, DialogContent, DialogHeader, DialogTitle, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, DialogFooter } from '@burdenoff/fe-libs/ui';
45
+ import { Tabs, TabsList, TabsTrigger, TabsContent, Card, CardContent, Table, TableHeader, TableRow, TableHead, TableBody, TableCell, Badge, CardHeader, Label, Input, Switch, Dialog, DialogContent, DialogHeader, DialogTitle, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, DialogFooter, Textarea } from '@burdenoff/fe-libs/ui';
46
46
  import { toast } from 'sonner';
47
47
  import { gql } from '@apollo/client';
48
48
  import * as TabsPrimitive from '@radix-ui/react-tabs';
@@ -1180,6 +1180,404 @@ function VariablesPage() {
1180
1180
  ] })
1181
1181
  ] });
1182
1182
  }
1183
+ var TOOL_APPROVAL_FIELDS = gql`
1184
+ fragment ToolApprovalRequestFields on ToolApprovalRequest {
1185
+ id
1186
+ runId
1187
+ nodeId
1188
+ workflowId
1189
+ workflowKey
1190
+ status
1191
+ toolCalls {
1192
+ callId
1193
+ name
1194
+ argsJson
1195
+ }
1196
+ decisions {
1197
+ callId
1198
+ decision
1199
+ reason
1200
+ }
1201
+ timeoutAt
1202
+ timeoutAction
1203
+ requestedAt
1204
+ decidedAt
1205
+ decidedBy
1206
+ }
1207
+ `;
1208
+ var GET_PENDING_TOOL_APPROVALS = gql`
1209
+ ${TOOL_APPROVAL_FIELDS}
1210
+ query GetPendingToolApprovals($limit: Int, $offset: Int) {
1211
+ pendingToolApprovals(limit: $limit, offset: $offset) {
1212
+ totalCount
1213
+ items {
1214
+ ...ToolApprovalRequestFields
1215
+ }
1216
+ }
1217
+ }
1218
+ `;
1219
+ var DECIDE_TOOL_APPROVAL = gql`
1220
+ ${TOOL_APPROVAL_FIELDS}
1221
+ mutation DecideToolApproval(
1222
+ $id: ID!
1223
+ $decisions: [ToolApprovalDecisionInput!]!
1224
+ ) {
1225
+ decideToolApproval(id: $id, decisions: $decisions) {
1226
+ success
1227
+ error
1228
+ resumedFromNodeId
1229
+ request {
1230
+ ...ToolApprovalRequestFields
1231
+ }
1232
+ }
1233
+ }
1234
+ `;
1235
+ function usePendingToolApprovals(options) {
1236
+ const limit = options?.limit ?? 50;
1237
+ const offset = options?.offset ?? 0;
1238
+ const pollIntervalMs = options?.pollIntervalMs ?? 15e3;
1239
+ const [items, setItems] = useState([]);
1240
+ const [totalCount, setTotalCount] = useState(0);
1241
+ const [loading, setLoading] = useState(true);
1242
+ const [error, setError] = useState(null);
1243
+ const fetchData = useCallback(async () => {
1244
+ setError(null);
1245
+ try {
1246
+ const client = getFluidGridsClient();
1247
+ const result = await client.query({
1248
+ query: GET_PENDING_TOOL_APPROVALS,
1249
+ variables: { limit, offset },
1250
+ fetchPolicy: "network-only"
1251
+ });
1252
+ setItems(result.data?.pendingToolApprovals?.items ?? []);
1253
+ setTotalCount(result.data?.pendingToolApprovals?.totalCount ?? 0);
1254
+ } catch (err) {
1255
+ setError(err);
1256
+ } finally {
1257
+ setLoading(false);
1258
+ }
1259
+ }, [limit, offset]);
1260
+ useEffect(() => {
1261
+ fetchData();
1262
+ if (pollIntervalMs <= 0) return;
1263
+ const interval = setInterval(fetchData, pollIntervalMs);
1264
+ return () => clearInterval(interval);
1265
+ }, [fetchData, pollIntervalMs]);
1266
+ return { items, totalCount, loading, error, refetch: fetchData };
1267
+ }
1268
+ function useDecideToolApproval() {
1269
+ const [loading, setLoading] = useState(false);
1270
+ const [error, setError] = useState(null);
1271
+ const decide = useCallback(
1272
+ async (id, decisions) => {
1273
+ setLoading(true);
1274
+ setError(null);
1275
+ try {
1276
+ const client = getFluidGridsClient();
1277
+ const result = throwIfResultError(
1278
+ await client.mutate({
1279
+ mutation: DECIDE_TOOL_APPROVAL,
1280
+ variables: { id, decisions }
1281
+ })
1282
+ );
1283
+ const response = result.data?.decideToolApproval;
1284
+ if (!response?.success) {
1285
+ throw new Error(response?.error ?? "Failed to record the decision");
1286
+ }
1287
+ return response;
1288
+ } catch (err) {
1289
+ setError(err);
1290
+ throw err;
1291
+ } finally {
1292
+ setLoading(false);
1293
+ }
1294
+ },
1295
+ []
1296
+ );
1297
+ const decideAll = useCallback(
1298
+ async (request, decision, reason) => {
1299
+ return decide(
1300
+ request.id,
1301
+ request.toolCalls.map((call) => ({
1302
+ callId: call.callId,
1303
+ decision,
1304
+ reason
1305
+ }))
1306
+ );
1307
+ },
1308
+ [decide]
1309
+ );
1310
+ return { decide, decideAll, loading, error };
1311
+ }
1312
+ function formatArgs(argsJson) {
1313
+ try {
1314
+ return JSON.stringify(JSON.parse(argsJson), null, 2);
1315
+ } catch {
1316
+ return argsJson;
1317
+ }
1318
+ }
1319
+ function timeAgo(iso, tr) {
1320
+ const then = new Date(iso).getTime();
1321
+ const diffMs = Date.now() - then;
1322
+ const minutes = Math.floor(diffMs / 6e4);
1323
+ if (minutes < 1) return tr("fluidgrids.toolApproval.justNow", "just now");
1324
+ if (minutes < 60)
1325
+ return tr("fluidgrids.toolApproval.minutesAgo", "{{minutes}}m ago", {
1326
+ minutes
1327
+ });
1328
+ const hours = Math.floor(minutes / 60);
1329
+ if (hours < 24)
1330
+ return tr("fluidgrids.toolApproval.hoursAgo", "{{hours}}h ago", { hours });
1331
+ const days = Math.floor(hours / 24);
1332
+ return tr("fluidgrids.toolApproval.daysAgo", "{{days}}d ago", { days });
1333
+ }
1334
+ function RejectDialog({
1335
+ open,
1336
+ onOpenChange,
1337
+ onConfirm,
1338
+ submitting
1339
+ }) {
1340
+ const tr = useTr();
1341
+ const [reason, setReason] = useState("");
1342
+ return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { children: [
1343
+ /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: tr(
1344
+ "fluidgrids.toolApproval.rejectDialogTitle",
1345
+ "Reject tool call"
1346
+ ) }) }),
1347
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
1348
+ /* @__PURE__ */ jsx(Label, { htmlFor: "reject-reason", children: tr(
1349
+ "fluidgrids.toolApproval.rejectReasonLabel",
1350
+ "Reason (optional)"
1351
+ ) }),
1352
+ /* @__PURE__ */ jsx(
1353
+ Textarea,
1354
+ {
1355
+ id: "reject-reason",
1356
+ value: reason,
1357
+ onChange: (e) => setReason(e.target.value),
1358
+ placeholder: tr(
1359
+ "fluidgrids.toolApproval.rejectReasonPlaceholder",
1360
+ "Why is this call being rejected? The agent sees this."
1361
+ )
1362
+ }
1363
+ )
1364
+ ] }),
1365
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
1366
+ /* @__PURE__ */ jsx(
1367
+ Button,
1368
+ {
1369
+ variant: "outline",
1370
+ onClick: () => onOpenChange(false),
1371
+ disabled: submitting,
1372
+ children: tr("fluidgrids.toolApproval.cancel", "Cancel")
1373
+ }
1374
+ ),
1375
+ /* @__PURE__ */ jsx(
1376
+ Button,
1377
+ {
1378
+ variant: "destructive",
1379
+ onClick: () => onConfirm(reason),
1380
+ disabled: submitting,
1381
+ children: submitting ? tr("fluidgrids.toolApproval.rejecting", "Rejecting\u2026") : tr("fluidgrids.toolApproval.rejectConfirm", "Reject")
1382
+ }
1383
+ )
1384
+ ] })
1385
+ ] }) });
1386
+ }
1387
+ function RequestCard({ request, onDecided }) {
1388
+ const tr = useTr();
1389
+ const { decideAll, loading } = useDecideToolApproval();
1390
+ const [showReject, setShowReject] = useState(false);
1391
+ const handleDecide = async (decision, reason) => {
1392
+ try {
1393
+ await decideAll(request, decision, reason);
1394
+ toast.success(
1395
+ decision === "APPROVED" ? tr(
1396
+ "fluidgrids.toolApproval.approvedToast",
1397
+ "Tool call approved \u2014 run resumed"
1398
+ ) : tr(
1399
+ "fluidgrids.toolApproval.rejectedToast",
1400
+ "Tool call rejected \u2014 run resumed"
1401
+ )
1402
+ );
1403
+ setShowReject(false);
1404
+ onDecided();
1405
+ } catch (err) {
1406
+ toast.error(
1407
+ err instanceof Error ? err.message : tr(
1408
+ "fluidgrids.toolApproval.decisionFailed",
1409
+ "Failed to record the decision"
1410
+ )
1411
+ );
1412
+ }
1413
+ };
1414
+ return /* @__PURE__ */ jsxs(
1415
+ Card,
1416
+ {
1417
+ className: "border border-border-secondary bg-background-secondary",
1418
+ "data-testid": "tool-approval-card",
1419
+ "data-request-id": request.id,
1420
+ children: [
1421
+ /* @__PURE__ */ jsxs(CardHeader, { className: "pb-3", children: [
1422
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
1423
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1424
+ /* @__PURE__ */ jsx(
1425
+ Icon,
1426
+ {
1427
+ icon: "mdi:robot-outline",
1428
+ width: 18,
1429
+ className: "text-text-secondary"
1430
+ }
1431
+ ),
1432
+ /* @__PURE__ */ jsxs("span", { className: "text-sm font-semibold text-text-primary", children: [
1433
+ tr("fluidgrids.toolApproval.nodeLabel", "Node"),
1434
+ ":",
1435
+ " ",
1436
+ request.nodeId
1437
+ ] })
1438
+ ] }),
1439
+ /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "font-mono text-[10px]", children: request.workflowKey })
1440
+ ] }),
1441
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-text-secondary", children: [
1442
+ tr("fluidgrids.toolApproval.requestedAt", "Requested {{when}}", {
1443
+ when: timeAgo(request.requestedAt, tr)
1444
+ }),
1445
+ request.timeoutAt && ` \xB7 ${tr(
1446
+ "fluidgrids.toolApproval.autoResolvesAt",
1447
+ "auto-resolves at {{time}}",
1448
+ {
1449
+ time: new Date(request.timeoutAt).toLocaleString()
1450
+ }
1451
+ )}`
1452
+ ] })
1453
+ ] }),
1454
+ /* @__PURE__ */ jsxs(CardContent, { className: "space-y-3", children: [
1455
+ request.toolCalls.map((call) => /* @__PURE__ */ jsxs(
1456
+ "div",
1457
+ {
1458
+ className: "rounded-md border border-border-secondary bg-background-primary p-3",
1459
+ children: [
1460
+ /* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center gap-2", children: [
1461
+ /* @__PURE__ */ jsx(
1462
+ Icon,
1463
+ {
1464
+ icon: "mdi:function-variant",
1465
+ width: 14,
1466
+ className: "text-text-secondary"
1467
+ }
1468
+ ),
1469
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-text-primary", children: call.name })
1470
+ ] }),
1471
+ /* @__PURE__ */ jsx("pre", { className: "overflow-x-auto whitespace-pre-wrap break-all text-xs text-text-secondary", children: formatArgs(call.argsJson) })
1472
+ ]
1473
+ },
1474
+ call.callId
1475
+ )),
1476
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 pt-1", children: [
1477
+ /* @__PURE__ */ jsxs(
1478
+ Button,
1479
+ {
1480
+ size: "sm",
1481
+ disabled: loading,
1482
+ onClick: () => handleDecide("APPROVED"),
1483
+ "data-testid": "tool-approval-approve",
1484
+ children: [
1485
+ /* @__PURE__ */ jsx(Icon, { icon: "mdi:check", className: "mr-1.5", width: 16 }),
1486
+ tr("fluidgrids.toolApproval.approve", "Approve")
1487
+ ]
1488
+ }
1489
+ ),
1490
+ /* @__PURE__ */ jsxs(
1491
+ Button,
1492
+ {
1493
+ size: "sm",
1494
+ variant: "destructive",
1495
+ disabled: loading,
1496
+ onClick: () => setShowReject(true),
1497
+ "data-testid": "tool-approval-reject",
1498
+ children: [
1499
+ /* @__PURE__ */ jsx(Icon, { icon: "mdi:close", className: "mr-1.5", width: 16 }),
1500
+ tr("fluidgrids.toolApproval.reject", "Reject")
1501
+ ]
1502
+ }
1503
+ )
1504
+ ] })
1505
+ ] }),
1506
+ /* @__PURE__ */ jsx(
1507
+ RejectDialog,
1508
+ {
1509
+ open: showReject,
1510
+ onOpenChange: setShowReject,
1511
+ submitting: loading,
1512
+ onConfirm: (reason) => handleDecide("REJECTED", reason || void 0)
1513
+ }
1514
+ )
1515
+ ]
1516
+ }
1517
+ );
1518
+ }
1519
+ function ApprovalInboxPage() {
1520
+ const tr = useTr();
1521
+ const navigate = useNavigate();
1522
+ const { items, totalCount, loading, error, refetch } = usePendingToolApprovals();
1523
+ return /* @__PURE__ */ jsxs("div", { className: "p-4 sm:p-6 bg-background-primary min-h-screen", children: [
1524
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6", children: [
1525
+ /* @__PURE__ */ jsxs("div", { children: [
1526
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl sm:text-3xl font-bold text-text-primary", children: tr("fluidgrids.toolApproval.pageTitle", "Tool Approvals") }),
1527
+ /* @__PURE__ */ jsx("p", { className: "text-text-secondary mt-1", children: tr(
1528
+ "fluidgrids.toolApproval.pageSubtitle",
1529
+ "AI Agent tool calls waiting for a human decision before they run"
1530
+ ) })
1531
+ ] }),
1532
+ /* @__PURE__ */ jsxs(Button, { variant: "outline", onClick: () => navigate("../runs"), children: [
1533
+ /* @__PURE__ */ jsx(Icon, { icon: "mdi:arrow-left", className: "mr-2", width: 16 }),
1534
+ tr("fluidgrids.toolApproval.backToRuns", "Back to Runs")
1535
+ ] })
1536
+ ] }),
1537
+ loading && items.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-text-secondary text-sm", children: tr("fluidgrids.toolApproval.loading", "Loading\u2026") }),
1538
+ error && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", role: "alert", children: tr(
1539
+ "fluidgrids.toolApproval.loadFailed",
1540
+ "Failed to load pending approvals: {{message}}",
1541
+ {
1542
+ message: error.message
1543
+ }
1544
+ ) }),
1545
+ !loading && !error && items.length === 0 && /* @__PURE__ */ jsx(Card, { className: "border border-border-secondary bg-background-secondary", children: /* @__PURE__ */ jsxs(CardContent, { className: "flex flex-col items-center gap-2 py-10 text-center", children: [
1546
+ /* @__PURE__ */ jsx(
1547
+ Icon,
1548
+ {
1549
+ icon: "mdi:shield-check-outline",
1550
+ width: 32,
1551
+ className: "text-text-secondary"
1552
+ }
1553
+ ),
1554
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-text-primary", children: tr(
1555
+ "fluidgrids.toolApproval.emptyTitle",
1556
+ "Nothing waiting for approval"
1557
+ ) }),
1558
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-text-secondary", children: tr(
1559
+ "fluidgrids.toolApproval.emptySubtitle",
1560
+ "Runs will appear here when an AI Agent with tool approval enabled proposes a tool call."
1561
+ ) })
1562
+ ] }) }),
1563
+ items.length > 0 && /* @__PURE__ */ jsx(
1564
+ "div",
1565
+ {
1566
+ className: "mb-3 text-xs text-text-secondary",
1567
+ "data-testid": "tool-approval-count",
1568
+ children: tr(
1569
+ "fluidgrids.toolApproval.count",
1570
+ "{{count}} pending ({{total}} total)",
1571
+ {
1572
+ count: items.length,
1573
+ total: totalCount
1574
+ }
1575
+ )
1576
+ }
1577
+ ),
1578
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-4 lg:grid-cols-2", children: items.map((request) => /* @__PURE__ */ jsx(RequestCard, { request, onDecided: refetch }, request.id)) })
1579
+ ] });
1580
+ }
1183
1581
 
1184
1582
  // src/graphql/client-helpers.ts
1185
1583
  function createFluidGridsClients(config2) {
@@ -1243,7 +1641,7 @@ var NODE_STATUS_CONFIG = {
1243
1641
  error: { label: "Error", color: "text-red-600", bgColor: "bg-red-100" },
1244
1642
  skipped: { label: "Skipped", color: "text-gray-400", bgColor: "bg-gray-50" }
1245
1643
  };
1246
- var Textarea = React4.forwardRef(
1644
+ var Textarea2 = React4.forwardRef(
1247
1645
  ({ className, ...props }, ref) => {
1248
1646
  return /* @__PURE__ */ jsx(
1249
1647
  "textarea",
@@ -1258,7 +1656,7 @@ var Textarea = React4.forwardRef(
1258
1656
  );
1259
1657
  }
1260
1658
  );
1261
- Textarea.displayName = "Textarea";
1659
+ Textarea2.displayName = "Textarea";
1262
1660
  function Tabs2({
1263
1661
  className,
1264
1662
  ...props
@@ -1679,6 +2077,6 @@ var PopoverContent = React4.forwardRef(({ className, align = "center", sideOffse
1679
2077
  ) }));
1680
2078
  PopoverContent.displayName = PopoverPrimitive.Content.displayName;
1681
2079
 
1682
- export { Checkbox, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, FluidGridsRoot, NODE_STATUS_CONFIG, Popover, PopoverContent, PopoverTrigger, RUN_STATUS_CONFIG, ScrollArea, ScrollBar, Select2 as Select, SelectContent2 as SelectContent, SelectGroup, SelectItem2 as SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger2 as SelectTrigger, SelectValue2 as SelectValue, Separator, Switch2 as Switch, Tabs2 as Tabs, TabsContent2 as TabsContent, TabsList2 as TabsList, TabsTrigger2 as TabsTrigger, Textarea, VariablesPage, WorkflowEmbedPage, createFluidGridsClients, useCreateVariable, useDeleteVariable, useRevealVariableValue, useSaveSecretsProviderConfig, useSecretsProviderConfigs, useTestSecretsProviderConnection, useUpdateVariable, useVariables };
2080
+ export { ApprovalInboxPage, Checkbox, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, FluidGridsRoot, NODE_STATUS_CONFIG, Popover, PopoverContent, PopoverTrigger, RUN_STATUS_CONFIG, ScrollArea, ScrollBar, Select2 as Select, SelectContent2 as SelectContent, SelectGroup, SelectItem2 as SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger2 as SelectTrigger, SelectValue2 as SelectValue, Separator, Switch2 as Switch, Tabs2 as Tabs, TabsContent2 as TabsContent, TabsList2 as TabsList, TabsTrigger2 as TabsTrigger, Textarea2 as Textarea, VariablesPage, WorkflowEmbedPage, createFluidGridsClients, useCreateVariable, useDecideToolApproval, useDeleteVariable, usePendingToolApprovals, useRevealVariableValue, useSaveSecretsProviderConfig, useSecretsProviderConfigs, useTestSecretsProviderConnection, useUpdateVariable, useVariables };
1683
2081
  //# sourceMappingURL=index.js.map
1684
2082
  //# sourceMappingURL=index.js.map