stageflow 0.40.0 → 0.42.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 (326) hide show
  1. package/README.md +3 -1
  2. package/dist/agents/authorChat.d.ts +4 -0
  3. package/dist/agents/authorChat.js +113 -12
  4. package/dist/agents/authorSessionStore.d.ts +35 -0
  5. package/dist/agents/authorSessionStore.js +152 -0
  6. package/dist/agents/chatTurn.d.ts +2 -0
  7. package/dist/agents/chatTurn.js +17 -1
  8. package/dist/agents/errors.d.ts +1 -1
  9. package/dist/agents/errors.js +1 -0
  10. package/dist/agents/evalGate.d.ts +61 -0
  11. package/dist/agents/evalGate.js +103 -0
  12. package/dist/agents/evalSession.d.ts +26 -0
  13. package/dist/agents/evalSession.js +29 -0
  14. package/dist/agents/hostContext.d.ts +3 -0
  15. package/dist/agents/hostContext.js +4 -1
  16. package/dist/agents/runtime/piRuntime.d.ts +1 -1
  17. package/dist/agents/runtime/piRuntime.js +4 -4
  18. package/dist/agents/runtime/types.d.ts +3 -1
  19. package/dist/agents/service.d.ts +27 -2
  20. package/dist/agents/service.js +88 -13
  21. package/dist/agents/sessions.d.ts +8 -0
  22. package/dist/agents/sessions.js +50 -10
  23. package/dist/agents/store.d.ts +14 -3
  24. package/dist/agents/store.js +45 -7
  25. package/dist/agents/toolRisk.d.ts +21 -0
  26. package/dist/agents/toolRisk.js +50 -0
  27. package/dist/cli/doctorCommand.js +26 -0
  28. package/dist/cli/evalCommand.d.ts +20 -0
  29. package/dist/cli/evalCommand.js +720 -0
  30. package/dist/cli/exportAllCommand.js +1 -1
  31. package/dist/cli/hostClient.d.ts +60 -0
  32. package/dist/cli/hostClient.js +71 -0
  33. package/dist/cli/validateOutput.d.ts +2 -2
  34. package/dist/cli/validateOutput.js +2 -2
  35. package/dist/cli.js +10 -0
  36. package/dist/config/atomicWrite.d.ts +15 -0
  37. package/dist/config/atomicWrite.js +43 -0
  38. package/dist/config/browseCatalog.d.ts +2 -0
  39. package/dist/config/browseCatalog.js +4 -2
  40. package/dist/config/csv.d.ts +9 -0
  41. package/dist/config/csv.js +81 -0
  42. package/dist/config/datasetAgentCheck.d.ts +18 -0
  43. package/dist/config/datasetAgentCheck.js +109 -0
  44. package/dist/config/datasetCandidates.d.ts +22 -0
  45. package/dist/config/datasetCandidates.js +86 -0
  46. package/dist/config/datasetFromSessions.d.ts +34 -0
  47. package/dist/config/datasetFromSessions.js +105 -0
  48. package/dist/config/datasetHash.d.ts +1 -0
  49. package/dist/config/datasetHash.js +4 -0
  50. package/dist/config/datasetImport.d.ts +21 -0
  51. package/dist/config/datasetImport.js +120 -0
  52. package/dist/config/datasetStageCheck.d.ts +9 -0
  53. package/dist/config/datasetStageCheck.js +17 -0
  54. package/dist/config/datasetTypes.d.ts +27 -0
  55. package/dist/config/datasetTypes.js +1 -0
  56. package/dist/config/ensureCatalogScanRoot.d.ts +2 -1
  57. package/dist/config/ensureCatalogScanRoot.js +2 -6
  58. package/dist/config/evalCatalog.d.ts +22 -0
  59. package/dist/config/evalCatalog.js +67 -0
  60. package/dist/config/evalWriteRoots.d.ts +17 -0
  61. package/dist/config/evalWriteRoots.js +109 -0
  62. package/dist/config/hostConfig.js +1 -0
  63. package/dist/config/installEvalSample.d.ts +34 -0
  64. package/dist/config/installEvalSample.js +182 -0
  65. package/dist/config/loadDataset.d.ts +16 -0
  66. package/dist/config/loadDataset.js +318 -0
  67. package/dist/config/loadEval.d.ts +32 -0
  68. package/dist/config/loadEval.js +1085 -0
  69. package/dist/config/loadOutcome.d.ts +5 -0
  70. package/dist/config/loadStageflowManifest.js +64 -0
  71. package/dist/config/resolveAgentTarget.d.ts +32 -0
  72. package/dist/config/resolveAgentTarget.js +118 -0
  73. package/dist/config/resolveEvalTarget.d.ts +21 -0
  74. package/dist/config/resolveEvalTarget.js +89 -0
  75. package/dist/config/scanCatalogPaths.d.ts +1 -1
  76. package/dist/config/scanCatalogPaths.js +17 -5
  77. package/dist/config/validateCatalog.d.ts +1 -1
  78. package/dist/config/validateCatalog.js +9 -1
  79. package/dist/config/validateEval.d.ts +36 -0
  80. package/dist/config/validateEval.js +405 -0
  81. package/dist/config/writeDataset.d.ts +54 -0
  82. package/dist/config/writeDataset.js +186 -0
  83. package/dist/config/writeEval.d.ts +25 -0
  84. package/dist/config/writeEval.js +398 -0
  85. package/dist/diagnostics/checks.js +1 -1
  86. package/dist/evals/agentDiff.d.ts +13 -0
  87. package/dist/evals/agentDiff.js +11 -0
  88. package/dist/evals/canonical.d.ts +3 -0
  89. package/dist/evals/canonical.js +24 -0
  90. package/dist/evals/caseTask.d.ts +3 -0
  91. package/dist/evals/caseTask.js +9 -0
  92. package/dist/evals/compare.d.ts +94 -0
  93. package/dist/evals/compare.js +201 -0
  94. package/dist/evals/constants.d.ts +60 -0
  95. package/dist/evals/constants.js +95 -0
  96. package/dist/evals/engine/FakeEngine.d.ts +33 -0
  97. package/dist/evals/engine/FakeEngine.js +176 -0
  98. package/dist/evals/engine/index.d.ts +9 -0
  99. package/dist/evals/engine/index.js +19 -0
  100. package/dist/evals/engine/promptfoo/PromptfooEngine.d.ts +52 -0
  101. package/dist/evals/engine/promptfoo/PromptfooEngine.js +311 -0
  102. package/dist/evals/engine/promptfoo/errors.d.ts +5 -0
  103. package/dist/evals/engine/promptfoo/errors.js +8 -0
  104. package/dist/evals/engine/promptfoo/generate.d.ts +30 -0
  105. package/dist/evals/engine/promptfoo/generate.js +342 -0
  106. package/dist/evals/engine/promptfoo/guard.d.ts +10 -0
  107. package/dist/evals/engine/promptfoo/guard.js +116 -0
  108. package/dist/evals/engine/promptfoo/install.d.ts +17 -0
  109. package/dist/evals/engine/promptfoo/install.js +73 -0
  110. package/dist/evals/engine/promptfoo/judgeProviders.d.ts +2 -0
  111. package/dist/evals/engine/promptfoo/judgeProviders.js +9 -0
  112. package/dist/evals/engine/promptfoo/judgeServices.d.ts +6 -0
  113. package/dist/evals/engine/promptfoo/judgeServices.js +41 -0
  114. package/dist/evals/engine/promptfoo/locate.d.ts +21 -0
  115. package/dist/evals/engine/promptfoo/locate.js +68 -0
  116. package/dist/evals/engine/promptfoo/nodeVersion.d.ts +7 -0
  117. package/dist/evals/engine/promptfoo/nodeVersion.js +14 -0
  118. package/dist/evals/engine/promptfoo/normalize.d.ts +66 -0
  119. package/dist/evals/engine/promptfoo/normalize.js +261 -0
  120. package/dist/evals/engine/promptfoo/pinned.d.ts +5 -0
  121. package/dist/evals/engine/promptfoo/pinned.js +5 -0
  122. package/dist/evals/engine/promptfoo/rubricPrompt.d.ts +14 -0
  123. package/dist/evals/engine/promptfoo/rubricPrompt.js +103 -0
  124. package/dist/evals/engine/promptfoo/runTokens.d.ts +18 -0
  125. package/dist/evals/engine/promptfoo/runTokens.js +31 -0
  126. package/dist/evals/engine/promptfoo/status.d.ts +13 -0
  127. package/dist/evals/engine/promptfoo/status.js +16 -0
  128. package/dist/evals/estimate.d.ts +56 -0
  129. package/dist/evals/estimate.js +66 -0
  130. package/dist/evals/graders/codeGrader.d.ts +6 -0
  131. package/dist/evals/graders/codeGrader.js +85 -0
  132. package/dist/evals/graders/conversationCheckGrader.d.ts +6 -0
  133. package/dist/evals/graders/conversationCheckGrader.js +62 -0
  134. package/dist/evals/graders/fieldGrader.d.ts +11 -0
  135. package/dist/evals/graders/fieldGrader.js +117 -0
  136. package/dist/evals/graders/goal.d.ts +3 -0
  137. package/dist/evals/graders/goal.js +14 -0
  138. package/dist/evals/graders/index.d.ts +18 -0
  139. package/dist/evals/graders/index.js +34 -0
  140. package/dist/evals/graders/notApplicable.d.ts +5 -0
  141. package/dist/evals/graders/notApplicable.js +9 -0
  142. package/dist/evals/graders/path.d.ts +26 -0
  143. package/dist/evals/graders/path.js +75 -0
  144. package/dist/evals/graders/referenceGrader.d.ts +5 -0
  145. package/dist/evals/graders/referenceGrader.js +59 -0
  146. package/dist/evals/graders/schemaGrader.d.ts +5 -0
  147. package/dist/evals/graders/schemaGrader.js +29 -0
  148. package/dist/evals/graders/subject.d.ts +6 -0
  149. package/dist/evals/graders/subject.js +33 -0
  150. package/dist/evals/graders/toolCallsGrader.d.ts +6 -0
  151. package/dist/evals/graders/toolCallsGrader.js +100 -0
  152. package/dist/evals/graders/transcript.d.ts +12 -0
  153. package/dist/evals/graders/transcript.js +57 -0
  154. package/dist/evals/graders/types.d.ts +20 -0
  155. package/dist/evals/graders/types.js +1 -0
  156. package/dist/evals/judgeProviders.d.ts +16 -0
  157. package/dist/evals/judgeProviders.js +27 -0
  158. package/dist/evals/lineDiff.d.ts +5 -0
  159. package/dist/evals/lineDiff.js +34 -0
  160. package/dist/evals/listStatus.d.ts +31 -0
  161. package/dist/evals/listStatus.js +48 -0
  162. package/dist/evals/manager.d.ts +174 -0
  163. package/dist/evals/manager.js +787 -0
  164. package/dist/evals/plan.d.ts +58 -0
  165. package/dist/evals/plan.js +602 -0
  166. package/dist/evals/providerKeys.d.ts +1 -0
  167. package/dist/evals/providerKeys.js +21 -0
  168. package/dist/evals/read/agentTargets.d.ts +37 -0
  169. package/dist/evals/read/agentTargets.js +101 -0
  170. package/dist/evals/read/api.d.ts +196 -0
  171. package/dist/evals/read/api.js +358 -0
  172. package/dist/evals/read/compare.d.ts +4 -0
  173. package/dist/evals/read/compare.js +39 -0
  174. package/dist/evals/read/datasets.d.ts +105 -0
  175. package/dist/evals/read/datasets.js +318 -0
  176. package/dist/evals/read/list.d.ts +66 -0
  177. package/dist/evals/read/list.js +173 -0
  178. package/dist/evals/read/results.d.ts +46 -0
  179. package/dist/evals/read/results.js +107 -0
  180. package/dist/evals/read/runs.d.ts +149 -0
  181. package/dist/evals/read/runs.js +289 -0
  182. package/dist/evals/read/suggestions.d.ts +43 -0
  183. package/dist/evals/read/suggestions.js +73 -0
  184. package/dist/evals/runner/FakeAgentBackend.d.ts +40 -0
  185. package/dist/evals/runner/FakeAgentBackend.js +82 -0
  186. package/dist/evals/runner/agentCaseRunner.d.ts +24 -0
  187. package/dist/evals/runner/agentCaseRunner.js +388 -0
  188. package/dist/evals/runner/caseRunner.d.ts +7 -0
  189. package/dist/evals/runner/caseRunner.js +17 -0
  190. package/dist/evals/runner/conversationSubject.d.ts +18 -0
  191. package/dist/evals/runner/conversationSubject.js +123 -0
  192. package/dist/evals/runner/simulatorModel.d.ts +51 -0
  193. package/dist/evals/runner/simulatorModel.js +58 -0
  194. package/dist/evals/runner/simulatorPrompt.d.ts +29 -0
  195. package/dist/evals/runner/simulatorPrompt.js +59 -0
  196. package/dist/evals/runner/stageCaseRunner.d.ts +30 -0
  197. package/dist/evals/runner/stageCaseRunner.js +279 -0
  198. package/dist/evals/runner/userDriver.d.ts +71 -0
  199. package/dist/evals/runner/userDriver.js +104 -0
  200. package/dist/evals/scoring.d.ts +34 -0
  201. package/dist/evals/scoring.js +67 -0
  202. package/dist/evals/snapshot.d.ts +183 -0
  203. package/dist/evals/snapshot.js +65 -0
  204. package/dist/evals/stageDiff.d.ts +8 -0
  205. package/dist/evals/stageDiff.js +48 -0
  206. package/dist/evals/stats.d.ts +95 -0
  207. package/dist/evals/stats.js +220 -0
  208. package/dist/evals/statsAdapter.d.ts +4 -0
  209. package/dist/evals/statsAdapter.js +30 -0
  210. package/dist/evals/statsTypes.d.ts +51 -0
  211. package/dist/evals/statsTypes.js +18 -0
  212. package/dist/evals/storeError.d.ts +5 -0
  213. package/dist/evals/storeError.js +8 -0
  214. package/dist/evals/storeTypes.d.ts +207 -0
  215. package/dist/evals/storeTypes.js +1 -0
  216. package/dist/evals/tTable.d.ts +3 -0
  217. package/dist/evals/tTable.js +15 -0
  218. package/dist/evals/triage/constants.d.ts +26 -0
  219. package/dist/evals/triage/constants.js +33 -0
  220. package/dist/evals/triage/credentials.d.ts +13 -0
  221. package/dist/evals/triage/credentials.js +20 -0
  222. package/dist/evals/triage/jevClassifier.d.ts +14 -0
  223. package/dist/evals/triage/jevClassifier.js +124 -0
  224. package/dist/evals/triage/judgeClassifier.d.ts +10 -0
  225. package/dist/evals/triage/judgeClassifier.js +76 -0
  226. package/dist/evals/triage/prompts.d.ts +16 -0
  227. package/dist/evals/triage/prompts.js +64 -0
  228. package/dist/evals/triage/service.d.ts +79 -0
  229. package/dist/evals/triage/service.js +217 -0
  230. package/dist/evals/triage/stateBuilder.d.ts +19 -0
  231. package/dist/evals/triage/stateBuilder.js +125 -0
  232. package/dist/evals/triage/types.d.ts +60 -0
  233. package/dist/evals/triage/types.js +8 -0
  234. package/dist/evals/types.d.ts +347 -0
  235. package/dist/evals/types.js +1 -0
  236. package/dist/mcp/deps.d.ts +7 -0
  237. package/dist/mcp/evalTools.d.ts +3 -0
  238. package/dist/mcp/evalTools.js +280 -0
  239. package/dist/mcp/server.js +1 -0
  240. package/dist/mcp/tools.js +2 -0
  241. package/dist/models/response.d.ts +7 -2
  242. package/dist/models/response.js +6 -1
  243. package/dist/operatorAgent/agentAuthorContext.d.ts +1 -1
  244. package/dist/operatorAgent/agentAuthorContext.js +1 -1
  245. package/dist/operatorAgent/piModel.js +17 -4
  246. package/dist/operatorAgent/profiles/agentAuthor.d.ts +3 -1
  247. package/dist/operatorAgent/profiles/agentAuthor.js +145 -20
  248. package/dist/operatorAgent/profiles/agentAuthorPlaybook.d.ts +1 -1
  249. package/dist/operatorAgent/profiles/agentAuthorPlaybook.js +48 -15
  250. package/dist/operatorAgent/profiles/authorReadable.d.ts +2 -0
  251. package/dist/operatorAgent/profiles/authorReadable.js +26 -0
  252. package/dist/package-meta.d.ts +1 -1
  253. package/dist/package-meta.js +1 -1
  254. package/dist/runstore/port.d.ts +43 -0
  255. package/dist/runstore/sqlite/SqliteRunStore.d.ts +28 -0
  256. package/dist/runstore/sqlite/SqliteRunStore.js +99 -5
  257. package/dist/runstore/sqlite/evalStore.d.ts +20 -0
  258. package/dist/runstore/sqlite/evalStore.js +541 -0
  259. package/dist/runstore/sqlite/migrations/015-evals.d.ts +12 -0
  260. package/dist/runstore/sqlite/migrations/015-evals.js +157 -0
  261. package/dist/runstore/sqlite/migrations/016-eval-subject-ref.d.ts +12 -0
  262. package/dist/runstore/sqlite/migrations/016-eval-subject-ref.js +20 -0
  263. package/dist/runstore/sqlite/migrations/017-eval-result-suggestions.d.ts +11 -0
  264. package/dist/runstore/sqlite/migrations/017-eval-result-suggestions.js +36 -0
  265. package/dist/runstore/sqlite/migrations/index.js +6 -0
  266. package/dist/runstore/sqlite/suggestionStore.d.ts +14 -0
  267. package/dist/runstore/sqlite/suggestionStore.js +132 -0
  268. package/dist/runtime/pipelineRunner.d.ts +2 -1
  269. package/dist/runtime/pipelineRunner.js +2 -0
  270. package/dist/runtime/runManager.d.ts +2 -1
  271. package/dist/runtime/runManager.js +8 -6
  272. package/dist/runtime/runRetentionSweep.js +2 -2
  273. package/dist/server/agentAuthorRoutes.js +50 -1
  274. package/dist/server/bootstrap.d.ts +8 -0
  275. package/dist/server/bootstrap.js +56 -0
  276. package/dist/server/controlToken.js +3 -0
  277. package/dist/server/evalInternalRoutes.d.ts +14 -0
  278. package/dist/server/evalInternalRoutes.js +87 -0
  279. package/dist/server/evalRouteContext.d.ts +46 -0
  280. package/dist/server/evalRouteContext.js +43 -0
  281. package/dist/server/evalRoutes.d.ts +5 -0
  282. package/dist/server/evalRoutes.js +1427 -0
  283. package/dist/server/evalTriageRoutes.d.ts +2 -0
  284. package/dist/server/evalTriageRoutes.js +95 -0
  285. package/dist/server/http.d.ts +5 -0
  286. package/dist/server/http.js +32 -2
  287. package/dist/server/mcpHost.js +3 -0
  288. package/dist/toolkit/meta.d.ts +1 -1
  289. package/dist/toolkit/meta.js +15 -0
  290. package/dist/toolkit/profiles.js +1 -1
  291. package/dist/types/stageflowManifest.d.ts +6 -0
  292. package/dist/ui/assets/AssistantRuntimeProvider-qIX1xNoH.js +9 -0
  293. package/dist/ui/assets/{MarkdownText-D7tYaIFO.js → MarkdownText-BTrOqu5J.js} +1 -1
  294. package/dist/ui/assets/chat-Df_wCpak.js +5 -0
  295. package/dist/ui/assets/chat-P7KQznHm.css +2 -0
  296. package/dist/ui/assets/{main-BFo79krC.css → main-CHlmTNJe.css} +1 -1
  297. package/dist/ui/assets/main-CvVfxmMd.js +271 -0
  298. package/dist/ui/chat.html +3 -3
  299. package/dist/ui/index.html +3 -3
  300. package/examples/README.md +2 -0
  301. package/examples/document-policy-review/README.md +35 -0
  302. package/examples/document-policy-review/agents/compliance.agent.yaml +42 -0
  303. package/examples/document-policy-review/documents/acme-vendor-sow.md +15 -0
  304. package/examples/document-policy-review/documents/support-runbook.md +13 -0
  305. package/examples/document-policy-review/policies/acceptable-use.md +13 -0
  306. package/examples/document-policy-review/policies/data-handling.md +17 -0
  307. package/examples/document-policy-review/policies/vendor-security.md +17 -0
  308. package/examples/evals/README.md +123 -0
  309. package/examples/evals/critique-sample.dataset.jsonl +8 -0
  310. package/examples/evals/critique-sample.eval.yaml +34 -0
  311. package/examples/evals/critique-sample.task.yaml +4 -0
  312. package/examples/evals/eval-sample.pipeline.yaml +35 -0
  313. package/examples/evals/support-sample.agent.yaml +20 -0
  314. package/examples/evals/support-sample.dataset.jsonl +6 -0
  315. package/examples/evals/support-sample.eval.yaml +38 -0
  316. package/examples/stageflow.yaml +6 -0
  317. package/package.json +1 -1
  318. package/skills/stageflow/SKILL.md +5 -2
  319. package/skills/stageflow/references/control-surface.md +4 -2
  320. package/skills/stageflow/references/evals.md +68 -0
  321. package/skills/stageflow-author/SKILL.md +4 -0
  322. package/skills/stageflow-run/SKILL.md +4 -0
  323. package/dist/ui/assets/AssistantRuntimeProvider-CsiPFhCT.js +0 -9
  324. package/dist/ui/assets/chat-BbLukq9W.js +0 -5
  325. package/dist/ui/assets/chat-zIZU_bP1.css +0 -2
  326. package/dist/ui/assets/main-CijYMeM0.js +0 -259
@@ -0,0 +1,37 @@
1
+ import { type ToolClass } from "../../agents/toolRisk.js";
2
+ import { type AgentCatalogPort, type AgentToolRisk } from "../../config/resolveAgentTarget.js";
3
+ export type AgentToolWire = {
4
+ id: string;
5
+ risk: AgentToolRisk;
6
+ class: ToolClass;
7
+ };
8
+ export type AgentWire = {
9
+ id: string;
10
+ name: string;
11
+ model: string | null;
12
+ revision: number;
13
+ enabled: boolean;
14
+ archived: boolean;
15
+ tools: AgentToolWire[];
16
+ ask_first: string[];
17
+ issues: string[];
18
+ };
19
+ export type AgentTargetItem = {
20
+ id: string;
21
+ name: string;
22
+ model: string | null;
23
+ revision: number;
24
+ enabled: boolean;
25
+ tools: AgentToolWire[];
26
+ ask_first: string[];
27
+ evals_count: number;
28
+ issues: string[];
29
+ unavailable_reason: "archived" | "invalid" | null;
30
+ };
31
+ /** The live agent behind an eval's target, for the editor and warnings; null when it cannot be read. */
32
+ export declare function liveAgentOf(agents: AgentCatalogPort | undefined, projectRoot: string, agentId: string): Promise<AgentWire | null>;
33
+ /**
34
+ * Agents an eval can target. `AgentService.list` hides archived agents, so an archived agent shows up only while an
35
+ * eval still names it (greyed in the picker). Never reads eval sessions.
36
+ */
37
+ export declare function listAgentTargets(agents: AgentCatalogPort, projectRoot: string): Promise<AgentTargetItem[]>;
@@ -0,0 +1,101 @@
1
+ import { classifyTool } from "../../agents/toolRisk.js";
2
+ import { isAgentError } from "../../agents/errors.js";
3
+ import { listEvalFiles } from "../../config/evalCatalog.js";
4
+ import { manifestForWrite } from "../../config/evalWriteRoots.js";
5
+ import { loadEvalOutcome } from "../../config/loadEval.js";
6
+ import { agentToolGrants } from "../../config/resolveAgentTarget.js";
7
+ function toolsOf(detail) {
8
+ const { definition } = detail;
9
+ if (definition === null)
10
+ return [];
11
+ return agentToolGrants(definition).map((tool) => ({
12
+ id: tool.id,
13
+ risk: tool.risk,
14
+ class: classifyTool(definition, tool.id).class,
15
+ }));
16
+ }
17
+ function issueTexts(detail) {
18
+ return detail.agent.issues.map((issue) => (issue.path ? `${issue.path}: ${issue.message}` : issue.message));
19
+ }
20
+ function toWire(detail, archived) {
21
+ const { agent, definition } = detail;
22
+ return {
23
+ id: agent.id,
24
+ name: agent.name,
25
+ model: definition?.model.id ?? agent.model?.id ?? null,
26
+ revision: agent.revision,
27
+ enabled: agent.enabled,
28
+ archived,
29
+ tools: toolsOf(detail),
30
+ ask_first: definition?.approvals.ask_first ?? agent.askFirst,
31
+ issues: issueTexts(detail),
32
+ };
33
+ }
34
+ /** The live agent behind an eval's target, for the editor and warnings; null when it cannot be read. */
35
+ export async function liveAgentOf(agents, projectRoot, agentId) {
36
+ if (!agents)
37
+ return null;
38
+ try {
39
+ const detail = await agents.get(projectRoot, agentId);
40
+ const listed = await agents.list(projectRoot);
41
+ return toWire(detail, !listed.some((agent) => agent.id === agentId));
42
+ }
43
+ catch (err) {
44
+ if (isAgentError(err))
45
+ return null;
46
+ throw err;
47
+ }
48
+ }
49
+ async function agentEvalCounts(projectRoot) {
50
+ const manifest = await manifestForWrite(projectRoot);
51
+ const counts = new Map();
52
+ for (const ref of await listEvalFiles(manifest)) {
53
+ const outcome = await loadEvalOutcome(ref.absPath, projectRoot);
54
+ if (!outcome.ok || outcome.value.file.target.kind !== "agent")
55
+ continue;
56
+ const id = outcome.value.file.target.agent;
57
+ counts.set(id, (counts.get(id) ?? 0) + 1);
58
+ }
59
+ return { counts, referenced: [...counts.keys()] };
60
+ }
61
+ /**
62
+ * Agents an eval can target. `AgentService.list` hides archived agents, so an archived agent shows up only while an
63
+ * eval still names it (greyed in the picker). Never reads eval sessions.
64
+ */
65
+ export async function listAgentTargets(agents, projectRoot) {
66
+ const [listed, { counts, referenced }] = await Promise.all([agents.list(projectRoot), agentEvalCounts(projectRoot)]);
67
+ const items = [];
68
+ const seen = new Set();
69
+ const add = async (id, archived) => {
70
+ let detail;
71
+ try {
72
+ detail = await agents.get(projectRoot, id);
73
+ }
74
+ catch (err) {
75
+ if (isAgentError(err))
76
+ return;
77
+ throw err;
78
+ }
79
+ seen.add(id);
80
+ const wire = toWire(detail, archived);
81
+ items.push({
82
+ id: wire.id,
83
+ name: wire.name,
84
+ model: wire.model,
85
+ revision: wire.revision,
86
+ enabled: wire.enabled,
87
+ tools: wire.tools,
88
+ ask_first: wire.ask_first,
89
+ evals_count: counts.get(id) ?? 0,
90
+ issues: wire.issues,
91
+ unavailable_reason: archived ? "archived" : detail.definition === null || wire.issues.length > 0 ? "invalid" : null,
92
+ });
93
+ };
94
+ for (const agent of listed)
95
+ await add(agent.id, false);
96
+ for (const id of referenced) {
97
+ if (!seen.has(id))
98
+ await add(id, true);
99
+ }
100
+ return items.sort((a, b) => a.id.localeCompare(b.id));
101
+ }
@@ -0,0 +1,196 @@
1
+ import { type CatalogRoot, type ResolveCatalogRootsOptions } from "../../config/resolveCatalogRoots.js";
2
+ import type { AgentCatalogPort } from "../../config/resolveAgentTarget.js";
3
+ import { type LoadedEval } from "../../config/loadEval.js";
4
+ import type { ValidationFinding } from "../../config/validateCatalog.js";
5
+ import type { RunStore } from "../../runstore/port.js";
6
+ import type { EngineStatus } from "../engine/promptfoo/status.js";
7
+ import { type AgentWire } from "./agentTargets.js";
8
+ import { type EvalListItem } from "./list.js";
9
+ import type { EvalRunRow } from "../storeTypes.js";
10
+ import { type BannerEvalInput } from "../listStatus.js";
11
+ import { runHeadline, type ActiveRun } from "./runs.js";
12
+ export declare const DEFAULT_LIMIT = 50;
13
+ export declare const MAX_LIMIT = 200;
14
+ export declare const ENGINE_NAME = "promptfoo";
15
+ export declare class EvalApiError extends Error {
16
+ readonly status: number;
17
+ readonly code: string;
18
+ readonly extra: Record<string, unknown>;
19
+ constructor(status: number, code: string, message: string, extra?: Record<string, unknown>);
20
+ }
21
+ export type IsKnownModel = (id: string) => Promise<boolean>;
22
+ export declare function rel(projectRoot: string, abs: string): string;
23
+ export declare function badRequest(message: string, code?: string): EvalApiError;
24
+ export declare function engineCheck(status: EngineStatus): void;
25
+ export declare function findEvalOr404(projectRoot: string, id: string): Promise<import("../../config/evalCatalog.js").CatalogFileRef>;
26
+ export declare function runByIdOr404(store: RunStore, id: string): Promise<EvalRunRow>;
27
+ export declare function encodeOffset(offset: number): string;
28
+ export declare function decodeOffset(cursor: string | null | undefined): number;
29
+ export type EvalDetailWire = {
30
+ id: string;
31
+ path: string;
32
+ file_hash: string;
33
+ definition: unknown;
34
+ yaml: string;
35
+ findings: ValidationFinding[];
36
+ runs_count: number;
37
+ active_run: ActiveRun | null;
38
+ last_run: ReturnType<typeof runHeadline> | null;
39
+ target_model: string | null;
40
+ judge_default: string | null;
41
+ agent?: AgentWire | null;
42
+ };
43
+ export declare function buildEvalDetail(input: {
44
+ store: RunStore;
45
+ projectRoot: string;
46
+ absPath: string;
47
+ isKnownModel?: IsKnownModel;
48
+ agents?: AgentCatalogPort;
49
+ }): Promise<EvalDetailWire>;
50
+ export declare function planFindings(input: {
51
+ projectRoot: string;
52
+ evalPath: string;
53
+ evalText: string;
54
+ datasetCases?: readonly unknown[];
55
+ isKnownModel?: IsKnownModel;
56
+ agents?: AgentCatalogPort;
57
+ }): Promise<{
58
+ ok: boolean;
59
+ findings: ValidationFinding[];
60
+ }>;
61
+ export declare function validateEvalText(input: {
62
+ projectRoot: string;
63
+ evalAbs: string;
64
+ derived: string;
65
+ datasetCases?: readonly unknown[];
66
+ isKnownModel?: IsKnownModel;
67
+ agents?: AgentCatalogPort;
68
+ }): Promise<{
69
+ findings: ValidationFinding[];
70
+ parsed: import("../../config/loadOutcome.js").LoadOutcome<LoadedEval>;
71
+ }>;
72
+ export declare function listEvalRunsPage(store: RunStore, input: {
73
+ projectRoot?: string;
74
+ wireRootOf?: (projectRoot: string) => string;
75
+ evalId?: string;
76
+ status?: string;
77
+ stageRunId?: string;
78
+ subjectRef?: string;
79
+ limit: number;
80
+ cursor?: string;
81
+ }): Promise<{
82
+ items: {
83
+ eval_run_id: string;
84
+ eval_id: string;
85
+ run_number: number;
86
+ status: string;
87
+ passed: boolean | null;
88
+ pass_rate: number | null;
89
+ pass_rate_ci: {
90
+ low: number;
91
+ high: number;
92
+ } | null;
93
+ results_executed: number;
94
+ cost_total: number;
95
+ finished_at: string | null;
96
+ fingerprint: string;
97
+ label: string | null;
98
+ created_at: string;
99
+ started_at: string | null;
100
+ results_total: number;
101
+ project_root: string;
102
+ }[];
103
+ next_cursor: string | null;
104
+ }>;
105
+ export declare function buildResultsPage(store: RunStore, id: string, input: {
106
+ status?: string;
107
+ grader?: string;
108
+ q?: string;
109
+ tag?: string;
110
+ limit: number;
111
+ cursor?: string | null;
112
+ }): Promise<{
113
+ items: import("./results.js").ResultRow[];
114
+ next_cursor: string | null;
115
+ counts: import("./results.js").ResultRowCounts;
116
+ }>;
117
+ export declare function buildResultDetail(store: RunStore, id: string, caseId: string): Promise<{
118
+ case: {
119
+ position: number;
120
+ };
121
+ repeats: {
122
+ case_id: string;
123
+ repeat_index: number;
124
+ status: string;
125
+ error_code: string | null;
126
+ error_message: string | null;
127
+ stage_run_id: string | null;
128
+ subject_ref: string | null;
129
+ turns: number | null;
130
+ tool_call_count: number | null;
131
+ agent?: {
132
+ id: string;
133
+ session_id: string | null;
134
+ } | null;
135
+ output: unknown;
136
+ latency_ms: number | null;
137
+ cost_usd: number | null;
138
+ tokens_in: number | null;
139
+ tokens_out: number | null;
140
+ score: number | null;
141
+ passed: boolean;
142
+ grades: import("./runs.js").WireGrade[];
143
+ budget_warnings: string[];
144
+ review: {
145
+ label: string | null;
146
+ note: string | null;
147
+ } | null;
148
+ tags: string[];
149
+ suggestion: import("./suggestions.js").WireSuggestion | null;
150
+ }[];
151
+ }>;
152
+ export declare function loadEvalListItems(input: {
153
+ store: RunStore;
154
+ projectRoot: string;
155
+ wireRoot?: string;
156
+ isKnownModel?: IsKnownModel;
157
+ agents?: AgentCatalogPort;
158
+ }): Promise<{
159
+ items: EvalListItem[];
160
+ banner: import("../listStatus.js").RegressionBanner | null;
161
+ bannerInputs: BannerEvalInput[];
162
+ manifest: import("../../types/stageflowManifest.js").LoadedManifest;
163
+ }>;
164
+ export type RootsOptions = ResolveCatalogRootsOptions;
165
+ export declare function evalScopeRoots(options: RootsOptions, wire: string | undefined): Promise<CatalogRoot[]>;
166
+ export declare function wireRootOf(options: RootsOptions): Promise<(projectRoot: string) => string>;
167
+ export declare function loadAggregatedEvalList(input: {
168
+ options: RootsOptions;
169
+ wire: string | undefined;
170
+ now: Date;
171
+ isKnownModel?: IsKnownModel;
172
+ agents?: AgentCatalogPort;
173
+ }): Promise<{
174
+ groups: {
175
+ root: CatalogRoot;
176
+ items: EvalListItem[];
177
+ }[];
178
+ items: EvalListItem[];
179
+ banner: import("../listStatus.js").RegressionBanner | null;
180
+ datasets: number;
181
+ cost: number;
182
+ }>;
183
+ export declare function locateEval(options: RootsOptions, wire: string | undefined, id: string, write?: boolean): Promise<{
184
+ root: CatalogRoot;
185
+ projectRoot: string;
186
+ ref: NonNullable<import("../../config/evalCatalog.js").CatalogFileRef | undefined>;
187
+ }>;
188
+ export declare function locateEvalRoot(options: RootsOptions, wire: string | undefined, id: string): Promise<CatalogRoot>;
189
+ export declare function locateDataset(options: RootsOptions, wire: string | undefined, id: string, opts: {
190
+ write: boolean;
191
+ missing: "throw" | "default";
192
+ }): Promise<{
193
+ root: CatalogRoot;
194
+ projectRoot: string;
195
+ ref: import("../../config/evalCatalog.js").CatalogFileRef | undefined;
196
+ }>;
@@ -0,0 +1,358 @@
1
+ import { readFile, stat } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { contentHash } from "../../config/atomicWrite.js";
4
+ import { resolveDefaultWriteRoot, resolveCatalogStartInput, } from "../../config/catalogRelativePath.js";
5
+ import { listDatasetFiles, loadEvalCatalog } from "../../config/evalCatalog.js";
6
+ import { resolveCatalogRoots, } from "../../config/resolveCatalogRoots.js";
7
+ import { loadDatasetFile } from "../../config/loadDataset.js";
8
+ import { evalStem, findDatasetFile, findEvalFile, manifestForWrite } from "../../config/evalWriteRoots.js";
9
+ import { resolveEvalTarget } from "../../config/resolveEvalTarget.js";
10
+ import { parseEvalOutcome, resolveEvalPaths } from "../../config/loadEval.js";
11
+ import { evalFindingsFromIssues } from "../../config/validateEval.js";
12
+ import { EVAL_RUN_STATUSES } from "../constants.js";
13
+ import { PINNED_PROMPTFOO_VERSION } from "../engine/promptfoo/pinned.js";
14
+ import { buildEvalPlan } from "../plan.js";
15
+ import { liveAgentOf } from "./agentTargets.js";
16
+ import { buildEvalListItems, costLastSevenDays } from "./list.js";
17
+ import { resultKey } from "../statsTypes.js";
18
+ import { regressionBanner } from "../listStatus.js";
19
+ import { buildResultRows, RESULT_ROW_STATUSES } from "./results.js";
20
+ import { activeRunOf, completedRunsNewestFirst, resultToWire, runHeadline } from "./runs.js";
21
+ import { suggestionFor } from "./suggestions.js";
22
+ export const DEFAULT_LIMIT = 50;
23
+ export const MAX_LIMIT = 200;
24
+ export const ENGINE_NAME = "promptfoo";
25
+ export class EvalApiError extends Error {
26
+ status;
27
+ code;
28
+ extra;
29
+ constructor(status, code, message, extra = {}) {
30
+ super(message);
31
+ this.status = status;
32
+ this.code = code;
33
+ this.extra = extra;
34
+ this.name = "EvalApiError";
35
+ }
36
+ }
37
+ export function rel(projectRoot, abs) {
38
+ return path.relative(projectRoot, abs).split(path.sep).join("/");
39
+ }
40
+ export function badRequest(message, code = "invalid_request") {
41
+ return new EvalApiError(400, code, message);
42
+ }
43
+ export function engineCheck(status) {
44
+ if (!status.nodeOk) {
45
+ throw new EvalApiError(503, "EVAL_NODE_TOO_OLD", `Evals need a newer Node.js; found ${status.node}`);
46
+ }
47
+ if (!status.installed) {
48
+ throw new EvalApiError(503, "EVAL_ENGINE_UNAVAILABLE", `The eval engine is not installed. Run \`${status.installCommand}\`.`);
49
+ }
50
+ }
51
+ export async function findEvalOr404(projectRoot, id) {
52
+ const ref = await findEvalFile(projectRoot, id);
53
+ if (ref === undefined)
54
+ throw new EvalApiError(404, "EVAL_NOT_FOUND", `Eval not found: ${id}`);
55
+ return ref;
56
+ }
57
+ export async function runByIdOr404(store, id) {
58
+ const row = await store.getEvalRun(id);
59
+ if (row === null)
60
+ throw new EvalApiError(404, "EVAL_RUN_NOT_FOUND", `Eval run not found: ${id}`);
61
+ return row;
62
+ }
63
+ export function encodeOffset(offset) {
64
+ return Buffer.from(JSON.stringify({ offset }), "utf8").toString("base64url");
65
+ }
66
+ export function decodeOffset(cursor) {
67
+ if (cursor === null || cursor === undefined || cursor === "")
68
+ return 0;
69
+ try {
70
+ const parsed = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
71
+ if (typeof parsed.offset === "number" && Number.isInteger(parsed.offset) && parsed.offset >= 0)
72
+ return parsed.offset;
73
+ }
74
+ catch {
75
+ // fall through to the error below
76
+ }
77
+ throw badRequest("Invalid cursor", "invalid_cursor");
78
+ }
79
+ export async function buildEvalDetail(input) {
80
+ const { store, projectRoot, absPath } = input;
81
+ const manifest = await manifestForWrite(projectRoot);
82
+ const entries = await loadEvalCatalog(manifest, input.isKnownModel ? { isKnownModel: input.isKnownModel } : {});
83
+ const entry = entries.find((e) => path.resolve(e.absPath) === path.resolve(absPath));
84
+ const bytes = await readFile(absPath);
85
+ const id = entry?.outcome.ok ? entry.outcome.value.id : evalStem(path.basename(absPath));
86
+ let runsCount = 0;
87
+ let cursor;
88
+ do {
89
+ const page = await store.listEvalRuns({ projectRoot, evalId: id, limit: MAX_LIMIT, ...(cursor ? { cursor } : {}) });
90
+ runsCount += page.items.length;
91
+ cursor = page.nextCursor ?? undefined;
92
+ } while (cursor !== undefined);
93
+ const last = (await completedRunsNewestFirst(store, projectRoot, id, 1))[0];
94
+ let targetModel = null;
95
+ let agent;
96
+ if (entry?.outcome.ok) {
97
+ const file = entry.outcome.value.file;
98
+ if (file.target.kind === "agent") {
99
+ agent = await liveAgentOf(input.agents, projectRoot, file.target.agent);
100
+ targetModel = file.defaults?.model ?? agent?.model ?? null;
101
+ }
102
+ else {
103
+ const target = await resolveEvalTarget(file, entry.outcome.value.path, projectRoot);
104
+ if (target.ok)
105
+ targetModel = target.value.modelResolved;
106
+ }
107
+ }
108
+ return {
109
+ id,
110
+ path: rel(projectRoot, absPath),
111
+ file_hash: contentHash(bytes),
112
+ definition: entry?.outcome.ok ? entry.outcome.value.file : null,
113
+ yaml: bytes.toString("utf8"),
114
+ findings: entry?.findings ?? [],
115
+ runs_count: runsCount,
116
+ active_run: await activeRunOf(store, projectRoot, id),
117
+ last_run: last ? runHeadline(last) : null,
118
+ target_model: targetModel,
119
+ judge_default: entry?.outcome.ok ? (entry.outcome.value.file.defaults?.judge ?? null) : null,
120
+ ...(agent !== undefined ? { agent } : {}),
121
+ };
122
+ }
123
+ export async function planFindings(input) {
124
+ const built = await buildEvalPlan({
125
+ projectRoot: input.projectRoot,
126
+ evalPath: input.evalPath,
127
+ evalText: input.evalText,
128
+ ...(input.datasetCases !== undefined ? { datasetCases: input.datasetCases } : {}),
129
+ evalRunId: "er_validate",
130
+ engine: { name: ENGINE_NAME, version: PINNED_PROMPTFOO_VERSION, nodeVersion: process.versions.node },
131
+ ...(input.isKnownModel ? { isKnownModel: input.isKnownModel } : {}),
132
+ ...(input.agents ? { agents: input.agents } : {}),
133
+ });
134
+ return { ok: built.ok, findings: built.findings };
135
+ }
136
+ export async function validateEvalText(input) {
137
+ const { projectRoot, evalAbs, derived } = input;
138
+ const parsed = parseEvalOutcome(derived, evalAbs, projectRoot);
139
+ const checked = await planFindings({
140
+ projectRoot,
141
+ evalPath: evalAbs,
142
+ evalText: derived,
143
+ ...(input.datasetCases !== undefined ? { datasetCases: input.datasetCases } : {}),
144
+ ...(input.isKnownModel ? { isKnownModel: input.isKnownModel } : {}),
145
+ ...(input.agents ? { agents: input.agents } : {}),
146
+ });
147
+ const findings = checked.findings.length > 0 || parsed.ok
148
+ ? checked.findings
149
+ : evalFindingsFromIssues(rel(projectRoot, evalAbs), parsed.issues);
150
+ return { findings, parsed };
151
+ }
152
+ export async function listEvalRunsPage(store, input) {
153
+ const { projectRoot, evalId, status, stageRunId, subjectRef, limit, cursor } = input;
154
+ if (status !== undefined && !EVAL_RUN_STATUSES.includes(status)) {
155
+ throw badRequest(`"status" must be one of ${EVAL_RUN_STATUSES.join(", ")}`);
156
+ }
157
+ let page;
158
+ try {
159
+ page = await store.listEvalRuns({
160
+ ...(projectRoot !== undefined ? { projectRoot } : {}),
161
+ ...(evalId !== undefined ? { evalId } : {}),
162
+ ...(status !== undefined ? { status: status } : {}),
163
+ ...(stageRunId !== undefined ? { stageRunId } : {}),
164
+ ...(subjectRef !== undefined ? { subjectRef } : {}),
165
+ limit,
166
+ ...(cursor !== undefined ? { cursor } : {}),
167
+ });
168
+ }
169
+ catch (err) {
170
+ if (cursor !== undefined && err instanceof SyntaxError)
171
+ throw badRequest("Invalid cursor", "invalid_cursor");
172
+ throw err;
173
+ }
174
+ const items = await Promise.all(page.items.map(async (row) => {
175
+ const headline = {
176
+ ...runHeadline(row),
177
+ created_at: row.createdAt,
178
+ started_at: row.startedAt,
179
+ results_total: row.resultsTotal,
180
+ project_root: input.wireRootOf?.(row.projectRoot) ?? row.projectRoot,
181
+ };
182
+ if (stageRunId === undefined && subjectRef === undefined)
183
+ return headline;
184
+ const match = (await store.listEvalResults(row.evalRunId)).find((r) => stageRunId !== undefined ? r.stageRunId === stageRunId : r.subjectRef === subjectRef);
185
+ return {
186
+ ...headline,
187
+ ...(match ? { result: { case_id: match.caseId, repeat_index: match.repeatIndex } } : {}),
188
+ };
189
+ }));
190
+ return { items, next_cursor: page.nextCursor };
191
+ }
192
+ export async function buildResultsPage(store, id, input) {
193
+ const row = await runByIdOr404(store, id);
194
+ const { status, grader, q, tag, limit } = input;
195
+ if (status !== undefined && !RESULT_ROW_STATUSES.includes(status)) {
196
+ throw badRequest(`"status" must be one of ${RESULT_ROW_STATUSES.join(", ")}`);
197
+ }
198
+ const offset = decodeOffset(input.cursor);
199
+ const [cases, results, reviews] = await Promise.all([
200
+ store.getEvalCases(row.evalRunId),
201
+ store.listEvalResults(row.evalRunId),
202
+ store.getEvalReviews(row.evalRunId),
203
+ ]);
204
+ const { rows, counts } = buildResultRows({
205
+ targetKind: row.targetKind,
206
+ cases,
207
+ results,
208
+ reviews,
209
+ filter: {
210
+ ...(status !== undefined ? { status: status } : {}),
211
+ ...(grader !== undefined ? { grader } : {}),
212
+ ...(q !== undefined ? { q } : {}),
213
+ ...(tag !== undefined ? { tag } : {}),
214
+ },
215
+ });
216
+ const page = rows.slice(offset, offset + limit);
217
+ return {
218
+ items: page,
219
+ next_cursor: offset + limit < rows.length ? encodeOffset(offset + limit) : null,
220
+ counts,
221
+ };
222
+ }
223
+ export async function buildResultDetail(store, id, caseId) {
224
+ const row = await runByIdOr404(store, id);
225
+ const [cases, results, reviews, suggestions] = await Promise.all([
226
+ store.getEvalCases(row.evalRunId),
227
+ store.getEvalResultsForCase(row.evalRunId, caseId),
228
+ store.getEvalReviews(row.evalRunId),
229
+ store.listEvalSuggestions(row.evalRunId),
230
+ ]);
231
+ const caseRow = cases.find((x) => x.caseId === caseId);
232
+ if (!caseRow || results.length === 0) {
233
+ throw new EvalApiError(404, "EVAL_RUN_NOT_FOUND", `Eval result not found: ${id}/${caseId}`);
234
+ }
235
+ const reviewByKey = new Map(reviews.map((r) => [resultKey(r.caseId, r.repeatIndex), r]));
236
+ return {
237
+ case: { ...caseRow.case, position: caseRow.position },
238
+ repeats: [...results]
239
+ .sort((a, b) => a.repeatIndex - b.repeatIndex)
240
+ .map((r) => ({
241
+ ...resultToWire(r, reviewByKey.get(resultKey(r.caseId, r.repeatIndex)), row.targetKind === "agent" ? (row.targetRef.agent_id ?? "") : undefined),
242
+ suggestion: suggestionFor(suggestions, r.caseId, r.repeatIndex),
243
+ })),
244
+ };
245
+ }
246
+ async function exists(abs) {
247
+ try {
248
+ await stat(abs);
249
+ return true;
250
+ }
251
+ catch {
252
+ return false;
253
+ }
254
+ }
255
+ export async function loadEvalListItems(input) {
256
+ const { store, projectRoot } = input;
257
+ const wireRoot = input.wireRoot ?? projectRoot;
258
+ const manifest = await manifestForWrite(projectRoot);
259
+ const counts = new Map();
260
+ const entries = await loadEvalCatalog(manifest, {
261
+ ...(input.isKnownModel ? { isKnownModel: input.isKnownModel } : {}),
262
+ datasetReportFor: async (loaded) => {
263
+ const { datasetPath } = resolveEvalPaths(loaded.file, loaded.path);
264
+ if (!(await exists(datasetPath)))
265
+ return undefined;
266
+ const report = await loadDatasetFile(datasetPath, { root: projectRoot });
267
+ counts.set(datasetPath, report.cases.length);
268
+ return report;
269
+ },
270
+ });
271
+ const built = await buildEvalListItems({
272
+ store,
273
+ projectRoot,
274
+ entries,
275
+ datasetCases: (abs) => counts.get(abs) ?? 0,
276
+ ...(input.agents ? { agents: input.agents } : {}),
277
+ });
278
+ const items = built.items.map((item) => ({ ...item, project_root: wireRoot }));
279
+ return { items, banner: built.banner, bannerInputs: built.bannerInputs, manifest };
280
+ }
281
+ export async function evalScopeRoots(options, wire) {
282
+ if (wire !== undefined)
283
+ return [(await resolveCatalogStartInput(options, wire)).wireRoot];
284
+ const roots = await resolveCatalogRoots(options);
285
+ return roots.filter((r) => !r.read_only && r.available !== false);
286
+ }
287
+ export async function wireRootOf(options) {
288
+ const roots = await resolveCatalogRoots(options);
289
+ return (projectRoot) => roots.find((r) => r.path === projectRoot)?.project_root ?? projectRoot;
290
+ }
291
+ export async function loadAggregatedEvalList(input) {
292
+ const roots = await evalScopeRoots(input.options, input.wire);
293
+ const groups = [];
294
+ const bannerInputs = [];
295
+ let datasets = 0;
296
+ let cost = 0;
297
+ for (const root of roots) {
298
+ const loaded = await loadEvalListItems({
299
+ store: input.options.store,
300
+ projectRoot: root.path,
301
+ wireRoot: root.project_root,
302
+ ...(input.isKnownModel ? { isKnownModel: input.isKnownModel } : {}),
303
+ ...(input.agents ? { agents: input.agents } : {}),
304
+ });
305
+ groups.push({ root, items: loaded.items });
306
+ bannerInputs.push(...loaded.bannerInputs);
307
+ datasets += (await listDatasetFiles(loaded.manifest)).length;
308
+ cost += await costLastSevenDays(input.options.store, root.path, input.now);
309
+ }
310
+ return {
311
+ groups,
312
+ items: groups.flatMap((g) => g.items),
313
+ banner: regressionBanner(bannerInputs),
314
+ datasets,
315
+ cost,
316
+ };
317
+ }
318
+ function ambiguous(code, noun, id, roots) {
319
+ return new EvalApiError(409, code, `${noun} "${id}" exists in more than one project; pass project_root`, { project_roots: roots });
320
+ }
321
+ async function locate(options, wire, write, find, fail, missing) {
322
+ const notFound = () => new EvalApiError(404, fail.notFoundCode, `${fail.noun} not found: ${fail.id}`);
323
+ if (wire !== undefined) {
324
+ const root = write
325
+ ? (await resolveDefaultWriteRoot(options, wire)).wireRoot
326
+ : (await resolveCatalogStartInput(options, wire)).wireRoot;
327
+ const ref = await find(root.path);
328
+ if (ref === undefined && missing === "throw")
329
+ throw notFound();
330
+ return { root, ref };
331
+ }
332
+ const matches = [];
333
+ for (const root of await evalScopeRoots(options, undefined)) {
334
+ const ref = await find(root.path);
335
+ if (ref !== undefined)
336
+ matches.push({ root, ref });
337
+ }
338
+ if (matches.length > 1) {
339
+ throw ambiguous(fail.ambiguousCode, fail.noun, fail.id, matches.map((m) => m.root.project_root));
340
+ }
341
+ if (matches[0] !== undefined)
342
+ return matches[0];
343
+ if (missing === "throw")
344
+ throw notFound();
345
+ return { root: (await resolveDefaultWriteRoot(options)).wireRoot, ref: undefined };
346
+ }
347
+ export async function locateEval(options, wire, id, write = false) {
348
+ const { root, ref } = await locate(options, wire, write, (projectRoot) => findEvalFile(projectRoot, id), { ambiguousCode: "EVAL_AMBIGUOUS", notFoundCode: "EVAL_NOT_FOUND", noun: "Eval", id }, "throw");
349
+ return { root, projectRoot: root.path, ref: ref };
350
+ }
351
+ export async function locateEvalRoot(options, wire, id) {
352
+ const { root } = await locate(options, wire, false, (projectRoot) => findEvalFile(projectRoot, id), { ambiguousCode: "EVAL_AMBIGUOUS", notFoundCode: "EVAL_NOT_FOUND", noun: "Eval", id }, "default");
353
+ return root;
354
+ }
355
+ export async function locateDataset(options, wire, id, opts) {
356
+ const { root, ref } = await locate(options, wire, opts.write, (projectRoot) => findDatasetFile(projectRoot, id), { ambiguousCode: "DATASET_AMBIGUOUS", notFoundCode: "DATASET_NOT_FOUND", noun: "Dataset", id }, opts.missing);
357
+ return { root, projectRoot: root.path, ref };
358
+ }