@agentv/core 4.32.0-next.1 → 4.34.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.
@@ -61,7 +61,9 @@ declare function validateTargetsFile(filePath: string): Promise<ValidationResult
61
61
  /**
62
62
  * Validate a config.yaml file for schema compliance and structural correctness.
63
63
  */
64
- declare function validateConfigFile(filePath: string): Promise<ValidationResult>;
64
+ declare function validateConfigFile(filePath: string, options?: {
65
+ scope?: 'project' | 'global';
66
+ }): Promise<ValidationResult>;
65
67
 
66
68
  /**
67
69
  * Validate that file references in eval file content exist.
@@ -61,7 +61,9 @@ declare function validateTargetsFile(filePath: string): Promise<ValidationResult
61
61
  /**
62
62
  * Validate a config.yaml file for schema compliance and structural correctness.
63
63
  */
64
- declare function validateConfigFile(filePath: string): Promise<ValidationResult>;
64
+ declare function validateConfigFile(filePath: string, options?: {
65
+ scope?: 'project' | 'global';
66
+ }): Promise<ValidationResult>;
65
67
 
66
68
  /**
67
69
  * Validate that file references in eval file content exist.
@@ -6,13 +6,14 @@ import {
6
6
  buildSearchRoots,
7
7
  findDeprecatedCamelCaseTargetWarnings,
8
8
  findGitRoot,
9
+ getAgentvConfigDir,
9
10
  interpolateEnv,
10
11
  isGraderKind,
11
12
  loadCasesFromDirectory,
12
13
  loadCasesFromFile,
13
14
  parseYamlValue,
14
15
  resolveFileReference
15
- } from "../../chunk-5RQMJZDJ.js";
16
+ } from "../../chunk-EW5X2RGJ.js";
16
17
 
17
18
  // src/evaluation/validation/file-type.ts
18
19
  import { readFile } from "node:fs/promises";
@@ -947,6 +948,9 @@ import path4 from "node:path";
947
948
  function isObject3(value) {
948
949
  return typeof value === "object" && value !== null && !Array.isArray(value);
949
950
  }
951
+ function isNonEmptyString(value) {
952
+ return typeof value === "string" && value.trim().length > 0;
953
+ }
950
954
  var COMMON_SETTINGS = new Set(COMMON_TARGET_SETTINGS);
951
955
  var RETRY_SETTINGS = /* @__PURE__ */ new Set([
952
956
  "max_retries",
@@ -1015,6 +1019,7 @@ var GEMINI_SETTINGS = /* @__PURE__ */ new Set([
1015
1019
  var CODEX_SETTINGS = /* @__PURE__ */ new Set([
1016
1020
  ...COMMON_SETTINGS,
1017
1021
  "model",
1022
+ "model_reasoning_effort",
1018
1023
  "executable",
1019
1024
  "command",
1020
1025
  "binary",
@@ -1024,8 +1029,7 @@ var CODEX_SETTINGS = /* @__PURE__ */ new Set([
1024
1029
  "timeout_seconds",
1025
1030
  "log_dir",
1026
1031
  "log_directory",
1027
- "log_format",
1028
- "log_output_format",
1032
+ "stream_log",
1029
1033
  "system_prompt"
1030
1034
  ]);
1031
1035
  var COPILOT_SDK_SETTINGS = /* @__PURE__ */ new Set([
@@ -1150,6 +1154,26 @@ function validateUnknownSettings(target, provider, absolutePath, location, error
1150
1154
  "api_format",
1151
1155
  "The 'api_format' field is no longer supported on Azure targets. AgentV always uses Azure's Responses API (`/openai/v1/responses`). If your deployment only exposes /chat/completions, use 'provider: openai' with a deployment-scoped 'base_url' instead."
1152
1156
  ]
1157
+ ]),
1158
+ codex: /* @__PURE__ */ new Map([
1159
+ [
1160
+ "log_format",
1161
+ "The 'log_format' field is no longer supported on Codex targets. Use 'stream_log: raw' for per-event logs or 'stream_log: summary' for consolidated logs."
1162
+ ],
1163
+ [
1164
+ "log_output_format",
1165
+ "The 'log_output_format' field is no longer supported on Codex targets. Use 'stream_log: raw' for per-event logs or 'stream_log: summary' for consolidated logs."
1166
+ ]
1167
+ ]),
1168
+ "codex-cli": /* @__PURE__ */ new Map([
1169
+ [
1170
+ "log_format",
1171
+ "The 'log_format' field is no longer supported on Codex targets. Use 'stream_log: raw' for per-event logs or 'stream_log: summary' for consolidated logs."
1172
+ ],
1173
+ [
1174
+ "log_output_format",
1175
+ "The 'log_output_format' field is no longer supported on Codex targets. Use 'stream_log: raw' for per-event logs or 'stream_log: summary' for consolidated logs."
1176
+ ]
1153
1177
  ])
1154
1178
  };
1155
1179
  const removedForProvider = removedPerProvider[provider];
@@ -1186,9 +1210,11 @@ async function validateTargetsFile(filePath) {
1186
1210
  const errors = [];
1187
1211
  const absolutePath = path4.resolve(filePath);
1188
1212
  let parsed;
1213
+ let rawParsed;
1189
1214
  try {
1190
1215
  const content = await readFile4(absolutePath, "utf8");
1191
- parsed = interpolateEnv(parseYamlValue(content), process.env);
1216
+ rawParsed = parseYamlValue(content);
1217
+ parsed = interpolateEnv(rawParsed, process.env);
1192
1218
  } catch (error) {
1193
1219
  errors.push({
1194
1220
  severity: "error",
@@ -1309,6 +1335,7 @@ async function validateTargetsFile(filePath) {
1309
1335
  };
1310
1336
  }
1311
1337
  const targets = parsed.targets;
1338
+ const rawTargets = isObject3(rawParsed) && Array.isArray(rawParsed.targets) ? rawParsed.targets : [];
1312
1339
  if (!Array.isArray(targets)) {
1313
1340
  errors.push({
1314
1341
  severity: "error",
@@ -1358,7 +1385,9 @@ async function validateTargetsFile(filePath) {
1358
1385
  });
1359
1386
  }
1360
1387
  const provider = target.provider;
1361
- const hasUseTarget = typeof target.use_target === "string" && target.use_target.trim().length > 0;
1388
+ const rawTarget = rawTargets[i];
1389
+ const rawUseTarget = isObject3(rawTarget) ? rawTarget.use_target : void 0;
1390
+ const hasUseTarget = isNonEmptyString(target.use_target) || isNonEmptyString(rawUseTarget);
1362
1391
  const providerValue = typeof provider === "string" ? provider.trim().toLowerCase() : void 0;
1363
1392
  const isTemplated = typeof provider === "string" && /^\$\{\{.+\}\}$/.test(provider.trim());
1364
1393
  if (!hasUseTarget && (typeof provider !== "string" || provider.trim().length === 0)) {
@@ -1402,8 +1431,10 @@ async function validateTargetsFile(filePath) {
1402
1431
 
1403
1432
  // src/evaluation/validation/config-validator.ts
1404
1433
  import { readFile as readFile5 } from "node:fs/promises";
1405
- async function validateConfigFile(filePath) {
1434
+ import path5 from "node:path";
1435
+ async function validateConfigFile(filePath, options = {}) {
1406
1436
  const errors = [];
1437
+ const scope = options.scope ?? inferConfigScope(filePath);
1407
1438
  try {
1408
1439
  const content = await readFile5(filePath, "utf8");
1409
1440
  const parsed = interpolateEnv(parseYamlValue(content), process.env);
@@ -1452,78 +1483,43 @@ async function validateConfigFile(filePath) {
1452
1483
  });
1453
1484
  }
1454
1485
  }
1455
- const results = config.results;
1456
- if (results !== void 0) {
1457
- if (typeof results !== "object" || results === null || Array.isArray(results)) {
1486
+ validateResultsConfig(errors, filePath, config.results, "results");
1487
+ const projects = config.projects;
1488
+ if (projects !== void 0) {
1489
+ if (scope === "project") {
1490
+ errors.push({
1491
+ severity: "warning",
1492
+ filePath,
1493
+ location: "projects",
1494
+ message: "Field 'projects' is only valid in $AGENTV_HOME/config.yaml. Ignoring project registry entries in project-local .agentv/config.yaml."
1495
+ });
1496
+ } else if (!Array.isArray(projects)) {
1458
1497
  errors.push({
1459
1498
  severity: "error",
1460
1499
  filePath,
1461
- location: "results",
1462
- message: "Field 'results' must be an object"
1500
+ location: "projects",
1501
+ message: "Field 'projects' must be an array"
1463
1502
  });
1464
1503
  } else {
1465
- const resultsRecord = results;
1466
- if (resultsRecord.mode !== "github") {
1467
- errors.push({
1468
- severity: "error",
1469
- filePath,
1470
- location: "results.mode",
1471
- message: "Field 'results.mode' must be 'github'"
1472
- });
1473
- }
1474
- if (typeof resultsRecord.repo !== "string" || resultsRecord.repo.trim().length === 0) {
1475
- errors.push({
1476
- severity: "error",
1477
- filePath,
1478
- location: "results.repo",
1479
- message: "Field 'results.repo' must be a non-empty string"
1480
- });
1481
- }
1482
- if (resultsRecord.path !== void 0) {
1483
- if (typeof resultsRecord.path !== "string" || resultsRecord.path.trim().length === 0) {
1484
- errors.push({
1485
- severity: "error",
1486
- filePath,
1487
- location: "results.path",
1488
- message: "Field 'results.path' must be a non-empty string"
1489
- });
1490
- } else {
1491
- const p = resultsRecord.path.trim();
1492
- const isFilesystemPath = p.startsWith("/") || p.startsWith("~/") || p.startsWith("~\\") || p === "~" || /^[A-Za-z]:[/\\]/.test(p);
1493
- if (!isFilesystemPath) {
1494
- errors.push({
1495
- severity: "error",
1496
- filePath,
1497
- location: "results.path",
1498
- message: `'results.path' must be an absolute or home-relative filesystem path (e.g., ~/data/agentv-results). Found: '${p}'. Remove 'path' to use the default.`
1499
- });
1500
- }
1501
- }
1502
- }
1503
- if (resultsRecord.auto_push !== void 0 && typeof resultsRecord.auto_push !== "boolean") {
1504
- errors.push({
1505
- severity: "error",
1506
- filePath,
1507
- location: "results.auto_push",
1508
- message: "Field 'results.auto_push' must be a boolean"
1509
- });
1510
- }
1511
- if (resultsRecord.branch_prefix !== void 0 && (typeof resultsRecord.branch_prefix !== "string" || resultsRecord.branch_prefix.trim().length === 0)) {
1512
- errors.push({
1513
- severity: "error",
1514
- filePath,
1515
- location: "results.branch_prefix",
1516
- message: "Field 'results.branch_prefix' must be a non-empty string"
1517
- });
1518
- }
1504
+ validateProjects(errors, filePath, projects);
1519
1505
  }
1520
1506
  }
1507
+ if (config.results_by_project !== void 0) {
1508
+ errors.push({
1509
+ severity: "warning",
1510
+ filePath,
1511
+ location: "results_by_project",
1512
+ message: "Field 'results_by_project' is deprecated. Put per-project result repo settings under projects[].results in $AGENTV_HOME/config.yaml."
1513
+ });
1514
+ }
1521
1515
  const allowedFields = /* @__PURE__ */ new Set([
1522
1516
  "$schema",
1523
1517
  "eval_patterns",
1524
1518
  "required_version",
1525
1519
  "execution",
1526
1520
  "results",
1521
+ "projects",
1522
+ "results_by_project",
1527
1523
  "dashboard",
1528
1524
  "studio"
1529
1525
  ]);
@@ -1550,16 +1546,115 @@ async function validateConfigFile(filePath) {
1550
1546
  return { valid: false, filePath, fileType: "config", errors };
1551
1547
  }
1552
1548
  }
1549
+ function inferConfigScope(filePath) {
1550
+ const globalConfigPath = path5.resolve(getAgentvConfigDir(), "config.yaml");
1551
+ if (path5.resolve(filePath) === globalConfigPath) {
1552
+ return "global";
1553
+ }
1554
+ return filePath.split(/[\\/]/).includes(".agentv") ? "project" : "global";
1555
+ }
1556
+ function validateProjects(errors, filePath, projects) {
1557
+ projects.forEach((project, index) => {
1558
+ const location = `projects[${index}]`;
1559
+ if (typeof project !== "object" || project === null || Array.isArray(project)) {
1560
+ errors.push({
1561
+ severity: "error",
1562
+ filePath,
1563
+ location,
1564
+ message: `Field '${location}' must be an object`
1565
+ });
1566
+ return;
1567
+ }
1568
+ const projectRecord = project;
1569
+ validateRequiredString(errors, filePath, projectRecord.id, `${location}.id`);
1570
+ validateRequiredString(errors, filePath, projectRecord.name, `${location}.name`);
1571
+ validateRequiredString(errors, filePath, projectRecord.path, `${location}.path`);
1572
+ validateResultsConfig(errors, filePath, projectRecord.results, `${location}.results`);
1573
+ });
1574
+ }
1575
+ function validateRequiredString(errors, filePath, value, location) {
1576
+ if (typeof value !== "string" || value.trim().length === 0) {
1577
+ errors.push({
1578
+ severity: "error",
1579
+ filePath,
1580
+ location,
1581
+ message: `Field '${location}' must be a non-empty string`
1582
+ });
1583
+ }
1584
+ }
1585
+ function validateResultsConfig(errors, filePath, rawResults, location) {
1586
+ if (rawResults === void 0) {
1587
+ return;
1588
+ }
1589
+ if (typeof rawResults !== "object" || rawResults === null || Array.isArray(rawResults)) {
1590
+ errors.push({
1591
+ severity: "error",
1592
+ filePath,
1593
+ location,
1594
+ message: `Field '${location}' must be an object`
1595
+ });
1596
+ return;
1597
+ }
1598
+ const resultsRecord = rawResults;
1599
+ if (resultsRecord.mode !== "github") {
1600
+ errors.push({
1601
+ severity: "error",
1602
+ filePath,
1603
+ location: `${location}.mode`,
1604
+ message: `Field '${location}.mode' must be 'github'`
1605
+ });
1606
+ }
1607
+ validateRequiredString(errors, filePath, resultsRecord.repo, `${location}.repo`);
1608
+ if (resultsRecord.path !== void 0) {
1609
+ if (typeof resultsRecord.path !== "string" || resultsRecord.path.trim().length === 0) {
1610
+ errors.push({
1611
+ severity: "error",
1612
+ filePath,
1613
+ location: `${location}.path`,
1614
+ message: `Field '${location}.path' must be a non-empty string`
1615
+ });
1616
+ } else {
1617
+ const p = resultsRecord.path.trim();
1618
+ if (!isFilesystemPath(p)) {
1619
+ errors.push({
1620
+ severity: "error",
1621
+ filePath,
1622
+ location: `${location}.path`,
1623
+ message: `'${location}.path' must be an absolute or home-relative filesystem path (e.g., ~/data/agentv-results). Found: '${p}'. Remove 'path' to use the default.`
1624
+ });
1625
+ }
1626
+ }
1627
+ }
1628
+ if (resultsRecord.auto_push !== void 0 && typeof resultsRecord.auto_push !== "boolean") {
1629
+ errors.push({
1630
+ severity: "error",
1631
+ filePath,
1632
+ location: `${location}.auto_push`,
1633
+ message: `Field '${location}.auto_push' must be a boolean`
1634
+ });
1635
+ }
1636
+ if (resultsRecord.branch_prefix !== void 0 && (typeof resultsRecord.branch_prefix !== "string" || resultsRecord.branch_prefix.trim().length === 0)) {
1637
+ errors.push({
1638
+ severity: "error",
1639
+ filePath,
1640
+ location: `${location}.branch_prefix`,
1641
+ message: `Field '${location}.branch_prefix' must be a non-empty string`
1642
+ });
1643
+ }
1644
+ }
1645
+ function isFilesystemPath(p) {
1646
+ return p.startsWith("/") || p.startsWith("~/") || p.startsWith("~\\") || p === "~" || /^[A-Za-z]:[/\\]/.test(p);
1647
+ }
1553
1648
 
1554
1649
  // src/evaluation/validation/file-reference-validator.ts
1555
1650
  import { readFile as readFile6 } from "node:fs/promises";
1556
- import path5 from "node:path";
1651
+ import path6 from "node:path";
1557
1652
  function isObject4(value) {
1558
1653
  return typeof value === "object" && value !== null && !Array.isArray(value);
1559
1654
  }
1560
1655
  async function validateFileReferences(evalFilePath) {
1561
1656
  const errors = [];
1562
- const absolutePath = path5.resolve(evalFilePath);
1657
+ const absolutePath = path6.resolve(evalFilePath);
1563
1658
  const gitRoot = await findGitRoot(absolutePath);
1564
1659
  if (!gitRoot) {
1565
1660
  errors.push({
@@ -1684,14 +1779,14 @@ async function validateMessagesFileRefs(messages, location, searchRoots, filePat
1684
1779
 
1685
1780
  // src/evaluation/validation/workspace-path-validator.ts
1686
1781
  import { access, readFile as readFile7 } from "node:fs/promises";
1687
- import path6 from "node:path";
1782
+ import path7 from "node:path";
1688
1783
  function isObject5(value) {
1689
1784
  return typeof value === "object" && value !== null && !Array.isArray(value);
1690
1785
  }
1691
1786
  async function validateWorkspacePaths(evalFilePath) {
1692
1787
  const errors = [];
1693
- const absolutePath = path6.resolve(evalFilePath);
1694
- const evalDir = path6.dirname(absolutePath);
1788
+ const absolutePath = path7.resolve(evalFilePath);
1789
+ const evalDir = path7.dirname(absolutePath);
1695
1790
  let parsed;
1696
1791
  try {
1697
1792
  const content = await readFile7(absolutePath, "utf8");
@@ -1703,12 +1798,12 @@ async function validateWorkspacePaths(evalFilePath) {
1703
1798
  const workspaceRaw = parsed.workspace;
1704
1799
  if (workspaceRaw === void 0 || workspaceRaw === null) return errors;
1705
1800
  if (typeof workspaceRaw === "string") {
1706
- const workspaceFilePath = path6.resolve(evalDir, workspaceRaw);
1801
+ const workspaceFilePath = path7.resolve(evalDir, workspaceRaw);
1707
1802
  try {
1708
1803
  const wsContent = await readFile7(workspaceFilePath, "utf8");
1709
1804
  const wsParsed = parseYamlValue(wsContent);
1710
1805
  if (isObject5(wsParsed)) {
1711
- const wsDir = path6.dirname(workspaceFilePath);
1806
+ const wsDir = path7.dirname(workspaceFilePath);
1712
1807
  await validateWorkspaceObject(wsParsed, wsDir, absolutePath, "workspace", errors);
1713
1808
  }
1714
1809
  } catch {
@@ -1721,7 +1816,7 @@ async function validateWorkspacePaths(evalFilePath) {
1721
1816
  async function validateWorkspaceObject(obj, baseDir, evalFilePath, location, errors) {
1722
1817
  const template = obj.template;
1723
1818
  if (typeof template === "string") {
1724
- const templatePath = path6.isAbsolute(template) ? template : path6.resolve(baseDir, template);
1819
+ const templatePath = path7.isAbsolute(template) ? template : path7.resolve(baseDir, template);
1725
1820
  if (!await fileExists(templatePath)) {
1726
1821
  errors.push({
1727
1822
  severity: "error",
@@ -1737,14 +1832,14 @@ async function validateWorkspaceObject(obj, baseDir, evalFilePath, location, err
1737
1832
  const hook = hooks[hookName];
1738
1833
  if (!isObject5(hook)) continue;
1739
1834
  const hookCwdRaw = typeof hook.cwd === "string" ? hook.cwd : void 0;
1740
- const hookCwd = hookCwdRaw ? path6.isAbsolute(hookCwdRaw) ? hookCwdRaw : path6.resolve(baseDir, hookCwdRaw) : baseDir;
1835
+ const hookCwd = hookCwdRaw ? path7.isAbsolute(hookCwdRaw) ? hookCwdRaw : path7.resolve(baseDir, hookCwdRaw) : baseDir;
1741
1836
  const command = hook.command ?? hook.script;
1742
1837
  if (!Array.isArray(command)) continue;
1743
1838
  for (let i = 0; i < command.length; i++) {
1744
1839
  const arg = command[i];
1745
1840
  if (typeof arg !== "string") continue;
1746
1841
  if (!looksLikeFilePath(arg)) continue;
1747
- const resolved = path6.isAbsolute(arg) ? arg : path6.resolve(hookCwd, arg);
1842
+ const resolved = path7.isAbsolute(arg) ? arg : path7.resolve(hookCwd, arg);
1748
1843
  if (!await fileExists(resolved)) {
1749
1844
  errors.push({
1750
1845
  severity: "error",