@burdenoff/fluidgrids-fe-sdk 2026.1028.6 → 2026.1028.7
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/{ExecutionDetailPage-DHIQNBAZ.js → ExecutionDetailPage-A4JQGL34.js} +8 -8
- package/dist/{ExecutionsListPage-IMN7OTNZ.js → ExecutionsListPage-BT3ADBXP.js} +6 -6
- package/dist/{WorkflowBuilder-VJAAKETX.js → WorkflowBuilder-SQQI4VSF.js} +9 -8
- package/dist/{WorkflowsList-3EWDWWIH.js → WorkflowsList-FZ4W64YZ.js} +7 -7
- package/dist/{chunk-A2JSAGNQ.js → chunk-27XYEFCK.js} +1 -1
- package/dist/{chunk-NAFJQGLV.js → chunk-2YDUYSB3.js} +5 -5
- package/dist/chunk-3ENHRYM5.js +749 -0
- package/dist/{chunk-F6NH55LU.js → chunk-5YP3U7BU.js} +3 -3
- package/dist/{chunk-C4AY6JI2.js → chunk-6NUHJPGJ.js} +1 -1
- package/dist/{chunk-Q22GC7RJ.js → chunk-BBORXXGZ.js} +1 -1
- package/dist/{chunk-FDEZGRRL.js → chunk-DUXOFCPO.js} +1 -1
- package/dist/{chunk-HHN2TIK2.js → chunk-HDUCP6EO.js} +1 -1
- package/dist/{chunk-RLA4YUDY.js → chunk-IRKB2GGJ.js} +3 -3
- package/dist/{chunk-IQE7ZY6Q.js → chunk-JNJDVC7V.js} +1 -1
- package/dist/{chunk-WBH2XEWO.js → chunk-JUUYPCFY.js} +4 -1
- package/dist/{chunk-3KNW5QLR.js → chunk-MUOJVRW7.js} +1 -1
- package/dist/{chunk-VQHBQAJY.js → chunk-NW6H7EN3.js} +1 -1
- package/dist/{chunk-XZPXIOGE.js → chunk-PBX42F4Q.js} +1400 -6
- package/dist/{chunk-RZQPHWFV.js → chunk-PYCKEQ4W.js} +1 -1
- package/dist/{chunk-VP2RXXRF.js → chunk-QMBXEECD.js} +6 -6
- package/dist/{chunk-FO22JCOW.js → chunk-RJ3YK6O7.js} +5 -5
- package/dist/{chunk-3VJ3AIFS.js → chunk-RKMTSOYH.js} +1 -1
- package/dist/{chunk-I2ZNBBRK.js → chunk-T7TRJBCG.js} +1 -1
- package/dist/{chunk-ACUNTZTQ.js → chunk-U7WLROQT.js} +3 -3
- package/dist/{chunk-STPQQDIJ.js → chunk-XYZKIFXY.js} +424 -117
- package/dist/{chunk-UWNYWWWG.js → chunk-ZB4BNTWR.js} +4 -4
- package/dist/executions.js +10 -10
- package/dist/global-operations-D2USVOPM.js +2 -0
- package/dist/history.js +4 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.js +31 -30
- package/dist/observability.js +5 -5
- package/dist/settings.js +6 -5
- package/dist/templates.js +8 -7
- package/dist/{workflow-builder-C7qKY7LD.d.ts → workflow-builder-BxyapBo5.d.ts} +18 -0
- package/dist/workflow-builder.d.ts +1 -1
- package/dist/workflow-builder.js +10 -9
- package/dist/workflows-list.js +7 -7
- package/package.json +1 -1
- package/dist/global-operations-ZWQZ7DNT.js +0 -2
|
@@ -0,0 +1,749 @@
|
|
|
1
|
+
import { throwIfResultError, useConnections, describeOperationError } from './chunk-6NUHJPGJ.js';
|
|
2
|
+
import { useTr } from './chunk-UULVIT25.js';
|
|
3
|
+
import { getFluidGridsClient } from './chunk-NTWG3CJX.js';
|
|
4
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
5
|
+
import { Icon } from '@iconify/react';
|
|
6
|
+
import { Button } from '@burdenoff/fe-libs/button';
|
|
7
|
+
import { Badge, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Label, Input, Switch, Textarea, Checkbox } from '@burdenoff/fe-libs/ui';
|
|
8
|
+
import { toast } from 'sonner';
|
|
9
|
+
import { gql } from '@apollo/client';
|
|
10
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
11
|
+
|
|
12
|
+
var AGENT_FIELDS = `
|
|
13
|
+
id
|
|
14
|
+
name
|
|
15
|
+
description
|
|
16
|
+
appId
|
|
17
|
+
toolAllowlist { nodeType operations }
|
|
18
|
+
connectionScope
|
|
19
|
+
requireToolApproval
|
|
20
|
+
ownerUserId
|
|
21
|
+
status
|
|
22
|
+
createdAt
|
|
23
|
+
updatedAt
|
|
24
|
+
`;
|
|
25
|
+
var GRANT_FIELDS = `
|
|
26
|
+
agentDefinitionId
|
|
27
|
+
userId
|
|
28
|
+
grantedBy
|
|
29
|
+
grantedAt
|
|
30
|
+
expiresAt
|
|
31
|
+
status
|
|
32
|
+
isMine
|
|
33
|
+
`;
|
|
34
|
+
var AGENT_DEFINITIONS = gql`
|
|
35
|
+
query AgentDefinitions {
|
|
36
|
+
agentDefinitions {
|
|
37
|
+
${AGENT_FIELDS}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
`;
|
|
41
|
+
var AGENT_GRANTS = gql`
|
|
42
|
+
query AgentDefinitionGrants($agentDefinitionId: ID!) {
|
|
43
|
+
agentDefinitionGrants(agentDefinitionId: $agentDefinitionId) {
|
|
44
|
+
${GRANT_FIELDS}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
`;
|
|
48
|
+
var CREATE_AGENT = gql`
|
|
49
|
+
mutation CreateAgentDefinition($input: AgentDefinitionInput!) {
|
|
50
|
+
createAgentDefinition(input: $input) {
|
|
51
|
+
${AGENT_FIELDS}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
`;
|
|
55
|
+
var UPDATE_AGENT = gql`
|
|
56
|
+
mutation UpdateAgentDefinition($id: ID!, $input: AgentDefinitionInput!) {
|
|
57
|
+
updateAgentDefinition(id: $id, input: $input) {
|
|
58
|
+
${AGENT_FIELDS}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
`;
|
|
62
|
+
var DELETE_AGENT = gql`
|
|
63
|
+
mutation DeleteAgentDefinition($id: ID!) {
|
|
64
|
+
deleteAgentDefinition(id: $id)
|
|
65
|
+
}
|
|
66
|
+
`;
|
|
67
|
+
var GRANT_AGENT = gql`
|
|
68
|
+
mutation GrantAgentDefinition($agentDefinitionId: ID!, $expiresInDays: Int) {
|
|
69
|
+
grantAgentDefinition(agentDefinitionId: $agentDefinitionId, expiresInDays: $expiresInDays) {
|
|
70
|
+
${GRANT_FIELDS}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
`;
|
|
74
|
+
var REVOKE_MY_GRANT = gql`
|
|
75
|
+
mutation RevokeMyAgentGrant($agentDefinitionId: ID!) {
|
|
76
|
+
revokeMyAgentGrant(agentDefinitionId: $agentDefinitionId)
|
|
77
|
+
}
|
|
78
|
+
`;
|
|
79
|
+
var REVOKE_GRANT = gql`
|
|
80
|
+
mutation RevokeAgentGrant($agentDefinitionId: ID!, $userId: String!) {
|
|
81
|
+
revokeAgentGrant(agentDefinitionId: $agentDefinitionId, userId: $userId)
|
|
82
|
+
}
|
|
83
|
+
`;
|
|
84
|
+
function asError(err) {
|
|
85
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
86
|
+
}
|
|
87
|
+
function useAgentDefinitionList() {
|
|
88
|
+
const [agents, setAgents] = useState([]);
|
|
89
|
+
const [loading, setLoading] = useState(true);
|
|
90
|
+
const [error, setError] = useState(null);
|
|
91
|
+
const reload = useCallback(async () => {
|
|
92
|
+
try {
|
|
93
|
+
const result = await getFluidGridsClient().query({ query: AGENT_DEFINITIONS, fetchPolicy: "network-only" });
|
|
94
|
+
throwIfResultError(result);
|
|
95
|
+
setAgents(result.data?.agentDefinitions ?? []);
|
|
96
|
+
setError(null);
|
|
97
|
+
} catch (err) {
|
|
98
|
+
setError(asError(err));
|
|
99
|
+
} finally {
|
|
100
|
+
setLoading(false);
|
|
101
|
+
}
|
|
102
|
+
}, []);
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
void reload();
|
|
105
|
+
}, [reload]);
|
|
106
|
+
return { agents, loading, error, reload };
|
|
107
|
+
}
|
|
108
|
+
function useAgentDefinitions() {
|
|
109
|
+
const list = useAgentDefinitionList();
|
|
110
|
+
const { reload } = list;
|
|
111
|
+
const save = useCallback(
|
|
112
|
+
async (id, input) => {
|
|
113
|
+
const client = getFluidGridsClient();
|
|
114
|
+
const result = id ? await client.mutate({
|
|
115
|
+
mutation: UPDATE_AGENT,
|
|
116
|
+
variables: { id, input }
|
|
117
|
+
}) : await client.mutate({ mutation: CREATE_AGENT, variables: { input } });
|
|
118
|
+
throwIfResultError(result);
|
|
119
|
+
await reload();
|
|
120
|
+
},
|
|
121
|
+
[reload]
|
|
122
|
+
);
|
|
123
|
+
const remove = useCallback(
|
|
124
|
+
async (id) => {
|
|
125
|
+
const result = await getFluidGridsClient().mutate({
|
|
126
|
+
mutation: DELETE_AGENT,
|
|
127
|
+
variables: { id }
|
|
128
|
+
});
|
|
129
|
+
throwIfResultError(result);
|
|
130
|
+
await reload();
|
|
131
|
+
},
|
|
132
|
+
[reload]
|
|
133
|
+
);
|
|
134
|
+
const grants = useCallback(async (agentDefinitionId) => {
|
|
135
|
+
const result = await getFluidGridsClient().query({
|
|
136
|
+
query: AGENT_GRANTS,
|
|
137
|
+
variables: { agentDefinitionId },
|
|
138
|
+
fetchPolicy: "network-only"
|
|
139
|
+
});
|
|
140
|
+
throwIfResultError(result);
|
|
141
|
+
return result.data?.agentDefinitionGrants ?? [];
|
|
142
|
+
}, []);
|
|
143
|
+
const grant = useCallback(
|
|
144
|
+
async (agentDefinitionId, expiresInDays) => {
|
|
145
|
+
const result = await getFluidGridsClient().mutate({
|
|
146
|
+
mutation: GRANT_AGENT,
|
|
147
|
+
variables: { agentDefinitionId, expiresInDays }
|
|
148
|
+
});
|
|
149
|
+
throwIfResultError(result);
|
|
150
|
+
},
|
|
151
|
+
[]
|
|
152
|
+
);
|
|
153
|
+
const revoke = useCallback(
|
|
154
|
+
async (agentDefinitionId, userId) => {
|
|
155
|
+
const result = userId ? await getFluidGridsClient().mutate({
|
|
156
|
+
mutation: REVOKE_GRANT,
|
|
157
|
+
variables: { agentDefinitionId, userId }
|
|
158
|
+
}) : await getFluidGridsClient().mutate({
|
|
159
|
+
mutation: REVOKE_MY_GRANT,
|
|
160
|
+
variables: { agentDefinitionId }
|
|
161
|
+
});
|
|
162
|
+
throwIfResultError(result);
|
|
163
|
+
},
|
|
164
|
+
[]
|
|
165
|
+
);
|
|
166
|
+
return { ...list, save, remove, grants, grant, revoke };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// src/modules/agent-definitions/utils/agent-form.ts
|
|
170
|
+
var DEFAULT_GRANT_DAYS = 30;
|
|
171
|
+
var MAX_GRANT_DAYS = 365;
|
|
172
|
+
var MAX_TOOL_RULES = 100;
|
|
173
|
+
var MAX_CONNECTIONS = 100;
|
|
174
|
+
var AgentFormError = class extends Error {
|
|
175
|
+
constructor(message) {
|
|
176
|
+
super(message);
|
|
177
|
+
this.name = "AgentFormError";
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
function emptyAgentForm() {
|
|
181
|
+
return {
|
|
182
|
+
name: "",
|
|
183
|
+
description: "",
|
|
184
|
+
tools: [{ nodeType: "", operations: "" }],
|
|
185
|
+
connectionScope: [],
|
|
186
|
+
requireToolApproval: false,
|
|
187
|
+
status: "ACTIVE"
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
function agentToForm(agent) {
|
|
191
|
+
return {
|
|
192
|
+
name: agent.name,
|
|
193
|
+
description: agent.description ?? "",
|
|
194
|
+
tools: agent.toolAllowlist.length > 0 ? agent.toolAllowlist.map((rule) => ({
|
|
195
|
+
nodeType: rule.nodeType,
|
|
196
|
+
operations: rule.operations.join(", ")
|
|
197
|
+
})) : [{ nodeType: "", operations: "" }],
|
|
198
|
+
connectionScope: [...agent.connectionScope],
|
|
199
|
+
requireToolApproval: agent.requireToolApproval,
|
|
200
|
+
status: agent.status
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function splitOperations(value) {
|
|
204
|
+
return [
|
|
205
|
+
...new Set(
|
|
206
|
+
value.split(",").map((operation) => operation.trim()).filter(Boolean)
|
|
207
|
+
)
|
|
208
|
+
];
|
|
209
|
+
}
|
|
210
|
+
function formToInput(form) {
|
|
211
|
+
const name = form.name.trim();
|
|
212
|
+
if (name.length === 0 || name.length > 200) {
|
|
213
|
+
throw new AgentFormError("Give the agent a name of 1 to 200 characters.");
|
|
214
|
+
}
|
|
215
|
+
const description = form.description.trim();
|
|
216
|
+
if (description.length > 2e3) {
|
|
217
|
+
throw new AgentFormError(
|
|
218
|
+
"The description must be at most 2000 characters."
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
const rules = form.tools.map((row) => ({
|
|
222
|
+
nodeType: row.nodeType.trim(),
|
|
223
|
+
operations: splitOperations(row.operations)
|
|
224
|
+
})).filter((rule) => rule.nodeType.length > 0 || rule.operations.length > 0);
|
|
225
|
+
if (rules.some((rule) => rule.nodeType.length === 0)) {
|
|
226
|
+
throw new AgentFormError("Every tool rule needs a node type.");
|
|
227
|
+
}
|
|
228
|
+
if (rules.length > MAX_TOOL_RULES) {
|
|
229
|
+
throw new AgentFormError(`At most ${MAX_TOOL_RULES} tool rules.`);
|
|
230
|
+
}
|
|
231
|
+
const connectionScope = [...new Set(form.connectionScope.filter(Boolean))];
|
|
232
|
+
if (connectionScope.length > MAX_CONNECTIONS) {
|
|
233
|
+
throw new AgentFormError(`At most ${MAX_CONNECTIONS} connections.`);
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
name,
|
|
237
|
+
description: description || null,
|
|
238
|
+
toolAllowlist: rules,
|
|
239
|
+
connectionScope,
|
|
240
|
+
requireToolApproval: form.requireToolApproval,
|
|
241
|
+
status: form.status
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function grantDays(value) {
|
|
245
|
+
const days = Number(value);
|
|
246
|
+
if (!Number.isInteger(days) || days < 1 || days > MAX_GRANT_DAYS) {
|
|
247
|
+
throw new AgentFormError(
|
|
248
|
+
`A grant lasts between 1 and ${MAX_GRANT_DAYS} days.`
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
return days;
|
|
252
|
+
}
|
|
253
|
+
function describeRule(rule) {
|
|
254
|
+
return rule.operations.length > 0 ? `${rule.nodeType}: ${rule.operations.join(", ")}` : `${rule.nodeType}: any operation`;
|
|
255
|
+
}
|
|
256
|
+
function isAgentNodeType(nodeType) {
|
|
257
|
+
if (!nodeType) return false;
|
|
258
|
+
return nodeType === "store/ai-agent" || nodeType.includes("store/ai-agent");
|
|
259
|
+
}
|
|
260
|
+
function errorMessage(err) {
|
|
261
|
+
if (err instanceof AgentFormError) return err.message;
|
|
262
|
+
return describeOperationError(err);
|
|
263
|
+
}
|
|
264
|
+
function AgentEditor({
|
|
265
|
+
initial,
|
|
266
|
+
onCancel,
|
|
267
|
+
onSave
|
|
268
|
+
}) {
|
|
269
|
+
const tr = useTr();
|
|
270
|
+
const [form, setForm] = useState(initial);
|
|
271
|
+
const [busy, setBusy] = useState(false);
|
|
272
|
+
const { connections, loading: connectionsLoading } = useConnections();
|
|
273
|
+
const setTool = (index, patch) => setForm((current) => ({
|
|
274
|
+
...current,
|
|
275
|
+
tools: current.tools.map(
|
|
276
|
+
(row, i) => i === index ? { ...row, ...patch } : row
|
|
277
|
+
)
|
|
278
|
+
}));
|
|
279
|
+
const toggleConnection = (id, on) => setForm((current) => ({
|
|
280
|
+
...current,
|
|
281
|
+
connectionScope: on ? [.../* @__PURE__ */ new Set([...current.connectionScope, id])] : current.connectionScope.filter((value) => value !== id)
|
|
282
|
+
}));
|
|
283
|
+
const submit = async () => {
|
|
284
|
+
setBusy(true);
|
|
285
|
+
try {
|
|
286
|
+
await onSave(form);
|
|
287
|
+
} catch (err) {
|
|
288
|
+
toast.error(errorMessage(err));
|
|
289
|
+
} finally {
|
|
290
|
+
setBusy(false);
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
return /* @__PURE__ */ jsxs(
|
|
294
|
+
"div",
|
|
295
|
+
{
|
|
296
|
+
className: "rounded-lg border border-border-secondary bg-background-primary p-4 space-y-4",
|
|
297
|
+
"data-testid": "agent-definition-editor",
|
|
298
|
+
children: [
|
|
299
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [
|
|
300
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
301
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "agent-name", children: tr("fluidgrids.agents.name", "Name") }),
|
|
302
|
+
/* @__PURE__ */ jsx(
|
|
303
|
+
Input,
|
|
304
|
+
{
|
|
305
|
+
id: "agent-name",
|
|
306
|
+
value: form.name,
|
|
307
|
+
onChange: (e) => setForm({ ...form, name: e.target.value }),
|
|
308
|
+
"data-testid": "agent-definition-name"
|
|
309
|
+
}
|
|
310
|
+
)
|
|
311
|
+
] }),
|
|
312
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-end gap-2", children: [
|
|
313
|
+
/* @__PURE__ */ jsx(
|
|
314
|
+
Switch,
|
|
315
|
+
{
|
|
316
|
+
id: "agent-active",
|
|
317
|
+
checked: form.status === "ACTIVE",
|
|
318
|
+
onCheckedChange: (on) => setForm({ ...form, status: on ? "ACTIVE" : "DISABLED" })
|
|
319
|
+
}
|
|
320
|
+
),
|
|
321
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "agent-active", children: tr("fluidgrids.agents.active", "Active") })
|
|
322
|
+
] })
|
|
323
|
+
] }),
|
|
324
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
325
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "agent-description", children: tr("fluidgrids.agents.description", "Description") }),
|
|
326
|
+
/* @__PURE__ */ jsx(
|
|
327
|
+
Textarea,
|
|
328
|
+
{
|
|
329
|
+
id: "agent-description",
|
|
330
|
+
value: form.description,
|
|
331
|
+
onChange: (e) => setForm({ ...form, description: e.target.value }),
|
|
332
|
+
rows: 2
|
|
333
|
+
}
|
|
334
|
+
)
|
|
335
|
+
] }),
|
|
336
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
337
|
+
/* @__PURE__ */ jsx(Label, { children: tr("fluidgrids.agents.tools", "Tools the agent may call") }),
|
|
338
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-text-secondary", children: tr(
|
|
339
|
+
"fluidgrids.agents.toolsHelp",
|
|
340
|
+
"A node type (for example store/slack) and the operations of it the agent may call, separated by commas. Leave the operations empty to allow every operation of that node type. Nothing else can be called."
|
|
341
|
+
) }),
|
|
342
|
+
form.tools.map((row, index) => /* @__PURE__ */ jsxs(
|
|
343
|
+
"div",
|
|
344
|
+
{
|
|
345
|
+
className: "flex flex-wrap items-center gap-2",
|
|
346
|
+
"data-testid": `agent-definition-tool-${index}`,
|
|
347
|
+
children: [
|
|
348
|
+
/* @__PURE__ */ jsx(
|
|
349
|
+
Input,
|
|
350
|
+
{
|
|
351
|
+
className: "w-48",
|
|
352
|
+
placeholder: "store/slack",
|
|
353
|
+
value: row.nodeType,
|
|
354
|
+
onChange: (e) => setTool(index, { nodeType: e.target.value }),
|
|
355
|
+
"aria-label": tr("fluidgrids.agents.nodeType", "Node type")
|
|
356
|
+
}
|
|
357
|
+
),
|
|
358
|
+
/* @__PURE__ */ jsx(
|
|
359
|
+
Input,
|
|
360
|
+
{
|
|
361
|
+
className: "min-w-48 flex-1",
|
|
362
|
+
placeholder: "postMessage, listChannels",
|
|
363
|
+
value: row.operations,
|
|
364
|
+
onChange: (e) => setTool(index, { operations: e.target.value }),
|
|
365
|
+
"aria-label": tr("fluidgrids.agents.operations", "Operations")
|
|
366
|
+
}
|
|
367
|
+
),
|
|
368
|
+
/* @__PURE__ */ jsx(
|
|
369
|
+
Button,
|
|
370
|
+
{
|
|
371
|
+
variant: "ghost",
|
|
372
|
+
size: "sm",
|
|
373
|
+
onClick: () => setForm({
|
|
374
|
+
...form,
|
|
375
|
+
tools: form.tools.filter((_, i) => i !== index)
|
|
376
|
+
}),
|
|
377
|
+
"aria-label": tr("fluidgrids.agents.removeTool", "Remove tool"),
|
|
378
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: "mdi:close", width: 16 })
|
|
379
|
+
}
|
|
380
|
+
)
|
|
381
|
+
]
|
|
382
|
+
},
|
|
383
|
+
index
|
|
384
|
+
)),
|
|
385
|
+
/* @__PURE__ */ jsxs(
|
|
386
|
+
Button,
|
|
387
|
+
{
|
|
388
|
+
variant: "outline",
|
|
389
|
+
size: "sm",
|
|
390
|
+
onClick: () => setForm({
|
|
391
|
+
...form,
|
|
392
|
+
tools: [...form.tools, { nodeType: "", operations: "" }]
|
|
393
|
+
}),
|
|
394
|
+
children: [
|
|
395
|
+
/* @__PURE__ */ jsx(Icon, { icon: "mdi:plus", width: 16 }),
|
|
396
|
+
tr("fluidgrids.agents.addTool", "Add tool")
|
|
397
|
+
]
|
|
398
|
+
}
|
|
399
|
+
)
|
|
400
|
+
] }),
|
|
401
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
402
|
+
/* @__PURE__ */ jsx(Label, { children: tr(
|
|
403
|
+
"fluidgrids.agents.connections",
|
|
404
|
+
"Connections the agent's tools may use"
|
|
405
|
+
) }),
|
|
406
|
+
connectionsLoading ? /* @__PURE__ */ jsx("p", { className: "text-xs text-text-secondary", children: tr("fluidgrids.agents.loadingConnections", "Loading connections\u2026") }) : connections.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-text-secondary", children: tr(
|
|
407
|
+
"fluidgrids.agents.noConnections",
|
|
408
|
+
"This workspace has no connections yet."
|
|
409
|
+
) }) : /* @__PURE__ */ jsx("div", { className: "grid gap-1 sm:grid-cols-2", children: connections.map((connection) => /* @__PURE__ */ jsxs(
|
|
410
|
+
"label",
|
|
411
|
+
{
|
|
412
|
+
className: "flex items-center gap-2 text-sm text-text-primary",
|
|
413
|
+
children: [
|
|
414
|
+
/* @__PURE__ */ jsx(
|
|
415
|
+
Checkbox,
|
|
416
|
+
{
|
|
417
|
+
checked: form.connectionScope.includes(connection.id),
|
|
418
|
+
onCheckedChange: (on) => toggleConnection(connection.id, on === true)
|
|
419
|
+
}
|
|
420
|
+
),
|
|
421
|
+
/* @__PURE__ */ jsx("span", { children: connection.name }),
|
|
422
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-text-tertiary", children: connection.provider?.name ?? connection.providerId })
|
|
423
|
+
]
|
|
424
|
+
},
|
|
425
|
+
connection.id
|
|
426
|
+
)) })
|
|
427
|
+
] }),
|
|
428
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
429
|
+
/* @__PURE__ */ jsx(
|
|
430
|
+
Switch,
|
|
431
|
+
{
|
|
432
|
+
id: "agent-approval",
|
|
433
|
+
checked: form.requireToolApproval,
|
|
434
|
+
onCheckedChange: (on) => setForm({ ...form, requireToolApproval: on })
|
|
435
|
+
}
|
|
436
|
+
),
|
|
437
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "agent-approval", children: tr(
|
|
438
|
+
"fluidgrids.agents.requireApproval",
|
|
439
|
+
"Every tool call waits for a person's approval"
|
|
440
|
+
) })
|
|
441
|
+
] }),
|
|
442
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
443
|
+
/* @__PURE__ */ jsx(
|
|
444
|
+
Button,
|
|
445
|
+
{
|
|
446
|
+
onClick: () => void submit(),
|
|
447
|
+
disabled: busy,
|
|
448
|
+
"data-testid": "agent-definition-save",
|
|
449
|
+
children: tr("fluidgrids.agents.save", "Save agent")
|
|
450
|
+
}
|
|
451
|
+
),
|
|
452
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: onCancel, disabled: busy, children: tr("fluidgrids.agents.cancel", "Cancel") })
|
|
453
|
+
] })
|
|
454
|
+
]
|
|
455
|
+
}
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
function AgentGrants({
|
|
459
|
+
agent,
|
|
460
|
+
loadGrants,
|
|
461
|
+
onGrant,
|
|
462
|
+
onRevoke
|
|
463
|
+
}) {
|
|
464
|
+
const tr = useTr();
|
|
465
|
+
const [grants, setGrants] = useState(null);
|
|
466
|
+
const [days, setDays] = useState(String(DEFAULT_GRANT_DAYS));
|
|
467
|
+
const [busy, setBusy] = useState(false);
|
|
468
|
+
const load = useCallback(async () => {
|
|
469
|
+
try {
|
|
470
|
+
setGrants(await loadGrants(agent.id));
|
|
471
|
+
} catch (err) {
|
|
472
|
+
setGrants([]);
|
|
473
|
+
toast.error(errorMessage(err));
|
|
474
|
+
}
|
|
475
|
+
}, [agent.id, loadGrants]);
|
|
476
|
+
useEffect(() => {
|
|
477
|
+
void load();
|
|
478
|
+
}, [load]);
|
|
479
|
+
const run = async (action, done) => {
|
|
480
|
+
setBusy(true);
|
|
481
|
+
try {
|
|
482
|
+
await action();
|
|
483
|
+
toast.success(done);
|
|
484
|
+
await load();
|
|
485
|
+
} catch (err) {
|
|
486
|
+
toast.error(errorMessage(err));
|
|
487
|
+
} finally {
|
|
488
|
+
setBusy(false);
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
const mine = grants?.find(
|
|
492
|
+
(grant) => grant.isMine && grant.status === "ACTIVE"
|
|
493
|
+
);
|
|
494
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", "data-testid": `agent-grants-${agent.id}`, children: [
|
|
495
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2", children: mine ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
496
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-text-primary", children: mine.expiresAt ? tr(
|
|
497
|
+
"fluidgrids.agents.myGrantUntil",
|
|
498
|
+
"This agent may act for you until {{date}}.",
|
|
499
|
+
{ date: new Date(mine.expiresAt).toLocaleDateString() }
|
|
500
|
+
) : tr(
|
|
501
|
+
"fluidgrids.agents.myGrant",
|
|
502
|
+
"This agent may act for you."
|
|
503
|
+
) }),
|
|
504
|
+
/* @__PURE__ */ jsx(
|
|
505
|
+
Button,
|
|
506
|
+
{
|
|
507
|
+
variant: "outline",
|
|
508
|
+
size: "sm",
|
|
509
|
+
disabled: busy,
|
|
510
|
+
onClick: () => void run(
|
|
511
|
+
() => onRevoke(agent.id, null),
|
|
512
|
+
tr("fluidgrids.agents.withdrawn", "Grant withdrawn")
|
|
513
|
+
),
|
|
514
|
+
"data-testid": "agent-grant-withdraw",
|
|
515
|
+
children: tr("fluidgrids.agents.withdraw", "Withdraw my grant")
|
|
516
|
+
}
|
|
517
|
+
)
|
|
518
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
519
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: `grant-days-${agent.id}`, className: "text-sm", children: tr("fluidgrids.agents.grantFor", "Let it act for me for") }),
|
|
520
|
+
/* @__PURE__ */ jsx(
|
|
521
|
+
Input,
|
|
522
|
+
{
|
|
523
|
+
id: `grant-days-${agent.id}`,
|
|
524
|
+
className: "w-20",
|
|
525
|
+
type: "number",
|
|
526
|
+
min: 1,
|
|
527
|
+
max: MAX_GRANT_DAYS,
|
|
528
|
+
value: days,
|
|
529
|
+
onChange: (e) => setDays(e.target.value)
|
|
530
|
+
}
|
|
531
|
+
),
|
|
532
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-text-secondary", children: tr("fluidgrids.agents.days", "days") }),
|
|
533
|
+
/* @__PURE__ */ jsx(
|
|
534
|
+
Button,
|
|
535
|
+
{
|
|
536
|
+
size: "sm",
|
|
537
|
+
disabled: busy || agent.status !== "ACTIVE",
|
|
538
|
+
onClick: () => void run(
|
|
539
|
+
async () => onGrant(agent.id, grantDays(days)),
|
|
540
|
+
tr("fluidgrids.agents.granted", "Grant given")
|
|
541
|
+
),
|
|
542
|
+
"data-testid": "agent-grant-give",
|
|
543
|
+
children: tr("fluidgrids.agents.grant", "Grant")
|
|
544
|
+
}
|
|
545
|
+
)
|
|
546
|
+
] }) }),
|
|
547
|
+
grants && grants.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "space-y-1 text-xs text-text-secondary", children: grants.map((grant) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-2", children: [
|
|
548
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono", children: grant.userId }),
|
|
549
|
+
/* @__PURE__ */ jsx(Badge, { variant: "outline", children: grant.status }),
|
|
550
|
+
grant.expiresAt ? /* @__PURE__ */ jsx("span", { children: tr("fluidgrids.agents.until", "until {{date}}", {
|
|
551
|
+
date: new Date(grant.expiresAt).toLocaleDateString()
|
|
552
|
+
}) }) : null,
|
|
553
|
+
!grant.isMine && grant.status === "ACTIVE" ? /* @__PURE__ */ jsx(
|
|
554
|
+
Button,
|
|
555
|
+
{
|
|
556
|
+
variant: "ghost",
|
|
557
|
+
size: "sm",
|
|
558
|
+
disabled: busy,
|
|
559
|
+
onClick: () => void run(
|
|
560
|
+
() => onRevoke(agent.id, grant.userId),
|
|
561
|
+
tr("fluidgrids.agents.revoked", "Grant revoked")
|
|
562
|
+
),
|
|
563
|
+
children: tr("fluidgrids.agents.revoke", "Revoke")
|
|
564
|
+
}
|
|
565
|
+
) : null
|
|
566
|
+
] }, grant.userId)) }) : null
|
|
567
|
+
] });
|
|
568
|
+
}
|
|
569
|
+
function AgentsTab() {
|
|
570
|
+
const tr = useTr();
|
|
571
|
+
const { agents, loading, error, save, remove, grants, grant, revoke } = useAgentDefinitions();
|
|
572
|
+
const [editing, setEditing] = useState(null);
|
|
573
|
+
const onSave = async (form) => {
|
|
574
|
+
const input = formToInput(form);
|
|
575
|
+
await save(editing?.id ?? null, input);
|
|
576
|
+
toast.success(tr("fluidgrids.agents.saved", "Agent saved"));
|
|
577
|
+
setEditing(null);
|
|
578
|
+
};
|
|
579
|
+
const onDelete = async (agent) => {
|
|
580
|
+
try {
|
|
581
|
+
await remove(agent.id);
|
|
582
|
+
toast.success(tr("fluidgrids.agents.deleted", "Agent deleted"));
|
|
583
|
+
} catch (err) {
|
|
584
|
+
toast.error(errorMessage(err));
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", "data-testid": "agents-tab", children: [
|
|
588
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start justify-between gap-2", children: [
|
|
589
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
590
|
+
/* @__PURE__ */ jsx("h3", { className: "text-base font-semibold text-text-primary", children: tr("fluidgrids.agents.title", "Agents") }),
|
|
591
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-text-secondary", children: tr(
|
|
592
|
+
"fluidgrids.agents.subtitle",
|
|
593
|
+
"Give an AI Agent node an identity of its own. It can only call the tools and connections you allow, only for people who granted it, and every tool call is recorded under the agent and the person."
|
|
594
|
+
) })
|
|
595
|
+
] }),
|
|
596
|
+
!editing ? /* @__PURE__ */ jsxs(
|
|
597
|
+
Button,
|
|
598
|
+
{
|
|
599
|
+
onClick: () => setEditing({ id: null, form: emptyAgentForm() }),
|
|
600
|
+
"data-testid": "agent-definition-new",
|
|
601
|
+
children: [
|
|
602
|
+
/* @__PURE__ */ jsx(Icon, { icon: "mdi:plus", width: 16 }),
|
|
603
|
+
tr("fluidgrids.agents.new", "New agent")
|
|
604
|
+
]
|
|
605
|
+
}
|
|
606
|
+
) : null
|
|
607
|
+
] }),
|
|
608
|
+
editing ? /* @__PURE__ */ jsx(
|
|
609
|
+
AgentEditor,
|
|
610
|
+
{
|
|
611
|
+
initial: editing.form,
|
|
612
|
+
onCancel: () => setEditing(null),
|
|
613
|
+
onSave
|
|
614
|
+
}
|
|
615
|
+
) : null,
|
|
616
|
+
error ? /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: errorMessage(error) }) : null,
|
|
617
|
+
loading ? /* @__PURE__ */ jsx("p", { className: "text-sm text-text-secondary", children: tr("fluidgrids.agents.loading", "Loading agents\u2026") }) : agents.length === 0 && !error ? /* @__PURE__ */ jsx("p", { className: "text-sm text-text-secondary", children: tr("fluidgrids.agents.empty", "No agents yet.") }) : /* @__PURE__ */ jsx("ul", { className: "space-y-3", children: agents.map((agent) => /* @__PURE__ */ jsxs(
|
|
618
|
+
"li",
|
|
619
|
+
{
|
|
620
|
+
className: "rounded-lg border border-border-secondary bg-background-secondary p-3 space-y-2",
|
|
621
|
+
"data-testid": `agent-definition-${agent.id}`,
|
|
622
|
+
children: [
|
|
623
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
|
|
624
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
625
|
+
/* @__PURE__ */ jsx(Icon, { icon: "mdi:robot-outline", width: 18 }),
|
|
626
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-text-primary", children: agent.name }),
|
|
627
|
+
/* @__PURE__ */ jsx(Badge, { variant: "outline", children: agent.status }),
|
|
628
|
+
agent.requireToolApproval ? /* @__PURE__ */ jsx(Badge, { variant: "outline", children: tr(
|
|
629
|
+
"fluidgrids.agents.approvalBadge",
|
|
630
|
+
"Approval required"
|
|
631
|
+
) }) : null
|
|
632
|
+
] }),
|
|
633
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
|
|
634
|
+
/* @__PURE__ */ jsx(
|
|
635
|
+
Button,
|
|
636
|
+
{
|
|
637
|
+
variant: "ghost",
|
|
638
|
+
size: "sm",
|
|
639
|
+
onClick: () => setEditing({ id: agent.id, form: agentToForm(agent) }),
|
|
640
|
+
children: tr("fluidgrids.agents.edit", "Edit")
|
|
641
|
+
}
|
|
642
|
+
),
|
|
643
|
+
/* @__PURE__ */ jsx(
|
|
644
|
+
Button,
|
|
645
|
+
{
|
|
646
|
+
variant: "ghost",
|
|
647
|
+
size: "sm",
|
|
648
|
+
onClick: () => void onDelete(agent),
|
|
649
|
+
children: tr("fluidgrids.agents.delete", "Delete")
|
|
650
|
+
}
|
|
651
|
+
)
|
|
652
|
+
] })
|
|
653
|
+
] }),
|
|
654
|
+
agent.description ? /* @__PURE__ */ jsx("p", { className: "text-sm text-text-secondary", children: agent.description }) : null,
|
|
655
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs text-text-secondary", children: agent.toolAllowlist.length > 0 ? agent.toolAllowlist.map(describeRule).join(" \xB7 ") : tr("fluidgrids.agents.noTools", "No tools allowed") }),
|
|
656
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs text-text-tertiary", children: tr(
|
|
657
|
+
"fluidgrids.agents.connectionCount",
|
|
658
|
+
"{{count}} connections in scope",
|
|
659
|
+
{ count: agent.connectionScope.length }
|
|
660
|
+
) }),
|
|
661
|
+
/* @__PURE__ */ jsx(
|
|
662
|
+
AgentGrants,
|
|
663
|
+
{
|
|
664
|
+
agent,
|
|
665
|
+
loadGrants: grants,
|
|
666
|
+
onGrant: grant,
|
|
667
|
+
onRevoke: revoke
|
|
668
|
+
}
|
|
669
|
+
)
|
|
670
|
+
]
|
|
671
|
+
},
|
|
672
|
+
agent.id
|
|
673
|
+
)) })
|
|
674
|
+
] });
|
|
675
|
+
}
|
|
676
|
+
var NONE = "__no_agent__";
|
|
677
|
+
function AgentDefinitionSelectField({
|
|
678
|
+
value,
|
|
679
|
+
onChange,
|
|
680
|
+
sectionLabelClassName,
|
|
681
|
+
testIdPrefix = "ndv"
|
|
682
|
+
}) {
|
|
683
|
+
const tr = useTr();
|
|
684
|
+
const { agents, loading } = useAgentDefinitionList();
|
|
685
|
+
const selected = value ? agents.find((agent) => agent.id === value) : void 0;
|
|
686
|
+
const missing = Boolean(value) && !loading && !selected;
|
|
687
|
+
return /* @__PURE__ */ jsxs(
|
|
688
|
+
"section",
|
|
689
|
+
{
|
|
690
|
+
className: "space-y-2",
|
|
691
|
+
"data-testid": `${testIdPrefix}-agent-identity`,
|
|
692
|
+
children: [
|
|
693
|
+
/* @__PURE__ */ jsx("div", { className: sectionLabelClassName, children: tr("fluidgrids.builder.agentIdentity.label", "Agent identity") }),
|
|
694
|
+
/* @__PURE__ */ jsxs(
|
|
695
|
+
Select,
|
|
696
|
+
{
|
|
697
|
+
value: value ?? NONE,
|
|
698
|
+
onValueChange: (next) => onChange(next === NONE ? null : next),
|
|
699
|
+
children: [
|
|
700
|
+
/* @__PURE__ */ jsx(
|
|
701
|
+
SelectTrigger,
|
|
702
|
+
{
|
|
703
|
+
className: "w-full",
|
|
704
|
+
"data-testid": `${testIdPrefix}-agent-identity-select`,
|
|
705
|
+
children: /* @__PURE__ */ jsx(SelectValue, {})
|
|
706
|
+
}
|
|
707
|
+
),
|
|
708
|
+
/* @__PURE__ */ jsxs(
|
|
709
|
+
SelectContent,
|
|
710
|
+
{
|
|
711
|
+
style: { zIndex: 1100 },
|
|
712
|
+
children: [
|
|
713
|
+
/* @__PURE__ */ jsx(SelectItem, { value: NONE, children: tr(
|
|
714
|
+
"fluidgrids.builder.agentIdentity.none",
|
|
715
|
+
"None (tools run as the workflow)"
|
|
716
|
+
) }),
|
|
717
|
+
agents.map((agent) => /* @__PURE__ */ jsx(
|
|
718
|
+
SelectItem,
|
|
719
|
+
{
|
|
720
|
+
value: agent.id,
|
|
721
|
+
disabled: agent.status !== "ACTIVE",
|
|
722
|
+
children: agent.name
|
|
723
|
+
},
|
|
724
|
+
agent.id
|
|
725
|
+
))
|
|
726
|
+
]
|
|
727
|
+
}
|
|
728
|
+
)
|
|
729
|
+
]
|
|
730
|
+
}
|
|
731
|
+
),
|
|
732
|
+
selected ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: selected.toolAllowlist.length > 0 ? tr(
|
|
733
|
+
"fluidgrids.builder.agentIdentity.allows",
|
|
734
|
+
"Allowed tools: {{tools}}. Each tool call needs a grant from the person the run is attributed to.",
|
|
735
|
+
{ tools: selected.toolAllowlist.map(describeRule).join(" \xB7 ") }
|
|
736
|
+
) : tr(
|
|
737
|
+
"fluidgrids.builder.agentIdentity.noTools",
|
|
738
|
+
"This agent allows no tools."
|
|
739
|
+
) }) : null,
|
|
740
|
+
missing ? /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: tr(
|
|
741
|
+
"fluidgrids.builder.agentIdentity.missing",
|
|
742
|
+
"The selected agent no longer exists in this workspace; saving will be refused."
|
|
743
|
+
) }) : null
|
|
744
|
+
]
|
|
745
|
+
}
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
export { AgentDefinitionSelectField, AgentsTab, isAgentNodeType };
|