@burdenoff/fluidgrids-fe-sdk 2026.1112.3 → 2026.1112.5

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 (78) hide show
  1. package/dist/{AiStudioPage-C-jio5Bz.js → AiStudioPage-BNmXx6LB.js} +10 -10
  2. package/dist/{ApprovalInboxPage-D8vhnloO.js → ApprovalInboxPage-QAJim81n.js} +3 -2
  3. package/dist/{DataSinkDashboardPage-CRBsNjoS.js → DataSinkDashboardPage-kyzcjpzL.js} +4 -4
  4. package/dist/{DataTablesPage-DoVBkb_T.js → DataTablesPage-Dm-dzVPD.js} +4 -4
  5. package/dist/{EntityTagPicker-Dk3CufFt.js → EntityTagPicker-dxfzv8aO.js} +1 -1
  6. package/dist/{ExecutionDetailPage-Cw4zyaIG.js → ExecutionDetailPage-B9eEep77.js} +977 -509
  7. package/dist/{ExecutionsListPage-BgeqwVr2.js → ExecutionsListPage-DuuPSVEj.js} +831 -146
  8. package/dist/{FailureTriagePanel-BzD4UJU5.js → FailureTriagePanel-D4lJ4zqQ.js} +1 -1
  9. package/dist/{FluidGridsProvider-BFfTxl9m.js → FluidGridsProvider-B1hgCbxG.js} +7 -7
  10. package/dist/{HistoryPage-BdijEOHp.js → HistoryPage-C7NwkVNC.js} +202 -36
  11. package/dist/{HumanRequestCard-mOhr-Ic9.js → HumanRequestCard-lbWDpWd7.js} +4 -216
  12. package/dist/{InsightsPage-2jnYlozA.js → InsightsPage-D9B_qW8e.js} +10 -10
  13. package/dist/{MissingNodesInstallModal-C0wPwR0Q.js → MissingNodesInstallModal-BkkgeFeN.js} +1 -1
  14. package/dist/NavigationV2Provider-DCDt35eG.js +17 -0
  15. package/dist/{NodesPage-Dhg5Q2R5.js → NodesPage-BEeUbIts.js} +4 -4
  16. package/dist/{QuotaExhaustedDialog-BKZ9moP8.js → QuotaExhaustedDialog-Bj7AG_0y.js} +1 -1
  17. package/dist/{RunnersPage-CLCOCclc.js → RunnersPage-D9Bgod_d.js} +4 -4
  18. package/dist/{SettingsPage-BaM98QU_.js → SettingsPage-k21044-Z.js} +16 -16
  19. package/dist/{TagFilterSelect-VC6JMK3F.js → TagFilterSelect-CqhxcDjh.js} +1 -1
  20. package/dist/{TaskQueuesPage-C3R5ZoKs.js → TaskQueuesPage-B86p51gH.js} +2 -2
  21. package/dist/{TemplateCard-DYDVNYo6.js → TemplateCard-DanOe65C.js} +3 -3
  22. package/dist/{TemplateConfigurationModal-BzdkGWim.js → TemplateConfigurationModal-CeMpPEhT.js} +3 -3
  23. package/dist/{TemplateDetailPanel-s4EqQhra.js → TemplateDetailPanel-DnoA6mC3.js} +2 -2
  24. package/dist/{TemplatePreview-B_-zS_Mm.js → TemplatePreview-CBwvi0ga.js} +4 -4
  25. package/dist/{TemplatesPage-OIMd6cIl.js → TemplatesPage-C7f2xq12.js} +14 -14
  26. package/dist/{VariablesPage-67WR_lVE.js → VariablesPage-BtoLjmUA.js} +1 -1
  27. package/dist/{VersionDiffDialog-Cp2doV3f.js → VersionDiffDialog-pLpG_I1r.js} +4 -4
  28. package/dist/{WorkflowBuilder-B2tIOPi0.js → WorkflowBuilder-DDgWgnwH.js} +7 -7
  29. package/dist/{WorkflowBuilder-DD4UIAjC.js → WorkflowBuilder-DmYS5JEY.js} +12 -12
  30. package/dist/{WorkflowProposalCard-U31u1qcw.js → WorkflowProposalCard-BjYAZH3q.js} +1 -1
  31. package/dist/{WorkflowSearchRedirect-BhXzWDj7.js → WorkflowSearchRedirect-iPQBL6WO.js} +1 -1
  32. package/dist/{WorkflowShareDialog-Dv46nAyh.js → WorkflowShareDialog-DB-qJLB2.js} +1 -1
  33. package/dist/{WorkflowsList-BV-2J_gG.js → WorkflowsList-rOxlI49U.js} +13 -13
  34. package/dist/{agentTools-EVdEThES.js → agentTools-B4Gjf8R7.js} +4 -4
  35. package/dist/{api-DwaHAJtA.js → api-Dvitj1aT.js} +1 -1
  36. package/dist/{api-C8WmUejq.js → api-JpNuowX4.js} +1 -1
  37. package/dist/{canvasNoteTools-C5Vf0pxu.js → canvasNoteTools-Bve4Fo7R.js} +3 -3
  38. package/dist/{code-editor-field-o7qp9eTq.js → code-editor-field-yJZEDJBi.js} +2 -2
  39. package/dist/{constants-B2lhWNoN.js → constants-dcxg6zG6.js} +2 -2
  40. package/dist/{definition-canvas-F8DGty5m.js → definition-canvas-Bshn_dSP.js} +1 -1
  41. package/dist/executions.js +2 -2
  42. package/dist/{flow-builder-Dn4PCWuf.js → flow-builder-BPe5YNF6.js} +48 -48
  43. package/dist/{graphql-operation-types-BdU65FNm.js → graphql-operation-types-z2oXfQvM.js} +10 -1
  44. package/dist/history.js +1 -1
  45. package/dist/{index-DbTgk7dK.d.ts → index-u-kM0seP.d.ts} +3 -2
  46. package/dist/index.d.ts +1 -1
  47. package/dist/index.js +43 -41
  48. package/dist/navigation-v2.js +2 -1
  49. package/dist/{node-detail-view-kWiwJEKr.js → node-detail-view-oAotM2EI.js} +18 -18
  50. package/dist/{node-editor-tab-body-C71Y0fRJ.js → node-editor-tab-body-DIcmar84.js} +6 -6
  51. package/dist/{node-endpoint-configs-BrzQMy10.js → node-endpoint-configs-typvqw2c.js} +1 -1
  52. package/dist/{node-operation-selection-icAhaNza.js → node-operation-selection-DAbS6jSY.js} +4 -4
  53. package/dist/nodes.js +1 -1
  54. package/dist/observability.d.ts +1 -1
  55. package/dist/observability.js +2 -2
  56. package/dist/{popover-v7g8TWmm.js → popover-DOeSGQOG.js} +3 -3
  57. package/dist/{quick-config-panel-C84vsDYD.js → quick-config-panel-Pm5nkZVr.js} +6 -6
  58. package/dist/{schedule-panel-BrNKy9DY.js → schedule-panel-Dizqz9eA.js} +29 -29
  59. package/dist/settings.js +1 -1
  60. package/dist/{sidebar-node-editor-D-2lPHwq.js → sidebar-node-editor-BNub2vUq.js} +4 -4
  61. package/dist/templates.js +2 -2
  62. package/dist/{useAgentDefinitions-DSU8Eek4.js → useAgentDefinitions-Uy6nhpKN.js} +1 -1
  63. package/dist/{useDurableParity-fFmfa97r.js → useDurableParity-Cb9nOdE8.js} +191 -4
  64. package/dist/{useEntityTags-C8rPBi6W.js → useEntityTags-D5C0tdLs.js} +1 -1
  65. package/dist/{useExecutions-DNGW4yxG.js → useExecutions-DJXtbA15.js} +59 -48
  66. package/dist/useHumanRequests-ZiR0sSzS.js +218 -0
  67. package/dist/{NavigationV2Provider-D4kj0Gjt.js → useNavigationV2-C2WeqN7d.js} +1 -14
  68. package/dist/{useObservability-u-MZCWUV.js → useObservability-Dfgh4yjv.js} +1 -1
  69. package/dist/{useRunHistory-x9bVUB9T.js → useRunHistory-BZQ3t68Q.js} +2 -2
  70. package/dist/{useWorkflowCodeExport-CV3SZeKm.js → useWorkflowCodeExport-D0zapXXm.js} +2 -2
  71. package/dist/{useWorkflows-CVb5zXKt.js → useWorkflows-CP-d3ygY.js} +2 -2
  72. package/dist/workflow-builder.js +5 -5
  73. package/dist/workflow-host.js +1 -1
  74. package/dist/workflow-pages.d.ts +1 -1
  75. package/dist/workflow-pages.js +19 -19
  76. package/dist/workflow-preview.js +4 -4
  77. package/dist/workflows-list.js +1 -1
  78. package/package.json +1 -1
@@ -1,34 +1,36 @@
1
1
  "use client";
2
2
  import { t as __exportAll } from "./rolldown-runtime-hDbaFkMy.js";
3
- import { i as useRunPending, n as useDurableParityRunFields, o as ExecutionStatusBadge, r as useRunMetadata, s as NodeStatusBadge, t as setRunVersioningOverride } from "./useDurableParity-fFmfa97r.js";
3
+ import { c as TechnicalDetails, d as errorAdvice, f as needsReconnect, i as useRunPending, m as NodeStatusBadge, n as useDurableParityRunFields, o as ConfirmStopRunDialog, p as ExecutionStatusBadge, r as useRunMetadata, s as RunActionsMenu, t as setRunVersioningOverride } from "./useDurableParity-Cb9nOdE8.js";
4
4
  import { n as formatRunVersion, r as nodeDisplayTitle, t as buildNodeDisplayMap } from "./run-display-BycKYSU0.js";
5
5
  import { r as useTr } from "./TranslationBridge-Da-TPgQm.js";
6
6
  import { s as isAgentNodeType } from "./agent-form-Bfvm32j1.js";
7
- import { An as runSignalTargets, Dn as durableInterfaceOf, I as describeRunError, L as describeRunStep, Tn as canSignalRun, dn as runDurationMs, ln as formatElapsedMs, un as nodeStateDurationMs } from "./FluidGridsProvider-BFfTxl9m.js";
7
+ import { An as runSignalTargets, Dn as durableInterfaceOf, I as describeRunError, L as describeRunStep, Tn as canSignalRun, dn as runDurationMs, ln as formatElapsedMs, un as nodeStateDurationMs } from "./FluidGridsProvider-B1hgCbxG.js";
8
8
  import { n as useFgAskable, r as useFgAskableSelection } from "./askAboutBridge-BV8m95kk.js";
9
- import { f as callbackTone, g as shortRunId, h as runChainLinks, m as parseMetadataDefinition, n as useExecution, p as overrideRequestOf, r as useExecutionActions, t as useAutoRefresh } from "./useExecutions-DNGW4yxG.js";
9
+ import { n as useNavigationV2 } from "./useNavigationV2-C2WeqN7d.js";
10
+ import { _ as shortRunId, g as runChainLinks, h as parseMetadataDefinition, m as overrideRequestOf, n as useExecution, p as callbackTone, r as useExecutionActions, s as buildRetryTriggerInput, t as useAutoRefresh } from "./useExecutions-DJXtbA15.js";
10
11
  import { _ as throwIfResultError, m as describeOperationError, n as getFluidGridsClient, r as getFluidGridsClientSafe, x as getFluidGridsConfig } from "./client-C9XSzSXr.js";
11
- import { Cn as RunLogsDocument, Zr as WorkflowRunHierarchyDocument, ft as ExplainRunDocument, o as AgentStepUpdatedDocument, pn as RunAiUsageDocument, s as AgentTraceDocument, xn as RunLogAppendedDocument } from "./graphql-operation-types-BdU65FNm.js";
12
- import { i as SdkPermissionButton, s as useFluidGridsPermission, t as FLUIDGRIDS_PERMISSIONS } from "./constants-B2lhWNoN.js";
13
- import { t as EntityTagPicker } from "./EntityTagPicker-Dk3CufFt.js";
14
- import { n as channelLabel, o as useRunHumanRequests, r as ApprovalApprovers, s as useRecipientLabel } from "./HumanRequestCard-mOhr-Ic9.js";
12
+ import { Cn as RunLogsDocument, Zr as WorkflowRunHierarchyDocument, ft as ExplainRunDocument, o as AgentStepUpdatedDocument, pn as RunAiUsageDocument, s as AgentTraceDocument, xn as RunLogAppendedDocument } from "./graphql-operation-types-z2oXfQvM.js";
13
+ import { i as SdkPermissionButton, s as useFluidGridsPermission, t as FLUIDGRIDS_PERMISSIONS } from "./constants-dcxg6zG6.js";
14
+ import { t as EntityTagPicker } from "./EntityTagPicker-dxfzv8aO.js";
15
+ import { i as useRecipientLabel, n as channelLabel, r as ApprovalApprovers } from "./HumanRequestCard-lbWDpWd7.js";
16
+ import { r as useRunHumanRequests } from "./useHumanRequests-ZiR0sSzS.js";
15
17
  import { a as encodedPayloadBytes, f as EffectBadge, h as useRunPolicyDecisions, i as decodePayloads, l as formatBudgetUsd, n as checkCodecServerUrl, o as isEncodedPayload, r as containsEncodedPayload, t as CodecServerError } from "./codec-client-CJ95Rmqo.js";
16
- import { n as useWorkflowProposal, o as canSaveRunAsTestCase, r as saveRunAsTestCase, t as WorkflowProposalCard } from "./WorkflowProposalCard-U31u1qcw.js";
18
+ import { n as useWorkflowProposal, o as canSaveRunAsTestCase, r as saveRunAsTestCase, t as WorkflowProposalCard } from "./WorkflowProposalCard-BjYAZH3q.js";
17
19
  import { t as AiCreditBalanceSlot } from "./aiCreditsBridge-BuE92tKo.js";
18
- import { a as fetchRunFailureClusters, i as dismissRunFailureCluster, r as OPEN_STATUSES, t as BUCKET_LABELS } from "./FailureTriagePanel-BzD4UJU5.js";
19
- import { a as useDurableRunFields, i as useDeterminismStatus, n as SignalRunDialog, o as useRunStackTrace, r as terminateDurableRun, s as RunHistoryView, t as useRunHistory } from "./useRunHistory-x9bVUB9T.js";
20
+ import { a as fetchRunFailureClusters, i as dismissRunFailureCluster, r as OPEN_STATUSES, t as BUCKET_LABELS } from "./FailureTriagePanel-D4lJ4zqQ.js";
21
+ import { a as useDurableRunFields, i as useDeterminismStatus, n as SignalRunDialog, o as useRunStackTrace, r as terminateDurableRun, s as RunHistoryView, t as useRunHistory } from "./useRunHistory-BZQ3t68Q.js";
20
22
  import { $P as VersioningBehavior, FI as WorkflowUpdateWaitFor, L as AgentStepKind, _I as WorkflowQueryRejectCondition, jd as ErrorType } from "./graphql-types-v6CTGwA0.js";
21
23
  import { a as formatTokens, d as formatMicroUsd, o as modelLabel, r as aiCost } from "./ai-usage-bmUn-gJP.js";
22
24
  import { c as useDurableParityEnabled, f as usePhaseCFlags, m as useWorkflowTestsEnabled, s as useDurableFunctionNodesEnabled } from "./feature-flags-DM5myWaD.js";
23
- import { A as revokeSignalToken, B as updateActivityOptions, D as resetActivities, E as queryRun, N as sendRunUpdate, O as resetRun, R as unpauseActivities, S as fetchSignalTokens, V as upsertRunSearchAttributes, _ as fetchPauseRules, b as fetchRunUpdate, d as failActivityById, g as fetchCodecServerSettings, i as completeActivityById, k as resetRunToLastCleanTask, s as createSignalToken, v as fetchRunControlFields, w as pauseActivities, y as fetchRunHistoryArchive } from "./api-DwaHAJtA.js";
24
- import { a as fetchTaskQueueStats } from "./api-C8WmUejq.js";
25
+ import { A as revokeSignalToken, B as updateActivityOptions, D as resetActivities, E as queryRun, N as sendRunUpdate, O as resetRun, R as unpauseActivities, S as fetchSignalTokens, V as upsertRunSearchAttributes, _ as fetchPauseRules, b as fetchRunUpdate, d as failActivityById, g as fetchCodecServerSettings, i as completeActivityById, k as resetRunToLastCleanTask, s as createSignalToken, v as fetchRunControlFields, w as pauseActivities, y as fetchRunHistoryArchive } from "./api-Dvitj1aT.js";
26
+ import { a as fetchTaskQueueStats } from "./api-JpNuowX4.js";
25
27
  import { a as RUN_CONTROL_DIALOG_STYLE, c as parseJsonInput, i as NativeSelect, l as parseJsonObjectInput, n as JsonBlock, o as StatusPill, r as JsonField, s as newRunControlId, t as FieldError, u as useResource } from "./shared-CMfn30xT.js";
26
- import { v as useWorkflowVersions } from "./useWorkflows-CVb5zXKt.js";
28
+ import { v as useWorkflowVersions } from "./useWorkflows-CP-d3ygY.js";
27
29
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
28
30
  import { Link, useNavigate, useParams, useSearchParams } from "react-router";
29
31
  import { Icon } from "@iconify/react";
30
32
  import { cn } from "@burdenoff/fe-libs/utils";
31
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
33
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
32
34
  import { toast } from "sonner";
33
35
  import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, Button, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, Input, Label, Switch, Textarea } from "@burdenoff/fe-libs/ui";
34
36
  import { Button as Button$1 } from "@burdenoff/fe-libs/button";
@@ -36,6 +38,127 @@ import { FlaskConical } from "lucide-react";
36
38
  import { buildActorFallback, useActorProfiles } from "@burdenoff/fe-libs/shared";
37
39
  import ReactMarkdown from "react-markdown";
38
40
 
41
+ // src/modules/human-response/utils/answer-rows.ts
42
+ /**
43
+ * What a person answered to a free-text or form ask, as label/value rows — the answer that drove
44
+ * the run, kept visible for audit. Approvals need none: the outcome line already says which.
45
+ */
46
+ function answerRows(request, tr) {
47
+ if (request.status !== "RESPONDED" || request.responseType === "APPROVAL") return [];
48
+ const data = request.response?.data;
49
+ if (!data || typeof data !== "object" || Array.isArray(data)) return [];
50
+ const values = data;
51
+ const show = (value) => {
52
+ if (Array.isArray(value)) return value.map(String).join(", ");
53
+ if (value === null || value === void 0) return "";
54
+ return String(value);
55
+ };
56
+ if (request.responseType === "FREE_TEXT") return [{
57
+ key: "text",
58
+ label: tr("fluidgrids.humanResponse.answerLabel", "Response"),
59
+ value: show(values.text)
60
+ }];
61
+ return request.fields.filter((field) => values[field.name] !== void 0).map((field) => ({
62
+ key: field.name,
63
+ label: field.label || field.name,
64
+ value: show(values[field.name])
65
+ }));
66
+ }
67
+
68
+ // src/modules/human-response/components/RunHumanRequests.tsx
69
+ /**
70
+ * Run detail: the asks a "Send and Wait for Response" node made (BOFF-7651) — who was asked, by
71
+ * which channel, until when, and what came back. An approval lists its approvers with each one's
72
+ * answer and comment (BOFF-9956). Renders nothing for runs that asked nobody.
73
+ */
74
+ function outcome(request, tr, plain) {
75
+ const when = request.respondedAt ? new Date(request.respondedAt).toLocaleString() : "";
76
+ switch (request.status) {
77
+ case "RESPONDED": return request.decision === "declined" ? tr("fluidgrids.humanResponse.outcomeDeclined", "Declined {{when}}", { when }) : request.decision === "approved" ? tr("fluidgrids.humanResponse.outcomeApproved", "Approved {{when}}", { when }) : tr("fluidgrids.humanResponse.outcomeResponded", "Answered {{when}}", { when });
78
+ case "TIMED_OUT":
79
+ if (plain) return tr("fluidgrids.humanResponse.outcomeTimedOutPlain", "No one answered in time");
80
+ return tr("fluidgrids.humanResponse.outcomeTimedOut", "Nobody answered in time — continued down Timed out");
81
+ case "CANCELLED":
82
+ if (plain) return tr("fluidgrids.humanResponse.outcomeCancelledPlain", "Withdrawn because the step was run again or the run stopped");
83
+ return tr("fluidgrids.humanResponse.outcomeCancelled", "Withdrawn (the node was retried or the run stopped)");
84
+ default: return request.expiresAt ? tr("fluidgrids.humanResponse.waitingUntil", "Waiting until {{time}}", { time: new Date(request.expiresAt).toLocaleString() }) : tr("fluidgrids.humanResponse.waiting", "Waiting for a response");
85
+ }
86
+ }
87
+ function RunHumanRequests({ requests }) {
88
+ const tr = useTr();
89
+ const v2 = useNavigationV2();
90
+ if (requests.length === 0) return null;
91
+ const pending = requests.some((request) => request.status === "PENDING");
92
+ return /* @__PURE__ */ jsxs("div", {
93
+ className: "mb-6 space-y-2 rounded-lg border border-border-secondary bg-background-secondary p-4",
94
+ "data-testid": "run-human-requests",
95
+ children: [/* @__PURE__ */ jsxs("div", {
96
+ className: "flex items-center gap-2 text-sm font-medium text-text-primary",
97
+ children: [
98
+ /* @__PURE__ */ jsx(Icon, {
99
+ icon: "mdi:account-clock-outline",
100
+ width: 18
101
+ }),
102
+ pending ? tr("fluidgrids.humanResponse.runHeading", "Waiting on people") : tr("fluidgrids.humanResponse.runHeadingDone", "People asked"),
103
+ v2 && pending && /* @__PURE__ */ jsx(Link, {
104
+ to: "../approvals",
105
+ "data-testid": "run-answer-in-approvals",
106
+ className: "ml-auto text-sm font-medium text-text-link underline-offset-2 hover:text-text-linkHover hover:underline",
107
+ children: tr("fluidgrids.humanResponse.answerInApprovals", "Answer in Approvals")
108
+ })
109
+ ]
110
+ }), requests.map((request) => /* @__PURE__ */ jsx(RunHumanRequestRow, {
111
+ request,
112
+ plain: v2
113
+ }, request.id))]
114
+ });
115
+ }
116
+ function RunHumanRequestRow({ request, plain }) {
117
+ const tr = useTr();
118
+ const answer = answerRows(request, tr);
119
+ const recipient = useRecipientLabel(request, tr("fluidgrids.humanResponse.anyMember", "any workspace member"));
120
+ return /* @__PURE__ */ jsxs("div", {
121
+ className: "flex flex-col gap-0.5 text-sm sm:flex-row sm:flex-wrap sm:items-baseline sm:gap-x-2",
122
+ "data-testid": "run-human-request",
123
+ "data-status": request.status,
124
+ children: [
125
+ /* @__PURE__ */ jsx("span", {
126
+ className: "font-medium text-text-primary",
127
+ children: request.title
128
+ }),
129
+ /* @__PURE__ */ jsxs("span", {
130
+ className: "text-text-secondary",
131
+ children: [
132
+ tr("fluidgrids.humanResponse.askedVia", "Asked {{recipient}} via {{channel}}", {
133
+ recipient,
134
+ channel: channelLabel(request.channel, tr)
135
+ }),
136
+ " · ",
137
+ outcome(request, tr, plain)
138
+ ]
139
+ }),
140
+ (request.approvals?.length ?? 0) > 0 && /* @__PURE__ */ jsx("div", {
141
+ className: "mt-1 w-full sm:mt-0",
142
+ children: /* @__PURE__ */ jsx(ApprovalApprovers, { request })
143
+ }),
144
+ answer.length > 0 && /* @__PURE__ */ jsx("dl", {
145
+ className: "mt-1 w-full space-y-0.5 text-xs sm:mt-0",
146
+ "data-testid": "run-human-answer",
147
+ children: answer.map((row) => /* @__PURE__ */ jsxs("div", {
148
+ className: "flex gap-2",
149
+ children: [/* @__PURE__ */ jsxs("dt", {
150
+ className: "shrink-0 font-medium text-text-secondary",
151
+ children: [row.label, ":"]
152
+ }), /* @__PURE__ */ jsx("dd", {
153
+ className: "whitespace-pre-wrap break-words text-text-primary",
154
+ children: row.value
155
+ })]
156
+ }, row.key))
157
+ })
158
+ ]
159
+ });
160
+ }
161
+
39
162
  // src/modules/executions/utils/agent-trace.ts
40
163
  /**
41
164
  * The run page's Agent tab (BOFF-9937): what an AI Agent node did, read from
@@ -290,15 +413,12 @@ function orderNodesByExecution(nodeStates, triggerNodeId) {
290
413
  }
291
414
  function NodeTimeline({ nodeStates, nodeDisplay, loading, triggerNodeId, onRetryNode, onViewChildRun, onViewAgentTrace, askRun }) {
292
415
  const tr = useTr();
293
- const [expandedNodes, setExpandedNodes] = useState(/* @__PURE__ */ new Set());
416
+ const v2 = useNavigationV2();
417
+ const [toggledNodes, setToggledNodes] = useState(/* @__PURE__ */ new Map());
294
418
  const [selectedTab, setSelectedTab] = useState("output");
295
- const toggleNode = (nodeId) => {
296
- setExpandedNodes((prev) => {
297
- const newSet = new Set(prev);
298
- if (newSet.has(nodeId)) newSet.delete(nodeId);
299
- else newSet.add(nodeId);
300
- return newSet;
301
- });
419
+ const isNodeOpen = (node) => toggledNodes.get(node.id) ?? (v2 && node.status === "FAILED");
420
+ const toggleNode = (node) => {
421
+ setToggledNodes((prev) => new Map(prev).set(node.id, !isNodeOpen(node)));
302
422
  };
303
423
  const formatTime = (dateStr) => {
304
424
  if (!dateStr) return "-";
@@ -321,6 +441,67 @@ function NodeTimeline({ nodeStates, nodeDisplay, loading, triggerNodeId, onRetry
321
441
  if (!metadata || typeof metadata !== "object") return false;
322
442
  return Object.keys(metadata).length > 0;
323
443
  };
444
+ /** The Output / Input toggle and the JSON it shows. */
445
+ const renderStepData = (node) => /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
446
+ className: "flex gap-2 mt-3 mb-3",
447
+ children: [/* @__PURE__ */ jsx("button", {
448
+ onClick: () => setSelectedTab("output"),
449
+ className: `px-3 py-1.5 text-sm rounded ${selectedTab === "output" ? "bg-action-primary-bg text-action-primary-text" : "bg-background-tertiary text-text-secondary"}`,
450
+ children: tr("fluidgrids.executions.nodeTimeline.outputTab", "Output")
451
+ }), /* @__PURE__ */ jsx("button", {
452
+ onClick: () => setSelectedTab("input"),
453
+ className: `px-3 py-1.5 text-sm rounded ${selectedTab === "input" ? "bg-action-primary-bg text-action-primary-text" : "bg-background-tertiary text-text-secondary"}`,
454
+ children: tr("fluidgrids.executions.nodeTimeline.inputTab", "Input")
455
+ })]
456
+ }), /* @__PURE__ */ jsx("div", {
457
+ className: "bg-background-primary rounded p-3 max-h-64 overflow-auto",
458
+ children: /* @__PURE__ */ jsx("pre", {
459
+ className: "text-xs text-text-primary whitespace-pre-wrap break-words font-mono",
460
+ children: selectedTab === "output" ? formatJsonData(node.output) : formatJsonData(node.inputArgs)
461
+ })
462
+ })] });
463
+ /** View sub-workflow run, View trace and Retry this step. */
464
+ const renderStepActions = (node) => /* @__PURE__ */ jsxs("div", {
465
+ className: "mt-3 flex flex-wrap justify-end gap-2",
466
+ children: [
467
+ node.childRunId && onViewChildRun && /* @__PURE__ */ jsxs("button", {
468
+ onClick: () => onViewChildRun(node.childRunId),
469
+ className: "px-3 py-1.5 text-sm bg-chart-12 text-action-primary-text rounded hover:bg-chart-12/90 flex items-center gap-1.5",
470
+ children: [/* @__PURE__ */ jsx(Icon, {
471
+ icon: "mdi:sitemap",
472
+ width: 16
473
+ }), tr("fluidgrids.executions.nodeTimeline.viewSubWorkflowRun", "View Sub-Workflow Run")]
474
+ }),
475
+ onViewAgentTrace && isAgentNodeType(node.nodeType) && /* @__PURE__ */ jsxs("button", {
476
+ type: "button",
477
+ onClick: () => onViewAgentTrace(node.nodeId),
478
+ className: "px-3 py-1.5 text-sm border border-border-secondary text-text-primary rounded hover:bg-background-tertiary flex items-center gap-1.5",
479
+ children: [/* @__PURE__ */ jsx(Icon, {
480
+ icon: "mdi:robot-outline",
481
+ width: 16
482
+ }), tr("fluidgrids.executions.nodeTimeline.viewAgentTrace", "View trace")]
483
+ }),
484
+ node.status === "FAILED" && onRetryNode && /* @__PURE__ */ jsxs("button", {
485
+ "data-agent": "confirm",
486
+ onClick: () => onRetryNode(node.nodeId),
487
+ className: "px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded hover:bg-action-primary-bgHover flex items-center gap-1.5",
488
+ children: [/* @__PURE__ */ jsx(Icon, {
489
+ icon: "mdi:refresh",
490
+ width: 16
491
+ }), tr("fluidgrids.executions.nodeTimeline.retryNodeButton", "Retry Node")]
492
+ })
493
+ ]
494
+ });
495
+ const renderStepMetadata = (node) => /* @__PURE__ */ jsx(Fragment$1, { children: hasMetadata(node.metadata) && /* @__PURE__ */ jsxs("details", {
496
+ className: "mt-3",
497
+ children: [/* @__PURE__ */ jsx("summary", {
498
+ className: "text-xs text-text-secondary cursor-pointer hover:text-text-primary",
499
+ children: tr("fluidgrids.executions.nodeTimeline.metadataSummary", "Metadata")
500
+ }), /* @__PURE__ */ jsx("pre", {
501
+ className: "mt-2 text-xs text-text-secondary bg-background-tertiary p-2 rounded overflow-auto max-h-32",
502
+ children: formatJsonData(node.metadata)
503
+ })]
504
+ }) });
324
505
  const sortedNodes = orderNodesByExecution(nodeStates, triggerNodeId);
325
506
  if (loading) return /* @__PURE__ */ jsx("div", {
326
507
  className: "space-y-4",
@@ -342,7 +523,7 @@ function NodeTimeline({ nodeStates, nodeDisplay, loading, triggerNodeId, onRetry
342
523
  children: [/* @__PURE__ */ jsx("div", { className: "absolute left-4 top-8 bottom-8 w-0.5 bg-border-secondary" }), /* @__PURE__ */ jsx("div", {
343
524
  className: "space-y-4",
344
525
  children: sortedNodes.map((node, index) => {
345
- const isExpanded = expandedNodes.has(node.id);
526
+ const isExpanded = isNodeOpen(node);
346
527
  const nodeError = node.error;
347
528
  const hasError = node.status === "FAILED" || Boolean(nodeError);
348
529
  const isRunning = node.status === "RUNNING";
@@ -378,155 +559,172 @@ function NodeTimeline({ nodeStates, nodeDisplay, loading, triggerNodeId, onRetry
378
559
  flex-1 rounded-lg border transition-all
379
560
  ${hasError ? "bg-[var(--color-status-error-bg-subtle)] border-[var(--color-status-error-border)]" : isRunning ? "bg-[var(--color-status-info-bg-subtle)] border-[var(--color-status-info-border)]" : "bg-background-secondary border-border-secondary"}
380
561
  `,
381
- children: [/* @__PURE__ */ jsxs("button", {
382
- onClick: () => toggleNode(node.id),
383
- className: "w-full px-4 py-3 flex items-center justify-between text-left",
384
- children: [/* @__PURE__ */ jsxs("div", {
385
- className: "flex items-center gap-3 min-w-0",
386
- children: [/* @__PURE__ */ jsx("div", {
387
- className: "flex-shrink-0",
388
- children: /* @__PURE__ */ jsx(NodeStatusBadge, {
389
- status: node.status,
390
- size: "sm",
391
- showLabel: false
392
- })
393
- }), /* @__PURE__ */ jsxs("div", {
394
- className: "min-w-0",
562
+ children: [
563
+ /* @__PURE__ */ jsxs("button", {
564
+ type: "button",
565
+ onClick: () => toggleNode(node),
566
+ "aria-expanded": isExpanded,
567
+ className: "w-full px-4 py-3 flex items-center justify-between text-left",
568
+ children: [/* @__PURE__ */ jsxs("div", {
569
+ className: "flex items-center gap-3 min-w-0",
395
570
  children: [/* @__PURE__ */ jsx("div", {
396
- className: "font-medium text-text-primary truncate",
397
- title: node.nodeId,
398
- children: nodeDisplayTitle(nodeDisplay?.[node.nodeId], node.nodeId)
571
+ className: "flex-shrink-0",
572
+ children: /* @__PURE__ */ jsx(NodeStatusBadge, {
573
+ status: node.status,
574
+ size: "sm",
575
+ showLabel: false
576
+ })
399
577
  }), /* @__PURE__ */ jsxs("div", {
400
- className: "text-xs text-text-secondary",
401
- children: [
402
- node.nodeType,
403
- " v",
404
- node.nodeVersion
405
- ]
406
- })]
407
- })]
408
- }), /* @__PURE__ */ jsxs("div", {
409
- className: "flex items-center gap-4 flex-shrink-0",
410
- children: [
411
- /* @__PURE__ */ jsxs("div", {
412
- className: "text-right text-xs text-text-secondary hidden sm:block",
413
- children: [/* @__PURE__ */ jsx("div", { children: formatTime(node.startTime) }), /* @__PURE__ */ jsx("div", { children: formatElapsedMs(nodeStateDurationMs(node)) })]
414
- }),
415
- hasChildRun && /* @__PURE__ */ jsxs("span", {
416
- className: "px-2 py-0.5 text-xs bg-chart-12/10 text-chart-12 rounded flex items-center gap-1",
417
- children: [/* @__PURE__ */ jsx(Icon, {
418
- icon: "mdi:sitemap",
419
- width: 12
420
- }), tr("fluidgrids.executions.nodeTimeline.subWorkflowBadge", "Sub-workflow")]
421
- }),
422
- isWaitingSubworkflow && /* @__PURE__ */ jsx("span", {
423
- className: "px-2 py-0.5 text-xs bg-chart-3/10 text-chart-3 rounded animate-pulse",
424
- children: tr("fluidgrids.executions.nodeTimeline.waitingBadge", "Waiting...")
425
- }),
426
- node.attemptCount > 1 && /* @__PURE__ */ jsxs("span", {
427
- className: "px-2 py-0.5 text-xs bg-[var(--color-status-warning-bg-subtle)] text-[var(--color-status-warning-text)] rounded",
428
- children: [
429
- tr("fluidgrids.executions.nodeTimeline.attemptLabel", "Attempt"),
430
- " ",
431
- node.attemptCount
432
- ]
433
- }),
434
- /* @__PURE__ */ jsx(Icon, {
435
- icon: isExpanded ? "mdi:chevron-up" : "mdi:chevron-down",
436
- className: "text-text-secondary",
437
- width: 20
438
- })
439
- ]
440
- })]
441
- }), isExpanded && /* @__PURE__ */ jsxs("div", {
442
- className: "px-4 pb-4 border-t border-border-secondary/50",
443
- children: [
444
- /* @__PURE__ */ jsxs("div", {
445
- className: "flex gap-2 mt-3 mb-3",
446
- children: [/* @__PURE__ */ jsx("button", {
447
- onClick: () => setSelectedTab("output"),
448
- className: `px-3 py-1.5 text-sm rounded ${selectedTab === "output" ? "bg-action-primary-bg text-action-primary-text" : "bg-background-tertiary text-text-secondary"}`,
449
- children: tr("fluidgrids.executions.nodeTimeline.outputTab", "Output")
450
- }), /* @__PURE__ */ jsx("button", {
451
- onClick: () => setSelectedTab("input"),
452
- className: `px-3 py-1.5 text-sm rounded ${selectedTab === "input" ? "bg-action-primary-bg text-action-primary-text" : "bg-background-tertiary text-text-secondary"}`,
453
- children: tr("fluidgrids.executions.nodeTimeline.inputTab", "Input")
454
- })]
455
- }),
456
- /* @__PURE__ */ jsx("div", {
457
- className: "bg-background-primary rounded p-3 max-h-64 overflow-auto",
458
- children: /* @__PURE__ */ jsx("pre", {
459
- className: "text-xs text-text-primary whitespace-pre-wrap break-words font-mono",
460
- children: selectedTab === "output" ? formatJsonData(node.output) : formatJsonData(node.inputArgs)
461
- })
462
- }),
463
- node.error && /* @__PURE__ */ jsxs(AskableSelectionDiv, {
464
- describe: describeStep,
465
- className: "mt-3 p-3 bg-[var(--color-status-error-bg-subtle)] border border-[var(--color-status-error-border)] rounded",
466
- children: [/* @__PURE__ */ jsxs("div", {
467
- className: "flex items-center gap-2 text-[var(--color-status-error-text)] mb-1",
468
- children: [
469
- /* @__PURE__ */ jsx(Icon, {
470
- icon: "mdi:alert-circle",
471
- width: 16
472
- }),
473
- /* @__PURE__ */ jsx("span", {
474
- className: "font-medium text-sm",
475
- children: tr("fluidgrids.executions.nodeTimeline.errorLabel", "Error")
476
- }),
477
- node.errorType && /* @__PURE__ */ jsx("span", {
478
- className: "text-xs bg-[var(--color-status-error-bg)] px-2 py-0.5 rounded",
479
- children: node.errorType
480
- })
481
- ]
482
- }), /* @__PURE__ */ jsx("p", {
483
- className: "text-sm text-[var(--color-status-error-text)]",
484
- children: node.error
578
+ className: "min-w-0",
579
+ children: [/* @__PURE__ */ jsx("div", {
580
+ className: "font-medium text-text-primary truncate",
581
+ title: node.nodeId,
582
+ children: nodeDisplayTitle(nodeDisplay?.[node.nodeId], node.nodeId)
583
+ }), !v2 && /* @__PURE__ */ jsxs("div", {
584
+ className: "text-xs text-text-secondary",
585
+ children: [
586
+ node.nodeType,
587
+ " v",
588
+ node.nodeVersion
589
+ ]
590
+ })]
485
591
  })]
486
- }),
487
- /* @__PURE__ */ jsxs("div", {
488
- className: "mt-3 flex flex-wrap justify-end gap-2",
592
+ }), /* @__PURE__ */ jsxs("div", {
593
+ className: "flex items-center gap-4 flex-shrink-0",
489
594
  children: [
490
- hasChildRun && onViewChildRun && /* @__PURE__ */ jsxs("button", {
491
- onClick: () => onViewChildRun(node.childRunId),
492
- className: "px-3 py-1.5 text-sm bg-chart-12 text-action-primary-text rounded hover:bg-chart-12/90 flex items-center gap-1.5",
595
+ !v2 && /* @__PURE__ */ jsxs("div", {
596
+ className: "text-right text-xs text-text-secondary hidden sm:block",
597
+ children: [/* @__PURE__ */ jsx("div", { children: formatTime(node.startTime) }), /* @__PURE__ */ jsx("div", { children: formatElapsedMs(nodeStateDurationMs(node)) })]
598
+ }),
599
+ !v2 && hasChildRun && /* @__PURE__ */ jsxs("span", {
600
+ className: "px-2 py-0.5 text-xs bg-chart-12/10 text-chart-12 rounded flex items-center gap-1",
493
601
  children: [/* @__PURE__ */ jsx(Icon, {
494
602
  icon: "mdi:sitemap",
495
- width: 16
496
- }), tr("fluidgrids.executions.nodeTimeline.viewSubWorkflowRun", "View Sub-Workflow Run")]
603
+ width: 12
604
+ }), tr("fluidgrids.executions.nodeTimeline.subWorkflowBadge", "Sub-workflow")]
497
605
  }),
498
- onViewAgentTrace && isAgentNodeType(node.nodeType) && /* @__PURE__ */ jsxs("button", {
499
- type: "button",
500
- onClick: () => onViewAgentTrace(node.nodeId),
501
- className: "px-3 py-1.5 text-sm border border-border-secondary text-text-primary rounded hover:bg-background-tertiary flex items-center gap-1.5",
502
- children: [/* @__PURE__ */ jsx(Icon, {
503
- icon: "mdi:robot-outline",
504
- width: 16
505
- }), tr("fluidgrids.executions.nodeTimeline.viewAgentTrace", "View trace")]
606
+ !v2 && isWaitingSubworkflow && /* @__PURE__ */ jsx("span", {
607
+ className: "px-2 py-0.5 text-xs bg-chart-3/10 text-chart-3 rounded animate-pulse",
608
+ children: tr("fluidgrids.executions.nodeTimeline.waitingBadge", "Waiting...")
609
+ }),
610
+ !v2 && node.attemptCount > 1 && /* @__PURE__ */ jsxs("span", {
611
+ className: "px-2 py-0.5 text-xs bg-[var(--color-status-warning-bg-subtle)] text-[var(--color-status-warning-text)] rounded",
612
+ children: [
613
+ tr("fluidgrids.executions.nodeTimeline.attemptLabel", "Attempt"),
614
+ " ",
615
+ node.attemptCount
616
+ ]
506
617
  }),
507
- node.status === "FAILED" && onRetryNode && /* @__PURE__ */ jsxs("button", {
508
- "data-agent": "confirm",
509
- onClick: () => onRetryNode(node.nodeId),
510
- className: "px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded hover:bg-action-primary-bgHover flex items-center gap-1.5",
618
+ /* @__PURE__ */ jsx(Icon, {
619
+ icon: isExpanded ? "mdi:chevron-up" : "mdi:chevron-down",
620
+ className: "text-text-secondary",
621
+ width: 20
622
+ })
623
+ ]
624
+ })]
625
+ }),
626
+ isExpanded && v2 && /* @__PURE__ */ jsxs("div", {
627
+ className: "px-4 pb-4 border-t border-border-secondary/50",
628
+ children: [
629
+ hasError && /* @__PURE__ */ jsx(AskableSelectionDiv, {
630
+ describe: describeStep,
631
+ className: "mt-3 p-3 bg-[var(--color-status-error-bg-subtle)] border border-[var(--color-status-error-border)] rounded",
632
+ children: /* @__PURE__ */ jsxs("div", {
633
+ className: "flex items-center gap-2 text-[var(--color-status-error-text)]",
511
634
  children: [/* @__PURE__ */ jsx(Icon, {
512
- icon: "mdi:refresh",
635
+ icon: "mdi:alert-circle",
513
636
  width: 16
514
- }), tr("fluidgrids.executions.nodeTimeline.retryNodeButton", "Retry Node")]
637
+ }), /* @__PURE__ */ jsx("p", {
638
+ className: "text-sm font-medium",
639
+ "data-testid": "run-step-advice",
640
+ children: errorAdvice(tr, node.errorType)
641
+ })]
515
642
  })
516
- ]
517
- }),
518
- hasMetadata(node.metadata) && /* @__PURE__ */ jsxs("details", {
519
- className: "mt-3",
520
- children: [/* @__PURE__ */ jsx("summary", {
521
- className: "text-xs text-text-secondary cursor-pointer hover:text-text-primary",
522
- children: tr("fluidgrids.executions.nodeTimeline.metadataSummary", "Metadata")
523
- }), /* @__PURE__ */ jsx("pre", {
524
- className: "mt-2 text-xs text-text-secondary bg-background-tertiary p-2 rounded overflow-auto max-h-32",
525
- children: formatJsonData(node.metadata)
526
- })]
527
- })
528
- ]
529
- })]
643
+ }),
644
+ renderStepActions(node),
645
+ /* @__PURE__ */ jsxs(TechnicalDetails, {
646
+ className: "mt-3",
647
+ "data-testid": "run-step-technical",
648
+ children: [
649
+ /* @__PURE__ */ jsxs("dl", {
650
+ className: "mb-3 grid grid-cols-1 gap-x-6 gap-y-1 text-xs text-text-secondary sm:grid-cols-2",
651
+ children: [
652
+ /* @__PURE__ */ jsxs("div", { children: [
653
+ node.nodeType,
654
+ " v",
655
+ node.nodeVersion
656
+ ] }),
657
+ /* @__PURE__ */ jsxs("div", { children: [
658
+ formatTime(node.startTime),
659
+ " ·",
660
+ " ",
661
+ formatElapsedMs(nodeStateDurationMs(node))
662
+ ] }),
663
+ node.attemptCount > 1 && /* @__PURE__ */ jsxs("div", { children: [
664
+ tr("fluidgrids.executions.nodeTimeline.attemptLabel", "Attempt"),
665
+ " ",
666
+ node.attemptCount
667
+ ] }),
668
+ hasChildRun && /* @__PURE__ */ jsx("div", { children: tr("fluidgrids.executions.nodeTimeline.subWorkflowBadge", "Sub-workflow") }),
669
+ isWaitingSubworkflow && /* @__PURE__ */ jsx("div", { children: tr("fluidgrids.executions.nodeTimeline.waitingBadge", "Waiting...") })
670
+ ]
671
+ }),
672
+ node.error && /* @__PURE__ */ jsxs(AskableSelectionDiv, {
673
+ describe: describeStep,
674
+ className: "mb-3 p-3 bg-[var(--color-status-error-bg-subtle)] border border-[var(--color-status-error-border)] rounded",
675
+ children: [/* @__PURE__ */ jsxs("div", {
676
+ className: "flex items-center gap-2 text-[var(--color-status-error-text)] mb-1",
677
+ children: [/* @__PURE__ */ jsx("span", {
678
+ className: "font-medium text-sm",
679
+ children: tr("fluidgrids.executions.nodeTimeline.errorLabel", "Error")
680
+ }), node.errorType && /* @__PURE__ */ jsx("span", {
681
+ className: "text-xs bg-[var(--color-status-error-bg)] px-2 py-0.5 rounded",
682
+ children: node.errorType
683
+ })]
684
+ }), /* @__PURE__ */ jsx("p", {
685
+ className: "text-sm text-[var(--color-status-error-text)] break-words",
686
+ children: node.error
687
+ })]
688
+ }),
689
+ renderStepData(node),
690
+ renderStepMetadata(node)
691
+ ]
692
+ })
693
+ ]
694
+ }),
695
+ isExpanded && !v2 && /* @__PURE__ */ jsxs("div", {
696
+ className: "px-4 pb-4 border-t border-border-secondary/50",
697
+ children: [
698
+ renderStepData(node),
699
+ node.error && /* @__PURE__ */ jsxs(AskableSelectionDiv, {
700
+ describe: describeStep,
701
+ className: "mt-3 p-3 bg-[var(--color-status-error-bg-subtle)] border border-[var(--color-status-error-border)] rounded",
702
+ children: [/* @__PURE__ */ jsxs("div", {
703
+ className: "flex items-center gap-2 text-[var(--color-status-error-text)] mb-1",
704
+ children: [
705
+ /* @__PURE__ */ jsx(Icon, {
706
+ icon: "mdi:alert-circle",
707
+ width: 16
708
+ }),
709
+ /* @__PURE__ */ jsx("span", {
710
+ className: "font-medium text-sm",
711
+ children: tr("fluidgrids.executions.nodeTimeline.errorLabel", "Error")
712
+ }),
713
+ node.errorType && /* @__PURE__ */ jsx("span", {
714
+ className: "text-xs bg-[var(--color-status-error-bg)] px-2 py-0.5 rounded",
715
+ children: node.errorType
716
+ })
717
+ ]
718
+ }), /* @__PURE__ */ jsx("p", {
719
+ className: "text-sm text-[var(--color-status-error-text)]",
720
+ children: node.error
721
+ })]
722
+ }),
723
+ renderStepActions(node),
724
+ renderStepMetadata(node)
725
+ ]
726
+ })
727
+ ]
530
728
  })]
531
729
  }, node.id);
532
730
  })
@@ -919,6 +1117,121 @@ function RunLogViewer({ runId, lines, nodeStates, nodeDisplay, loading, error, h
919
1117
  });
920
1118
  }
921
1119
 
1120
+ // src/modules/executions/components/RunSummaryCard.tsx
1121
+ /**
1122
+ * "What happened" at the top of a run (BOFF-11846): one plain sentence and the
1123
+ * next-step buttons (Run again, Open workflow, Reconnect), above the step list
1124
+ * and any tab. Navigation v2 only.
1125
+ */
1126
+ const TONE = {
1127
+ FAILED: "border-[var(--color-status-error-border)] bg-[var(--color-status-error-bg-subtle)]",
1128
+ CANCELLED: "border-[var(--color-status-warning-border)] bg-[var(--color-status-warning-bg-subtle)]",
1129
+ TERMINATED: "border-[var(--color-status-warning-border)] bg-[var(--color-status-warning-bg-subtle)]",
1130
+ COMPLETED: "border-[var(--color-status-success-border)] bg-[var(--color-status-success-bg-subtle)]"
1131
+ };
1132
+ function RunSummaryCard({ status, sentence, onRunAgain, runAgainBusy, onOpenWorkflow, reconnectTo }) {
1133
+ const tr = useTr();
1134
+ const tone = TONE[status] ?? "border-border-secondary bg-background-secondary";
1135
+ return /* @__PURE__ */ jsxs("section", {
1136
+ "aria-label": tr("fluidgrids.executions.runSummary.heading", "What happened"),
1137
+ "data-testid": "run-summary-card",
1138
+ "data-status": status,
1139
+ className: `mb-6 rounded-lg border p-4 ${tone}`,
1140
+ children: [/* @__PURE__ */ jsx("p", {
1141
+ className: "text-base font-medium text-text-primary",
1142
+ children: sentence
1143
+ }), (onRunAgain || onOpenWorkflow || reconnectTo) && /* @__PURE__ */ jsxs("div", {
1144
+ className: "mt-3 flex flex-wrap items-center gap-2",
1145
+ children: [
1146
+ reconnectTo && /* @__PURE__ */ jsx(Button$1, {
1147
+ asChild: true,
1148
+ size: "sm",
1149
+ "data-testid": "run-reconnect",
1150
+ children: /* @__PURE__ */ jsxs(Link, {
1151
+ to: reconnectTo,
1152
+ children: [/* @__PURE__ */ jsx(Icon, {
1153
+ icon: "mdi:link-variant",
1154
+ className: "mr-1",
1155
+ width: 16
1156
+ }), tr("fluidgrids.executions.executionDetail.reconnectButton", "Reconnect")]
1157
+ })
1158
+ }),
1159
+ onRunAgain && /* @__PURE__ */ jsxs(Button$1, {
1160
+ "data-agent": "confirm",
1161
+ size: "sm",
1162
+ variant: reconnectTo ? "outline" : "default",
1163
+ onClick: onRunAgain,
1164
+ disabled: runAgainBusy,
1165
+ "data-testid": "run-again",
1166
+ children: [/* @__PURE__ */ jsx(Icon, {
1167
+ icon: "mdi:refresh",
1168
+ className: "mr-1",
1169
+ width: 16
1170
+ }), tr("fluidgrids.executions.executionDetail.runAgainButton", "Run again")]
1171
+ }),
1172
+ onOpenWorkflow && /* @__PURE__ */ jsxs(Button$1, {
1173
+ variant: "outline",
1174
+ size: "sm",
1175
+ onClick: onOpenWorkflow,
1176
+ "data-testid": "run-open-workflow",
1177
+ children: [/* @__PURE__ */ jsx(Icon, {
1178
+ icon: "mdi:open-in-new",
1179
+ className: "mr-1",
1180
+ width: 16
1181
+ }), tr("fluidgrids.executions.executionDetail.openWorkflowButton", "Open workflow")]
1182
+ })
1183
+ ]
1184
+ })]
1185
+ });
1186
+ }
1187
+
1188
+ // src/modules/executions/utils/run-summary.ts
1189
+ function summarizeRun(tr, input) {
1190
+ const none = {
1191
+ errorType: null,
1192
+ failedNodeId: null
1193
+ };
1194
+ switch (input.status) {
1195
+ case "FAILED": {
1196
+ const failed = input.nodeStates.find((node) => node.status === "FAILED");
1197
+ const errorType = input.errorType ?? failed?.errorType ?? "UNKNOWN";
1198
+ const advice = errorAdvice(tr, errorType);
1199
+ return {
1200
+ sentence: failed ? tr("fluidgrids.executions.runSummary.failedAtStep", "This run stopped at “{{step}}”. {{advice}}", {
1201
+ step: input.stepLabel(failed.nodeId),
1202
+ advice
1203
+ }) : tr("fluidgrids.executions.runSummary.failed", "This run didn't finish. {{advice}}", { advice }),
1204
+ errorType,
1205
+ failedNodeId: failed?.nodeId ?? null
1206
+ };
1207
+ }
1208
+ case "CANCELLED": return {
1209
+ ...none,
1210
+ sentence: tr("fluidgrids.executions.runSummary.stopped", "This run was stopped.")
1211
+ };
1212
+ case "TERMINATED": return {
1213
+ ...none,
1214
+ sentence: tr("fluidgrids.executions.runSummary.forceStopped", "This run was force-stopped.")
1215
+ };
1216
+ case "WAITING": return {
1217
+ ...none,
1218
+ sentence: input.awaitingPerson ? tr("fluidgrids.executions.runSummary.onHoldPerson", "This run is on hold until someone answers.") : tr("fluidgrids.executions.runSummary.onHold", "This run is on hold.")
1219
+ };
1220
+ case "COMPLETED": return {
1221
+ ...none,
1222
+ sentence: tr("fluidgrids.executions.runSummary.succeeded", "This run succeeded.")
1223
+ };
1224
+ case "RUNNING": return {
1225
+ ...none,
1226
+ sentence: tr("fluidgrids.executions.runSummary.running", "This run is in progress.")
1227
+ };
1228
+ default: return {
1229
+ ...none,
1230
+ sentence: tr("fluidgrids.executions.runSummary.startingSoon", "This run will start soon.")
1231
+ };
1232
+ }
1233
+ }
1234
+
922
1235
  // src/modules/executions/hooks/useRunLogs.ts
923
1236
  /**
924
1237
  * useRunLogs — a GitHub-Actions-style live tail for a workflow run.
@@ -1273,156 +1586,53 @@ function ChildRunsPanel({ view, nodeDisplay, formatDateTime }) {
1273
1586
  width: 18
1274
1587
  }),
1275
1588
  /* @__PURE__ */ jsx("h2", {
1276
- id: "run-child-runs-heading",
1277
- children: tr("fluidgrids.executions.hierarchy.childRunsTitle", "Child runs")
1278
- }),
1279
- /* @__PURE__ */ jsx("span", {
1280
- className: "rounded-full bg-background-tertiary px-2 py-0.5 text-xs text-text-secondary",
1281
- children: children.length.toLocaleString()
1282
- })
1283
- ]
1284
- }),
1285
- view?.truncated && /* @__PURE__ */ jsx("p", {
1286
- className: "mt-2 text-xs text-text-secondary",
1287
- children: tr("fluidgrids.executions.hierarchy.truncated", "This call tree has more runs than can be listed here, so some child runs may be missing.")
1288
- }),
1289
- /* @__PURE__ */ jsx("ul", {
1290
- className: "mt-3 divide-y divide-border-secondary",
1291
- children: children.map((child) => {
1292
- const node = child.parentNodeId ? nodeDisplay[child.parentNodeId]?.label ?? child.parentNodeId : null;
1293
- return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Link, {
1294
- to: runPath(child.id),
1295
- className: "flex flex-col gap-1 py-2 hover:bg-background-tertiary sm:flex-row sm:items-center sm:gap-3",
1296
- "data-testid": "run-child-run",
1297
- children: [
1298
- /* @__PURE__ */ jsx(ExecutionStatusBadge, {
1299
- status: child.status,
1300
- size: "sm"
1301
- }),
1302
- /* @__PURE__ */ jsxs("span", {
1303
- className: "min-w-0 flex-1",
1304
- children: [/* @__PURE__ */ jsx("span", {
1305
- className: "block truncate text-sm font-medium text-text-link",
1306
- children: child.name
1307
- }), node && /* @__PURE__ */ jsx("span", {
1308
- className: "block truncate text-xs text-text-secondary",
1309
- children: tr("fluidgrids.executions.hierarchy.startedByNode", "Started by {{node}}", { node })
1310
- })]
1311
- }),
1312
- /* @__PURE__ */ jsx("span", {
1313
- className: "text-xs text-text-secondary",
1314
- children: formatDateTime(child.startedAt ?? child.createdAt)
1315
- }),
1316
- /* @__PURE__ */ jsx("span", {
1317
- className: "w-16 text-xs text-text-secondary sm:text-right",
1318
- children: formatElapsedMs(runDurationMs(child))
1319
- })
1320
- ]
1321
- }) }, child.id);
1322
- })
1323
- })
1324
- ]
1325
- });
1326
- }
1327
-
1328
- // src/modules/human-response/utils/answer-rows.ts
1329
- /**
1330
- * What a person answered to a free-text or form ask, as label/value rows — the answer that drove
1331
- * the run, kept visible for audit. Approvals need none: the outcome line already says which.
1332
- */
1333
- function answerRows(request, tr) {
1334
- if (request.status !== "RESPONDED" || request.responseType === "APPROVAL") return [];
1335
- const data = request.response?.data;
1336
- if (!data || typeof data !== "object" || Array.isArray(data)) return [];
1337
- const values = data;
1338
- const show = (value) => {
1339
- if (Array.isArray(value)) return value.map(String).join(", ");
1340
- if (value === null || value === void 0) return "";
1341
- return String(value);
1342
- };
1343
- if (request.responseType === "FREE_TEXT") return [{
1344
- key: "text",
1345
- label: tr("fluidgrids.humanResponse.answerLabel", "Response"),
1346
- value: show(values.text)
1347
- }];
1348
- return request.fields.filter((field) => values[field.name] !== void 0).map((field) => ({
1349
- key: field.name,
1350
- label: field.label || field.name,
1351
- value: show(values[field.name])
1352
- }));
1353
- }
1354
-
1355
- // src/modules/human-response/components/RunHumanRequests.tsx
1356
- /**
1357
- * Run detail: the asks a "Send and Wait for Response" node made (BOFF-7651) — who was asked, by
1358
- * which channel, until when, and what came back. An approval lists its approvers with each one's
1359
- * answer and comment (BOFF-9956). Renders nothing for runs that asked nobody.
1360
- */
1361
- function outcome(request, tr) {
1362
- const when = request.respondedAt ? new Date(request.respondedAt).toLocaleString() : "";
1363
- switch (request.status) {
1364
- case "RESPONDED": return request.decision === "declined" ? tr("fluidgrids.humanResponse.outcomeDeclined", "Declined {{when}}", { when }) : request.decision === "approved" ? tr("fluidgrids.humanResponse.outcomeApproved", "Approved {{when}}", { when }) : tr("fluidgrids.humanResponse.outcomeResponded", "Answered {{when}}", { when });
1365
- case "TIMED_OUT": return tr("fluidgrids.humanResponse.outcomeTimedOut", "Nobody answered in time — continued down Timed out");
1366
- case "CANCELLED": return tr("fluidgrids.humanResponse.outcomeCancelled", "Withdrawn (the node was retried or the run stopped)");
1367
- default: return request.expiresAt ? tr("fluidgrids.humanResponse.waitingUntil", "Waiting until {{time}}", { time: new Date(request.expiresAt).toLocaleString() }) : tr("fluidgrids.humanResponse.waiting", "Waiting for a response");
1368
- }
1369
- }
1370
- function RunHumanRequests({ requests }) {
1371
- const tr = useTr();
1372
- if (requests.length === 0) return null;
1373
- return /* @__PURE__ */ jsxs("div", {
1374
- className: "mb-6 space-y-2 rounded-lg border border-border-secondary bg-background-secondary p-4",
1375
- "data-testid": "run-human-requests",
1376
- children: [/* @__PURE__ */ jsxs("div", {
1377
- className: "flex items-center gap-2 text-sm font-medium text-text-primary",
1378
- children: [/* @__PURE__ */ jsx(Icon, {
1379
- icon: "mdi:account-clock-outline",
1380
- width: 18
1381
- }), requests.some((request) => request.status === "PENDING") ? tr("fluidgrids.humanResponse.runHeading", "Waiting on people") : tr("fluidgrids.humanResponse.runHeadingDone", "People asked")]
1382
- }), requests.map((request) => /* @__PURE__ */ jsx(RunHumanRequestRow, { request }, request.id))]
1383
- });
1384
- }
1385
- function RunHumanRequestRow({ request }) {
1386
- const tr = useTr();
1387
- const answer = answerRows(request, tr);
1388
- const recipient = useRecipientLabel(request, tr("fluidgrids.humanResponse.anyMember", "any workspace member"));
1389
- return /* @__PURE__ */ jsxs("div", {
1390
- className: "flex flex-col gap-0.5 text-sm sm:flex-row sm:flex-wrap sm:items-baseline sm:gap-x-2",
1391
- "data-testid": "run-human-request",
1392
- "data-status": request.status,
1393
- children: [
1394
- /* @__PURE__ */ jsx("span", {
1395
- className: "font-medium text-text-primary",
1396
- children: request.title
1397
- }),
1398
- /* @__PURE__ */ jsxs("span", {
1399
- className: "text-text-secondary",
1400
- children: [
1401
- tr("fluidgrids.humanResponse.askedVia", "Asked {{recipient}} via {{channel}}", {
1402
- recipient,
1403
- channel: channelLabel(request.channel, tr)
1589
+ id: "run-child-runs-heading",
1590
+ children: tr("fluidgrids.executions.hierarchy.childRunsTitle", "Child runs")
1404
1591
  }),
1405
- " · ",
1406
- outcome(request, tr)
1592
+ /* @__PURE__ */ jsx("span", {
1593
+ className: "rounded-full bg-background-tertiary px-2 py-0.5 text-xs text-text-secondary",
1594
+ children: children.length.toLocaleString()
1595
+ })
1407
1596
  ]
1408
1597
  }),
1409
- (request.approvals?.length ?? 0) > 0 && /* @__PURE__ */ jsx("div", {
1410
- className: "mt-1 w-full sm:mt-0",
1411
- children: /* @__PURE__ */ jsx(ApprovalApprovers, { request })
1598
+ view?.truncated && /* @__PURE__ */ jsx("p", {
1599
+ className: "mt-2 text-xs text-text-secondary",
1600
+ children: tr("fluidgrids.executions.hierarchy.truncated", "This call tree has more runs than can be listed here, so some child runs may be missing.")
1412
1601
  }),
1413
- answer.length > 0 && /* @__PURE__ */ jsx("dl", {
1414
- className: "mt-1 w-full space-y-0.5 text-xs sm:mt-0",
1415
- "data-testid": "run-human-answer",
1416
- children: answer.map((row) => /* @__PURE__ */ jsxs("div", {
1417
- className: "flex gap-2",
1418
- children: [/* @__PURE__ */ jsxs("dt", {
1419
- className: "shrink-0 font-medium text-text-secondary",
1420
- children: [row.label, ":"]
1421
- }), /* @__PURE__ */ jsx("dd", {
1422
- className: "whitespace-pre-wrap break-words text-text-primary",
1423
- children: row.value
1424
- })]
1425
- }, row.key))
1602
+ /* @__PURE__ */ jsx("ul", {
1603
+ className: "mt-3 divide-y divide-border-secondary",
1604
+ children: children.map((child) => {
1605
+ const node = child.parentNodeId ? nodeDisplay[child.parentNodeId]?.label ?? child.parentNodeId : null;
1606
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Link, {
1607
+ to: runPath(child.id),
1608
+ className: "flex flex-col gap-1 py-2 hover:bg-background-tertiary sm:flex-row sm:items-center sm:gap-3",
1609
+ "data-testid": "run-child-run",
1610
+ children: [
1611
+ /* @__PURE__ */ jsx(ExecutionStatusBadge, {
1612
+ status: child.status,
1613
+ size: "sm"
1614
+ }),
1615
+ /* @__PURE__ */ jsxs("span", {
1616
+ className: "min-w-0 flex-1",
1617
+ children: [/* @__PURE__ */ jsx("span", {
1618
+ className: "block truncate text-sm font-medium text-text-link",
1619
+ children: child.name
1620
+ }), node && /* @__PURE__ */ jsx("span", {
1621
+ className: "block truncate text-xs text-text-secondary",
1622
+ children: tr("fluidgrids.executions.hierarchy.startedByNode", "Started by {{node}}", { node })
1623
+ })]
1624
+ }),
1625
+ /* @__PURE__ */ jsx("span", {
1626
+ className: "text-xs text-text-secondary",
1627
+ children: formatDateTime(child.startedAt ?? child.createdAt)
1628
+ }),
1629
+ /* @__PURE__ */ jsx("span", {
1630
+ className: "w-16 text-xs text-text-secondary sm:text-right",
1631
+ children: formatElapsedMs(runDurationMs(child))
1632
+ })
1633
+ ]
1634
+ }) }, child.id);
1635
+ })
1426
1636
  })
1427
1637
  ]
1428
1638
  });
@@ -2603,12 +2813,12 @@ function AgentTracePanel({ traces, nodeDisplay, usage, usageLoading, usageError,
2603
2813
  }
2604
2814
  function CostValue({ usage, tr }) {
2605
2815
  const cost = aiCost(usage);
2606
- if (cost.kind === "priced") return /* @__PURE__ */ jsx(Fragment, { children: cost.text });
2816
+ if (cost.kind === "priced") return /* @__PURE__ */ jsx(Fragment$1, { children: cost.text });
2607
2817
  if (cost.kind === "unpriced") return /* @__PURE__ */ jsx("span", {
2608
2818
  className: "text-text-secondary",
2609
2819
  children: tr("fluidgrids.executions.executionDetail.agent.notPricedYet", "Not priced yet")
2610
2820
  });
2611
- return /* @__PURE__ */ jsx(Fragment, { children: "—" });
2821
+ return /* @__PURE__ */ jsx(Fragment$1, { children: "—" });
2612
2822
  }
2613
2823
  function Stat({ label, children, detail }) {
2614
2824
  return /* @__PURE__ */ jsxs("div", {
@@ -2668,7 +2878,7 @@ function AiUsageRollup({ usage, loading, error, onRefresh, nodeDisplay, tr }) {
2668
2878
  4
2669
2879
  ].map((item) => /* @__PURE__ */ jsx("div", { className: "h-20 animate-pulse rounded-lg bg-background-tertiary" }, item))
2670
2880
  }),
2671
- total ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
2881
+ total ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
2672
2882
  className: "grid grid-cols-2 gap-3 lg:grid-cols-4",
2673
2883
  children: [
2674
2884
  /* @__PURE__ */ jsx(Stat, {
@@ -2985,11 +3195,11 @@ function StepBlock({ label, value, empty }) {
2985
3195
  })] });
2986
3196
  }
2987
3197
  function StepCost({ value, tr }) {
2988
- if (value === null) return /* @__PURE__ */ jsx(Fragment, { children: tr("fluidgrids.executions.executionDetail.agent.notPricedYet", "Not priced yet") });
2989
- return /* @__PURE__ */ jsx(Fragment, { children: formatMicroUsd(value) });
3198
+ if (value === null) return /* @__PURE__ */ jsx(Fragment$1, { children: tr("fluidgrids.executions.executionDetail.agent.notPricedYet", "Not priced yet") });
3199
+ return /* @__PURE__ */ jsx(Fragment$1, { children: formatMicroUsd(value) });
2990
3200
  }
2991
3201
  function TokensInOut({ tokensIn, tokensOut, tr }) {
2992
- return /* @__PURE__ */ jsx(Fragment, { children: tr("fluidgrids.executions.executionDetail.agent.tokensInOut", "{{input}} in / {{output}} out tokens", {
3202
+ return /* @__PURE__ */ jsx(Fragment$1, { children: tr("fluidgrids.executions.executionDetail.agent.tokensInOut", "{{input}} in / {{output}} out tokens", {
2993
3203
  input: formatTokens(tokensIn),
2994
3204
  output: formatTokens(tokensOut)
2995
3205
  }) });
@@ -3857,7 +4067,7 @@ function ActivityActionDialog({ runId, action, activity, onClose, onDone }) {
3857
4067
  error: parsedResult.ok ? null : parsedResult.error,
3858
4068
  testId: "activity-complete-result"
3859
4069
  }),
3860
- action === "fail" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
4070
+ action === "fail" && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
3861
4071
  className: "space-y-1.5",
3862
4072
  children: [/* @__PURE__ */ jsx(Label, {
3863
4073
  htmlFor: "activity-fail-message",
@@ -4443,7 +4653,7 @@ function ResetRunForm({ runId, point, onOpenChange, onReset }) {
4443
4653
  "data-testid": "run-reset-reason"
4444
4654
  })]
4445
4655
  }),
4446
- point.kind === "seq" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("label", {
4656
+ point.kind === "seq" && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("label", {
4447
4657
  className: "flex items-center justify-between gap-3 text-sm text-text-primary",
4448
4658
  children: [tr("fluidgrids.runControl.reset.reapplySignals", "Re-apply the signals received after it"), /* @__PURE__ */ jsx(Switch, {
4449
4659
  checked: reapplySignals,
@@ -5886,7 +6096,7 @@ function SaveRunAsTestCase({ runId }) {
5886
6096
  setSaving(false);
5887
6097
  }
5888
6098
  };
5889
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(SdkPermissionButton, {
6099
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(SdkPermissionButton, {
5890
6100
  permission: FLUIDGRIDS_PERMISSIONS.WORKFLOW_UPDATE,
5891
6101
  deniedTooltip: tr("fluidgrids.executions.saveAsTestCase.denied", "You need edit access to this workflow to save test cases"),
5892
6102
  children: /* @__PURE__ */ jsxs(Button$1, {
@@ -5961,15 +6171,26 @@ const ALL_DETAIL_TABS = [
5961
6171
  * The tabs a run offers: History only on a durable-function run, Agent
5962
6172
  * (BOFF-9937) only when the run has an AI Agent node state.
5963
6173
  */
5964
- function availableDetailTabs(isDurableRun, hasAgent) {
5965
- return ALL_DETAIL_TABS.filter((tab) => (tab !== "history" || isDurableRun) && (tab !== "agent" || hasAgent));
6174
+ function availableDetailTabs(isDurableRun, hasAgent, v2) {
6175
+ return ALL_DETAIL_TABS.filter((tab) => (tab !== "history" || isDurableRun) && (tab !== "agent" || hasAgent) && (tab !== "context" || !v2));
6176
+ }
6177
+ /**
6178
+ * Where "Reconnect" goes: the host's connections page. It sits outside the
6179
+ * workflows module, so the link is absolute (Navigation v2, FluidGrids only).
6180
+ */
6181
+ const CONNECTIONS_ROUTE = "/integrations/connections";
6182
+ /** The URL's `#…` fragment; empty where there is no window (server render). */
6183
+ function searchHash() {
6184
+ return typeof window === "undefined" ? "" : window.location.hash;
5966
6185
  }
5967
6186
  function ExecutionDetailPage() {
5968
6187
  const tr = useTr();
5969
6188
  const { runId } = useParams();
5970
6189
  const navigate = useNavigate();
5971
6190
  const [searchParams, setSearchParams] = useSearchParams();
5972
- const requestedTab = parseDetailTabParam(searchParams.get("tab"), ALL_DETAIL_TABS);
6191
+ const v2 = useNavigationV2();
6192
+ const requestedTabParam = parseDetailTabParam(searchParams.get("tab"), ALL_DETAIL_TABS);
6193
+ const requestedTab = v2 && requestedTabParam === "context" ? "input" : requestedTabParam ?? (v2 && /^#L/.test(searchHash()) ? "logs" : null);
5973
6194
  const requestedNodeId = searchParams.get("node");
5974
6195
  const [tabSelection, setTabSelection] = useState({
5975
6196
  runId,
@@ -6063,7 +6284,7 @@ function ExecutionDetailPage() {
6063
6284
  actorId: execution.triggeredById,
6064
6285
  actorType: "user"
6065
6286
  }).displayName : null;
6066
- const { pauseExecution, resumeExecution, cancelExecution, retryNode, replayExecution, terminateExecution, loading: actionLoading } = useExecutionActions();
6287
+ const { controlExecution, retryExecution, retryNode, replayExecution, terminateExecution, loading: actionLoading } = useExecutionActions();
6067
6288
  const isActive = runCanChange;
6068
6289
  const isHistoricalTestRun = execution?.mode === "TEST" && (execution.status === "COMPLETED" || execution.status === "FAILED" || execution.status === "CANCELLED" || execution.status === "TERMINATED");
6069
6290
  useAutoRefresh(useCallback(() => {
@@ -6076,7 +6297,7 @@ function ExecutionDetailPage() {
6076
6297
  });
6077
6298
  const isDurableRun = durableEnabled && execution?.executionKind === "DURABLE_FN";
6078
6299
  const hasAgent = hasAgentNodeState(execution?.nodeStates);
6079
- const activeTab = resolveDetailTab(selectionForRun(tabSelection, runId, requestedTab, "timeline"), runId, availableDetailTabs(isDurableRun, hasAgent), "timeline");
6300
+ const activeTab = resolveDetailTab(selectionForRun(tabSelection, runId, requestedTab, "timeline"), runId, availableDetailTabs(isDurableRun, hasAgent, v2), "timeline");
6080
6301
  const agentFocusNodeId = agentFocus.runId === runId ? agentFocus.nodeId : requestedNodeId;
6081
6302
  const runAiUsage = useRunAiUsage(runId, {
6082
6303
  enabled: hasAgent && activeTab === "agent",
@@ -6119,21 +6340,20 @@ function ExecutionDetailPage() {
6119
6340
  }, [durableEnabled, execution]);
6120
6341
  const canSendSignal = canControlRuns && canSignalRun(signalTargets);
6121
6342
  const waitingOnSignal = !isDurableRun && signalTargets.targets.some((target) => !!target.nodeId);
6122
- const handlePause = async () => {
6123
- if (!runId) return;
6124
- await pauseExecution(runId);
6125
- refetch();
6126
- };
6127
- const handleResume = async () => {
6343
+ const handleControl = async (action) => {
6128
6344
  if (!runId) return;
6129
- await resumeExecution(runId);
6345
+ const result = await controlExecution(action, runId);
6346
+ if (!result.ok) toast.error(action === "pause" ? tr("fluidgrids.executions.executionDetail.pauseFailed", "Could not pause this run") : action === "resume" ? tr("fluidgrids.executions.executionDetail.resumeFailed", "Could not resume this run") : tr("fluidgrids.executions.executionDetail.cancelFailed", "Could not cancel this run"), { description: result.message });
6130
6347
  refetch();
6131
6348
  };
6132
- const handleCancel = async () => {
6133
- if (!runId) return;
6134
- await cancelExecution(runId);
6135
- refetch();
6349
+ const [showStopDialog, setShowStopDialog] = useState(false);
6350
+ const requestCancel = () => {
6351
+ if (v2) setShowStopDialog(true);
6352
+ else handleCancel();
6136
6353
  };
6354
+ const handlePause = () => handleControl("pause");
6355
+ const handleResume = () => handleControl("resume");
6356
+ const handleCancel = () => handleControl("cancel");
6137
6357
  const handleTerminate = async () => {
6138
6358
  if (!runId) return;
6139
6359
  if (isDurableRun) {
@@ -6141,7 +6361,7 @@ function ExecutionDetailPage() {
6141
6361
  setShowTerminateDialog(false);
6142
6362
  return;
6143
6363
  }
6144
- await terminateExecution(runId, "User terminated");
6364
+ if (!await terminateExecution(runId, "User terminated")) toast.error(tr("fluidgrids.executions.executionDetail.terminateFailed", "Could not terminate this run"));
6145
6365
  setShowTerminateDialog(false);
6146
6366
  refetch();
6147
6367
  };
@@ -6157,7 +6377,7 @@ function ExecutionDetailPage() {
6157
6377
  };
6158
6378
  const handleRetryNode = async (nodeId) => {
6159
6379
  if (!runId) return;
6160
- await retryNode(runId, nodeId);
6380
+ if (!await retryNode(runId, nodeId)) toast.error(tr("fluidgrids.executions.executionDetail.retryNodeFailed", "Could not retry this step"));
6161
6381
  refetch();
6162
6382
  };
6163
6383
  const handleReplay = async () => {
@@ -6170,6 +6390,21 @@ function ExecutionDetailPage() {
6170
6390
  toast.success(tr("fluidgrids.executions.executionDetail.replayStarted", "Replay started in test mode"));
6171
6391
  navigate(`../runs/${replayed.runId}`);
6172
6392
  };
6393
+ const handleRunAgain = async () => {
6394
+ if (!execution) return;
6395
+ const result = await retryExecution(execution);
6396
+ if (!result.ok) {
6397
+ toast.error(tr("fluidgrids.executions.executionsList.retryFailed", "Could not start a new run"), { description: result.message });
6398
+ return;
6399
+ }
6400
+ toast.success(tr("fluidgrids.executions.executionsList.retryStarted", "New run started"));
6401
+ navigate(`../runs/${result.run.runId}`);
6402
+ };
6403
+ const handleOpenWorkflow = () => {
6404
+ if (!execution?.workflowKey) return;
6405
+ const base = `../workflow/${encodeURIComponent(execution.workflowKey)}`;
6406
+ navigate(execution.version ? `${base}/${encodeURIComponent(execution.version)}` : base);
6407
+ };
6173
6408
  const handleLoadInBuilder = () => {
6174
6409
  if (!execution || execution.mode !== "TEST" || !execution.workflowKey || !execution.version) return;
6175
6410
  const builderPath = `../workflow/${encodeURIComponent(execution.workflowKey)}/${encodeURIComponent(execution.version)}`;
@@ -6228,7 +6463,14 @@ function ExecutionDetailPage() {
6228
6463
  }),
6229
6464
  /* @__PURE__ */ jsx("p", {
6230
6465
  className: "text-[var(--color-status-error-text)] mb-4",
6231
- children: error.message
6466
+ children: tr("fluidgrids.executions.executionDetail.loadErrorDescription", "We couldn't load this run. Try again in a moment.")
6467
+ }),
6468
+ /* @__PURE__ */ jsx(TechnicalDetails, {
6469
+ className: "mb-4 text-[var(--color-status-error-text)]",
6470
+ children: /* @__PURE__ */ jsx("p", {
6471
+ className: "text-sm font-mono break-words",
6472
+ children: error.message
6473
+ })
6232
6474
  }),
6233
6475
  /* @__PURE__ */ jsx(Button$1, {
6234
6476
  variant: "outline",
@@ -6278,7 +6520,7 @@ function ExecutionDetailPage() {
6278
6520
  const tabs = [
6279
6521
  {
6280
6522
  id: "timeline",
6281
- label: tr("fluidgrids.executions.executionDetail.timelineTab", "Timeline"),
6523
+ label: v2 ? tr("fluidgrids.executions.executionDetail.stepsTab", "Steps") : tr("fluidgrids.executions.executionDetail.timelineTab", "Timeline"),
6282
6524
  icon: "mdi:timeline"
6283
6525
  },
6284
6526
  ...isDurableRun ? [{
@@ -6298,15 +6540,236 @@ function ExecutionDetailPage() {
6298
6540
  },
6299
6541
  {
6300
6542
  id: "input",
6301
- label: tr("fluidgrids.executions.executionDetail.inputTab", "Input Data"),
6543
+ label: v2 ? tr("fluidgrids.executions.executionDetail.dataTab", "Data") : tr("fluidgrids.executions.executionDetail.inputTab", "Input Data"),
6302
6544
  icon: "mdi:code-json"
6303
6545
  },
6304
- {
6546
+ ...v2 ? [] : [{
6305
6547
  id: "context",
6306
6548
  label: tr("fluidgrids.executions.executionDetail.contextTab", "Context"),
6307
6549
  icon: "mdi:information-outline"
6308
- }
6550
+ }]
6309
6551
  ];
6552
+ const contextJsonBlock = /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h3", {
6553
+ className: "text-lg font-medium text-text-primary mb-4",
6554
+ children: tr("fluidgrids.executions.executionDetail.executionContextHeading", "Execution Context")
6555
+ }), /* @__PURE__ */ jsx("div", {
6556
+ className: "bg-background-primary rounded p-4 max-h-[300px] overflow-auto",
6557
+ children: /* @__PURE__ */ jsx("pre", {
6558
+ className: "text-sm text-text-primary font-mono whitespace-pre-wrap break-words",
6559
+ children: JSON.stringify(execution.context ?? null, null, 2)
6560
+ })
6561
+ })] });
6562
+ const contextDetailsBlock = /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h3", {
6563
+ className: "text-lg font-medium text-text-primary mb-4",
6564
+ children: tr("fluidgrids.executions.executionDetail.executionDetailsHeading", "Execution Details")
6565
+ }), /* @__PURE__ */ jsxs("div", {
6566
+ className: "space-y-3",
6567
+ children: [
6568
+ /* @__PURE__ */ jsxs("div", {
6569
+ className: "flex justify-between py-2 border-b border-border-secondary",
6570
+ children: [/* @__PURE__ */ jsx("span", {
6571
+ className: "text-text-secondary",
6572
+ children: tr("fluidgrids.executions.executionDetail.runIdRow", "Run ID")
6573
+ }), /* @__PURE__ */ jsx("code", {
6574
+ className: "font-mono text-text-primary",
6575
+ children: execution.id
6576
+ })]
6577
+ }),
6578
+ /* @__PURE__ */ jsxs("div", {
6579
+ className: "flex justify-between py-2 border-b border-border-secondary",
6580
+ children: [/* @__PURE__ */ jsx("span", {
6581
+ className: "text-text-secondary",
6582
+ children: tr("fluidgrids.executions.executionDetail.workflowRow", "Workflow")
6583
+ }), /* @__PURE__ */ jsx("span", {
6584
+ className: "text-text-primary",
6585
+ children: execution.workflow?.name || execution.workflowKey
6586
+ })]
6587
+ }),
6588
+ /* @__PURE__ */ jsxs("div", {
6589
+ className: "flex justify-between py-2 border-b border-border-secondary",
6590
+ children: [/* @__PURE__ */ jsx("span", {
6591
+ className: "text-text-secondary",
6592
+ children: tr("fluidgrids.executions.executionDetail.workflowKeyRow", "Workflow Key")
6593
+ }), /* @__PURE__ */ jsx("code", {
6594
+ className: "font-mono text-text-primary",
6595
+ children: execution.workflowKey
6596
+ })]
6597
+ }),
6598
+ /* @__PURE__ */ jsxs("div", {
6599
+ className: "flex justify-between py-2 border-b border-border-secondary",
6600
+ children: [/* @__PURE__ */ jsx("span", {
6601
+ className: "text-text-secondary",
6602
+ children: tr("fluidgrids.executions.executionDetail.versionRow", "Version")
6603
+ }), /* @__PURE__ */ jsx("span", {
6604
+ className: "text-text-primary",
6605
+ children: formatRunVersion(execution.version)
6606
+ })]
6607
+ }),
6608
+ isDurableRun && /* @__PURE__ */ jsxs(Fragment$1, { children: [
6609
+ /* @__PURE__ */ jsxs("div", {
6610
+ className: "flex justify-between py-2 border-b border-border-secondary",
6611
+ children: [/* @__PURE__ */ jsx("span", {
6612
+ className: "text-text-secondary",
6613
+ children: tr("fluidgrids.executions.executionDetail.workflowFnRow", "Function")
6614
+ }), /* @__PURE__ */ jsx("code", {
6615
+ className: "font-mono text-text-primary",
6616
+ children: execution.workflowFn ?? "—"
6617
+ })]
6618
+ }),
6619
+ /* @__PURE__ */ jsxs("div", {
6620
+ className: "flex justify-between py-2 border-b border-border-secondary",
6621
+ children: [/* @__PURE__ */ jsx("span", {
6622
+ className: "text-text-secondary",
6623
+ children: tr("fluidgrids.executions.executionDetail.taskQueueRow", "Task queue")
6624
+ }), /* @__PURE__ */ jsx("span", {
6625
+ className: "text-text-primary",
6626
+ children: execution.taskQueue ?? "—"
6627
+ })]
6628
+ }),
6629
+ /* @__PURE__ */ jsxs("div", {
6630
+ className: "flex justify-between gap-4 py-2 border-b border-border-secondary",
6631
+ children: [/* @__PURE__ */ jsx("span", {
6632
+ className: "text-text-secondary",
6633
+ children: tr("fluidgrids.executions.executionDetail.buildIdRow", "Build")
6634
+ }), /* @__PURE__ */ jsx("code", {
6635
+ className: "truncate font-mono text-text-primary",
6636
+ title: execution.buildId ?? void 0,
6637
+ children: execution.buildId ?? "—"
6638
+ })]
6639
+ }),
6640
+ /* @__PURE__ */ jsxs("div", {
6641
+ className: "flex justify-between py-2 border-b border-border-secondary",
6642
+ children: [/* @__PURE__ */ jsx("span", {
6643
+ className: "text-text-secondary",
6644
+ children: tr("fluidgrids.executions.executionDetail.historyLengthRow", "History events")
6645
+ }), /* @__PURE__ */ jsx("span", {
6646
+ className: "text-text-primary",
6647
+ children: execution.historyLength ?? 0
6648
+ })]
6649
+ })
6650
+ ] }),
6651
+ execution.triggeredById && /* @__PURE__ */ jsxs("div", {
6652
+ className: "flex justify-between py-2 border-b border-border-secondary",
6653
+ children: [/* @__PURE__ */ jsx("span", {
6654
+ className: "text-text-secondary",
6655
+ children: tr("fluidgrids.executions.executionDetail.triggeredByRow", "Triggered By")
6656
+ }), /* @__PURE__ */ jsx("span", {
6657
+ className: "text-text-primary",
6658
+ title: execution.triggeredById,
6659
+ children: triggeredByLabel
6660
+ })]
6661
+ }),
6662
+ execution.parentRunId && /* @__PURE__ */ jsxs("div", {
6663
+ className: "flex justify-between py-2 border-b border-border-secondary",
6664
+ children: [/* @__PURE__ */ jsx("span", {
6665
+ className: "text-text-secondary",
6666
+ children: tr("fluidgrids.executions.executionDetail.parentRunRow", "Parent Run")
6667
+ }), /* @__PURE__ */ jsxs(Link, {
6668
+ to: `../runs/${execution.parentRunId}`,
6669
+ className: "text-text-link hover:text-text-linkHover hover:underline font-mono",
6670
+ children: [execution.parentRunId.slice(0, 12), "..."]
6671
+ })]
6672
+ }),
6673
+ /* @__PURE__ */ jsxs("div", {
6674
+ className: "flex justify-between py-2 border-b border-border-secondary",
6675
+ children: [/* @__PURE__ */ jsx("span", {
6676
+ className: "text-text-secondary",
6677
+ children: tr("fluidgrids.executions.executionDetail.createdRow", "Created")
6678
+ }), /* @__PURE__ */ jsx("span", {
6679
+ className: "text-text-primary",
6680
+ children: formatDateTime(execution.createdAt)
6681
+ })]
6682
+ }),
6683
+ /* @__PURE__ */ jsxs("div", {
6684
+ className: "flex justify-between py-2 border-b border-border-secondary",
6685
+ children: [/* @__PURE__ */ jsx("span", {
6686
+ className: "text-text-secondary",
6687
+ children: tr("fluidgrids.executions.executionDetail.startedRow", "Started")
6688
+ }), /* @__PURE__ */ jsx("span", {
6689
+ className: "text-text-primary",
6690
+ children: formatDateTime(execution.startedAt)
6691
+ })]
6692
+ }),
6693
+ execution.completedAt && /* @__PURE__ */ jsxs("div", {
6694
+ className: "flex justify-between py-2 border-b border-border-secondary",
6695
+ children: [/* @__PURE__ */ jsx("span", {
6696
+ className: "text-text-secondary",
6697
+ children: tr("fluidgrids.executions.executionDetail.completedRow", "Completed")
6698
+ }), /* @__PURE__ */ jsx("span", {
6699
+ className: "text-text-primary",
6700
+ children: formatDateTime(execution.completedAt)
6701
+ })]
6702
+ })
6703
+ ]
6704
+ })] });
6705
+ const triggerDataBlock = /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h3", {
6706
+ className: "text-lg font-medium text-text-primary mb-4",
6707
+ children: tr("fluidgrids.executions.executionDetail.triggerDataHeading", "Trigger Data")
6708
+ }), /* @__PURE__ */ jsx("div", {
6709
+ className: "bg-background-primary rounded p-4 max-h-[400px] overflow-auto",
6710
+ children: /* @__PURE__ */ jsx("pre", {
6711
+ className: "text-sm text-text-primary font-mono whitespace-pre-wrap break-words",
6712
+ children: JSON.stringify(execution.triggerData ?? null, null, 2)
6713
+ })
6714
+ })] });
6715
+ const orderedSteps = orderNodesByExecution(execution.nodeStates ?? [], execution.triggerNodeId ?? void 0);
6716
+ const summary = v2 ? summarizeRun(tr, {
6717
+ status: execution.status,
6718
+ errorType: execution.errorType,
6719
+ nodeStates: orderedSteps,
6720
+ stepLabel: (nodeId) => nodeDisplayTitle(nodeDisplay[nodeId], nodeId),
6721
+ awaitingPerson
6722
+ }) : null;
6723
+ const canRunAgain = canControlRuns && (execution.status === "FAILED" || execution.status === "CANCELLED" || execution.status === "TERMINATED") && buildRetryTriggerInput(execution) !== null;
6724
+ const copyReference = async (reference) => {
6725
+ try {
6726
+ await navigator.clipboard.writeText(reference);
6727
+ toast.success(tr("fluidgrids.executions.executionDetail.referenceCopied", "Reference copied"));
6728
+ } catch {
6729
+ toast.error(tr("fluidgrids.executions.executionDetail.referenceCopyFailed", "Could not copy the reference"));
6730
+ }
6731
+ };
6732
+ const menuItems = [];
6733
+ if (canControlRuns && execution.status === "RUNNING" && !isDurableRun) menuItems.push({
6734
+ id: "pause",
6735
+ icon: "mdi:pause",
6736
+ label: tr("fluidgrids.executions.executionDetail.pauseButton", "Pause"),
6737
+ onSelect: () => void handlePause(),
6738
+ disabled: actionLoading
6739
+ });
6740
+ if (canSendSignal) menuItems.push({
6741
+ id: "signal",
6742
+ icon: "mdi:bell-ring-outline",
6743
+ label: tr("fluidgrids.executions.executionDetail.signalButton", "Signal"),
6744
+ onSelect: () => setShowSignalDialog(true)
6745
+ });
6746
+ if (canControlRuns && !isDurableRun && execution.status !== "RUNNING" && execution.status !== "PENDING" && execution.status !== "WAITING") menuItems.push({
6747
+ id: "replay",
6748
+ icon: "mdi:replay",
6749
+ label: tr("fluidgrids.executions.executionDetail.replayButton", "Replay in test mode"),
6750
+ onSelect: () => void handleReplay(),
6751
+ disabled: actionLoading
6752
+ });
6753
+ if (isHistoricalTestRun && execution.workflowKey && execution.version) menuItems.push({
6754
+ id: "open-in-editor",
6755
+ icon: "mdi:open-in-new",
6756
+ label: tr("fluidgrids.executions.executionDetail.loadTestRunButton", "Load TEST run in builder"),
6757
+ onSelect: handleLoadInBuilder
6758
+ });
6759
+ if ((isDurableRun ? canTerminateDurable : canControlRuns) && (execution.status === "RUNNING" || execution.status === "WAITING")) menuItems.push({
6760
+ id: "terminate",
6761
+ icon: "mdi:close-octagon",
6762
+ label: tr("fluidgrids.executions.executionDetail.terminateButton", "Terminate"),
6763
+ onSelect: () => setShowTerminateDialog(true),
6764
+ disabled: actionLoading,
6765
+ destructive: true
6766
+ });
6767
+ const menuToggles = isActive ? [{
6768
+ id: "live-updates",
6769
+ label: tr("fluidgrids.executions.executionDetail.liveUpdates", "Live updates"),
6770
+ checked: autoRefreshEnabled,
6771
+ onCheckedChange: setAutoRefreshEnabled
6772
+ }] : [];
6310
6773
  const askRun = {
6311
6774
  runId: execution.id,
6312
6775
  status: execution.status,
@@ -6328,7 +6791,19 @@ function ExecutionDetailPage() {
6328
6791
  tr("fluidgrids.executions.executionDetail.backToExecutions", "Back to Executions")
6329
6792
  ]
6330
6793
  }),
6331
- /* @__PURE__ */ jsx(RunCallPath, {
6794
+ v2 ? execution.parentRunId && /* @__PURE__ */ jsxs("p", {
6795
+ className: "mb-4 text-sm text-text-secondary",
6796
+ "data-testid": "run-started-by-workflow",
6797
+ children: [
6798
+ tr("fluidgrids.executions.executionDetail.startedByWorkflow", "Started by another workflow"),
6799
+ " · ",
6800
+ /* @__PURE__ */ jsx(Link, {
6801
+ to: `../runs/${execution.parentRunId}`,
6802
+ className: "text-text-link hover:text-text-linkHover hover:underline",
6803
+ children: tr("fluidgrids.executions.executionDetail.openParentRun", "Open it")
6804
+ })
6805
+ ]
6806
+ }) : /* @__PURE__ */ jsx(RunCallPath, {
6332
6807
  view: hierarchy,
6333
6808
  parentRunId: execution.parentRunId
6334
6809
  }),
@@ -6346,7 +6821,12 @@ function ExecutionDetailPage() {
6346
6821
  status: execution.status,
6347
6822
  size: "lg"
6348
6823
  }),
6349
- execution.parentRunId && /* @__PURE__ */ jsx("span", {
6824
+ v2 && execution.mode === "TEST" && /* @__PURE__ */ jsx("span", {
6825
+ className: "px-2 py-0.5 text-xs border border-border-default text-text-secondary rounded",
6826
+ "data-testid": "run-test-badge",
6827
+ children: tr("fluidgrids.executions.executionDetail.testBadge", "Test")
6828
+ }),
6829
+ !v2 && execution.parentRunId && /* @__PURE__ */ jsx("span", {
6350
6830
  className: "px-2 py-0.5 text-xs bg-chart-12/10 text-chart-12 rounded",
6351
6831
  children: tr("fluidgrids.executions.executionDetail.subWorkflowBadge", "Sub-Workflow")
6352
6832
  })
@@ -6357,7 +6837,7 @@ function ExecutionDetailPage() {
6357
6837
  "data-testid": "run-summary",
6358
6838
  children: parityRun.summary
6359
6839
  }),
6360
- /* @__PURE__ */ jsxs("div", {
6840
+ !v2 && /* @__PURE__ */ jsxs("div", {
6361
6841
  className: "flex flex-wrap items-center gap-4 text-sm text-text-secondary",
6362
6842
  children: [
6363
6843
  /* @__PURE__ */ jsxs("span", {
@@ -6398,7 +6878,95 @@ function ExecutionDetailPage() {
6398
6878
  })
6399
6879
  ]
6400
6880
  }),
6401
- /* @__PURE__ */ jsx("div", {
6881
+ v2 && /* @__PURE__ */ jsxs("div", {
6882
+ className: "flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-text-secondary",
6883
+ "data-testid": "run-header-meta",
6884
+ children: [
6885
+ execution.triggeredById && /* @__PURE__ */ jsx("span", {
6886
+ title: execution.triggeredById,
6887
+ children: tr("fluidgrids.executions.executionDetail.startedByPerson", "Started by {{name}}", { name: triggeredByLabel ?? "" })
6888
+ }),
6889
+ /* @__PURE__ */ jsxs("span", {
6890
+ className: "flex items-center gap-1",
6891
+ children: [/* @__PURE__ */ jsx(Icon, {
6892
+ icon: "mdi:clock-outline",
6893
+ width: 16
6894
+ }), formatDateTime(execution.startedAt || execution.createdAt)]
6895
+ }),
6896
+ execution.completedAt && /* @__PURE__ */ jsx("span", { children: tr("fluidgrids.executions.executionDetail.finishedAt", "Finished {{when}}", { when: formatDateTime(execution.completedAt) }) })
6897
+ ]
6898
+ }),
6899
+ v2 && /* @__PURE__ */ jsx(TechnicalDetails, {
6900
+ className: "mt-3",
6901
+ "data-testid": "run-technical",
6902
+ children: /* @__PURE__ */ jsxs("div", {
6903
+ className: "space-y-2 text-sm text-text-secondary",
6904
+ children: [
6905
+ /* @__PURE__ */ jsxs("div", {
6906
+ className: "flex flex-wrap items-center gap-3",
6907
+ children: [/* @__PURE__ */ jsxs("span", {
6908
+ className: "flex items-center gap-1",
6909
+ children: [/* @__PURE__ */ jsx(Icon, {
6910
+ icon: "mdi:identifier",
6911
+ width: 16
6912
+ }), /* @__PURE__ */ jsx("code", {
6913
+ className: "font-mono break-all",
6914
+ children: execution.id
6915
+ })]
6916
+ }), /* @__PURE__ */ jsxs(Button$1, {
6917
+ variant: "outline",
6918
+ size: "sm",
6919
+ onClick: () => void copyReference(execution.id),
6920
+ "data-testid": "run-copy-reference",
6921
+ children: [/* @__PURE__ */ jsx(Icon, {
6922
+ icon: "mdi:content-copy",
6923
+ className: "mr-1",
6924
+ width: 14
6925
+ }), tr("fluidgrids.executions.executionDetail.copyReference", "Copy reference")]
6926
+ })]
6927
+ }),
6928
+ parityRun?.businessId && /* @__PURE__ */ jsxs("span", {
6929
+ className: "flex items-center gap-1",
6930
+ "data-testid": "run-workflow-id",
6931
+ title: parityRun.businessId,
6932
+ children: [/* @__PURE__ */ jsx(Icon, {
6933
+ icon: "mdi:key-outline",
6934
+ width: 16
6935
+ }), /* @__PURE__ */ jsx("code", {
6936
+ className: "max-w-[16rem] truncate font-mono",
6937
+ children: parityRun.businessId
6938
+ })]
6939
+ }),
6940
+ /* @__PURE__ */ jsxs("span", {
6941
+ className: "flex items-center gap-1",
6942
+ children: [/* @__PURE__ */ jsx(Icon, {
6943
+ icon: "mdi:tag-outline",
6944
+ width: 16
6945
+ }), formatRunVersion(execution.version)]
6946
+ }),
6947
+ execution.parentRunId && /* @__PURE__ */ jsx("span", {
6948
+ className: "flex items-center gap-2",
6949
+ children: /* @__PURE__ */ jsx("span", {
6950
+ className: "px-2 py-0.5 text-xs bg-chart-12/10 text-chart-12 rounded",
6951
+ children: tr("fluidgrids.executions.executionDetail.subWorkflowBadge", "Sub-Workflow")
6952
+ })
6953
+ }),
6954
+ /* @__PURE__ */ jsx(RunCallPath, {
6955
+ view: hierarchy,
6956
+ parentRunId: execution.parentRunId
6957
+ }),
6958
+ /* @__PURE__ */ jsx("div", {
6959
+ "data-testid": "run-tags",
6960
+ children: /* @__PURE__ */ jsx(EntityTagPicker, {
6961
+ entityType: "FLUIDGRIDS_RUN",
6962
+ entityId: execution.id,
6963
+ readOnly: !canTagRun
6964
+ })
6965
+ })
6966
+ ]
6967
+ })
6968
+ }),
6969
+ !v2 && /* @__PURE__ */ jsx("div", {
6402
6970
  className: "mt-3",
6403
6971
  "data-testid": "run-tags",
6404
6972
  children: /* @__PURE__ */ jsx(EntityTagPicker, {
@@ -6410,7 +6978,7 @@ function ExecutionDetailPage() {
6410
6978
  ] }), /* @__PURE__ */ jsxs("div", {
6411
6979
  className: "flex flex-wrap items-center gap-2",
6412
6980
  children: [
6413
- isActive && /* @__PURE__ */ jsxs("label", {
6981
+ !v2 && isActive && /* @__PURE__ */ jsxs("label", {
6414
6982
  className: "flex items-center gap-2 text-sm text-text-secondary mr-4",
6415
6983
  children: [/* @__PURE__ */ jsx("input", {
6416
6984
  type: "checkbox",
@@ -6419,7 +6987,7 @@ function ExecutionDetailPage() {
6419
6987
  className: "w-4 h-4 rounded"
6420
6988
  }), tr("fluidgrids.executions.executionDetail.autoRefreshToggle", "Auto-refresh")]
6421
6989
  }),
6422
- canControlRuns && execution.status === "RUNNING" && /* @__PURE__ */ jsxs(Fragment, { children: [!isDurableRun && /* @__PURE__ */ jsxs(Button$1, {
6990
+ canControlRuns && execution.status === "RUNNING" && /* @__PURE__ */ jsxs(Fragment$1, { children: [!v2 && !isDurableRun && /* @__PURE__ */ jsxs(Button$1, {
6423
6991
  "data-agent": "confirm",
6424
6992
  variant: "outline",
6425
6993
  size: "sm",
@@ -6434,7 +7002,7 @@ function ExecutionDetailPage() {
6434
7002
  "data-agent": "confirm",
6435
7003
  variant: "outline",
6436
7004
  size: "sm",
6437
- onClick: handleCancel,
7005
+ onClick: requestCancel,
6438
7006
  disabled: actionLoading,
6439
7007
  className: "border-[var(--color-status-warning-border)] text-[var(--color-status-warning-text)] hover:bg-[var(--color-status-warning-bg-subtle)]",
6440
7008
  children: [/* @__PURE__ */ jsx(Icon, {
@@ -6443,7 +7011,7 @@ function ExecutionDetailPage() {
6443
7011
  width: 16
6444
7012
  }), tr("fluidgrids.executions.executionDetail.cancelButton", "Cancel")]
6445
7013
  })] }),
6446
- canControlRuns && execution.status === "WAITING" && /* @__PURE__ */ jsxs(Fragment, { children: [
7014
+ canControlRuns && execution.status === "WAITING" && /* @__PURE__ */ jsxs(Fragment$1, { children: [
6447
7015
  humanRequestsCurrent && !awaitingPerson && !isDurableRun && !waitingOnSignal && /* @__PURE__ */ jsxs(Button$1, {
6448
7016
  "data-agent": "confirm",
6449
7017
  variant: "outline",
@@ -6474,7 +7042,7 @@ function ExecutionDetailPage() {
6474
7042
  "data-agent": "confirm",
6475
7043
  variant: "outline",
6476
7044
  size: "sm",
6477
- onClick: handleCancel,
7045
+ onClick: requestCancel,
6478
7046
  disabled: actionLoading,
6479
7047
  className: "border-[var(--color-status-warning-border)] text-[var(--color-status-warning-text)] hover:bg-[var(--color-status-warning-bg-subtle)]",
6480
7048
  children: [/* @__PURE__ */ jsx(Icon, {
@@ -6488,7 +7056,7 @@ function ExecutionDetailPage() {
6488
7056
  "data-agent": "confirm",
6489
7057
  variant: "outline",
6490
7058
  size: "sm",
6491
- onClick: handleCancel,
7059
+ onClick: requestCancel,
6492
7060
  disabled: actionLoading,
6493
7061
  children: [/* @__PURE__ */ jsx(Icon, {
6494
7062
  icon: "mdi:stop",
@@ -6496,7 +7064,7 @@ function ExecutionDetailPage() {
6496
7064
  width: 16
6497
7065
  }), tr("fluidgrids.executions.executionDetail.cancelButton", "Cancel")]
6498
7066
  }),
6499
- canSendSignal && /* @__PURE__ */ jsxs(Button$1, {
7067
+ !v2 && canSendSignal && /* @__PURE__ */ jsxs(Button$1, {
6500
7068
  variant: "outline",
6501
7069
  size: "sm",
6502
7070
  onClick: () => setShowSignalDialog(true),
@@ -6507,7 +7075,7 @@ function ExecutionDetailPage() {
6507
7075
  width: 16
6508
7076
  }), tr("fluidgrids.executions.executionDetail.signalButton", "Signal")]
6509
7077
  }),
6510
- (isDurableRun ? canTerminateDurable : canControlRuns) && (execution.status === "RUNNING" || execution.status === "WAITING") && /* @__PURE__ */ jsxs(Button$1, {
7078
+ !v2 && (isDurableRun ? canTerminateDurable : canControlRuns) && (execution.status === "RUNNING" || execution.status === "WAITING") && /* @__PURE__ */ jsxs(Button$1, {
6511
7079
  variant: "outline",
6512
7080
  size: "sm",
6513
7081
  onClick: () => setShowTerminateDialog(true),
@@ -6519,7 +7087,7 @@ function ExecutionDetailPage() {
6519
7087
  width: 16
6520
7088
  }), tr("fluidgrids.executions.executionDetail.terminateButton", "Terminate")]
6521
7089
  }),
6522
- isHistoricalTestRun && execution.workflowKey && execution.version && /* @__PURE__ */ jsxs(Button$1, {
7090
+ !v2 && isHistoricalTestRun && execution.workflowKey && execution.version && /* @__PURE__ */ jsxs(Button$1, {
6523
7091
  variant: "outline",
6524
7092
  size: "sm",
6525
7093
  onClick: handleLoadInBuilder,
@@ -6529,7 +7097,7 @@ function ExecutionDetailPage() {
6529
7097
  width: 16
6530
7098
  }), tr("fluidgrids.executions.executionDetail.loadTestRunButton", "Load TEST run in builder")]
6531
7099
  }),
6532
- canControlRuns && !isDurableRun && execution.status !== "RUNNING" && execution.status !== "PENDING" && execution.status !== "WAITING" && /* @__PURE__ */ jsxs(Button$1, {
7100
+ !v2 && canControlRuns && !isDurableRun && execution.status !== "RUNNING" && execution.status !== "PENDING" && execution.status !== "WAITING" && /* @__PURE__ */ jsxs(Button$1, {
6533
7101
  "data-agent": "confirm",
6534
7102
  variant: "outline",
6535
7103
  size: "sm",
@@ -6556,10 +7124,22 @@ function ExecutionDetailPage() {
6556
7124
  className: "mr-1",
6557
7125
  width: 16
6558
7126
  }), tr("fluidgrids.executions.executionDetail.refreshButton", "Refresh")]
7127
+ }),
7128
+ v2 && /* @__PURE__ */ jsx(RunActionsMenu, {
7129
+ items: menuItems,
7130
+ toggles: menuToggles
6559
7131
  })
6560
7132
  ]
6561
7133
  })]
6562
7134
  }),
7135
+ summary && /* @__PURE__ */ jsx(RunSummaryCard, {
7136
+ status: execution.status,
7137
+ sentence: summary.sentence,
7138
+ onRunAgain: canRunAgain ? () => void handleRunAgain() : void 0,
7139
+ runAgainBusy: actionLoading,
7140
+ onOpenWorkflow: execution.workflowKey ? handleOpenWorkflow : void 0,
7141
+ reconnectTo: needsReconnect(summary.errorType) ? CONNECTIONS_ROUTE : void 0
7142
+ }),
6563
7143
  /* @__PURE__ */ jsxs("div", {
6564
7144
  className: "grid grid-cols-2 sm:grid-cols-4 gap-4 mb-6",
6565
7145
  children: [
@@ -6637,7 +7217,24 @@ function ExecutionDetailPage() {
6637
7217
  })
6638
7218
  ]
6639
7219
  }),
6640
- execution.errorMessage && /* @__PURE__ */ jsxs(AskableDiv, {
7220
+ v2 && execution.errorMessage && /* @__PURE__ */ jsx(AskableDiv, {
7221
+ describe: () => describeRunError(askRun),
7222
+ "data-testid": "run-error-banner",
7223
+ className: "mb-6",
7224
+ children: /* @__PURE__ */ jsx(TechnicalDetails, { children: /* @__PURE__ */ jsx(AskableSelectionDiv, {
7225
+ describe: () => describeRunError(askRun),
7226
+ children: /* @__PURE__ */ jsxs("p", {
7227
+ className: "text-sm text-[var(--color-status-error-text)] break-words",
7228
+ children: [
7229
+ tr("fluidgrids.executions.executionDetail.errorLabel", "Error"),
7230
+ execution.errorType ? ` (${execution.errorType})` : "",
7231
+ ": ",
7232
+ execution.errorMessage
7233
+ ]
7234
+ })
7235
+ }) })
7236
+ }),
7237
+ !v2 && execution.errorMessage && /* @__PURE__ */ jsxs(AskableDiv, {
6641
7238
  describe: () => describeRunError(askRun),
6642
7239
  "data-testid": "run-error-banner",
6643
7240
  className: "mb-6 p-4 bg-[var(--color-status-error-bg-subtle)] border border-[var(--color-status-error-border)] rounded-lg",
@@ -6847,7 +7444,19 @@ function ExecutionDetailPage() {
6847
7444
  onLoadMore: () => void runLogs.loadMore()
6848
7445
  })
6849
7446
  }),
6850
- activeTab === "input" && /* @__PURE__ */ jsxs("div", {
7447
+ activeTab === "input" && v2 && /* @__PURE__ */ jsxs("div", {
7448
+ className: "space-y-6 p-4 sm:p-6",
7449
+ "data-testid": "run-data-tab",
7450
+ children: [
7451
+ triggerDataBlock,
7452
+ contextJsonBlock,
7453
+ /* @__PURE__ */ jsx(TechnicalDetails, {
7454
+ "data-testid": "run-context-details",
7455
+ children: contextDetailsBlock
7456
+ })
7457
+ ]
7458
+ }),
7459
+ activeTab === "input" && !v2 && /* @__PURE__ */ jsxs("div", {
6851
7460
  className: "p-4 sm:p-6",
6852
7461
  children: [/* @__PURE__ */ jsx("h3", {
6853
7462
  className: "text-lg font-medium text-text-primary mb-4",
@@ -6860,162 +7469,11 @@ function ExecutionDetailPage() {
6860
7469
  })
6861
7470
  })]
6862
7471
  }),
6863
- activeTab === "context" && /* @__PURE__ */ jsx("div", {
7472
+ activeTab === "context" && !v2 && /* @__PURE__ */ jsx("div", {
6864
7473
  className: "p-4 sm:p-6",
6865
7474
  children: /* @__PURE__ */ jsxs("div", {
6866
7475
  className: "grid grid-cols-1 lg:grid-cols-2 gap-6",
6867
- children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h3", {
6868
- className: "text-lg font-medium text-text-primary mb-4",
6869
- children: tr("fluidgrids.executions.executionDetail.executionContextHeading", "Execution Context")
6870
- }), /* @__PURE__ */ jsx("div", {
6871
- className: "bg-background-primary rounded p-4 max-h-[300px] overflow-auto",
6872
- children: /* @__PURE__ */ jsx("pre", {
6873
- className: "text-sm text-text-primary font-mono whitespace-pre-wrap break-words",
6874
- children: JSON.stringify(execution.context ?? null, null, 2)
6875
- })
6876
- })] }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h3", {
6877
- className: "text-lg font-medium text-text-primary mb-4",
6878
- children: tr("fluidgrids.executions.executionDetail.executionDetailsHeading", "Execution Details")
6879
- }), /* @__PURE__ */ jsxs("div", {
6880
- className: "space-y-3",
6881
- children: [
6882
- /* @__PURE__ */ jsxs("div", {
6883
- className: "flex justify-between py-2 border-b border-border-secondary",
6884
- children: [/* @__PURE__ */ jsx("span", {
6885
- className: "text-text-secondary",
6886
- children: tr("fluidgrids.executions.executionDetail.runIdRow", "Run ID")
6887
- }), /* @__PURE__ */ jsx("code", {
6888
- className: "font-mono text-text-primary",
6889
- children: execution.id
6890
- })]
6891
- }),
6892
- /* @__PURE__ */ jsxs("div", {
6893
- className: "flex justify-between py-2 border-b border-border-secondary",
6894
- children: [/* @__PURE__ */ jsx("span", {
6895
- className: "text-text-secondary",
6896
- children: tr("fluidgrids.executions.executionDetail.workflowRow", "Workflow")
6897
- }), /* @__PURE__ */ jsx("span", {
6898
- className: "text-text-primary",
6899
- children: execution.workflow?.name || execution.workflowKey
6900
- })]
6901
- }),
6902
- /* @__PURE__ */ jsxs("div", {
6903
- className: "flex justify-between py-2 border-b border-border-secondary",
6904
- children: [/* @__PURE__ */ jsx("span", {
6905
- className: "text-text-secondary",
6906
- children: tr("fluidgrids.executions.executionDetail.workflowKeyRow", "Workflow Key")
6907
- }), /* @__PURE__ */ jsx("code", {
6908
- className: "font-mono text-text-primary",
6909
- children: execution.workflowKey
6910
- })]
6911
- }),
6912
- /* @__PURE__ */ jsxs("div", {
6913
- className: "flex justify-between py-2 border-b border-border-secondary",
6914
- children: [/* @__PURE__ */ jsx("span", {
6915
- className: "text-text-secondary",
6916
- children: tr("fluidgrids.executions.executionDetail.versionRow", "Version")
6917
- }), /* @__PURE__ */ jsx("span", {
6918
- className: "text-text-primary",
6919
- children: formatRunVersion(execution.version)
6920
- })]
6921
- }),
6922
- isDurableRun && /* @__PURE__ */ jsxs(Fragment, { children: [
6923
- /* @__PURE__ */ jsxs("div", {
6924
- className: "flex justify-between py-2 border-b border-border-secondary",
6925
- children: [/* @__PURE__ */ jsx("span", {
6926
- className: "text-text-secondary",
6927
- children: tr("fluidgrids.executions.executionDetail.workflowFnRow", "Function")
6928
- }), /* @__PURE__ */ jsx("code", {
6929
- className: "font-mono text-text-primary",
6930
- children: execution.workflowFn ?? "—"
6931
- })]
6932
- }),
6933
- /* @__PURE__ */ jsxs("div", {
6934
- className: "flex justify-between py-2 border-b border-border-secondary",
6935
- children: [/* @__PURE__ */ jsx("span", {
6936
- className: "text-text-secondary",
6937
- children: tr("fluidgrids.executions.executionDetail.taskQueueRow", "Task queue")
6938
- }), /* @__PURE__ */ jsx("span", {
6939
- className: "text-text-primary",
6940
- children: execution.taskQueue ?? "—"
6941
- })]
6942
- }),
6943
- /* @__PURE__ */ jsxs("div", {
6944
- className: "flex justify-between gap-4 py-2 border-b border-border-secondary",
6945
- children: [/* @__PURE__ */ jsx("span", {
6946
- className: "text-text-secondary",
6947
- children: tr("fluidgrids.executions.executionDetail.buildIdRow", "Build")
6948
- }), /* @__PURE__ */ jsx("code", {
6949
- className: "truncate font-mono text-text-primary",
6950
- title: execution.buildId ?? void 0,
6951
- children: execution.buildId ?? "—"
6952
- })]
6953
- }),
6954
- /* @__PURE__ */ jsxs("div", {
6955
- className: "flex justify-between py-2 border-b border-border-secondary",
6956
- children: [/* @__PURE__ */ jsx("span", {
6957
- className: "text-text-secondary",
6958
- children: tr("fluidgrids.executions.executionDetail.historyLengthRow", "History events")
6959
- }), /* @__PURE__ */ jsx("span", {
6960
- className: "text-text-primary",
6961
- children: execution.historyLength ?? 0
6962
- })]
6963
- })
6964
- ] }),
6965
- execution.triggeredById && /* @__PURE__ */ jsxs("div", {
6966
- className: "flex justify-between py-2 border-b border-border-secondary",
6967
- children: [/* @__PURE__ */ jsx("span", {
6968
- className: "text-text-secondary",
6969
- children: tr("fluidgrids.executions.executionDetail.triggeredByRow", "Triggered By")
6970
- }), /* @__PURE__ */ jsx("span", {
6971
- className: "text-text-primary",
6972
- title: execution.triggeredById,
6973
- children: triggeredByLabel
6974
- })]
6975
- }),
6976
- execution.parentRunId && /* @__PURE__ */ jsxs("div", {
6977
- className: "flex justify-between py-2 border-b border-border-secondary",
6978
- children: [/* @__PURE__ */ jsx("span", {
6979
- className: "text-text-secondary",
6980
- children: tr("fluidgrids.executions.executionDetail.parentRunRow", "Parent Run")
6981
- }), /* @__PURE__ */ jsxs(Link, {
6982
- to: `../runs/${execution.parentRunId}`,
6983
- className: "text-text-link hover:text-text-linkHover hover:underline font-mono",
6984
- children: [execution.parentRunId.slice(0, 12), "..."]
6985
- })]
6986
- }),
6987
- /* @__PURE__ */ jsxs("div", {
6988
- className: "flex justify-between py-2 border-b border-border-secondary",
6989
- children: [/* @__PURE__ */ jsx("span", {
6990
- className: "text-text-secondary",
6991
- children: tr("fluidgrids.executions.executionDetail.createdRow", "Created")
6992
- }), /* @__PURE__ */ jsx("span", {
6993
- className: "text-text-primary",
6994
- children: formatDateTime(execution.createdAt)
6995
- })]
6996
- }),
6997
- /* @__PURE__ */ jsxs("div", {
6998
- className: "flex justify-between py-2 border-b border-border-secondary",
6999
- children: [/* @__PURE__ */ jsx("span", {
7000
- className: "text-text-secondary",
7001
- children: tr("fluidgrids.executions.executionDetail.startedRow", "Started")
7002
- }), /* @__PURE__ */ jsx("span", {
7003
- className: "text-text-primary",
7004
- children: formatDateTime(execution.startedAt)
7005
- })]
7006
- }),
7007
- execution.completedAt && /* @__PURE__ */ jsxs("div", {
7008
- className: "flex justify-between py-2 border-b border-border-secondary",
7009
- children: [/* @__PURE__ */ jsx("span", {
7010
- className: "text-text-secondary",
7011
- children: tr("fluidgrids.executions.executionDetail.completedRow", "Completed")
7012
- }), /* @__PURE__ */ jsx("span", {
7013
- className: "text-text-primary",
7014
- children: formatDateTime(execution.completedAt)
7015
- })]
7016
- })
7017
- ]
7018
- })] })]
7476
+ children: [contextJsonBlock, contextDetailsBlock]
7019
7477
  })
7020
7478
  })
7021
7479
  ]
@@ -7037,12 +7495,22 @@ function ExecutionDetailPage() {
7037
7495
  onOpenChange: setShowOverrideDialog,
7038
7496
  onChanged: () => void refetch()
7039
7497
  }),
7498
+ v2 && /* @__PURE__ */ jsx(ConfirmStopRunDialog, {
7499
+ open: showStopDialog,
7500
+ workflowName: execution.workflow?.name || execution.workflowKey,
7501
+ busy: actionLoading,
7502
+ onConfirm: () => {
7503
+ setShowStopDialog(false);
7504
+ handleCancel();
7505
+ },
7506
+ onOpenChange: setShowStopDialog
7507
+ }),
7040
7508
  /* @__PURE__ */ jsx(AlertDialog, {
7041
7509
  open: showTerminateDialog,
7042
7510
  onOpenChange: setShowTerminateDialog,
7043
7511
  children: /* @__PURE__ */ jsxs(AlertDialogContent, { children: [/* @__PURE__ */ jsxs(AlertDialogHeader, { children: [/* @__PURE__ */ jsx(AlertDialogTitle, { children: tr("fluidgrids.executions.executionDetail.terminateDialogTitle", "Terminate Execution") }), /* @__PURE__ */ jsx(AlertDialogDescription, { children: tr("fluidgrids.executions.executionDetail.terminateDialogDescription", "Are you sure you want to force terminate this execution? This will immediately stop the running workflow and cannot be resumed.") })] }), /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [/* @__PURE__ */ jsx(AlertDialogCancel, {
7044
7512
  disabled: actionLoading,
7045
- children: tr("fluidgrids.executions.executionDetail.cancelButton", "Cancel")
7513
+ children: tr("fluidgrids.executions.executionDetail.terminateKeepButton", "Keep run")
7046
7514
  }), /* @__PURE__ */ jsx(AlertDialogAction, {
7047
7515
  onClick: handleTerminate,
7048
7516
  disabled: actionLoading,