agentv 4.44.0-next.1 → 5.0.0-next.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.
Files changed (52) hide show
  1. package/README.md +2 -2
  2. package/dist/{artifact-writer-72MQQELS.js → artifact-writer-QD52FC56.js} +12 -10
  3. package/dist/{chunk-KAGZRUO6.js → chunk-6JVM4GJI.js} +3 -2
  4. package/dist/chunk-6JVM4GJI.js.map +1 -0
  5. package/dist/{chunk-2N3YSEMI.js → chunk-6Q56CEXV.js} +333 -99
  6. package/dist/chunk-6Q56CEXV.js.map +1 -0
  7. package/dist/{chunk-5DYGN547.js → chunk-GZJMODLW.js} +1008 -663
  8. package/dist/chunk-GZJMODLW.js.map +1 -0
  9. package/dist/{chunk-CXHYTYHU.js → chunk-IBI7A5RP.js} +621 -191
  10. package/dist/chunk-IBI7A5RP.js.map +1 -0
  11. package/dist/{chunk-NPVGBFF6.js → chunk-IJEZFZJR.js} +2 -43
  12. package/dist/chunk-IJEZFZJR.js.map +1 -0
  13. package/dist/{chunk-BPGJ4HBU.js → chunk-RCIEHFQ2.js} +3 -3
  14. package/dist/{chunk-NCNDA3F6.js → chunk-UG4JHZYM.js} +1176 -224
  15. package/dist/chunk-UG4JHZYM.js.map +1 -0
  16. package/dist/cli.js +7 -7
  17. package/dist/dashboard/assets/index-BVMrdN8a.js +119 -0
  18. package/dist/dashboard/assets/{index-DitffAxS.js → index-D5_8DE_B.js} +1 -1
  19. package/dist/dashboard/assets/index-DZWXfWsv.css +1 -0
  20. package/dist/dashboard/index.html +2 -2
  21. package/dist/{dist-L4YCQOQ6.js → dist-KJ6PELQY.js} +45 -11
  22. package/dist/{docker-workspace-RPPXBT27-B4AQHVWA.js → docker-workspace-ZJ2ESVBI-6UDETQII.js} +2 -2
  23. package/dist/{exec-AR6JUUN5-6MBPURPR.js → exec-MYPAJJP7-C5HD5WG5.js} +2 -2
  24. package/dist/index.js +7 -7
  25. package/dist/{interactive-DRNRKLEQ.js → interactive-2WMVAASS.js} +7 -7
  26. package/dist/skills/agentv-bench/SKILL.md +3 -3
  27. package/dist/skills/agentv-bench/agents/analyzer.md +1 -1
  28. package/dist/skills/agentv-bench/references/autoresearch.md +1 -1
  29. package/dist/skills/agentv-bench/references/eval-yaml-spec.md +13 -3
  30. package/dist/skills/agentv-bench/references/migrating-from-skill-creator.md +2 -2
  31. package/dist/skills/agentv-bench/references/schemas.md +6 -6
  32. package/dist/skills/agentv-bench/references/subagent-pipeline.md +1 -1
  33. package/dist/skills/agentv-eval-writer/SKILL.md +10 -0
  34. package/dist/skills/agentv-eval-writer/references/eval-schema.json +3 -63
  35. package/dist/skills/agentv-eval-writer/references/experiment-schema.json +278 -0
  36. package/dist/{ts-eval-loader-S7ESROX2-6CB3HB6H.js → ts-eval-loader-RE43OAGX-AMRCUKBZ.js} +3 -3
  37. package/package.json +1 -1
  38. package/dist/chunk-2N3YSEMI.js.map +0 -1
  39. package/dist/chunk-5DYGN547.js.map +0 -1
  40. package/dist/chunk-CXHYTYHU.js.map +0 -1
  41. package/dist/chunk-KAGZRUO6.js.map +0 -1
  42. package/dist/chunk-NCNDA3F6.js.map +0 -1
  43. package/dist/chunk-NPVGBFF6.js.map +0 -1
  44. package/dist/dashboard/assets/index-1Hvy8sBy.js +0 -119
  45. package/dist/dashboard/assets/index-J5sPcQO3.css +0 -1
  46. /package/dist/{artifact-writer-72MQQELS.js.map → artifact-writer-QD52FC56.js.map} +0 -0
  47. /package/dist/{chunk-BPGJ4HBU.js.map → chunk-RCIEHFQ2.js.map} +0 -0
  48. /package/dist/{dist-L4YCQOQ6.js.map → dist-KJ6PELQY.js.map} +0 -0
  49. /package/dist/{docker-workspace-RPPXBT27-B4AQHVWA.js.map → docker-workspace-ZJ2ESVBI-6UDETQII.js.map} +0 -0
  50. /package/dist/{exec-AR6JUUN5-6MBPURPR.js.map → exec-MYPAJJP7-C5HD5WG5.js.map} +0 -0
  51. /package/dist/{interactive-DRNRKLEQ.js.map → interactive-2WMVAASS.js.map} +0 -0
  52. /package/dist/{ts-eval-loader-S7ESROX2-6CB3HB6H.js.map → ts-eval-loader-RE43OAGX-AMRCUKBZ.js.map} +0 -0
@@ -2,16 +2,21 @@ import { createRequire } from 'node:module'; const require = createRequire(impor
2
2
  import {
3
3
  toSnakeCaseDeep as toSnakeCaseDeep2,
4
4
  writeArtifactsFromResults
5
- } from "./chunk-KAGZRUO6.js";
5
+ } from "./chunk-6JVM4GJI.js";
6
6
  import {
7
7
  RunBudgetTracker,
8
+ buildExperimentArtifactMetadata,
8
9
  buildWipBranchName,
10
+ confirmResultsMergeAndPull,
9
11
  deleteWipBranch,
10
12
  deriveCategory,
13
+ deriveExperimentNameFromPath,
11
14
  directPushResultsWithDetails,
12
15
  directorySizeBytes,
13
16
  getResultsRepoSyncStatus,
17
+ isExperimentFileReference,
14
18
  listGitRuns,
19
+ loadExperimentConfig,
15
20
  loadTsConfig,
16
21
  materializeGitRun,
17
22
  normalizeResultsConfig,
@@ -19,8 +24,10 @@ import {
19
24
  resolveResultsRepoRunsDir,
20
25
  setupWipWorktree,
21
26
  syncResultsRepoForProject
22
- } from "./chunk-NCNDA3F6.js";
27
+ } from "./chunk-UG4JHZYM.js";
23
28
  import {
29
+ AGENTV_LOCAL_CONFIG_FILE_NAME,
30
+ AGENTV_LOCAL_CONFIG_YML_FILE_NAME,
24
31
  CLI_PLACEHOLDERS,
25
32
  COMMON_TARGET_SETTINGS,
26
33
  DEFAULT_EVAL_PATTERNS,
@@ -32,27 +39,33 @@ import {
32
39
  aggregateRunDir,
33
40
  buildDirectoryChain,
34
41
  buildSearchRoots,
42
+ buildSearchRoots2,
35
43
  buildTestTargetKey,
36
44
  buildTraceFromMessages,
37
45
  deduplicateByTestIdTarget,
38
46
  ensureVSCodeSubagents,
47
+ external_exports,
39
48
  findDeprecatedCamelCaseTargetWarnings,
40
49
  findGitRoot,
41
50
  getAgentvConfigDir,
51
+ getLocalConfigPath,
42
52
  getProject,
43
53
  getProjectForPath,
44
54
  interpolateEnv,
55
+ isAgentVConfigFileName,
45
56
  isGraderKind,
46
57
  listTargetNames,
47
58
  loadCasesFromDirectory,
48
59
  loadCasesFromFile,
49
60
  loadConfig,
61
+ loadPromptMdFallback,
50
62
  loadTestSuite,
51
63
  normalizeResultRow,
52
64
  parseJsonlResults,
53
65
  parseYamlValue,
54
66
  readTargetDefinitions,
55
67
  readTestSuiteMetadata,
68
+ resolveDefaultExperimentReference,
56
69
  resolveFileReference,
57
70
  resolveResultsConfigForProject,
58
71
  resolveTargetDefinition,
@@ -66,8 +79,8 @@ import {
66
79
  toCamelCaseDeep,
67
80
  toSnakeCaseDeep,
68
81
  traceFromTranscriptJsonLines,
69
- writeInitialBenchmarkArtifact
70
- } from "./chunk-5DYGN547.js";
82
+ writeInitialRunSummaryArtifact
83
+ } from "./chunk-GZJMODLW.js";
71
84
 
72
85
  // src/commands/eval/shared.ts
73
86
  import { constants } from "node:fs";
@@ -183,7 +196,7 @@ async function findRepoRoot(start) {
183
196
  // package.json
184
197
  var package_default = {
185
198
  name: "agentv",
186
- version: "4.44.0-next.1",
199
+ version: "5.0.0-next.1",
187
200
  description: "CLI entry point for AgentV",
188
201
  type: "module",
189
202
  repository: {
@@ -291,10 +304,49 @@ async function discoverTargetsFile(options) {
291
304
  throw new Error("Unable to locate targets.yaml. Use --targets to specify the file explicitly.");
292
305
  }
293
306
 
307
+ // src/commands/eval/discover.ts
308
+ import path3 from "node:path";
309
+ import fg2 from "fast-glob";
310
+ async function discoverEvalFiles(cwd) {
311
+ const repoRoot = await findRepoRoot(cwd);
312
+ const config = await loadConfig(path3.join(cwd, "_"), repoRoot);
313
+ const patterns = config?.eval_patterns && config.eval_patterns.length > 0 ? config.eval_patterns : DEFAULT_EVAL_PATTERNS;
314
+ const ignore = ["**/node_modules/**", "**/dist/**"];
315
+ const matches = await fg2(patterns, {
316
+ cwd,
317
+ absolute: true,
318
+ onlyFiles: true,
319
+ ignore,
320
+ followSymbolicLinks: true,
321
+ caseSensitiveMatch: false
322
+ });
323
+ const evalFiles = matches.map((absPath) => {
324
+ const relativePath = path3.relative(cwd, absPath);
325
+ const category = deriveCategory(relativePath);
326
+ return { path: absPath, relativePath, category };
327
+ });
328
+ evalFiles.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
329
+ return evalFiles;
330
+ }
331
+ function getCategories(files) {
332
+ const categories = /* @__PURE__ */ new Set();
333
+ for (const file of files) {
334
+ categories.add(file.category);
335
+ }
336
+ const sorted = Array.from(categories);
337
+ sorted.sort();
338
+ return sorted;
339
+ }
340
+ function filterByCategory(files, category) {
341
+ return files.filter((f) => f.category === category);
342
+ }
343
+
294
344
  // src/commands/eval/run-eval.ts
345
+ import { spawn } from "node:child_process";
295
346
  import { constants as constants4, existsSync as existsSync7, mkdirSync as mkdirSync2 } from "node:fs";
296
347
  import { access as access6, readFile as readFile8 } from "node:fs/promises";
297
- import path14 from "node:path";
348
+ import { createRequire as createNodeRequire } from "node:module";
349
+ import path15 from "node:path";
298
350
  import { pathToFileURL as pathToFileURL2 } from "node:url";
299
351
 
300
352
  // src/version-check.ts
@@ -350,11 +402,11 @@ function enforceRequiredVersion(requiredVersion, options) {
350
402
 
351
403
  // src/commands/results/remote.ts
352
404
  import { existsSync as existsSync5 } from "node:fs";
353
- import path8 from "node:path";
405
+ import path9 from "node:path";
354
406
 
355
407
  // src/commands/eval/result-layout.ts
356
408
  import { existsSync, statSync } from "node:fs";
357
- import path3 from "node:path";
409
+ import path4 from "node:path";
358
410
  var RESULT_INDEX_FILENAME = "index.jsonl";
359
411
  var RESULTS_DIRNAME = "results";
360
412
  var DEFAULT_EXPERIMENT_NAME = "default";
@@ -385,19 +437,19 @@ function defaultRunPathSegments(experiment, runDirName) {
385
437
  return [normalizedExperiment, runDirName];
386
438
  }
387
439
  function buildResultsRootDir(cwd) {
388
- return path3.join(cwd, ".agentv", RESULTS_DIRNAME);
440
+ return path4.join(cwd, ".agentv", RESULTS_DIRNAME);
389
441
  }
390
442
  function buildDefaultRunDirFromName(cwd, experiment, runDirName) {
391
- return path3.join(buildResultsRootDir(cwd), ...defaultRunPathSegments(experiment, runDirName));
443
+ return path4.join(buildResultsRootDir(cwd), ...defaultRunPathSegments(experiment, runDirName));
392
444
  }
393
445
  function buildDefaultRunDir(cwd, experiment, timestamp = /* @__PURE__ */ new Date()) {
394
446
  return buildDefaultRunDirFromName(cwd, experiment, createRunDirName(timestamp));
395
447
  }
396
448
  function resolveRunIndexPath(runDir) {
397
- return path3.join(runDir, RESULT_INDEX_FILENAME);
449
+ return path4.join(runDir, RESULT_INDEX_FILENAME);
398
450
  }
399
451
  function isRunManifestPath(filePath) {
400
- return path3.basename(filePath) === RESULT_INDEX_FILENAME;
452
+ return path4.basename(filePath) === RESULT_INDEX_FILENAME;
401
453
  }
402
454
  function resolveExistingRunPrimaryPath(runDir) {
403
455
  const indexPath = resolveRunIndexPath(runDir);
@@ -435,24 +487,24 @@ function resolveRunManifestPath(filePath) {
435
487
  return filePath;
436
488
  }
437
489
  function relativeRunPathFromCwd(cwd, runDir) {
438
- const relative = path3.relative(path3.resolve(buildResultsRootDir(cwd)), path3.resolve(runDir));
439
- if (!relative || relative.startsWith("..") || path3.isAbsolute(relative)) {
490
+ const relative = path4.relative(path4.resolve(buildResultsRootDir(cwd)), path4.resolve(runDir));
491
+ if (!relative || relative.startsWith("..") || path4.isAbsolute(relative)) {
440
492
  return void 0;
441
493
  }
442
- const parts = relative.split(path3.sep).filter(Boolean);
494
+ const parts = relative.split(path4.sep).filter(Boolean);
443
495
  if (parts.length < 2 || isReservedResultsNamespace(parts[0])) {
444
496
  return void 0;
445
497
  }
446
- return parts.join(path3.posix.sep);
498
+ return parts.join(path4.posix.sep);
447
499
  }
448
500
 
449
501
  // src/commands/inspect/utils.ts
450
502
  import { readFileSync as readFileSync2, readdirSync, statSync as statSync2 } from "node:fs";
451
- import path5 from "node:path";
503
+ import path6 from "node:path";
452
504
 
453
505
  // src/commands/results/manifest.ts
454
506
  import { existsSync as existsSync2, readFileSync } from "node:fs";
455
- import path4 from "node:path";
507
+ import path5 from "node:path";
456
508
  function parseJsonlLines(content) {
457
509
  return content.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0).map((line) => JSON.parse(line));
458
510
  }
@@ -479,7 +531,7 @@ function readOptionalText(baseDir, relativePath) {
479
531
  if (!relativePath) {
480
532
  return void 0;
481
533
  }
482
- const absolutePath = path4.join(baseDir, relativePath);
534
+ const absolutePath = path5.join(baseDir, relativePath);
483
535
  if (!existsSync2(absolutePath)) {
484
536
  return void 0;
485
537
  }
@@ -601,8 +653,8 @@ function parseResultManifest(content) {
601
653
  return parseResultRows(content);
602
654
  }
603
655
  function resolveResultSourcePath(source, cwd) {
604
- const resolved = path4.isAbsolute(source) ? source : path4.resolve(cwd ?? process.cwd(), source);
605
- if (isDirectoryPath(resolved) || path4.basename(resolved) === RESULT_INDEX_FILENAME) {
656
+ const resolved = path5.isAbsolute(source) ? source : path5.resolve(cwd ?? process.cwd(), source);
657
+ if (isDirectoryPath(resolved) || path5.basename(resolved) === RESULT_INDEX_FILENAME) {
606
658
  return resolveRunManifestPath(resolved);
607
659
  }
608
660
  return resolved;
@@ -611,7 +663,7 @@ function loadManifestResults(sourceFile, options = {}) {
611
663
  const resolvedSourceFile = resolveRunManifestPath(sourceFile);
612
664
  const content = readFileSync(resolvedSourceFile, "utf8");
613
665
  const records = parseResultRows(content, resolvedSourceFile);
614
- const baseDir = path4.dirname(resolvedSourceFile);
666
+ const baseDir = path5.dirname(resolvedSourceFile);
615
667
  return records.map((record) => hydrateManifestRecord(baseDir, record, options));
616
668
  }
617
669
  function loadLightweightResults(sourceFile) {
@@ -659,10 +711,10 @@ function padLeft(str, len) {
659
711
  }
660
712
  function loadResultFile(filePath) {
661
713
  const resolvedFilePath = resolveTraceResultPath(filePath);
662
- if (path5.extname(resolvedFilePath) === ".json") {
714
+ if (path6.extname(resolvedFilePath) === ".json") {
663
715
  return loadOtlpTraceFile(resolvedFilePath);
664
716
  }
665
- if (path5.basename(resolvedFilePath) === RESULT_INDEX_FILENAME) {
717
+ if (path6.basename(resolvedFilePath) === RESULT_INDEX_FILENAME) {
666
718
  return loadManifestAsRawResults(resolvedFilePath);
667
719
  }
668
720
  return loadJsonlRecords(resolvedFilePath);
@@ -960,7 +1012,7 @@ function toTraceSummary(result) {
960
1012
  return toCamelCaseDeep(rawTrace);
961
1013
  }
962
1014
  function buildRunId(relativeRunPath) {
963
- const normalized = relativeRunPath.split(path5.sep).join("/");
1015
+ const normalized = relativeRunPath.split(path6.sep).join("/");
964
1016
  const segments = normalized.split("/").filter(Boolean);
965
1017
  if (segments.length >= 2) {
966
1018
  const experiment = segments.slice(0, -1).join("/");
@@ -974,8 +1026,8 @@ function buildRunId(relativeRunPath) {
974
1026
  }
975
1027
  function readRunDisplayName(runDir) {
976
1028
  try {
977
- const benchmark = JSON.parse(readFileSync2(path5.join(runDir, "benchmark.json"), "utf8"));
978
- const displayName = benchmark.metadata?.display_name;
1029
+ const summary = JSON.parse(readFileSync2(path6.join(runDir, "summary.json"), "utf8"));
1030
+ const displayName = summary.metadata?.display_name;
979
1031
  return typeof displayName === "string" && displayName.trim() ? displayName.trim() : void 0;
980
1032
  } catch {
981
1033
  return void 0;
@@ -984,8 +1036,8 @@ function readRunDisplayName(runDir) {
984
1036
  function collectRunManifestPaths(runsDir, currentDir, files) {
985
1037
  const primaryPath = resolveExistingRunPrimaryPath(currentDir);
986
1038
  if (primaryPath) {
987
- const relativeRunPath = path5.relative(runsDir, currentDir);
988
- const sortName = path5.basename(currentDir);
1039
+ const relativeRunPath = path6.relative(runsDir, currentDir);
1040
+ const sortName = path6.basename(currentDir);
989
1041
  files.push({
990
1042
  filePath: primaryPath,
991
1043
  displayName: readRunDisplayName(currentDir) ?? sortName,
@@ -997,7 +1049,7 @@ function collectRunManifestPaths(runsDir, currentDir, files) {
997
1049
  const entries = readdirSync(currentDir, { withFileTypes: true });
998
1050
  for (const entry of entries) {
999
1051
  if (entry.isDirectory()) {
1000
- collectRunManifestPaths(runsDir, path5.join(currentDir, entry.name), files);
1052
+ collectRunManifestPaths(runsDir, path6.join(currentDir, entry.name), files);
1001
1053
  }
1002
1054
  }
1003
1055
  }
@@ -1010,7 +1062,7 @@ function listResultFilesFromRoot(runsDir, options) {
1010
1062
  if (options?.skipTopLevelDirs?.has(entry.name) || isReservedResultsNamespace(entry.name)) {
1011
1063
  continue;
1012
1064
  }
1013
- collectRunManifestPaths(runsDir, path5.join(runsDir, entry.name), files);
1065
+ collectRunManifestPaths(runsDir, path6.join(runsDir, entry.name), files);
1014
1066
  }
1015
1067
  }
1016
1068
  } catch {
@@ -1091,7 +1143,7 @@ function formatScore(score) {
1091
1143
  // src/commands/results/remote-metadata.ts
1092
1144
  import { execFileSync } from "node:child_process";
1093
1145
  import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync4, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
1094
- import path7 from "node:path";
1146
+ import path8 from "node:path";
1095
1147
 
1096
1148
  // src/commands/results/run-state.ts
1097
1149
  import { createHash } from "node:crypto";
@@ -1132,12 +1184,12 @@ function materializeRunState(input) {
1132
1184
 
1133
1185
  // src/commands/results/run-tags.ts
1134
1186
  import { existsSync as existsSync3, readFileSync as readFileSync3, unlinkSync, writeFileSync } from "node:fs";
1135
- import path6 from "node:path";
1187
+ import path7 from "node:path";
1136
1188
  var RUN_TAGS_FILENAME = "tags.json";
1137
1189
  var MAX_TAGS_PER_RUN = 20;
1138
1190
  var MAX_TAG_LENGTH = 60;
1139
1191
  function runTagsPath(manifestPath) {
1140
- return path6.join(path6.dirname(manifestPath), RUN_TAGS_FILENAME);
1192
+ return path7.join(path7.dirname(manifestPath), RUN_TAGS_FILENAME);
1141
1193
  }
1142
1194
  function readRunTags(manifestPath) {
1143
1195
  const fp = runTagsPath(manifestPath);
@@ -1202,7 +1254,7 @@ function normalizeTags(tags) {
1202
1254
 
1203
1255
  // src/commands/results/remote-metadata.ts
1204
1256
  var RESULTS_RUNS_DIR = "runs";
1205
- var REMOTE_METADATA_RUNS_DIR = path7.join("metadata", "runs");
1257
+ var REMOTE_METADATA_RUNS_DIR = path8.join("metadata", "runs");
1206
1258
  function cleanGitEnv() {
1207
1259
  const env = {};
1208
1260
  for (const [key, value] of Object.entries(process.env)) {
@@ -1228,7 +1280,7 @@ function tryRunGit(repoDir, args) {
1228
1280
  }
1229
1281
  }
1230
1282
  function toGitPath(filePath) {
1231
- return filePath.split(path7.sep).join("/");
1283
+ return filePath.split(path8.sep).join("/");
1232
1284
  }
1233
1285
  function readTagsFile(filePath) {
1234
1286
  if (!existsSync4(filePath)) return void 0;
@@ -1282,27 +1334,27 @@ function resolveComparisonRef(repoDir) {
1282
1334
  return tryRunGit(repoDir, ["rev-parse", "--verify", "HEAD"]) ? "HEAD" : void 0;
1283
1335
  }
1284
1336
  function resolveRemoteRunMetadataPaths(repoDir, manifestPath) {
1285
- const runsRoot = path7.resolve(repoDir, RESULTS_RUNS_DIR);
1286
- const manifestDir = path7.resolve(path7.dirname(manifestPath));
1287
- const runRelativePath = path7.relative(runsRoot, manifestDir);
1288
- if (runRelativePath.length === 0 || runRelativePath.startsWith("..") || path7.isAbsolute(runRelativePath)) {
1337
+ const runsRoot = path8.resolve(repoDir, RESULTS_RUNS_DIR);
1338
+ const manifestDir = path8.resolve(path8.dirname(manifestPath));
1339
+ const runRelativePath = path8.relative(runsRoot, manifestDir);
1340
+ if (runRelativePath.length === 0 || runRelativePath.startsWith("..") || path8.isAbsolute(runRelativePath)) {
1289
1341
  throw new Error(
1290
1342
  `Remote run manifest is outside the results repo runs directory: ${manifestPath}`
1291
1343
  );
1292
1344
  }
1293
- const overlayTagsPath = path7.join(
1345
+ const overlayTagsPath = path8.join(
1294
1346
  repoDir,
1295
1347
  REMOTE_METADATA_RUNS_DIR,
1296
1348
  runRelativePath,
1297
1349
  RUN_TAGS_FILENAME
1298
1350
  );
1299
- const artifactTagsPath = path7.join(runsRoot, runRelativePath, RUN_TAGS_FILENAME);
1351
+ const artifactTagsPath = path8.join(runsRoot, runRelativePath, RUN_TAGS_FILENAME);
1300
1352
  return {
1301
1353
  runRelativePath,
1302
1354
  artifactTagsPath,
1303
- artifactTagsGitPath: toGitPath(path7.relative(repoDir, artifactTagsPath)),
1355
+ artifactTagsGitPath: toGitPath(path8.relative(repoDir, artifactTagsPath)),
1304
1356
  overlayTagsPath,
1305
- overlayTagsGitPath: toGitPath(path7.relative(repoDir, overlayTagsPath))
1357
+ overlayTagsGitPath: toGitPath(path8.relative(repoDir, overlayTagsPath))
1306
1358
  };
1307
1359
  }
1308
1360
  function readRemoteRunTagsContext(repoDir, manifestPath, comparisonRef = resolveComparisonRef(repoDir)) {
@@ -1363,7 +1415,7 @@ function writeRemoteRunTags(repoDir, manifestPath, tags, comparisonRef, expected
1363
1415
  updated_at: updatedAt,
1364
1416
  tag_revision: createTagRevision(cleaned, updatedAt)
1365
1417
  };
1366
- mkdirSync(path7.dirname(context.paths.overlayTagsPath), { recursive: true });
1418
+ mkdirSync(path8.dirname(context.paths.overlayTagsPath), { recursive: true });
1367
1419
  writeFileSync2(context.paths.overlayTagsPath, `${JSON.stringify(entry, null, 2)}
1368
1420
  `, "utf8");
1369
1421
  return readRemoteRunTags(repoDir, manifestPath, comparisonRef);
@@ -1407,7 +1459,7 @@ function invalidateGitRunsCache(repoDir) {
1407
1459
  }
1408
1460
  }
1409
1461
  function relativeLocalRunPath(cwd, manifestPath) {
1410
- const manifestDir = path8.resolve(path8.dirname(manifestPath));
1462
+ const manifestDir = path9.resolve(path9.dirname(manifestPath));
1411
1463
  return relativeRunPathFromCwd(cwd, manifestDir);
1412
1464
  }
1413
1465
  function remoteMetadataManifestPath(cwd, config, meta) {
@@ -1421,7 +1473,7 @@ function remoteMetadataManifestPath(cwd, config, meta) {
1421
1473
  if (!relativeRunPath) {
1422
1474
  return void 0;
1423
1475
  }
1424
- return path8.join(config.path, "runs", ...relativeRunPath.split("/"), "index.jsonl");
1476
+ return path9.join(config.path, "runs", ...relativeRunPath.split("/"), "index.jsonl");
1425
1477
  }
1426
1478
  var REMOTE_RUN_PREFIX = "remote::";
1427
1479
  var SIZE_WARNING_BYTES = 10 * 1024 * 1024;
@@ -1434,7 +1486,7 @@ function getRelativeRunPath(cwd, runDir) {
1434
1486
  return relative;
1435
1487
  }
1436
1488
  throw new Error(
1437
- `Run workspace must use .agentv/results/<experiment>/<timestamp>: ${path8.resolve(runDir)}`
1489
+ `Run workspace must use .agentv/results/<experiment>/<timestamp>: ${path9.resolve(runDir)}`
1438
1490
  );
1439
1491
  }
1440
1492
  function buildCommitTitle(payload) {
@@ -1453,7 +1505,7 @@ async function maybeWarnLargeArtifact(runDir) {
1453
1505
  }
1454
1506
  async function loadNormalizedResultsConfig(cwd, projectId, overrides) {
1455
1507
  const repoRoot = await findRepoRoot(cwd) ?? cwd;
1456
- const config = await loadConfig(path8.join(cwd, "_"), repoRoot);
1508
+ const config = await loadConfig(path9.join(cwd, "_"), repoRoot);
1457
1509
  const project = projectId !== void 0 ? getProject(projectId) : getProjectForPath(repoRoot) ?? getProjectForPath(cwd);
1458
1510
  const projectResults = project?.results ? {
1459
1511
  mode: "github",
@@ -1571,6 +1623,32 @@ async function syncRemoteResults(cwd, projectId) {
1571
1623
  };
1572
1624
  }
1573
1625
  }
1626
+ async function confirmRemoteResultsMerge(cwd, projectId) {
1627
+ const config = await loadNormalizedResultsConfig(cwd, projectId);
1628
+ if (!config) {
1629
+ return {
1630
+ ...await getResultsRepoSyncStatus(),
1631
+ run_count: 0
1632
+ };
1633
+ }
1634
+ try {
1635
+ const status = await confirmResultsMergeAndPull(config);
1636
+ invalidateGitRunsCache(config.path);
1637
+ return {
1638
+ ...status,
1639
+ run_count: await getRemoteRunCount(config, status)
1640
+ };
1641
+ } catch (error) {
1642
+ const status = await getResultsRepoSyncStatus(config);
1643
+ return {
1644
+ ...status,
1645
+ run_count: await getRemoteRunCount(config, status),
1646
+ last_error: getStatusMessage(error),
1647
+ blocked: true,
1648
+ block_reason: getStatusMessage(error)
1649
+ };
1650
+ }
1651
+ }
1574
1652
  function dedupeSyncedRunCopies(runs) {
1575
1653
  const byRunId = /* @__PURE__ */ new Map();
1576
1654
  for (const run of runs) {
@@ -1613,7 +1691,7 @@ async function listMergedResultFiles(cwd, limit, projectId) {
1613
1691
  raw_filename: r.run_id,
1614
1692
  source: "remote",
1615
1693
  on_remote: true,
1616
- path: path8.join(config.path, r.manifest_path),
1694
+ path: path9.join(config.path, r.manifest_path),
1617
1695
  displayName: r.display_name,
1618
1696
  timestamp: r.timestamp,
1619
1697
  testCount: r.test_count,
@@ -1668,11 +1746,11 @@ async function ensureRemoteRunAvailable(cwd, meta, projectId) {
1668
1746
  if (!config) {
1669
1747
  throw new Error("Remote results are not configured");
1670
1748
  }
1671
- const relativeManifestPath = path8.relative(config.path, meta.path).split(path8.sep).join("/");
1749
+ const relativeManifestPath = path9.relative(config.path, meta.path).split(path9.sep).join("/");
1672
1750
  if (relativeManifestPath.length === 0 || relativeManifestPath === meta.path || relativeManifestPath.startsWith("../")) {
1673
1751
  throw new Error(`Remote manifest path is outside the results repo clone: ${meta.path}`);
1674
1752
  }
1675
- const relativeRunPath = path8.posix.relative("runs", path8.posix.dirname(relativeManifestPath));
1753
+ const relativeRunPath = path9.posix.relative("runs", path9.posix.dirname(relativeManifestPath));
1676
1754
  await materializeGitRun(config.path, relativeRunPath, getResultsStorageRef(config));
1677
1755
  }
1678
1756
  async function readRemoteRunTagState(cwd, meta, projectId) {
@@ -1781,13 +1859,13 @@ async function maybeAutoExportRunArtifacts(payload) {
1781
1859
  // src/commands/eval/env.ts
1782
1860
  import { constants as constants3 } from "node:fs";
1783
1861
  import { access as access3 } from "node:fs/promises";
1784
- import path9 from "node:path";
1862
+ import path10 from "node:path";
1785
1863
  import { config as loadDotenv } from "dotenv";
1786
1864
  function uniqueDirs(directories) {
1787
1865
  const seen = /* @__PURE__ */ new Set();
1788
1866
  const result = [];
1789
1867
  for (const dir of directories) {
1790
- const absolute = path9.resolve(dir);
1868
+ const absolute = path10.resolve(dir);
1791
1869
  if (seen.has(absolute)) {
1792
1870
  continue;
1793
1871
  }
@@ -1806,14 +1884,14 @@ async function fileExists2(filePath) {
1806
1884
  }
1807
1885
  function collectAncestorDirectories(start, boundary) {
1808
1886
  const directories = [];
1809
- const boundaryDir = path9.resolve(boundary);
1810
- let current = path9.resolve(start);
1887
+ const boundaryDir = path10.resolve(boundary);
1888
+ let current = path10.resolve(start);
1811
1889
  while (current !== void 0) {
1812
1890
  directories.push(current);
1813
1891
  if (current === boundaryDir) {
1814
1892
  break;
1815
1893
  }
1816
- const parent = path9.dirname(current);
1894
+ const parent = path10.dirname(current);
1817
1895
  if (parent === current) {
1818
1896
  break;
1819
1897
  }
@@ -1823,12 +1901,12 @@ function collectAncestorDirectories(start, boundary) {
1823
1901
  }
1824
1902
  async function loadEnvFromHierarchy(options) {
1825
1903
  const { testFilePath, repoRoot, verbose } = options;
1826
- const testDir = path9.dirname(path9.resolve(testFilePath));
1904
+ const testDir = path10.dirname(path10.resolve(testFilePath));
1827
1905
  const cwd = process.cwd();
1828
1906
  const searchDirs = uniqueDirs([...collectAncestorDirectories(testDir, repoRoot), repoRoot, cwd]);
1829
1907
  const envFiles = [];
1830
1908
  for (const dir of searchDirs) {
1831
- const candidate = path9.join(dir, ".env");
1909
+ const candidate = path10.join(dir, ".env");
1832
1910
  if (await fileExists2(candidate)) {
1833
1911
  envFiles.push(candidate);
1834
1912
  }
@@ -1851,7 +1929,7 @@ async function loadEnvFromHierarchy(options) {
1851
1929
 
1852
1930
  // src/commands/eval/otel-backends.ts
1853
1931
  import { access as access4 } from "node:fs/promises";
1854
- import path10 from "node:path";
1932
+ import path11 from "node:path";
1855
1933
  import { pathToFileURL } from "node:url";
1856
1934
  var RESOLVER_EXTENSIONS = [".mjs", ".js"];
1857
1935
  var builtinOtelBackendResolvers = [
@@ -1915,7 +1993,7 @@ async function findLocalOtelBackendResolver(name, cwd) {
1915
1993
  }
1916
1994
  for (const dir of getResolverSearchDirs(cwd)) {
1917
1995
  for (const ext of RESOLVER_EXTENSIONS) {
1918
- const candidate = path10.join(dir, `${name}${ext}`);
1996
+ const candidate = path11.join(dir, `${name}${ext}`);
1919
1997
  try {
1920
1998
  await access4(candidate);
1921
1999
  return candidate;
@@ -1927,11 +2005,11 @@ async function findLocalOtelBackendResolver(name, cwd) {
1927
2005
  }
1928
2006
  function getResolverSearchDirs(cwd) {
1929
2007
  const dirs = [];
1930
- let current = path10.resolve(cwd);
1931
- const root = path10.parse(current).root;
2008
+ let current = path11.resolve(cwd);
2009
+ const root = path11.parse(current).root;
1932
2010
  while (current !== root) {
1933
- dirs.push(path10.join(current, ".agentv", "otel-backends"));
1934
- current = path10.dirname(current);
2011
+ dirs.push(path11.join(current, ".agentv", "otel-backends"));
2012
+ current = path11.dirname(current);
1935
2013
  }
1936
2014
  return dirs;
1937
2015
  }
@@ -1960,7 +2038,7 @@ function trimTrailingSlash(value) {
1960
2038
  // src/commands/eval/jsonl-writer.ts
1961
2039
  import { createWriteStream } from "node:fs";
1962
2040
  import { mkdir } from "node:fs/promises";
1963
- import path11 from "node:path";
2041
+ import path12 from "node:path";
1964
2042
  import { finished } from "node:stream/promises";
1965
2043
 
1966
2044
  // ../../node_modules/.bun/async-mutex@0.5.0/node_modules/async-mutex/index.mjs
@@ -2178,7 +2256,7 @@ var JsonlWriter = class _JsonlWriter {
2178
2256
  this.stream = stream;
2179
2257
  }
2180
2258
  static async open(filePath, options) {
2181
- await mkdir(path11.dirname(filePath), { recursive: true });
2259
+ await mkdir(path12.dirname(filePath), { recursive: true });
2182
2260
  const flags = options?.append ? "a" : "w";
2183
2261
  const stream = createWriteStream(filePath, { flags, encoding: "utf8" });
2184
2262
  return new _JsonlWriter(stream);
@@ -2363,7 +2441,7 @@ async function loadNonErrorResults(jsonlPath) {
2363
2441
  // src/commands/eval/run-cache.ts
2364
2442
  import { existsSync as existsSync6 } from "node:fs";
2365
2443
  import { mkdir as mkdir2, readFile, writeFile } from "node:fs/promises";
2366
- import path12 from "node:path";
2444
+ import path13 from "node:path";
2367
2445
  var CACHE_FILENAME = "cache.json";
2368
2446
  function resolveRunCacheFile(cache) {
2369
2447
  if (cache.lastRunDir) {
@@ -2372,7 +2450,7 @@ function resolveRunCacheFile(cache) {
2372
2450
  return "";
2373
2451
  }
2374
2452
  function cachePath(cwd) {
2375
- return path12.join(cwd, ".agentv", CACHE_FILENAME);
2453
+ return path13.join(cwd, ".agentv", CACHE_FILENAME);
2376
2454
  }
2377
2455
  async function loadRunCache(cwd) {
2378
2456
  try {
@@ -2389,13 +2467,13 @@ async function resolveCachedRunDir(cwd) {
2389
2467
  return cache.lastRunDir;
2390
2468
  }
2391
2469
  async function saveRunCache(cwd, resultPath) {
2392
- if (path12.basename(resultPath) !== RESULT_INDEX_FILENAME) {
2470
+ if (path13.basename(resultPath) !== RESULT_INDEX_FILENAME) {
2393
2471
  return;
2394
2472
  }
2395
- const dir = path12.join(cwd, ".agentv");
2473
+ const dir = path13.join(cwd, ".agentv");
2396
2474
  await mkdir2(dir, { recursive: true });
2397
2475
  const cache = {
2398
- lastRunDir: path12.dirname(resultPath),
2476
+ lastRunDir: path13.dirname(resultPath),
2399
2477
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
2400
2478
  };
2401
2479
  await writeFile(cachePath(cwd), `${JSON.stringify(cache, null, 2)}
@@ -2664,7 +2742,7 @@ function formatMatrixSummary(results) {
2664
2742
 
2665
2743
  // ../../packages/core/dist/evaluation/validation/index.js
2666
2744
  import { readFile as readFile2 } from "node:fs/promises";
2667
- import path13 from "node:path";
2745
+ import path14 from "node:path";
2668
2746
  import { readFile as readFile22, readdir, stat as stat2 } from "node:fs/promises";
2669
2747
  import path22 from "node:path";
2670
2748
  import { readFile as readFile3 } from "node:fs/promises";
@@ -2710,10 +2788,13 @@ async function detectFileType(filePath) {
2710
2788
  }
2711
2789
  }
2712
2790
  function inferFileTypeFromPath(filePath) {
2713
- const normalized = path13.normalize(filePath).replace(/\\/g, "/");
2714
- const basename = path13.basename(filePath);
2791
+ const normalized = path14.normalize(filePath).replace(/\\/g, "/");
2792
+ const basename = path14.basename(filePath);
2793
+ if (basename === AGENTV_LOCAL_CONFIG_FILE_NAME || basename === AGENTV_LOCAL_CONFIG_YML_FILE_NAME) {
2794
+ return "config";
2795
+ }
2715
2796
  if (normalized.includes("/.agentv/")) {
2716
- if (basename === "config.yaml" || basename === "config.yml") {
2797
+ if (isAgentVConfigFileName(basename)) {
2717
2798
  return "config";
2718
2799
  }
2719
2800
  if (basename === "targets.yaml" || basename === "targets.yml") {
@@ -2726,6 +2807,62 @@ function inferFileTypeFromPath(filePath) {
2726
2807
  }
2727
2808
  return "unknown";
2728
2809
  }
2810
+ var JsonObjectSchema = external_exports.object({}).catchall(external_exports.unknown());
2811
+ var StringOrStringArraySchema = external_exports.union([external_exports.string().min(1), external_exports.array(external_exports.string().min(1)).min(1)]);
2812
+ var ExperimentScriptSchema = external_exports.union([
2813
+ external_exports.string().min(1),
2814
+ external_exports.object({
2815
+ command: StringOrStringArraySchema.optional(),
2816
+ script: StringOrStringArraySchema.optional(),
2817
+ timeout_seconds: external_exports.number().gt(0).optional(),
2818
+ cwd: external_exports.string().min(1).optional(),
2819
+ env: external_exports.record(external_exports.string()).optional()
2820
+ }).strict().refine((value) => value.command !== void 0 || value.script !== void 0, {
2821
+ message: "Experiment step must define command or script."
2822
+ })
2823
+ ]);
2824
+ var ExperimentRepeatSchema = external_exports.object({
2825
+ count: external_exports.number().int().min(1),
2826
+ strategy: external_exports.enum(["pass_at_k", "mean", "confidence_interval"]).optional(),
2827
+ cost_limit_usd: external_exports.number().min(0).optional(),
2828
+ costLimitUsd: external_exports.number().min(0).optional()
2829
+ }).strict();
2830
+ var ExperimentTargetRefSchema = external_exports.union([
2831
+ external_exports.string().min(1),
2832
+ external_exports.object({
2833
+ name: external_exports.string().min(1),
2834
+ use_target: external_exports.string().min(1).optional(),
2835
+ hooks: JsonObjectSchema.optional()
2836
+ }).strict()
2837
+ ]);
2838
+ var ExperimentSuiteSelectSchema = external_exports.object({
2839
+ test_ids: external_exports.array(external_exports.string().min(1)).min(1)
2840
+ }).strict();
2841
+ var ExperimentSuiteRefSchema = external_exports.object({
2842
+ ref: external_exports.string().min(1),
2843
+ select: ExperimentSuiteSelectSchema.optional()
2844
+ }).strict();
2845
+ var ExperimentFileSchema = external_exports.object({
2846
+ name: external_exports.string().min(1).optional(),
2847
+ agent: external_exports.string().min(1).optional(),
2848
+ target: external_exports.string().min(1).optional(),
2849
+ targets: external_exports.array(ExperimentTargetRefSchema).min(1).optional(),
2850
+ model: external_exports.string().min(1).optional(),
2851
+ agent_options: JsonObjectSchema.optional(),
2852
+ suites: external_exports.array(ExperimentSuiteRefSchema).min(1).optional(),
2853
+ scripts: external_exports.array(ExperimentScriptSchema).optional(),
2854
+ repeat: ExperimentRepeatSchema.optional(),
2855
+ runs: external_exports.number().int().min(1).optional(),
2856
+ early_exit: external_exports.boolean().optional(),
2857
+ timeout_seconds: external_exports.number().gt(0).optional(),
2858
+ workers: external_exports.number().int().min(1).optional(),
2859
+ budget_usd: external_exports.number().gt(0).optional(),
2860
+ sandbox: external_exports.enum(["auto", "docker", "vercel"]).optional(),
2861
+ workspace: JsonObjectSchema.optional(),
2862
+ setup: external_exports.array(ExperimentScriptSchema).optional()
2863
+ }).strict().refine((value) => value.repeat === void 0 || value.runs === void 0, {
2864
+ message: "Use repeat or runs, not both."
2865
+ });
2729
2866
  var ASSERTION_TYPES_WITH_STRING_VALUE = /* @__PURE__ */ new Set([
2730
2867
  "contains",
2731
2868
  "icontains",
@@ -3023,8 +3160,16 @@ async function validateEvalFile(filePath) {
3023
3160
  message: "Invalid 'criteria' field (must be a non-empty string if provided)"
3024
3161
  });
3025
3162
  }
3163
+ const caseExecution = isObject(evalCase.execution) ? evalCase.execution : void 0;
3164
+ const skipDefaults = caseExecution?.skip_defaults === true;
3165
+ const hasPromptMdFallback = evalCase.input === void 0 ? await loadPromptMdFallback({
3166
+ evalFilePath: absolutePath,
3167
+ searchRoots: buildSearchRoots2(absolutePath, process.cwd()),
3168
+ testInputFiles: evalCase.input_files,
3169
+ suiteInputFiles: skipDefaults ? void 0 : parsed.input_files
3170
+ }) !== void 0 : false;
3026
3171
  validateInputField(evalCase.input, `${location}.input`, absolutePath, errors, {
3027
- required: true
3172
+ required: !hasPromptMdFallback
3028
3173
  });
3029
3174
  const expectedOutputField = evalCase.expected_output;
3030
3175
  if (expectedOutputField !== void 0) {
@@ -3247,7 +3392,7 @@ function validateInputField(inputField, location, filePath, errors, options = {}
3247
3392
  severity: "error",
3248
3393
  filePath,
3249
3394
  location,
3250
- message: "Missing 'input' field (must be a string, object, or array of messages)"
3395
+ message: "Missing 'input' field (provide a string, object, message array, or PROMPT.md next to EVAL.yaml / referenced in input_files)"
3251
3396
  });
3252
3397
  }
3253
3398
  return;
@@ -4241,7 +4386,9 @@ async function validateConfigFile(filePath, options = {}) {
4241
4386
  }
4242
4387
  function inferConfigScope(filePath) {
4243
4388
  const globalConfigPath = path52.resolve(getAgentvConfigDir(), "config.yaml");
4244
- if (path52.resolve(filePath) === globalConfigPath) {
4389
+ const globalLocalConfigPath = path52.resolve(getLocalConfigPath(globalConfigPath));
4390
+ const resolvedPath = path52.resolve(filePath);
4391
+ if (resolvedPath === globalConfigPath || resolvedPath === globalLocalConfigPath) {
4245
4392
  return "global";
4246
4393
  }
4247
4394
  return filePath.split(/[\\/]/).includes(".agentv") ? "project" : "global";
@@ -4474,12 +4621,19 @@ function validateResultsSyncAndBranchPrefix(errors, filePath, resultsRecord, loc
4474
4621
  `Field '${location}.sync.require_push' must be a boolean`
4475
4622
  );
4476
4623
  }
4477
- if (syncRecord.push_conflict_policy !== void 0 && syncRecord.push_conflict_policy !== "block" && syncRecord.push_conflict_policy !== "backup_and_force_push") {
4624
+ if (syncRecord.push_conflict_policy === "backup_and_force_push") {
4478
4625
  addError(
4479
4626
  errors,
4480
4627
  filePath,
4481
4628
  `${location}.sync.push_conflict_policy`,
4482
- `Field '${location}.sync.push_conflict_policy' must be 'block' or 'backup_and_force_push'`
4629
+ `Field '${location}.sync.push_conflict_policy' uses removed value 'backup_and_force_push'; remove it or set it to 'block'. AgentV never force-pushes result branches.`
4630
+ );
4631
+ } else if (syncRecord.push_conflict_policy !== void 0 && syncRecord.push_conflict_policy !== "block") {
4632
+ addError(
4633
+ errors,
4634
+ filePath,
4635
+ `${location}.sync.push_conflict_policy`,
4636
+ `Field '${location}.sync.push_conflict_policy' must be 'block'`
4483
4637
  );
4484
4638
  }
4485
4639
  }
@@ -5226,6 +5380,8 @@ var WipCheckpointLoop = class {
5226
5380
 
5227
5381
  // src/commands/eval/run-eval.ts
5228
5382
  var DEFAULT_WORKERS = 3;
5383
+ var loadCjsModule = createNodeRequire(import.meta.url);
5384
+ var micromatch = loadCjsModule("micromatch");
5229
5385
  function shouldSkipExistingResultForResume(result, rerunFailed) {
5230
5386
  if (rerunFailed) {
5231
5387
  return result.executionStatus === "ok";
@@ -5306,13 +5462,13 @@ function normalizeSourceMetadataByEvalFile(value) {
5306
5462
  const entries = [...value.entries()].filter(
5307
5463
  (entry) => typeof entry[0] === "string" && typeof entry[1] === "object" && entry[1] !== null && !Array.isArray(entry[1])
5308
5464
  );
5309
- return entries.length > 0 ? new Map(entries.map(([key, metadata]) => [path14.resolve(key), metadata])) : void 0;
5465
+ return entries.length > 0 ? new Map(entries.map(([key, metadata]) => [path15.resolve(key), metadata])) : void 0;
5310
5466
  }
5311
5467
  if (typeof value === "object" && value !== null && !Array.isArray(value)) {
5312
5468
  const entries = Object.entries(value).filter(
5313
5469
  (entry) => typeof entry[1] === "object" && entry[1] !== null && !Array.isArray(entry[1])
5314
5470
  );
5315
- return entries.length > 0 ? new Map(entries.map(([key, metadata]) => [path14.resolve(key), metadata])) : void 0;
5471
+ return entries.length > 0 ? new Map(entries.map(([key, metadata]) => [path15.resolve(key), metadata])) : void 0;
5316
5472
  }
5317
5473
  return void 0;
5318
5474
  }
@@ -5326,10 +5482,10 @@ var LEGACY_OUTPUT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
5326
5482
  ".htm"
5327
5483
  ]);
5328
5484
  function looksLikeLegacyOutputFilePath(value) {
5329
- return LEGACY_OUTPUT_FILE_EXTENSIONS.has(path14.extname(value).toLowerCase());
5485
+ return LEGACY_OUTPUT_FILE_EXTENSIONS.has(path15.extname(value).toLowerCase());
5330
5486
  }
5331
5487
  function outputFileMigrationMessage(value) {
5332
- const ext = path14.extname(value).toLowerCase();
5488
+ const ext = path15.extname(value).toLowerCase();
5333
5489
  const removalHint = ext === ".xml" ? "JUnit XML export from agentv eval has been removed." : "Flat result file export from agentv eval has been removed.";
5334
5490
  return `--output expects a run directory, not a file path: ${value}
5335
5491
  ${removalHint} Set --output <dir> for the canonical run artifacts; AgentV always writes <dir>/index.jsonl.`;
@@ -5337,7 +5493,7 @@ ${removalHint} Set --output <dir> for the canonical run artifacts; AgentV always
5337
5493
  function artifactsMigrationMessage(artifactsDir, outputDir) {
5338
5494
  const lines = [`--artifacts was removed from agentv eval. Use --output ${artifactsDir} instead.`];
5339
5495
  if (outputDir && looksLikeLegacyOutputFilePath(outputDir)) {
5340
- const ext = path14.extname(outputDir).toLowerCase();
5496
+ const ext = path15.extname(outputDir).toLowerCase();
5341
5497
  lines.push(
5342
5498
  ext === ".xml" ? "JUnit XML export from agentv eval has been removed." : "Flat result file export from agentv eval has been removed."
5343
5499
  );
@@ -5493,7 +5649,7 @@ function normalizeOptions(rawOptions, config, yamlExecution) {
5493
5649
  };
5494
5650
  }
5495
5651
  function withSourceMetadata(result, testFilePath, options) {
5496
- const sourceMetadata = options.sourceMetadataByEvalFile?.get(path14.resolve(testFilePath));
5652
+ const sourceMetadata = options.sourceMetadataByEvalFile?.get(path15.resolve(testFilePath));
5497
5653
  if (!sourceMetadata) {
5498
5654
  return result;
5499
5655
  }
@@ -5515,7 +5671,258 @@ async function ensureFileExists(filePath, description) {
5515
5671
  function buildDefaultOutputPathForExperiment(cwd, experiment, runDirName) {
5516
5672
  const runDir = buildDefaultRunDirFromName(cwd, experiment, runDirName);
5517
5673
  mkdirSync2(runDir, { recursive: true });
5518
- return path14.join(runDir, "index.jsonl");
5674
+ return path15.join(runDir, "index.jsonl");
5675
+ }
5676
+ function normalizeTsDefaultExperiment(config) {
5677
+ return normalizeString(config?.experiments?.default) ?? normalizeString(config?.defaultExperiment);
5678
+ }
5679
+ async function resolveExperimentForRun(params) {
5680
+ const experimentRef = params.explicitExperiment ?? params.yamlDefaultExperiment ?? params.tsDefaultExperiment;
5681
+ if (!experimentRef) {
5682
+ return {};
5683
+ }
5684
+ const experimentPath = resolveExperimentFilePath(params.cwd, experimentRef);
5685
+ if (!experimentPath) {
5686
+ if (isExperimentFileReference(experimentRef)) {
5687
+ throw new Error(`Experiment file not found: ${experimentRef}`);
5688
+ }
5689
+ return { name: experimentRef };
5690
+ }
5691
+ const config = await loadExperimentConfig(experimentPath);
5692
+ return {
5693
+ name: config.name ?? deriveExperimentNameFromPath(experimentPath),
5694
+ config
5695
+ };
5696
+ }
5697
+ function resolveExperimentFilePath(cwd, experimentRef) {
5698
+ if (isExperimentFileReference(experimentRef)) {
5699
+ const experimentPath = path15.isAbsolute(experimentRef) ? experimentRef : path15.resolve(cwd, experimentRef);
5700
+ return existsSync7(experimentPath) ? experimentPath : void 0;
5701
+ }
5702
+ for (const ext of ["yaml", "yml", "ts", "js", "mts", "mjs"]) {
5703
+ const candidate = path15.resolve(cwd, "experiments", `${experimentRef}.${ext}`);
5704
+ if (existsSync7(candidate)) {
5705
+ return candidate;
5706
+ }
5707
+ }
5708
+ return void 0;
5709
+ }
5710
+ function applyExperimentOptions(options, experiment) {
5711
+ if (!experiment) {
5712
+ return options;
5713
+ }
5714
+ const experimentTargetRefs = buildExperimentTargetRefs(experiment);
5715
+ const experimentTargetNames = experimentTargetRefs?.map((target) => target.name) ?? [];
5716
+ const experimentTarget = experiment.target && experiment.target.trim().length > 0 ? experiment.target : void 0;
5717
+ const nextCliTargets = options.cliTargets.length > 0 ? options.cliTargets : experimentTargetNames.length > 0 ? experimentTargetNames : experimentTarget ? [experimentTarget] : options.cliTargets;
5718
+ const workspaceMode = options.workspaceMode ?? readExperimentWorkspaceMode(experiment.workspace?.mode);
5719
+ const workspacePath = options.workspacePath ?? readExperimentWorkspacePath(experiment.workspace);
5720
+ return {
5721
+ ...options,
5722
+ target: options.target ?? (nextCliTargets.length === 1 ? nextCliTargets[0] : void 0),
5723
+ cliTargets: nextCliTargets,
5724
+ agentTimeoutSeconds: options.agentTimeoutSeconds ?? experiment.timeoutSeconds,
5725
+ workers: options.workers ?? experiment.workers,
5726
+ workspaceMode: workspacePath ? "static" : workspaceMode,
5727
+ workspacePath,
5728
+ budgetUsd: options.budgetUsd ?? experiment.budgetUsd,
5729
+ experimentConfig: experiment,
5730
+ experimentMetadata: buildExperimentArtifactMetadata(experiment),
5731
+ experimentTargetRefs: options.cliTargets.length === 0 ? experimentTargetRefs : void 0,
5732
+ experimentTrialsConfig: buildExperimentTrialsConfig(experiment)
5733
+ };
5734
+ }
5735
+ function buildExperimentTargetRefs(experiment) {
5736
+ if (!experiment.targets || experiment.targets.length === 0) {
5737
+ return void 0;
5738
+ }
5739
+ return experiment.targets.map((target) => {
5740
+ if (typeof target === "string") {
5741
+ return { name: target };
5742
+ }
5743
+ return {
5744
+ name: target.name,
5745
+ ...target.useTarget !== void 0 && { use_target: target.useTarget },
5746
+ ...target.hooks !== void 0 && {
5747
+ hooks: target.hooks
5748
+ }
5749
+ };
5750
+ });
5751
+ }
5752
+ function buildExperimentTrialsConfig(experiment) {
5753
+ if (experiment.repeat) {
5754
+ if (experiment.repeat.count <= 1) {
5755
+ return void 0;
5756
+ }
5757
+ return {
5758
+ count: experiment.repeat.count,
5759
+ strategy: experiment.repeat.strategy,
5760
+ ...experiment.repeat.costLimitUsd !== void 0 && {
5761
+ costLimitUsd: experiment.repeat.costLimitUsd
5762
+ },
5763
+ ...experiment.earlyExit !== void 0 && { earlyExit: experiment.earlyExit }
5764
+ };
5765
+ }
5766
+ if (!experiment.runs || experiment.runs <= 1) {
5767
+ return void 0;
5768
+ }
5769
+ return {
5770
+ count: experiment.runs,
5771
+ strategy: "pass_at_k",
5772
+ ...experiment.earlyExit !== void 0 && { earlyExit: experiment.earlyExit }
5773
+ };
5774
+ }
5775
+ function readExperimentWorkspaceMode(value) {
5776
+ return value === "pooled" || value === "temp" || value === "static" ? value : void 0;
5777
+ }
5778
+ function readExperimentWorkspacePath(workspace) {
5779
+ const value = workspace?.path;
5780
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
5781
+ }
5782
+ function matchesTestFilter(id, filter) {
5783
+ return typeof filter === "string" ? micromatch.isMatch(id, filter) : filter.some((pattern) => micromatch.isMatch(id, pattern));
5784
+ }
5785
+ async function resolveExperimentSuiteSelection(suites, cwd) {
5786
+ if (!suites || suites.length === 0) {
5787
+ return void 0;
5788
+ }
5789
+ const testFiles = /* @__PURE__ */ new Set();
5790
+ const selectedTestIdsByEvalFile = /* @__PURE__ */ new Map();
5791
+ for (const suite of suites) {
5792
+ const resolvedSuiteFiles = await resolveEvalPaths([suite.ref], cwd);
5793
+ for (const testFilePath of resolvedSuiteFiles) {
5794
+ const resolvedPath = path15.resolve(testFilePath);
5795
+ testFiles.add(resolvedPath);
5796
+ if (suite.select?.testIds && suite.select.testIds.length > 0) {
5797
+ const existing = selectedTestIdsByEvalFile.get(resolvedPath) ?? [];
5798
+ selectedTestIdsByEvalFile.set(resolvedPath, [...existing, ...suite.select.testIds]);
5799
+ }
5800
+ }
5801
+ }
5802
+ const filtersByEvalFile = /* @__PURE__ */ new Map();
5803
+ for (const [testFilePath, testIds] of selectedTestIdsByEvalFile.entries()) {
5804
+ const uniqueTestIds = [...new Set(testIds)];
5805
+ filtersByEvalFile.set(
5806
+ testFilePath,
5807
+ uniqueTestIds.length === 1 ? uniqueTestIds[0] : uniqueTestIds
5808
+ );
5809
+ }
5810
+ return {
5811
+ testFiles: [...testFiles],
5812
+ filtersByEvalFile
5813
+ };
5814
+ }
5815
+ async function runExperimentSteps(params) {
5816
+ const steps = params.steps ?? [];
5817
+ if (steps.length === 0) {
5818
+ return;
5819
+ }
5820
+ for (let index = 0; index < steps.length; index++) {
5821
+ const step = steps[index];
5822
+ const command = buildExperimentStepCommand(step);
5823
+ const cwd = resolveExperimentStepCwd(params.cwd, params.experimentConfig, step.cwd);
5824
+ console.log(`Experiment ${params.label} ${index + 1}/${steps.length}: ${command.display}`);
5825
+ await runExperimentCommand(command.argv, {
5826
+ cwd,
5827
+ env: step.env,
5828
+ timeoutMs: step.timeoutSeconds ? step.timeoutSeconds * 1e3 : void 0,
5829
+ label: `experiment ${params.label}`
5830
+ });
5831
+ }
5832
+ }
5833
+ async function runExperimentSetup(params) {
5834
+ const setup = params.config?.setup;
5835
+ if (typeof setup === "function") {
5836
+ console.log("Experiment setup: running TypeScript setup()");
5837
+ await setup({
5838
+ cwd: params.cwd,
5839
+ runDir: params.runDir,
5840
+ experiment: params.config,
5841
+ env: process.env
5842
+ });
5843
+ return;
5844
+ }
5845
+ await runExperimentSteps({
5846
+ label: "setup",
5847
+ steps: setup,
5848
+ cwd: params.cwd,
5849
+ experimentConfig: params.config
5850
+ });
5851
+ }
5852
+ function buildExperimentStepCommand(step) {
5853
+ if (step.command && step.command.length > 0) {
5854
+ return { argv: step.command, display: step.command.join(" ") };
5855
+ }
5856
+ if (typeof step.script === "string" && step.script.trim().length > 0) {
5857
+ return {
5858
+ argv: shellCommand(step.script),
5859
+ display: step.script
5860
+ };
5861
+ }
5862
+ if (Array.isArray(step.script) && step.script.length > 0) {
5863
+ return { argv: step.script, display: step.script.join(" ") };
5864
+ }
5865
+ throw new Error("Experiment step must define command or script.");
5866
+ }
5867
+ function shellCommand(script) {
5868
+ return process.platform === "win32" ? ["cmd", "/c", script] : ["sh", "-c", script];
5869
+ }
5870
+ function resolveExperimentStepCwd(cwd, experimentConfig, stepCwd) {
5871
+ const base = experimentConfig?.sourcePath ? path15.dirname(experimentConfig.sourcePath) : cwd;
5872
+ if (!stepCwd) {
5873
+ return base;
5874
+ }
5875
+ return path15.isAbsolute(stepCwd) ? stepCwd : path15.resolve(base, stepCwd);
5876
+ }
5877
+ async function runExperimentCommand(argv, options) {
5878
+ if (argv.length === 0) {
5879
+ throw new Error(`${options.label} command must not be empty.`);
5880
+ }
5881
+ await new Promise((resolve, reject) => {
5882
+ const cmd = argv[0];
5883
+ if (!cmd) {
5884
+ reject(new Error(`${options.label} command must not be empty.`));
5885
+ return;
5886
+ }
5887
+ const args = argv.slice(1);
5888
+ const child = spawn(cmd, args, {
5889
+ cwd: options.cwd,
5890
+ env: options.env ? { ...process.env, ...options.env } : process.env,
5891
+ stdio: "inherit"
5892
+ });
5893
+ let completed = false;
5894
+ const timeout = options.timeoutMs !== void 0 ? setTimeout(() => {
5895
+ if (!completed) {
5896
+ completed = true;
5897
+ child.kill("SIGKILL");
5898
+ reject(new Error(`${options.label} timed out after ${options.timeoutMs}ms`));
5899
+ }
5900
+ }, options.timeoutMs) : void 0;
5901
+ child.on("error", (error) => {
5902
+ if (completed) {
5903
+ return;
5904
+ }
5905
+ completed = true;
5906
+ if (timeout !== void 0) {
5907
+ clearTimeout(timeout);
5908
+ }
5909
+ reject(error);
5910
+ });
5911
+ child.on("exit", (code) => {
5912
+ if (completed) {
5913
+ return;
5914
+ }
5915
+ completed = true;
5916
+ if (timeout !== void 0) {
5917
+ clearTimeout(timeout);
5918
+ }
5919
+ if (code === 0) {
5920
+ resolve();
5921
+ } else {
5922
+ reject(new Error(`${options.label} exited with code ${code ?? "unknown"}`));
5923
+ }
5924
+ });
5925
+ });
5519
5926
  }
5520
5927
  function createProgressReporter(maxWorkers, options) {
5521
5928
  const display = new ProgressDisplay(maxWorkers, options);
@@ -5529,7 +5936,7 @@ function createProgressReporter(maxWorkers, options) {
5529
5936
  };
5530
5937
  }
5531
5938
  function makeTestCaseKey(testFilePath, testId) {
5532
- return `${path14.resolve(testFilePath)}::${testId}`;
5939
+ return `${path15.resolve(testFilePath)}::${testId}`;
5533
5940
  }
5534
5941
  function resolveTargetLabel(requestedName, resolvedName) {
5535
5942
  if (resolvedName !== requestedName) {
@@ -5584,22 +5991,40 @@ function applyVerboseOverride(selection, cliVerbose) {
5584
5991
  };
5585
5992
  }
5586
5993
  async function prepareFileMetadata(params) {
5587
- const { testFilePath, repoRoot, cwd, options } = params;
5994
+ const { testFilePath, repoRoot, cwd, options, suiteFilter } = params;
5588
5995
  await ensureFileExists(testFilePath, "Test file");
5589
5996
  await loadEnvFromHierarchy({
5590
5997
  testFilePath,
5591
5998
  repoRoot,
5592
5999
  verbose: options.verbose
5593
6000
  });
5594
- const relativePath = path14.relative(cwd, testFilePath);
6001
+ const relativePath = path15.relative(cwd, testFilePath);
5595
6002
  const category = deriveCategory(relativePath);
5596
6003
  const suite = await loadTestSuite(testFilePath, repoRoot, {
5597
6004
  verbose: options.verbose,
5598
- filter: options.filter,
6005
+ filter: suiteFilter ?? options.filter,
5599
6006
  category
5600
6007
  });
5601
- const testIds = suite.tests.map((value) => value.id);
6008
+ const testCases = suiteFilter && options.filter ? suite.tests.filter((testCase) => matchesTestFilter(testCase.id, options.filter ?? "")) : suite.tests;
6009
+ const testIds = testCases.map((value) => value.id);
5602
6010
  const suiteTargets = suite.targets;
6011
+ if (testCases.length === 0) {
6012
+ return {
6013
+ testIds,
6014
+ testCases,
6015
+ selections: [],
6016
+ trialsConfig: options.experimentTrialsConfig,
6017
+ suiteTargets,
6018
+ yamlWorkers: suite.workers,
6019
+ yamlCache: suite.cacheConfig?.enabled,
6020
+ yamlCachePath: suite.cacheConfig?.cachePath,
6021
+ budgetUsd: suite.budgetUsd,
6022
+ failOnError: suite.failOnError,
6023
+ threshold: suite.threshold,
6024
+ tags: suite.metadata?.tags,
6025
+ providerFactory: suite.providerFactory
6026
+ };
6027
+ }
5603
6028
  let selections;
5604
6029
  if (options.transcript) {
5605
6030
  const transcriptSelection = {
@@ -5616,7 +6041,7 @@ async function prepareFileMetadata(params) {
5616
6041
  selections = [
5617
6042
  {
5618
6043
  selection: transcriptSelection,
5619
- inlineTargetLabel: `transcript (${path14.basename(options.transcript)})`
6044
+ inlineTargetLabel: `transcript (${path15.basename(options.transcript)})`
5620
6045
  }
5621
6046
  ];
5622
6047
  } else if (suite.inlineTarget && options.cliTargets.length === 0) {
@@ -5671,15 +6096,20 @@ async function prepareFileMetadata(params) {
5671
6096
  const cliTargets = options.cliTargets;
5672
6097
  const suiteTargets2 = suite.targets;
5673
6098
  const suiteTargetRefs = suite.targetRefs;
6099
+ const experimentTargetRefs = options.experimentTargetRefs;
5674
6100
  let targetNames;
6101
+ let targetRefs;
5675
6102
  if (cliTargets.length > 0) {
5676
6103
  targetNames = cliTargets;
6104
+ targetRefs = experimentTargetRefs;
5677
6105
  } else if (suiteTargets2 && suiteTargets2.length > 0) {
5678
6106
  targetNames = suiteTargets2;
6107
+ targetRefs = suiteTargetRefs;
5679
6108
  } else {
5680
6109
  targetNames = [];
6110
+ targetRefs = void 0;
5681
6111
  }
5682
- if (targetNames.length > 1) {
6112
+ if (targetNames.length > 1 || targetNames.length === 1 && targetRefs) {
5683
6113
  const multiSelections = await selectMultipleTargets({
5684
6114
  testFilePath,
5685
6115
  repoRoot,
@@ -5691,7 +6121,7 @@ async function prepareFileMetadata(params) {
5691
6121
  dryRunDelayMax: options.dryRunDelayMax,
5692
6122
  env: process.env,
5693
6123
  targetNames,
5694
- targetRefs: suiteTargetRefs
6124
+ targetRefs
5695
6125
  });
5696
6126
  selections = multiSelections.map((sel) => ({
5697
6127
  selection: sel,
@@ -5710,9 +6140,7 @@ async function prepareFileMetadata(params) {
5710
6140
  dryRunDelayMax: options.dryRunDelayMax,
5711
6141
  env: process.env
5712
6142
  });
5713
- const singleTargetHooks = suiteTargetRefs?.find(
5714
- (ref) => ref.name === selection.targetName
5715
- )?.hooks;
6143
+ const singleTargetHooks = targetRefs?.find((ref) => ref.name === selection.targetName)?.hooks;
5716
6144
  const augmentedSelection = singleTargetHooks ? { ...selection, targetHooks: singleTargetHooks } : selection;
5717
6145
  selections = [
5718
6146
  {
@@ -5727,9 +6155,9 @@ async function prepareFileMetadata(params) {
5727
6155
  }
5728
6156
  return {
5729
6157
  testIds,
5730
- testCases: suite.tests,
6158
+ testCases,
5731
6159
  selections,
5732
- trialsConfig: suite.trials,
6160
+ trialsConfig: options.experimentTrialsConfig,
5733
6161
  suiteTargets,
5734
6162
  yamlWorkers: suite.workers,
5735
6163
  yamlCache: suite.cacheConfig?.enabled,
@@ -5782,7 +6210,7 @@ async function runSingleEvalFile(params) {
5782
6210
  } = params;
5783
6211
  const targetName = selection.targetName;
5784
6212
  const replayRecording = options.recordReplay ? {
5785
- fixturesPath: path14.resolve(options.recordReplay),
6213
+ fixturesPath: path15.resolve(options.recordReplay),
5786
6214
  sourceTarget: targetName,
5787
6215
  variant: options.recordReplayVariant
5788
6216
  } : void 0;
@@ -5926,7 +6354,7 @@ async function runEvalCommand(input) {
5926
6354
  );
5927
6355
  }
5928
6356
  const repoRoot = await findRepoRoot(cwd);
5929
- const yamlConfig = await loadConfig(path14.join(cwd, "_"), repoRoot);
6357
+ const yamlConfig = await loadConfig(path15.join(cwd, "_"), repoRoot);
5930
6358
  let requiredVersionCheck;
5931
6359
  if (yamlConfig?.required_version) {
5932
6360
  requiredVersionCheck = await enforceRequiredVersion(yamlConfig.required_version, {
@@ -5934,6 +6362,33 @@ async function runEvalCommand(input) {
5934
6362
  });
5935
6363
  }
5936
6364
  let options = normalizeOptions(input.rawOptions, config, yamlConfig?.execution);
6365
+ const resolvedExperiment = await resolveExperimentForRun({
6366
+ cwd,
6367
+ explicitExperiment: options.experiment,
6368
+ yamlDefaultExperiment: resolveDefaultExperimentReference(yamlConfig),
6369
+ tsDefaultExperiment: normalizeTsDefaultExperiment(config)
6370
+ });
6371
+ options = {
6372
+ ...applyExperimentOptions(options, resolvedExperiment.config),
6373
+ experiment: resolvedExperiment.name
6374
+ };
6375
+ const suiteSelection = await resolveExperimentSuiteSelection(
6376
+ options.experimentConfig?.suites,
6377
+ cwd
6378
+ );
6379
+ const evalPathInputs = input.testFiles.length > 0 ? [...input.testFiles] : suiteSelection ? [...suiteSelection.testFiles] : [];
6380
+ if (evalPathInputs.length === 0 && process.stdin.isTTY) {
6381
+ const { launchInteractiveWizard } = await import("./interactive-2WMVAASS.js");
6382
+ await launchInteractiveWizard();
6383
+ return void 0;
6384
+ }
6385
+ const resolvedTestFiles = await resolveEvalPaths(evalPathInputs, cwd);
6386
+ options = {
6387
+ ...options,
6388
+ ...suiteSelection !== void 0 && {
6389
+ suiteFiltersByEvalFile: suiteSelection.filtersByEvalFile
6390
+ }
6391
+ };
5937
6392
  if (!process.env.AGENTV_EXPERIMENT) {
5938
6393
  process.env.AGENTV_EXPERIMENT = normalizeExperimentName(options.experiment);
5939
6394
  }
@@ -5962,7 +6417,7 @@ async function runEvalCommand(input) {
5962
6417
  }
5963
6418
  let retryNonErrorResults;
5964
6419
  if (options.retryErrors) {
5965
- const retryPath = path14.resolve(options.retryErrors);
6420
+ const retryPath = path15.resolve(options.retryErrors);
5966
6421
  await ensureFileExists(retryPath, "Retry-errors JSONL file");
5967
6422
  const completedIds = await loadFullyCompletedTestIds(retryPath);
5968
6423
  const errorIds = await loadErrorTestIds(retryPath);
@@ -5980,7 +6435,7 @@ async function runEvalCommand(input) {
5980
6435
  if (cachedDir) {
5981
6436
  options = { ...options, outputDir: cachedDir };
5982
6437
  const flagLabel = options.rerunFailed ? "rerun-failed" : "resume";
5983
- const displayDir = path14.relative(cwd, cachedDir) || cachedDir;
6438
+ const displayDir = path15.relative(cwd, cachedDir) || cachedDir;
5984
6439
  console.log(`Auto-detected last run dir for --${flagLabel}: ${displayDir}`);
5985
6440
  }
5986
6441
  }
@@ -5989,7 +6444,7 @@ async function runEvalCommand(input) {
5989
6444
  if (options.resume && !options.retryErrors) {
5990
6445
  const explicitResumeDir = options.outputDir;
5991
6446
  if (explicitResumeDir) {
5992
- const resumeIndexPath = path14.join(path14.resolve(explicitResumeDir), "index.jsonl");
6447
+ const resumeIndexPath = path15.join(path15.resolve(explicitResumeDir), "index.jsonl");
5993
6448
  if (existsSync7(resumeIndexPath)) {
5994
6449
  const content = await readFile8(resumeIndexPath, "utf8");
5995
6450
  const existingResults = parseJsonlResults(content);
@@ -6014,7 +6469,7 @@ async function runEvalCommand(input) {
6014
6469
  }
6015
6470
  }
6016
6471
  if (options.workspacePath) {
6017
- const resolvedWorkspace = path14.resolve(options.workspacePath);
6472
+ const resolvedWorkspace = path15.resolve(options.workspacePath);
6018
6473
  try {
6019
6474
  const { stat: stat3 } = await import("node:fs/promises");
6020
6475
  const stats = await stat3(resolvedWorkspace);
@@ -6037,22 +6492,22 @@ async function runEvalCommand(input) {
6037
6492
  let outputPath;
6038
6493
  const runDirName = process.env.AGENTV_RUN_TIMESTAMP?.trim() || createRunDirName();
6039
6494
  if (explicitDir) {
6040
- runDir = path14.resolve(explicitDir);
6495
+ runDir = path15.resolve(explicitDir);
6041
6496
  mkdirSync2(runDir, { recursive: true });
6042
- outputPath = path14.join(runDir, "index.jsonl");
6497
+ outputPath = path15.join(runDir, "index.jsonl");
6043
6498
  } else {
6044
6499
  outputPath = buildDefaultOutputPathForExperiment(cwd, options.experiment, runDirName);
6045
- runDir = path14.dirname(outputPath);
6500
+ runDir = path15.dirname(outputPath);
6046
6501
  }
6047
6502
  if (!process.env.AGENTV_RUN_TIMESTAMP) {
6048
- process.env.AGENTV_RUN_TIMESTAMP = path14.basename(runDir);
6503
+ process.env.AGENTV_RUN_TIMESTAMP = path15.basename(runDir);
6049
6504
  }
6050
6505
  process.env.AGENTV_RUN_DIR = runDir;
6051
6506
  let otelExporter = null;
6052
6507
  const useFileExport = !!options.otelFile;
6053
6508
  if (options.exportOtel || useFileExport) {
6054
6509
  try {
6055
- const { OtelTraceExporter } = await import("./dist-L4YCQOQ6.js");
6510
+ const { OtelTraceExporter } = await import("./dist-KJ6PELQY.js");
6056
6511
  let endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
6057
6512
  let headers = {};
6058
6513
  let resourceAttributes = {};
@@ -6085,7 +6540,7 @@ async function runEvalCommand(input) {
6085
6540
  resourceAttributes,
6086
6541
  captureContent,
6087
6542
  groupTurns: options.otelGroupTurns,
6088
- otlpFilePath: options.otelFile ? path14.resolve(options.otelFile) : void 0
6543
+ otlpFilePath: options.otelFile ? path15.resolve(options.otelFile) : void 0
6089
6544
  });
6090
6545
  const initialized = await otelExporter.init();
6091
6546
  if (!initialized) {
@@ -6103,9 +6558,13 @@ async function runEvalCommand(input) {
6103
6558
  }
6104
6559
  const primaryWritePath = outputPath;
6105
6560
  console.log(`Artifact directory: ${runDir}`);
6106
- const resolvedTestFiles = input.testFiles.map((file) => path14.resolve(file));
6561
+ await runExperimentSetup({
6562
+ config: options.experimentConfig,
6563
+ cwd,
6564
+ runDir
6565
+ });
6107
6566
  if (options.otelFile) {
6108
- console.log(`OTLP JSON file: ${path14.resolve(options.otelFile)}`);
6567
+ console.log(`OTLP JSON file: ${path15.resolve(options.otelFile)}`);
6109
6568
  }
6110
6569
  const evaluationRunner = await resolveEvaluationRunner();
6111
6570
  const allResults = [];
@@ -6123,7 +6582,8 @@ async function runEvalCommand(input) {
6123
6582
  testFilePath,
6124
6583
  repoRoot,
6125
6584
  cwd,
6126
- options
6585
+ options,
6586
+ suiteFilter: options.suiteFiltersByEvalFile?.get(path15.resolve(testFilePath))
6127
6587
  });
6128
6588
  fileMetadata.set(testFilePath, meta);
6129
6589
  }
@@ -6133,7 +6593,7 @@ async function runEvalCommand(input) {
6133
6593
  for (const [testFilePath, meta] of fileMetadata.entries()) {
6134
6594
  if (!matchesTagFilters(meta.tags, options.tags, options.excludeTags)) {
6135
6595
  fileMetadata.delete(testFilePath);
6136
- skippedFiles.push(path14.relative(cwd, testFilePath));
6596
+ skippedFiles.push(path15.relative(cwd, testFilePath));
6137
6597
  }
6138
6598
  }
6139
6599
  if (skippedFiles.length > 0 && options.verbose) {
@@ -6156,12 +6616,12 @@ async function runEvalCommand(input) {
6156
6616
  tsConfigCache: options.tsConfigCache
6157
6617
  });
6158
6618
  const activeCachePath = options.cachePath ?? yamlCachePath ?? options.tsConfigCachePath;
6159
- const cache = cacheEnabled ? new ResponseCache(activeCachePath ? path14.resolve(activeCachePath) : void 0) : void 0;
6619
+ const cache = cacheEnabled ? new ResponseCache(activeCachePath ? path15.resolve(activeCachePath) : void 0) : void 0;
6160
6620
  if (cache) {
6161
6621
  console.log(`Response cache: enabled (${cache.cachePath})`);
6162
6622
  }
6163
6623
  if (options.recordReplay) {
6164
- console.log(`Replay recording: ${path14.resolve(options.recordReplay)}`);
6624
+ console.log(`Replay recording: ${path15.resolve(options.recordReplay)}`);
6165
6625
  }
6166
6626
  const yamlThreshold = firstMeta?.threshold;
6167
6627
  const resolvedThreshold = options.threshold ?? yamlThreshold;
@@ -6257,7 +6717,7 @@ async function runEvalCommand(input) {
6257
6717
  const activeTestFiles = resolvedTestFiles.filter((f) => fileMetadata.has(f));
6258
6718
  let transcriptProviderFactory;
6259
6719
  if (options.transcript) {
6260
- const { TranscriptProvider } = await import("./dist-L4YCQOQ6.js");
6720
+ const { TranscriptProvider } = await import("./dist-KJ6PELQY.js");
6261
6721
  const transcriptProvider = await TranscriptProvider.fromFile(options.transcript);
6262
6722
  const totalTests = [...fileMetadata.values()].reduce(
6263
6723
  (sum, meta) => sum + meta.testCases.length,
@@ -6275,10 +6735,11 @@ async function runEvalCommand(input) {
6275
6735
  }
6276
6736
  if (!isResumeAppend && totalEvalCount > 0) {
6277
6737
  const evalFile = activeTestFiles.length === 1 ? activeTestFiles[0] : "";
6278
- await writeInitialBenchmarkArtifact(runDir, {
6738
+ await writeInitialRunSummaryArtifact(runDir, {
6279
6739
  evalFile,
6280
6740
  plannedTestCount: totalEvalCount,
6281
- experiment: normalizeExperimentName(options.experiment)
6741
+ experiment: normalizeExperimentName(options.experiment),
6742
+ experimentMetadata: options.experimentMetadata
6282
6743
  });
6283
6744
  }
6284
6745
  let wipLoop;
@@ -6306,7 +6767,7 @@ async function runEvalCommand(input) {
6306
6767
  if (!targetPrep2) continue;
6307
6768
  const budgetMsg = `Run budget exceeded ($${runBudgetTracker.currentCostUsd.toFixed(4)} / $${runBudgetTracker.budgetCapUsd.toFixed(4)})`;
6308
6769
  console.log(`
6309
- \u26A0 ${budgetMsg} \u2014 skipping ${path14.basename(testFilePath)}`);
6770
+ \u26A0 ${budgetMsg} \u2014 skipping ${path15.basename(testFilePath)}`);
6310
6771
  for (const { selection } of targetPrep2.selections) {
6311
6772
  const skippedResults = targetPrep2.testCases.map((testCase) => ({
6312
6773
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -6385,7 +6846,7 @@ async function runEvalCommand(input) {
6385
6846
  threshold: resolvedThreshold,
6386
6847
  providerFactory: transcriptProviderFactory ?? targetPrep.providerFactory
6387
6848
  });
6388
- const evalFile = path14.relative(cwd, testFilePath);
6849
+ const evalFile = path15.relative(cwd, testFilePath);
6389
6850
  const existingSummary = remoteEvalSummaries.find(
6390
6851
  (summary2) => summary2.evalFile === evalFile
6391
6852
  );
@@ -6402,7 +6863,7 @@ async function runEvalCommand(input) {
6402
6863
  const message = fileError instanceof Error ? fileError.message : String(fileError);
6403
6864
  console.error(
6404
6865
  `
6405
- [ERROR] \u26A0 Eval file failed: ${path14.basename(testFilePath)} \u2014 ${message}
6866
+ [ERROR] \u26A0 Eval file failed: ${path15.basename(testFilePath)} \u2014 ${message}
6406
6867
  `
6407
6868
  );
6408
6869
  const errorResults = filteredTestCases.map(
@@ -6481,7 +6942,7 @@ ${formatRequiredVersionFailureNote(requiredVersionCheck)}`);
6481
6942
  );
6482
6943
  const taskBundleTargets = buildTaskBundleTargetSelections(activeTestFiles, fileMetadata);
6483
6944
  if (isResumeAppend) {
6484
- const { writePerTestArtifacts } = await import("./artifact-writer-72MQQELS.js");
6945
+ const { writePerTestArtifacts } = await import("./artifact-writer-QD52FC56.js");
6485
6946
  await writePerTestArtifacts(allResults, runDir, {
6486
6947
  experiment: normalizeExperimentName(options.experiment),
6487
6948
  cwd,
@@ -6489,37 +6950,36 @@ ${formatRequiredVersionFailureNote(requiredVersionCheck)}`);
6489
6950
  sourceTests,
6490
6951
  taskBundleTargets
6491
6952
  });
6492
- const { benchmarkPath: workspaceBenchmarkPath, timingPath } = await aggregateRunDir(
6493
- runDir,
6494
- { evalFile, experiment: normalizeExperimentName(options.experiment) }
6495
- );
6496
- const indexPath = path14.join(runDir, "index.jsonl");
6953
+ const { summaryPath } = await aggregateRunDir(runDir, {
6954
+ evalFile,
6955
+ experiment: normalizeExperimentName(options.experiment),
6956
+ experimentMetadata: options.experimentMetadata
6957
+ });
6958
+ const indexPath = path15.join(runDir, "index.jsonl");
6497
6959
  console.log(`Artifact workspace updated: ${runDir}`);
6498
6960
  console.log(` Index: ${indexPath}`);
6499
6961
  console.log(` Per-test artifacts: ${runDir} (${allResults.length} new test directories)`);
6500
- console.log(` Timing: ${timingPath}`);
6501
- console.log(` Benchmark: ${workspaceBenchmarkPath}`);
6962
+ console.log(` Summary: ${summaryPath}`);
6502
6963
  } else {
6503
- const {
6504
- testArtifactDir,
6505
- timingPath,
6506
- benchmarkPath: workspaceBenchmarkPath,
6507
- indexPath
6508
- } = await writeArtifactsFromResults(allResults, runDir, {
6509
- evalFile,
6510
- experiment: normalizeExperimentName(options.experiment),
6511
- cwd,
6512
- repoRoot,
6513
- sourceTests,
6514
- taskBundleTargets
6515
- });
6964
+ const { testArtifactDir, summaryPath, indexPath } = await writeArtifactsFromResults(
6965
+ allResults,
6966
+ runDir,
6967
+ {
6968
+ evalFile,
6969
+ experiment: normalizeExperimentName(options.experiment),
6970
+ experimentMetadata: options.experimentMetadata,
6971
+ cwd,
6972
+ repoRoot,
6973
+ sourceTests,
6974
+ taskBundleTargets
6975
+ }
6976
+ );
6516
6977
  console.log(`Artifact workspace written to: ${runDir}`);
6517
6978
  console.log(` Index: ${indexPath}`);
6518
6979
  console.log(
6519
6980
  ` Per-test artifacts: ${testArtifactDir} (${allResults.length} test directories)`
6520
6981
  );
6521
- console.log(` Timing: ${timingPath}`);
6522
- console.log(` Benchmark: ${workspaceBenchmarkPath}`);
6982
+ console.log(` Summary: ${summaryPath}`);
6523
6983
  }
6524
6984
  }
6525
6985
  const resultsWithWorkspaces = allResults.filter((r) => r.workspacePath);
@@ -6559,9 +7019,9 @@ Results written to: ${outputPath}`);
6559
7019
  });
6560
7020
  }
6561
7021
  if (summary.executionErrorCount > 0 && !options.retryErrors && !options.resume) {
6562
- const evalFileArgs = activeTestFiles.map((f) => path14.relative(cwd, f)).join(" ");
7022
+ const evalFileArgs = activeTestFiles.map((f) => path15.relative(cwd, f)).join(" ");
6563
7023
  const targetFlag = options.target ? ` --target ${options.target}` : "";
6564
- const relativeRunDir = path14.relative(cwd, runDir);
7024
+ const relativeRunDir = path15.relative(cwd, runDir);
6565
7025
  console.log(
6566
7026
  `
6567
7027
  Tip: ${summary.executionErrorCount} execution error(s) detected. Re-run failed tests with:
@@ -6579,6 +7039,12 @@ Tip: ${summary.executionErrorCount} execution error(s) detected. Re-run failed t
6579
7039
  wipCleanedUp = true;
6580
7040
  await wipLoop.stopAndDeleteWipBranch();
6581
7041
  }
7042
+ await runExperimentSteps({
7043
+ label: "script",
7044
+ steps: options.experimentConfig?.scripts,
7045
+ cwd,
7046
+ experimentConfig: options.experimentConfig
7047
+ });
6582
7048
  return {
6583
7049
  executionErrorCount: summary.executionErrorCount,
6584
7050
  outputPath,
@@ -6610,7 +7076,7 @@ async function resolveEvaluationRunner() {
6610
7076
  if (!overridePath) {
6611
7077
  return runEvaluation;
6612
7078
  }
6613
- const resolved = path14.isAbsolute(overridePath) ? overridePath : path14.resolve(process.cwd(), overridePath);
7079
+ const resolved = path15.isAbsolute(overridePath) ? overridePath : path15.resolve(process.cwd(), overridePath);
6614
7080
  const moduleUrl = pathToFileURL2(resolved).href;
6615
7081
  const mod = await import(moduleUrl);
6616
7082
  const candidate = mod.runEvaluation;
@@ -6622,43 +7088,6 @@ async function resolveEvaluationRunner() {
6622
7088
  return candidate;
6623
7089
  }
6624
7090
 
6625
- // src/commands/eval/discover.ts
6626
- import path15 from "node:path";
6627
- import fg2 from "fast-glob";
6628
- async function discoverEvalFiles(cwd) {
6629
- const repoRoot = await findRepoRoot(cwd);
6630
- const config = await loadConfig(path15.join(cwd, "_"), repoRoot);
6631
- const patterns = config?.eval_patterns && config.eval_patterns.length > 0 ? config.eval_patterns : DEFAULT_EVAL_PATTERNS;
6632
- const ignore = ["**/node_modules/**", "**/dist/**"];
6633
- const matches = await fg2(patterns, {
6634
- cwd,
6635
- absolute: true,
6636
- onlyFiles: true,
6637
- ignore,
6638
- followSymbolicLinks: true,
6639
- caseSensitiveMatch: false
6640
- });
6641
- const evalFiles = matches.map((absPath) => {
6642
- const relativePath = path15.relative(cwd, absPath);
6643
- const category = deriveCategory(relativePath);
6644
- return { path: absPath, relativePath, category };
6645
- });
6646
- evalFiles.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
6647
- return evalFiles;
6648
- }
6649
- function getCategories(files) {
6650
- const categories = /* @__PURE__ */ new Set();
6651
- for (const file of files) {
6652
- categories.add(file.category);
6653
- }
6654
- const sorted = Array.from(categories);
6655
- sorted.sort();
6656
- return sorted;
6657
- }
6658
- function filterByCategory(files, category) {
6659
- return files.filter((f) => f.category === category);
6660
- }
6661
-
6662
7091
  export {
6663
7092
  resolveEvalPaths,
6664
7093
  findRepoRoot,
@@ -6714,6 +7143,7 @@ export {
6714
7143
  isRemoteRunId,
6715
7144
  getRemoteResultsStatus,
6716
7145
  syncRemoteResults,
7146
+ confirmRemoteResultsMerge,
6717
7147
  listMergedResultFiles,
6718
7148
  findRunById,
6719
7149
  ensureRemoteRunAvailable,
@@ -6723,9 +7153,9 @@ export {
6723
7153
  maybeAutoExportRunArtifacts,
6724
7154
  resolveRunCacheFile,
6725
7155
  loadRunCache,
6726
- runEvalCommand,
6727
7156
  discoverEvalFiles,
6728
7157
  getCategories,
6729
- filterByCategory
7158
+ filterByCategory,
7159
+ runEvalCommand
6730
7160
  };
6731
- //# sourceMappingURL=chunk-CXHYTYHU.js.map
7161
+ //# sourceMappingURL=chunk-IBI7A5RP.js.map