@agentv/core 0.2.3

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.
@@ -0,0 +1,617 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/evaluation/validation/index.ts
31
+ var validation_exports = {};
32
+ __export(validation_exports, {
33
+ detectFileType: () => detectFileType,
34
+ getExpectedSchema: () => getExpectedSchema,
35
+ isValidSchema: () => isValidSchema,
36
+ validateEvalFile: () => validateEvalFile,
37
+ validateFileReferences: () => validateFileReferences,
38
+ validateTargetsFile: () => validateTargetsFile
39
+ });
40
+ module.exports = __toCommonJS(validation_exports);
41
+
42
+ // src/evaluation/validation/file-type.ts
43
+ var import_promises = require("fs/promises");
44
+ var import_yaml = require("yaml");
45
+ var SCHEMA_EVAL_V2 = "agentv-eval-v2";
46
+ var SCHEMA_TARGETS_V2 = "agentv-targets-v2";
47
+ async function detectFileType(filePath) {
48
+ try {
49
+ const content = await (0, import_promises.readFile)(filePath, "utf8");
50
+ const parsed = (0, import_yaml.parse)(content);
51
+ if (typeof parsed !== "object" || parsed === null) {
52
+ return "unknown";
53
+ }
54
+ const record = parsed;
55
+ const schema = record["$schema"];
56
+ if (typeof schema !== "string") {
57
+ return "unknown";
58
+ }
59
+ switch (schema) {
60
+ case SCHEMA_EVAL_V2:
61
+ return "eval";
62
+ case SCHEMA_TARGETS_V2:
63
+ return "targets";
64
+ default:
65
+ return "unknown";
66
+ }
67
+ } catch {
68
+ return "unknown";
69
+ }
70
+ }
71
+ function isValidSchema(schema) {
72
+ return schema === SCHEMA_EVAL_V2 || schema === SCHEMA_TARGETS_V2;
73
+ }
74
+ function getExpectedSchema(fileType) {
75
+ switch (fileType) {
76
+ case "eval":
77
+ return SCHEMA_EVAL_V2;
78
+ case "targets":
79
+ return SCHEMA_TARGETS_V2;
80
+ default:
81
+ return void 0;
82
+ }
83
+ }
84
+
85
+ // src/evaluation/validation/eval-validator.ts
86
+ var import_promises2 = require("fs/promises");
87
+ var import_node_path = __toESM(require("path"), 1);
88
+ var import_yaml2 = require("yaml");
89
+ var SCHEMA_EVAL_V22 = "agentv-eval-v2";
90
+ function isObject(value) {
91
+ return typeof value === "object" && value !== null && !Array.isArray(value);
92
+ }
93
+ async function validateEvalFile(filePath) {
94
+ const errors = [];
95
+ const absolutePath = import_node_path.default.resolve(filePath);
96
+ let parsed;
97
+ try {
98
+ const content = await (0, import_promises2.readFile)(absolutePath, "utf8");
99
+ parsed = (0, import_yaml2.parse)(content);
100
+ } catch (error) {
101
+ errors.push({
102
+ severity: "error",
103
+ filePath: absolutePath,
104
+ message: `Failed to parse YAML: ${error.message}`
105
+ });
106
+ return {
107
+ valid: false,
108
+ filePath: absolutePath,
109
+ fileType: "eval",
110
+ errors
111
+ };
112
+ }
113
+ if (!isObject(parsed)) {
114
+ errors.push({
115
+ severity: "error",
116
+ filePath: absolutePath,
117
+ message: "File must contain a YAML object"
118
+ });
119
+ return {
120
+ valid: false,
121
+ filePath: absolutePath,
122
+ fileType: "eval",
123
+ errors
124
+ };
125
+ }
126
+ const schema = parsed["$schema"];
127
+ if (schema !== SCHEMA_EVAL_V22) {
128
+ const message = typeof schema === "string" ? `Invalid $schema value '${schema}'. Expected '${SCHEMA_EVAL_V22}'` : `Missing required field '$schema'. Expected '${SCHEMA_EVAL_V22}'`;
129
+ errors.push({
130
+ severity: "error",
131
+ filePath: absolutePath,
132
+ location: "$schema",
133
+ message
134
+ });
135
+ }
136
+ const evalcases = parsed["evalcases"];
137
+ if (!Array.isArray(evalcases)) {
138
+ errors.push({
139
+ severity: "error",
140
+ filePath: absolutePath,
141
+ location: "evalcases",
142
+ message: "Missing or invalid 'evalcases' field (must be an array)"
143
+ });
144
+ return {
145
+ valid: errors.length === 0,
146
+ filePath: absolutePath,
147
+ fileType: "eval",
148
+ errors
149
+ };
150
+ }
151
+ for (let i = 0; i < evalcases.length; i++) {
152
+ const evalCase = evalcases[i];
153
+ const location = `evalcases[${i}]`;
154
+ if (!isObject(evalCase)) {
155
+ errors.push({
156
+ severity: "error",
157
+ filePath: absolutePath,
158
+ location,
159
+ message: "Eval case must be an object"
160
+ });
161
+ continue;
162
+ }
163
+ const id = evalCase["id"];
164
+ if (typeof id !== "string" || id.trim().length === 0) {
165
+ errors.push({
166
+ severity: "error",
167
+ filePath: absolutePath,
168
+ location: `${location}.id`,
169
+ message: "Missing or invalid 'id' field (must be a non-empty string)"
170
+ });
171
+ }
172
+ const outcome = evalCase["outcome"];
173
+ if (typeof outcome !== "string" || outcome.trim().length === 0) {
174
+ errors.push({
175
+ severity: "error",
176
+ filePath: absolutePath,
177
+ location: `${location}.outcome`,
178
+ message: "Missing or invalid 'outcome' field (must be a non-empty string)"
179
+ });
180
+ }
181
+ const inputMessages = evalCase["input_messages"];
182
+ if (!Array.isArray(inputMessages)) {
183
+ errors.push({
184
+ severity: "error",
185
+ filePath: absolutePath,
186
+ location: `${location}.input_messages`,
187
+ message: "Missing or invalid 'input_messages' field (must be an array)"
188
+ });
189
+ } else {
190
+ validateMessages(inputMessages, `${location}.input_messages`, absolutePath, errors);
191
+ }
192
+ const expectedMessages = evalCase["expected_messages"];
193
+ if (!Array.isArray(expectedMessages)) {
194
+ errors.push({
195
+ severity: "error",
196
+ filePath: absolutePath,
197
+ location: `${location}.expected_messages`,
198
+ message: "Missing or invalid 'expected_messages' field (must be an array)"
199
+ });
200
+ } else {
201
+ validateMessages(expectedMessages, `${location}.expected_messages`, absolutePath, errors);
202
+ }
203
+ }
204
+ return {
205
+ valid: errors.length === 0,
206
+ filePath: absolutePath,
207
+ fileType: "eval",
208
+ errors
209
+ };
210
+ }
211
+ function validateMessages(messages, location, filePath, errors) {
212
+ for (let i = 0; i < messages.length; i++) {
213
+ const message = messages[i];
214
+ const msgLocation = `${location}[${i}]`;
215
+ if (!isObject(message)) {
216
+ errors.push({
217
+ severity: "error",
218
+ filePath,
219
+ location: msgLocation,
220
+ message: "Message must be an object"
221
+ });
222
+ continue;
223
+ }
224
+ const role = message["role"];
225
+ const validRoles = ["system", "user", "assistant"];
226
+ if (!validRoles.includes(role)) {
227
+ errors.push({
228
+ severity: "error",
229
+ filePath,
230
+ location: `${msgLocation}.role`,
231
+ message: `Invalid role '${role}'. Must be one of: ${validRoles.join(", ")}`
232
+ });
233
+ }
234
+ const content = message["content"];
235
+ if (typeof content === "string") {
236
+ } else if (Array.isArray(content)) {
237
+ for (let j = 0; j < content.length; j++) {
238
+ const contentItem = content[j];
239
+ const contentLocation = `${msgLocation}.content[${j}]`;
240
+ if (typeof contentItem === "string") {
241
+ } else if (isObject(contentItem)) {
242
+ const type = contentItem["type"];
243
+ if (typeof type !== "string") {
244
+ errors.push({
245
+ severity: "error",
246
+ filePath,
247
+ location: `${contentLocation}.type`,
248
+ message: "Content object must have a 'type' field"
249
+ });
250
+ }
251
+ if (type === "text") {
252
+ const value = contentItem["value"];
253
+ if (typeof value !== "string") {
254
+ errors.push({
255
+ severity: "error",
256
+ filePath,
257
+ location: `${contentLocation}.value`,
258
+ message: "Content with type 'text' must have a 'value' field"
259
+ });
260
+ }
261
+ }
262
+ } else {
263
+ errors.push({
264
+ severity: "error",
265
+ filePath,
266
+ location: contentLocation,
267
+ message: "Content array items must be strings or objects"
268
+ });
269
+ }
270
+ }
271
+ } else {
272
+ errors.push({
273
+ severity: "error",
274
+ filePath,
275
+ location: `${msgLocation}.content`,
276
+ message: "Missing or invalid 'content' field (must be a string or array)"
277
+ });
278
+ }
279
+ }
280
+ }
281
+
282
+ // src/evaluation/validation/targets-validator.ts
283
+ var import_promises3 = require("fs/promises");
284
+ var import_node_path2 = __toESM(require("path"), 1);
285
+ var import_yaml3 = require("yaml");
286
+ var SCHEMA_TARGETS_V22 = "agentv-targets-v2";
287
+ function isObject2(value) {
288
+ return typeof value === "object" && value !== null && !Array.isArray(value);
289
+ }
290
+ async function validateTargetsFile(filePath) {
291
+ const errors = [];
292
+ const absolutePath = import_node_path2.default.resolve(filePath);
293
+ let parsed;
294
+ try {
295
+ const content = await (0, import_promises3.readFile)(absolutePath, "utf8");
296
+ parsed = (0, import_yaml3.parse)(content);
297
+ } catch (error) {
298
+ errors.push({
299
+ severity: "error",
300
+ filePath: absolutePath,
301
+ message: `Failed to parse YAML: ${error.message}`
302
+ });
303
+ return {
304
+ valid: false,
305
+ filePath: absolutePath,
306
+ fileType: "targets",
307
+ errors
308
+ };
309
+ }
310
+ if (!isObject2(parsed)) {
311
+ errors.push({
312
+ severity: "error",
313
+ filePath: absolutePath,
314
+ message: "File must contain a YAML object"
315
+ });
316
+ return {
317
+ valid: false,
318
+ filePath: absolutePath,
319
+ fileType: "targets",
320
+ errors
321
+ };
322
+ }
323
+ const schema = parsed["$schema"];
324
+ if (schema !== SCHEMA_TARGETS_V22) {
325
+ const message = typeof schema === "string" ? `Invalid $schema value '${schema}'. Expected '${SCHEMA_TARGETS_V22}'` : `Missing required field '$schema'. Expected '${SCHEMA_TARGETS_V22}'`;
326
+ errors.push({
327
+ severity: "error",
328
+ filePath: absolutePath,
329
+ location: "$schema",
330
+ message
331
+ });
332
+ }
333
+ const targets = parsed["targets"];
334
+ if (!Array.isArray(targets)) {
335
+ errors.push({
336
+ severity: "error",
337
+ filePath: absolutePath,
338
+ location: "targets",
339
+ message: "Missing or invalid 'targets' field (must be an array)"
340
+ });
341
+ return {
342
+ valid: errors.length === 0,
343
+ filePath: absolutePath,
344
+ fileType: "targets",
345
+ errors
346
+ };
347
+ }
348
+ const knownProviders = ["azure", "openai", "anthropic", "bedrock", "vertex"];
349
+ for (let i = 0; i < targets.length; i++) {
350
+ const target = targets[i];
351
+ const location = `targets[${i}]`;
352
+ if (!isObject2(target)) {
353
+ errors.push({
354
+ severity: "error",
355
+ filePath: absolutePath,
356
+ location,
357
+ message: "Target must be an object"
358
+ });
359
+ continue;
360
+ }
361
+ const name = target["name"];
362
+ if (typeof name !== "string" || name.trim().length === 0) {
363
+ errors.push({
364
+ severity: "error",
365
+ filePath: absolutePath,
366
+ location: `${location}.name`,
367
+ message: "Missing or invalid 'name' field (must be a non-empty string)"
368
+ });
369
+ }
370
+ const provider = target["provider"];
371
+ if (typeof provider !== "string" || provider.trim().length === 0) {
372
+ errors.push({
373
+ severity: "error",
374
+ filePath: absolutePath,
375
+ location: `${location}.provider`,
376
+ message: "Missing or invalid 'provider' field (must be a non-empty string)"
377
+ });
378
+ } else if (!knownProviders.includes(provider)) {
379
+ errors.push({
380
+ severity: "warning",
381
+ filePath: absolutePath,
382
+ location: `${location}.provider`,
383
+ message: `Unknown provider '${provider}'. Known providers: ${knownProviders.join(", ")}`
384
+ });
385
+ }
386
+ const settings = target["settings"];
387
+ if (settings !== void 0 && !isObject2(settings)) {
388
+ errors.push({
389
+ severity: "error",
390
+ filePath: absolutePath,
391
+ location: `${location}.settings`,
392
+ message: "Invalid 'settings' field (must be an object)"
393
+ });
394
+ }
395
+ const judgeTarget = target["judge_target"];
396
+ if (judgeTarget !== void 0 && typeof judgeTarget !== "string") {
397
+ errors.push({
398
+ severity: "error",
399
+ filePath: absolutePath,
400
+ location: `${location}.judge_target`,
401
+ message: "Invalid 'judge_target' field (must be a string)"
402
+ });
403
+ }
404
+ }
405
+ return {
406
+ valid: errors.filter((e) => e.severity === "error").length === 0,
407
+ filePath: absolutePath,
408
+ fileType: "targets",
409
+ errors
410
+ };
411
+ }
412
+
413
+ // src/evaluation/validation/file-reference-validator.ts
414
+ var import_promises5 = require("fs/promises");
415
+ var import_node_path4 = __toESM(require("path"), 1);
416
+ var import_yaml4 = require("yaml");
417
+
418
+ // src/evaluation/file-utils.ts
419
+ var import_node_fs = require("fs");
420
+ var import_promises4 = require("fs/promises");
421
+ var import_node_path3 = __toESM(require("path"), 1);
422
+ async function fileExists(filePath) {
423
+ try {
424
+ await (0, import_promises4.access)(filePath, import_node_fs.constants.F_OK);
425
+ return true;
426
+ } catch {
427
+ return false;
428
+ }
429
+ }
430
+ async function findGitRoot(startPath) {
431
+ let currentDir = import_node_path3.default.dirname(import_node_path3.default.resolve(startPath));
432
+ const root = import_node_path3.default.parse(currentDir).root;
433
+ while (currentDir !== root) {
434
+ const gitPath = import_node_path3.default.join(currentDir, ".git");
435
+ if (await fileExists(gitPath)) {
436
+ return currentDir;
437
+ }
438
+ const parentDir = import_node_path3.default.dirname(currentDir);
439
+ if (parentDir === currentDir) {
440
+ break;
441
+ }
442
+ currentDir = parentDir;
443
+ }
444
+ return null;
445
+ }
446
+ function buildSearchRoots(evalPath, repoRoot) {
447
+ const uniqueRoots = [];
448
+ const addRoot = (root) => {
449
+ const normalized = import_node_path3.default.resolve(root);
450
+ if (!uniqueRoots.includes(normalized)) {
451
+ uniqueRoots.push(normalized);
452
+ }
453
+ };
454
+ let currentDir = import_node_path3.default.dirname(evalPath);
455
+ let reachedBoundary = false;
456
+ while (!reachedBoundary) {
457
+ addRoot(currentDir);
458
+ const parentDir = import_node_path3.default.dirname(currentDir);
459
+ if (currentDir === repoRoot || parentDir === currentDir) {
460
+ reachedBoundary = true;
461
+ } else {
462
+ currentDir = parentDir;
463
+ }
464
+ }
465
+ addRoot(repoRoot);
466
+ addRoot(process.cwd());
467
+ return uniqueRoots;
468
+ }
469
+ function trimLeadingSeparators(value) {
470
+ const trimmed = value.replace(/^[/\\]+/, "");
471
+ return trimmed.length > 0 ? trimmed : value;
472
+ }
473
+ async function resolveFileReference(rawValue, searchRoots) {
474
+ const displayPath = trimLeadingSeparators(rawValue);
475
+ const potentialPaths = [];
476
+ if (import_node_path3.default.isAbsolute(rawValue)) {
477
+ potentialPaths.push(import_node_path3.default.normalize(rawValue));
478
+ }
479
+ for (const base of searchRoots) {
480
+ potentialPaths.push(import_node_path3.default.resolve(base, displayPath));
481
+ }
482
+ const attempted = [];
483
+ const seen = /* @__PURE__ */ new Set();
484
+ for (const candidate of potentialPaths) {
485
+ const absoluteCandidate = import_node_path3.default.resolve(candidate);
486
+ if (seen.has(absoluteCandidate)) {
487
+ continue;
488
+ }
489
+ seen.add(absoluteCandidate);
490
+ attempted.push(absoluteCandidate);
491
+ if (await fileExists(absoluteCandidate)) {
492
+ return { displayPath, resolvedPath: absoluteCandidate, attempted };
493
+ }
494
+ }
495
+ return { displayPath, attempted };
496
+ }
497
+
498
+ // src/evaluation/validation/file-reference-validator.ts
499
+ function isObject3(value) {
500
+ return typeof value === "object" && value !== null && !Array.isArray(value);
501
+ }
502
+ async function validateFileReferences(evalFilePath) {
503
+ const errors = [];
504
+ const absolutePath = import_node_path4.default.resolve(evalFilePath);
505
+ const gitRoot = await findGitRoot(absolutePath);
506
+ if (!gitRoot) {
507
+ errors.push({
508
+ severity: "error",
509
+ filePath: absolutePath,
510
+ message: "Cannot validate file references: git repository root not found"
511
+ });
512
+ return errors;
513
+ }
514
+ const searchRoots = buildSearchRoots(absolutePath, gitRoot);
515
+ let parsed;
516
+ try {
517
+ const content = await (0, import_promises5.readFile)(absolutePath, "utf8");
518
+ parsed = (0, import_yaml4.parse)(content);
519
+ } catch {
520
+ return errors;
521
+ }
522
+ if (!isObject3(parsed)) {
523
+ return errors;
524
+ }
525
+ const evalcases = parsed["evalcases"];
526
+ if (!Array.isArray(evalcases)) {
527
+ return errors;
528
+ }
529
+ for (let i = 0; i < evalcases.length; i++) {
530
+ const evalCase = evalcases[i];
531
+ if (!isObject3(evalCase)) {
532
+ continue;
533
+ }
534
+ const inputMessages = evalCase["input_messages"];
535
+ if (Array.isArray(inputMessages)) {
536
+ await validateMessagesFileRefs(inputMessages, `evalcases[${i}].input_messages`, searchRoots, absolutePath, errors);
537
+ }
538
+ const expectedMessages = evalCase["expected_messages"];
539
+ if (Array.isArray(expectedMessages)) {
540
+ await validateMessagesFileRefs(expectedMessages, `evalcases[${i}].expected_messages`, searchRoots, absolutePath, errors);
541
+ }
542
+ }
543
+ return errors;
544
+ }
545
+ async function validateMessagesFileRefs(messages, location, searchRoots, filePath, errors) {
546
+ for (let i = 0; i < messages.length; i++) {
547
+ const message = messages[i];
548
+ if (!isObject3(message)) {
549
+ continue;
550
+ }
551
+ const content = message["content"];
552
+ if (typeof content === "string") {
553
+ continue;
554
+ }
555
+ if (!Array.isArray(content)) {
556
+ continue;
557
+ }
558
+ for (let j = 0; j < content.length; j++) {
559
+ const contentItem = content[j];
560
+ if (!isObject3(contentItem)) {
561
+ continue;
562
+ }
563
+ const type = contentItem["type"];
564
+ if (type !== "file") {
565
+ continue;
566
+ }
567
+ const value = contentItem["value"];
568
+ if (typeof value !== "string") {
569
+ errors.push({
570
+ severity: "error",
571
+ filePath,
572
+ location: `${location}[${i}].content[${j}].value`,
573
+ message: "File reference must have a 'value' field with the file path"
574
+ });
575
+ continue;
576
+ }
577
+ const { resolvedPath } = await resolveFileReference(value, searchRoots);
578
+ if (!resolvedPath) {
579
+ errors.push({
580
+ severity: "error",
581
+ filePath,
582
+ location: `${location}[${i}].content[${j}]`,
583
+ message: `Referenced file not found: ${value}`
584
+ });
585
+ } else {
586
+ try {
587
+ const fileContent = await (0, import_promises5.readFile)(resolvedPath, "utf8");
588
+ if (fileContent.trim().length === 0) {
589
+ errors.push({
590
+ severity: "warning",
591
+ filePath,
592
+ location: `${location}[${i}].content[${j}]`,
593
+ message: `Referenced file is empty: ${value}`
594
+ });
595
+ }
596
+ } catch (error) {
597
+ errors.push({
598
+ severity: "error",
599
+ filePath,
600
+ location: `${location}[${i}].content[${j}]`,
601
+ message: `Cannot read referenced file: ${value} (${error.message})`
602
+ });
603
+ }
604
+ }
605
+ }
606
+ }
607
+ }
608
+ // Annotate the CommonJS export names for ESM import in node:
609
+ 0 && (module.exports = {
610
+ detectFileType,
611
+ getExpectedSchema,
612
+ isValidSchema,
613
+ validateEvalFile,
614
+ validateFileReferences,
615
+ validateTargetsFile
616
+ });
617
+ //# sourceMappingURL=index.cjs.map