@post-print/agent-test 0.3.3 → 0.3.5
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 +126 -157
- package/dist/check.d.ts +38 -0
- package/dist/check.d.ts.map +1 -0
- package/dist/check.js +169 -0
- package/dist/check.js.map +1 -0
- package/dist/cli.d.ts +12 -14
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +342 -366
- package/dist/cli.js.map +1 -1
- package/dist/compare-scenario.d.ts +33 -0
- package/dist/compare-scenario.d.ts.map +1 -0
- package/dist/compare-scenario.js +128 -0
- package/dist/compare-scenario.js.map +1 -0
- package/dist/debug-bundle.d.ts +2 -2
- package/dist/debug-bundle.d.ts.map +1 -1
- package/dist/debug-bundle.js +12 -12
- package/dist/debug-bundle.js.map +1 -1
- package/dist/doctor.d.ts +6 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +136 -14
- package/dist/doctor.js.map +1 -1
- package/dist/expect.d.ts +6 -5
- package/dist/expect.d.ts.map +1 -1
- package/dist/expect.js +67 -57
- package/dist/expect.js.map +1 -1
- package/dist/host-log.d.ts +17 -0
- package/dist/host-log.d.ts.map +1 -0
- package/dist/host-log.js +154 -0
- package/dist/host-log.js.map +1 -0
- package/dist/hosts.d.ts +22 -0
- package/dist/hosts.d.ts.map +1 -0
- package/dist/hosts.js +84 -0
- package/dist/hosts.js.map +1 -0
- package/dist/html-report.d.ts +0 -13
- package/dist/html-report.d.ts.map +1 -1
- package/dist/html-report.js +381 -318
- package/dist/html-report.js.map +1 -1
- package/dist/index.d.ts +15 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -7
- package/dist/index.js.map +1 -1
- package/dist/live-isolation.d.ts +11 -5
- package/dist/live-isolation.d.ts.map +1 -1
- package/dist/live-isolation.js +32 -14
- package/dist/live-isolation.js.map +1 -1
- package/dist/load-adapters.d.ts +13 -0
- package/dist/load-adapters.d.ts.map +1 -0
- package/dist/load-adapters.js +76 -0
- package/dist/load-adapters.js.map +1 -0
- package/dist/load-suite.d.ts.map +1 -1
- package/dist/load-suite.js +16 -0
- package/dist/load-suite.js.map +1 -1
- package/dist/mcp-config.d.ts +8 -0
- package/dist/mcp-config.d.ts.map +1 -1
- package/dist/mcp-config.js +46 -0
- package/dist/mcp-config.js.map +1 -1
- package/dist/preflight.d.ts +2 -2
- package/dist/preflight.d.ts.map +1 -1
- package/dist/preflight.js +4 -4
- package/dist/preflight.js.map +1 -1
- package/dist/progress.d.ts +7 -0
- package/dist/progress.d.ts.map +1 -1
- package/dist/progress.js +39 -9
- package/dist/progress.js.map +1 -1
- package/dist/record-trace.d.ts +11 -20
- package/dist/record-trace.d.ts.map +1 -1
- package/dist/record-trace.js +10 -57
- package/dist/record-trace.js.map +1 -1
- package/dist/report-preview-server.d.ts +2 -0
- package/dist/report-preview-server.d.ts.map +1 -0
- package/dist/report-preview-server.js +24 -0
- package/dist/report-preview-server.js.map +1 -0
- package/dist/report-preview.d.ts +11 -0
- package/dist/report-preview.d.ts.map +1 -0
- package/dist/report-preview.js +161 -0
- package/dist/report-preview.js.map +1 -0
- package/dist/run-suite.d.ts +62 -11
- package/dist/run-suite.d.ts.map +1 -1
- package/dist/run-suite.js +697 -206
- package/dist/run-suite.js.map +1 -1
- package/dist/scenario-story.d.ts +41 -0
- package/dist/scenario-story.d.ts.map +1 -0
- package/dist/scenario-story.js +198 -0
- package/dist/scenario-story.js.map +1 -0
- package/dist/suite-summary.d.ts +2 -0
- package/dist/suite-summary.d.ts.map +1 -1
- package/dist/suite-summary.js +70 -22
- package/dist/suite-summary.js.map +1 -1
- package/dist/theme.d.ts +29 -5
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +204 -45
- package/dist/theme.js.map +1 -1
- package/dist/types.d.ts +107 -24
- package/dist/types.d.ts.map +1 -1
- package/dist/validate-seeds.d.ts.map +1 -1
- package/dist/validate-seeds.js +21 -6
- package/dist/validate-seeds.js.map +1 -1
- package/dist/validate-suite.d.ts +3 -1
- package/dist/validate-suite.d.ts.map +1 -1
- package/dist/validate-suite.js +256 -22
- package/dist/validate-suite.js.map +1 -1
- package/package.json +5 -5
- package/dist/compare.d.ts +0 -79
- package/dist/compare.d.ts.map +0 -1
- package/dist/compare.js +0 -232
- package/dist/compare.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,42 +1,107 @@
|
|
|
1
1
|
#!/usr/bin/env -S node --disable-warning=ExperimentalWarning
|
|
2
|
-
import { access, mkdir, writeFile } from "node:fs/promises";
|
|
3
2
|
import { join, resolve } from "node:path";
|
|
4
3
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import {
|
|
4
|
+
import { cleanupStaleScenarioWorktrees, isPathUnderRoot, knownAgentHosts, missingClassifierAuth, } from "@post-print/agent-harness";
|
|
5
|
+
import { formatCheckReport, formatCheckSummary, missingHostsAuth, runCheck } from "./check.js";
|
|
6
6
|
import { isCliMain } from "./cli-entry.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
7
|
+
import { missingAgentAuth } from "./doctor.js";
|
|
8
|
+
import { installHostLogFilter } from "./host-log.js";
|
|
9
|
+
import { parseHostList, uniqueHosts } from "./hosts.js";
|
|
10
10
|
import { writeHtmlReport } from "./html-report.js";
|
|
11
|
-
import {
|
|
11
|
+
import { loadHostAdapters } from "./load-adapters.js";
|
|
12
|
+
import { assertDirectAgentPreflight } from "./preflight.js";
|
|
12
13
|
import { logProgress } from "./progress.js";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
14
|
+
import { cleanupStagingSession, createLiveStagingSessionId, getLiveStagingSessionRoot, setLiveStagingRootOverride, } from "./record-trace.js";
|
|
15
|
+
import { shouldStartReportPreview, startDetachedReportPreview } from "./report-preview.js";
|
|
16
|
+
import { registerLiveRunHandlers, runAllSuites, selectedRunNeedsJudge } from "./run-suite.js";
|
|
15
17
|
import { formatRunSummary, shouldFailScenario, summarizeReports, } from "./suite-summary.js";
|
|
16
18
|
import { configureCliColor, theme } from "./theme.js";
|
|
17
|
-
import { formatSeedValidationReport, validateSeedPatches } from "./validate-seeds.js";
|
|
18
|
-
import { formatValidationReport, validateSuitePaths } from "./validate-suite.js";
|
|
19
19
|
import { suppressNoisyRuntimeWarnings } from "./warnings.js";
|
|
20
20
|
suppressNoisyRuntimeWarnings();
|
|
21
21
|
configureCliColor();
|
|
22
|
+
function splitArgvFlag(token) {
|
|
23
|
+
if (!token.startsWith("-") || token === "-") {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
if (token.startsWith("--")) {
|
|
27
|
+
const eq = token.indexOf("=");
|
|
28
|
+
if (eq === -1) {
|
|
29
|
+
return { flag: token };
|
|
30
|
+
}
|
|
31
|
+
return { flag: token.slice(0, eq), inline: token.slice(eq + 1) };
|
|
32
|
+
}
|
|
33
|
+
return { flag: token };
|
|
34
|
+
}
|
|
35
|
+
function readFlagValue(flag, inline, argv, index) {
|
|
36
|
+
if (inline !== undefined) {
|
|
37
|
+
if (inline.length === 0) {
|
|
38
|
+
throw new Error(`${flag} requires a value`);
|
|
39
|
+
}
|
|
40
|
+
return { value: inline, nextIndex: index };
|
|
41
|
+
}
|
|
42
|
+
const next = argv[index + 1];
|
|
43
|
+
if (!next || next.startsWith("--") || /^-[A-Za-z]/.test(next)) {
|
|
44
|
+
throw new Error(`${flag} requires a value`);
|
|
45
|
+
}
|
|
46
|
+
return { value: next, nextIndex: index + 1 };
|
|
47
|
+
}
|
|
48
|
+
function enableCheckMode(target) {
|
|
49
|
+
target.check = true;
|
|
50
|
+
target.validateOnly = true;
|
|
51
|
+
target.validatePaths = true;
|
|
52
|
+
target.validateSeeds = true;
|
|
53
|
+
}
|
|
54
|
+
/** Usage text for `--help`. */
|
|
55
|
+
export function formatHelp() {
|
|
56
|
+
return [
|
|
57
|
+
"agent-test [options] [suite]",
|
|
58
|
+
"",
|
|
59
|
+
"Launch a host agent and score the transcript.",
|
|
60
|
+
"A run checks the suite and host first.",
|
|
61
|
+
"",
|
|
62
|
+
" --check Check the suite and host. Do not launch an agent.",
|
|
63
|
+
" --help, -h Print this help.",
|
|
64
|
+
"",
|
|
65
|
+
" --host cursor|claude|openai|all Host adapter (default: suite or cursor).",
|
|
66
|
+
" Repeat or comma-separate for a matrix.",
|
|
67
|
+
" --adapter <module> Load a consumer host adapter module.",
|
|
68
|
+
" --suites-dir <path> Suite root (default: agent-suites)",
|
|
69
|
+
" --suite <name> Run one suite",
|
|
70
|
+
" --scenario <name> Run one scenario",
|
|
71
|
+
" --no-judge Skip the judge",
|
|
72
|
+
" --fail-on all|behavior|infra-only",
|
|
73
|
+
" --allow-user-input Let a user agent answer AskQuestion tools",
|
|
74
|
+
" --timeout-ms <n> Agent deadline",
|
|
75
|
+
" --no-timeout Disable the deadline",
|
|
76
|
+
" --scenario-retries <n> Announce-stop retries",
|
|
77
|
+
" --debug Keep recordings and write a debug bundle",
|
|
78
|
+
" --debug-dir <path> Debug session parent directory",
|
|
79
|
+
" --report-out <path> HTML report file or directory",
|
|
80
|
+
" --no-html-report Skip the HTML report",
|
|
81
|
+
" --no-worktree Run in the caller checkout (needs AGENT_TEST_ALLOW_IN_PLACE=1)",
|
|
82
|
+
"",
|
|
83
|
+
"--doctor, --validate-only, --validate-paths, and --validate-seeds run --check.",
|
|
84
|
+
].join("\n");
|
|
85
|
+
}
|
|
22
86
|
/** Parse agent-test CLI argv (exported for unit tests). */
|
|
23
87
|
export function parseCliArgs(argv) {
|
|
24
88
|
const cwd = process.cwd();
|
|
25
89
|
let suitesDir = "agent-suites";
|
|
26
90
|
let rubricsDir;
|
|
27
|
-
|
|
91
|
+
const parsedHosts = [];
|
|
92
|
+
let hostAll = false;
|
|
93
|
+
const adapterModules = [];
|
|
28
94
|
let filter;
|
|
29
95
|
let scenarioFilter;
|
|
30
96
|
let stagingSessionId;
|
|
31
|
-
let record = false;
|
|
32
|
-
let recordFixtures = false;
|
|
33
|
-
let live = false;
|
|
34
97
|
let judge;
|
|
35
98
|
let worktree;
|
|
36
99
|
let keepRecordings = false;
|
|
37
100
|
let timeoutMs;
|
|
38
101
|
let noTimeout = false;
|
|
39
102
|
let allowUserInput = false;
|
|
103
|
+
let check = false;
|
|
104
|
+
let help = false;
|
|
40
105
|
let doctor = false;
|
|
41
106
|
let htmlReport = true;
|
|
42
107
|
let reportOut;
|
|
@@ -47,141 +112,164 @@ export function parseCliArgs(argv) {
|
|
|
47
112
|
let validatePaths = false;
|
|
48
113
|
let failOn = "all";
|
|
49
114
|
let scenarioRetries;
|
|
50
|
-
let compareMode = false;
|
|
51
|
-
let compareA;
|
|
52
|
-
let compareB;
|
|
53
|
-
let comparePairs;
|
|
54
|
-
let compareOutDir;
|
|
55
|
-
const startIndex = argv[2] === "compare" ? 3 : 2;
|
|
56
115
|
if (argv[2] === "compare") {
|
|
57
|
-
|
|
116
|
+
throw new Error("The compare subcommand is removed. Add compare.a and compare.b on one scenario. Set compare.faster or compare.cheaper when one arm must win on time or tokens. Add rubric.judge only when you want a pairwise judge.");
|
|
58
117
|
}
|
|
118
|
+
const startIndex = 2;
|
|
119
|
+
const checkMode = {
|
|
120
|
+
check: false,
|
|
121
|
+
validateOnly: false,
|
|
122
|
+
validatePaths: false,
|
|
123
|
+
validateSeeds: false,
|
|
124
|
+
};
|
|
59
125
|
for (let i = startIndex; i < argv.length; i++) {
|
|
60
126
|
const token = argv[i];
|
|
61
|
-
if (token
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
else if (token === "--suites-dir" && argv[i + 1]) {
|
|
65
|
-
suitesDir = argv[++i];
|
|
66
|
-
}
|
|
67
|
-
else if (token === "--rubrics-dir" && argv[i + 1]) {
|
|
68
|
-
rubricsDir = argv[++i];
|
|
69
|
-
}
|
|
70
|
-
else if (token === "--suite" && argv[i + 1]) {
|
|
71
|
-
filter = argv[++i];
|
|
72
|
-
}
|
|
73
|
-
else if (token === "--scenario" && argv[i + 1]) {
|
|
74
|
-
scenarioFilter = argv[++i];
|
|
75
|
-
}
|
|
76
|
-
else if (token === "--staging-session-id" && argv[i + 1]) {
|
|
77
|
-
stagingSessionId = argv[++i];
|
|
78
|
-
}
|
|
79
|
-
else if (token === "--record") {
|
|
80
|
-
record = true;
|
|
81
|
-
}
|
|
82
|
-
else if (token === "--record-fixtures") {
|
|
83
|
-
record = true;
|
|
84
|
-
recordFixtures = true;
|
|
127
|
+
if (!token) {
|
|
128
|
+
continue;
|
|
85
129
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
130
|
+
const parsed = splitArgvFlag(token);
|
|
131
|
+
if (!parsed) {
|
|
132
|
+
filter = token;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const { flag, inline } = parsed;
|
|
136
|
+
const read = () => {
|
|
137
|
+
const result = readFlagValue(flag, inline, argv, i);
|
|
138
|
+
i = result.nextIndex;
|
|
139
|
+
return result.value;
|
|
140
|
+
};
|
|
141
|
+
switch (flag) {
|
|
142
|
+
case "--help":
|
|
143
|
+
case "-h":
|
|
144
|
+
help = true;
|
|
145
|
+
break;
|
|
146
|
+
case "--check":
|
|
147
|
+
enableCheckMode(checkMode);
|
|
148
|
+
break;
|
|
149
|
+
case "--doctor":
|
|
150
|
+
doctor = true;
|
|
151
|
+
enableCheckMode(checkMode);
|
|
152
|
+
break;
|
|
153
|
+
case "--validate-only":
|
|
154
|
+
enableCheckMode(checkMode);
|
|
155
|
+
break;
|
|
156
|
+
case "--validate-seeds":
|
|
157
|
+
enableCheckMode(checkMode);
|
|
158
|
+
break;
|
|
159
|
+
case "--validate-paths":
|
|
160
|
+
enableCheckMode(checkMode);
|
|
161
|
+
break;
|
|
162
|
+
case "--host": {
|
|
163
|
+
const value = read();
|
|
164
|
+
if (value
|
|
165
|
+
.split(",")
|
|
166
|
+
.map((token) => token.trim().toLowerCase())
|
|
167
|
+
.includes("all")) {
|
|
168
|
+
hostAll = true;
|
|
169
|
+
}
|
|
170
|
+
parsedHosts.push(...parseHostList(value));
|
|
171
|
+
break;
|
|
105
172
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
173
|
+
case "--adapter":
|
|
174
|
+
adapterModules.push(resolve(cwd, read()));
|
|
175
|
+
break;
|
|
176
|
+
case "--suites-dir":
|
|
177
|
+
suitesDir = read();
|
|
178
|
+
break;
|
|
179
|
+
case "--rubrics-dir":
|
|
180
|
+
rubricsDir = read();
|
|
181
|
+
break;
|
|
182
|
+
case "--suite":
|
|
183
|
+
filter = read();
|
|
184
|
+
break;
|
|
185
|
+
case "--scenario":
|
|
186
|
+
scenarioFilter = read();
|
|
187
|
+
break;
|
|
188
|
+
case "--staging-session-id":
|
|
189
|
+
stagingSessionId = read();
|
|
190
|
+
break;
|
|
191
|
+
case "--live":
|
|
192
|
+
throw new Error("--live was removed because agent-test now always runs a real agent");
|
|
193
|
+
case "--record":
|
|
194
|
+
throw new Error("--record was removed; direct runs capture transient traces automatically (use --keep-recordings to retain them)");
|
|
195
|
+
case "--record-fixtures":
|
|
196
|
+
throw new Error("--record-fixtures was removed because replay-based testing is deprecated and no longer supported");
|
|
197
|
+
case "--keep-recordings":
|
|
198
|
+
keepRecordings = true;
|
|
199
|
+
break;
|
|
200
|
+
case "--judge":
|
|
201
|
+
judge = true;
|
|
202
|
+
break;
|
|
203
|
+
case "--no-judge":
|
|
204
|
+
judge = false;
|
|
205
|
+
break;
|
|
206
|
+
case "--no-worktree":
|
|
207
|
+
worktree = false;
|
|
208
|
+
break;
|
|
209
|
+
case "--timeout-ms": {
|
|
210
|
+
const parsedTimeout = Number(read());
|
|
211
|
+
if (Number.isFinite(parsedTimeout) && parsedTimeout > 0) {
|
|
212
|
+
timeoutMs = parsedTimeout;
|
|
213
|
+
}
|
|
214
|
+
break;
|
|
129
215
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
216
|
+
case "--no-timeout":
|
|
217
|
+
noTimeout = true;
|
|
218
|
+
break;
|
|
219
|
+
case "--allow-user-input":
|
|
220
|
+
allowUserInput = true;
|
|
221
|
+
break;
|
|
222
|
+
case "--fail-on": {
|
|
223
|
+
const mode = read();
|
|
224
|
+
if (mode !== "all" && mode !== "behavior" && mode !== "infra-only") {
|
|
225
|
+
throw new Error("--fail-on must be all|behavior|infra-only");
|
|
226
|
+
}
|
|
227
|
+
failOn = mode;
|
|
228
|
+
break;
|
|
136
229
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
compareMode = true;
|
|
145
|
-
}
|
|
146
|
-
else if ((token === "--b" || token === "--compare-b") && argv[i + 1]) {
|
|
147
|
-
compareB = argv[++i];
|
|
148
|
-
compareMode = true;
|
|
149
|
-
}
|
|
150
|
-
else if ((token === "--out-dir" || token === "--compare-out") && argv[i + 1]) {
|
|
151
|
-
compareOutDir = argv[++i];
|
|
152
|
-
}
|
|
153
|
-
else if (token === "--report-out" && argv[i + 1]) {
|
|
154
|
-
reportOut = argv[++i];
|
|
155
|
-
}
|
|
156
|
-
else if (token === "--no-html-report") {
|
|
157
|
-
htmlReport = false;
|
|
158
|
-
}
|
|
159
|
-
else if (token === "--debug") {
|
|
160
|
-
debug = true;
|
|
161
|
-
}
|
|
162
|
-
else if (token === "--debug-dir") {
|
|
163
|
-
const value = argv[++i];
|
|
164
|
-
if (!value || value.startsWith("-")) {
|
|
165
|
-
throw new Error("--debug-dir requires a non-empty path argument");
|
|
230
|
+
case "--scenario-retries": {
|
|
231
|
+
const parsedRetries = Number(read());
|
|
232
|
+
if (!Number.isInteger(parsedRetries) || parsedRetries < 0) {
|
|
233
|
+
throw new Error("--scenario-retries must be an integer >= 0");
|
|
234
|
+
}
|
|
235
|
+
scenarioRetries = parsedRetries;
|
|
236
|
+
break;
|
|
166
237
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
238
|
+
case "--compare-pairs":
|
|
239
|
+
case "--a":
|
|
240
|
+
case "--b":
|
|
241
|
+
case "--compare-a":
|
|
242
|
+
case "--compare-b":
|
|
243
|
+
case "--out-dir":
|
|
244
|
+
case "--compare-out":
|
|
245
|
+
throw new Error(`${flag} is removed. Add compare.a and compare.b on one scenario. Set compare.faster or compare.cheaper when one arm must win on time or tokens. Add rubric.judge only when you want a pairwise judge.`);
|
|
246
|
+
case "--report-out":
|
|
247
|
+
reportOut = read();
|
|
248
|
+
break;
|
|
249
|
+
case "--no-html-report":
|
|
250
|
+
htmlReport = false;
|
|
251
|
+
break;
|
|
252
|
+
case "--debug":
|
|
253
|
+
debug = true;
|
|
254
|
+
break;
|
|
255
|
+
case "--debug-dir": {
|
|
256
|
+
const value = read();
|
|
257
|
+
debugDir = value;
|
|
258
|
+
debug = true;
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
default:
|
|
262
|
+
throw new Error(`Unknown flag: ${flag}`);
|
|
177
263
|
}
|
|
178
264
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
265
|
+
check = checkMode.check;
|
|
266
|
+
validateOnly = checkMode.validateOnly;
|
|
267
|
+
validatePaths = checkMode.validatePaths;
|
|
268
|
+
validateSeeds = checkMode.validateSeeds;
|
|
269
|
+
const hosts = uniqueHosts(parsedHosts);
|
|
270
|
+
const host = hosts?.[0];
|
|
271
|
+
judge = judge ?? true;
|
|
272
|
+
worktree = worktree ?? true;
|
|
185
273
|
if (debug) {
|
|
186
274
|
keepRecordings = true;
|
|
187
275
|
process.env.AGENT_TEST_DEBUG = "1";
|
|
@@ -193,18 +281,20 @@ export function parseCliArgs(argv) {
|
|
|
193
281
|
suitesDir,
|
|
194
282
|
rubricsDir,
|
|
195
283
|
host,
|
|
284
|
+
hosts,
|
|
285
|
+
hostAll,
|
|
286
|
+
adapterModules: adapterModules.length > 0 ? adapterModules : undefined,
|
|
196
287
|
filter,
|
|
197
288
|
scenarioFilter,
|
|
198
289
|
stagingSessionId,
|
|
199
|
-
record,
|
|
200
|
-
recordFixtures,
|
|
201
|
-
live,
|
|
202
290
|
judge,
|
|
203
291
|
worktree,
|
|
204
292
|
keepRecordings,
|
|
205
293
|
timeoutMs: noTimeout ? 0 : timeoutMs,
|
|
206
294
|
noTimeout,
|
|
207
295
|
allowUserInput,
|
|
296
|
+
check,
|
|
297
|
+
help,
|
|
208
298
|
doctor,
|
|
209
299
|
htmlReport,
|
|
210
300
|
reportOut: reportOut ? resolve(cwd, reportOut) : undefined,
|
|
@@ -215,70 +305,9 @@ export function parseCliArgs(argv) {
|
|
|
215
305
|
validatePaths,
|
|
216
306
|
failOn,
|
|
217
307
|
scenarioRetries,
|
|
218
|
-
compareMode,
|
|
219
|
-
compareA,
|
|
220
|
-
compareB,
|
|
221
|
-
comparePairs,
|
|
222
|
-
compareOutDir: compareOutDir ? resolve(cwd, compareOutDir) : undefined,
|
|
223
308
|
};
|
|
224
309
|
}
|
|
225
|
-
|
|
226
|
-
try {
|
|
227
|
-
await access(path);
|
|
228
|
-
return true;
|
|
229
|
-
}
|
|
230
|
-
catch {
|
|
231
|
-
return false;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
async function resolveSuiteScenariosPath(cwd, suitesDir, side) {
|
|
235
|
-
const candidates = [
|
|
236
|
-
resolve(cwd, side, "scenarios.json"),
|
|
237
|
-
resolve(cwd, suitesDir, side, "scenarios.json"),
|
|
238
|
-
resolve(side, "scenarios.json"),
|
|
239
|
-
];
|
|
240
|
-
for (const candidate of candidates) {
|
|
241
|
-
if (await pathExists(candidate)) {
|
|
242
|
-
return candidate;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
throw new Error(`compare side "${side}" is not a suite report JSON or suite dir (looked under ${suitesDir}/ and cwd)`);
|
|
246
|
-
}
|
|
247
|
-
async function loadOrRunCompareSide(args, side, stagingSessionId) {
|
|
248
|
-
const resolved = resolve(args.cwd, side);
|
|
249
|
-
if (side.endsWith(".json") || resolved.endsWith(".json")) {
|
|
250
|
-
const jsonPath = side.endsWith(".json") ? resolved : resolve(args.cwd, `${side}.json`);
|
|
251
|
-
const path = (await pathExists(resolved)) ? resolved : jsonPath;
|
|
252
|
-
if (!(await pathExists(path))) {
|
|
253
|
-
throw new Error(`compare report not found: ${side}`);
|
|
254
|
-
}
|
|
255
|
-
return loadSuiteRunReport(path);
|
|
256
|
-
}
|
|
257
|
-
const suitePath = await resolveSuiteScenariosPath(args.cwd, args.suitesDir, side);
|
|
258
|
-
return runSuite({
|
|
259
|
-
cwd: args.cwd,
|
|
260
|
-
suitePath,
|
|
261
|
-
host: args.host,
|
|
262
|
-
scenarioFilter: args.scenarioFilter,
|
|
263
|
-
record: args.record,
|
|
264
|
-
recordFixtures: args.recordFixtures,
|
|
265
|
-
judge: args.judge,
|
|
266
|
-
worktree: args.worktree,
|
|
267
|
-
stagingSessionId,
|
|
268
|
-
keepRecordings: args.keepRecordings,
|
|
269
|
-
suitesDir: args.suitesDir,
|
|
270
|
-
rubricsDir: args.rubricsDir,
|
|
271
|
-
timeoutMs: args.timeoutMs,
|
|
272
|
-
allowUserInput: args.allowUserInput,
|
|
273
|
-
debug: args.debug,
|
|
274
|
-
debugDir: args.debugDir,
|
|
275
|
-
scenarioRetries: args.scenarioRetries,
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
|
-
* Split `--report-out` into an HTML file path and, when a directory was given,
|
|
280
|
-
* the directory every other report artifact is written to.
|
|
281
|
-
*/
|
|
310
|
+
/** Resolve an explicit report target into the HTML path and optional artifact directory. */
|
|
282
311
|
export function resolveReportOutput(reportOut) {
|
|
283
312
|
if (!reportOut) {
|
|
284
313
|
return {};
|
|
@@ -288,13 +317,16 @@ export function resolveReportOutput(reportOut) {
|
|
|
288
317
|
}
|
|
289
318
|
return { htmlPath: join(reportOut, "report.html"), outDir: reportOut };
|
|
290
319
|
}
|
|
291
|
-
async function
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
320
|
+
async function htmlReportTip(label, reportPath) {
|
|
321
|
+
if (shouldStartReportPreview()) {
|
|
322
|
+
const url = await startDetachedReportPreview(reportPath);
|
|
323
|
+
if (url) {
|
|
324
|
+
return theme.fileTip(label, url);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return theme.fileTip(label, reportPath);
|
|
296
328
|
}
|
|
297
|
-
async function
|
|
329
|
+
async function cleanupRunArtifacts(stagingSessionRoot, keepRecordings) {
|
|
298
330
|
if (keepRecordings) {
|
|
299
331
|
return;
|
|
300
332
|
}
|
|
@@ -306,123 +338,116 @@ async function cleanupLiveRunArtifacts(cwd, stagingSessionRoot, keepRecordings)
|
|
|
306
338
|
// best-effort
|
|
307
339
|
}
|
|
308
340
|
}
|
|
309
|
-
const legacyRemoved = await cleanupLegacyRepoRecordings(cwd);
|
|
310
|
-
if (legacyRemoved.length > 0) {
|
|
311
|
-
console.log(`Removed legacy in-repo recording dir(s):\n ${legacyRemoved.join("\n ")}`);
|
|
312
|
-
}
|
|
313
341
|
}
|
|
314
342
|
async function main() {
|
|
315
343
|
let args;
|
|
316
344
|
try {
|
|
317
345
|
args = parseCliArgs(process.argv);
|
|
346
|
+
installHostLogFilter();
|
|
318
347
|
}
|
|
319
348
|
catch (error) {
|
|
320
349
|
console.error(error instanceof Error ? error.message : error);
|
|
321
350
|
return 1;
|
|
322
351
|
}
|
|
323
|
-
if (args.
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
352
|
+
if (args.help) {
|
|
353
|
+
console.log(formatHelp());
|
|
354
|
+
return 0;
|
|
355
|
+
}
|
|
356
|
+
try {
|
|
357
|
+
await loadHostAdapters({ cwd: args.cwd, adapterModules: args.adapterModules });
|
|
358
|
+
if (args.hostAll) {
|
|
359
|
+
args.hosts = uniqueHosts(knownAgentHosts());
|
|
360
|
+
args.host = args.hosts?.[0];
|
|
327
361
|
}
|
|
328
|
-
return report.ok ? 0 : 1;
|
|
329
362
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
const aReport = await loadSuiteRunReport(aPath);
|
|
334
|
-
const bReport = await loadSuiteRunReport(bPath);
|
|
335
|
-
const outDir = args.compareOutDir ??
|
|
336
|
-
resolveReportOutput(args.reportOut).outDir ??
|
|
337
|
-
resolve(args.cwd, "compare-out");
|
|
338
|
-
const compare = compareSuiteReports({
|
|
339
|
-
aLabel: labelForCompareSide(aPath),
|
|
340
|
-
bLabel: labelForCompareSide(bPath),
|
|
341
|
-
a: aReport,
|
|
342
|
-
b: bReport,
|
|
343
|
-
});
|
|
344
|
-
const written = await writeCompareReport({ outDir, report: compare });
|
|
345
|
-
console.log(theme.tip(`compare JSON: ${written.jsonPath}`));
|
|
346
|
-
console.log(theme.tip(`compare markdown: ${written.markdownPath}`));
|
|
347
|
-
console.log(theme.fileTip("compare HTML", written.htmlPath));
|
|
348
|
-
return compare.summary.passRegressions > 0 ? 1 : 0;
|
|
363
|
+
catch (error) {
|
|
364
|
+
console.error(error instanceof Error ? error.message : error);
|
|
365
|
+
return 1;
|
|
349
366
|
}
|
|
350
|
-
if (args.validateOnly || args.validateSeeds) {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
console.log(formatValidationReport(report));
|
|
362
|
-
if (!report.ok) {
|
|
363
|
-
return 1;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
if (args.validateSeeds) {
|
|
367
|
-
const report = await validateSeedPatches({
|
|
368
|
-
cwd: args.cwd,
|
|
369
|
-
suitesDir: args.suitesDir,
|
|
370
|
-
rubricsDir: args.rubricsDir,
|
|
371
|
-
filter: args.filter,
|
|
372
|
-
});
|
|
373
|
-
console.log(formatSeedValidationReport(report));
|
|
374
|
-
if (!report.ok) {
|
|
375
|
-
return 1;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
return 0;
|
|
367
|
+
if (args.check || args.doctor || args.validateOnly || args.validateSeeds) {
|
|
368
|
+
const report = await runCheck({
|
|
369
|
+
cwd: args.cwd,
|
|
370
|
+
suitesDir: args.suitesDir,
|
|
371
|
+
filter: args.filter,
|
|
372
|
+
rubricsDir: args.rubricsDir,
|
|
373
|
+
host: args.host,
|
|
374
|
+
hosts: args.hosts,
|
|
375
|
+
});
|
|
376
|
+
console.log(formatCheckReport(report));
|
|
377
|
+
return report.ok ? 0 : 1;
|
|
379
378
|
}
|
|
380
379
|
if (args.debugDir) {
|
|
381
380
|
setLiveStagingRootOverride(args.debugDir);
|
|
382
381
|
}
|
|
383
382
|
const isChild = process.env.AGENT_TEST_CHILD === "1";
|
|
384
383
|
if (args.debugDir && isPathUnderRoot(args.debugDir, args.cwd) && !isChild) {
|
|
385
|
-
console.warn(theme.warn(`--debug-dir is inside the repo (${args.debugDir}). Default is $TMPDIR/agent-spec — prefer that for
|
|
384
|
+
console.warn(theme.warn(`--debug-dir is inside the repo (${args.debugDir}). Default is $TMPDIR/agent-spec — prefer that for direct runs so debug output stays out of git status.`));
|
|
386
385
|
}
|
|
387
386
|
const verbose = args.debug || process.env.AGENT_TEST_VERBOSE === "1" || process.env.AGENT_TEST_DEBUG === "1";
|
|
388
387
|
const stagingSessionId = args.stagingSessionId?.trim() ||
|
|
389
388
|
process.env.AGENT_TEST_STAGING_SESSION_ID?.trim() ||
|
|
390
|
-
(
|
|
391
|
-
? createLiveStagingSessionId()
|
|
392
|
-
: undefined);
|
|
389
|
+
createLiveStagingSessionId();
|
|
393
390
|
const stagingSessionRoot = stagingSessionId
|
|
394
391
|
? getLiveStagingSessionRoot(stagingSessionId)
|
|
395
392
|
: undefined;
|
|
396
|
-
const reportOutput = resolveReportOutput(args.reportOut);
|
|
397
393
|
try {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
394
|
+
let checkHosts = args.hosts ?? (args.host ? [args.host] : ["cursor"]);
|
|
395
|
+
if (!isChild) {
|
|
396
|
+
const report = await runCheck({
|
|
397
|
+
cwd: args.cwd,
|
|
398
|
+
suitesDir: args.suitesDir,
|
|
399
|
+
filter: args.filter,
|
|
400
|
+
rubricsDir: args.rubricsDir,
|
|
401
|
+
host: args.host,
|
|
402
|
+
hosts: args.hosts,
|
|
403
|
+
});
|
|
404
|
+
console.log(theme.tip(formatCheckSummary(report)));
|
|
405
|
+
if (!report.ok) {
|
|
406
|
+
console.error(formatCheckReport(report));
|
|
407
|
+
return 1;
|
|
412
408
|
}
|
|
413
|
-
|
|
414
|
-
|
|
409
|
+
checkHosts = report.hosts;
|
|
410
|
+
const missingHost = missingHostsAuth(report.hosts);
|
|
411
|
+
if (missingHost) {
|
|
412
|
+
console.error(missingHost);
|
|
415
413
|
return 1;
|
|
416
414
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
const missingHost = missingAgentAuth(args.host ?? "cursor");
|
|
418
|
+
if (missingHost) {
|
|
419
|
+
console.error(missingHost);
|
|
420
420
|
return 1;
|
|
421
421
|
}
|
|
422
422
|
}
|
|
423
|
-
if (args.
|
|
423
|
+
if (args.judge !== false) {
|
|
424
|
+
let needsJudge = false;
|
|
424
425
|
try {
|
|
425
|
-
await
|
|
426
|
+
needsJudge = await selectedRunNeedsJudge({
|
|
427
|
+
cwd: args.cwd,
|
|
428
|
+
suitesDir: args.suitesDir,
|
|
429
|
+
filter: args.filter,
|
|
430
|
+
scenarioFilter: args.scenarioFilter,
|
|
431
|
+
rubricsDir: args.rubricsDir,
|
|
432
|
+
judge: args.judge,
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
catch {
|
|
436
|
+
needsJudge = false;
|
|
437
|
+
}
|
|
438
|
+
if (needsJudge) {
|
|
439
|
+
for (const host of checkHosts) {
|
|
440
|
+
const missingJudge = missingClassifierAuth(host);
|
|
441
|
+
if (missingJudge) {
|
|
442
|
+
console.error(`${missingJudge} (use --no-judge to skip)`);
|
|
443
|
+
return 1;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
{
|
|
449
|
+
try {
|
|
450
|
+
await assertDirectAgentPreflight(args.cwd, args.suitesDir);
|
|
426
451
|
}
|
|
427
452
|
catch (error) {
|
|
428
453
|
console.error(error instanceof Error ? error.message : error);
|
|
@@ -433,7 +458,7 @@ async function main() {
|
|
|
433
458
|
process.env.AGENT_TEST_NO_WORKTREE === "1" ||
|
|
434
459
|
process.env.AGENT_TEST_NO_WORKTREE === "true";
|
|
435
460
|
if (worktreeDisabled && !inPlaceAllowed) {
|
|
436
|
-
console.error("
|
|
461
|
+
console.error("Direct agent tests require a sealed temp workspace. Set AGENT_TEST_ALLOW_IN_PLACE=1 to run in repo cwd (--no-worktree leaks agent edits into your working tree).");
|
|
437
462
|
return 1;
|
|
438
463
|
}
|
|
439
464
|
registerLiveRunHandlers();
|
|
@@ -442,70 +467,32 @@ async function main() {
|
|
|
442
467
|
if (removed.length > 0) {
|
|
443
468
|
console.log(theme.warn(`Cleaned ${removed.length} stale agent-test worktree(s) from a prior crash`));
|
|
444
469
|
}
|
|
445
|
-
console.log(theme.banner(args.debug ? "
|
|
470
|
+
console.log(theme.banner(args.debug ? "direct debug" : "direct"));
|
|
446
471
|
if (stagingSessionRoot) {
|
|
447
472
|
console.log(theme.bannerSession(stagingSessionRoot));
|
|
448
473
|
}
|
|
449
|
-
|
|
450
|
-
if (worktreeDisabled) {
|
|
451
|
-
console.warn(theme.warn("running in repo cwd — agent file edits will persist in your working tree"));
|
|
452
|
-
}
|
|
474
|
+
const hints = ["Ctrl+C cancels"];
|
|
453
475
|
if (args.debug || process.env.AGENT_TEST_VERBOSE === "1") {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
}
|
|
457
|
-
else {
|
|
458
|
-
console.log(` ${theme.tip("debug: recordings kept")}`);
|
|
459
|
-
}
|
|
460
|
-
console.log(` ${theme.tip("exit 137 = macOS OOM — isolated subprocesses (AGENT_TEST_NO_ISOLATE=1 to disable)")}`);
|
|
476
|
+
hints.push(args.keepRecordings ? "recordings kept" : "traces removed on exit");
|
|
477
|
+
hints.push("exit 137 = macOS OOM");
|
|
461
478
|
}
|
|
462
479
|
else if (!args.keepRecordings) {
|
|
463
|
-
|
|
480
|
+
hints.push("traces removed on exit");
|
|
481
|
+
}
|
|
482
|
+
console.log(theme.bannerHints(hints));
|
|
483
|
+
if (worktreeDisabled) {
|
|
484
|
+
console.warn(theme.warn("running in repo cwd — agent file edits will persist in your working tree"));
|
|
464
485
|
}
|
|
465
486
|
}
|
|
466
487
|
}
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
const aReport = await loadOrRunCompareSide(args, a, stagingSessionId);
|
|
476
|
-
const bReport = await loadOrRunCompareSide(args, b, stagingSessionId);
|
|
477
|
-
reports = [aReport, bReport];
|
|
478
|
-
if (!isChild) {
|
|
479
|
-
const outDir = args.compareOutDir ??
|
|
480
|
-
reportOutput.outDir ??
|
|
481
|
-
(stagingSessionRoot
|
|
482
|
-
? join(stagingSessionRoot, "compare")
|
|
483
|
-
: resolve(args.cwd, "compare-out"));
|
|
484
|
-
await writeSuiteReportDump(outDir, labelForCompareSide(a), aReport);
|
|
485
|
-
await writeSuiteReportDump(outDir, labelForCompareSide(b), bReport);
|
|
486
|
-
const compare = compareSuiteReports({
|
|
487
|
-
aLabel: labelForCompareSide(a),
|
|
488
|
-
bLabel: labelForCompareSide(b),
|
|
489
|
-
a: aReport,
|
|
490
|
-
b: bReport,
|
|
491
|
-
});
|
|
492
|
-
comparePassRegressions = compare.summary.passRegressions;
|
|
493
|
-
const written = await writeCompareReport({ outDir, report: compare });
|
|
494
|
-
console.log(`\n${theme.tip(`compare JSON: ${written.jsonPath}`)}`);
|
|
495
|
-
console.log(theme.tip(`compare markdown: ${written.markdownPath}`));
|
|
496
|
-
console.log(theme.fileTip("compare HTML", written.htmlPath));
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
else {
|
|
500
|
-
reports = await runAllSuites({
|
|
501
|
-
...args,
|
|
502
|
-
stagingSessionId,
|
|
503
|
-
timeoutMs: args.timeoutMs,
|
|
504
|
-
allowUserInput: args.allowUserInput,
|
|
505
|
-
debug: args.debug,
|
|
506
|
-
debugDir: args.debugDir,
|
|
507
|
-
});
|
|
508
|
-
}
|
|
488
|
+
const reports = await runAllSuites({
|
|
489
|
+
...args,
|
|
490
|
+
stagingSessionId,
|
|
491
|
+
timeoutMs: args.timeoutMs,
|
|
492
|
+
allowUserInput: args.allowUserInput,
|
|
493
|
+
debug: args.debug,
|
|
494
|
+
debugDir: args.debugDir,
|
|
495
|
+
});
|
|
509
496
|
let exitCode = 0;
|
|
510
497
|
if (!isChild) {
|
|
511
498
|
for (const report of reports) {
|
|
@@ -531,7 +518,10 @@ async function main() {
|
|
|
531
518
|
console.log(theme.summary(report.suite, report.passed, report.failed, report.skipped));
|
|
532
519
|
}
|
|
533
520
|
const runSummary = summarizeReports(reports);
|
|
534
|
-
|
|
521
|
+
const runSummaryText = formatRunSummary(runSummary);
|
|
522
|
+
if (runSummaryText) {
|
|
523
|
+
console.log(`\n${theme.runSummary(runSummaryText)}`);
|
|
524
|
+
}
|
|
535
525
|
if (args.failOn === "behavior" && runSummary.infraFailures > 0 && exitCode === 0) {
|
|
536
526
|
console.log(theme.tip(`${runSummary.infraFailures} infra failure(s) ignored (--fail-on=behavior)`));
|
|
537
527
|
}
|
|
@@ -540,30 +530,16 @@ async function main() {
|
|
|
540
530
|
}
|
|
541
531
|
if (args.htmlReport && reports.length > 0) {
|
|
542
532
|
try {
|
|
543
|
-
const pair = args.comparePairs ? parseComparePairToken(args.comparePairs) : undefined;
|
|
544
533
|
const reportPath = await writeHtmlReport(reports, {
|
|
545
534
|
host: args.host,
|
|
546
535
|
suitesDir: args.suitesDir,
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
compareBLabel: pair ? labelForCompareSide(pair.b) : undefined,
|
|
550
|
-
}, reportOutput.htmlPath);
|
|
551
|
-
console.log(`\n${theme.fileTip("HTML report", reportPath)}`);
|
|
552
|
-
// A directory collects everything, not just the HTML.
|
|
553
|
-
if (reportOutput.outDir && !args.comparePairs) {
|
|
554
|
-
for (const report of reports) {
|
|
555
|
-
const dumpPath = await writeSuiteReportDump(reportOutput.outDir, report.suite, report);
|
|
556
|
-
console.log(theme.tip(`suite JSON: ${dumpPath}`));
|
|
557
|
-
}
|
|
558
|
-
}
|
|
536
|
+
});
|
|
537
|
+
console.log(`\n${await htmlReportTip("HTML report", reportPath)}`);
|
|
559
538
|
}
|
|
560
539
|
catch (error) {
|
|
561
540
|
console.warn(theme.warn(`HTML report failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
562
541
|
}
|
|
563
542
|
}
|
|
564
|
-
if (comparePassRegressions > 0) {
|
|
565
|
-
exitCode = 1;
|
|
566
|
-
}
|
|
567
543
|
}
|
|
568
544
|
else {
|
|
569
545
|
for (const report of reports) {
|
|
@@ -581,7 +557,7 @@ async function main() {
|
|
|
581
557
|
}
|
|
582
558
|
finally {
|
|
583
559
|
if (stagingSessionId && !isChild) {
|
|
584
|
-
await
|
|
560
|
+
await cleanupRunArtifacts(stagingSessionRoot, args.keepRecordings);
|
|
585
561
|
}
|
|
586
562
|
}
|
|
587
563
|
}
|