@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.
- package/dist/evaluation/validation/index.cjs +295 -50
- package/dist/evaluation/validation/index.cjs.map +1 -1
- package/dist/evaluation/validation/index.d.cts +29 -2
- package/dist/evaluation/validation/index.d.ts +29 -2
- package/dist/evaluation/validation/index.js +276 -33
- package/dist/evaluation/validation/index.js.map +1 -1
- package/dist/index.cjs +4 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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/
|
|
1014
|
+
// src/evaluation/validation/cases-validator.ts
|
|
948
1015
|
var import_promises4 = require("fs/promises");
|
|
949
|
-
var
|
|
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
|
|
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
|
|
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 =
|
|
1531
|
+
const absolutePath = import_node_path6.default.resolve(filePath);
|
|
1389
1532
|
let parsed;
|
|
1390
1533
|
try {
|
|
1391
|
-
const content = await (0,
|
|
1392
|
-
parsed = (0,
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
|
1606
|
-
var
|
|
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,
|
|
1611
|
-
const parsed = (0,
|
|
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
|
|
1748
|
-
var
|
|
1749
|
-
var
|
|
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
|
|
1754
|
-
var
|
|
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,
|
|
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 =
|
|
1765
|
-
const 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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 (
|
|
1810
|
-
potentialPaths.push(
|
|
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(
|
|
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 =
|
|
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
|
|
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 =
|
|
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,
|
|
1851
|
-
parsed = (0,
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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,
|
|
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
|