@burdenoff/fluidgrids-fe-sdk 2026.1105.1 → 2026.1105.3

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 (60) hide show
  1. package/README.md +30 -0
  2. package/dist/ExecutionDetailPage-JXGPSLS3.js +30 -0
  3. package/dist/{WorkflowsList-EV725VG5.js → ExecutionsListPage-U3TC5JUI.js} +11 -8
  4. package/dist/{WorkflowBuilder-CRR5WP4V.js → WorkflowBuilder-6DOD6WWH.js} +16 -13
  5. package/dist/{ExecutionsListPage-NQIQXMTY.js → WorkflowsList-XOWRQNST.js} +10 -8
  6. package/dist/{agentTools-PN4W7QZH.js → agentTools-YQBN33WG.js} +7 -5
  7. package/dist/{chunk-VFVN7D3A.js → chunk-22U3TUKZ.js} +3 -93
  8. package/dist/{chunk-XLI75E3D.js → chunk-2WUVF4EQ.js} +2 -2
  9. package/dist/{chunk-GVIYGJQG.js → chunk-3MZOYJ5I.js} +1 -1
  10. package/dist/{chunk-L44BRLW5.js → chunk-3ZYRLGDF.js} +612 -22
  11. package/dist/{chunk-FCT6POFX.js → chunk-4NNF7DIB.js} +1 -1
  12. package/dist/chunk-5DVP2UYH.js +1924 -0
  13. package/dist/{chunk-JDQYGH4Q.js → chunk-EKDUYCTS.js} +4 -4
  14. package/dist/{chunk-6P7DOBVN.js → chunk-EYQ7GJRW.js} +2 -2
  15. package/dist/{chunk-HLAUK5GM.js → chunk-FVWRGL7S.js} +1 -1
  16. package/dist/{chunk-AZKVI4YN.js → chunk-G33OMSW6.js} +8 -6
  17. package/dist/{chunk-D7G5VWSH.js → chunk-G774FI2E.js} +2 -2
  18. package/dist/{chunk-DANOMJ5I.js → chunk-IJ7UIDCW.js} +7 -6
  19. package/dist/{chunk-EC2AN74G.js → chunk-IL343FKX.js} +1 -1
  20. package/dist/chunk-JYYN3CLL.js +32 -0
  21. package/dist/{chunk-O2EB7UT2.js → chunk-KAWRHIJZ.js} +2 -2
  22. package/dist/{chunk-JC4D7RQ6.js → chunk-KN37RIFC.js} +5 -4
  23. package/dist/{chunk-AASHQI77.js → chunk-KZCNJAMK.js} +2 -1
  24. package/dist/{chunk-BHAKNT5M.js → chunk-LDWBMCX4.js} +4 -3
  25. package/dist/{chunk-MSQAULLG.js → chunk-MA6I4KY5.js} +186 -7
  26. package/dist/{chunk-R3Z7W4EX.js → chunk-MP44TAOO.js} +276 -196
  27. package/dist/{chunk-FJSXXO5W.js → chunk-MY7SEUXM.js} +1 -1
  28. package/dist/{chunk-I3QPAEL6.js → chunk-NMQENSPW.js} +15 -14
  29. package/dist/chunk-NZAYYUQ4.js +172 -0
  30. package/dist/{chunk-6EZJ5J3B.js → chunk-NZFPCTIS.js} +4 -4
  31. package/dist/{chunk-MZFJL4X3.js → chunk-OXXXUAWM.js} +1 -1
  32. package/dist/chunk-SDZEAIIE.js +9 -0
  33. package/dist/{chunk-CGR3AEZU.js → chunk-TCIBTMJI.js} +68 -2
  34. package/dist/{chunk-4QB6S7YW.js → chunk-TJAG2DQT.js} +9 -38
  35. package/dist/{chunk-MQN6YDX4.js → chunk-TLHX4DK4.js} +7 -6
  36. package/dist/{chunk-AG3VC7Y2.js → chunk-VESQH55Q.js} +2 -2
  37. package/dist/chunk-WZ3EJVS6.js +93 -0
  38. package/dist/{chunk-OCJTTHTS.js → chunk-Y7FKJJMF.js} +1 -9
  39. package/dist/{chunk-HOTVUVZG.js → chunk-YGOGJDN4.js} +2 -2
  40. package/dist/{chunk-AVHBWJRY.js → chunk-Z3ABLERJ.js} +3 -1
  41. package/dist/{chunk-C26QAWCT.js → chunk-ZBPRNWKY.js} +235 -2146
  42. package/dist/{chunk-2QOAJF7V.js → chunk-ZLBTUCC2.js} +4 -3
  43. package/dist/code-editor-field-WWPDMF4O.js +3 -0
  44. package/dist/executions.js +16 -12
  45. package/dist/history.js +9 -6
  46. package/dist/index.d.ts +77 -1
  47. package/dist/index.js +46 -41
  48. package/dist/{node-detail-view-3VG7XFIZ.js → node-detail-view-U5HOP2WX.js} +35 -9
  49. package/dist/nodes.js +2 -2
  50. package/dist/observability.js +9 -6
  51. package/dist/{quick-config-panel-GB7SDWHD.js → quick-config-panel-XAISBW6Y.js} +10 -7
  52. package/dist/settings.js +9 -6
  53. package/dist/templates.js +15 -12
  54. package/dist/workflow-builder.js +17 -14
  55. package/dist/workflow-preview.d.ts +158 -0
  56. package/dist/workflow-preview.js +721 -0
  57. package/dist/workflows-list.js +10 -8
  58. package/package.json +5 -1
  59. package/dist/ExecutionDetailPage-YBAMVPMB.js +0 -26
  60. package/dist/code-editor-field-XWUVOKTA.js +0 -2
@@ -1,21 +1,22 @@
1
- import { useDurableRunFields, useRunHistory, useDeterminismStatus, useRunStackTrace, RunHistoryView, SignalRunDialog, terminateDurableRun } from './chunk-XLI75E3D.js';
2
- import { useDurableParityRunFields, useRunMetadata, useRunPending, ExecutionStatusBadge, NodeTimeline, RunLogViewer, setRunVersioningOverride } from './chunk-MSQAULLG.js';
3
- import { useExecution, useExecutionActions, useAutoRefresh, runChainLinks, shortRunId, callbackTone, parseMetadataDefinition, overrideRequestOf } from './chunk-AG3VC7Y2.js';
4
- import { fetchRunFailureClusters, OPEN_STATUSES, dismissRunFailureCluster, BUCKET_LABELS } from './chunk-CGR3AEZU.js';
1
+ import { useDurableRunFields, useRunHistory, useDeterminismStatus, useRunStackTrace, RunHistoryView, SignalRunDialog, terminateDurableRun } from './chunk-2WUVF4EQ.js';
2
+ import { useDurableParityRunFields, hasAgentNodeState, agentNodeTraces, orderNodesByExecution, useRunMetadata, useRunPending, ExecutionStatusBadge, NodeTimeline, RunLogViewer, NodeStatusBadge, setRunVersioningOverride } from './chunk-MA6I4KY5.js';
3
+ import { useExecution, useExecutionActions, useAutoRefresh, runChainLinks, shortRunId, callbackTone, parseMetadataDefinition, overrideRequestOf } from './chunk-VESQH55Q.js';
4
+ import { fetchRunFailureClusters, OPEN_STATUSES, dismissRunFailureCluster, BUCKET_LABELS, formatDuration } from './chunk-TCIBTMJI.js';
5
+ import { aiCost, formatTokens, modelLabel } from './chunk-NZAYYUQ4.js';
5
6
  import { useRunPolicyDecisions, RunPolicyDecisions } from './chunk-AMXJLVZN.js';
6
- import { EntityTagPicker, canSaveRunAsTestCase, useWorkflowProposal, WorkflowProposalCard, MAX_CASE_NAME, saveRunAsTestCase } from './chunk-6P7DOBVN.js';
7
- import { buildNodeDisplayMap, formatRunVersion } from './chunk-EC2AN74G.js';
7
+ import { EntityTagPicker, canSaveRunAsTestCase, useWorkflowProposal, WorkflowProposalCard, MAX_CASE_NAME, saveRunAsTestCase } from './chunk-EYQ7GJRW.js';
8
+ import { buildNodeDisplayMap, formatRunVersion, nodeDisplayTitle } from './chunk-IL343FKX.js';
8
9
  import { AiCreditBalanceSlot } from './chunk-Y53CVZVV.js';
9
- import { useWorkflowVersions } from './chunk-HOTVUVZG.js';
10
- import { useDurableFunctionNodesEnabled, useDurableParityEnabled, runSignalTargets, canSignalRun, useWorkflowTestsEnabled } from './chunk-C26QAWCT.js';
10
+ import { useWorkflowVersions } from './chunk-YGOGJDN4.js';
11
+ import { useDurableFunctionNodesEnabled, useDurableParityEnabled, runSignalTargets, canSignalRun, useWorkflowTestsEnabled } from './chunk-ZBPRNWKY.js';
11
12
  import { useFluidGridsPermission, FLUIDGRIDS_PERMISSIONS, SdkPermissionButton } from './chunk-5375NUIO.js';
12
- import { RunLogsDocument, RunLogAppendedDocument, ExplainRunDocument } from './chunk-AVHBWJRY.js';
13
+ import { RunLogsDocument, RunLogAppendedDocument, ExplainRunDocument, RunAiUsageDocument } from './chunk-Z3ABLERJ.js';
13
14
  import { useTr } from './chunk-UULVIT25.js';
14
15
  import { getFluidGridsClient, getFluidGridsClientSafe } from './chunk-3IXU7WUC.js';
15
16
  import { throwIfResultError, describeOperationError } from './chunk-US5QSLPV.js';
16
17
  import { getFluidGridsConfig } from './chunk-UEG2WC3G.js';
17
18
  import { useState, useRef, useCallback, useEffect, useMemo } from 'react';
18
- import { useParams, useNavigate, Link } from 'react-router';
19
+ import { useParams, useNavigate, useSearchParams, Link } from 'react-router';
19
20
  import { Icon } from '@iconify/react';
20
21
  import { toast } from 'sonner';
21
22
  import { Button } from '@burdenoff/fe-libs/button';
@@ -1435,6 +1436,533 @@ function resolveDetailTab(selection, runId, available, fallback) {
1435
1436
  if (selection.runId !== runId) return fallback;
1436
1437
  return available.includes(selection.tab) ? selection.tab : fallback;
1437
1438
  }
1439
+ function parseDetailTabParam(value, known) {
1440
+ if (!value) return null;
1441
+ return known.includes(value) ? value : null;
1442
+ }
1443
+ function selectionForRun(selection, runId, requested, fallback) {
1444
+ if (selection.runId === runId) return selection;
1445
+ return { runId, tab: requested ?? fallback };
1446
+ }
1447
+ function AgentTracePanel({
1448
+ traces,
1449
+ nodeDisplay,
1450
+ usage,
1451
+ usageLoading,
1452
+ usageError,
1453
+ onRefreshUsage,
1454
+ focusNodeId
1455
+ }) {
1456
+ const tr = useTr();
1457
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6", "data-testid": "agent-trace-panel", children: [
1458
+ /* @__PURE__ */ jsx(
1459
+ AiUsageRollup,
1460
+ {
1461
+ usage,
1462
+ loading: usageLoading,
1463
+ error: usageError,
1464
+ onRefresh: onRefreshUsage,
1465
+ nodeDisplay,
1466
+ tr
1467
+ }
1468
+ ),
1469
+ traces.map((trace) => /* @__PURE__ */ jsx(
1470
+ AgentNodeCard,
1471
+ {
1472
+ trace,
1473
+ title: nodeDisplayTitle(nodeDisplay[trace.nodeId], trace.nodeId),
1474
+ focused: !!focusNodeId && focusNodeId === trace.nodeId,
1475
+ tr
1476
+ },
1477
+ trace.nodeStateId
1478
+ )),
1479
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-text-secondary", children: tr(
1480
+ "fluidgrids.executions.executionDetail.agent.traceApiNote",
1481
+ "Each tool call is shown as the agent recorded it. The prompt and completion of each model call, and the timing of each iteration, will appear here once agent tracing is available."
1482
+ ) })
1483
+ ] });
1484
+ }
1485
+ function CostValue({
1486
+ usage,
1487
+ tr
1488
+ }) {
1489
+ const cost = aiCost(usage);
1490
+ if (cost.kind === "priced") return /* @__PURE__ */ jsx(Fragment, { children: cost.text });
1491
+ if (cost.kind === "unpriced") {
1492
+ return /* @__PURE__ */ jsx("span", { className: "text-text-secondary", children: tr(
1493
+ "fluidgrids.executions.executionDetail.agent.notPricedYet",
1494
+ "Not priced yet"
1495
+ ) });
1496
+ }
1497
+ return /* @__PURE__ */ jsx(Fragment, { children: "\u2014" });
1498
+ }
1499
+ function Stat({
1500
+ label,
1501
+ children,
1502
+ detail
1503
+ }) {
1504
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border-secondary bg-background-primary p-3", children: [
1505
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-text-secondary", children: label }),
1506
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-lg font-semibold text-text-primary break-words", children }),
1507
+ detail && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-text-secondary", children: detail })
1508
+ ] });
1509
+ }
1510
+ function AiUsageRollup({
1511
+ usage,
1512
+ loading,
1513
+ error,
1514
+ onRefresh,
1515
+ nodeDisplay,
1516
+ tr
1517
+ }) {
1518
+ const total = usage?.total ?? null;
1519
+ const cost = aiCost(total);
1520
+ return /* @__PURE__ */ jsxs(
1521
+ "section",
1522
+ {
1523
+ "aria-label": tr(
1524
+ "fluidgrids.executions.executionDetail.agent.usageHeading",
1525
+ "AI usage"
1526
+ ),
1527
+ className: "space-y-3",
1528
+ children: [
1529
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
1530
+ /* @__PURE__ */ jsx("h3", { className: "text-base font-semibold text-text-primary", children: tr(
1531
+ "fluidgrids.executions.executionDetail.agent.usageHeading",
1532
+ "AI usage"
1533
+ ) }),
1534
+ /* @__PURE__ */ jsxs(
1535
+ "button",
1536
+ {
1537
+ type: "button",
1538
+ onClick: onRefresh,
1539
+ disabled: loading,
1540
+ className: "flex items-center gap-1 rounded px-2 py-1 text-xs text-text-secondary hover:text-text-primary disabled:opacity-50",
1541
+ children: [
1542
+ /* @__PURE__ */ jsx(
1543
+ Icon,
1544
+ {
1545
+ icon: "mdi:refresh",
1546
+ width: 14,
1547
+ className: loading ? "animate-spin" : void 0
1548
+ }
1549
+ ),
1550
+ tr(
1551
+ "fluidgrids.executions.executionDetail.agent.refreshUsage",
1552
+ "Refresh"
1553
+ )
1554
+ ]
1555
+ }
1556
+ )
1557
+ ] }),
1558
+ error && /* @__PURE__ */ jsx(
1559
+ "div",
1560
+ {
1561
+ role: "alert",
1562
+ className: "rounded-lg border border-status-error-border bg-status-error-bg-subtle p-3 text-sm text-status-error-text",
1563
+ children: tr(
1564
+ "fluidgrids.executions.executionDetail.agent.usageError",
1565
+ "Could not load this run's AI usage: {{message}}",
1566
+ { message: error }
1567
+ )
1568
+ }
1569
+ ),
1570
+ !usage && loading && /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-3 lg:grid-cols-4", "aria-busy": "true", children: [1, 2, 3, 4].map((item) => /* @__PURE__ */ jsx(
1571
+ "div",
1572
+ {
1573
+ className: "h-20 animate-pulse rounded-lg bg-background-tertiary"
1574
+ },
1575
+ item
1576
+ )) }),
1577
+ total ? /* @__PURE__ */ jsxs(Fragment, { children: [
1578
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3 lg:grid-cols-4", children: [
1579
+ /* @__PURE__ */ jsx(
1580
+ Stat,
1581
+ {
1582
+ label: tr(
1583
+ "fluidgrids.executions.executionDetail.agent.modelCalls",
1584
+ "Model calls"
1585
+ ),
1586
+ children: total.calls.toLocaleString("en-US")
1587
+ }
1588
+ ),
1589
+ /* @__PURE__ */ jsx(
1590
+ Stat,
1591
+ {
1592
+ label: tr(
1593
+ "fluidgrids.executions.executionDetail.agent.inputTokens",
1594
+ "Input tokens"
1595
+ ),
1596
+ detail: total.cachedInputTokens > 0 ? tr(
1597
+ "fluidgrids.executions.executionDetail.agent.cachedTokens",
1598
+ "{{count}} cached",
1599
+ { count: formatTokens(total.cachedInputTokens) }
1600
+ ) : null,
1601
+ children: formatTokens(total.inputTokens)
1602
+ }
1603
+ ),
1604
+ /* @__PURE__ */ jsx(
1605
+ Stat,
1606
+ {
1607
+ label: tr(
1608
+ "fluidgrids.executions.executionDetail.agent.outputTokens",
1609
+ "Output tokens"
1610
+ ),
1611
+ children: formatTokens(total.outputTokens)
1612
+ }
1613
+ ),
1614
+ /* @__PURE__ */ jsx(
1615
+ Stat,
1616
+ {
1617
+ label: tr(
1618
+ "fluidgrids.executions.executionDetail.agent.estimatedCost",
1619
+ "Estimated cost"
1620
+ ),
1621
+ detail: cost.kind === "priced" && cost.partial ? tr(
1622
+ "fluidgrids.executions.executionDetail.agent.partialCost",
1623
+ "Calls not priced yet: {{count}}",
1624
+ { count: cost.unpricedCalls }
1625
+ ) : null,
1626
+ children: /* @__PURE__ */ jsx(CostValue, { usage: total, tr })
1627
+ }
1628
+ )
1629
+ ] }),
1630
+ total.calls === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-text-secondary", children: tr(
1631
+ "fluidgrids.executions.executionDetail.agent.noModelCalls",
1632
+ "No model calls recorded for this run yet."
1633
+ ) }) : usage && usage.lines.length > 0 && /* @__PURE__ */ jsx(
1634
+ UsageLines,
1635
+ {
1636
+ lines: usage.lines,
1637
+ nodeDisplay,
1638
+ tr
1639
+ }
1640
+ )
1641
+ ] }) : null
1642
+ ]
1643
+ }
1644
+ );
1645
+ }
1646
+ function UsageLines({
1647
+ lines,
1648
+ nodeDisplay,
1649
+ tr
1650
+ }) {
1651
+ return /* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded-lg border border-border-secondary", children: /* @__PURE__ */ jsxs("table", { className: "w-full min-w-[560px] text-sm", children: [
1652
+ /* @__PURE__ */ jsx("thead", { className: "bg-background-tertiary text-left text-xs text-text-secondary", children: /* @__PURE__ */ jsxs("tr", { children: [
1653
+ /* @__PURE__ */ jsx("th", { className: "px-3 py-2 font-medium", children: tr(
1654
+ "fluidgrids.executions.executionDetail.agent.lineNode",
1655
+ "Node"
1656
+ ) }),
1657
+ /* @__PURE__ */ jsx("th", { className: "px-3 py-2 font-medium", children: tr(
1658
+ "fluidgrids.executions.executionDetail.agent.lineModel",
1659
+ "Model"
1660
+ ) }),
1661
+ /* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium", children: tr(
1662
+ "fluidgrids.executions.executionDetail.agent.lineCalls",
1663
+ "Calls"
1664
+ ) }),
1665
+ /* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium", children: tr(
1666
+ "fluidgrids.executions.executionDetail.agent.lineTokens",
1667
+ "Tokens in / out"
1668
+ ) }),
1669
+ /* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium", children: tr(
1670
+ "fluidgrids.executions.executionDetail.agent.lineCost",
1671
+ "Cost"
1672
+ ) })
1673
+ ] }) }),
1674
+ /* @__PURE__ */ jsx("tbody", { children: lines.map((line) => /* @__PURE__ */ jsxs(
1675
+ "tr",
1676
+ {
1677
+ className: "border-t border-border-secondary",
1678
+ children: [
1679
+ /* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-text-primary", children: nodeDisplayTitle(nodeDisplay[line.nodeId], line.nodeId) }),
1680
+ /* @__PURE__ */ jsx("td", { className: "px-3 py-2 font-mono text-xs text-text-secondary", children: modelLabel(line.providerKey, line.modelKey) ?? tr(
1681
+ "fluidgrids.executions.executionDetail.agent.unknownModel",
1682
+ "Unknown model"
1683
+ ) }),
1684
+ /* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-right text-text-primary", children: line.usage.calls.toLocaleString("en-US") }),
1685
+ /* @__PURE__ */ jsxs("td", { className: "px-3 py-2 text-right text-text-primary", children: [
1686
+ formatTokens(line.usage.inputTokens),
1687
+ " /",
1688
+ " ",
1689
+ formatTokens(line.usage.outputTokens)
1690
+ ] }),
1691
+ /* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-right text-text-primary", children: /* @__PURE__ */ jsx(CostValue, { usage: line.usage, tr }) })
1692
+ ]
1693
+ },
1694
+ `${line.nodeId}:${line.operation}:${line.providerKey ?? ""}:${line.modelKey ?? ""}`
1695
+ )) })
1696
+ ] }) });
1697
+ }
1698
+ function AgentNodeCard({
1699
+ trace,
1700
+ title,
1701
+ focused,
1702
+ tr
1703
+ }) {
1704
+ const ref = useRef(null);
1705
+ useEffect(() => {
1706
+ if (focused) ref.current?.scrollIntoView({ block: "start" });
1707
+ }, [focused]);
1708
+ return /* @__PURE__ */ jsxs(
1709
+ "section",
1710
+ {
1711
+ ref,
1712
+ "aria-label": title,
1713
+ "data-agent-node": trace.nodeId,
1714
+ className: `rounded-lg border bg-background-primary p-4 ${focused ? "border-action-primary-bg" : "border-border-secondary"}`,
1715
+ children: [
1716
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between", children: [
1717
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
1718
+ /* @__PURE__ */ jsx(
1719
+ Icon,
1720
+ {
1721
+ icon: "mdi:robot-outline",
1722
+ width: 18,
1723
+ className: "shrink-0 text-text-secondary"
1724
+ }
1725
+ ),
1726
+ /* @__PURE__ */ jsx("h4", { className: "truncate font-medium text-text-primary", children: title })
1727
+ ] }),
1728
+ /* @__PURE__ */ jsx("div", { className: "self-start sm:self-auto", children: /* @__PURE__ */ jsx(NodeStatusBadge, { status: trace.status, size: "sm" }) })
1729
+ ] }),
1730
+ /* @__PURE__ */ jsxs("dl", { className: "mt-3 grid grid-cols-2 gap-x-4 gap-y-2 text-sm sm:grid-cols-4", children: [
1731
+ /* @__PURE__ */ jsx(
1732
+ Fact,
1733
+ {
1734
+ label: tr(
1735
+ "fluidgrids.executions.executionDetail.agent.iterations",
1736
+ "Iterations"
1737
+ ),
1738
+ value: trace.iterationCount?.toLocaleString("en-US") ?? "\u2014"
1739
+ }
1740
+ ),
1741
+ /* @__PURE__ */ jsx(
1742
+ Fact,
1743
+ {
1744
+ label: tr(
1745
+ "fluidgrids.executions.executionDetail.agent.tokensUsed",
1746
+ "Tokens used"
1747
+ ),
1748
+ value: trace.tokensUsed === null ? "\u2014" : formatTokens(trace.tokensUsed)
1749
+ }
1750
+ ),
1751
+ /* @__PURE__ */ jsx(
1752
+ Fact,
1753
+ {
1754
+ label: tr(
1755
+ "fluidgrids.executions.executionDetail.agent.toolCalls",
1756
+ "Tool calls"
1757
+ ),
1758
+ value: trace.steps.length.toLocaleString("en-US")
1759
+ }
1760
+ ),
1761
+ /* @__PURE__ */ jsx(
1762
+ Fact,
1763
+ {
1764
+ label: tr(
1765
+ "fluidgrids.executions.executionDetail.agent.duration",
1766
+ "Duration"
1767
+ ),
1768
+ value: trace.durationMs === null ? "\u2014" : formatDuration(trace.durationMs / 1e3)
1769
+ }
1770
+ )
1771
+ ] }),
1772
+ trace.error && /* @__PURE__ */ jsxs("div", { className: "mt-3 rounded border border-status-error-border bg-status-error-bg-subtle p-3 text-sm text-status-error-text", children: [
1773
+ /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
1774
+ tr("fluidgrids.executions.executionDetail.agent.error", "Error"),
1775
+ ":",
1776
+ " "
1777
+ ] }),
1778
+ /* @__PURE__ */ jsx("span", { className: "break-words", children: trace.error })
1779
+ ] }),
1780
+ trace.waitingForApproval && /* @__PURE__ */ jsx(PendingApprovals, { trace, tr }),
1781
+ /* @__PURE__ */ jsxs("div", { className: "mt-4", children: [
1782
+ /* @__PURE__ */ jsx("h5", { className: "mb-2 text-sm font-medium text-text-primary", children: tr(
1783
+ "fluidgrids.executions.executionDetail.agent.stepsHeading",
1784
+ "Tool calls"
1785
+ ) }),
1786
+ trace.steps.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-text-secondary", children: tr(
1787
+ "fluidgrids.executions.executionDetail.agent.noSteps",
1788
+ "The agent made no tool calls."
1789
+ ) }) : /* @__PURE__ */ jsx("ol", { className: "space-y-2", children: trace.steps.map((step) => /* @__PURE__ */ jsx(AgentStepRow, { step, tr }, step.index)) })
1790
+ ] }),
1791
+ trace.response && /* @__PURE__ */ jsxs("details", { className: "mt-4", open: true, children: [
1792
+ /* @__PURE__ */ jsx("summary", { className: "cursor-pointer text-sm font-medium text-text-primary", children: tr(
1793
+ "fluidgrids.executions.executionDetail.agent.finalResponse",
1794
+ "Final response"
1795
+ ) }),
1796
+ /* @__PURE__ */ jsx("pre", { className: "mt-2 max-h-80 overflow-auto rounded bg-background-tertiary p-3 font-mono text-xs whitespace-pre-wrap break-words text-text-primary", children: trace.response })
1797
+ ] })
1798
+ ]
1799
+ }
1800
+ );
1801
+ }
1802
+ function Fact({ label, value }) {
1803
+ return /* @__PURE__ */ jsxs("div", { children: [
1804
+ /* @__PURE__ */ jsx("dt", { className: "text-xs text-text-secondary", children: label }),
1805
+ /* @__PURE__ */ jsx("dd", { className: "text-text-primary", children: value })
1806
+ ] });
1807
+ }
1808
+ function PendingApprovals({
1809
+ trace,
1810
+ tr
1811
+ }) {
1812
+ return /* @__PURE__ */ jsxs("div", { className: "mt-3 rounded-lg border border-status-warning-border bg-status-warning-bg-subtle p-3", children: [
1813
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between", children: [
1814
+ /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-sm font-medium text-status-warning-text", children: [
1815
+ /* @__PURE__ */ jsx(Icon, { icon: "mdi:account-clock-outline", width: 16 }),
1816
+ tr(
1817
+ "fluidgrids.executions.executionDetail.agent.awaitingApproval",
1818
+ "Tool calls waiting for approval: {{count}}",
1819
+ { count: trace.pendingApprovals.length }
1820
+ )
1821
+ ] }),
1822
+ /* @__PURE__ */ jsx(
1823
+ Link,
1824
+ {
1825
+ to: "../approvals",
1826
+ className: "text-sm font-medium text-text-link underline-offset-2 hover:text-text-linkHover hover:underline",
1827
+ children: tr(
1828
+ "fluidgrids.executions.executionDetail.agent.reviewApprovals",
1829
+ "Review in Approvals"
1830
+ )
1831
+ }
1832
+ )
1833
+ ] }),
1834
+ trace.pendingApprovals.length > 0 && /* @__PURE__ */ jsx("ul", { className: "mt-2 space-y-2", children: trace.pendingApprovals.map((call) => /* @__PURE__ */ jsxs("li", { className: "rounded bg-background-primary p-2", children: [
1835
+ /* @__PURE__ */ jsx("p", { className: "font-mono text-xs font-medium text-text-primary", children: call.tool }),
1836
+ call.args && /* @__PURE__ */ jsx("pre", { className: "mt-1 max-h-48 overflow-auto font-mono text-xs whitespace-pre-wrap break-words text-text-secondary", children: call.args })
1837
+ ] }, call.key)) })
1838
+ ] });
1839
+ }
1840
+ function AgentStepRow({ step, tr }) {
1841
+ const marked = step.outcome !== "ok";
1842
+ return /* @__PURE__ */ jsx(
1843
+ "li",
1844
+ {
1845
+ className: `rounded border ${step.outcome === "rejected" ? "border-status-warning-border" : step.outcome === "failed" ? "border-status-error-border" : "border-border-secondary"}`,
1846
+ children: /* @__PURE__ */ jsxs("details", { children: [
1847
+ /* @__PURE__ */ jsxs("summary", { className: "flex cursor-pointer flex-wrap items-center gap-2 px-3 py-2 text-sm", children: [
1848
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-text-secondary", children: [
1849
+ step.index + 1,
1850
+ "."
1851
+ ] }),
1852
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 break-all font-mono text-text-primary", children: step.tool }),
1853
+ step.outcome === "rejected" && /* @__PURE__ */ jsx("span", { className: "rounded bg-status-warning-bg-subtle px-1.5 py-0.5 text-xs text-status-warning-text", children: tr(
1854
+ "fluidgrids.executions.executionDetail.agent.stepRejected",
1855
+ "Rejected"
1856
+ ) }),
1857
+ step.outcome === "failed" && /* @__PURE__ */ jsx("span", { className: "rounded bg-status-error-bg-subtle px-1.5 py-0.5 text-xs text-status-error-text", children: tr(
1858
+ "fluidgrids.executions.executionDetail.agent.stepFailed",
1859
+ "Failed"
1860
+ ) }),
1861
+ step.durationMs !== null && /* @__PURE__ */ jsx("span", { className: "ml-auto text-xs text-text-secondary", children: formatDuration(step.durationMs / 1e3) })
1862
+ ] }),
1863
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2 border-t border-border-secondary px-3 py-2", children: [
1864
+ marked && step.reason && /* @__PURE__ */ jsx(
1865
+ "p",
1866
+ {
1867
+ className: `text-sm ${step.outcome === "rejected" ? "text-status-warning-text" : "text-status-error-text"}`,
1868
+ children: step.reason
1869
+ }
1870
+ ),
1871
+ /* @__PURE__ */ jsx(
1872
+ StepBlock,
1873
+ {
1874
+ label: tr(
1875
+ "fluidgrids.executions.executionDetail.agent.stepArguments",
1876
+ "Arguments"
1877
+ ),
1878
+ value: step.args,
1879
+ empty: tr(
1880
+ "fluidgrids.executions.executionDetail.agent.stepNoArguments",
1881
+ "No arguments"
1882
+ )
1883
+ }
1884
+ ),
1885
+ /* @__PURE__ */ jsx(
1886
+ StepBlock,
1887
+ {
1888
+ label: tr(
1889
+ "fluidgrids.executions.executionDetail.agent.stepResult",
1890
+ "Result"
1891
+ ),
1892
+ value: step.observation,
1893
+ empty: tr(
1894
+ "fluidgrids.executions.executionDetail.agent.stepNoResult",
1895
+ "No result recorded"
1896
+ )
1897
+ }
1898
+ )
1899
+ ] })
1900
+ ] })
1901
+ }
1902
+ );
1903
+ }
1904
+ function StepBlock({
1905
+ label,
1906
+ value,
1907
+ empty
1908
+ }) {
1909
+ return /* @__PURE__ */ jsxs("div", { children: [
1910
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-text-secondary", children: label }),
1911
+ value ? /* @__PURE__ */ jsx("pre", { className: "mt-1 max-h-64 overflow-auto rounded bg-background-tertiary p-2 font-mono text-xs whitespace-pre-wrap break-words text-text-primary", children: value }) : /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-text-secondary", children: empty })
1912
+ ] });
1913
+ }
1914
+ var RUN_AI_USAGE_POLL_INTERVAL_MS = 5e3;
1915
+ async function fetchRunAiUsage(runId) {
1916
+ const result = await getFluidGridsClient().query({
1917
+ query: RunAiUsageDocument,
1918
+ variables: { runId },
1919
+ fetchPolicy: "network-only"
1920
+ });
1921
+ throwIfResultError(result);
1922
+ return result.data?.runAiUsage ?? null;
1923
+ }
1924
+ function useRunAiUsage(runId, { enabled, live }) {
1925
+ const [sent, setSent] = useState(0);
1926
+ const [settled, setSettled] = useState(null);
1927
+ const request = useRef(0);
1928
+ const read = useCallback(async () => {
1929
+ if (!runId || !enabled) return;
1930
+ const id = ++request.current;
1931
+ setSent(id);
1932
+ let next;
1933
+ try {
1934
+ next = { usage: await fetchRunAiUsage(runId), error: null };
1935
+ } catch (err) {
1936
+ next = {
1937
+ usage: null,
1938
+ error: err instanceof Error ? err.message : String(err)
1939
+ };
1940
+ }
1941
+ if (id !== request.current) return;
1942
+ setSettled((previous) => ({
1943
+ request: id,
1944
+ runId,
1945
+ // A failed refresh keeps the last good read of the same run on screen.
1946
+ usage: next.usage ?? (next.error && previous?.runId === runId ? previous.usage : null),
1947
+ error: next.error
1948
+ }));
1949
+ }, [runId, enabled]);
1950
+ useEffect(() => {
1951
+ void read();
1952
+ }, [read]);
1953
+ useEffect(() => {
1954
+ if (!enabled || !live) return;
1955
+ const timer = setInterval(() => void read(), RUN_AI_USAGE_POLL_INTERVAL_MS);
1956
+ return () => clearInterval(timer);
1957
+ }, [enabled, live, read]);
1958
+ const current = enabled && settled?.runId === runId ? settled : null;
1959
+ return {
1960
+ usage: current?.usage ?? null,
1961
+ loading: enabled && !!runId && (current?.request ?? -1) !== sent,
1962
+ error: current?.error ?? null,
1963
+ refetch: read
1964
+ };
1965
+ }
1438
1966
  function formatTime(value) {
1439
1967
  if (!value) return null;
1440
1968
  const date = new Date(value);
@@ -2299,19 +2827,43 @@ function SaveRunAsTestCase({ runId }) {
2299
2827
  var ALL_DETAIL_TABS = [
2300
2828
  "timeline",
2301
2829
  "history",
2830
+ "agent",
2302
2831
  "logs",
2303
2832
  "input",
2304
2833
  "context"
2305
2834
  ];
2306
- var GRAPH_DETAIL_TABS = ALL_DETAIL_TABS.filter(
2307
- (tab) => tab !== "history"
2308
- );
2835
+ function availableDetailTabs(isDurableRun, hasAgent) {
2836
+ return ALL_DETAIL_TABS.filter(
2837
+ (tab) => (tab !== "history" || isDurableRun) && (tab !== "agent" || hasAgent)
2838
+ );
2839
+ }
2309
2840
  function ExecutionDetailPage() {
2310
2841
  const tr = useTr();
2311
2842
  const { runId } = useParams();
2312
2843
  const navigate = useNavigate();
2313
- const [tabSelection, setTabSelection] = useState({ runId, tab: "timeline" });
2314
- const setActiveTab = (tab) => setTabSelection({ runId, tab });
2844
+ const [searchParams, setSearchParams] = useSearchParams();
2845
+ const requestedTab = parseDetailTabParam(
2846
+ searchParams.get("tab"),
2847
+ ALL_DETAIL_TABS
2848
+ );
2849
+ const requestedNodeId = searchParams.get("node");
2850
+ const [tabSelection, setTabSelection] = useState({ runId, tab: requestedTab ?? "timeline" });
2851
+ const [agentFocus, setAgentFocus] = useState({ runId, nodeId: requestedNodeId });
2852
+ const setActiveTab = (tab, focusNodeId = null) => {
2853
+ setTabSelection({ runId, tab });
2854
+ setAgentFocus({ runId, nodeId: focusNodeId });
2855
+ setSearchParams(
2856
+ (params) => {
2857
+ const next = new URLSearchParams(params);
2858
+ if (tab === "timeline") next.delete("tab");
2859
+ else next.set("tab", tab);
2860
+ if (focusNodeId) next.set("node", focusNodeId);
2861
+ else next.delete("node");
2862
+ return next;
2863
+ },
2864
+ { replace: true }
2865
+ );
2866
+ };
2315
2867
  const [autoRefreshEnabled, setAutoRefreshEnabled] = useState(true);
2316
2868
  const [showTerminateDialog, setShowTerminateDialog] = useState(false);
2317
2869
  const { allowed: canControlRuns } = useFluidGridsPermission(
@@ -2427,12 +2979,27 @@ function ExecutionDetailPage() {
2427
2979
  minLevel: minLogLevel
2428
2980
  });
2429
2981
  const isDurableRun = durableEnabled && execution?.executionKind === "DURABLE_FN";
2982
+ const hasAgent = hasAgentNodeState(execution?.nodeStates);
2430
2983
  const activeTab = resolveDetailTab(
2431
- tabSelection,
2984
+ selectionForRun(tabSelection, runId, requestedTab, "timeline"),
2432
2985
  runId,
2433
- isDurableRun ? ALL_DETAIL_TABS : GRAPH_DETAIL_TABS,
2986
+ availableDetailTabs(isDurableRun, hasAgent),
2434
2987
  "timeline"
2435
2988
  );
2989
+ const agentFocusNodeId = agentFocus.runId === runId ? agentFocus.nodeId : requestedNodeId;
2990
+ const runAiUsage = useRunAiUsage(runId, {
2991
+ enabled: hasAgent && activeTab === "agent",
2992
+ live: autoRefreshEnabled && isActive
2993
+ });
2994
+ const agentTraces = useMemo(
2995
+ () => agentNodeTraces(
2996
+ orderNodesByExecution(
2997
+ execution?.nodeStates ?? [],
2998
+ execution?.triggerNodeId ?? void 0
2999
+ )
3000
+ ),
3001
+ [execution?.nodeStates, execution?.triggerNodeId]
3002
+ );
2436
3003
  const runHistory = useRunHistory(runId, {
2437
3004
  enabled: isDurableRun && activeTab === "history",
2438
3005
  live: isActive
@@ -2547,7 +3114,7 @@ function ExecutionDetailPage() {
2547
3114
  },
2548
3115
  [navigate]
2549
3116
  );
2550
- const formatDuration = (ms) => {
3117
+ const formatDuration2 = (ms) => {
2551
3118
  if (!ms) return "-";
2552
3119
  if (ms < 1e3) return `${ms}ms`;
2553
3120
  if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
@@ -2677,6 +3244,16 @@ function ExecutionDetailPage() {
2677
3244
  icon: "mdi:history"
2678
3245
  }
2679
3246
  ] : [],
3247
+ ...hasAgent ? [
3248
+ {
3249
+ id: "agent",
3250
+ label: tr(
3251
+ "fluidgrids.executions.executionDetail.agentTab",
3252
+ "Agent"
3253
+ ),
3254
+ icon: "mdi:robot-outline"
3255
+ }
3256
+ ] : [],
2680
3257
  {
2681
3258
  id: "logs",
2682
3259
  label: tr("fluidgrids.executions.executionDetail.logsTab", "Logs"),
@@ -3001,7 +3578,7 @@ function ExecutionDetailPage() {
3001
3578
  "fluidgrids.executions.executionDetail.durationStat",
3002
3579
  "Duration"
3003
3580
  ) }),
3004
- /* @__PURE__ */ jsx("div", { className: "text-xl font-bold text-text-primary mt-1", children: formatDuration(execution.duration) })
3581
+ /* @__PURE__ */ jsx("div", { className: "text-xl font-bold text-text-primary mt-1", children: formatDuration2(execution.duration) })
3005
3582
  ] }),
3006
3583
  /* @__PURE__ */ jsxs("div", { className: "bg-background-secondary rounded-lg border border-border-secondary p-4", children: [
3007
3584
  /* @__PURE__ */ jsx("div", { className: "text-sm text-text-secondary", children: tr(
@@ -3126,12 +3703,12 @@ function ExecutionDetailPage() {
3126
3703
  onViewChildRun: handleViewChildRun
3127
3704
  }
3128
3705
  ),
3129
- /* @__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(
3706
+ /* @__PURE__ */ jsx("div", { className: "border-b border-border-secondary mb-6", children: /* @__PURE__ */ jsx("nav", { className: "flex gap-4 -mb-px overflow-x-auto", children: tabs.map((tab) => /* @__PURE__ */ jsxs(
3130
3707
  "button",
3131
3708
  {
3132
3709
  onClick: () => setActiveTab(tab.id),
3133
3710
  className: `
3134
- flex items-center gap-2 px-4 py-3 border-b-2 text-sm font-medium transition-colors
3711
+ flex shrink-0 items-center gap-2 whitespace-nowrap px-4 py-3 border-b-2 text-sm font-medium transition-colors
3135
3712
  ${activeTab === tab.id ? "border-action-primary-bg text-action-primary-text" : "border-transparent text-text-secondary hover:text-text-primary hover:border-border-primary"}
3136
3713
  `,
3137
3714
  children: [
@@ -3149,7 +3726,20 @@ function ExecutionDetailPage() {
3149
3726
  nodeDisplay,
3150
3727
  triggerNodeId: execution.triggerNodeId ?? void 0,
3151
3728
  onRetryNode: canControlRuns ? handleRetryNode : void 0,
3152
- onViewChildRun: handleViewChildRun
3729
+ onViewChildRun: handleViewChildRun,
3730
+ onViewAgentTrace: (nodeId) => setActiveTab("agent", nodeId)
3731
+ }
3732
+ ) }),
3733
+ activeTab === "agent" && hasAgent && /* @__PURE__ */ jsx("div", { className: "p-4 sm:p-6", children: /* @__PURE__ */ jsx(
3734
+ AgentTracePanel,
3735
+ {
3736
+ traces: agentTraces,
3737
+ nodeDisplay,
3738
+ usage: runAiUsage.usage,
3739
+ usageLoading: runAiUsage.loading,
3740
+ usageError: runAiUsage.error,
3741
+ onRefreshUsage: () => void runAiUsage.refetch(),
3742
+ focusNodeId: agentFocusNodeId
3153
3743
  }
3154
3744
  ) }),
3155
3745
  activeTab === "history" && isDurableRun && /* @__PURE__ */ jsxs("div", { className: "space-y-4 p-4 sm:p-6", children: [