@burdenoff/fluidgrids-fe-sdk 2026.1111.19 → 2026.1111.21

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 (51) hide show
  1. package/dist/{AiStudioPage-htYcXz43.js → AiStudioPage-CJdzRL26.js} +4 -4
  2. package/dist/ConnectionFormDialog-Bg9_OdHX.js +119 -0
  3. package/dist/{DataSinkDashboardPage-1h2QsMzH.js → DataSinkDashboardPage-hrwQSvks.js} +1 -1
  4. package/dist/{ExecutionDetailPage-BdcZmLT4.js → ExecutionDetailPage-DKp421Fj.js} +61 -14
  5. package/dist/{ExecutionsListPage-BCUJoAIE.js → ExecutionsListPage-DwJQfDPt.js} +4 -4
  6. package/dist/{FluidGridsProvider-dvZSNtSW.js → FluidGridsProvider-CbY91ZQq.js} +416 -99
  7. package/dist/{HistoryPage-U0tqupBN.js → HistoryPage-BLXZJDoJ.js} +1 -1
  8. package/dist/{InsightsPage-CALo16eB.js → InsightsPage-ChnoW5U4.js} +1 -1
  9. package/dist/{MissingNodesInstallModal-Dly7T2_7.js → MissingNodesInstallModal-mYFfy9Gg.js} +1 -1
  10. package/dist/{QuotaExhaustedDialog-7IFSaLyi.js → QuotaExhaustedDialog-DWm_YWL7.js} +1 -1
  11. package/dist/{SettingsPage-CJLZvv4J.js → SettingsPage-CXoKX0px.js} +2 -2
  12. package/dist/{TemplateCard-BdB087SP.js → TemplateCard-HIMsb47R.js} +2 -2
  13. package/dist/{TemplateConfigurationModal-CPVcTDSi.js → TemplateConfigurationModal-BNtbJ2fv.js} +1 -1
  14. package/dist/{TemplateDetailPanel-DruadMkg.js → TemplateDetailPanel-C6Zh2WRF.js} +1 -1
  15. package/dist/{TemplatePreview-CNrYJXw2.js → TemplatePreview-_wUMSwh_.js} +3 -3
  16. package/dist/{TemplatesPage-BU4tSqOo.js → TemplatesPage-DBfL-ao9.js} +4 -4
  17. package/dist/{WorkflowBuilder-DMonxuoY.js → WorkflowBuilder-CMZi6wQE.js} +7 -7
  18. package/dist/{WorkflowBuilder-BSPGcOfA.js → WorkflowBuilder-CYmpQtIQ.js} +3 -3
  19. package/dist/{WorkflowSearchRedirect-DwDNZ_q5.js → WorkflowSearchRedirect-iWzvY9Qo.js} +1 -1
  20. package/dist/{WorkflowsList-DiiGyZkT.js → WorkflowsList-C8aoqtRE.js} +5 -5
  21. package/dist/{agentTools-C7pdNJqE.js → agentTools-CrEWQ5st.js} +3 -4
  22. package/dist/askAboutBridge-BV8m95kk.js +65 -0
  23. package/dist/assistant.d.ts +30 -1
  24. package/dist/assistant.js +2 -1
  25. package/dist/{canvasNoteTools-CqiPTBa1.js → canvasNoteTools-6BQYNCee.js} +2 -2
  26. package/dist/{code-editor-field-9-GdVE_8.js → code-editor-field-CoRq10k7.js} +1 -1
  27. package/dist/executions.js +2 -2
  28. package/dist/{flow-builder-CV5Zu_Hq.js → flow-builder-Dwev-C_g.js} +191 -173
  29. package/dist/history.js +1 -1
  30. package/dist/index.js +25 -24
  31. package/dist/{node-detail-view-ttMp6H6K.js → node-detail-view-BFBSKj5w.js} +5 -5
  32. package/dist/{node-editor-tab-body-Bqb--pZy.js → node-editor-tab-body-Cz9kOlxu.js} +2 -2
  33. package/dist/observability.js +2 -2
  34. package/dist/{popover-BbVOHsfO.js → popover-xPh0iIzy.js} +2 -2
  35. package/dist/preloadable-lazy-Pp47mqQ9.js +65 -0
  36. package/dist/{quick-config-panel-Da4uOVSs.js → quick-config-panel-Ym05lyGn.js} +3 -3
  37. package/dist/{schedule-panel-DnFN933b.js → schedule-panel-54D5W5Qk.js} +4 -4
  38. package/dist/settings.js +1 -1
  39. package/dist/{sidebar-node-editor-lcnIZxdR.js → sidebar-node-editor-LL3ofDN7.js} +3 -3
  40. package/dist/templates.js +2 -2
  41. package/dist/{useDurableParity-CzUQMFJ3.js → useDurableParity-BScby67i.js} +1 -1
  42. package/dist/{useExecutions-9aLuOt1c.js → useExecutions-B0VzQcY2.js} +1 -1
  43. package/dist/{useObservability-CfEw3ZFD.js → useObservability-FfQGgW6W.js} +1 -1
  44. package/dist/{useRunHistory-B1xJDwl1.js → useRunHistory-BZJg72kd.js} +1 -1
  45. package/dist/{useWorkflowCodeExport-Bu2QYJzA.js → useWorkflowCodeExport-CUWMlJbi.js} +1 -1
  46. package/dist/{useWorkflows-BlVVu8NM.js → useWorkflows-Czz5DlL0.js} +1 -1
  47. package/dist/workflow-builder.js +4 -4
  48. package/dist/workflow-pages.d.ts +19 -19
  49. package/dist/workflow-pages.js +16 -24
  50. package/dist/workflows-list.js +1 -1
  51. package/package.json +1 -1
@@ -7,6 +7,7 @@ import { n as FluidGridsTranslationProvider, r as useTr, t as ENGLISH_FALLBACK_T
7
7
  import { s as useDurableFunctionNodesEnabled } from "./feature-flags-DM5myWaD.js";
8
8
  import { A as CreateWorkflowDefinitionDocument, At as NodePaletteEntriesDocument, Er as WorkflowDefinitionDocument, L as CreateWorkflowVersionDocument, Tn as SaveWorkflowDraftDocument, _n as RunFromNodeDocument, at as ExecuteStepDocument, it as EventRegistrationsDocument, kt as NodeManifestsDocument, ot as ExecutionDetailDocument, qn as TriggerWorkflowDocument, qr as WorkflowRunDocument } from "./graphql-operation-types-kX-MMjgW.js";
9
9
  import { _ as errorCodesOf, g as collectExtensions, i as emitRunStarted, m as RUNS_QUOTA_NAME, n as emitRunFailed, p as QUOTA_EXHAUSTED_CODE, r as emitRunLimitHit, s as emitWorkflowCreated, v as findQuotaRefusal } from "./fluidgridsEvents-CNdcrUNN.js";
10
+ import { n as useFgAskable } from "./askAboutBridge-BV8m95kk.js";
10
11
  import { t as fetchAllNodeEndpointConfigs } from "./node-endpoint-configs-B5bjNMDW.js";
11
12
  import { createContext, memo, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
12
13
  import { useNavigate, useParams } from "react-router";
@@ -15,7 +16,6 @@ import { create } from "zustand";
15
16
  import { Handle, NodeResizer, Position, getConnectedEdges, useReactFlow, useStore } from "@xyflow/react";
16
17
  import { Icon } from "@iconify/react";
17
18
  import { nanoid } from "nanoid";
18
- import ReactMarkdown from "react-markdown";
19
19
  import { cn } from "@burdenoff/fe-libs/utils";
20
20
  import { nanoid as nanoid$1 } from "nanoid/non-secure";
21
21
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -66,6 +66,59 @@ function useBuilderReadOnly() {
66
66
  return useContext(BuilderReadOnlyContext);
67
67
  }
68
68
 
69
+ // src/components/shared/markdownRenderer.ts
70
+ /**
71
+ * The markdown renderer, loaded on first use (BOFF-11944). See LazyMarkdown.
72
+ */
73
+ let renderer = null;
74
+ let pending = null;
75
+ /**
76
+ * Loads the markdown renderer once for every caller. A failed load is
77
+ * forgotten, so the next caller asks again.
78
+ */
79
+ function loadMarkdown() {
80
+ pending ??= import("react-markdown").then((m) => {
81
+ renderer = m.default;
82
+ return m.default;
83
+ }, (error) => {
84
+ pending = null;
85
+ throw error;
86
+ });
87
+ return pending;
88
+ }
89
+ /** The markdown renderer once loaded (it starts the load), else null. */
90
+ function useMarkdownRenderer() {
91
+ const [Markdown, setMarkdown] = useState(() => renderer);
92
+ useEffect(() => {
93
+ if (Markdown) return void 0;
94
+ let live = true;
95
+ loadMarkdown().then((loaded) => {
96
+ if (live) setMarkdown(() => loaded);
97
+ }, (error) => {
98
+ console.error("[FluidGrids] Markdown renderer failed to load:", error);
99
+ });
100
+ return () => {
101
+ live = false;
102
+ };
103
+ }, [Markdown]);
104
+ return Markdown;
105
+ }
106
+
107
+ // src/components/shared/LazyMarkdown.tsx
108
+ /** `children` as markdown once the renderer is in; plain text until then. */
109
+ function LazyMarkdown({ children, components }) {
110
+ const Markdown = useMarkdownRenderer();
111
+ if (!Markdown) return /* @__PURE__ */ jsx("p", {
112
+ className: "whitespace-pre-wrap",
113
+ "data-markdown-pending": "",
114
+ children
115
+ });
116
+ return /* @__PURE__ */ jsx(Markdown, {
117
+ components,
118
+ children
119
+ });
120
+ }
121
+
69
122
  // src/builder/components/flow-builder/components/blocks/nodes/sticky-note/sticky-note-node.tsx
70
123
  const STICKY_DEFAULT_SIZE = {
71
124
  width: 260,
@@ -265,7 +318,7 @@ function StickyNoteNodeComponent({ id, selected, data }) {
265
318
  className: "nodrag nowheel h-full w-full resize-none bg-transparent p-3 font-mono text-xs leading-relaxed text-foreground outline-none placeholder:text-muted-foreground/60"
266
319
  }) : content.trim() ? /* @__PURE__ */ jsx("div", {
267
320
  className: cn("nowheel h-full w-full overflow-auto p-3 text-xs leading-relaxed text-foreground", "[&_h1]:mb-1.5 [&_h1]:text-sm [&_h1]:font-bold", "[&_h2]:mb-1 [&_h2]:text-[13px] [&_h2]:font-bold", "[&_h3]:mb-1 [&_h3]:text-xs [&_h3]:font-semibold", "[&_p]:mb-1.5 [&_ul]:mb-1.5 [&_ul]:list-disc [&_ul]:pl-4", "[&_ol]:mb-1.5 [&_ol]:list-decimal [&_ol]:pl-4 [&_li]:mb-0.5", "[&_a]:underline [&_code]:rounded [&_code]:bg-muted [&_code]:px-1"),
268
- children: /* @__PURE__ */ jsx(ReactMarkdown, { children: content })
321
+ children: /* @__PURE__ */ jsx(LazyMarkdown, { children: content })
269
322
  }) : /* @__PURE__ */ jsx("div", {
270
323
  className: "flex h-full w-full items-center justify-center p-3 text-xs text-muted-foreground/70",
271
324
  children: tr("fluidgrids.builder.stickyNote.emptyStateHint", "Double-click to edit")
@@ -1396,11 +1449,11 @@ function resolveNodeOperation(data, fallbackOperations) {
1396
1449
  function isRecord$3(value) {
1397
1450
  return typeof value === "object" && value !== null && !Array.isArray(value);
1398
1451
  }
1399
- function record(value) {
1452
+ function record$1(value) {
1400
1453
  return isRecord$3(value) ? value : void 0;
1401
1454
  }
1402
1455
  function declaredType(schema) {
1403
- const type = typeof schema === "string" ? schema : record(schema)?.type;
1456
+ const type = typeof schema === "string" ? schema : record$1(schema)?.type;
1404
1457
  return typeof type === "string" ? type.toLowerCase().trim() : "";
1405
1458
  }
1406
1459
  function structuredParameterType(schema) {
@@ -1409,19 +1462,19 @@ function structuredParameterType(schema) {
1409
1462
  if (type === "object") return "object";
1410
1463
  }
1411
1464
  function itemSchema(schema) {
1412
- return record(schema)?.items ?? declaredType(schema).match(/^array<(.+)>$/)?.[1];
1465
+ return record$1(schema)?.items ?? declaredType(schema).match(/^array<(.+)>$/)?.[1];
1413
1466
  }
1414
1467
  function matchesSchema(value, schema) {
1415
1468
  const type = declaredType(schema);
1416
1469
  const structured = structuredParameterType(schema);
1417
1470
  if (structured === "array") return Array.isArray(value) && value.every((item) => matchesSchema(item, itemSchema(schema)));
1418
1471
  if (structured === "object") {
1419
- const object = record(value);
1472
+ const object = record$1(value);
1420
1473
  if (!object) return false;
1421
- const definition = record(schema);
1474
+ const definition = record$1(schema);
1422
1475
  const required = definition?.required;
1423
1476
  if (Array.isArray(required) && required.some((key) => typeof key === "string" && !Object.hasOwn(object, key))) return false;
1424
- return Object.entries(record(definition?.properties) ?? {}).every(([key, property]) => Object.hasOwn(object, key) ? matchesSchema(object[key], property) : record(property)?.required !== true);
1477
+ return Object.entries(record$1(definition?.properties) ?? {}).every(([key, property]) => Object.hasOwn(object, key) ? matchesSchema(object[key], property) : record$1(property)?.required !== true);
1425
1478
  }
1426
1479
  if (type === "string") return typeof value === "string";
1427
1480
  if (type === "number") return typeof value === "number" && Number.isFinite(value);
@@ -1460,7 +1513,7 @@ function parseParameterValue(schema, value) {
1460
1513
  };
1461
1514
  }
1462
1515
  }
1463
- if (type === "array" ? !Array.isArray(parsed) : !record(parsed)) return {
1516
+ if (type === "array" ? !Array.isArray(parsed) : !record$1(parsed)) return {
1464
1517
  valid: false,
1465
1518
  error: `Expected a JSON ${type}.`
1466
1519
  };
@@ -1479,9 +1532,9 @@ function formatParameterValue(value) {
1479
1532
  }
1480
1533
  function normalizeNodeParameters(data, fallbackOperations, mappedFields = /* @__PURE__ */ new Set()) {
1481
1534
  const operation = resolveNodeOperation(data, fallbackOperations).selected;
1482
- const fields = record(operation?.input_fields);
1483
- const args = record(data.args);
1484
- const state = record(args?.state) ?? {};
1535
+ const fields = record$1(operation?.input_fields);
1536
+ const args = record$1(data.args);
1537
+ const state = record$1(args?.state) ?? {};
1485
1538
  if (!fields) return data;
1486
1539
  const visible = dropHiddenParameters(state, fields, mappedFields);
1487
1540
  const structuredFields = Object.entries(fields).filter(([name, schema]) => structuredParameterType(schema) && !mappedFields.has(name));
@@ -1510,7 +1563,7 @@ function normalizeNodeParameters(data, fallbackOperations, mappedFields = /* @__
1510
1563
  function normalizeWorkflowParameters(nodes, edges, fallbackOperations) {
1511
1564
  return nodes.map((node) => {
1512
1565
  if (node.data.disabled === true) return node;
1513
- const mappedFields = new Set(edges.filter((edge) => edge.target === node.id).flatMap((edge) => Object.entries(record(record(record(edge)?.mapping)?.definition) ?? {}).filter(([, definition]) => typeof definition === "string" ? definition.trim().length > 0 : definition !== void 0).map(([name]) => name)));
1566
+ const mappedFields = new Set(edges.filter((edge) => edge.target === node.id).flatMap((edge) => Object.entries(record$1(record$1(record$1(edge)?.mapping)?.definition) ?? {}).filter(([, definition]) => typeof definition === "string" ? definition.trim().length > 0 : definition !== void 0).map(([name]) => name)));
1514
1567
  try {
1515
1568
  return {
1516
1569
  ...node,
@@ -5763,6 +5816,234 @@ function useInsertNode() {
5763
5816
  ]);
5764
5817
  }
5765
5818
 
5819
+ // src/builder/lib/sensitive-params.ts
5820
+ /**
5821
+ * Which step settings hold a secret, so no agent tool sets them and nothing the
5822
+ * assistant is told about a step carries their value.
5823
+ *
5824
+ * Two signals, either is enough: the manifest marks the field `secret` (the
5825
+ * panel masks it), or its type or name reads like a credential. The name rule is
5826
+ * deliberately broad; a false positive only hides a value.
5827
+ */
5828
+ const SENSITIVE_PARAM = /pass(word)?|secret|token|api[-_]?key|private[-_]?key|credential|connection/i;
5829
+ function isSensitiveParam(param) {
5830
+ return param.secret === true || SENSITIVE_PARAM.test(`${param.type} ${param.name}`);
5831
+ }
5832
+
5833
+ // src/askAbout/describeAskables.ts
5834
+ /**
5835
+ * ★ BOFF-11897 "Ask AI about this": what the assistant is told about the
5836
+ * FluidGrids thing a person picked. Pure functions of what the page already
5837
+ * shows, so they are cheap to call at the moment of the right-click, and
5838
+ * unit-tested on their own.
5839
+ *
5840
+ * ★★ Order matters. fe-libs keeps the note within a budget and drops facts
5841
+ * from the END when it is full, so the facts that answer "why is this not
5842
+ * working?" come first and the plain settings come last.
5843
+ *
5844
+ * ★★ Secret settings never travel. A setting the manifest marks `secret`, or
5845
+ * whose type or name reads like a credential (`isSensitiveParam`), is named
5846
+ * with its value hidden. Connections are named by their app only: the
5847
+ * connection itself is a reference the person picked, never a value.
5848
+ */
5849
+ /** How a hidden value reads. */
5850
+ const HIDDEN_VALUE = "(hidden: secret)";
5851
+ /** At most this many settings are listed; they come after everything else. */
5852
+ const MAX_SETTINGS_LISTED = 8;
5853
+ const SETTING_VALUE_CHARS = 200;
5854
+ function valueText(value) {
5855
+ if (value === null || value === void 0) return "";
5856
+ if (typeof value === "string") return value.trim();
5857
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
5858
+ try {
5859
+ const json = JSON.stringify(value);
5860
+ return json === void 0 || json === "{}" || json === "[]" ? "" : json;
5861
+ } catch {
5862
+ return "";
5863
+ }
5864
+ }
5865
+ function cut(text, max) {
5866
+ return text.length > max ? `${text.slice(0, max - 1)}…` : text;
5867
+ }
5868
+ const RUN_STATUS_TEXT = {
5869
+ idle: "",
5870
+ running: "running",
5871
+ success: "succeeded",
5872
+ error: "failed"
5873
+ };
5874
+ /** A step on the workflow canvas. */
5875
+ function describeCanvasStep(step) {
5876
+ const name = step.label?.trim() || step.appName;
5877
+ const facts = [];
5878
+ facts.push({
5879
+ label: "Type",
5880
+ value: step.type && step.type !== step.appName ? `${step.appName} (${step.type})` : step.appName
5881
+ });
5882
+ if (step.operation) facts.push({
5883
+ label: "Operation",
5884
+ value: step.operation
5885
+ });
5886
+ if (step.trigger) facts.push({
5887
+ label: "Role",
5888
+ value: "trigger (starts the workflow)"
5889
+ });
5890
+ if (step.disabled) facts.push({
5891
+ label: "Disabled",
5892
+ value: "yes (skipped when the workflow runs)"
5893
+ });
5894
+ if (step.missingSettings && step.missingSettings.length > 0) facts.push({
5895
+ label: "Required settings still empty",
5896
+ value: step.missingSettings.join(", ")
5897
+ });
5898
+ if (step.missingConnection) facts.push({
5899
+ label: "Connection",
5900
+ value: "a required connection is not picked yet"
5901
+ });
5902
+ const run = step.runStatus ? RUN_STATUS_TEXT[step.runStatus] : "";
5903
+ if (run) facts.push({
5904
+ label: "Last test run on this canvas",
5905
+ value: run
5906
+ });
5907
+ if (step.runError?.trim()) facts.push({
5908
+ label: "Last run error",
5909
+ value: step.runError.trim()
5910
+ });
5911
+ if (step.connections && step.connections.length > 0) facts.push({
5912
+ label: "Connections picked",
5913
+ value: step.connections.join(", ")
5914
+ });
5915
+ let listed = 0;
5916
+ for (const setting of step.settings ?? []) {
5917
+ if (listed >= 8) break;
5918
+ const text = valueText(setting.value);
5919
+ if (!text) continue;
5920
+ facts.push({
5921
+ label: `Setting ${setting.label?.trim() || setting.name}`,
5922
+ value: isSensitiveParam(setting) ? HIDDEN_VALUE : cut(text, SETTING_VALUE_CHARS)
5923
+ });
5924
+ listed += 1;
5925
+ }
5926
+ return {
5927
+ kind: "workflow step",
5928
+ label: name,
5929
+ id: step.id,
5930
+ facts,
5931
+ pointBack: {
5932
+ tool: "fg_canvas_focus_node",
5933
+ args: { node: step.id }
5934
+ }
5935
+ };
5936
+ }
5937
+ function record(value) {
5938
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
5939
+ }
5940
+ /**
5941
+ * The facts `describeCanvasStep` reads, from the builder's own state. The name
5942
+ * is the registry's title, or, when the type is not in the registry (an
5943
+ * uninstalled store node), the node's saved manifest name, as the card shows.
5944
+ */
5945
+ function canvasStepFactsOf(step) {
5946
+ const data = step.data ?? {};
5947
+ const savedName = record(data.nodeData)?.name;
5948
+ const appName = (step.registryTitle === step.type && typeof savedName === "string" ? savedName : step.registryTitle) || step.id;
5949
+ const connections = record(data.args)?.connections;
5950
+ const runError = [...step.execution.logs].reverse().find((log) => log.nodeId === step.id && log.status === "failed")?.error ?? null;
5951
+ return {
5952
+ id: step.id,
5953
+ ...typeof data.label === "string" ? { label: data.label } : {},
5954
+ appName,
5955
+ ...step.type ? { type: step.type } : {},
5956
+ ...typeof data.operation_name === "string" ? { operation: data.operation_name } : {},
5957
+ disabled: data.disabled === true,
5958
+ trigger: step.trigger,
5959
+ missingSettings: step.missingSettings,
5960
+ missingConnection: step.missingConnection,
5961
+ connections: Array.isArray(connections) ? connections.flatMap((entry) => {
5962
+ const adapter = record(entry)?.adapter;
5963
+ return typeof adapter === "string" ? [adapter] : [];
5964
+ }) : [],
5965
+ settings: step.params.map((param) => ({
5966
+ name: param.name,
5967
+ type: param.type,
5968
+ ...param.label ? { label: param.label } : {},
5969
+ ...param.secret ? { secret: true } : {},
5970
+ value: step.state[param.name]
5971
+ })),
5972
+ ...step.execution.nodeStatuses[step.id] ? { runStatus: step.execution.nodeStatuses[step.id] } : {},
5973
+ runError
5974
+ };
5975
+ }
5976
+ function runFacts(run) {
5977
+ const facts = [{
5978
+ label: "Run",
5979
+ value: run.runId
5980
+ }, {
5981
+ label: "Run status",
5982
+ value: run.status
5983
+ }];
5984
+ if (run.workflowKey) facts.push({
5985
+ label: "Workflow",
5986
+ value: run.version !== void 0 ? `${run.workflowKey} v${run.version}` : run.workflowKey
5987
+ });
5988
+ return facts;
5989
+ }
5990
+ /** One step of a run, on the run's page. */
5991
+ function describeRunStep(step) {
5992
+ const facts = [{
5993
+ label: "Step status",
5994
+ value: step.status
5995
+ }];
5996
+ if (step.errorType?.trim()) facts.push({
5997
+ label: "Error type",
5998
+ value: step.errorType.trim()
5999
+ });
6000
+ if (step.error?.trim()) facts.push({
6001
+ label: "Error",
6002
+ value: step.error.trim()
6003
+ });
6004
+ if (step.stepType) facts.push({
6005
+ label: "Step type",
6006
+ value: step.stepVersion ? `${step.stepType} v${step.stepVersion}` : step.stepType
6007
+ });
6008
+ facts.push(...runFacts(step.run));
6009
+ return {
6010
+ kind: "run step",
6011
+ label: step.stepName,
6012
+ id: step.stepId,
6013
+ facts,
6014
+ pointBack: {
6015
+ tool: "fg_ui_point_at",
6016
+ args: {
6017
+ name: step.stepName,
6018
+ role: "button"
6019
+ }
6020
+ }
6021
+ };
6022
+ }
6023
+ /** The error a whole run ended with, on the run's page. */
6024
+ function describeRunError(run) {
6025
+ const facts = [];
6026
+ if (run.errorType?.trim()) facts.push({
6027
+ label: "Error type",
6028
+ value: run.errorType.trim()
6029
+ });
6030
+ if (run.errorMessage?.trim()) facts.push({
6031
+ label: "Error",
6032
+ value: run.errorMessage.trim()
6033
+ });
6034
+ if (run.failedSteps !== void 0 && run.failedSteps > 0) facts.push({
6035
+ label: "Failed steps",
6036
+ value: String(run.failedSteps)
6037
+ });
6038
+ facts.push(...runFacts(run));
6039
+ return {
6040
+ kind: "run error",
6041
+ label: "Run error",
6042
+ id: run.runId,
6043
+ facts
6044
+ };
6045
+ }
6046
+
5766
6047
  // src/builder/components/flow-builder/components/blocks/nodes/node/node.tsx
5767
6048
  /**
5768
6049
  * Adapters that are auto-managed by the runtime (e.g. the workspace-wide
@@ -6229,6 +6510,36 @@ const getNodeMetadata = (value) => {
6229
6510
  id,
6230
6511
  value?.type
6231
6512
  ]);
6513
+ const describeStep = useCallback(() => describeCanvasStep(canvasStepFactsOf({
6514
+ id,
6515
+ type: value?.type,
6516
+ data,
6517
+ registryTitle: meta.title,
6518
+ trigger: isNodeTrigger({
6519
+ type: value?.type,
6520
+ data
6521
+ }),
6522
+ missingSettings: missingParameters,
6523
+ missingConnection: missingCredentials,
6524
+ params: nodeParamsOf({
6525
+ id,
6526
+ type: value?.type,
6527
+ data
6528
+ }),
6529
+ state: nodeStateOf({
6530
+ id,
6531
+ data
6532
+ }),
6533
+ execution: useFlowStore.getState().execution
6534
+ })), [
6535
+ data,
6536
+ id,
6537
+ meta.title,
6538
+ missingCredentials,
6539
+ missingParameters
6540
+ ]);
6541
+ const askable = useFgAskable(describeStep);
6542
+ const askableRef = useCallback((element) => askable(element?.closest(".react-flow__node") ?? element), [askable]);
6232
6543
  if (isAdapterSubNode) return /* @__PURE__ */ jsx(SubNodeComponent, {
6233
6544
  ...props,
6234
6545
  nodeType: value?.type
@@ -6274,105 +6585,111 @@ const getNodeMetadata = (value) => {
6274
6585
  const mode = argsState?.mode;
6275
6586
  const runControlSubtitle = runControlNodeSubtitle(tr, value?.type, argsState);
6276
6587
  const logicSubtitle = isWaitForSignal ? readSignalName(argsState) : runControlSubtitle !== void 0 ? runControlSubtitle : Array.isArray(rules) ? `${rules.length} rule${rules.length === 1 ? "" : "s"}` : typeof mode === "string" ? mode : void 0;
6277
- return /* @__PURE__ */ jsxs("div", { children: [
6278
- /* @__PURE__ */ jsx(LogicNodeCard, {
6279
- nodeId: id,
6280
- title: data?.label || meta.title,
6281
- subtitle: logicSubtitle,
6282
- icon: meta.icon,
6283
- accent: roleAccent,
6284
- selected: !!selected,
6588
+ return /* @__PURE__ */ jsxs("div", {
6589
+ ref: askableRef,
6590
+ children: [
6591
+ /* @__PURE__ */ jsx(LogicNodeCard, {
6592
+ nodeId: id,
6593
+ title: data?.label || meta.title,
6594
+ subtitle: logicSubtitle,
6595
+ icon: meta.icon,
6596
+ accent: roleAccent,
6597
+ selected: !!selected,
6598
+ executionStatus,
6599
+ itemCount,
6600
+ isTrigger,
6601
+ isExecuting: isThisNodeExecuting || stepGuard.preparing,
6602
+ inputHandles,
6603
+ outputHandles,
6604
+ isConnectable: typeof isConnectable === "boolean" ? isConnectable : void 0,
6605
+ onClick: handleCardClick,
6606
+ onDoubleClick: handleOpenNdv,
6607
+ onSettings: handleOpenNdv,
6608
+ onExecute: () => void stepGuard.requestRun(),
6609
+ onDelete: readOnly ? void 0 : handleDeleteNode,
6610
+ infoButton: /* @__PURE__ */ jsx(NodeInfoButtonWithModal, { metadata: nodeInfoMetadata }),
6611
+ disabled: isDisabled,
6612
+ onToggleDisabled: readOnly ? void 0 : handleToggleDisabled,
6613
+ isRenaming: isRenamingThisNode && !readOnly,
6614
+ onStartRename: readOnly ? void 0 : handleStartRename,
6615
+ onCommitRename: handleCommitRename,
6616
+ onCancelRename: handleCancelRename
6617
+ }),
6618
+ /* @__PURE__ */ jsx(AdapterConnectors, {
6619
+ nodeId: id,
6620
+ adapters: connections.adapters
6621
+ }),
6622
+ stepGuard.dialog
6623
+ ]
6624
+ });
6625
+ }
6626
+ return /* @__PURE__ */ jsxs("div", {
6627
+ ref: askableRef,
6628
+ children: [
6629
+ /* @__PURE__ */ jsxs(NodeCard, {
6630
+ "data-selected": selected,
6631
+ selected,
6632
+ onClick: handleCardClick,
6633
+ onDoubleClick: handleOpenNdv,
6285
6634
  executionStatus,
6286
6635
  itemCount,
6287
6636
  isTrigger,
6637
+ accent: roleAccent,
6288
6638
  isExecuting: isThisNodeExecuting || stepGuard.preparing,
6289
- inputHandles,
6290
- outputHandles,
6291
- isConnectable: typeof isConnectable === "boolean" ? isConnectable : void 0,
6292
- onClick: handleCardClick,
6293
- onDoubleClick: handleOpenNdv,
6294
6639
  onSettings: handleOpenNdv,
6295
6640
  onExecute: () => void stepGuard.requestRun(),
6296
6641
  onDelete: readOnly ? void 0 : handleDeleteNode,
6297
6642
  infoButton: /* @__PURE__ */ jsx(NodeInfoButtonWithModal, { metadata: nodeInfoMetadata }),
6298
6643
  disabled: isDisabled,
6299
6644
  onToggleDisabled: readOnly ? void 0 : handleToggleDisabled,
6300
- isRenaming: isRenamingThisNode && !readOnly,
6301
- onStartRename: readOnly ? void 0 : handleStartRename,
6302
- onCommitRename: handleCommitRename,
6303
- onCancelRename: handleCancelRename
6645
+ children: [
6646
+ /* @__PURE__ */ jsx(NodeCardHeader, {
6647
+ icon: cardIcon,
6648
+ appName,
6649
+ accentKey: value?.type || meta.title,
6650
+ missingCredentials,
6651
+ missingParameters,
6652
+ label: nodeLabel,
6653
+ isRenaming: isRenamingThisNode && !readOnly,
6654
+ onStartRename: readOnly ? void 0 : handleStartRename,
6655
+ onCommitRename: handleCommitRename,
6656
+ onCancelRename: handleCancelRename
6657
+ }),
6658
+ isCodeWorkflow ? /* @__PURE__ */ jsx(CodeWorkflowCardSection, {
6659
+ args: data?.args?.state,
6660
+ durableEnabled
6661
+ }) : operationDisplay === "execute" && (value?.type || "").startsWith("internal:") || isTrigger && !resolveOperationName(data?.operation_name, "") ? /* @__PURE__ */ jsx("div", {
6662
+ className: "rounded-b-xl border-t border-border/60 px-3 pb-3 pt-2.5",
6663
+ children: /* @__PURE__ */ jsx("p", {
6664
+ className: "line-clamp-2 text-xs leading-snug text-muted-foreground",
6665
+ children: nodeDescription
6666
+ })
6667
+ }) : /* @__PURE__ */ jsx(NodeCardBody, {
6668
+ operationName: operationDisplay,
6669
+ description: nodeDescription
6670
+ }),
6671
+ handleSpacer > 0 && /* @__PURE__ */ jsx("div", { style: { height: handleSpacer } }),
6672
+ !isTrigger && /* @__PURE__ */ jsx(InputHandles, {
6673
+ nodeId: id,
6674
+ handles: inputHandles,
6675
+ accent: roleAccent,
6676
+ isConnectable: typeof isConnectable === "boolean" ? isConnectable : void 0
6677
+ }),
6678
+ /* @__PURE__ */ jsx(OutputHandles, {
6679
+ nodeId: id,
6680
+ handles: outputHandles,
6681
+ accent: roleAccent,
6682
+ isConnectable: typeof isConnectable === "boolean" ? isConnectable : void 0
6683
+ })
6684
+ ]
6304
6685
  }),
6686
+ stepGuard.dialog,
6305
6687
  /* @__PURE__ */ jsx(AdapterConnectors, {
6306
6688
  nodeId: id,
6307
6689
  adapters: connections.adapters
6308
- }),
6309
- stepGuard.dialog
6310
- ] });
6311
- }
6312
- return /* @__PURE__ */ jsxs("div", { children: [
6313
- /* @__PURE__ */ jsxs(NodeCard, {
6314
- "data-selected": selected,
6315
- selected,
6316
- onClick: handleCardClick,
6317
- onDoubleClick: handleOpenNdv,
6318
- executionStatus,
6319
- itemCount,
6320
- isTrigger,
6321
- accent: roleAccent,
6322
- isExecuting: isThisNodeExecuting || stepGuard.preparing,
6323
- onSettings: handleOpenNdv,
6324
- onExecute: () => void stepGuard.requestRun(),
6325
- onDelete: readOnly ? void 0 : handleDeleteNode,
6326
- infoButton: /* @__PURE__ */ jsx(NodeInfoButtonWithModal, { metadata: nodeInfoMetadata }),
6327
- disabled: isDisabled,
6328
- onToggleDisabled: readOnly ? void 0 : handleToggleDisabled,
6329
- children: [
6330
- /* @__PURE__ */ jsx(NodeCardHeader, {
6331
- icon: cardIcon,
6332
- appName,
6333
- accentKey: value?.type || meta.title,
6334
- missingCredentials,
6335
- missingParameters,
6336
- label: nodeLabel,
6337
- isRenaming: isRenamingThisNode && !readOnly,
6338
- onStartRename: readOnly ? void 0 : handleStartRename,
6339
- onCommitRename: handleCommitRename,
6340
- onCancelRename: handleCancelRename
6341
- }),
6342
- isCodeWorkflow ? /* @__PURE__ */ jsx(CodeWorkflowCardSection, {
6343
- args: data?.args?.state,
6344
- durableEnabled
6345
- }) : operationDisplay === "execute" && (value?.type || "").startsWith("internal:") || isTrigger && !resolveOperationName(data?.operation_name, "") ? /* @__PURE__ */ jsx("div", {
6346
- className: "rounded-b-xl border-t border-border/60 px-3 pb-3 pt-2.5",
6347
- children: /* @__PURE__ */ jsx("p", {
6348
- className: "line-clamp-2 text-xs leading-snug text-muted-foreground",
6349
- children: nodeDescription
6350
- })
6351
- }) : /* @__PURE__ */ jsx(NodeCardBody, {
6352
- operationName: operationDisplay,
6353
- description: nodeDescription
6354
- }),
6355
- handleSpacer > 0 && /* @__PURE__ */ jsx("div", { style: { height: handleSpacer } }),
6356
- !isTrigger && /* @__PURE__ */ jsx(InputHandles, {
6357
- nodeId: id,
6358
- handles: inputHandles,
6359
- accent: roleAccent,
6360
- isConnectable: typeof isConnectable === "boolean" ? isConnectable : void 0
6361
- }),
6362
- /* @__PURE__ */ jsx(OutputHandles, {
6363
- nodeId: id,
6364
- handles: outputHandles,
6365
- accent: roleAccent,
6366
- isConnectable: typeof isConnectable === "boolean" ? isConnectable : void 0
6367
- })
6368
- ]
6369
- }),
6370
- stepGuard.dialog,
6371
- /* @__PURE__ */ jsx(AdapterConnectors, {
6372
- nodeId: id,
6373
- adapters: connections.adapters
6374
- })
6375
- ] });
6690
+ })
6691
+ ]
6692
+ });
6376
6693
  }
6377
6694
  return {
6378
6695
  type: value?.type,
@@ -10417,4 +10734,4 @@ function useFluidGridsContext() {
10417
10734
  return context;
10418
10735
  }
10419
10736
 
10420
- export { isStringOptionList as $, inAppLeaveTarget as $t, StoreNodeProductsByTypeDocument as A, newConnectionProviderKey as An, useSaveStatusStore as At, useRunGuard as B, shouldReleaseForIdle as Bt, GetMyNodeInstallationsDocument as C, parseRawSignalPayload as Cn, useSaveWorkflow as Ct, InstallWorkflowNodeDocument as D, connectionsForAdapter as Dn, createThenOpen as Dt, InstallPurchasedNodeDocument as E, signalPayloadCard as En, classifyCreateRefusal as Et, getNodeMetadata as F, useBuilderReadOnly as Fn, leaseCallQueue as Ft, nodeSubtitle as G, sessionBeforeReload as Gt, asRecord as H, EDIT_SESSION_ID as Ht, buildOperationSeed as I, leaseCallSequence as It, paramsFromOperation as J, AUTOSAVE_DEBOUNCE_MS as Jt, nodeTitle as K, rememberOwnStatusChange as Kt, createConfiguredNode as L, phaseAfterAcquire as Lt, UninstallOrganizationPrivateNodeDocument as M, getNodeDetail as Mn, useEditLeaseStore as Mt, UninstallWorkflowNodeDocument as N, buildStickyNode as Nn, bannerOf as Nt, MyWorkflowTemplateSubmissionsDocument as O, isOptionalAdapter as On, refusalOfQuota as Ot, WithdrawWorkflowTemplateSubmissionDocument as P, BuilderReadOnlyProvider as Pn, isLocked as Pt, stripSubmitActions as Q, findSaveConflict as Qt, useInsertNode as R, phaseAfterTakeOver as Rt, CreateStoreOrderDocument as S, durableInterfaceOf as Sn, useFlowValidator as St, InstallOrganizationPrivateNodeDocument as T, runSignalTargets as Tn, builderPathOf as Tt, coerceNumber as U, forgetSessionForReload as Ut, workflowParameterIssues as V, tickAction as Vt, fixedParameterState as W, rememberSessionForReload as Wt, useNodeConfig as X, canAutosave as Xt, selectedOperationOf as Y, AUTOSAVE_RETRY_DELAYS_MS as Yt, resolveUiSpec as Z, codeBuildIdOf as Zt, registryNodeData as _, clearCodeWorkflowDefinitionCache as _n, leavingLosesChanges as _t, NODES_METADATA as a, nodeStateDurationMs as an, casesFromRules as at, BrowseWorkflowNodesDocument as b, canSignalRun as bn, watchLeavingLosesChanges as bt, ensureInstalledNodeTypesLoaded as c, parseParameterValue as cn, latestNodeLog as ct, ensureRegistryNodeTypesLoaded as d, resolveNodeOperation as dn, useGuardedNavigate as dt, resettledBaseline as en, optionListText as et, isNodeConfigsInitialized as f, isParamVisible as fn, useManualSave as ft, registerWorkflowNodeTypes as g, useNodeRegistryRevision as gn, editingLeaseLocked as gt, registerNodeManifests as h, useCanvasEdges as hn, editStateFor as ht, AVAILABLE_NODES as i, formatElapsedMs as in, SwitchRulesEditor as it, SubmitWorkflowTemplateDocument as j, replaceAdapterConnections as jn, useEditLeaseLocked as jt, OrganizationPrivateNodesDocument as k, matchesAdapterProvider as kn, saveIndicatorOf as kt, ensureNodeConfigsLoaded as l, structuredParameterType as ln, boundFieldsOf as lt, refetchStoreNodes as m, openRunQuotaUpgradePrompt as mn, departingSave as mt, FluidGridsProvider as n, workflowFingerprint as nn, resolveLoadOptionsSpec as nt, NODE_CONFIGS as o, runDurationMs as on, FieldTree as ot, onNodeTypesUpdate as p, useFluidGridsAnalytics as pn, autosaveAllowedFor as pt, optionText as q, rememberRevision as qt, useFluidGridsContext as r, useNodeExecution as rn, ConditionTreeBuilder as rt, NODE_TYPES as s, formatParameterValue as sn, parseMappingValue as st, FluidGridsContext as t, saveFailureActionOf as tn, optionListValues as tt, ensureNodeTypesLoaded as u, fieldsFromValue as un, missingRequiredParameters as ut, scheduleNodePaletteLoad as v, loadDefinitionJson as vn, savedEverythingBeforeLeaving as vt, GetNodeInstallableVersionsDocument as w, payloadFromCardValues as wn, blankWorkflowCreateInput as wt, BrowseWorkflowTemplatesDocument as x, declaredSignals as xn, isFlowComplete as xt, global_operations_exports as y, useCodeWorkflowInterface as yn, touchesSavedFields as yt, MissingParametersDialog as z, releasesOnClose as zt };
10737
+ export { useNodeConfig as $, canAutosave as $t, StoreNodeProductsByTypeDocument as A, connectionsForAdapter as An, createThenOpen as At, createConfiguredNode as B, useBuilderReadOnly as Bn, phaseAfterAcquire as Bt, GetMyNodeInstallationsDocument as C, canSignalRun as Cn, watchLeavingLosesChanges as Ct, InstallWorkflowNodeDocument as D, payloadFromCardValues as Dn, blankWorkflowCreateInput as Dt, InstallPurchasedNodeDocument as E, parseRawSignalPayload as En, useSaveWorkflow as Et, getNodeMetadata as F, getNodeDetail as Fn, useEditLeaseStore as Ft, asRecord as G, EDIT_SESSION_ID as Gt, MissingParametersDialog as H, releasesOnClose as Ht, describeRunError as I, buildStickyNode as In, bannerOf as It, nodeSubtitle as J, sessionBeforeReload as Jt, coerceNumber as K, forgetSessionForReload as Kt, describeRunStep as L, loadMarkdown as Ln, isLocked as Lt, UninstallOrganizationPrivateNodeDocument as M, matchesAdapterProvider as Mn, saveIndicatorOf as Mt, UninstallWorkflowNodeDocument as N, newConnectionProviderKey as Nn, useSaveStatusStore as Nt, MyWorkflowTemplateSubmissionsDocument as O, runSignalTargets as On, builderPathOf as Ot, WithdrawWorkflowTemplateSubmissionDocument as P, replaceAdapterConnections as Pn, useEditLeaseLocked as Pt, selectedOperationOf as Q, AUTOSAVE_RETRY_DELAYS_MS as Qt, SENSITIVE_PARAM as R, useMarkdownRenderer as Rn, leaseCallQueue as Rt, CreateStoreOrderDocument as S, useCodeWorkflowInterface as Sn, touchesSavedFields as St, InstallOrganizationPrivateNodeDocument as T, durableInterfaceOf as Tn, useFlowValidator as Tt, useRunGuard as U, shouldReleaseForIdle as Ut, useInsertNode as V, phaseAfterTakeOver as Vt, workflowParameterIssues as W, tickAction as Wt, optionText as X, rememberRevision as Xt, nodeTitle as Y, rememberOwnStatusChange as Yt, paramsFromOperation as Z, AUTOSAVE_DEBOUNCE_MS as Zt, registryNodeData as _, openRunQuotaUpgradePrompt as _n, departingSave as _t, NODES_METADATA as a, workflowFingerprint as an, resolveLoadOptionsSpec as at, BrowseWorkflowNodesDocument as b, clearCodeWorkflowDefinitionCache as bn, leavingLosesChanges as bt, ensureInstalledNodeTypesLoaded as c, nodeStateDurationMs as cn, casesFromRules as ct, ensureRegistryNodeTypesLoaded as d, parseParameterValue as dn, latestNodeLog as dt, codeBuildIdOf as en, resolveUiSpec as et, isNodeConfigsInitialized as f, structuredParameterType as fn, boundFieldsOf as ft, registerWorkflowNodeTypes as g, useFluidGridsAnalytics as gn, autosaveAllowedFor as gt, registerNodeManifests as h, isParamVisible as hn, useManualSave as ht, AVAILABLE_NODES as i, saveFailureActionOf as in, optionListValues as it, SubmitWorkflowTemplateDocument as j, isOptionalAdapter as jn, refusalOfQuota as jt, OrganizationPrivateNodesDocument as k, signalPayloadCard as kn, classifyCreateRefusal as kt, ensureNodeConfigsLoaded as l, runDurationMs as ln, FieldTree as lt, refetchStoreNodes as m, resolveNodeOperation as mn, useGuardedNavigate as mt, FluidGridsProvider as n, inAppLeaveTarget as nn, isStringOptionList as nt, NODE_CONFIGS as o, useNodeExecution as on, ConditionTreeBuilder as ot, onNodeTypesUpdate as p, fieldsFromValue as pn, missingRequiredParameters as pt, fixedParameterState as q, rememberSessionForReload as qt, useFluidGridsContext as r, resettledBaseline as rn, optionListText as rt, NODE_TYPES as s, formatElapsedMs as sn, SwitchRulesEditor as st, FluidGridsContext as t, findSaveConflict as tn, stripSubmitActions as tt, ensureNodeTypesLoaded as u, formatParameterValue as un, parseMappingValue as ut, scheduleNodePaletteLoad as v, useCanvasEdges as vn, editStateFor as vt, GetNodeInstallableVersionsDocument as w, declaredSignals as wn, isFlowComplete as wt, BrowseWorkflowTemplatesDocument as x, loadDefinitionJson as xn, savedEverythingBeforeLeaving as xt, global_operations_exports as y, useNodeRegistryRevision as yn, editingLeaseLocked as yt, buildOperationSeed as z, BuilderReadOnlyProvider as zn, leaseCallSequence as zt };
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { t as __exportAll } from "./rolldown-runtime-hDbaFkMy.js";
3
- import { d as useRestoreWorkflowVersion, h as useWorkflowDefinitions } from "./useWorkflows-BlVVu8NM.js";
3
+ import { d as useRestoreWorkflowVersion, h as useWorkflowDefinitions } from "./useWorkflows-Czz5DlL0.js";
4
4
  import { i as versionsOfWorkflow, t as VersionDiffDialog } from "./VersionDiffDialog-DSN0mU0f.js";
5
5
  import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
6
6
  import { x as getFluidGridsConfig } from "./client-C9XSzSXr.js";
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { t as __exportAll } from "./rolldown-runtime-hDbaFkMy.js";
3
3
  import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
4
- import { o as useRunReplayActions, r as useInsightsData } from "./useObservability-CfEw3ZFD.js";
4
+ import { o as useRunReplayActions, r as useInsightsData } from "./useObservability-FfQGgW6W.js";
5
5
  import { a as getTopFailingWorkflows, i as getTopFailingNodes, n as formatDuration, o as percentage, r as getFailureBreakdown } from "./utils-uaL5UVxb.js";
6
6
  import { n as FailureTriagePanel } from "./FailureTriagePanel-B9hDQymi.js";
7
7
  import { o as useContinuousOptimisationEnabled } from "./feature-flags-DM5myWaD.js";
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { a as DialogFooter, i as DialogDescription, l as DialogTitle, o as DialogHeader, r as DialogContent, t as Dialog } from "./dialog-Cjb4lB4D.js";
3
3
  import { t as Button } from "./button-DG7y_FVJ.js";
4
- import { r as Badge } from "./TemplateCard-BdB087SP.js";
4
+ import { r as Badge } from "./TemplateCard-HIMsb47R.js";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
6
  import { AlertTriangle, CheckCircle2, Download, ExternalLink, Loader2, Puzzle, ShoppingCart } from "lucide-react";
7
7