@burdenoff/fluidgrids-fe-sdk 2026.1027.11 → 2026.1027.13

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 (36) hide show
  1. package/dist/{ExecutionDetailPage-I7CHMI47.js → ExecutionDetailPage-WI2HBMLR.js} +7 -7
  2. package/dist/{ExecutionsListPage-V6SEHLIO.js → ExecutionsListPage-PYWVUCD5.js} +7 -7
  3. package/dist/{WorkflowBuilder-6AOA4S5Q.js → WorkflowBuilder-DKEBE6HA.js} +9 -9
  4. package/dist/{WorkflowsList-GD7TKQE3.js → WorkflowsList-FAEZCPKT.js} +7 -7
  5. package/dist/{chunk-FEJQKL2W.js → chunk-2UJE5MIB.js} +4 -4
  6. package/dist/{chunk-T36KKYRZ.js → chunk-35W2XOJD.js} +1 -1
  7. package/dist/{chunk-MQSNHJTA.js → chunk-5GWRNWNN.js} +1 -1
  8. package/dist/{chunk-S2WZK7QG.js → chunk-6AHGSM4G.js} +3 -3
  9. package/dist/{chunk-LYY3A4JM.js → chunk-7GL3WK76.js} +2 -2
  10. package/dist/{chunk-EKE75E6N.js → chunk-AUXJFMUD.js} +4 -4
  11. package/dist/{chunk-4DIWB6K3.js → chunk-BIW7JS4G.js} +1 -1
  12. package/dist/{chunk-Z6AFJGX3.js → chunk-BN6MTJ6A.js} +1 -1
  13. package/dist/{chunk-46VSRXC2.js → chunk-FUVZHSU3.js} +540 -6
  14. package/dist/{chunk-QVYC37ZX.js → chunk-GVLJRPVK.js} +2 -2
  15. package/dist/{chunk-6Z6SWFBA.js → chunk-J4PFYC4Z.js} +1 -1
  16. package/dist/{chunk-H7PTIEUR.js → chunk-JK5GPFYR.js} +1 -1
  17. package/dist/{chunk-I56INMUC.js → chunk-K42RDV5W.js} +115 -9
  18. package/dist/{chunk-TXQVCIVV.js → chunk-M3Q6GJCR.js} +8 -1
  19. package/dist/{chunk-SLJMCABK.js → chunk-PCB67G7B.js} +3 -3
  20. package/dist/{chunk-MFWZIZQR.js → chunk-Q7ROGMZN.js} +5 -5
  21. package/dist/{chunk-EGY24ZA6.js → chunk-TR2AQDEW.js} +1 -1
  22. package/dist/{chunk-2BA6V24R.js → chunk-VBZNSN3Y.js} +3 -3
  23. package/dist/{chunk-ZQTAZE55.js → chunk-WJ3SQ7CF.js} +2 -2
  24. package/dist/{chunk-SEQ3HMR3.js → chunk-YFPO2YSH.js} +1 -1
  25. package/dist/{chunk-QT3FONT3.js → chunk-ZPBAMINO.js} +1 -1
  26. package/dist/executions.js +9 -9
  27. package/dist/history.js +4 -4
  28. package/dist/index.d.ts +1282 -6
  29. package/dist/index.js +31 -31
  30. package/dist/nodes.js +2 -2
  31. package/dist/observability.js +4 -4
  32. package/dist/settings.js +4 -4
  33. package/dist/templates.js +8 -8
  34. package/dist/workflow-builder.js +10 -10
  35. package/dist/workflows-list.js +7 -7
  36. package/package.json +1 -1
@@ -1,9 +1,9 @@
1
- import { buildNodeDisplayMap, ExecutionStatusBadge, formatRunVersion, NodeTimeline, RunLogViewer } from './chunk-ZQTAZE55.js';
2
- import { useExecution, useExecutionActions, useAutoRefresh } from './chunk-QVYC37ZX.js';
3
- import { EntityTagPicker } from './chunk-Z6AFJGX3.js';
4
- import { throwIfResultError, useFluidGridsPermission, FLUIDGRIDS_PERMISSIONS } from './chunk-H7PTIEUR.js';
1
+ import { buildNodeDisplayMap, ExecutionStatusBadge, formatRunVersion, NodeTimeline, RunLogViewer } from './chunk-WJ3SQ7CF.js';
2
+ import { useExecution, useExecutionActions, useAutoRefresh } from './chunk-GVLJRPVK.js';
3
+ import { EntityTagPicker } from './chunk-BN6MTJ6A.js';
4
+ import { throwIfResultError, useFluidGridsPermission, FLUIDGRIDS_PERMISSIONS } from './chunk-JK5GPFYR.js';
5
5
  import { getFluidGridsConfig } from './chunk-CY6KX4RG.js';
6
- import { RunLogsDocument, RunLogAppendedDocument } from './chunk-TXQVCIVV.js';
6
+ import { RunLogsDocument, RunLogAppendedDocument, ExplainRunDocument, WorkflowProposalDocument, ProposeRunFixDocument, ProposeWorkflowChangeDocument, ApplyWorkflowProposalDocument, RejectWorkflowProposalDocument, CancelWorkflowProposalDocument } from './chunk-M3Q6GJCR.js';
7
7
  import { useTr } from './chunk-UULVIT25.js';
8
8
  import { getFluidGridsClient, getFluidGridsClientSafe } from './chunk-NTWG3CJX.js';
9
9
  import { useState, useRef, useCallback, useEffect, useMemo } from 'react';
@@ -11,7 +11,7 @@ import { useParams, useNavigate, Link } from 'react-router';
11
11
  import { Icon } from '@iconify/react';
12
12
  import { toast } from 'sonner';
13
13
  import { Button } from '@burdenoff/fe-libs/button';
14
- import { Card, CardHeader, Badge, CardContent, Label, Textarea, AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction, Input } from '@burdenoff/fe-libs/ui';
14
+ import { Card, CardHeader, Badge, CardContent, Label, Textarea, AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction, Input, Button as Button$1 } from '@burdenoff/fe-libs/ui';
15
15
  import { useActorProfiles, buildActorFallback } from '@burdenoff/fe-libs/shared';
16
16
  import { gql } from '@apollo/client';
17
17
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -800,6 +800,522 @@ function RunQueuedBanner({
800
800
  }
801
801
  );
802
802
  }
803
+ var PROPOSAL_POLL_INTERVAL_MS = 3e3;
804
+ var PROPOSAL_MAX_WAIT_MS = 6 * 60 * 1e3;
805
+ function messageOf(error) {
806
+ return error instanceof Error ? error.message : String(error);
807
+ }
808
+ function isProposalRunning(status) {
809
+ return status === "PROCESSING";
810
+ }
811
+ function useWorkflowProposal() {
812
+ const [proposal, setProposal] = useState(null);
813
+ const [isWorking, setIsWorking] = useState(false);
814
+ const [isDeciding, setIsDeciding] = useState(false);
815
+ const [error, setError] = useState(null);
816
+ const [timedOut, setTimedOut] = useState(false);
817
+ const runRef = useRef(0);
818
+ const timerRef = useRef(null);
819
+ const clearTimer = useCallback(() => {
820
+ if (timerRef.current) clearTimeout(timerRef.current);
821
+ timerRef.current = null;
822
+ }, []);
823
+ useEffect(
824
+ () => () => {
825
+ runRef.current++;
826
+ clearTimer();
827
+ },
828
+ [clearTimer]
829
+ );
830
+ const reset = useCallback(() => {
831
+ runRef.current++;
832
+ clearTimer();
833
+ setProposal(null);
834
+ setIsWorking(false);
835
+ setIsDeciding(false);
836
+ setError(null);
837
+ setTimedOut(false);
838
+ }, [clearTimer]);
839
+ const poll = useCallback(
840
+ async (run, id, startedAt) => {
841
+ if (run !== runRef.current) return;
842
+ try {
843
+ const result = throwIfResultError(
844
+ await getFluidGridsClient().query({
845
+ query: WorkflowProposalDocument,
846
+ variables: { id },
847
+ fetchPolicy: "network-only"
848
+ })
849
+ );
850
+ if (run !== runRef.current) return;
851
+ const next = result.data?.workflowProposal ?? null;
852
+ if (next) setProposal(next);
853
+ const status = next ? String(next.status) : void 0;
854
+ if (isProposalRunning(status)) {
855
+ if (Date.now() - startedAt < PROPOSAL_MAX_WAIT_MS) {
856
+ timerRef.current = setTimeout(
857
+ () => void poll(run, id, startedAt),
858
+ PROPOSAL_POLL_INTERVAL_MS
859
+ );
860
+ return;
861
+ }
862
+ setTimedOut(true);
863
+ }
864
+ setIsWorking(false);
865
+ } catch (pollError) {
866
+ if (run !== runRef.current) return;
867
+ setError(messageOf(pollError));
868
+ setIsWorking(false);
869
+ }
870
+ },
871
+ []
872
+ );
873
+ const start = useCallback(
874
+ async (input) => {
875
+ const run = ++runRef.current;
876
+ clearTimer();
877
+ setProposal(null);
878
+ setError(null);
879
+ setTimedOut(false);
880
+ setIsWorking(true);
881
+ try {
882
+ const client = getFluidGridsClient();
883
+ let started;
884
+ if (input.kind === "runFix") {
885
+ const result = throwIfResultError(
886
+ await client.mutate({
887
+ mutation: ProposeRunFixDocument,
888
+ variables: {
889
+ input: {
890
+ runId: input.runId,
891
+ ...input.prompt ? { prompt: input.prompt } : {}
892
+ }
893
+ }
894
+ })
895
+ );
896
+ started = result.data?.proposeRunFix;
897
+ } else {
898
+ const result = throwIfResultError(
899
+ await client.mutate({
900
+ mutation: ProposeWorkflowChangeDocument,
901
+ variables: {
902
+ input: {
903
+ prompt: input.prompt,
904
+ ...input.workflowKey ? { workflowKey: input.workflowKey } : {},
905
+ ...input.baseVersion ? { baseVersion: input.baseVersion } : {}
906
+ }
907
+ }
908
+ })
909
+ );
910
+ started = result.data?.proposeWorkflowChange;
911
+ }
912
+ if (!started?.id) throw new Error("The proposal did not start");
913
+ if (run !== runRef.current) return;
914
+ setProposal(started);
915
+ const id = started.id;
916
+ timerRef.current = setTimeout(
917
+ () => void poll(run, id, Date.now()),
918
+ PROPOSAL_POLL_INTERVAL_MS
919
+ );
920
+ } catch (startError) {
921
+ if (run !== runRef.current) return;
922
+ setError(messageOf(startError));
923
+ setIsWorking(false);
924
+ }
925
+ },
926
+ [clearTimer, poll]
927
+ );
928
+ const decide = useCallback(
929
+ async (action) => {
930
+ const id = proposal?.id;
931
+ if (!id) return null;
932
+ const run = runRef.current;
933
+ setIsDeciding(true);
934
+ setError(null);
935
+ try {
936
+ const client = getFluidGridsClient();
937
+ let next;
938
+ if (action === "apply") {
939
+ const result = throwIfResultError(
940
+ await client.mutate({ mutation: ApplyWorkflowProposalDocument, variables: { id } })
941
+ );
942
+ next = result.data?.applyWorkflowProposal;
943
+ } else if (action === "reject") {
944
+ const result = throwIfResultError(
945
+ await client.mutate({ mutation: RejectWorkflowProposalDocument, variables: { id } })
946
+ );
947
+ next = result.data?.rejectWorkflowProposal;
948
+ } else {
949
+ clearTimer();
950
+ const result = throwIfResultError(
951
+ await client.mutate({ mutation: CancelWorkflowProposalDocument, variables: { id } })
952
+ );
953
+ next = result.data?.cancelWorkflowProposal;
954
+ setIsWorking(false);
955
+ }
956
+ if (run === runRef.current && next) setProposal(next);
957
+ return next ?? null;
958
+ } catch (decideError) {
959
+ if (run === runRef.current) setError(messageOf(decideError));
960
+ return null;
961
+ } finally {
962
+ if (run === runRef.current) setIsDeciding(false);
963
+ }
964
+ },
965
+ [clearTimer, proposal?.id]
966
+ );
967
+ const apply = useCallback(() => decide("apply"), [decide]);
968
+ const reject = useCallback(async () => {
969
+ await decide("reject");
970
+ }, [decide]);
971
+ const cancel = useCallback(async () => {
972
+ await decide("cancel");
973
+ }, [decide]);
974
+ return {
975
+ proposal,
976
+ isWorking,
977
+ isDeciding,
978
+ error,
979
+ timedOut,
980
+ start,
981
+ apply,
982
+ reject,
983
+ cancel,
984
+ reset
985
+ };
986
+ }
987
+ var STATUS_TONE = {
988
+ PROCESSING: "secondary",
989
+ PROPOSED: "default",
990
+ APPLIED: "default",
991
+ REJECTED: "outline",
992
+ STALE: "outline",
993
+ CANCELLED: "outline",
994
+ FAILED: "destructive"
995
+ };
996
+ function edgeLabel(edge) {
997
+ const from = edge.sourceHandle ? `${edge.source} (${edge.sourceHandle})` : edge.source;
998
+ return `${from} \u2192 ${edge.target}`;
999
+ }
1000
+ function ChangeList({
1001
+ title,
1002
+ icon,
1003
+ items,
1004
+ testId
1005
+ }) {
1006
+ if (items.length === 0) return null;
1007
+ return /* @__PURE__ */ jsxs("div", { "data-testid": testId, children: [
1008
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs font-medium uppercase tracking-wide text-text-secondary", children: [
1009
+ /* @__PURE__ */ jsx(Icon, { icon, width: 14 }),
1010
+ title,
1011
+ " (",
1012
+ items.length,
1013
+ ")"
1014
+ ] }),
1015
+ /* @__PURE__ */ jsx("ul", { className: "mt-1 space-y-0.5 text-sm text-text-primary", children: items.map((item) => /* @__PURE__ */ jsx("li", { className: "truncate", children: item }, item)) })
1016
+ ] });
1017
+ }
1018
+ function nodeLine(node, withFields) {
1019
+ const base = `${node.label} \xB7 ${node.type}`;
1020
+ return withFields && node.fields.length > 0 ? `${base} \u2014 ${node.fields.join(", ")}` : base;
1021
+ }
1022
+ function WorkflowProposalCard({
1023
+ proposal,
1024
+ isDeciding = false,
1025
+ onOpenWorkflow,
1026
+ onApply,
1027
+ onReject,
1028
+ onCancel
1029
+ }) {
1030
+ const tr = useTr();
1031
+ const status = String(proposal.status);
1032
+ const diff = proposal.diff;
1033
+ const warnings = proposal.validation?.warnings ?? [];
1034
+ const errors = proposal.validation?.valid === false ? proposal.validation.errors : [];
1035
+ return /* @__PURE__ */ jsxs(
1036
+ "section",
1037
+ {
1038
+ className: "rounded-lg border border-border-secondary bg-background-secondary p-4",
1039
+ "data-testid": "workflow-proposal-card",
1040
+ "data-status": status,
1041
+ children: [
1042
+ /* @__PURE__ */ jsxs("header", { className: "flex flex-wrap items-center gap-2", children: [
1043
+ /* @__PURE__ */ jsx(
1044
+ Icon,
1045
+ {
1046
+ icon: "mdi:creation-outline",
1047
+ width: 18,
1048
+ className: "text-text-secondary"
1049
+ }
1050
+ ),
1051
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-text-primary", children: proposal.name ?? tr("fluidgrids.ai.proposal.title", "AI proposal") }),
1052
+ /* @__PURE__ */ jsx(
1053
+ Badge,
1054
+ {
1055
+ variant: STATUS_TONE[status] ?? "outline",
1056
+ "data-testid": "workflow-proposal-status",
1057
+ children: status
1058
+ }
1059
+ ),
1060
+ proposal.baseVersion && /* @__PURE__ */ jsx("span", { className: "text-xs text-text-secondary", children: tr(
1061
+ "fluidgrids.ai.proposal.against",
1062
+ "against version {{version}}",
1063
+ {
1064
+ version: proposal.baseVersion
1065
+ }
1066
+ ) })
1067
+ ] }),
1068
+ status === "PROCESSING" && /* @__PURE__ */ jsxs("div", { className: "mt-3 flex items-center gap-2 text-sm text-text-secondary", children: [
1069
+ /* @__PURE__ */ jsx(Icon, { icon: "mdi:loading", width: 16, className: "animate-spin" }),
1070
+ tr("fluidgrids.ai.proposal.working", "Working ({{stage}})\u2026", {
1071
+ stage: proposal.progress?.stage ?? "prompting"
1072
+ }),
1073
+ onCancel && /* @__PURE__ */ jsx(
1074
+ Button$1,
1075
+ {
1076
+ size: "sm",
1077
+ variant: "ghost",
1078
+ onClick: onCancel,
1079
+ disabled: isDeciding,
1080
+ children: tr("fluidgrids.ai.proposal.cancel", "Cancel")
1081
+ }
1082
+ )
1083
+ ] }),
1084
+ proposal.explanation && /* @__PURE__ */ jsx(
1085
+ "p",
1086
+ {
1087
+ className: "mt-3 text-sm text-text-primary",
1088
+ "data-testid": "workflow-proposal-explanation",
1089
+ children: proposal.explanation
1090
+ }
1091
+ ),
1092
+ proposal.message && status !== "PROPOSED" && status !== "PROCESSING" && status !== "APPLIED" && /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-text-secondary", children: proposal.message }),
1093
+ diff && /* @__PURE__ */ jsxs("div", { className: "mt-3 space-y-2", "data-testid": "workflow-proposal-diff", children: [
1094
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-text-primary", children: diff.summary }),
1095
+ /* @__PURE__ */ jsx(
1096
+ ChangeList,
1097
+ {
1098
+ testId: "workflow-proposal-added",
1099
+ icon: "mdi:plus-circle-outline",
1100
+ title: tr("fluidgrids.ai.proposal.added", "Added nodes"),
1101
+ items: diff.addedNodes.map((node) => nodeLine(node, false))
1102
+ }
1103
+ ),
1104
+ /* @__PURE__ */ jsx(
1105
+ ChangeList,
1106
+ {
1107
+ testId: "workflow-proposal-changed",
1108
+ icon: "mdi:pencil-circle-outline",
1109
+ title: tr("fluidgrids.ai.proposal.changed", "Changed nodes"),
1110
+ items: diff.changedNodes.map((node) => nodeLine(node, true))
1111
+ }
1112
+ ),
1113
+ /* @__PURE__ */ jsx(
1114
+ ChangeList,
1115
+ {
1116
+ testId: "workflow-proposal-removed",
1117
+ icon: "mdi:minus-circle-outline",
1118
+ title: tr("fluidgrids.ai.proposal.removed", "Removed nodes"),
1119
+ items: diff.removedNodes.map((node) => nodeLine(node, false))
1120
+ }
1121
+ ),
1122
+ /* @__PURE__ */ jsx(
1123
+ ChangeList,
1124
+ {
1125
+ testId: "workflow-proposal-edges",
1126
+ icon: "mdi:vector-line",
1127
+ title: tr(
1128
+ "fluidgrids.ai.proposal.connections",
1129
+ "Connection changes"
1130
+ ),
1131
+ items: [
1132
+ ...diff.addedEdges.map((edge) => `+ ${edgeLabel(edge)}`),
1133
+ ...diff.removedEdges.map((edge) => `\u2212 ${edgeLabel(edge)}`)
1134
+ ]
1135
+ }
1136
+ )
1137
+ ] }),
1138
+ errors.length > 0 && /* @__PURE__ */ jsx("ul", { className: "mt-3 space-y-1 text-sm text-[var(--color-status-error-text)]", children: errors.map((message) => /* @__PURE__ */ jsx("li", { children: message }, message)) }),
1139
+ warnings.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mt-3", "data-testid": "workflow-proposal-warnings", children: [
1140
+ /* @__PURE__ */ jsx("div", { className: "text-xs font-medium uppercase tracking-wide text-text-secondary", children: tr("fluidgrids.ai.proposal.todo", "Still to set up") }),
1141
+ /* @__PURE__ */ jsx("ul", { className: "mt-1 space-y-0.5 text-sm text-text-secondary", children: warnings.map((warning) => /* @__PURE__ */ jsx("li", { children: warning }, warning)) })
1142
+ ] }),
1143
+ status === "PROPOSED" && (onApply || onReject) && /* @__PURE__ */ jsxs("div", { className: "mt-4 flex flex-wrap items-center gap-2", children: [
1144
+ onApply && /* @__PURE__ */ jsx(
1145
+ Button$1,
1146
+ {
1147
+ size: "sm",
1148
+ onClick: onApply,
1149
+ disabled: isDeciding,
1150
+ "data-testid": "workflow-proposal-apply",
1151
+ children: proposal.baseVersion ? tr(
1152
+ "fluidgrids.ai.proposal.applyVersion",
1153
+ "Apply as a new draft version"
1154
+ ) : tr(
1155
+ "fluidgrids.ai.proposal.applyNew",
1156
+ "Create as a draft workflow"
1157
+ )
1158
+ }
1159
+ ),
1160
+ onReject && /* @__PURE__ */ jsx(
1161
+ Button$1,
1162
+ {
1163
+ size: "sm",
1164
+ variant: "outline",
1165
+ onClick: onReject,
1166
+ disabled: isDeciding,
1167
+ children: tr("fluidgrids.ai.proposal.reject", "Reject")
1168
+ }
1169
+ ),
1170
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-text-secondary", children: tr(
1171
+ "fluidgrids.ai.proposal.neverActivates",
1172
+ "Nothing goes live: the result is a draft you can review, run as a test and activate yourself."
1173
+ ) })
1174
+ ] }),
1175
+ status === "APPLIED" && proposal.appliedWorkflowKey && proposal.appliedVersion && /* @__PURE__ */ jsxs("div", { className: "mt-4 space-y-1", children: [
1176
+ onOpenWorkflow && /* @__PURE__ */ jsx(
1177
+ Button$1,
1178
+ {
1179
+ size: "sm",
1180
+ variant: "outline",
1181
+ "data-testid": "workflow-proposal-open",
1182
+ onClick: () => onOpenWorkflow(
1183
+ proposal.appliedWorkflowKey ?? "",
1184
+ proposal.appliedVersion ?? ""
1185
+ ),
1186
+ children: tr("fluidgrids.ai.proposal.open", "Open draft {{version}}", {
1187
+ version: proposal.appliedVersion
1188
+ })
1189
+ }
1190
+ ),
1191
+ proposal.restoreHint && /* @__PURE__ */ jsx("p", { className: "text-xs text-text-secondary", children: proposal.restoreHint })
1192
+ ] })
1193
+ ]
1194
+ }
1195
+ );
1196
+ }
1197
+ function RunAiAssistPanel({
1198
+ runId,
1199
+ status,
1200
+ canPropose,
1201
+ canApply,
1202
+ onOpenWorkflow
1203
+ }) {
1204
+ const tr = useTr();
1205
+ const [explanation, setExplanation] = useState(null);
1206
+ const [explaining, setExplaining] = useState(false);
1207
+ const [explainError, setExplainError] = useState(null);
1208
+ const proposal = useWorkflowProposal();
1209
+ const explain = useCallback(async () => {
1210
+ setExplaining(true);
1211
+ setExplainError(null);
1212
+ try {
1213
+ const result = throwIfResultError(
1214
+ await getFluidGridsClient().query({
1215
+ query: ExplainRunDocument,
1216
+ variables: { runId },
1217
+ fetchPolicy: "network-only"
1218
+ })
1219
+ );
1220
+ setExplanation(result.data?.explainRun ?? null);
1221
+ } catch (error) {
1222
+ setExplainError(error instanceof Error ? error.message : String(error));
1223
+ } finally {
1224
+ setExplaining(false);
1225
+ }
1226
+ }, [runId]);
1227
+ const proposeFix = useCallback(() => {
1228
+ void proposal.start({ kind: "runFix", runId });
1229
+ }, [proposal, runId]);
1230
+ const apply = useCallback(async () => {
1231
+ const applied = await proposal.apply();
1232
+ const appliedStatus = applied ? String(applied.status) : void 0;
1233
+ if (appliedStatus === "APPLIED") {
1234
+ toast.success(
1235
+ tr(
1236
+ "fluidgrids.ai.runAssist.appliedToast",
1237
+ "Fix saved as draft {{version}}",
1238
+ {
1239
+ version: applied?.appliedVersion ?? ""
1240
+ }
1241
+ )
1242
+ );
1243
+ } else if (appliedStatus === "STALE") {
1244
+ toast.warning(
1245
+ tr(
1246
+ "fluidgrids.ai.runAssist.staleToast",
1247
+ "The workflow changed since this run. Propose the fix again."
1248
+ )
1249
+ );
1250
+ }
1251
+ }, [proposal, tr]);
1252
+ if (status !== "FAILED" && status !== "TERMINATED") return null;
1253
+ return /* @__PURE__ */ jsxs(
1254
+ "section",
1255
+ {
1256
+ className: "mb-6 rounded-lg border border-border-secondary bg-background-primary p-4",
1257
+ "data-testid": "run-ai-assist",
1258
+ children: [
1259
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
1260
+ /* @__PURE__ */ jsx(
1261
+ Icon,
1262
+ {
1263
+ icon: "mdi:robot-outline",
1264
+ width: 18,
1265
+ className: "text-text-secondary"
1266
+ }
1267
+ ),
1268
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-text-primary", children: tr("fluidgrids.ai.runAssist.title", "AI assistant") }),
1269
+ /* @__PURE__ */ jsxs("div", { className: "ml-auto flex gap-2", children: [
1270
+ /* @__PURE__ */ jsx(
1271
+ Button$1,
1272
+ {
1273
+ size: "sm",
1274
+ variant: "outline",
1275
+ onClick: () => void explain(),
1276
+ disabled: explaining,
1277
+ "data-testid": "run-ai-explain",
1278
+ children: explaining ? tr("fluidgrids.ai.runAssist.explaining", "Explaining\u2026") : tr("fluidgrids.ai.runAssist.explain", "Explain failure")
1279
+ }
1280
+ ),
1281
+ canPropose && /* @__PURE__ */ jsx(
1282
+ Button$1,
1283
+ {
1284
+ size: "sm",
1285
+ onClick: proposeFix,
1286
+ disabled: proposal.isWorking,
1287
+ "data-testid": "run-ai-propose-fix",
1288
+ children: proposal.isWorking ? tr("fluidgrids.ai.runAssist.proposing", "Proposing\u2026") : tr("fluidgrids.ai.runAssist.proposeFix", "Propose a fix")
1289
+ }
1290
+ )
1291
+ ] })
1292
+ ] }),
1293
+ explainError && /* @__PURE__ */ jsx("p", { className: "mt-3 text-sm text-[var(--color-status-error-text)]", children: explainError }),
1294
+ explanation && /* @__PURE__ */ jsxs("div", { className: "mt-3 space-y-2", "data-testid": "run-ai-explanation", children: [
1295
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-text-primary", children: explanation.cause }),
1296
+ explanation.evidence.length > 0 && /* @__PURE__ */ jsx("pre", { className: "max-h-40 overflow-auto whitespace-pre-wrap rounded bg-background-secondary p-2 text-xs text-text-secondary", children: explanation.evidence.join("\n") }),
1297
+ explanation.suggestions.length > 0 && /* @__PURE__ */ jsx("ul", { className: "list-disc space-y-0.5 pl-5 text-sm text-text-primary", children: explanation.suggestions.map((suggestion) => /* @__PURE__ */ jsx("li", { children: suggestion }, suggestion)) })
1298
+ ] }),
1299
+ proposal.error && /* @__PURE__ */ jsx("p", { className: "mt-3 text-sm text-[var(--color-status-error-text)]", children: proposal.error }),
1300
+ proposal.timedOut && /* @__PURE__ */ jsx("p", { className: "mt-3 text-sm text-text-secondary", children: tr(
1301
+ "fluidgrids.ai.runAssist.timedOut",
1302
+ "The fix is taking longer than usual. Check back in a minute."
1303
+ ) }),
1304
+ proposal.proposal && /* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsx(
1305
+ WorkflowProposalCard,
1306
+ {
1307
+ proposal: proposal.proposal,
1308
+ isDeciding: proposal.isDeciding,
1309
+ onOpenWorkflow,
1310
+ ...canApply ? { onApply: () => void apply() } : {},
1311
+ onReject: () => void proposal.reject(),
1312
+ onCancel: () => void proposal.cancel()
1313
+ }
1314
+ ) })
1315
+ ]
1316
+ }
1317
+ );
1318
+ }
803
1319
  function ExecutionDetailPage() {
804
1320
  const tr = useTr();
805
1321
  const { runId } = useParams();
@@ -813,6 +1329,12 @@ function ExecutionDetailPage() {
813
1329
  const { allowed: canTagRun } = useFluidGridsPermission(
814
1330
  FLUIDGRIDS_PERMISSIONS.WORKFLOW_UPDATE
815
1331
  );
1332
+ const { allowed: canProposeFix } = useFluidGridsPermission(
1333
+ FLUIDGRIDS_PERMISSIONS.AI_WORKFLOW_CREATE
1334
+ );
1335
+ const { allowed: canApplyFix } = useFluidGridsPermission(
1336
+ FLUIDGRIDS_PERMISSIONS.WORKFLOW_CREATE
1337
+ );
816
1338
  const [minLogLevel, setMinLogLevel] = useState(null);
817
1339
  const { execution, loading, error, refetch } = useExecution(runId);
818
1340
  const runCanChange = execution?.status === "RUNNING" || execution?.status === "PENDING" || execution?.status === "WAITING";
@@ -1394,6 +1916,18 @@ function ExecutionDetailPage() {
1394
1916
  ] }),
1395
1917
  /* @__PURE__ */ jsx("p", { className: "text-[var(--color-status-error-text)]", children: execution.errorMessage })
1396
1918
  ] }),
1919
+ /* @__PURE__ */ jsx(
1920
+ RunAiAssistPanel,
1921
+ {
1922
+ runId: execution.id,
1923
+ status: execution.status,
1924
+ canPropose: canProposeFix,
1925
+ canApply: canApplyFix,
1926
+ onOpenWorkflow: (workflowKey, version) => navigate(
1927
+ `../workflow/${encodeURIComponent(workflowKey)}/${encodeURIComponent(version)}`
1928
+ )
1929
+ }
1930
+ ),
1397
1931
  /* @__PURE__ */ jsx(RunQueuedBanner, { status: execution.status, context: execution.context }),
1398
1932
  /* @__PURE__ */ jsx(RunDataPrunedNotice, { dataPrunedAt: execution.dataPrunedAt }),
1399
1933
  /* @__PURE__ */ jsx(RunHumanRequests, { requests: humanRequests }),
@@ -1,7 +1,7 @@
1
1
  import { REPLAY_WORKFLOW_RUN, RETRY_WORKFLOW_RUNS } from './chunk-5ERAJC2I.js';
2
- import { useFluidGridsAnalytics, throwIfResultError, emitRunStarted, openRunQuotaUpgradePrompt } from './chunk-H7PTIEUR.js';
2
+ import { useFluidGridsAnalytics, throwIfResultError, emitRunStarted, openRunQuotaUpgradePrompt } from './chunk-JK5GPFYR.js';
3
3
  import { useExecutionsStore } from './chunk-CY6KX4RG.js';
4
- import { ExecutionsListDocument, ExecutionDetailDocument, ExecutionStatsDocument, PauseExecutionDocument, ResumeExecutionDocument, CancelExecutionDocument, RetryNodeDocument, TriggerWorkflowDocument, TerminateExecutionDocument } from './chunk-TXQVCIVV.js';
4
+ import { ExecutionsListDocument, ExecutionDetailDocument, ExecutionStatsDocument, PauseExecutionDocument, ResumeExecutionDocument, CancelExecutionDocument, RetryNodeDocument, TriggerWorkflowDocument, TerminateExecutionDocument } from './chunk-M3Q6GJCR.js';
5
5
  import { useTr } from './chunk-UULVIT25.js';
6
6
  import { getFluidGridsClient } from './chunk-NTWG3CJX.js';
7
7
  import { useState, useRef, useCallback, useEffect, useMemo } from 'react';
@@ -1,5 +1,5 @@
1
1
  import { LOG_STREAM_DESTINATIONS, CREATE_LOG_STREAM_DESTINATION, UPDATE_LOG_STREAM_DESTINATION, DELETE_LOG_STREAM_DESTINATION, TEST_LOG_STREAM_DESTINATION, REPLAY_WORKFLOW_RUN, RETRY_WORKFLOW_RUNS, OBSERVABILITY_DASHBOARD_SUMMARY, OBSERVABILITY_FAILED_RUNS, OBSERVABILITY_DATA_SINKS, OBSERVABILITY_DATA_SINK_DATA } from './chunk-5ERAJC2I.js';
2
- import { throwIfResultError, describeOperationError, useFluidGridsAnalytics, emitRunStarted, openRunQuotaUpgradePrompt } from './chunk-H7PTIEUR.js';
2
+ import { throwIfResultError, describeOperationError, useFluidGridsAnalytics, emitRunStarted, openRunQuotaUpgradePrompt } from './chunk-JK5GPFYR.js';
3
3
  import { useTr } from './chunk-UULVIT25.js';
4
4
  import { getFluidGridsClient } from './chunk-NTWG3CJX.js';
5
5
  import { useOptionalFeatureFlag } from '@burdenoff/fe-libs/shared/feature-flags';
@@ -2,7 +2,7 @@ import { cn as cn$1 } from './chunk-JL7CASN5.js';
2
2
  import { getGlobalStoreClient, createGlobalStoreClient, setGlobalStoreClient, resetGlobalStoreClient } from './chunk-PXIFUMLA.js';
3
3
  import { getFluidGridsConfig, validateTrustedOrigin, resetFluidGridsExecutionState, setFluidGridsConfig, resetFluidGridsConfig } from './chunk-CY6KX4RG.js';
4
4
  import { GetMyNodeInstallationsDocument } from './chunk-WBH2XEWO.js';
5
- import { fetchAllNodeEndpointConfigs, NodeManifestDocument, UpdateWorkflowDefinitionDocument, CreateWorkflowDefinitionDocument, CreateWorkflowVersionDocument, ExecuteStepDocument, TriggerWorkflowDocument, RunFromNodeDocument, ExecutionDetailDocument, WorkflowDefinitionDocument, WorkflowRunDocument } from './chunk-TXQVCIVV.js';
5
+ import { fetchAllNodeEndpointConfigs, NodeManifestDocument, UpdateWorkflowDefinitionDocument, CreateWorkflowDefinitionDocument, CreateWorkflowVersionDocument, ExecuteStepDocument, TriggerWorkflowDocument, RunFromNodeDocument, ExecutionDetailDocument, WorkflowDefinitionDocument, WorkflowRunDocument } from './chunk-M3Q6GJCR.js';
6
6
  import { useTr, ENGLISH_FALLBACK_TRANSLATOR, FluidGridsTranslationProvider } from './chunk-UULVIT25.js';
7
7
  import { waitForFluidGridsClient, getFluidGridsClient, getFluidGridsClientSafe, createFluidGridsClient, setFluidGridsClient, resetFluidGridsClient } from './chunk-NTWG3CJX.js';
8
8
  import { CombinedGraphQLErrors, ServerError } from '@apollo/client/errors';