@post-print/agent-test 2.0.11 → 2.1.0

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 (37) hide show
  1. package/dist/sdk/cli.d.ts +10 -0
  2. package/dist/sdk/cli.d.ts.map +1 -1
  3. package/dist/sdk/cli.js +27 -2
  4. package/dist/sdk/cli.js.map +1 -1
  5. package/dist/sdk/stored-value.d.ts.map +1 -1
  6. package/dist/sdk/stored-value.js +11 -0
  7. package/dist/sdk/stored-value.js.map +1 -1
  8. package/dist/sdk/workspace.d.ts.map +1 -1
  9. package/dist/sdk/workspace.js +14 -1
  10. package/dist/sdk/workspace.js.map +1 -1
  11. package/dist/viewer/client-entry.js +21 -20
  12. package/dist/viewer/main.js +16 -4
  13. package/dist/viewer/main.js.map +1 -1
  14. package/dist/viewer/presentation.d.ts +20 -2
  15. package/dist/viewer/presentation.d.ts.map +1 -1
  16. package/dist/viewer/presentation.js +64 -2
  17. package/dist/viewer/presentation.js.map +1 -1
  18. package/dist/viewer/run-inbox.d.ts +7 -0
  19. package/dist/viewer/run-inbox.d.ts.map +1 -0
  20. package/dist/viewer/run-inbox.js +335 -0
  21. package/dist/viewer/run-inbox.js.map +1 -0
  22. package/dist/viewer/styles.d.ts.map +1 -1
  23. package/dist/viewer/styles.js +5 -0
  24. package/dist/viewer/styles.js.map +1 -1
  25. package/dist/viewer/stylex.css +230 -2
  26. package/dist/viewer/tool-images.d.ts +13 -0
  27. package/dist/viewer/tool-images.d.ts.map +1 -0
  28. package/dist/viewer/tool-images.js +77 -0
  29. package/dist/viewer/tool-images.js.map +1 -0
  30. package/dist/viewer/v2-client.d.ts.map +1 -1
  31. package/dist/viewer/v2-client.js +300 -172
  32. package/dist/viewer/v2-client.js.map +1 -1
  33. package/dist/viewer/viewer-queries.d.ts +26 -0
  34. package/dist/viewer/viewer-queries.d.ts.map +1 -0
  35. package/dist/viewer/viewer-queries.js +31 -0
  36. package/dist/viewer/viewer-queries.js.map +1 -0
  37. package/package.json +2 -2
@@ -4,9 +4,11 @@ import { useMutation, useQueries, useQuery, useQueryClient } from "@tanstack/rea
4
4
  import { createRootRoute, createRoute, createRouter, Link, Outlet, useNavigate, } from "@tanstack/react-router";
5
5
  import { createContext, useCallback, useContext, useEffect, useId, useRef, useState, } from "react";
6
6
  import Markdown from "react-markdown";
7
- import { viewerApi } from "./generated-api.js";
8
- import { assertionComparison, conversationPlaceholder, preferredExecutionId, stripAnsi, } from "./presentation.js";
9
- const SENTENCE_BOUNDARY = /(?<=[.!?])\s+/;
7
+ import { assertionComparison, conversationPlaceholder, formatRunDate, median, preferredTestExecutionId, stripAnsi, testRunStatus, unwrapShellCommand, } from "./presentation.js";
8
+ import { RunInboxPage, RunPage } from "./run-inbox.js";
9
+ import { ToolImages, toolImages } from "./tool-images.js";
10
+ import { cancelExecution, fetchExecution as fetchExecutionRecord, startSuite, startTest, startTestGroup, useCatalog, useExecutionHistory, } from "./viewer-queries.js";
11
+ const fetchExecution = (id) => fetchExecutionRecord(id);
10
12
  const LOCAL_PATH_LINK = /\[([^\]]+)\]\(<local-path>[^)]*\)/g;
11
13
  const runningPulse = stylex.keyframes({
12
14
  "0%, 60%, 100%": { opacity: 0.3, transform: "translateY(0)" },
@@ -20,7 +22,7 @@ const rootRoute = createRootRoute({ component: ViewerLayout });
20
22
  const indexRoute = createRoute({
21
23
  getParentRoute: () => rootRoute,
22
24
  path: "/",
23
- component: TestCatalogPage,
25
+ component: RunInboxPage,
24
26
  });
25
27
  const testRoute = createRoute({
26
28
  getParentRoute: () => rootRoute,
@@ -36,7 +38,7 @@ const testRoute = createRoute({
36
38
  const executionRoute = createRoute({
37
39
  getParentRoute: () => rootRoute,
38
40
  path: "/executions/$executionId",
39
- component: ExecutionPage,
41
+ component: ExecutionRunPage,
40
42
  });
41
43
  const compareRoute = createRoute({
42
44
  getParentRoute: () => rootRoute,
@@ -374,6 +376,14 @@ const styles = stylex.create({
374
376
  border: "1px solid transparent",
375
377
  backgroundColor: { default: "transparent", ":hover": "var(--panel)" },
376
378
  },
379
+ batchRunLink: {
380
+ justifySelf: "start",
381
+ marginTop: "0.75rem",
382
+ color: { default: "var(--muted)", ":hover": "var(--text)" },
383
+ fontSize: "0.78rem",
384
+ textDecoration: "none",
385
+ },
386
+ runsLink: { display: "flex", alignItems: "center", gap: "0.5rem", fontWeight: 650 },
377
387
  navItemHeader: {
378
388
  display: "grid",
379
389
  gridTemplateColumns: "0.65rem minmax(0, 1fr)",
@@ -590,7 +600,7 @@ const styles = stylex.create({
590
600
  },
591
601
  quietHeader: {
592
602
  display: "flex",
593
- justifyContent: "flex-end",
603
+ justifyContent: "flex-start",
594
604
  marginBottom: "0.75rem",
595
605
  },
596
606
  statusPanel: {
@@ -622,6 +632,37 @@ const styles = stylex.create({
622
632
  gap: "0.7rem",
623
633
  },
624
634
  attemptTitle: { fontSize: "0.84rem", lineHeight: 1.4 },
635
+ verdictPanel: {
636
+ display: "grid",
637
+ gap: "0.55rem",
638
+ padding: "0.7rem 0.8rem",
639
+ borderRadius: 10,
640
+ borderWidth: 1,
641
+ borderStyle: "solid",
642
+ borderColor: "var(--border)",
643
+ backgroundColor: "var(--panel-2)",
644
+ },
645
+ verdictPass: { borderColor: "oklch(0.79 0.16 155 / 0.45)", backgroundColor: "var(--pass-soft)" },
646
+ verdictFail: { borderColor: "oklch(0.73 0.18 25 / 0.5)", backgroundColor: "var(--fail-soft)" },
647
+ verdictHeadline: { margin: 0, fontSize: "0.92rem", fontWeight: 720, color: "var(--text)" },
648
+ verdictMetrics: {
649
+ display: "grid",
650
+ gridTemplateColumns: "repeat(auto-fit, minmax(7.5rem, 1fr))",
651
+ gap: "0.4rem 1rem",
652
+ margin: 0,
653
+ },
654
+ verdictMetric: { display: "grid", gap: "0.1rem", minWidth: 0 },
655
+ verdictMetricValue: {
656
+ display: "grid",
657
+ margin: 0,
658
+ color: "var(--text)",
659
+ fontSize: "0.86rem",
660
+ fontWeight: 650,
661
+ fontVariantNumeric: "tabular-nums",
662
+ },
663
+ verdictMetricMedian: { color: "var(--subtle)", fontSize: "0.68rem", fontWeight: 500 },
664
+ verdictPassedChecks: { display: "grid", gap: "0.45rem" },
665
+ verdictCount: { color: "var(--muted)", fontWeight: 560 },
625
666
  resultPanel: {
626
667
  display: "grid",
627
668
  gap: "0.55rem",
@@ -712,9 +753,52 @@ const styles = stylex.create({
712
753
  },
713
754
  conversationHeader: {
714
755
  display: "flex",
715
- alignItems: "center",
756
+ flexWrap: "wrap",
757
+ alignItems: "baseline",
716
758
  justifyContent: "flex-start",
717
- gap: "0.75rem",
759
+ gap: "0.25rem 0.75rem",
760
+ },
761
+ sectionIntro: { margin: 0, color: "var(--subtle)", fontSize: "0.72rem", lineHeight: 1.4 },
762
+ ranRow: { display: "flex", flexWrap: "wrap", alignItems: "center", gap: "0.35rem" },
763
+ ranLabel: {
764
+ color: "var(--subtle)",
765
+ fontSize: "0.66rem",
766
+ fontWeight: 700,
767
+ letterSpacing: "0.06em",
768
+ textTransform: "uppercase",
769
+ },
770
+ ranChip: {
771
+ padding: "0.12rem 0.45rem",
772
+ borderRadius: 999,
773
+ backgroundColor: "var(--panel-3)",
774
+ color: "var(--muted)",
775
+ fontSize: "0.72rem",
776
+ },
777
+ judgeOutcomeSource: {
778
+ gridColumn: 2,
779
+ margin: 0,
780
+ color: "var(--subtle)",
781
+ fontSize: "0.68rem",
782
+ lineHeight: 1.4,
783
+ },
784
+ judgeReason: { margin: 0, color: "var(--muted)", fontSize: "0.74rem", lineHeight: 1.5 },
785
+ judgeAskedLabel: {
786
+ marginRight: "0.35rem",
787
+ color: "var(--subtle)",
788
+ fontSize: "0.61rem",
789
+ fontWeight: 750,
790
+ letterSpacing: "0.06em",
791
+ textTransform: "uppercase",
792
+ },
793
+ judgeContextDisclosure: {
794
+ borderTop: "1px solid var(--border)",
795
+ paddingTop: "0.45rem",
796
+ },
797
+ judgeContextSummary: {
798
+ cursor: "pointer",
799
+ color: { default: "var(--muted)", ":hover": "var(--text)" },
800
+ fontSize: "0.74rem",
801
+ fontWeight: 650,
718
802
  },
719
803
  runTabs: { display: "inline-flex", alignItems: "center", gap: "0.12rem" },
720
804
  runTab: {
@@ -846,15 +930,11 @@ const styles = stylex.create({
846
930
  display: "grid",
847
931
  justifySelf: "stretch",
848
932
  gap: "0.45rem",
849
- padding: "0.7rem",
850
- border: "1px solid oklch(0.75 0.12 175 / 0.3)",
851
- borderRadius: 10,
852
- backgroundColor: "var(--accent-soft)",
853
933
  },
854
934
  judgeQuestionText: {
855
935
  margin: 0,
856
- color: "var(--text)",
857
- fontSize: "0.79rem",
936
+ color: "var(--muted)",
937
+ fontSize: "0.76rem",
858
938
  lineHeight: 1.5,
859
939
  },
860
940
  judgeContext: {
@@ -863,14 +943,6 @@ const styles = stylex.create({
863
943
  paddingTop: "0.55rem",
864
944
  borderTop: "1px solid oklch(0.75 0.12 175 / 0.22)",
865
945
  },
866
- judgeContextHeader: {
867
- display: "flex",
868
- alignItems: "baseline",
869
- justifyContent: "space-between",
870
- gap: "0.75rem",
871
- color: "var(--text)",
872
- fontSize: "0.7rem",
873
- },
874
946
  judgeContextNote: { margin: 0, color: "var(--muted)", fontSize: "0.68rem", lineHeight: 1.4 },
875
947
  judgeContextGroup: { display: "grid", gap: "0.35rem" },
876
948
  judgeContextGroupHeading: {
@@ -944,13 +1016,12 @@ const styles = stylex.create({
944
1016
  },
945
1017
  judgeResponse: {
946
1018
  display: "grid",
947
- justifySelf: "start",
1019
+ justifySelf: "stretch",
948
1020
  gap: "0.4rem",
949
1021
  padding: "0.65rem 0.7rem",
950
1022
  border: "1px solid var(--border)",
951
1023
  borderRadius: 10,
952
1024
  backgroundColor: "var(--panel-2)",
953
- maxWidth: "78%",
954
1025
  },
955
1026
  judgeResponseHeading: {
956
1027
  margin: 0,
@@ -1044,7 +1115,7 @@ const styles = stylex.create({
1044
1115
  },
1045
1116
  criteriaList: { display: "grid", gap: "0.55rem", margin: 0, padding: 0, listStyle: "none" },
1046
1117
  criterion: { display: "flex", gap: "0.6rem", color: "var(--muted)", fontSize: "0.82rem" },
1047
- criterionIcon: { color: "var(--accent)", fontWeight: 800 },
1118
+ criterionIcon: { color: "var(--subtle)", fontWeight: 800 },
1048
1119
  passReasonList: { display: "grid", gap: "0.35rem", margin: 0, padding: 0, listStyle: "none" },
1049
1120
  passReason: { display: "flex", gap: "0.5rem", color: "var(--text)", fontSize: "0.76rem" },
1050
1121
  passReasonIcon: { color: "var(--pass)", fontWeight: 800 },
@@ -1170,9 +1241,6 @@ function useNarrowViewer() {
1170
1241
  }, []);
1171
1242
  return narrow;
1172
1243
  }
1173
- function useCatalog() {
1174
- return useQuery({ queryKey: ["test-catalog"], queryFn: fetchTestCatalog });
1175
- }
1176
1244
  function MobileViewerHeader({ buttonRef, open, onOpen, }) {
1177
1245
  const catalog = useCatalog();
1178
1246
  return (_jsxs("header", { ...stylex.props(styles.mobileHeader), children: [_jsx("button", { ref: buttonRef, type: "button", "aria-haspopup": "dialog", "aria-controls": "mobile-test-drawer", "aria-expanded": open, "aria-label": "Open test menu", ...stylex.props(styles.buttonReset, styles.mobileMenuButton), onClick: onOpen, children: _jsx("span", { "aria-hidden": "true", children: "\u2630" }) }), _jsxs("div", { ...stylex.props(styles.mobileHeaderIdentity), children: [_jsx("span", { ...stylex.props(styles.mobileHeaderTitle), children: "Tests" }), _jsx("span", { ...stylex.props(styles.countBadge), children: catalog.data?.tests.length ?? 0 })] })] }));
@@ -1227,7 +1295,7 @@ function SidebarHeader({ mode, open, count, headingId, navigationId, onToggle, o
1227
1295
  onNavigate?.();
1228
1296
  },
1229
1297
  });
1230
- return (_jsx("header", { ...stylex.props(styles.sidebarHeader, !open && styles.sidebarHeaderCollapsed), children: open ? (_jsxs(_Fragment, { children: [_jsxs("div", { ...stylex.props(styles.sidebarTitleRow), children: [_jsxs("div", { ...stylex.props(styles.sidebarTitleIdentity), children: [_jsx("h1", { id: headingId, ...stylex.props(styles.title, styles.sidebarTitle), children: "Tests" }), _jsx("span", { ...stylex.props(styles.countBadge), children: count })] }), _jsx(SidebarToggle, { mode: mode, open: open, navigationId: navigationId, onToggle: onToggle })] }), _jsxs("div", { ...stylex.props(styles.suiteControls), children: [_jsxs("button", { type: "button", ...stylex.props(styles.buttonReset, styles.suiteRunButton), onClick: () => run.mutate(), disabled: run.isPending || count === 0, children: [_jsx("span", { "aria-hidden": "true", children: run.isPending ? "●" : "▶" }), run.isPending ? "Starting suite…" : "Run all tests"] }), _jsx(WorkerControl, {})] }), run.isError ? _jsx("p", { role: "alert", children: run.error.message }) : null] })) : (_jsx(SidebarToggle, { mode: mode, open: open, navigationId: navigationId, onToggle: onToggle })) }));
1298
+ return (_jsx("header", { ...stylex.props(styles.sidebarHeader, !open && styles.sidebarHeaderCollapsed), children: open ? (_jsxs(_Fragment, { children: [_jsxs("div", { ...stylex.props(styles.sidebarTitleRow), children: [_jsxs("div", { ...stylex.props(styles.sidebarTitleIdentity), children: [_jsx("h1", { id: headingId, ...stylex.props(styles.title, styles.sidebarTitle), children: "Tests" }), _jsx("span", { ...stylex.props(styles.countBadge), children: count })] }), _jsx(SidebarToggle, { mode: mode, open: open, navigationId: navigationId, onToggle: onToggle })] }), _jsxs(Link, { to: "/", activeOptions: { exact: true }, ...stylex.props(styles.navLink, styles.runsLink), activeProps: stylex.props(styles.navLink, styles.runsLink, styles.active), onClick: onNavigate, children: [_jsx("span", { "aria-hidden": "true", children: "\u25F7" }), " Runs"] }), _jsxs("div", { ...stylex.props(styles.suiteControls), children: [_jsxs("button", { type: "button", ...stylex.props(styles.buttonReset, styles.suiteRunButton), onClick: () => run.mutate(), disabled: run.isPending || count === 0, children: [_jsx("span", { "aria-hidden": "true", children: run.isPending ? "●" : "▶" }), run.isPending ? "Starting suite…" : "Run all tests"] }), _jsx(WorkerControl, {})] }), run.isError ? _jsx("p", { role: "alert", children: run.error.message }) : null] })) : (_jsx(SidebarToggle, { mode: mode, open: open, navigationId: navigationId, onToggle: onToggle })) }));
1231
1299
  }
1232
1300
  function WorkerControl() {
1233
1301
  const { workers, setWorkers } = useContext(WorkerContext);
@@ -1264,9 +1332,6 @@ function TestLink({ test, status, onNavigate, }) {
1264
1332
  function TestStatus({ status }) {
1265
1333
  return (_jsx("span", { role: "img", "aria-label": `Latest execution: ${status}`, title: `Latest execution: ${status}`, ...stylex.props(styles.navStatus), children: _jsx("span", { "aria-hidden": "true", ...stylex.props(styles.navStatusDot, status === "passed" && styles.navStatusDotPass, status === "failed" && styles.navStatusDotFail, status === "running" && styles.navStatusDotRunning) }) }));
1266
1334
  }
1267
- function TestCatalogPage() {
1268
- return (_jsxs("section", { ...stylex.props(styles.hero), children: [_jsx("div", { ...stylex.props(styles.brand), children: "Test explorer" }), _jsx("h2", { ...stylex.props(styles.title), children: "Choose a test from the sidebar" }), _jsx("p", { ...stylex.props(styles.description), children: "Read what it checks, see its agents and judges, then start a run or inspect a saved execution." })] }));
1269
- }
1270
1335
  function TestPage() {
1271
1336
  const { testId } = testRoute.useParams();
1272
1337
  const catalog = useCatalog();
@@ -1282,7 +1347,7 @@ function TestDetail({ test }) {
1282
1347
  const history = useExecutionHistory();
1283
1348
  const navigate = useNavigate();
1284
1349
  const runs = history.data?.filter((item) => item.testIds?.includes(test.id)) ?? [];
1285
- const selected = execution ?? preferredExecutionId(runs);
1350
+ const selected = execution ?? preferredTestExecutionId(runs, test.id);
1286
1351
  const activeView = view ?? "current";
1287
1352
  const chooseExecution = (id) => void navigate({
1288
1353
  to: "/tests/$testId",
@@ -1294,7 +1359,14 @@ function TestDetail({ test }) {
1294
1359
  params: { testId: test.id },
1295
1360
  search: { execution, view: next === "current" ? undefined : next },
1296
1361
  });
1297
- return (_jsxs(_Fragment, { children: [_jsx(TestHeader, { test: test, onStarted: chooseExecution }), _jsx(TestTabs, { active: activeView, onChange: chooseView }), _jsx(TestViewPanel, { view: activeView, test: test, runs: runs, selected: selected, onChooseExecution: chooseExecution })] }));
1362
+ return (_jsxs(_Fragment, { children: [_jsx(TestHeader, { test: test, onStarted: chooseExecution }), _jsx(BatchRunLink, { run: runs.find((run) => run.id === selected) }), _jsx(TestTabs, { active: activeView, onChange: chooseView }), _jsx(TestViewPanel, { view: activeView, test: test, runs: runs, selected: selected, historyLoading: history.isPending, onChooseExecution: chooseExecution })] }));
1363
+ }
1364
+ /** Return path to the run inbox when this test was opened from a batch run. */
1365
+ function BatchRunLink({ run }) {
1366
+ const count = run?.testIds?.length ?? 0;
1367
+ if (!run || count < 2)
1368
+ return null;
1369
+ return (_jsxs(Link, { to: "/executions/$executionId", params: { executionId: run.id }, ...stylex.props(styles.batchRunLink), children: ["\u2190 Back to run \u00B7 ", formatRunDate(run.startedAt), " \u00B7 ", count, " tests"] }));
1298
1370
  }
1299
1371
  function TestHeader({ test, onStarted }) {
1300
1372
  return (_jsxs("header", { ...stylex.props(styles.hero), children: [_jsxs("div", { ...stylex.props(styles.heroTop), children: [_jsxs("div", { ...stylex.props(styles.heroCopy), children: [_jsx("div", { ...stylex.props(styles.brand), children: test.title.slice(0, -1).join(" / ") }), _jsx("h2", { ...stylex.props(styles.title), children: test.title.at(-1) }), _jsx("p", { ...stylex.props(styles.description), children: test.description ?? "This test has no additional description." })] }), _jsx(RunTestButton, { test: test, onStarted: onStarted })] }), _jsxs("div", { ...stylex.props(styles.path), children: [test.project, " \u00B7 ", test.file] })] }));
@@ -1324,16 +1396,21 @@ function handleTabKeyDown(event, active, onChange) {
1324
1396
  document.getElementById(`test-tab-${next.id}`)?.focus();
1325
1397
  }
1326
1398
  function TestViewPanel(props) {
1327
- return (_jsxs("div", { id: `test-panel-${props.view}`, role: "tabpanel", "aria-labelledby": `test-tab-${props.view}`, ...stylex.props(styles.tabPanel), children: [props.view === "setup" ? _jsx(TestResources, { test: props.test }) : null, props.view === "current" ? (_jsx(CurrentRun, { test: props.test, executionId: props.selected })) : null, props.view === "history" ? (_jsx(RunHistory, { runs: props.runs, onChoose: props.onChooseExecution })) : null] }));
1399
+ return (_jsxs("div", { id: `test-panel-${props.view}`, role: "tabpanel", "aria-labelledby": `test-tab-${props.view}`, ...stylex.props(styles.tabPanel), children: [props.view === "setup" ? _jsx(TestResources, { test: props.test }) : null, props.view === "current" ? (_jsx(CurrentRun, { test: props.test, executionId: props.selected, loading: props.historyLoading })) : null, props.view === "history" ? (_jsx(RunHistory, { runs: props.runs, testId: props.test.id, onChoose: props.onChooseExecution })) : null] }));
1400
+ }
1401
+ function CurrentRun({ test, executionId, loading, }) {
1402
+ return (_jsx("section", { ...stylex.props(styles.tabSection), children: loading && !executionId ? (_jsx("p", { ...stylex.props(styles.empty), children: "Loading runs\u2026" })) : executionId ? (_jsx(TestExecution, { id: executionId, test: test })) : (_jsx("p", { ...stylex.props(styles.empty), children: "No runs yet. Start this test to see its progress here." })) }));
1328
1403
  }
1329
- function CurrentRun({ test, executionId }) {
1330
- return (_jsx("section", { ...stylex.props(styles.tabSection), children: executionId ? (_jsx(TestExecution, { id: executionId, test: test })) : (_jsx("p", { ...stylex.props(styles.empty), children: "No runs yet. Start this test to see its progress here." })) }));
1404
+ function RunHistory({ runs, testId, onChoose, }) {
1405
+ return (_jsxs("section", { ...stylex.props(styles.tabSection), children: [_jsx("div", { ...stylex.props(styles.quietHeader), children: _jsxs("span", { ...stylex.props(styles.sectionIntro), children: [runs.length, " ", pluralize(runs.length, "run"), " included this test. Each shows this test's own result."] }) }), runs.length ? (_jsx("div", { ...stylex.props(styles.historyList), children: runs.map((run) => (_jsx(HistoryItem, { run: run, testId: testId, onChoose: onChoose }, run.id))) })) : (_jsx("p", { ...stylex.props(styles.empty), children: "No saved runs for this test." }))] }));
1331
1406
  }
1332
- function RunHistory({ runs, onChoose, }) {
1333
- return (_jsxs("section", { ...stylex.props(styles.tabSection), children: [_jsx("div", { ...stylex.props(styles.quietHeader), children: _jsxs("span", { ...stylex.props(styles.resourceCount), children: [runs.length, " executions"] }) }), runs.length ? (_jsx("div", { ...stylex.props(styles.historyList), children: runs.map((run) => (_jsx(HistoryItem, { run: run, onChoose: onChoose }, run.id))) })) : (_jsx("p", { ...stylex.props(styles.empty), children: "No saved runs for this test." }))] }));
1407
+ function HistoryItem({ run, testId, onChoose, }) {
1408
+ const status = testRunStatus(run, testId);
1409
+ const others = (run.testIds?.length ?? 1) - 1;
1410
+ return (_jsxs("button", { type: "button", "aria-label": `Open ${status} execution ${run.id.slice(0, 8)}`, ...stylex.props(styles.buttonReset, styles.historyButton), onClick: () => onChoose(run.id), children: [_jsxs("span", { ...stylex.props(styles.historyIdentity), children: [_jsx("span", { ...stylex.props(styles.historyDate), children: formatRunDate(run.startedAt) }), _jsxs("span", { ...stylex.props(styles.historyId), children: [run.id.slice(0, 8), others > 0 ? ` · with ${others} other ${pluralize(others, "test")}` : ""] })] }), _jsx(StatusPill, { status: status })] }));
1334
1411
  }
1335
- function HistoryItem({ run, onChoose }) {
1336
- return (_jsxs("button", { type: "button", "aria-label": `Open ${run.status} execution ${run.id.slice(0, 8)}`, ...stylex.props(styles.buttonReset, styles.historyButton), onClick: () => onChoose(run.id), children: [_jsxs("span", { ...stylex.props(styles.historyIdentity), children: [_jsx("span", { ...stylex.props(styles.historyDate), children: new Date(run.startedAt).toLocaleString() }), _jsx("span", { ...stylex.props(styles.historyId), children: run.id.slice(0, 8) })] }), _jsx("span", { ...stylex.props(styles.status, run.status === "passed" && styles.statusPass, run.status === "failed" && styles.statusFail, run.status === "running" && styles.statusRunning), children: run.status })] }));
1412
+ function StatusPill({ status }) {
1413
+ return (_jsx("span", { ...stylex.props(styles.status, status === "passed" && styles.statusPass, status === "failed" && styles.statusFail, (status === "running" || status === "queued") && styles.statusRunning), children: status }));
1337
1414
  }
1338
1415
  function RunTestButton({ test, onStarted }) {
1339
1416
  const { workers } = useContext(WorkerContext);
@@ -1355,7 +1432,7 @@ function TestResources({ test }) {
1355
1432
  }
1356
1433
  function CriteriaSetup({ test }) {
1357
1434
  const criteria = testCriteria(test);
1358
- return (_jsxs("section", { "aria-labelledby": "setup-criteria", ...stylex.props(styles.criteriaPanel), children: [_jsx("h3", { id: "setup-criteria", ...stylex.props(styles.contentHeading), children: "Pass criteria" }), criteria.length ? (_jsx("ul", { ...stylex.props(styles.criteriaList), children: criteria.map((criterion) => (_jsxs("li", { ...stylex.props(styles.criterion), children: [_jsx("span", { "aria-hidden": "true", ...stylex.props(styles.criterionIcon), children: "\u2713" }), _jsx("span", { children: criterion })] }, criterion))) })) : (_jsx("p", { ...stylex.props(styles.meta), children: "No explicit pass criteria are declared." }))] }));
1435
+ return (_jsxs("section", { "aria-labelledby": "setup-criteria", ...stylex.props(styles.criteriaPanel), children: [_jsx("h3", { id: "setup-criteria", ...stylex.props(styles.contentHeading), children: "Pass criteria" }), _jsx("p", { ...stylex.props(styles.sectionIntro), children: "Every one of these must hold for the test to pass." }), criteria.length ? (_jsx("ul", { ...stylex.props(styles.criteriaList), children: criteria.map((criterion) => (_jsxs("li", { ...stylex.props(styles.criterion), children: [_jsx("span", { "aria-hidden": "true", ...stylex.props(styles.criterionIcon), children: "\u2022" }), _jsx("span", { children: criterion })] }, criterion))) })) : (_jsx("p", { ...stylex.props(styles.meta), children: "No explicit pass criteria are declared." }))] }));
1359
1436
  }
1360
1437
  function ResourceSection({ title, description, resources, }) {
1361
1438
  const id = `setup-${title.toLowerCase()}`;
@@ -1366,7 +1443,7 @@ function ResourceCard({ resource }) {
1366
1443
  return (_jsxs("article", { ...stylex.props(styles.card, styles.resourceCard), children: [_jsx("div", { ...stylex.props(styles.resourceHeader), children: _jsx("strong", { children: resource.name }) }), _jsx(ResourceContext, { resource: resource })] }));
1367
1444
  }
1368
1445
  function WorkspaceSetup({ test }) {
1369
- return (_jsxs("section", { "aria-labelledby": "setup-workspace", ...stylex.props(styles.setupSection), children: [_jsxs("div", { ...stylex.props(styles.setupSectionCopy), children: [_jsx("h3", { id: "setup-workspace", ...stylex.props(styles.contentHeading), children: "Workspace" }), _jsx("p", { ...stylex.props(styles.meta), children: "Starting files and execution target for this test." })] }), _jsxs("div", { ...stylex.props(styles.workspaceRow), children: [_jsx(ResourceFact, { label: "Source folder", value: test.workspace ?? "Project directory" }), _jsx(ResourceFact, { label: "Project", value: test.project }), _jsx(ResourceFact, { label: "Test file", value: test.file })] })] }));
1446
+ return (_jsxs("section", { "aria-labelledby": "setup-workspace", ...stylex.props(styles.setupSection), children: [_jsxs("div", { ...stylex.props(styles.setupSectionCopy), children: [_jsx("h3", { id: "setup-workspace", ...stylex.props(styles.contentHeading), children: "Workspace" }), _jsx("p", { ...stylex.props(styles.meta), children: "Each agent run starts from a fresh copy of this folder." })] }), _jsx("div", { ...stylex.props(styles.workspaceRow), children: _jsx(ResourceFact, { label: "Source folder", value: test.workspace ?? "Project directory" }) })] }));
1370
1447
  }
1371
1448
  function testCriteria(test) {
1372
1449
  if (test.criteria?.length)
@@ -1391,19 +1468,20 @@ function TestExecution({ id, test }) {
1391
1468
  const attempts = detail.data.attempts.filter((attempt) => attempt.testId === test.id);
1392
1469
  if (detail.data.testIds && !detail.data.testIds.includes(test.id))
1393
1470
  return _jsx("p", { children: "This execution does not contain this test." });
1394
- return (_jsx(ExecutionDetailView, { execution: { ...detail.data, attempts }, criteria: testCriteria(test) }));
1471
+ if (attempts.length === 0 && testRunStatus(detail.data, test.id) === "skipped")
1472
+ return (_jsx("p", { ...stylex.props(styles.empty), children: "This test was skipped in this run. Open Run history to find a run that executed it." }));
1473
+ return (_jsx(AgentDescriptions.Provider, { value: agentDescriptions(test), children: _jsx(ExecutionDetailView, { execution: { ...detail.data, attempts }, criteria: testCriteria(test) }) }));
1474
+ }
1475
+ /** What each agent in the selected test is for, from the test's own `agent({ description })`. */
1476
+ const AgentDescriptions = createContext(new Map());
1477
+ function agentDescriptions(test) {
1478
+ return new Map((test.resources ?? []).flatMap((resource) => resource.kind === "agent" && resource.description
1479
+ ? [[resource.name, resource.description]]
1480
+ : []));
1395
1481
  }
1396
- function ExecutionPage() {
1482
+ function ExecutionRunPage() {
1397
1483
  const { executionId } = executionRoute.useParams();
1398
- const detail = useQuery({
1399
- queryKey: ["execution", executionId],
1400
- queryFn: () => fetchExecution(executionId),
1401
- });
1402
- if (detail.isLoading)
1403
- return _jsx("p", { ...stylex.props(styles.empty), children: "Loading execution\u2026" });
1404
- if (detail.isError || !detail.data)
1405
- return _jsx("p", { ...stylex.props(styles.empty), children: "Execution is unavailable." });
1406
- return _jsx(ExecutionDetailView, { execution: detail.data });
1484
+ return _jsx(RunPage, { executionId: executionId }, executionId);
1407
1485
  }
1408
1486
  function ExecutionDetailView({ execution, criteria = [], }) {
1409
1487
  const client = useQueryClient();
@@ -1413,7 +1491,7 @@ function ExecutionDetailView({ execution, criteria = [], }) {
1413
1491
  });
1414
1492
  const stopping = execution.status === "running" && (cancel.isPending || cancel.isSuccess);
1415
1493
  const stopLabel = (execution.testIds?.length ?? 0) > 1 ? "Stop suite run" : "Stop this run";
1416
- return (_jsxs("div", { ...stylex.props(styles.statusPanel), children: [execution.status === "running" && execution.cancellable ? (_jsx("div", { ...stylex.props(styles.runControls), children: _jsx("button", { type: "button", ...stylex.props(styles.buttonReset, styles.button, styles.secondaryButton), onClick: () => cancel.mutate(), disabled: stopping, children: stopping ? "Stopping…" : stopLabel }) })) : null, cancel.isError ? _jsx("p", { role: "alert", children: "The run could not be stopped." }) : null, _jsxs("section", { ...stylex.props(styles.list), children: [execution.attempts.map((attempt) => (_jsx(AttemptCard, { attempt: attempt, criteria: criteria }, attempt.id))), execution.attempts.length === 0 ? (_jsx("p", { ...stylex.props(styles.empty), children: emptyExecutionMessage(execution, stopping) })) : null] })] }));
1494
+ return (_jsxs("div", { ...stylex.props(styles.statusPanel), children: [execution.status === "running" && execution.cancellable ? (_jsx("div", { ...stylex.props(styles.runControls), children: _jsx("button", { type: "button", ...stylex.props(styles.buttonReset, styles.button, styles.secondaryButton), onClick: () => cancel.mutate(), disabled: stopping, children: stopping ? "Stopping…" : stopLabel }) })) : null, cancel.isError ? _jsx("p", { role: "alert", children: "The run could not be stopped." }) : null, _jsxs("section", { ...stylex.props(styles.list), children: [execution.attempts.map((attempt) => (_jsx(AttemptCard, { attempt: attempt, criteria: criteria, showTitle: false }, attempt.id))), execution.attempts.length === 0 ? (_jsx("p", { ...stylex.props(styles.empty), children: emptyExecutionMessage(execution, stopping) })) : null] })] }));
1417
1495
  }
1418
1496
  function emptyExecutionMessage(execution, stopping) {
1419
1497
  if (stopping)
@@ -1444,19 +1522,92 @@ function ComparePage() {
1444
1522
  function ComparisonColumn({ execution, label }) {
1445
1523
  return (_jsxs("article", { ...stylex.props(styles.card), children: [_jsxs("strong", { children: [label, ": ", execution.status] }), _jsx("p", { ...stylex.props(styles.meta), children: execution.id.slice(0, 8) }), execution.attempts.map((attempt) => (_jsx(AttemptCard, { attempt: attempt }, attempt.id)))] }));
1446
1524
  }
1447
- function AttemptCard({ attempt, criteria = [] }) {
1525
+ /** `showTitle` is off on a test page, whose header already names the test. */
1526
+ function AttemptCard({ attempt, criteria = [], showTitle = true, }) {
1448
1527
  const { operations, output, title } = attemptPresentation(attempt, criteria);
1449
1528
  const agents = operations.filter((operation) => operation.kind !== "evaluation");
1450
1529
  const judges = operations.filter((operation) => operation.kind === "evaluation");
1451
- return (_jsxs("article", { ...stylex.props(styles.attemptCard), children: [_jsxs("div", { ...stylex.props(styles.row), children: [_jsxs("div", { children: [_jsx("strong", { ...stylex.props(styles.attemptTitle), children: title }), _jsxs("p", { ...stylex.props(styles.meta), children: [attempt.project || "default", " \u00B7 ", attempt.operations.length, " named", " ", pluralize(attempt.operations.length, "operation")] })] }), _jsxs("div", { ...stylex.props(styles.attemptActions), children: [attempt.status === "failed" ? (_jsx(CopyFailureButton, { attempt: attempt, criteria: criteria })) : null, _jsx("span", { ...stylex.props(styles.status, attempt.status === "passed" && styles.statusPass, attempt.status === "failed" && styles.statusFail, attempt.status === "running" && styles.statusRunning), children: attempt.status })] })] }), _jsx(OperationList, { operations: operations }), _jsx(ConversationSection, { label: "Agents", sectionOperations: agents, allOperations: operations, attempt: attempt, criteria: criteria }), _jsx(ConversationSection, { label: "Judges", sectionOperations: judges, allOperations: operations, attempt: attempt, criteria: criteria }), _jsx(TechnicalDetails, { operations: operations, output: output })] }));
1530
+ return (_jsxs("article", { ...stylex.props(styles.attemptCard), children: [_jsxs("div", { ...stylex.props(styles.row), children: [_jsxs("div", { children: [showTitle ? _jsx("strong", { ...stylex.props(styles.attemptTitle), children: title }) : null, _jsxs("p", { ...stylex.props(styles.meta), children: [attempt.project || "default", " \u00B7 ", attempt.operations.length, " named", " ", pluralize(attempt.operations.length, "operation")] })] }), _jsxs("div", { ...stylex.props(styles.attemptActions), children: [attempt.status === "failed" ? (_jsx(CopyFailureButton, { attempt: attempt, criteria: criteria })) : null, _jsx("span", { ...stylex.props(styles.status, attempt.status === "passed" && styles.statusPass, attempt.status === "failed" && styles.statusFail, attempt.status === "running" && styles.statusRunning), children: attempt.status })] })] }), _jsx(TestVerdict, { attempt: attempt, criteria: criteria, operations: operations }), _jsx(OperationList, { operations: operations }), _jsx(ConversationSection, { label: "Judges", sectionOperations: judges, allOperations: operations, attempt: attempt }), _jsx(ConversationSection, { label: "Agents", sectionOperations: agents, allOperations: operations, attempt: attempt }), _jsx(TechnicalDetails, { operations: operations, output: output })] }));
1452
1531
  }
1453
- function ConversationSection({ label, sectionOperations, allOperations, attempt, criteria, }) {
1532
+ /** Test-level outcome first: every check with its result, including judge-decided checks. */
1533
+ function TestVerdict({ attempt, criteria, operations, }) {
1534
+ const presentations = criterionPresentations({
1535
+ criteria,
1536
+ explanations: judgeCriterionExplanations(criteria, operations),
1537
+ results: attempt.criterionResults,
1538
+ status: attempt.status,
1539
+ errors: attempt.errors,
1540
+ });
1541
+ return (_jsxs("section", { "aria-label": "Test verdict", ...stylex.props(styles.verdictPanel, attempt.status === "passed" && styles.verdictPass, attempt.status === "failed" && styles.verdictFail), children: [_jsxs("p", { ...stylex.props(styles.verdictHeadline), children: [capitalize(attempt.status), _jsx("span", { ...stylex.props(styles.verdictCount), children: checkSummary(presentations, attempt.status) })] }), attempt.status === "running" ? (_jsx("p", { ...stylex.props(styles.meta), children: "The test is still running." })) : null, _jsx(VerdictMetrics, { operations: operations }), _jsx(VerdictChecks, { presentations: presentations }), _jsx(UnassignedFailures, { errors: attempt.errors, presentations: presentations })] }));
1542
+ }
1543
+ const SHORT_CHECK_LIST = 3;
1544
+ /** Checks needing attention always show; passing checks fold away once the list is long. */
1545
+ function VerdictChecks({ presentations }) {
1546
+ const checks = presentations;
1547
+ if (checks.length <= SHORT_CHECK_LIST)
1548
+ return _jsx(CriterionResults, { presentations: checks });
1549
+ const attention = checks.filter((check) => check.outcome !== "passed");
1550
+ const passed = checks.filter((check) => check.outcome === "passed");
1551
+ return (_jsxs(_Fragment, { children: [_jsx(CriterionResults, { presentations: attention }), passed.length ? (_jsxs("details", { "data-disclosure": true, ...stylex.props(styles.verdictPassedChecks), children: [_jsxs("summary", { ...stylex.props(styles.judgeContextSummary), children: [passed.length, " passed ", pluralize(passed.length, "check")] }), _jsx(CriterionResults, { presentations: passed, label: "Passed checks" })] })) : null] }));
1552
+ }
1553
+ /** Failures no check claimed, such as a crash before the first assertion. */
1554
+ function UnassignedFailures({ errors, presentations, }) {
1555
+ const assigned = new Set(presentations.map((item) => item.failure).filter(Boolean));
1556
+ return errors
1557
+ .filter((error) => !assigned.has(error.message))
1558
+ .map((error, index) => _jsx(FailureDetails, { message: error.message ?? "Test failed" }, index));
1559
+ }
1560
+ const VERDICT_METRICS = [
1561
+ { key: "durationMs", label: "Duration", format: formatDuration },
1562
+ { key: "totalTokens", label: "Tokens", format: (value) => Math.round(value).toLocaleString() },
1563
+ { key: "toolCallCount", label: "Tool calls", format: (value) => String(Math.round(value)) },
1564
+ {
1565
+ key: "changedPathCount",
1566
+ label: "Files changed",
1567
+ format: (value) => String(Math.round(value)),
1568
+ },
1569
+ ];
1570
+ /** Agent totals for the attempt; a median per run when the test made several agent runs. */
1571
+ function VerdictMetrics({ operations }) {
1572
+ const runs = operations
1573
+ .filter((operation) => operation.kind !== "evaluation")
1574
+ .map((operation) => operationResult([operation]));
1575
+ const rows = VERDICT_METRICS.flatMap((metric) => {
1576
+ const values = runs.flatMap((run) => run[metric.key] === undefined ? [] : [run[metric.key] ?? 0]);
1577
+ if (!values.length)
1578
+ return [];
1579
+ const total = values.reduce((sum, value) => sum + value, 0);
1580
+ return [{ ...metric, total, median: median(values) ?? total }];
1581
+ });
1582
+ if (!rows.length)
1583
+ return null;
1584
+ const repeated = runs.length > 1;
1585
+ return (_jsx("dl", { "aria-label": "Run metrics", ...stylex.props(styles.verdictMetrics), children: rows.map((row) => (_jsxs("div", { ...stylex.props(styles.verdictMetric), children: [_jsx("dt", { ...stylex.props(styles.resourceFactLabel), children: row.label }), _jsxs("dd", { ...stylex.props(styles.verdictMetricValue), children: [row.format(row.total), repeated ? (_jsxs("span", { ...stylex.props(styles.verdictMetricMedian), children: ["median ", row.format(row.median), " per run"] })) : null] })] }, row.key))) }));
1586
+ }
1587
+ function checkSummary(presentations, status) {
1588
+ const total = presentations.length;
1589
+ if (!total)
1590
+ return "";
1591
+ const failed = presentations.filter((item) => item.outcome === "failed").length;
1592
+ const passed = presentations.filter((item) => item.outcome === "passed").length;
1593
+ const checks = pluralize(total, "check");
1594
+ if (failed)
1595
+ return ` · ${failed} of ${total} ${checks} failed`;
1596
+ return ` · ${passed} of ${total} ${checks} passed${status === "running" ? " so far" : ""}`;
1597
+ }
1598
+ function judgeCriterionExplanations(criteria, operations) {
1599
+ const judges = operations.filter((operation) => operation.kind === "evaluation");
1600
+ return criteria.map((_, index) => judges
1601
+ .map((judge) => operationCriterionExplanations(criteria, judge)[index])
1602
+ .find((explanation) => explanation !== undefined));
1603
+ }
1604
+ function ConversationSection({ label, sectionOperations, allOperations, attempt, }) {
1454
1605
  const [selectedId, setSelectedId] = useState(sectionOperations[0]?.id);
1455
1606
  if (!sectionOperations.length && (allOperations.length > 0 || label === "Judges"))
1456
1607
  return null;
1457
1608
  const selected = sectionOperations.find((operation) => operation.id === selectedId) ?? sectionOperations[0];
1458
1609
  const status = attempt.status === "running" ? (selected?.status ?? attempt.status) : attempt.status;
1459
- return (_jsx(Conversation, { label: label, operation: selected, operations: sectionOperations, allOperations: allOperations, select: setSelectedId, status: status, attemptStatus: attempt.status, result: _jsx(AttemptResult, { attempt: attempt, criteria: criteria, operation: selected, operations: allOperations, result: selectedOperationResult(selected) }) }));
1610
+ return (_jsx(Conversation, { label: label, operation: selected, operations: sectionOperations, allOperations: allOperations, select: setSelectedId, status: status, attemptStatus: attempt.status, result: _jsx(AttemptResult, { result: selectedOperationResult(selected) }) }));
1460
1611
  }
1461
1612
  function attemptPresentation(attempt, criteria) {
1462
1613
  return {
@@ -1500,21 +1651,9 @@ function operationInvocationIndex(operation) {
1500
1651
  return [value.invocationIndex];
1501
1652
  })[0];
1502
1653
  }
1503
- function AttemptResult({ attempt, criteria, operation, operations, result, }) {
1504
- const hasJudge = operations.some((item) => item.kind === "evaluation");
1505
- const includeShared = attempt.status !== "running" && (operation?.kind === "evaluation" || !hasJudge);
1506
- const indexes = selectedCriterionIndexes({ criteria, operation, operations, includeShared });
1507
- return (_jsx(RunResult, { result: result, status: attempt.status, criteria: selectIndexes(criteria, indexes), explanations: selectOptionalIndexes(operationCriterionExplanations(criteria, operation), indexes), errors: attempt.errors, criterionResults: selectIndexes(attempt.criterionResults, indexes) }));
1508
- }
1509
- function selectedCriterionIndexes(input) {
1510
- const { criteria, operation, operations, includeShared } = input;
1511
- const recorded = operationCriterionIndexes(criteria, operation);
1512
- const allRecorded = new Set(operations.flatMap((item) => operationCriterionIndexes(criteria, item)));
1513
- if (allRecorded.size)
1514
- return criteria.flatMap((_, index) => recorded.includes(index) || (includeShared && !allRecorded.has(index)) ? [index] : []);
1515
- if (!includeShared)
1516
- return [];
1517
- return inferredRepeatedRunCriterionIndexes(criteria, operation, operations);
1654
+ /** Per-run metrics for comparing runs. Totals and checks live in the test verdict. */
1655
+ function AttemptResult({ result }) {
1656
+ return _jsx(RunResult, { result: result });
1518
1657
  }
1519
1658
  function operationCriterionIndexes(criteria, operation) {
1520
1659
  return [
@@ -1534,19 +1673,6 @@ function criterionRecordIndexes(criteria, value) {
1534
1673
  }
1535
1674
  const RUN_ORDINALS = ["first", "second", "third", "fourth", "fifth"];
1536
1675
  const CRITERION_WORD_SEPARATOR = /[^a-z0-9]+/;
1537
- function inferredRepeatedRunCriterionIndexes(criteria, operation, operations) {
1538
- if (!operation)
1539
- return undefined;
1540
- const runs = operations.filter((item) => operationIdentity(item) === operationIdentity(operation));
1541
- if (runs.length < 2)
1542
- return undefined;
1543
- const selected = inferredOperationOrdinal(criteria, operation) ??
1544
- runs.findIndex((run) => run.id === operation.id);
1545
- const scopes = criteria.map(criterionRunOrdinal);
1546
- if (selected < 0 || !scopes.some((scope) => scope !== undefined))
1547
- return undefined;
1548
- return scopes.flatMap((scope, index) => scope === undefined || scope === selected ? [index] : []);
1549
- }
1550
1676
  function inferredOperationOrdinal(criteria, operation) {
1551
1677
  const output = operationResult([operation]).output?.trim().toLowerCase();
1552
1678
  if (!output)
@@ -1571,14 +1697,6 @@ function criterionRunOrdinal(criterion) {
1571
1697
  const index = RUN_ORDINALS.findIndex((ordinal) => normalized.includes(`${ordinal} `));
1572
1698
  return index < 0 ? undefined : index;
1573
1699
  }
1574
- function selectIndexes(values, indexes) {
1575
- return indexes
1576
- ? indexes.flatMap((index) => (values[index] === undefined ? [] : [values[index]]))
1577
- : values;
1578
- }
1579
- function selectOptionalIndexes(values, indexes) {
1580
- return indexes ? indexes.map((index) => values[index]) : values;
1581
- }
1582
1700
  function CopyFailureButton({ attempt, criteria }) {
1583
1701
  const [state, setState] = useState("idle");
1584
1702
  const copy = async () => {
@@ -1678,6 +1796,7 @@ function toolCallsFromOperation(value) {
1678
1796
  name: call.name,
1679
1797
  args: isRecord(call.args) ? call.args : undefined,
1680
1798
  result: optionalString(call.result),
1799
+ images: toolImages(call.images),
1681
1800
  succeeded: typeof call.succeeded === "boolean" ? call.succeeded : undefined,
1682
1801
  exitCode: optionalNumber(call.exitCode),
1683
1802
  seq: optionalNumber(call.seq),
@@ -1747,6 +1866,7 @@ function liveToolEvent(record, seq) {
1747
1866
  name: record.name,
1748
1867
  args: isRecord(record.args) ? record.args : undefined,
1749
1868
  result: optionalString(record.result),
1869
+ images: toolImages(record.images),
1750
1870
  exitCode: optionalNumber(record.exitCode),
1751
1871
  succeeded: typeof record.succeeded === "boolean" ? record.succeeded : undefined,
1752
1872
  seq,
@@ -1816,18 +1936,8 @@ function nestedNumber(value, parent, child) {
1816
1936
  function nestedArrayLength(value, key) {
1817
1937
  return isRecord(value) ? optionalArrayLength(value[key]) : undefined;
1818
1938
  }
1819
- function RunResult({ result, status, criteria, explanations, errors, criterionResults, }) {
1820
- const presentations = criterionPresentations({
1821
- criteria,
1822
- explanations,
1823
- results: criterionResults,
1824
- status,
1825
- errors,
1826
- });
1827
- const assignedFailures = new Set(presentations.map((item) => item.failure).filter(Boolean));
1828
- return (_jsxs("section", { "aria-label": "Result", ...stylex.props(styles.resultPanel), children: [_jsx("h3", { ...stylex.props(styles.contentHeading), children: "Result" }), _jsx(ResultVerdict, { status: status }), _jsx(CriterionResults, { presentations: presentations }), errors
1829
- .filter((error) => !assignedFailures.has(error.message))
1830
- .map((error, index) => (_jsx(FailureDetails, { message: error.message ?? "Test failed" }, index))), _jsx(ResultMetrics, { result: result })] }));
1939
+ function RunResult({ result }) {
1940
+ return (_jsxs("section", { "aria-label": "Result", ...stylex.props(styles.resultPanel), children: [_jsx("h3", { ...stylex.props(styles.contentHeading), children: "Result" }), _jsx(ResultMetrics, { result: result })] }));
1831
1941
  }
1832
1942
  function FailureDetails({ message }) {
1833
1943
  const comparison = assertionComparison(message);
@@ -1838,9 +1948,6 @@ function FailureDetails({ message }) {
1838
1948
  function AssertionValue({ label, value }) {
1839
1949
  return (_jsxs("div", { ...stylex.props(styles.assertionValue), children: [_jsx("dt", { ...stylex.props(styles.assertionLabel), children: label }), _jsx("dd", { ...stylex.props(styles.assertionCode), children: value || "(empty)" })] }));
1840
1950
  }
1841
- function ResultVerdict({ status }) {
1842
- return status === "running" ? (_jsx("p", { ...stylex.props(styles.resultOutput), children: "The test is still running." })) : null;
1843
- }
1844
1951
  function criterionPresentations({ criteria, explanations, results, status, errors, }) {
1845
1952
  const presentations = criteria.map((criterion, index) => ({
1846
1953
  criterion,
@@ -1857,10 +1964,10 @@ function criterionPresentations({ criteria, explanations, results, status, error
1857
1964
  };
1858
1965
  });
1859
1966
  }
1860
- function CriterionResults({ presentations }) {
1967
+ function CriterionResults({ presentations, label = "Criterion results", }) {
1861
1968
  if (!presentations.length)
1862
1969
  return null;
1863
- return (_jsx("section", { "aria-label": "Criterion results", ...stylex.props(styles.criterionResults), children: _jsx("ul", { ...stylex.props(styles.criterionResultList), children: presentations.map((presentation) => (_jsx(CriterionResultRow, { criterion: presentation.criterion, outcome: presentation.outcome, explanation: presentation.explanation, failure: presentation.failure }, presentation.criterion))) }) }));
1970
+ return (_jsx("section", { "aria-label": label, ...stylex.props(styles.criterionResults), children: _jsx("ul", { ...stylex.props(styles.criterionResultList), children: presentations.map((presentation) => (_jsx(CriterionResultRow, { criterion: presentation.criterion, outcome: presentation.outcome, explanation: presentation.explanation, failure: presentation.failure }, presentation.criterion))) }) }));
1864
1971
  }
1865
1972
  function criterionDisplayOutcome(input) {
1866
1973
  const outcome = criterionOutcome(input);
@@ -1918,9 +2025,13 @@ function ResultMetrics({ result }) {
1918
2025
  ];
1919
2026
  return (_jsx("div", { ...stylex.props(styles.metricGrid), children: metrics.map(([label, value]) => (_jsxs("div", { ...stylex.props(styles.metric), children: [_jsx("span", { ...stylex.props(styles.resourceFactLabel), children: label }), _jsx("span", { ...stylex.props(styles.metricValue), children: value })] }, label))) }));
1920
2027
  }
2028
+ const SECTION_INTROS = {
2029
+ Agents: "The task each agent received and how it answered.",
2030
+ Judges: "A separate reviewer graded the agent answers below. Its findings feed the checks above.",
2031
+ };
1921
2032
  function Conversation({ label, operation, operations, allOperations, select, status, attemptStatus, result, }) {
1922
2033
  const { timeline, judge } = conversationContent(operation, operations);
1923
- return (_jsx("section", { "aria-label": label, ...stylex.props(styles.conversation), children: _jsxs("div", { ...stylex.props(styles.conversationSurface), children: [_jsx("header", { ...stylex.props(styles.conversationHeader), children: _jsx("h3", { ...stylex.props(styles.contentHeading), children: label }) }), _jsx(ConversationTabs, { label: label, operations: operations, selected: operation, select: select, attemptStatus: attemptStatus }), _jsx(ConversationRunTabs, { operation: operation, operations: operations, select: select }), operation?.kind === "evaluation" ? null : result, _jsxs("section", { "aria-label": `${label} messages`, ...stylex.props(styles.conversationThread), children: [judge ? (_jsx(JudgeQuestion, { review: judge, operations: allOperations })) : (_jsx(ConversationEvents, { timeline: timeline })), _jsx(JudgeResponse, { review: judge, operations: allOperations })] }), timeline.length === 0 && status !== "running" ? (_jsx("p", { ...stylex.props(styles.meta), children: conversationPlaceholder(status) })) : null, status === "running" ? _jsx(RunningIndicator, { kind: operation?.kind }) : null] }) }));
2034
+ return (_jsx("section", { "aria-label": label, ...stylex.props(styles.conversation), children: _jsxs("div", { ...stylex.props(styles.conversationSurface), children: [_jsxs("header", { ...stylex.props(styles.conversationHeader), children: [_jsx("h3", { ...stylex.props(styles.contentHeading), children: label }), _jsx("p", { ...stylex.props(styles.sectionIntro), children: SECTION_INTROS[label] })] }), _jsx(ConversationTabs, { label: label, operations: operations, selected: operation, select: select, attemptStatus: attemptStatus }), _jsx(ConversationRunTabs, { operation: operation, operations: operations, select: select }), operation?.kind === "evaluation" || operations.length < 2 ? null : result, _jsx("section", { "aria-label": `${label} messages`, ...stylex.props(styles.conversationThread), children: judge ? (_jsxs(_Fragment, { children: [_jsx(JudgeAsked, { review: judge }), _jsx(JudgeResponse, { review: judge, operations: allOperations }), _jsx(JudgeInputs, { review: judge, operations: allOperations })] })) : (_jsx(ConversationEvents, { timeline: timeline })) }), timeline.length === 0 && status !== "running" ? (_jsx("p", { ...stylex.props(styles.meta), children: conversationPlaceholder(status) })) : null, status === "running" ? _jsx(RunningIndicator, { kind: operation?.kind }) : null] }) }));
1924
2035
  }
1925
2036
  function conversationContent(operation, operations) {
1926
2037
  if (operation?.kind === "evaluation") {
@@ -1952,17 +2063,26 @@ function recordedTimeline(result) {
1952
2063
  function ConversationEvents({ timeline }) {
1953
2064
  return timeline.map((event, index) => event.kind === "tool" ? (_jsx(ToolActivity, { tool: event.tool }, `tool:${event.tool.seq ?? index}:${event.tool.name}`)) : (_jsx(MessageBubble, { message: event.message }, `message:${event.message.seq ?? index}:${event.message.role}`)));
1954
2065
  }
1955
- function JudgeQuestion({ review, operations, }) {
1956
- if (!review.question && !review.context.length)
2066
+ /** The judge's instruction comes first: it defines what each finding below means. */
2067
+ function JudgeAsked({ review }) {
2068
+ if (!review.question)
2069
+ return null;
2070
+ return (_jsx("section", { "aria-label": "Judge question", ...stylex.props(styles.judgeQuestion), children: _jsxs("p", { ...stylex.props(styles.judgeQuestionText), children: [_jsx("span", { ...stylex.props(styles.judgeAskedLabel), children: "Asked" }), _jsx("span", { children: review.question })] }) }));
2071
+ }
2072
+ /** Everything the test sent the judge, folded away: it mostly repeats the agent answers. */
2073
+ function JudgeInputs({ review, operations, }) {
2074
+ if (!review.context.length)
1957
2075
  return null;
1958
- return (_jsxs("section", { "aria-label": "Judge question", ...stylex.props(styles.judgeQuestion), children: [_jsx("h4", { ...stylex.props(styles.judgeResponseHeading), children: "Judge question" }), review.question ? (_jsx("p", { ...stylex.props(styles.judgeQuestionText), children: review.question })) : null, review.context.length ? (_jsx(JudgeContext, { context: review.context, operations: operations })) : null] }));
2076
+ // Inputs matter most before findings exist and when a finding failed.
2077
+ const inputsFirst = review.outcomes.length === 0 || review.outcomes.some((outcome) => !outcome.passed);
2078
+ return (_jsx("section", { "aria-label": "Context included", children: _jsxs("details", { "data-disclosure": true, open: inputsFirst, ...stylex.props(styles.judgeContextDisclosure), children: [_jsxs("summary", { ...stylex.props(styles.judgeContextSummary), children: ["What the judge saw \u00B7 ", review.context.length, " selected", " ", pluralize(review.context.length, "field")] }), _jsx(JudgeContext, { context: review.context, operations: operations })] }) }));
1959
2079
  }
1960
2080
  function JudgeContext({ context, operations, }) {
1961
2081
  const { answers, supplied } = judgeContextGroups(context, operations);
1962
2082
  const [selectedId, setSelectedId] = useState(answers[0]?.id);
1963
2083
  const selected = answers.find((item) => item.id === selectedId) ?? answers[0];
1964
2084
  const panelId = useId();
1965
- return (_jsxs("section", { "aria-label": "Context included", ...stylex.props(styles.judgeContext), children: [_jsxs("div", { ...stylex.props(styles.judgeContextHeader), children: [_jsx("strong", { children: "Context included" }), _jsxs("span", { children: [context.length, " selected fields"] })] }), _jsx("p", { ...stylex.props(styles.judgeContextNote), children: "Only these explicitly selected fields were sent to this judge." }), answers.length ? (_jsxs("section", { "aria-label": "Agent answers", ...stylex.props(styles.judgeContextGroup), children: [_jsx("h5", { ...stylex.props(styles.judgeContextGroupHeading), children: "Agent answers" }), _jsx("div", { role: "tablist", "aria-label": "Agent answers included", ...stylex.props(styles.judgeContextTabs), children: answers.map((answer, index) => (_jsx("button", { type: "button", role: "tab", "aria-selected": answer.id === selected?.id, "aria-controls": panelId, ...stylex.props(styles.buttonReset, styles.judgeContextTab, answer.id === selected?.id && styles.judgeContextTabActive), onClick: () => setSelectedId(answer.id), onKeyDown: (event) => selectContextWithKeyboard({ event, answers, index, select: setSelectedId }), children: answer.tabLabel }, answer.id))) }), selected ? (_jsx("div", { id: panelId, role: "tabpanel", "aria-label": `Agent response from ${selected.tabLabel}`, ...stylex.props(styles.judgeContextAnswer), children: _jsx("div", { ...stylex.props(styles.judgeContextValue), children: _jsx("div", { className: "message-markdown", children: _jsx(Markdown, { children: normalizeTranscriptMarkdown(selected.value) }) }) }) })) : null] })) : null, supplied.length ? (_jsxs("section", { "aria-label": "Additional context", ...stylex.props(styles.judgeContextGroup), children: [_jsx("h5", { ...stylex.props(styles.judgeContextGroupHeading), children: "Additional context" }), _jsx("dl", { ...stylex.props(styles.judgeContextList), children: supplied.map((item) => (_jsxs("div", { ...stylex.props(styles.judgeContextItem), children: [_jsx("dt", { ...stylex.props(styles.judgeContextLabel), children: item.label }), _jsx("dd", { ...stylex.props(styles.judgeContextValue), children: item.value })] }, item.label))) })] })) : null] }));
2085
+ return (_jsxs("div", { ...stylex.props(styles.judgeContext), children: [_jsx("p", { ...stylex.props(styles.judgeContextNote), children: "Only these fields, chosen by the test, were sent to the judge." }), answers.length ? (_jsxs("section", { "aria-label": "Agent answers", ...stylex.props(styles.judgeContextGroup), children: [_jsx("h5", { ...stylex.props(styles.judgeContextGroupHeading), children: "Agent answers" }), _jsx("div", { role: "tablist", "aria-label": "Agent answers included", ...stylex.props(styles.judgeContextTabs), children: answers.map((answer, index) => (_jsx("button", { type: "button", role: "tab", "aria-selected": answer.id === selected?.id, "aria-controls": panelId, ...stylex.props(styles.buttonReset, styles.judgeContextTab, answer.id === selected?.id && styles.judgeContextTabActive), onClick: () => setSelectedId(answer.id), onKeyDown: (event) => selectContextWithKeyboard({ event, answers, index, select: setSelectedId }), children: answer.tabLabel }, answer.id))) }), selected ? (_jsx("div", { id: panelId, role: "tabpanel", "aria-label": `Agent response from ${selected.tabLabel}`, ...stylex.props(styles.judgeContextAnswer), children: _jsx("div", { ...stylex.props(styles.judgeContextValue), children: _jsx("div", { className: "message-markdown", children: _jsx(Markdown, { children: normalizeTranscriptMarkdown(selected.value) }) }) }) })) : null] })) : null, supplied.length ? (_jsxs("section", { "aria-label": "Additional context", ...stylex.props(styles.judgeContextGroup), children: [_jsx("h5", { ...stylex.props(styles.judgeContextGroupHeading), children: "Additional context" }), _jsx("dl", { ...stylex.props(styles.judgeContextList), children: supplied.map((item) => (_jsxs("div", { ...stylex.props(styles.judgeContextItem), children: [_jsx("dt", { ...stylex.props(styles.judgeContextLabel), children: item.label }), _jsx("dd", { ...stylex.props(styles.judgeContextValue), children: item.value })] }, item.label))) })] })) : null] }));
1966
2086
  }
1967
2087
  function judgeContextGroups(context, operations) {
1968
2088
  const candidates = operations
@@ -1982,6 +2102,7 @@ function judgeContextGroups(context, operations) {
1982
2102
  ...item,
1983
2103
  id: `${match.operation.id}:${index}`,
1984
2104
  tabLabel: agentRunLabel(match.operation, operations),
2105
+ agentName: match.operation.name,
1985
2106
  });
1986
2107
  }
1987
2108
  return { answers, supplied };
@@ -2026,15 +2147,50 @@ function selectContextWithKeyboard(input) {
2026
2147
  function JudgeResponse({ review, operations, }) {
2027
2148
  if (!review || (!review.outcomes.length && !review.reason))
2028
2149
  return null;
2029
- const { answers } = judgeContextGroups(review.context, operations);
2030
- return _jsx(JudgeFindings, { outcomes: review.outcomes, reason: review.reason, answers: answers });
2031
- }
2032
- function JudgeFindings({ outcomes, reason, answers = [], }) {
2033
- return (_jsxs("section", { "aria-label": "Judge response", ...stylex.props(styles.judgeResponse), children: [_jsx("h4", { ...stylex.props(styles.judgeResponseHeading), children: "Judge response" }), _jsx("div", { ...stylex.props(styles.judgeOutcomeList), children: outcomes.map((outcome) => (_jsxs("div", { ...stylex.props(styles.judgeOutcome), children: [_jsx(CriterionOutcomeIcon, { outcome: outcome.passed ? "passed" : "failed", label: outcome.passed ? "Passed" : "Failed" }), _jsx("span", { ...stylex.props(styles.judgeOutcomeLabel), children: judgeOutcomeLabel(outcome.label, answers) }), outcome.explanation ? (_jsx("p", { ...stylex.props(styles.judgeOutcomeExplanation), children: outcome.explanation })) : null] }, outcome.label))) }), reason && !outcomes.length ? (_jsx("p", { ...stylex.props(styles.judgeOutcomeExplanation), children: reason })) : null] }));
2034
- }
2035
- function judgeOutcomeLabel(label, answers) {
2036
- const answer = answers.find((item) => label === `${item.label} Correct` || label === item.label);
2037
- return answer?.tabLabel ?? label;
2150
+ const { answers, supplied } = judgeContextGroups(review.context, operations);
2151
+ return (_jsx(JudgeFindings, { outcomes: review.outcomes, reason: review.reason, answers: answers, references: supplied.filter((item) => item.value.length <= SHORT_REFERENCE) }));
2152
+ }
2153
+ const SHORT_REFERENCE = 120;
2154
+ const ANSWER_QUOTE = 160;
2155
+ /**
2156
+ * One row per judged field, quoting the answer it judged, then the short reference values the
2157
+ * test supplied and the judge's shared reason. Together they show why each row passed or failed.
2158
+ */
2159
+ function JudgeFindings({ outcomes, reason, answers = [], references = [], }) {
2160
+ return (_jsxs("section", { "aria-label": "Judge response", ...stylex.props(styles.judgeResponse), children: [_jsx("h4", { ...stylex.props(styles.judgeResponseHeading), children: "Findings" }), _jsx("div", { ...stylex.props(styles.judgeOutcomeList), children: outcomes.map((outcome) => (_jsx(JudgeFindingRow, { outcome: outcome, answers: answers }, outcome.label))) }), outcomes.length && references.length ? (_jsxs("p", { ...stylex.props(styles.judgeReason), children: [_jsx("span", { ...stylex.props(styles.judgeAskedLabel), children: "Compared with" }), references.map((item) => `${item.label}: ${item.value}`).join(" · ")] })) : null, reason ? (_jsxs("p", { ...stylex.props(styles.judgeReason), children: [outcomes.length ? _jsx("span", { ...stylex.props(styles.judgeAskedLabel), children: "Reason" }) : null, _jsx("span", { children: reason })] })) : null] }));
2161
+ }
2162
+ function JudgeFindingRow({ outcome, answers, }) {
2163
+ const answer = judgedAnswer(outcome, answers);
2164
+ return (_jsxs("div", { ...stylex.props(styles.judgeOutcome), children: [_jsx(CriterionOutcomeIcon, { outcome: outcome.passed ? "passed" : "failed", label: outcome.passed ? "Passed" : "Failed" }), _jsx("span", { ...stylex.props(styles.judgeOutcomeLabel), children: judgeOutcomeLabel(outcome, answer) }), outcome.explanation ? (_jsx("p", { ...stylex.props(styles.judgeOutcomeExplanation), children: outcome.explanation })) : null, answer ? _jsx(JudgedAnswerSource, { answer: answer }) : null, answer ? (_jsxs("p", { ...stylex.props(styles.judgeOutcomeExplanation), children: ["\u201C", answerQuote(answer.value), "\u201D"] })) : null] }));
2165
+ }
2166
+ /** Which agent run produced the judged answer, and what that agent is for when the test says. */
2167
+ function JudgedAnswerSource({ answer }) {
2168
+ const descriptions = useContext(AgentDescriptions);
2169
+ const description = answer.agentName ? descriptions.get(answer.agentName) : undefined;
2170
+ return (_jsxs("p", { ...stylex.props(styles.judgeOutcomeSource), children: ["From ", answer.tabLabel, description ? ` — ${description}` : ""] }));
2171
+ }
2172
+ /** The answer a field is named after: "fileRoundOneCorrect" judges the "fileRoundOne" input. */
2173
+ function judgedAnswer(outcome, answers) {
2174
+ return answers.find((item) => outcome.label === item.label || outcome.label.startsWith(`${item.label} `));
2175
+ }
2176
+ /**
2177
+ * A finding is named by what the judge looked at, in the test's own words: the input label
2178
+ * ("File round one") when the field judges an answer, otherwise the field ("Explains risk").
2179
+ * The pass/fail icon carries the verdict, so the label never repeats it.
2180
+ */
2181
+ function judgeOutcomeLabel(outcome, answer) {
2182
+ return sentenceCase(answer?.label ?? outcome.label);
2183
+ }
2184
+ function sentenceCase(value) {
2185
+ const lower = value.toLowerCase();
2186
+ return lower.charAt(0).toUpperCase() + lower.slice(1);
2187
+ }
2188
+ function answerQuote(value) {
2189
+ const plain = value
2190
+ .replace(/[*_`#>]/g, "")
2191
+ .replace(/\s+/g, " ")
2192
+ .trim();
2193
+ return plain.length > ANSWER_QUOTE ? `${plain.slice(0, ANSWER_QUOTE - 1)}…` : plain;
2038
2194
  }
2039
2195
  function judgeReviewData(operation) {
2040
2196
  const records = operation.data.filter(isRecord);
@@ -2044,7 +2200,7 @@ function judgeReviewData(operation) {
2044
2200
  return {
2045
2201
  question: judgeQuestion(record),
2046
2202
  context: judgeContext(record?.input),
2047
- outcomes: judgeOutcomes(output, reason),
2203
+ outcomes: judgeOutcomes(output),
2048
2204
  reason,
2049
2205
  };
2050
2206
  }
@@ -2072,23 +2228,15 @@ function judgeQuestion(record) {
2072
2228
  const evaluation = isRecord(record?.evaluation) ? record.evaluation : undefined;
2073
2229
  return optionalString(evaluation?.prompt);
2074
2230
  }
2075
- function judgeOutcomes(output, fallbackExplanation) {
2231
+ /** Only an explanation the judge gave for that field attaches to it; a shared reason never does. */
2232
+ function judgeOutcomes(output) {
2076
2233
  const entries = Object.entries(output).filter((entry) => typeof entry[1] === "boolean");
2077
- const fallbacks = splitJudgeExplanation(fallbackExplanation, entries.length);
2078
- return entries.map(([key, passed], index) => ({
2234
+ return entries.map(([key, passed]) => ({
2079
2235
  label: humanizeKey(key),
2080
2236
  passed,
2081
- explanation: judgeOutcomeExplanation(output, key) ?? fallbacks[index],
2237
+ explanation: judgeOutcomeExplanation(output, key),
2082
2238
  }));
2083
2239
  }
2084
- function splitJudgeExplanation(value, count) {
2085
- if (!value || count < 1)
2086
- return [];
2087
- const sentences = value.split(SENTENCE_BOUNDARY).filter(Boolean);
2088
- if (sentences.length < count)
2089
- return Array.from({ length: count }, () => value);
2090
- return Array.from({ length: count }, (_, index) => index === count - 1 ? sentences.slice(index).join(" ") : (sentences[index] ?? value));
2091
- }
2092
2240
  function judgeOutcomeExplanation(output, key) {
2093
2241
  const explanations = isRecord(output.explanations) ? output.explanations : {};
2094
2242
  return (optionalString(explanations[key]) ??
@@ -2205,7 +2353,7 @@ function RunningIndicator({ kind }) {
2205
2353
  function ToolActivity({ tool }) {
2206
2354
  const presentation = toolPresentation(tool);
2207
2355
  const [outputOpen, setOutputOpen] = useState(false);
2208
- return (_jsxs("article", { ...stylex.props(styles.toolEvent), children: [_jsx("span", { "aria-hidden": "true", ...stylex.props(styles.toolIcon), children: presentation.icon }), _jsxs("div", { ...stylex.props(styles.toolCopy), children: [_jsx("strong", { ...stylex.props(styles.toolTitle), children: presentation.title }), presentation.detail ? (_jsx("span", { ...stylex.props(styles.toolDetail), children: presentation.detail })) : null] }), _jsxs("div", { role: "group", "aria-label": "Tool controls", ...stylex.props(styles.toolActions), children: [_jsx("span", { ...stylex.props(styles.toolOutcome, presentation.failed && styles.toolOutcomeFail), children: presentation.outcome }), tool.result ? (_jsx("button", { type: "button", ...stylex.props(styles.buttonReset, styles.toolResultSummary), onClick: () => setOutputOpen((open) => !open), children: outputOpen ? "Hide output" : "View output" })) : null] }), tool.result && outputOpen ? (_jsx("pre", { ...stylex.props(styles.toolResultPre), children: tool.result })) : null] }));
2356
+ return (_jsxs("article", { ...stylex.props(styles.toolEvent), children: [_jsx("span", { "aria-hidden": "true", ...stylex.props(styles.toolIcon), children: presentation.icon }), _jsxs("div", { ...stylex.props(styles.toolCopy), children: [_jsx("strong", { ...stylex.props(styles.toolTitle), children: presentation.title }), presentation.detail ? (_jsx("span", { title: presentation.detail, ...stylex.props(styles.toolDetail), children: presentation.detail })) : null] }), _jsxs("div", { role: "group", "aria-label": "Tool controls", ...stylex.props(styles.toolActions), children: [_jsx("span", { ...stylex.props(styles.toolOutcome, presentation.failed && styles.toolOutcomeFail), children: presentation.outcome }), tool.result ? (_jsx("button", { type: "button", ...stylex.props(styles.buttonReset, styles.toolResultSummary), onClick: () => setOutputOpen((open) => !open), children: outputOpen ? "Hide output" : "View output" })) : null] }), tool.result && outputOpen ? (_jsx("pre", { ...stylex.props(styles.toolResultPre), children: tool.result })) : null, tool.images ? _jsx(ToolImages, { images: tool.images, toolName: presentation.title }) : null] }));
2209
2357
  }
2210
2358
  function toolPresentation(tool) {
2211
2359
  const name = tool.name.toLowerCase();
@@ -2214,7 +2362,13 @@ function toolPresentation(tool) {
2214
2362
  if (name === "read" || name.includes("read_file"))
2215
2363
  return { icon: "↗", title: "Read file", detail: argument(tool, "path"), outcome, failed };
2216
2364
  if (["shell", "bash", "exec_command"].includes(name))
2217
- return { icon: ">", title: "Run command", detail: argument(tool, "command"), outcome, failed };
2365
+ return {
2366
+ icon: ">",
2367
+ title: "Run command",
2368
+ detail: unwrapShellCommand(argument(tool, "command")),
2369
+ outcome,
2370
+ failed,
2371
+ };
2218
2372
  if (name.includes("edit") || name.includes("write"))
2219
2373
  return { icon: "✎", title: "Change file", detail: argument(tool, "path"), outcome, failed };
2220
2374
  return { icon: "◆", title: tool.name, detail: summarizeArgs(tool.args), outcome, failed };
@@ -2260,7 +2414,7 @@ function OperationList({ operations }) {
2260
2414
  if (operations.length === 0)
2261
2415
  return null;
2262
2416
  const summaries = summarizeOperations(operations);
2263
- return (_jsx("div", { ...stylex.props(styles.list), children: summaries.map((summary) => (_jsxs("div", { ...stylex.props(styles.meta), children: [summary.name, summary.count > 1 ? ` ×${summary.count}` : ""] }, summary.key))) }));
2417
+ return (_jsxs("div", { ...stylex.props(styles.ranRow), children: [_jsx("span", { ...stylex.props(styles.ranLabel), children: "Ran" }), summaries.map((summary) => (_jsxs("span", { ...stylex.props(styles.ranChip), children: [summary.name, summary.count > 1 ? ` ×${summary.count}` : ""] }, summary.key)))] }));
2264
2418
  }
2265
2419
  function summarizeOperations(operations) {
2266
2420
  const summaries = new Map();
@@ -2275,40 +2429,14 @@ function summarizeOperations(operations) {
2275
2429
  }
2276
2430
  return [...summaries.values()];
2277
2431
  }
2278
- function useExecutionHistory() {
2279
- return useQuery({
2280
- queryKey: ["execution-history"],
2281
- queryFn: fetchExecutionHistory,
2282
- });
2283
- }
2284
- async function fetchTestCatalog() {
2285
- return viewerApi.getTestCatalog();
2286
- }
2287
- async function fetchExecutionHistory() {
2288
- return (await viewerApi.listExecutions()).executions;
2289
- }
2290
- async function fetchExecution(id) {
2291
- return (await viewerApi.getExecution(id)).execution;
2292
- }
2293
- async function startTest(testId, workers) {
2294
- return viewerApi.startExecution(testId, workers);
2295
- }
2296
- async function startSuite(workers) {
2297
- return viewerApi.startSuiteExecution(workers);
2298
- }
2299
- async function startTestGroup(testIds, workers) {
2300
- return viewerApi.startTestGroup(testIds, workers);
2301
- }
2302
- async function cancelExecution(id) {
2303
- await viewerApi.cancelExecution(id);
2304
- }
2305
2432
  function latestTestStatuses(executions) {
2306
2433
  const statuses = new Map();
2307
2434
  for (const execution of executions) {
2308
2435
  for (const testId of execution.testIds ?? []) {
2309
- if (statuses.has(testId))
2310
- continue;
2311
- statuses.set(testId, execution.testStatuses?.[testId] ?? execution.status);
2436
+ const status = testRunStatus(execution, testId);
2437
+ const known = statuses.get(testId);
2438
+ if (known === undefined || (known === "skipped" && status !== "skipped"))
2439
+ statuses.set(testId, status);
2312
2440
  }
2313
2441
  }
2314
2442
  return statuses;