@agentv/core 4.15.8-next.1 → 4.15.9-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.
@@ -33,10 +33,12 @@ __export(validation_exports, {
33
33
  detectFileType: () => detectFileType,
34
34
  getExpectedSchema: () => getExpectedSchema,
35
35
  isValidSchema: () => isValidSchema,
36
+ validateCasesFile: () => validateCasesFile,
36
37
  validateConfigFile: () => validateConfigFile,
37
38
  validateEvalFile: () => validateEvalFile,
38
39
  validateFileReferences: () => validateFileReferences,
39
- validateTargetsFile: () => validateTargetsFile
40
+ validateTargetsFile: () => validateTargetsFile,
41
+ validateWorkspacePaths: () => validateWorkspacePaths
40
42
  });
41
43
  module.exports = __toCommonJS(validation_exports);
42
44
 
@@ -51,6 +53,9 @@ async function detectFileType(filePath) {
51
53
  try {
52
54
  const content = await (0, import_promises.readFile)(filePath, "utf8");
53
55
  const parsed = (0, import_yaml.parse)(content);
56
+ if (Array.isArray(parsed)) {
57
+ return "cases";
58
+ }
54
59
  if (typeof parsed !== "object" || parsed === null) {
55
60
  return inferFileTypeFromPath(filePath);
56
61
  }
@@ -84,7 +89,11 @@ function inferFileTypeFromPath(filePath) {
84
89
  return "targets";
85
90
  }
86
91
  }
87
- return "eval";
92
+ const lower = basename.toLowerCase();
93
+ if (lower.endsWith(".eval.yaml") || lower.endsWith(".eval.yml")) {
94
+ return "eval";
95
+ }
96
+ return "unknown";
88
97
  }
89
98
  function isValidSchema(schema) {
90
99
  return schema === SCHEMA_EVAL_V2 || schema === SCHEMA_TARGETS_V2 || schema === SCHEMA_CONFIG_V2;
@@ -272,6 +281,7 @@ var KNOWN_TOP_LEVEL_FIELDS = /* @__PURE__ */ new Set([
272
281
  "$schema",
273
282
  "name",
274
283
  "description",
284
+ "category",
275
285
  "version",
276
286
  "author",
277
287
  "tags",
@@ -280,13 +290,19 @@ var KNOWN_TOP_LEVEL_FIELDS = /* @__PURE__ */ new Set([
280
290
  "input",
281
291
  "input_files",
282
292
  "tests",
283
- "eval_cases",
284
293
  "target",
285
294
  "execution",
286
295
  "assertions",
287
296
  "evaluators",
297
+ "preprocessors",
288
298
  "workspace"
289
299
  ]);
300
+ var DEPRECATED_TOP_LEVEL_FIELDS = /* @__PURE__ */ new Map([
301
+ ["eval_cases", "'eval_cases' is deprecated. Use 'tests' instead."],
302
+ ["evalcases", "'evalcases' is deprecated. Use 'tests' instead."],
303
+ ["evaluator", "'evaluator' is deprecated. Use 'assertions' instead."],
304
+ ["assert", "'assert' is deprecated. Use 'assertions' instead."]
305
+ ]);
290
306
  var KNOWN_TEST_FIELDS = /* @__PURE__ */ new Set([
291
307
  "id",
292
308
  "criteria",
@@ -295,12 +311,12 @@ var KNOWN_TEST_FIELDS = /* @__PURE__ */ new Set([
295
311
  "expected_output",
296
312
  "assertions",
297
313
  "evaluators",
314
+ "rubrics",
298
315
  "execution",
299
316
  "workspace",
300
317
  "metadata",
301
318
  "conversation_id",
302
319
  "suite",
303
- "note",
304
320
  "depends_on",
305
321
  "on_dependency_failure",
306
322
  "mode",
@@ -309,13 +325,48 @@ var KNOWN_TEST_FIELDS = /* @__PURE__ */ new Set([
309
325
  "on_turn_failure",
310
326
  "window_size"
311
327
  ]);
328
+ var DEPRECATED_TEST_FIELDS = /* @__PURE__ */ new Map([
329
+ ["evaluator", "'evaluator' is deprecated. Use 'assertions' instead."],
330
+ ["assert", "'assert' is deprecated. Use 'assertions' instead."],
331
+ ["expected_outcome", "'expected_outcome' is deprecated. Use 'criteria' instead."]
332
+ ]);
312
333
  var NAME_PATTERN = /^[a-z0-9-]+$/;
334
+ var ASSERTION_SCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".js", ".mts", ".mjs", ".cts", ".cjs"]);
335
+ var customAssertionCache = /* @__PURE__ */ new Map();
336
+ function discoverCustomAssertionTypes(baseDir) {
337
+ const resolved = import_node_path3.default.resolve(baseDir);
338
+ const cached = customAssertionCache.get(resolved);
339
+ if (cached) return cached;
340
+ const promise = (async () => {
341
+ const types = /* @__PURE__ */ new Set();
342
+ let dir = resolved;
343
+ const root = import_node_path3.default.parse(dir).root;
344
+ while (dir !== root) {
345
+ const assertionsDir = import_node_path3.default.join(dir, ".agentv", "assertions");
346
+ try {
347
+ const entries = await (0, import_promises3.readdir)(assertionsDir, { withFileTypes: true });
348
+ for (const entry of entries) {
349
+ if (!entry.isFile()) continue;
350
+ const ext = import_node_path3.default.extname(entry.name).toLowerCase();
351
+ if (!ASSERTION_SCRIPT_EXTENSIONS.has(ext)) continue;
352
+ types.add(entry.name.slice(0, -ext.length));
353
+ }
354
+ } catch {
355
+ }
356
+ dir = import_node_path3.default.dirname(dir);
357
+ }
358
+ return types;
359
+ })();
360
+ customAssertionCache.set(resolved, promise);
361
+ return promise;
362
+ }
313
363
  function isObject(value) {
314
364
  return typeof value === "object" && value !== null && !Array.isArray(value);
315
365
  }
316
366
  async function validateEvalFile(filePath) {
317
367
  const errors = [];
318
368
  const absolutePath = import_node_path3.default.resolve(filePath);
369
+ const customAssertionTypes = await discoverCustomAssertionTypes(import_node_path3.default.dirname(absolutePath));
319
370
  let parsed;
320
371
  try {
321
372
  const content = await (0, import_promises3.readFile)(absolutePath, "utf8");
@@ -348,7 +399,15 @@ async function validateEvalFile(filePath) {
348
399
  }
349
400
  validateMetadata(parsed, absolutePath, errors);
350
401
  for (const key of Object.keys(parsed)) {
351
- if (!KNOWN_TOP_LEVEL_FIELDS.has(key)) {
402
+ const deprecationMessage = DEPRECATED_TOP_LEVEL_FIELDS.get(key);
403
+ if (deprecationMessage) {
404
+ errors.push({
405
+ severity: "warning",
406
+ filePath: absolutePath,
407
+ location: key,
408
+ message: deprecationMessage
409
+ });
410
+ } else if (!KNOWN_TOP_LEVEL_FIELDS.has(key)) {
352
411
  errors.push({
353
412
  severity: "warning",
354
413
  filePath: absolutePath,
@@ -446,7 +505,15 @@ async function validateEvalFile(filePath) {
446
505
  continue;
447
506
  }
448
507
  for (const key of Object.keys(evalCase)) {
449
- if (!KNOWN_TEST_FIELDS.has(key)) {
508
+ const deprecationMessage = DEPRECATED_TEST_FIELDS.get(key);
509
+ if (deprecationMessage) {
510
+ errors.push({
511
+ severity: "warning",
512
+ filePath: absolutePath,
513
+ location: `${location}.${key}`,
514
+ message: deprecationMessage
515
+ });
516
+ } else if (!KNOWN_TEST_FIELDS.has(key)) {
450
517
  errors.push({
451
518
  severity: "warning",
452
519
  filePath: absolutePath,
@@ -518,7 +585,7 @@ async function validateEvalFile(filePath) {
518
585
  }
519
586
  const assertField = evalCase.assertions;
520
587
  if (assertField !== void 0) {
521
- validateAssertArray(assertField, location, absolutePath, errors);
588
+ validateAssertArray(assertField, location, absolutePath, errors, customAssertionTypes);
522
589
  }
523
590
  validateConversationMode(evalCase, location, absolutePath, errors);
524
591
  await validateWorkspaceConfig(
@@ -729,7 +796,7 @@ function validateTestsStringPath(testsPath, filePath, errors) {
729
796
  });
730
797
  }
731
798
  }
732
- function validateAssertArray(assertField, parentLocation, filePath, errors) {
799
+ function validateAssertArray(assertField, parentLocation, filePath, errors, customAssertionTypes = /* @__PURE__ */ new Set()) {
733
800
  if (!Array.isArray(assertField)) {
734
801
  errors.push({
735
802
  severity: "warning",
@@ -777,7 +844,7 @@ function validateAssertArray(assertField, parentLocation, filePath, errors) {
777
844
  continue;
778
845
  }
779
846
  const typeValue = rawTypeValue.replace(/_/g, "-");
780
- if (!isEvaluatorKind(typeValue)) {
847
+ if (!isEvaluatorKind(typeValue) && !customAssertionTypes.has(typeValue)) {
781
848
  errors.push({
782
849
  severity: "warning",
783
850
  filePath,
@@ -944,13 +1011,89 @@ function validateConversationMode(evalCase, location, filePath, errors) {
944
1011
  }
945
1012
  }
946
1013
 
947
- // src/evaluation/validation/targets-validator.ts
1014
+ // src/evaluation/validation/cases-validator.ts
948
1015
  var import_promises4 = require("fs/promises");
949
- var import_node_path5 = __toESM(require("path"), 1);
1016
+ var import_node_path4 = __toESM(require("path"), 1);
950
1017
  var import_yaml4 = require("yaml");
1018
+ function isObject2(value) {
1019
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1020
+ }
1021
+ async function validateCasesFile(filePath) {
1022
+ const errors = [];
1023
+ const absolutePath = import_node_path4.default.resolve(filePath);
1024
+ let parsed;
1025
+ try {
1026
+ const content = await (0, import_promises4.readFile)(absolutePath, "utf8");
1027
+ parsed = (0, import_yaml4.parse)(content);
1028
+ } catch (error) {
1029
+ errors.push({
1030
+ severity: "error",
1031
+ filePath: absolutePath,
1032
+ message: `Failed to parse YAML: ${error.message}`
1033
+ });
1034
+ return { valid: false, filePath: absolutePath, fileType: "cases", errors };
1035
+ }
1036
+ if (!Array.isArray(parsed)) {
1037
+ errors.push({
1038
+ severity: "error",
1039
+ filePath: absolutePath,
1040
+ message: "Cases file must contain a YAML array of test case objects"
1041
+ });
1042
+ return { valid: false, filePath: absolutePath, fileType: "cases", errors };
1043
+ }
1044
+ for (let i = 0; i < parsed.length; i++) {
1045
+ const item = parsed[i];
1046
+ const location = `[${i}]`;
1047
+ if (!isObject2(item)) {
1048
+ errors.push({
1049
+ severity: "error",
1050
+ filePath: absolutePath,
1051
+ location,
1052
+ message: "Each test case must be an object"
1053
+ });
1054
+ continue;
1055
+ }
1056
+ const id = item.id;
1057
+ if (typeof id !== "string" || id.trim().length === 0) {
1058
+ errors.push({
1059
+ severity: "error",
1060
+ filePath: absolutePath,
1061
+ location: `${location}.id`,
1062
+ message: "Missing or invalid 'id' field (must be a non-empty string)"
1063
+ });
1064
+ }
1065
+ const input = item.input;
1066
+ if (input === void 0) {
1067
+ errors.push({
1068
+ severity: "error",
1069
+ filePath: absolutePath,
1070
+ location: `${location}.input`,
1071
+ message: "Missing 'input' field (must be a string or array of messages)"
1072
+ });
1073
+ } else if (typeof input !== "string" && !Array.isArray(input)) {
1074
+ errors.push({
1075
+ severity: "error",
1076
+ filePath: absolutePath,
1077
+ location: `${location}.input`,
1078
+ message: "Invalid 'input' field (must be a string or array of messages)"
1079
+ });
1080
+ }
1081
+ }
1082
+ return {
1083
+ valid: errors.filter((e) => e.severity === "error").length === 0,
1084
+ filePath: absolutePath,
1085
+ fileType: "cases",
1086
+ errors
1087
+ };
1088
+ }
1089
+
1090
+ // src/evaluation/validation/targets-validator.ts
1091
+ var import_promises5 = require("fs/promises");
1092
+ var import_node_path6 = __toESM(require("path"), 1);
1093
+ var import_yaml5 = require("yaml");
951
1094
 
952
1095
  // src/evaluation/providers/targets.ts
953
- var import_node_path4 = __toESM(require("path"), 1);
1096
+ var import_node_path5 = __toESM(require("path"), 1);
954
1097
  var import_zod = require("zod");
955
1098
  var CliHealthcheckHttpInputSchema = import_zod.z.object({
956
1099
  url: import_zod.z.string().min(1, "healthcheck URL is required"),
@@ -1163,7 +1306,7 @@ var PROVIDER_ALIASES = [
1163
1306
  ];
1164
1307
 
1165
1308
  // src/evaluation/validation/targets-validator.ts
1166
- function isObject2(value) {
1309
+ function isObject3(value) {
1167
1310
  return typeof value === "object" && value !== null && !Array.isArray(value);
1168
1311
  }
1169
1312
  var COMMON_SETTINGS = new Set(COMMON_TARGET_SETTINGS);
@@ -1385,11 +1528,11 @@ function validateUnknownSettings(target, provider, absolutePath, location, error
1385
1528
  }
1386
1529
  async function validateTargetsFile(filePath) {
1387
1530
  const errors = [];
1388
- const absolutePath = import_node_path5.default.resolve(filePath);
1531
+ const absolutePath = import_node_path6.default.resolve(filePath);
1389
1532
  let parsed;
1390
1533
  try {
1391
- const content = await (0, import_promises4.readFile)(absolutePath, "utf8");
1392
- parsed = (0, import_yaml4.parse)(content);
1534
+ const content = await (0, import_promises5.readFile)(absolutePath, "utf8");
1535
+ parsed = (0, import_yaml5.parse)(content);
1393
1536
  } catch (error) {
1394
1537
  errors.push({
1395
1538
  severity: "error",
@@ -1421,7 +1564,7 @@ async function validateTargetsFile(filePath) {
1421
1564
  }
1422
1565
  }
1423
1566
  function validateCliHealthcheck(healthcheck, absolutePath2, location, errors2) {
1424
- if (!isObject2(healthcheck)) {
1567
+ if (!isObject3(healthcheck)) {
1425
1568
  errors2.push({
1426
1569
  severity: "error",
1427
1570
  filePath: absolutePath2,
@@ -1496,7 +1639,7 @@ async function validateTargetsFile(filePath) {
1496
1639
  }
1497
1640
  return result;
1498
1641
  }
1499
- if (!isObject2(parsed)) {
1642
+ if (!isObject3(parsed)) {
1500
1643
  errors.push({
1501
1644
  severity: "error",
1502
1645
  filePath: absolutePath,
@@ -1528,7 +1671,7 @@ async function validateTargetsFile(filePath) {
1528
1671
  for (let i = 0; i < targets.length; i++) {
1529
1672
  const target = targets[i];
1530
1673
  const location = `targets[${i}]`;
1531
- if (!isObject2(target)) {
1674
+ if (!isObject3(target)) {
1532
1675
  errors.push({
1533
1676
  severity: "error",
1534
1677
  filePath: absolutePath,
@@ -1602,13 +1745,13 @@ async function validateTargetsFile(filePath) {
1602
1745
  }
1603
1746
 
1604
1747
  // src/evaluation/validation/config-validator.ts
1605
- var import_promises5 = require("fs/promises");
1606
- var import_yaml5 = require("yaml");
1748
+ var import_promises6 = require("fs/promises");
1749
+ var import_yaml6 = require("yaml");
1607
1750
  async function validateConfigFile(filePath) {
1608
1751
  const errors = [];
1609
1752
  try {
1610
- const content = await (0, import_promises5.readFile)(filePath, "utf8");
1611
- const parsed = (0, import_yaml5.parse)(content);
1753
+ const content = await (0, import_promises6.readFile)(filePath, "utf8");
1754
+ const parsed = (0, import_yaml6.parse)(content);
1612
1755
  if (typeof parsed !== "object" || parsed === null) {
1613
1756
  errors.push({
1614
1757
  severity: "error",
@@ -1744,31 +1887,31 @@ async function validateConfigFile(filePath) {
1744
1887
  }
1745
1888
 
1746
1889
  // src/evaluation/validation/file-reference-validator.ts
1747
- var import_promises7 = require("fs/promises");
1748
- var import_node_path7 = __toESM(require("path"), 1);
1749
- var import_yaml6 = require("yaml");
1890
+ var import_promises8 = require("fs/promises");
1891
+ var import_node_path8 = __toESM(require("path"), 1);
1892
+ var import_yaml7 = require("yaml");
1750
1893
 
1751
1894
  // src/evaluation/file-utils.ts
1752
1895
  var import_node_fs = require("fs");
1753
- var import_promises6 = require("fs/promises");
1754
- var import_node_path6 = __toESM(require("path"), 1);
1896
+ var import_promises7 = require("fs/promises");
1897
+ var import_node_path7 = __toESM(require("path"), 1);
1755
1898
  async function fileExists(filePath) {
1756
1899
  try {
1757
- await (0, import_promises6.access)(filePath, import_node_fs.constants.F_OK);
1900
+ await (0, import_promises7.access)(filePath, import_node_fs.constants.F_OK);
1758
1901
  return true;
1759
1902
  } catch {
1760
1903
  return false;
1761
1904
  }
1762
1905
  }
1763
1906
  async function findGitRoot(startPath) {
1764
- let currentDir = import_node_path6.default.dirname(import_node_path6.default.resolve(startPath));
1765
- const root = import_node_path6.default.parse(currentDir).root;
1907
+ let currentDir = import_node_path7.default.dirname(import_node_path7.default.resolve(startPath));
1908
+ const root = import_node_path7.default.parse(currentDir).root;
1766
1909
  while (currentDir !== root) {
1767
- const gitPath = import_node_path6.default.join(currentDir, ".git");
1910
+ const gitPath = import_node_path7.default.join(currentDir, ".git");
1768
1911
  if (await fileExists(gitPath)) {
1769
1912
  return currentDir;
1770
1913
  }
1771
- const parentDir = import_node_path6.default.dirname(currentDir);
1914
+ const parentDir = import_node_path7.default.dirname(currentDir);
1772
1915
  if (parentDir === currentDir) {
1773
1916
  break;
1774
1917
  }
@@ -1779,16 +1922,16 @@ async function findGitRoot(startPath) {
1779
1922
  function buildSearchRoots(evalPath, repoRoot) {
1780
1923
  const uniqueRoots = [];
1781
1924
  const addRoot = (root) => {
1782
- const normalized = import_node_path6.default.resolve(root);
1925
+ const normalized = import_node_path7.default.resolve(root);
1783
1926
  if (!uniqueRoots.includes(normalized)) {
1784
1927
  uniqueRoots.push(normalized);
1785
1928
  }
1786
1929
  };
1787
- let currentDir = import_node_path6.default.dirname(evalPath);
1930
+ let currentDir = import_node_path7.default.dirname(evalPath);
1788
1931
  let reachedBoundary = false;
1789
1932
  while (!reachedBoundary) {
1790
1933
  addRoot(currentDir);
1791
- const parentDir = import_node_path6.default.dirname(currentDir);
1934
+ const parentDir = import_node_path7.default.dirname(currentDir);
1792
1935
  if (currentDir === repoRoot || parentDir === currentDir) {
1793
1936
  reachedBoundary = true;
1794
1937
  } else {
@@ -1806,16 +1949,16 @@ function trimLeadingSeparators(value) {
1806
1949
  async function resolveFileReference(rawValue, searchRoots) {
1807
1950
  const displayPath = trimLeadingSeparators(rawValue);
1808
1951
  const potentialPaths = [];
1809
- if (import_node_path6.default.isAbsolute(rawValue)) {
1810
- potentialPaths.push(import_node_path6.default.normalize(rawValue));
1952
+ if (import_node_path7.default.isAbsolute(rawValue)) {
1953
+ potentialPaths.push(import_node_path7.default.normalize(rawValue));
1811
1954
  }
1812
1955
  for (const base of searchRoots) {
1813
- potentialPaths.push(import_node_path6.default.resolve(base, displayPath));
1956
+ potentialPaths.push(import_node_path7.default.resolve(base, displayPath));
1814
1957
  }
1815
1958
  const attempted = [];
1816
1959
  const seen = /* @__PURE__ */ new Set();
1817
1960
  for (const candidate of potentialPaths) {
1818
- const absoluteCandidate = import_node_path6.default.resolve(candidate);
1961
+ const absoluteCandidate = import_node_path7.default.resolve(candidate);
1819
1962
  if (seen.has(absoluteCandidate)) {
1820
1963
  continue;
1821
1964
  }
@@ -1829,12 +1972,12 @@ async function resolveFileReference(rawValue, searchRoots) {
1829
1972
  }
1830
1973
 
1831
1974
  // src/evaluation/validation/file-reference-validator.ts
1832
- function isObject3(value) {
1975
+ function isObject4(value) {
1833
1976
  return typeof value === "object" && value !== null && !Array.isArray(value);
1834
1977
  }
1835
1978
  async function validateFileReferences(evalFilePath) {
1836
1979
  const errors = [];
1837
- const absolutePath = import_node_path7.default.resolve(evalFilePath);
1980
+ const absolutePath = import_node_path8.default.resolve(evalFilePath);
1838
1981
  const gitRoot = await findGitRoot(absolutePath);
1839
1982
  if (!gitRoot) {
1840
1983
  errors.push({
@@ -1847,12 +1990,12 @@ async function validateFileReferences(evalFilePath) {
1847
1990
  const searchRoots = buildSearchRoots(absolutePath, gitRoot);
1848
1991
  let parsed;
1849
1992
  try {
1850
- const content = await (0, import_promises7.readFile)(absolutePath, "utf8");
1851
- parsed = (0, import_yaml6.parse)(content);
1993
+ const content = await (0, import_promises8.readFile)(absolutePath, "utf8");
1994
+ parsed = (0, import_yaml7.parse)(content);
1852
1995
  } catch {
1853
1996
  return errors;
1854
1997
  }
1855
- if (!isObject3(parsed)) {
1998
+ if (!isObject4(parsed)) {
1856
1999
  return errors;
1857
2000
  }
1858
2001
  let cases = parsed.tests;
@@ -1867,7 +2010,7 @@ async function validateFileReferences(evalFilePath) {
1867
2010
  }
1868
2011
  for (let i = 0; i < cases.length; i++) {
1869
2012
  const evalCase = cases[i];
1870
- if (!isObject3(evalCase)) {
2013
+ if (!isObject4(evalCase)) {
1871
2014
  continue;
1872
2015
  }
1873
2016
  const inputField = evalCase.input;
@@ -1896,7 +2039,7 @@ async function validateFileReferences(evalFilePath) {
1896
2039
  async function validateMessagesFileRefs(messages, location, searchRoots, filePath, errors) {
1897
2040
  for (let i = 0; i < messages.length; i++) {
1898
2041
  const message = messages[i];
1899
- if (!isObject3(message)) {
2042
+ if (!isObject4(message)) {
1900
2043
  continue;
1901
2044
  }
1902
2045
  const content = message.content;
@@ -1908,7 +2051,7 @@ async function validateMessagesFileRefs(messages, location, searchRoots, filePat
1908
2051
  }
1909
2052
  for (let j = 0; j < content.length; j++) {
1910
2053
  const contentItem = content[j];
1911
- if (!isObject3(contentItem)) {
2054
+ if (!isObject4(contentItem)) {
1912
2055
  continue;
1913
2056
  }
1914
2057
  const type = contentItem.type;
@@ -1935,7 +2078,7 @@ async function validateMessagesFileRefs(messages, location, searchRoots, filePat
1935
2078
  });
1936
2079
  } else {
1937
2080
  try {
1938
- const fileContent = await (0, import_promises7.readFile)(resolvedPath, "utf8");
2081
+ const fileContent = await (0, import_promises8.readFile)(resolvedPath, "utf8");
1939
2082
  if (fileContent.trim().length === 0) {
1940
2083
  errors.push({
1941
2084
  severity: "warning",
@@ -1956,14 +2099,116 @@ async function validateMessagesFileRefs(messages, location, searchRoots, filePat
1956
2099
  }
1957
2100
  }
1958
2101
  }
2102
+
2103
+ // src/evaluation/validation/workspace-path-validator.ts
2104
+ var import_promises9 = require("fs/promises");
2105
+ var import_node_path9 = __toESM(require("path"), 1);
2106
+ var import_yaml8 = require("yaml");
2107
+ function isObject5(value) {
2108
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2109
+ }
2110
+ async function validateWorkspacePaths(evalFilePath) {
2111
+ const errors = [];
2112
+ const absolutePath = import_node_path9.default.resolve(evalFilePath);
2113
+ const evalDir = import_node_path9.default.dirname(absolutePath);
2114
+ let parsed;
2115
+ try {
2116
+ const content = await (0, import_promises9.readFile)(absolutePath, "utf8");
2117
+ parsed = (0, import_yaml8.parse)(content);
2118
+ } catch {
2119
+ return errors;
2120
+ }
2121
+ if (!isObject5(parsed)) return errors;
2122
+ const workspaceRaw = parsed.workspace;
2123
+ if (workspaceRaw === void 0 || workspaceRaw === null) return errors;
2124
+ if (typeof workspaceRaw === "string") {
2125
+ const workspaceFilePath = import_node_path9.default.resolve(evalDir, workspaceRaw);
2126
+ try {
2127
+ const wsContent = await (0, import_promises9.readFile)(workspaceFilePath, "utf8");
2128
+ const wsParsed = (0, import_yaml8.parse)(wsContent);
2129
+ if (isObject5(wsParsed)) {
2130
+ const wsDir = import_node_path9.default.dirname(workspaceFilePath);
2131
+ await validateWorkspaceObject(wsParsed, wsDir, absolutePath, "workspace", errors);
2132
+ }
2133
+ } catch {
2134
+ }
2135
+ } else if (isObject5(workspaceRaw)) {
2136
+ await validateWorkspaceObject(workspaceRaw, evalDir, absolutePath, "workspace", errors);
2137
+ }
2138
+ return errors;
2139
+ }
2140
+ async function validateWorkspaceObject(obj, baseDir, evalFilePath, location, errors) {
2141
+ const template = obj.template;
2142
+ if (typeof template === "string") {
2143
+ const templatePath = import_node_path9.default.isAbsolute(template) ? template : import_node_path9.default.resolve(baseDir, template);
2144
+ if (!await fileExists2(templatePath)) {
2145
+ errors.push({
2146
+ severity: "error",
2147
+ filePath: evalFilePath,
2148
+ location: `${location}.template`,
2149
+ message: `Template path not found: ${template} (resolved to ${templatePath})`
2150
+ });
2151
+ }
2152
+ }
2153
+ const hooks = obj.hooks;
2154
+ if (!isObject5(hooks)) return;
2155
+ for (const hookName of ["before_all", "before_each", "after_each", "after_all"]) {
2156
+ const hook = hooks[hookName];
2157
+ if (!isObject5(hook)) continue;
2158
+ const hookCwdRaw = typeof hook.cwd === "string" ? hook.cwd : void 0;
2159
+ const hookCwd = hookCwdRaw ? import_node_path9.default.isAbsolute(hookCwdRaw) ? hookCwdRaw : import_node_path9.default.resolve(baseDir, hookCwdRaw) : baseDir;
2160
+ const command = hook.command ?? hook.script;
2161
+ if (!Array.isArray(command)) continue;
2162
+ for (let i = 0; i < command.length; i++) {
2163
+ const arg = command[i];
2164
+ if (typeof arg !== "string") continue;
2165
+ if (!looksLikeFilePath(arg)) continue;
2166
+ const resolved = import_node_path9.default.isAbsolute(arg) ? arg : import_node_path9.default.resolve(hookCwd, arg);
2167
+ if (!await fileExists2(resolved)) {
2168
+ errors.push({
2169
+ severity: "error",
2170
+ filePath: evalFilePath,
2171
+ location: `${location}.hooks.${hookName}.command[${i}]`,
2172
+ message: `Hook script not found: ${arg} (resolved to ${resolved})`
2173
+ });
2174
+ }
2175
+ }
2176
+ }
2177
+ }
2178
+ function looksLikeFilePath(arg) {
2179
+ if (arg.startsWith("./") || arg.startsWith("../")) return true;
2180
+ const scriptExtensions = [
2181
+ ".mjs",
2182
+ ".cjs",
2183
+ ".js",
2184
+ ".ts",
2185
+ ".sh",
2186
+ ".bash",
2187
+ ".zsh",
2188
+ ".py",
2189
+ ".rb",
2190
+ ".pl"
2191
+ ];
2192
+ return scriptExtensions.some((ext) => arg.endsWith(ext));
2193
+ }
2194
+ async function fileExists2(filePath) {
2195
+ try {
2196
+ await (0, import_promises9.access)(filePath);
2197
+ return true;
2198
+ } catch {
2199
+ return false;
2200
+ }
2201
+ }
1959
2202
  // Annotate the CommonJS export names for ESM import in node:
1960
2203
  0 && (module.exports = {
1961
2204
  detectFileType,
1962
2205
  getExpectedSchema,
1963
2206
  isValidSchema,
2207
+ validateCasesFile,
1964
2208
  validateConfigFile,
1965
2209
  validateEvalFile,
1966
2210
  validateFileReferences,
1967
- validateTargetsFile
2211
+ validateTargetsFile,
2212
+ validateWorkspacePaths
1968
2213
  });
1969
2214
  //# sourceMappingURL=index.cjs.map