@post-print/agent-test 2.1.0 → 2.2.1
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/README.md +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/sdk/execution-reporter.d.ts.map +1 -1
- package/dist/sdk/execution-reporter.js +4 -0
- package/dist/sdk/execution-reporter.js.map +1 -1
- package/dist/sdk/execution-store.d.ts +9 -0
- package/dist/sdk/execution-store.d.ts.map +1 -1
- package/dist/sdk/execution-store.js +27 -5
- package/dist/sdk/execution-store.js.map +1 -1
- package/dist/sdk/reporter.js +5 -1
- package/dist/sdk/reporter.js.map +1 -1
- package/dist/sdk/test.d.ts +7 -1
- package/dist/sdk/test.d.ts.map +1 -1
- package/dist/sdk/test.js +14 -2
- package/dist/sdk/test.js.map +1 -1
- package/dist/viewer/client-entry.js +23 -21
- package/dist/viewer/elapsed-time.d.ts +5 -0
- package/dist/viewer/elapsed-time.d.ts.map +1 -0
- package/dist/viewer/elapsed-time.js +16 -0
- package/dist/viewer/elapsed-time.js.map +1 -0
- package/dist/viewer/presentation.d.ts +20 -0
- package/dist/viewer/presentation.d.ts.map +1 -1
- package/dist/viewer/presentation.js +52 -0
- package/dist/viewer/presentation.js.map +1 -1
- package/dist/viewer/run-inbox.d.ts.map +1 -1
- package/dist/viewer/run-inbox.js +12 -5
- package/dist/viewer/run-inbox.js.map +1 -1
- package/dist/viewer/server.d.ts.map +1 -1
- package/dist/viewer/server.js +52 -4
- package/dist/viewer/server.js.map +1 -1
- package/dist/viewer/stylex.css +28 -4
- package/dist/viewer/test-catalog.d.ts +2 -0
- package/dist/viewer/test-catalog.d.ts.map +1 -1
- package/dist/viewer/test-catalog.js +1 -0
- package/dist/viewer/test-catalog.js.map +1 -1
- package/dist/viewer/v2-client.d.ts.map +1 -1
- package/dist/viewer/v2-client.js +227 -27
- package/dist/viewer/v2-client.js.map +1 -1
- package/dist/viewer/viewer-queries.d.ts +1 -0
- package/dist/viewer/viewer-queries.d.ts.map +1 -1
- package/dist/viewer/viewer-queries.js +7 -1
- package/dist/viewer/viewer-queries.js.map +1 -1
- package/package.json +2 -2
package/dist/viewer/v2-client.js
CHANGED
|
@@ -4,7 +4,8 @@ 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 {
|
|
7
|
+
import { ElapsedTime } from "./elapsed-time.js";
|
|
8
|
+
import { assertionComparison, conversationPlaceholder, describeShape, formatRunDate, median, preferredTestExecutionId, stripAnsi, testIntent, testRunStatus, unwrapShellCommand, } from "./presentation.js";
|
|
8
9
|
import { RunInboxPage, RunPage } from "./run-inbox.js";
|
|
9
10
|
import { ToolImages, toolImages } from "./tool-images.js";
|
|
10
11
|
import { cancelExecution, fetchExecution as fetchExecutionRecord, startSuite, startTest, startTestGroup, useCatalog, useExecutionHistory, } from "./viewer-queries.js";
|
|
@@ -363,6 +364,18 @@ const styles = stylex.create({
|
|
|
363
364
|
letterSpacing: "0.08em",
|
|
364
365
|
textTransform: "uppercase",
|
|
365
366
|
},
|
|
367
|
+
catalogNotice: {
|
|
368
|
+
margin: "0.6rem 1.25rem 0",
|
|
369
|
+
color: "var(--muted)",
|
|
370
|
+
fontSize: "0.72rem",
|
|
371
|
+
lineHeight: 1.45,
|
|
372
|
+
},
|
|
373
|
+
catalogNoticeError: {
|
|
374
|
+
padding: "0.45rem 0.6rem",
|
|
375
|
+
borderRadius: 8,
|
|
376
|
+
color: "var(--fail)",
|
|
377
|
+
backgroundColor: "var(--fail-soft)",
|
|
378
|
+
},
|
|
366
379
|
groupCount: { color: "var(--subtle)", fontSize: "0.66rem", fontVariantNumeric: "tabular-nums" },
|
|
367
380
|
navLink: {
|
|
368
381
|
display: "block",
|
|
@@ -431,6 +444,16 @@ const styles = stylex.create({
|
|
|
431
444
|
alignSelf: "start",
|
|
432
445
|
},
|
|
433
446
|
description: { color: "var(--muted)", fontSize: "0.82rem", lineHeight: 1.5, maxWidth: "54rem" },
|
|
447
|
+
suiteDescription: {
|
|
448
|
+
margin: 0,
|
|
449
|
+
color: "var(--subtle)",
|
|
450
|
+
fontSize: "0.74rem",
|
|
451
|
+
lineHeight: 1.45,
|
|
452
|
+
overflow: "hidden",
|
|
453
|
+
textOverflow: "ellipsis",
|
|
454
|
+
whiteSpace: "nowrap",
|
|
455
|
+
},
|
|
456
|
+
headerChecks: { paddingTop: "0.55rem" },
|
|
434
457
|
path: {
|
|
435
458
|
color: "var(--subtle)",
|
|
436
459
|
fontSize: "0.7rem",
|
|
@@ -632,6 +655,46 @@ const styles = stylex.create({
|
|
|
632
655
|
gap: "0.7rem",
|
|
633
656
|
},
|
|
634
657
|
attemptTitle: { fontSize: "0.84rem", lineHeight: 1.4 },
|
|
658
|
+
liveStrip: {
|
|
659
|
+
position: "sticky",
|
|
660
|
+
top: 0,
|
|
661
|
+
zIndex: 2,
|
|
662
|
+
display: "grid",
|
|
663
|
+
gap: "0.3rem",
|
|
664
|
+
padding: "0.55rem 0.75rem",
|
|
665
|
+
border: "1px solid var(--border-strong)",
|
|
666
|
+
borderRadius: 10,
|
|
667
|
+
backgroundColor: "var(--panel-2)",
|
|
668
|
+
minWidth: 0,
|
|
669
|
+
},
|
|
670
|
+
liveStripLine: {
|
|
671
|
+
display: "flex",
|
|
672
|
+
flexWrap: "wrap",
|
|
673
|
+
alignItems: "center",
|
|
674
|
+
gap: "0.4rem 0.75rem",
|
|
675
|
+
},
|
|
676
|
+
liveStripElapsed: {
|
|
677
|
+
color: "var(--text)",
|
|
678
|
+
fontSize: "0.82rem",
|
|
679
|
+
fontWeight: 700,
|
|
680
|
+
fontVariantNumeric: "tabular-nums",
|
|
681
|
+
},
|
|
682
|
+
liveStripMeta: { color: "var(--muted)", fontSize: "0.76rem", fontVariantNumeric: "tabular-nums" },
|
|
683
|
+
liveStripNow: {
|
|
684
|
+
margin: 0,
|
|
685
|
+
minWidth: 0,
|
|
686
|
+
overflow: "hidden",
|
|
687
|
+
textOverflow: "ellipsis",
|
|
688
|
+
whiteSpace: "nowrap",
|
|
689
|
+
color: "var(--text)",
|
|
690
|
+
fontSize: "0.78rem",
|
|
691
|
+
},
|
|
692
|
+
liveStripLabel: { color: "var(--subtle)", fontWeight: 650 },
|
|
693
|
+
liveStripDetail: {
|
|
694
|
+
marginLeft: "0.4rem",
|
|
695
|
+
color: "var(--muted)",
|
|
696
|
+
fontSize: "0.72rem",
|
|
697
|
+
},
|
|
635
698
|
verdictPanel: {
|
|
636
699
|
display: "grid",
|
|
637
700
|
gap: "0.55rem",
|
|
@@ -652,6 +715,16 @@ const styles = stylex.create({
|
|
|
652
715
|
margin: 0,
|
|
653
716
|
},
|
|
654
717
|
verdictMetric: { display: "grid", gap: "0.1rem", minWidth: 0 },
|
|
718
|
+
verdictAttachments: {
|
|
719
|
+
display: "flex",
|
|
720
|
+
flexWrap: "wrap",
|
|
721
|
+
gap: "0.25rem 1rem",
|
|
722
|
+
margin: 0,
|
|
723
|
+
padding: 0,
|
|
724
|
+
listStyle: "none",
|
|
725
|
+
fontSize: "0.8rem",
|
|
726
|
+
overflowWrap: "anywhere",
|
|
727
|
+
},
|
|
655
728
|
verdictMetricValue: {
|
|
656
729
|
display: "grid",
|
|
657
730
|
margin: 0,
|
|
@@ -990,15 +1063,38 @@ const styles = stylex.create({
|
|
|
990
1063
|
judgeContextList: { display: "grid", gap: "0.3rem", margin: 0 },
|
|
991
1064
|
judgeContextItem: {
|
|
992
1065
|
display: "grid",
|
|
993
|
-
|
|
994
|
-
default: "minmax(7.5rem, 0.3fr) minmax(0, 1fr)",
|
|
995
|
-
"@media (max-width: 620px)": "1fr",
|
|
996
|
-
},
|
|
997
|
-
gap: "0.65rem",
|
|
1066
|
+
gap: "0.45rem",
|
|
998
1067
|
padding: "0.4rem 0.5rem",
|
|
999
1068
|
borderRadius: 6,
|
|
1000
1069
|
backgroundColor: "var(--bg)",
|
|
1001
1070
|
},
|
|
1071
|
+
judgeContextFieldSummary: {
|
|
1072
|
+
display: "flex",
|
|
1073
|
+
alignItems: "baseline",
|
|
1074
|
+
gap: "0.5rem",
|
|
1075
|
+
cursor: "pointer",
|
|
1076
|
+
minWidth: 0,
|
|
1077
|
+
},
|
|
1078
|
+
judgeContextShape: {
|
|
1079
|
+
color: "var(--muted)",
|
|
1080
|
+
fontSize: "0.68rem",
|
|
1081
|
+
fontVariantNumeric: "tabular-nums",
|
|
1082
|
+
},
|
|
1083
|
+
judgeContextFieldBody: {
|
|
1084
|
+
display: "grid",
|
|
1085
|
+
gap: "0.35rem",
|
|
1086
|
+
maxHeight: "24rem",
|
|
1087
|
+
overflow: "auto",
|
|
1088
|
+
},
|
|
1089
|
+
judgeContextStringList: {
|
|
1090
|
+
margin: 0,
|
|
1091
|
+
paddingLeft: "1.1rem",
|
|
1092
|
+
color: "var(--text)",
|
|
1093
|
+
fontSize: "0.72rem",
|
|
1094
|
+
lineHeight: 1.5,
|
|
1095
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
1096
|
+
wordBreak: "break-word",
|
|
1097
|
+
},
|
|
1002
1098
|
judgeContextLabel: {
|
|
1003
1099
|
color: "var(--subtle)",
|
|
1004
1100
|
fontSize: "0.64rem",
|
|
@@ -1281,7 +1377,7 @@ function ViewerSidebarContent({ mode, open = true, headingId, navigationId, onTo
|
|
|
1281
1377
|
const history = useExecutionHistory();
|
|
1282
1378
|
const groups = groupTests(catalog.data?.tests ?? []);
|
|
1283
1379
|
const statuses = latestTestStatuses(history.data ?? []);
|
|
1284
|
-
return (_jsxs(_Fragment, { children: [_jsx(SidebarHeader, { mode: mode, open: open, count: catalog.data?.tests.length ?? 0, headingId: headingId, navigationId: navigationId, onToggle: mode === "mobile" ? onClose : onToggle, onNavigate: onClose }), open ? (_jsx(SidebarNavigation, { groups: groups, statuses: statuses,
|
|
1380
|
+
return (_jsxs(_Fragment, { children: [_jsx(SidebarHeader, { mode: mode, open: open, count: catalog.data?.tests.length ?? 0, headingId: headingId, navigationId: navigationId, onToggle: mode === "mobile" ? onClose : onToggle, onNavigate: onClose }), open ? (_jsx(SidebarNavigation, { groups: groups, statuses: statuses, catalogState: catalogState(catalog), navigationId: navigationId, onNavigate: onClose })) : null] }));
|
|
1285
1381
|
}
|
|
1286
1382
|
function SidebarHeader({ mode, open, count, headingId, navigationId, onToggle, onNavigate, }) {
|
|
1287
1383
|
const { workers } = useContext(WorkerContext);
|
|
@@ -1309,8 +1405,21 @@ function SidebarToggle({ mode, open, navigationId, onToggle, }) {
|
|
|
1309
1405
|
const mobile = mode === "mobile";
|
|
1310
1406
|
return (_jsx("button", { type: "button", "data-drawer-close": mobile ? "true" : undefined, "aria-controls": navigationId, "aria-expanded": open, "aria-label": mobile ? "Close test menu" : open ? "Collapse test sidebar" : "Expand test sidebar", ...stylex.props(styles.buttonReset, styles.toggleButton, mobile && styles.mobileCloseButton), onClick: onToggle, children: mobile ? "×" : open ? "←" : "→" }));
|
|
1311
1407
|
}
|
|
1312
|
-
|
|
1313
|
-
|
|
1408
|
+
/** A failed refresh over a list already shown is stale, not missing. */
|
|
1409
|
+
function catalogState(catalog) {
|
|
1410
|
+
if (!catalog.isError)
|
|
1411
|
+
return "ready";
|
|
1412
|
+
return catalog.data ? "stale" : "unavailable";
|
|
1413
|
+
}
|
|
1414
|
+
function CatalogNotice({ state }) {
|
|
1415
|
+
if (state === "ready")
|
|
1416
|
+
return null;
|
|
1417
|
+
if (state === "stale")
|
|
1418
|
+
return (_jsx("p", { role: "status", ...stylex.props(styles.catalogNotice), children: "Test list may be out of date. Retrying\u2026" }));
|
|
1419
|
+
return (_jsx("p", { role: "alert", ...stylex.props(styles.catalogNotice, styles.catalogNoticeError), children: "Test discovery is unavailable. Retrying\u2026" }));
|
|
1420
|
+
}
|
|
1421
|
+
function SidebarNavigation({ groups, statuses, catalogState, navigationId, onNavigate, }) {
|
|
1422
|
+
return (_jsxs(_Fragment, { children: [_jsx(CatalogNotice, { state: catalogState }), _jsx("nav", { id: navigationId, "aria-label": "Tests", ...stylex.props(styles.navigation), children: [...groups].map(([name, tests]) => (_jsxs("section", { "aria-label": `${name} tests`, ...stylex.props(styles.navSection), children: [_jsxs("div", { ...stylex.props(styles.groupHeader), children: [_jsx("strong", { title: tests[0] ? testIntent(tests[0], tests).suite : undefined, ...stylex.props(styles.groupLabel), children: name }), _jsxs("div", { ...stylex.props(styles.groupHeaderActions), children: [_jsx("span", { ...stylex.props(styles.groupCount), children: tests.length }), _jsx(RunTestGroupButton, { name: name, tests: tests, onNavigate: onNavigate })] })] }), tests.map((test) => (_jsx(TestLink, { test: test, hint: testIntent(test, tests).own, status: statuses.get(test.id), onNavigate: onNavigate }, test.id)))] }, name))) })] }));
|
|
1314
1423
|
}
|
|
1315
1424
|
function RunTestGroupButton({ name, tests, onNavigate, }) {
|
|
1316
1425
|
const { workers } = useContext(WorkerContext);
|
|
@@ -1326,8 +1435,8 @@ function RunTestGroupButton({ name, tests, onNavigate, }) {
|
|
|
1326
1435
|
});
|
|
1327
1436
|
return (_jsx("button", { type: "button", "aria-label": `Run ${name} suite`, title: `Run ${name} suite`, ...stylex.props(styles.buttonReset, styles.groupRunButton), onClick: () => run.mutate(), disabled: run.isPending, children: run.isPending ? "●" : "▶" }));
|
|
1328
1437
|
}
|
|
1329
|
-
function TestLink({ test, status, onNavigate, }) {
|
|
1330
|
-
return (_jsxs(Link, { to: "/tests/$testId", params: { testId: test.id }, search: {}, ...stylex.props(styles.navLink), activeProps: stylex.props(styles.navLink, styles.active), onClick: onNavigate, children: [_jsxs("div", { ...stylex.props(styles.navItemHeader), children: [status ? _jsx(TestStatus, { status: status }) : _jsx("span", { "aria-hidden": "true" }), _jsx("span", { ...stylex.props(styles.navItemTitle), children: test.title.at(-1) })] }), test.project !== "default" ? _jsx("div", { ...stylex.props(styles.meta), children: test.project }) : null] }));
|
|
1438
|
+
function TestLink({ test, hint, status, onNavigate, }) {
|
|
1439
|
+
return (_jsxs(Link, { to: "/tests/$testId", params: { testId: test.id }, search: {}, ...stylex.props(styles.navLink), activeProps: stylex.props(styles.navLink, styles.active), onClick: onNavigate, children: [_jsxs("div", { ...stylex.props(styles.navItemHeader), children: [status ? _jsx(TestStatus, { status: status }) : _jsx("span", { "aria-hidden": "true" }), _jsx("span", { title: hint ?? test.title.at(-1), ...stylex.props(styles.navItemTitle), children: test.title.at(-1) })] }), test.project !== "default" ? _jsx("div", { ...stylex.props(styles.meta), children: test.project }) : null] }));
|
|
1331
1440
|
}
|
|
1332
1441
|
function TestStatus({ status }) {
|
|
1333
1442
|
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) }) }));
|
|
@@ -1340,9 +1449,9 @@ function TestPage() {
|
|
|
1340
1449
|
const test = catalog.data?.tests.find((item) => item.id === testId);
|
|
1341
1450
|
if (!test)
|
|
1342
1451
|
return (_jsx("p", { role: "alert", children: "This test is no longer available. Choose another test in the sidebar." }));
|
|
1343
|
-
return _jsx(TestDetail, { test: test }, test.id);
|
|
1452
|
+
return (_jsx(TestDetail, { test: test, intent: testIntent(test, catalog.data?.tests ?? []) }, test.id));
|
|
1344
1453
|
}
|
|
1345
|
-
function TestDetail({ test }) {
|
|
1454
|
+
function TestDetail({ test, intent }) {
|
|
1346
1455
|
const { execution, view } = testRoute.useSearch();
|
|
1347
1456
|
const history = useExecutionHistory();
|
|
1348
1457
|
const navigate = useNavigate();
|
|
@@ -1359,7 +1468,7 @@ function TestDetail({ test }) {
|
|
|
1359
1468
|
params: { testId: test.id },
|
|
1360
1469
|
search: { execution, view: next === "current" ? undefined : next },
|
|
1361
1470
|
});
|
|
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 })] }));
|
|
1471
|
+
return (_jsxs(_Fragment, { children: [_jsx(TestHeader, { test: test, intent: intent, 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
1472
|
}
|
|
1364
1473
|
/** Return path to the run inbox when this test was opened from a batch run. */
|
|
1365
1474
|
function BatchRunLink({ run }) {
|
|
@@ -1368,8 +1477,14 @@ function BatchRunLink({ run }) {
|
|
|
1368
1477
|
return null;
|
|
1369
1478
|
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"] }));
|
|
1370
1479
|
}
|
|
1371
|
-
function TestHeader({ test, onStarted }) {
|
|
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:
|
|
1480
|
+
function TestHeader({ test, intent, onStarted, }) {
|
|
1481
|
+
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(" / ") }), intent.suite ? (_jsx("p", { title: intent.suite, ...stylex.props(styles.suiteDescription), children: intent.suite })) : null, _jsx("h2", { ...stylex.props(styles.title), children: test.title.at(-1) }), intent.own ? _jsx("p", { ...stylex.props(styles.description), children: intent.own }) : null] }), _jsx(RunTestButton, { test: test, onStarted: onStarted })] }), _jsx(TestChecks, { criteria: test.criteria ?? [] }), _jsxs("div", { ...stylex.props(styles.path), children: [test.project, " \u00B7 ", test.file] })] }));
|
|
1482
|
+
}
|
|
1483
|
+
/** What the test checks, folded: the verdict below lists the same checks once a run exists. */
|
|
1484
|
+
function TestChecks({ criteria }) {
|
|
1485
|
+
if (!criteria.length)
|
|
1486
|
+
return null;
|
|
1487
|
+
return (_jsxs("details", { "data-disclosure": true, ...stylex.props(styles.judgeContextDisclosure), children: [_jsxs("summary", { ...stylex.props(styles.judgeContextSummary), children: ["Passes when \u00B7 ", criteria.length, " ", pluralize(criteria.length, "check")] }), _jsx("ul", { ...stylex.props(styles.criteriaList, styles.headerChecks), 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))) })] }));
|
|
1373
1488
|
}
|
|
1374
1489
|
const TEST_VIEWS = [
|
|
1375
1490
|
{ id: "current", label: "Current run" },
|
|
@@ -1491,7 +1606,7 @@ function ExecutionDetailView({ execution, criteria = [], }) {
|
|
|
1491
1606
|
});
|
|
1492
1607
|
const stopping = execution.status === "running" && (cancel.isPending || cancel.isSuccess);
|
|
1493
1608
|
const stopLabel = (execution.testIds?.length ?? 0) > 1 ? "Stop suite run" : "Stop this run";
|
|
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] })] }));
|
|
1609
|
+
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, progress: execution.progress?.filter((entry) => entry.attemptId === attempt.id) }, attempt.id))), execution.attempts.length === 0 ? (_jsx("p", { ...stylex.props(styles.empty), children: emptyExecutionMessage(execution, stopping) })) : null] })] }));
|
|
1495
1610
|
}
|
|
1496
1611
|
function emptyExecutionMessage(execution, stopping) {
|
|
1497
1612
|
if (stopping)
|
|
@@ -1523,11 +1638,45 @@ function ComparisonColumn({ execution, label }) {
|
|
|
1523
1638
|
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)))] }));
|
|
1524
1639
|
}
|
|
1525
1640
|
/** `showTitle` is off on a test page, whose header already names the test. */
|
|
1526
|
-
function AttemptCard({ attempt, criteria = [], showTitle = true, }) {
|
|
1641
|
+
function AttemptCard({ attempt, criteria = [], showTitle = true, progress = [], }) {
|
|
1527
1642
|
const { operations, output, title } = attemptPresentation(attempt, criteria);
|
|
1528
1643
|
const agents = operations.filter((operation) => operation.kind !== "evaluation");
|
|
1529
1644
|
const judges = operations.filter((operation) => operation.kind === "evaluation");
|
|
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 })] }));
|
|
1645
|
+
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 })] })] }), attempt.status === "running" ? (_jsx(LiveRunStrip, { attempt: attempt, operations: operations, progress: progress.at(-1) })) : null, _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 })] }));
|
|
1646
|
+
}
|
|
1647
|
+
/** Where a long run is right now: how long it has run, how far it got, and what it is doing. */
|
|
1648
|
+
function LiveRunStrip({ attempt, operations, progress, }) {
|
|
1649
|
+
const step = currentStep(operations);
|
|
1650
|
+
const reported = progress ? progressSummary(progress.data) : undefined;
|
|
1651
|
+
return (_jsxs("section", { "aria-label": "Live run status", ...stylex.props(styles.liveStrip), children: [_jsxs("div", { ...stylex.props(styles.liveStripLine), children: [_jsx(RunningIndicator, { kind: step.kind }), attempt.startedAt ? (_jsx("span", { ...stylex.props(styles.liveStripElapsed), children: _jsx(ElapsedTime, { since: attempt.startedAt }) })) : null, _jsxs("span", { ...stylex.props(styles.liveStripMeta), children: [step.count, " ", pluralize(step.count, "step")] })] }), _jsxs("p", { "aria-live": "polite", title: step.detail, ...stylex.props(styles.liveStripNow), children: [_jsx("span", { ...stylex.props(styles.liveStripLabel), children: "Now:" }), " ", step.label, step.detail ? _jsx("code", { ...stylex.props(styles.liveStripDetail), children: step.detail }) : null] }), reported ? (_jsxs("p", { title: reported, ...stylex.props(styles.liveStripNow), children: [_jsx("span", { ...stylex.props(styles.liveStripLabel), children: "Progress:" }), " ", reported] })) : null] }));
|
|
1652
|
+
}
|
|
1653
|
+
/** The newest thing the running test did, read from the same live records as the transcript. */
|
|
1654
|
+
function currentStep(operations) {
|
|
1655
|
+
const count = operations
|
|
1656
|
+
.filter((operation) => operation.kind !== "evaluation")
|
|
1657
|
+
.flatMap((operation) => liveConversationTimeline(operation.data.filter(isRecord)))
|
|
1658
|
+
.filter((event) => event.kind === "tool").length;
|
|
1659
|
+
const active = [...operations].reverse().find((operation) => operation.status === "running");
|
|
1660
|
+
if (!active)
|
|
1661
|
+
return { label: "Preparing the next step", count };
|
|
1662
|
+
if (active.kind === "evaluation")
|
|
1663
|
+
return { kind: active.kind, label: `Judge ${active.name ?? ""} is reviewing`.trim(), count };
|
|
1664
|
+
const latest = liveConversationTimeline(active.data.filter(isRecord)).at(-1);
|
|
1665
|
+
if (latest?.kind === "tool") {
|
|
1666
|
+
const tool = toolPresentation(latest.tool);
|
|
1667
|
+
return { kind: active.kind, label: tool.title, detail: tool.detail || undefined, count };
|
|
1668
|
+
}
|
|
1669
|
+
const label = latest ? "Writing a reply" : "Waiting for the first agent event";
|
|
1670
|
+
return { kind: active.kind, label, count };
|
|
1671
|
+
}
|
|
1672
|
+
/** One line from whatever the test passed to reportProgress. */
|
|
1673
|
+
function progressSummary(data) {
|
|
1674
|
+
if (typeof data === "string")
|
|
1675
|
+
return data || undefined;
|
|
1676
|
+
if (!isRecord(data))
|
|
1677
|
+
return data === undefined || data === null ? undefined : String(data);
|
|
1678
|
+
const facts = Object.entries(data).map(([key, value]) => `${humanizeKey(key)}: ${formatConversationValue(value)}`);
|
|
1679
|
+
return facts.join(" · ") || undefined;
|
|
1531
1680
|
}
|
|
1532
1681
|
/** Test-level outcome first: every check with its result, including judge-decided checks. */
|
|
1533
1682
|
function TestVerdict({ attempt, criteria, operations, }) {
|
|
@@ -1538,7 +1687,7 @@ function TestVerdict({ attempt, criteria, operations, }) {
|
|
|
1538
1687
|
status: attempt.status,
|
|
1539
1688
|
errors: attempt.errors,
|
|
1540
1689
|
});
|
|
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) })] }),
|
|
1690
|
+
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) })] }), _jsx(VerdictMetrics, { operations: operations, reported: attempt.metrics }), _jsx(VerdictChecks, { presentations: presentations }), _jsx(UnassignedFailures, { errors: attempt.errors, presentations: presentations }), _jsx(VerdictAttachments, { attachments: attempt.attachments })] }));
|
|
1542
1691
|
}
|
|
1543
1692
|
const SHORT_CHECK_LIST = 3;
|
|
1544
1693
|
/** Checks needing attention always show; passing checks fold away once the list is long. */
|
|
@@ -1567,8 +1716,22 @@ const VERDICT_METRICS = [
|
|
|
1567
1716
|
format: (value) => String(Math.round(value)),
|
|
1568
1717
|
},
|
|
1569
1718
|
];
|
|
1719
|
+
/** `p95LatencyMs` → "P95 latency"; numbers keyed `…Ms` read as durations. */
|
|
1720
|
+
function reportedMetricRows(reported) {
|
|
1721
|
+
return Object.entries(reported ?? {}).map(([key, value]) => {
|
|
1722
|
+
const duration = key.endsWith("Ms");
|
|
1723
|
+
const words = (duration ? key.slice(0, -2) : key).replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
|
1724
|
+
return {
|
|
1725
|
+
key: `reported:${key}`,
|
|
1726
|
+
label: capitalize(words.toLowerCase()),
|
|
1727
|
+
value: duration
|
|
1728
|
+
? formatDuration(Math.round(value))
|
|
1729
|
+
: value.toLocaleString(undefined, { maximumFractionDigits: 2 }),
|
|
1730
|
+
};
|
|
1731
|
+
});
|
|
1732
|
+
}
|
|
1570
1733
|
/** Agent totals for the attempt; a median per run when the test made several agent runs. */
|
|
1571
|
-
function VerdictMetrics({ operations }) {
|
|
1734
|
+
function VerdictMetrics({ operations, reported, }) {
|
|
1572
1735
|
const runs = operations
|
|
1573
1736
|
.filter((operation) => operation.kind !== "evaluation")
|
|
1574
1737
|
.map((operation) => operationResult([operation]));
|
|
@@ -1579,10 +1742,17 @@ function VerdictMetrics({ operations }) {
|
|
|
1579
1742
|
const total = values.reduce((sum, value) => sum + value, 0);
|
|
1580
1743
|
return [{ ...metric, total, median: median(values) ?? total }];
|
|
1581
1744
|
});
|
|
1582
|
-
|
|
1745
|
+
const reportedRows = reportedMetricRows(reported);
|
|
1746
|
+
if (!rows.length && !reportedRows.length)
|
|
1583
1747
|
return null;
|
|
1584
1748
|
const repeated = runs.length > 1;
|
|
1585
|
-
return (
|
|
1749
|
+
return (_jsxs("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))), reportedRows.map((row) => (_jsxs("div", { ...stylex.props(styles.verdictMetric), children: [_jsx("dt", { ...stylex.props(styles.resourceFactLabel), children: row.label }), _jsx("dd", { ...stylex.props(styles.verdictMetricValue), children: row.value })] }, row.key)))] }));
|
|
1750
|
+
}
|
|
1751
|
+
/** Files the test attached with a path, opened from the viewer server. */
|
|
1752
|
+
function VerdictAttachments({ attachments }) {
|
|
1753
|
+
if (!attachments?.length)
|
|
1754
|
+
return null;
|
|
1755
|
+
return (_jsx("ul", { "aria-label": "Attachments", ...stylex.props(styles.verdictAttachments), children: attachments.map((attachment) => (_jsxs("li", { children: [_jsx("a", { href: attachment.url, target: "_blank", rel: "noreferrer", children: attachment.name }), _jsxs("span", { ...stylex.props(styles.verdictMetricMedian), children: [" ", attachment.contentType] })] }, attachment.url))) }));
|
|
1586
1756
|
}
|
|
1587
1757
|
function checkSummary(presentations, status) {
|
|
1588
1758
|
const total = presentations.length;
|
|
@@ -2069,12 +2239,12 @@ function JudgeAsked({ review }) {
|
|
|
2069
2239
|
return null;
|
|
2070
2240
|
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
2241
|
}
|
|
2072
|
-
/** Everything the test sent the judge, folded away
|
|
2242
|
+
/** Everything the test sent the judge, folded away unless a finding failed. */
|
|
2073
2243
|
function JudgeInputs({ review, operations, }) {
|
|
2074
2244
|
if (!review.context.length)
|
|
2075
2245
|
return null;
|
|
2076
|
-
// Inputs
|
|
2077
|
-
const inputsFirst = review.outcomes.
|
|
2246
|
+
// Inputs explain a failed finding; while the judge runs they only push the transcript away.
|
|
2247
|
+
const inputsFirst = review.outcomes.some((outcome) => !outcome.passed);
|
|
2078
2248
|
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 })] }) }));
|
|
2079
2249
|
}
|
|
2080
2250
|
function JudgeContext({ context, operations, }) {
|
|
@@ -2082,7 +2252,37 @@ function JudgeContext({ context, operations, }) {
|
|
|
2082
2252
|
const [selectedId, setSelectedId] = useState(answers[0]?.id);
|
|
2083
2253
|
const selected = answers.find((item) => item.id === selectedId) ?? answers[0];
|
|
2084
2254
|
const panelId = useId();
|
|
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("
|
|
2255
|
+
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("div", { ...stylex.props(styles.judgeContextList), children: supplied.map((item) => (_jsx(JudgeContextField, { item: item }, item.label))) })] })) : null] }));
|
|
2256
|
+
}
|
|
2257
|
+
/** One supplied judge input. A short value reads inline; anything larger folds behind its size. */
|
|
2258
|
+
function JudgeContextField({ item }) {
|
|
2259
|
+
if (isInlineValue(item.rawValue))
|
|
2260
|
+
return (_jsxs("div", { ...stylex.props(styles.judgeContextItem, styles.judgeContextFieldSummary), children: [_jsx("span", { ...stylex.props(styles.judgeContextLabel), children: item.label }), _jsx("span", { ...stylex.props(styles.judgeContextValue), children: item.value })] }));
|
|
2261
|
+
return (_jsxs("details", { "data-disclosure": true, ...stylex.props(styles.judgeContextItem), children: [_jsxs("summary", { ...stylex.props(styles.judgeContextFieldSummary), children: [_jsx("span", { ...stylex.props(styles.judgeContextLabel), children: item.label }), _jsx("span", { ...stylex.props(styles.judgeContextShape), children: describeShape(item.rawValue) })] }), _jsx(JudgeContextFieldValue, { item: item })] }));
|
|
2262
|
+
}
|
|
2263
|
+
function JudgeContextFieldValue({ item }) {
|
|
2264
|
+
const tools = judgeInputToolCalls(item.rawValue);
|
|
2265
|
+
if (tools)
|
|
2266
|
+
return (_jsx("div", { ...stylex.props(styles.judgeContextFieldBody), children: tools.map((tool, index) => (_jsx(ToolActivity, { tool: tool }, tool.seq ?? index))) }));
|
|
2267
|
+
if (isStringList(item.rawValue))
|
|
2268
|
+
return (_jsx("ul", { ...stylex.props(styles.judgeContextFieldBody, styles.judgeContextStringList), children: item.rawValue.map((value, index) => (_jsx("li", { children: value }, `${index}:${value}`))) }));
|
|
2269
|
+
return (_jsx("div", { ...stylex.props(styles.judgeContextFieldBody, styles.judgeContextValue), children: item.value }));
|
|
2270
|
+
}
|
|
2271
|
+
/** A judge input that is a list of agent tool calls reads like the transcript, not as JSON. */
|
|
2272
|
+
function judgeInputToolCalls(value) {
|
|
2273
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
2274
|
+
return undefined;
|
|
2275
|
+
const tools = toolCallsFromOperation({ trace: { toolCalls: value } });
|
|
2276
|
+
return tools.length === value.length ? tools : undefined;
|
|
2277
|
+
}
|
|
2278
|
+
const INLINE_VALUE_LENGTH = 120;
|
|
2279
|
+
function isInlineValue(value) {
|
|
2280
|
+
if (typeof value === "string")
|
|
2281
|
+
return value.length <= INLINE_VALUE_LENGTH && !value.includes("\n");
|
|
2282
|
+
return value === null || value === undefined || typeof value !== "object";
|
|
2283
|
+
}
|
|
2284
|
+
function isStringList(value) {
|
|
2285
|
+
return (Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === "string"));
|
|
2086
2286
|
}
|
|
2087
2287
|
function judgeContextGroups(context, operations) {
|
|
2088
2288
|
const candidates = operations
|