agentv 4.37.0-next.1 → 4.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/{artifact-writer-GFNKYREE.js → artifact-writer-MK5X5MSO.js} +4 -4
  2. package/dist/{chunk-P4LSNFZR.js → chunk-3G4BK6Z5.js} +21 -20
  3. package/dist/{chunk-P4LSNFZR.js.map → chunk-3G4BK6Z5.js.map} +1 -1
  4. package/dist/{chunk-N6E5XFOM.js → chunk-DKUAETXE.js} +3 -3
  5. package/dist/{chunk-M7AMFWBZ.js → chunk-EKMMIULD.js} +42 -37
  6. package/dist/chunk-EKMMIULD.js.map +1 -0
  7. package/dist/{chunk-OYI35QFW.js → chunk-NLTIK3LV.js} +32 -250
  8. package/dist/chunk-NLTIK3LV.js.map +1 -0
  9. package/dist/{chunk-RL4S2FBZ.js → chunk-VBHHZQS6.js} +902 -488
  10. package/dist/chunk-VBHHZQS6.js.map +1 -0
  11. package/dist/cli.js +5 -5
  12. package/dist/dashboard/assets/index-BpnllKET.css +1 -0
  13. package/dist/dashboard/assets/index-Cm9SUopp.js +118 -0
  14. package/dist/dashboard/assets/{index-BDRYJsGF.js → index-SIl6NbIJ.js} +1 -1
  15. package/dist/dashboard/index.html +2 -2
  16. package/dist/{dist-OY3JSP6Z.js → dist-HVLBDG5F.js} +17 -13
  17. package/dist/index.js +5 -5
  18. package/dist/{interactive-CQELHITQ.js → interactive-QFAAM4SI.js} +5 -5
  19. package/dist/skills/agentv-eval-writer/SKILL.md +28 -36
  20. package/dist/skills/agentv-eval-writer/references/eval-schema.json +57 -210
  21. package/dist/{ts-eval-loader-RBTB2HG2-H5TRXZLO.js → ts-eval-loader-TJT6BGFF-DI7XNSO4.js} +2 -2
  22. package/package.json +1 -1
  23. package/dist/chunk-M7AMFWBZ.js.map +0 -1
  24. package/dist/chunk-OYI35QFW.js.map +0 -1
  25. package/dist/chunk-RL4S2FBZ.js.map +0 -1
  26. package/dist/dashboard/assets/index-9tV-u4HJ.css +0 -1
  27. package/dist/dashboard/assets/index-DuESU7zZ.js +0 -118
  28. /package/dist/{artifact-writer-GFNKYREE.js.map → artifact-writer-MK5X5MSO.js.map} +0 -0
  29. /package/dist/{chunk-N6E5XFOM.js.map → chunk-DKUAETXE.js.map} +0 -0
  30. /package/dist/{dist-OY3JSP6Z.js.map → dist-HVLBDG5F.js.map} +0 -0
  31. /package/dist/{interactive-CQELHITQ.js.map → interactive-QFAAM4SI.js.map} +0 -0
  32. /package/dist/{ts-eval-loader-RBTB2HG2-H5TRXZLO.js.map → ts-eval-loader-TJT6BGFF-DI7XNSO4.js.map} +0 -0
@@ -493,8 +493,8 @@ function getErrorMap() {
493
493
 
494
494
  // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
495
495
  var makeIssue = (params) => {
496
- const { data, path: path48, errorMaps, issueData } = params;
497
- const fullPath = [...path48, ...issueData.path || []];
496
+ const { data, path: path49, errorMaps, issueData } = params;
497
+ const fullPath = [...path49, ...issueData.path || []];
498
498
  const fullIssue = {
499
499
  ...issueData,
500
500
  path: fullPath
@@ -610,11 +610,11 @@ var errorUtil;
610
610
 
611
611
  // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/types.js
612
612
  var ParseInputLazyPath = class {
613
- constructor(parent, value, path48, key) {
613
+ constructor(parent, value, path49, key) {
614
614
  this._cachedPath = [];
615
615
  this.parent = parent;
616
616
  this.data = value;
617
- this._path = path48;
617
+ this._path = path49;
618
618
  this._key = key;
619
619
  }
620
620
  get path() {
@@ -4056,7 +4056,7 @@ var coerce = {
4056
4056
  };
4057
4057
  var NEVER = INVALID;
4058
4058
 
4059
- // ../../packages/core/dist/chunk-M4YHZQII.js
4059
+ // ../../packages/core/dist/chunk-M54RBDXI.js
4060
4060
  import { parse } from "yaml";
4061
4061
  import os from "node:os";
4062
4062
  import path from "node:path";
@@ -6184,21 +6184,21 @@ async function expandFileReferences(tests, evalFileDir) {
6184
6184
  return expanded;
6185
6185
  }
6186
6186
 
6187
- // ../../packages/core/dist/chunk-RRVABR4Z.js
6188
- import path47 from "node:path";
6187
+ // ../../packages/core/dist/chunk-RH5LAMMU.js
6188
+ import path48 from "node:path";
6189
6189
  import { pathToFileURL as pathToFileURL2 } from "node:url";
6190
- import { existsSync as existsSync6 } from "node:fs";
6191
- import path46 from "node:path";
6190
+ import { existsSync as existsSync7 } from "node:fs";
6191
+ import path47 from "node:path";
6192
6192
  import micromatch4 from "micromatch";
6193
6193
  import { mkdir, readFile as readFile3, writeFile } from "node:fs/promises";
6194
6194
  import path5 from "node:path";
6195
- import { execFile as execFile3 } from "node:child_process";
6196
- import { createHash as createHash4, randomUUID as randomUUID9 } from "node:crypto";
6197
- import { existsSync as existsSync5 } from "node:fs";
6198
- import { copyFile as copyFile2, mkdir as mkdir16, readdir as readdir8, stat as stat9 } from "node:fs/promises";
6199
- import path45 from "node:path";
6195
+ import { execFile as execFile2 } from "node:child_process";
6196
+ import { createHash as createHash5, randomUUID as randomUUID10 } from "node:crypto";
6197
+ import { existsSync as existsSync6 } from "node:fs";
6198
+ import { copyFile as copyFile2, mkdir as mkdir17, readdir as readdir8, stat as stat9 } from "node:fs/promises";
6199
+ import path46 from "node:path";
6200
6200
  import { fileURLToPath as fileURLToPath5 } from "node:url";
6201
- import { promisify as promisify7 } from "node:util";
6201
+ import { promisify as promisify6 } from "node:util";
6202
6202
  import micromatch3 from "micromatch";
6203
6203
  import { mkdtemp, rm, writeFile as writeFile2 } from "node:fs/promises";
6204
6204
  import { tmpdir } from "node:os";
@@ -6916,10 +6916,10 @@ function assignProp(target, prop, value) {
6916
6916
  configurable: true
6917
6917
  });
6918
6918
  }
6919
- function getElementAtPath(obj, path48) {
6920
- if (!path48)
6919
+ function getElementAtPath(obj, path49) {
6920
+ if (!path49)
6921
6921
  return obj;
6922
- return path48.reduce((acc, key) => acc?.[key], obj);
6922
+ return path49.reduce((acc, key) => acc?.[key], obj);
6923
6923
  }
6924
6924
  function promiseAllObject(promisesObj) {
6925
6925
  const keys = Object.keys(promisesObj);
@@ -7239,11 +7239,11 @@ function aborted(x, startIndex = 0) {
7239
7239
  }
7240
7240
  return false;
7241
7241
  }
7242
- function prefixIssues(path48, issues) {
7242
+ function prefixIssues(path49, issues) {
7243
7243
  return issues.map((iss) => {
7244
7244
  var _a;
7245
7245
  (_a = iss).path ?? (_a.path = []);
7246
- iss.path.unshift(path48);
7246
+ iss.path.unshift(path49);
7247
7247
  return iss;
7248
7248
  });
7249
7249
  }
@@ -7380,7 +7380,7 @@ function treeifyError(error40, _mapper) {
7380
7380
  return issue2.message;
7381
7381
  };
7382
7382
  const result = { errors: [] };
7383
- const processError = (error41, path48 = []) => {
7383
+ const processError = (error41, path49 = []) => {
7384
7384
  var _a, _b;
7385
7385
  for (const issue2 of error41.issues) {
7386
7386
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -7390,7 +7390,7 @@ function treeifyError(error40, _mapper) {
7390
7390
  } else if (issue2.code === "invalid_element") {
7391
7391
  processError({ issues: issue2.issues }, issue2.path);
7392
7392
  } else {
7393
- const fullpath = [...path48, ...issue2.path];
7393
+ const fullpath = [...path49, ...issue2.path];
7394
7394
  if (fullpath.length === 0) {
7395
7395
  result.errors.push(mapper(issue2));
7396
7396
  continue;
@@ -7420,9 +7420,9 @@ function treeifyError(error40, _mapper) {
7420
7420
  processError(error40);
7421
7421
  return result;
7422
7422
  }
7423
- function toDotPath(path48) {
7423
+ function toDotPath(path49) {
7424
7424
  const segs = [];
7425
- for (const seg of path48) {
7425
+ for (const seg of path49) {
7426
7426
  if (typeof seg === "number")
7427
7427
  segs.push(`[${seg}]`);
7428
7428
  else if (typeof seg === "symbol")
@@ -18852,7 +18852,7 @@ var RequestError = class _RequestError extends Error {
18852
18852
  }
18853
18853
  };
18854
18854
 
18855
- // ../../packages/core/dist/chunk-RRVABR4Z.js
18855
+ // ../../packages/core/dist/chunk-RH5LAMMU.js
18856
18856
  import { exec as execCallback } from "node:child_process";
18857
18857
  import { readdirSync, statSync } from "node:fs";
18858
18858
  import { readFile as readFile32, readdir as readdir2, stat as stat2 } from "node:fs/promises";
@@ -18927,38 +18927,41 @@ import path33 from "node:path";
18927
18927
  import fg3 from "fast-glob";
18928
18928
  import { cp, mkdir as mkdir14, readdir as readdir5, rm as rm4, stat as stat6 } from "node:fs/promises";
18929
18929
  import path34 from "node:path";
18930
- import { execFile } from "node:child_process";
18931
18930
  import { createHash as createHash3 } from "node:crypto";
18932
18931
  import { existsSync as existsSync3 } from "node:fs";
18933
18932
  import { cp as cp2, mkdir as mkdir15, readFile as readFile11, readdir as readdir6, rm as rm5, unlink, writeFile as writeFile9 } from "node:fs/promises";
18934
18933
  import path35 from "node:path";
18934
+ import { execFile, spawn as spawn5 } from "node:child_process";
18935
+ import { createHash as createHash4, randomUUID as randomUUID9 } from "node:crypto";
18936
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
18937
+ import { mkdir as mkdir16, rename, rm as rm6 } from "node:fs/promises";
18938
+ import path37 from "node:path";
18935
18939
  import { promisify as promisify5 } from "node:util";
18936
- import { execFile as execFile2 } from "node:child_process";
18937
- import { existsSync as existsSync4 } from "node:fs";
18940
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync22, readdirSync as readdirSync3, statSync as statSync2, writeFileSync } from "node:fs";
18938
18941
  import path36 from "node:path";
18939
- import { promisify as promisify6 } from "node:util";
18942
+ import { stringify as stringifyYaml } from "yaml";
18940
18943
  import { readdir as readdir7, stat as stat7 } from "node:fs/promises";
18941
- import path37 from "node:path";
18944
+ import path38 from "node:path";
18942
18945
  import { readFile as readFile18, stat as stat8 } from "node:fs/promises";
18943
- import path44 from "node:path";
18946
+ import path45 from "node:path";
18944
18947
  import micromatch2 from "micromatch";
18945
- import { stringify as stringifyYaml } from "yaml";
18948
+ import { stringify as stringifyYaml2 } from "yaml";
18946
18949
  import { readFile as readFile12 } from "node:fs/promises";
18947
- import path38 from "node:path";
18950
+ import path39 from "node:path";
18948
18951
  import { readFile as readFile13 } from "node:fs/promises";
18949
- import path40 from "node:path";
18952
+ import path41 from "node:path";
18950
18953
  import { constants as constants4 } from "node:fs";
18951
18954
  import { access as access4 } from "node:fs/promises";
18952
- import path39 from "node:path";
18955
+ import path40 from "node:path";
18953
18956
  import { fileURLToPath as fileURLToPath4 } from "node:url";
18954
18957
  import { readFile as readFile15 } from "node:fs/promises";
18955
- import path41 from "node:path";
18958
+ import path422 from "node:path";
18956
18959
  import { readFile as readFile14 } from "node:fs/promises";
18957
18960
  import { readFile as readFile17 } from "node:fs/promises";
18958
- import path43 from "node:path";
18961
+ import path44 from "node:path";
18959
18962
  import micromatch from "micromatch";
18960
18963
  import { readFile as readFile16 } from "node:fs/promises";
18961
- import path422 from "node:path";
18964
+ import path43 from "node:path";
18962
18965
  var DEFAULT_CACHE_PATH = ".agentv/cache";
18963
18966
  var ResponseCache = class {
18964
18967
  cachePath;
@@ -19489,14 +19492,14 @@ function toCamelCaseDeep(obj) {
19489
19492
  }
19490
19493
  return obj;
19491
19494
  }
19492
- function getRepoCheckoutRef(checkout) {
19493
- return checkout?.base_commit ?? checkout?.ref ?? "HEAD";
19495
+ function getRepoCheckoutRef(repo) {
19496
+ return repo?.commit ?? repo?.base_commit ?? "HEAD";
19494
19497
  }
19495
19498
  function getRepoCheckoutTargets(repos) {
19496
19499
  if (!repos) return [];
19497
- return repos.filter((repo) => repo.checkout?.base_commit || repo.checkout?.ref).map((repo) => ({
19500
+ return repos.filter((repo) => repo.commit || repo.base_commit).map((repo) => ({
19498
19501
  path: repo.path,
19499
- ref: getRepoCheckoutRef(repo.checkout)
19502
+ ref: getRepoCheckoutRef(repo)
19500
19503
  }));
19501
19504
  }
19502
19505
  var FILE_BACKED_OUTPUT_THRESHOLD = 5e4;
@@ -22529,115 +22532,115 @@ var FieldAccuracyGrader = class {
22529
22532
  * Evaluate a single field against the expected value.
22530
22533
  */
22531
22534
  evaluateField(fieldConfig, candidateData, expectedData) {
22532
- const { path: path48, match, required: required2 = true, weight = 1 } = fieldConfig;
22533
- const candidateValue = resolvePath(candidateData, path48);
22534
- const expectedValue = resolvePath(expectedData, path48);
22535
+ const { path: path49, match, required: required2 = true, weight = 1 } = fieldConfig;
22536
+ const candidateValue = resolvePath(candidateData, path49);
22537
+ const expectedValue = resolvePath(expectedData, path49);
22535
22538
  if (expectedValue === void 0) {
22536
22539
  return {
22537
- path: path48,
22540
+ path: path49,
22538
22541
  score: 1,
22539
22542
  // No expected value means no comparison needed
22540
22543
  weight,
22541
22544
  hit: true,
22542
- message: `${path48}: no expected value`
22545
+ message: `${path49}: no expected value`
22543
22546
  };
22544
22547
  }
22545
22548
  if (candidateValue === void 0) {
22546
22549
  if (required2) {
22547
22550
  return {
22548
- path: path48,
22551
+ path: path49,
22549
22552
  score: 0,
22550
22553
  weight,
22551
22554
  hit: false,
22552
- message: `${path48} (required, missing)`
22555
+ message: `${path49} (required, missing)`
22553
22556
  };
22554
22557
  }
22555
22558
  return {
22556
- path: path48,
22559
+ path: path49,
22557
22560
  score: 1,
22558
22561
  // Don't penalize missing optional fields
22559
22562
  weight: 0,
22560
22563
  // Zero weight means it won't affect the score
22561
22564
  hit: true,
22562
- message: `${path48}: optional field missing`
22565
+ message: `${path49}: optional field missing`
22563
22566
  };
22564
22567
  }
22565
22568
  switch (match) {
22566
22569
  case "exact":
22567
- return this.compareExact(path48, candidateValue, expectedValue, weight);
22570
+ return this.compareExact(path49, candidateValue, expectedValue, weight);
22568
22571
  case "numeric_tolerance":
22569
22572
  return this.compareNumericTolerance(
22570
- path48,
22573
+ path49,
22571
22574
  candidateValue,
22572
22575
  expectedValue,
22573
22576
  fieldConfig,
22574
22577
  weight
22575
22578
  );
22576
22579
  case "date":
22577
- return this.compareDate(path48, candidateValue, expectedValue, fieldConfig, weight);
22580
+ return this.compareDate(path49, candidateValue, expectedValue, fieldConfig, weight);
22578
22581
  default:
22579
22582
  return {
22580
- path: path48,
22583
+ path: path49,
22581
22584
  score: 0,
22582
22585
  weight,
22583
22586
  hit: false,
22584
- message: `${path48}: unknown match type "${match}"`
22587
+ message: `${path49}: unknown match type "${match}"`
22585
22588
  };
22586
22589
  }
22587
22590
  }
22588
22591
  /**
22589
22592
  * Exact equality comparison.
22590
22593
  */
22591
- compareExact(path48, candidateValue, expectedValue, weight) {
22594
+ compareExact(path49, candidateValue, expectedValue, weight) {
22592
22595
  if (deepEqual(candidateValue, expectedValue)) {
22593
22596
  return {
22594
- path: path48,
22597
+ path: path49,
22595
22598
  score: 1,
22596
22599
  weight,
22597
22600
  hit: true,
22598
- message: path48
22601
+ message: path49
22599
22602
  };
22600
22603
  }
22601
22604
  if (typeof candidateValue !== typeof expectedValue) {
22602
22605
  return {
22603
- path: path48,
22606
+ path: path49,
22604
22607
  score: 0,
22605
22608
  weight,
22606
22609
  hit: false,
22607
- message: `${path48} (type mismatch: got ${typeof candidateValue}, expected ${typeof expectedValue})`
22610
+ message: `${path49} (type mismatch: got ${typeof candidateValue}, expected ${typeof expectedValue})`
22608
22611
  };
22609
22612
  }
22610
22613
  return {
22611
- path: path48,
22614
+ path: path49,
22612
22615
  score: 0,
22613
22616
  weight,
22614
22617
  hit: false,
22615
- message: `${path48} (value mismatch)`
22618
+ message: `${path49} (value mismatch)`
22616
22619
  };
22617
22620
  }
22618
22621
  /**
22619
22622
  * Numeric comparison with absolute or relative tolerance.
22620
22623
  */
22621
- compareNumericTolerance(path48, candidateValue, expectedValue, fieldConfig, weight) {
22624
+ compareNumericTolerance(path49, candidateValue, expectedValue, fieldConfig, weight) {
22622
22625
  const { tolerance = 0, relative = false } = fieldConfig;
22623
22626
  const candidateNum = toNumber(candidateValue);
22624
22627
  const expectedNum = toNumber(expectedValue);
22625
22628
  if (candidateNum === null || expectedNum === null) {
22626
22629
  return {
22627
- path: path48,
22630
+ path: path49,
22628
22631
  score: 0,
22629
22632
  weight,
22630
22633
  hit: false,
22631
- message: `${path48} (non-numeric value)`
22634
+ message: `${path49} (non-numeric value)`
22632
22635
  };
22633
22636
  }
22634
22637
  if (!Number.isFinite(candidateNum) || !Number.isFinite(expectedNum)) {
22635
22638
  return {
22636
- path: path48,
22639
+ path: path49,
22637
22640
  score: 0,
22638
22641
  weight,
22639
22642
  hit: false,
22640
- message: `${path48} (invalid numeric value)`
22643
+ message: `${path49} (invalid numeric value)`
22641
22644
  };
22642
22645
  }
22643
22646
  const diff = Math.abs(candidateNum - expectedNum);
@@ -22650,61 +22653,61 @@ var FieldAccuracyGrader = class {
22650
22653
  }
22651
22654
  if (withinTolerance) {
22652
22655
  return {
22653
- path: path48,
22656
+ path: path49,
22654
22657
  score: 1,
22655
22658
  weight,
22656
22659
  hit: true,
22657
- message: `${path48} (within tolerance: diff=${diff.toFixed(2)})`
22660
+ message: `${path49} (within tolerance: diff=${diff.toFixed(2)})`
22658
22661
  };
22659
22662
  }
22660
22663
  return {
22661
- path: path48,
22664
+ path: path49,
22662
22665
  score: 0,
22663
22666
  weight,
22664
22667
  hit: false,
22665
- message: `${path48} (outside tolerance: diff=${diff.toFixed(2)}, tolerance=${tolerance})`
22668
+ message: `${path49} (outside tolerance: diff=${diff.toFixed(2)}, tolerance=${tolerance})`
22666
22669
  };
22667
22670
  }
22668
22671
  /**
22669
22672
  * Date comparison with format normalization.
22670
22673
  */
22671
- compareDate(path48, candidateValue, expectedValue, fieldConfig, weight) {
22674
+ compareDate(path49, candidateValue, expectedValue, fieldConfig, weight) {
22672
22675
  const formats = fieldConfig.formats ?? DEFAULT_DATE_FORMATS;
22673
22676
  const candidateDate = parseDate(String(candidateValue), formats);
22674
22677
  const expectedDate = parseDate(String(expectedValue), formats);
22675
22678
  if (candidateDate === null) {
22676
22679
  return {
22677
- path: path48,
22680
+ path: path49,
22678
22681
  score: 0,
22679
22682
  weight,
22680
22683
  hit: false,
22681
- message: `${path48} (unparseable candidate date)`
22684
+ message: `${path49} (unparseable candidate date)`
22682
22685
  };
22683
22686
  }
22684
22687
  if (expectedDate === null) {
22685
22688
  return {
22686
- path: path48,
22689
+ path: path49,
22687
22690
  score: 0,
22688
22691
  weight,
22689
22692
  hit: false,
22690
- message: `${path48} (unparseable expected date)`
22693
+ message: `${path49} (unparseable expected date)`
22691
22694
  };
22692
22695
  }
22693
22696
  if (candidateDate.getFullYear() === expectedDate.getFullYear() && candidateDate.getMonth() === expectedDate.getMonth() && candidateDate.getDate() === expectedDate.getDate()) {
22694
22697
  return {
22695
- path: path48,
22698
+ path: path49,
22696
22699
  score: 1,
22697
22700
  weight,
22698
22701
  hit: true,
22699
- message: path48
22702
+ message: path49
22700
22703
  };
22701
22704
  }
22702
22705
  return {
22703
- path: path48,
22706
+ path: path49,
22704
22707
  score: 0,
22705
22708
  weight,
22706
22709
  hit: false,
22707
- message: `${path48} (date mismatch: got ${formatDateISO(candidateDate)}, expected ${formatDateISO(expectedDate)})`
22710
+ message: `${path49} (date mismatch: got ${formatDateISO(candidateDate)}, expected ${formatDateISO(expectedDate)})`
22708
22711
  };
22709
22712
  }
22710
22713
  /**
@@ -22737,11 +22740,11 @@ var FieldAccuracyGrader = class {
22737
22740
  };
22738
22741
  }
22739
22742
  };
22740
- function resolvePath(obj, path48) {
22741
- if (!path48 || !obj) {
22743
+ function resolvePath(obj, path49) {
22744
+ if (!path49 || !obj) {
22742
22745
  return void 0;
22743
22746
  }
22744
- const parts = path48.split(/\.|\[|\]/).filter((p) => p.length > 0);
22747
+ const parts = path49.split(/\.|\[|\]/).filter((p) => p.length > 0);
22745
22748
  let current = obj;
22746
22749
  for (const part of parts) {
22747
22750
  if (current === null || current === void 0) {
@@ -23275,8 +23278,8 @@ var TokenUsageGrader = class {
23275
23278
  };
23276
23279
  }
23277
23280
  };
23278
- function getNestedValue(obj, path48) {
23279
- const parts = path48.split(".");
23281
+ function getNestedValue(obj, path49) {
23282
+ const parts = path49.split(".");
23280
23283
  let current = obj;
23281
23284
  for (const part of parts) {
23282
23285
  if (current === null || current === void 0 || typeof current !== "object") {
@@ -32958,47 +32961,57 @@ async function cleanupEvalWorkspaces(evalRunId, workspaceRoot) {
32958
32961
  await rm4(evalDir, { recursive: true, force: true });
32959
32962
  }
32960
32963
  }
32961
- var execFileAsync = promisify5(execFile);
32962
- function gitEnv() {
32963
- const env = { ...process.env };
32964
- for (const key of Object.keys(env)) {
32965
- if (key.startsWith("GIT_") && key !== "GIT_SSH_COMMAND") {
32966
- delete env[key];
32964
+ var GITHUB_SHORTHAND_RE = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
32965
+ function resolveRepoCloneUrl(repo) {
32966
+ const trimmed = repo.trim();
32967
+ if (GITHUB_SHORTHAND_RE.test(trimmed)) {
32968
+ return `https://github.com/${trimmed}.git`;
32969
+ }
32970
+ return trimmed;
32971
+ }
32972
+ function normalizeRepoIdentity(repo) {
32973
+ const cloneUrl = resolveRepoCloneUrl(repo);
32974
+ const sshMatch = /^git@([^:]+):(.+)$/.exec(cloneUrl);
32975
+ if (sshMatch) {
32976
+ return normalizeHostPath(sshMatch[1], sshMatch[2]);
32977
+ }
32978
+ try {
32979
+ const parsed = new URL(cloneUrl);
32980
+ if (parsed.protocol === "ssh:" && parsed.username === "git") {
32981
+ return normalizeHostPath(parsed.hostname, parsed.pathname);
32982
+ }
32983
+ if (parsed.protocol === "http:" || parsed.protocol === "https:") {
32984
+ return normalizeHostPath(parsed.hostname, parsed.pathname);
32967
32985
  }
32986
+ if (parsed.protocol === "file:") {
32987
+ return `file://${stripGitSuffix(decodeURIComponent(parsed.pathname)).replace(/\/+$/, "")}`;
32988
+ }
32989
+ } catch {
32968
32990
  }
32969
- return {
32970
- ...env,
32971
- GIT_TERMINAL_PROMPT: "0",
32972
- GIT_ASKPASS: "",
32973
- GIT_SSH_COMMAND: "ssh -o BatchMode=yes"
32974
- };
32991
+ return stripGitSuffix(cloneUrl).replace(/\/+$/, "");
32975
32992
  }
32976
- async function git(args, opts) {
32977
- const { stdout } = await execFileAsync("git", args, {
32978
- cwd: opts?.cwd,
32979
- timeout: opts?.timeout ?? 3e5,
32980
- env: gitEnv(),
32981
- maxBuffer: 50 * 1024 * 1024
32982
- });
32983
- return stdout.trim();
32993
+ function normalizeHostPath(host, rawPath) {
32994
+ const normalizedPath = stripGitSuffix(rawPath.replace(/^\/+/, "").replace(/\/+$/, ""));
32995
+ const normalized = host.toLowerCase() === "github.com" ? normalizedPath.toLowerCase() : normalizedPath;
32996
+ return `${host.toLowerCase()}/${normalized}`;
32997
+ }
32998
+ function stripGitSuffix(value) {
32999
+ return value.replace(/\.git$/i, "");
32984
33000
  }
32985
33001
  function normalizeRepoForFingerprint(repo) {
32986
33002
  const result = {};
32987
33003
  if (repo.path) {
32988
33004
  result.path = repo.path;
32989
33005
  }
32990
- if (repo.source) {
32991
- result.source = repo.source.type === "git" ? { type: "git", url: repo.source.url.toLowerCase().replace(/\.git$/, "") } : { type: "local", path: repo.source.path };
32992
- }
32993
- result.ref = getRepoCheckoutRef(repo.checkout);
32994
- if (repo.clone?.depth !== void 0) {
32995
- result.depth = repo.clone.depth;
33006
+ if (repo.repo) {
33007
+ result.repo = normalizeRepoIdentity(repo.repo);
32996
33008
  }
32997
- if (repo.clone?.filter !== void 0) {
32998
- result.filter = repo.clone.filter;
33009
+ result.ref = getRepoCheckoutRef(repo);
33010
+ if (repo.ancestor !== void 0) {
33011
+ result.ancestor = repo.ancestor;
32999
33012
  }
33000
- if (repo.clone?.sparse?.length) {
33001
- result.sparse = [...repo.clone.sparse].sort();
33013
+ if (repo.sparse?.length) {
33014
+ result.sparse = [...repo.sparse].sort();
33002
33015
  }
33003
33016
  return result;
33004
33017
  }
@@ -33064,7 +33077,7 @@ var WorkspacePoolManager = class {
33064
33077
  }
33065
33078
  const slotExists = existsSync3(slotPath);
33066
33079
  if (slotExists) {
33067
- await this.resetSlot(slotPath, templatePath, repos, poolReset);
33080
+ await this.resetSlot(slotPath, templatePath, repos, options.repoManager, poolReset);
33068
33081
  return {
33069
33082
  index: i,
33070
33083
  path: slotPath,
@@ -33193,33 +33206,12 @@ var WorkspacePoolManager = class {
33193
33206
  }
33194
33207
  /**
33195
33208
  * Reset an existing slot for reuse:
33196
- * 1. Reset repos (fetch from origin when resolve=remote, then git reset --hard && git clean per repo)
33209
+ * 1. Reset repos to their declared checkout, then git clean per repo
33197
33210
  * 2. Re-copy template files (skip repo directories)
33198
33211
  */
33199
- async resetSlot(slotPath, templatePath, repos, poolReset = "fast") {
33200
- for (const repo of repos) {
33201
- if (!repo.path || !repo.source) continue;
33202
- const repoDir = path35.join(slotPath, repo.path);
33203
- if (!existsSync3(repoDir)) {
33204
- continue;
33205
- }
33206
- if (poolReset === "none") {
33207
- continue;
33208
- }
33209
- const ref = getRepoCheckoutRef(repo.checkout);
33210
- const resolve = repo.checkout?.resolve ?? "remote";
33211
- if (resolve === "remote") {
33212
- const fetchArgs = ["fetch", "origin", ref];
33213
- if (repo.clone?.depth) {
33214
- fetchArgs.splice(1, 0, "--depth", String(repo.clone.depth));
33215
- }
33216
- await git(fetchArgs, { cwd: repoDir });
33217
- await git(["reset", "--hard", "FETCH_HEAD"], { cwd: repoDir });
33218
- } else {
33219
- await git(["reset", "--hard", ref], { cwd: repoDir });
33220
- }
33221
- const cleanFlag = poolReset === "strict" ? "-fdx" : "-fd";
33222
- await git(["clean", cleanFlag], { cwd: repoDir });
33212
+ async resetSlot(slotPath, templatePath, repos, repoManager, poolReset = "fast") {
33213
+ if (poolReset !== "none") {
33214
+ await repoManager.reset(repos, slotPath, poolReset);
33223
33215
  }
33224
33216
  if (templatePath) {
33225
33217
  const repoDirNames = new Set(
@@ -33232,9 +33224,201 @@ var WorkspacePoolManager = class {
33232
33224
  }
33233
33225
  }
33234
33226
  };
33235
- var execFileAsync2 = promisify6(execFile2);
33227
+ function getProjectsRegistryPath() {
33228
+ return path36.join(getAgentvConfigDir(), "config.yaml");
33229
+ }
33230
+ function fromYaml(raw) {
33231
+ if (!raw || typeof raw !== "object") return null;
33232
+ const e = raw;
33233
+ if (typeof e.id !== "string" || typeof e.name !== "string" || typeof e.path !== "string") {
33234
+ return null;
33235
+ }
33236
+ const entry = {
33237
+ id: e.id,
33238
+ name: e.name,
33239
+ path: e.path,
33240
+ addedAt: typeof e.added_at === "string" ? e.added_at : "",
33241
+ lastOpenedAt: typeof e.last_opened_at === "string" ? e.last_opened_at : ""
33242
+ };
33243
+ if (typeof e.repo_url === "string" && e.repo_url.trim().length > 0) {
33244
+ entry.repoUrl = e.repo_url.trim();
33245
+ }
33246
+ if (typeof e.ref === "string" && e.ref.trim().length > 0) {
33247
+ entry.ref = e.ref.trim();
33248
+ }
33249
+ if (e.results && typeof e.results === "object") {
33250
+ const r = e.results;
33251
+ if (typeof r.repo_url === "string" && r.repo_url.trim().length > 0) {
33252
+ const sync = r.sync && typeof r.sync === "object" ? r.sync : void 0;
33253
+ entry.results = {
33254
+ repoUrl: r.repo_url.trim(),
33255
+ ...typeof r.branch === "string" && r.branch.trim().length > 0 ? { branch: r.branch.trim() } : {},
33256
+ ...typeof r.path === "string" && r.path.trim().length > 0 ? { path: r.path.trim() } : {},
33257
+ ...sync && typeof sync.auto_push === "boolean" ? { sync: { autoPush: sync.auto_push } } : {},
33258
+ ...typeof r.branch_prefix === "string" && r.branch_prefix.trim().length > 0 ? { branchPrefix: r.branch_prefix.trim() } : {}
33259
+ };
33260
+ }
33261
+ }
33262
+ return entry;
33263
+ }
33264
+ function toYaml(entry) {
33265
+ const yaml = {
33266
+ id: entry.id,
33267
+ name: entry.name,
33268
+ ...entry.repoUrl !== void 0 && { repo_url: entry.repoUrl },
33269
+ path: entry.path,
33270
+ ...entry.ref !== void 0 && { ref: entry.ref },
33271
+ added_at: entry.addedAt,
33272
+ last_opened_at: entry.lastOpenedAt
33273
+ };
33274
+ if (entry.results) {
33275
+ yaml.results = {
33276
+ repo_url: entry.results.repoUrl,
33277
+ ...entry.results.branch !== void 0 && { branch: entry.results.branch },
33278
+ ...entry.results.path !== void 0 && { path: entry.results.path },
33279
+ ...entry.results.sync?.autoPush !== void 0 && {
33280
+ sync: { auto_push: entry.results.sync.autoPush }
33281
+ },
33282
+ ...entry.results.branchPrefix !== void 0 && {
33283
+ branch_prefix: entry.results.branchPrefix
33284
+ }
33285
+ };
33286
+ }
33287
+ return yaml;
33288
+ }
33289
+ function loadProjectRegistry() {
33290
+ const registryPath = getProjectsRegistryPath();
33291
+ if (!existsSync4(registryPath)) {
33292
+ return { projects: [] };
33293
+ }
33294
+ try {
33295
+ const raw = readFileSync22(registryPath, "utf-8");
33296
+ const parsed = parseYamlValue(raw);
33297
+ if (!parsed || typeof parsed !== "object") {
33298
+ return { projects: [] };
33299
+ }
33300
+ const env = process.env;
33301
+ const projects = Array.isArray(parsed.projects) ? parsed.projects.map((e) => fromYaml(interpolateEnv(e, env))).filter((e) => e !== null) : [];
33302
+ return { projects };
33303
+ } catch {
33304
+ return { projects: [] };
33305
+ }
33306
+ }
33307
+ function saveProjectRegistry(registry2) {
33308
+ const registryPath = getProjectsRegistryPath();
33309
+ const dir = path36.dirname(registryPath);
33310
+ if (!existsSync4(dir)) {
33311
+ mkdirSync2(dir, { recursive: true });
33312
+ }
33313
+ const payload = { ...readHomeConfig(registryPath), projects: registry2.projects.map(toYaml) };
33314
+ writeFileSync(registryPath, stringifyYaml(payload), "utf-8");
33315
+ }
33316
+ function readHomeConfig(configPath2) {
33317
+ if (!existsSync4(configPath2)) return {};
33318
+ try {
33319
+ const parsed = parseYamlValue(readFileSync22(configPath2, "utf-8"));
33320
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
33321
+ } catch {
33322
+ return {};
33323
+ }
33324
+ }
33325
+ function deriveProjectId(dirPath, existingIds) {
33326
+ const base = path36.basename(dirPath).toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
33327
+ let candidate = base || "project";
33328
+ let suffix = 2;
33329
+ while (existingIds.includes(candidate)) {
33330
+ candidate = `${base}-${suffix}`;
33331
+ suffix++;
33332
+ }
33333
+ return candidate;
33334
+ }
33335
+ function addProject(projectPath) {
33336
+ const absPath = path36.resolve(projectPath);
33337
+ if (!existsSync4(absPath)) {
33338
+ throw new Error(`Directory not found: ${absPath}`);
33339
+ }
33340
+ if (!existsSync4(path36.join(absPath, ".agentv"))) {
33341
+ throw new Error(`No .agentv/ directory found in ${absPath}. Run an evaluation first.`);
33342
+ }
33343
+ const registry2 = loadProjectRegistry();
33344
+ const existing = registry2.projects.find((p) => p.path === absPath);
33345
+ if (existing) {
33346
+ return existing;
33347
+ }
33348
+ const now = (/* @__PURE__ */ new Date()).toISOString();
33349
+ const entry = {
33350
+ id: deriveProjectId(
33351
+ absPath,
33352
+ registry2.projects.map((p) => p.id)
33353
+ ),
33354
+ name: path36.basename(absPath),
33355
+ path: absPath,
33356
+ addedAt: now,
33357
+ lastOpenedAt: now
33358
+ };
33359
+ registry2.projects.push(entry);
33360
+ saveProjectRegistry(registry2);
33361
+ return entry;
33362
+ }
33363
+ function removeProject(projectId) {
33364
+ const registry2 = loadProjectRegistry();
33365
+ const idx = registry2.projects.findIndex((p) => p.id === projectId);
33366
+ if (idx < 0) return false;
33367
+ registry2.projects.splice(idx, 1);
33368
+ saveProjectRegistry(registry2);
33369
+ return true;
33370
+ }
33371
+ function getProject(projectId) {
33372
+ return loadProjectRegistry().projects.find((p) => p.id === projectId);
33373
+ }
33374
+ function getProjectForPath(fsPath) {
33375
+ const absPath = path36.resolve(fsPath);
33376
+ return loadProjectRegistry().projects.filter((p) => {
33377
+ const projectPath = path36.resolve(p.path);
33378
+ const relative = path36.relative(projectPath, absPath);
33379
+ return relative === "" || !relative.startsWith("..") && !path36.isAbsolute(relative);
33380
+ }).sort((a, b) => path36.resolve(b.path).length - path36.resolve(a.path).length)[0];
33381
+ }
33382
+ function touchProject(projectId) {
33383
+ const registry2 = loadProjectRegistry();
33384
+ const entry = registry2.projects.find((p) => p.id === projectId);
33385
+ if (entry) {
33386
+ entry.lastOpenedAt = (/* @__PURE__ */ new Date()).toISOString();
33387
+ saveProjectRegistry(registry2);
33388
+ }
33389
+ }
33390
+ function discoverProjects(rootDir, maxDepth = 2) {
33391
+ const absRoot = path36.resolve(rootDir);
33392
+ if (!existsSync4(absRoot) || !statSync2(absRoot).isDirectory()) {
33393
+ return [];
33394
+ }
33395
+ const results = [];
33396
+ function scan(dir, depth) {
33397
+ if (depth > maxDepth) return;
33398
+ if (existsSync4(path36.join(dir, ".agentv"))) {
33399
+ results.push(dir);
33400
+ return;
33401
+ }
33402
+ if (depth === maxDepth) return;
33403
+ try {
33404
+ const entries = readdirSync3(dir, { withFileTypes: true });
33405
+ for (const entry of entries) {
33406
+ if (!entry.isDirectory()) continue;
33407
+ if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
33408
+ scan(path36.join(dir, entry.name), depth + 1);
33409
+ }
33410
+ } catch {
33411
+ }
33412
+ }
33413
+ scan(absRoot, 0);
33414
+ return results.sort();
33415
+ }
33416
+ var execFileAsync = promisify5(execFile);
33236
33417
  var DEFAULT_TIMEOUT_MS2 = 3e5;
33237
- function gitEnv2() {
33418
+ var DEFAULT_HEARTBEAT_MS = 3e4;
33419
+ var ERROR_OUTPUT_LIMIT = 1024 * 1024;
33420
+ var LOCK_POLL_MS = 100;
33421
+ function gitEnv() {
33238
33422
  const env = { ...process.env };
33239
33423
  for (const key of Object.keys(env)) {
33240
33424
  if (key.startsWith("GIT_") && key !== "GIT_SSH_COMMAND") {
@@ -33248,64 +33432,62 @@ function gitEnv2() {
33248
33432
  GIT_SSH_COMMAND: "ssh -o BatchMode=yes"
33249
33433
  };
33250
33434
  }
33251
- function getSourceUrl(source) {
33252
- return source.type === "git" ? source.url : source.path;
33435
+ function appendLimited(current, chunk) {
33436
+ if (current.length >= ERROR_OUTPUT_LIMIT) return current;
33437
+ const next = current + chunk.toString();
33438
+ return next.length > ERROR_OUTPUT_LIMIT ? next.slice(-ERROR_OUTPUT_LIMIT) : next;
33439
+ }
33440
+ function formatDuration(ms) {
33441
+ const seconds = Math.round(ms / 1e3);
33442
+ if (seconds < 60) return `${seconds}s`;
33443
+ const minutes = Math.floor(seconds / 60);
33444
+ const remainder = seconds % 60;
33445
+ return remainder === 0 ? `${minutes}m` : `${minutes}m ${remainder}s`;
33253
33446
  }
33254
33447
  function isFullCommitSha(ref) {
33255
33448
  return typeof ref === "string" && /^[0-9a-f]{40}$/i.test(ref);
33256
33449
  }
33257
- async function git2(args, opts) {
33258
- const { stdout } = await execFileAsync2("git", args, {
33450
+ function assertSafeGitOperand(value, label) {
33451
+ if (value.length === 0) {
33452
+ throw new Error(`${label} must not be empty.`);
33453
+ }
33454
+ if (value.includes("\0")) {
33455
+ throw new Error(`${label} must not contain NUL bytes.`);
33456
+ }
33457
+ if (value.startsWith("-")) {
33458
+ throw new Error(`${label} must not start with '-'.`);
33459
+ }
33460
+ }
33461
+ function sleep2(ms) {
33462
+ return new Promise((resolve) => setTimeout(resolve, ms));
33463
+ }
33464
+ function configPath() {
33465
+ return path37.join(getAgentvConfigDir(), "config.yaml");
33466
+ }
33467
+ function expandHome(value) {
33468
+ if (value === "~") return process.env.HOME ?? value;
33469
+ if (value.startsWith("~/")) return path37.join(process.env.HOME ?? "~", value.slice(2));
33470
+ return value;
33471
+ }
33472
+ async function git(args, opts) {
33473
+ const { stdout } = await execFileAsync("git", args, {
33259
33474
  cwd: opts?.cwd,
33260
33475
  timeout: opts?.timeout ?? DEFAULT_TIMEOUT_MS2,
33261
- env: gitEnv2(),
33476
+ env: gitEnv(),
33262
33477
  maxBuffer: 50 * 1024 * 1024
33263
- // 50MB
33264
33478
  });
33265
33479
  return stdout.trim();
33266
33480
  }
33267
33481
  var RepoManager = class {
33268
33482
  verbose;
33269
- constructor(verbose = false) {
33483
+ progress;
33484
+ heartbeatMs;
33485
+ timeoutMs;
33486
+ constructor(verbose = false, options = {}) {
33270
33487
  this.verbose = verbose;
33271
- }
33272
- /**
33273
- * Validate that all local repo source paths exist before attempting materialization.
33274
- * Returns an array of validation errors (empty if all paths are valid).
33275
- */
33276
- static validateLocalPaths(repos) {
33277
- const errors = [];
33278
- for (const repo of repos) {
33279
- if (!repo.source || repo.source.type !== "local") continue;
33280
- const sourcePath = repo.source.path;
33281
- if (!sourcePath || sourcePath.trim() === "") {
33282
- errors.push({
33283
- repoPath: repo.path ?? "(none)",
33284
- resolvedSourcePath: sourcePath ?? "",
33285
- reason: "empty_path"
33286
- });
33287
- } else if (!existsSync4(sourcePath)) {
33288
- errors.push({
33289
- repoPath: repo.path ?? "(none)",
33290
- resolvedSourcePath: sourcePath,
33291
- reason: "not_found"
33292
- });
33293
- }
33294
- }
33295
- return errors;
33296
- }
33297
- /**
33298
- * Format validation errors into a human-readable warning message.
33299
- */
33300
- static formatValidationErrors(errors) {
33301
- const lines = errors.map((e) => {
33302
- if (e.reason === "empty_path") {
33303
- return ` - repo "${e.repoPath}": local source path is empty (check that the env var is set)`;
33304
- }
33305
- return ` - repo "${e.repoPath}": local source path not found: ${e.resolvedSourcePath}`;
33306
- });
33307
- return `Local repo path validation failed:
33308
- ${lines.join("\n")}`;
33488
+ this.progress = options.progress ?? true;
33489
+ this.heartbeatMs = options.heartbeatMs ?? DEFAULT_HEARTBEAT_MS;
33490
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
33309
33491
  }
33310
33492
  async runGit(args, opts) {
33311
33493
  const startedAt = Date.now();
@@ -33313,7 +33495,7 @@ ${lines.join("\n")}`;
33313
33495
  console.log(`[repo] git start cwd=${opts?.cwd ?? process.cwd()} args=${args.join(" ")}`);
33314
33496
  }
33315
33497
  try {
33316
- const output = await git2(args, opts);
33498
+ const output = await git(args, { ...opts, timeout: opts?.timeout ?? this.timeoutMs });
33317
33499
  if (this.verbose) {
33318
33500
  console.log(`[repo] git ok durationMs=${Date.now() - startedAt} args=${args.join(" ")}`);
33319
33501
  }
@@ -33328,100 +33510,370 @@ ${lines.join("\n")}`;
33328
33510
  throw error40;
33329
33511
  }
33330
33512
  }
33513
+ runGitStreaming(args, opts) {
33514
+ const startedAt = Date.now();
33515
+ const timeout = opts.timeout ?? this.timeoutMs;
33516
+ if (this.verbose) {
33517
+ console.log(`[repo] git start cwd=${opts.cwd ?? process.cwd()} args=${args.join(" ")}`);
33518
+ }
33519
+ return new Promise((resolve, reject) => {
33520
+ let stdout = "";
33521
+ let stderr = "";
33522
+ let settled = false;
33523
+ let timedOut = false;
33524
+ const child = spawn5("git", args, {
33525
+ cwd: opts.cwd,
33526
+ env: gitEnv(),
33527
+ stdio: ["ignore", "pipe", "pipe"]
33528
+ });
33529
+ let timeoutHandle;
33530
+ const resetIdleTimeout = () => {
33531
+ if (timeoutHandle) clearTimeout(timeoutHandle);
33532
+ timeoutHandle = setTimeout(() => {
33533
+ timedOut = true;
33534
+ child.kill("SIGTERM");
33535
+ }, timeout);
33536
+ };
33537
+ resetIdleTimeout();
33538
+ const heartbeatHandle = this.progress && this.heartbeatMs > 0 ? setInterval(() => {
33539
+ const elapsed = formatDuration(Date.now() - startedAt);
33540
+ console.error(`[repo] ${opts.description} still running after ${elapsed}`);
33541
+ }, this.heartbeatMs) : void 0;
33542
+ const finish = (error40) => {
33543
+ if (settled) return;
33544
+ settled = true;
33545
+ if (timeoutHandle) clearTimeout(timeoutHandle);
33546
+ if (heartbeatHandle) clearInterval(heartbeatHandle);
33547
+ if (error40) {
33548
+ reject(error40);
33549
+ } else {
33550
+ resolve();
33551
+ }
33552
+ };
33553
+ child.stdout.on("data", (chunk) => {
33554
+ stdout = appendLimited(stdout, chunk);
33555
+ if (!timedOut) resetIdleTimeout();
33556
+ if (this.progress) process.stdout.write(chunk);
33557
+ });
33558
+ child.stderr.on("data", (chunk) => {
33559
+ stderr = appendLimited(stderr, chunk);
33560
+ if (!timedOut) resetIdleTimeout();
33561
+ if (this.progress) process.stderr.write(chunk);
33562
+ });
33563
+ child.on("error", (error40) => finish(error40));
33564
+ child.on("close", (code, signal) => {
33565
+ const durationMs = Date.now() - startedAt;
33566
+ if (this.verbose) {
33567
+ console.log(
33568
+ `[repo] git ${code === 0 ? "ok" : "fail"} durationMs=${durationMs} args=${args.join(" ")}`
33569
+ );
33570
+ }
33571
+ if (timedOut) {
33572
+ finish(
33573
+ new Error(
33574
+ `${opts.description} made no progress for ${formatDuration(timeout)}. Register a matching local checkout, configure git_cache.mirrors in ${configPath()}, or check network connectivity.`
33575
+ )
33576
+ );
33577
+ return;
33578
+ }
33579
+ if (code !== 0) {
33580
+ const output = [stderr.trim(), stdout.trim()].filter(Boolean).join("\n");
33581
+ finish(
33582
+ new Error(
33583
+ `git ${args.join(" ")} failed with code ${code ?? "unknown"}${signal ? ` (signal ${signal})` : ""}${output ? `:
33584
+ ${output}` : ""}`
33585
+ )
33586
+ );
33587
+ return;
33588
+ }
33589
+ finish();
33590
+ });
33591
+ });
33592
+ }
33593
+ loadConfiguredMirrors() {
33594
+ const filePath = configPath();
33595
+ if (!existsSync5(filePath)) return {};
33596
+ try {
33597
+ const parsed = parseYamlValue(readFileSync3(filePath, "utf-8"));
33598
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
33599
+ const config2 = parsed;
33600
+ const gitCache = config2.git_cache;
33601
+ if (!gitCache || typeof gitCache !== "object" || Array.isArray(gitCache)) return {};
33602
+ const mirrors = gitCache.mirrors;
33603
+ if (!mirrors || typeof mirrors !== "object" || Array.isArray(mirrors)) return {};
33604
+ const result = {};
33605
+ for (const [repo, localPath] of Object.entries(mirrors)) {
33606
+ if (typeof localPath === "string" && localPath.trim().length > 0) {
33607
+ result[repo] = expandHome(localPath.trim());
33608
+ }
33609
+ }
33610
+ return result;
33611
+ } catch {
33612
+ return {};
33613
+ }
33614
+ }
33615
+ findConfiguredMirror(repoIdentity) {
33616
+ const mirrors = this.loadConfiguredMirrors();
33617
+ for (const [repo, localPath] of Object.entries(mirrors)) {
33618
+ if (normalizeRepoIdentity(repo) !== repoIdentity) continue;
33619
+ if (!existsSync5(localPath)) {
33620
+ console.warn(`[repo] configured mirror not found, falling back: ${localPath}`);
33621
+ continue;
33622
+ }
33623
+ return localPath;
33624
+ }
33625
+ return void 0;
33626
+ }
33627
+ async findRegisteredProject(repoIdentity) {
33628
+ for (const project of loadProjectRegistry().projects) {
33629
+ if (!existsSync5(project.path)) continue;
33630
+ try {
33631
+ const origin = await this.runGit(["remote", "get-url", "origin"], {
33632
+ cwd: project.path,
33633
+ timeout: 1e4
33634
+ });
33635
+ if (normalizeRepoIdentity(origin) === repoIdentity) {
33636
+ return project.path;
33637
+ }
33638
+ } catch {
33639
+ if (project.repoUrl && normalizeRepoIdentity(project.repoUrl) === repoIdentity) {
33640
+ return project.path;
33641
+ }
33642
+ }
33643
+ }
33644
+ return void 0;
33645
+ }
33646
+ gitCachePath(repoIdentity) {
33647
+ const hash = createHash4("sha256").update(repoIdentity).digest("hex");
33648
+ return path37.join(getAgentvDataDir(), "git-cache", hash);
33649
+ }
33650
+ async withMirrorCacheLock(mirrorPath, action) {
33651
+ const lockPath = `${mirrorPath}.lock`;
33652
+ const startedAt = Date.now();
33653
+ while (true) {
33654
+ try {
33655
+ await mkdir16(lockPath);
33656
+ break;
33657
+ } catch (error40) {
33658
+ const code = error40.code;
33659
+ if (code !== "EEXIST") throw error40;
33660
+ if (Date.now() - startedAt > this.timeoutMs) {
33661
+ throw new Error(`Timed out waiting for git cache lock: ${lockPath}`);
33662
+ }
33663
+ await sleep2(LOCK_POLL_MS);
33664
+ }
33665
+ }
33666
+ try {
33667
+ return await action();
33668
+ } finally {
33669
+ await rm6(lockPath, { recursive: true, force: true });
33670
+ }
33671
+ }
33672
+ async isValidBareRepo(repoPath) {
33673
+ if (!existsSync5(path37.join(repoPath, "HEAD"))) return false;
33674
+ try {
33675
+ return await this.runGit(["rev-parse", "--is-bare-repository"], {
33676
+ cwd: repoPath,
33677
+ timeout: 1e4
33678
+ }) === "true";
33679
+ } catch {
33680
+ return false;
33681
+ }
33682
+ }
33683
+ async removeInvalidMirrorCache(mirrorPath) {
33684
+ if (!existsSync5(mirrorPath)) return;
33685
+ const quarantinePath = `${mirrorPath}.invalid-${process.pid}-${Date.now()}-${randomUUID9()}`;
33686
+ try {
33687
+ await rename(mirrorPath, quarantinePath);
33688
+ await rm6(quarantinePath, { recursive: true, force: true });
33689
+ } catch {
33690
+ await rm6(mirrorPath, { recursive: true, force: true });
33691
+ }
33692
+ }
33693
+ async prepareMirrorCache(seedSource, repoIdentity) {
33694
+ assertSafeGitOperand(seedSource, "repo clone source");
33695
+ const mirrorPath = this.gitCachePath(repoIdentity);
33696
+ try {
33697
+ await mkdir16(path37.dirname(mirrorPath), { recursive: true });
33698
+ return await this.withMirrorCacheLock(mirrorPath, async () => {
33699
+ if (await this.isValidBareRepo(mirrorPath)) {
33700
+ try {
33701
+ await this.runGit(["remote", "set-url", "origin", seedSource], {
33702
+ cwd: mirrorPath,
33703
+ timeout: 1e4
33704
+ });
33705
+ await this.runGitStreaming(["fetch", "--prune", "--progress", "origin"], {
33706
+ cwd: mirrorPath,
33707
+ description: `git fetch cache for ${seedSource}`
33708
+ });
33709
+ } catch (error40) {
33710
+ const message = error40 instanceof Error ? error40.message : String(error40);
33711
+ console.warn(`[repo] mirror cache fetch failed; using existing cache: ${message}`);
33712
+ }
33713
+ return mirrorPath;
33714
+ }
33715
+ await this.removeInvalidMirrorCache(mirrorPath);
33716
+ const tempPath = path37.join(
33717
+ path37.dirname(mirrorPath),
33718
+ `${path37.basename(mirrorPath)}.tmp-${process.pid}-${Date.now()}-${randomUUID9()}`
33719
+ );
33720
+ try {
33721
+ await this.runGitStreaming(
33722
+ ["clone", "--mirror", "--progress", "--", seedSource, tempPath],
33723
+ {
33724
+ description: `git mirror clone ${seedSource}`
33725
+ }
33726
+ );
33727
+ if (!await this.isValidBareRepo(tempPath)) {
33728
+ throw new Error(`git mirror clone did not create a valid bare repo at ${tempPath}`);
33729
+ }
33730
+ await rename(tempPath, mirrorPath);
33731
+ return mirrorPath;
33732
+ } finally {
33733
+ await rm6(tempPath, { recursive: true, force: true });
33734
+ }
33735
+ });
33736
+ } catch (error40) {
33737
+ const message = error40 instanceof Error ? error40.message : String(error40);
33738
+ console.warn(`[repo] mirror cache unavailable; trying next acquisition source: ${message}`);
33739
+ return void 0;
33740
+ }
33741
+ }
33742
+ async resolveCommit(ref, cwd) {
33743
+ assertSafeGitOperand(ref, "repo checkout ref");
33744
+ const candidates = [ref];
33745
+ if (!ref.startsWith("refs/") && !ref.startsWith("origin/") && !isFullCommitSha(ref)) {
33746
+ candidates.push(`refs/remotes/origin/${ref}`);
33747
+ }
33748
+ for (const candidate of candidates) {
33749
+ try {
33750
+ return await this.runGit(
33751
+ ["rev-parse", "--verify", "--end-of-options", `${candidate}^{commit}`],
33752
+ { cwd }
33753
+ );
33754
+ } catch {
33755
+ }
33756
+ }
33757
+ throw new Error(`Cannot resolve ref '${ref}' to a commit.`);
33758
+ }
33759
+ async resolveCheckoutCommit(repo, targetDir) {
33760
+ const ref = getRepoCheckoutRef(repo);
33761
+ const checkoutSha = await this.resolveCommit(ref, targetDir);
33762
+ const ancestor = repo.ancestor ?? 0;
33763
+ if (ancestor === 0) {
33764
+ return checkoutSha;
33765
+ }
33766
+ try {
33767
+ return await this.resolveCommit(`${checkoutSha}~${ancestor}`, targetDir);
33768
+ } catch {
33769
+ const shallowHint = isFullCommitSha(ref) ? "" : " Ensure the declared commit has enough reachable history in the selected repo.";
33770
+ throw new Error(`Cannot resolve ancestor ${ancestor} of ref '${ref}'.${shallowHint}`);
33771
+ }
33772
+ }
33773
+ assertNoUserOwnedAlternates(targetDir, acquisition) {
33774
+ const alternatesPath = path37.join(targetDir, ".git", "objects", "info", "alternates");
33775
+ if (!existsSync5(alternatesPath)) return;
33776
+ const alternates = readFileSync3(alternatesPath, "utf-8").trim();
33777
+ if (alternates.length > 0) {
33778
+ throw new Error(
33779
+ `git clone for ${acquisition.kind} left an alternates dependency at ${alternatesPath}`
33780
+ );
33781
+ }
33782
+ }
33783
+ async resolveAcquisition(repo) {
33784
+ const declaredRepo = repo.repo;
33785
+ if (!declaredRepo) {
33786
+ throw new Error(`repo is required for workspace repo at path ${repo.path ?? "(none)"}`);
33787
+ }
33788
+ const originUrl = resolveRepoCloneUrl(declaredRepo);
33789
+ const repoIdentity = normalizeRepoIdentity(declaredRepo);
33790
+ const registeredProject = await this.findRegisteredProject(repoIdentity);
33791
+ if (registeredProject) {
33792
+ const mirrorCache2 = await this.prepareMirrorCache(registeredProject, repoIdentity);
33793
+ if (mirrorCache2) {
33794
+ return {
33795
+ kind: "registered-project",
33796
+ sourceUrl: mirrorCache2,
33797
+ originUrl
33798
+ };
33799
+ }
33800
+ }
33801
+ const configuredMirror = this.findConfiguredMirror(repoIdentity);
33802
+ if (configuredMirror) {
33803
+ const mirrorCache2 = await this.prepareMirrorCache(configuredMirror, repoIdentity);
33804
+ if (mirrorCache2) {
33805
+ return {
33806
+ kind: "configured-mirror",
33807
+ sourceUrl: mirrorCache2,
33808
+ originUrl
33809
+ };
33810
+ }
33811
+ }
33812
+ const mirrorCache = await this.prepareMirrorCache(originUrl, repoIdentity);
33813
+ if (mirrorCache) {
33814
+ return {
33815
+ kind: "mirror-cache",
33816
+ sourceUrl: mirrorCache,
33817
+ originUrl
33818
+ };
33819
+ }
33820
+ return { kind: "remote", sourceUrl: originUrl, originUrl };
33821
+ }
33331
33822
  /**
33332
- * Clone a repo directly from source into the workspace at the configured path.
33333
- * Handles checkout, ref resolution, ancestor walking, shallow clone, sparse checkout.
33823
+ * Clone a repo into the workspace at the configured path.
33824
+ * Handles acquisition resolution, sparse checkout, commit checkout, and ancestor walking.
33334
33825
  */
33335
33826
  async materialize(repo, workspacePath) {
33336
- if (!repo.source || !repo.path) {
33827
+ if (!repo.repo || !repo.path) {
33337
33828
  if (this.verbose) {
33338
- console.log(`[repo] materialize skip path=${repo.path ?? "(none)"} (no source or path)`);
33829
+ console.log(`[repo] materialize skip path=${repo.path ?? "(none)"} (no repo or path)`);
33339
33830
  }
33340
33831
  return;
33341
33832
  }
33342
- const targetDir = path36.join(workspacePath, repo.path);
33343
- const sourceUrl = getSourceUrl(repo.source);
33833
+ const targetDir = path37.join(workspacePath, repo.path);
33834
+ const acquisition = await this.resolveAcquisition(repo);
33344
33835
  const startedAt = Date.now();
33345
33836
  if (this.verbose) {
33346
33837
  console.log(
33347
- `[repo] materialize start path=${repo.path} source=${sourceUrl} workspace=${workspacePath}`
33838
+ `[repo] materialize start path=${repo.path} repo=${repo.repo} acquisition=${acquisition.kind} workspace=${workspacePath}`
33348
33839
  );
33349
33840
  }
33350
- const cloneArgs = ["clone"];
33351
- if (repo.clone?.depth) {
33352
- cloneArgs.push("--depth", String(repo.clone.depth));
33353
- }
33354
- if (repo.clone?.filter) {
33355
- cloneArgs.push("--filter", repo.clone.filter);
33841
+ const cloneArgs = ["clone", "--progress", "--no-checkout"];
33842
+ assertSafeGitOperand(acquisition.sourceUrl, "repo clone source");
33843
+ cloneArgs.push("--", acquisition.sourceUrl, targetDir);
33844
+ await this.runGitStreaming(cloneArgs, {
33845
+ description: `git clone ${repo.repo}`
33846
+ });
33847
+ this.assertNoUserOwnedAlternates(targetDir, acquisition);
33848
+ if (acquisition.sourceUrl !== acquisition.originUrl) {
33849
+ assertSafeGitOperand(acquisition.originUrl, "repo origin URL");
33850
+ await this.runGit(["remote", "set-url", "origin", acquisition.originUrl], { cwd: targetDir });
33356
33851
  }
33357
- cloneArgs.push("--no-checkout");
33358
- const cloneUrl = (repo.clone?.depth || repo.clone?.filter) && repo.source.type === "local" ? `file://${sourceUrl}` : sourceUrl;
33359
- cloneArgs.push(cloneUrl, targetDir);
33360
- await this.runGit(cloneArgs);
33361
- if (repo.clone?.sparse?.length) {
33362
- await this.runGit(["sparse-checkout", "init", "--cone"], { cwd: targetDir });
33363
- await this.runGit(["sparse-checkout", "set", ...repo.clone.sparse], { cwd: targetDir });
33364
- }
33365
- const ref = getRepoCheckoutRef(repo.checkout);
33366
- const resolve = repo.checkout?.resolve ?? "remote";
33367
- const baseCommit = repo.checkout?.base_commit;
33368
- const shouldResolveLocally = resolve === "local" || repo.source.type === "git" && isFullCommitSha(baseCommit);
33369
- let resolvedSha;
33370
- if (!shouldResolveLocally && repo.source.type === "git") {
33371
- const url2 = getSourceUrl(repo.source);
33372
- try {
33373
- const lsOutput = await this.runGit(["ls-remote", url2, ref]);
33374
- const match = lsOutput.split(" ")[0];
33375
- if (!match) {
33376
- throw new Error(`Ref '${ref}' not found on remote ${url2}`);
33377
- }
33378
- resolvedSha = match;
33379
- } catch (err) {
33380
- if (err instanceof Error && err.message.includes("not found")) throw err;
33381
- resolvedSha = ref;
33852
+ if (repo.sparse?.length) {
33853
+ for (const sparsePath of repo.sparse) {
33854
+ assertSafeGitOperand(sparsePath, "repo sparse path");
33382
33855
  }
33383
- } else {
33384
- resolvedSha = ref;
33856
+ await this.runGit(["sparse-checkout", "init", "--cone"], { cwd: targetDir });
33857
+ await this.runGit(["sparse-checkout", "set", "--", ...repo.sparse], { cwd: targetDir });
33385
33858
  }
33859
+ const ref = getRepoCheckoutRef(repo);
33386
33860
  if (this.verbose) {
33387
- console.log(
33388
- `[repo] checkout path=${repo.path} ref=${ref} resolved=${resolvedSha} resolve=${resolve}`
33389
- );
33390
- }
33391
- await this.runGit(["checkout", resolvedSha], { cwd: targetDir });
33392
- const ancestor = repo.checkout?.ancestor ?? 0;
33393
- if (ancestor > 0) {
33394
- try {
33395
- const ancestorSha = await this.runGit(["rev-parse", `HEAD~${ancestor}`], {
33396
- cwd: targetDir
33397
- });
33398
- await this.runGit(["checkout", ancestorSha], { cwd: targetDir });
33399
- } catch {
33400
- if (repo.clone?.depth) {
33401
- await this.runGit(["fetch", "--deepen", String(ancestor)], { cwd: targetDir });
33402
- const ancestorSha = await this.runGit(["rev-parse", `HEAD~${ancestor}`], {
33403
- cwd: targetDir
33404
- });
33405
- await this.runGit(["checkout", ancestorSha], { cwd: targetDir });
33406
- } else {
33407
- throw new Error(
33408
- `Cannot resolve ancestor ${ancestor} of ref '${ref}'. If using shallow clone, increase clone.depth to at least ${ancestor + 1}.`
33409
- );
33410
- }
33411
- }
33861
+ console.log(`[repo] checkout path=${repo.path} ref=${ref}`);
33412
33862
  }
33863
+ const checkoutSha = await this.resolveCheckoutCommit(repo, targetDir);
33864
+ await this.runGit(["checkout", "--detach", checkoutSha], { cwd: targetDir });
33413
33865
  if (this.verbose) {
33414
33866
  console.log(
33415
33867
  `[repo] materialize done path=${repo.path} target=${targetDir} durationMs=${Date.now() - startedAt}`
33416
33868
  );
33417
33869
  }
33418
33870
  }
33419
- /** Materialize all repos into the workspace. Skips repos without source (Docker-only repos). */
33871
+ /** Materialize all repos into the workspace. Skips repos without repo (Docker-only repos). */
33420
33872
  async materializeAll(repos, workspacePath) {
33421
- const materializableRepos = repos.filter((r) => r.source);
33873
+ const materializableRepos = repos.filter((r) => r.repo);
33422
33874
  if (this.verbose) {
33423
33875
  console.log(
33424
- `[repo] materializeAll count=${materializableRepos.length} (${repos.length - materializableRepos.length} skipped, no source) workspace=${workspacePath}`
33876
+ `[repo] materializeAll count=${materializableRepos.length} (${repos.length - materializableRepos.length} skipped, no repo) workspace=${workspacePath}`
33425
33877
  );
33426
33878
  }
33427
33879
  for (const repo of materializableRepos) {
@@ -33431,13 +33883,14 @@ ${lines.join("\n")}`;
33431
33883
  console.log("[repo] materializeAll complete");
33432
33884
  }
33433
33885
  }
33434
- /** Reset repos in workspace to their checkout state. Skips repos without path or source. */
33886
+ /** Reset repos in workspace to their checkout state. Skips repos without path or repo. */
33435
33887
  async reset(repos, workspacePath, reset) {
33436
33888
  const cleanFlag = reset === "strict" ? "-fdx" : "-fd";
33437
33889
  for (const repo of repos) {
33438
- if (!repo.path || !repo.source) continue;
33439
- const targetDir = path36.join(workspacePath, repo.path);
33440
- await this.runGit(["reset", "--hard", "HEAD"], { cwd: targetDir });
33890
+ if (!repo.path || !repo.repo) continue;
33891
+ const targetDir = path37.join(workspacePath, repo.path);
33892
+ const resetSha = await this.resolveCheckoutCommit(repo, targetDir);
33893
+ await this.runGit(["reset", "--hard", resetSha], { cwd: targetDir });
33441
33894
  await this.runGit(["clean", cleanFlag], { cwd: targetDir });
33442
33895
  }
33443
33896
  }
@@ -33446,11 +33899,11 @@ async function resolveWorkspaceTemplate(templatePath) {
33446
33899
  if (!templatePath) {
33447
33900
  return void 0;
33448
33901
  }
33449
- const resolved = path37.resolve(templatePath);
33902
+ const resolved = path38.resolve(templatePath);
33450
33903
  const stats = await stat7(resolved);
33451
33904
  if (stats.isFile()) {
33452
33905
  return {
33453
- dir: path37.dirname(resolved),
33906
+ dir: path38.dirname(resolved),
33454
33907
  workspaceFile: resolved
33455
33908
  };
33456
33909
  }
@@ -33462,14 +33915,14 @@ async function resolveWorkspaceTemplate(templatePath) {
33462
33915
  if (workspaceFiles.length === 1) {
33463
33916
  return {
33464
33917
  dir: resolved,
33465
- workspaceFile: path37.join(resolved, workspaceFiles[0])
33918
+ workspaceFile: path38.join(resolved, workspaceFiles[0])
33466
33919
  };
33467
33920
  }
33468
33921
  if (workspaceFiles.length > 1) {
33469
33922
  const conventionFile = workspaceFiles.find((f) => f === "template.code-workspace");
33470
33923
  return {
33471
33924
  dir: resolved,
33472
- workspaceFile: conventionFile ? path37.join(resolved, conventionFile) : void 0
33925
+ workspaceFile: conventionFile ? path38.join(resolved, conventionFile) : void 0
33473
33926
  };
33474
33927
  }
33475
33928
  return { dir: resolved };
@@ -33595,7 +34048,7 @@ async function loadTestsFromAgentSkills(filePath) {
33595
34048
  } catch {
33596
34049
  throw new Error(`Invalid Agent Skills evals.json: failed to parse JSON in '${filePath}'`);
33597
34050
  }
33598
- return parseAgentSkillsEvals(parsed, filePath, path38.dirname(path38.resolve(filePath)));
34051
+ return parseAgentSkillsEvals(parsed, filePath, path39.dirname(path39.resolve(filePath)));
33599
34052
  }
33600
34053
  function parseAgentSkillsEvals(parsed, source = "evals.json", baseDir) {
33601
34054
  if (!isAgentSkillsFormat(parsed)) {
@@ -33633,7 +34086,7 @@ function parseAgentSkillsEvals(parsed, source = "evals.json", baseDir) {
33633
34086
  if (baseDir) {
33634
34087
  metadata.agent_skills_base_dir = baseDir;
33635
34088
  for (const file2 of evalCase.files) {
33636
- filePaths.push(path38.resolve(baseDir, file2));
34089
+ filePaths.push(path39.resolve(baseDir, file2));
33637
34090
  }
33638
34091
  }
33639
34092
  }
@@ -33669,15 +34122,15 @@ function resolveToAbsolutePath(candidate) {
33669
34122
  if (candidate.startsWith("file:")) {
33670
34123
  return fileURLToPath4(candidate);
33671
34124
  }
33672
- return path39.resolve(candidate);
34125
+ return path40.resolve(candidate);
33673
34126
  }
33674
34127
  throw new TypeError("Unsupported repoRoot value. Expected string or URL.");
33675
34128
  }
33676
34129
  function buildDirectoryChain2(filePath, repoRoot) {
33677
34130
  const directories = [];
33678
34131
  const seen = /* @__PURE__ */ new Set();
33679
- const boundary = path39.resolve(repoRoot);
33680
- let current = path39.resolve(path39.dirname(filePath));
34132
+ const boundary = path40.resolve(repoRoot);
34133
+ let current = path40.resolve(path40.dirname(filePath));
33681
34134
  while (current !== void 0) {
33682
34135
  if (!seen.has(current)) {
33683
34136
  directories.push(current);
@@ -33686,7 +34139,7 @@ function buildDirectoryChain2(filePath, repoRoot) {
33686
34139
  if (current === boundary) {
33687
34140
  break;
33688
34141
  }
33689
- const parent = path39.dirname(current);
34142
+ const parent = path40.dirname(current);
33690
34143
  if (parent === current) {
33691
34144
  break;
33692
34145
  }
@@ -33700,16 +34153,16 @@ function buildDirectoryChain2(filePath, repoRoot) {
33700
34153
  function buildSearchRoots2(evalPath, repoRoot) {
33701
34154
  const uniqueRoots = [];
33702
34155
  const addRoot = (root) => {
33703
- const normalized = path39.resolve(root);
34156
+ const normalized = path40.resolve(root);
33704
34157
  if (!uniqueRoots.includes(normalized)) {
33705
34158
  uniqueRoots.push(normalized);
33706
34159
  }
33707
34160
  };
33708
- let currentDir = path39.dirname(evalPath);
34161
+ let currentDir = path40.dirname(evalPath);
33709
34162
  let reachedBoundary = false;
33710
34163
  while (!reachedBoundary) {
33711
34164
  addRoot(currentDir);
33712
- const parentDir = path39.dirname(currentDir);
34165
+ const parentDir = path40.dirname(currentDir);
33713
34166
  if (currentDir === repoRoot || parentDir === currentDir) {
33714
34167
  reachedBoundary = true;
33715
34168
  } else {
@@ -33727,16 +34180,16 @@ function trimLeadingSeparators2(value) {
33727
34180
  async function resolveFileReference3(rawValue, searchRoots) {
33728
34181
  const displayPath = trimLeadingSeparators2(rawValue);
33729
34182
  const potentialPaths = [];
33730
- if (path39.isAbsolute(rawValue)) {
33731
- potentialPaths.push(path39.normalize(rawValue));
34183
+ if (path40.isAbsolute(rawValue)) {
34184
+ potentialPaths.push(path40.normalize(rawValue));
33732
34185
  }
33733
34186
  for (const base of searchRoots) {
33734
- potentialPaths.push(path39.resolve(base, displayPath));
34187
+ potentialPaths.push(path40.resolve(base, displayPath));
33735
34188
  }
33736
34189
  const attempted = [];
33737
34190
  const seen = /* @__PURE__ */ new Set();
33738
34191
  for (const candidate of potentialPaths) {
33739
- const absoluteCandidate = path39.resolve(candidate);
34192
+ const absoluteCandidate = path40.resolve(candidate);
33740
34193
  if (seen.has(absoluteCandidate)) {
33741
34194
  continue;
33742
34195
  }
@@ -33757,48 +34210,48 @@ var DEFAULT_EVAL_PATTERNS = [
33757
34210
  ];
33758
34211
  async function loadConfig(evalFilePath, repoRoot) {
33759
34212
  const directories = buildDirectoryChain2(evalFilePath, repoRoot);
33760
- const globalConfigPath = path40.join(getAgentvConfigDir(), "config.yaml");
34213
+ const globalConfigPath = path41.join(getAgentvConfigDir(), "config.yaml");
33761
34214
  for (const directory of directories) {
33762
- const configPath = path40.join(directory, ".agentv", "config.yaml");
33763
- if (!await fileExists3(configPath)) {
34215
+ const configPath2 = path41.join(directory, ".agentv", "config.yaml");
34216
+ if (!await fileExists3(configPath2)) {
33764
34217
  continue;
33765
34218
  }
33766
- const config2 = await readConfigFile(configPath);
34219
+ const config2 = await readConfigFile(configPath2);
33767
34220
  if (config2) {
33768
34221
  return config2;
33769
34222
  }
33770
34223
  }
33771
34224
  return await fileExists3(globalConfigPath) ? readConfigFile(globalConfigPath) : null;
33772
34225
  }
33773
- async function readConfigFile(configPath) {
34226
+ async function readConfigFile(configPath2) {
33774
34227
  try {
33775
- const rawConfig = await readFile13(configPath, "utf8");
34228
+ const rawConfig = await readFile13(configPath2, "utf8");
33776
34229
  const parsed = interpolateEnv(parseYamlValue(rawConfig), process.env);
33777
34230
  if (!isJsonObject(parsed)) {
33778
- logWarning(`Invalid config.yaml format at ${configPath}`);
34231
+ logWarning(`Invalid config.yaml format at ${configPath2}`);
33779
34232
  return null;
33780
34233
  }
33781
34234
  const config2 = parsed;
33782
34235
  const requiredVersion = parsed.required_version;
33783
34236
  if (requiredVersion !== void 0 && typeof requiredVersion !== "string") {
33784
- logWarning(`Invalid required_version in ${configPath}, expected string`);
34237
+ logWarning(`Invalid required_version in ${configPath2}, expected string`);
33785
34238
  return null;
33786
34239
  }
33787
34240
  const evalPatterns = config2.eval_patterns;
33788
34241
  if (evalPatterns !== void 0 && !Array.isArray(evalPatterns)) {
33789
- logWarning(`Invalid eval_patterns in ${configPath}, expected array`);
34242
+ logWarning(`Invalid eval_patterns in ${configPath2}, expected array`);
33790
34243
  return null;
33791
34244
  }
33792
34245
  if (Array.isArray(evalPatterns) && !evalPatterns.every((p) => typeof p === "string")) {
33793
- logWarning(`Invalid eval_patterns in ${configPath}, all entries must be strings`);
34246
+ logWarning(`Invalid eval_patterns in ${configPath2}, all entries must be strings`);
33794
34247
  return null;
33795
34248
  }
33796
34249
  const executionDefaults = parseExecutionDefaults(
33797
34250
  parsed.execution,
33798
- configPath
34251
+ configPath2
33799
34252
  );
33800
- const results = parseResultsConfig(parsed.results, configPath);
33801
- const hooks = parseHooksConfig(parsed.hooks, configPath);
34253
+ const results = parseResultsConfig(parsed.results, configPath2);
34254
+ const hooks = parseHooksConfig(parsed.hooks, configPath2);
33802
34255
  return {
33803
34256
  required_version: requiredVersion,
33804
34257
  eval_patterns: evalPatterns,
@@ -33807,7 +34260,7 @@ async function readConfigFile(configPath) {
33807
34260
  ...hooks && { hooks }
33808
34261
  };
33809
34262
  } catch (error40) {
33810
- logWarning(`Could not read config.yaml at ${configPath}: ${error40.message}`);
34263
+ logWarning(`Could not read config.yaml at ${configPath2}: ${error40.message}`);
33811
34264
  return null;
33812
34265
  }
33813
34266
  }
@@ -34045,7 +34498,7 @@ function extractThreshold(suite) {
34045
34498
  logWarning(`Invalid execution.threshold: ${raw}. Must be a number between 0 and 1. Ignoring.`);
34046
34499
  return void 0;
34047
34500
  }
34048
- function parseExecutionDefaults(raw, configPath) {
34501
+ function parseExecutionDefaults(raw, configPath2) {
34049
34502
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
34050
34503
  return void 0;
34051
34504
  }
@@ -34054,78 +34507,78 @@ function parseExecutionDefaults(raw, configPath) {
34054
34507
  if (typeof obj.verbose === "boolean") {
34055
34508
  result.verbose = obj.verbose;
34056
34509
  } else if (obj.verbose !== void 0) {
34057
- logWarning(`Invalid execution.verbose in ${configPath}, expected boolean`);
34510
+ logWarning(`Invalid execution.verbose in ${configPath2}, expected boolean`);
34058
34511
  }
34059
34512
  if (typeof obj.keep_workspaces === "boolean") {
34060
34513
  result.keep_workspaces = obj.keep_workspaces;
34061
34514
  } else if (obj.keep_workspaces !== void 0) {
34062
- logWarning(`Invalid execution.keep_workspaces in ${configPath}, expected boolean`);
34515
+ logWarning(`Invalid execution.keep_workspaces in ${configPath2}, expected boolean`);
34063
34516
  }
34064
34517
  const otelFile = obj.otel_file;
34065
34518
  if (typeof otelFile === "string" && otelFile.trim().length > 0) {
34066
34519
  result.otel_file = otelFile.trim();
34067
34520
  } else if (otelFile !== void 0) {
34068
- logWarning(`Invalid execution.otel_file in ${configPath}, expected non-empty string`);
34521
+ logWarning(`Invalid execution.otel_file in ${configPath2}, expected non-empty string`);
34069
34522
  }
34070
34523
  if (typeof obj.export_otel === "boolean") {
34071
34524
  result.export_otel = obj.export_otel;
34072
34525
  } else if (obj.export_otel !== void 0) {
34073
- logWarning(`Invalid execution.export_otel in ${configPath}, expected boolean`);
34526
+ logWarning(`Invalid execution.export_otel in ${configPath2}, expected boolean`);
34074
34527
  }
34075
34528
  const otelBackend = obj.otel_backend;
34076
34529
  if (typeof otelBackend === "string" && otelBackend.trim().length > 0) {
34077
34530
  result.otel_backend = otelBackend.trim();
34078
34531
  } else if (otelBackend !== void 0) {
34079
- logWarning(`Invalid execution.otel_backend in ${configPath}, expected non-empty string`);
34532
+ logWarning(`Invalid execution.otel_backend in ${configPath2}, expected non-empty string`);
34080
34533
  }
34081
34534
  if (typeof obj.otel_capture_content === "boolean") {
34082
34535
  result.otel_capture_content = obj.otel_capture_content;
34083
34536
  } else if (obj.otel_capture_content !== void 0) {
34084
- logWarning(`Invalid execution.otel_capture_content in ${configPath}, expected boolean`);
34537
+ logWarning(`Invalid execution.otel_capture_content in ${configPath2}, expected boolean`);
34085
34538
  }
34086
34539
  if (typeof obj.otel_group_turns === "boolean") {
34087
34540
  result.otel_group_turns = obj.otel_group_turns;
34088
34541
  } else if (obj.otel_group_turns !== void 0) {
34089
- logWarning(`Invalid execution.otel_group_turns in ${configPath}, expected boolean`);
34542
+ logWarning(`Invalid execution.otel_group_turns in ${configPath2}, expected boolean`);
34090
34543
  }
34091
34544
  if (typeof obj.pool_workspaces === "boolean") {
34092
34545
  result.pool_workspaces = obj.pool_workspaces;
34093
34546
  } else if (obj.pool_workspaces !== void 0) {
34094
- logWarning(`Invalid execution.pool_workspaces in ${configPath}, expected boolean`);
34547
+ logWarning(`Invalid execution.pool_workspaces in ${configPath2}, expected boolean`);
34095
34548
  }
34096
34549
  const poolSlots = obj.pool_slots;
34097
34550
  if (typeof poolSlots === "number" && Number.isInteger(poolSlots) && poolSlots >= 1 && poolSlots <= 50) {
34098
34551
  result.pool_slots = poolSlots;
34099
34552
  } else if (poolSlots !== void 0) {
34100
- logWarning(`Invalid execution.pool_slots in ${configPath}, expected integer 1-50`);
34553
+ logWarning(`Invalid execution.pool_slots in ${configPath2}, expected integer 1-50`);
34101
34554
  }
34102
34555
  return Object.keys(result).length > 0 ? result : void 0;
34103
34556
  }
34104
34557
  function isFilesystemPath(p) {
34105
34558
  return p.startsWith("/") || p.startsWith("~/") || p.startsWith("~\\") || p === "~" || /^[A-Za-z]:[/\\]/.test(p);
34106
34559
  }
34107
- function parseResultsConfig(raw, configPath) {
34560
+ function parseResultsConfig(raw, configPath2) {
34108
34561
  if (raw === void 0 || raw === null) {
34109
34562
  return void 0;
34110
34563
  }
34111
34564
  if (typeof raw !== "object" || Array.isArray(raw)) {
34112
- logWarning(`Invalid results in ${configPath}, expected object`);
34565
+ logWarning(`Invalid results in ${configPath2}, expected object`);
34113
34566
  return void 0;
34114
34567
  }
34115
34568
  const obj = raw;
34116
34569
  if (obj.mode !== "github") {
34117
- logWarning(`Invalid results.mode in ${configPath}, expected 'github'`);
34570
+ logWarning(`Invalid results.mode in ${configPath2}, expected 'github'`);
34118
34571
  return void 0;
34119
34572
  }
34120
34573
  const repo = typeof obj.repo === "string" ? obj.repo.trim() : "";
34121
34574
  if (!repo) {
34122
- logWarning(`Invalid results.repo in ${configPath}, expected non-empty string`);
34575
+ logWarning(`Invalid results.repo in ${configPath2}, expected non-empty string`);
34123
34576
  return void 0;
34124
34577
  }
34125
34578
  let branch;
34126
34579
  if (obj.branch !== void 0) {
34127
34580
  if (typeof obj.branch !== "string" || obj.branch.trim().length === 0) {
34128
- logWarning(`Invalid results.branch in ${configPath}, expected non-empty string`);
34581
+ logWarning(`Invalid results.branch in ${configPath2}, expected non-empty string`);
34129
34582
  return void 0;
34130
34583
  }
34131
34584
  branch = obj.branch.trim();
@@ -34133,26 +34586,26 @@ function parseResultsConfig(raw, configPath) {
34133
34586
  let resultsPath;
34134
34587
  if (obj.path !== void 0) {
34135
34588
  if (typeof obj.path !== "string" || obj.path.trim().length === 0) {
34136
- logWarning(`Invalid results.path in ${configPath}, expected non-empty string`);
34589
+ logWarning(`Invalid results.path in ${configPath2}, expected non-empty string`);
34137
34590
  return void 0;
34138
34591
  }
34139
34592
  const trimmedPath = obj.path.trim();
34140
34593
  if (!isFilesystemPath(trimmedPath)) {
34141
34594
  logWarning(
34142
- `Invalid results.path in ${configPath}: '${trimmedPath}' looks like a repo subdirectory. results.path now specifies the local filesystem directory for the clone (e.g., ~/data/agentv-results). Remove 'path' to use the default or set an absolute/home-relative path.`
34595
+ `Invalid results.path in ${configPath2}: '${trimmedPath}' looks like a repo subdirectory. results.path now specifies the local filesystem directory for the clone (e.g., ~/data/agentv-results). Remove 'path' to use the default or set an absolute/home-relative path.`
34143
34596
  );
34144
34597
  return void 0;
34145
34598
  }
34146
34599
  resultsPath = trimmedPath;
34147
34600
  }
34148
34601
  if (obj.auto_push !== void 0 && typeof obj.auto_push !== "boolean") {
34149
- logWarning(`Invalid results.auto_push in ${configPath}, expected boolean`);
34602
+ logWarning(`Invalid results.auto_push in ${configPath2}, expected boolean`);
34150
34603
  return void 0;
34151
34604
  }
34152
34605
  let branchPrefix;
34153
34606
  if (obj.branch_prefix !== void 0) {
34154
34607
  if (typeof obj.branch_prefix !== "string" || obj.branch_prefix.trim().length === 0) {
34155
- logWarning(`Invalid results.branch_prefix in ${configPath}, expected non-empty string`);
34608
+ logWarning(`Invalid results.branch_prefix in ${configPath2}, expected non-empty string`);
34156
34609
  return void 0;
34157
34610
  }
34158
34611
  branchPrefix = obj.branch_prefix.trim();
@@ -34172,19 +34625,19 @@ function resolveResultsConfigForProject(config2, _projectId) {
34172
34625
  }
34173
34626
  return config2.results;
34174
34627
  }
34175
- function parseHooksConfig(raw, configPath) {
34628
+ function parseHooksConfig(raw, configPath2) {
34176
34629
  if (raw === void 0 || raw === null) {
34177
34630
  return void 0;
34178
34631
  }
34179
34632
  if (typeof raw !== "object" || Array.isArray(raw)) {
34180
- logWarning(`Invalid hooks in ${configPath}, expected object`);
34633
+ logWarning(`Invalid hooks in ${configPath2}, expected object`);
34181
34634
  return void 0;
34182
34635
  }
34183
34636
  const obj = raw;
34184
34637
  const beforeSession = obj.before_session;
34185
34638
  if (beforeSession !== void 0) {
34186
34639
  if (typeof beforeSession !== "string" || beforeSession.trim().length === 0) {
34187
- logWarning(`Invalid hooks.before_session in ${configPath}, expected non-empty string`);
34640
+ logWarning(`Invalid hooks.before_session in ${configPath2}, expected non-empty string`);
34188
34641
  return void 0;
34189
34642
  }
34190
34643
  return { before_session: beforeSession.trim() };
@@ -34286,8 +34739,8 @@ function isTemplateReference(value) {
34286
34739
  }
34287
34740
  async function resolveAssertionTemplateReference(include, searchRoots) {
34288
34741
  const templateCandidates = isTemplateReference(include) ? [
34289
- path41.join(".agentv", "templates", `${include}.yaml`),
34290
- path41.join(".agentv", "templates", `${include}.yml`)
34742
+ path422.join(".agentv", "templates", `${include}.yaml`),
34743
+ path422.join(".agentv", "templates", `${include}.yml`)
34291
34744
  ] : [include];
34292
34745
  const attempted = [];
34293
34746
  for (const candidate of templateCandidates) {
@@ -34340,10 +34793,10 @@ ${resolved.attempted.map((attempt) => ` Tried: ${attempt}`).join("\n")}` : "";
34340
34793
  `Invalid assertion template file in '${evalId}': ${resolved.resolvedPath} is missing a top-level assertions array`
34341
34794
  );
34342
34795
  }
34343
- const templateDir = path41.dirname(resolved.resolvedPath);
34796
+ const templateDir = path422.dirname(resolved.resolvedPath);
34344
34797
  const nestedSearchRoots = [
34345
34798
  templateDir,
34346
- ...searchRoots.filter((root) => path41.resolve(root) !== templateDir)
34799
+ ...searchRoots.filter((root) => path422.resolve(root) !== templateDir)
34347
34800
  ];
34348
34801
  return await expandGraderEntries(assertions, nestedSearchRoots, evalId, {
34349
34802
  depth: nextDepth,
@@ -34404,7 +34857,7 @@ async function collectAssertionTemplateReferencesFromValue(value, searchRoots, e
34404
34857
  references.push({
34405
34858
  kind: "assertion_template",
34406
34859
  displayPath: resolved.displayPath,
34407
- ...resolved.resolvedPath ? { resolvedPath: path41.resolve(resolved.resolvedPath) } : {}
34860
+ ...resolved.resolvedPath ? { resolvedPath: path422.resolve(resolved.resolvedPath) } : {}
34408
34861
  });
34409
34862
  if (resolved.resolvedPath) {
34410
34863
  if (includeContext.chain.includes(resolved.resolvedPath)) {
@@ -34414,10 +34867,10 @@ async function collectAssertionTemplateReferencesFromValue(value, searchRoots, e
34414
34867
  const content = await readFile15(resolved.resolvedPath, "utf8");
34415
34868
  const parsed = interpolateEnv(parseYamlValue(content), process.env);
34416
34869
  if (isJsonObject2(parsed) && Array.isArray(parsed.assertions)) {
34417
- const templateDir = path41.dirname(resolved.resolvedPath);
34870
+ const templateDir = path422.dirname(resolved.resolvedPath);
34418
34871
  const nestedSearchRoots = [
34419
34872
  templateDir,
34420
- ...searchRoots.filter((root) => path41.resolve(root) !== templateDir)
34873
+ ...searchRoots.filter((root) => path422.resolve(root) !== templateDir)
34421
34874
  ];
34422
34875
  references.push(
34423
34876
  ...await collectAssertionTemplateReferencesFromValue(
@@ -34603,7 +35056,7 @@ async function parseGraderList(candidateEvaluators, searchRoots, evalId, default
34603
35056
  if (cwd) {
34604
35057
  const resolved = await resolveFileReference3(cwd, searchRoots);
34605
35058
  if (resolved.resolvedPath) {
34606
- resolvedCwd = path41.resolve(resolved.resolvedPath);
35059
+ resolvedCwd = path422.resolve(resolved.resolvedPath);
34607
35060
  } else {
34608
35061
  logWarning2(
34609
35062
  `Code-grader evaluator '${name}' in '${evalId}': cwd not found (${resolved.displayPath})`,
@@ -34789,7 +35242,7 @@ async function parseGraderList(candidateEvaluators, searchRoots, evalId, default
34789
35242
  aggregatorPrompt = fileRef;
34790
35243
  const resolved = await resolveFileReference3(fileRef, searchRoots);
34791
35244
  if (resolved.resolvedPath) {
34792
- promptPath2 = path41.resolve(resolved.resolvedPath);
35245
+ promptPath2 = path422.resolve(resolved.resolvedPath);
34793
35246
  } else {
34794
35247
  throw new Error(
34795
35248
  `Composite aggregator in '${evalId}': prompt file not found: ${resolved.displayPath}`
@@ -35469,7 +35922,7 @@ async function parseGraderList(candidateEvaluators, searchRoots, evalId, default
35469
35922
  const commandPath = commandArray[commandArray.length - 1];
35470
35923
  const resolved = await resolveFileReference3(commandPath, searchRoots);
35471
35924
  if (resolved.resolvedPath) {
35472
- resolvedPromptScript = [...commandArray.slice(0, -1), path41.resolve(resolved.resolvedPath)];
35925
+ resolvedPromptScript = [...commandArray.slice(0, -1), path422.resolve(resolved.resolvedPath)];
35473
35926
  } else {
35474
35927
  throw new Error(
35475
35928
  `Grader '${name}' in '${evalId}': prompt command file not found: ${resolved.displayPath}`
@@ -35484,7 +35937,7 @@ async function parseGraderList(candidateEvaluators, searchRoots, evalId, default
35484
35937
  prompt = fileRef;
35485
35938
  const resolved = await resolveFileReference3(fileRef, searchRoots);
35486
35939
  if (resolved.resolvedPath) {
35487
- promptPath = path41.resolve(resolved.resolvedPath);
35940
+ promptPath = path422.resolve(resolved.resolvedPath);
35488
35941
  try {
35489
35942
  await validateCustomPromptContent(promptPath);
35490
35943
  } catch (error40) {
@@ -35642,7 +36095,7 @@ async function parsePreprocessors(rawValue, searchRoots, evaluatorName, evalId)
35642
36095
  preprocessors.push({
35643
36096
  type,
35644
36097
  command,
35645
- resolvedCommand: [...command.slice(0, -1), path41.resolve(resolved.resolvedPath)]
36098
+ resolvedCommand: [...command.slice(0, -1), path422.resolve(resolved.resolvedPath)]
35646
36099
  });
35647
36100
  }
35648
36101
  return preprocessors;
@@ -35737,10 +36190,10 @@ async function resolveOptionalCommandSource(command, searchRoots) {
35737
36190
  return void 0;
35738
36191
  }
35739
36192
  const resolved = await resolveFileReference3(candidate, searchRoots);
35740
- return resolved.resolvedPath ? path41.resolve(resolved.resolvedPath) : void 0;
36193
+ return resolved.resolvedPath ? path422.resolve(resolved.resolvedPath) : void 0;
35741
36194
  }
35742
36195
  function looksLikeFilePath(value) {
35743
- return path41.isAbsolute(value) || value.startsWith(".") || value.includes("/") || value.includes("\\") || /\.[cm]?[jt]sx?$|\.py$|\.sh$|\.bash$|\.rb$|\.go$|\.rs$/i.test(value);
36196
+ return path422.isAbsolute(value) || value.startsWith(".") || value.includes("/") || value.includes("\\") || /\.[cm]?[jt]sx?$|\.py$|\.sh$|\.bash$|\.rb$|\.go$|\.rs$/i.test(value);
35744
36197
  }
35745
36198
  function parseCommandToArgv(command) {
35746
36199
  if (process.platform === "win32") {
@@ -36128,7 +36581,7 @@ var IMAGE_MEDIA_TYPES = {
36128
36581
  ".bmp": "image/bmp"
36129
36582
  };
36130
36583
  function detectImageMediaType(filePath) {
36131
- const ext = path422.extname(filePath).toLowerCase();
36584
+ const ext = path43.extname(filePath).toLowerCase();
36132
36585
  return IMAGE_MEDIA_TYPES[ext];
36133
36586
  }
36134
36587
  var ANSI_YELLOW5 = "\x1B[33m";
@@ -36192,7 +36645,7 @@ async function processMessages(options) {
36192
36645
  ...cloneJsonObject(rawSegment),
36193
36646
  path: displayPath,
36194
36647
  text: fileContent,
36195
- resolvedPath: path422.resolve(resolvedPath)
36648
+ resolvedPath: path43.resolve(resolvedPath)
36196
36649
  });
36197
36650
  if (verbose) {
36198
36651
  const label = messageType === "input" ? "[File]" : "[Expected Output File]";
@@ -36316,7 +36769,7 @@ async function processExpectedMessages(options) {
36316
36769
  type: "file",
36317
36770
  path: displayPath,
36318
36771
  text: fileContent,
36319
- resolvedPath: path422.resolve(resolvedPath)
36772
+ resolvedPath: path43.resolve(resolvedPath)
36320
36773
  });
36321
36774
  if (verbose) {
36322
36775
  console.log(` [Expected Output File] Found: ${displayPath}`);
@@ -36462,7 +36915,7 @@ function matchesFilter(id, filter) {
36462
36915
  return typeof filter === "string" ? micromatch.isMatch(id, filter) : filter.some((pattern) => micromatch.isMatch(id, pattern));
36463
36916
  }
36464
36917
  function detectFormat(filePath) {
36465
- const ext = path43.extname(filePath).toLowerCase();
36918
+ const ext = path44.extname(filePath).toLowerCase();
36466
36919
  if (ext === ".jsonl") return "jsonl";
36467
36920
  if (ext === ".yaml" || ext === ".yml") return "yaml";
36468
36921
  if (ext === ".json") return "agent-skills-json";
@@ -36472,9 +36925,9 @@ function detectFormat(filePath) {
36472
36925
  );
36473
36926
  }
36474
36927
  async function loadSidecarMetadata(jsonlPath, verbose) {
36475
- const dir = path43.dirname(jsonlPath);
36476
- const base = path43.basename(jsonlPath, ".jsonl");
36477
- const sidecarPath = path43.join(dir, `${base}.yaml`);
36928
+ const dir = path44.dirname(jsonlPath);
36929
+ const base = path44.basename(jsonlPath, ".jsonl");
36930
+ const sidecarPath = path44.join(dir, `${base}.yaml`);
36478
36931
  if (!await fileExists3(sidecarPath)) {
36479
36932
  if (verbose) {
36480
36933
  logWarning4(`Sidecar metadata file not found: ${sidecarPath} (using defaults)`);
@@ -36523,13 +36976,13 @@ function parseJsonlContent(content, filePath) {
36523
36976
  async function loadTestsFromJsonl(evalFilePath, repoRoot, options) {
36524
36977
  const verbose = options?.verbose ?? false;
36525
36978
  const filterPattern = options?.filter;
36526
- const absoluteTestPath = path43.resolve(evalFilePath);
36979
+ const absoluteTestPath = path44.resolve(evalFilePath);
36527
36980
  const repoRootPath = resolveToAbsolutePath(repoRoot);
36528
36981
  const searchRoots = buildSearchRoots2(absoluteTestPath, repoRootPath);
36529
36982
  const sidecar = await loadSidecarMetadata(absoluteTestPath, verbose);
36530
36983
  const rawFile = await readFile17(absoluteTestPath, "utf8");
36531
36984
  const rawCases = parseJsonlContent(rawFile, evalFilePath);
36532
- const fallbackSuiteName = path43.basename(absoluteTestPath, ".jsonl") || "eval";
36985
+ const fallbackSuiteName = path44.basename(absoluteTestPath, ".jsonl") || "eval";
36533
36986
  const suiteName = sidecar.name && sidecar.name.trim().length > 0 ? sidecar.name : fallbackSuiteName;
36534
36987
  const globalEvaluator = coerceEvaluator(sidecar.evaluator, "sidecar") ?? "llm-grader";
36535
36988
  const globalExecution = sidecar.execution;
@@ -36687,60 +37140,51 @@ function parseMetadata(suite) {
36687
37140
  requires: suite.requires
36688
37141
  });
36689
37142
  }
36690
- function parseRepoSource(raw) {
37143
+ function readString(obj, key) {
37144
+ const value = obj[key];
37145
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
37146
+ }
37147
+ function readStringArray(obj, key) {
37148
+ const value = obj[key];
37149
+ if (!Array.isArray(value)) return void 0;
37150
+ const strings = value.filter((item) => typeof item === "string");
37151
+ return strings.length > 0 ? strings : void 0;
37152
+ }
37153
+ function parseRepoConfig(raw) {
36691
37154
  if (!isJsonObject(raw)) return void 0;
36692
37155
  const obj = raw;
36693
- if (obj.type === "git" && typeof obj.url === "string") {
36694
- return { type: "git", url: obj.url };
37156
+ if ("source" in obj) {
37157
+ throw new Error("workspace.repos[].source has been removed. Use workspace.repos[].repo.");
37158
+ }
37159
+ if ("checkout" in obj) {
37160
+ throw new Error(
37161
+ "workspace.repos[].checkout has been removed. Use top-level commit, base_commit, and ancestor."
37162
+ );
36695
37163
  }
36696
- if (obj.type === "local" && typeof obj.path === "string") {
36697
- return { type: "local", path: obj.path };
37164
+ if ("clone" in obj) {
37165
+ throw new Error("workspace.repos[].clone has been removed. Use top-level sparse if needed.");
36698
37166
  }
36699
- return void 0;
36700
- }
36701
- function parseRepoCheckout(raw) {
36702
- if (!isJsonObject(raw)) return void 0;
36703
- const obj = raw;
36704
- const ref = typeof obj.ref === "string" ? obj.ref : void 0;
36705
- const baseCommit = typeof obj.base_commit === "string" ? obj.base_commit : void 0;
36706
- const resolve = obj.resolve === "remote" || obj.resolve === "local" ? obj.resolve : void 0;
37167
+ const repoPath = readString(obj, "path");
37168
+ const repo = readString(obj, "repo");
37169
+ const commit = readString(obj, "commit");
37170
+ const baseCommit = readString(obj, "base_commit");
36707
37171
  const ancestor = typeof obj.ancestor === "number" ? obj.ancestor : void 0;
36708
- if (!ref && !baseCommit && !resolve && ancestor === void 0) return void 0;
37172
+ const sparse = readStringArray(obj, "sparse");
37173
+ if (commit !== void 0 && baseCommit !== void 0 && commit !== baseCommit) {
37174
+ throw new Error("workspace.repos[].commit and workspace.repos[].base_commit must match.");
37175
+ }
37176
+ if (!repoPath && !repo && !commit && !baseCommit && ancestor === void 0 && !sparse) {
37177
+ return void 0;
37178
+ }
36709
37179
  return {
36710
- ...ref !== void 0 && { ref },
37180
+ ...repoPath !== void 0 && { path: repoPath },
37181
+ ...repo !== void 0 && { repo },
37182
+ ...commit !== void 0 && { commit },
36711
37183
  ...baseCommit !== void 0 && { base_commit: baseCommit },
36712
- ...resolve !== void 0 && { resolve },
36713
- ...ancestor !== void 0 && { ancestor }
36714
- };
36715
- }
36716
- function parseRepoClone(raw) {
36717
- if (!isJsonObject(raw)) return void 0;
36718
- const obj = raw;
36719
- const depth = typeof obj.depth === "number" ? obj.depth : void 0;
36720
- const filter = typeof obj.filter === "string" ? obj.filter : void 0;
36721
- const sparse = Array.isArray(obj.sparse) ? obj.sparse.filter((s) => typeof s === "string") : void 0;
36722
- if (depth === void 0 && !filter && !sparse) return void 0;
36723
- return {
36724
- ...depth !== void 0 && { depth },
36725
- ...filter !== void 0 && { filter },
37184
+ ...ancestor !== void 0 && { ancestor },
36726
37185
  ...sparse !== void 0 && { sparse }
36727
37186
  };
36728
37187
  }
36729
- function parseRepoConfig(raw) {
36730
- if (!isJsonObject(raw)) return void 0;
36731
- const obj = raw;
36732
- const repoPath = typeof obj.path === "string" ? obj.path : void 0;
36733
- const source = parseRepoSource(obj.source);
36734
- const checkout = parseRepoCheckout(obj.checkout);
36735
- const clone2 = parseRepoClone(obj.clone);
36736
- if (!repoPath && !source && !checkout && !clone2) return void 0;
36737
- return {
36738
- ...repoPath !== void 0 && { path: repoPath },
36739
- ...source !== void 0 && { source },
36740
- ...checkout !== void 0 && { checkout },
36741
- ...clone2 !== void 0 && { clone: clone2 }
36742
- };
36743
- }
36744
37188
  async function buildPromptInputs(testCase, mode = "lm") {
36745
37189
  const segmentsByMessage = testCase.input.map(
36746
37190
  (message) => extractContentSegments(message.content)
@@ -36935,7 +37379,7 @@ function interpolateRawEvalCase(raw, vars) {
36935
37379
  }
36936
37380
  async function readTestSuiteMetadata(testFilePath) {
36937
37381
  try {
36938
- const absolutePath = path44.resolve(testFilePath);
37382
+ const absolutePath = path45.resolve(testFilePath);
36939
37383
  const content = await readFile18(absolutePath, "utf8");
36940
37384
  const parsed = interpolateEnv(parseYamlValue(content), process.env);
36941
37385
  if (!isJsonObject(parsed)) {
@@ -36960,7 +37404,7 @@ async function loadTestSuite(evalFilePath, repoRoot, options) {
36960
37404
  return { tests: await loadTestsFromAgentSkills(evalFilePath) };
36961
37405
  }
36962
37406
  if (format === "typescript") {
36963
- const { loadTsEvalSuite: loadTsEvalSuite2 } = await import("./ts-eval-loader-RBTB2HG2-H5TRXZLO.js");
37407
+ const { loadTsEvalSuite: loadTsEvalSuite2 } = await import("./ts-eval-loader-TJT6BGFF-DI7XNSO4.js");
36964
37408
  return loadTsEvalSuite2(evalFilePath, resolveToAbsolutePath(repoRoot), options);
36965
37409
  }
36966
37410
  const { tests, parsed, suiteWorkspacePath } = await loadTestsFromYaml(
@@ -36995,7 +37439,7 @@ async function loadTests(evalFilePath, repoRoot, options) {
36995
37439
  return loadTestsFromAgentSkills(evalFilePath);
36996
37440
  }
36997
37441
  if (format === "typescript") {
36998
- const { loadTsEvalSuite: loadTsEvalSuite2 } = await import("./ts-eval-loader-RBTB2HG2-H5TRXZLO.js");
37442
+ const { loadTsEvalSuite: loadTsEvalSuite2 } = await import("./ts-eval-loader-TJT6BGFF-DI7XNSO4.js");
36999
37443
  const suite = await loadTsEvalSuite2(evalFilePath, resolveToAbsolutePath(repoRoot), options);
37000
37444
  return suite.tests;
37001
37445
  }
@@ -37006,7 +37450,7 @@ var loadEvalCases = loadTests;
37006
37450
  async function loadTestsFromYaml(evalFilePath, repoRoot, options) {
37007
37451
  const verbose = options?.verbose ?? false;
37008
37452
  const filterPattern = options?.filter;
37009
- const absoluteTestPath = path44.resolve(evalFilePath);
37453
+ const absoluteTestPath = path45.resolve(evalFilePath);
37010
37454
  const repoRootPath = resolveToAbsolutePath(repoRoot);
37011
37455
  const searchRoots = buildSearchRoots2(absoluteTestPath, repoRootPath);
37012
37456
  const config2 = await loadConfig(absoluteTestPath, repoRootPath);
@@ -37019,7 +37463,7 @@ async function loadTestsFromYaml(evalFilePath, repoRoot, options) {
37019
37463
  }
37020
37464
  const suite = interpolated;
37021
37465
  const suiteNameFromFile = asString5(suite.name)?.trim();
37022
- const fallbackSuiteName = path44.basename(absoluteTestPath).replace(/\.eval\.ya?ml$/i, "").replace(/\.ya?ml$/i, "") || "eval";
37466
+ const fallbackSuiteName = path45.basename(absoluteTestPath).replace(/\.eval\.ya?ml$/i, "").replace(/\.ya?ml$/i, "") || "eval";
37023
37467
  const suiteName = suiteNameFromFile && suiteNameFromFile.length > 0 ? suiteNameFromFile : fallbackSuiteName;
37024
37468
  const rawTestCases = resolveTests(suite);
37025
37469
  const globalEvaluator = coerceEvaluator(suite.evaluator, "global") ?? "llm-grader";
@@ -37029,10 +37473,10 @@ async function loadTestsFromYaml(evalFilePath, repoRoot, options) {
37029
37473
  "<suite>",
37030
37474
  absoluteTestPath
37031
37475
  );
37032
- const evalFileDir = path44.dirname(absoluteTestPath);
37476
+ const evalFileDir = path45.dirname(absoluteTestPath);
37033
37477
  let expandedTestCases;
37034
37478
  if (typeof rawTestCases === "string") {
37035
- const externalPath = path44.resolve(evalFileDir, rawTestCases);
37479
+ const externalPath = path45.resolve(evalFileDir, rawTestCases);
37036
37480
  let isDir = false;
37037
37481
  try {
37038
37482
  const pathStat = await stat8(externalPath);
@@ -37266,7 +37710,7 @@ function buildEvalTestSource(params) {
37266
37710
  };
37267
37711
  }
37268
37712
  function stringifySourceYaml(value) {
37269
- return stringifyYaml(sanitizeSourceValue(value), { lineWidth: 0 }).trimEnd();
37713
+ return stringifyYaml2(sanitizeSourceValue(value), { lineWidth: 0 }).trimEnd();
37270
37714
  }
37271
37715
  function sanitizeSourceValue(value, keyHint) {
37272
37716
  if (keyHint && SOURCE_SECRET_KEY_PATTERN.test(keyHint)) {
@@ -37336,7 +37780,7 @@ function collectInputSourceReferences(inputMessages) {
37336
37780
  references.push({
37337
37781
  kind: "input_file",
37338
37782
  displayPath,
37339
- ...typeof segment.resolvedPath === "string" ? { resolvedPath: path44.resolve(segment.resolvedPath) } : {}
37783
+ ...typeof segment.resolvedPath === "string" ? { resolvedPath: path45.resolve(segment.resolvedPath) } : {}
37340
37784
  });
37341
37785
  }
37342
37786
  }
@@ -37409,7 +37853,7 @@ function collectSingleGraderSourceReferences(evaluator) {
37409
37853
  references.push({
37410
37854
  kind: "code_grader_command",
37411
37855
  displayPath: evaluator.aggregator.path,
37412
- resolvedPath: path44.resolve(evaluator.aggregator.cwd ?? "", evaluator.aggregator.path),
37856
+ resolvedPath: path45.resolve(evaluator.aggregator.cwd ?? "", evaluator.aggregator.path),
37413
37857
  graderName: evaluator.name
37414
37858
  });
37415
37859
  } else if (evaluator.aggregator.type === "llm-grader" && evaluator.aggregator.promptPath) {
@@ -37442,9 +37886,9 @@ function dedupeSourceReferences(references) {
37442
37886
  return deduped;
37443
37887
  }
37444
37888
  function toPortableRelativePath(root, candidate) {
37445
- const relative = path44.relative(root, candidate);
37446
- if (relative && !relative.startsWith("..") && !path44.isAbsolute(relative)) {
37447
- return relative.split(path44.sep).join("/");
37889
+ const relative = path45.relative(root, candidate);
37890
+ if (relative && !relative.startsWith("..") && !path45.isAbsolute(relative)) {
37891
+ return relative.split(path45.sep).join("/");
37448
37892
  }
37449
37893
  return void 0;
37450
37894
  }
@@ -37498,8 +37942,8 @@ function parseWorkspaceScriptConfig(raw, evalFileDir) {
37498
37942
  if (!command) return void 0;
37499
37943
  const timeoutMs = typeof obj.timeout_ms === "number" ? obj.timeout_ms : void 0;
37500
37944
  let cwd = typeof obj.cwd === "string" ? obj.cwd : void 0;
37501
- if (cwd && !path44.isAbsolute(cwd)) {
37502
- cwd = path44.resolve(evalFileDir, cwd);
37945
+ if (cwd && !path45.isAbsolute(cwd)) {
37946
+ cwd = path45.resolve(evalFileDir, cwd);
37503
37947
  }
37504
37948
  const config2 = { command };
37505
37949
  if (timeoutMs !== void 0) {
@@ -37537,7 +37981,7 @@ function parseWorkspaceHooksConfig(raw, evalFileDir) {
37537
37981
  }
37538
37982
  async function resolveWorkspaceConfig(raw, evalFileDir) {
37539
37983
  if (typeof raw === "string") {
37540
- const workspaceFilePath = path44.resolve(evalFileDir, raw);
37984
+ const workspaceFilePath = path45.resolve(evalFileDir, raw);
37541
37985
  let content;
37542
37986
  try {
37543
37987
  content = await readFile18(workspaceFilePath, "utf8");
@@ -37550,7 +37994,7 @@ async function resolveWorkspaceConfig(raw, evalFileDir) {
37550
37994
  `Invalid workspace file format: ${workspaceFilePath} (expected a YAML object)`
37551
37995
  );
37552
37996
  }
37553
- const workspaceFileDir = path44.dirname(workspaceFilePath);
37997
+ const workspaceFileDir = path45.dirname(workspaceFilePath);
37554
37998
  const resolvedWorkspace = parseWorkspaceConfig(parsed, workspaceFileDir);
37555
37999
  if (resolvedWorkspace) {
37556
38000
  return { ...resolvedWorkspace, workspaceFileDir };
@@ -37584,8 +38028,8 @@ function parseWorkspaceConfig(raw, evalFileDir) {
37584
38028
  throw new Error("workspace.static has been removed. Use workspace.mode='static'.");
37585
38029
  }
37586
38030
  let template = typeof obj.template === "string" ? obj.template : void 0;
37587
- if (template && !path44.isAbsolute(template)) {
37588
- template = path44.resolve(evalFileDir, template);
38031
+ if (template && !path45.isAbsolute(template)) {
38032
+ template = path45.resolve(evalFileDir, template);
37589
38033
  }
37590
38034
  const isolation = obj.isolation === "shared" || obj.isolation === "per_test" ? obj.isolation : void 0;
37591
38035
  const repos = Array.isArray(obj.repos) ? obj.repos.map(parseRepoConfig).filter(Boolean) : void 0;
@@ -37722,7 +38166,7 @@ ${detailBlock}${ANSI_RESET8}`);
37722
38166
  console.error(`${ANSI_RED3}Error: ${message}${ANSI_RESET8}`);
37723
38167
  }
37724
38168
  }
37725
- var execFileAsync3 = promisify7(execFile3);
38169
+ var execFileAsync2 = promisify6(execFile2);
37726
38170
  var WORKSPACE_GIT_TIMEOUT_MS = 3e5;
37727
38171
  function pathFromRoot(root) {
37728
38172
  return root instanceof URL ? fileURLToPath5(root) : String(root);
@@ -37779,7 +38223,7 @@ function workspaceGitEnv() {
37779
38223
  };
37780
38224
  }
37781
38225
  async function resetWorkspaceRoot(workspacePath, resetMode, baselineRef) {
37782
- if (!existsSync5(path45.join(workspacePath, ".git"))) {
38226
+ if (!existsSync6(path46.join(workspacePath, ".git"))) {
37783
38227
  return false;
37784
38228
  }
37785
38229
  const cleanFlag = resetMode === "strict" ? "-fdx" : "-fd";
@@ -37789,8 +38233,8 @@ async function resetWorkspaceRoot(workspacePath, resetMode, baselineRef) {
37789
38233
  env: workspaceGitEnv(),
37790
38234
  maxBuffer: 50 * 1024 * 1024
37791
38235
  };
37792
- await execFileAsync3("git", ["reset", "--hard", baselineRef ?? "HEAD"], opts);
37793
- await execFileAsync3("git", ["clean", cleanFlag], opts);
38236
+ await execFileAsync2("git", ["reset", "--hard", baselineRef ?? "HEAD"], opts);
38237
+ await execFileAsync2("git", ["clean", cleanFlag], opts);
37794
38238
  return true;
37795
38239
  }
37796
38240
  function validateDependencyGraph(tests) {
@@ -37822,18 +38266,18 @@ function validateDependencyGraph(tests) {
37822
38266
  }
37823
38267
  const visited = /* @__PURE__ */ new Set();
37824
38268
  const visiting = /* @__PURE__ */ new Set();
37825
- function visit(id, path48) {
38269
+ function visit(id, path49) {
37826
38270
  if (visiting.has(id)) {
37827
- const cycle = [...path48.slice(path48.indexOf(id)), id];
38271
+ const cycle = [...path49.slice(path49.indexOf(id)), id];
37828
38272
  throw new Error(`Circular dependency detected: ${cycle.join(" \u2192 ")}`);
37829
38273
  }
37830
38274
  if (visited.has(id)) return;
37831
38275
  visiting.add(id);
37832
- path48.push(id);
38276
+ path49.push(id);
37833
38277
  for (const dep of depMap.get(id) ?? []) {
37834
- visit(dep, path48);
38278
+ visit(dep, path49);
37835
38279
  }
37836
- path48.pop();
38280
+ path49.pop();
37837
38281
  visiting.delete(id);
37838
38282
  visited.add(id);
37839
38283
  }
@@ -37933,7 +38377,7 @@ async function runEvaluation(options) {
37933
38377
  );
37934
38378
  useCache = false;
37935
38379
  }
37936
- const evalRunId = randomUUID9();
38380
+ const evalRunId = randomUUID10();
37937
38381
  const evalCases = preloadedEvalCases ?? await loadTests(evalFilePath, repoRoot, { verbose, filter });
37938
38382
  const filteredEvalCases = filterEvalCases(evalCases, filter);
37939
38383
  if (filteredEvalCases.length === 0) {
@@ -38015,7 +38459,7 @@ async function runEvaluation(options) {
38015
38459
  ];
38016
38460
  const evaluatorRegistry = buildEvaluatorRegistry(evaluators, resolveGraderProvider);
38017
38461
  const typeRegistry = createBuiltinRegistry();
38018
- const discoveryBaseDir = evalFilePath ? path45.dirname(path45.resolve(evalFilePath)) : process.cwd();
38462
+ const discoveryBaseDir = evalFilePath ? path46.dirname(path46.resolve(evalFilePath)) : process.cwd();
38019
38463
  const evalDir = discoveryBaseDir;
38020
38464
  await discoverAssertions(typeRegistry, discoveryBaseDir);
38021
38465
  await discoverGraders(typeRegistry, discoveryBaseDir);
@@ -38081,29 +38525,6 @@ async function runEvaluation(options) {
38081
38525
  console.log(`[setup] ${message}`);
38082
38526
  }
38083
38527
  };
38084
- const allRepos = /* @__PURE__ */ new Map();
38085
- for (const ec of filteredEvalCases) {
38086
- if (ec.workspace?.repos) {
38087
- for (const repo of ec.workspace.repos) {
38088
- if (!repo.source) continue;
38089
- const key = `${repo.path ?? ""}::${repo.source.type === "local" ? repo.source.path : ""}`;
38090
- if (!allRepos.has(key)) {
38091
- allRepos.set(key, repo);
38092
- }
38093
- }
38094
- }
38095
- }
38096
- if (allRepos.size > 0) {
38097
- const localPathErrors = RepoManager.validateLocalPaths([...allRepos.values()]);
38098
- if (localPathErrors.length > 0) {
38099
- const message = RepoManager.formatValidationErrors(localPathErrors);
38100
- console.warn(`Warning: ${message}`);
38101
- const invalidLocalRepoPaths = new Set(localPathErrors.map((e) => e.repoPath));
38102
- if (suiteWorkspace?.repos?.some((r) => r.path && invalidLocalRepoPaths.has(r.path))) {
38103
- throw new Error(message);
38104
- }
38105
- }
38106
- }
38107
38528
  const isPerTestIsolation = suiteWorkspace?.isolation === "per_test";
38108
38529
  const cliWorkspacePath = workspacePath ?? legacyWorkspacePath;
38109
38530
  const yamlWorkspacePath = suiteWorkspace?.path;
@@ -38172,7 +38593,7 @@ async function runEvaluation(options) {
38172
38593
  const isEmpty = dirExists ? (await readdir8(configuredStaticPath)).length === 0 : false;
38173
38594
  if (isYamlConfiguredPath && (!dirExists || isEmpty)) {
38174
38595
  if (!dirExists) {
38175
- await mkdir16(configuredStaticPath, { recursive: true });
38596
+ await mkdir17(configuredStaticPath, { recursive: true });
38176
38597
  }
38177
38598
  if (workspaceTemplate) {
38178
38599
  await copyDirectoryRecursive(workspaceTemplate, configuredStaticPath);
@@ -38217,7 +38638,7 @@ async function runEvaluation(options) {
38217
38638
  }
38218
38639
  } else if (!isPerTestIsolation && (suiteWorkspace?.hooks || suiteWorkspace?.repos?.length)) {
38219
38640
  sharedWorkspacePath = getWorkspacePath(evalRunId, "shared");
38220
- await mkdir16(sharedWorkspacePath, { recursive: true });
38641
+ await mkdir17(sharedWorkspacePath, { recursive: true });
38221
38642
  setupLog(`created empty shared workspace at: ${sharedWorkspacePath}`);
38222
38643
  }
38223
38644
  try {
@@ -38258,7 +38679,7 @@ async function runEvaluation(options) {
38258
38679
  };
38259
38680
  var toDependencyResult = toDependencyResult2, checkDependencies = checkDependencies2, extractEvaluationCostUsd = extractEvaluationCostUsd2;
38260
38681
  if (suiteWorkspaceFile && sharedWorkspacePath) {
38261
- const copiedWorkspaceFile = path45.join(sharedWorkspacePath, path45.basename(suiteWorkspaceFile));
38682
+ const copiedWorkspaceFile = path46.join(sharedWorkspacePath, path46.basename(suiteWorkspaceFile));
38262
38683
  try {
38263
38684
  await stat9(copiedWorkspaceFile);
38264
38685
  suiteWorkspaceFile = copiedWorkspaceFile;
@@ -38273,9 +38694,9 @@ async function runEvaluation(options) {
38273
38694
  try {
38274
38695
  if (needsPerRepoCheck) {
38275
38696
  for (const repo of suiteWorkspace.repos) {
38276
- if (!repo.path || !repo.source) continue;
38277
- const targetDir = path45.join(sharedWorkspacePath, repo.path);
38278
- if (existsSync5(targetDir)) {
38697
+ if (!repo.path || !repo.repo) continue;
38698
+ const targetDir = path46.join(sharedWorkspacePath, repo.path);
38699
+ if (existsSync6(targetDir)) {
38279
38700
  setupLog(`reusing existing repo at: ${targetDir}`);
38280
38701
  continue;
38281
38702
  }
@@ -39140,7 +39561,7 @@ async function runEvalCase(options) {
39140
39561
  );
39141
39562
  }
39142
39563
  if (caseWorkspaceFile && workspacePath) {
39143
- const copiedFile = path45.join(workspacePath, path45.basename(caseWorkspaceFile));
39564
+ const copiedFile = path46.join(workspacePath, path46.basename(caseWorkspaceFile));
39144
39565
  try {
39145
39566
  await stat9(copiedFile);
39146
39567
  caseWorkspaceFile = copiedFile;
@@ -39150,25 +39571,7 @@ async function runEvalCase(options) {
39150
39571
  }
39151
39572
  if (!workspacePath && (evalCase.workspace?.hooks || evalCase.workspace?.repos?.length) && evalRunId) {
39152
39573
  workspacePath = getWorkspacePath(evalRunId, evalCase.id);
39153
- await mkdir16(workspacePath, { recursive: true });
39154
- }
39155
- if (evalCase.workspace?.repos?.length && workspacePath) {
39156
- const localPathErrors = RepoManager.validateLocalPaths(evalCase.workspace.repos);
39157
- if (localPathErrors.length > 0) {
39158
- const message = RepoManager.formatValidationErrors(localPathErrors);
39159
- console.warn(`Warning: test=${evalCase.id} ${message}`);
39160
- return buildErrorResult(
39161
- evalCase,
39162
- target.name,
39163
- nowFn(),
39164
- new Error(message),
39165
- promptInputs,
39166
- provider,
39167
- "repo_setup",
39168
- "local_path_not_found",
39169
- verbose
39170
- );
39171
- }
39574
+ await mkdir17(workspacePath, { recursive: true });
39172
39575
  }
39173
39576
  if (evalCase.workspace?.repos?.length && workspacePath) {
39174
39577
  const perCaseRepoManager = new RepoManager(setupDebug);
@@ -39202,10 +39605,10 @@ async function runEvalCase(options) {
39202
39605
  const files = evalCase.metadata.agent_skills_files;
39203
39606
  if (baseDir && files.length > 0) {
39204
39607
  for (const relPath of files) {
39205
- const srcPath = path45.resolve(baseDir, relPath);
39206
- const destPath = path45.resolve(workspacePath, relPath);
39608
+ const srcPath = path46.resolve(baseDir, relPath);
39609
+ const destPath = path46.resolve(workspacePath, relPath);
39207
39610
  try {
39208
- await mkdir16(path45.dirname(destPath), { recursive: true });
39611
+ await mkdir17(path46.dirname(destPath), { recursive: true });
39209
39612
  await copyFile2(srcPath, destPath);
39210
39613
  } catch (error40) {
39211
39614
  const message = error40 instanceof Error ? error40.message : String(error40);
@@ -39434,7 +39837,7 @@ async function runEvalCase(options) {
39434
39837
  lastError = error40;
39435
39838
  if (attempt + 1 < attemptBudget) {
39436
39839
  const delayMs = retryBackoffMs(attempt);
39437
- await sleep2(delayMs, signal);
39840
+ await sleep3(delayMs, signal);
39438
39841
  attempt += 1;
39439
39842
  continue;
39440
39843
  }
@@ -40085,7 +40488,7 @@ async function runEvaluatorList(options) {
40085
40488
  dockerConfig,
40086
40489
  dependencyResults
40087
40490
  };
40088
- const evalFileDir = evalCase.file_paths[0] ? path45.dirname(evalCase.file_paths[0]) : process.cwd();
40491
+ const evalFileDir = evalCase.file_paths[0] ? path46.dirname(evalCase.file_paths[0]) : process.cwd();
40089
40492
  const dispatchContext = {
40090
40493
  graderProvider,
40091
40494
  targetResolver,
@@ -40621,7 +41024,7 @@ function extractProviderError(response) {
40621
41024
  return trimmed.length > 0 ? trimmed : void 0;
40622
41025
  }
40623
41026
  function createCacheKey(provider, target, evalCase, promptInputs) {
40624
- const hash = createHash4("sha256");
41027
+ const hash = createHash5("sha256");
40625
41028
  hash.update(provider.id);
40626
41029
  hash.update(target.name);
40627
41030
  hash.update(evalCase.id);
@@ -40706,7 +41109,7 @@ function extractErrorMessage(error40) {
40706
41109
  function retryBackoffMs(attempt) {
40707
41110
  return Math.min(2 ** attempt * 1e3, 3e4);
40708
41111
  }
40709
- function sleep2(ms, signal) {
41112
+ function sleep3(ms, signal) {
40710
41113
  if (signal?.aborted) return Promise.resolve();
40711
41114
  return new Promise((resolve) => {
40712
41115
  const timer = setTimeout(resolve, ms);
@@ -40748,15 +41151,15 @@ function computeWeightedMean(entries) {
40748
41151
  return totalWeight > 0 ? weightedSum / totalWeight : 0;
40749
41152
  }
40750
41153
  async function runPreflightChecks(env, cwd, log) {
40751
- const execFileAsync4 = promisify7(execFile3);
41154
+ const execFileAsync3 = promisify6(execFile2);
40752
41155
  const missing = [];
40753
41156
  for (const cmd of env.required_commands ?? []) {
40754
41157
  log(`preflight: checking command "${cmd}"`);
40755
41158
  try {
40756
41159
  if (process.platform === "win32") {
40757
- await execFileAsync4("where", [cmd], { cwd });
41160
+ await execFileAsync3("where", [cmd], { cwd });
40758
41161
  } else {
40759
- await execFileAsync4("sh", ["-c", `command -v ${cmd}`], { cwd });
41162
+ await execFileAsync3("sh", ["-c", `command -v ${cmd}`], { cwd });
40760
41163
  }
40761
41164
  } catch {
40762
41165
  missing.push(`command: ${cmd}`);
@@ -40765,7 +41168,7 @@ async function runPreflightChecks(env, cwd, log) {
40765
41168
  for (const mod of env.required_python_modules ?? []) {
40766
41169
  log(`preflight: checking Python module "${mod}"`);
40767
41170
  try {
40768
- await execFileAsync4("python3", ["-c", `import ${mod}`], { cwd });
41171
+ await execFileAsync3("python3", ["-c", `import ${mod}`], { cwd });
40769
41172
  } catch {
40770
41173
  missing.push(`python module: ${mod}`);
40771
41174
  }
@@ -40843,7 +41246,7 @@ async function evaluate(config2) {
40843
41246
  cliNoCache: false,
40844
41247
  yamlCache: config2.cache === void 0 ? materialized.cache : void 0
40845
41248
  });
40846
- const cache = cacheEnabled ? new ResponseCache(materialized.cachePath ? path46.resolve(materialized.cachePath) : void 0) : void 0;
41249
+ const cache = cacheEnabled ? new ResponseCache(materialized.cachePath ? path47.resolve(materialized.cachePath) : void 0) : void 0;
40847
41250
  const results = await runEvaluation({
40848
41251
  testFilePath,
40849
41252
  repoRoot,
@@ -40874,7 +41277,7 @@ async function evaluate(config2) {
40874
41277
  async function materializeEvalConfig(config2, options) {
40875
41278
  const baseDir = options?.baseDir ?? process.cwd();
40876
41279
  const repoRoot = options?.repoRoot ?? await findGitRoot(baseDir) ?? baseDir;
40877
- const testFilePath = config2.specFile ? path46.resolve(baseDir, config2.specFile) : path46.join(baseDir, "__programmatic__.yaml");
41280
+ const testFilePath = config2.specFile ? path47.resolve(baseDir, config2.specFile) : path47.join(baseDir, "__programmatic__.yaml");
40878
41281
  const effectiveFilter = options?.filter ?? config2.filter;
40879
41282
  if (config2.specFile) {
40880
41283
  const suite = await loadTestSuite(testFilePath, repoRoot, {
@@ -40951,7 +41354,7 @@ function convertAssertions(entries) {
40951
41354
  }
40952
41355
  function buildInlineEvalTests(config2, options) {
40953
41356
  const suiteWorkspace = config2.beforeAll ? { hooks: { before_all: toBeforeAllHook(config2.beforeAll) } } : void 0;
40954
- const derivedSuiteName = path46.basename(options.testFilePath).replace(/\.eval\.[cm]?ts$/i, "").replace(/\.[cm]?ts$/i, "");
41357
+ const derivedSuiteName = path47.basename(options.testFilePath).replace(/\.eval\.[cm]?ts$/i, "").replace(/\.[cm]?ts$/i, "");
40955
41358
  const suiteName = config2.metadata?.name ?? (derivedSuiteName || "eval");
40956
41359
  return (config2.tests ?? []).filter((test) => !options.filter || matchesFilter4(test.id, options.filter)).map((test) => {
40957
41360
  const isConversation = test.mode === "conversation" || test.turns && test.turns.length > 0;
@@ -41047,11 +41450,11 @@ function computeSummary(results, durationMs, threshold = DEFAULT_THRESHOLD) {
41047
41450
  var TARGET_FILE_CANDIDATES = [".agentv/targets.yaml", ".agentv/targets.yml"];
41048
41451
  async function discoverDefaultTarget(repoRoot) {
41049
41452
  const cwd = process.cwd();
41050
- const chain = buildDirectoryChain(path46.join(cwd, "_placeholder"), repoRoot);
41453
+ const chain = buildDirectoryChain(path47.join(cwd, "_placeholder"), repoRoot);
41051
41454
  for (const dir of chain) {
41052
41455
  for (const candidate of TARGET_FILE_CANDIDATES) {
41053
- const targetsPath = path46.join(dir, candidate);
41054
- if (!existsSync6(targetsPath)) continue;
41456
+ const targetsPath = path47.join(dir, candidate);
41457
+ if (!existsSync7(targetsPath)) continue;
41055
41458
  try {
41056
41459
  const definitions = await readTargetDefinitions(targetsPath);
41057
41460
  const defaultTarget = definitions.find((d) => d.name === "default");
@@ -41063,16 +41466,16 @@ async function discoverDefaultTarget(repoRoot) {
41063
41466
  return null;
41064
41467
  }
41065
41468
  async function loadEnvHierarchy(repoRoot, startPath) {
41066
- const { readFileSync: readFileSync22 } = await import("node:fs");
41469
+ const { readFileSync: readFileSync4 } = await import("node:fs");
41067
41470
  const chain = buildDirectoryChain(startPath, repoRoot);
41068
41471
  const envFiles = [];
41069
41472
  for (const dir of chain) {
41070
- const envPath = path46.join(dir, ".env");
41071
- if (existsSync6(envPath)) envFiles.push(envPath);
41473
+ const envPath = path47.join(dir, ".env");
41474
+ if (existsSync7(envPath)) envFiles.push(envPath);
41072
41475
  }
41073
41476
  for (let i = 0; i < envFiles.length; i++) {
41074
41477
  try {
41075
- const content = readFileSync22(envFiles[i], "utf8");
41478
+ const content = readFileSync4(envFiles[i], "utf8");
41076
41479
  for (const line of content.split("\n")) {
41077
41480
  const trimmed = line.trim();
41078
41481
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -41093,7 +41496,7 @@ async function loadEnvHierarchy(repoRoot, startPath) {
41093
41496
  }
41094
41497
  var EXPORT_NAMES = ["default", "config", "evalConfig"];
41095
41498
  async function loadTsEvalFile(filePath) {
41096
- const absolutePath = path47.resolve(filePath);
41499
+ const absolutePath = path48.resolve(filePath);
41097
41500
  const moduleUrl = pathToFileURL2(absolutePath).href;
41098
41501
  const module = await import(moduleUrl);
41099
41502
  let config2;
@@ -41115,7 +41518,7 @@ async function loadTsEvalSuite(filePath, repoRoot, options) {
41115
41518
  const { config: config2, filePath: absolutePath } = await loadTsEvalFile(filePath);
41116
41519
  const materialized = await materializeEvalConfig(config2, {
41117
41520
  repoRoot,
41118
- baseDir: path47.dirname(absolutePath),
41521
+ baseDir: path48.dirname(absolutePath),
41119
41522
  filter: options?.filter,
41120
41523
  category: options?.category
41121
41524
  });
@@ -41203,6 +41606,7 @@ export {
41203
41606
  negateScore,
41204
41607
  toSnakeCaseDeep,
41205
41608
  toCamelCaseDeep,
41609
+ getRepoCheckoutRef,
41206
41610
  CodeGrader,
41207
41611
  executeScript,
41208
41612
  DEFAULT_GRADER_TEMPLATE,
@@ -41357,8 +41761,20 @@ export {
41357
41761
  createTempWorkspace,
41358
41762
  cleanupWorkspace,
41359
41763
  cleanupEvalWorkspaces,
41764
+ resolveRepoCloneUrl,
41765
+ normalizeRepoIdentity,
41360
41766
  computeWorkspaceFingerprint,
41361
41767
  WorkspacePoolManager,
41768
+ getProjectsRegistryPath,
41769
+ loadProjectRegistry,
41770
+ saveProjectRegistry,
41771
+ deriveProjectId,
41772
+ addProject,
41773
+ removeProject,
41774
+ getProject,
41775
+ getProjectForPath,
41776
+ touchProject,
41777
+ discoverProjects,
41362
41778
  RepoManager,
41363
41779
  resolveWorkspaceTemplate,
41364
41780
  executeWorkspaceScript,
@@ -41377,9 +41793,7 @@ export {
41377
41793
  extractThreshold,
41378
41794
  resolveResultsConfigForProject,
41379
41795
  detectFormat,
41380
- parseRepoSource,
41381
- parseRepoCheckout,
41382
- parseRepoClone,
41796
+ parseRepoConfig,
41383
41797
  buildPromptInputs,
41384
41798
  readTestSuiteMetadata,
41385
41799
  loadTestSuite,
@@ -41394,4 +41808,4 @@ export {
41394
41808
  loadTsEvalFile,
41395
41809
  loadTsEvalSuite
41396
41810
  };
41397
- //# sourceMappingURL=chunk-RL4S2FBZ.js.map
41811
+ //# sourceMappingURL=chunk-VBHHZQS6.js.map