@burdenoff/fluidgrids-fe-sdk 2026.1029.5 → 2026.1029.7

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 (43) hide show
  1. package/dist/ExecutionDetailPage-L7LZYFOT.js +22 -0
  2. package/dist/{WorkflowsList-2OYIXGWQ.js → ExecutionsListPage-7NMW6S5A.js} +9 -9
  3. package/dist/{WorkflowBuilder-EVZGOXZZ.js → WorkflowBuilder-RXVUBDZJ.js} +12 -12
  4. package/dist/{ExecutionsListPage-KC3KRBNS.js → WorkflowsList-5U74DYQ3.js} +9 -8
  5. package/dist/{chunk-5ITN7CKS.js → chunk-4MHWFZJV.js} +1 -1
  6. package/dist/{chunk-CY6KX4RG.js → chunk-6AMNEX2Y.js} +6 -1
  7. package/dist/{chunk-XRHZ3IEL.js → chunk-6SZXW2BF.js} +255 -8
  8. package/dist/{chunk-U6KI4AKI.js → chunk-6TMLIQHB.js} +199 -1
  9. package/dist/{chunk-EAJB5BNF.js → chunk-7CTIVBBA.js} +183 -26
  10. package/dist/{chunk-UZMLMPFJ.js → chunk-AXMEATLG.js} +117 -8
  11. package/dist/{chunk-NM5L7MBJ.js → chunk-BZTVAUOQ.js} +3 -3
  12. package/dist/{chunk-X3LQJLV7.js → chunk-DEGCZCHP.js} +2 -2
  13. package/dist/{chunk-WDTO6ATM.js → chunk-E7UAY3JJ.js} +2 -2
  14. package/dist/{chunk-O24MDGZY.js → chunk-EGIEFGOV.js} +677 -43
  15. package/dist/{chunk-LGGHQ7UE.js → chunk-FFOZ5VUJ.js} +1 -1
  16. package/dist/{chunk-6Y6I2ODM.js → chunk-HAFUSBYP.js} +937 -26
  17. package/dist/{chunk-ZDFDC5YW.js → chunk-IDGDMJMJ.js} +118 -96
  18. package/dist/{chunk-2U56452G.js → chunk-K4BF7PAW.js} +125 -89
  19. package/dist/{chunk-MNPZMFLF.js → chunk-KANARCPR.js} +3 -3
  20. package/dist/{chunk-5GD6OXYW.js → chunk-LAVHDFHI.js} +1 -1
  21. package/dist/{chunk-35SQULD5.js → chunk-NL4JQFFA.js} +1 -1
  22. package/dist/{chunk-SRE2JVSJ.js → chunk-OJPJ2QXD.js} +3 -3
  23. package/dist/{chunk-JUA57OVN.js → chunk-RZR3BDDZ.js} +7 -7
  24. package/dist/{chunk-DASEJQ4G.js → chunk-SZTKLZZR.js} +145 -8
  25. package/dist/{chunk-LB62TAF7.js → chunk-TYYSYSAL.js} +1 -1
  26. package/dist/{chunk-VYH65RLH.js → chunk-US4LPOB3.js} +1 -1
  27. package/dist/{chunk-XTXT4NHE.js → chunk-VIZ7W5WJ.js} +5 -5
  28. package/dist/{chunk-7PP6CRXS.js → chunk-YTUIGIVP.js} +15 -1
  29. package/dist/{chunk-TNPK2LZT.js → chunk-Z4YQTXCD.js} +4 -4
  30. package/dist/{chunk-R4SBXQ2K.js → chunk-ZE5KS2YL.js} +2 -2
  31. package/dist/{config-T7BQ7AO2.js → config-VM7FTHMZ.js} +1 -1
  32. package/dist/executions.js +13 -13
  33. package/dist/history.js +5 -5
  34. package/dist/index.d.ts +2529 -24
  35. package/dist/index.js +118 -51
  36. package/dist/nodes.js +2 -2
  37. package/dist/observability.js +7 -7
  38. package/dist/settings.js +7 -7
  39. package/dist/templates.js +11 -11
  40. package/dist/workflow-builder.js +13 -13
  41. package/dist/workflows-list.js +9 -9
  42. package/package.json +1 -1
  43. package/dist/ExecutionDetailPage-Y4K35GJ7.js +0 -21
@@ -1,12 +1,13 @@
1
- import { buildNodeDisplayMap, ExecutionStatusBadge, formatRunVersion, NodeTimeline, RunLogViewer } from './chunk-EAJB5BNF.js';
2
- import { useExecution, useExecutionActions, useAutoRefresh } from './chunk-UZMLMPFJ.js';
3
- import { fetchRunFailureClusters, OPEN_STATUSES, dismissRunFailureCluster, BUCKET_LABELS } from './chunk-R4SBXQ2K.js';
4
- import { useRunPolicyDecisions, RunPolicyDecisions } from './chunk-35SQULD5.js';
5
- import { useDurableRunFields, useRunHistory, useDeterminismStatus, useRunStackTrace, EntityTagPicker, RunHistoryView, SignalRunDialog, terminateDurableRun, useWorkflowProposal, WorkflowProposalCard } from './chunk-ZDFDC5YW.js';
1
+ import { useDurableParityRunFields, buildNodeDisplayMap, useRunMetadata, useRunPending, ExecutionStatusBadge, formatRunVersion, NodeTimeline, RunLogViewer, setRunVersioningOverride } from './chunk-7CTIVBBA.js';
2
+ import { useExecution, useExecutionActions, useAutoRefresh, runChainLinks, shortRunId, callbackTone, parseMetadataDefinition, overrideRequestOf } from './chunk-AXMEATLG.js';
3
+ import { fetchRunFailureClusters, OPEN_STATUSES, dismissRunFailureCluster, BUCKET_LABELS } from './chunk-ZE5KS2YL.js';
4
+ import { useRunPolicyDecisions, RunPolicyDecisions } from './chunk-NL4JQFFA.js';
5
+ import { useDurableRunFields, useRunHistory, useDeterminismStatus, useRunStackTrace, EntityTagPicker, RunHistoryView, SignalRunDialog, terminateDurableRun, useWorkflowProposal, WorkflowProposalCard } from './chunk-IDGDMJMJ.js';
6
6
  import { AiCreditBalanceSlot } from './chunk-Y53CVZVV.js';
7
- import { throwIfResultError, useFluidGridsPermission, FLUIDGRIDS_PERMISSIONS, useDurableFunctionNodesEnabled, runSignalTargets, canSignalRun } from './chunk-2U56452G.js';
8
- import { getFluidGridsConfig } from './chunk-CY6KX4RG.js';
9
- import { RunLogsDocument, RunLogAppendedDocument, ExplainRunDocument } from './chunk-7PP6CRXS.js';
7
+ import { useWorkflowVersions } from './chunk-OJPJ2QXD.js';
8
+ import { throwIfResultError, useFluidGridsPermission, FLUIDGRIDS_PERMISSIONS, useDurableFunctionNodesEnabled, useDurableParityEnabled, runSignalTargets, canSignalRun, describeOperationError } from './chunk-K4BF7PAW.js';
9
+ import { getFluidGridsConfig } from './chunk-6AMNEX2Y.js';
10
+ import { RunLogsDocument, RunLogAppendedDocument, ExplainRunDocument } from './chunk-YTUIGIVP.js';
10
11
  import { useTr } from './chunk-UULVIT25.js';
11
12
  import { getFluidGridsClient, getFluidGridsClientSafe } from './chunk-NTWG3CJX.js';
12
13
  import { useState, useRef, useCallback, useEffect, useMemo } from 'react';
@@ -18,6 +19,8 @@ import { Card, CardHeader, Badge, CardContent, Label, Textarea, AlertDialog, Ale
18
19
  import { useActorProfiles, buildActorFallback } from '@burdenoff/fe-libs/shared';
19
20
  import { gql } from '@apollo/client';
20
21
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
22
+ import ReactMarkdown from 'react-markdown';
23
+ import { cn } from '@burdenoff/fe-libs/utils';
21
24
 
22
25
  var POLL_INTERVAL_MS = 2e3;
23
26
  var PAGE_SIZE = 500;
@@ -1248,11 +1251,130 @@ function DurableDeterminismBanner({
1248
1251
  }
1249
1252
  );
1250
1253
  }
1254
+ var SAFE_LINK = /^(https?:|mailto:)/i;
1255
+ function safeMarkdownHref(url) {
1256
+ const value = url.trim();
1257
+ return SAFE_LINK.test(value) ? value : void 0;
1258
+ }
1259
+ var COMPONENTS = {
1260
+ h1: ({ children }) => /* @__PURE__ */ jsx("p", { className: "mb-1 mt-2 text-sm font-semibold text-text-primary", children }),
1261
+ h2: ({ children }) => /* @__PURE__ */ jsx("p", { className: "mb-1 mt-2 text-sm font-semibold text-text-primary", children }),
1262
+ h3: ({ children }) => /* @__PURE__ */ jsx("p", { className: "mb-1 mt-2 text-xs font-semibold uppercase tracking-wide text-text-secondary", children }),
1263
+ p: ({ children }) => /* @__PURE__ */ jsx("p", { className: "mb-2 last:mb-0", children }),
1264
+ ul: ({ children }) => /* @__PURE__ */ jsx("ul", { className: "mb-2 list-disc space-y-0.5 pl-5 last:mb-0", children }),
1265
+ ol: ({ children }) => /* @__PURE__ */ jsx("ol", { className: "mb-2 list-decimal space-y-0.5 pl-5 last:mb-0", children }),
1266
+ a: ({ children, href }) => {
1267
+ const safe = href ? safeMarkdownHref(href) : void 0;
1268
+ return safe ? /* @__PURE__ */ jsx(
1269
+ "a",
1270
+ {
1271
+ href: safe,
1272
+ target: "_blank",
1273
+ rel: "noreferrer noopener",
1274
+ className: "text-text-link underline underline-offset-2 hover:text-text-linkHover",
1275
+ children
1276
+ }
1277
+ ) : /* @__PURE__ */ jsx("span", { children });
1278
+ },
1279
+ img: ({ alt }) => /* @__PURE__ */ jsx("span", { children: alt ?? "" }),
1280
+ code: ({ children }) => /* @__PURE__ */ jsx("code", { className: "rounded bg-background-tertiary px-1 py-0.5 font-mono text-xs text-text-primary", children }),
1281
+ pre: ({ children }) => /* @__PURE__ */ jsx("pre", { className: "mb-2 overflow-auto rounded bg-background-tertiary p-2 font-mono text-xs", children }),
1282
+ hr: () => /* @__PURE__ */ jsx("hr", { className: "my-2 border-border-secondary" })
1283
+ };
1284
+ function urlTransform(url, key) {
1285
+ if (key === "src") return void 0;
1286
+ return safeMarkdownHref(url);
1287
+ }
1288
+ function RunMarkdown({
1289
+ text,
1290
+ "data-testid": testId
1291
+ }) {
1292
+ return /* @__PURE__ */ jsx(
1293
+ "div",
1294
+ {
1295
+ "data-testid": testId,
1296
+ className: "break-words text-sm leading-relaxed text-text-primary",
1297
+ children: /* @__PURE__ */ jsx(
1298
+ ReactMarkdown,
1299
+ {
1300
+ components: COMPONENTS,
1301
+ skipHtml: true,
1302
+ urlTransform,
1303
+ children: text
1304
+ }
1305
+ )
1306
+ }
1307
+ );
1308
+ }
1309
+ function HandlerList({
1310
+ title,
1311
+ handlers,
1312
+ testId
1313
+ }) {
1314
+ if (handlers.length === 0) return null;
1315
+ return /* @__PURE__ */ jsxs("div", { "data-testid": testId, children: [
1316
+ /* @__PURE__ */ jsx("h5", { className: "text-xs font-medium uppercase tracking-wide text-text-secondary", children: title }),
1317
+ /* @__PURE__ */ jsx("ul", { className: "mt-1 space-y-0.5", children: handlers.map((handler) => /* @__PURE__ */ jsxs("li", { className: "text-xs text-text-primary", children: [
1318
+ /* @__PURE__ */ jsx("code", { className: "font-mono", children: handler.name }),
1319
+ handler.description && /* @__PURE__ */ jsxs("span", { className: "text-text-secondary", children: [
1320
+ " ",
1321
+ "\xB7 ",
1322
+ handler.description
1323
+ ] })
1324
+ ] }, handler.name)) })
1325
+ ] });
1326
+ }
1327
+ function MetadataSection({ metadata }) {
1328
+ const tr = useTr();
1329
+ const definition = parseMetadataDefinition(metadata.definition);
1330
+ const hasHandlers = definition.signals.length > 0 || definition.queries.length > 0 || definition.updates.length > 0;
1331
+ if (!hasHandlers && !metadata.currentDetails) return null;
1332
+ return /* @__PURE__ */ jsxs(
1333
+ "div",
1334
+ {
1335
+ "data-testid": "waiting-at-metadata",
1336
+ className: "mt-3 space-y-3 border-t border-border-secondary pt-3",
1337
+ children: [
1338
+ metadata.currentDetails && /* @__PURE__ */ jsxs("div", { children: [
1339
+ /* @__PURE__ */ jsx("h5", { className: "text-xs font-medium uppercase tracking-wide text-text-secondary", children: tr("fluidgrids.executions.waitingAt.currentDetails", "Now") }),
1340
+ /* @__PURE__ */ jsx(RunMarkdown, { text: metadata.currentDetails })
1341
+ ] }),
1342
+ hasHandlers && /* @__PURE__ */ jsxs("div", { className: "grid gap-3 sm:grid-cols-3", children: [
1343
+ /* @__PURE__ */ jsx(
1344
+ HandlerList,
1345
+ {
1346
+ title: tr("fluidgrids.executions.waitingAt.signals", "Signals"),
1347
+ handlers: definition.signals,
1348
+ testId: "waiting-at-signals"
1349
+ }
1350
+ ),
1351
+ /* @__PURE__ */ jsx(
1352
+ HandlerList,
1353
+ {
1354
+ title: tr("fluidgrids.executions.waitingAt.queries", "Queries"),
1355
+ handlers: definition.queries,
1356
+ testId: "waiting-at-queries"
1357
+ }
1358
+ ),
1359
+ /* @__PURE__ */ jsx(
1360
+ HandlerList,
1361
+ {
1362
+ title: tr("fluidgrids.executions.waitingAt.updates", "Updates"),
1363
+ handlers: definition.updates,
1364
+ testId: "waiting-at-updates"
1365
+ }
1366
+ )
1367
+ ] })
1368
+ ]
1369
+ }
1370
+ );
1371
+ }
1251
1372
  function WaitingAtPanel({
1252
1373
  trace,
1253
1374
  loading,
1254
1375
  error,
1255
- onRefresh
1376
+ onRefresh,
1377
+ metadata
1256
1378
  }) {
1257
1379
  const tr = useTr();
1258
1380
  return /* @__PURE__ */ jsxs(
@@ -1296,7 +1418,8 @@ function WaitingAtPanel({
1296
1418
  ] }) : loading && trace === null ? /* @__PURE__ */ jsx("div", { className: "h-12 animate-pulse rounded bg-background-tertiary" }) : trace ? /* @__PURE__ */ jsx("pre", { className: "max-h-64 overflow-auto whitespace-pre-wrap break-words font-mono text-xs text-text-primary", children: trace }) : /* @__PURE__ */ jsx("p", { className: "text-sm text-text-secondary", children: tr(
1297
1419
  "fluidgrids.executions.waitingAt.none",
1298
1420
  "The run is not waiting on anything right now."
1299
- ) })
1421
+ ) }),
1422
+ !error && metadata && /* @__PURE__ */ jsx(MetadataSection, { metadata })
1300
1423
  ]
1301
1424
  }
1302
1425
  );
@@ -1307,6 +1430,724 @@ function resolveDetailTab(selection, runId, available, fallback) {
1307
1430
  if (selection.runId !== runId) return fallback;
1308
1431
  return available.includes(selection.tab) ? selection.tab : fallback;
1309
1432
  }
1433
+ function formatTime(value) {
1434
+ if (!value) return null;
1435
+ const date = new Date(value);
1436
+ if (Number.isNaN(date.getTime())) return null;
1437
+ return date.toLocaleString("en-US", {
1438
+ month: "short",
1439
+ day: "numeric",
1440
+ hour: "2-digit",
1441
+ minute: "2-digit",
1442
+ second: "2-digit"
1443
+ });
1444
+ }
1445
+ function pendingValueText(value) {
1446
+ if (value === null || value === void 0) return null;
1447
+ if (typeof value === "string") return value;
1448
+ if (typeof value === "object" && !Array.isArray(value) && "message" in value) {
1449
+ const message = value.message;
1450
+ if (typeof message === "string" && message.trim()) return message;
1451
+ }
1452
+ try {
1453
+ return JSON.stringify(value);
1454
+ } catch {
1455
+ return null;
1456
+ }
1457
+ }
1458
+ function isPendingEmpty(pending) {
1459
+ return pending.activities.length === 0 && pending.children.length === 0 && pending.timers.length === 0 && pending.bufferedSignals === 0 && !pending.workflowTask;
1460
+ }
1461
+ var STATE_CLASS = {
1462
+ STARTED: "border-[var(--color-status-info-border)] bg-[var(--color-status-info-bg-subtle)] text-[var(--color-status-info-text)]",
1463
+ RUNNING: "border-[var(--color-status-info-border)] bg-[var(--color-status-info-bg-subtle)] text-[var(--color-status-info-text)]",
1464
+ BACKOFF: "border-[var(--color-status-warning-border)] bg-[var(--color-status-warning-bg-subtle)] text-[var(--color-status-warning-text)]",
1465
+ CANCEL_REQUESTED: "border-[var(--color-status-warning-border)] bg-[var(--color-status-warning-bg-subtle)] text-[var(--color-status-warning-text)]"
1466
+ };
1467
+ var NEUTRAL_STATE = "border-border-secondary bg-background-tertiary text-text-secondary";
1468
+ function StateChip({ state }) {
1469
+ if (!state) return null;
1470
+ return /* @__PURE__ */ jsx(
1471
+ "span",
1472
+ {
1473
+ className: cn(
1474
+ "shrink-0 rounded border px-1.5 py-0.5 text-xs",
1475
+ STATE_CLASS[state] ?? NEUTRAL_STATE
1476
+ ),
1477
+ children: state
1478
+ }
1479
+ );
1480
+ }
1481
+ function RunPendingCard({
1482
+ pending,
1483
+ loading,
1484
+ error,
1485
+ onRefresh,
1486
+ onViewChildRun
1487
+ }) {
1488
+ const tr = useTr();
1489
+ return /* @__PURE__ */ jsxs(
1490
+ "section",
1491
+ {
1492
+ "data-testid": "run-pending-card",
1493
+ className: "mb-6 rounded-lg border border-border-secondary bg-background-secondary p-4",
1494
+ children: [
1495
+ /* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center gap-2", children: [
1496
+ /* @__PURE__ */ jsx(
1497
+ Icon,
1498
+ {
1499
+ icon: "mdi:progress-clock",
1500
+ width: 18,
1501
+ className: "text-text-secondary"
1502
+ }
1503
+ ),
1504
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium text-text-primary", children: tr("fluidgrids.executions.pending.title", "Waiting on") }),
1505
+ /* @__PURE__ */ jsxs(
1506
+ Button,
1507
+ {
1508
+ variant: "outline",
1509
+ size: "sm",
1510
+ className: "ml-auto",
1511
+ onClick: onRefresh,
1512
+ disabled: loading,
1513
+ children: [
1514
+ /* @__PURE__ */ jsx(Icon, { icon: "mdi:refresh", className: "mr-1", width: 14 }),
1515
+ tr("fluidgrids.executions.pending.refresh", "Refresh")
1516
+ ]
1517
+ }
1518
+ )
1519
+ ] }),
1520
+ error ? /* @__PURE__ */ jsxs(
1521
+ "p",
1522
+ {
1523
+ role: "alert",
1524
+ className: "text-sm text-[var(--color-status-error-text)]",
1525
+ children: [
1526
+ tr(
1527
+ "fluidgrids.executions.pending.error",
1528
+ "Could not read what the run is waiting on:"
1529
+ ),
1530
+ " ",
1531
+ error
1532
+ ]
1533
+ }
1534
+ ) : !pending ? loading ? /* @__PURE__ */ jsx("div", { className: "h-12 animate-pulse rounded bg-background-tertiary" }) : null : isPendingEmpty(pending) ? /* @__PURE__ */ jsx("p", { className: "text-sm text-text-secondary", children: tr(
1535
+ "fluidgrids.executions.pending.none",
1536
+ "Nothing is outstanding right now."
1537
+ ) }) : /* @__PURE__ */ jsxs("div", { className: "space-y-3 text-sm", children: [
1538
+ pending.workflowTask && /* @__PURE__ */ jsxs(
1539
+ "div",
1540
+ {
1541
+ "data-testid": "pending-workflow-task",
1542
+ className: "flex flex-wrap items-center gap-2",
1543
+ children: [
1544
+ /* @__PURE__ */ jsx(
1545
+ Icon,
1546
+ {
1547
+ icon: "mdi:cog-sync-outline",
1548
+ width: 16,
1549
+ className: "text-text-secondary"
1550
+ }
1551
+ ),
1552
+ /* @__PURE__ */ jsx("span", { className: "text-text-primary", children: tr(
1553
+ "fluidgrids.executions.pending.workflowTask",
1554
+ "Workflow task"
1555
+ ) }),
1556
+ /* @__PURE__ */ jsx(StateChip, { state: pending.workflowTask.state }),
1557
+ pending.workflowTask.attempt > 1 && /* @__PURE__ */ jsx("span", { className: "text-xs text-text-secondary", children: tr(
1558
+ "fluidgrids.executions.pending.attempt",
1559
+ "attempt {{attempt}}",
1560
+ { attempt: pending.workflowTask.attempt }
1561
+ ) }),
1562
+ pending.workflowTask.failureCause && /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--color-status-error-text)]", children: pending.workflowTask.failureCause })
1563
+ ]
1564
+ }
1565
+ ),
1566
+ pending.activities.length > 0 && /* @__PURE__ */ jsx("ul", { className: "divide-y divide-border-secondary rounded-md border border-border-secondary", children: pending.activities.map((activity) => {
1567
+ const failure = pendingValueText(activity.lastFailure);
1568
+ const heartbeat = pendingValueText(activity.heartbeatDetails);
1569
+ const next = formatTime(activity.nextAttemptAt);
1570
+ return /* @__PURE__ */ jsxs(
1571
+ "li",
1572
+ {
1573
+ "data-testid": `pending-activity-${activity.activityId}`,
1574
+ className: "px-3 py-2",
1575
+ children: [
1576
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
1577
+ /* @__PURE__ */ jsx(
1578
+ Icon,
1579
+ {
1580
+ icon: "mdi:lightning-bolt-outline",
1581
+ width: 16,
1582
+ className: "text-text-secondary"
1583
+ }
1584
+ ),
1585
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate font-medium text-text-primary", children: activity.summary || activity.activityType }),
1586
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-text-secondary", children: activity.maxAttempts ? tr(
1587
+ "fluidgrids.executions.pending.attemptOf",
1588
+ "attempt {{attempt}} of {{max}}",
1589
+ {
1590
+ attempt: activity.attempt,
1591
+ max: activity.maxAttempts
1592
+ }
1593
+ ) : tr(
1594
+ "fluidgrids.executions.pending.attempt",
1595
+ "attempt {{attempt}}",
1596
+ { attempt: activity.attempt }
1597
+ ) }),
1598
+ /* @__PURE__ */ jsx(StateChip, { state: activity.state })
1599
+ ] }),
1600
+ /* @__PURE__ */ jsxs("div", { className: "mt-1 space-y-0.5 pl-6 text-xs text-text-secondary", children: [
1601
+ activity.summary && /* @__PURE__ */ jsx("p", { className: "font-mono", children: activity.activityType }),
1602
+ next && /* @__PURE__ */ jsx("p", { children: tr(
1603
+ "fluidgrids.executions.pending.nextAttempt",
1604
+ "Next attempt {{time}}",
1605
+ { time: next }
1606
+ ) }),
1607
+ heartbeat && /* @__PURE__ */ jsxs("p", { className: "truncate", title: heartbeat, children: [
1608
+ tr(
1609
+ "fluidgrids.executions.pending.heartbeat",
1610
+ "Heartbeat:"
1611
+ ),
1612
+ " ",
1613
+ heartbeat
1614
+ ] }),
1615
+ failure && /* @__PURE__ */ jsxs(
1616
+ "p",
1617
+ {
1618
+ className: "truncate text-[var(--color-status-error-text)]",
1619
+ title: failure,
1620
+ children: [
1621
+ tr(
1622
+ "fluidgrids.executions.pending.lastFailure",
1623
+ "Last failure:"
1624
+ ),
1625
+ " ",
1626
+ failure
1627
+ ]
1628
+ }
1629
+ )
1630
+ ] })
1631
+ ]
1632
+ },
1633
+ activity.activityId
1634
+ );
1635
+ }) }),
1636
+ pending.timers.length > 0 && /* @__PURE__ */ jsx("ul", { className: "space-y-1", children: pending.timers.map((timer) => /* @__PURE__ */ jsxs(
1637
+ "li",
1638
+ {
1639
+ "data-testid": `pending-timer-${timer.timerId}`,
1640
+ className: "flex items-center gap-2",
1641
+ children: [
1642
+ /* @__PURE__ */ jsx(
1643
+ Icon,
1644
+ {
1645
+ icon: "mdi:timer-sand",
1646
+ width: 16,
1647
+ className: "text-text-secondary"
1648
+ }
1649
+ ),
1650
+ /* @__PURE__ */ jsx("span", { className: "text-text-primary", children: timer.summary || tr("fluidgrids.executions.pending.timer", "Timer") }),
1651
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-text-secondary", children: tr(
1652
+ "fluidgrids.executions.pending.firesAt",
1653
+ "fires {{time}}",
1654
+ { time: formatTime(timer.fireAt) ?? timer.fireAt }
1655
+ ) })
1656
+ ]
1657
+ },
1658
+ timer.timerId
1659
+ )) }),
1660
+ pending.children.length > 0 && /* @__PURE__ */ jsx("ul", { className: "space-y-1", children: pending.children.map((child) => /* @__PURE__ */ jsxs(
1661
+ "li",
1662
+ {
1663
+ "data-testid": `pending-child-${child.runId}`,
1664
+ className: "flex flex-wrap items-center gap-2",
1665
+ children: [
1666
+ /* @__PURE__ */ jsx(
1667
+ Icon,
1668
+ {
1669
+ icon: "mdi:source-branch",
1670
+ width: 16,
1671
+ className: "text-text-secondary"
1672
+ }
1673
+ ),
1674
+ /* @__PURE__ */ jsx("span", { className: "text-text-primary", children: child.workflowKey }),
1675
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-xs text-text-secondary", children: shortRunId(child.runId) }),
1676
+ /* @__PURE__ */ jsx(StateChip, { state: child.status }),
1677
+ onViewChildRun && /* @__PURE__ */ jsxs(
1678
+ Button,
1679
+ {
1680
+ variant: "ghost",
1681
+ size: "sm",
1682
+ onClick: () => onViewChildRun(child.runId),
1683
+ children: [
1684
+ /* @__PURE__ */ jsx(
1685
+ Icon,
1686
+ {
1687
+ icon: "mdi:open-in-new",
1688
+ className: "mr-1",
1689
+ width: 14
1690
+ }
1691
+ ),
1692
+ tr("fluidgrids.executions.pending.openChild", "Open")
1693
+ ]
1694
+ }
1695
+ )
1696
+ ]
1697
+ },
1698
+ child.runId
1699
+ )) }),
1700
+ pending.bufferedSignals > 0 && /* @__PURE__ */ jsxs(
1701
+ "p",
1702
+ {
1703
+ "data-testid": "pending-buffered-signals",
1704
+ className: "flex items-center gap-2 text-text-secondary",
1705
+ children: [
1706
+ /* @__PURE__ */ jsx(Icon, { icon: "mdi:bell-ring-outline", width: 16 }),
1707
+ tr(
1708
+ "fluidgrids.executions.pending.bufferedSignals",
1709
+ "{{count}} signals waiting for a Wait for Signal node",
1710
+ { count: pending.bufferedSignals }
1711
+ )
1712
+ ]
1713
+ }
1714
+ )
1715
+ ] })
1716
+ ]
1717
+ }
1718
+ );
1719
+ }
1720
+ var TONE_CLASS = {
1721
+ success: "border-[var(--color-status-success-border)] bg-[var(--color-status-success-bg-subtle)] text-[var(--color-status-success-text)]",
1722
+ error: "border-[var(--color-status-error-border)] bg-[var(--color-status-error-bg-subtle)] text-[var(--color-status-error-text)]",
1723
+ info: "border-[var(--color-status-info-border)] bg-[var(--color-status-info-bg-subtle)] text-[var(--color-status-info-text)]",
1724
+ neutral: "border-border-secondary bg-background-tertiary text-text-secondary"
1725
+ };
1726
+ var LINK_LABELS = {
1727
+ first: ["fluidgrids.executions.parity.firstRun", "First run"],
1728
+ previous: ["fluidgrids.executions.parity.previousRun", "Continues"],
1729
+ next: ["fluidgrids.executions.parity.nextRun", "Continued by"],
1730
+ root: ["fluidgrids.executions.parity.rootRun", "Root run"]
1731
+ };
1732
+ function formatDateTime(value) {
1733
+ if (!value) return null;
1734
+ const date = new Date(value);
1735
+ if (Number.isNaN(date.getTime())) return null;
1736
+ return date.toLocaleString("en-US", {
1737
+ month: "short",
1738
+ day: "numeric",
1739
+ year: "numeric",
1740
+ hour: "2-digit",
1741
+ minute: "2-digit",
1742
+ second: "2-digit"
1743
+ });
1744
+ }
1745
+ function Row({
1746
+ label,
1747
+ children,
1748
+ testId
1749
+ }) {
1750
+ return /* @__PURE__ */ jsxs(
1751
+ "div",
1752
+ {
1753
+ "data-testid": testId,
1754
+ className: "flex flex-col gap-1 border-b border-border-secondary py-2 sm:flex-row sm:items-center sm:justify-between sm:gap-4",
1755
+ children: [
1756
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm text-text-secondary", children: label }),
1757
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 text-sm text-text-primary sm:text-right", children })
1758
+ ]
1759
+ }
1760
+ );
1761
+ }
1762
+ function RunParityPanel({
1763
+ run,
1764
+ open,
1765
+ durable,
1766
+ canOverride,
1767
+ onOverride
1768
+ }) {
1769
+ const tr = useTr();
1770
+ const links = runChainLinks(run);
1771
+ const callback = run.completionCallback;
1772
+ const versioning = durable ? run.versioning : null;
1773
+ const deadlines = [
1774
+ [
1775
+ tr("fluidgrids.executions.parity.startDelayUntil", "Starts at"),
1776
+ formatDateTime(run.startDelayUntil)
1777
+ ],
1778
+ [
1779
+ tr("fluidgrids.executions.parity.runTimeoutAt", "Run timeout"),
1780
+ formatDateTime(run.runTimeoutAt)
1781
+ ],
1782
+ [
1783
+ tr(
1784
+ "fluidgrids.executions.parity.executionTimeoutAt",
1785
+ "Execution timeout"
1786
+ ),
1787
+ formatDateTime(run.executionTimeoutAt)
1788
+ ]
1789
+ ];
1790
+ const shownDeadlines = deadlines.filter(([, value]) => value !== null);
1791
+ return /* @__PURE__ */ jsxs(
1792
+ "section",
1793
+ {
1794
+ "data-testid": "run-parity-panel",
1795
+ className: "mb-6 rounded-lg border border-border-secondary bg-background-secondary p-4",
1796
+ children: [
1797
+ /* @__PURE__ */ jsxs("div", { children: [
1798
+ run.businessId && /* @__PURE__ */ jsx(
1799
+ Row,
1800
+ {
1801
+ label: tr("fluidgrids.executions.parity.workflowId", "Workflow ID"),
1802
+ testId: "run-parity-workflow-id",
1803
+ children: /* @__PURE__ */ jsx("code", { className: "break-all font-mono", children: run.businessId })
1804
+ }
1805
+ ),
1806
+ links.length > 0 && /* @__PURE__ */ jsx(
1807
+ Row,
1808
+ {
1809
+ label: tr("fluidgrids.executions.parity.chain", "Chain"),
1810
+ testId: "run-parity-chain",
1811
+ children: /* @__PURE__ */ jsx("span", { className: "flex flex-wrap gap-x-3 gap-y-1 sm:justify-end", children: links.map((link) => /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
1812
+ /* @__PURE__ */ jsx("span", { className: "text-text-secondary", children: tr(...LINK_LABELS[link.kind]) }),
1813
+ /* @__PURE__ */ jsx(
1814
+ Link,
1815
+ {
1816
+ to: `../runs/${encodeURIComponent(link.runId)}`,
1817
+ className: "font-mono text-text-link hover:text-text-linkHover hover:underline",
1818
+ "data-testid": `run-chain-${link.kind}`,
1819
+ title: link.runId,
1820
+ children: shortRunId(link.runId)
1821
+ }
1822
+ )
1823
+ ] }, link.kind)) })
1824
+ }
1825
+ ),
1826
+ (run.runAttempt > 1 || run.priority !== 3) && /* @__PURE__ */ jsx(
1827
+ Row,
1828
+ {
1829
+ label: tr(
1830
+ "fluidgrids.executions.parity.attemptPriority",
1831
+ "Attempt \xB7 priority"
1832
+ ),
1833
+ children: tr(
1834
+ "fluidgrids.executions.parity.attemptPriorityValue",
1835
+ "attempt {{attempt}} \xB7 priority {{priority}}",
1836
+ { attempt: run.runAttempt, priority: run.priority }
1837
+ )
1838
+ }
1839
+ ),
1840
+ shownDeadlines.map(([label, value]) => /* @__PURE__ */ jsx(Row, { label, children: value }, label)),
1841
+ callback && /* @__PURE__ */ jsx(
1842
+ Row,
1843
+ {
1844
+ label: tr(
1845
+ "fluidgrids.executions.parity.callback",
1846
+ "Completion callback"
1847
+ ),
1848
+ testId: "run-parity-callback",
1849
+ children: /* @__PURE__ */ jsxs("span", { className: "flex flex-wrap items-center gap-2 sm:justify-end", children: [
1850
+ /* @__PURE__ */ jsx("code", { className: "break-all font-mono text-xs", children: callback.url }),
1851
+ /* @__PURE__ */ jsx(
1852
+ "span",
1853
+ {
1854
+ className: cn(
1855
+ "rounded border px-1.5 py-0.5 text-xs",
1856
+ TONE_CLASS[callbackTone(callback.status)]
1857
+ ),
1858
+ "data-testid": "run-parity-callback-status",
1859
+ children: callback.status
1860
+ }
1861
+ ),
1862
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-text-secondary", children: [
1863
+ tr(
1864
+ "fluidgrids.executions.parity.callbackAttempts",
1865
+ "{{count}} attempts",
1866
+ { count: callback.attempts }
1867
+ ),
1868
+ callback.lastStatusCode ? ` \xB7 HTTP ${callback.lastStatusCode}` : ""
1869
+ ] }),
1870
+ callback.lastError && callback.status !== "DELIVERED" && /* @__PURE__ */ jsx("span", { className: "w-full text-xs text-[var(--color-status-error-text)]", children: callback.lastError }),
1871
+ callback.nextAttemptAt && callback.status === "PENDING" && /* @__PURE__ */ jsx("span", { className: "w-full text-xs text-text-secondary", children: tr(
1872
+ "fluidgrids.executions.parity.callbackNext",
1873
+ "Next attempt {{time}}",
1874
+ {
1875
+ time: formatDateTime(callback.nextAttemptAt) ?? callback.nextAttemptAt
1876
+ }
1877
+ ) })
1878
+ ] })
1879
+ }
1880
+ ),
1881
+ versioning && /* @__PURE__ */ jsx(
1882
+ Row,
1883
+ {
1884
+ label: tr("fluidgrids.executions.parity.versioning", "Versioning"),
1885
+ testId: "run-parity-versioning",
1886
+ children: /* @__PURE__ */ jsxs("span", { className: "flex flex-wrap items-center gap-2 sm:justify-end", children: [
1887
+ /* @__PURE__ */ jsx(
1888
+ "span",
1889
+ {
1890
+ className: "rounded border border-border-secondary bg-background-tertiary px-1.5 py-0.5 text-xs",
1891
+ "data-testid": "run-parity-behavior",
1892
+ children: versioning.behavior === "AUTO_UPGRADE" /* AutoUpgrade */ ? tr(
1893
+ "fluidgrids.executions.parity.autoUpgrade",
1894
+ "Auto-upgrade"
1895
+ ) : tr("fluidgrids.executions.parity.pinned", "Pinned")
1896
+ }
1897
+ ),
1898
+ versioning.override && /* @__PURE__ */ jsx("span", { className: "text-xs text-text-secondary", children: tr("fluidgrids.executions.parity.overridden", "overridden") }),
1899
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-text-secondary", children: tr(
1900
+ "fluidgrids.executions.parity.servedBy",
1901
+ "served by {{version}}",
1902
+ { version: versioning.servingVersion }
1903
+ ) }),
1904
+ versioning.heldFromVersion && /* @__PURE__ */ jsx("span", { className: "text-xs text-text-secondary", children: tr(
1905
+ "fluidgrids.executions.parity.heldFrom",
1906
+ "will not move back to {{version}}",
1907
+ { version: versioning.heldFromVersion }
1908
+ ) }),
1909
+ versioning.pendingOverride && /* @__PURE__ */ jsx(
1910
+ "span",
1911
+ {
1912
+ className: "text-xs text-[var(--color-status-info-text)]",
1913
+ "data-testid": "run-parity-pending-override",
1914
+ children: tr(
1915
+ "fluidgrids.executions.parity.pendingOverride",
1916
+ "replay check {{status}}",
1917
+ { status: versioning.pendingOverride.status }
1918
+ )
1919
+ }
1920
+ ),
1921
+ open && canOverride && /* @__PURE__ */ jsxs(
1922
+ Button,
1923
+ {
1924
+ variant: "outline",
1925
+ size: "sm",
1926
+ onClick: onOverride,
1927
+ "data-testid": "run-override-button",
1928
+ children: [
1929
+ /* @__PURE__ */ jsx(Icon, { icon: "mdi:pin-outline", className: "mr-1", width: 14 }),
1930
+ tr("fluidgrids.executions.parity.overrideButton", "Change")
1931
+ ]
1932
+ }
1933
+ )
1934
+ ] })
1935
+ }
1936
+ )
1937
+ ] }),
1938
+ run.details && /* @__PURE__ */ jsxs("div", { className: "mt-4", "data-testid": "run-parity-details", children: [
1939
+ /* @__PURE__ */ jsx("h3", { className: "mb-1 text-sm font-medium text-text-secondary", children: tr("fluidgrids.executions.parity.details", "Details") }),
1940
+ /* @__PURE__ */ jsx(RunMarkdown, { text: run.details })
1941
+ ] }),
1942
+ run.currentDetails && /* @__PURE__ */ jsxs("div", { className: "mt-4", "data-testid": "run-parity-current-details", children: [
1943
+ /* @__PURE__ */ jsx("h3", { className: "mb-1 text-sm font-medium text-text-secondary", children: tr("fluidgrids.executions.parity.currentDetails", "Now") }),
1944
+ /* @__PURE__ */ jsx(RunMarkdown, { text: run.currentDetails })
1945
+ ] })
1946
+ ]
1947
+ }
1948
+ );
1949
+ }
1950
+ function hasParityDetails(run, durable) {
1951
+ return Boolean(
1952
+ run.businessId || runChainLinks(run).length > 0 || run.runAttempt > 1 || run.priority !== 3 || run.startDelayUntil || run.runTimeoutAt || run.executionTimeoutAt || run.completionCallback || run.details || run.currentDetails || durable && run.versioning
1953
+ );
1954
+ }
1955
+ function initialChoice(current) {
1956
+ if (current?.override?.behavior === "AUTO_UPGRADE" /* AutoUpgrade */)
1957
+ return "autoUpgrade";
1958
+ return "pin";
1959
+ }
1960
+ function RunVersioningOverrideDialog({
1961
+ open,
1962
+ onOpenChange,
1963
+ runId,
1964
+ versions,
1965
+ current,
1966
+ onChanged
1967
+ }) {
1968
+ const tr = useTr();
1969
+ const [choice, setChoice] = useState(
1970
+ () => initialChoice(current)
1971
+ );
1972
+ const [version, setVersion] = useState(current?.servingVersion ?? "");
1973
+ const [reason, setReason] = useState("");
1974
+ const [saving, setSaving] = useState(false);
1975
+ const [error, setError] = useState(null);
1976
+ useEffect(() => {
1977
+ if (!open) return;
1978
+ setChoice(initialChoice(current));
1979
+ setVersion(current?.override?.version ?? current?.servingVersion ?? "");
1980
+ setReason("");
1981
+ setError(null);
1982
+ }, [open, current]);
1983
+ const submit = async () => {
1984
+ const request = overrideRequestOf(choice, version);
1985
+ if (!request.ok) {
1986
+ setError(
1987
+ tr(
1988
+ "fluidgrids.executions.override.versionRequired",
1989
+ "Choose the version to pin the run to."
1990
+ )
1991
+ );
1992
+ return;
1993
+ }
1994
+ setSaving(true);
1995
+ setError(null);
1996
+ try {
1997
+ const versioning = await setRunVersioningOverride({
1998
+ runId,
1999
+ override: request.override,
2000
+ reason
2001
+ });
2002
+ toast.success(
2003
+ versioning?.pendingOverride ? tr(
2004
+ "fluidgrids.executions.override.checkQueued",
2005
+ "Replay check started; the run moves once it passes"
2006
+ ) : tr("fluidgrids.executions.override.saved", "Override saved")
2007
+ );
2008
+ onChanged(versioning);
2009
+ onOpenChange(false);
2010
+ } catch (err) {
2011
+ setError(describeOperationError(err));
2012
+ } finally {
2013
+ setSaving(false);
2014
+ }
2015
+ };
2016
+ const options = [
2017
+ {
2018
+ id: "pin",
2019
+ label: tr("fluidgrids.executions.override.pin", "Pin to a version"),
2020
+ hint: tr(
2021
+ "fluidgrids.executions.override.pinHint",
2022
+ "The run keeps that version's bundle. A different bundle is replay-checked first."
2023
+ )
2024
+ },
2025
+ {
2026
+ id: "autoUpgrade",
2027
+ label: tr("fluidgrids.executions.override.autoUpgrade", "Auto-upgrade"),
2028
+ hint: tr(
2029
+ "fluidgrids.executions.override.autoUpgradeHint",
2030
+ "Each new workflow task uses the active version's bundle, after a replay check."
2031
+ )
2032
+ },
2033
+ {
2034
+ id: "remove",
2035
+ label: tr("fluidgrids.executions.override.remove", "Remove override"),
2036
+ hint: tr(
2037
+ "fluidgrids.executions.override.removeHint",
2038
+ "The run follows the behaviour it started with. A pinned run keeps its bundle."
2039
+ )
2040
+ }
2041
+ ];
2042
+ return /* @__PURE__ */ jsx(AlertDialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(AlertDialogContent, { "data-testid": "run-override-dialog", children: [
2043
+ /* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
2044
+ /* @__PURE__ */ jsx(AlertDialogTitle, { children: tr(
2045
+ "fluidgrids.executions.override.title",
2046
+ "Change the run's versioning"
2047
+ ) }),
2048
+ /* @__PURE__ */ jsx(AlertDialogDescription, { children: tr(
2049
+ "fluidgrids.executions.override.description",
2050
+ "This changes which code bundle serves this run from its next workflow task. The change is recorded in the run's history."
2051
+ ) })
2052
+ ] }),
2053
+ /* @__PURE__ */ jsxs("fieldset", { className: "space-y-2", disabled: saving, children: [
2054
+ options.map((option) => /* @__PURE__ */ jsxs(
2055
+ "label",
2056
+ {
2057
+ className: "flex cursor-pointer gap-2 rounded-md border border-border-secondary p-2",
2058
+ children: [
2059
+ /* @__PURE__ */ jsx(
2060
+ "input",
2061
+ {
2062
+ type: "radio",
2063
+ name: "run-override",
2064
+ value: option.id,
2065
+ checked: choice === option.id,
2066
+ onChange: () => setChoice(option.id),
2067
+ "data-testid": `run-override-${option.id}`,
2068
+ className: "mt-1"
2069
+ }
2070
+ ),
2071
+ /* @__PURE__ */ jsxs("span", { children: [
2072
+ /* @__PURE__ */ jsx("span", { className: "block text-sm font-medium text-text-primary", children: option.label }),
2073
+ /* @__PURE__ */ jsx("span", { className: "block text-xs text-text-secondary", children: option.hint })
2074
+ ] })
2075
+ ]
2076
+ },
2077
+ option.id
2078
+ )),
2079
+ choice === "pin" && /* @__PURE__ */ jsxs("label", { className: "block text-sm text-text-secondary", children: [
2080
+ tr("fluidgrids.executions.override.version", "Version"),
2081
+ /* @__PURE__ */ jsxs(
2082
+ "select",
2083
+ {
2084
+ value: version,
2085
+ onChange: (event) => setVersion(event.target.value),
2086
+ className: "mt-1 h-9 w-full rounded-md border border-border-secondary bg-background-primary px-2 text-sm text-text-primary",
2087
+ "data-testid": "run-override-version",
2088
+ children: [
2089
+ /* @__PURE__ */ jsx("option", { value: "", children: tr(
2090
+ "fluidgrids.executions.override.chooseVersion",
2091
+ "Choose a version"
2092
+ ) }),
2093
+ versions.map((value) => /* @__PURE__ */ jsx("option", { value, children: value }, value))
2094
+ ]
2095
+ }
2096
+ )
2097
+ ] }),
2098
+ /* @__PURE__ */ jsxs("label", { className: "block text-sm text-text-secondary", children: [
2099
+ tr("fluidgrids.executions.override.reason", "Reason (optional)"),
2100
+ /* @__PURE__ */ jsx(
2101
+ Input,
2102
+ {
2103
+ value: reason,
2104
+ maxLength: 500,
2105
+ onChange: (event) => setReason(event.target.value),
2106
+ className: "mt-1",
2107
+ "data-testid": "run-override-reason"
2108
+ }
2109
+ )
2110
+ ] })
2111
+ ] }),
2112
+ error && /* @__PURE__ */ jsx(
2113
+ "p",
2114
+ {
2115
+ role: "alert",
2116
+ className: "text-sm text-[var(--color-status-error-text)]",
2117
+ children: error
2118
+ }
2119
+ ),
2120
+ /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [
2121
+ /* @__PURE__ */ jsx(AlertDialogCancel, { disabled: saving, children: tr("fluidgrids.executions.override.cancel", "Cancel") }),
2122
+ /* @__PURE__ */ jsx(
2123
+ AlertDialogAction,
2124
+ {
2125
+ "data-agent": "confirm",
2126
+ disabled: saving,
2127
+ onClick: (event) => {
2128
+ event.preventDefault();
2129
+ void submit();
2130
+ },
2131
+ children: saving ? tr("fluidgrids.executions.override.saving", "Saving\u2026") : tr("fluidgrids.executions.override.confirm", "Apply")
2132
+ }
2133
+ )
2134
+ ] })
2135
+ ] }) });
2136
+ }
2137
+ function RunVersioningOverrideDialogHost({
2138
+ workflowKey,
2139
+ ...props
2140
+ }) {
2141
+ const { versions } = useWorkflowVersions(workflowKey);
2142
+ return /* @__PURE__ */ jsx(
2143
+ RunVersioningOverrideDialog,
2144
+ {
2145
+ ...props,
2146
+ open: true,
2147
+ versions: versions.map((definition) => definition.version)
2148
+ }
2149
+ );
2150
+ }
1310
2151
  var ALL_DETAIL_TABS = [
1311
2152
  "timeline",
1312
2153
  "history",
@@ -1344,7 +2185,9 @@ function ExecutionDetailPage() {
1344
2185
  FLUIDGRIDS_PERMISSIONS.WORKFLOW_PUBLISH
1345
2186
  );
1346
2187
  const durableEnabled = useDurableFunctionNodesEnabled();
2188
+ const parityEnabled = useDurableParityEnabled();
1347
2189
  const [showSignalDialog, setShowSignalDialog] = useState(false);
2190
+ const [showOverrideDialog, setShowOverrideDialog] = useState(false);
1348
2191
  const [minLogLevel, setMinLogLevel] = useState(null);
1349
2192
  const {
1350
2193
  execution: baseExecution,
@@ -1358,9 +2201,15 @@ function ExecutionDetailPage() {
1358
2201
  [baseExecution, durableFields.fields]
1359
2202
  );
1360
2203
  const refetchDurableFields = durableFields.refetch;
2204
+ const parityFields = useDurableParityRunFields(runId, parityEnabled);
2205
+ const refetchParityFields = parityFields.refetch;
1361
2206
  const refetch = useCallback(async () => {
1362
- await Promise.all([refetchExecution(), refetchDurableFields()]);
1363
- }, [refetchExecution, refetchDurableFields]);
2207
+ await Promise.all([
2208
+ refetchExecution(),
2209
+ refetchDurableFields(),
2210
+ refetchParityFields()
2211
+ ]);
2212
+ }, [refetchExecution, refetchDurableFields, refetchParityFields]);
1364
2213
  const runCanChange = execution?.status === "RUNNING" || execution?.status === "PENDING" || execution?.status === "WAITING";
1365
2214
  const {
1366
2215
  items: humanRequests,
@@ -1444,10 +2293,20 @@ function ExecutionDetailPage() {
1444
2293
  enabled: isDurableRun && runCanChange,
1445
2294
  live: isActive
1446
2295
  });
1447
- const stackTrace = useRunStackTrace(
1448
- runId,
1449
- isDurableRun && runCanChange && activeTab === "history"
1450
- );
2296
+ const waitingAtShown = isDurableRun && runCanChange && activeTab === "history";
2297
+ const stackTrace = useRunStackTrace(runId, waitingAtShown && !parityEnabled);
2298
+ const runMetadata = useRunMetadata(runId, waitingAtShown && parityEnabled);
2299
+ const waitingAt = parityEnabled ? {
2300
+ trace: runMetadata.answer?.trace ?? null,
2301
+ loading: runMetadata.loading,
2302
+ error: runMetadata.error,
2303
+ refetch: runMetadata.refetch
2304
+ } : stackTrace;
2305
+ const runPending = useRunPending(runId, {
2306
+ enabled: parityEnabled && runCanChange,
2307
+ live: isActive
2308
+ });
2309
+ const parityRun = parityEnabled && execution && parityFields.fields?.id === execution.id ? parityFields.fields : null;
1451
2310
  const signalTargets = useMemo(
1452
2311
  () => durableEnabled && execution ? runSignalTargets(execution) : { targets: [], openNames: false },
1453
2312
  [durableEnabled, execution]
@@ -1548,7 +2407,7 @@ function ExecutionDetailPage() {
1548
2407
  return `${Math.round(ms / 6e4)}m ${Math.round(ms % 6e4 / 1e3)}s`;
1549
2408
  return `${Math.round(ms / 36e5)}h ${Math.round(ms % 36e5 / 6e4)}m`;
1550
2409
  };
1551
- const formatDateTime = (dateStr) => {
2410
+ const formatDateTime2 = (dateStr) => {
1552
2411
  if (!dateStr) return "-";
1553
2412
  const date = new Date(dateStr);
1554
2413
  return date.toLocaleString("en-US", {
@@ -1735,6 +2594,14 @@ function ExecutionDetailPage() {
1735
2594
  "Sub-Workflow"
1736
2595
  ) })
1737
2596
  ] }),
2597
+ parityRun?.summary && /* @__PURE__ */ jsx(
2598
+ "p",
2599
+ {
2600
+ className: "mb-2 text-sm text-text-primary",
2601
+ "data-testid": "run-summary",
2602
+ children: parityRun.summary
2603
+ }
2604
+ ),
1738
2605
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-4 text-sm text-text-secondary", children: [
1739
2606
  /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
1740
2607
  /* @__PURE__ */ jsx(Icon, { icon: "mdi:identifier", width: 16 }),
@@ -1743,13 +2610,25 @@ function ExecutionDetailPage() {
1743
2610
  "..."
1744
2611
  ] })
1745
2612
  ] }),
2613
+ parityRun?.businessId && /* @__PURE__ */ jsxs(
2614
+ "span",
2615
+ {
2616
+ className: "flex items-center gap-1",
2617
+ "data-testid": "run-workflow-id",
2618
+ title: parityRun.businessId,
2619
+ children: [
2620
+ /* @__PURE__ */ jsx(Icon, { icon: "mdi:key-outline", width: 16 }),
2621
+ /* @__PURE__ */ jsx("code", { className: "max-w-[16rem] truncate font-mono", children: parityRun.businessId })
2622
+ ]
2623
+ }
2624
+ ),
1746
2625
  /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
1747
2626
  /* @__PURE__ */ jsx(Icon, { icon: "mdi:tag-outline", width: 16 }),
1748
2627
  formatRunVersion(execution.version)
1749
2628
  ] }),
1750
2629
  /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
1751
2630
  /* @__PURE__ */ jsx(Icon, { icon: "mdi:clock-outline", width: 16 }),
1752
- formatDateTime(execution.startedAt || execution.createdAt)
2631
+ formatDateTime2(execution.startedAt || execution.createdAt)
1753
2632
  ] })
1754
2633
  ] }),
1755
2634
  /* @__PURE__ */ jsx("div", { className: "mt-3", "data-testid": "run-tags", children: /* @__PURE__ */ jsx(
@@ -2070,6 +2949,26 @@ function ExecutionDetailPage() {
2070
2949
  }
2071
2950
  }
2072
2951
  ),
2952
+ parityRun && hasParityDetails(parityRun, isDurableRun) && /* @__PURE__ */ jsx(
2953
+ RunParityPanel,
2954
+ {
2955
+ run: { ...parityRun, parentRunId: execution.parentRunId },
2956
+ open: runCanChange,
2957
+ durable: isDurableRun,
2958
+ canOverride: canControlRuns,
2959
+ onOverride: () => setShowOverrideDialog(true)
2960
+ }
2961
+ ),
2962
+ parityEnabled && runCanChange && /* @__PURE__ */ jsx(
2963
+ RunPendingCard,
2964
+ {
2965
+ pending: runPending.pending,
2966
+ loading: runPending.loading,
2967
+ error: runPending.error,
2968
+ onRefresh: () => void runPending.refetch(),
2969
+ onViewChildRun: handleViewChildRun
2970
+ }
2971
+ ),
2073
2972
  /* @__PURE__ */ jsx("div", { className: "border-b border-border-secondary mb-6", children: /* @__PURE__ */ jsx("nav", { className: "flex gap-4 -mb-px", children: tabs.map((tab) => /* @__PURE__ */ jsxs(
2074
2973
  "button",
2075
2974
  {
@@ -2100,10 +2999,11 @@ function ExecutionDetailPage() {
2100
2999
  runCanChange && /* @__PURE__ */ jsx(
2101
3000
  WaitingAtPanel,
2102
3001
  {
2103
- trace: stackTrace.trace,
2104
- loading: stackTrace.loading,
2105
- error: stackTrace.error,
2106
- onRefresh: () => void stackTrace.refetch()
3002
+ trace: waitingAt.trace,
3003
+ loading: waitingAt.loading,
3004
+ error: waitingAt.error,
3005
+ onRefresh: () => void waitingAt.refetch(),
3006
+ metadata: parityEnabled ? runMetadata.answer?.metadata : null
2107
3007
  }
2108
3008
  ),
2109
3009
  /* @__PURE__ */ jsx(
@@ -2117,7 +3017,8 @@ function ExecutionDetailPage() {
2117
3017
  truncated: runHistory.truncated,
2118
3018
  historyLength: runHistory.historyLength,
2119
3019
  onRefresh: () => void runHistory.refetch(),
2120
- onViewChildRun: handleViewChildRun
3020
+ onViewChildRun: handleViewChildRun,
3021
+ showBenign: parityEnabled
2121
3022
  }
2122
3023
  )
2123
3024
  ] }),
@@ -2261,21 +3162,21 @@ function ExecutionDetailPage() {
2261
3162
  "fluidgrids.executions.executionDetail.createdRow",
2262
3163
  "Created"
2263
3164
  ) }),
2264
- /* @__PURE__ */ jsx("span", { className: "text-text-primary", children: formatDateTime(execution.createdAt) })
3165
+ /* @__PURE__ */ jsx("span", { className: "text-text-primary", children: formatDateTime2(execution.createdAt) })
2265
3166
  ] }),
2266
3167
  /* @__PURE__ */ jsxs("div", { className: "flex justify-between py-2 border-b border-border-secondary", children: [
2267
3168
  /* @__PURE__ */ jsx("span", { className: "text-text-secondary", children: tr(
2268
3169
  "fluidgrids.executions.executionDetail.startedRow",
2269
3170
  "Started"
2270
3171
  ) }),
2271
- /* @__PURE__ */ jsx("span", { className: "text-text-primary", children: formatDateTime(execution.startedAt) })
3172
+ /* @__PURE__ */ jsx("span", { className: "text-text-primary", children: formatDateTime2(execution.startedAt) })
2272
3173
  ] }),
2273
3174
  execution.completedAt && /* @__PURE__ */ jsxs("div", { className: "flex justify-between py-2 border-b border-border-secondary", children: [
2274
3175
  /* @__PURE__ */ jsx("span", { className: "text-text-secondary", children: tr(
2275
3176
  "fluidgrids.executions.executionDetail.completedRow",
2276
3177
  "Completed"
2277
3178
  ) }),
2278
- /* @__PURE__ */ jsx("span", { className: "text-text-primary", children: formatDateTime(execution.completedAt) })
3179
+ /* @__PURE__ */ jsx("span", { className: "text-text-primary", children: formatDateTime2(execution.completedAt) })
2279
3180
  ] })
2280
3181
  ] })
2281
3182
  ] })
@@ -2294,6 +3195,16 @@ function ExecutionDetailPage() {
2294
3195
  }
2295
3196
  }
2296
3197
  ),
3198
+ showOverrideDialog && parityRun && /* @__PURE__ */ jsx(
3199
+ RunVersioningOverrideDialogHost,
3200
+ {
3201
+ workflowKey: execution.workflowKey,
3202
+ runId: execution.id,
3203
+ current: parityRun.versioning ?? null,
3204
+ onOpenChange: setShowOverrideDialog,
3205
+ onChanged: () => void refetch()
3206
+ }
3207
+ ),
2297
3208
  /* @__PURE__ */ jsx(
2298
3209
  AlertDialog,
2299
3210
  {