@post-print/agent-test 2.2.0 → 2.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- 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/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/stylex.css +24 -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 +193 -24
- 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",
|
|
@@ -1000,15 +1063,38 @@ const styles = stylex.create({
|
|
|
1000
1063
|
judgeContextList: { display: "grid", gap: "0.3rem", margin: 0 },
|
|
1001
1064
|
judgeContextItem: {
|
|
1002
1065
|
display: "grid",
|
|
1003
|
-
|
|
1004
|
-
default: "minmax(7.5rem, 0.3fr) minmax(0, 1fr)",
|
|
1005
|
-
"@media (max-width: 620px)": "1fr",
|
|
1006
|
-
},
|
|
1007
|
-
gap: "0.65rem",
|
|
1066
|
+
gap: "0.45rem",
|
|
1008
1067
|
padding: "0.4rem 0.5rem",
|
|
1009
1068
|
borderRadius: 6,
|
|
1010
1069
|
backgroundColor: "var(--bg)",
|
|
1011
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
|
+
},
|
|
1012
1098
|
judgeContextLabel: {
|
|
1013
1099
|
color: "var(--subtle)",
|
|
1014
1100
|
fontSize: "0.64rem",
|
|
@@ -1291,7 +1377,7 @@ function ViewerSidebarContent({ mode, open = true, headingId, navigationId, onTo
|
|
|
1291
1377
|
const history = useExecutionHistory();
|
|
1292
1378
|
const groups = groupTests(catalog.data?.tests ?? []);
|
|
1293
1379
|
const statuses = latestTestStatuses(history.data ?? []);
|
|
1294
|
-
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] }));
|
|
1295
1381
|
}
|
|
1296
1382
|
function SidebarHeader({ mode, open, count, headingId, navigationId, onToggle, onNavigate, }) {
|
|
1297
1383
|
const { workers } = useContext(WorkerContext);
|
|
@@ -1319,8 +1405,21 @@ function SidebarToggle({ mode, open, navigationId, onToggle, }) {
|
|
|
1319
1405
|
const mobile = mode === "mobile";
|
|
1320
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 ? "←" : "→" }));
|
|
1321
1407
|
}
|
|
1322
|
-
|
|
1323
|
-
|
|
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))) })] }));
|
|
1324
1423
|
}
|
|
1325
1424
|
function RunTestGroupButton({ name, tests, onNavigate, }) {
|
|
1326
1425
|
const { workers } = useContext(WorkerContext);
|
|
@@ -1336,8 +1435,8 @@ function RunTestGroupButton({ name, tests, onNavigate, }) {
|
|
|
1336
1435
|
});
|
|
1337
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 ? "●" : "▶" }));
|
|
1338
1437
|
}
|
|
1339
|
-
function TestLink({ test, status, onNavigate, }) {
|
|
1340
|
-
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] }));
|
|
1341
1440
|
}
|
|
1342
1441
|
function TestStatus({ status }) {
|
|
1343
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) }) }));
|
|
@@ -1350,9 +1449,9 @@ function TestPage() {
|
|
|
1350
1449
|
const test = catalog.data?.tests.find((item) => item.id === testId);
|
|
1351
1450
|
if (!test)
|
|
1352
1451
|
return (_jsx("p", { role: "alert", children: "This test is no longer available. Choose another test in the sidebar." }));
|
|
1353
|
-
return _jsx(TestDetail, { test: test }, test.id);
|
|
1452
|
+
return (_jsx(TestDetail, { test: test, intent: testIntent(test, catalog.data?.tests ?? []) }, test.id));
|
|
1354
1453
|
}
|
|
1355
|
-
function TestDetail({ test }) {
|
|
1454
|
+
function TestDetail({ test, intent }) {
|
|
1356
1455
|
const { execution, view } = testRoute.useSearch();
|
|
1357
1456
|
const history = useExecutionHistory();
|
|
1358
1457
|
const navigate = useNavigate();
|
|
@@ -1369,7 +1468,7 @@ function TestDetail({ test }) {
|
|
|
1369
1468
|
params: { testId: test.id },
|
|
1370
1469
|
search: { execution, view: next === "current" ? undefined : next },
|
|
1371
1470
|
});
|
|
1372
|
-
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 })] }));
|
|
1373
1472
|
}
|
|
1374
1473
|
/** Return path to the run inbox when this test was opened from a batch run. */
|
|
1375
1474
|
function BatchRunLink({ run }) {
|
|
@@ -1378,8 +1477,14 @@ function BatchRunLink({ run }) {
|
|
|
1378
1477
|
return null;
|
|
1379
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"] }));
|
|
1380
1479
|
}
|
|
1381
|
-
function TestHeader({ test, onStarted }) {
|
|
1382
|
-
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))) })] }));
|
|
1383
1488
|
}
|
|
1384
1489
|
const TEST_VIEWS = [
|
|
1385
1490
|
{ id: "current", label: "Current run" },
|
|
@@ -1501,7 +1606,7 @@ function ExecutionDetailView({ execution, criteria = [], }) {
|
|
|
1501
1606
|
});
|
|
1502
1607
|
const stopping = execution.status === "running" && (cancel.isPending || cancel.isSuccess);
|
|
1503
1608
|
const stopLabel = (execution.testIds?.length ?? 0) > 1 ? "Stop suite run" : "Stop this run";
|
|
1504
|
-
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] })] }));
|
|
1505
1610
|
}
|
|
1506
1611
|
function emptyExecutionMessage(execution, stopping) {
|
|
1507
1612
|
if (stopping)
|
|
@@ -1533,11 +1638,45 @@ function ComparisonColumn({ execution, label }) {
|
|
|
1533
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)))] }));
|
|
1534
1639
|
}
|
|
1535
1640
|
/** `showTitle` is off on a test page, whose header already names the test. */
|
|
1536
|
-
function AttemptCard({ attempt, criteria = [], showTitle = true, }) {
|
|
1641
|
+
function AttemptCard({ attempt, criteria = [], showTitle = true, progress = [], }) {
|
|
1537
1642
|
const { operations, output, title } = attemptPresentation(attempt, criteria);
|
|
1538
1643
|
const agents = operations.filter((operation) => operation.kind !== "evaluation");
|
|
1539
1644
|
const judges = operations.filter((operation) => operation.kind === "evaluation");
|
|
1540
|
-
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;
|
|
1541
1680
|
}
|
|
1542
1681
|
/** Test-level outcome first: every check with its result, including judge-decided checks. */
|
|
1543
1682
|
function TestVerdict({ attempt, criteria, operations, }) {
|
|
@@ -1548,7 +1687,7 @@ function TestVerdict({ attempt, criteria, operations, }) {
|
|
|
1548
1687
|
status: attempt.status,
|
|
1549
1688
|
errors: attempt.errors,
|
|
1550
1689
|
});
|
|
1551
|
-
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 })] }));
|
|
1552
1691
|
}
|
|
1553
1692
|
const SHORT_CHECK_LIST = 3;
|
|
1554
1693
|
/** Checks needing attention always show; passing checks fold away once the list is long. */
|
|
@@ -2100,12 +2239,12 @@ function JudgeAsked({ review }) {
|
|
|
2100
2239
|
return null;
|
|
2101
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 })] }) }));
|
|
2102
2241
|
}
|
|
2103
|
-
/** Everything the test sent the judge, folded away
|
|
2242
|
+
/** Everything the test sent the judge, folded away unless a finding failed. */
|
|
2104
2243
|
function JudgeInputs({ review, operations, }) {
|
|
2105
2244
|
if (!review.context.length)
|
|
2106
2245
|
return null;
|
|
2107
|
-
// Inputs
|
|
2108
|
-
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);
|
|
2109
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 })] }) }));
|
|
2110
2249
|
}
|
|
2111
2250
|
function JudgeContext({ context, operations, }) {
|
|
@@ -2113,7 +2252,37 @@ function JudgeContext({ context, operations, }) {
|
|
|
2113
2252
|
const [selectedId, setSelectedId] = useState(answers[0]?.id);
|
|
2114
2253
|
const selected = answers.find((item) => item.id === selectedId) ?? answers[0];
|
|
2115
2254
|
const panelId = useId();
|
|
2116
|
-
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"));
|
|
2117
2286
|
}
|
|
2118
2287
|
function judgeContextGroups(context, operations) {
|
|
2119
2288
|
const candidates = operations
|