agentv 4.38.1 → 4.40.1-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/{artifact-writer-MK5X5MSO.js → artifact-writer-GIAIMGPQ.js} +14 -11
  2. package/dist/{chunk-QOBQ5XYF.js → chunk-76FOHROU.js} +16 -4
  3. package/dist/chunk-76FOHROU.js.map +1 -0
  4. package/dist/{chunk-VBHHZQS6.js → chunk-BLXYBUU4.js} +1825 -333
  5. package/dist/chunk-BLXYBUU4.js.map +1 -0
  6. package/dist/{chunk-NLTIK3LV.js → chunk-I3SC4FOT.js} +499 -347
  7. package/dist/chunk-I3SC4FOT.js.map +1 -0
  8. package/dist/{chunk-OIN3MVOD.js → chunk-S2JJCLHV.js} +67 -68
  9. package/dist/chunk-S2JJCLHV.js.map +1 -0
  10. package/dist/chunk-TWQP7JYQ.js +494 -0
  11. package/dist/chunk-TWQP7JYQ.js.map +1 -0
  12. package/dist/{chunk-6M5S4IJW.js → chunk-WKA5QDNQ.js} +586 -183
  13. package/dist/chunk-WKA5QDNQ.js.map +1 -0
  14. package/dist/cli.js +6 -6
  15. package/dist/dashboard/assets/index-BnYCCJ7O.css +1 -0
  16. package/dist/dashboard/assets/index-DaueD7GO.js +118 -0
  17. package/dist/dashboard/assets/{index-SIl6NbIJ.js → index-_jpKSzIf.js} +1 -1
  18. package/dist/dashboard/index.html +2 -2
  19. package/dist/{dist-HVLBDG5F.js → dist-6Z4OSITR.js} +54 -16
  20. package/dist/index.js +6 -6
  21. package/dist/{interactive-45LPG2YJ.js → interactive-OUB3GZRC.js} +6 -6
  22. package/dist/{otlp-json-file-exporter-RJFPCKVK-T6N4OGWG.js → otlp-json-file-exporter-RY63S3IG-PZBQPVYY.js} +2 -2
  23. package/dist/skills/agentv-eval-writer/SKILL.md +49 -24
  24. package/dist/skills/agentv-eval-writer/references/custom-evaluators.md +21 -15
  25. package/dist/{ts-eval-loader-TJT6BGFF-DI7XNSO4.js → ts-eval-loader-NWH3B4HG-UXXCZKLP.js} +2 -2
  26. package/package.json +1 -1
  27. package/dist/chunk-6M5S4IJW.js.map +0 -1
  28. package/dist/chunk-DKUAETXE.js +0 -1362
  29. package/dist/chunk-DKUAETXE.js.map +0 -1
  30. package/dist/chunk-NLTIK3LV.js.map +0 -1
  31. package/dist/chunk-OIN3MVOD.js.map +0 -1
  32. package/dist/chunk-QOBQ5XYF.js.map +0 -1
  33. package/dist/chunk-VBHHZQS6.js.map +0 -1
  34. package/dist/dashboard/assets/index-BpnllKET.css +0 -1
  35. package/dist/dashboard/assets/index-Cm9SUopp.js +0 -118
  36. /package/dist/{artifact-writer-MK5X5MSO.js.map → artifact-writer-GIAIMGPQ.js.map} +0 -0
  37. /package/dist/{dist-HVLBDG5F.js.map → dist-6Z4OSITR.js.map} +0 -0
  38. /package/dist/{interactive-45LPG2YJ.js.map → interactive-OUB3GZRC.js.map} +0 -0
  39. /package/dist/{otlp-json-file-exporter-RJFPCKVK-T6N4OGWG.js.map → otlp-json-file-exporter-RY63S3IG-PZBQPVYY.js.map} +0 -0
  40. /package/dist/{ts-eval-loader-TJT6BGFF-DI7XNSO4.js.map → ts-eval-loader-NWH3B4HG-UXXCZKLP.js.map} +0 -0
@@ -1,17 +1,18 @@
1
1
  import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
2
2
  import {
3
- buildTraceFromMessages,
4
3
  external_exports,
5
4
  extractLastAssistantContent,
6
5
  getAgentvDataDir,
7
6
  getRepoCheckoutRef,
7
+ groupTranscriptJsonLines,
8
8
  interpolateEnv,
9
9
  normalizeRepoIdentity,
10
10
  normalizeToolCall,
11
11
  parseRepoConfig,
12
12
  parseYamlValue,
13
+ readTranscriptJsonl,
13
14
  resolveRepoCloneUrl
14
- } from "./chunk-VBHHZQS6.js";
15
+ } from "./chunk-BLXYBUU4.js";
15
16
 
16
17
  // ../../packages/core/dist/index.js
17
18
  import { readFileSync } from "node:fs";
@@ -28,7 +29,7 @@ import {
28
29
  rmSync,
29
30
  writeFileSync
30
31
  } from "node:fs";
31
- import { cp, mkdtemp, readdir, rm, stat } from "node:fs/promises";
32
+ import { cp, lstat, mkdtemp, readdir, rm, stat } from "node:fs/promises";
32
33
  import os from "node:os";
33
34
  import path3 from "node:path";
34
35
  import { promisify } from "node:util";
@@ -41,7 +42,6 @@ import path4 from "node:path";
41
42
  import { readdir as readdir3, stat as stat3 } from "node:fs/promises";
42
43
  import { homedir as homedir2 } from "node:os";
43
44
  import path5 from "node:path";
44
- import { readFile as readFile2 } from "node:fs/promises";
45
45
  function codeGraderInstruction(graderName, description) {
46
46
  const desc = description ? ` This grader: ${description}.` : "";
47
47
  return `Run \`agentv eval assert ${graderName} --agent-output <agent_output> --agent-input <original_prompt>\` and check the result.${desc} The command accepts --agent-output (the agent's full response text) and --agent-input (the original user prompt). It returns JSON on stdout: {"score": 0-1, "reasoning": "..."}. A score >= 0.5 means pass (exit 0); below 0.5 means fail (exit 1).`;
@@ -529,6 +529,8 @@ var RESULTS_REPO_RESULTS_DIR = ".agentv/results";
529
529
  var RESULTS_REPO_RUNS_DIR = `${RESULTS_REPO_RESULTS_DIR}/runs`;
530
530
  var RESULTS_REPO_COMMIT_EMAIL = "agentv@results-repo";
531
531
  var RESULTS_REPO_COMMIT_NAME = "AgentV Results";
532
+ var DEFAULT_RESULTS_BRANCH = "agentv/results/v1";
533
+ var GIT_EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
532
534
  var activeResultsRepoSyncs = /* @__PURE__ */ new Set();
533
535
  function sanitizeRepoSlug(repo) {
534
536
  return repo.trim().replace(/[^A-Za-z0-9._-]+/g, "-");
@@ -547,16 +549,26 @@ function expandHome(p) {
547
549
  }
548
550
  return p;
549
551
  }
550
- function normalizeResultsConfig(config) {
551
- const repo = config.repo.trim();
552
- const branch = config.branch?.trim();
553
- const resolvedPath = config.path ? expandHome(config.path.trim()) : path3.join(getAgentvDataDir(), "results", sanitizeRepoSlug(repo));
552
+ function normalizeResultsConfig(config, options) {
553
+ const repoUrl = (config.repo_url ?? config.repo)?.trim();
554
+ const repoPath = config.repo_path?.trim();
555
+ const repo = repoUrl ?? repoPath ?? "";
556
+ const branch = config.branch?.trim() || (repoPath ? DEFAULT_RESULTS_BRANCH : void 0);
557
+ const remote = config.remote?.trim() || "origin";
558
+ const autoPush = config.sync?.auto_push ?? config.auto_push === true;
559
+ const requirePush = config.sync?.require_push === true;
560
+ const resolvedRepoPath = repoPath ? path3.resolve(options?.baseDir ?? process.cwd(), expandHome(repoPath)) : void 0;
561
+ const resolvedPath = config.path ? expandHome(config.path.trim()) : repoUrl ? path3.join(getAgentvDataDir(), "results", sanitizeRepoSlug(repoUrl)) : resolvedRepoPath ?? path3.join(getAgentvDataDir(), "results", sanitizeRepoSlug(repo));
554
562
  return {
555
563
  mode: "github",
556
564
  repo,
565
+ ...repoUrl ? { repo_url: repoUrl } : {},
566
+ ...resolvedRepoPath ? { repo_path: resolvedRepoPath } : {},
557
567
  ...branch ? { branch } : {},
568
+ remote,
558
569
  path: resolvedPath,
559
- auto_push: config.auto_push === true,
570
+ auto_push: autoPush,
571
+ require_push: requirePush,
560
572
  branch_prefix: config.branch_prefix?.trim() || "eval-results"
561
573
  };
562
574
  }
@@ -614,10 +626,11 @@ function getGitEnv() {
614
626
  env[key] = value;
615
627
  }
616
628
  }
629
+ env.GIT_TERMINAL_PROMPT = "0";
617
630
  return env;
618
631
  }
619
632
  async function runGit(args, options) {
620
- return runCommand("git", args, { ...options, env: getGitEnv() });
633
+ return runCommand("git", args, { ...options, env: { ...getGitEnv(), ...options?.env } });
621
634
  }
622
635
  async function runGh(args, options) {
623
636
  return runCommand("gh", args, options);
@@ -645,40 +658,34 @@ async function resolveDefaultBranch(repoDir) {
645
658
  }
646
659
  return "main";
647
660
  }
648
- async function fetchResultsRepo(repoDir) {
649
- await runGit(["fetch", "origin", "--prune"], { cwd: repoDir });
661
+ async function fetchResultsRepo(repoDir, remote = "origin") {
662
+ await runGit(["fetch", remote, "--prune"], { cwd: repoDir });
650
663
  }
651
- function remoteBranchRef(branch) {
652
- return `origin/${branch}`;
664
+ function remoteBranchRef(branch, remote = "origin") {
665
+ return `${remote}/${branch}`;
653
666
  }
654
- function missingConfiguredBranchError(config) {
655
- const branch = config.branch ?? "<unknown>";
656
- return new Error(
657
- [
658
- `Results repo remote branch '${branch}' does not exist in ${config.repo}.`,
659
- "Create the storage branch once, then retry. Example:",
660
- ` git clone ${resolveResultsRepoUrl(config.repo)} /tmp/agentv-results-init`,
661
- " cd /tmp/agentv-results-init",
662
- ` git switch --orphan ${branch}`,
663
- " git rm -rf .",
664
- ' git commit --allow-empty -m "chore(results): initialize AgentV results branch"',
665
- ` git push origin HEAD:${branch}`
666
- ].join("\n")
667
- );
668
- }
669
- async function assertConfiguredResultsBranchExists(repoDir, config) {
670
- if (!config.branch) {
671
- return void 0;
672
- }
673
- const ref = remoteBranchRef(config.branch);
667
+ async function gitRefExists(repoDir, ref) {
674
668
  const { stdout } = await runGit(["rev-parse", "--verify", `${ref}^{commit}`], {
675
669
  cwd: repoDir,
676
670
  check: false
677
671
  });
678
- if (!stdout.trim()) {
679
- throw missingConfiguredBranchError(config);
672
+ return stdout.trim().length > 0;
673
+ }
674
+ async function configuredResultsBranchRef(repoDir, config) {
675
+ if (!config.branch) {
676
+ return void 0;
677
+ }
678
+ const remoteRef = remoteBranchRef(config.branch, config.remote);
679
+ if (await gitRefExists(repoDir, remoteRef)) {
680
+ return remoteRef;
680
681
  }
681
- return ref;
682
+ if (await gitRefExists(repoDir, config.branch)) {
683
+ return config.branch;
684
+ }
685
+ return void 0;
686
+ }
687
+ async function assertConfiguredResultsBranchExists(repoDir, config) {
688
+ return configuredResultsBranchRef(repoDir, config);
682
689
  }
683
690
  async function localBranchExists(repoDir, branch) {
684
691
  const { stdout } = await runGit(["rev-parse", "--verify", `refs/heads/${branch}`], {
@@ -688,24 +695,40 @@ async function localBranchExists(repoDir, branch) {
688
695
  return stdout.trim().length > 0;
689
696
  }
690
697
  async function checkoutConfiguredResultsBranch(repoDir, config) {
698
+ const branch = config.branch;
699
+ if (!branch) {
700
+ return void 0;
701
+ }
691
702
  const remoteRef = await assertConfiguredResultsBranchExists(repoDir, config);
692
- if (!config.branch || !remoteRef) {
703
+ if (!remoteRef) {
704
+ await createOrphanResultsBranch(repoDir, branch);
705
+ await runGit(["checkout", branch], { cwd: repoDir });
693
706
  return void 0;
694
707
  }
695
708
  const currentBranch = await getCurrentBranch(repoDir);
696
- if (currentBranch !== config.branch) {
697
- if (await localBranchExists(repoDir, config.branch)) {
698
- await runGit(["checkout", config.branch], { cwd: repoDir });
709
+ if (currentBranch !== branch) {
710
+ if (await localBranchExists(repoDir, branch)) {
711
+ await runGit(["checkout", branch], { cwd: repoDir });
699
712
  } else {
700
- await runGit(["checkout", "--track", "-b", config.branch, remoteRef], { cwd: repoDir });
713
+ await runGit(["checkout", "--track", "-b", branch, remoteRef], { cwd: repoDir });
701
714
  }
702
715
  }
703
- await runGit(["branch", "--set-upstream-to", remoteRef, config.branch], {
704
- cwd: repoDir,
705
- check: false
706
- });
716
+ if (remoteRef === remoteBranchRef(branch, config.remote)) {
717
+ await runGit(["branch", "--set-upstream-to", remoteRef, branch], {
718
+ cwd: repoDir,
719
+ check: false
720
+ });
721
+ }
707
722
  return remoteRef;
708
723
  }
724
+ async function createOrphanResultsBranch(repoDir, branch) {
725
+ await ensureResultsRepoCommitIdentity(repoDir);
726
+ const { stdout } = await runGit(
727
+ ["commit-tree", GIT_EMPTY_TREE, "-m", "chore(results): initialize AgentV results branch"],
728
+ { cwd: repoDir }
729
+ );
730
+ await runGit(["update-ref", `refs/heads/${branch}`, stdout.trim()], { cwd: repoDir });
731
+ }
709
732
  async function isGitRepository(repoDir) {
710
733
  try {
711
734
  const { stdout } = await runGit(["rev-parse", "--is-inside-work-tree"], { cwd: repoDir });
@@ -714,8 +737,13 @@ async function isGitRepository(repoDir) {
714
737
  return false;
715
738
  }
716
739
  }
740
+ async function resolveGitTopLevel(repoDir) {
741
+ const { stdout } = await runGit(["rev-parse", "--show-toplevel"], { cwd: repoDir });
742
+ return stdout.trim() || repoDir;
743
+ }
717
744
  function updateStatusFile(config, patch) {
718
- const cachePaths = getResultsRepoLocalPaths(config.repo);
745
+ const repo = typeof config.repo === "string" ? config.repo : config.repo_url ?? config.repo_path ?? "";
746
+ const cachePaths = getResultsRepoLocalPaths(repo);
719
747
  const current = readPersistedStatus(cachePaths.statusFile);
720
748
  writePersistedStatus(cachePaths.statusFile, {
721
749
  ...current,
@@ -724,6 +752,12 @@ function updateStatusFile(config, patch) {
724
752
  }
725
753
  async function ensureResultsRepoClone(config) {
726
754
  const normalized = normalizeResultsConfig(config);
755
+ if (normalized.repo_path) {
756
+ if (!await isGitRepository(normalized.repo_path)) {
757
+ throw new Error(`Results repo_path is not a git repository: ${normalized.repo_path}`);
758
+ }
759
+ return resolveGitTopLevel(normalized.repo_path);
760
+ }
727
761
  const cachePaths = getResultsRepoLocalPaths(normalized.repo);
728
762
  const cloneDir = normalized.path;
729
763
  mkdirSync(cachePaths.rootDir, { recursive: true });
@@ -736,7 +770,7 @@ async function ensureResultsRepoClone(config) {
736
770
  await runGit([
737
771
  "clone",
738
772
  "--filter=blob:none",
739
- resolveResultsRepoUrl(normalized.repo),
773
+ resolveResultsRepoUrl(normalized.repo_url ?? normalized.repo),
740
774
  cloneDir
741
775
  ]);
742
776
  return cloneDir;
@@ -767,8 +801,10 @@ function getResultsRepoStatus(config) {
767
801
  configured: true,
768
802
  available: existsSync(normalized.path),
769
803
  repo: normalized.repo,
804
+ ...normalized.repo_path !== void 0 && { repo_path: normalized.repo_path },
770
805
  path: normalized.path,
771
806
  auto_push: normalized.auto_push,
807
+ require_push: normalized.require_push,
772
808
  branch_prefix: normalized.branch_prefix,
773
809
  local_dir: normalized.path,
774
810
  last_synced_at: persisted.last_synced_at,
@@ -822,7 +858,7 @@ async function resolveComparisonRef(repoDir, config) {
822
858
  return upstreamRef;
823
859
  }
824
860
  const baseBranch = await resolveDefaultBranch(repoDir);
825
- const fallback = `origin/${baseBranch}`;
861
+ const fallback = `${config?.remote ?? "origin"}/${baseBranch}`;
826
862
  const { stdout: fallbackSha } = await runGit(["rev-parse", "--verify", fallback], {
827
863
  cwd: repoDir,
828
864
  check: false
@@ -845,6 +881,56 @@ async function getAheadBehind(repoDir, upstream) {
845
881
  ...Number.isFinite(behind) && { behind }
846
882
  };
847
883
  }
884
+ async function getAheadBehindForRefs(repoDir, leftRef, rightRef) {
885
+ const { stdout } = await runGit(
886
+ ["rev-list", "--left-right", "--count", `${leftRef}...${rightRef}`],
887
+ {
888
+ cwd: repoDir,
889
+ check: false
890
+ }
891
+ );
892
+ const [aheadText, behindText] = stdout.trim().split(/\s+/);
893
+ const ahead = Number.parseInt(aheadText ?? "", 10);
894
+ const behind = Number.parseInt(behindText ?? "", 10);
895
+ return {
896
+ ...Number.isFinite(ahead) && { ahead },
897
+ ...Number.isFinite(behind) && { behind }
898
+ };
899
+ }
900
+ async function inspectResultsStorageBranchGit(repoDir, config) {
901
+ if (!config.branch) {
902
+ return {
903
+ syncStatus: "clean",
904
+ dirtyPaths: [],
905
+ conflictedPaths: []
906
+ };
907
+ }
908
+ const localRef = `refs/heads/${config.branch}`;
909
+ const upstream = remoteBranchRef(config.branch, config.remote);
910
+ const localExists = await gitRefExists(repoDir, localRef);
911
+ const remoteExists = await gitRefExists(repoDir, upstream);
912
+ const { ahead = 0, behind = 0 } = localExists && remoteExists ? await getAheadBehindForRefs(repoDir, localRef, upstream) : {
913
+ ahead: localExists && !remoteExists ? 1 : 0,
914
+ behind: !localExists && remoteExists ? 1 : 0
915
+ };
916
+ let syncStatus = "clean";
917
+ if (ahead > 0 && behind > 0) {
918
+ syncStatus = "diverged";
919
+ } else if (behind > 0) {
920
+ syncStatus = "behind";
921
+ } else if (ahead > 0) {
922
+ syncStatus = "ahead";
923
+ }
924
+ return {
925
+ syncStatus,
926
+ branch: config.branch,
927
+ ...remoteExists && { upstream },
928
+ ahead,
929
+ behind,
930
+ dirtyPaths: [],
931
+ conflictedPaths: []
932
+ };
933
+ }
848
934
  async function hasInProgressGitConflict(repoDir) {
849
935
  const markers = ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "REBASE_HEAD"];
850
936
  for (const marker of markers) {
@@ -971,18 +1057,19 @@ function isSafeResultsRepoPath(p) {
971
1057
  function areSafeResultsRepoPaths(paths) {
972
1058
  return paths.length > 0 && paths.every(isSafeResultsRepoPath);
973
1059
  }
974
- async function getAheadPaths(repoDir, upstream) {
1060
+ async function getAheadPaths(repoDir, upstream, branch = "HEAD") {
975
1061
  if (!upstream) {
976
1062
  return [];
977
1063
  }
978
- const { stdout } = await runGit(["diff", "--name-only", `${upstream}..HEAD`], {
1064
+ const { stdout } = await runGit(["diff", "--name-only", `${upstream}..${branch}`], {
979
1065
  cwd: repoDir,
980
1066
  check: false
981
1067
  });
982
1068
  return stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).sort();
983
1069
  }
984
- function getPushTargetBranch(upstream, baseBranch) {
985
- return upstream?.startsWith("origin/") ? upstream.slice("origin/".length) : baseBranch;
1070
+ function getPushTargetBranch(upstream, baseBranch, remote = "origin") {
1071
+ const prefix = `${remote}/`;
1072
+ return upstream?.startsWith(prefix) ? upstream.slice(prefix.length) : baseBranch;
986
1073
  }
987
1074
  async function statusFromInspection(normalized, repoDir) {
988
1075
  return withGitInspection(
@@ -1009,8 +1096,15 @@ async function getResultsRepoSyncStatus(config) {
1009
1096
  };
1010
1097
  }
1011
1098
  try {
1099
+ if (normalized.repo_path) {
1100
+ await fetchResultsRepo(normalized.path, normalized.remote).catch(() => void 0);
1101
+ return withGitInspection(
1102
+ baseStatus,
1103
+ await inspectResultsStorageBranchGit(normalized.path, normalized)
1104
+ );
1105
+ }
1012
1106
  if (normalized.branch) {
1013
- await fetchResultsRepo(normalized.path);
1107
+ await fetchResultsRepo(normalized.path, normalized.remote).catch(() => void 0);
1014
1108
  await checkoutConfiguredResultsBranch(normalized.path, normalized);
1015
1109
  }
1016
1110
  return withGitInspection(baseStatus, await inspectResultsRepoGit(normalized.path, normalized));
@@ -1027,8 +1121,10 @@ async function syncResultsRepo(config) {
1027
1121
  const normalized = normalizeResultsConfig(config);
1028
1122
  try {
1029
1123
  const repoDir = await ensureResultsRepoClone(normalized);
1030
- await fetchResultsRepo(repoDir);
1031
- await checkoutConfiguredResultsBranch(repoDir, normalized);
1124
+ await fetchResultsRepo(repoDir, normalized.remote);
1125
+ if (!normalized.repo_path) {
1126
+ await checkoutConfiguredResultsBranch(repoDir, normalized);
1127
+ }
1032
1128
  updateStatusFile(normalized, {
1033
1129
  last_synced_at: (/* @__PURE__ */ new Date()).toISOString(),
1034
1130
  last_error: void 0
@@ -1061,7 +1157,59 @@ async function syncResultsRepoForProject(config) {
1061
1157
  let commitCreated = false;
1062
1158
  try {
1063
1159
  const repoDir = await ensureResultsRepoClone(normalized);
1064
- await fetchResultsRepo(repoDir);
1160
+ if (normalized.repo_path) {
1161
+ try {
1162
+ await fetchResultsRepo(repoDir, normalized.remote);
1163
+ } catch (error) {
1164
+ if (normalized.require_push) {
1165
+ throw error;
1166
+ }
1167
+ }
1168
+ if (normalized.auto_push || normalized.require_push) {
1169
+ if (normalized.branch) {
1170
+ const localRef = `refs/heads/${normalized.branch}`;
1171
+ if (await gitRefExists(repoDir, localRef)) {
1172
+ try {
1173
+ await runGit(
1174
+ [
1175
+ "push",
1176
+ "--porcelain",
1177
+ normalized.remote,
1178
+ `${localRef}:refs/heads/${normalized.branch}`
1179
+ ],
1180
+ { cwd: repoDir }
1181
+ );
1182
+ pushPerformed = true;
1183
+ } catch (error) {
1184
+ updateStatusFile(normalized, { last_error: getStatusMessage(error) });
1185
+ if (normalized.require_push) {
1186
+ throw error;
1187
+ }
1188
+ const status = await getResultsRepoSyncStatus(normalized);
1189
+ return withBlockedStatus(
1190
+ status,
1191
+ `Results repo push was rejected: ${getStatusMessage(error)}`,
1192
+ {
1193
+ pullPerformed,
1194
+ pushPerformed,
1195
+ commitCreated
1196
+ }
1197
+ );
1198
+ }
1199
+ }
1200
+ }
1201
+ }
1202
+ updateStatusFile(normalized, {
1203
+ last_synced_at: (/* @__PURE__ */ new Date()).toISOString(),
1204
+ last_error: void 0
1205
+ });
1206
+ return withActionFlags(await getResultsRepoSyncStatus(normalized), {
1207
+ pullPerformed,
1208
+ pushPerformed,
1209
+ commitCreated
1210
+ });
1211
+ }
1212
+ await fetchResultsRepo(repoDir, normalized.remote);
1065
1213
  await checkoutConfiguredResultsBranch(repoDir, normalized);
1066
1214
  let inspection = await inspectResultsRepoGit(repoDir, normalized);
1067
1215
  if (inspection.syncStatus === "conflicted") {
@@ -1198,14 +1346,14 @@ async function syncResultsRepoForProject(config) {
1198
1346
  });
1199
1347
  }
1200
1348
  const baseBranch = normalized.branch ?? await resolveDefaultBranch(repoDir);
1201
- const targetBranch = getPushTargetBranch(inspection.upstream, baseBranch);
1349
+ const targetBranch = getPushTargetBranch(inspection.upstream, baseBranch, normalized.remote);
1202
1350
  try {
1203
- await runGit(["push", "origin", `HEAD:${targetBranch}`], { cwd: repoDir });
1351
+ await runGit(["push", normalized.remote, `HEAD:${targetBranch}`], { cwd: repoDir });
1204
1352
  pushPerformed = true;
1205
- await fetchResultsRepo(repoDir);
1353
+ await fetchResultsRepo(repoDir, normalized.remote);
1206
1354
  inspection = await inspectResultsRepoGit(repoDir, normalized);
1207
1355
  } catch (error) {
1208
- await fetchResultsRepo(repoDir).catch(() => void 0);
1356
+ await fetchResultsRepo(repoDir, normalized.remote).catch(() => void 0);
1209
1357
  inspection = await inspectResultsRepoGit(repoDir, normalized);
1210
1358
  const status = withGitInspection(getResultsRepoStatus(normalized), inspection);
1211
1359
  const reason = `Results repo push was rejected: ${getStatusMessage(error)}`;
@@ -1239,8 +1387,10 @@ async function checkoutResultsRepoBranch(config, branchName) {
1239
1387
  const normalized = normalizeResultsConfig(config);
1240
1388
  const repoDir = await ensureResultsRepoClone(normalized);
1241
1389
  const baseBranch = await resolveDefaultBranch(repoDir);
1242
- await fetchResultsRepo(repoDir);
1243
- await runGit(["checkout", "-B", branchName, `origin/${baseBranch}`], { cwd: repoDir });
1390
+ await fetchResultsRepo(repoDir, normalized.remote);
1391
+ await runGit(["checkout", "-B", branchName, `${normalized.remote}/${baseBranch}`], {
1392
+ cwd: repoDir
1393
+ });
1244
1394
  updateStatusFile(normalized, { last_error: void 0 });
1245
1395
  return {
1246
1396
  branchName,
@@ -1252,12 +1402,15 @@ async function prepareResultsRepoBranch(config, branchName) {
1252
1402
  const normalized = normalizeResultsConfig(config);
1253
1403
  const cloneDir = await ensureResultsRepoClone(normalized);
1254
1404
  const baseBranch = await resolveDefaultBranch(cloneDir);
1255
- await fetchResultsRepo(cloneDir);
1405
+ await fetchResultsRepo(cloneDir, normalized.remote);
1256
1406
  const worktreeRoot = await mkdtemp(path3.join(os.tmpdir(), "agentv-results-repo-"));
1257
1407
  const worktreeDir = path3.join(worktreeRoot, "repo");
1258
- await runGit(["worktree", "add", "-B", branchName, worktreeDir, `origin/${baseBranch}`], {
1259
- cwd: cloneDir
1260
- });
1408
+ await runGit(
1409
+ ["worktree", "add", "-B", branchName, worktreeDir, `${normalized.remote}/${baseBranch}`],
1410
+ {
1411
+ cwd: cloneDir
1412
+ }
1413
+ );
1261
1414
  return {
1262
1415
  branchName,
1263
1416
  baseBranch,
@@ -1307,7 +1460,7 @@ async function commitAndPushResultsBranch(params) {
1307
1460
  }
1308
1461
  async function pushResultsRepoBranch(config, branchName, cwd) {
1309
1462
  const normalized = normalizeResultsConfig(config);
1310
- await runGit(["push", "-u", "origin", branchName], {
1463
+ await runGit(["push", "-u", normalized.remote, branchName], {
1311
1464
  cwd: cwd ?? normalized.path
1312
1465
  });
1313
1466
  updateStatusFile(normalized, {
@@ -1337,27 +1490,230 @@ async function createDraftResultsPr(params) {
1337
1490
  return stdout.trim();
1338
1491
  }
1339
1492
  var DIRECT_PUSH_MAX_RETRIES = 3;
1340
- async function hasUnpushedCommits(repoDir, upstreamRef) {
1341
- const { stdout } = await runGit(["rev-list", "--count", `${upstreamRef}..HEAD`], {
1493
+ async function hasUnpushedCommits(repoDir, upstreamRef, branch) {
1494
+ const { stdout } = await runGit(["rev-list", "--count", `${upstreamRef}..refs/heads/${branch}`], {
1342
1495
  cwd: repoDir,
1343
1496
  check: false
1344
1497
  });
1345
1498
  return Number.parseInt(stdout.trim(), 10) > 0;
1346
1499
  }
1500
+ async function countUnpushedCommits(repoDir, upstreamRef, branch) {
1501
+ const { stdout } = await runGit(["rev-list", "--count", `${upstreamRef}..refs/heads/${branch}`], {
1502
+ cwd: repoDir,
1503
+ check: false
1504
+ });
1505
+ const count = Number.parseInt(stdout.trim(), 10);
1506
+ return Number.isFinite(count) ? count : 0;
1507
+ }
1508
+ async function assertValidResultsBranchName(repoDir, branch) {
1509
+ if (branch.length === 0 || branch.includes("..") || branch.startsWith("/") || branch.endsWith("/") || branch.endsWith(".lock") || [...branch].some((char) => {
1510
+ const code = char.charCodeAt(0);
1511
+ return code <= 31 || code === 127;
1512
+ })) {
1513
+ throw new Error(`Invalid results branch name: ${branch}`);
1514
+ }
1515
+ await runGit(["check-ref-format", "--branch", branch], { cwd: repoDir });
1516
+ }
1517
+ function normalizeDestinationPath(destinationPath) {
1518
+ const normalized = destinationPath.split(path3.sep).join("/");
1519
+ const segments = normalized.split("/").filter(Boolean);
1520
+ if (segments.length === 0 || normalized.startsWith("/") || segments.some((segment) => segment === "..")) {
1521
+ throw new Error(`Invalid results destination path: ${destinationPath}`);
1522
+ }
1523
+ return segments.join("/");
1524
+ }
1525
+ async function listSourceFiles(sourceDir) {
1526
+ const entries = [];
1527
+ async function visit(dir) {
1528
+ for (const entry of await readdir(dir, { withFileTypes: true })) {
1529
+ const absolutePath = path3.join(dir, entry.name);
1530
+ if (entry.isDirectory()) {
1531
+ await visit(absolutePath);
1532
+ } else if (entry.isFile() || entry.isSymbolicLink()) {
1533
+ entries.push(absolutePath);
1534
+ }
1535
+ }
1536
+ }
1537
+ await visit(sourceDir);
1538
+ entries.sort();
1539
+ return entries;
1540
+ }
1541
+ async function getExistingRunTreePaths(repoDir, ref, destinationTreePath) {
1542
+ if (!ref) {
1543
+ return [];
1544
+ }
1545
+ const { stdout } = await runGit(["ls-tree", "-r", "--name-only", ref, destinationTreePath], {
1546
+ cwd: repoDir,
1547
+ check: false
1548
+ });
1549
+ return stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
1550
+ }
1551
+ async function resolveStorageBranchBase(params) {
1552
+ const localRef = `refs/heads/${params.normalized.branch}`;
1553
+ const remoteRef = `refs/remotes/${params.normalized.remote}/${params.normalized.branch}`;
1554
+ const localExists = await gitRefExists(params.repoDir, localRef);
1555
+ const remoteExists = await gitRefExists(params.repoDir, remoteRef);
1556
+ const baseRef = params.preferRemote ? remoteExists ? remoteRef : localExists ? localRef : void 0 : localExists ? localRef : remoteExists ? remoteRef : void 0;
1557
+ const baseCommit = baseRef ? (await runGit(["rev-parse", `${baseRef}^{commit}`], {
1558
+ cwd: params.repoDir
1559
+ })).stdout.trim() : void 0;
1560
+ const baseTree = baseRef ? (await runGit(["rev-parse", `${baseRef}^{tree}`], {
1561
+ cwd: params.repoDir
1562
+ })).stdout.trim() : void 0;
1563
+ return { baseRef, baseCommit, baseTree, localRef, localExists, remoteRef, remoteExists };
1564
+ }
1565
+ async function ensureResultsBranchNotCheckedOut(repoDir, normalized) {
1566
+ const currentBranch = await getCurrentBranch(repoDir);
1567
+ if (currentBranch !== normalized.branch) {
1568
+ return;
1569
+ }
1570
+ if (normalized.repo_path) {
1571
+ throw new Error(
1572
+ `Refusing to publish results while '${normalized.branch}' is checked out in ${repoDir}`
1573
+ );
1574
+ }
1575
+ await runGit(["checkout", "--detach"], { cwd: repoDir });
1576
+ }
1577
+ async function commitResultsRunWithTemporaryIndex(params) {
1578
+ const { normalized } = params;
1579
+ await assertValidResultsBranchName(params.repoDir, normalized.branch);
1580
+ await ensureResultsBranchNotCheckedOut(params.repoDir, normalized);
1581
+ const destinationRunPath = normalizeDestinationPath(params.destinationPath);
1582
+ const destinationTreePath = path3.posix.join(RESULTS_REPO_RUNS_DIR, destinationRunPath);
1583
+ const base = await resolveStorageBranchBase({
1584
+ repoDir: params.repoDir,
1585
+ normalized,
1586
+ preferRemote: params.preferRemoteBase
1587
+ });
1588
+ const indexRoot = await mkdtemp(path3.join(os.tmpdir(), "agentv-results-index-"));
1589
+ const indexFile = path3.join(indexRoot, "index");
1590
+ const indexEnv = { GIT_INDEX_FILE: indexFile };
1591
+ try {
1592
+ if (base.baseRef) {
1593
+ await runGit(["read-tree", base.baseRef], { cwd: params.repoDir, env: indexEnv });
1594
+ } else {
1595
+ await runGit(["read-tree", "--empty"], { cwd: params.repoDir, env: indexEnv });
1596
+ }
1597
+ const existingPaths = await getExistingRunTreePaths(
1598
+ params.repoDir,
1599
+ base.baseRef,
1600
+ destinationTreePath
1601
+ );
1602
+ for (const existingPath of existingPaths) {
1603
+ await runGit(["update-index", "--force-remove", "--", existingPath], {
1604
+ cwd: params.repoDir,
1605
+ env: indexEnv,
1606
+ check: false
1607
+ });
1608
+ }
1609
+ const sourceFiles = await listSourceFiles(params.sourceDir);
1610
+ for (const sourceFile of sourceFiles) {
1611
+ const relativeFile = path3.relative(params.sourceDir, sourceFile).split(path3.sep).join("/");
1612
+ const destinationFile = path3.posix.join(destinationTreePath, relativeFile);
1613
+ const fileStat = await lstat(sourceFile);
1614
+ let mode = fileStat.mode & 73 ? "100755" : "100644";
1615
+ let hashInputPath = sourceFile;
1616
+ if (fileStat.isSymbolicLink()) {
1617
+ mode = "120000";
1618
+ hashInputPath = sourceFile;
1619
+ }
1620
+ const { stdout: blob } = await runGit(["hash-object", "-w", "--no-filters", hashInputPath], {
1621
+ cwd: params.repoDir
1622
+ });
1623
+ await runGit(
1624
+ ["update-index", "--add", "--cacheinfo", `${mode},${blob.trim()},${destinationFile}`],
1625
+ {
1626
+ cwd: params.repoDir,
1627
+ env: indexEnv
1628
+ }
1629
+ );
1630
+ }
1631
+ const { stdout: newTreeStdout } = await runGit(["write-tree"], {
1632
+ cwd: params.repoDir,
1633
+ env: indexEnv
1634
+ });
1635
+ const newTree = newTreeStdout.trim();
1636
+ if (base.baseTree && newTree === base.baseTree) {
1637
+ return {
1638
+ commitCreated: false,
1639
+ branchUpdated: false,
1640
+ upstreamRef: base.remoteExists ? base.remoteRef : void 0
1641
+ };
1642
+ }
1643
+ await ensureResultsRepoCommitIdentity(params.repoDir);
1644
+ const commitArgs = [
1645
+ "commit-tree",
1646
+ newTree,
1647
+ ...base.baseCommit ? ["-p", base.baseCommit] : [],
1648
+ "-m",
1649
+ params.commitMessage,
1650
+ "-m",
1651
+ `AgentV-Run: ${params.targetRunId}`
1652
+ ];
1653
+ const { stdout: commitStdout } = await runGit(commitArgs, { cwd: params.repoDir });
1654
+ const commitSha = commitStdout.trim();
1655
+ await runGit(
1656
+ [
1657
+ "update-ref",
1658
+ `refs/heads/${normalized.branch}`,
1659
+ commitSha,
1660
+ ...base.localExists && !params.preferRemoteBase ? [base.baseCommit ?? ""] : []
1661
+ ].filter(Boolean),
1662
+ { cwd: params.repoDir }
1663
+ );
1664
+ if (base.remoteExists) {
1665
+ await runGit(["branch", "--set-upstream-to", base.remoteRef, normalized.branch], {
1666
+ cwd: params.repoDir,
1667
+ check: false
1668
+ });
1669
+ }
1670
+ return {
1671
+ commitCreated: true,
1672
+ branchUpdated: true,
1673
+ upstreamRef: base.remoteExists ? base.remoteRef : void 0
1674
+ };
1675
+ } finally {
1676
+ await rm(indexRoot, { recursive: true, force: true }).catch(() => void 0);
1677
+ }
1678
+ }
1347
1679
  async function pushDirectResultsToStorageBranch(params) {
1348
1680
  for (let attempt = 1; attempt <= DIRECT_PUSH_MAX_RETRIES; attempt++) {
1349
1681
  try {
1350
- await runGit(["push", "origin", `HEAD:${params.storageBranch}`], { cwd: params.repoDir });
1682
+ await runGit(
1683
+ [
1684
+ "push",
1685
+ "--porcelain",
1686
+ params.normalized.remote,
1687
+ `refs/heads/${params.storageBranch}:refs/heads/${params.storageBranch}`
1688
+ ],
1689
+ { cwd: params.repoDir }
1690
+ );
1351
1691
  updateStatusFile(params.normalized, {
1352
1692
  last_synced_at: (/* @__PURE__ */ new Date()).toISOString(),
1353
1693
  last_error: void 0
1354
1694
  });
1695
+ await fetchResultsRepo(params.repoDir, params.normalized.remote).catch(() => void 0);
1355
1696
  return;
1356
1697
  } catch (error) {
1357
1698
  const message = error instanceof Error ? error.message : String(error);
1358
1699
  if (attempt < DIRECT_PUSH_MAX_RETRIES && message.includes("non-fast-forward")) {
1359
- await fetchResultsRepo(params.repoDir);
1360
- await runGit(["rebase", params.upstreamRef], { cwd: params.repoDir });
1700
+ await fetchResultsRepo(params.repoDir, params.normalized.remote);
1701
+ const remoteRef = remoteBranchRef(params.storageBranch, params.normalized.remote);
1702
+ const localOnlyCount = await gitRefExists(params.repoDir, remoteRef) ? await countUnpushedCommits(params.repoDir, remoteRef, params.storageBranch) : 0;
1703
+ if (localOnlyCount > 1) {
1704
+ throw new Error(
1705
+ `Results branch has ${localOnlyCount} local commits and remote advanced; push manually after reconciling ${params.storageBranch}`
1706
+ );
1707
+ }
1708
+ await commitResultsRunWithTemporaryIndex({
1709
+ normalized: params.normalized,
1710
+ repoDir: params.repoDir,
1711
+ sourceDir: params.sourceDir,
1712
+ destinationPath: params.destinationPath,
1713
+ commitMessage: params.commitMessage,
1714
+ targetRunId: params.targetRunId,
1715
+ preferRemoteBase: true
1716
+ });
1361
1717
  } else {
1362
1718
  throw error;
1363
1719
  }
@@ -1367,43 +1723,63 @@ async function pushDirectResultsToStorageBranch(params) {
1367
1723
  async function directPushResults(params) {
1368
1724
  const normalized = normalizeResultsConfig(params.config);
1369
1725
  const repoDir = await ensureResultsRepoClone(normalized);
1370
- await fetchResultsRepo(repoDir);
1371
- const configuredRef = await checkoutConfiguredResultsBranch(repoDir, normalized);
1726
+ await fetchResultsRepo(repoDir, normalized.remote).catch((error) => {
1727
+ if (normalized.require_push) {
1728
+ throw error;
1729
+ }
1730
+ });
1372
1731
  const storageBranch = normalized.branch ?? await resolveDefaultBranch(repoDir);
1373
- const upstreamRef = configuredRef ?? remoteBranchRef(storageBranch);
1732
+ const storageConfig = {
1733
+ ...normalized,
1734
+ branch: storageBranch
1735
+ };
1374
1736
  const targetRunId = buildGitRunId(params.destinationPath);
1375
- const destinationDir = path3.join(
1376
- repoDir,
1377
- RESULTS_REPO_RESULTS_DIR,
1378
- "runs",
1379
- params.destinationPath
1380
- );
1381
- await stageResultsArtifacts({
1737
+ const result = await commitResultsRunWithTemporaryIndex({
1738
+ normalized: storageConfig,
1382
1739
  repoDir,
1383
1740
  sourceDir: params.sourceDir,
1384
- destinationDir
1385
- });
1386
- await runGit(["add", "--all"], { cwd: repoDir });
1387
- const { stdout: status } = await runGit(["status", "--porcelain"], {
1388
- cwd: repoDir,
1389
- check: false
1741
+ destinationPath: params.destinationPath,
1742
+ commitMessage: params.commitMessage,
1743
+ targetRunId
1390
1744
  });
1391
- if (status.trim().length === 0) {
1392
- if (await hasUnpushedCommits(repoDir, upstreamRef)) {
1393
- const aheadPaths = await getAheadPaths(repoDir, upstreamRef);
1394
- if (!areSafeResultsRepoPaths(aheadPaths)) {
1395
- throw new Error("Results repo has non-results committed changes");
1745
+ const shouldPush = normalized.auto_push || normalized.require_push;
1746
+ if (!shouldPush) {
1747
+ updateStatusFile(storageConfig, { last_error: void 0 });
1748
+ return result.commitCreated;
1749
+ }
1750
+ if (!result.commitCreated) {
1751
+ const hasUnpushed = result.upstreamRef ? await hasUnpushedCommits(repoDir, result.upstreamRef, storageBranch) : true;
1752
+ if (hasUnpushed) {
1753
+ const aheadPaths = result.upstreamRef ? await getAheadPaths(repoDir, result.upstreamRef, `refs/heads/${storageBranch}`) : [];
1754
+ if (result.upstreamRef && !areSafeResultsRepoPaths(aheadPaths)) {
1755
+ const error = new Error("Results repo has non-results committed changes");
1756
+ updateStatusFile(storageConfig, { last_error: error.message });
1757
+ throw error;
1396
1758
  }
1397
- await pushDirectResultsToStorageBranch({ normalized, repoDir, storageBranch, upstreamRef });
1759
+ await pushDirectResultsToStorageBranch({
1760
+ normalized: storageConfig,
1761
+ repoDir,
1762
+ storageBranch,
1763
+ upstreamRef: result.upstreamRef,
1764
+ sourceDir: params.sourceDir,
1765
+ destinationPath: params.destinationPath,
1766
+ commitMessage: params.commitMessage,
1767
+ targetRunId
1768
+ });
1398
1769
  return true;
1399
1770
  }
1400
1771
  return false;
1401
1772
  }
1402
- await ensureResultsRepoCommitIdentity(repoDir);
1403
- await runGit(["commit", "-m", params.commitMessage, "-m", `Agentv-Run: ${targetRunId}`], {
1404
- cwd: repoDir
1773
+ await pushDirectResultsToStorageBranch({
1774
+ normalized: storageConfig,
1775
+ repoDir,
1776
+ storageBranch,
1777
+ upstreamRef: result.upstreamRef,
1778
+ sourceDir: params.sourceDir,
1779
+ destinationPath: params.destinationPath,
1780
+ commitMessage: params.commitMessage,
1781
+ targetRunId
1405
1782
  });
1406
- await pushDirectResultsToStorageBranch({ normalized, repoDir, storageBranch, upstreamRef });
1407
1783
  return true;
1408
1784
  }
1409
1785
  function buildGitRunId(relativeRunPath) {
@@ -1517,10 +1893,18 @@ function buildWipBranchName(runDir) {
1517
1893
  async function setupWipWorktree(params) {
1518
1894
  const normalized = normalizeResultsConfig(params.config);
1519
1895
  const cloneDir = await ensureResultsRepoClone(normalized);
1520
- await fetchResultsRepo(cloneDir);
1521
- const baseRef = normalized.branch ? await assertConfiguredResultsBranchExists(cloneDir, normalized) : remoteBranchRef(await resolveDefaultBranch(cloneDir));
1896
+ await fetchResultsRepo(cloneDir, normalized.remote).catch((error) => {
1897
+ if (normalized.require_push) {
1898
+ throw error;
1899
+ }
1900
+ });
1901
+ let baseRef = normalized.branch ? await configuredResultsBranchRef(cloneDir, normalized) : remoteBranchRef(await resolveDefaultBranch(cloneDir), normalized.remote);
1902
+ if (!baseRef && normalized.branch) {
1903
+ await createOrphanResultsBranch(cloneDir, normalized.branch);
1904
+ baseRef = normalized.branch;
1905
+ }
1522
1906
  if (!baseRef) {
1523
- throw missingConfiguredBranchError(normalized);
1907
+ throw new Error("Could not resolve a base ref for the WIP results branch");
1524
1908
  }
1525
1909
  const worktreeRoot = await mkdtemp(path3.join(os.tmpdir(), "agentv-wip-"));
1526
1910
  const worktreeDir = path3.join(worktreeRoot, "repo");
@@ -1533,6 +1917,7 @@ async function setupWipWorktree(params) {
1533
1917
  wipBranch: params.wipBranch,
1534
1918
  worktreeDir,
1535
1919
  cloneDir,
1920
+ remote: normalized.remote,
1536
1921
  cleanup: async () => {
1537
1922
  try {
1538
1923
  await runGit(["worktree", "remove", "--force", worktreeDir], { cwd: cloneDir });
@@ -1568,7 +1953,7 @@ async function pushWipCheckpoint(params) {
1568
1953
  ["commit", "--amend", "-m", `wip(results): checkpoint ${params.handle.wipBranch} ${timestamp}`],
1569
1954
  { cwd: params.handle.worktreeDir }
1570
1955
  );
1571
- await runGit(["push", "--force", "origin", params.handle.wipBranch], {
1956
+ await runGit(["push", "--force", params.handle.remote, params.handle.wipBranch], {
1572
1957
  cwd: params.handle.worktreeDir
1573
1958
  });
1574
1959
  return true;
@@ -1576,7 +1961,7 @@ async function pushWipCheckpoint(params) {
1576
1961
  async function deleteWipBranch(params) {
1577
1962
  const normalized = normalizeResultsConfig(params.config);
1578
1963
  const cloneDir = await ensureResultsRepoClone(normalized);
1579
- await runGit(["push", "origin", "--delete", params.wipBranch], { cwd: cloneDir });
1964
+ await runGit(["push", normalized.remote, "--delete", params.wipBranch], { cwd: cloneDir });
1580
1965
  }
1581
1966
  async function listGitRuns(repoDir, ref = "origin/main") {
1582
1967
  const { stdout: treeOut } = await runGit(
@@ -1745,38 +2130,6 @@ function deriveCategory(relativePath) {
1745
2130
  const dirs = parts.slice(0, -1).filter((d) => d !== "evals");
1746
2131
  return dirs.length > 0 ? dirs.join("/") : DEFAULT_CATEGORY;
1747
2132
  }
1748
- var OTEL_BACKEND_PRESETS = {
1749
- langfuse: {
1750
- name: "langfuse",
1751
- endpoint: process.env.LANGFUSE_HOST ? `${process.env.LANGFUSE_HOST}/api/public/otel/v1/traces` : "https://cloud.langfuse.com/api/public/otel/v1/traces",
1752
- headers: (env) => {
1753
- const pub = env.LANGFUSE_PUBLIC_KEY ?? "";
1754
- const secret = env.LANGFUSE_SECRET_KEY ?? "";
1755
- return { Authorization: `Basic ${Buffer.from(`${pub}:${secret}`).toString("base64")}` };
1756
- }
1757
- },
1758
- braintrust: {
1759
- name: "braintrust",
1760
- endpoint: "https://api.braintrust.dev/otel/v1/traces",
1761
- headers: (env) => {
1762
- const headers = {
1763
- Authorization: `Bearer ${env.BRAINTRUST_API_KEY ?? ""}`
1764
- };
1765
- const parent = env.BRAINTRUST_PARENT ?? (env.BRAINTRUST_PROJECT_ID ? `project_id:${env.BRAINTRUST_PROJECT_ID}` : void 0) ?? (env.BRAINTRUST_PROJECT ? `project_name:${env.BRAINTRUST_PROJECT}` : void 0);
1766
- if (parent) {
1767
- headers["x-bt-parent"] = parent;
1768
- }
1769
- return headers;
1770
- }
1771
- },
1772
- confident: {
1773
- name: "confident",
1774
- endpoint: "https://otel.confident-ai.com/v1/traces",
1775
- headers: (env) => ({
1776
- "x-confident-api-key": env.CONFIDENT_API_KEY ?? ""
1777
- })
1778
- }
1779
- };
1780
2133
  var OtelTraceExporter = class {
1781
2134
  constructor(options) {
1782
2135
  this.options = options;
@@ -1799,9 +2152,11 @@ var OtelTraceExporter = class {
1799
2152
  const { NodeTracerProvider: Provider, SimpleSpanProcessor } = sdkTraceNode;
1800
2153
  const { resourceFromAttributes } = resourcesMod;
1801
2154
  const { ATTR_SERVICE_NAME } = semconvMod;
1802
- const resource = resourceFromAttributes({
1803
- [ATTR_SERVICE_NAME]: this.options.serviceName ?? "agentv"
1804
- });
2155
+ const resourceAttributes = {
2156
+ [ATTR_SERVICE_NAME]: this.options.serviceName ?? "agentv",
2157
+ ...this.options.resourceAttributes
2158
+ };
2159
+ const resource = resourceFromAttributes(resourceAttributes);
1805
2160
  const processors = [];
1806
2161
  if (this.options.endpoint) {
1807
2162
  const otlpHttp = await import("./esm-TTEMJEVV.js");
@@ -1813,9 +2168,11 @@ var OtelTraceExporter = class {
1813
2168
  processors.push(new SimpleSpanProcessor(exporter));
1814
2169
  }
1815
2170
  if (this.options.otlpFilePath) {
1816
- const { OtlpJsonFileExporter: OtlpJsonFileExporter2 } = await import("./otlp-json-file-exporter-RJFPCKVK-T6N4OGWG.js");
2171
+ const { OtlpJsonFileExporter: OtlpJsonFileExporter2 } = await import("./otlp-json-file-exporter-RY63S3IG-PZBQPVYY.js");
1817
2172
  processors.push(
1818
- new SimpleSpanProcessor(new OtlpJsonFileExporter2(this.options.otlpFilePath))
2173
+ new SimpleSpanProcessor(
2174
+ new OtlpJsonFileExporter2(this.options.otlpFilePath, resourceAttributes)
2175
+ )
1819
2176
  );
1820
2177
  }
1821
2178
  if (processors.length === 0) {
@@ -2776,204 +3133,6 @@ async function discoverClaudeSessions(opts) {
2776
3133
  sessions.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
2777
3134
  return sessions.slice(0, limit);
2778
3135
  }
2779
- function dropUndefined(value) {
2780
- return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
2781
- }
2782
- function toTranscriptTokenUsage(usage) {
2783
- if (!usage) {
2784
- return void 0;
2785
- }
2786
- return dropUndefined({
2787
- input: usage.input,
2788
- output: usage.output,
2789
- cached: usage.cached,
2790
- reasoning: usage.reasoning
2791
- });
2792
- }
2793
- function toTranscriptToolCall(toolCall) {
2794
- return dropUndefined({
2795
- tool: toolCall.tool,
2796
- input: toolCall.input,
2797
- output: toolCall.output,
2798
- id: toolCall.id,
2799
- start_time: toolCall.startTime,
2800
- end_time: toolCall.endTime,
2801
- duration_ms: toolCall.durationMs
2802
- });
2803
- }
2804
- function toTranscriptMessageFields(message) {
2805
- return dropUndefined({
2806
- role: message.role,
2807
- name: message.name,
2808
- content: message.content,
2809
- tool_calls: message.toolCalls?.map(toTranscriptToolCall),
2810
- start_time: message.startTime,
2811
- end_time: message.endTime,
2812
- duration_ms: message.durationMs,
2813
- metadata: message.metadata,
2814
- token_usage: toTranscriptTokenUsage(message.tokenUsage)
2815
- });
2816
- }
2817
- function toTranscriptJsonLines(entry, options) {
2818
- const source = {
2819
- provider: entry.source.provider,
2820
- session_id: entry.source.sessionId,
2821
- model: entry.source.model,
2822
- timestamp: entry.source.startedAt,
2823
- git_branch: entry.source.gitBranch,
2824
- cwd: entry.source.cwd ?? entry.source.projectPath,
2825
- version: entry.source.version
2826
- };
2827
- const transcriptTokenUsage = entry.tokenUsage ? {
2828
- input: entry.tokenUsage.input,
2829
- output: entry.tokenUsage.output,
2830
- cached: entry.tokenUsage.cached,
2831
- reasoning: entry.tokenUsage.reasoning
2832
- } : void 0;
2833
- const testId = options?.testId ?? entry.source.sessionId;
2834
- const target = options?.target ?? entry.source.provider;
2835
- return entry.messages.map((message, index) => ({
2836
- test_id: testId,
2837
- target,
2838
- message_index: index,
2839
- ...toTranscriptMessageFields(message),
2840
- transcript_token_usage: transcriptTokenUsage,
2841
- transcript_duration_ms: entry.durationMs,
2842
- transcript_cost_usd: entry.costUsd,
2843
- source
2844
- }));
2845
- }
2846
- function traceToTranscriptJsonLines(trace, options) {
2847
- const provider = (typeof trace.metadata?.provider === "string" ? trace.metadata.provider : void 0) ?? options?.target ?? "agentv";
2848
- const sessionId = (typeof trace.metadata?.provider_session_id === "string" ? trace.metadata.provider_session_id : void 0) ?? (typeof trace.metadata?.eval_case_id === "string" ? trace.metadata.eval_case_id : void 0) ?? options?.testId ?? "trace";
2849
- return toTranscriptJsonLines(
2850
- {
2851
- messages: [...trace.messages],
2852
- source: {
2853
- provider,
2854
- sessionId,
2855
- startedAt: trace.startTime
2856
- },
2857
- tokenUsage: trace.tokenUsage,
2858
- durationMs: trace.durationMs,
2859
- costUsd: trace.costUsd
2860
- },
2861
- options
2862
- );
2863
- }
2864
- function traceFromTranscriptJsonLines(lines) {
2865
- const [entry] = groupTranscriptJsonLines(lines);
2866
- if (!entry) {
2867
- return buildTraceFromMessages();
2868
- }
2869
- return buildTraceFromMessages({
2870
- output: entry.messages,
2871
- tokenUsage: entry.tokenUsage,
2872
- durationMs: entry.durationMs,
2873
- costUsd: entry.costUsd ?? void 0,
2874
- startTime: entry.source.startedAt,
2875
- provider: entry.source.provider,
2876
- target: entry.target,
2877
- testId: entry.testId,
2878
- conversationId: entry.source.sessionId
2879
- });
2880
- }
2881
- function fromTranscriptTokenUsage(usage) {
2882
- if (!usage) {
2883
- return void 0;
2884
- }
2885
- return {
2886
- input: usage.input,
2887
- output: usage.output,
2888
- cached: usage.cached,
2889
- reasoning: usage.reasoning
2890
- };
2891
- }
2892
- function readOptionalString(record, key) {
2893
- const value = record[key];
2894
- return typeof value === "string" ? value : void 0;
2895
- }
2896
- function readOptionalNumber(record, key) {
2897
- const value = record[key];
2898
- return typeof value === "number" && Number.isFinite(value) ? value : void 0;
2899
- }
2900
- function fromTranscriptToolCall(wire) {
2901
- const tool = readOptionalString(wire, "tool");
2902
- if (!tool) {
2903
- return void 0;
2904
- }
2905
- return {
2906
- tool,
2907
- input: wire.input,
2908
- output: wire.output,
2909
- id: readOptionalString(wire, "id"),
2910
- startTime: readOptionalString(wire, "start_time"),
2911
- endTime: readOptionalString(wire, "end_time"),
2912
- durationMs: readOptionalNumber(wire, "duration_ms")
2913
- };
2914
- }
2915
- function buildReplayMessage(line) {
2916
- return {
2917
- role: line.role,
2918
- name: line.name,
2919
- content: line.content,
2920
- toolCalls: line.tool_calls?.map(fromTranscriptToolCall).filter((toolCall) => toolCall !== void 0),
2921
- startTime: line.start_time,
2922
- endTime: line.end_time,
2923
- durationMs: line.duration_ms,
2924
- metadata: line.metadata,
2925
- tokenUsage: fromTranscriptTokenUsage(line.token_usage)
2926
- };
2927
- }
2928
- function groupTranscriptJsonLines(lines) {
2929
- const grouped = /* @__PURE__ */ new Map();
2930
- for (const line of lines) {
2931
- const existing = grouped.get(line.test_id);
2932
- const source = {
2933
- provider: line.source.provider,
2934
- sessionId: line.source.session_id,
2935
- startedAt: line.source.timestamp,
2936
- model: line.source.model,
2937
- gitBranch: line.source.git_branch,
2938
- cwd: line.source.cwd,
2939
- version: line.source.version
2940
- };
2941
- const transcriptTokenUsage = line.transcript_token_usage ? {
2942
- input: line.transcript_token_usage.input,
2943
- output: line.transcript_token_usage.output,
2944
- cached: line.transcript_token_usage.cached,
2945
- reasoning: line.transcript_token_usage.reasoning
2946
- } : void 0;
2947
- if (existing) {
2948
- existing.messages.push({ index: line.message_index, message: buildReplayMessage(line) });
2949
- continue;
2950
- }
2951
- grouped.set(line.test_id, {
2952
- target: line.target,
2953
- tokenUsage: transcriptTokenUsage,
2954
- durationMs: line.transcript_duration_ms,
2955
- costUsd: line.transcript_cost_usd,
2956
- source,
2957
- messages: [{ index: line.message_index, message: buildReplayMessage(line) }]
2958
- });
2959
- }
2960
- return [...grouped.entries()].map(([testId, entry]) => ({
2961
- testId,
2962
- target: entry.target,
2963
- tokenUsage: entry.tokenUsage,
2964
- durationMs: entry.durationMs,
2965
- costUsd: entry.costUsd,
2966
- source: entry.source,
2967
- messages: entry.messages.sort((first, second) => first.index - second.index).map((item) => item.message)
2968
- }));
2969
- }
2970
- async function readTranscriptJsonl(filePath) {
2971
- const text = await readFile2(filePath, "utf8");
2972
- return text.split("\n").filter((line) => line.trim().length > 0).map((line) => JSON.parse(line));
2973
- }
2974
- async function readTranscriptFile(filePath) {
2975
- return readFile2(filePath, "utf8");
2976
- }
2977
3136
  var TranscriptProvider = class _TranscriptProvider {
2978
3137
  id;
2979
3138
  kind = "transcript";
@@ -3061,7 +3220,6 @@ export {
3061
3220
  trimBaselineResult,
3062
3221
  DEFAULT_CATEGORY,
3063
3222
  deriveCategory,
3064
- OTEL_BACKEND_PRESETS,
3065
3223
  OtelTraceExporter,
3066
3224
  OtelStreamingObserver,
3067
3225
  RunBudgetTracker,
@@ -3071,13 +3229,7 @@ export {
3071
3229
  parseCodexSession,
3072
3230
  discoverCodexSessions,
3073
3231
  discoverClaudeSessions,
3074
- toTranscriptJsonLines,
3075
- traceToTranscriptJsonLines,
3076
- traceFromTranscriptJsonLines,
3077
- groupTranscriptJsonLines,
3078
- readTranscriptJsonl,
3079
- readTranscriptFile,
3080
3232
  TranscriptProvider,
3081
3233
  createAgentKernel
3082
3234
  };
3083
- //# sourceMappingURL=chunk-NLTIK3LV.js.map
3235
+ //# sourceMappingURL=chunk-I3SC4FOT.js.map