@anchrd/intel-ui 0.29.0 → 0.31.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anchrd/intel-ui",
3
- "version": "0.29.0",
3
+ "version": "0.31.0",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -33,7 +33,7 @@
33
33
  "typecheck": "tsc --noEmit"
34
34
  },
35
35
  "dependencies": {
36
- "@anchrd/intel-contract": "^0.18.0",
36
+ "@anchrd/intel-contract": "^0.19.0",
37
37
  "@blocknote/core": "^0.52.1",
38
38
  "@blocknote/react": "^0.52.1",
39
39
  "@blocknote/shadcn": "^0.52.1",
@@ -1,6 +1,7 @@
1
1
  import type { Flow, FlowGraph, FlowNode } from "@anchrd/intel-contract/flow";
2
2
  import { flowNodeLayer } from "@anchrd/intel-contract/flow";
3
3
  import type { Node } from "@anchrd/intel-contract/node";
4
+ import { serverOf } from "@anchrd/intel-contract/tool";
4
5
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
5
6
  import { useNavigate, useRouterState } from "@tanstack/react-router";
6
7
  import {
@@ -367,9 +368,25 @@ function FlowsEditor() {
367
368
  const navigate = useNavigate();
368
369
  // The servers a tool step can name (#489). The flat function list the picker used to show is
369
370
  // what that ticket set out to remove; which function runs is decided while the flow runs.
371
+ // ⚠️ The flat function list is NOT what a step is chosen from any more (#489) — it is only what
372
+ // an author narrows a chosen server WITH. It is asked for regardless of whether a tool step is
373
+ // selected, because a query that starts when a panel opens shows a spinner inside the panel.
374
+ const tools = useQuery({
375
+ queryKey: ["tools"],
376
+ queryFn: () => data.listTools(),
377
+ // ⚠️ No retry. This query only ENRICHES the panel — the server is already chosen without it —
378
+ // and a retry is paused while the tab is unfocused, so a deterministic failure would leave the
379
+ // function list empty for as long as the reader looks away, with nothing saying why (#212).
380
+ retry: false,
381
+ });
370
382
  const toolServers = useQuery({
371
383
  queryKey: ["tool-servers"],
372
384
  queryFn: () => data.listToolServers(),
385
+ // ⚠️ No retry here either, and for a sharper reason than the list above: the panel shows
386
+ // NOTHING while this is pending. A deterministic 502 with an unfocused tab pauses the retry,
387
+ // and the author then sits in front of an empty server field with no sentence saying why —
388
+ // which is #212 exactly. `error` is a state this panel can speak; `pending` is not.
389
+ retry: false,
373
390
  });
374
391
  // A tree link points at a node, so the editor needs candidates. The graph is the authorized
375
392
  // flat list of them; walking the folder tree for a picker would be the N+1 all over again.
@@ -420,6 +437,13 @@ function FlowsEditor() {
420
437
  // guarantee that the loaded document is both the selected flow and settled.
421
438
  const documentReady = selectedFlowId !== null && document.data?.flow.id === selectedFlowId;
422
439
  const canMutate = documentReady && !document.isFetching;
440
+ // ⚠️ A tool step without a server cannot be saved at all — `ToolServerHandle` has a minimum
441
+ // length. Letting the request go out anyway answers with a generic failure that names no step, so
442
+ // the author learns "something is wrong" instead of "this one needs a server". The refusal
443
+ // belongs here, before the write (#517).
444
+ const stepsMissingServer = nodes.filter(
445
+ (node) => node.data.node.kind === "tool" && node.data.node.configuration.server === "",
446
+ );
423
447
  useEffect(() => {
424
448
  if (!document.data || document.data.flow.id !== selectedFlowId) return;
425
449
  const next = canvas(document.data.version?.graph ?? defaultGraph());
@@ -468,6 +492,7 @@ function FlowsEditor() {
468
492
  dirty={dirty}
469
493
  saving={save.isPending}
470
494
  canMutate={canMutate}
495
+ incomplete={stepsMissingServer.map((node) => ({ label: node.data.node.label }))}
471
496
  onSave={() => save.mutate()}
472
497
  onPublish={() => setPublishing(true)}
473
498
  />
@@ -647,6 +672,18 @@ function FlowsEditor() {
647
672
  node={selectedNode}
648
673
  update={updateNode}
649
674
  toolServers={toolServers.data?.items ?? []}
675
+ toolServersState={
676
+ toolServers.isPending
677
+ ? "pending"
678
+ : toolServers.isError
679
+ ? "error"
680
+ : toolServers.data?.portalConnected === false
681
+ ? "disconnected"
682
+ : "ready"
683
+ }
684
+ toolNames={(tools.data?.items ?? []).map((entry) => entry.name)}
685
+ toolNamesFailed={tools.isError}
686
+ toolNamesPending={tools.isPending}
650
687
  flows={(callable.data?.items ?? []).filter(
651
688
  (entry) => entry.id !== selectedFlowId,
652
689
  )}
@@ -707,6 +744,7 @@ function FlowTitle({
707
744
  dirty,
708
745
  saving,
709
746
  canMutate,
747
+ incomplete,
710
748
  onSave,
711
749
  onPublish,
712
750
  }: {
@@ -714,6 +752,9 @@ function FlowTitle({
714
752
  dirty: boolean;
715
753
  saving: boolean;
716
754
  canMutate: boolean;
755
+ // Tool steps that name no server yet. They cannot be saved, so the refusal is shown here rather
756
+ // than fetched from the server as a message that names no step.
757
+ incomplete: Array<{ label: string }>;
717
758
  onSave(): void;
718
759
  onPublish(): void;
719
760
  }) {
@@ -740,7 +781,16 @@ function FlowTitle({
740
781
  applies EVERYWHERE stands. But it says how THIS flow is shown, and so belongs in the line
741
782
  that names this flow. A flow is the only level with runs, and therefore the only one with
742
783
  three views (#35). */}
743
- <SaveButton dirty={dirty && canMutate} saving={saving} onSave={onSave} />
784
+ {incomplete.length > 0 && (
785
+ <p className="mr-2 text-xs text-destructive">
786
+ {i18n.t("flows.toolStepNeedsServer", { label: incomplete[0]?.label ?? "" })}
787
+ </p>
788
+ )}
789
+ <SaveButton
790
+ dirty={dirty && canMutate && incomplete.length === 0}
791
+ saving={saving}
792
+ onSave={onSave}
793
+ />
744
794
  <TooltipProvider delayDuration={300}>
745
795
  <Tooltip>
746
796
  <TooltipTrigger asChild>
@@ -804,10 +854,34 @@ function PublishPreview({
804
854
  ) : (
805
855
  <>
806
856
  <p className="text-sm text-muted-foreground">
807
- {preview.data.calls.length === 0
857
+ {preview.data.calls.length === 0 && preview.data.tools.length === 0
808
858
  ? i18n.t("flows.publishPreviewEmpty")
809
859
  : i18n.t("flows.publishPreviewIntro")}
810
860
  </p>
861
+ {/* ⚠️ The tool surface belongs in the preview because publishing FREEZES it: after this
862
+ confirmation the step is pinned to these functions and inherits nothing the provider
863
+ adds later. An author who is not shown it is asked to freeze something unseen (#517). */}
864
+ {preview.data.tools.length > 0 && (
865
+ <ul className="mt-4 space-y-2">
866
+ {preview.data.tools.map((tool) => (
867
+ <li key={tool.nodeId} className="rounded-md border p-3 text-sm">
868
+ <span className="block font-medium">{tool.nodeLabel}</span>
869
+ <span className="block text-xs text-muted-foreground">{tool.server}</span>
870
+ <span
871
+ className={`mt-1 block text-xs ${tool.available ? "text-muted-foreground" : "text-destructive"}`}
872
+ >
873
+ {!tool.available
874
+ ? i18n.t("flows.publishPreviewToolUnavailable")
875
+ : tool.allow === null
876
+ ? i18n.t("flows.toolAllowAll")
877
+ : i18n.t("flows.publishPreviewToolAllow", {
878
+ functions: tool.allow.join(", "),
879
+ })}
880
+ </span>
881
+ </li>
882
+ ))}
883
+ </ul>
884
+ )}
811
885
  <ul className="mt-4 space-y-2">
812
886
  {preview.data.calls.map((call) => {
813
887
  // ⚠️ One condition for the sentence and its colour. They were two, so a call that
@@ -860,11 +934,29 @@ function NodeInspector({
860
934
  node,
861
935
  update,
862
936
  toolServers,
937
+ toolServersState,
938
+ toolNames,
939
+ toolNamesFailed,
940
+ toolNamesPending,
863
941
  flows,
864
942
  }: {
865
943
  node: CanvasNode | null;
866
944
  update(fn: (node: FlowNode) => FlowNode): void;
867
945
  toolServers: Array<{ handle: string; name: string; toolCount: number }>;
946
+ // ⚠️ FOUR answers, not two. A list that is still loading is not one that came back empty; a
947
+ // failure is neither; and a portal this user never connected is not a portal that reaches
948
+ // nothing. Drawing them all as "no servers" is the mistake #350 records for the tools screen —
949
+ // and the fourth one is the worst of them: it answers a SIGN-IN problem with a sentence about
950
+ // reach, in a place that offers no way to sign in.
951
+ toolServersState: "pending" | "error" | "disconnected" | "ready";
952
+ toolNames: string[];
953
+ // Told apart from "this server offers nothing": a function list that could not be loaded must not
954
+ // read as a narrowing that lost its functions.
955
+ toolNamesFailed: boolean;
956
+ // ⚠️ A query that has not answered is not one that answered "nothing". Without this a step opened
957
+ // while the catalog is still loading shows its allowed functions as missing — the panel accusing
958
+ // the author of a narrowing that broke, moments before the list arrives.
959
+ toolNamesPending: boolean;
868
960
  flows: Flow[];
869
961
  }) {
870
962
  const i18n = useI18n();
@@ -953,40 +1045,198 @@ function NodeInspector({
953
1045
  </div>
954
1046
  )}
955
1047
  {contract.kind === "tool" && (
956
- <div className="mt-4">
957
- <label className="block text-sm font-medium">
958
- {i18n.t("flows.tool")}
959
- {/* ⚠️ The step names a SERVER since #489. This picker is the smallest thing that keeps
960
- the field usable on the new model; the real one — server names from the portal plus
961
- an optional narrowing to single functions — is #489's second package and is
962
- deliberately not smuggled in here. */}
963
- <select
964
- value={contract.configuration.server}
965
- onChange={(event) => {
966
- const server = event.target.value;
967
- update((value) =>
968
- value.kind === "tool"
969
- ? {
970
- ...value,
971
- // The fingerprint belongs to a surface that publishing freezes, and it is
972
- // the server's to compute — a value guessed in the browser would be a
973
- // second answer to a question the API already owns.
974
- configuration: { ...value.configuration, server, fingerprint: null },
975
- }
976
- : value,
977
- );
978
- }}
979
- className="mt-2 w-full rounded-md border bg-background px-3 py-2 outline-none focus-visible:ring-2 focus-visible:ring-ring"
980
- >
981
- <option value="">{i18n.t("flows.selectTool")}</option>
982
- {toolServers.map((entry) => (
983
- <option key={entry.handle} value={entry.handle}>
984
- {entry.name}
985
- </option>
986
- ))}
987
- </select>
1048
+ <ToolStepFields
1049
+ configuration={contract.configuration}
1050
+ servers={toolServers}
1051
+ state={toolServersState}
1052
+ toolNames={toolNames}
1053
+ toolNamesFailed={toolNamesFailed}
1054
+ toolNamesPending={toolNamesPending}
1055
+ update={update}
1056
+ />
1057
+ )}
1058
+ </div>
1059
+ );
1060
+ }
1061
+
1062
+ /**
1063
+ * What a tool step may reach: one server, and optionally only some of its functions.
1064
+ *
1065
+ * ⚠️ The server comes first and the functions are a NARROWING, not the other way round (#489). The
1066
+ * old field offered every function of every server in one flat list — 85 entries here, portal
1067
+ * management tools among them — and asked the author to pick the one call they wanted before they
1068
+ * knew what the step would say. Which function runs is a run-time answer; which server may be
1069
+ * reached is an authoring decision, and that is the one this asks for.
1070
+ */
1071
+ export function ToolStepFields({
1072
+ configuration,
1073
+ servers,
1074
+ state,
1075
+ toolNames,
1076
+ toolNamesFailed,
1077
+ toolNamesPending,
1078
+ update,
1079
+ }: {
1080
+ configuration: { server: string; allow: string[] | null; fingerprint: string | null };
1081
+ servers: Array<{ handle: string; name: string; toolCount: number }>;
1082
+ state: "pending" | "error" | "disconnected" | "ready";
1083
+ toolNames: string[];
1084
+ toolNamesFailed: boolean;
1085
+ toolNamesPending: boolean;
1086
+ update(fn: (node: FlowNode) => FlowNode): void;
1087
+ }) {
1088
+ const i18n = useI18n();
1089
+ // ⚠️ The group's identity, not its data. Two steps on one screen with the same server would
1090
+ // otherwise share one radio group and unset each other — and the component is exported now, so
1091
+ // that invariant no longer lives in the file that guarantees it.
1092
+ const groupId = useId();
1093
+ const hintId = `${groupId}-hint`;
1094
+ // The one sentence this field has to say, or none. Derived once so the control and the paragraph
1095
+ // cannot disagree about whether there is something to point at.
1096
+ const hint =
1097
+ state === "error"
1098
+ ? ({ key: "flows.toolServersFailed", tone: "bad" } as const)
1099
+ : state === "disconnected"
1100
+ ? ({ key: "flows.toolServersDisconnected", tone: "bad" } as const)
1101
+ : state === "ready" && servers.length === 0
1102
+ ? ({ key: "flows.toolServersEmpty", tone: "plain" } as const)
1103
+ : null;
1104
+ const ownFunctions = toolNames.filter(
1105
+ (name) => configuration.server !== "" && serverOf(name, [configuration.server]) !== null,
1106
+ );
1107
+ // ⚠️ An allowed function the catalog does not carry is DRAWN, not dropped. Rendering only what
1108
+ // the catalog knows would let the panel and the saved graph disagree in silence: the author reads
1109
+ // "narrowed to nothing" while the step still names something. The API treats the same condition
1110
+ // as serious enough to refuse a publish, so the editor may not swallow it.
1111
+ // Nothing is "missing" until the catalog has actually answered.
1112
+ const missing = toolNamesPending
1113
+ ? []
1114
+ : (configuration.allow ?? []).filter((name) => !ownFunctions.includes(name));
1115
+ const functions = [...ownFunctions, ...missing];
1116
+
1117
+ function setTool(
1118
+ change: (current: { server: string; allow: string[] | null; fingerprint: string | null }) => {
1119
+ server: string;
1120
+ allow: string[] | null;
1121
+ fingerprint: string | null;
1122
+ },
1123
+ ) {
1124
+ update((value) =>
1125
+ value.kind === "tool" ? { ...value, configuration: change(value.configuration) } : value,
1126
+ );
1127
+ }
1128
+
1129
+ return (
1130
+ <div className="mt-4 space-y-3">
1131
+ <label className="block text-sm font-medium">
1132
+ {i18n.t("flows.tool")}
1133
+ <select
1134
+ value={configuration.server}
1135
+ // ⚠️ A disabled control drops out of the tab order, so the reason has to be ANNOUNCED
1136
+ // rather than merely printed beside it. It points at the hint only when the hint is
1137
+ // actually rendered — `pending` prints nothing on purpose, and a reference to an id that
1138
+ // does not exist is worse than none: a screen reader reads it as a broken relation.
1139
+ aria-describedby={hint === null ? undefined : hintId}
1140
+ disabled={state !== "ready" || servers.length === 0}
1141
+ onChange={(event) => {
1142
+ // ⚠️ Read out of the event NOW, not inside the updater. The updater runs later, and by
1143
+ // then this controlled select has been set back to the state's value — the change would
1144
+ // apply to itself and nothing would move.
1145
+ const server = event.target.value;
1146
+ // Changing the server drops the narrowing AND the frozen surface. Keeping `allow` would
1147
+ // leave functions of the old server behind — the contract refuses that outright — and
1148
+ // keeping the fingerprint would claim a surface nobody has confirmed.
1149
+ setTool(() => ({ server, allow: null, fingerprint: null }));
1150
+ }}
1151
+ className="mt-2 w-full rounded-md border bg-background px-3 py-2 outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50"
1152
+ >
1153
+ <option value="">{i18n.t("flows.selectTool")}</option>
1154
+ {servers.map((entry) => (
1155
+ <option key={entry.handle} value={entry.handle}>
1156
+ {entry.name}
1157
+ </option>
1158
+ ))}
1159
+ </select>
1160
+ </label>
1161
+ {/* The three states of the list, told apart. Silence while loading; a reason when it failed;
1162
+ and "you reach none" only once that is actually known. */}
1163
+ {hint !== null && (
1164
+ <p
1165
+ id={hintId}
1166
+ className={`text-xs ${hint.tone === "bad" ? "text-destructive" : "text-muted-foreground"}`}
1167
+ >
1168
+ {i18n.t(hint.key)}
1169
+ </p>
1170
+ )}
1171
+ {configuration.server !== "" && (
1172
+ <fieldset className="space-y-2">
1173
+ <legend className="text-sm font-medium">{i18n.t("flows.toolFunctions")}</legend>
1174
+ <label className="flex items-center gap-2 text-sm">
1175
+ <input
1176
+ type="radio"
1177
+ name={groupId}
1178
+ checked={configuration.allow === null}
1179
+ onChange={() =>
1180
+ setTool((current) => ({ ...current, allow: null, fingerprint: null }))
1181
+ }
1182
+ className="size-4"
1183
+ />
1184
+ {i18n.t("flows.toolAllowAll")}
988
1185
  </label>
989
- </div>
1186
+ <label className="flex items-center gap-2 text-sm">
1187
+ <input
1188
+ type="radio"
1189
+ name={groupId}
1190
+ checked={configuration.allow !== null}
1191
+ onChange={() => setTool((current) => ({ ...current, allow: [], fingerprint: null }))}
1192
+ className="size-4"
1193
+ />
1194
+ {i18n.t("flows.toolAllowSome")}
1195
+ </label>
1196
+ {configuration.allow !== null && (
1197
+ <ul className="max-h-48 space-y-1 overflow-y-auto pl-6">
1198
+ {functions.map((name) => (
1199
+ <li key={name}>
1200
+ <label className="flex items-center gap-2 text-sm">
1201
+ <input
1202
+ type="checkbox"
1203
+ checked={configuration.allow?.includes(name) ?? false}
1204
+ onChange={(event) => {
1205
+ // Same reason as the select above: the checked flag is read now, the
1206
+ // updater runs later.
1207
+ const add = event.target.checked;
1208
+ setTool((current) => ({
1209
+ ...current,
1210
+ allow: add
1211
+ ? [...(current.allow ?? []), name]
1212
+ : (current.allow ?? []).filter((entry) => entry !== name),
1213
+ fingerprint: null,
1214
+ }));
1215
+ }}
1216
+ className="size-4"
1217
+ />
1218
+ <span className="min-w-0 truncate">{name}</span>
1219
+ </label>
1220
+ </li>
1221
+ ))}
1222
+ </ul>
1223
+ )}
1224
+ {/* Two sentences that only appear when they are true, and neither is the generic hint:
1225
+ a narrowing to nothing cannot be published, and a name the catalog does not carry is
1226
+ something the author has to see rather than lose. */}
1227
+ {configuration.allow?.length === 0 && (
1228
+ <p className="text-xs text-destructive">{i18n.t("flows.toolAllowNone")}</p>
1229
+ )}
1230
+ {toolNamesFailed && (
1231
+ <p className="text-xs text-destructive">{i18n.t("flows.toolFunctionsFailed")}</p>
1232
+ )}
1233
+ {!toolNamesFailed && missing.length > 0 && (
1234
+ <p className="text-xs text-destructive">
1235
+ {i18n.t("flows.toolAllowUnknown", { count: missing.length })}
1236
+ </p>
1237
+ )}
1238
+ <p className="text-xs text-muted-foreground">{i18n.t("flows.toolAllowHint")}</p>
1239
+ </fieldset>
990
1240
  )}
991
1241
  </div>
992
1242
  );
package/src/i18n/de.json CHANGED
@@ -97,7 +97,7 @@
97
97
  "archive.restoreFailed": "Es wurde nicht wiederhergestellt. Vielleicht hat jemand anderes es geändert — lade neu und versuche es erneut.",
98
98
  "archive.purge": "{title} endgültig löschen",
99
99
  "archive.purge.title": "Endgültig löschen?",
100
- "archive.purge.body": "„{title}\u201c und alles, was dazugehört — jede Version, der Inhalt und die Einträge im Suchindex — ist danach weg. Das lässt sich nicht rückgängig machen.",
100
+ "archive.purge.body": "„{title} und alles, was dazugehört — jede Version, der Inhalt und die Einträge im Suchindex — ist danach weg. Das lässt sich nicht rückgängig machen.",
101
101
  "archive.purge.links.none": "Keine anderen Dokumente verweisen darauf.",
102
102
  "archive.purge.links.one": "Ein anderes Dokument verweist darauf; dieser Verweis wird brechen.",
103
103
  "archive.purge.links.many": "{count} andere Dokumente verweisen darauf; diese Verweise werden brechen.",
@@ -275,8 +275,8 @@
275
275
  "flows.publishPreviewUnavailable": "Der aufgerufene Flow hat noch nichts veröffentlicht, das lässt sich nicht einfrieren.",
276
276
  "flows.publishConfirm": "Veröffentlichen",
277
277
  "flows.publishFailed": "Das Veröffentlichen ist fehlgeschlagen. Lade die neueste Fassung und versuche es erneut.",
278
- "flows.tool": "MCP-Werkzeug",
279
- "flows.selectTool": "Wähle ein gefundenes Werkzeug",
278
+ "flows.tool": "MCP-Server",
279
+ "flows.selectTool": "Server auswählen",
280
280
  "flows.operationFailed": "Der Flow-Vorgang ist fehlgeschlagen. Lade die neueste Fassung und versuche es erneut.",
281
281
  "flows.needs": "Was dieser Flow braucht",
282
282
  "flows.needsNodes": "Dokumente",
@@ -351,5 +351,18 @@
351
351
  "common.noAccess": "Das ist für dich nicht verfügbar. Es existiert nicht, oder es ist nicht mehr für dich freigegeben.",
352
352
  "common.noPermission": "Dir fehlt die Berechtigung, das zu sehen.",
353
353
  "title.descriptionMore": "Mehr",
354
- "title.descriptionLess": "Weniger"
354
+ "title.descriptionLess": "Weniger",
355
+ "flows.toolServersEmpty": "Über das Portal erreichst du noch keinen MCP-Server.",
356
+ "flows.toolServersFailed": "Die Serverliste ließ sich nicht laden, daher lässt sich gerade keiner auswählen.",
357
+ "flows.toolAllowAll": "Der Agent darf jede Funktion nutzen",
358
+ "flows.toolAllowSome": "Den Agenten auf bestimmte Funktionen hinweisen",
359
+ "flows.toolAllowHint": "Intel sagt, wofür ein Schritt da ist; der Agent wählt den Aufruf und erreicht das Portal mit seinen eigenen Rechten.",
360
+ "flows.toolFunctions": "Funktionen",
361
+ "flows.toolServersDisconnected": "Du bist nicht mit dem Portal verbunden, daher lässt sich kein Server wählen. Verbinde es zuerst in den Werkzeug-Einstellungen.",
362
+ "flows.toolAllowNone": "Es ist noch keine Funktion ausgewählt, daher lässt sich dieser Schritt nicht veröffentlichen.",
363
+ "flows.toolAllowUnknown": "{count} ausgewählte Funktion(en) bietet dieser Server gerade nicht an.",
364
+ "flows.toolStepNeedsServer": "Dem Schritt „{label}“ fehlt noch ein Server.",
365
+ "flows.toolFunctionsFailed": "Die Funktionsliste ließ sich nicht laden, daher ist die Auswahl möglicherweise unvollständig.",
366
+ "flows.publishPreviewToolUnavailable": "Diesen Server erreichst du nicht, daher wird das Veröffentlichen abgelehnt.",
367
+ "flows.publishPreviewToolAllow": "Eingefroren auf: {functions}"
355
368
  }
package/src/i18n/en.json CHANGED
@@ -275,8 +275,8 @@
275
275
  "flows.publishPreviewUnavailable": "The called flow has published nothing yet, so this cannot be frozen.",
276
276
  "flows.publishConfirm": "Publish",
277
277
  "flows.publishFailed": "Publishing failed. Reload the latest version and try again.",
278
- "flows.tool": "MCP tool",
279
- "flows.selectTool": "Select a discovered tool",
278
+ "flows.tool": "MCP server",
279
+ "flows.selectTool": "Select a server",
280
280
  "flows.operationFailed": "The flow operation failed. Reload the latest version and try again.",
281
281
  "flows.needs": "What this flow needs",
282
282
  "flows.needsNodes": "Documents",
@@ -351,5 +351,18 @@
351
351
  "common.noAccess": "This is not available to you. It may not exist, or it may no longer be shared with you.",
352
352
  "common.noPermission": "You do not have permission to see this.",
353
353
  "title.descriptionMore": "More",
354
- "title.descriptionLess": "Less"
354
+ "title.descriptionLess": "Less",
355
+ "flows.toolServersEmpty": "You reach no MCP server through the portal yet.",
356
+ "flows.toolServersFailed": "The list of servers could not be loaded, so none can be chosen right now.",
357
+ "flows.toolAllowAll": "Let the agent use any function",
358
+ "flows.toolAllowSome": "Point the agent at specific functions",
359
+ "flows.toolAllowHint": "Intel says what a step is for; the agent chooses the call and reaches the portal with its own permissions.",
360
+ "flows.toolFunctions": "Functions",
361
+ "flows.toolServersDisconnected": "You are not connected to the portal, so no server can be chosen. Connect it in the tool settings first.",
362
+ "flows.toolAllowNone": "No function is selected yet, so this step cannot be published.",
363
+ "flows.toolAllowUnknown": "{count} selected function(s) are not offered by this server right now.",
364
+ "flows.toolStepNeedsServer": "The step “{label}” still needs a server.",
365
+ "flows.toolFunctionsFailed": "The list of functions could not be loaded, so the selection may be incomplete.",
366
+ "flows.publishPreviewToolUnavailable": "You do not reach this server, so publishing will be refused.",
367
+ "flows.publishPreviewToolAllow": "Frozen to: {functions}"
355
368
  }
package/src/i18n/es.json CHANGED
@@ -275,8 +275,8 @@
275
275
  "flows.publishPreviewUnavailable": "El flujo llamado todavía no ha publicado nada, así que esto no se puede congelar.",
276
276
  "flows.publishConfirm": "Publicar",
277
277
  "flows.publishFailed": "La publicación ha fallado. Carga la versión más reciente e inténtalo de nuevo.",
278
- "flows.tool": "Herramienta MCP",
279
- "flows.selectTool": "Elige una herramienta descubierta",
278
+ "flows.tool": "Servidor MCP",
279
+ "flows.selectTool": "Elegir un servidor",
280
280
  "flows.operationFailed": "La operación del flujo ha fallado. Carga la versión más reciente e inténtalo de nuevo.",
281
281
  "flows.needs": "Qué necesita este flujo",
282
282
  "flows.needsNodes": "Documentos",
@@ -351,5 +351,18 @@
351
351
  "common.noAccess": "Esto no está disponible para ti. Puede que no exista o que ya no esté compartido contigo.",
352
352
  "common.noPermission": "No tienes permiso para ver esto.",
353
353
  "title.descriptionMore": "Más",
354
- "title.descriptionLess": "Menos"
354
+ "title.descriptionLess": "Menos",
355
+ "flows.toolServersEmpty": "Todavía no alcanzas ningún servidor MCP a través del portal.",
356
+ "flows.toolServersFailed": "No se pudo cargar la lista de servidores, así que ahora no se puede elegir ninguno.",
357
+ "flows.toolAllowAll": "El agente puede usar cualquier función",
358
+ "flows.toolAllowSome": "Indicar al agente funciones concretas",
359
+ "flows.toolAllowHint": "Intel dice para qué sirve un paso; el agente elige la llamada y accede al portal con sus propios permisos.",
360
+ "flows.toolFunctions": "Funciones",
361
+ "flows.toolServersDisconnected": "No estás conectado al portal, así que no se puede elegir ningún servidor. Conéctalo primero en la configuración de herramientas.",
362
+ "flows.toolAllowNone": "Todavía no hay ninguna función seleccionada, así que este paso no se puede publicar.",
363
+ "flows.toolAllowUnknown": "Este servidor no ofrece ahora mismo {count} función(es) seleccionada(s).",
364
+ "flows.toolStepNeedsServer": "Al paso «{label}» todavía le falta un servidor.",
365
+ "flows.toolFunctionsFailed": "No se pudo cargar la lista de funciones, así que la selección puede estar incompleta.",
366
+ "flows.publishPreviewToolUnavailable": "No alcanzas este servidor, así que la publicación será rechazada.",
367
+ "flows.publishPreviewToolAllow": "Congelado en: {functions}"
355
368
  }