agentv 5.2.0-next.1 → 5.3.1-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 (46) hide show
  1. package/README.md +22 -20
  2. package/dist/{artifact-writer-SN6UNL5G.js → artifact-writer-7NBCOAYC.js} +4 -4
  3. package/dist/{chunk-3X357HS4.js → chunk-ASIGJIOJ.js} +107 -31
  4. package/dist/chunk-ASIGJIOJ.js.map +1 -0
  5. package/dist/{chunk-3S6A2RKR.js → chunk-ELCJ23K4.js} +18879 -17538
  6. package/dist/chunk-ELCJ23K4.js.map +1 -0
  7. package/dist/{chunk-T4AD3H3Y.js → chunk-LKGARI3W.js} +399 -32
  8. package/dist/chunk-LKGARI3W.js.map +1 -0
  9. package/dist/{chunk-ALVZQUZP.js → chunk-LXBI3SPX.js} +142 -14
  10. package/dist/chunk-LXBI3SPX.js.map +1 -0
  11. package/dist/{chunk-46K2OET3.js → chunk-RKE7SSET.js} +2 -2
  12. package/dist/cli.js +5 -5
  13. package/dist/dashboard/assets/{index-DNgf3qJ2.js → index-CbEMiJSb.js} +1 -1
  14. package/dist/dashboard/assets/{index-r_jSJmlw.js → index-DTA6-l7q.js} +3 -3
  15. package/dist/dashboard/index.html +1 -1
  16. package/dist/{dist-DEPJOMCA.js → dist-NMXMI5SK.js} +25 -5
  17. package/dist/index.js +5 -5
  18. package/dist/{interactive-36ZNMQB2.js → interactive-BN527UV3.js} +5 -5
  19. package/dist/skills/agentv-bench/SKILL.md +15 -14
  20. package/dist/skills/agentv-bench/agents/analyzer.md +2 -2
  21. package/dist/skills/agentv-bench/agents/comparator.md +3 -3
  22. package/dist/skills/agentv-bench/agents/executor.md +1 -1
  23. package/dist/skills/agentv-bench/agents/grader.md +4 -2
  24. package/dist/skills/agentv-bench/references/autoresearch.md +9 -9
  25. package/dist/skills/agentv-bench/references/description-optimization.md +5 -6
  26. package/dist/skills/agentv-bench/references/environment-adaptation.md +6 -6
  27. package/dist/skills/agentv-bench/references/eval-yaml-spec.md +50 -56
  28. package/dist/skills/agentv-bench/references/schemas.md +44 -60
  29. package/dist/skills/agentv-bench/references/subagent-pipeline.md +20 -18
  30. package/dist/skills/agentv-eval-migrations/SKILL.md +13 -0
  31. package/dist/skills/agentv-eval-migrations/references/breaking-changes.md +138 -22
  32. package/dist/skills/agentv-eval-writer/SKILL.md +103 -63
  33. package/dist/skills/agentv-eval-writer/references/custom-evaluators.md +10 -5
  34. package/dist/skills/agentv-eval-writer/references/eval.schema.json +749 -2158
  35. package/dist/skills/agentv-eval-writer/references/rubric-evaluator.md +1 -1
  36. package/dist/{ts-eval-loader-OMG2JBHP-OP5RR7A3.js → ts-eval-loader-2RFVZHCT-7CZ3DCDD.js} +8 -4
  37. package/package.json +1 -1
  38. package/dist/chunk-3S6A2RKR.js.map +0 -1
  39. package/dist/chunk-3X357HS4.js.map +0 -1
  40. package/dist/chunk-ALVZQUZP.js.map +0 -1
  41. package/dist/chunk-T4AD3H3Y.js.map +0 -1
  42. /package/dist/{artifact-writer-SN6UNL5G.js.map → artifact-writer-7NBCOAYC.js.map} +0 -0
  43. /package/dist/{chunk-46K2OET3.js.map → chunk-RKE7SSET.js.map} +0 -0
  44. /package/dist/{dist-DEPJOMCA.js.map → dist-NMXMI5SK.js.map} +0 -0
  45. /package/dist/{interactive-36ZNMQB2.js.map → interactive-BN527UV3.js.map} +0 -0
  46. /package/dist/{ts-eval-loader-OMG2JBHP-OP5RR7A3.js.map → ts-eval-loader-2RFVZHCT-7CZ3DCDD.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-46K2OET3.js";
4
+ } from "./chunk-RKE7SSET.js";
5
5
  import {
6
6
  RunBudgetTracker,
7
7
  buildWipBranchName,
@@ -18,7 +18,7 @@ import {
18
18
  resolveResultsRepoRunsDir,
19
19
  setupWipWorktree,
20
20
  syncResultsRepoForProject
21
- } from "./chunk-ALVZQUZP.js";
21
+ } from "./chunk-LXBI3SPX.js";
22
22
  import {
23
23
  AGENTV_LOCAL_CONFIG_FILE_NAME,
24
24
  AGENTV_LOCAL_CONFIG_YML_FILE_NAME,
@@ -51,6 +51,7 @@ import {
51
51
  interpolateEnv,
52
52
  isAgentVConfigFileName,
53
53
  isGraderKind,
54
+ isTypeScriptEvalConfigFileName,
54
55
  listTargetNames,
55
56
  loadCasesFromDirectory,
56
57
  loadCasesFromFile,
@@ -59,6 +60,7 @@ import {
59
60
  loadTestSuite,
60
61
  loadTestSuiteFromYamlObject,
61
62
  normalizeComposableConfigGraph,
63
+ normalizeMetricsArtifactWire,
62
64
  normalizeResultRow,
63
65
  normalizeTargetDefinition,
64
66
  parseJsonlResults,
@@ -82,8 +84,9 @@ import {
82
84
  toSnakeCaseDeep,
83
85
  traceEnvelopeToTraceSummary,
84
86
  traceEnvelopeToTranscriptMessages,
87
+ typeScriptEvalConfigGlob,
85
88
  writeInitialRunSummaryArtifact
86
- } from "./chunk-3S6A2RKR.js";
89
+ } from "./chunk-ELCJ23K4.js";
87
90
 
88
91
  // src/commands/eval/shared.ts
89
92
  import { constants } from "node:fs";
@@ -214,7 +217,7 @@ function agentSkillsToAgentVYamlObject(suite) {
214
217
 
215
218
  // src/commands/eval/shared.ts
216
219
  function isNativeEvalFile(filePath) {
217
- return /\.(ya?ml|jsonl|[cm]?ts)$/i.test(filePath);
220
+ return /\.(ya?ml|jsonl)$/i.test(filePath) || isTypeScriptEvalConfigFileName(filePath);
218
221
  }
219
222
  function shouldInspectJsonPath(filePath) {
220
223
  const base = path.basename(filePath).toLowerCase();
@@ -264,7 +267,7 @@ async function resolveEvalPaths(evalPaths, cwd, options = {}) {
264
267
  continue;
265
268
  }
266
269
  if (candidateStats.isDirectory()) {
267
- 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}";
270
+ 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()}}`;
268
271
  const dirGlob = path.posix.join(candidatePath.replace(/\\/g, "/"), `**/${filePattern}`);
269
272
  const dirMatches = await fg(dirGlob, {
270
273
  absolute: true,
@@ -346,7 +349,7 @@ async function findRepoRoot(start) {
346
349
  // package.json
347
350
  var package_default = {
348
351
  name: "agentv",
349
- version: "5.2.0-next.1",
352
+ version: "5.3.1-next.1",
350
353
  description: "CLI entry point for AgentV",
351
354
  type: "module",
352
355
  repository: {
@@ -1053,7 +1056,9 @@ function hydrateTrace(baseDir, record, options) {
1053
1056
  }
1054
1057
  function hydrateManifestRecord(baseDir, record, options) {
1055
1058
  const grading = readOptionalJson(baseDir, record.grading_path);
1056
- const metrics = readOptionalJson(baseDir, record.metrics_path);
1059
+ const metrics = normalizeMetricsArtifactWire(
1060
+ readOptionalJson(baseDir, record.metrics_path)
1061
+ );
1057
1062
  const timing = metrics ?? readOptionalJson(baseDir, record.timing_path);
1058
1063
  const testId = record.test_id ?? "unknown";
1059
1064
  const gradingAssertions = grading ? collectComponentAssertions(grading.component_results) : void 0;
@@ -2853,18 +2858,63 @@ var UNSUPPORTED_PROMPTFOO_ASSERTION_TYPES = /* @__PURE__ */ new Set([
2853
2858
  "human",
2854
2859
  "max-score",
2855
2860
  "tool-call-f1",
2856
- "skill-used",
2857
- "trajectory:goal-success",
2858
- "trajectory:tool-args-match",
2859
- "trajectory:step-count",
2860
- "trajectory:tool-sequence",
2861
- "trajectory:tool-used",
2862
2861
  "trace-error-spans",
2863
2862
  "trace-span-count",
2864
2863
  "trace-span-duration",
2865
2864
  "search-rubric",
2866
2865
  "word-count"
2867
2866
  ]);
2867
+ function formatJsonValue(value) {
2868
+ if (typeof value === "string") return value;
2869
+ try {
2870
+ return JSON.stringify(value);
2871
+ } catch {
2872
+ return String(value);
2873
+ }
2874
+ }
2875
+ function hasToolTrajectoryLatencyCheck(value) {
2876
+ const expected = value.expected;
2877
+ return Array.isArray(expected) && expected.some(
2878
+ (item) => isObject(item) && (item.max_duration_ms !== void 0 || item.maxDurationMs !== void 0)
2879
+ );
2880
+ }
2881
+ function staleSkillTriggerMessage(value) {
2882
+ const skill = typeof value.skill === "string" && value.skill.trim() ? value.skill.trim() : "";
2883
+ const shouldTrigger = value.should_trigger !== false;
2884
+ if (!skill) {
2885
+ return "Authored assertion type 'skill-trigger' has been removed. Use 'skill-used' with value: <skill> for expected skill use, or 'not-skill-used' with value: <skill> when the skill must not be used.";
2886
+ }
2887
+ const replacementType = shouldTrigger ? "skill-used" : "not-skill-used";
2888
+ return `Authored assertion type 'skill-trigger' has been removed. Replace skill: ${skill} with type: ${replacementType}, value: ${skill}.`;
2889
+ }
2890
+ function staleToolTrajectoryMessage(value) {
2891
+ const mode = typeof value.mode === "string" ? value.mode : void 0;
2892
+ if (hasToolTrajectoryLatencyCheck(value)) {
2893
+ return "Authored assertion type 'tool-trajectory' has been removed. Per-tool latency checks such as max_duration_ms have no Promptfoo trajectory:* equivalent in AgentV yet; use a custom script assertion or track this as unsupported future scope.";
2894
+ }
2895
+ if (mode === "any_order" && isObject(value.minimums)) {
2896
+ const [toolName, min] = Object.entries(value.minimums)[0] ?? [];
2897
+ const example = typeof toolName === "string" ? ` For example: type: trajectory:tool-used, value: { name: ${toolName}, min: ${formatJsonValue(min)} }.` : "";
2898
+ return `Authored assertion type 'tool-trajectory' has been removed. Replace mode: any_order minimums with one Promptfoo trajectory:tool-used assertion per tool.${example}`;
2899
+ }
2900
+ if ((mode === "in_order" || mode === "exact") && Array.isArray(value.expected)) {
2901
+ const steps = value.expected.filter(isObject).map((item) => item.tool).filter((tool) => typeof tool === "string");
2902
+ const hasArgs = value.expected.some(
2903
+ (item) => isObject(item) && item.args !== void 0 && item.args !== "any"
2904
+ );
2905
+ const sequenceHint = steps.length > 0 ? ` Use type: trajectory:tool-sequence, value: { mode: ${mode}, steps: ${formatJsonValue(steps)} }.` : " Use type: trajectory:tool-sequence with value: { mode, steps }.";
2906
+ const argsHint = hasArgs ? " Move expected args checks to trajectory:tool-args-match with Promptfoo mode: partial or exact." : "";
2907
+ return `Authored assertion type 'tool-trajectory' has been removed.${sequenceHint}${argsHint}`;
2908
+ }
2909
+ return "Authored assertion type 'tool-trajectory' has been removed. Use Promptfoo trajectory:* assertions: trajectory:tool-used for tool presence/counts, trajectory:tool-sequence for in_order/exact steps, and trajectory:tool-args-match for argument checks. AgentV-specific latency checks are unsupported future scope.";
2910
+ }
2911
+ function staleAuthoredAssertionMessage(value) {
2912
+ if (typeof value.type !== "string") return void 0;
2913
+ const type = value.type.replace(/_/g, "-");
2914
+ if (type === "skill-trigger") return staleSkillTriggerMessage(value);
2915
+ if (type === "tool-trajectory") return staleToolTrajectoryMessage(value);
2916
+ return void 0;
2917
+ }
2868
2918
  var VALID_TEST_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
2869
2919
  ".csv",
2870
2920
  ".cjs",
@@ -2984,6 +3034,14 @@ var REMOVED_TOP_LEVEL_FIELDS = /* @__PURE__ */ new Map([
2984
3034
  "providers",
2985
3035
  "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."
2986
3036
  ],
3037
+ [
3038
+ "eval_cases",
3039
+ "Top-level 'eval_cases' has been removed from authored eval YAML. Use 'tests' instead."
3040
+ ],
3041
+ [
3042
+ "evalcases",
3043
+ "Top-level 'evalcases' has been removed from authored eval YAML. Use 'tests' instead."
3044
+ ],
2987
3045
  ["repeat", "Top-level 'repeat' has been removed. Use evaluate_options.repeat instead."],
2988
3046
  ["runs", "Top-level 'runs' has been removed. Use evaluate_options.repeat.count instead."],
2989
3047
  [
@@ -2998,11 +3056,11 @@ var REMOVED_TOP_LEVEL_FIELDS = /* @__PURE__ */ new Map([
2998
3056
  [
2999
3057
  "preprocessors",
3000
3058
  "Top-level 'preprocessors' has been removed from authored eval YAML. Use default_test.options.transform or assertion-level transform instead."
3001
- ]
3059
+ ],
3060
+ ["providerPromptMap", "Top-level 'providerPromptMap' is not supported. Use 'targets'."],
3061
+ ["provider_prompt_map", "Top-level 'provider_prompt_map' is not supported. Use 'targets'."]
3002
3062
  ]);
3003
3063
  var DEPRECATED_TOP_LEVEL_FIELDS = /* @__PURE__ */ new Map([
3004
- ["eval_cases", "'eval_cases' is deprecated. Use 'tests' instead."],
3005
- ["evalcases", "'evalcases' is deprecated. Use 'tests' instead."],
3006
3064
  ["evaluator", "'evaluator' is deprecated. Use 'assert' instead."]
3007
3065
  ]);
3008
3066
  var KNOWN_TEST_FIELDS = /* @__PURE__ */ new Set([
@@ -3035,6 +3093,7 @@ var KNOWN_TEST_FIELDS = /* @__PURE__ */ new Set([
3035
3093
  "on_turn_failure",
3036
3094
  "window_size"
3037
3095
  ]);
3096
+ var KNOWN_SCENARIO_FIELDS = /* @__PURE__ */ new Set(["description", "config", "tests"]);
3038
3097
  var SUPPORTED_WORKSPACE_REPO_FIELDS = /* @__PURE__ */ new Set(["path", "repo", "commit", "ancestor", "sparse"]);
3039
3098
  var KNOWN_REMOVED_WORKSPACE_FIELDS = /* @__PURE__ */ new Set([
3040
3099
  "template",
@@ -3052,6 +3111,22 @@ REMOVED_TEST_FIELDS.set(
3052
3111
  "input",
3053
3112
  "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."
3054
3113
  );
3114
+ REMOVED_TEST_FIELDS.set(
3115
+ "preprocessors",
3116
+ "tests[].preprocessors has been removed from authored eval YAML. Use tests[].options.transform or assertion-level transform instead."
3117
+ );
3118
+ REMOVED_TEST_FIELDS.set(
3119
+ "postprocess",
3120
+ "tests[].postprocess has been removed from authored eval YAML. Use tests[].options.transform instead."
3121
+ );
3122
+ REMOVED_TEST_FIELDS.set(
3123
+ "eval_cases",
3124
+ "tests[].eval_cases has been removed from authored eval YAML. Use 'tests' rows directly."
3125
+ );
3126
+ REMOVED_TEST_FIELDS.set(
3127
+ "evalcases",
3128
+ "tests[].evalcases has been removed from authored eval YAML. Use 'tests' rows directly."
3129
+ );
3055
3130
  var DEPRECATED_TEST_FIELDS = /* @__PURE__ */ new Map([
3056
3131
  ["evaluator", "'evaluator' is deprecated. Use 'assert' instead."],
3057
3132
  ["expected_outcome", "'expected_outcome' is deprecated. Use 'assert' instead."]
@@ -3185,6 +3260,7 @@ async function validateEvalFile(filePath) {
3185
3260
  validateEvaluateOptions(parsed.evaluate_options, "evaluate_options", absolutePath, errors);
3186
3261
  validateAssertArray(parsed.assert, "assert", absolutePath, errors, customAssertionTypes);
3187
3262
  validateDefaultTest(parsed.default_test, absolutePath, errors, customAssertionTypes);
3263
+ await validateScenarios(parsed.scenarios, parsed, absolutePath, errors, customAssertionTypes);
3188
3264
  const cases = parsed.tests;
3189
3265
  const hasImports = collectImportEntries(parsed).length > 0;
3190
3266
  const hasScenarios = Array.isArray(parsed.scenarios);
@@ -3357,6 +3433,32 @@ async function validateEvalFile(filePath) {
3357
3433
  errors
3358
3434
  };
3359
3435
  }
3436
+ async function validateTypeScriptEvalConfigFile(filePath) {
3437
+ const absolutePath = path22.resolve(filePath);
3438
+ try {
3439
+ const { loadTsEvalSuite } = await import("./ts-eval-loader-2RFVZHCT-7CZ3DCDD.js");
3440
+ await loadTsEvalSuite(absolutePath, process.cwd());
3441
+ return {
3442
+ valid: true,
3443
+ filePath: absolutePath,
3444
+ fileType: "eval",
3445
+ errors: []
3446
+ };
3447
+ } catch (error) {
3448
+ return {
3449
+ valid: false,
3450
+ filePath: absolutePath,
3451
+ fileType: "eval",
3452
+ errors: [
3453
+ {
3454
+ severity: "error",
3455
+ filePath: absolutePath,
3456
+ message: error.message
3457
+ }
3458
+ ]
3459
+ };
3460
+ }
3461
+ }
3360
3462
  async function validateSuiteWorkspaceConfigs(parsed, absolutePath, errors) {
3361
3463
  await validateWorkspaceConfig(parsed.workspace, absolutePath, errors, "workspace");
3362
3464
  if (isObject(parsed.execution)) {
@@ -3843,6 +3945,182 @@ function validateDefaultTest(defaultTest, filePath, errors, customAssertionTypes
3843
3945
  });
3844
3946
  }
3845
3947
  }
3948
+ async function validateScenarios(scenarios, parsed, filePath, errors, customAssertionTypes) {
3949
+ if (scenarios === void 0) {
3950
+ return;
3951
+ }
3952
+ if (!Array.isArray(scenarios)) {
3953
+ errors.push({
3954
+ severity: "error",
3955
+ filePath,
3956
+ location: "scenarios",
3957
+ message: "Invalid 'scenarios' field (must be an array)"
3958
+ });
3959
+ return;
3960
+ }
3961
+ for (let scenarioIndex = 0; scenarioIndex < scenarios.length; scenarioIndex++) {
3962
+ const scenario = scenarios[scenarioIndex];
3963
+ const scenarioLocation = `scenarios[${scenarioIndex}]`;
3964
+ if (!isObject(scenario)) {
3965
+ errors.push({
3966
+ severity: "error",
3967
+ filePath,
3968
+ location: scenarioLocation,
3969
+ message: "Scenario must be an object"
3970
+ });
3971
+ continue;
3972
+ }
3973
+ for (const key of Object.keys(scenario)) {
3974
+ if (!KNOWN_SCENARIO_FIELDS.has(key)) {
3975
+ errors.push({
3976
+ severity: "error",
3977
+ filePath,
3978
+ location: `${scenarioLocation}.${key}`,
3979
+ message: `Unknown scenario field '${key}'.`
3980
+ });
3981
+ }
3982
+ }
3983
+ if (!Array.isArray(scenario.config)) {
3984
+ errors.push({
3985
+ severity: "error",
3986
+ filePath,
3987
+ location: `${scenarioLocation}.config`,
3988
+ message: "Invalid 'config' field (must be an array)"
3989
+ });
3990
+ } else {
3991
+ for (let configIndex = 0; configIndex < scenario.config.length; configIndex++) {
3992
+ await validateScenarioTestLikeRow(
3993
+ scenario.config[configIndex],
3994
+ `${scenarioLocation}.config[${configIndex}]`,
3995
+ filePath,
3996
+ errors,
3997
+ customAssertionTypes,
3998
+ { requireInput: false }
3999
+ );
4000
+ }
4001
+ }
4002
+ const scenarioConfigs = Array.isArray(scenario.config) ? scenario.config : [];
4003
+ if (!Array.isArray(scenario.tests)) {
4004
+ errors.push({
4005
+ severity: "error",
4006
+ filePath,
4007
+ location: `${scenarioLocation}.tests`,
4008
+ message: "Invalid 'tests' field (must be an array)"
4009
+ });
4010
+ continue;
4011
+ }
4012
+ for (let testIndex = 0; testIndex < scenario.tests.length; testIndex++) {
4013
+ const test = scenario.tests[testIndex];
4014
+ const everyConfigProvidesInput = scenarioConfigs.length > 0 && scenarioConfigs.every(
4015
+ (config) => isObject(config) && (config.prompts !== void 0 || config.provider_output !== void 0)
4016
+ );
4017
+ const requireInput = isObject(test) && parsed.prompts === void 0 && test.prompts === void 0 && test.provider_output === void 0 && !everyConfigProvidesInput;
4018
+ await validateScenarioTestLikeRow(
4019
+ test,
4020
+ `${scenarioLocation}.tests[${testIndex}]`,
4021
+ filePath,
4022
+ errors,
4023
+ customAssertionTypes,
4024
+ { requireInput }
4025
+ );
4026
+ }
4027
+ }
4028
+ }
4029
+ async function validateScenarioTestLikeRow(row, location, filePath, errors, customAssertionTypes, options) {
4030
+ if (!isObject(row)) {
4031
+ errors.push({
4032
+ severity: "error",
4033
+ filePath,
4034
+ location,
4035
+ message: "Scenario row must be an object"
4036
+ });
4037
+ return;
4038
+ }
4039
+ for (const key of Object.keys(row)) {
4040
+ const removedMessage = REMOVED_TEST_FIELDS.get(key);
4041
+ if (removedMessage) {
4042
+ errors.push({
4043
+ severity: "error",
4044
+ filePath,
4045
+ location: `${location}.${key}`,
4046
+ message: removedMessage.replaceAll("tests[]", location)
4047
+ });
4048
+ continue;
4049
+ }
4050
+ const deprecationMessage = DEPRECATED_TEST_FIELDS.get(key);
4051
+ if (deprecationMessage) {
4052
+ errors.push({
4053
+ severity: "warning",
4054
+ filePath,
4055
+ location: `${location}.${key}`,
4056
+ message: deprecationMessage
4057
+ });
4058
+ } else if (!KNOWN_TEST_FIELDS.has(key)) {
4059
+ errors.push({
4060
+ severity: "error",
4061
+ filePath,
4062
+ location: `${location}.${key}`,
4063
+ message: `Unknown test field '${key}'.`
4064
+ });
4065
+ }
4066
+ }
4067
+ const id = row.id;
4068
+ if (id !== void 0 && (typeof id !== "string" || id.trim().length === 0)) {
4069
+ errors.push({
4070
+ severity: "error",
4071
+ filePath,
4072
+ location: `${location}.id`,
4073
+ message: "Invalid 'id' field (must be a non-empty string when provided)"
4074
+ });
4075
+ }
4076
+ const criteria = row.criteria;
4077
+ if (criteria !== void 0 && (typeof criteria !== "string" || criteria.trim().length === 0)) {
4078
+ errors.push({
4079
+ severity: "error",
4080
+ filePath,
4081
+ location: `${location}.criteria`,
4082
+ message: "Invalid 'criteria' field (must be a non-empty string if provided)"
4083
+ });
4084
+ } else if (criteria !== void 0 && row.assert !== void 0) {
4085
+ errors.push({
4086
+ severity: "error",
4087
+ filePath,
4088
+ location: `${location}.criteria`,
4089
+ 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: ... }."
4090
+ });
4091
+ }
4092
+ validateInputField(row.input, `${location}.input`, filePath, errors, {
4093
+ required: options.requireInput
4094
+ });
4095
+ if (row.expected_output !== void 0) {
4096
+ errors.push({
4097
+ severity: "error",
4098
+ filePath,
4099
+ location: `${location}.expected_output`,
4100
+ 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 }}' }."
4101
+ });
4102
+ }
4103
+ validateAssertArray(row.assert, `${location}.assert`, filePath, errors, customAssertionTypes);
4104
+ validateRunOverride(row.run, `${location}.run`, filePath, errors);
4105
+ validateTestOptions(row.options, `${location}.options`, filePath, errors);
4106
+ await validateEnvironmentConfig(row.environment, filePath, errors, `${location}.environment`);
4107
+ validateConversationMode(row, location, filePath, errors);
4108
+ await validateWorkspaceConfig(row.workspace, filePath, errors, `${location}.workspace`);
4109
+ const caseExecution = isObject(row.execution) ? row.execution : void 0;
4110
+ if (caseExecution) {
4111
+ validateTestExecutionFields(caseExecution, filePath, errors, location);
4112
+ rejectRuntimeWorkspaceConfig(
4113
+ caseExecution.workspace,
4114
+ filePath,
4115
+ errors,
4116
+ `${location}.execution.workspace`
4117
+ );
4118
+ }
4119
+ const targets = row.providers !== void 0 ? row.providers : row.provider;
4120
+ if (targets !== void 0) {
4121
+ validateTargetTestbedFields(targets, `${location}.providers`, filePath, errors);
4122
+ }
4123
+ }
3846
4124
  function validateEvaluateOptions(evaluateOptions, location, filePath, errors) {
3847
4125
  if (evaluateOptions === void 0) {
3848
4126
  return;
@@ -4611,6 +4889,16 @@ function validateAssertArray(assertField, location, filePath, errors, customAsse
4611
4889
  continue;
4612
4890
  }
4613
4891
  const typeValue = rawTypeValue.replace(/_/g, "-");
4892
+ const staleMessage = staleAuthoredAssertionMessage(item);
4893
+ if (staleMessage) {
4894
+ errors.push({
4895
+ severity: "error",
4896
+ filePath,
4897
+ location: `${itemLocation}.type`,
4898
+ message: staleMessage
4899
+ });
4900
+ continue;
4901
+ }
4614
4902
  const replacement = REMOVED_ASSERTION_TYPE_REPLACEMENTS.get(typeValue);
4615
4903
  if (replacement) {
4616
4904
  errors.push({
@@ -4631,7 +4919,7 @@ function validateAssertArray(assertField, location, filePath, errors, customAsse
4631
4919
  });
4632
4920
  continue;
4633
4921
  }
4634
- if (!isGraderKind(typeValue) && !PROMPTFOO_ASSERTION_TYPES.has(typeValue) && !customAssertionTypes.has(typeValue)) {
4922
+ if (!isGraderKind(typeValue) && !isGraderKind(baseTypeValue) && !PROMPTFOO_ASSERTION_TYPES.has(typeValue) && !customAssertionTypes.has(typeValue)) {
4635
4923
  errors.push({
4636
4924
  severity: "warning",
4637
4925
  filePath,
@@ -5154,6 +5442,7 @@ function validateUnknownSettings(target, provider, absolutePath, location, error
5154
5442
  "env",
5155
5443
  "grader_target",
5156
5444
  "judge_target",
5445
+ "use_target",
5157
5446
  "workers",
5158
5447
  "$schema",
5159
5448
  "targets"
@@ -5436,15 +5725,22 @@ async function validateTargetsFile(filePath) {
5436
5725
  const provider = effectiveTarget.provider;
5437
5726
  const rawTarget = rawTargets[i];
5438
5727
  const rawUseTarget = isObject3(rawTarget) ? rawTarget.use_target : void 0;
5439
- const hasUseTarget = isNonEmptyString(effectiveTarget.use_target) || isNonEmptyString(rawUseTarget);
5728
+ if (effectiveTarget.use_target !== void 0 || rawUseTarget !== void 0) {
5729
+ errors.push({
5730
+ severity: "error",
5731
+ filePath: absolutePath,
5732
+ location: `${location}.use_target`,
5733
+ message: "The 'use_target' field has been removed from authored targets.yaml target definitions. Define a concrete target with 'provider' instead."
5734
+ });
5735
+ }
5440
5736
  const providerValue = typeof provider === "string" ? provider.trim().toLowerCase() : void 0;
5441
5737
  const isTemplated = isEnvTemplated(provider);
5442
- if (!hasUseTarget && (typeof provider !== "string" || provider.trim().length === 0)) {
5738
+ if (typeof provider !== "string" || provider.trim().length === 0) {
5443
5739
  errors.push({
5444
5740
  severity: "error",
5445
5741
  filePath: absolutePath,
5446
5742
  location: `${location}.provider`,
5447
- message: "Missing or invalid 'provider' field (must be a non-empty string, or use use_target for delegation)"
5743
+ message: "Missing or invalid 'provider' field (must be a non-empty string)"
5448
5744
  });
5449
5745
  } else if (!isTemplated && (providerValue === "claude" || providerValue === "copilot")) {
5450
5746
  errors.push({
@@ -5579,6 +5875,9 @@ async function validateConfigFile(filePath, options = {}) {
5579
5875
  validateRefsConfig(errors, filePath, config.refs);
5580
5876
  validateComposableGraph(errors, filePath, config);
5581
5877
  validateDashboardConfig(errors, filePath, config.dashboard);
5878
+ validateHooksConfig(errors, filePath, config.hooks);
5879
+ validateEnvPathConfig(errors, filePath, config.env_path);
5880
+ validateEnvFromConfig(errors, filePath, config.env_from);
5582
5881
  const projects = config.projects;
5583
5882
  if (projects !== void 0) {
5584
5883
  if (scope === "project") {
@@ -5613,7 +5912,10 @@ async function validateConfigFile(filePath, options = {}) {
5613
5912
  "defaults",
5614
5913
  "projects",
5615
5914
  "dashboard",
5616
- "studio"
5915
+ "studio",
5916
+ "hooks",
5917
+ "env_path",
5918
+ "env_from"
5617
5919
  ]);
5618
5920
  const unexpectedFields = Object.keys(config).filter((key) => !allowedFields.has(key));
5619
5921
  if (unexpectedFields.length > 0) {
@@ -5662,6 +5964,76 @@ function validateDashboardConfig(errors, filePath, rawDashboard) {
5662
5964
  );
5663
5965
  }
5664
5966
  }
5967
+ var ENV_FROM_FORMATS = /* @__PURE__ */ new Set(["shell_exports", "json"]);
5968
+ function validateHooksConfig(errors, filePath, rawHooks) {
5969
+ if (rawHooks === void 0 || rawHooks === null) {
5970
+ return;
5971
+ }
5972
+ if (!isPlainObject(rawHooks)) {
5973
+ addError(errors, filePath, "hooks", "Field 'hooks' must be an object");
5974
+ return;
5975
+ }
5976
+ if (rawHooks.before_session !== void 0) {
5977
+ validateOptionalString(errors, filePath, rawHooks.before_session, "hooks.before_session");
5978
+ errors.push({
5979
+ severity: "warning",
5980
+ filePath,
5981
+ location: "hooks.before_session",
5982
+ message: "'hooks.before_session' is deprecated; use 'env_path' and/or 'env_from' to load environment variables before validation and eval."
5983
+ });
5984
+ }
5985
+ }
5986
+ function validateEnvPathConfig(errors, filePath, rawEnvPath) {
5987
+ if (rawEnvPath === void 0 || rawEnvPath === null) {
5988
+ return;
5989
+ }
5990
+ const isList = Array.isArray(rawEnvPath);
5991
+ const entries = isList ? rawEnvPath : [rawEnvPath];
5992
+ entries.forEach((entry, index) => {
5993
+ const location = isList ? `env_path[${index}]` : "env_path";
5994
+ if (typeof entry !== "string" || entry.trim().length === 0) {
5995
+ addError(errors, filePath, location, `Field '${location}' must be a non-empty string`);
5996
+ }
5997
+ });
5998
+ }
5999
+ function validateEnvFromConfig(errors, filePath, rawEnvFrom) {
6000
+ if (rawEnvFrom === void 0 || rawEnvFrom === null) {
6001
+ return;
6002
+ }
6003
+ const isList = Array.isArray(rawEnvFrom);
6004
+ const entries = isList ? rawEnvFrom : [rawEnvFrom];
6005
+ entries.forEach((entry, index) => {
6006
+ const location = isList ? `env_from[${index}]` : "env_from";
6007
+ if (!isPlainObject(entry)) {
6008
+ addError(errors, filePath, location, `Field '${location}' must be an object`);
6009
+ return;
6010
+ }
6011
+ const command = entry.command;
6012
+ if (typeof command === "string") {
6013
+ addError(
6014
+ errors,
6015
+ filePath,
6016
+ `${location}.command`,
6017
+ `Field '${location}.command' must be an argv array of strings, not a shell command string. Use e.g. ["bun", "scripts/load-secrets.ts"].`
6018
+ );
6019
+ } else if (!Array.isArray(command) || command.length === 0 || !command.every((part) => typeof part === "string" && part.length > 0)) {
6020
+ addError(
6021
+ errors,
6022
+ filePath,
6023
+ `${location}.command`,
6024
+ `Field '${location}.command' must be a non-empty array of strings`
6025
+ );
6026
+ }
6027
+ if (entry.format !== void 0 && !ENV_FROM_FORMATS.has(entry.format)) {
6028
+ addError(
6029
+ errors,
6030
+ filePath,
6031
+ `${location}.format`,
6032
+ `Field '${location}.format' must be "shell_exports" or "json"`
6033
+ );
6034
+ }
6035
+ });
6036
+ }
5665
6037
  function validateRefsConfig(errors, filePath, rawRefs) {
5666
6038
  if (rawRefs === void 0) {
5667
6039
  return;
@@ -5874,13 +6246,7 @@ async function validateFileReferences(evalFilePath) {
5874
6246
  if (!isObject4(parsed)) {
5875
6247
  return errors;
5876
6248
  }
5877
- let cases = parsed.tests;
5878
- if (cases === void 0 && "eval_cases" in parsed) {
5879
- cases = parsed.eval_cases;
5880
- }
5881
- if (cases === void 0 && "evalcases" in parsed) {
5882
- cases = parsed.evalcases;
5883
- }
6249
+ const cases = parsed.tests;
5884
6250
  if (!Array.isArray(cases)) {
5885
6251
  return errors;
5886
6252
  }
@@ -7527,7 +7893,7 @@ async function runEvalCommand(input) {
7527
7893
  const resolvedExperiment = resolveExperimentForRun(options.experiment);
7528
7894
  const evalPathInputs = input.testFiles.length > 0 ? [...input.testFiles] : [];
7529
7895
  if (evalPathInputs.length === 0 && process.stdin.isTTY) {
7530
- const { launchInteractiveWizard } = await import("./interactive-36ZNMQB2.js");
7896
+ const { launchInteractiveWizard } = await import("./interactive-BN527UV3.js");
7531
7897
  await launchInteractiveWizard();
7532
7898
  return void 0;
7533
7899
  }
@@ -7867,7 +8233,7 @@ async function runEvalCommand(input) {
7867
8233
  });
7868
8234
  let transcriptProviderFactory;
7869
8235
  if (options.transcript) {
7870
- const { TranscriptProvider } = await import("./dist-DEPJOMCA.js");
8236
+ const { TranscriptProvider } = await import("./dist-NMXMI5SK.js");
7871
8237
  const transcriptProvider = await TranscriptProvider.fromFile(options.transcript);
7872
8238
  const totalTests = [...fileMetadata.values()].reduce(
7873
8239
  (sum, meta) => sum + meta.testCases.length,
@@ -8133,7 +8499,7 @@ ${formatRequiredVersionFailureNote(requiredVersionCheck)}`);
8133
8499
  const sourceTests = activeSourceTests;
8134
8500
  const taskBundleTargets = buildTaskBundleTargetSelections(activeTestFiles, fileMetadata);
8135
8501
  if (isResumeAppend) {
8136
- const { writePerTestArtifacts } = await import("./artifact-writer-SN6UNL5G.js");
8502
+ const { writePerTestArtifacts } = await import("./artifact-writer-7NBCOAYC.js");
8137
8503
  await writePerTestArtifacts(allResults, runDir, {
8138
8504
  experiment: normalizeExperimentName(options.experiment),
8139
8505
  runId: path12.basename(runDir),
@@ -8313,6 +8679,7 @@ export {
8313
8679
  loadEnvFromHierarchy,
8314
8680
  detectFileType,
8315
8681
  validateEvalFile,
8682
+ validateTypeScriptEvalConfigFile,
8316
8683
  validateCasesFile,
8317
8684
  validateTargetsFile,
8318
8685
  validateConfigFile,
@@ -8351,4 +8718,4 @@ export {
8351
8718
  filterByCategory,
8352
8719
  runEvalCommand
8353
8720
  };
8354
- //# sourceMappingURL=chunk-T4AD3H3Y.js.map
8721
+ //# sourceMappingURL=chunk-LKGARI3W.js.map