agentv 5.3.0-next.1 → 5.3.2-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 (75) hide show
  1. package/README.md +70 -60
  2. package/dist/{artifact-writer-JFNIPMKW.js → artifact-writer-KJEOROKQ.js} +5 -5
  3. package/dist/{chunk-6ZZCDZPD.js → chunk-6262OKXM.js} +21774 -21034
  4. package/dist/chunk-6262OKXM.js.map +1 -0
  5. package/dist/chunk-AQ5BIAXF.js +604 -0
  6. package/dist/chunk-AQ5BIAXF.js.map +1 -0
  7. package/dist/chunk-BV5VQLI2.js +2 -0
  8. package/dist/{chunk-V52ATPTT.js → chunk-JGSRUJZQ.js} +186 -32
  9. package/dist/chunk-JGSRUJZQ.js.map +1 -0
  10. package/dist/chunk-MMDLXYBX.js +721 -0
  11. package/dist/chunk-MMDLXYBX.js.map +1 -0
  12. package/dist/{chunk-FVR4RQFK.js → chunk-QSK3PC44.js} +1131 -580
  13. package/dist/chunk-QSK3PC44.js.map +1 -0
  14. package/dist/chunk-TEEXVJWM.js +2 -0
  15. package/dist/{chunk-BHKQHG26.js → chunk-WIAJ7JAV.js} +186 -64
  16. package/dist/chunk-WIAJ7JAV.js.map +1 -0
  17. package/dist/cli.d.ts +1 -0
  18. package/dist/cli.js +17517 -10
  19. package/dist/cli.js.map +1 -1
  20. package/dist/config.d.ts +2 -0
  21. package/dist/{ts-eval-loader-DQDYRULE-V3377FOL.js → config.js} +7 -7
  22. package/dist/config.js.map +1 -0
  23. package/dist/contracts-DsZmZLl8.d.ts +742 -0
  24. package/dist/contracts.d.ts +2 -0
  25. package/dist/contracts.js +28 -0
  26. package/dist/contracts.js.map +1 -0
  27. package/dist/dashboard/assets/{index-DNgf3qJ2.js → index-BfqOlLOF.js} +1 -1
  28. package/dist/dashboard/assets/index-Bh8lpGce.css +1 -0
  29. package/dist/dashboard/assets/index-oPR3ywQb.js +121 -0
  30. package/dist/dashboard/index.html +2 -2
  31. package/dist/{dist-6Z7U473R.js → dist-A3SGR7TW.js} +30 -18
  32. package/dist/dist-A3SGR7TW.js.map +1 -0
  33. package/dist/index.d.ts +4 -0
  34. package/dist/index.js +137 -18
  35. package/dist/{interactive-RUY3OCBI.js → interactive-DL7N2C7K.js} +24 -24
  36. package/dist/interactive-DL7N2C7K.js.map +1 -0
  37. package/dist/provider.d.ts +2 -0
  38. package/dist/provider.js +24 -0
  39. package/dist/provider.js.map +1 -0
  40. package/dist/sdk.d.ts +802 -0
  41. package/dist/sdk.js +145 -0
  42. package/dist/sdk.js.map +1 -0
  43. package/dist/skills/agentv-bench/SKILL.md +14 -13
  44. package/dist/skills/agentv-bench/agents/analyzer.md +1 -1
  45. package/dist/skills/agentv-bench/agents/executor.md +1 -1
  46. package/dist/skills/agentv-bench/references/autoresearch.md +9 -9
  47. package/dist/skills/agentv-bench/references/environment-adaptation.md +4 -4
  48. package/dist/skills/agentv-bench/references/eval-yaml-spec.md +30 -47
  49. package/dist/skills/agentv-bench/references/schemas.md +44 -60
  50. package/dist/skills/agentv-bench/references/subagent-pipeline.md +20 -18
  51. package/dist/skills/agentv-eval-migrations/SKILL.md +13 -0
  52. package/dist/skills/agentv-eval-migrations/references/breaking-changes.md +56 -39
  53. package/dist/skills/agentv-eval-writer/SKILL.md +101 -60
  54. package/dist/skills/agentv-eval-writer/references/custom-evaluators.md +15 -10
  55. package/dist/skills/agentv-eval-writer/references/eval.schema.json +4543 -5189
  56. package/dist/skills/agentv-eval-writer/references/python-helpers.md +2 -2
  57. package/dist/skills/agentv-eval-writer/references/rubric-evaluator.md +20 -3
  58. package/dist/templates/.agentv/providers.yaml +42 -0
  59. package/dist/templates/.env.example +2 -2
  60. package/dist/ts-eval-loader-3G5GEAEC-6F52JLPP.js +18 -0
  61. package/dist/ts-eval-loader-3G5GEAEC-6F52JLPP.js.map +1 -0
  62. package/package.json +29 -4
  63. package/dist/chunk-6ZZCDZPD.js.map +0 -1
  64. package/dist/chunk-BHKQHG26.js.map +0 -1
  65. package/dist/chunk-FVR4RQFK.js.map +0 -1
  66. package/dist/chunk-T32NL3E6.js +0 -17973
  67. package/dist/chunk-T32NL3E6.js.map +0 -1
  68. package/dist/chunk-V52ATPTT.js.map +0 -1
  69. package/dist/dashboard/assets/index-D_bokML8.css +0 -1
  70. package/dist/dashboard/assets/index-r_jSJmlw.js +0 -121
  71. package/dist/interactive-RUY3OCBI.js.map +0 -1
  72. package/dist/templates/.agentv/targets.yaml +0 -97
  73. /package/dist/{artifact-writer-JFNIPMKW.js.map → artifact-writer-KJEOROKQ.js.map} +0 -0
  74. /package/dist/{dist-6Z7U473R.js.map → chunk-BV5VQLI2.js.map} +0 -0
  75. /package/dist/{ts-eval-loader-DQDYRULE-V3377FOL.js.map → chunk-TEEXVJWM.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
2
2
  import {
3
3
  writeArtifactsFromResults
4
- } from "./chunk-BHKQHG26.js";
4
+ } from "./chunk-WIAJ7JAV.js";
5
5
  import {
6
6
  RunBudgetTracker,
7
7
  buildWipBranchName,
@@ -18,12 +18,12 @@ import {
18
18
  resolveResultsRepoRunsDir,
19
19
  setupWipWorktree,
20
20
  syncResultsRepoForProject
21
- } from "./chunk-V52ATPTT.js";
21
+ } from "./chunk-JGSRUJZQ.js";
22
22
  import {
23
23
  AGENTV_LOCAL_CONFIG_FILE_NAME,
24
24
  AGENTV_LOCAL_CONFIG_YML_FILE_NAME,
25
25
  CLI_PLACEHOLDERS,
26
- COMMON_TARGET_SETTINGS,
26
+ COMMON_PROVIDER_SETTINGS,
27
27
  DEFAULT_EVAL_PATTERNS,
28
28
  DEFAULT_THRESHOLD,
29
29
  KNOWN_PROVIDERS,
@@ -51,7 +51,9 @@ import {
51
51
  interpolateEnv,
52
52
  isAgentVConfigFileName,
53
53
  isGraderKind,
54
- listTargetNames,
54
+ isScenarioFileReference,
55
+ isTypeScriptEvalConfigFileName,
56
+ listProviderLabels,
55
57
  loadCasesFromDirectory,
56
58
  loadCasesFromFile,
57
59
  loadConfig,
@@ -60,17 +62,19 @@ import {
60
62
  loadTestSuiteFromYamlObject,
61
63
  normalizeComposableConfigGraph,
62
64
  normalizeMetricsArtifactWire,
65
+ normalizeProviderDefinition,
63
66
  normalizeResultRow,
64
- normalizeTargetDefinition,
65
67
  parseJsonlResults,
66
68
  parseYamlValue,
67
- readTargetDefinitions,
69
+ readProviderDefinitions,
68
70
  readTestSuiteMetadata,
69
71
  resolveConfigFieldReferences,
70
72
  resolveEnvironmentRecipe,
71
73
  resolveFileReference,
74
+ resolveProviderDefinition,
75
+ resolveProviderDefinitionEnvironments,
72
76
  resolveResultsConfigForProject,
73
- resolveTargetDefinition,
77
+ resolveScenarioFileReference,
74
78
  runEvaluation,
75
79
  serializeEvaluationResultWire,
76
80
  shouldEnableCache,
@@ -83,8 +87,9 @@ import {
83
87
  toSnakeCaseDeep,
84
88
  traceEnvelopeToTraceSummary,
85
89
  traceEnvelopeToTranscriptMessages,
90
+ typeScriptEvalConfigGlob,
86
91
  writeInitialRunSummaryArtifact
87
- } from "./chunk-6ZZCDZPD.js";
92
+ } from "./chunk-6262OKXM.js";
88
93
 
89
94
  // src/commands/eval/shared.ts
90
95
  import { constants } from "node:fs";
@@ -215,7 +220,7 @@ function agentSkillsToAgentVYamlObject(suite) {
215
220
 
216
221
  // src/commands/eval/shared.ts
217
222
  function isNativeEvalFile(filePath) {
218
- return /\.(ya?ml|jsonl|[cm]?ts)$/i.test(filePath);
223
+ return /\.(ya?ml|jsonl)$/i.test(filePath) || isTypeScriptEvalConfigFileName(filePath);
219
224
  }
220
225
  function shouldInspectJsonPath(filePath) {
221
226
  const base = path.basename(filePath).toLowerCase();
@@ -265,7 +270,7 @@ async function resolveEvalPaths(evalPaths, cwd, options = {}) {
265
270
  continue;
266
271
  }
267
272
  if (candidateStats.isDirectory()) {
268
- const filePattern = options.allowReadAdapters ? "{suite.yaml,suite.yml,*.eval.yaml,*.eval.yml,eval.yaml,eval.yml,*.eval.ts,*.eval.mts,evals.json,*.evals.json}" : "{suite.yaml,suite.yml,*.eval.yaml,*.eval.yml,eval.yaml,eval.yml,*.eval.ts,*.eval.mts}";
273
+ const filePattern = options.allowReadAdapters ? `{suite.yaml,suite.yml,*.eval.yaml,*.eval.yml,eval.yaml,eval.yml,${typeScriptEvalConfigGlob()},evals.json,*.evals.json}` : `{suite.yaml,suite.yml,*.eval.yaml,*.eval.yml,eval.yaml,eval.yml,${typeScriptEvalConfigGlob()}}`;
269
274
  const dirGlob = path.posix.join(candidatePath.replace(/\\/g, "/"), `**/${filePattern}`);
270
275
  const dirMatches = await fg(dirGlob, {
271
276
  absolute: true,
@@ -344,83 +349,6 @@ async function findRepoRoot(start) {
344
349
  return fallback;
345
350
  }
346
351
 
347
- // package.json
348
- var package_default = {
349
- name: "agentv",
350
- version: "5.3.0-next.1",
351
- description: "CLI entry point for AgentV",
352
- type: "module",
353
- repository: {
354
- type: "git",
355
- url: "https://github.com/EntityProcess/agentv.git"
356
- },
357
- homepage: "https://agentv.dev",
358
- bugs: {
359
- url: "https://github.com/EntityProcess/agentv/issues"
360
- },
361
- bin: {
362
- agentv: "./dist/cli.js"
363
- },
364
- files: ["dist", "README.md"],
365
- scripts: {
366
- dev: "bun src/cli.ts",
367
- build: "(cd ../../packages/sdk && bun run build) && tsup && bun run copy-readme",
368
- "copy-readme": `bun -e "import { cpSync } from 'fs'; cpSync('../../README.md', 'README.md')"`,
369
- prepublishOnly: `node -e "if(process.env.ALLOW_PUBLISH!=='1'){console.error('ERROR: Use bun run publish:next, then bun run promote:latest');process.exit(1)}"`,
370
- typecheck: "(cd ../../packages/sdk && bun run build) && tsc --noEmit",
371
- lint: "biome check .",
372
- format: "biome format --write .",
373
- fix: "biome check --write .",
374
- test: "(cd ../../packages/sdk && bun run build) && bun test",
375
- "test:watch": "bun test --watch"
376
- },
377
- dependencies: {
378
- "@hono/node-server": "^1.19.11",
379
- "@inquirer/prompts": "^8.2.1",
380
- "@earendil-works/pi-ai": "^0.74.0",
381
- "cmd-ts": "^0.14.3",
382
- dotenv: "^16.4.5",
383
- "fast-glob": "^3.3.3",
384
- hono: "^4.12.9",
385
- json5: "^2.2.3",
386
- micromatch: "^4.0.8",
387
- semver: "^7.7.4",
388
- yaml: "^2.8.3"
389
- },
390
- peerDependencies: {
391
- "@anthropic-ai/claude-agent-sdk": "^0.2.89",
392
- "@github/copilot-sdk": "^1.0.3",
393
- "@openai/codex-sdk": "^0.136.0",
394
- "@earendil-works/pi-coding-agent": "^0.74.0"
395
- },
396
- peerDependenciesMeta: {
397
- "@anthropic-ai/claude-agent-sdk": {
398
- optional: true
399
- },
400
- "@github/copilot-sdk": {
401
- optional: true
402
- },
403
- "@openai/codex-sdk": {
404
- optional: true
405
- },
406
- "@earendil-works/pi-coding-agent": {
407
- optional: true
408
- }
409
- },
410
- optionalDependencies: {
411
- "@anthropic-ai/claude-agent-sdk": "^0.2.89",
412
- "@github/copilot-sdk": "^1.0.3",
413
- "@openai/codex-sdk": "^0.136.0",
414
- "@earendil-works/pi-coding-agent": "^0.74.0"
415
- },
416
- devDependencies: {
417
- "@agentv/core": "workspace:*",
418
- "@agentv/sdk": "workspace:*",
419
- "@types/semver": "^7.7.1",
420
- execa: "^9.3.0"
421
- }
422
- };
423
-
424
352
  // src/commands/eval/result-layout.ts
425
353
  import { existsSync, readdirSync, statSync } from "node:fs";
426
354
  import path2 from "node:path";
@@ -558,11 +486,17 @@ function relativeRunPathFromCwd(cwd, runDir) {
558
486
  return parts.join(path2.posix.sep);
559
487
  }
560
488
 
561
- // src/utils/targets.ts
489
+ // src/utils/providers.ts
562
490
  import { constants as constants2 } from "node:fs";
563
- import { access as access2 } from "node:fs/promises";
491
+ import { access as access2, stat as stat2 } from "node:fs/promises";
564
492
  import path3 from "node:path";
565
- var TARGET_FILE_CANDIDATES = [
493
+ var PROVIDER_FILE_CANDIDATES = [
494
+ "providers.yaml",
495
+ "providers.yml",
496
+ path3.join(".agentv", "providers.yaml"),
497
+ path3.join(".agentv", "providers.yml")
498
+ ];
499
+ var LEGACY_TARGET_FILE_CANDIDATES = [
566
500
  "targets.yaml",
567
501
  "targets.yml",
568
502
  path3.join(".agentv", "targets.yaml"),
@@ -576,20 +510,71 @@ async function fileExists(filePath) {
576
510
  return false;
577
511
  }
578
512
  }
579
- async function discoverTargetsFile(options) {
580
- const { explicitPath, testFilePath, repoRoot, cwd } = options;
513
+ async function pathKind(filePath) {
514
+ try {
515
+ const info = await stat2(filePath);
516
+ if (info.isDirectory()) {
517
+ return "directory";
518
+ }
519
+ if (info.isFile()) {
520
+ return "file";
521
+ }
522
+ return void 0;
523
+ } catch {
524
+ return void 0;
525
+ }
526
+ }
527
+ function isLegacyTargetFile(filePath) {
528
+ return /^targets\.ya?ml$/i.test(path3.basename(filePath));
529
+ }
530
+ function removedTargetsFileError(filePath) {
531
+ return new Error(
532
+ `Authored targets.yaml files were removed. Rename ${filePath} to providers.yaml and use --providers to specify it explicitly.`
533
+ );
534
+ }
535
+ async function findProviderFileInDirectory(directory) {
536
+ for (const candidate of PROVIDER_FILE_CANDIDATES) {
537
+ const fullPath = path3.join(directory, candidate);
538
+ if (await pathKind(fullPath) === "file") {
539
+ return fullPath;
540
+ }
541
+ }
542
+ return void 0;
543
+ }
544
+ async function findLegacyGeneratedTargetFileInDirectory(directory) {
545
+ for (const candidate of LEGACY_TARGET_FILE_CANDIDATES) {
546
+ const fullPath = path3.join(directory, candidate);
547
+ if (await pathKind(fullPath) === "file") {
548
+ return fullPath;
549
+ }
550
+ }
551
+ return void 0;
552
+ }
553
+ async function discoverProvidersFile(options) {
554
+ const { explicitPath, testFilePath, repoRoot, cwd, allowLegacyTargetFiles = false } = options;
581
555
  if (explicitPath) {
582
556
  const resolvedExplicit = path3.resolve(explicitPath);
583
- if (await fileExists(resolvedExplicit)) {
557
+ const kind = await pathKind(resolvedExplicit);
558
+ if (kind === "file") {
559
+ if (!allowLegacyTargetFiles && isLegacyTargetFile(resolvedExplicit)) {
560
+ throw removedTargetsFileError(resolvedExplicit);
561
+ }
584
562
  return resolvedExplicit;
585
563
  }
586
- for (const candidate of TARGET_FILE_CANDIDATES) {
587
- const nested = path3.join(resolvedExplicit, candidate);
588
- if (await fileExists(nested)) {
589
- return nested;
564
+ if (kind === "directory") {
565
+ const providerFile = await findProviderFileInDirectory(resolvedExplicit);
566
+ if (providerFile) {
567
+ return providerFile;
568
+ }
569
+ const legacyTargetFile = await findLegacyGeneratedTargetFileInDirectory(resolvedExplicit);
570
+ if (legacyTargetFile) {
571
+ if (allowLegacyTargetFiles) {
572
+ return legacyTargetFile;
573
+ }
574
+ throw removedTargetsFileError(legacyTargetFile);
590
575
  }
591
576
  }
592
- throw new Error(`targets.yaml not found at provided path: ${resolvedExplicit}`);
577
+ throw new Error(`providers.yaml not found at provided path: ${resolvedExplicit}`);
593
578
  }
594
579
  const directories = [...buildDirectoryChain(testFilePath, repoRoot)];
595
580
  const resolvedCwd = path3.resolve(cwd);
@@ -597,14 +582,23 @@ async function discoverTargetsFile(options) {
597
582
  directories.push(resolvedCwd);
598
583
  }
599
584
  for (const directory of directories) {
600
- for (const candidate of TARGET_FILE_CANDIDATES) {
601
- const fullPath = path3.join(directory, candidate);
602
- if (await fileExists(fullPath)) {
603
- return fullPath;
585
+ const providerFile = await findProviderFileInDirectory(directory);
586
+ if (providerFile) {
587
+ return providerFile;
588
+ }
589
+ }
590
+ for (const directory of directories) {
591
+ const legacyTargetFile = await findLegacyGeneratedTargetFileInDirectory(directory);
592
+ if (legacyTargetFile) {
593
+ if (allowLegacyTargetFiles) {
594
+ return legacyTargetFile;
604
595
  }
596
+ throw removedTargetsFileError(legacyTargetFile);
605
597
  }
606
598
  }
607
- throw new Error("Unable to locate targets.yaml. Use --targets to specify the file explicitly.");
599
+ throw new Error(
600
+ "Unable to locate providers.yaml. Use --providers to specify the file explicitly."
601
+ );
608
602
  }
609
603
 
610
604
  // src/commands/eval/discover.ts
@@ -648,7 +642,7 @@ function filterByCategory(files, category) {
648
642
  import { constants as constants4, existsSync as existsSync5, mkdirSync } from "node:fs";
649
643
  import { access as access5, readFile as readFile8 } from "node:fs/promises";
650
644
  import { createRequire as createNodeRequire } from "node:module";
651
- import path12 from "node:path";
645
+ import path13 from "node:path";
652
646
  import { pathToFileURL } from "node:url";
653
647
 
654
648
  // ../../node_modules/.bun/yocto-queue@1.2.2/node_modules/yocto-queue/index.js
@@ -792,6 +786,110 @@ function validateConcurrency(concurrency) {
792
786
 
793
787
  // src/version-check.ts
794
788
  import { coerce, satisfies, validRange } from "semver";
789
+
790
+ // package.json
791
+ var package_default = {
792
+ name: "agentv",
793
+ version: "5.3.2-next.1",
794
+ description: "CLI entry point for AgentV",
795
+ type: "module",
796
+ repository: {
797
+ type: "git",
798
+ url: "https://github.com/EntityProcess/agentv.git"
799
+ },
800
+ homepage: "https://agentv.dev",
801
+ bugs: {
802
+ url: "https://github.com/EntityProcess/agentv/issues"
803
+ },
804
+ bin: {
805
+ agentv: "./dist/cli.js"
806
+ },
807
+ main: "./dist/index.js",
808
+ types: "./dist/index.d.ts",
809
+ exports: {
810
+ ".": {
811
+ types: "./dist/index.d.ts",
812
+ import: "./dist/index.js"
813
+ },
814
+ "./sdk": {
815
+ types: "./dist/sdk.d.ts",
816
+ import: "./dist/sdk.js"
817
+ },
818
+ "./provider": {
819
+ types: "./dist/provider.d.ts",
820
+ import: "./dist/provider.js"
821
+ },
822
+ "./contracts": {
823
+ types: "./dist/contracts.d.ts",
824
+ import: "./dist/contracts.js"
825
+ },
826
+ "./config": {
827
+ types: "./dist/config.d.ts",
828
+ import: "./dist/config.js"
829
+ },
830
+ "./package.json": "./package.json"
831
+ },
832
+ files: ["dist", "README.md"],
833
+ scripts: {
834
+ dev: "bun src/cli.ts",
835
+ build: "tsup && bun run copy-readme",
836
+ "copy-readme": `bun -e "import { cpSync } from 'fs'; cpSync('../../README.md', 'README.md')"`,
837
+ prepublishOnly: `node -e "if(process.env.ALLOW_PUBLISH!=='1'){console.error('ERROR: Use bun run publish:next, then bun run promote:latest');process.exit(1)}"`,
838
+ typecheck: "tsc --noEmit",
839
+ lint: "biome check .",
840
+ format: "biome format --write .",
841
+ fix: "biome check --write .",
842
+ test: "bun test",
843
+ "test:watch": "bun test --watch"
844
+ },
845
+ dependencies: {
846
+ "@hono/node-server": "^1.19.11",
847
+ "@inquirer/prompts": "^8.2.1",
848
+ "@earendil-works/pi-ai": "^0.74.0",
849
+ "cmd-ts": "^0.14.3",
850
+ dotenv: "^16.4.5",
851
+ "fast-glob": "^3.3.3",
852
+ hono: "^4.12.9",
853
+ json5: "^2.2.3",
854
+ micromatch: "^4.0.8",
855
+ semver: "^7.7.4",
856
+ yaml: "^2.8.3"
857
+ },
858
+ peerDependencies: {
859
+ "@anthropic-ai/claude-agent-sdk": "^0.2.89",
860
+ "@github/copilot-sdk": "^1.0.3",
861
+ "@openai/codex-sdk": "^0.136.0",
862
+ "@earendil-works/pi-coding-agent": "^0.74.0"
863
+ },
864
+ peerDependenciesMeta: {
865
+ "@anthropic-ai/claude-agent-sdk": {
866
+ optional: true
867
+ },
868
+ "@github/copilot-sdk": {
869
+ optional: true
870
+ },
871
+ "@openai/codex-sdk": {
872
+ optional: true
873
+ },
874
+ "@earendil-works/pi-coding-agent": {
875
+ optional: true
876
+ }
877
+ },
878
+ optionalDependencies: {
879
+ "@anthropic-ai/claude-agent-sdk": "^0.2.89",
880
+ "@github/copilot-sdk": "^1.0.3",
881
+ "@openai/codex-sdk": "^0.136.0",
882
+ "@earendil-works/pi-coding-agent": "^0.74.0"
883
+ },
884
+ devDependencies: {
885
+ "@agentv/core": "workspace:*",
886
+ "@agentv/sdk": "workspace:*",
887
+ "@types/semver": "^7.7.1",
888
+ execa: "^9.3.0"
889
+ }
890
+ };
891
+
892
+ // src/version-check.ts
795
893
  var ANSI_YELLOW = "\x1B[33m";
796
894
  var ANSI_RED = "\x1B[31m";
797
895
  var ANSI_RESET = "\x1B[0m";
@@ -856,24 +954,6 @@ function manifestBaseDir(indexPath) {
856
954
  const dir = path5.dirname(indexPath);
857
955
  return path5.basename(dir) === ".internal" ? path5.dirname(dir) : dir;
858
956
  }
859
- function mapGradingAssertions(value) {
860
- if (!Array.isArray(value)) {
861
- return void 0;
862
- }
863
- return value.map((assertion) => {
864
- const record = assertion;
865
- return {
866
- text: String(record.text ?? ""),
867
- passed: Boolean(record.passed),
868
- evidence: typeof record.evidence === "string" ? record.evidence : void 0
869
- };
870
- });
871
- }
872
- function readGradingAssertionResults(record) {
873
- return mapGradingAssertions(
874
- Array.isArray(record.assertion_results) ? record.assertion_results : record.assertions
875
- );
876
- }
877
957
  function readNestedGradingScores(record) {
878
958
  if (Array.isArray(record.component_results)) {
879
959
  return record.component_results;
@@ -881,12 +961,6 @@ function readNestedGradingScores(record) {
881
961
  if (Array.isArray(record.scores)) {
882
962
  return record.scores;
883
963
  }
884
- if (Array.isArray(record.graders)) {
885
- return record.graders;
886
- }
887
- if (Array.isArray(record.evaluators)) {
888
- return record.evaluators;
889
- }
890
964
  return void 0;
891
965
  }
892
966
  function componentLabel(component) {
@@ -923,7 +997,7 @@ function collectComponentAssertions(value) {
923
997
  });
924
998
  }
925
999
  function mapGradingEvaluator(evaluator) {
926
- const pass = typeof evaluator.pass === "boolean" ? evaluator.pass : evaluator.verdict === "pass" || typeof evaluator.score === "number" && evaluator.score >= 0.8;
1000
+ const pass = evaluator.pass === true;
927
1001
  const verdict = pass ? "pass" : "fail";
928
1002
  const details = evaluator.details && typeof evaluator.details === "object" && !Array.isArray(evaluator.details) ? evaluator.details : void 0;
929
1003
  const assertion = evaluator.assertion && typeof evaluator.assertion === "object" && !Array.isArray(evaluator.assertion) ? evaluator.assertion : void 0;
@@ -937,7 +1011,7 @@ function mapGradingEvaluator(evaluator) {
937
1011
  if (nestedAssertions.length > 0) {
938
1012
  return nestedAssertions;
939
1013
  }
940
- return readGradingAssertionResults(evaluator) ?? [mapComponentAssertion(evaluator)];
1014
+ return [mapComponentAssertion(evaluator)];
941
1015
  })(),
942
1016
  scores: mapGradingEvaluators(readNestedGradingScores(evaluator)),
943
1017
  weight: typeof evaluator.weight === "number" ? evaluator.weight : void 0,
@@ -1061,9 +1135,7 @@ function hydrateManifestRecord(baseDir, record, options) {
1061
1135
  const testId = record.test_id ?? "unknown";
1062
1136
  const gradingAssertions = grading ? collectComponentAssertions(grading.component_results) : void 0;
1063
1137
  const gradingRecord = grading;
1064
- const gradingScores = mapGradingEvaluators(
1065
- gradingRecord?.component_results ?? gradingRecord?.graders ?? gradingRecord?.evaluators
1066
- );
1138
+ const gradingScores = mapGradingEvaluators(gradingRecord?.component_results);
1067
1139
  return {
1068
1140
  timestamp: record.timestamp,
1069
1141
  testId,
@@ -1079,11 +1151,7 @@ function hydrateManifestRecord(baseDir, record, options) {
1079
1151
  passed: assertion.passed,
1080
1152
  evidence: assertion.evidence
1081
1153
  })),
1082
- scores: (
1083
- // `evaluators` was renamed to `graders` in v4.13 — read both for backwards compat with old artifacts.
1084
- // TODO: remove `evaluators` fallback once old run directories are no longer in use.
1085
- gradingScores ?? record.scores
1086
- ),
1154
+ scores: gradingScores ?? record.scores,
1087
1155
  tokenUsage: metrics?.tokens ? {
1088
1156
  input: metrics.tokens.input,
1089
1157
  output: metrics.tokens.output,
@@ -1147,8 +1215,16 @@ function loadLightweightResults(sourceFile) {
1147
1215
  executionStatus: record.execution_status,
1148
1216
  error: record.error,
1149
1217
  costUsd: record.cost_usd,
1218
+ durationMs: record.duration_ms,
1219
+ tokenUsage: record.token_usage,
1150
1220
  timestamp: record.timestamp,
1151
- runtimeSource: record.runtime_source
1221
+ runtimeSource: record.runtime_source,
1222
+ resultDir: record.result_dir,
1223
+ outputPath: record.output_path,
1224
+ answerPath: record.answer_path,
1225
+ gradingPath: record.grading_path,
1226
+ transcriptPath: record.transcript_path,
1227
+ metricsPath: record.metrics_path
1152
1228
  }));
1153
1229
  }
1154
1230
 
@@ -1888,6 +1964,16 @@ function relativeRunPathFromManifestPath(relativeManifestPath) {
1888
1964
  const manifestDir = path7.posix.dirname(relativeManifestPath);
1889
1965
  return path7.posix.basename(manifestDir) === ".internal" ? path7.posix.dirname(manifestDir) : manifestDir;
1890
1966
  }
1967
+ function formatPendingMergeWarnings(pendingMerge) {
1968
+ if (!pendingMerge) {
1969
+ return [];
1970
+ }
1971
+ const { temp_branch, target_branch, compare_url } = pendingMerge;
1972
+ const mergeSuffix = compare_url ? `: ${compare_url}` : ".";
1973
+ return [
1974
+ `Warning: results were pushed to '${temp_branch}' instead \u2014 they are not lost. Merge that branch into ${target_branch} with a pull request${mergeSuffix}`
1975
+ ];
1976
+ }
1891
1977
  async function maybeAutoExportRunArtifacts(payload) {
1892
1978
  const config = await loadNormalizedResultsConfig(
1893
1979
  payload.cwd,
@@ -1912,6 +1998,9 @@ async function maybeAutoExportRunArtifacts(payload) {
1912
1998
  throw new Error(pushResult.block_reason ?? "Results branch push conflict");
1913
1999
  }
1914
2000
  console.warn(`Warning: skipping results export: ${pushResult.block_reason}`);
2001
+ for (const line of formatPendingMergeWarnings(pushResult.pending_merge)) {
2002
+ console.warn(line);
2003
+ }
1915
2004
  return "failed";
1916
2005
  }
1917
2006
  if (!pushResult.changed) {
@@ -2714,10 +2803,13 @@ function formatMatrixSummary(results) {
2714
2803
  return lines.join("\n");
2715
2804
  }
2716
2805
 
2806
+ // src/commands/eval/targets.ts
2807
+ import path12 from "node:path";
2808
+
2717
2809
  // ../../packages/core/dist/evaluation/validation/index.js
2718
2810
  import { readFile as readFile2 } from "node:fs/promises";
2719
2811
  import path11 from "node:path";
2720
- import { readFile as readFile22, readdir, realpath, stat as stat2 } from "node:fs/promises";
2812
+ import { readFile as readFile22, readdir, realpath, stat as stat3 } from "node:fs/promises";
2721
2813
  import path22 from "node:path";
2722
2814
  import fg3 from "fast-glob";
2723
2815
  import { readFile as readFile3 } from "node:fs/promises";
@@ -2738,6 +2830,10 @@ async function detectFileType(filePath) {
2738
2830
  const content = await readFile2(filePath, "utf8");
2739
2831
  const parsed = parseYamlValue(content);
2740
2832
  if (Array.isArray(parsed)) {
2833
+ const inferred = inferFileTypeFromPath(filePath);
2834
+ if (inferred === "targets") {
2835
+ return inferred;
2836
+ }
2741
2837
  return "cases";
2742
2838
  }
2743
2839
  if (typeof parsed !== "object" || parsed === null) {
@@ -2772,7 +2868,7 @@ function inferFileTypeFromPath(filePath) {
2772
2868
  if (isAgentVConfigFileName(basename)) {
2773
2869
  return "config";
2774
2870
  }
2775
- if (basename === "targets.yaml" || basename === "targets.yml") {
2871
+ if (basename === "providers.yaml" || basename === "providers.yml") {
2776
2872
  return "targets";
2777
2873
  }
2778
2874
  }
@@ -2799,6 +2895,7 @@ var ASSERTION_TYPES_WITH_ARRAY_VALUE = /* @__PURE__ */ new Set([
2799
2895
  var PROMPTFOO_ASSERTION_TYPES = /* @__PURE__ */ new Set([
2800
2896
  "assert-set",
2801
2897
  "llm-rubric",
2898
+ "agent-rubric",
2802
2899
  "javascript",
2803
2900
  "python",
2804
2901
  "webhook",
@@ -2812,11 +2909,9 @@ var REMOVED_ASSERTION_TYPE_REPLACEMENTS = /* @__PURE__ */ new Map([
2812
2909
  ["rubrics", "llm-rubric with value"],
2813
2910
  ["rubric", "llm-rubric with value"],
2814
2911
  ["script-grader", "script"],
2815
- ["code-judge", "script"],
2816
- ["llm-judge", "llm-grader"]
2912
+ ["code-judge", "script"]
2817
2913
  ]);
2818
2914
  var UNSUPPORTED_PROMPTFOO_ASSERTION_TYPES = /* @__PURE__ */ new Set([
2819
- "agent-rubric",
2820
2915
  "answer-relevance",
2821
2916
  "bleu",
2822
2917
  "classifier",
@@ -2909,6 +3004,9 @@ function staleToolTrajectoryMessage(value) {
2909
3004
  function staleAuthoredAssertionMessage(value) {
2910
3005
  if (typeof value.type !== "string") return void 0;
2911
3006
  const type = value.type.replace(/_/g, "-");
3007
+ if (type === "llm-grader" || type === "llm-judge") {
3008
+ return "Authored assertion type 'llm-grader' has been removed. Use 'llm-rubric' for free-form rubric checks or 'agent-rubric' for agentic rubric checks.";
3009
+ }
2912
3010
  if (type === "skill-trigger") return staleSkillTriggerMessage(value);
2913
3011
  if (type === "tool-trajectory") return staleToolTrajectoryMessage(value);
2914
3012
  return void 0;
@@ -2939,6 +3037,7 @@ var KNOWN_TOP_LEVEL_FIELDS = /* @__PURE__ */ new Set([
2939
3037
  "input_files",
2940
3038
  "prompts",
2941
3039
  "tests",
3040
+ "providers",
2942
3041
  "graders",
2943
3042
  "defaults",
2944
3043
  "environment",
@@ -2974,7 +3073,6 @@ var KNOWN_DEFAULT_TEST_FIELDS = /* @__PURE__ */ new Set([
2974
3073
  "provider",
2975
3074
  "providers",
2976
3075
  "prompts",
2977
- "provider_output",
2978
3076
  "assert",
2979
3077
  "assert_scoring_function",
2980
3078
  "options",
@@ -2992,8 +3090,6 @@ var KNOWN_EVALUATE_OPTION_FIELDS = /* @__PURE__ */ new Set([
2992
3090
  "max_eval_time_ms",
2993
3091
  "filter_range"
2994
3092
  ]);
2995
- var KNOWN_REPEAT_FIELDS = /* @__PURE__ */ new Set(["count", "strategy", "early_exit", "cost_limit_usd"]);
2996
- var KNOWN_REPEAT_STRATEGIES = /* @__PURE__ */ new Set(["pass_any", "pass_all", "mean", "confidence_interval"]);
2997
3093
  var KNOWN_TEST_EXECUTION_FIELDS = /* @__PURE__ */ new Set([
2998
3094
  "assert",
2999
3095
  "skip_defaults",
@@ -3007,9 +3103,13 @@ var KNOWN_TEST_EXECUTION_FIELDS = /* @__PURE__ */ new Set([
3007
3103
  "workspace"
3008
3104
  ]);
3009
3105
  var REMOVED_TOP_LEVEL_FIELDS = /* @__PURE__ */ new Map([
3106
+ [
3107
+ "experiment",
3108
+ "Top-level 'experiment' has been removed from authored eval YAML. Use tags.experiment in the eval file or CLI --experiment at run time."
3109
+ ],
3010
3110
  [
3011
3111
  "imports",
3012
- "Top-level 'imports' is not supported. Run eval files directly with CLI multi-file selection and tags for grouping. For raw case files, use tests: file://... or string entries under tests. For reusable config, use prompts: file://..., default_test: file://..., and environment: file://... for coding-agent testbeds."
3112
+ "Top-level 'imports' is not supported. Run eval files directly with CLI multi-file selection and tags for grouping. For raw case files, use tests: file://... or string entries under tests. For reusable scenarios, use scenarios: [file://...]. For reusable config, use prompts: file://..., default_test: file://..., and environment: file://... for coding-agent testbeds."
3013
3113
  ],
3014
3114
  [
3015
3115
  "expected_output",
@@ -3023,20 +3123,29 @@ var REMOVED_TOP_LEVEL_FIELDS = /* @__PURE__ */ new Map([
3023
3123
  "workers",
3024
3124
  "'workers' has been removed from eval YAML. Set authored eval concurrency with evaluate_options.max_concurrency."
3025
3125
  ],
3026
- ["model", "Top-level 'model' is not part of eval YAML. Put model inside the target object."],
3126
+ ["model", "Top-level 'model' is not part of eval YAML. Put model inside providers[].config."],
3027
3127
  [
3028
3128
  "policy",
3029
3129
  "Top-level 'policy' is not part of eval YAML. Put repeat under evaluate_options.repeat, timeout_seconds and threshold at the top level, and budget_usd under evaluate_options."
3030
3130
  ],
3131
+ ["target", "Top-level 'target' has been removed. Use top-level 'providers' instead."],
3132
+ [
3133
+ "targets",
3134
+ "Top-level 'targets' has been removed. Use 'providers'; map targets[].id to providers[].label and targets[].provider to providers[].id."
3135
+ ],
3031
3136
  [
3032
- "providers",
3033
- "Top-level 'providers' is not a runtime alias in AgentV eval YAML. Use 'targets' for systems under test; provider names backend kind inside each target."
3137
+ "eval_cases",
3138
+ "Top-level 'eval_cases' has been removed from authored eval YAML. Use 'tests' instead."
3139
+ ],
3140
+ [
3141
+ "evalcases",
3142
+ "Top-level 'evalcases' has been removed from authored eval YAML. Use 'tests' instead."
3034
3143
  ],
3035
3144
  ["repeat", "Top-level 'repeat' has been removed. Use evaluate_options.repeat instead."],
3036
- ["runs", "Top-level 'runs' has been removed. Use evaluate_options.repeat.count instead."],
3145
+ ["runs", "Top-level 'runs' has been removed. Use a positive integer evaluate_options.repeat."],
3037
3146
  [
3038
3147
  "early_exit",
3039
- "Top-level 'early_exit' has been removed. Use evaluate_options.repeat.early_exit instead."
3148
+ "Top-level 'early_exit' has been removed. Use a positive integer evaluate_options.repeat."
3040
3149
  ],
3041
3150
  ["budget_usd", "Top-level 'budget_usd' has been removed. Use evaluate_options.budget_usd."],
3042
3151
  [
@@ -3046,11 +3155,11 @@ var REMOVED_TOP_LEVEL_FIELDS = /* @__PURE__ */ new Map([
3046
3155
  [
3047
3156
  "preprocessors",
3048
3157
  "Top-level 'preprocessors' has been removed from authored eval YAML. Use default_test.options.transform or assertion-level transform instead."
3049
- ]
3158
+ ],
3159
+ ["providerPromptMap", "Top-level 'providerPromptMap' is not supported. Use 'providers'."],
3160
+ ["provider_prompt_map", "Top-level 'provider_prompt_map' is not supported. Use 'providers'."]
3050
3161
  ]);
3051
3162
  var DEPRECATED_TOP_LEVEL_FIELDS = /* @__PURE__ */ new Map([
3052
- ["eval_cases", "'eval_cases' is deprecated. Use 'tests' instead."],
3053
- ["evalcases", "'evalcases' is deprecated. Use 'tests' instead."],
3054
3163
  ["evaluator", "'evaluator' is deprecated. Use 'assert' instead."]
3055
3164
  ]);
3056
3165
  var KNOWN_TEST_FIELDS = /* @__PURE__ */ new Set([
@@ -3061,7 +3170,6 @@ var KNOWN_TEST_FIELDS = /* @__PURE__ */ new Set([
3061
3170
  "provider",
3062
3171
  "providers",
3063
3172
  "prompts",
3064
- "provider_output",
3065
3173
  "input",
3066
3174
  "input_files",
3067
3175
  "assert",
@@ -3083,6 +3191,7 @@ var KNOWN_TEST_FIELDS = /* @__PURE__ */ new Set([
3083
3191
  "on_turn_failure",
3084
3192
  "window_size"
3085
3193
  ]);
3194
+ var KNOWN_SCENARIO_FIELDS = /* @__PURE__ */ new Set(["description", "config", "tests"]);
3086
3195
  var SUPPORTED_WORKSPACE_REPO_FIELDS = /* @__PURE__ */ new Set(["path", "repo", "commit", "ancestor", "sparse"]);
3087
3196
  var KNOWN_REMOVED_WORKSPACE_FIELDS = /* @__PURE__ */ new Set([
3088
3197
  "template",
@@ -3100,6 +3209,26 @@ REMOVED_TEST_FIELDS.set(
3100
3209
  "input",
3101
3210
  "tests[].input has been removed from authored eval YAML. Put prompt text or chat/system/user messages in top-level 'prompts' and put row-specific data in tests[].vars."
3102
3211
  );
3212
+ REMOVED_TEST_FIELDS.set(
3213
+ "provider_output",
3214
+ "tests[].provider_output is not supported in authored AgentV YAML. Use an explicit deterministic target such as provider: cli for fixed outputs, or use a replay/fixture target for captured provider responses."
3215
+ );
3216
+ REMOVED_TEST_FIELDS.set(
3217
+ "preprocessors",
3218
+ "tests[].preprocessors has been removed from authored eval YAML. Use tests[].options.transform or assertion-level transform instead."
3219
+ );
3220
+ REMOVED_TEST_FIELDS.set(
3221
+ "postprocess",
3222
+ "tests[].postprocess has been removed from authored eval YAML. Use tests[].options.transform instead."
3223
+ );
3224
+ REMOVED_TEST_FIELDS.set(
3225
+ "eval_cases",
3226
+ "tests[].eval_cases has been removed from authored eval YAML. Use 'tests' rows directly."
3227
+ );
3228
+ REMOVED_TEST_FIELDS.set(
3229
+ "evalcases",
3230
+ "tests[].evalcases has been removed from authored eval YAML. Use 'tests' rows directly."
3231
+ );
3103
3232
  var DEPRECATED_TEST_FIELDS = /* @__PURE__ */ new Map([
3104
3233
  ["evaluator", "'evaluator' is deprecated. Use 'assert' instead."],
3105
3234
  ["expected_outcome", "'expected_outcome' is deprecated. Use 'assert' instead."]
@@ -3188,14 +3317,6 @@ async function validateEvalFile(filePath) {
3188
3317
  };
3189
3318
  }
3190
3319
  validateMetadata(parsed, absolutePath, errors);
3191
- if (parsed.experiment !== void 0 && typeof parsed.experiment !== "string") {
3192
- errors.push({
3193
- severity: "error",
3194
- filePath: absolutePath,
3195
- location: "experiment",
3196
- message: "Top-level 'experiment' must be a string run/result grouping label."
3197
- });
3198
- }
3199
3320
  for (const key of Object.keys(parsed)) {
3200
3321
  const removedMessage = REMOVED_TOP_LEVEL_FIELDS.get(key);
3201
3322
  if (removedMessage) {
@@ -3225,6 +3346,7 @@ async function validateEvalFile(filePath) {
3225
3346
  }
3226
3347
  }
3227
3348
  await validateEnvironmentConfig(parsed.environment, absolutePath, errors, "environment");
3349
+ await validateProviderEnvironmentFields(parsed.providers, "providers", absolutePath, errors);
3228
3350
  await validateSuiteWorkspaceConfigs(parsed, absolutePath, errors);
3229
3351
  validateTargetTestbedFields(parsed.target, "target", absolutePath, errors);
3230
3352
  validateTargetsTestbedFields(parsed.targets, "targets", absolutePath, errors);
@@ -3233,9 +3355,15 @@ async function validateEvalFile(filePath) {
3233
3355
  validateEvaluateOptions(parsed.evaluate_options, "evaluate_options", absolutePath, errors);
3234
3356
  validateAssertArray(parsed.assert, "assert", absolutePath, errors, customAssertionTypes);
3235
3357
  validateDefaultTest(parsed.default_test, absolutePath, errors, customAssertionTypes);
3358
+ const expandedScenarios = await expandScenariosForValidation(
3359
+ parsed.scenarios,
3360
+ absolutePath,
3361
+ errors
3362
+ );
3363
+ await validateScenarios(expandedScenarios, parsed, absolutePath, errors, customAssertionTypes);
3236
3364
  const cases = parsed.tests;
3237
3365
  const hasImports = collectImportEntries(parsed).length > 0;
3238
- const hasScenarios = Array.isArray(parsed.scenarios);
3366
+ const hasScenarios = Array.isArray(expandedScenarios);
3239
3367
  if (typeof cases === "string") {
3240
3368
  await validateRawCaseImportPath(cases, absolutePath, "tests", errors);
3241
3369
  await validateCompositionDiagnostics(absolutePath, parsed, errors);
@@ -3362,7 +3490,7 @@ async function validateEvalFile(filePath) {
3362
3490
  suiteInputFiles: skipDefaults ? void 0 : parsed.input_files
3363
3491
  }) !== void 0 : false;
3364
3492
  validateInputField(evalCase.input, `${location}.input`, absolutePath, errors, {
3365
- required: !hasPromptMdFallback && evalCase.prompts === void 0 && parsed.prompts === void 0 && evalCase.provider_output === void 0
3493
+ required: !hasPromptMdFallback && evalCase.prompts === void 0 && parsed.prompts === void 0
3366
3494
  });
3367
3495
  const expectedOutputField = evalCase.expected_output;
3368
3496
  if (expectedOutputField !== void 0) {
@@ -3405,6 +3533,32 @@ async function validateEvalFile(filePath) {
3405
3533
  errors
3406
3534
  };
3407
3535
  }
3536
+ async function validateTypeScriptEvalConfigFile(filePath) {
3537
+ const absolutePath = path22.resolve(filePath);
3538
+ try {
3539
+ const { loadTsEvalSuite } = await import("./ts-eval-loader-3G5GEAEC-6F52JLPP.js");
3540
+ await loadTsEvalSuite(absolutePath, process.cwd());
3541
+ return {
3542
+ valid: true,
3543
+ filePath: absolutePath,
3544
+ fileType: "eval",
3545
+ errors: []
3546
+ };
3547
+ } catch (error) {
3548
+ return {
3549
+ valid: false,
3550
+ filePath: absolutePath,
3551
+ fileType: "eval",
3552
+ errors: [
3553
+ {
3554
+ severity: "error",
3555
+ filePath: absolutePath,
3556
+ message: error.message
3557
+ }
3558
+ ]
3559
+ };
3560
+ }
3561
+ }
3408
3562
  async function validateSuiteWorkspaceConfigs(parsed, absolutePath, errors) {
3409
3563
  await validateWorkspaceConfig(parsed.workspace, absolutePath, errors, "workspace");
3410
3564
  if (isObject(parsed.execution)) {
@@ -3447,6 +3601,31 @@ function validateTargetTestbedFields(target, location, filePath, errors) {
3447
3601
  });
3448
3602
  }
3449
3603
  }
3604
+ async function validateProviderEnvironmentFields(providers, location, filePath, errors) {
3605
+ const entries = Array.isArray(providers) ? providers : providers === void 0 ? [] : [providers];
3606
+ for (let index = 0; index < entries.length; index++) {
3607
+ const entry = entries[index];
3608
+ if (!isObject(entry)) {
3609
+ continue;
3610
+ }
3611
+ await validateEnvironmentConfig(
3612
+ entry.environment,
3613
+ filePath,
3614
+ errors,
3615
+ `${location}[${index}].environment`
3616
+ );
3617
+ for (const field of ["container", "install"]) {
3618
+ if (entry[field] !== void 0) {
3619
+ errors.push({
3620
+ severity: "error",
3621
+ filePath,
3622
+ location: `${location}[${index}].${field}`,
3623
+ message: `Provider definitions cannot include ${field}; use an environment recipe for testbed setup.`
3624
+ });
3625
+ }
3626
+ }
3627
+ }
3628
+ }
3450
3629
  function validateTargetsTestbedFields(targets, location, filePath, errors) {
3451
3630
  const entries = Array.isArray(targets) ? targets : targets === void 0 ? [] : [targets];
3452
3631
  entries.forEach((entry, index) => {
@@ -3634,7 +3813,7 @@ async function validateCompositionDiagnostics(filePath, parsed, errors) {
3634
3813
  severity: "warning",
3635
3814
  filePath,
3636
3815
  location: entry.location,
3637
- message: parentHasRuntime ? `Imported suite '${resolvedSuite.displayPath}' defines ${runtimeFields.join(", ")}, but child target and run controls are ignored by legacy tests[].include suite imports. Prefer running eval files directly with CLI multi-file selection and tags.` : `Imported suite '${resolvedSuite.displayPath}' defines ${runtimeFields.join(", ")}, but child target and run controls are ignored by legacy tests[].include suite imports. Prefer running eval files directly with CLI multi-file selection and tags.`
3816
+ message: parentHasRuntime ? `Imported suite '${resolvedSuite.displayPath}' defines ${runtimeFields.join(", ")}, but child providers and run controls are ignored by legacy tests[].include suite imports. Prefer running eval files directly with CLI multi-file selection and tags.` : `Imported suite '${resolvedSuite.displayPath}' defines ${runtimeFields.join(", ")}, but child providers and run controls are ignored by legacy tests[].include suite imports. Prefer running eval files directly with CLI multi-file selection and tags.`
3638
3817
  });
3639
3818
  }
3640
3819
  continue;
@@ -3656,7 +3835,9 @@ async function validateCompositionDiagnostics(filePath, parsed, errors) {
3656
3835
  }
3657
3836
  var WRAPPER_RUNTIME_CONTROL_FIELDS = [
3658
3837
  "experiment",
3838
+ "providers",
3659
3839
  "target",
3840
+ "targets",
3660
3841
  "repeat",
3661
3842
  "timeout_seconds",
3662
3843
  "evaluate_options",
@@ -3852,10 +4033,18 @@ function validateDefaultTest(defaultTest, filePath, errors, customAssertionTypes
3852
4033
  severity: "error",
3853
4034
  filePath,
3854
4035
  location: `default_test.${key}`,
3855
- message: "Invalid default_test field. Supported fields: vars, provider, providers, prompts, provider_output, assert, assert_scoring_function, options, threshold, metadata."
4036
+ message: "Invalid default_test field. Supported fields: vars, provider, providers, prompts, assert, assert_scoring_function, options, threshold, metadata."
3856
4037
  });
3857
4038
  }
3858
4039
  }
4040
+ if (defaultTest.provider_output !== void 0) {
4041
+ errors.push({
4042
+ severity: "error",
4043
+ filePath,
4044
+ location: "default_test.provider_output",
4045
+ message: "default_test.provider_output is not supported in authored AgentV YAML. Use an explicit deterministic target such as provider: cli for fixed outputs, or use a replay/fixture target for captured provider responses."
4046
+ });
4047
+ }
3859
4048
  if (defaultTest.expected_output !== void 0) {
3860
4049
  errors.push({
3861
4050
  severity: "error",
@@ -3877,19 +4066,222 @@ function validateDefaultTest(defaultTest, filePath, errors, customAssertionTypes
3877
4066
  errors.push({
3878
4067
  severity: "error",
3879
4068
  filePath,
3880
- location: "default_test.threshold",
3881
- message: "Invalid 'default_test.threshold' field (must be a number between 0 and 1)"
4069
+ location: "default_test.threshold",
4070
+ message: "Invalid 'default_test.threshold' field (must be a number between 0 and 1)"
4071
+ });
4072
+ }
4073
+ const options = defaultTest.options;
4074
+ if (options !== void 0 && !isObject(options)) {
4075
+ errors.push({
4076
+ severity: "error",
4077
+ filePath,
4078
+ location: "default_test.options",
4079
+ message: "Invalid 'default_test.options' field (must be an object)"
4080
+ });
4081
+ }
4082
+ }
4083
+ async function validateScenarios(scenarios, parsed, filePath, errors, customAssertionTypes) {
4084
+ if (scenarios === void 0) {
4085
+ return;
4086
+ }
4087
+ if (!Array.isArray(scenarios)) {
4088
+ errors.push({
4089
+ severity: "error",
4090
+ filePath,
4091
+ location: "scenarios",
4092
+ message: "Invalid 'scenarios' field (must be an array)"
4093
+ });
4094
+ return;
4095
+ }
4096
+ for (let scenarioIndex = 0; scenarioIndex < scenarios.length; scenarioIndex++) {
4097
+ const scenario = scenarios[scenarioIndex];
4098
+ const scenarioLocation = `scenarios[${scenarioIndex}]`;
4099
+ if (!isObject(scenario)) {
4100
+ errors.push({
4101
+ severity: "error",
4102
+ filePath,
4103
+ location: scenarioLocation,
4104
+ message: "Scenario must be an object"
4105
+ });
4106
+ continue;
4107
+ }
4108
+ for (const key of Object.keys(scenario)) {
4109
+ if (!KNOWN_SCENARIO_FIELDS.has(key)) {
4110
+ errors.push({
4111
+ severity: "error",
4112
+ filePath,
4113
+ location: `${scenarioLocation}.${key}`,
4114
+ message: `Unknown scenario field '${key}'.`
4115
+ });
4116
+ }
4117
+ }
4118
+ if (!Array.isArray(scenario.config)) {
4119
+ errors.push({
4120
+ severity: "error",
4121
+ filePath,
4122
+ location: `${scenarioLocation}.config`,
4123
+ message: "Invalid 'config' field (must be an array)"
4124
+ });
4125
+ } else {
4126
+ for (let configIndex = 0; configIndex < scenario.config.length; configIndex++) {
4127
+ await validateScenarioTestLikeRow(
4128
+ scenario.config[configIndex],
4129
+ `${scenarioLocation}.config[${configIndex}]`,
4130
+ filePath,
4131
+ errors,
4132
+ customAssertionTypes,
4133
+ { requireInput: false }
4134
+ );
4135
+ }
4136
+ }
4137
+ const scenarioConfigs = Array.isArray(scenario.config) ? scenario.config : [];
4138
+ if (!Array.isArray(scenario.tests)) {
4139
+ errors.push({
4140
+ severity: "error",
4141
+ filePath,
4142
+ location: `${scenarioLocation}.tests`,
4143
+ message: "Invalid 'tests' field (must be an array)"
4144
+ });
4145
+ continue;
4146
+ }
4147
+ for (let testIndex = 0; testIndex < scenario.tests.length; testIndex++) {
4148
+ const test = scenario.tests[testIndex];
4149
+ const everyConfigProvidesInput = scenarioConfigs.length > 0 && scenarioConfigs.every((config) => isObject(config) && config.prompts !== void 0);
4150
+ const requireInput = isObject(test) && parsed.prompts === void 0 && test.prompts === void 0 && !everyConfigProvidesInput;
4151
+ await validateScenarioTestLikeRow(
4152
+ test,
4153
+ `${scenarioLocation}.tests[${testIndex}]`,
4154
+ filePath,
4155
+ errors,
4156
+ customAssertionTypes,
4157
+ { requireInput }
4158
+ );
4159
+ }
4160
+ }
4161
+ }
4162
+ async function expandScenariosForValidation(scenarios, filePath, errors) {
4163
+ if (!Array.isArray(scenarios)) {
4164
+ return scenarios;
4165
+ }
4166
+ const expanded = [];
4167
+ for (let scenarioIndex = 0; scenarioIndex < scenarios.length; scenarioIndex++) {
4168
+ const scenario = scenarios[scenarioIndex];
4169
+ if (!isScenarioFileReference(scenario)) {
4170
+ expanded.push(scenario);
4171
+ continue;
4172
+ }
4173
+ try {
4174
+ const externalScenarios = await resolveScenarioFileReference(
4175
+ scenario,
4176
+ path22.dirname(filePath)
4177
+ );
4178
+ expanded.push(...externalScenarios);
4179
+ } catch (error) {
4180
+ const message = error instanceof Error ? error.message : String(error);
4181
+ errors.push({
4182
+ severity: "error",
4183
+ filePath,
4184
+ location: `scenarios[${scenarioIndex}]`,
4185
+ message
4186
+ });
4187
+ }
4188
+ }
4189
+ return expanded;
4190
+ }
4191
+ async function validateScenarioTestLikeRow(row, location, filePath, errors, customAssertionTypes, options) {
4192
+ if (!isObject(row)) {
4193
+ errors.push({
4194
+ severity: "error",
4195
+ filePath,
4196
+ location,
4197
+ message: "Scenario row must be an object"
4198
+ });
4199
+ return;
4200
+ }
4201
+ for (const key of Object.keys(row)) {
4202
+ const removedMessage = REMOVED_TEST_FIELDS.get(key);
4203
+ if (removedMessage) {
4204
+ errors.push({
4205
+ severity: "error",
4206
+ filePath,
4207
+ location: `${location}.${key}`,
4208
+ message: removedMessage.replaceAll("tests[]", location)
4209
+ });
4210
+ continue;
4211
+ }
4212
+ const deprecationMessage = DEPRECATED_TEST_FIELDS.get(key);
4213
+ if (deprecationMessage) {
4214
+ errors.push({
4215
+ severity: "warning",
4216
+ filePath,
4217
+ location: `${location}.${key}`,
4218
+ message: deprecationMessage
4219
+ });
4220
+ } else if (!KNOWN_TEST_FIELDS.has(key)) {
4221
+ errors.push({
4222
+ severity: "error",
4223
+ filePath,
4224
+ location: `${location}.${key}`,
4225
+ message: `Unknown test field '${key}'.`
4226
+ });
4227
+ }
4228
+ }
4229
+ const id = row.id;
4230
+ if (id !== void 0 && (typeof id !== "string" || id.trim().length === 0)) {
4231
+ errors.push({
4232
+ severity: "error",
4233
+ filePath,
4234
+ location: `${location}.id`,
4235
+ message: "Invalid 'id' field (must be a non-empty string when provided)"
4236
+ });
4237
+ }
4238
+ const criteria = row.criteria;
4239
+ if (criteria !== void 0 && (typeof criteria !== "string" || criteria.trim().length === 0)) {
4240
+ errors.push({
4241
+ severity: "error",
4242
+ filePath,
4243
+ location: `${location}.criteria`,
4244
+ message: "Invalid 'criteria' field (must be a non-empty string if provided)"
4245
+ });
4246
+ } else if (criteria !== void 0 && row.assert !== void 0) {
4247
+ errors.push({
4248
+ severity: "error",
4249
+ filePath,
4250
+ location: `${location}.criteria`,
4251
+ message: "Do not combine test-level 'criteria' with 'assert'. Put human-readable case descriptions in tests[].description, or express grading text as an explicit assertion such as { type: 'llm-rubric', value: ... }."
3882
4252
  });
3883
4253
  }
3884
- const options = defaultTest.options;
3885
- if (options !== void 0 && !isObject(options)) {
4254
+ validateInputField(row.input, `${location}.input`, filePath, errors, {
4255
+ required: options.requireInput
4256
+ });
4257
+ if (row.expected_output !== void 0) {
3886
4258
  errors.push({
3887
4259
  severity: "error",
3888
4260
  filePath,
3889
- location: "default_test.options",
3890
- message: "Invalid 'default_test.options' field (must be an object)"
4261
+ location: `${location}.expected_output`,
4262
+ message: "tests[].expected_output has been removed from authored eval YAML. Put the reference answer in tests[].vars.expected_output and consume it with an explicit assertion, for example { type: 'llm-rubric', value: 'Matches the reference answer: {{ expected_output }}' }."
3891
4263
  });
3892
4264
  }
4265
+ validateAssertArray(row.assert, `${location}.assert`, filePath, errors, customAssertionTypes);
4266
+ validateRunOverride(row.run, `${location}.run`, filePath, errors);
4267
+ validateTestOptions(row.options, `${location}.options`, filePath, errors);
4268
+ await validateEnvironmentConfig(row.environment, filePath, errors, `${location}.environment`);
4269
+ validateConversationMode(row, location, filePath, errors);
4270
+ await validateWorkspaceConfig(row.workspace, filePath, errors, `${location}.workspace`);
4271
+ const caseExecution = isObject(row.execution) ? row.execution : void 0;
4272
+ if (caseExecution) {
4273
+ validateTestExecutionFields(caseExecution, filePath, errors, location);
4274
+ rejectRuntimeWorkspaceConfig(
4275
+ caseExecution.workspace,
4276
+ filePath,
4277
+ errors,
4278
+ `${location}.execution.workspace`
4279
+ );
4280
+ }
4281
+ const targets = row.providers !== void 0 ? row.providers : row.provider;
4282
+ if (targets !== void 0) {
4283
+ await validateProviderEnvironmentFields(targets, `${location}.providers`, filePath, errors);
4284
+ }
3893
4285
  }
3894
4286
  function validateEvaluateOptions(evaluateOptions, location, filePath, errors) {
3895
4287
  if (evaluateOptions === void 0) {
@@ -3978,7 +4370,12 @@ function validateEvaluateOptionsRepeat(repeat, location, filePath, errors) {
3978
4370
  }
3979
4371
  return;
3980
4372
  }
3981
- validateRepeatOverride(repeat, location, filePath, errors);
4373
+ errors.push({
4374
+ severity: "error",
4375
+ filePath,
4376
+ location,
4377
+ message: `Invalid '${location}' field (must be a positive integer). The repeat object shape has been removed; use ${location}: 2.`
4378
+ });
3982
4379
  }
3983
4380
  function validateFilterRange(filterRange, location, filePath, errors) {
3984
4381
  if (filterRange === void 0 || typeof filterRange === "string") {
@@ -3997,57 +4394,12 @@ function validateRepeatOverride(repeat, location, filePath, errors) {
3997
4394
  if (repeat === void 0) {
3998
4395
  return;
3999
4396
  }
4000
- if (!isObject(repeat)) {
4397
+ if (typeof repeat !== "number" || !Number.isInteger(repeat) || repeat < 1) {
4001
4398
  errors.push({
4002
4399
  severity: "error",
4003
4400
  filePath,
4004
4401
  location,
4005
- message: "Invalid 'repeat' field (must be an object)"
4006
- });
4007
- return;
4008
- }
4009
- for (const key of Object.keys(repeat)) {
4010
- if (!KNOWN_REPEAT_FIELDS.has(key)) {
4011
- errors.push({
4012
- severity: "error",
4013
- filePath,
4014
- location: `${location}.${key}`,
4015
- message: "Invalid repeat field. Supported fields: count, strategy, early_exit, cost_limit_usd."
4016
- });
4017
- }
4018
- }
4019
- if (typeof repeat.count !== "number" || !Number.isInteger(repeat.count) || repeat.count < 1) {
4020
- errors.push({
4021
- severity: "error",
4022
- filePath,
4023
- location: `${location}.count`,
4024
- message: "Invalid 'count' field (must be a positive integer)"
4025
- });
4026
- }
4027
- if (repeat.strategy !== void 0 && (typeof repeat.strategy !== "string" || !KNOWN_REPEAT_STRATEGIES.has(repeat.strategy))) {
4028
- errors.push({
4029
- severity: "error",
4030
- filePath,
4031
- location: `${location}.strategy`,
4032
- message: "Invalid 'strategy' field (must be pass_any, pass_all, mean, or confidence_interval; use pass_any instead of removed pass_at_k)"
4033
- });
4034
- }
4035
- const earlyExit = repeat.early_exit;
4036
- if (earlyExit !== void 0 && typeof earlyExit !== "boolean") {
4037
- errors.push({
4038
- severity: "error",
4039
- filePath,
4040
- location: `${location}.early_exit`,
4041
- message: "Invalid 'early_exit' field (must be a boolean)"
4042
- });
4043
- }
4044
- const costLimit = repeat.cost_limit_usd;
4045
- if (costLimit !== void 0 && (typeof costLimit !== "number" || costLimit < 0)) {
4046
- errors.push({
4047
- severity: "error",
4048
- filePath,
4049
- location: `${location}.cost_limit_usd`,
4050
- message: "Invalid 'cost_limit_usd' field (must be a non-negative number)"
4402
+ message: "Invalid 'repeat' field (must be a positive integer). The repeat object shape has been removed; use repeat: 2."
4051
4403
  });
4052
4404
  }
4053
4405
  }
@@ -4489,7 +4841,7 @@ async function validateRawCaseImportPath(testsPath, filePath, location, errors)
4489
4841
  let caseIndex = 0;
4490
4842
  for (const casePath of caseFiles) {
4491
4843
  const statPath = casePath.match(/\.(?:mjs|cjs|js|py):[^/\\:]+$/i) ? casePath.slice(0, casePath.lastIndexOf(":")) : casePath;
4492
- const pathStat = await stat2(statPath).catch(() => void 0);
4844
+ const pathStat = await stat3(statPath).catch(() => void 0);
4493
4845
  const externalCases = pathStat?.isDirectory() ? await loadCasesFromDirectory(casePath) : await loadCasesFromFile(casePath);
4494
4846
  for (const externalCase of externalCases) {
4495
4847
  await validateWorkspaceConfig(
@@ -4925,6 +5277,11 @@ async function validateCasesFile(filePath) {
4925
5277
  };
4926
5278
  }
4927
5279
  var LEGACY_ENV_PATTERN = /\$\{\{\s*([A-Z_][A-Z0-9_]*)\s*\}\}/g;
5280
+ var REMOVED_RUNNER_BATCHING_FIELDS = /* @__PURE__ */ new Set([
5281
+ "batch_requests",
5282
+ "provider_batching",
5283
+ "providerBatching"
5284
+ ]);
4928
5285
  function isObject3(value) {
4929
5286
  return typeof value === "object" && value !== null && !Array.isArray(value);
4930
5287
  }
@@ -4959,7 +5316,7 @@ function validateLegacyEnvTemplates(value, filePath, location, errors) {
4959
5316
  }
4960
5317
  }
4961
5318
  }
4962
- var COMMON_SETTINGS = new Set(COMMON_TARGET_SETTINGS);
5319
+ var COMMON_SETTINGS = new Set(COMMON_PROVIDER_SETTINGS);
4963
5320
  var RETRY_SETTINGS = /* @__PURE__ */ new Set([
4964
5321
  "max_retries",
4965
5322
  "retry_initial_delay_ms",
@@ -5205,6 +5562,7 @@ function validateUnknownSettings(target, provider, absolutePath, location, error
5205
5562
  "name",
5206
5563
  "label",
5207
5564
  "provider",
5565
+ "provider_spec",
5208
5566
  "config",
5209
5567
  "prompts",
5210
5568
  "transform",
@@ -5212,6 +5570,7 @@ function validateUnknownSettings(target, provider, absolutePath, location, error
5212
5570
  "env",
5213
5571
  "grader_target",
5214
5572
  "judge_target",
5573
+ "use_target",
5215
5574
  "workers",
5216
5575
  "$schema",
5217
5576
  "targets"
@@ -5227,6 +5586,9 @@ function validateUnknownSettings(target, provider, absolutePath, location, error
5227
5586
  };
5228
5587
  const removedForProvider = removedPerProvider[provider];
5229
5588
  for (const key of Object.keys(target)) {
5589
+ if (REMOVED_RUNNER_BATCHING_FIELDS.has(key)) {
5590
+ continue;
5591
+ }
5230
5592
  if (removedFields.has(key)) {
5231
5593
  errors.push({
5232
5594
  severity: "warning",
@@ -5245,15 +5607,6 @@ function validateUnknownSettings(target, provider, absolutePath, location, error
5245
5607
  });
5246
5608
  continue;
5247
5609
  }
5248
- if (key === "provider_batching") {
5249
- errors.push({
5250
- severity: "error",
5251
- filePath: absolutePath,
5252
- location: `${location}.${key}`,
5253
- message: "The 'provider_batching' field has been removed. Use 'batch_requests' instead."
5254
- });
5255
- continue;
5256
- }
5257
5610
  if (removedForProvider?.has(key)) {
5258
5611
  errors.push({
5259
5612
  severity: "error",
@@ -5273,6 +5626,22 @@ function validateUnknownSettings(target, provider, absolutePath, location, error
5273
5626
  }
5274
5627
  }
5275
5628
  }
5629
+ function buildRemovedRunnerBatchingMessage(field) {
5630
+ return `The '${field}' field has been removed. Runner-level batching was removed; providers should implement internal queueing/batching behind per-request invocation.`;
5631
+ }
5632
+ function validateRemovedRunnerBatchingFields(target, absolutePath, location, errors) {
5633
+ for (const key of Object.keys(target)) {
5634
+ if (!REMOVED_RUNNER_BATCHING_FIELDS.has(key)) {
5635
+ continue;
5636
+ }
5637
+ errors.push({
5638
+ severity: "error",
5639
+ filePath: absolutePath,
5640
+ location: `${location}.${key}`,
5641
+ message: buildRemovedRunnerBatchingMessage(key)
5642
+ });
5643
+ }
5644
+ }
5276
5645
  async function validateTargetsFile(filePath) {
5277
5646
  const errors = [];
5278
5647
  const absolutePath = path42.resolve(filePath);
@@ -5388,11 +5757,11 @@ async function validateTargetsFile(filePath) {
5388
5757
  }
5389
5758
  return result;
5390
5759
  }
5391
- if (!isObject3(parsed)) {
5760
+ if (!Array.isArray(parsed) && !isObject3(parsed)) {
5392
5761
  errors.push({
5393
5762
  severity: "error",
5394
5763
  filePath: absolutePath,
5395
- message: "File must contain a YAML object"
5764
+ message: "File must contain a YAML array or an object with a providers array"
5396
5765
  });
5397
5766
  return {
5398
5767
  valid: false,
@@ -5401,22 +5770,22 @@ async function validateTargetsFile(filePath) {
5401
5770
  errors
5402
5771
  };
5403
5772
  }
5404
- if (Array.isArray(parsed.providers)) {
5773
+ if (isObject3(parsed) && Array.isArray(parsed.targets)) {
5405
5774
  errors.push({
5406
5775
  severity: "error",
5407
5776
  filePath: absolutePath,
5408
- location: "providers",
5409
- message: "Top-level 'providers' is not a runtime alias in targets.yaml. Use 'targets' for systems under test; provider names backend kind inside each target."
5777
+ location: "targets",
5778
+ message: "Top-level 'targets' has been removed. Use 'providers'; map targets[].id to providers[].label and targets[].provider to providers[].id."
5410
5779
  });
5411
5780
  }
5412
- const targets = parsed.targets;
5413
- const rawTargets = isObject3(rawParsed) && Array.isArray(rawParsed.targets) ? rawParsed.targets : [];
5414
- if (!Array.isArray(targets)) {
5781
+ const providers = Array.isArray(parsed) ? parsed : parsed.providers;
5782
+ const rawTargets = Array.isArray(rawParsed) ? rawParsed : isObject3(rawParsed) && Array.isArray(rawParsed.providers) ? rawParsed.providers : [];
5783
+ if (!Array.isArray(providers)) {
5415
5784
  errors.push({
5416
5785
  severity: "error",
5417
5786
  filePath: absolutePath,
5418
- location: "targets",
5419
- message: "Missing or invalid 'targets' field (must be an array)"
5787
+ location: "providers",
5788
+ message: "Missing or invalid 'providers' field (must be an array)"
5420
5789
  });
5421
5790
  return {
5422
5791
  valid: errors.length === 0,
@@ -5426,22 +5795,24 @@ async function validateTargetsFile(filePath) {
5426
5795
  };
5427
5796
  }
5428
5797
  const knownProviders = [...KNOWN_PROVIDERS];
5429
- for (let i = 0; i < targets.length; i++) {
5430
- const target = targets[i];
5431
- const location = `targets[${i}]`;
5432
- if (!isObject3(target)) {
5798
+ for (let i = 0; i < providers.length; i++) {
5799
+ const providerEntry = providers[i];
5800
+ const location = `providers[${i}]`;
5801
+ if (!isObject3(providerEntry)) {
5433
5802
  errors.push({
5434
5803
  severity: "error",
5435
5804
  filePath: absolutePath,
5436
5805
  location,
5437
- message: "Target must be an object"
5806
+ message: "Provider must be an object"
5438
5807
  });
5439
5808
  continue;
5440
5809
  }
5441
- validateLegacyEnvTemplates(rawTargets[i] ?? target, absolutePath, location, errors);
5810
+ validateLegacyEnvTemplates(rawTargets[i] ?? providerEntry, absolutePath, location, errors);
5442
5811
  let normalizedTarget;
5443
5812
  try {
5444
- normalizedTarget = normalizeTargetDefinition(target);
5813
+ normalizedTarget = normalizeProviderDefinition(providerEntry, {
5814
+ location
5815
+ });
5445
5816
  } catch (error) {
5446
5817
  errors.push({
5447
5818
  severity: "error",
@@ -5451,7 +5822,7 @@ async function validateTargetsFile(filePath) {
5451
5822
  });
5452
5823
  }
5453
5824
  for (const warning of findDeprecatedCamelCaseTargetWarnings(
5454
- normalizedTarget ?? target,
5825
+ normalizedTarget ?? providerEntry,
5455
5826
  location
5456
5827
  )) {
5457
5828
  const fieldMatch = warning.message.match(/field '([^']+)'/);
@@ -5462,10 +5833,10 @@ async function validateTargetsFile(filePath) {
5462
5833
  severity: "error",
5463
5834
  filePath: absolutePath,
5464
5835
  location: warning.location,
5465
- message: `camelCase field '${field}' is no longer supported in targets.yaml. Use '${replacement}' instead.`
5836
+ message: `camelCase field '${field}' is no longer supported in providers.yaml. Use '${replacement}' instead.`
5466
5837
  });
5467
5838
  }
5468
- const id = target.id;
5839
+ const id = providerEntry.id;
5469
5840
  if (typeof id !== "string" || id.trim().length === 0) {
5470
5841
  errors.push({
5471
5842
  severity: "error",
@@ -5474,51 +5845,51 @@ async function validateTargetsFile(filePath) {
5474
5845
  message: "Missing or invalid 'id' field (must be a non-empty string)"
5475
5846
  });
5476
5847
  }
5477
- if (typeof target.label === "string" && target.label.trim().length > 0) {
5848
+ if (typeof providerEntry.name === "string" && providerEntry.name.trim().length > 0) {
5478
5849
  errors.push({
5479
5850
  severity: "error",
5480
5851
  filePath: absolutePath,
5481
- location: `${location}.label`,
5482
- message: "The target 'label' field has been removed. Use 'id' instead."
5852
+ location: `${location}.name`,
5853
+ message: "The provider 'name' field has been removed. Use 'label' instead."
5483
5854
  });
5484
5855
  }
5485
- if (typeof target.name === "string" && target.name.trim().length > 0) {
5856
+ const effectiveTarget = normalizedTarget ?? providerEntry;
5857
+ const provider = effectiveTarget.provider;
5858
+ const rawTarget = rawTargets[i];
5859
+ const rawUseTarget = isObject3(rawTarget) ? rawTarget.use_target : void 0;
5860
+ if (effectiveTarget.use_target !== void 0 || rawUseTarget !== void 0) {
5486
5861
  errors.push({
5487
5862
  severity: "error",
5488
5863
  filePath: absolutePath,
5489
- location: `${location}.name`,
5490
- message: "The target 'name' field has been removed. Use 'id' instead."
5864
+ location: `${location}.use_target`,
5865
+ message: "The 'use_target' field has been removed from authored providers.yaml definitions. Define a concrete provider entry instead."
5491
5866
  });
5492
5867
  }
5493
- const effectiveTarget = normalizedTarget ?? target;
5494
- const provider = effectiveTarget.provider;
5495
- const rawTarget = rawTargets[i];
5496
- const rawUseTarget = isObject3(rawTarget) ? rawTarget.use_target : void 0;
5497
- const hasUseTarget = isNonEmptyString(effectiveTarget.use_target) || isNonEmptyString(rawUseTarget);
5498
5868
  const providerValue = typeof provider === "string" ? provider.trim().toLowerCase() : void 0;
5499
5869
  const isTemplated = isEnvTemplated(provider);
5500
- if (!hasUseTarget && (typeof provider !== "string" || provider.trim().length === 0)) {
5870
+ if (typeof provider !== "string" || provider.trim().length === 0) {
5501
5871
  errors.push({
5502
5872
  severity: "error",
5503
5873
  filePath: absolutePath,
5504
- location: `${location}.provider`,
5505
- message: "Missing or invalid 'provider' field (must be a non-empty string, or use use_target for delegation)"
5874
+ location: `${location}.id`,
5875
+ message: "Missing or invalid 'id' field (must be a non-empty provider backend string)"
5506
5876
  });
5507
5877
  } else if (!isTemplated && (providerValue === "claude" || providerValue === "copilot")) {
5508
5878
  errors.push({
5509
5879
  severity: "error",
5510
5880
  filePath: absolutePath,
5511
- location: `${location}.provider`,
5881
+ location: `${location}.id`,
5512
5882
  message: `Ambiguous provider '${providerValue}' is not supported. Choose an explicit provider such as '${providerValue}-cli' or '${providerValue}-sdk'.`
5513
5883
  });
5514
5884
  } else if (typeof provider === "string" && !isTemplated && !knownProviders.includes(provider)) {
5515
5885
  errors.push({
5516
5886
  severity: "warning",
5517
5887
  filePath: absolutePath,
5518
- location: `${location}.provider`,
5888
+ location: `${location}.id`,
5519
5889
  message: `Unknown provider '${provider}'. Known providers: ${knownProviders.join(", ")}`
5520
5890
  });
5521
5891
  }
5892
+ validateRemovedRunnerBatchingFields(effectiveTarget, absolutePath, location, errors);
5522
5893
  if (providerValue === "cli") {
5523
5894
  validateCliSettings(effectiveTarget, absolutePath, location, errors);
5524
5895
  }
@@ -5637,6 +6008,9 @@ async function validateConfigFile(filePath, options = {}) {
5637
6008
  validateRefsConfig(errors, filePath, config.refs);
5638
6009
  validateComposableGraph(errors, filePath, config);
5639
6010
  validateDashboardConfig(errors, filePath, config.dashboard);
6011
+ validateHooksConfig(errors, filePath, config.hooks);
6012
+ validateEnvPathConfig(errors, filePath, config.env_path);
6013
+ validateEnvFromConfig(errors, filePath, config.env_from);
5640
6014
  const projects = config.projects;
5641
6015
  if (projects !== void 0) {
5642
6016
  if (scope === "project") {
@@ -5665,13 +6039,17 @@ async function validateConfigFile(filePath, options = {}) {
5665
6039
  "results",
5666
6040
  "repo_resolvers",
5667
6041
  "refs",
6042
+ "providers",
5668
6043
  "targets",
5669
6044
  "graders",
5670
6045
  "tests",
5671
6046
  "defaults",
5672
6047
  "projects",
5673
6048
  "dashboard",
5674
- "studio"
6049
+ "studio",
6050
+ "hooks",
6051
+ "env_path",
6052
+ "env_from"
5675
6053
  ]);
5676
6054
  const unexpectedFields = Object.keys(config).filter((key) => !allowedFields.has(key));
5677
6055
  if (unexpectedFields.length > 0) {
@@ -5698,7 +6076,7 @@ async function validateConfigFile(filePath, options = {}) {
5698
6076
  }
5699
6077
  function validateComposableGraph(errors, filePath, config) {
5700
6078
  try {
5701
- normalizeComposableConfigGraph(config, filePath);
6079
+ normalizeComposableConfigGraph(config, filePath, { allowExecutionDefaultFields: true });
5702
6080
  } catch (error) {
5703
6081
  addError(errors, filePath, void 0, error.message);
5704
6082
  }
@@ -5720,6 +6098,76 @@ function validateDashboardConfig(errors, filePath, rawDashboard) {
5720
6098
  );
5721
6099
  }
5722
6100
  }
6101
+ var ENV_FROM_FORMATS = /* @__PURE__ */ new Set(["shell_exports", "json"]);
6102
+ function validateHooksConfig(errors, filePath, rawHooks) {
6103
+ if (rawHooks === void 0 || rawHooks === null) {
6104
+ return;
6105
+ }
6106
+ if (!isPlainObject(rawHooks)) {
6107
+ addError(errors, filePath, "hooks", "Field 'hooks' must be an object");
6108
+ return;
6109
+ }
6110
+ if (rawHooks.before_session !== void 0) {
6111
+ validateOptionalString(errors, filePath, rawHooks.before_session, "hooks.before_session");
6112
+ errors.push({
6113
+ severity: "warning",
6114
+ filePath,
6115
+ location: "hooks.before_session",
6116
+ message: "'hooks.before_session' is deprecated; use 'env_path' and/or 'env_from' to load environment variables before validation and eval."
6117
+ });
6118
+ }
6119
+ }
6120
+ function validateEnvPathConfig(errors, filePath, rawEnvPath) {
6121
+ if (rawEnvPath === void 0 || rawEnvPath === null) {
6122
+ return;
6123
+ }
6124
+ const isList = Array.isArray(rawEnvPath);
6125
+ const entries = isList ? rawEnvPath : [rawEnvPath];
6126
+ entries.forEach((entry, index) => {
6127
+ const location = isList ? `env_path[${index}]` : "env_path";
6128
+ if (typeof entry !== "string" || entry.trim().length === 0) {
6129
+ addError(errors, filePath, location, `Field '${location}' must be a non-empty string`);
6130
+ }
6131
+ });
6132
+ }
6133
+ function validateEnvFromConfig(errors, filePath, rawEnvFrom) {
6134
+ if (rawEnvFrom === void 0 || rawEnvFrom === null) {
6135
+ return;
6136
+ }
6137
+ const isList = Array.isArray(rawEnvFrom);
6138
+ const entries = isList ? rawEnvFrom : [rawEnvFrom];
6139
+ entries.forEach((entry, index) => {
6140
+ const location = isList ? `env_from[${index}]` : "env_from";
6141
+ if (!isPlainObject(entry)) {
6142
+ addError(errors, filePath, location, `Field '${location}' must be an object`);
6143
+ return;
6144
+ }
6145
+ const command = entry.command;
6146
+ if (typeof command === "string") {
6147
+ addError(
6148
+ errors,
6149
+ filePath,
6150
+ `${location}.command`,
6151
+ `Field '${location}.command' must be an argv array of strings, not a shell command string. Use e.g. ["bun", "scripts/load-secrets.ts"].`
6152
+ );
6153
+ } else if (!Array.isArray(command) || command.length === 0 || !command.every((part) => typeof part === "string" && part.length > 0)) {
6154
+ addError(
6155
+ errors,
6156
+ filePath,
6157
+ `${location}.command`,
6158
+ `Field '${location}.command' must be a non-empty array of strings`
6159
+ );
6160
+ }
6161
+ if (entry.format !== void 0 && !ENV_FROM_FORMATS.has(entry.format)) {
6162
+ addError(
6163
+ errors,
6164
+ filePath,
6165
+ `${location}.format`,
6166
+ `Field '${location}.format' must be "shell_exports" or "json"`
6167
+ );
6168
+ }
6169
+ });
6170
+ }
5723
6171
  function validateRefsConfig(errors, filePath, rawRefs) {
5724
6172
  if (rawRefs === void 0) {
5725
6173
  return;
@@ -5932,13 +6380,7 @@ async function validateFileReferences(evalFilePath) {
5932
6380
  if (!isObject4(parsed)) {
5933
6381
  return errors;
5934
6382
  }
5935
- let cases = parsed.tests;
5936
- if (cases === void 0 && "eval_cases" in parsed) {
5937
- cases = parsed.eval_cases;
5938
- }
5939
- if (cases === void 0 && "evalcases" in parsed) {
5940
- cases = parsed.evalcases;
5941
- }
6383
+ const cases = parsed.tests;
5942
6384
  if (!Array.isArray(cases)) {
5943
6385
  return errors;
5944
6386
  }
@@ -6145,13 +6587,13 @@ var ANSI_RESET3 = "\x1B[0m";
6145
6587
  function isTTY() {
6146
6588
  return process.stdout.isTTY ?? false;
6147
6589
  }
6148
- function resolveUseTarget(name, definitions, env, targetsFilePath) {
6590
+ function resolveUseTarget(name, definitions, env, providersFilePath) {
6149
6591
  const maxDepth = 5;
6150
6592
  let current = definitions.find((d) => d.name === name);
6151
6593
  if (!current) {
6152
- const available = listTargetNames(definitions).join(", ");
6594
+ const available = listProviderLabels(definitions).join(", ");
6153
6595
  throw new Error(
6154
- `Target '${name}' not found in ${targetsFilePath}. Available targets: ${available}`
6596
+ `Provider '${name}' not found in ${providersFilePath}. Available providers: ${available}`
6155
6597
  );
6156
6598
  }
6157
6599
  for (let depth = 0; depth < maxDepth; depth++) {
@@ -6162,27 +6604,82 @@ function resolveUseTarget(name, definitions, env, targetsFilePath) {
6162
6604
  const envMatch = raw.match(/^\$\{\{\s*([A-Z0-9_]+)\s*\}\}$/i);
6163
6605
  const resolved = envMatch ? env[envMatch[1]] ?? "" : raw;
6164
6606
  if (resolved.trim().length === 0) break;
6165
- const next = definitions.find((d) => d.name === resolved.trim());
6607
+ const next = definitions.find(
6608
+ (d) => d.name === resolved.trim()
6609
+ );
6166
6610
  if (!next) {
6167
- const available = listTargetNames(definitions).join(", ");
6611
+ const available = listProviderLabels(definitions).join(", ");
6168
6612
  throw new Error(
6169
- `Target '${name}' use_target '${resolved.trim()}' not found in ${targetsFilePath}. Available targets: ${available}`
6613
+ `Provider '${name}' use_target '${resolved.trim()}' not found in ${providersFilePath}. Available providers: ${available}`
6170
6614
  );
6171
6615
  }
6172
6616
  current = next;
6173
6617
  }
6174
6618
  return current;
6175
6619
  }
6176
- async function readTestSuiteTarget(testFilePath) {
6620
+ async function readTestSuiteProvider(testFilePath) {
6177
6621
  const metadata = await readTestSuiteMetadata(testFilePath);
6178
6622
  return metadata.target;
6179
6623
  }
6180
- function pickTargetName(options) {
6181
- const cliName = options.cliTargetName?.trim();
6624
+ async function readProviderCatalog(options) {
6625
+ if (!options.explicitProvidersPath && options.providerDefinitions) {
6626
+ return {
6627
+ definitions: options.providerDefinitions,
6628
+ sourcePath: options.providerDefinitionsSource ?? ".agentv/config.yaml:providers"
6629
+ };
6630
+ }
6631
+ if (!options.explicitProvidersPath && options.requireExplicitProviderCatalog) {
6632
+ throw new Error(
6633
+ "No provider catalog configured. Add `providers:` to .agentv/config.yaml, use `providers: file://providers.yaml`, or pass --providers <path>."
6634
+ );
6635
+ }
6636
+ const providersFilePath = await discoverProvidersFile({
6637
+ explicitPath: options.explicitProvidersPath,
6638
+ testFilePath: options.testFilePath,
6639
+ repoRoot: options.repoRoot,
6640
+ cwd: options.cwd,
6641
+ allowLegacyTargetFiles: options.allowLegacyTargetFiles
6642
+ });
6643
+ await validateProviderCatalogFile(providersFilePath);
6644
+ return {
6645
+ definitions: await readProviderDefinitions(providersFilePath),
6646
+ sourcePath: providersFilePath
6647
+ };
6648
+ }
6649
+ async function validateProviderCatalogFile(providersFilePath) {
6650
+ const validationResult = await validateTargetsFile(providersFilePath);
6651
+ const warnings = validationResult.errors.filter((e) => e.severity === "warning");
6652
+ const useColors2 = isTTY();
6653
+ if (warnings.length > 0) {
6654
+ console.warn(`
6655
+ Warnings in ${providersFilePath}:`);
6656
+ for (const warning of warnings) {
6657
+ const location = warning.location ? ` [${warning.location}]` : "";
6658
+ const prefix = useColors2 ? `${ANSI_YELLOW3} \u26A0${ANSI_RESET3}` : " \u26A0";
6659
+ const message = useColors2 ? `${ANSI_YELLOW3}${warning.message}${ANSI_RESET3}` : warning.message;
6660
+ console.warn(`${prefix}${location} ${message}`);
6661
+ }
6662
+ console.warn("");
6663
+ }
6664
+ const errors = validationResult.errors.filter((e) => e.severity === "error");
6665
+ if (errors.length > 0) {
6666
+ console.error(`
6667
+ Errors in ${providersFilePath}:`);
6668
+ for (const error of errors) {
6669
+ const location = error.location ? ` [${error.location}]` : "";
6670
+ const prefix = useColors2 ? `${ANSI_RED3} \u2717${ANSI_RESET3}` : " \u2717";
6671
+ const message = useColors2 ? `${ANSI_RED3}${error.message}${ANSI_RESET3}` : error.message;
6672
+ console.error(`${prefix}${location} ${message}`);
6673
+ }
6674
+ throw new Error(`Providers file validation failed with ${errors.length} error(s)`);
6675
+ }
6676
+ }
6677
+ function pickProviderLabel(options) {
6678
+ const cliName = options.cliProviderLabel?.trim();
6182
6679
  if (cliName && cliName !== "default") {
6183
6680
  return { name: cliName, source: "cli" };
6184
6681
  }
6185
- const fileName = options.fileTargetName?.trim();
6682
+ const fileName = options.fileProviderLabel?.trim();
6186
6683
  if (fileName && fileName.length > 0) {
6187
6684
  return { name: fileName, source: "test-file" };
6188
6685
  }
@@ -6193,12 +6690,12 @@ function withModelOverride(target, modelOverride) {
6193
6690
  return model && model.length > 0 ? { ...target, model } : target;
6194
6691
  }
6195
6692
  function overlayTargetDefinition(params) {
6196
- const { spec, definitions, env, targetsFilePath } = params;
6693
+ const { spec, definitions, env, providersFilePath } = params;
6197
6694
  if (!spec?.definition) {
6198
6695
  return void 0;
6199
6696
  }
6200
6697
  if (spec.extends) {
6201
- const base = resolveUseTarget(spec.extends, definitions, env, targetsFilePath);
6698
+ const base = resolveUseTarget(spec.extends, definitions, env, providersFilePath);
6202
6699
  return {
6203
6700
  ...base,
6204
6701
  ...spec.definition,
@@ -6210,82 +6707,86 @@ function overlayTargetDefinition(params) {
6210
6707
  function definitionsWithEffectiveTarget(definitions, effective) {
6211
6708
  return [effective, ...definitions.filter((definition) => definition.name !== effective.name)];
6212
6709
  }
6213
- async function selectTarget(options) {
6214
- const { testFilePath, repoRoot, cwd, explicitTargetsPath, cliTargetName, modelOverride, env } = options;
6215
- const targetsFilePath = await discoverTargetsFile({
6216
- explicitPath: explicitTargetsPath,
6710
+ async function resolveInlineDefinitionEnvironment(definition, testFilePath, location) {
6711
+ const [resolved] = await resolveProviderDefinitionEnvironments(
6712
+ [definition],
6713
+ path12.dirname(path12.resolve(testFilePath)),
6714
+ { location }
6715
+ );
6716
+ return resolved ?? definition;
6717
+ }
6718
+ async function selectProvider(options) {
6719
+ const {
6720
+ testFilePath,
6721
+ repoRoot,
6722
+ cwd,
6723
+ explicitProvidersPath,
6724
+ providerDefinitions,
6725
+ providerDefinitionsSource,
6726
+ requireExplicitProviderCatalog,
6727
+ allowLegacyTargetFiles,
6728
+ cliProviderLabel,
6729
+ modelOverride,
6730
+ env
6731
+ } = options;
6732
+ const providerCatalog = await readProviderCatalog({
6733
+ explicitProvidersPath,
6734
+ providerDefinitions,
6735
+ providerDefinitionsSource,
6736
+ requireExplicitProviderCatalog,
6217
6737
  testFilePath,
6218
6738
  repoRoot,
6219
- cwd
6739
+ cwd,
6740
+ allowLegacyTargetFiles
6220
6741
  });
6221
- const validationResult = await validateTargetsFile(targetsFilePath);
6222
- const warnings = validationResult.errors.filter((e) => e.severity === "warning");
6223
- const useColors2 = isTTY();
6224
- if (warnings.length > 0) {
6225
- console.warn(`
6226
- Warnings in ${targetsFilePath}:`);
6227
- for (const warning of warnings) {
6228
- const location = warning.location ? ` [${warning.location}]` : "";
6229
- const prefix = useColors2 ? `${ANSI_YELLOW3} \u26A0${ANSI_RESET3}` : " \u26A0";
6230
- const message = useColors2 ? `${ANSI_YELLOW3}${warning.message}${ANSI_RESET3}` : warning.message;
6231
- console.warn(`${prefix}${location} ${message}`);
6232
- }
6233
- console.warn("");
6234
- }
6235
- const errors = validationResult.errors.filter((e) => e.severity === "error");
6236
- if (errors.length > 0) {
6237
- console.error(`
6238
- Errors in ${targetsFilePath}:`);
6239
- for (const error of errors) {
6240
- const location = error.location ? ` [${error.location}]` : "";
6241
- const prefix = useColors2 ? `${ANSI_RED3} \u2717${ANSI_RESET3}` : " \u2717";
6242
- const message = useColors2 ? `${ANSI_RED3}${error.message}${ANSI_RESET3}` : error.message;
6243
- console.error(`${prefix}${location} ${message}`);
6244
- }
6245
- throw new Error(`Targets file validation failed with ${errors.length} error(s)`);
6246
- }
6247
- const definitions = await readTargetDefinitions(targetsFilePath);
6742
+ const definitions = providerCatalog.definitions;
6743
+ const providersFilePath = providerCatalog.sourcePath;
6248
6744
  const fileTargetSpec = options.fileTargetSpec;
6249
- const fileTargetName = options.fileTargetName ?? fileTargetSpec?.name ?? await readTestSuiteTarget(testFilePath);
6250
- const targetChoice = pickTargetName({ cliTargetName, fileTargetName });
6251
- const overlayDefinition = targetChoice.source === "test-file" ? overlayTargetDefinition({ spec: fileTargetSpec, definitions, env, targetsFilePath }) : void 0;
6745
+ const fileProviderLabel = options.fileProviderLabel ?? fileTargetSpec?.name ?? await readTestSuiteProvider(testFilePath);
6746
+ const providerChoice = pickProviderLabel({ cliProviderLabel, fileProviderLabel });
6747
+ const rawOverlayDefinition = providerChoice.source === "test-file" ? overlayTargetDefinition({ spec: fileTargetSpec, definitions, env, providersFilePath }) : void 0;
6748
+ const overlayDefinition = rawOverlayDefinition ? await resolveInlineDefinitionEnvironment(rawOverlayDefinition, testFilePath, "providers") : void 0;
6252
6749
  const targetDefinition = withModelOverride(
6253
- overlayDefinition ?? resolveUseTarget(targetChoice.name, definitions, env, targetsFilePath),
6750
+ overlayDefinition ?? resolveUseTarget(providerChoice.name, definitions, env, providersFilePath),
6254
6751
  modelOverride
6255
6752
  );
6256
6753
  const effectiveDefinitions = overlayDefinition !== void 0 ? definitionsWithEffectiveTarget(definitions, targetDefinition) : definitions;
6257
6754
  try {
6258
- const resolvedTarget = resolveTargetDefinition(targetDefinition, env, testFilePath, {
6755
+ const resolvedProvider = resolveProviderDefinition(targetDefinition, env, testFilePath, {
6259
6756
  emitDeprecationWarnings: false
6260
6757
  });
6261
6758
  return {
6262
6759
  definitions: effectiveDefinitions,
6263
- resolvedTarget,
6264
- targetName: targetChoice.name,
6265
- targetSource: targetChoice.source,
6266
- targetsFilePath,
6267
- ...targetChoice.source === "test-file" && fileTargetSpec?.hooks ? { targetHooks: fileTargetSpec.hooks } : {}
6760
+ resolvedProvider,
6761
+ providerLabel: providerChoice.name,
6762
+ providerSource: providerChoice.source,
6763
+ providersFilePath,
6764
+ ...providerChoice.source === "test-file" && fileTargetSpec?.hooks ? { providerHooks: fileTargetSpec.hooks } : {}
6268
6765
  };
6269
6766
  } catch (error) {
6270
6767
  const message = error instanceof Error ? error.message : String(error);
6271
- throw new Error(`Failed to resolve target '${targetChoice.name}': ${message}`);
6768
+ throw new Error(`Failed to resolve provider '${providerChoice.name}': ${message}`);
6272
6769
  }
6273
6770
  }
6274
- async function selectMultipleTargets(options) {
6771
+ async function selectMultipleProviders(options) {
6275
6772
  const {
6276
6773
  testFilePath,
6277
6774
  repoRoot,
6278
6775
  cwd,
6279
- explicitTargetsPath,
6776
+ explicitProvidersPath,
6777
+ providerDefinitions,
6778
+ providerDefinitionsSource,
6779
+ requireExplicitProviderCatalog,
6780
+ allowLegacyTargetFiles,
6280
6781
  env,
6281
- targetNames,
6282
- targetRefs,
6782
+ providerLabels,
6783
+ providerRefs,
6283
6784
  modelOverride
6284
6785
  } = options;
6285
6786
  const hooksMap = /* @__PURE__ */ new Map();
6286
6787
  const labelsMap = /* @__PURE__ */ new Map();
6287
- if (targetRefs) {
6288
- for (const ref of targetRefs) {
6788
+ if (providerRefs) {
6789
+ for (const ref of providerRefs) {
6289
6790
  if (ref.hooks) {
6290
6791
  hooksMap.set(ref.name, ref.hooks);
6291
6792
  }
@@ -6294,73 +6795,54 @@ async function selectMultipleTargets(options) {
6294
6795
  }
6295
6796
  }
6296
6797
  }
6297
- const targetsFilePath = await discoverTargetsFile({
6298
- explicitPath: explicitTargetsPath,
6798
+ const providerCatalog = await readProviderCatalog({
6799
+ explicitProvidersPath,
6800
+ providerDefinitions,
6801
+ providerDefinitionsSource,
6802
+ requireExplicitProviderCatalog,
6299
6803
  testFilePath,
6300
6804
  repoRoot,
6301
- cwd
6805
+ cwd,
6806
+ allowLegacyTargetFiles
6302
6807
  });
6303
- const validationResult = await validateTargetsFile(targetsFilePath);
6304
- const warnings = validationResult.errors.filter((e) => e.severity === "warning");
6305
- const useColors2 = isTTY();
6306
- if (warnings.length > 0) {
6307
- console.warn(`
6308
- Warnings in ${targetsFilePath}:`);
6309
- for (const warning of warnings) {
6310
- const location = warning.location ? ` [${warning.location}]` : "";
6311
- const prefix = useColors2 ? `${ANSI_YELLOW3} \u26A0${ANSI_RESET3}` : " \u26A0";
6312
- const message = useColors2 ? `${ANSI_YELLOW3}${warning.message}${ANSI_RESET3}` : warning.message;
6313
- console.warn(`${prefix}${location} ${message}`);
6314
- }
6315
- console.warn("");
6316
- }
6317
- const errors = validationResult.errors.filter((e) => e.severity === "error");
6318
- if (errors.length > 0) {
6319
- console.error(`
6320
- Errors in ${targetsFilePath}:`);
6321
- for (const error of errors) {
6322
- const location = error.location ? ` [${error.location}]` : "";
6323
- const prefix = useColors2 ? `${ANSI_RED3} \u2717${ANSI_RESET3}` : " \u2717";
6324
- const message = useColors2 ? `${ANSI_RED3}${error.message}${ANSI_RESET3}` : error.message;
6325
- console.error(`${prefix}${location} ${message}`);
6326
- }
6327
- throw new Error(`Targets file validation failed with ${errors.length} error(s)`);
6328
- }
6329
- const fileDefinitions = await readTargetDefinitions(targetsFilePath);
6808
+ const providersFilePath = providerCatalog.sourcePath;
6809
+ const fileDefinitions = providerCatalog.definitions;
6330
6810
  const definitions = [...fileDefinitions];
6331
- if (targetRefs) {
6332
- for (const ref of targetRefs) {
6811
+ if (providerRefs) {
6812
+ for (const ref of providerRefs) {
6333
6813
  if (ref.definition && !fileDefinitions.some((d) => d.name === ref.name)) {
6334
- definitions.push(ref.definition);
6814
+ definitions.push(
6815
+ await resolveInlineDefinitionEnvironment(ref.definition, testFilePath, "providers")
6816
+ );
6335
6817
  } else if (ref.use_target && !fileDefinitions.some((d) => d.name === ref.name)) {
6336
6818
  definitions.push({ name: ref.name, use_target: ref.use_target });
6337
6819
  }
6338
6820
  }
6339
6821
  }
6340
6822
  const results = [];
6341
- for (const name of targetNames) {
6823
+ for (const name of providerLabels) {
6342
6824
  const targetDefinition = withModelOverride(
6343
- resolveUseTarget(name, definitions, env, targetsFilePath),
6825
+ resolveUseTarget(name, definitions, env, providersFilePath),
6344
6826
  modelOverride
6345
6827
  );
6346
6828
  const hooks = hooksMap.get(name);
6347
- const targetLabel = labelsMap.get(name);
6829
+ const providerDisplayLabel = labelsMap.get(name);
6348
6830
  try {
6349
- const resolvedTarget = resolveTargetDefinition(targetDefinition, env, testFilePath, {
6831
+ const resolvedProvider = resolveProviderDefinition(targetDefinition, env, testFilePath, {
6350
6832
  emitDeprecationWarnings: false
6351
6833
  });
6352
6834
  results.push({
6353
6835
  definitions,
6354
- resolvedTarget,
6355
- targetName: name,
6356
- ...targetLabel ? { targetLabel } : {},
6357
- targetSource: options.targetSource ?? "cli",
6358
- targetsFilePath,
6359
- ...hooks && { targetHooks: hooks }
6836
+ resolvedProvider,
6837
+ providerLabel: name,
6838
+ ...providerDisplayLabel ? { providerDisplayLabel } : {},
6839
+ providerSource: options.providerSource ?? "cli",
6840
+ providersFilePath,
6841
+ ...hooks && { providerHooks: hooks }
6360
6842
  });
6361
6843
  } catch (error) {
6362
6844
  const message = error instanceof Error ? error.message : String(error);
6363
- throw new Error(`Failed to resolve target '${name}': ${message}`);
6845
+ throw new Error(`Failed to resolve provider '${name}': ${message}`);
6364
6846
  }
6365
6847
  }
6366
6848
  return results;
@@ -6459,7 +6941,7 @@ var DEFAULT_WORKERS = 3;
6459
6941
  var loadCjsModule = createNodeRequire(import.meta.url);
6460
6942
  var micromatch = loadCjsModule("micromatch");
6461
6943
  async function loadCliEvalSuite(testFilePath, repoRoot, options) {
6462
- if (path12.extname(testFilePath).toLowerCase() === ".json") {
6944
+ if (path13.extname(testFilePath).toLowerCase() === ".json") {
6463
6945
  const adapterSuite = readAgentSkillsEvalsFile(testFilePath);
6464
6946
  return loadTestSuiteFromYamlObject(
6465
6947
  testFilePath,
@@ -6575,8 +7057,8 @@ function resultsRepoOverride(value) {
6575
7057
  if (!value) {
6576
7058
  return {};
6577
7059
  }
6578
- if (value === "current" || value === "." || value.startsWith("./") || value.startsWith("../") || value.startsWith("/") || value.startsWith("~/") || value.startsWith("~\\") || /^[A-Za-z]:[/\\]/.test(value)) {
6579
- return { repo_path: value === "current" ? "." : value };
7060
+ if (value === "current" || value === "current/." || value === "." || value.startsWith("./") || value.startsWith("../") || value.startsWith("/") || value.startsWith("~/") || value.startsWith("~\\") || /^[A-Za-z]:[/\\]/.test(value)) {
7061
+ return { repo_path: value === "current" || value === "current/." ? "." : value };
6580
7062
  }
6581
7063
  return { repo: value };
6582
7064
  }
@@ -6613,13 +7095,32 @@ function normalizeSourceMetadataByEvalFile(value) {
6613
7095
  const entries = [...value.entries()].filter(
6614
7096
  (entry) => typeof entry[0] === "string" && typeof entry[1] === "object" && entry[1] !== null && !Array.isArray(entry[1])
6615
7097
  );
6616
- return entries.length > 0 ? new Map(entries.map(([key, metadata]) => [path12.resolve(key), metadata])) : void 0;
7098
+ return entries.length > 0 ? new Map(entries.map(([key, metadata]) => [path13.resolve(key), metadata])) : void 0;
6617
7099
  }
6618
7100
  if (typeof value === "object" && value !== null && !Array.isArray(value)) {
6619
7101
  const entries = Object.entries(value).filter(
6620
7102
  (entry) => typeof entry[1] === "object" && entry[1] !== null && !Array.isArray(entry[1])
6621
7103
  );
6622
- return entries.length > 0 ? new Map(entries.map(([key, metadata]) => [path12.resolve(key), metadata])) : void 0;
7104
+ return entries.length > 0 ? new Map(entries.map(([key, metadata]) => [path13.resolve(key), metadata])) : void 0;
7105
+ }
7106
+ return void 0;
7107
+ }
7108
+ function normalizeProviderCatalogPathByEvalFile(value) {
7109
+ if (value instanceof Map) {
7110
+ const entries = [...value.entries()].filter(
7111
+ (entry) => typeof entry[0] === "string" && typeof entry[1] === "string" && entry[1].trim().length > 0
7112
+ );
7113
+ return entries.length > 0 ? new Map(
7114
+ entries.map(([key, providerPath]) => [path13.resolve(key), path13.resolve(providerPath)])
7115
+ ) : void 0;
7116
+ }
7117
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
7118
+ const entries = Object.entries(value).filter(
7119
+ (entry) => typeof entry[1] === "string" && entry[1].trim().length > 0
7120
+ );
7121
+ return entries.length > 0 ? new Map(
7122
+ entries.map(([key, providerPath]) => [path13.resolve(key), path13.resolve(providerPath)])
7123
+ ) : void 0;
6623
7124
  }
6624
7125
  return void 0;
6625
7126
  }
@@ -6633,10 +7134,10 @@ var LEGACY_OUTPUT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
6633
7134
  ".htm"
6634
7135
  ]);
6635
7136
  function looksLikeLegacyOutputFilePath(value) {
6636
- return LEGACY_OUTPUT_FILE_EXTENSIONS.has(path12.extname(value).toLowerCase());
7137
+ return LEGACY_OUTPUT_FILE_EXTENSIONS.has(path13.extname(value).toLowerCase());
6637
7138
  }
6638
7139
  function outputFileMigrationMessage(value) {
6639
- const ext = path12.extname(value).toLowerCase();
7140
+ const ext = path13.extname(value).toLowerCase();
6640
7141
  const removalHint = ext === ".xml" ? "JUnit XML export from agentv eval has been removed." : "Flat result file export from agentv eval has been removed.";
6641
7142
  return `--output expects a run directory, not a file path: ${value}
6642
7143
  ${removalHint} Set --output <dir> for the canonical run artifacts; AgentV always writes <dir>/${RESULT_INDEX_FILENAME}.`;
@@ -6644,7 +7145,7 @@ ${removalHint} Set --output <dir> for the canonical run artifacts; AgentV always
6644
7145
  function artifactsMigrationMessage(artifactsDir, outputDir) {
6645
7146
  const lines = [`--artifacts was removed from agentv eval. Use --output ${artifactsDir} instead.`];
6646
7147
  if (outputDir && looksLikeLegacyOutputFilePath(outputDir)) {
6647
- const ext = path12.extname(outputDir).toLowerCase();
7148
+ const ext = path13.extname(outputDir).toLowerCase();
6648
7149
  lines.push(
6649
7150
  ext === ".xml" ? "JUnit XML export from agentv eval has been removed." : "Flat result file export from agentv eval has been removed."
6650
7151
  );
@@ -6737,17 +7238,19 @@ function normalizeOptions(rawOptions, config, yamlExecution) {
6737
7238
  const configWorkers = config?.execution?.maxConcurrency ?? yamlExecution?.max_concurrency;
6738
7239
  const workers = cliWorkers ?? configWorkers ?? 0;
6739
7240
  const cliOutputDir = normalizeString(rawOptions.output);
6740
- const rawTarget = rawOptions.target;
6741
- let cliTargets = [];
6742
- let singleTarget;
6743
- if (Array.isArray(rawTarget)) {
6744
- cliTargets = rawTarget.filter((v) => typeof v === "string" && v.trim().length > 0);
6745
- singleTarget = cliTargets.length === 1 ? cliTargets[0] : void 0;
6746
- } else if (typeof rawTarget === "string") {
6747
- const trimmed = rawTarget.trim();
7241
+ const rawProvider = rawOptions.provider;
7242
+ let cliProviderLabels = [];
7243
+ let singleProvider;
7244
+ if (Array.isArray(rawProvider)) {
7245
+ cliProviderLabels = rawProvider.filter(
7246
+ (v) => typeof v === "string" && v.trim().length > 0
7247
+ );
7248
+ singleProvider = cliProviderLabels.length === 1 ? cliProviderLabels[0] : void 0;
7249
+ } else if (typeof rawProvider === "string") {
7250
+ const trimmed = rawProvider.trim();
6748
7251
  if (trimmed.length > 0 && trimmed !== "default") {
6749
- cliTargets = [trimmed];
6750
- singleTarget = trimmed;
7252
+ cliProviderLabels = [trimmed];
7253
+ singleProvider = trimmed;
6751
7254
  }
6752
7255
  }
6753
7256
  const cliAgentTimeout = normalizeOptionalNumber(rawOptions.agentTimeout);
@@ -6783,9 +7286,13 @@ function normalizeOptions(rawOptions, config, yamlExecution) {
6783
7286
  };
6784
7287
  const cliTags = splitCliTags(rawOptions.tag);
6785
7288
  return {
6786
- target: singleTarget,
6787
- cliTargets,
6788
- targetsPath: normalizeString(rawOptions.targets),
7289
+ provider: singleProvider,
7290
+ cliProviderLabels,
7291
+ providersPath: normalizeString(rawOptions.providers),
7292
+ allowLegacyTargetFiles: normalizeBoolean(rawOptions.allowLegacyTargetFiles),
7293
+ providerCatalogPathByEvalFile: normalizeProviderCatalogPathByEvalFile(
7294
+ rawOptions.providerCatalogPathByEvalFile
7295
+ ),
6789
7296
  filter: normalizeFilter(rawOptions.filter),
6790
7297
  workers: workers > 0 ? workers : void 0,
6791
7298
  outputDir: cliOutputDir ?? configOutputDir,
@@ -6832,7 +7339,7 @@ function normalizeOptions(rawOptions, config, yamlExecution) {
6832
7339
  };
6833
7340
  }
6834
7341
  function withSourceMetadata(result, testFilePath, options) {
6835
- const sourceMetadata = options.sourceMetadataByEvalFile?.get(path12.resolve(testFilePath));
7342
+ const sourceMetadata = options.sourceMetadataByEvalFile?.get(path13.resolve(testFilePath));
6836
7343
  if (!sourceMetadata) {
6837
7344
  return result;
6838
7345
  }
@@ -6855,11 +7362,11 @@ function deriveEvalResultGroupName(evalFilePath) {
6855
7362
  if (!evalFilePath) {
6856
7363
  return "eval";
6857
7364
  }
6858
- return path12.basename(evalFilePath).replace(/\.eval\.ya?ml$/i, "").replace(/\.ya?ml$/i, "").replace(/[^A-Za-z0-9._-]/g, "-") || "eval";
7365
+ return path13.basename(evalFilePath).replace(/\.eval\.ya?ml$/i, "").replace(/\.ya?ml$/i, "").replace(/[^A-Za-z0-9._-]/g, "-") || "eval";
6859
7366
  }
6860
7367
  var CLI_RUNTIME_SOURCE_OPTION_KEYS = [
6861
- "target",
6862
- "targets",
7368
+ "provider",
7369
+ "providers",
6863
7370
  "filter",
6864
7371
  "tag",
6865
7372
  "excludeTag",
@@ -6902,10 +7409,10 @@ function toRuntimeSourcePath(cwd, filePath) {
6902
7409
  if (!trimmed) {
6903
7410
  return void 0;
6904
7411
  }
6905
- const resolved = path12.isAbsolute(trimmed) ? trimmed : path12.resolve(cwd, trimmed);
6906
- const relative = path12.relative(cwd, resolved);
6907
- const displayPath = relative && !relative.startsWith("..") && !path12.isAbsolute(relative) ? relative : trimmed;
6908
- return displayPath.split(path12.sep).join("/");
7412
+ const resolved = path13.isAbsolute(trimmed) ? trimmed : path13.resolve(cwd, trimmed);
7413
+ const relative = path13.relative(cwd, resolved);
7414
+ const displayPath = relative && !relative.startsWith("..") && !path13.isAbsolute(relative) ? relative : trimmed;
7415
+ return displayPath.split(path13.sep).join("/");
6909
7416
  }
6910
7417
  function uniqueRuntimeSourcePaths(values) {
6911
7418
  return [...new Set([...values].filter((value) => Boolean(value)))].sort();
@@ -6915,7 +7422,7 @@ function testSourceEvalPath(cwd, test) {
6915
7422
  }
6916
7423
  function testSourceEvalPathForComparison(test) {
6917
7424
  const sourcePath = test.source?.evalFileAbsolutePath ?? test.source?.evalFilePath;
6918
- return sourcePath ? path12.resolve(sourcePath) : void 0;
7425
+ return sourcePath ? path13.resolve(sourcePath) : void 0;
6919
7426
  }
6920
7427
  function buildRuntimeConfigSource(params) {
6921
7428
  const inlineFingerprints = /* @__PURE__ */ new Set();
@@ -6946,7 +7453,7 @@ function buildRuntimeSourceMetadata(params) {
6946
7453
  params.activeTestFiles.map((filePath) => toRuntimeSourcePath(params.cwd, filePath))
6947
7454
  );
6948
7455
  const activeResolvedFiles = new Set(
6949
- params.activeTestFiles.map((filePath) => path12.resolve(filePath))
7456
+ params.activeTestFiles.map((filePath) => path13.resolve(filePath))
6950
7457
  );
6951
7458
  const sourceEvalFiles = uniqueRuntimeSourcePaths(
6952
7459
  params.sourceTests.map((test) => testSourceEvalPath(params.cwd, test))
@@ -6977,22 +7484,21 @@ function applyExperimentOptions(options, experiment) {
6977
7484
  if (!experiment) {
6978
7485
  return options;
6979
7486
  }
6980
- const experimentTargetRefs = buildExperimentTargetRefs(experiment);
6981
- const experimentTargetNames = experimentTargetRefs?.map((target) => target.name) ?? [];
7487
+ const experimentProviderRefs = buildExperimentTargetRefs(experiment);
7488
+ const experimentProviderLabels = experimentProviderRefs?.map((target) => target.name) ?? [];
6982
7489
  const experimentTarget = experiment.target && experiment.target.trim().length > 0 ? experiment.target : void 0;
6983
- const experimentTargets = options.cliTargets.length === 0 ? experimentTargetNames.length > 0 ? experimentTargetNames : experimentTarget ? [experimentTarget] : void 0 : void 0;
7490
+ const experimentProviders = options.cliProviderLabels.length === 0 ? experimentProviderLabels.length > 0 ? experimentProviderLabels : experimentTarget ? [experimentTarget] : void 0 : void 0;
6984
7491
  return {
6985
7492
  ...options,
6986
- target: options.target,
6987
7493
  agentTimeoutSeconds: options.agentTimeoutSeconds ?? experiment.timeoutSeconds,
6988
7494
  workspacePath: options.workspacePath,
6989
7495
  budgetUsd: options.budgetUsd ?? experiment.budgetUsd,
6990
7496
  threshold: options.threshold ?? experiment.threshold,
6991
7497
  experimentConfig: experiment,
6992
7498
  experimentMetadata: buildExperimentArtifactMetadata(experiment),
6993
- experimentTargets,
6994
- experimentTargetRefs: options.cliTargets.length === 0 ? experimentTargetRefs : void 0,
6995
- targetModelOverride: options.targetModelOverride ?? experiment.model,
7499
+ experimentProviderLabels: experimentProviders,
7500
+ experimentProviderRefs: options.cliProviderLabels.length === 0 ? experimentProviderRefs : void 0,
7501
+ providerModelOverride: options.providerModelOverride ?? experiment.model,
6996
7502
  experimentTrialsConfig: buildExperimentTrialsConfig(experiment)
6997
7503
  };
6998
7504
  }
@@ -7020,11 +7526,7 @@ function buildExperimentTrialsConfig(experiment) {
7020
7526
  }
7021
7527
  return {
7022
7528
  count: experiment.repeat.count,
7023
- strategy: experiment.repeat.strategy,
7024
- ...experiment.repeat.costLimitUsd !== void 0 && {
7025
- costLimitUsd: experiment.repeat.costLimitUsd
7026
- },
7027
- ...experiment.repeat.earlyExit !== void 0 && { earlyExit: experiment.repeat.earlyExit }
7529
+ strategy: experiment.repeat.strategy
7028
7530
  };
7029
7531
  }
7030
7532
  return void 0;
@@ -7036,9 +7538,7 @@ function buildRunOverrideTrialsConfig(run) {
7036
7538
  }
7037
7539
  return {
7038
7540
  count: repeat.count,
7039
- strategy: repeat.strategy,
7040
- ...repeat.costLimitUsd !== void 0 && { costLimitUsd: repeat.costLimitUsd },
7041
- ...repeat.earlyExit !== void 0 && { earlyExit: repeat.earlyExit }
7541
+ strategy: repeat.strategy
7042
7542
  };
7043
7543
  }
7044
7544
  function resolveEffectiveRunPolicy(params) {
@@ -7100,7 +7600,7 @@ function createProgressReporter(maxWorkers, options) {
7100
7600
  };
7101
7601
  }
7102
7602
  function makeTestCaseKey(testFilePath, testId) {
7103
- return `${path12.resolve(testFilePath)}::${testId}`;
7603
+ return `${path13.resolve(testFilePath)}::${testId}`;
7104
7604
  }
7105
7605
  function resolveTargetLabel(requestedName, resolvedName) {
7106
7606
  if (resolvedName !== requestedName) {
@@ -7139,23 +7639,23 @@ function buildTargetLabelSuffix(providerLabel, target) {
7139
7639
  return `[${parts.join(", ")}]`;
7140
7640
  }
7141
7641
  function applyVerboseOverride(selection, cliVerbose) {
7142
- const { resolvedTarget } = selection;
7143
- if (resolvedTarget.kind !== "cli") {
7642
+ const { resolvedProvider } = selection;
7643
+ if (resolvedProvider.kind !== "cli") {
7144
7644
  return selection;
7145
7645
  }
7146
7646
  return {
7147
7647
  ...selection,
7148
- resolvedTarget: {
7149
- ...resolvedTarget,
7648
+ resolvedProvider: {
7649
+ ...resolvedProvider,
7150
7650
  config: {
7151
- ...resolvedTarget.config,
7651
+ ...resolvedProvider.config,
7152
7652
  verbose: cliVerbose
7153
7653
  }
7154
7654
  }
7155
7655
  };
7156
7656
  }
7157
7657
  function targetVariantForSelection(selection) {
7158
- const target = selection.resolvedTarget;
7658
+ const target = selection.resolvedProvider;
7159
7659
  if (target.kind === "replay") {
7160
7660
  return target.config.variant;
7161
7661
  }
@@ -7197,20 +7697,24 @@ async function readExistingResultsFromRunDir(runDir) {
7197
7697
  }
7198
7698
  return results;
7199
7699
  }
7700
+ async function collectTerminalSummaryResults(params) {
7701
+ const rawResults = params.isResumeAppend ? await (params.readExistingResults ?? readExistingResultsFromRunDir)(params.runDir) : params.allResults;
7702
+ return deduplicateByTestIdTarget(rawResults);
7703
+ }
7200
7704
  async function resolveRerunFailedRunDir(cwd, source) {
7201
7705
  const trimmed = source.trim();
7202
7706
  if (!trimmed) {
7203
7707
  throw new Error("--rerun-failed requires a run ID, run workspace, or index.jsonl path.");
7204
7708
  }
7205
- const candidate = path12.isAbsolute(trimmed) ? trimmed : path12.resolve(cwd, trimmed);
7709
+ const candidate = path13.isAbsolute(trimmed) ? trimmed : path13.resolve(cwd, trimmed);
7206
7710
  if (existsSync5(candidate)) {
7207
- if (path12.basename(candidate) !== RESULT_INDEX_FILENAME) {
7711
+ if (path13.basename(candidate) !== RESULT_INDEX_FILENAME) {
7208
7712
  return candidate;
7209
7713
  }
7210
- const manifestDir = path12.dirname(candidate);
7211
- return path12.basename(manifestDir) === ".internal" ? path12.dirname(manifestDir) : manifestDir;
7714
+ const manifestDir = path13.dirname(candidate);
7715
+ return path13.basename(manifestDir) === ".internal" ? path13.dirname(manifestDir) : manifestDir;
7212
7716
  }
7213
- const runIdCandidate = path12.join(cwd, ".agentv", "results", trimmed);
7717
+ const runIdCandidate = path13.join(cwd, ".agentv", "results", trimmed);
7214
7718
  if (existsSync5(runIdCandidate)) {
7215
7719
  return runIdCandidate;
7216
7720
  }
@@ -7219,14 +7723,23 @@ async function resolveRerunFailedRunDir(cwd, source) {
7219
7723
  );
7220
7724
  }
7221
7725
  async function prepareFileMetadata(params) {
7222
- const { testFilePath, repoRoot, cwd, options, suiteFilter } = params;
7726
+ const {
7727
+ testFilePath,
7728
+ repoRoot,
7729
+ cwd,
7730
+ options,
7731
+ providerCatalogPath,
7732
+ providerDefinitions,
7733
+ providerDefinitionsSource,
7734
+ suiteFilter
7735
+ } = params;
7223
7736
  await ensureFileExists(testFilePath, "Test file");
7224
7737
  await loadEnvFromHierarchy({
7225
7738
  testFilePath,
7226
7739
  repoRoot,
7227
7740
  verbose: options.verbose
7228
7741
  });
7229
- const relativePath = path12.relative(cwd, testFilePath);
7742
+ const relativePath = path13.relative(cwd, testFilePath);
7230
7743
  const category = deriveCategory(relativePath);
7231
7744
  const suite = await loadCliEvalSuite(testFilePath, repoRoot, {
7232
7745
  verbose: options.verbose,
@@ -7235,21 +7748,24 @@ async function prepareFileMetadata(params) {
7235
7748
  });
7236
7749
  const experimentOptions = applyExperimentOptions(options, suite.experimentConfig);
7237
7750
  const effectiveOptions = experimentOptions.workers === void 0 && suite.workers !== void 0 ? { ...experimentOptions, workers: suite.workers } : experimentOptions;
7751
+ const effectiveProviderCatalogPath = effectiveOptions.providersPath ?? providerCatalogPath;
7238
7752
  const testCases = suiteFilter && effectiveOptions.filter ? suite.tests.filter(
7239
7753
  (testCase) => matchesTestFilter(testCase.id, effectiveOptions.filter ?? "")
7240
7754
  ) : suite.tests;
7241
7755
  const testIds = testCases.map((value) => value.id);
7242
7756
  const suiteTargetSpec = suite.targetSpec;
7757
+ const suiteDefaults = suite.defaults;
7243
7758
  const suiteTargets = suiteTargetSpec ? [suiteTargetSpec.name] : suite.targets;
7759
+ const fileOptions = suiteDefaults?.grader && !effectiveOptions.graderTarget ? { ...effectiveOptions, defaultGraderTarget: suiteDefaults.grader } : effectiveOptions;
7244
7760
  const defaultBudgetUsd = effectiveOptions.cliBudgetUsd === void 0 ? effectiveOptions.budgetUsd ?? suite.budgetUsd : void 0;
7245
7761
  const suiteDefaultThreshold = suite.defaultTest?.threshold ?? suite.threshold;
7246
7762
  if (testCases.length === 0) {
7247
7763
  return {
7248
- options: effectiveOptions,
7764
+ options: fileOptions,
7249
7765
  testIds,
7250
7766
  testCases,
7251
7767
  selections: [],
7252
- trialsConfig: effectiveOptions.experimentTrialsConfig,
7768
+ trialsConfig: fileOptions.experimentTrialsConfig,
7253
7769
  suiteTargets,
7254
7770
  yamlCache: suite.cacheConfig?.enabled,
7255
7771
  yamlCachePath: suite.cacheConfig?.cachePath,
@@ -7261,42 +7777,47 @@ async function prepareFileMetadata(params) {
7261
7777
  };
7262
7778
  }
7263
7779
  let selections;
7264
- if (effectiveOptions.transcript) {
7780
+ if (fileOptions.transcript) {
7265
7781
  const transcriptSelection = {
7266
7782
  definitions: [],
7267
- resolvedTarget: {
7783
+ resolvedProvider: {
7268
7784
  kind: "transcript",
7269
7785
  name: "transcript",
7270
7786
  config: {}
7271
7787
  },
7272
- targetName: "transcript",
7273
- targetSource: "cli",
7274
- targetsFilePath: effectiveOptions.transcript
7788
+ providerLabel: "transcript",
7789
+ providerSource: "cli",
7790
+ providersFilePath: fileOptions.transcript
7275
7791
  };
7276
7792
  selections = [
7277
7793
  {
7278
7794
  selection: transcriptSelection,
7279
- inlineTargetLabel: `transcript (${path12.basename(effectiveOptions.transcript)})`
7795
+ inlineTargetLabel: `transcript (${path13.basename(fileOptions.transcript)})`
7280
7796
  }
7281
7797
  ];
7282
- } else if (suite.inlineTarget && effectiveOptions.cliTargets.length === 0) {
7798
+ } else if (suite.inlineTarget && fileOptions.cliProviderLabels.length === 0) {
7283
7799
  const targetDefinition = suite.inlineTarget;
7284
- const resolvedTarget = resolveTargetDefinition(targetDefinition, process.env, testFilePath, {
7285
- emitDeprecationWarnings: false
7286
- });
7800
+ const resolvedProvider = resolveProviderDefinition(
7801
+ targetDefinition,
7802
+ process.env,
7803
+ testFilePath,
7804
+ {
7805
+ emitDeprecationWarnings: false
7806
+ }
7807
+ );
7287
7808
  selections = [
7288
7809
  {
7289
7810
  selection: {
7290
7811
  definitions: [targetDefinition],
7291
- resolvedTarget,
7292
- targetName: targetDefinition.name,
7293
- targetSource: "test-file",
7294
- targetsFilePath: testFilePath
7812
+ resolvedProvider,
7813
+ providerLabel: targetDefinition.name,
7814
+ providerSource: "test-file",
7815
+ providersFilePath: testFilePath
7295
7816
  },
7296
- inlineTargetLabel: resolveTargetLabel(targetDefinition.name, resolvedTarget.name)
7817
+ inlineTargetLabel: resolveTargetLabel(targetDefinition.name, resolvedProvider.name)
7297
7818
  }
7298
7819
  ];
7299
- } else if (suite.providerFactory && effectiveOptions.cliTargets.length === 0) {
7820
+ } else if (suite.providerFactory && fileOptions.cliProviderLabels.length === 0) {
7300
7821
  const taskTarget = {
7301
7822
  kind: "mock",
7302
7823
  name: "custom-task",
@@ -7307,89 +7828,100 @@ async function prepareFileMetadata(params) {
7307
7828
  {
7308
7829
  selection: {
7309
7830
  definitions: [],
7310
- resolvedTarget: taskTarget,
7311
- targetName: "custom-task",
7312
- targetSource: "test-file",
7313
- targetsFilePath: testFilePath
7831
+ resolvedProvider: taskTarget,
7832
+ providerLabel: "custom-task",
7833
+ providerSource: "test-file",
7834
+ providersFilePath: testFilePath
7314
7835
  },
7315
7836
  inlineTargetLabel: "custom-task"
7316
7837
  }
7317
7838
  ];
7318
7839
  } else {
7319
- const cliTargets = effectiveOptions.cliTargets;
7320
- const experimentTargets = effectiveOptions.experimentTargets ?? [];
7840
+ const cliProviderLabels = fileOptions.cliProviderLabels;
7841
+ const experimentProviderLabels = fileOptions.experimentProviderLabels ?? [];
7321
7842
  const suiteTargetSpec2 = suite.targetSpec;
7322
- const suiteTargets2 = suiteTargetSpec2 ? [suiteTargetSpec2.name] : suite.targets;
7323
- const suiteTargetRefs = suite.targetRefs;
7324
- const experimentTargetRefs = effectiveOptions.experimentTargetRefs;
7325
- let targetNames;
7326
- let targetRefs;
7327
- let targetSource = "test-file";
7328
- if (cliTargets.length > 0) {
7329
- targetNames = cliTargets;
7330
- targetRefs = experimentTargetRefs;
7331
- targetSource = "cli";
7332
- } else if (experimentTargets.length > 0) {
7333
- targetNames = experimentTargets;
7334
- targetRefs = experimentTargetRefs;
7335
- } else if (suiteTargets2 && suiteTargets2.length > 0) {
7336
- targetNames = suiteTargets2;
7337
- targetRefs = suiteTargetRefs;
7843
+ const suiteProviderLabels = suiteTargetSpec2 ? [suiteTargetSpec2.name] : suite.targets;
7844
+ const suiteProviderRefs = suite.targetRefs;
7845
+ const experimentProviderRefs = fileOptions.experimentProviderRefs;
7846
+ let providerLabels;
7847
+ let providerRefs;
7848
+ let providerSource = "test-file";
7849
+ if (cliProviderLabels.length > 0) {
7850
+ providerLabels = cliProviderLabels;
7851
+ providerRefs = experimentProviderRefs;
7852
+ providerSource = "cli";
7853
+ } else if (experimentProviderLabels.length > 0) {
7854
+ providerLabels = experimentProviderLabels;
7855
+ providerRefs = experimentProviderRefs;
7856
+ } else if (suiteProviderLabels && suiteProviderLabels.length > 0) {
7857
+ providerLabels = suiteProviderLabels;
7858
+ providerRefs = suiteProviderRefs;
7859
+ } else if (suiteDefaults?.provider) {
7860
+ providerLabels = [suiteDefaults.provider];
7861
+ providerRefs = void 0;
7338
7862
  } else {
7339
- targetNames = [];
7340
- targetRefs = void 0;
7863
+ providerLabels = [];
7864
+ providerRefs = void 0;
7341
7865
  }
7342
- if (targetNames.length > 1 || targetNames.length === 1 && targetRefs) {
7343
- const multiSelections = await selectMultipleTargets({
7866
+ if (providerLabels.length > 1 || providerLabels.length === 1 && providerRefs) {
7867
+ const multiSelections = await selectMultipleProviders({
7344
7868
  testFilePath,
7345
7869
  repoRoot,
7346
7870
  cwd,
7347
- explicitTargetsPath: effectiveOptions.targetsPath,
7871
+ explicitProvidersPath: effectiveProviderCatalogPath,
7872
+ providerDefinitions,
7873
+ providerDefinitionsSource,
7874
+ requireExplicitProviderCatalog: true,
7875
+ allowLegacyTargetFiles: fileOptions.allowLegacyTargetFiles,
7348
7876
  env: process.env,
7349
- targetNames,
7350
- targetRefs,
7351
- targetSource,
7352
- modelOverride: effectiveOptions.targetModelOverride
7877
+ providerLabels,
7878
+ providerRefs,
7879
+ providerSource,
7880
+ modelOverride: fileOptions.providerModelOverride
7353
7881
  });
7354
7882
  selections = multiSelections.map((sel) => ({
7355
7883
  selection: sel,
7356
- inlineTargetLabel: sel.targetLabel ?? resolveTargetLabel(sel.targetName, sel.resolvedTarget.name)
7884
+ inlineTargetLabel: sel.providerDisplayLabel ?? resolveTargetLabel(sel.providerLabel, sel.resolvedProvider.name)
7357
7885
  }));
7358
7886
  } else {
7359
- const selection = await selectTarget({
7887
+ const selection = await selectProvider({
7360
7888
  testFilePath,
7361
7889
  repoRoot,
7362
7890
  cwd,
7363
- explicitTargetsPath: effectiveOptions.targetsPath,
7364
- cliTargetName: targetSource === "cli" ? targetNames.length === 1 ? targetNames[0] : effectiveOptions.target : effectiveOptions.target,
7365
- fileTargetName: targetSource === "test-file" && targetNames.length === 1 ? targetNames[0] : void 0,
7366
- fileTargetSpec: targetSource === "test-file" && targetNames.length === 1 ? suiteTargetSpec2 : void 0,
7367
- modelOverride: effectiveOptions.targetModelOverride,
7891
+ explicitProvidersPath: effectiveProviderCatalogPath,
7892
+ providerDefinitions,
7893
+ providerDefinitionsSource,
7894
+ requireExplicitProviderCatalog: true,
7895
+ allowLegacyTargetFiles: fileOptions.allowLegacyTargetFiles,
7896
+ cliProviderLabel: providerSource === "cli" ? providerLabels.length === 1 ? providerLabels[0] : fileOptions.provider : fileOptions.provider,
7897
+ fileProviderLabel: providerSource === "test-file" && providerLabels.length === 1 ? providerLabels[0] : void 0,
7898
+ fileTargetSpec: providerSource === "test-file" && providerLabels.length === 1 ? suiteTargetSpec2 : void 0,
7899
+ modelOverride: fileOptions.providerModelOverride,
7368
7900
  env: process.env
7369
7901
  });
7370
- const singleTargetRef = targetRefs?.find((ref) => ref.name === selection.targetName);
7902
+ const singleProviderRef = providerRefs?.find((ref) => ref.name === selection.providerLabel);
7371
7903
  const augmentedSelection = {
7372
7904
  ...selection,
7373
- ...singleTargetRef?.label ? { targetLabel: singleTargetRef.label } : {},
7374
- ...singleTargetRef?.hooks ? { targetHooks: singleTargetRef.hooks } : {}
7905
+ ...singleProviderRef?.label ? { providerDisplayLabel: singleProviderRef.label } : {},
7906
+ ...singleProviderRef?.hooks ? { providerHooks: singleProviderRef.hooks } : {}
7375
7907
  };
7376
7908
  selections = [
7377
7909
  {
7378
7910
  selection: augmentedSelection,
7379
- inlineTargetLabel: augmentedSelection.targetLabel ?? resolveTargetLabel(
7380
- augmentedSelection.targetName,
7381
- augmentedSelection.resolvedTarget.name
7911
+ inlineTargetLabel: augmentedSelection.providerDisplayLabel ?? resolveTargetLabel(
7912
+ augmentedSelection.providerLabel,
7913
+ augmentedSelection.resolvedProvider.name
7382
7914
  )
7383
7915
  }
7384
7916
  ];
7385
7917
  }
7386
7918
  }
7387
7919
  return {
7388
- options: effectiveOptions,
7920
+ options: fileOptions,
7389
7921
  testIds,
7390
7922
  testCases,
7391
7923
  selections,
7392
- trialsConfig: effectiveOptions.experimentTrialsConfig,
7924
+ trialsConfig: fileOptions.experimentTrialsConfig,
7393
7925
  suiteTargets,
7394
7926
  yamlCache: suite.cacheConfig?.enabled,
7395
7927
  yamlCachePath: suite.cacheConfig?.cachePath,
@@ -7400,7 +7932,7 @@ async function prepareFileMetadata(params) {
7400
7932
  providerFactory: suite.providerFactory
7401
7933
  };
7402
7934
  }
7403
- function buildTaskBundleTargetSelections(activeTestFiles, fileMetadata) {
7935
+ function buildTaskBundleProviderSelections(activeTestFiles, fileMetadata) {
7404
7936
  return activeTestFiles.flatMap((testFilePath) => {
7405
7937
  const meta = fileMetadata.get(testFilePath);
7406
7938
  if (!meta) {
@@ -7408,8 +7940,8 @@ function buildTaskBundleTargetSelections(activeTestFiles, fileMetadata) {
7408
7940
  }
7409
7941
  return meta.selections.map(({ selection }) => ({
7410
7942
  evalFileAbsolutePath: testFilePath,
7411
- targetName: selection.targetName,
7412
- resolvedTargetName: selection.resolvedTarget.name,
7943
+ providerLabel: selection.providerLabel,
7944
+ resolvedProviderName: selection.resolvedProvider.name,
7413
7945
  definitions: selection.definitions
7414
7946
  }));
7415
7947
  });
@@ -7438,22 +7970,22 @@ async function runSingleEvalFile(params) {
7438
7970
  failOnError,
7439
7971
  providerFactory
7440
7972
  } = params;
7441
- const targetName = selection.targetName;
7973
+ const providerLabel = selection.providerLabel;
7442
7974
  const replayRecording = options.recordReplay ? {
7443
- fixturesPath: path12.resolve(options.recordReplay),
7444
- sourceTarget: targetName,
7975
+ fixturesPath: path13.resolve(options.recordReplay),
7976
+ sourceTarget: providerLabel,
7445
7977
  variant: options.recordReplayVariant
7446
7978
  } : void 0;
7447
7979
  await ensureFileExists(testFilePath, "Test file");
7448
- const resolvedTargetSelection = applyVerboseOverride(selection, options.verbose);
7449
- const explicitVariant = targetVariantForSelection(resolvedTargetSelection);
7450
- const providerLabel = resolvedTargetSelection.resolvedTarget.kind;
7451
- const targetMessage = options.verbose ? `Using target (${resolvedTargetSelection.targetSource}): ${resolvedTargetSelection.targetName} ${buildTargetLabelSuffix(providerLabel, resolvedTargetSelection.resolvedTarget)} via ${resolvedTargetSelection.targetsFilePath}` : `Using target: ${inlineTargetLabel}`;
7980
+ const resolvedProviderSelection = applyVerboseOverride(selection, options.verbose);
7981
+ const explicitVariant = targetVariantForSelection(resolvedProviderSelection);
7982
+ const providerId = resolvedProviderSelection.resolvedProvider.kind;
7983
+ const targetMessage = options.verbose ? `Using provider (${resolvedProviderSelection.providerSource}): ${resolvedProviderSelection.providerLabel} ${buildTargetLabelSuffix(providerId, resolvedProviderSelection.resolvedProvider)} via ${resolvedProviderSelection.providersFilePath}` : `Using provider: ${inlineTargetLabel}`;
7452
7984
  if (!progressReporter.isInteractive || options.verbose) {
7453
7985
  console.log(`${targetMessage}`);
7454
7986
  }
7455
- const targetKind = resolvedTargetSelection.resolvedTarget.kind;
7456
- if (targetKind === "claude-cli" || targetKind === "copilot-cli") {
7987
+ const providerKind = resolvedProviderSelection.resolvedProvider.kind;
7988
+ if (providerKind === "claude-cli" || providerKind === "copilot-cli") {
7457
7989
  console.log("");
7458
7990
  console.log(" TIP: For subagent-mode evals, use `agentv pipeline` instead of `eval run`.");
7459
7991
  console.log(" The agent orchestrates executor + grader subagents directly.");
@@ -7462,12 +7994,12 @@ async function runSingleEvalFile(params) {
7462
7994
  }
7463
7995
  const agentTimeoutMs = agentTimeoutSeconds != null ? Math.max(0, agentTimeoutSeconds) * 1e3 : void 0;
7464
7996
  const workerPreference = workersOverride ?? options.workers;
7465
- let resolvedWorkers = workerPreference ?? resolvedTargetSelection.resolvedTarget.workers ?? DEFAULT_WORKERS;
7997
+ let resolvedWorkers = workerPreference ?? resolvedProviderSelection.resolvedProvider.workers ?? DEFAULT_WORKERS;
7466
7998
  if (resolvedWorkers < 1 || resolvedWorkers > 50) {
7467
7999
  throw new Error(`Workers must be between 1 and 50, got: ${resolvedWorkers}`);
7468
8000
  }
7469
8001
  const isVSCodeProvider = ["vscode", "vscode-insiders"].includes(
7470
- resolvedTargetSelection.resolvedTarget.kind
8002
+ resolvedProviderSelection.resolvedProvider.kind
7471
8003
  );
7472
8004
  if (isVSCodeProvider && resolvedWorkers > 1) {
7473
8005
  console.warn(
@@ -7476,9 +8008,9 @@ async function runSingleEvalFile(params) {
7476
8008
  resolvedWorkers = 1;
7477
8009
  }
7478
8010
  if (isVSCodeProvider) {
7479
- const vsConfig = resolvedTargetSelection.resolvedTarget.config;
8011
+ const vsConfig = resolvedProviderSelection.resolvedProvider.config;
7480
8012
  await ensureVSCodeSubagents({
7481
- kind: resolvedTargetSelection.resolvedTarget.kind,
8013
+ kind: resolvedProviderSelection.resolvedProvider.kind,
7482
8014
  count: resolvedWorkers,
7483
8015
  verbose: options.verbose,
7484
8016
  vscodeCmd: vsConfig.executable
@@ -7487,15 +8019,15 @@ async function runSingleEvalFile(params) {
7487
8019
  const results = await evaluationRunner({
7488
8020
  testFilePath,
7489
8021
  repoRoot,
7490
- target: resolvedTargetSelection.resolvedTarget,
7491
- targets: resolvedTargetSelection.definitions,
8022
+ target: resolvedProviderSelection.resolvedProvider,
8023
+ targets: resolvedProviderSelection.definitions,
7492
8024
  env: process.env,
7493
8025
  maxRetries: Math.max(0, options.maxRetries),
7494
8026
  agentTimeoutMs,
7495
8027
  cache,
7496
8028
  useCache: (() => {
7497
8029
  if (!cache) return false;
7498
- const targetConfig = resolvedTargetSelection.resolvedTarget.config;
8030
+ const targetConfig = resolvedProviderSelection.resolvedProvider.config;
7499
8031
  if (shouldSkipCacheForTemperature(targetConfig)) {
7500
8032
  if (options.verbose) {
7501
8033
  console.log("Cache skipped: target temperature > 0");
@@ -7515,9 +8047,10 @@ async function runSingleEvalFile(params) {
7515
8047
  runBudgetTracker,
7516
8048
  failOnError,
7517
8049
  graderTarget: options.graderTarget,
8050
+ defaultGraderTarget: options.defaultGraderTarget,
7518
8051
  model: options.model,
7519
8052
  threshold: params.threshold,
7520
- targetHooks: resolvedTargetSelection.targetHooks,
8053
+ targetHooks: resolvedProviderSelection.providerHooks,
7521
8054
  replayRecording,
7522
8055
  providerFactory,
7523
8056
  onResult: async (result) => {
@@ -7527,7 +8060,7 @@ async function runSingleEvalFile(params) {
7527
8060
  await outputWriter.append(trimmedResult);
7528
8061
  },
7529
8062
  onProgress: async (event) => {
7530
- const testCaseKeyId = matrixMode ? `${event.testId}@${targetName}` : event.testId;
8063
+ const testCaseKeyId = matrixMode ? `${event.testId}@${providerLabel}` : event.testId;
7531
8064
  const testCaseKey = makeTestCaseKey(testFilePath, testCaseKeyId);
7532
8065
  if (event.status === "pending" && !seenTestCases.has(testCaseKey)) {
7533
8066
  seenTestCases.add(testCaseKey);
@@ -7540,7 +8073,7 @@ async function runSingleEvalFile(params) {
7540
8073
  else if (event.executionStatus === "execution_error") verdict = "ERROR";
7541
8074
  progressReporter.update(displayId, {
7542
8075
  workerId: displayId,
7543
- testId: matrixMode ? `${event.testId}@${targetName}` : event.testId,
8076
+ testId: matrixMode ? `${event.testId}@${providerLabel}` : event.testId,
7544
8077
  status: event.status,
7545
8078
  startedAt: event.startedAt,
7546
8079
  completedAt: event.completedAt,
@@ -7574,7 +8107,7 @@ async function runEvalCommand(input) {
7574
8107
  );
7575
8108
  }
7576
8109
  const repoRoot = await findRepoRoot(cwd);
7577
- const yamlConfig = await loadConfig(path12.join(cwd, "_"), repoRoot);
8110
+ const yamlConfig = await loadConfig(path13.join(cwd, "_"), repoRoot);
7578
8111
  let requiredVersionCheck;
7579
8112
  if (yamlConfig?.required_version) {
7580
8113
  requiredVersionCheck = await enforceRequiredVersion(yamlConfig.required_version, {
@@ -7582,10 +8115,19 @@ async function runEvalCommand(input) {
7582
8115
  });
7583
8116
  }
7584
8117
  let options = normalizeOptions(input.rawOptions, config, yamlConfig?.execution);
8118
+ if (yamlConfig?.defaults?.provider && options.cliProviderLabels.length === 0 && !options.provider) {
8119
+ options = { ...options, provider: yamlConfig.defaults.provider };
8120
+ }
8121
+ if (yamlConfig?.defaults?.grader) {
8122
+ options = { ...options, defaultGraderTarget: yamlConfig.defaults.grader };
8123
+ }
8124
+ const providerCatalogPath = yamlConfig?.providerCatalogPath;
8125
+ const providerDefinitions = yamlConfig?.providerDefinitions;
8126
+ const providerDefinitionsSource = providerDefinitions ? providerCatalogPath ?? ".agentv/config.yaml:providers" : void 0;
7585
8127
  const resolvedExperiment = resolveExperimentForRun(options.experiment);
7586
8128
  const evalPathInputs = input.testFiles.length > 0 ? [...input.testFiles] : [];
7587
8129
  if (evalPathInputs.length === 0 && process.stdin.isTTY) {
7588
- const { launchInteractiveWizard } = await import("./interactive-RUY3OCBI.js");
8130
+ const { launchInteractiveWizard } = await import("./interactive-DL7N2C7K.js");
7589
8131
  await launchInteractiveWizard();
7590
8132
  return void 0;
7591
8133
  }
@@ -7596,7 +8138,7 @@ async function runEvalCommand(input) {
7596
8138
  const primarySuite = resolvedTestFiles.length > 0 ? await loadCliEvalSuite(resolvedTestFiles[0], repoRoot, {
7597
8139
  verbose: options.verbose,
7598
8140
  filter: options.filter,
7599
- category: deriveCategory(path12.relative(cwd, resolvedTestFiles[0]))
8141
+ category: deriveCategory(path13.relative(cwd, resolvedTestFiles[0]))
7600
8142
  }) : void 0;
7601
8143
  const resultGroupName = resolvedTestFiles.length === 1 ? primarySuite?.metadata?.name ?? fallbackResultGroupName : fallbackResultGroupName;
7602
8144
  const resolvedTags = resolveEffectiveTags({
@@ -7625,7 +8167,7 @@ async function runEvalCommand(input) {
7625
8167
  process.env.AGENTV_EXPERIMENT = normalizedExperiment;
7626
8168
  }
7627
8169
  if (options.graderTarget === "agentv" && !options.model) {
7628
- throw new Error("--grader-target agentv requires --model (e.g., --model openai:gpt-5-mini)");
8170
+ throw new Error("--grader-provider agentv requires --model (e.g., --model openai:gpt-5-mini)");
7629
8171
  }
7630
8172
  if (options.removedOut) {
7631
8173
  throw new Error(
@@ -7649,7 +8191,7 @@ async function runEvalCommand(input) {
7649
8191
  }
7650
8192
  let retryNonErrorResults;
7651
8193
  if (options.retryErrors) {
7652
- const retryPath = path12.resolve(options.retryErrors);
8194
+ const retryPath = path13.resolve(options.retryErrors);
7653
8195
  await ensureFileExists(retryPath, "Retry-errors JSONL file");
7654
8196
  const completedIds = await loadFullyCompletedTestIds(retryPath);
7655
8197
  const errorIds = await loadErrorTestIds(retryPath);
@@ -7666,7 +8208,7 @@ async function runEvalCommand(input) {
7666
8208
  const cachedDir = await resolveCachedRunDir(cwd);
7667
8209
  if (cachedDir) {
7668
8210
  options = { ...options, outputDir: cachedDir };
7669
- const displayDir = path12.relative(cwd, cachedDir) || cachedDir;
8211
+ const displayDir = path13.relative(cwd, cachedDir) || cachedDir;
7670
8212
  console.log(`Auto-detected last run dir for --resume: ${displayDir}`);
7671
8213
  }
7672
8214
  }
@@ -7674,7 +8216,7 @@ async function runEvalCommand(input) {
7674
8216
  let rerunIncludeKeys;
7675
8217
  let isResumeAppend = false;
7676
8218
  if (options.resume && !options.retryErrors) {
7677
- const sourceRunDir = options.rerunFailedSource ? await resolveRerunFailedRunDir(cwd, options.rerunFailedSource) : options.outputDir ? path12.resolve(options.outputDir) : void 0;
8219
+ const sourceRunDir = options.rerunFailedSource ? await resolveRerunFailedRunDir(cwd, options.rerunFailedSource) : options.outputDir ? path13.resolve(options.outputDir) : void 0;
7678
8220
  if (sourceRunDir) {
7679
8221
  if (options.rerunFailedSource && !options.outputDir) {
7680
8222
  options = { ...options, outputDir: sourceRunDir };
@@ -7693,7 +8235,7 @@ async function runEvalCommand(input) {
7693
8235
  addResumeIdentityEntry(rerunIncludeKeys, entry);
7694
8236
  }
7695
8237
  }
7696
- isResumeAppend = options.outputDir !== void 0 && path12.resolve(options.outputDir) === path12.resolve(sourceRunDir);
8238
+ isResumeAppend = options.outputDir !== void 0 && path13.resolve(options.outputDir) === path13.resolve(sourceRunDir);
7697
8239
  const modeLabel = options.rerunFailed ? "Rerun-failed" : "Resume";
7698
8240
  console.log(
7699
8241
  `${modeLabel}: found ${existingResults.length} existing result(s), skipping ${completedResultCount} completed.`
@@ -7711,10 +8253,10 @@ async function runEvalCommand(input) {
7711
8253
  }
7712
8254
  }
7713
8255
  if (options.workspacePath) {
7714
- const resolvedWorkspace = path12.resolve(options.workspacePath);
8256
+ const resolvedWorkspace = path13.resolve(options.workspacePath);
7715
8257
  try {
7716
- const { stat: stat3 } = await import("node:fs/promises");
7717
- const stats = await stat3(resolvedWorkspace);
8258
+ const { stat: stat4 } = await import("node:fs/promises");
8259
+ const stats = await stat4(resolvedWorkspace);
7718
8260
  if (!stats.isDirectory()) {
7719
8261
  throw new Error(`--workspace-path is not a directory: ${resolvedWorkspace}`);
7720
8262
  }
@@ -7734,7 +8276,7 @@ async function runEvalCommand(input) {
7734
8276
  let outputPath;
7735
8277
  let runDirName = process.env.AGENTV_RUN_TIMESTAMP?.trim() || createRunDirName();
7736
8278
  if (explicitDir) {
7737
- runDir = path12.resolve(explicitDir);
8279
+ runDir = path13.resolve(explicitDir);
7738
8280
  mkdirSync(runDir, { recursive: true });
7739
8281
  outputPath = runDir;
7740
8282
  } else {
@@ -7749,7 +8291,7 @@ async function runEvalCommand(input) {
7749
8291
  outputPath = runDir;
7750
8292
  }
7751
8293
  if (!process.env.AGENTV_RUN_TIMESTAMP) {
7752
- process.env.AGENTV_RUN_TIMESTAMP = path12.basename(runDir);
8294
+ process.env.AGENTV_RUN_TIMESTAMP = path13.basename(runDir);
7753
8295
  }
7754
8296
  process.env.AGENTV_RUN_DIR = runDir;
7755
8297
  console.log(`Artifact directory: ${runDir}`);
@@ -7764,11 +8306,15 @@ async function runEvalCommand(input) {
7764
8306
  }
7765
8307
  const fileMetadata = /* @__PURE__ */ new Map();
7766
8308
  for (const testFilePath of resolvedTestFiles) {
8309
+ const fileProviderCatalogPath = options.providerCatalogPathByEvalFile?.get(path13.resolve(testFilePath)) ?? providerCatalogPath;
7767
8310
  const meta = await prepareFileMetadata({
7768
8311
  testFilePath,
7769
8312
  repoRoot,
7770
8313
  cwd,
7771
8314
  options,
8315
+ providerCatalogPath: fileProviderCatalogPath,
8316
+ providerDefinitions,
8317
+ providerDefinitionsSource: fileProviderCatalogPath ? fileProviderCatalogPath === providerCatalogPath ? providerDefinitionsSource : void 0 : providerDefinitionsSource,
7772
8318
  suiteFilter: void 0
7773
8319
  });
7774
8320
  fileMetadata.set(testFilePath, meta);
@@ -7779,7 +8325,7 @@ async function runEvalCommand(input) {
7779
8325
  for (const [testFilePath, meta] of fileMetadata.entries()) {
7780
8326
  if (!matchesTagFilters(meta.tags, options.tags, options.excludeTags)) {
7781
8327
  fileMetadata.delete(testFilePath);
7782
- skippedFiles.push(path12.relative(cwd, testFilePath));
8328
+ skippedFiles.push(path13.relative(cwd, testFilePath));
7783
8329
  }
7784
8330
  }
7785
8331
  if (skippedFiles.length > 0 && options.verbose) {
@@ -7802,12 +8348,12 @@ async function runEvalCommand(input) {
7802
8348
  tsConfigCache: options.tsConfigCache
7803
8349
  });
7804
8350
  const activeCachePath = options.cachePath ?? yamlCachePath ?? options.tsConfigCachePath;
7805
- const cache = cacheEnabled ? new ResponseCache(activeCachePath ? path12.resolve(activeCachePath) : void 0) : void 0;
8351
+ const cache = cacheEnabled ? new ResponseCache(activeCachePath ? path13.resolve(activeCachePath) : void 0) : void 0;
7806
8352
  if (cache) {
7807
8353
  console.log(`Response cache: enabled (${cache.cachePath})`);
7808
8354
  }
7809
8355
  if (options.recordReplay) {
7810
- console.log(`Replay recording: ${path12.resolve(options.recordReplay)}`);
8356
+ console.log(`Replay recording: ${path13.resolve(options.recordReplay)}`);
7811
8357
  }
7812
8358
  const yamlThreshold = firstMeta?.threshold;
7813
8359
  const resolvedThreshold = options.threshold ?? yamlThreshold;
@@ -7821,7 +8367,7 @@ async function runEvalCommand(input) {
7821
8367
  for (const meta of fileMetadata.values()) {
7822
8368
  for (const test of meta.testCases) {
7823
8369
  for (const { selection } of meta.selections) {
7824
- const target = selection.targetName;
8370
+ const target = selection.providerLabel;
7825
8371
  const variant = targetVariantForSelection(selection);
7826
8372
  if (rerunIncludeKeys) {
7827
8373
  if (resumeIdentityMatches(rerunIncludeKeys, test, target, variant)) {
@@ -7893,13 +8439,13 @@ async function runEvalCommand(input) {
7893
8439
  for (const testId of meta.testIds) {
7894
8440
  const testCaseKey = makeTestCaseKey(
7895
8441
  testFilePath,
7896
- meta.selections.length > 1 ? `${testId}@${selection.targetName}` : testId
8442
+ meta.selections.length > 1 ? `${testId}@${selection.providerLabel}` : testId
7897
8443
  );
7898
8444
  seenTestCases.add(testCaseKey);
7899
8445
  const displayId = displayIdTracker.getOrAssign(testCaseKey);
7900
8446
  progressReporter.update(displayId, {
7901
8447
  workerId: displayId,
7902
- testId: meta.selections.length > 1 ? `${testId}@${selection.targetName}` : testId,
8448
+ testId: meta.selections.length > 1 ? `${testId}@${selection.providerLabel}` : testId,
7903
8449
  status: "pending",
7904
8450
  targetLabel: inlineTargetLabel
7905
8451
  });
@@ -7925,7 +8471,7 @@ async function runEvalCommand(input) {
7925
8471
  });
7926
8472
  let transcriptProviderFactory;
7927
8473
  if (options.transcript) {
7928
- const { TranscriptProvider } = await import("./dist-6Z7U473R.js");
8474
+ const { TranscriptProvider } = await import("./dist-A3SGR7TW.js");
7929
8475
  const transcriptProvider = await TranscriptProvider.fromFile(options.transcript);
7930
8476
  const totalTests = [...fileMetadata.values()].reduce(
7931
8477
  (sum, meta) => sum + meta.testCases.length,
@@ -7958,12 +8504,12 @@ async function runEvalCommand(input) {
7958
8504
  );
7959
8505
  }
7960
8506
  if (!isResumeAppend && totalEvalCount > 0) {
7961
- const evalFile = activeTestFiles.length === 1 ? activeTestFiles[0] : "";
8507
+ const evalFile = activeTestFiles.length === 1 ? path13.relative(cwd, activeTestFiles[0]) : "";
7962
8508
  await writeInitialRunSummaryArtifact(runDir, {
7963
8509
  evalFile,
7964
8510
  plannedTestCount: totalEvalCount,
7965
8511
  experiment: normalizeExperimentName(options.experiment),
7966
- runId: path12.basename(runDir),
8512
+ runId: path13.basename(runDir),
7967
8513
  experimentMetadata: runExperimentMetadata,
7968
8514
  runtimeSource: runtimeSourceMetadata,
7969
8515
  tags: emittedTags
@@ -7999,7 +8545,7 @@ async function runEvalCommand(input) {
7999
8545
  if (fileBudgetTracker?.isExceeded()) {
8000
8546
  const budgetMsg = `Run budget exceeded ($${fileBudgetTracker.currentCostUsd.toFixed(4)} / $${fileBudgetTracker.budgetCapUsd.toFixed(4)})`;
8001
8547
  console.log(`
8002
- \u26A0 ${budgetMsg} \u2014 skipping ${path12.basename(testFilePath)}`);
8548
+ \u26A0 ${budgetMsg} \u2014 skipping ${path13.basename(testFilePath)}`);
8003
8549
  for (const { selection } of targetPrep.selections) {
8004
8550
  const explicitVariant = targetVariantForSelection(selection);
8005
8551
  const skippedResults = targetPrep.testCases.map((testCase) => ({
@@ -8013,7 +8559,7 @@ async function runEvalCommand(input) {
8013
8559
  input: testCase.input,
8014
8560
  output: [{ role: "assistant", content: budgetMsg }],
8015
8561
  finalOutput: budgetMsg,
8016
- target: selection.targetName,
8562
+ target: selection.providerLabel,
8017
8563
  testId: testCase.testId ?? testCase.id,
8018
8564
  conversationId: testCase.conversation_id,
8019
8565
  error: budgetMsg
@@ -8024,7 +8570,7 @@ async function runEvalCommand(input) {
8024
8570
  failureStage: "setup",
8025
8571
  failureReasonCode: "budget_exceeded",
8026
8572
  executionError: { message: budgetMsg, stage: "setup" },
8027
- target: selection.targetName,
8573
+ target: selection.providerLabel,
8028
8574
  variant: explicitVariant
8029
8575
  }));
8030
8576
  for (const r of skippedResults) {
@@ -8043,18 +8589,18 @@ async function runEvalCommand(input) {
8043
8589
  const targetResults = await Promise.all(
8044
8590
  targetPrep.selections.map(
8045
8591
  ({ selection, inlineTargetLabel }) => limitTarget(async () => {
8046
- const targetName = selection.targetName;
8592
+ const providerLabel = selection.providerLabel;
8047
8593
  const applicableTestCases = targetPrep.testCases;
8048
8594
  const filteredTestCases = rerunIncludeKeys ? applicableTestCases.filter(
8049
8595
  (test) => resumeIdentityMatches(
8050
8596
  rerunIncludeKeys,
8051
8597
  test,
8052
- targetName,
8598
+ providerLabel,
8053
8599
  targetVariantForSelection(selection)
8054
8600
  )
8055
8601
  ) : resumeSkipKeys ? applicableTestCases.filter((test) => {
8056
8602
  const variant = targetVariantForSelection(selection);
8057
- return !resumeIdentityMatches(resumeSkipKeys, test, targetName, variant);
8603
+ return !resumeIdentityMatches(resumeSkipKeys, test, providerLabel, variant);
8058
8604
  }) : applicableTestCases;
8059
8605
  if (filteredTestCases.length === 0) {
8060
8606
  return [];
@@ -8097,7 +8643,7 @@ async function runEvalCommand(input) {
8097
8643
  });
8098
8644
  groupResults.push(...result.results);
8099
8645
  }
8100
- const evalFile = path12.relative(cwd, testFilePath);
8646
+ const evalFile = path13.relative(cwd, testFilePath);
8101
8647
  const existingSummary = remoteEvalSummaries.find(
8102
8648
  (summary2) => summary2.evalFile === evalFile
8103
8649
  );
@@ -8114,7 +8660,7 @@ async function runEvalCommand(input) {
8114
8660
  const message = fileError instanceof Error ? fileError.message : String(fileError);
8115
8661
  console.error(
8116
8662
  `
8117
- [ERROR] \u26A0 Eval file failed: ${path12.basename(testFilePath)} \u2014 ${message}
8663
+ [ERROR] \u26A0 Eval file failed: ${path13.basename(testFilePath)} \u2014 ${message}
8118
8664
  `
8119
8665
  );
8120
8666
  const explicitVariant = targetVariantForSelection(selection);
@@ -8131,7 +8677,7 @@ async function runEvalCommand(input) {
8131
8677
  input: testCase.input,
8132
8678
  output: [{ role: "assistant", content: message }],
8133
8679
  finalOutput: message,
8134
- target: selection.targetName,
8680
+ target: selection.providerLabel,
8135
8681
  testId: testCase.testId ?? testCase.id,
8136
8682
  conversationId: testCase.conversation_id,
8137
8683
  error: message
@@ -8143,7 +8689,7 @@ async function runEvalCommand(input) {
8143
8689
  failureReasonCode: "setup_error",
8144
8690
  durationMs: 0,
8145
8691
  tokenUsage: { input: 0, output: 0 },
8146
- target: selection.targetName,
8692
+ target: selection.providerLabel,
8147
8693
  variant: explicitVariant
8148
8694
  },
8149
8695
  testFilePath,
@@ -8173,7 +8719,11 @@ async function runEvalCommand(input) {
8173
8719
  );
8174
8720
  }
8175
8721
  await outputWriter.close().catch(() => void 0);
8176
- const summaryResults = deduplicateByTestIdTarget(await readExistingResultsFromRunDir(runDir));
8722
+ const summaryResults = await collectTerminalSummaryResults({
8723
+ allResults,
8724
+ isResumeAppend,
8725
+ runDir
8726
+ });
8177
8727
  const thresholdOpts = hasScopedRunPolicies || hasPerFileRuntimeThresholds ? { thresholdLabel: "configured threshold(s)", useExecutionStatus: true } : resolvedThreshold !== void 0 ? { threshold: resolvedThreshold } : void 0;
8178
8728
  const summary = calculateEvaluationSummary(summaryResults, thresholdOpts);
8179
8729
  console.log(formatEvaluationSummary(summary, thresholdOpts));
@@ -8187,14 +8737,14 @@ ${formatRequiredVersionFailureNote(requiredVersionCheck)}`);
8187
8737
  console.log(formatMatrixSummary(summaryResults));
8188
8738
  }
8189
8739
  if (allResults.length > 0) {
8190
- const evalFile = activeTestFiles.length === 1 ? activeTestFiles[0] : "";
8740
+ const evalFile = activeTestFiles.length === 1 ? path13.relative(cwd, activeTestFiles[0]) : "";
8191
8741
  const sourceTests = activeSourceTests;
8192
- const taskBundleTargets = buildTaskBundleTargetSelections(activeTestFiles, fileMetadata);
8742
+ const taskBundleTargets = buildTaskBundleProviderSelections(activeTestFiles, fileMetadata);
8193
8743
  if (isResumeAppend) {
8194
- const { writePerTestArtifacts } = await import("./artifact-writer-JFNIPMKW.js");
8744
+ const { writePerTestArtifacts } = await import("./artifact-writer-KJEOROKQ.js");
8195
8745
  await writePerTestArtifacts(allResults, runDir, {
8196
8746
  experiment: normalizeExperimentName(options.experiment),
8197
- runId: path12.basename(runDir),
8747
+ runId: path13.basename(runDir),
8198
8748
  resultGroup: resultGroupName,
8199
8749
  cwd,
8200
8750
  repoRoot,
@@ -8206,7 +8756,7 @@ ${formatRequiredVersionFailureNote(requiredVersionCheck)}`);
8206
8756
  const { summaryPath } = await aggregateRunDir(runDir, {
8207
8757
  evalFile,
8208
8758
  experiment: normalizeExperimentName(options.experiment),
8209
- runId: path12.basename(runDir),
8759
+ runId: path13.basename(runDir),
8210
8760
  experimentMetadata: runExperimentMetadata,
8211
8761
  runtimeSource: runtimeSourceMetadata,
8212
8762
  tags: emittedTags
@@ -8223,7 +8773,7 @@ ${formatRequiredVersionFailureNote(requiredVersionCheck)}`);
8223
8773
  {
8224
8774
  evalFile,
8225
8775
  experiment: normalizeExperimentName(options.experiment),
8226
- runId: path12.basename(runDir),
8776
+ runId: path13.basename(runDir),
8227
8777
  experimentMetadata: runExperimentMetadata,
8228
8778
  resultGroup: resultGroupName,
8229
8779
  cwd,
@@ -8286,9 +8836,9 @@ Results written under: ${runDir}`);
8286
8836
  });
8287
8837
  }
8288
8838
  if (summary.executionErrorCount > 0 && !options.retryErrors && !options.resume) {
8289
- const evalFileArgs = activeTestFiles.map((f) => path12.relative(cwd, f)).join(" ");
8290
- const targetFlag = options.target ? ` --target ${options.target}` : "";
8291
- const relativeRunDir = path12.relative(cwd, runDir);
8839
+ const evalFileArgs = activeTestFiles.map((f) => path13.relative(cwd, f)).join(" ");
8840
+ const targetFlag = options.provider ? ` --provider ${options.provider}` : "";
8841
+ const relativeRunDir = path13.relative(cwd, runDir);
8292
8842
  console.log(
8293
8843
  `
8294
8844
  Tip: ${summary.executionErrorCount} execution error(s) detected. Re-run failed tests with:
@@ -8310,7 +8860,7 @@ Tip: ${summary.executionErrorCount} execution error(s) detected. Re-run failed t
8310
8860
  executionErrorCount: summary.executionErrorCount,
8311
8861
  outputPath,
8312
8862
  testFiles: activeTestFiles,
8313
- target: options.target,
8863
+ provider: options.provider,
8314
8864
  thresholdFailed,
8315
8865
  allExecutionErrors,
8316
8866
  budgetExceeded: runBudgetExceeded || void 0
@@ -8331,7 +8881,7 @@ async function resolveEvaluationRunner() {
8331
8881
  if (!overridePath) {
8332
8882
  return runEvaluation;
8333
8883
  }
8334
- const resolved = path12.isAbsolute(overridePath) ? overridePath : path12.resolve(process.cwd(), overridePath);
8884
+ const resolved = path13.isAbsolute(overridePath) ? overridePath : path13.resolve(process.cwd(), overridePath);
8335
8885
  const moduleUrl = pathToFileURL(resolved).href;
8336
8886
  const mod = await import(moduleUrl);
8337
8887
  const candidate = mod.runEvaluation;
@@ -8365,20 +8915,21 @@ export {
8365
8915
  normalizeTagMap,
8366
8916
  loadLightweightResults,
8367
8917
  Mutex,
8368
- TARGET_FILE_CANDIDATES,
8918
+ PROVIDER_FILE_CANDIDATES,
8369
8919
  fileExists,
8370
- discoverTargetsFile,
8920
+ discoverProvidersFile,
8371
8921
  loadEnvFromHierarchy,
8372
8922
  detectFileType,
8373
8923
  validateEvalFile,
8924
+ validateTypeScriptEvalConfigFile,
8374
8925
  validateCasesFile,
8375
8926
  validateTargetsFile,
8376
8927
  validateConfigFile,
8377
8928
  validateFileReferences,
8378
8929
  validateWorkspacePaths,
8379
- readTestSuiteTarget,
8380
- selectTarget,
8381
- selectMultipleTargets,
8930
+ readTestSuiteProvider,
8931
+ selectProvider,
8932
+ selectMultipleProviders,
8382
8933
  enforceRequiredVersion,
8383
8934
  c,
8384
8935
  padRight,
@@ -8409,4 +8960,4 @@ export {
8409
8960
  filterByCategory,
8410
8961
  runEvalCommand
8411
8962
  };
8412
- //# sourceMappingURL=chunk-FVR4RQFK.js.map
8963
+ //# sourceMappingURL=chunk-QSK3PC44.js.map