@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.
- package/dist/sdk/cli.d.ts +10 -0
- package/dist/sdk/cli.d.ts.map +1 -1
- package/dist/sdk/cli.js +27 -2
- package/dist/sdk/cli.js.map +1 -1
- package/dist/sdk/stored-value.d.ts.map +1 -1
- package/dist/sdk/stored-value.js +11 -0
- package/dist/sdk/stored-value.js.map +1 -1
- package/dist/sdk/workspace.d.ts.map +1 -1
- package/dist/sdk/workspace.js +14 -1
- package/dist/sdk/workspace.js.map +1 -1
- package/dist/viewer/client-entry.js +21 -20
- package/dist/viewer/main.js +16 -4
- package/dist/viewer/main.js.map +1 -1
- package/dist/viewer/presentation.d.ts +20 -2
- package/dist/viewer/presentation.d.ts.map +1 -1
- package/dist/viewer/presentation.js +64 -2
- package/dist/viewer/presentation.js.map +1 -1
- package/dist/viewer/run-inbox.d.ts +7 -0
- package/dist/viewer/run-inbox.d.ts.map +1 -0
- package/dist/viewer/run-inbox.js +335 -0
- package/dist/viewer/run-inbox.js.map +1 -0
- package/dist/viewer/styles.d.ts.map +1 -1
- package/dist/viewer/styles.js +5 -0
- package/dist/viewer/styles.js.map +1 -1
- package/dist/viewer/stylex.css +230 -2
- package/dist/viewer/tool-images.d.ts +13 -0
- package/dist/viewer/tool-images.d.ts.map +1 -0
- package/dist/viewer/tool-images.js +77 -0
- package/dist/viewer/tool-images.js.map +1 -0
- package/dist/viewer/v2-client.d.ts.map +1 -1
- package/dist/viewer/v2-client.js +300 -172
- package/dist/viewer/v2-client.js.map +1 -1
- package/dist/viewer/viewer-queries.d.ts +26 -0
- package/dist/viewer/viewer-queries.d.ts.map +1 -0
- package/dist/viewer/viewer-queries.js +31 -0
- package/dist/viewer/viewer-queries.js.map +1 -0
- package/package.json +2 -2
package/dist/viewer/v2-client.js
CHANGED
|
@@ -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 {
|
|
8
|
-
import {
|
|
9
|
-
|
|
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:
|
|
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:
|
|
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-
|
|
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
|
-
|
|
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(--
|
|
857
|
-
fontSize: "0.
|
|
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: "
|
|
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(--
|
|
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 ??
|
|
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
|
|
1330
|
-
return (
|
|
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
|
|
1333
|
-
|
|
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
|
|
1336
|
-
return (
|
|
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: "\
|
|
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: "
|
|
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
|
-
|
|
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
|
|
1482
|
+
function ExecutionRunPage() {
|
|
1397
1483
|
const { executionId } = executionRoute.useParams();
|
|
1398
|
-
|
|
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
|
-
|
|
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: "
|
|
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
|
-
|
|
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, {
|
|
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
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
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
|
|
1820
|
-
|
|
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":
|
|
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: [
|
|
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
|
-
|
|
1956
|
-
|
|
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
|
-
|
|
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("
|
|
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
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
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 {
|
|
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 (
|
|
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
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
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;
|