@agent-native/dispatch 0.24.0 → 0.24.2

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 (161) hide show
  1. package/dist/actions/provider-api-register.d.ts +12 -12
  2. package/dist/components/admin-navigation.d.ts.map +1 -1
  3. package/dist/components/admin-navigation.js +21 -5
  4. package/dist/components/admin-navigation.js.map +1 -1
  5. package/dist/components/app-list-row.d.ts +1 -1
  6. package/dist/components/app-list-row.d.ts.map +1 -1
  7. package/dist/components/app-list-row.js +1 -1
  8. package/dist/components/app-list-row.js.map +1 -1
  9. package/dist/components/app-open-actions.d.ts +1 -17
  10. package/dist/components/app-open-actions.d.ts.map +1 -1
  11. package/dist/components/app-open-actions.js +1 -20
  12. package/dist/components/app-open-actions.js.map +1 -1
  13. package/dist/components/app-row-settings.d.ts +6 -0
  14. package/dist/components/app-row-settings.d.ts.map +1 -0
  15. package/dist/components/app-row-settings.js +11 -0
  16. package/dist/components/app-row-settings.js.map +1 -0
  17. package/dist/components/automation-run-history-dialog.d.ts.map +1 -1
  18. package/dist/components/automation-run-history-dialog.js +3 -2
  19. package/dist/components/automation-run-history-dialog.js.map +1 -1
  20. package/dist/components/connected-app-card.d.ts.map +1 -1
  21. package/dist/components/connected-app-card.js +1 -1
  22. package/dist/components/connected-app-card.js.map +1 -1
  23. package/dist/components/create-app-popover.js +2 -2
  24. package/dist/components/create-app-popover.js.map +1 -1
  25. package/dist/components/dispatch-control-plane.d.ts.map +1 -1
  26. package/dist/components/dispatch-control-plane.js +23 -7
  27. package/dist/components/dispatch-control-plane.js.map +1 -1
  28. package/dist/components/dispatch-shell.d.ts.map +1 -1
  29. package/dist/components/dispatch-shell.js +5 -1
  30. package/dist/components/dispatch-shell.js.map +1 -1
  31. package/dist/components/docs-link.d.ts +16 -0
  32. package/dist/components/docs-link.d.ts.map +1 -0
  33. package/dist/components/docs-link.js +34 -0
  34. package/dist/components/docs-link.js.map +1 -0
  35. package/dist/components/layout/Layout.d.ts +1 -0
  36. package/dist/components/layout/Layout.d.ts.map +1 -1
  37. package/dist/components/layout/Layout.js +37 -28
  38. package/dist/components/layout/Layout.js.map +1 -1
  39. package/dist/components/messaging-setup-panel.d.ts.map +1 -1
  40. package/dist/components/messaging-setup-panel.js +3 -2
  41. package/dist/components/messaging-setup-panel.js.map +1 -1
  42. package/dist/components/usage-alerts-panel.d.ts +11 -0
  43. package/dist/components/usage-alerts-panel.d.ts.map +1 -0
  44. package/dist/components/usage-alerts-panel.js +125 -0
  45. package/dist/components/usage-alerts-panel.js.map +1 -0
  46. package/dist/components/workspace-app-card.d.ts +3 -1
  47. package/dist/components/workspace-app-card.d.ts.map +1 -1
  48. package/dist/components/workspace-app-card.js +70 -29
  49. package/dist/components/workspace-app-card.js.map +1 -1
  50. package/dist/components/workspace-app-search.d.ts +10 -0
  51. package/dist/components/workspace-app-search.d.ts.map +1 -0
  52. package/dist/components/workspace-app-search.js +27 -0
  53. package/dist/components/workspace-app-search.js.map +1 -0
  54. package/dist/components/workspace-resource-effective-stack.d.ts.map +1 -1
  55. package/dist/components/workspace-resource-effective-stack.js +3 -2
  56. package/dist/components/workspace-resource-effective-stack.js.map +1 -1
  57. package/dist/components/workspace-template-card.d.ts.map +1 -1
  58. package/dist/components/workspace-template-card.js +2 -1
  59. package/dist/components/workspace-template-card.js.map +1 -1
  60. package/dist/lib/workspace-app-layout.d.ts +19 -0
  61. package/dist/lib/workspace-app-layout.d.ts.map +1 -0
  62. package/dist/lib/workspace-app-layout.js +75 -0
  63. package/dist/lib/workspace-app-layout.js.map +1 -0
  64. package/dist/lib/workspace-apps.d.ts +10 -0
  65. package/dist/lib/workspace-apps.d.ts.map +1 -1
  66. package/dist/lib/workspace-apps.js +59 -0
  67. package/dist/lib/workspace-apps.js.map +1 -1
  68. package/dist/lib/workspace-apps.test.js +18 -1
  69. package/dist/lib/workspace-apps.test.js.map +1 -1
  70. package/dist/routes/pages/$appId.d.ts.map +1 -1
  71. package/dist/routes/pages/$appId.js +4 -2
  72. package/dist/routes/pages/$appId.js.map +1 -1
  73. package/dist/routes/pages/agents.d.ts.map +1 -1
  74. package/dist/routes/pages/agents.js +2 -3
  75. package/dist/routes/pages/agents.js.map +1 -1
  76. package/dist/routes/pages/apps.$appId.d.ts.map +1 -1
  77. package/dist/routes/pages/apps.$appId.js +8 -3
  78. package/dist/routes/pages/apps.$appId.js.map +1 -1
  79. package/dist/routes/pages/apps.d.ts.map +1 -1
  80. package/dist/routes/pages/apps.js +31 -16
  81. package/dist/routes/pages/apps.js.map +1 -1
  82. package/dist/routes/pages/audit.d.ts.map +1 -1
  83. package/dist/routes/pages/audit.js +2 -1
  84. package/dist/routes/pages/audit.js.map +1 -1
  85. package/dist/routes/pages/dreams.d.ts.map +1 -1
  86. package/dist/routes/pages/dreams.js +14 -18
  87. package/dist/routes/pages/dreams.js.map +1 -1
  88. package/dist/routes/pages/integrations.d.ts.map +1 -1
  89. package/dist/routes/pages/integrations.js +51 -14
  90. package/dist/routes/pages/integrations.js.map +1 -1
  91. package/dist/routes/pages/metrics.d.ts.map +1 -1
  92. package/dist/routes/pages/metrics.js +4 -1
  93. package/dist/routes/pages/metrics.js.map +1 -1
  94. package/dist/routes/pages/operations.js +1 -1
  95. package/dist/routes/pages/operations.js.map +1 -1
  96. package/dist/routes/pages/settings.d.ts.map +1 -1
  97. package/dist/routes/pages/settings.js +7 -2
  98. package/dist/routes/pages/settings.js.map +1 -1
  99. package/dist/routes/pages/thread-debug.d.ts.map +1 -1
  100. package/dist/routes/pages/thread-debug.js +5 -8
  101. package/dist/routes/pages/thread-debug.js.map +1 -1
  102. package/dist/routes/pages/vault.d.ts.map +1 -1
  103. package/dist/routes/pages/vault.js +1 -1
  104. package/dist/routes/pages/vault.js.map +1 -1
  105. package/dist/routes/pages/workspace.d.ts.map +1 -1
  106. package/dist/routes/pages/workspace.js +13 -10
  107. package/dist/routes/pages/workspace.js.map +1 -1
  108. package/dist/server/index.d.ts.map +1 -1
  109. package/dist/server/index.js +3 -0
  110. package/dist/server/index.js.map +1 -1
  111. package/dist/server/lib/app-creation-store.d.ts.map +1 -1
  112. package/dist/server/lib/app-creation-store.js +12 -52
  113. package/dist/server/lib/app-creation-store.js.map +1 -1
  114. package/dist/shared/app-roles.d.ts +9 -0
  115. package/dist/shared/app-roles.d.ts.map +1 -0
  116. package/dist/shared/app-roles.js +7 -0
  117. package/dist/shared/app-roles.js.map +1 -0
  118. package/package.json +4 -3
  119. package/src/components/admin-navigation.tsx +57 -11
  120. package/src/components/app-list-row.tsx +1 -2
  121. package/src/components/app-open-actions.tsx +5 -114
  122. package/src/components/app-row-settings.tsx +54 -0
  123. package/src/components/automation-run-history-dialog.tsx +8 -4
  124. package/src/components/connected-app-card.tsx +0 -1
  125. package/src/components/create-app-popover.spec.tsx +1 -1
  126. package/src/components/create-app-popover.tsx +2 -2
  127. package/src/components/dispatch-control-plane.spec.tsx +36 -2
  128. package/src/components/dispatch-control-plane.tsx +83 -27
  129. package/src/components/dispatch-shell.tsx +7 -1
  130. package/src/components/docs-link.tsx +52 -0
  131. package/src/components/layout/Layout.spec.tsx +10 -0
  132. package/src/components/layout/Layout.tsx +50 -23
  133. package/src/components/messaging-setup-panel.tsx +10 -3
  134. package/src/components/usage-alerts-panel.tsx +445 -0
  135. package/src/components/workspace-app-card.spec.tsx +130 -9
  136. package/src/components/workspace-app-card.tsx +128 -71
  137. package/src/components/workspace-app-search.tsx +96 -0
  138. package/src/components/workspace-resource-effective-stack.tsx +10 -5
  139. package/src/components/workspace-template-card.spec.tsx +52 -23
  140. package/src/components/workspace-template-card.tsx +7 -10
  141. package/src/lib/workspace-app-layout.spec.ts +47 -0
  142. package/src/lib/workspace-app-layout.ts +121 -0
  143. package/src/lib/workspace-apps.test.ts +45 -1
  144. package/src/lib/workspace-apps.ts +72 -0
  145. package/src/routes/pages/$appId.tsx +3 -6
  146. package/src/routes/pages/agents.tsx +6 -3
  147. package/src/routes/pages/apps.$appId.tsx +6 -6
  148. package/src/routes/pages/apps.tsx +116 -62
  149. package/src/routes/pages/audit.tsx +28 -8
  150. package/src/routes/pages/dreams.tsx +92 -142
  151. package/src/routes/pages/integrations.tsx +147 -68
  152. package/src/routes/pages/metrics.tsx +8 -0
  153. package/src/routes/pages/operations.tsx +2 -2
  154. package/src/routes/pages/settings.tsx +14 -9
  155. package/src/routes/pages/thread-debug.tsx +18 -36
  156. package/src/routes/pages/vault.tsx +7 -2
  157. package/src/routes/pages/workspace.tsx +92 -56
  158. package/src/server/index.ts +4 -0
  159. package/src/server/lib/app-creation-store.spec.ts +90 -43
  160. package/src/server/lib/app-creation-store.ts +15 -61
  161. package/src/shared/app-roles.ts +8 -0
@@ -2,6 +2,7 @@ import { useActionQuery } from "@agent-native/core/client/hooks";
2
2
 
3
3
  import { ActionQueryError } from "../../components/action-query-error";
4
4
  import { DispatchShell } from "../../components/dispatch-shell";
5
+ import { Skeleton } from "../../components/ui/skeleton";
5
6
 
6
7
  export function meta() {
7
8
  return [{ title: "Audit — Dispatch" }];
@@ -16,30 +17,49 @@ export default function AuditRoute() {
16
17
  title="Audit"
17
18
  description="Change history for routes, settings, and approvals."
18
19
  >
19
- <section className="rounded-2xl bg-card p-5">
20
+ <section className="overflow-hidden rounded-lg bg-card">
20
21
  {query.isError ? (
21
22
  <ActionQueryError
22
23
  error={query.error}
23
24
  onRetry={() => void query.refetch()}
24
25
  />
26
+ ) : query.isLoading ? (
27
+ <div>
28
+ {Array.from({ length: 6 }).map((_, index) => (
29
+ <div
30
+ key={index}
31
+ className="grid gap-2 border-b px-4 py-3 last:border-b-0 sm:grid-cols-[minmax(0,1fr)_auto] sm:items-center"
32
+ >
33
+ <div>
34
+ <Skeleton className="h-4 w-2/3 max-w-sm" />
35
+ <Skeleton className="mt-2 h-3 w-1/2 max-w-xs" />
36
+ </div>
37
+ <Skeleton className="h-3 w-28" />
38
+ </div>
39
+ ))}
40
+ </div>
25
41
  ) : (
26
- <div className="space-y-3">
42
+ <div>
27
43
  {(data || []).map((event: any) => (
28
44
  <div
29
45
  key={event.id}
30
- className="rounded-xl border bg-muted/30 px-4 py-3"
46
+ className="grid gap-1 border-b px-4 py-3 last:border-b-0 sm:grid-cols-[minmax(0,1fr)_auto] sm:items-center"
31
47
  >
32
- <div className="text-sm font-medium text-foreground">
33
- {event.summary}
48
+ <div className="min-w-0">
49
+ <div className="truncate text-sm font-medium text-foreground">
50
+ {event.summary}
51
+ </div>
52
+ <div className="mt-1 text-xs text-muted-foreground">
53
+ {event.actor} · {event.action}
54
+ </div>
34
55
  </div>
35
- <div className="mt-1 text-xs text-muted-foreground">
36
- {event.actor} · {event.action} ·{" "}
56
+ <div className="text-xs text-muted-foreground sm:text-right">
37
57
  {new Date(event.createdAt).toLocaleString()}
38
58
  </div>
39
59
  </div>
40
60
  ))}
41
61
  {(data?.length || 0) === 0 && (
42
- <div className="rounded-xl border border-dashed px-4 py-6 text-sm text-muted-foreground">
62
+ <div className="px-4 py-10 text-center text-sm text-muted-foreground">
43
63
  No audit entries yet.
44
64
  </div>
45
65
  )}
@@ -4,11 +4,8 @@ import {
4
4
  } from "@agent-native/core/client/hooks";
5
5
  import {
6
6
  IconAlertTriangle,
7
- IconBrain,
8
7
  IconCalendarTime,
9
8
  IconCheck,
10
- IconCircleDashed,
11
- IconClock,
12
9
  IconDatabase,
13
10
  IconFileDiff,
14
11
  IconPlayerPlay,
@@ -548,32 +545,6 @@ function ProposalSkeleton() {
548
545
  );
549
546
  }
550
547
 
551
- function StatTile({
552
- label,
553
- value,
554
- icon: Icon,
555
- }: {
556
- label: string;
557
- value: string | number;
558
- icon: typeof IconBrain;
559
- }) {
560
- return (
561
- <div className="rounded-lg bg-card px-3 py-2.5">
562
- <div className="flex items-center justify-between gap-2">
563
- <div className="min-w-0 flex-1">
564
- <div className="truncate text-[11px] font-medium uppercase tracking-wide text-muted-foreground">
565
- {label}
566
- </div>
567
- <div className="mt-1 text-xl font-semibold tabular-nums text-foreground">
568
- {value}
569
- </div>
570
- </div>
571
- <Icon size={18} className="shrink-0 text-muted-foreground" />
572
- </div>
573
- </div>
574
- );
575
- }
576
-
577
548
  function DreamSettingsSheet({
578
549
  open,
579
550
  onOpenChange,
@@ -1358,10 +1329,6 @@ export default function DreamsRoute() {
1358
1329
  const evidenceItems: DreamEvidence[] = detail?.evidence ?? [];
1359
1330
  const selectedSourceHealth: DreamSourceHealth[] =
1360
1331
  selectedDream?.sourceHealth ?? [];
1361
- const pendingProposalCount = proposals.filter(
1362
- (proposal) =>
1363
- String(proposal.status || "pending").toLowerCase() === "pending",
1364
- ).length;
1365
1332
  const appliedProposalCount = proposals.filter(
1366
1333
  (proposal) => String(proposal.status || "").toLowerCase() === "applied",
1367
1334
  ).length;
@@ -1428,32 +1395,10 @@ export default function DreamsRoute() {
1428
1395
  description="Review agent runs, propose memory improvements, and apply evidence-backed learning changes."
1429
1396
  >
1430
1397
  <div className="space-y-4">
1431
- <div className="flex flex-col gap-3">
1432
- <div className="grid grid-cols-2 gap-2 md:grid-cols-4">
1433
- <StatTile
1434
- label="Dream passes"
1435
- value={dreams.length}
1436
- icon={IconBrain}
1437
- />
1438
- <StatTile
1439
- label="Pending proposals"
1440
- value={pendingProposalCount}
1441
- icon={IconCircleDashed}
1442
- />
1443
- <StatTile
1444
- label="Candidate runs"
1445
- value={candidates.length}
1446
- icon={IconClock}
1447
- />
1448
- <StatTile
1449
- label="Inspected threads"
1450
- value={selectedDream ? dreamInspectedCount(selectedDream) : 0}
1451
- icon={IconCheck}
1452
- />
1453
- </div>
1454
- <div className="flex flex-wrap items-center gap-2">
1398
+ <div className="flex flex-wrap items-center justify-between gap-3 border-b pb-3">
1399
+ <div className="flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1">
1455
1400
  {dreamSettings ? (
1456
- <Badge variant="outline" className="h-9 max-w-full truncate px-3">
1401
+ <Badge variant="outline" className="max-w-full truncate">
1457
1402
  {dreamSettings.enabled ? "Enabled" : "Paused"} ·{" "}
1458
1403
  {dreamSettings.allSources
1459
1404
  ? "All sources"
@@ -1461,6 +1406,11 @@ export default function DreamsRoute() {
1461
1406
  · {dreamSettings.schedule}
1462
1407
  </Badge>
1463
1408
  ) : null}
1409
+ <span className="text-xs text-muted-foreground">
1410
+ {dreams.length} passes · {candidates.length} candidate runs
1411
+ </span>
1412
+ </div>
1413
+ <div className="flex flex-wrap items-center gap-2">
1464
1414
  <DreamSettingsSheet
1465
1415
  open={settingsOpen}
1466
1416
  onOpenChange={handleSettingsOpenChange}
@@ -1519,8 +1469,8 @@ export default function DreamsRoute() {
1519
1469
  </div>
1520
1470
  </div>
1521
1471
 
1522
- <div className="grid gap-4 lg:grid-cols-2 xl:grid-cols-[280px_minmax(0,1fr)_380px]">
1523
- <section className="rounded-lg bg-card">
1472
+ <div className="grid gap-4 lg:grid-cols-[280px_minmax(0,1fr)]">
1473
+ <section className="min-w-0 rounded-lg bg-card">
1524
1474
  <div className="border-b px-4 py-3">
1525
1475
  <div className="text-sm font-semibold text-foreground">
1526
1476
  Recent passes
@@ -1770,92 +1720,92 @@ export default function DreamsRoute() {
1770
1720
  ) : null}
1771
1721
  </div>
1772
1722
  </section>
1723
+ </div>
1773
1724
 
1774
- <section className="rounded-lg bg-card">
1775
- <div className="border-b px-4 py-3">
1776
- <div>
1777
- <div className="text-sm font-semibold text-foreground">
1778
- Candidate runs
1779
- </div>
1780
- <div className="mt-1 text-xs text-muted-foreground">
1781
- Grounded signals ready for review.
1782
- </div>
1725
+ <section className="min-w-0 rounded-lg bg-card">
1726
+ <div className="border-b px-4 py-3">
1727
+ <div>
1728
+ <div className="text-sm font-semibold text-foreground">
1729
+ Candidate runs
1730
+ </div>
1731
+ <div className="mt-1 text-xs text-muted-foreground">
1732
+ Grounded signals ready for review.
1783
1733
  </div>
1784
1734
  </div>
1785
- <div className="max-h-[720px] overflow-auto p-3">
1786
- <QueryState
1787
- error={candidatesQuery.error}
1788
- label="Could not load candidates"
1789
- />
1790
- {candidatesQuery.isLoading ? <DreamListSkeleton /> : null}
1791
- {!candidatesQuery.isLoading &&
1792
- !candidatesQuery.error &&
1793
- candidateSourceHealth.length > 0 ? (
1794
- <div className="mb-3">
1795
- <SourceHealthPanel sources={candidateSourceHealth} />
1796
- </div>
1797
- ) : null}
1798
- {!candidatesQuery.isLoading && !candidatesQuery.error ? (
1799
- candidates.length > 0 ? (
1800
- <Table>
1801
- <TableHeader>
1802
- <TableRow>
1803
- <TableHead>Run</TableHead>
1804
- <TableHead>Signals</TableHead>
1805
- <TableHead className="w-20 text-right">Score</TableHead>
1806
- </TableRow>
1807
- </TableHeader>
1808
- <TableBody>
1809
- {candidates.map((candidate) => {
1810
- const id = candidateId(candidate);
1811
- const signals = candidateSignals(candidate);
1812
- return (
1813
- <TableRow key={id}>
1814
- <TableCell className="min-w-0 py-3">
1815
- <div className="max-w-[230px] truncate text-sm font-medium text-foreground">
1816
- {candidateLabel(candidate)}
1817
- </div>
1818
- <div className="mt-1 truncate font-mono text-[11px] text-muted-foreground">
1819
- {candidate.thread?.id ??
1820
- candidate.threadId ??
1821
- candidate.runId ??
1822
- id}
1823
- </div>
1824
- <div className="mt-1 text-[11px] text-muted-foreground">
1825
- {candidateOwner(candidate)} ·{" "}
1826
- {compactDate(candidateUpdatedAt(candidate))}
1827
- </div>
1828
- </TableCell>
1829
- <TableCell className="py-3">
1830
- <div className="mt-1 flex flex-wrap gap-1">
1831
- <Badge variant="outline">
1832
- {candidateStatus(candidate)}
1735
+ </div>
1736
+ <div className="max-h-[720px] overflow-auto p-3">
1737
+ <QueryState
1738
+ error={candidatesQuery.error}
1739
+ label="Could not load candidates"
1740
+ />
1741
+ {candidatesQuery.isLoading ? <DreamListSkeleton /> : null}
1742
+ {!candidatesQuery.isLoading &&
1743
+ !candidatesQuery.error &&
1744
+ candidateSourceHealth.length > 0 ? (
1745
+ <div className="mb-3">
1746
+ <SourceHealthPanel sources={candidateSourceHealth} />
1747
+ </div>
1748
+ ) : null}
1749
+ {!candidatesQuery.isLoading && !candidatesQuery.error ? (
1750
+ candidates.length > 0 ? (
1751
+ <Table>
1752
+ <TableHeader>
1753
+ <TableRow>
1754
+ <TableHead>Run</TableHead>
1755
+ <TableHead>Signals</TableHead>
1756
+ <TableHead className="w-20 text-right">Score</TableHead>
1757
+ </TableRow>
1758
+ </TableHeader>
1759
+ <TableBody>
1760
+ {candidates.map((candidate) => {
1761
+ const id = candidateId(candidate);
1762
+ const signals = candidateSignals(candidate);
1763
+ return (
1764
+ <TableRow key={id}>
1765
+ <TableCell className="min-w-0 py-3">
1766
+ <div className="max-w-[230px] truncate text-sm font-medium text-foreground">
1767
+ {candidateLabel(candidate)}
1768
+ </div>
1769
+ <div className="mt-1 truncate font-mono text-[11px] text-muted-foreground">
1770
+ {candidate.thread?.id ??
1771
+ candidate.threadId ??
1772
+ candidate.runId ??
1773
+ id}
1774
+ </div>
1775
+ <div className="mt-1 text-[11px] text-muted-foreground">
1776
+ {candidateOwner(candidate)} ·{" "}
1777
+ {compactDate(candidateUpdatedAt(candidate))}
1778
+ </div>
1779
+ </TableCell>
1780
+ <TableCell className="py-3">
1781
+ <div className="mt-1 flex flex-wrap gap-1">
1782
+ <Badge variant="outline">
1783
+ {candidateStatus(candidate)}
1784
+ </Badge>
1785
+ {signals.slice(0, 2).map((signal) => (
1786
+ <Badge key={signal} variant="secondary">
1787
+ {signal}
1833
1788
  </Badge>
1834
- {signals.slice(0, 2).map((signal) => (
1835
- <Badge key={signal} variant="secondary">
1836
- {signal}
1837
- </Badge>
1838
- ))}
1839
- </div>
1840
- </TableCell>
1841
- <TableCell className="py-3 text-right text-sm tabular-nums text-muted-foreground">
1842
- {candidate.score ?? "n/a"}
1843
- </TableCell>
1844
- </TableRow>
1845
- );
1846
- })}
1847
- </TableBody>
1848
- </Table>
1849
- ) : (
1850
- <EmptyPanel
1851
- title="No candidates"
1852
- description="No recent runs matched the dream candidate heuristics."
1853
- />
1854
- )
1855
- ) : null}
1856
- </div>
1857
- </section>
1858
- </div>
1789
+ ))}
1790
+ </div>
1791
+ </TableCell>
1792
+ <TableCell className="py-3 text-right text-sm tabular-nums text-muted-foreground">
1793
+ {candidate.score ?? "n/a"}
1794
+ </TableCell>
1795
+ </TableRow>
1796
+ );
1797
+ })}
1798
+ </TableBody>
1799
+ </Table>
1800
+ ) : (
1801
+ <EmptyPanel
1802
+ title="No candidates"
1803
+ description="No recent runs matched the dream candidate heuristics."
1804
+ />
1805
+ )
1806
+ ) : null}
1807
+ </div>
1808
+ </section>
1859
1809
  </div>
1860
1810
  </DispatchShell>
1861
1811
  );