@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.
Files changed (40) hide show
  1. package/dist/{ExecutionDetailPage-DHIQNBAZ.js → ExecutionDetailPage-A4JQGL34.js} +8 -8
  2. package/dist/{ExecutionsListPage-IMN7OTNZ.js → ExecutionsListPage-BT3ADBXP.js} +6 -6
  3. package/dist/{WorkflowBuilder-VJAAKETX.js → WorkflowBuilder-SQQI4VSF.js} +9 -8
  4. package/dist/{WorkflowsList-3EWDWWIH.js → WorkflowsList-FZ4W64YZ.js} +7 -7
  5. package/dist/{chunk-A2JSAGNQ.js → chunk-27XYEFCK.js} +1 -1
  6. package/dist/{chunk-NAFJQGLV.js → chunk-2YDUYSB3.js} +5 -5
  7. package/dist/chunk-3ENHRYM5.js +749 -0
  8. package/dist/{chunk-F6NH55LU.js → chunk-5YP3U7BU.js} +3 -3
  9. package/dist/{chunk-C4AY6JI2.js → chunk-6NUHJPGJ.js} +1 -1
  10. package/dist/{chunk-Q22GC7RJ.js → chunk-BBORXXGZ.js} +1 -1
  11. package/dist/{chunk-FDEZGRRL.js → chunk-DUXOFCPO.js} +1 -1
  12. package/dist/{chunk-HHN2TIK2.js → chunk-HDUCP6EO.js} +1 -1
  13. package/dist/{chunk-RLA4YUDY.js → chunk-IRKB2GGJ.js} +3 -3
  14. package/dist/{chunk-IQE7ZY6Q.js → chunk-JNJDVC7V.js} +1 -1
  15. package/dist/{chunk-WBH2XEWO.js → chunk-JUUYPCFY.js} +4 -1
  16. package/dist/{chunk-3KNW5QLR.js → chunk-MUOJVRW7.js} +1 -1
  17. package/dist/{chunk-VQHBQAJY.js → chunk-NW6H7EN3.js} +1 -1
  18. package/dist/{chunk-XZPXIOGE.js → chunk-PBX42F4Q.js} +1400 -6
  19. package/dist/{chunk-RZQPHWFV.js → chunk-PYCKEQ4W.js} +1 -1
  20. package/dist/{chunk-VP2RXXRF.js → chunk-QMBXEECD.js} +6 -6
  21. package/dist/{chunk-FO22JCOW.js → chunk-RJ3YK6O7.js} +5 -5
  22. package/dist/{chunk-3VJ3AIFS.js → chunk-RKMTSOYH.js} +1 -1
  23. package/dist/{chunk-I2ZNBBRK.js → chunk-T7TRJBCG.js} +1 -1
  24. package/dist/{chunk-ACUNTZTQ.js → chunk-U7WLROQT.js} +3 -3
  25. package/dist/{chunk-STPQQDIJ.js → chunk-XYZKIFXY.js} +424 -117
  26. package/dist/{chunk-UWNYWWWG.js → chunk-ZB4BNTWR.js} +4 -4
  27. package/dist/executions.js +10 -10
  28. package/dist/global-operations-D2USVOPM.js +2 -0
  29. package/dist/history.js +4 -4
  30. package/dist/index.d.ts +2 -2
  31. package/dist/index.js +31 -30
  32. package/dist/observability.js +5 -5
  33. package/dist/settings.js +6 -5
  34. package/dist/templates.js +8 -7
  35. package/dist/{workflow-builder-C7qKY7LD.d.ts → workflow-builder-BxyapBo5.d.ts} +18 -0
  36. package/dist/workflow-builder.d.ts +1 -1
  37. package/dist/workflow-builder.js +10 -9
  38. package/dist/workflows-list.js +7 -7
  39. package/package.json +1 -1
  40. 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 };