agentv 5.1.0-next.1 → 5.3.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +16 -12
  2. package/dist/{artifact-writer-IORG7CW5.js → artifact-writer-JFNIPMKW.js} +5 -5
  3. package/dist/{chunk-ZGPCX334.js → chunk-6ZZCDZPD.js} +4101 -1204
  4. package/dist/chunk-6ZZCDZPD.js.map +1 -0
  5. package/dist/{chunk-CBBBRTQ3.js → chunk-BHKQHG26.js} +64 -3
  6. package/dist/chunk-BHKQHG26.js.map +1 -0
  7. package/dist/{chunk-AM2IPHKS.js → chunk-FVR4RQFK.js} +302 -162
  8. package/dist/chunk-FVR4RQFK.js.map +1 -0
  9. package/dist/{chunk-GIRUNQGP.js → chunk-T32NL3E6.js} +262 -154
  10. package/dist/chunk-T32NL3E6.js.map +1 -0
  11. package/dist/{chunk-GJWPQXHN.js → chunk-V52ATPTT.js} +104 -188
  12. package/dist/chunk-V52ATPTT.js.map +1 -0
  13. package/dist/cli.js +6 -6
  14. package/dist/dashboard/assets/{index-BSHoWrdr.js → index-DNgf3qJ2.js} +1 -1
  15. package/dist/dashboard/assets/{index-zvUKAkKn.css → index-D_bokML8.css} +1 -1
  16. package/dist/dashboard/assets/index-r_jSJmlw.js +121 -0
  17. package/dist/dashboard/index.html +2 -2
  18. package/dist/{dist-2D2GBHZV.js → dist-6Z7U473R.js} +14 -8
  19. package/dist/index.js +6 -6
  20. package/dist/{interactive-V4SEHLNY.js → interactive-RUY3OCBI.js} +6 -6
  21. package/dist/skills/agentv-bench/SKILL.md +17 -13
  22. package/dist/skills/agentv-bench/agents/analyzer.md +1 -1
  23. package/dist/skills/agentv-bench/agents/comparator.md +3 -3
  24. package/dist/skills/agentv-bench/agents/grader.md +4 -2
  25. package/dist/skills/agentv-bench/references/description-optimization.md +5 -6
  26. package/dist/skills/agentv-bench/references/environment-adaptation.md +2 -2
  27. package/dist/skills/agentv-bench/references/eval-yaml-spec.md +35 -19
  28. package/dist/skills/agentv-bench/references/migrating-from-skill-creator.md +1 -1
  29. package/dist/skills/agentv-eval-migrations/references/breaking-changes.md +125 -28
  30. package/dist/skills/agentv-eval-writer/SKILL.md +113 -87
  31. package/dist/skills/agentv-eval-writer/references/eval.schema.json +772 -1367
  32. package/dist/{ts-eval-loader-SSPJVEHD-AL2A3V5Z.js → ts-eval-loader-DQDYRULE-V3377FOL.js} +3 -2
  33. package/package.json +8 -2
  34. package/dist/chunk-AM2IPHKS.js.map +0 -1
  35. package/dist/chunk-CBBBRTQ3.js.map +0 -1
  36. package/dist/chunk-GIRUNQGP.js.map +0 -1
  37. package/dist/chunk-GJWPQXHN.js.map +0 -1
  38. package/dist/chunk-ZGPCX334.js.map +0 -1
  39. package/dist/dashboard/assets/index-BaLLxmVO.js +0 -121
  40. /package/dist/{artifact-writer-IORG7CW5.js.map → artifact-writer-JFNIPMKW.js.map} +0 -0
  41. /package/dist/{dist-2D2GBHZV.js.map → dist-6Z7U473R.js.map} +0 -0
  42. /package/dist/{interactive-V4SEHLNY.js.map → interactive-RUY3OCBI.js.map} +0 -0
  43. /package/dist/{ts-eval-loader-SSPJVEHD-AL2A3V5Z.js.map → ts-eval-loader-DQDYRULE-V3377FOL.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-CBBBRTQ3.js";
4
+ } from "./chunk-BHKQHG26.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-GJWPQXHN.js";
21
+ } from "./chunk-V52ATPTT.js";
22
22
  import {
23
23
  AGENTV_LOCAL_CONFIG_FILE_NAME,
24
24
  AGENTV_LOCAL_CONFIG_YML_FILE_NAME,
@@ -59,6 +59,7 @@ import {
59
59
  loadTestSuite,
60
60
  loadTestSuiteFromYamlObject,
61
61
  normalizeComposableConfigGraph,
62
+ normalizeMetricsArtifactWire,
62
63
  normalizeResultRow,
63
64
  normalizeTargetDefinition,
64
65
  parseJsonlResults,
@@ -66,6 +67,7 @@ import {
66
67
  readTargetDefinitions,
67
68
  readTestSuiteMetadata,
68
69
  resolveConfigFieldReferences,
70
+ resolveEnvironmentRecipe,
69
71
  resolveFileReference,
70
72
  resolveResultsConfigForProject,
71
73
  resolveTargetDefinition,
@@ -82,7 +84,7 @@ import {
82
84
  traceEnvelopeToTraceSummary,
83
85
  traceEnvelopeToTranscriptMessages,
84
86
  writeInitialRunSummaryArtifact
85
- } from "./chunk-ZGPCX334.js";
87
+ } from "./chunk-6ZZCDZPD.js";
86
88
 
87
89
  // src/commands/eval/shared.ts
88
90
  import { constants } from "node:fs";
@@ -345,7 +347,7 @@ async function findRepoRoot(start) {
345
347
  // package.json
346
348
  var package_default = {
347
349
  name: "agentv",
348
- version: "5.1.0-next.1",
350
+ version: "5.3.0-next.1",
349
351
  description: "CLI entry point for AgentV",
350
352
  type: "module",
351
353
  repository: {
@@ -386,7 +388,7 @@ var package_default = {
386
388
  yaml: "^2.8.3"
387
389
  },
388
390
  peerDependencies: {
389
- "@anthropic-ai/claude-agent-sdk": "^0.2.88",
391
+ "@anthropic-ai/claude-agent-sdk": "^0.2.89",
390
392
  "@github/copilot-sdk": "^1.0.3",
391
393
  "@openai/codex-sdk": "^0.136.0",
392
394
  "@earendil-works/pi-coding-agent": "^0.74.0"
@@ -405,6 +407,12 @@ var package_default = {
405
407
  optional: true
406
408
  }
407
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
+ },
408
416
  devDependencies: {
409
417
  "@agentv/core": "workspace:*",
410
418
  "@agentv/sdk": "workspace:*",
@@ -867,6 +875,9 @@ function readGradingAssertionResults(record) {
867
875
  );
868
876
  }
869
877
  function readNestedGradingScores(record) {
878
+ if (Array.isArray(record.component_results)) {
879
+ return record.component_results;
880
+ }
870
881
  if (Array.isArray(record.scores)) {
871
882
  return record.scores;
872
883
  }
@@ -878,14 +889,56 @@ function readNestedGradingScores(record) {
878
889
  }
879
890
  return void 0;
880
891
  }
892
+ function componentLabel(component) {
893
+ const assertion = component.assertion;
894
+ if (assertion && typeof assertion === "object" && !Array.isArray(assertion)) {
895
+ const record = assertion;
896
+ for (const key of ["value", "name", "id", "type"]) {
897
+ const value = record[key];
898
+ if (typeof value === "string" && value.trim().length > 0) {
899
+ return value;
900
+ }
901
+ }
902
+ }
903
+ return typeof component.reason === "string" ? component.reason : "grading component";
904
+ }
905
+ function mapComponentAssertion(component) {
906
+ return {
907
+ text: componentLabel(component),
908
+ passed: component.pass === true,
909
+ evidence: typeof component.reason === "string" ? component.reason : void 0
910
+ };
911
+ }
912
+ function collectComponentAssertions(value) {
913
+ if (!Array.isArray(value)) {
914
+ return [];
915
+ }
916
+ return value.flatMap((component) => {
917
+ if (!component || typeof component !== "object" || Array.isArray(component)) {
918
+ return [];
919
+ }
920
+ const record = component;
921
+ const nested = collectComponentAssertions(record.component_results);
922
+ return nested.length > 0 ? nested : [mapComponentAssertion(record)];
923
+ });
924
+ }
881
925
  function mapGradingEvaluator(evaluator) {
882
- const verdict = evaluator.verdict === "pass" || evaluator.verdict === "fail" || evaluator.verdict === "skip" ? evaluator.verdict : void 0;
926
+ const pass = typeof evaluator.pass === "boolean" ? evaluator.pass : evaluator.verdict === "pass" || typeof evaluator.score === "number" && evaluator.score >= 0.8;
927
+ const verdict = pass ? "pass" : "fail";
883
928
  const details = evaluator.details && typeof evaluator.details === "object" && !Array.isArray(evaluator.details) ? evaluator.details : void 0;
929
+ const assertion = evaluator.assertion && typeof evaluator.assertion === "object" && !Array.isArray(evaluator.assertion) ? evaluator.assertion : void 0;
884
930
  return {
885
- name: String(evaluator.name ?? ""),
886
- type: String(evaluator.type ?? ""),
931
+ name: String(assertion?.name ?? assertion?.id ?? evaluator.name ?? componentLabel(evaluator)),
932
+ type: String(assertion?.type ?? evaluator.type ?? "llm-grader"),
887
933
  score: typeof evaluator.score === "number" ? evaluator.score : 0,
888
- assertions: readGradingAssertionResults(evaluator) ?? [],
934
+ reason: typeof evaluator.reason === "string" ? evaluator.reason : void 0,
935
+ assertions: (() => {
936
+ const nestedAssertions = collectComponentAssertions(evaluator.component_results);
937
+ if (nestedAssertions.length > 0) {
938
+ return nestedAssertions;
939
+ }
940
+ return readGradingAssertionResults(evaluator) ?? [mapComponentAssertion(evaluator)];
941
+ })(),
889
942
  scores: mapGradingEvaluators(readNestedGradingScores(evaluator)),
890
943
  weight: typeof evaluator.weight === "number" ? evaluator.weight : void 0,
891
944
  verdict,
@@ -1001,12 +1054,15 @@ function hydrateTrace(baseDir, record, options) {
1001
1054
  }
1002
1055
  function hydrateManifestRecord(baseDir, record, options) {
1003
1056
  const grading = readOptionalJson(baseDir, record.grading_path);
1004
- const metrics = readOptionalJson(baseDir, record.metrics_path);
1057
+ const metrics = normalizeMetricsArtifactWire(
1058
+ readOptionalJson(baseDir, record.metrics_path)
1059
+ );
1005
1060
  const timing = metrics ?? readOptionalJson(baseDir, record.timing_path);
1006
1061
  const testId = record.test_id ?? "unknown";
1007
- const gradingAssertions = grading ? readGradingAssertionResults(grading) : void 0;
1062
+ const gradingAssertions = grading ? collectComponentAssertions(grading.component_results) : void 0;
1063
+ const gradingRecord = grading;
1008
1064
  const gradingScores = mapGradingEvaluators(
1009
- grading?.graders ?? grading?.evaluators
1065
+ gradingRecord?.component_results ?? gradingRecord?.graders ?? gradingRecord?.evaluators
1010
1066
  );
1011
1067
  return {
1012
1068
  timestamp: record.timestamp,
@@ -2800,18 +2856,63 @@ var UNSUPPORTED_PROMPTFOO_ASSERTION_TYPES = /* @__PURE__ */ new Set([
2800
2856
  "human",
2801
2857
  "max-score",
2802
2858
  "tool-call-f1",
2803
- "skill-used",
2804
- "trajectory:goal-success",
2805
- "trajectory:tool-args-match",
2806
- "trajectory:step-count",
2807
- "trajectory:tool-sequence",
2808
- "trajectory:tool-used",
2809
2859
  "trace-error-spans",
2810
2860
  "trace-span-count",
2811
2861
  "trace-span-duration",
2812
2862
  "search-rubric",
2813
2863
  "word-count"
2814
2864
  ]);
2865
+ function formatJsonValue(value) {
2866
+ if (typeof value === "string") return value;
2867
+ try {
2868
+ return JSON.stringify(value);
2869
+ } catch {
2870
+ return String(value);
2871
+ }
2872
+ }
2873
+ function hasToolTrajectoryLatencyCheck(value) {
2874
+ const expected = value.expected;
2875
+ return Array.isArray(expected) && expected.some(
2876
+ (item) => isObject(item) && (item.max_duration_ms !== void 0 || item.maxDurationMs !== void 0)
2877
+ );
2878
+ }
2879
+ function staleSkillTriggerMessage(value) {
2880
+ const skill = typeof value.skill === "string" && value.skill.trim() ? value.skill.trim() : "";
2881
+ const shouldTrigger = value.should_trigger !== false;
2882
+ if (!skill) {
2883
+ 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.";
2884
+ }
2885
+ const replacementType = shouldTrigger ? "skill-used" : "not-skill-used";
2886
+ return `Authored assertion type 'skill-trigger' has been removed. Replace skill: ${skill} with type: ${replacementType}, value: ${skill}.`;
2887
+ }
2888
+ function staleToolTrajectoryMessage(value) {
2889
+ const mode = typeof value.mode === "string" ? value.mode : void 0;
2890
+ if (hasToolTrajectoryLatencyCheck(value)) {
2891
+ 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.";
2892
+ }
2893
+ if (mode === "any_order" && isObject(value.minimums)) {
2894
+ const [toolName, min] = Object.entries(value.minimums)[0] ?? [];
2895
+ const example = typeof toolName === "string" ? ` For example: type: trajectory:tool-used, value: { name: ${toolName}, min: ${formatJsonValue(min)} }.` : "";
2896
+ return `Authored assertion type 'tool-trajectory' has been removed. Replace mode: any_order minimums with one Promptfoo trajectory:tool-used assertion per tool.${example}`;
2897
+ }
2898
+ if ((mode === "in_order" || mode === "exact") && Array.isArray(value.expected)) {
2899
+ const steps = value.expected.filter(isObject).map((item) => item.tool).filter((tool) => typeof tool === "string");
2900
+ const hasArgs = value.expected.some(
2901
+ (item) => isObject(item) && item.args !== void 0 && item.args !== "any"
2902
+ );
2903
+ 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 }.";
2904
+ const argsHint = hasArgs ? " Move expected args checks to trajectory:tool-args-match with Promptfoo mode: partial or exact." : "";
2905
+ return `Authored assertion type 'tool-trajectory' has been removed.${sequenceHint}${argsHint}`;
2906
+ }
2907
+ 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.";
2908
+ }
2909
+ function staleAuthoredAssertionMessage(value) {
2910
+ if (typeof value.type !== "string") return void 0;
2911
+ const type = value.type.replace(/_/g, "-");
2912
+ if (type === "skill-trigger") return staleSkillTriggerMessage(value);
2913
+ if (type === "tool-trajectory") return staleToolTrajectoryMessage(value);
2914
+ return void 0;
2915
+ }
2815
2916
  var VALID_TEST_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
2816
2917
  ".csv",
2817
2918
  ".cjs",
@@ -2837,10 +2938,10 @@ var KNOWN_TOP_LEVEL_FIELDS = /* @__PURE__ */ new Set([
2837
2938
  "input",
2838
2939
  "input_files",
2839
2940
  "prompts",
2840
- "imports",
2841
2941
  "tests",
2842
2942
  "graders",
2843
2943
  "defaults",
2944
+ "environment",
2844
2945
  "target",
2845
2946
  "targets",
2846
2947
  "model",
@@ -2867,7 +2968,6 @@ var KNOWN_TOP_LEVEL_FIELDS = /* @__PURE__ */ new Set([
2867
2968
  "governance"
2868
2969
  ]);
2869
2970
  var KNOWN_INCLUDE_FIELDS = /* @__PURE__ */ new Set(["include", "type", "select", "run"]);
2870
- var KNOWN_IMPORT_FIELDS = /* @__PURE__ */ new Set(["path", "select", "run"]);
2871
2971
  var KNOWN_RUN_OVERRIDE_FIELDS = /* @__PURE__ */ new Set(["threshold", "repeat", "timeout_seconds", "budget_usd"]);
2872
2972
  var KNOWN_DEFAULT_TEST_FIELDS = /* @__PURE__ */ new Set([
2873
2973
  "vars",
@@ -2907,6 +3007,10 @@ var KNOWN_TEST_EXECUTION_FIELDS = /* @__PURE__ */ new Set([
2907
3007
  "workspace"
2908
3008
  ]);
2909
3009
  var REMOVED_TOP_LEVEL_FIELDS = /* @__PURE__ */ new Map([
3010
+ [
3011
+ "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."
3013
+ ],
2910
3014
  [
2911
3015
  "expected_output",
2912
3016
  "Top-level 'expected_output' has been removed from authored eval YAML. Put reference answers in default_test.vars.expected_output or tests[].vars.expected_output and consume them with an explicit assertion such as { type: 'llm-rubric', value: 'Matches the reference answer: {{ expected_output }}' }."
@@ -2966,6 +3070,7 @@ var KNOWN_TEST_FIELDS = /* @__PURE__ */ new Set([
2966
3070
  "threshold",
2967
3071
  "execution",
2968
3072
  "run",
3073
+ "environment",
2969
3074
  "workspace",
2970
3075
  "metadata",
2971
3076
  "conversation_id",
@@ -2979,6 +3084,17 @@ var KNOWN_TEST_FIELDS = /* @__PURE__ */ new Set([
2979
3084
  "window_size"
2980
3085
  ]);
2981
3086
  var SUPPORTED_WORKSPACE_REPO_FIELDS = /* @__PURE__ */ new Set(["path", "repo", "commit", "ancestor", "sparse"]);
3087
+ var KNOWN_REMOVED_WORKSPACE_FIELDS = /* @__PURE__ */ new Set([
3088
+ "template",
3089
+ "repos",
3090
+ "scope",
3091
+ "docker",
3092
+ "hooks",
3093
+ "env",
3094
+ "mode",
3095
+ "isolation",
3096
+ "path"
3097
+ ]);
2982
3098
  var REMOVED_TEST_FIELDS = /* @__PURE__ */ new Map([]);
2983
3099
  REMOVED_TEST_FIELDS.set(
2984
3100
  "input",
@@ -3108,13 +3224,15 @@ async function validateEvalFile(filePath) {
3108
3224
  });
3109
3225
  }
3110
3226
  }
3227
+ await validateEnvironmentConfig(parsed.environment, absolutePath, errors, "environment");
3111
3228
  await validateSuiteWorkspaceConfigs(parsed, absolutePath, errors);
3229
+ validateTargetTestbedFields(parsed.target, "target", absolutePath, errors);
3230
+ validateTargetsTestbedFields(parsed.targets, "targets", absolutePath, errors);
3112
3231
  validateAuthoredWorkers(parsed, absolutePath, errors);
3113
3232
  validateExecutionPolicy(parsed.execution, "execution", absolutePath, errors);
3114
3233
  validateEvaluateOptions(parsed.evaluate_options, "evaluate_options", absolutePath, errors);
3115
3234
  validateAssertArray(parsed.assert, "assert", absolutePath, errors, customAssertionTypes);
3116
3235
  validateDefaultTest(parsed.default_test, absolutePath, errors, customAssertionTypes);
3117
- await validateImportsField(parsed.imports, absolutePath, errors);
3118
3236
  const cases = parsed.tests;
3119
3237
  const hasImports = collectImportEntries(parsed).length > 0;
3120
3238
  const hasScenarios = Array.isArray(parsed.scenarios);
@@ -3144,7 +3262,7 @@ async function validateEvalFile(filePath) {
3144
3262
  severity: "error",
3145
3263
  filePath: absolutePath,
3146
3264
  location: "tests",
3147
- message: "Missing or invalid 'tests' field (must be an array or a file path string, unless imports are provided)"
3265
+ message: "Missing or invalid 'tests' field (must be an array or a file path string, unless scenarios are provided)"
3148
3266
  });
3149
3267
  return {
3150
3268
  valid: errors.length === 0,
@@ -3264,6 +3382,12 @@ async function validateEvalFile(filePath) {
3264
3382
  );
3265
3383
  validateRunOverride(evalCase.run, `${location}.run`, absolutePath, errors);
3266
3384
  validateTestOptions(evalCase.options, `${location}.options`, absolutePath, errors);
3385
+ await validateEnvironmentConfig(
3386
+ evalCase.environment,
3387
+ absolutePath,
3388
+ errors,
3389
+ `${location}.environment`
3390
+ );
3267
3391
  validateConversationMode(evalCase, location, absolutePath, errors);
3268
3392
  await validateWorkspaceConfig(
3269
3393
  evalCase.workspace,
@@ -3292,6 +3416,43 @@ async function validateSuiteWorkspaceConfigs(parsed, absolutePath, errors) {
3292
3416
  );
3293
3417
  }
3294
3418
  }
3419
+ async function validateEnvironmentConfig(environment, evalFilePath, errors, location) {
3420
+ if (environment === void 0) {
3421
+ return;
3422
+ }
3423
+ try {
3424
+ await resolveEnvironmentRecipe(environment, path22.dirname(evalFilePath), location);
3425
+ } catch (error) {
3426
+ errors.push({
3427
+ severity: "error",
3428
+ filePath: evalFilePath,
3429
+ location,
3430
+ message: error.message
3431
+ });
3432
+ }
3433
+ }
3434
+ function validateTargetTestbedFields(target, location, filePath, errors) {
3435
+ if (!isObject(target)) {
3436
+ return;
3437
+ }
3438
+ for (const field of ["environment", "container", "install"]) {
3439
+ if (target[field] === void 0) {
3440
+ continue;
3441
+ }
3442
+ errors.push({
3443
+ severity: "error",
3444
+ filePath,
3445
+ location: `${location}.${field}`,
3446
+ message: field === "environment" ? "Target definitions cannot include environment; author environment at suite/test/case scope." : `Target definitions cannot include ${field}; use an environment recipe for testbed setup.`
3447
+ });
3448
+ }
3449
+ }
3450
+ function validateTargetsTestbedFields(targets, location, filePath, errors) {
3451
+ const entries = Array.isArray(targets) ? targets : targets === void 0 ? [] : [targets];
3452
+ entries.forEach((entry, index) => {
3453
+ validateTargetTestbedFields(entry, `${location}[${index}]`, filePath, errors);
3454
+ });
3455
+ }
3295
3456
  function validateTestExecutionFields(caseExecution, filePath, errors, location) {
3296
3457
  for (const key of Object.keys(caseExecution)) {
3297
3458
  if (key === "workers") {
@@ -3413,87 +3574,11 @@ function rejectRuntimeWorkspaceConfig(workspace, filePath, errors, location) {
3413
3574
  severity: "error",
3414
3575
  filePath,
3415
3576
  location,
3416
- message: `${location} has been removed from eval YAML. Put machine-local workspace_path in .agentv/config.local.yaml under execution, or pass --workspace-path. Keep portable task setup in top-level workspace.`
3577
+ message: `${location} has been removed from eval YAML. Put machine-local workspace_path in .agentv/config.local.yaml under execution, or pass --workspace-path. Keep portable coding-agent testbed setup in environment.`
3417
3578
  });
3418
3579
  }
3419
- function importEntryPath(value) {
3420
- if (typeof value === "string" && value.trim().length > 0) {
3421
- return value.trim();
3422
- }
3423
- if (!isObject(value)) {
3424
- return void 0;
3425
- }
3426
- return typeof value.path === "string" && value.path.trim().length > 0 ? value.path.trim() : void 0;
3427
- }
3428
- async function validateImportsField(imports, filePath, errors) {
3429
- if (imports === void 0) {
3430
- return;
3431
- }
3432
- if (!isObject(imports)) {
3433
- errors.push({
3434
- severity: "error",
3435
- filePath,
3436
- location: "imports",
3437
- message: "Invalid 'imports' field. Use imports.suites and/or imports.tests."
3438
- });
3439
- return;
3440
- }
3441
- for (const key of Object.keys(imports)) {
3442
- if (key !== "suites" && key !== "tests") {
3443
- errors.push({
3444
- severity: "warning",
3445
- filePath,
3446
- location: `imports.${key}`,
3447
- message: `Unknown imports field '${key}'. Use imports.suites or imports.tests.`
3448
- });
3449
- }
3450
- }
3451
- await validateImportGroup(imports.suites, "suite", "imports.suites", filePath, errors);
3452
- await validateImportGroup(imports.tests, "tests", "imports.tests", filePath, errors);
3453
- }
3454
- async function validateImportGroup(group, type, location, filePath, errors) {
3455
- if (group === void 0) {
3456
- return;
3457
- }
3458
- const entries = Array.isArray(group) ? group : [group];
3459
- for (let i = 0; i < entries.length; i++) {
3460
- const entry = entries[i];
3461
- const entryLocation = `${location}[${i}]`;
3462
- const entryPath = importEntryPath(entry);
3463
- if (!entryPath) {
3464
- errors.push({
3465
- severity: "error",
3466
- filePath,
3467
- location: entryLocation,
3468
- message: "Invalid import entry. Use a path string or an object with a non-empty 'path'."
3469
- });
3470
- continue;
3471
- }
3472
- if (type === "tests" && !/\.eval\.ya?ml$/i.test(entryPath)) {
3473
- await validateRawCaseImportPath(entryPath, filePath, `${entryLocation}.path`, errors);
3474
- }
3475
- if (isObject(entry)) {
3476
- for (const key of Object.keys(entry)) {
3477
- if (!KNOWN_IMPORT_FIELDS.has(key)) {
3478
- errors.push({
3479
- severity: "warning",
3480
- filePath,
3481
- location: `${entryLocation}.${key}`,
3482
- message: `Unknown field '${key}'. This field will be ignored.`
3483
- });
3484
- }
3485
- }
3486
- validateIncludeSelect(entry.select, `${entryLocation}.select`, filePath, errors);
3487
- validateRunOverride(entry.run, `${entryLocation}.run`, filePath, errors);
3488
- }
3489
- }
3490
- }
3491
3580
  function collectImportEntries(parsed) {
3492
3581
  const entries = [];
3493
- if (isObject(parsed.imports)) {
3494
- entries.push(...collectImportGroup(parsed.imports.suites, "suite", "imports.suites"));
3495
- entries.push(...collectImportGroup(parsed.imports.tests, "tests", "imports.tests"));
3496
- }
3497
3582
  const tests = parsed.tests;
3498
3583
  if (Array.isArray(tests)) {
3499
3584
  for (let i = 0; i < tests.length; i++) {
@@ -3516,42 +3601,24 @@ function collectImportEntries(parsed) {
3516
3601
  }
3517
3602
  return entries;
3518
3603
  }
3519
- function collectImportGroup(group, type, location) {
3520
- if (group === void 0) {
3521
- return [];
3522
- }
3523
- const entries = Array.isArray(group) ? group : [group];
3524
- return entries.flatMap((entry, index) => {
3525
- const pathValue = importEntryPath(entry);
3526
- return pathValue ? [
3527
- {
3528
- path: pathValue,
3529
- type,
3530
- location: `${location}[${index}].path`,
3531
- ...isObject(entry) && entry.select !== void 0 ? { select: entry.select } : {},
3532
- ...isObject(entry) && entry.run !== void 0 ? { run: entry.run } : {}
3533
- }
3534
- ] : [];
3535
- });
3536
- }
3537
3604
  async function validateCompositionDiagnostics(filePath, parsed, errors) {
3538
- const imports = collectImportEntries(parsed);
3539
- if (imports.length === 0) {
3605
+ const includeEntries = collectImportEntries(parsed);
3606
+ if (includeEntries.length === 0) {
3540
3607
  return;
3541
3608
  }
3542
3609
  const parentHasRuntime = hasWrapperRuntimeControls(parsed);
3543
- const hasSuiteImport = imports.some((entry) => entry.type === "suite");
3610
+ const hasSuiteImport = includeEntries.some((entry) => entry.type === "suite");
3544
3611
  if (hasSuiteImport) {
3545
3612
  for (const location of parentWorkspaceLocations(parsed)) {
3546
3613
  errors.push({
3547
3614
  severity: "error",
3548
3615
  filePath,
3549
3616
  location,
3550
- message: "Parent workspace is not allowed when an eval imports suites with type: suite. A wrapper eval owns target and run controls, while imported suites own task environment. Move workspace into the child suite, or import raw cases with type: tests when you intentionally want parent workspace context."
3617
+ message: location === "environment" ? "Parent environment is not allowed with legacy tests[].include suite entries. Run eval files directly, or use tests: file://... for raw cases that should use the parent environment." : "Parent workspace is not allowed with legacy tests[].include suite entries. Run eval files directly, or use tests: file://... for raw cases that should use the parent workspace."
3551
3618
  });
3552
3619
  }
3553
3620
  }
3554
- for (const entry of imports) {
3621
+ for (const entry of includeEntries) {
3555
3622
  const resolvedSuites = await resolveSuiteIncludePaths(entry.path, path22.dirname(filePath));
3556
3623
  if (entry.type === "suite") {
3557
3624
  for (const resolvedSuite of resolvedSuites) {
@@ -3567,7 +3634,7 @@ async function validateCompositionDiagnostics(filePath, parsed, errors) {
3567
3634
  severity: "warning",
3568
3635
  filePath,
3569
3636
  location: entry.location,
3570
- message: parentHasRuntime ? `Imported suite '${resolvedSuite.displayPath}' defines ${runtimeFields.join(", ")}, but child target and run controls are ignored for imports.suites. The parent eval owns wrapper target and run controls; move them to the parent eval or use import run overrides for per-case thresholds, timeouts, and budgets.` : `Imported suite '${resolvedSuite.displayPath}' defines ${runtimeFields.join(", ")}, but child target and run controls are ignored for imports.suites. The parent eval owns wrapper target and run controls, and this parent has none, so no child target or run controls are applied. Add parent target/run controls or use import run overrides for per-case thresholds, timeouts, and budgets.`
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.`
3571
3638
  });
3572
3639
  }
3573
3640
  continue;
@@ -3581,7 +3648,7 @@ async function validateCompositionDiagnostics(filePath, parsed, errors) {
3581
3648
  severity: "warning",
3582
3649
  filePath,
3583
3650
  location: entry.location,
3584
- message: `imports.tests imports raw cases from eval suite '${resolvedSuite.displayPath}' and drops suite context, including child workspace, input, assertions, metadata, target, and run controls. Parent suite context applies. Use imports.suites to preserve child test and workspace semantics.`
3651
+ message: `Legacy tests[].include with type: tests imports raw cases from eval suite '${resolvedSuite.displayPath}' and drops suite context, including child environment, workspace, input, assertions, metadata, target, and run controls. Prefer tests: file://... for raw case files.`
3585
3652
  });
3586
3653
  }
3587
3654
  }
@@ -3620,6 +3687,9 @@ function parentWorkspaceLocations(parsed) {
3620
3687
  if (parsed.workspace !== void 0) {
3621
3688
  locations.push("workspace");
3622
3689
  }
3690
+ if (parsed.environment !== void 0) {
3691
+ locations.push("environment");
3692
+ }
3623
3693
  if (isObject(parsed.execution) && parsed.execution.workspace !== void 0) {
3624
3694
  locations.push("execution.workspace");
3625
3695
  }
@@ -3634,12 +3704,11 @@ async function readImportedSuite(filePath) {
3634
3704
  }
3635
3705
  }
3636
3706
  function validateIncludeEntry(entry, location, filePath, errors) {
3637
- const mode = entry.type === "suite" ? "suites" : entry.type === "tests" ? "tests" : void 0;
3638
3707
  errors.push({
3639
3708
  severity: "warning",
3640
3709
  filePath,
3641
3710
  location,
3642
- message: mode ? `tests[].include is deprecated. Use imports.${mode} entries with path instead.` : "tests[].include is deprecated. Use imports.suites or imports.tests entries with path instead."
3711
+ message: entry.type === "suite" ? "tests[].include with type: suite is deprecated. Run eval files directly with CLI multi-file selection and tags instead." : "tests[].include is deprecated. Use tests: file://... or string entries inside tests: for raw case files."
3643
3712
  });
3644
3713
  for (const key of Object.keys(entry)) {
3645
3714
  if (!KNOWN_INCLUDE_FIELDS.has(key)) {
@@ -4041,35 +4110,18 @@ async function validateWorkspaceConfig(workspace, evalFilePath, errors, location
4041
4110
  if (workspace === void 0) {
4042
4111
  return;
4043
4112
  }
4044
- if (isObject(workspace)) {
4045
- validateWorkspaceRepoConfig(workspace, evalFilePath, errors, location);
4046
- return;
4047
- }
4048
- if (typeof workspace !== "string") {
4049
- return;
4050
- }
4051
- const workspacePath = path22.resolve(path22.dirname(evalFilePath), workspace);
4052
- try {
4053
- const workspaceContent = await readFile22(workspacePath, "utf8");
4054
- const parsedWorkspace = interpolateEnv(parseYamlValue(workspaceContent), process.env);
4055
- if (!isObject(parsedWorkspace)) {
4056
- errors.push({
4057
- severity: "error",
4058
- filePath: evalFilePath,
4059
- location,
4060
- message: `External workspace file must contain a YAML object: ${workspace}`
4061
- });
4062
- return;
4063
- }
4064
- validateWorkspaceRepoConfig(parsedWorkspace, workspacePath, errors, location);
4065
- } catch (error) {
4066
- const message = error instanceof Error ? error.message : String(error);
4113
+ if (typeof workspace === "string") {
4067
4114
  errors.push({
4068
4115
  severity: "error",
4069
4116
  filePath: evalFilePath,
4070
4117
  location,
4071
- message: `Failed to load external workspace file '${workspace}': ${message}`
4118
+ message: `${location} has been removed from public eval YAML. Use environment: file://... for reusable coding-agent testbed recipes.`
4072
4119
  });
4120
+ return;
4121
+ }
4122
+ if (isObject(workspace)) {
4123
+ validateWorkspaceRepoConfig(workspace, evalFilePath, errors, location);
4124
+ return;
4073
4125
  }
4074
4126
  }
4075
4127
  function validateWorkspaceRepoConfig(workspace, filePath, errors, location) {
@@ -4078,12 +4130,79 @@ function validateWorkspaceRepoConfig(workspace, filePath, errors, location) {
4078
4130
  const afterEachHook = isObject(hooks) ? hooks.after_each : void 0;
4079
4131
  const scope = workspace.scope;
4080
4132
  const docker = workspace.docker;
4133
+ let hasRemovedTestbedField = false;
4134
+ if (Object.keys(workspace).length === 0) {
4135
+ errors.push({
4136
+ severity: "error",
4137
+ filePath,
4138
+ location,
4139
+ message: "workspace has been removed from public eval YAML. Use environment for coding-agent testbed setup."
4140
+ });
4141
+ return;
4142
+ }
4143
+ if ("template" in workspace) {
4144
+ hasRemovedTestbedField = true;
4145
+ errors.push({
4146
+ severity: "error",
4147
+ filePath,
4148
+ location: `${location}.template`,
4149
+ message: "workspace.template has been removed from public eval YAML. Use environment.workdir and environment.setup for authored testbed setup."
4150
+ });
4151
+ }
4152
+ if ("repos" in workspace) {
4153
+ hasRemovedTestbedField = true;
4154
+ errors.push({
4155
+ severity: "error",
4156
+ filePath,
4157
+ location: `${location}.repos`,
4158
+ message: "workspace.repos has been removed from public eval YAML. Use environment.setup.command argv to materialize repositories."
4159
+ });
4160
+ }
4161
+ if ("scope" in workspace) {
4162
+ hasRemovedTestbedField = true;
4163
+ errors.push({
4164
+ severity: "error",
4165
+ filePath,
4166
+ location: `${location}.scope`,
4167
+ message: "workspace.scope has been removed from public eval YAML. Use environment at suite/test scope and let runtime manage workspace lifetime."
4168
+ });
4169
+ }
4170
+ if ("docker" in workspace) {
4171
+ hasRemovedTestbedField = true;
4172
+ errors.push({
4173
+ severity: "error",
4174
+ filePath,
4175
+ location: `${location}.docker`,
4176
+ message: "workspace.docker has been removed from public eval YAML. Use environment.type: docker with image or context."
4177
+ });
4178
+ }
4179
+ if ("hooks" in workspace) {
4180
+ hasRemovedTestbedField = true;
4181
+ errors.push({
4182
+ severity: "error",
4183
+ filePath,
4184
+ location: `${location}.hooks`,
4185
+ message: "workspace.hooks has been removed from public eval YAML. Use environment.setup for testbed setup and extensions for lifecycle instrumentation."
4186
+ });
4187
+ }
4188
+ if ("env" in workspace) {
4189
+ hasRemovedTestbedField = true;
4190
+ errors.push({
4191
+ severity: "error",
4192
+ filePath,
4193
+ location: `${location}.env`,
4194
+ message: "workspace.env has been removed from public eval YAML. Use top-level env for provider/eval variables or environment.env for environment-scoped variables."
4195
+ });
4196
+ }
4197
+ if (hasRemovedTestbedField) {
4198
+ return;
4199
+ }
4081
4200
  if ("mode" in workspace) {
4082
4201
  errors.push({
4083
4202
  severity: "error",
4084
4203
  filePath,
4085
4204
  location: `${location}.mode`,
4086
- message: "workspace.mode has been removed from eval YAML. Use workspace.scope: suite|attempt."
4205
+ message: "workspace.mode has been removed from eval YAML. Use --workspace-path or execution.workspace_path for machine-local existing directories, and environment for portable testbed setup."
4087
4206
  });
4088
4207
  }
4089
4208
  if ("isolation" in workspace) {
@@ -4091,7 +4210,7 @@ function validateWorkspaceRepoConfig(workspace, filePath, errors, location) {
4091
4210
  severity: "error",
4092
4211
  filePath,
4093
4212
  location: `${location}.isolation`,
4094
- message: "workspace.isolation has been removed. Use workspace.scope: suite|attempt."
4213
+ message: "workspace.isolation has been removed from eval YAML. Use environment at suite/test scope and let AgentV manage runtime isolation."
4095
4214
  });
4096
4215
  }
4097
4216
  if ("path" in workspace) {
@@ -4099,7 +4218,18 @@ function validateWorkspaceRepoConfig(workspace, filePath, errors, location) {
4099
4218
  severity: "error",
4100
4219
  filePath,
4101
4220
  location: `${location}.path`,
4102
- message: "workspace.path has been removed from eval YAML. Put existing workspace paths in .agentv/config.local.yaml execution.workspace_path or pass --workspace-path."
4221
+ message: "workspace.path has been removed from eval YAML. Use environment.workdir for portable testbed cwd; put existing machine-local workspace paths in .agentv/config.local.yaml execution.workspace_path or pass --workspace-path."
4222
+ });
4223
+ }
4224
+ for (const key of Object.keys(workspace)) {
4225
+ if (KNOWN_REMOVED_WORKSPACE_FIELDS.has(key)) {
4226
+ continue;
4227
+ }
4228
+ errors.push({
4229
+ severity: "error",
4230
+ filePath,
4231
+ location: `${location}.${key}`,
4232
+ message: `${location}.${key} has been removed from public eval YAML. Use environment for portable coding-agent testbed setup.`
4103
4233
  });
4104
4234
  }
4105
4235
  if (scope !== void 0 && scope !== "suite" && scope !== "attempt") {
@@ -4412,11 +4542,11 @@ async function validateSuiteImportCycles(filePath, parsed, errors) {
4412
4542
  await validateSuiteImportCyclesFromParsed(filePath, parsed, [root], errors);
4413
4543
  }
4414
4544
  async function validateSuiteImportCyclesFromParsed(currentFilePath, parsed, stack, errors) {
4415
- const imports = collectImportEntries(parsed);
4416
- if (imports.length === 0) {
4545
+ const includeEntries = collectImportEntries(parsed);
4546
+ if (includeEntries.length === 0) {
4417
4547
  return;
4418
4548
  }
4419
- for (const entry of imports) {
4549
+ for (const entry of includeEntries) {
4420
4550
  if (entry.type !== "suite") {
4421
4551
  continue;
4422
4552
  }
@@ -4529,6 +4659,16 @@ function validateAssertArray(assertField, location, filePath, errors, customAsse
4529
4659
  continue;
4530
4660
  }
4531
4661
  const typeValue = rawTypeValue.replace(/_/g, "-");
4662
+ const staleMessage = staleAuthoredAssertionMessage(item);
4663
+ if (staleMessage) {
4664
+ errors.push({
4665
+ severity: "error",
4666
+ filePath,
4667
+ location: `${itemLocation}.type`,
4668
+ message: staleMessage
4669
+ });
4670
+ continue;
4671
+ }
4532
4672
  const replacement = REMOVED_ASSERTION_TYPE_REPLACEMENTS.get(typeValue);
4533
4673
  if (replacement) {
4534
4674
  errors.push({
@@ -4549,7 +4689,7 @@ function validateAssertArray(assertField, location, filePath, errors, customAsse
4549
4689
  });
4550
4690
  continue;
4551
4691
  }
4552
- if (!isGraderKind(typeValue) && !PROMPTFOO_ASSERTION_TYPES.has(typeValue) && !customAssertionTypes.has(typeValue)) {
4692
+ if (!isGraderKind(typeValue) && !isGraderKind(baseTypeValue) && !PROMPTFOO_ASSERTION_TYPES.has(typeValue) && !customAssertionTypes.has(typeValue)) {
4553
4693
  errors.push({
4554
4694
  severity: "warning",
4555
4695
  filePath,
@@ -5092,7 +5232,7 @@ function validateUnknownSettings(target, provider, absolutePath, location, error
5092
5232
  severity: "warning",
5093
5233
  filePath: absolutePath,
5094
5234
  location: `${location}.${key}`,
5095
- message: "workspace_template has been removed from targets. Use eval-level workspace.template instead."
5235
+ message: "workspace_template has been removed from targets. Use eval-level environment.workdir and environment.setup instead."
5096
5236
  });
5097
5237
  continue;
5098
5238
  }
@@ -7445,7 +7585,7 @@ async function runEvalCommand(input) {
7445
7585
  const resolvedExperiment = resolveExperimentForRun(options.experiment);
7446
7586
  const evalPathInputs = input.testFiles.length > 0 ? [...input.testFiles] : [];
7447
7587
  if (evalPathInputs.length === 0 && process.stdin.isTTY) {
7448
- const { launchInteractiveWizard } = await import("./interactive-V4SEHLNY.js");
7588
+ const { launchInteractiveWizard } = await import("./interactive-RUY3OCBI.js");
7449
7589
  await launchInteractiveWizard();
7450
7590
  return void 0;
7451
7591
  }
@@ -7785,7 +7925,7 @@ async function runEvalCommand(input) {
7785
7925
  });
7786
7926
  let transcriptProviderFactory;
7787
7927
  if (options.transcript) {
7788
- const { TranscriptProvider } = await import("./dist-2D2GBHZV.js");
7928
+ const { TranscriptProvider } = await import("./dist-6Z7U473R.js");
7789
7929
  const transcriptProvider = await TranscriptProvider.fromFile(options.transcript);
7790
7930
  const totalTests = [...fileMetadata.values()].reduce(
7791
7931
  (sum, meta) => sum + meta.testCases.length,
@@ -8051,7 +8191,7 @@ ${formatRequiredVersionFailureNote(requiredVersionCheck)}`);
8051
8191
  const sourceTests = activeSourceTests;
8052
8192
  const taskBundleTargets = buildTaskBundleTargetSelections(activeTestFiles, fileMetadata);
8053
8193
  if (isResumeAppend) {
8054
- const { writePerTestArtifacts } = await import("./artifact-writer-IORG7CW5.js");
8194
+ const { writePerTestArtifacts } = await import("./artifact-writer-JFNIPMKW.js");
8055
8195
  await writePerTestArtifacts(allResults, runDir, {
8056
8196
  experiment: normalizeExperimentName(options.experiment),
8057
8197
  runId: path12.basename(runDir),
@@ -8269,4 +8409,4 @@ export {
8269
8409
  filterByCategory,
8270
8410
  runEvalCommand
8271
8411
  };
8272
- //# sourceMappingURL=chunk-AM2IPHKS.js.map
8412
+ //# sourceMappingURL=chunk-FVR4RQFK.js.map