@burdenoff/fluidgrids-fe-sdk 2026.1111.15 → 2026.1111.17
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/README.md +41 -0
- package/dist/AiStudioPage-kJHSsIcj.js +774 -0
- package/dist/ApprovalInboxPage-dbBDSJ-f.js +558 -0
- package/dist/DataSinkDashboardPage-1h2QsMzH.js +270 -0
- package/dist/DataTablesPage-BZy2z0LB.js +1690 -0
- package/dist/EntityTagPicker-BqRNsLCQ.js +135 -0
- package/dist/{ExecutionDetailPage-DO38vZ_F.js → ExecutionDetailPage-BdcZmLT4.js} +799 -4420
- package/dist/ExecutionDetailPage-DvQ667OE.d.ts +10 -0
- package/dist/ExecutionsListPage-BCUJoAIE.js +2742 -0
- package/dist/{FailureTriagePanel-BHiduL7A.js → FailureTriagePanel-B9hDQymi.js} +4 -97
- package/dist/{FluidGridsProvider-B9RR_CI_.js → FluidGridsProvider-dvZSNtSW.js} +28 -350
- package/dist/HistoryPage-BLoRpJ1R.d.ts +6 -0
- package/dist/{HistoryPage-CA42pU85.js → HistoryPage-U0tqupBN.js} +6 -4
- package/dist/HumanRequestCard-D-ooVPDn.js +439 -0
- package/dist/{InsightsPage-CBoN_Mj9.js → InsightsPage-CALo16eB.js} +17 -328
- package/dist/ManagedAiPolicyAuthority-BxwJ3ptd.js +40 -0
- package/dist/{ManagedAiPolicyAuthority-CGux_DaV.d.ts → ManagedAiPolicyAuthority-DM3t3wNr.d.ts} +1 -5
- package/dist/ManagedAiPolicyAuthorityContext-CNkxIOZ1.js +10 -0
- package/dist/{MissingNodesInstallModal-Dm0nMBXM.js → MissingNodesInstallModal-CMybsI8h.js} +3 -2
- package/dist/{NodesPage-ClvhUfbh.js → NodesPage-Bf5PhfBK.js} +7 -3
- package/dist/NodesPage-DEbnz9iU.d.ts +6 -0
- package/dist/{PublishCustomNodeAction-D2F7oN0S.js → PublishCustomNodeAction-5XXnSA3H.js} +3 -45
- package/dist/QuotaExhaustedDialog-7IFSaLyi.js +54 -0
- package/dist/RunnerStatusPill-Ctg4TJcP.js +187 -0
- package/dist/RunnersPage-CLCOCclc.js +467 -0
- package/dist/{ManagedAiPolicyAuthority-CKihmbJE.js → SettingsPage-CgkRlCM5.js} +19 -50
- package/dist/SettingsPage-DMHQFj5-.d.ts +9 -0
- package/dist/TagFilterSelect-DCBOH6Ou.js +41 -0
- package/dist/TaskQueuesPage-9VgSnLdL.js +951 -0
- package/dist/TemplateCard-DsC0KNH1.js +1416 -0
- package/dist/{TemplateConfigurationModal-Bs_V-E4u.js → TemplateConfigurationModal-BhGWIzpm.js} +3 -2
- package/dist/{TemplateDetailPanel-MlT39VLG.js → TemplateDetailPanel-D4cUVr-m.js} +3 -3
- package/dist/TemplatePreview-GthzCOSo.js +231 -0
- package/dist/TemplatesPage-BG3H-6v6.d.ts +10 -0
- package/dist/{TemplatesPage-CHVpWBuw.js → TemplatesPage-CsDNT54N.js} +11 -228
- package/dist/VariablesPage-67WR_lVE.js +949 -0
- package/dist/{VersionDiffDialog-Dq2xlPFI.js → VersionDiffDialog-DSN0mU0f.js} +2 -1
- package/dist/WorkflowBuilder-DEi_9YOb.js +555 -0
- package/dist/{WorkflowSearchRedirect-3gyHcYY2.js → WorkflowBuilder-iPqXgbYr.js} +19 -585
- package/dist/{WorkflowProposalCard-8Vr56kQa.js → WorkflowProposalCard-qrEeNyYG.js} +4 -405
- package/dist/WorkflowSearchRedirect-DwDNZ_q5.js +36 -0
- package/dist/{WorkflowShareDialog-0Ai1HED-.js → WorkflowShareDialog-B_gn968c.js} +6 -52
- package/dist/{WorkflowsList-CJ3KmMHX.js → WorkflowsList-CA20dVIl.js} +11 -6
- package/dist/WorkflowsList-CcNDQge5.d.ts +6 -0
- package/dist/agent-form-Bfvm32j1.js +83 -0
- package/dist/{agentTools-B1IK1JEO.js → agentTools-CkRJB7r5.js} +3 -2
- package/dist/{ai-usage-D5p7ZeRR.js → ai-usage-glCwHyQz.js} +1 -1
- package/dist/api-BrCHB5oA.js +224 -0
- package/dist/api-C8e0iV-Q.js +55 -0
- package/dist/button-DG7y_FVJ.js +48 -0
- package/dist/{canvasNoteTools-eewVHVuy.js → canvasNoteTools-CQIc7Oxg.js} +2 -2
- package/dist/client-C9XSzSXr.js +1147 -0
- package/dist/{codec-client-DstqyzTN.js → codec-client-CJ95Rmqo.js} +1 -1
- package/dist/constants-B2lhWNoN.js +202 -0
- package/dist/constants-XdvUxeWK.d.ts +162 -0
- package/dist/dialog-Cjb4lB4D.js +105 -0
- package/dist/executions.d.ts +1 -1
- package/dist/executions.js +2 -1
- package/dist/feature-flags-DM5myWaD.js +296 -0
- package/dist/flags.d.ts +3 -0
- package/dist/flags.js +4 -0
- package/dist/{flow-builder-C_3sY3pZ.js → flow-builder-y2Y1iG4Y.js} +182 -1821
- package/dist/{node-endpoint-configs-DHFGA5MQ.js → graphql-operation-types-kX-MMjgW.js} +1 -1194
- package/dist/{graphql-types-XcDLtFwU.js → graphql-types-CDKVWH2R.js} +2 -202
- package/dist/history.d.ts +1 -1
- package/dist/history.js +1 -1
- package/dist/index-72XFgvbn.d.ts +132 -0
- package/dist/index-BcdtKBM-.d.ts +56 -0
- package/dist/{index-B3BWqafN.d.ts → index-DjiFgEQl.d.ts} +5 -131
- package/dist/{index-DRf25-D3.d.ts → index-W2o__Nm6.d.ts} +32 -214
- package/dist/index.d.ts +13 -9
- package/dist/index.js +50 -5300
- package/dist/{node-detail-view-CAws5qjg.js → node-detail-view-B91kXzIy.js} +11 -8
- package/dist/node-endpoint-configs-B5bjNMDW.js +37 -0
- package/dist/node-registry-refresh-Dy0uRg9f.js +84 -0
- package/dist/nodes.d.ts +1 -1
- package/dist/nodes.js +1 -1
- package/dist/observability.d.ts +1 -1
- package/dist/observability.js +2 -1
- package/dist/{quick-config-panel-DOzNkSVV.js → quick-config-panel-DggPl1vY.js} +2 -2
- package/dist/rolldown-runtime-hDbaFkMy.js +18 -0
- package/dist/run-display-BycKYSU0.js +46 -0
- package/dist/run-list-links-ByYC0OlV.js +98 -0
- package/dist/settings.d.ts +2 -1
- package/dist/settings.js +2 -1
- package/dist/shared-CMfn30xT.js +196 -0
- package/dist/templates.d.ts +1 -1
- package/dist/templates.js +2 -1
- package/dist/theme-provider-B3mIuz2d.js +11 -0
- package/dist/{useAgentDefinitions-NFtLytDR.js → useAgentDefinitions-Dpz0HDTq.js} +2 -1
- package/dist/useDurableParity-CzUQMFJ3.js +415 -0
- package/dist/useEntityTags-DhnsZcTl.js +236 -0
- package/dist/{useExecutions-DkS0FTfw.js → useExecutions-9aLuOt1c.js} +4 -3
- package/dist/{useObservability-BkxhHZsX.js → useObservability-CfEw3ZFD.js} +2 -2
- package/dist/{useRunHistory-DHehG09Z.js → useRunHistory-B1xJDwl1.js} +4 -3
- package/dist/{useWorkflows-DyhtoVwp.js → useWorkflows-BlVVu8NM.js} +3 -2
- package/dist/utils-uaL5UVxb.js +65 -0
- package/dist/workflow-builder.d.ts +1 -1
- package/dist/workflow-builder.js +4 -2
- package/dist/workflow-host.d.ts +6 -0
- package/dist/workflow-host.js +9 -0
- package/dist/workflow-pages.d.ts +55 -0
- package/dist/workflow-pages.js +113 -0
- package/dist/workflows-list.d.ts +1 -1
- package/dist/workflows-list.js +1 -1
- package/package.json +13 -1
- package/dist/ExecutionDetailPage-MvsnOnE1.d.ts +0 -4
- package/dist/HistoryPage-BqUizaFv.d.ts +0 -3
- package/dist/NodesPage-C97-9K2Y.d.ts +0 -3
- package/dist/TemplatesPage-D0qnXzHh.d.ts +0 -4
- package/dist/WorkflowsList-0d5GSKdh.d.ts +0 -3
- package/dist/agent-form-BvMWhQ42.js +0 -496
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { t as __exportAll } from "./rolldown-runtime-hDbaFkMy.js";
|
|
3
|
+
import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
|
|
4
|
+
import { r as usePendingHumanRequests, t as HumanRequestCard } from "./HumanRequestCard-D-ooVPDn.js";
|
|
5
|
+
import { t as FluidGridsDocsLink } from "./FluidGridsDocsLink-JxkBpOeb.js";
|
|
6
|
+
import { _ as throwIfResultError, h as isForbiddenError, n as getFluidGridsClient } from "./client-C9XSzSXr.js";
|
|
7
|
+
import { i as SdkPermissionButton, s as useFluidGridsPermission, t as FLUIDGRIDS_PERMISSIONS } from "./constants-B2lhWNoN.js";
|
|
8
|
+
import { useCallback, useEffect, useState } from "react";
|
|
9
|
+
import { useNavigate } from "react-router";
|
|
10
|
+
import { Icon } from "@iconify/react";
|
|
11
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
+
import { toast } from "sonner";
|
|
13
|
+
import { gql } from "@apollo/client";
|
|
14
|
+
import { Badge, Card, CardContent, CardHeader, Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, Label, Textarea } from "@burdenoff/fe-libs/ui";
|
|
15
|
+
import { Button as Button$1 } from "@burdenoff/fe-libs/button";
|
|
16
|
+
|
|
17
|
+
// src/modules/tool-approval/hooks/useToolApprovals.ts
|
|
18
|
+
/**
|
|
19
|
+
* Custom hooks for the AI Agent tool-approval inbox (BOFF-7521).
|
|
20
|
+
* Queries/mutates wspace-workflow-svc via the federated GraphQL gateway —
|
|
21
|
+
* mirrors the variables module's hook pattern
|
|
22
|
+
* (`modules/variables/hooks/useVariables.ts`).
|
|
23
|
+
*/
|
|
24
|
+
const TOOL_APPROVAL_FIELDS = gql`
|
|
25
|
+
fragment ToolApprovalRequestFields on ToolApprovalRequest {
|
|
26
|
+
id
|
|
27
|
+
runId
|
|
28
|
+
nodeId
|
|
29
|
+
workflowId
|
|
30
|
+
workflowKey
|
|
31
|
+
status
|
|
32
|
+
toolCalls {
|
|
33
|
+
callId
|
|
34
|
+
name
|
|
35
|
+
argsJson
|
|
36
|
+
}
|
|
37
|
+
decisions {
|
|
38
|
+
callId
|
|
39
|
+
decision
|
|
40
|
+
reason
|
|
41
|
+
}
|
|
42
|
+
timeoutAt
|
|
43
|
+
timeoutAction
|
|
44
|
+
requestedAt
|
|
45
|
+
decidedAt
|
|
46
|
+
decidedBy
|
|
47
|
+
}
|
|
48
|
+
`;
|
|
49
|
+
const GET_PENDING_TOOL_APPROVALS = gql`
|
|
50
|
+
${TOOL_APPROVAL_FIELDS}
|
|
51
|
+
query GetPendingToolApprovals($limit: Int, $offset: Int) {
|
|
52
|
+
pendingToolApprovals(limit: $limit, offset: $offset) {
|
|
53
|
+
totalCount
|
|
54
|
+
items {
|
|
55
|
+
...ToolApprovalRequestFields
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
`;
|
|
60
|
+
const DECIDE_TOOL_APPROVAL = gql`
|
|
61
|
+
${TOOL_APPROVAL_FIELDS}
|
|
62
|
+
mutation DecideToolApproval(
|
|
63
|
+
$id: ID!
|
|
64
|
+
$decisions: [ToolApprovalDecisionInput!]!
|
|
65
|
+
) {
|
|
66
|
+
decideToolApproval(id: $id, decisions: $decisions) {
|
|
67
|
+
success
|
|
68
|
+
error
|
|
69
|
+
resumedFromNodeId
|
|
70
|
+
request {
|
|
71
|
+
...ToolApprovalRequestFields
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
`;
|
|
76
|
+
/**
|
|
77
|
+
* Polls the pending tool-approval inbox. Short polling interval (not a
|
|
78
|
+
* subscription) — approvals are rare/low-volume and the caller wants a
|
|
79
|
+
* near-real-time inbox without standing up a dedicated subscription channel
|
|
80
|
+
* for a v1 feature.
|
|
81
|
+
*
|
|
82
|
+
* Pass `enabled: false` when the caller lacks `tool-approval-request:read`:
|
|
83
|
+
* the query is then never sent, so the gateway's FORBIDDEN never reaches the
|
|
84
|
+
* shell's global error toast (BOFF-10104). If the gateway still denies the
|
|
85
|
+
* query, `forbidden` turns true and polling stops.
|
|
86
|
+
*/
|
|
87
|
+
function usePendingToolApprovals(options) {
|
|
88
|
+
const limit = options?.limit ?? 50;
|
|
89
|
+
const offset = options?.offset ?? 0;
|
|
90
|
+
const pollIntervalMs = options?.pollIntervalMs ?? 15e3;
|
|
91
|
+
const enabled = options?.enabled ?? true;
|
|
92
|
+
const [items, setItems] = useState([]);
|
|
93
|
+
const [totalCount, setTotalCount] = useState(0);
|
|
94
|
+
const [loading, setLoading] = useState(enabled);
|
|
95
|
+
const [error, setError] = useState(null);
|
|
96
|
+
const [forbidden, setForbidden] = useState(false);
|
|
97
|
+
const fetchData = useCallback(async () => {
|
|
98
|
+
if (!enabled) return;
|
|
99
|
+
setError(null);
|
|
100
|
+
try {
|
|
101
|
+
const client = getFluidGridsClient();
|
|
102
|
+
const result = throwIfResultError(await client.query({
|
|
103
|
+
query: GET_PENDING_TOOL_APPROVALS,
|
|
104
|
+
variables: {
|
|
105
|
+
limit,
|
|
106
|
+
offset
|
|
107
|
+
},
|
|
108
|
+
fetchPolicy: "network-only"
|
|
109
|
+
}));
|
|
110
|
+
setItems(result.data?.pendingToolApprovals?.items ?? []);
|
|
111
|
+
setTotalCount(result.data?.pendingToolApprovals?.totalCount ?? 0);
|
|
112
|
+
} catch (err) {
|
|
113
|
+
if (isForbiddenError(err)) {
|
|
114
|
+
setForbidden(true);
|
|
115
|
+
setItems([]);
|
|
116
|
+
setTotalCount(0);
|
|
117
|
+
} else setError(err);
|
|
118
|
+
} finally {
|
|
119
|
+
setLoading(false);
|
|
120
|
+
}
|
|
121
|
+
}, [
|
|
122
|
+
enabled,
|
|
123
|
+
limit,
|
|
124
|
+
offset
|
|
125
|
+
]);
|
|
126
|
+
useEffect(() => {
|
|
127
|
+
setForbidden(false);
|
|
128
|
+
if (!enabled) {
|
|
129
|
+
setItems([]);
|
|
130
|
+
setTotalCount(0);
|
|
131
|
+
setError(null);
|
|
132
|
+
}
|
|
133
|
+
}, [enabled]);
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
if (!enabled || forbidden) {
|
|
136
|
+
setLoading(false);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
setLoading(true);
|
|
140
|
+
fetchData();
|
|
141
|
+
if (pollIntervalMs <= 0) return;
|
|
142
|
+
const interval = setInterval(fetchData, pollIntervalMs);
|
|
143
|
+
return () => clearInterval(interval);
|
|
144
|
+
}, [
|
|
145
|
+
enabled,
|
|
146
|
+
fetchData,
|
|
147
|
+
forbidden,
|
|
148
|
+
pollIntervalMs
|
|
149
|
+
]);
|
|
150
|
+
return {
|
|
151
|
+
items,
|
|
152
|
+
totalCount,
|
|
153
|
+
loading,
|
|
154
|
+
error,
|
|
155
|
+
forbidden: forbidden || !enabled,
|
|
156
|
+
refetch: fetchData
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function useDecideToolApproval() {
|
|
160
|
+
const [loading, setLoading] = useState(false);
|
|
161
|
+
const [error, setError] = useState(null);
|
|
162
|
+
const decide = useCallback(async (id, decisions) => {
|
|
163
|
+
setLoading(true);
|
|
164
|
+
setError(null);
|
|
165
|
+
try {
|
|
166
|
+
const client = getFluidGridsClient();
|
|
167
|
+
const response = throwIfResultError(await client.mutate({
|
|
168
|
+
mutation: DECIDE_TOOL_APPROVAL,
|
|
169
|
+
variables: {
|
|
170
|
+
id,
|
|
171
|
+
decisions
|
|
172
|
+
}
|
|
173
|
+
})).data?.decideToolApproval;
|
|
174
|
+
if (!response?.success) throw new Error(response?.error ?? "Failed to record the decision");
|
|
175
|
+
return response;
|
|
176
|
+
} catch (err) {
|
|
177
|
+
setError(err);
|
|
178
|
+
throw err;
|
|
179
|
+
} finally {
|
|
180
|
+
setLoading(false);
|
|
181
|
+
}
|
|
182
|
+
}, []);
|
|
183
|
+
return {
|
|
184
|
+
decide,
|
|
185
|
+
decideAll: useCallback(async (request, decision, reason) => {
|
|
186
|
+
return decide(request.id, request.toolCalls.map((call) => ({
|
|
187
|
+
callId: call.callId,
|
|
188
|
+
decision,
|
|
189
|
+
reason
|
|
190
|
+
})));
|
|
191
|
+
}, [decide]),
|
|
192
|
+
loading,
|
|
193
|
+
error
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// src/modules/tool-approval/utils/timeout.ts
|
|
198
|
+
/**
|
|
199
|
+
* Classifies timeout metadata without changing server-owned request status.
|
|
200
|
+
* The timeout sweep remains authoritative for auto-resolving WAITING runs.
|
|
201
|
+
*/
|
|
202
|
+
function getToolApprovalTimeoutState(timeoutAt, nowMs = Date.now()) {
|
|
203
|
+
if (!timeoutAt) return "NONE";
|
|
204
|
+
const timeoutMs = Date.parse(timeoutAt);
|
|
205
|
+
if (!Number.isFinite(timeoutMs)) return "NONE";
|
|
206
|
+
return timeoutMs <= nowMs ? "DUE" : "ACTIVE";
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/modules/tool-approval/pages/ApprovalInboxPage.tsx
|
|
210
|
+
/**
|
|
211
|
+
* Tool Approval Inbox (BOFF-7521)
|
|
212
|
+
*
|
|
213
|
+
* Human-in-the-loop gate for AI Agent tool calls: lists every run currently
|
|
214
|
+
* paused waiting for a decision, shows the proposed tool call(s) and their
|
|
215
|
+
* arguments, and lets the reviewer approve or reject the whole batch (or, in
|
|
216
|
+
* a future iteration, individual calls). Approving/rejecting resumes the
|
|
217
|
+
* underlying run via `decideToolApproval`, which reuses the exact same
|
|
218
|
+
* WAITING/`resumeWorkflowRun` mechanism the Wait node uses.
|
|
219
|
+
*/
|
|
220
|
+
var ApprovalInboxPage_exports = /* @__PURE__ */ __exportAll({ ApprovalInboxPage: () => ApprovalInboxPage });
|
|
221
|
+
function formatArgs(argsJson) {
|
|
222
|
+
try {
|
|
223
|
+
return JSON.stringify(JSON.parse(argsJson), null, 2);
|
|
224
|
+
} catch {
|
|
225
|
+
return argsJson;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function timeAgo(iso, tr) {
|
|
229
|
+
const then = new Date(iso).getTime();
|
|
230
|
+
const diffMs = Date.now() - then;
|
|
231
|
+
const minutes = Math.floor(diffMs / 6e4);
|
|
232
|
+
if (minutes < 1) return tr("fluidgrids.toolApproval.justNow", "just now");
|
|
233
|
+
if (minutes < 60) return tr("fluidgrids.toolApproval.minutesAgo", "{{minutes}}m ago", { minutes });
|
|
234
|
+
const hours = Math.floor(minutes / 60);
|
|
235
|
+
if (hours < 24) return tr("fluidgrids.toolApproval.hoursAgo", "{{hours}}h ago", { hours });
|
|
236
|
+
return tr("fluidgrids.toolApproval.daysAgo", "{{days}}d ago", { days: Math.floor(hours / 24) });
|
|
237
|
+
}
|
|
238
|
+
function RejectDialog({ open, onOpenChange, onConfirm, submitting }) {
|
|
239
|
+
const tr = useTr();
|
|
240
|
+
const [reason, setReason] = useState("");
|
|
241
|
+
return /* @__PURE__ */ jsx(Dialog, {
|
|
242
|
+
open,
|
|
243
|
+
onOpenChange,
|
|
244
|
+
children: /* @__PURE__ */ jsxs(DialogContent, { children: [
|
|
245
|
+
/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: tr("fluidgrids.toolApproval.rejectDialogTitle", "Reject tool call") }) }),
|
|
246
|
+
/* @__PURE__ */ jsxs("div", {
|
|
247
|
+
className: "space-y-1.5",
|
|
248
|
+
children: [/* @__PURE__ */ jsx(Label, {
|
|
249
|
+
htmlFor: "reject-reason",
|
|
250
|
+
children: tr("fluidgrids.toolApproval.rejectReasonLabel", "Reason (optional)")
|
|
251
|
+
}), /* @__PURE__ */ jsx(Textarea, {
|
|
252
|
+
id: "reject-reason",
|
|
253
|
+
value: reason,
|
|
254
|
+
onChange: (e) => setReason(e.target.value),
|
|
255
|
+
placeholder: tr("fluidgrids.toolApproval.rejectReasonPlaceholder", "Why is this call being rejected? The agent sees this.")
|
|
256
|
+
})]
|
|
257
|
+
}),
|
|
258
|
+
/* @__PURE__ */ jsxs(DialogFooter, { children: [/* @__PURE__ */ jsx(Button$1, {
|
|
259
|
+
variant: "outline",
|
|
260
|
+
onClick: () => onOpenChange(false),
|
|
261
|
+
disabled: submitting,
|
|
262
|
+
children: tr("fluidgrids.toolApproval.cancel", "Cancel")
|
|
263
|
+
}), /* @__PURE__ */ jsx(Button$1, {
|
|
264
|
+
variant: "destructive",
|
|
265
|
+
onClick: () => onConfirm(reason),
|
|
266
|
+
disabled: submitting,
|
|
267
|
+
children: submitting ? tr("fluidgrids.toolApproval.rejecting", "Rejecting…") : tr("fluidgrids.toolApproval.rejectConfirm", "Reject")
|
|
268
|
+
})] })
|
|
269
|
+
] })
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
function RequestCard({ request, onDecided }) {
|
|
273
|
+
const tr = useTr();
|
|
274
|
+
const { decideAll, loading } = useDecideToolApproval();
|
|
275
|
+
const [showReject, setShowReject] = useState(false);
|
|
276
|
+
const [nowMs, setNowMs] = useState(() => Date.now());
|
|
277
|
+
const timeoutState = getToolApprovalTimeoutState(request.timeoutAt, nowMs);
|
|
278
|
+
const timeoutDue = timeoutState === "DUE";
|
|
279
|
+
useEffect(() => {
|
|
280
|
+
if (timeoutState !== "ACTIVE") return;
|
|
281
|
+
const interval = setInterval(() => setNowMs(Date.now()), 1e3);
|
|
282
|
+
return () => clearInterval(interval);
|
|
283
|
+
}, [timeoutState]);
|
|
284
|
+
const handleDecide = async (decision, reason) => {
|
|
285
|
+
try {
|
|
286
|
+
await decideAll(request, decision, reason);
|
|
287
|
+
toast.success(decision === "APPROVED" ? tr("fluidgrids.toolApproval.approvedToast", "Tool call approved — run resumed") : tr("fluidgrids.toolApproval.rejectedToast", "Tool call rejected — run resumed"));
|
|
288
|
+
setShowReject(false);
|
|
289
|
+
onDecided();
|
|
290
|
+
} catch (err) {
|
|
291
|
+
toast.error(err instanceof Error ? err.message : tr("fluidgrids.toolApproval.decisionFailed", "Failed to record the decision"));
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
return /* @__PURE__ */ jsxs(Card, {
|
|
295
|
+
className: "border border-border-secondary bg-background-secondary",
|
|
296
|
+
"data-testid": "tool-approval-card",
|
|
297
|
+
"data-request-id": request.id,
|
|
298
|
+
children: [
|
|
299
|
+
/* @__PURE__ */ jsxs(CardHeader, {
|
|
300
|
+
className: "pb-3",
|
|
301
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
302
|
+
className: "flex flex-wrap items-center justify-between gap-2",
|
|
303
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
304
|
+
className: "flex items-center gap-2",
|
|
305
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
306
|
+
icon: "mdi:robot-outline",
|
|
307
|
+
width: 18,
|
|
308
|
+
className: "text-text-secondary"
|
|
309
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
310
|
+
className: "text-sm font-semibold text-text-primary",
|
|
311
|
+
children: [
|
|
312
|
+
tr("fluidgrids.toolApproval.nodeLabel", "Node"),
|
|
313
|
+
":",
|
|
314
|
+
" ",
|
|
315
|
+
request.nodeId
|
|
316
|
+
]
|
|
317
|
+
})]
|
|
318
|
+
}), /* @__PURE__ */ jsx(Badge, {
|
|
319
|
+
variant: "outline",
|
|
320
|
+
className: "font-mono text-[10px]",
|
|
321
|
+
children: request.workflowKey
|
|
322
|
+
})]
|
|
323
|
+
}), /* @__PURE__ */ jsxs("p", {
|
|
324
|
+
className: "text-xs text-text-secondary",
|
|
325
|
+
children: [tr("fluidgrids.toolApproval.requestedAt", "Requested {{when}}", { when: timeAgo(request.requestedAt, tr) }), request.timeoutAt && timeoutDue ? ` · ${tr("fluidgrids.toolApproval.timeoutDue", "timeout reached; automatic decision pending")}` : request.timeoutAt && ` · ${tr("fluidgrids.toolApproval.autoResolvesAt", "auto-resolves at {{time}}", { time: new Date(request.timeoutAt).toLocaleString() })}`]
|
|
326
|
+
})]
|
|
327
|
+
}),
|
|
328
|
+
/* @__PURE__ */ jsxs(CardContent, {
|
|
329
|
+
className: "space-y-3",
|
|
330
|
+
children: [request.toolCalls.map((call) => /* @__PURE__ */ jsxs("div", {
|
|
331
|
+
className: "rounded-md border border-border-secondary bg-background-primary p-3",
|
|
332
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
333
|
+
className: "mb-1 flex items-center gap-2",
|
|
334
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
335
|
+
icon: "mdi:function-variant",
|
|
336
|
+
width: 14,
|
|
337
|
+
className: "text-text-secondary"
|
|
338
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
339
|
+
className: "text-sm font-medium text-text-primary",
|
|
340
|
+
children: call.name
|
|
341
|
+
})]
|
|
342
|
+
}), /* @__PURE__ */ jsx("pre", {
|
|
343
|
+
className: "overflow-x-auto whitespace-pre-wrap break-all text-xs text-text-secondary",
|
|
344
|
+
children: formatArgs(call.argsJson)
|
|
345
|
+
})]
|
|
346
|
+
}, call.callId)), /* @__PURE__ */ jsxs("div", {
|
|
347
|
+
className: "flex items-center gap-2 pt-1",
|
|
348
|
+
children: [/* @__PURE__ */ jsx(SdkPermissionButton, {
|
|
349
|
+
permission: FLUIDGRIDS_PERMISSIONS.TOOL_APPROVAL_REQUEST_EXECUTE,
|
|
350
|
+
deniedTooltip: tr("fluidgrids.toolApproval.decideDeniedTooltip", "You don't have permission to decide tool approvals"),
|
|
351
|
+
children: /* @__PURE__ */ jsxs(Button$1, {
|
|
352
|
+
"data-agent": "confirm",
|
|
353
|
+
size: "sm",
|
|
354
|
+
disabled: loading || timeoutDue,
|
|
355
|
+
onClick: () => handleDecide("APPROVED"),
|
|
356
|
+
"data-testid": "tool-approval-approve",
|
|
357
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
358
|
+
icon: "mdi:check",
|
|
359
|
+
className: "mr-1.5",
|
|
360
|
+
width: 16
|
|
361
|
+
}), tr("fluidgrids.toolApproval.approve", "Approve")]
|
|
362
|
+
})
|
|
363
|
+
}), /* @__PURE__ */ jsx(SdkPermissionButton, {
|
|
364
|
+
permission: FLUIDGRIDS_PERMISSIONS.TOOL_APPROVAL_REQUEST_EXECUTE,
|
|
365
|
+
deniedTooltip: tr("fluidgrids.toolApproval.decideDeniedTooltip", "You don't have permission to decide tool approvals"),
|
|
366
|
+
children: /* @__PURE__ */ jsxs(Button$1, {
|
|
367
|
+
size: "sm",
|
|
368
|
+
variant: "destructive",
|
|
369
|
+
disabled: loading || timeoutDue,
|
|
370
|
+
onClick: () => setShowReject(true),
|
|
371
|
+
"data-testid": "tool-approval-reject",
|
|
372
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
373
|
+
icon: "mdi:close",
|
|
374
|
+
className: "mr-1.5",
|
|
375
|
+
width: 16
|
|
376
|
+
}), tr("fluidgrids.toolApproval.reject", "Reject")]
|
|
377
|
+
})
|
|
378
|
+
})]
|
|
379
|
+
})]
|
|
380
|
+
}),
|
|
381
|
+
/* @__PURE__ */ jsx(RejectDialog, {
|
|
382
|
+
open: showReject,
|
|
383
|
+
onOpenChange: setShowReject,
|
|
384
|
+
submitting: loading,
|
|
385
|
+
onConfirm: (reason) => handleDecide("REJECTED", reason || void 0)
|
|
386
|
+
})
|
|
387
|
+
]
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
const HUMAN_PAGE_SIZE = 50;
|
|
391
|
+
function ApprovalInboxPage() {
|
|
392
|
+
const tr = useTr();
|
|
393
|
+
const navigate = useNavigate();
|
|
394
|
+
const { allowed: canReadToolApprovals, isLoading: permissionsLoading } = useFluidGridsPermission(FLUIDGRIDS_PERMISSIONS.TOOL_APPROVAL_REQUEST_READ);
|
|
395
|
+
const { items, totalCount, loading, error, forbidden, refetch } = usePendingToolApprovals({ enabled: canReadToolApprovals });
|
|
396
|
+
const toolsForbidden = forbidden && !permissionsLoading;
|
|
397
|
+
const [humanOffset, setHumanOffset] = useState(0);
|
|
398
|
+
const human = usePendingHumanRequests({
|
|
399
|
+
limit: HUMAN_PAGE_SIZE,
|
|
400
|
+
offset: humanOffset
|
|
401
|
+
});
|
|
402
|
+
const humanTotal = human.totalCount;
|
|
403
|
+
useEffect(() => {
|
|
404
|
+
if (humanOffset > 0 && humanOffset >= humanTotal) setHumanOffset(Math.max(0, Math.ceil(humanTotal / HUMAN_PAGE_SIZE) - 1) * HUMAN_PAGE_SIZE);
|
|
405
|
+
}, [humanOffset, humanTotal]);
|
|
406
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
407
|
+
className: "p-4 sm:p-6 bg-background-primary min-h-screen",
|
|
408
|
+
children: [
|
|
409
|
+
/* @__PURE__ */ jsxs("div", {
|
|
410
|
+
className: "flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6",
|
|
411
|
+
"data-tour": "fluidgrids-approvals-header",
|
|
412
|
+
children: [/* @__PURE__ */ jsxs("div", { children: [
|
|
413
|
+
/* @__PURE__ */ jsx("h1", {
|
|
414
|
+
className: "text-2xl sm:text-3xl font-bold text-text-primary",
|
|
415
|
+
children: tr("fluidgrids.approvals.pageTitle", "Approvals")
|
|
416
|
+
}),
|
|
417
|
+
/* @__PURE__ */ jsx("p", {
|
|
418
|
+
className: "text-text-secondary mt-1",
|
|
419
|
+
children: tr("fluidgrids.approvals.pageSubtitle", "Requests from your workflows waiting for a person, and AI Agent tool calls waiting for a decision")
|
|
420
|
+
}),
|
|
421
|
+
/* @__PURE__ */ jsx(FluidGridsDocsLink, {
|
|
422
|
+
topic: "approvals",
|
|
423
|
+
className: "mt-1"
|
|
424
|
+
})
|
|
425
|
+
] }), /* @__PURE__ */ jsxs(Button$1, {
|
|
426
|
+
variant: "outline",
|
|
427
|
+
onClick: () => navigate("../runs"),
|
|
428
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
429
|
+
icon: "mdi:arrow-left",
|
|
430
|
+
className: "mr-2",
|
|
431
|
+
width: 16
|
|
432
|
+
}), tr("fluidgrids.toolApproval.backToRuns", "Back to Runs")]
|
|
433
|
+
})]
|
|
434
|
+
}),
|
|
435
|
+
(loading || permissionsLoading) && items.length === 0 && /* @__PURE__ */ jsx("p", {
|
|
436
|
+
className: "text-text-secondary text-sm",
|
|
437
|
+
children: tr("fluidgrids.toolApproval.loading", "Loading…")
|
|
438
|
+
}),
|
|
439
|
+
error && /* @__PURE__ */ jsx("p", {
|
|
440
|
+
className: "text-sm text-destructive",
|
|
441
|
+
role: "alert",
|
|
442
|
+
children: tr("fluidgrids.toolApproval.loadFailed", "Failed to load pending approvals: {{message}}", { message: error.message })
|
|
443
|
+
}),
|
|
444
|
+
human.items.length > 0 && /* @__PURE__ */ jsxs("section", {
|
|
445
|
+
className: "mb-8",
|
|
446
|
+
"data-testid": "human-requests-section",
|
|
447
|
+
"data-tour": "fluidgrids-approvals-human",
|
|
448
|
+
children: [
|
|
449
|
+
/* @__PURE__ */ jsx("h2", {
|
|
450
|
+
className: "mb-1 text-lg font-semibold text-text-primary",
|
|
451
|
+
children: tr("fluidgrids.humanResponse.inboxHeading", "Waiting for a response")
|
|
452
|
+
}),
|
|
453
|
+
/* @__PURE__ */ jsx("p", {
|
|
454
|
+
className: "mb-3 text-xs text-text-secondary",
|
|
455
|
+
children: tr("fluidgrids.humanResponse.inboxSubtitle", "Workflows paused on a person — approve, decline or answer to let them continue.")
|
|
456
|
+
}),
|
|
457
|
+
/* @__PURE__ */ jsx("div", {
|
|
458
|
+
className: "grid grid-cols-1 gap-4 lg:grid-cols-2",
|
|
459
|
+
children: human.items.map((request) => /* @__PURE__ */ jsx(HumanRequestCard, {
|
|
460
|
+
request,
|
|
461
|
+
onAnswered: human.refetch
|
|
462
|
+
}, request.id))
|
|
463
|
+
}),
|
|
464
|
+
human.totalCount > HUMAN_PAGE_SIZE && /* @__PURE__ */ jsxs("div", {
|
|
465
|
+
className: "mt-4 flex items-center gap-3",
|
|
466
|
+
children: [
|
|
467
|
+
/* @__PURE__ */ jsx("span", {
|
|
468
|
+
className: "text-xs text-text-secondary",
|
|
469
|
+
children: tr("fluidgrids.humanResponse.pageRange", "{{from}}–{{to}} of {{total}}", {
|
|
470
|
+
from: humanOffset + 1,
|
|
471
|
+
to: humanOffset + human.items.length,
|
|
472
|
+
total: human.totalCount
|
|
473
|
+
})
|
|
474
|
+
}),
|
|
475
|
+
/* @__PURE__ */ jsx(Button$1, {
|
|
476
|
+
variant: "outline",
|
|
477
|
+
size: "sm",
|
|
478
|
+
disabled: humanOffset === 0,
|
|
479
|
+
onClick: () => setHumanOffset((current) => Math.max(0, current - HUMAN_PAGE_SIZE)),
|
|
480
|
+
children: tr("fluidgrids.humanResponse.previousPage", "Previous")
|
|
481
|
+
}),
|
|
482
|
+
/* @__PURE__ */ jsx(Button$1, {
|
|
483
|
+
variant: "outline",
|
|
484
|
+
size: "sm",
|
|
485
|
+
disabled: humanOffset + HUMAN_PAGE_SIZE >= human.totalCount,
|
|
486
|
+
onClick: () => setHumanOffset((current) => current + HUMAN_PAGE_SIZE),
|
|
487
|
+
children: tr("fluidgrids.humanResponse.nextPage", "Next")
|
|
488
|
+
})
|
|
489
|
+
]
|
|
490
|
+
})
|
|
491
|
+
]
|
|
492
|
+
}),
|
|
493
|
+
human.error && /* @__PURE__ */ jsx("p", {
|
|
494
|
+
className: "mb-4 text-sm text-destructive",
|
|
495
|
+
role: "alert",
|
|
496
|
+
children: tr("fluidgrids.humanResponse.loadFailed", "Failed to load requests waiting for a response: {{message}}", { message: human.error.message })
|
|
497
|
+
}),
|
|
498
|
+
toolsForbidden && /* @__PURE__ */ jsx(Card, {
|
|
499
|
+
className: "mb-6 border border-border-secondary bg-background-secondary",
|
|
500
|
+
"data-testid": "tool-approval-no-access",
|
|
501
|
+
children: /* @__PURE__ */ jsxs(CardContent, {
|
|
502
|
+
className: "flex items-start gap-3 py-4",
|
|
503
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
504
|
+
icon: "mdi:lock-outline",
|
|
505
|
+
width: 20,
|
|
506
|
+
className: "mt-0.5 shrink-0 text-text-secondary"
|
|
507
|
+
}), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", {
|
|
508
|
+
className: "text-sm font-medium text-text-primary",
|
|
509
|
+
children: tr("fluidgrids.toolApproval.noAccessTitle", "You can't review AI Agent tool calls")
|
|
510
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
511
|
+
className: "mt-1 text-xs text-text-secondary",
|
|
512
|
+
children: tr("fluidgrids.toolApproval.noAccessSubtitle", "Your role doesn't include permission to read tool approval requests. Ask a workspace admin if you need access.")
|
|
513
|
+
})] })]
|
|
514
|
+
})
|
|
515
|
+
}),
|
|
516
|
+
!loading && !permissionsLoading && !toolsForbidden && !error && !human.loading && !human.error && items.length === 0 && human.items.length === 0 && /* @__PURE__ */ jsx(Card, {
|
|
517
|
+
className: "border border-border-secondary bg-background-secondary",
|
|
518
|
+
"data-tour": "fluidgrids-approvals-empty",
|
|
519
|
+
children: /* @__PURE__ */ jsxs(CardContent, {
|
|
520
|
+
className: "flex flex-col items-center gap-2 py-10 text-center",
|
|
521
|
+
children: [
|
|
522
|
+
/* @__PURE__ */ jsx(Icon, {
|
|
523
|
+
icon: "mdi:shield-check-outline",
|
|
524
|
+
width: 32,
|
|
525
|
+
className: "text-text-secondary"
|
|
526
|
+
}),
|
|
527
|
+
/* @__PURE__ */ jsx("p", {
|
|
528
|
+
className: "text-sm font-medium text-text-primary",
|
|
529
|
+
children: tr("fluidgrids.toolApproval.emptyTitle", "Nothing waiting for approval")
|
|
530
|
+
}),
|
|
531
|
+
/* @__PURE__ */ jsx("p", {
|
|
532
|
+
className: "text-xs text-text-secondary",
|
|
533
|
+
children: tr("fluidgrids.toolApproval.emptySubtitle", "Runs will appear here when an AI Agent with tool approval enabled proposes a tool call.")
|
|
534
|
+
})
|
|
535
|
+
]
|
|
536
|
+
})
|
|
537
|
+
}),
|
|
538
|
+
!toolsForbidden && items.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
539
|
+
className: "mb-3 text-xs text-text-secondary",
|
|
540
|
+
"data-testid": "tool-approval-count",
|
|
541
|
+
children: tr("fluidgrids.toolApproval.count", "{{count}} pending ({{total}} total)", {
|
|
542
|
+
count: items.length,
|
|
543
|
+
total: totalCount
|
|
544
|
+
})
|
|
545
|
+
}),
|
|
546
|
+
/* @__PURE__ */ jsx("div", {
|
|
547
|
+
className: "grid grid-cols-1 gap-4 lg:grid-cols-2",
|
|
548
|
+
"data-tour": "fluidgrids-approvals-tools",
|
|
549
|
+
children: !toolsForbidden && items.map((request) => /* @__PURE__ */ jsx(RequestCard, {
|
|
550
|
+
request,
|
|
551
|
+
onDecided: refetch
|
|
552
|
+
}, request.id))
|
|
553
|
+
})
|
|
554
|
+
]
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
export { usePendingToolApprovals as i, ApprovalInboxPage_exports as n, useDecideToolApproval as r, ApprovalInboxPage as t };
|