@datatechsolutions/ui 2.11.62 → 2.11.63

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.
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { ToggleSwitch, GlassModal, ContextMenu, FormInput, FormSelect, FormTextarea, Button, IconButton } from './chunk-KRWGJS25.mjs';
2
+ import { ToggleSwitch, GlassModal, ContextMenu, FormSelect, FormInput, FormTextarea, Button, IconButton } from './chunk-T5SX6BD6.mjs';
3
3
  import { useTranslations, I18nProvider, createI18nFromMessages } from './chunk-7VJ7CMMT.mjs';
4
4
  import { GraphNodeHeader, GraphNodeMeta, GraphNodeBadge, GraphNodeIconBubble } from './chunk-OZNTQROP.mjs';
5
5
  import { getAgentTier, createDefaultLogicNodeConfig, applyDagreLayout } from './chunk-WNCPAWLC.mjs';
@@ -1232,7 +1232,15 @@ function DatasourceNodeConfigForm({
1232
1232
  const [selectedColumns, setSelectedColumns] = useState([...config.selectedColumns ?? []]);
1233
1233
  const [outputVariable, setOutputVariable] = useState(config.outputVariable);
1234
1234
  const [limit, setLimit] = useState(config.limit);
1235
- const [filterVariables, setFilterVariables] = useState({ ...config.filterVariables });
1235
+ const [filters, setFilters] = useState(() => {
1236
+ if (config.filters && config.filters.length > 0) return [...config.filters];
1237
+ const legacy = Object.entries(config.filterVariables ?? {}).filter(([variableName, column]) => variableName && column).map(([variableName, column]) => ({
1238
+ column,
1239
+ op: "eq",
1240
+ value: `{{ inputs.${variableName} }}`
1241
+ }));
1242
+ return legacy;
1243
+ });
1236
1244
  const [tableSearch, setTableSearch] = useState("");
1237
1245
  const [columnSearch, setColumnSearch] = useState("");
1238
1246
  const allColumnNames = availableColumns.map((column) => column.name);
@@ -1274,7 +1282,7 @@ function DatasourceNodeConfigForm({
1274
1282
  setAvailableTables([]);
1275
1283
  setAvailableColumns([]);
1276
1284
  setSelectedColumns([]);
1277
- setFilterVariables({});
1285
+ setFilters([]);
1278
1286
  setTableSearch("");
1279
1287
  setColumnSearch("");
1280
1288
  };
@@ -1282,7 +1290,7 @@ function DatasourceNodeConfigForm({
1282
1290
  setSelectedTable(table);
1283
1291
  setAvailableColumns([]);
1284
1292
  setSelectedColumns([]);
1285
- setFilterVariables({});
1293
+ setFilters([]);
1286
1294
  setColumnSearch("");
1287
1295
  };
1288
1296
  const handleToggleColumn = (columnName) => {
@@ -1291,32 +1299,46 @@ function DatasourceNodeConfigForm({
1291
1299
  );
1292
1300
  };
1293
1301
  const handleAddFilter = () => {
1294
- setFilterVariables((previous) => ({ ...previous, [`field_${Object.keys(previous).length}`]: "" }));
1302
+ setFilters((previous) => [...previous, { column: "", op: "eq", value: "" }]);
1295
1303
  };
1296
- const handleUpdateFilterVariable = (oldKey, newKey) => {
1297
- setFilterVariables((previous) => {
1298
- const updated = { ...previous };
1299
- const value = updated[oldKey] ?? "";
1300
- delete updated[oldKey];
1301
- updated[newKey] = value;
1302
- return updated;
1303
- });
1304
+ const handleUpdateFilterColumn = (index, nextColumn) => {
1305
+ setFilters((previous) => previous.map((row, i) => i === index ? { ...row, column: nextColumn } : row));
1304
1306
  };
1305
- const handleUpdateFilterField = (key, newValue) => {
1306
- setFilterVariables((previous) => ({ ...previous, [key]: newValue }));
1307
+ const handleUpdateFilterOp = (index, nextOp) => {
1308
+ setFilters((previous) => previous.map((row, i) => {
1309
+ if (i !== index) return row;
1310
+ if (nextOp === "in") {
1311
+ const arr = Array.isArray(row.value) ? row.value : row.value == null || row.value === "" ? [] : [row.value];
1312
+ return { ...row, op: nextOp, value: arr };
1313
+ }
1314
+ if (Array.isArray(row.value)) {
1315
+ return { ...row, op: nextOp, value: row.value[0] ?? "" };
1316
+ }
1317
+ return { ...row, op: nextOp };
1318
+ }));
1307
1319
  };
1308
- const handleRemoveFilter = (key) => {
1309
- setFilterVariables((previous) => {
1310
- const updated = { ...previous };
1311
- delete updated[key];
1312
- return updated;
1313
- });
1320
+ const handleUpdateFilterValue = (index, nextValue) => {
1321
+ setFilters((previous) => previous.map((row, i) => {
1322
+ if (i !== index) return row;
1323
+ if (row.op === "in") {
1324
+ const arr = nextValue.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
1325
+ return { ...row, value: arr };
1326
+ }
1327
+ return { ...row, value: nextValue };
1328
+ }));
1329
+ };
1330
+ const handleRemoveFilter = (index) => {
1331
+ setFilters((previous) => previous.filter((_, i) => i !== index));
1314
1332
  };
1315
1333
  const handleSave = () => {
1316
- const cleanedFilters = {};
1317
- for (const [key, value] of Object.entries(filterVariables)) {
1318
- if (key.trim() && value.trim()) cleanedFilters[key.trim()] = value.trim();
1319
- }
1334
+ const cleaned = filters.filter((row) => row.column.trim()).map((row) => {
1335
+ const op = row.op ?? "eq";
1336
+ if (op === "in") {
1337
+ const arr = Array.isArray(row.value) ? row.value : [];
1338
+ return { column: row.column.trim(), op, value: arr };
1339
+ }
1340
+ return { column: row.column.trim(), op, value: row.value ?? "" };
1341
+ });
1320
1342
  onSave({
1321
1343
  ...config,
1322
1344
  datasourceId: selectedDatasourceId,
@@ -1325,10 +1347,13 @@ function DatasourceNodeConfigForm({
1325
1347
  selectedColumns,
1326
1348
  outputVariable: outputVariable.trim(),
1327
1349
  limit,
1328
- filterVariables: cleanedFilters
1350
+ filters: cleaned,
1351
+ // Clear the legacy field so the engine's back-compat path doesn't
1352
+ // double-apply filters after a round-trip through this form.
1353
+ filterVariables: void 0
1329
1354
  });
1330
1355
  };
1331
- const filterEntries = Object.entries(filterVariables);
1356
+ const filterEntries = filters;
1332
1357
  const canSave = selectedDatasourceId && selectedTable && selectedColumns.length > 0;
1333
1358
  function renderSection() {
1334
1359
  switch (activeSectionId) {
@@ -1502,42 +1527,65 @@ function DatasourceNodeConfigForm({
1502
1527
  ]
1503
1528
  }
1504
1529
  )
1505
- ] }) : /* @__PURE__ */ jsx("div", { className: "space-y-2", children: filterEntries.map(([variableName, columnName], index) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-xl border border-gray-200 bg-white px-3 py-2 dark:border-white/10 dark:bg-white/[0.03]", children: [
1506
- /* @__PURE__ */ jsxs(
1507
- "select",
1508
- {
1509
- value: columnName,
1510
- onChange: (event) => handleUpdateFilterField(variableName, event.target.value),
1511
- disabled: readOnly,
1512
- className: "flex-1 rounded bg-transparent text-xs text-gray-900 outline-none disabled:opacity-60 dark:text-white",
1513
- children: [
1514
- /* @__PURE__ */ jsx("option", { value: "", children: t("columnName") }),
1515
- availableColumns.map((column) => /* @__PURE__ */ jsx("option", { value: column.name, children: column.name }, column.name))
1516
- ]
1517
- }
1518
- ),
1519
- /* @__PURE__ */ jsx("span", { className: "text-[10px] font-bold text-gray-400", children: "=" }),
1520
- /* @__PURE__ */ jsx(
1521
- "input",
1522
- {
1523
- type: "text",
1524
- value: variableName,
1525
- onChange: (event) => handleUpdateFilterVariable(variableName, event.target.value),
1526
- placeholder: t("variableReference"),
1527
- readOnly,
1528
- className: "flex-1 bg-transparent text-xs text-gray-900 outline-none placeholder:text-gray-400 dark:text-white dark:placeholder:text-gray-500"
1529
- }
1530
- ),
1531
- !readOnly && /* @__PURE__ */ jsx(
1532
- "button",
1533
- {
1534
- type: "button",
1535
- onClick: () => handleRemoveFilter(variableName),
1536
- className: "shrink-0 rounded-md p-1 text-gray-400 transition-colors hover:bg-red-50 hover:text-red-500 dark:hover:bg-red-900/20",
1537
- children: /* @__PURE__ */ jsx(XMarkIcon$1, { className: "h-3 w-3" })
1538
- }
1539
- )
1540
- ] }, index)) })
1530
+ ] }) : /* @__PURE__ */ jsx("div", { className: "space-y-2", children: filterEntries.map((row, index) => {
1531
+ const currentOp = row.op ?? "eq";
1532
+ const valueDisplay = Array.isArray(row.value) ? row.value.map(String).join(", ") : row.value == null ? "" : String(row.value);
1533
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-xl border border-gray-200 bg-white px-3 py-2 dark:border-white/10 dark:bg-white/[0.03]", children: [
1534
+ /* @__PURE__ */ jsxs(
1535
+ "select",
1536
+ {
1537
+ value: row.column,
1538
+ onChange: (event) => handleUpdateFilterColumn(index, event.target.value),
1539
+ disabled: readOnly,
1540
+ className: "w-[28%] rounded bg-transparent text-xs text-gray-900 outline-none disabled:opacity-60 dark:text-white",
1541
+ children: [
1542
+ /* @__PURE__ */ jsx("option", { value: "", children: t("columnName") }),
1543
+ availableColumns.map((column) => /* @__PURE__ */ jsx("option", { value: column.name, children: column.name }, column.name))
1544
+ ]
1545
+ }
1546
+ ),
1547
+ /* @__PURE__ */ jsxs(
1548
+ "select",
1549
+ {
1550
+ value: currentOp,
1551
+ onChange: (event) => handleUpdateFilterOp(index, event.target.value),
1552
+ disabled: readOnly,
1553
+ className: "w-[18%] rounded bg-gray-50 px-1 text-center text-[10px] font-semibold uppercase text-gray-700 outline-none disabled:opacity-60 dark:bg-white/[0.05] dark:text-gray-200",
1554
+ title: t("filterOperator", { _: "Filter operator" }),
1555
+ children: [
1556
+ /* @__PURE__ */ jsx("option", { value: "eq", children: "= (eq)" }),
1557
+ /* @__PURE__ */ jsx("option", { value: "neq", children: "\u2260 (neq)" }),
1558
+ /* @__PURE__ */ jsx("option", { value: "lt", children: "< (lt)" }),
1559
+ /* @__PURE__ */ jsx("option", { value: "lte", children: "\u2264 (lte)" }),
1560
+ /* @__PURE__ */ jsx("option", { value: "gt", children: "> (gt)" }),
1561
+ /* @__PURE__ */ jsx("option", { value: "gte", children: "\u2265 (gte)" }),
1562
+ /* @__PURE__ */ jsx("option", { value: "like", children: "LIKE" }),
1563
+ /* @__PURE__ */ jsx("option", { value: "in", children: "IN (\u2026)" })
1564
+ ]
1565
+ }
1566
+ ),
1567
+ /* @__PURE__ */ jsx(
1568
+ "input",
1569
+ {
1570
+ type: "text",
1571
+ value: valueDisplay,
1572
+ onChange: (event) => handleUpdateFilterValue(index, event.target.value),
1573
+ placeholder: currentOp === "in" ? "comma-separated, e.g. SP, RJ, MG" : t("variableReference"),
1574
+ readOnly,
1575
+ className: "flex-1 bg-transparent text-xs text-gray-900 outline-none placeholder:text-gray-400 dark:text-white dark:placeholder:text-gray-500"
1576
+ }
1577
+ ),
1578
+ !readOnly && /* @__PURE__ */ jsx(
1579
+ "button",
1580
+ {
1581
+ type: "button",
1582
+ onClick: () => handleRemoveFilter(index),
1583
+ className: "shrink-0 rounded-md p-1 text-gray-400 transition-colors hover:bg-red-50 hover:text-red-500 dark:hover:bg-red-900/20",
1584
+ children: /* @__PURE__ */ jsx(XMarkIcon$1, { className: "h-3 w-3" })
1585
+ }
1586
+ )
1587
+ ] }, index);
1588
+ }) })
1541
1589
  ] });
1542
1590
  case "output":
1543
1591
  return /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
@@ -2673,7 +2721,9 @@ var CodeFlowNode = memo(function CodeFlowNode2({ id, data, selected }) {
2673
2721
  const t = useTranslations("agents.workflow");
2674
2722
  const { config, label, onDelete, onEdit } = data;
2675
2723
  const isCompact = data.displayMode === "compact";
2676
- const codePreview = config.code ? config.code.slice(0, 30) : "";
2724
+ const isScripted = "language" in config;
2725
+ const badgeLabel = isScripted ? config.language : config.operation;
2726
+ const codePreview = isScripted && config.code ? config.code.slice(0, 30) : "";
2677
2727
  return /* @__PURE__ */ jsxs(Fragment, { children: [
2678
2728
  /* @__PURE__ */ jsx(NodeRunningIndicator, { nodeId: id }),
2679
2729
  /* @__PURE__ */ jsx(WorkflowHandle, { type: "target", position: Position.Left, id: "left-in", colorClass: "!bg-cyan-500" }),
@@ -2699,7 +2749,7 @@ var CodeFlowNode = memo(function CodeFlowNode2({ id, data, selected }) {
2699
2749
  /* @__PURE__ */ jsxs(NodeCardMeta, { compact: isCompact, children: [
2700
2750
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2701
2751
  /* @__PURE__ */ jsx(NodeCardBadge, { tone: "code", children: t("codeNode") }),
2702
- /* @__PURE__ */ jsx(NodeCardBadge, { tone: "code", soft: true, children: config.language }),
2752
+ /* @__PURE__ */ jsx(NodeCardBadge, { tone: "code", soft: true, children: badgeLabel }),
2703
2753
  codePreview && /* @__PURE__ */ jsxs("span", { className: "truncate text-[10px] text-gray-400 dark:text-gray-500", children: [
2704
2754
  codePreview,
2705
2755
  "..."
@@ -4080,59 +4130,274 @@ function IfElseNodeConfigForm({ config, onSave, onCancel }) {
4080
4130
  ] });
4081
4131
  }
4082
4132
  var LANGUAGE_OPTIONS = ["javascript", "python", "typescript"];
4133
+ var OPERATION_OPTIONS = [
4134
+ { value: "passthrough", label: "passthrough", help: "Emit the incoming value unchanged." },
4135
+ { value: "return", label: "return", help: "Emit a literal JSON value." },
4136
+ { value: "merge", label: "merge", help: "Shallow-merge incoming + a literal object." },
4137
+ { value: "pick", label: "pick", help: "Project specific keys off the incoming object." },
4138
+ { value: "parse_json", label: "parse_json", help: "Parse a stringified JSON out of an upstream field (e.g. an agent's `.text`)." },
4139
+ { value: "regex_extract", label: "regex_extract", help: "Run a regex over `from`, return the capture group." },
4140
+ { value: "xml_titles", label: "xml_titles", help: "Extract every `<title>` from an RSS/Atom body." }
4141
+ ];
4142
+ function hasLanguage(config) {
4143
+ return "language" in config && typeof config.language === "string";
4144
+ }
4145
+ function hasOperation(config) {
4146
+ return "operation" in config && typeof config.operation === "string";
4147
+ }
4083
4148
  function CodeNodeConfigForm({ config, onSave, onCancel }) {
4084
4149
  const t = useTranslations("agents.workflow.codeNodeConfig");
4085
- const [language, setLanguage] = useState(config.language);
4086
- const [code, setCode] = useState(config.code);
4150
+ const initialMode = hasOperation(config) ? "operation" : "scripted";
4151
+ const [mode, setMode] = useState(initialMode);
4152
+ const [language, setLanguage] = useState(
4153
+ hasLanguage(config) ? config.language : "javascript"
4154
+ );
4155
+ const [code, setCode] = useState(hasLanguage(config) ? config.code : "");
4156
+ const op0 = hasOperation(config) ? config : void 0;
4157
+ const [operation, setOperation] = useState(op0?.operation ?? "passthrough");
4158
+ const [from, setFrom] = useState(op0?.from ?? "");
4159
+ const [valueText, setValueText] = useState(
4160
+ op0?.value !== void 0 ? JSON.stringify(op0.value, null, 2) : ""
4161
+ );
4162
+ const [fieldsText, setFieldsText] = useState((op0?.fields ?? []).join(", "));
4163
+ const [pattern, setPattern] = useState(op0?.pattern ?? "");
4164
+ const [flags, setFlags] = useState(op0?.flags ?? "");
4165
+ const [group, setGroup] = useState(op0?.group ?? 1);
4166
+ const [output, setOutput] = useState(op0?.output ?? "");
4167
+ const [keepChannel, setKeepChannel] = useState(op0?.keepChannel ?? false);
4168
+ const selectedOpHelp = useMemo(
4169
+ () => OPERATION_OPTIONS.find((o) => o.value === operation)?.help ?? "",
4170
+ [operation]
4171
+ );
4087
4172
  const handleSave = () => {
4088
- onSave({ ...config, language, code });
4173
+ if (mode === "scripted") {
4174
+ onSave({ type: "code", language, code });
4175
+ return;
4176
+ }
4177
+ const next = { type: "code", operation };
4178
+ if (from.trim()) next.from = from.trim();
4179
+ if (operation === "return" && valueText.trim()) {
4180
+ try {
4181
+ next.value = JSON.parse(valueText);
4182
+ } catch {
4183
+ next.value = valueText;
4184
+ }
4185
+ }
4186
+ if (operation === "merge" && valueText.trim()) {
4187
+ try {
4188
+ next.value = JSON.parse(valueText);
4189
+ } catch {
4190
+ next.value = valueText;
4191
+ }
4192
+ }
4193
+ if (operation === "pick") {
4194
+ next.fields = fieldsText.split(",").map((s) => s.trim()).filter(Boolean);
4195
+ }
4196
+ if (operation === "regex_extract") {
4197
+ next.pattern = pattern;
4198
+ if (flags.trim()) next.flags = flags.trim();
4199
+ if (group !== 1) next.group = group;
4200
+ }
4201
+ if (operation === "xml_titles" && keepChannel) next.keepChannel = true;
4202
+ if ((operation === "regex_extract" || operation === "xml_titles") && output.trim()) {
4203
+ next.output = output.trim();
4204
+ }
4205
+ onSave(next);
4089
4206
  };
4207
+ const labelClass = "mb-1 block text-xs font-medium text-gray-700 dark:text-gray-300";
4208
+ const inputClass = "w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 placeholder-gray-400 outline-none focus:border-indigo-400 focus:ring-2 focus:ring-indigo-400/20 dark:border-gray-600 dark:bg-gray-800 dark:text-white dark:placeholder-gray-500";
4209
+ const textareaClass = `${inputClass} font-mono text-xs`;
4090
4210
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
4091
- /* @__PURE__ */ jsxs("div", { children: [
4092
- /* @__PURE__ */ jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700 dark:text-gray-300", children: t("languageLabel") }),
4093
- /* @__PURE__ */ jsx(
4094
- "select",
4095
- {
4096
- value: language,
4097
- onChange: (event) => setLanguage(event.target.value),
4098
- className: "w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 placeholder-gray-400 outline-none focus:border-indigo-400 focus:ring-2 focus:ring-indigo-400/20 dark:border-gray-600 dark:bg-gray-800 dark:text-white dark:placeholder-gray-500",
4099
- children: LANGUAGE_OPTIONS.map((option) => /* @__PURE__ */ jsx("option", { value: option, children: option }, option))
4100
- }
4101
- )
4102
- ] }),
4103
- /* @__PURE__ */ jsxs("div", { children: [
4104
- /* @__PURE__ */ jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700 dark:text-gray-300", children: t("codeLabel") }),
4105
- /* @__PURE__ */ jsx(
4106
- "textarea",
4107
- {
4108
- value: code,
4109
- onChange: (event) => setCode(event.target.value),
4110
- placeholder: t("codePlaceholder"),
4111
- rows: 10,
4112
- className: "w-full rounded-lg border border-gray-300 bg-white px-3 py-2 font-mono text-xs text-gray-900 placeholder-gray-400 outline-none focus:border-indigo-400 focus:ring-2 focus:ring-indigo-400/20 dark:border-gray-600 dark:bg-gray-800 dark:text-white dark:placeholder-gray-500"
4113
- }
4114
- )
4115
- ] }),
4116
- /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2 pt-4 border-t border-gray-200 dark:border-gray-700", children: [
4211
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2 rounded-lg bg-gray-100 p-1 text-sm dark:bg-gray-800", children: [
4117
4212
  /* @__PURE__ */ jsx(
4118
4213
  "button",
4119
4214
  {
4120
4215
  type: "button",
4121
- onClick: onCancel,
4122
- className: "rounded-lg border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800",
4123
- children: t("cancel")
4216
+ onClick: () => setMode("operation"),
4217
+ className: `flex-1 rounded-md px-3 py-1.5 font-medium transition ${mode === "operation" ? "bg-white text-indigo-600 shadow-sm dark:bg-gray-900 dark:text-indigo-400" : "text-gray-600 dark:text-gray-400"}`,
4218
+ children: t("modeOperation", { _: "Operation" })
4124
4219
  }
4125
4220
  ),
4126
4221
  /* @__PURE__ */ jsx(
4127
4222
  "button",
4128
4223
  {
4129
4224
  type: "button",
4130
- onClick: handleSave,
4131
- className: "rounded-lg bg-indigo-600 px-4 py-2 text-sm font-medium text-white hover:bg-indigo-700 dark:bg-indigo-500 dark:hover:bg-indigo-600",
4132
- children: t("save")
4225
+ onClick: () => setMode("scripted"),
4226
+ className: `flex-1 rounded-md px-3 py-1.5 font-medium transition ${mode === "scripted" ? "bg-white text-indigo-600 shadow-sm dark:bg-gray-900 dark:text-indigo-400" : "text-gray-600 dark:text-gray-400"}`,
4227
+ children: t("modeScripted", { _: "Scripted" })
4133
4228
  }
4134
4229
  )
4135
- ] })
4230
+ ] }),
4231
+ mode === "scripted" ? /* @__PURE__ */ jsxs(Fragment, { children: [
4232
+ /* @__PURE__ */ jsxs("div", { children: [
4233
+ /* @__PURE__ */ jsx("label", { className: labelClass, children: t("languageLabel") }),
4234
+ /* @__PURE__ */ jsx(
4235
+ "select",
4236
+ {
4237
+ value: language,
4238
+ onChange: (event) => setLanguage(event.target.value),
4239
+ className: inputClass,
4240
+ children: LANGUAGE_OPTIONS.map((option) => /* @__PURE__ */ jsx("option", { value: option, children: option }, option))
4241
+ }
4242
+ ),
4243
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-[11px] text-amber-600 dark:text-amber-400", children: "The engine has no embedded script runtime \u2014 scripted nodes fail at execution today. Prefer the Operation mode for real graphs." })
4244
+ ] }),
4245
+ /* @__PURE__ */ jsxs("div", { children: [
4246
+ /* @__PURE__ */ jsx("label", { className: labelClass, children: t("codeLabel") }),
4247
+ /* @__PURE__ */ jsx(
4248
+ "textarea",
4249
+ {
4250
+ value: code,
4251
+ onChange: (event) => setCode(event.target.value),
4252
+ placeholder: t("codePlaceholder"),
4253
+ rows: 10,
4254
+ className: textareaClass
4255
+ }
4256
+ )
4257
+ ] })
4258
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
4259
+ /* @__PURE__ */ jsxs("div", { children: [
4260
+ /* @__PURE__ */ jsx("label", { className: labelClass, children: "Operation" }),
4261
+ /* @__PURE__ */ jsx(
4262
+ "select",
4263
+ {
4264
+ value: operation,
4265
+ onChange: (event) => setOperation(event.target.value),
4266
+ className: inputClass,
4267
+ children: OPERATION_OPTIONS.map((o) => /* @__PURE__ */ jsx("option", { value: o.value, children: o.label }, o.value))
4268
+ }
4269
+ ),
4270
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-[11px] text-gray-500 dark:text-gray-400", children: selectedOpHelp })
4271
+ ] }),
4272
+ operation !== "return" && /* @__PURE__ */ jsxs("div", { children: [
4273
+ /* @__PURE__ */ jsx("label", { className: labelClass, children: "from" }),
4274
+ /* @__PURE__ */ jsx(
4275
+ "input",
4276
+ {
4277
+ type: "text",
4278
+ value: from,
4279
+ onChange: (event) => setFrom(event.target.value),
4280
+ placeholder: "e.g. http-news.body",
4281
+ className: inputClass
4282
+ }
4283
+ ),
4284
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-[11px] text-gray-500 dark:text-gray-400", children: "Dotted reference into the variable pool. Leave blank to read the sole incoming edge." })
4285
+ ] }),
4286
+ (operation === "return" || operation === "merge") && /* @__PURE__ */ jsxs("div", { children: [
4287
+ /* @__PURE__ */ jsxs("label", { className: labelClass, children: [
4288
+ "value ",
4289
+ operation === "merge" ? "(object to merge in)" : "(literal JSON)"
4290
+ ] }),
4291
+ /* @__PURE__ */ jsx(
4292
+ "textarea",
4293
+ {
4294
+ value: valueText,
4295
+ onChange: (event) => setValueText(event.target.value),
4296
+ placeholder: '{"recommendedPrice": 6.29}',
4297
+ rows: 6,
4298
+ className: textareaClass
4299
+ }
4300
+ )
4301
+ ] }),
4302
+ operation === "pick" && /* @__PURE__ */ jsxs("div", { children: [
4303
+ /* @__PURE__ */ jsx("label", { className: labelClass, children: "fields" }),
4304
+ /* @__PURE__ */ jsx(
4305
+ "input",
4306
+ {
4307
+ type: "text",
4308
+ value: fieldsText,
4309
+ onChange: (event) => setFieldsText(event.target.value),
4310
+ placeholder: "id, name, state",
4311
+ className: inputClass
4312
+ }
4313
+ ),
4314
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-[11px] text-gray-500 dark:text-gray-400", children: "Comma-separated keys to project." })
4315
+ ] }),
4316
+ operation === "regex_extract" && /* @__PURE__ */ jsxs(Fragment, { children: [
4317
+ /* @__PURE__ */ jsxs("div", { children: [
4318
+ /* @__PURE__ */ jsx("label", { className: labelClass, children: "pattern" }),
4319
+ /* @__PURE__ */ jsx(
4320
+ "input",
4321
+ {
4322
+ type: "text",
4323
+ value: pattern,
4324
+ onChange: (event) => setPattern(event.target.value),
4325
+ placeholder: `R\\$(\\d+\\.\\d+)`,
4326
+ className: `${inputClass} font-mono text-xs`
4327
+ }
4328
+ )
4329
+ ] }),
4330
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
4331
+ /* @__PURE__ */ jsxs("div", { children: [
4332
+ /* @__PURE__ */ jsx("label", { className: labelClass, children: "flags" }),
4333
+ /* @__PURE__ */ jsx(
4334
+ "input",
4335
+ {
4336
+ type: "text",
4337
+ value: flags,
4338
+ onChange: (event) => setFlags(event.target.value),
4339
+ placeholder: "e.g. i or ims",
4340
+ className: inputClass
4341
+ }
4342
+ ),
4343
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-[11px] text-gray-500 dark:text-gray-400", children: "i, m, s, x, U, R (g is implicit)." })
4344
+ ] }),
4345
+ /* @__PURE__ */ jsxs("div", { children: [
4346
+ /* @__PURE__ */ jsx("label", { className: labelClass, children: "group" }),
4347
+ /* @__PURE__ */ jsx(
4348
+ "input",
4349
+ {
4350
+ type: "number",
4351
+ value: group,
4352
+ onChange: (event) => setGroup(Number(event.target.value) || 0),
4353
+ min: 0,
4354
+ className: inputClass
4355
+ }
4356
+ ),
4357
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-[11px] text-gray-500 dark:text-gray-400", children: "0 = whole match, 1 = first capture group (default)." })
4358
+ ] })
4359
+ ] })
4360
+ ] }),
4361
+ operation === "xml_titles" && /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300", children: [
4362
+ /* @__PURE__ */ jsx(
4363
+ "input",
4364
+ {
4365
+ type: "checkbox",
4366
+ checked: keepChannel,
4367
+ onChange: (event) => setKeepChannel(event.target.checked),
4368
+ className: "h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500 dark:border-gray-600"
4369
+ }
4370
+ ),
4371
+ "Keep channel/feed title (default: drop the first `<title>`)."
4372
+ ] }),
4373
+ (operation === "regex_extract" || operation === "xml_titles") && /* @__PURE__ */ jsxs("div", { children: [
4374
+ /* @__PURE__ */ jsx("label", { className: labelClass, children: "output key" }),
4375
+ /* @__PURE__ */ jsx(
4376
+ "input",
4377
+ {
4378
+ type: "text",
4379
+ value: output,
4380
+ onChange: (event) => setOutput(event.target.value),
4381
+ placeholder: operation === "regex_extract" ? "matches" : "titles",
4382
+ className: inputClass
4383
+ }
4384
+ ),
4385
+ /* @__PURE__ */ jsxs("p", { className: "mt-1 text-[11px] text-gray-500 dark:text-gray-400", children: [
4386
+ "Defaults to `",
4387
+ operation === "regex_extract" ? "matches" : "titles",
4388
+ "` if left blank."
4389
+ ] })
4390
+ ] })
4391
+ ] }),
4392
+ /* @__PURE__ */ jsx(
4393
+ ConfigFormActions,
4394
+ {
4395
+ cancelLabel: t("cancel"),
4396
+ saveLabel: t("save"),
4397
+ onCancel,
4398
+ onSave: handleSave
4399
+ }
4400
+ )
4136
4401
  ] });
4137
4402
  }
4138
4403
  var HTTP_METHODS = ["GET", "POST", "PUT", "DELETE"];
@@ -4937,7 +5202,7 @@ function DocumentExtractorNodeConfigForm({ config, onSave, onCancel }) {
4937
5202
  )
4938
5203
  ] });
4939
5204
  }
4940
- var OPERATION_OPTIONS = ["filter", "map", "sort", "limit", "deduplicate", "flatten"];
5205
+ var OPERATION_OPTIONS2 = ["filter", "map", "sort", "limit", "deduplicate", "flatten"];
4941
5206
  var SORT_ORDER_OPTIONS = ["asc", "desc"];
4942
5207
  function ListOperatorNodeConfigForm({ config, onSave, onCancel }) {
4943
5208
  const t = useTranslations("agents.workflow.listOperatorNodeConfig");
@@ -4979,7 +5244,7 @@ function ListOperatorNodeConfigForm({ config, onSave, onCancel }) {
4979
5244
  value: operation,
4980
5245
  onChange: (event) => setOperation(event.target.value),
4981
5246
  className: "w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 placeholder-gray-400 outline-none focus:border-indigo-400 focus:ring-2 focus:ring-indigo-400/20 dark:border-gray-600 dark:bg-gray-800 dark:text-white dark:placeholder-gray-500",
4982
- children: OPERATION_OPTIONS.map((operationOption) => /* @__PURE__ */ jsx("option", { value: operationOption, children: t(`operation_${operationOption}`) }, operationOption))
5247
+ children: OPERATION_OPTIONS2.map((operationOption) => /* @__PURE__ */ jsx("option", { value: operationOption, children: t(`operation_${operationOption}`) }, operationOption))
4983
5248
  }
4984
5249
  )
4985
5250
  ] }),
@@ -5648,6 +5913,220 @@ function ModelProviderNodeConfigForm({ config, onSave, onCancel }) {
5648
5913
  )
5649
5914
  ] });
5650
5915
  }
5916
+ function RuleNodeConfigForm({ config, onSave, onCancel, availableRules }) {
5917
+ const t = useTranslations("agents.workflow.ruleNodeConfig");
5918
+ const [ruleId, setRuleId] = useState(config.ruleId ?? "");
5919
+ const [priority, setPriority] = useState(config.priority ?? 1);
5920
+ const [enabled, setEnabled] = useState(config.enabled ?? true);
5921
+ const handleSave = () => {
5922
+ const next = { ...config, type: "rule", ruleId: ruleId.trim(), priority, enabled };
5923
+ onSave(next);
5924
+ };
5925
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
5926
+ /* @__PURE__ */ jsxs("div", { children: [
5927
+ /* @__PURE__ */ jsx("label", { className: "mb-1 block text-xs font-medium text-gray-700 dark:text-gray-300", children: t("ruleIdLabel", { _: "Rule" }) }),
5928
+ availableRules && availableRules.length > 0 ? /* @__PURE__ */ jsxs(
5929
+ "select",
5930
+ {
5931
+ value: ruleId,
5932
+ onChange: (event) => setRuleId(event.target.value),
5933
+ className: "w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 outline-none focus:border-indigo-400 focus:ring-2 focus:ring-indigo-400/20 dark:border-gray-600 dark:bg-gray-800 dark:text-white",
5934
+ children: [
5935
+ /* @__PURE__ */ jsx("option", { value: "", children: t("rulePickPrompt", { _: "Choose a rule\u2026" }) }),
5936
+ availableRules.map((rule) => /* @__PURE__ */ jsx("option", { value: rule.ruleId, children: rule.name ? `${rule.name} (${rule.ruleId})` : rule.ruleId }, rule.ruleId))
5937
+ ]
5938
+ }
5939
+ ) : /* @__PURE__ */ jsx(
5940
+ FormInput,
5941
+ {
5942
+ type: "text",
5943
+ value: ruleId,
5944
+ onValueChange: setRuleId,
5945
+ placeholder: "e.g. peak_hours"
5946
+ }
5947
+ ),
5948
+ /* @__PURE__ */ jsxs("p", { className: "mt-1 text-[11px] text-gray-500 dark:text-gray-400", children: [
5949
+ "References an ",
5950
+ /* @__PURE__ */ jsx("code", { children: "astrlabe.agent_rules.rule_id" }),
5951
+ " row in this organization."
5952
+ ] })
5953
+ ] }),
5954
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
5955
+ /* @__PURE__ */ jsx(
5956
+ FormInput,
5957
+ {
5958
+ type: "number",
5959
+ label: t("priorityLabel", { _: "Priority" }),
5960
+ value: String(priority),
5961
+ onValueChange: (value) => setPriority(Number(value) || 0),
5962
+ min: 0
5963
+ }
5964
+ ),
5965
+ /* @__PURE__ */ jsxs("label", { className: "flex items-end gap-2 pb-2 text-sm text-gray-700 dark:text-gray-300", children: [
5966
+ /* @__PURE__ */ jsx(
5967
+ "input",
5968
+ {
5969
+ type: "checkbox",
5970
+ checked: enabled,
5971
+ onChange: (event) => setEnabled(event.target.checked),
5972
+ className: "h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500 dark:border-gray-600"
5973
+ }
5974
+ ),
5975
+ t("enabledLabel", { _: "Enabled" })
5976
+ ] })
5977
+ ] }),
5978
+ /* @__PURE__ */ jsx(
5979
+ ConfigFormActions,
5980
+ {
5981
+ cancelLabel: t("cancel"),
5982
+ saveLabel: t("save"),
5983
+ onCancel,
5984
+ onSave: handleSave
5985
+ }
5986
+ )
5987
+ ] });
5988
+ }
5989
+ function AgentNodeConfigForm({
5990
+ config,
5991
+ onSave,
5992
+ onCancel,
5993
+ availableAgents,
5994
+ availableConnections
5995
+ }) {
5996
+ const t = useTranslations("agents.workflow.agentNodeConfig");
5997
+ const [agentId, setAgentId] = useState(config.agentId ?? "");
5998
+ const [connectionId, setConnectionId] = useState(config.connectionId ?? "");
5999
+ const [modelId, setModelId] = useState(config.modelId ?? "");
6000
+ const [systemPrompt, setSystemPrompt] = useState(config.systemPrompt ?? "");
6001
+ const [userPrompt, setUserPrompt] = useState(config.userPrompt ?? "");
6002
+ const [maxTokens, setMaxTokens] = useState(config.maxTokens ?? 1024);
6003
+ const [temperature, setTemperature] = useState(config.temperature ?? 0.2);
6004
+ const handleSave = () => {
6005
+ const next = {
6006
+ ...config,
6007
+ type: "agent",
6008
+ agentId: agentId.trim() || void 0,
6009
+ connectionId: connectionId.trim() || void 0,
6010
+ modelId: modelId.trim() || void 0,
6011
+ systemPrompt: systemPrompt || void 0,
6012
+ userPrompt: userPrompt || void 0,
6013
+ maxTokens,
6014
+ temperature
6015
+ };
6016
+ onSave(next);
6017
+ };
6018
+ const agentOptions = (availableAgents ?? []).map((agent) => ({
6019
+ value: agent.id,
6020
+ label: agent.name ?? agent.slug ?? agent.id
6021
+ }));
6022
+ const connOptions = (availableConnections ?? []).map((conn) => ({
6023
+ value: conn.id,
6024
+ label: conn.providerType ? `${conn.name ?? conn.id} (${conn.providerType})` : conn.name ?? conn.id
6025
+ }));
6026
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
6027
+ agentOptions.length > 0 ? /* @__PURE__ */ jsx(
6028
+ FormSelect,
6029
+ {
6030
+ label: t("agentLabel", { _: "Agent" }),
6031
+ value: agentId,
6032
+ onValueChange: setAgentId,
6033
+ options: [{ value: "", label: t("agentPickPrompt", { _: "Choose an agent\u2026" }) }, ...agentOptions]
6034
+ }
6035
+ ) : /* @__PURE__ */ jsx(
6036
+ FormInput,
6037
+ {
6038
+ type: "text",
6039
+ label: t("agentIdLabel", { _: "Agent ID" }),
6040
+ value: agentId,
6041
+ onValueChange: setAgentId,
6042
+ placeholder: "UUID of an astrlabe.agents row"
6043
+ }
6044
+ ),
6045
+ connOptions.length > 0 ? /* @__PURE__ */ jsx(
6046
+ FormSelect,
6047
+ {
6048
+ label: t("connectionLabel", { _: "Model provider connection" }),
6049
+ value: connectionId,
6050
+ onValueChange: setConnectionId,
6051
+ options: [{ value: "", label: t("connectionPickPrompt", { _: "Inline model (no connection)" }) }, ...connOptions]
6052
+ }
6053
+ ) : /* @__PURE__ */ jsx(
6054
+ FormInput,
6055
+ {
6056
+ type: "text",
6057
+ label: t("connectionIdLabel", { _: "Connection ID" }),
6058
+ value: connectionId,
6059
+ onValueChange: setConnectionId,
6060
+ placeholder: "UUID of a model_provider_connections row"
6061
+ }
6062
+ ),
6063
+ /* @__PURE__ */ jsx(
6064
+ FormInput,
6065
+ {
6066
+ type: "text",
6067
+ label: t("modelIdLabel", { _: "Model override (optional)" }),
6068
+ value: modelId,
6069
+ onValueChange: setModelId,
6070
+ placeholder: "amazon.nova-lite-v1:0"
6071
+ }
6072
+ ),
6073
+ /* @__PURE__ */ jsx(
6074
+ FormTextarea,
6075
+ {
6076
+ label: t("systemPromptLabel", { _: "System prompt" }),
6077
+ value: systemPrompt,
6078
+ onValueChange: setSystemPrompt,
6079
+ placeholder: "You are a data analyst. Base every sentence on the provided data \u2014 no speculation.",
6080
+ rows: 4
6081
+ }
6082
+ ),
6083
+ /* @__PURE__ */ jsx(
6084
+ FormTextarea,
6085
+ {
6086
+ label: t("userPromptLabel", { _: "User prompt" }),
6087
+ value: userPrompt,
6088
+ onValueChange: setUserPrompt,
6089
+ placeholder: "Station profile:\n{{ ds-station.station }}\n\nProduce a \u2264120-word briefing\u2026",
6090
+ rows: 8
6091
+ }
6092
+ ),
6093
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
6094
+ /* @__PURE__ */ jsx(
6095
+ FormInput,
6096
+ {
6097
+ type: "number",
6098
+ label: t("maxTokensLabel", { _: "Max output tokens" }),
6099
+ value: String(maxTokens),
6100
+ onValueChange: (value) => setMaxTokens(Number(value) || 0),
6101
+ min: 1,
6102
+ max: 8192,
6103
+ step: 32
6104
+ }
6105
+ ),
6106
+ /* @__PURE__ */ jsx(
6107
+ FormInput,
6108
+ {
6109
+ type: "number",
6110
+ label: t("temperatureLabel", { _: "Temperature" }),
6111
+ value: String(temperature),
6112
+ onValueChange: (value) => setTemperature(Number(value) || 0),
6113
+ min: 0,
6114
+ max: 2,
6115
+ step: 0.1
6116
+ }
6117
+ )
6118
+ ] }),
6119
+ /* @__PURE__ */ jsx(
6120
+ ConfigFormActions,
6121
+ {
6122
+ cancelLabel: t("cancel"),
6123
+ saveLabel: t("save"),
6124
+ onCancel,
6125
+ onSave: handleSave
6126
+ }
6127
+ )
6128
+ ] });
6129
+ }
5651
6130
  var NODE_TITLE_KEYS = {
5652
6131
  start: "startNodeConfig",
5653
6132
  end: "endNodeConfig",
@@ -5668,7 +6147,9 @@ var NODE_TITLE_KEYS = {
5668
6147
  entity: "entityNodeConfig",
5669
6148
  datasource: "datasourceNodeConfig",
5670
6149
  group: "groupNodeConfig",
5671
- model_provider: "modelProviderNodeConfig"
6150
+ model_provider: "modelProviderNodeConfig",
6151
+ rule: "ruleNodeConfig",
6152
+ agent: "agentNodeConfig"
5672
6153
  };
5673
6154
  function LogicNodeModal({ onSave, entities = [], datasources = [], onLoadTables, onLoadSchema }) {
5674
6155
  const t = useTranslations("agents.workflow");
@@ -5729,6 +6210,10 @@ function LogicNodeModal({ onSave, entities = [], datasources = [], onLoadTables,
5729
6210
  return /* @__PURE__ */ jsx(GroupNodeConfigForm, { config, onSave: handleSave, onCancel: closeModal });
5730
6211
  case "model_provider":
5731
6212
  return /* @__PURE__ */ jsx(ModelProviderNodeConfigForm, { config, onSave: handleSave, onCancel: closeModal });
6213
+ case "rule":
6214
+ return /* @__PURE__ */ jsx(RuleNodeConfigForm, { config, onSave: handleSave, onCancel: closeModal });
6215
+ case "agent":
6216
+ return /* @__PURE__ */ jsx(AgentNodeConfigForm, { config, onSave: handleSave, onCancel: closeModal });
5732
6217
  default:
5733
6218
  return null;
5734
6219
  }
@@ -7843,5 +8328,5 @@ function Workspace({
7843
8328
  }
7844
8329
 
7845
8330
  export { AgentFlowNode, AgentToolFlowNode, AnswerFlowNode, AnthropicIcon, CATEGORY_COLORS, CATEGORY_PILL_COLORS, CodeFlowNode, CrewAIIcon, DocumentExtractorFlowNode, EndFlowNode, EntityFlowNode, FRAMEWORK_META, GoogleADKIcon, GroupFlowNode, HttpRequestFlowNode, ICON_MAP, IfElseFlowNode, IterationFlowNode, IterationStartFlowNode, KnowledgeBaseFlowNode, LOGIC_ICON_MAP, LOGIC_NODE_BADGE_COLORS, LOGIC_NODE_GRADIENTS, LOGIC_NODE_HANDLE_COLORS, LangChainIcon, ListOperatorFlowNode, LogicNodeModal, MINIMAP_NODE_COLORS, ModelProviderFlowNode, NODE_EXECUTION_ACCENT_COLORS, NodeCard, NodeContextMenu, NoteFlowNode, OpenAIIcon, PanelContextMenu, ParameterExtractorFlowNode, QuestionClassifierFlowNode, RuleFlowNode, SelectionContextMenu, StartFlowNode, StrandsIcon, TemplateTransformFlowNode, ToolFlowNode, VariableAggregatorFlowNode, VariableAssignerFlowNode, WorkflowBuilderProvider, WorkflowCanvas, Workspace, getCompatibleModels, getDefaultFrameworkForModel, getEntityBadgeColor, getEntityGradient, getEntityHandleColor, getEntityIcon, getEntityMinimapColor, getFrameworkMeta, getNodeExecutionAccent, getNodeExecutionAccentRgb, isFrameworkCompatibleWithProviders, isModelCompatibleWithFramework, useModalStore, useWorkflowBuilderClient, useWorkflowBuilderClientOptional, useWorkflowStore };
7846
- //# sourceMappingURL=chunk-VG6AMK5R.mjs.map
7847
- //# sourceMappingURL=chunk-VG6AMK5R.mjs.map
8331
+ //# sourceMappingURL=chunk-BZX5WQOL.mjs.map
8332
+ //# sourceMappingURL=chunk-BZX5WQOL.mjs.map