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,347 @@
1
+ export type FieldOp = "exists" | "not_empty" | "equals" | "not_equals" | "contains" | "matches" | "gte" | "lte" | "between" | "in" | "length_gte" | "length_lte";
2
+ export type FieldCheck = {
3
+ path: string;
4
+ op: FieldOp;
5
+ value?: unknown;
6
+ };
7
+ export type RubricExample = {
8
+ verdict: "pass" | "fail";
9
+ output: string;
10
+ reason?: string;
11
+ } | {
12
+ score: number;
13
+ output: string;
14
+ reason?: string;
15
+ };
16
+ export type EvalType = "capability" | "regression";
17
+ export type StageEvalTarget = {
18
+ kind: "stage";
19
+ pipeline?: string;
20
+ stage?: string;
21
+ file?: string;
22
+ };
23
+ export type AgentEvalTarget = {
24
+ kind: "agent";
25
+ agent: string;
26
+ };
27
+ export type EvalTarget = StageEvalTarget | AgentEvalTarget;
28
+ export type ConversationMode = "script" | "simulated";
29
+ export type ConversationTools = "real" | "read_only" | "mocked";
30
+ export type ConversationApprovals = "deny" | "auto_approve_read" | "record_only";
31
+ export type EvalConversation = {
32
+ mode?: ConversationMode;
33
+ max_turns?: number;
34
+ turn_timeout_ms?: number;
35
+ tools?: ConversationTools;
36
+ approvals?: ConversationApprovals;
37
+ read_only_tools?: string[];
38
+ allow_real_tools?: boolean;
39
+ };
40
+ export type ResolvedConversation = {
41
+ mode: ConversationMode;
42
+ maxTurns: number;
43
+ turnTimeoutMs: number;
44
+ tools: ConversationTools;
45
+ approvals: ConversationApprovals;
46
+ readOnlyTools: string[];
47
+ allowRealTools: boolean;
48
+ };
49
+ export type EvalTask = {
50
+ goal?: string;
51
+ context?: string;
52
+ constraints?: string;
53
+ };
54
+ export type EvalDefaults = {
55
+ repeats?: number;
56
+ judge?: string;
57
+ pass_threshold?: number;
58
+ max_cost_usd?: number;
59
+ concurrency?: number;
60
+ model?: string;
61
+ user_model?: string;
62
+ };
63
+ export type EvalBudget = {
64
+ latency_ms?: number;
65
+ cost_usd?: number;
66
+ };
67
+ type GraderCommon = {
68
+ id: string;
69
+ description?: string;
70
+ weight?: number;
71
+ must_pass?: boolean;
72
+ };
73
+ export type ToolArgCheck = {
74
+ tool: string;
75
+ path: string;
76
+ op: FieldOp;
77
+ value?: unknown;
78
+ };
79
+ export type FinalReplyCheck = {
80
+ op: FieldOp;
81
+ value?: unknown;
82
+ };
83
+ export type GraderConfig = (GraderCommon & {
84
+ type: "schema";
85
+ }) | (GraderCommon & {
86
+ type: "field";
87
+ checks: FieldCheck[];
88
+ }) | (GraderCommon & {
89
+ type: "reference";
90
+ mode: "each_mentioned" | "equals";
91
+ path?: string;
92
+ expected: string;
93
+ pass_at?: number;
94
+ }) | (GraderCommon & {
95
+ type: "rubric";
96
+ rubric: string;
97
+ scale?: "pass_fail" | "1_to_5";
98
+ pass_at?: number;
99
+ judge?: string;
100
+ examples?: RubricExample[];
101
+ }) | (GraderCommon & {
102
+ type: "code";
103
+ code: string;
104
+ }) | (GraderCommon & {
105
+ type: "tool_calls";
106
+ must_call?: string[];
107
+ must_not_call?: string[];
108
+ must_ask_first?: string[];
109
+ max_calls?: number;
110
+ args?: ToolArgCheck[];
111
+ }) | (GraderCommon & {
112
+ type: "conversation_check";
113
+ max_turns?: number;
114
+ max_agent_questions_per_turn?: number;
115
+ final_reply?: FinalReplyCheck[];
116
+ }) | (GraderCommon & {
117
+ type: "goal_reached";
118
+ judge?: string;
119
+ guidance?: string;
120
+ });
121
+ export type EvalFile = {
122
+ version: 1;
123
+ id: string;
124
+ name?: string;
125
+ description?: string;
126
+ type?: EvalType;
127
+ target: EvalTarget;
128
+ dataset: string;
129
+ task?: EvalTask;
130
+ defaults?: EvalDefaults;
131
+ conversation?: EvalConversation;
132
+ graders: GraderConfig[];
133
+ budget?: EvalBudget;
134
+ };
135
+ export type StageTargetRef = {
136
+ kind: "stage";
137
+ pipelinePath?: string;
138
+ stageId?: string;
139
+ file?: string;
140
+ };
141
+ export type AgentTargetRef = {
142
+ kind: "agent";
143
+ agentId: string;
144
+ revision?: number;
145
+ };
146
+ export type TargetRef = StageTargetRef | AgentTargetRef;
147
+ type ResolvedGraderCommon = {
148
+ id: string;
149
+ weight: number;
150
+ mustPass: boolean;
151
+ hash: string;
152
+ description?: string;
153
+ };
154
+ export type ResolvedGrader = (ResolvedGraderCommon & {
155
+ type: "schema";
156
+ }) | (ResolvedGraderCommon & {
157
+ type: "field";
158
+ checks: FieldCheck[];
159
+ }) | (ResolvedGraderCommon & {
160
+ type: "reference";
161
+ mode: "each_mentioned" | "equals";
162
+ path: string;
163
+ expected: string;
164
+ passAt: number;
165
+ }) | (ResolvedGraderCommon & {
166
+ type: "rubric";
167
+ rubric: string;
168
+ scale: "pass_fail" | "1_to_5";
169
+ passAt: number;
170
+ judge: string;
171
+ examples: RubricExample[];
172
+ }) | (ResolvedGraderCommon & {
173
+ type: "code";
174
+ code: string;
175
+ }) | (ResolvedGraderCommon & {
176
+ type: "tool_calls";
177
+ mustCall?: string[];
178
+ mustNotCall?: string[];
179
+ mustAskFirst?: string[];
180
+ maxCalls?: number;
181
+ args: ToolArgCheck[];
182
+ }) | (ResolvedGraderCommon & {
183
+ type: "conversation_check";
184
+ maxTurns?: number;
185
+ maxAgentQuestionsPerTurn?: number;
186
+ finalReply: FinalReplyCheck[];
187
+ }) | (ResolvedGraderCommon & {
188
+ type: "goal_reached";
189
+ judge: string;
190
+ guidance?: string;
191
+ });
192
+ export type PlanCase = {
193
+ id: string;
194
+ position: number;
195
+ title?: string;
196
+ goal?: string;
197
+ input: Record<string, unknown>;
198
+ expected?: unknown;
199
+ tags: string[];
200
+ metadata?: unknown;
201
+ };
202
+ export type EvalPlan = {
203
+ evalRunId: string;
204
+ projectRoot: string;
205
+ evalId: string;
206
+ target: TargetRef;
207
+ targetVersionHash: string;
208
+ outputSchema?: unknown;
209
+ stageModel: string;
210
+ task: {
211
+ goal?: string;
212
+ context?: string;
213
+ constraints?: string;
214
+ };
215
+ cases: PlanCase[];
216
+ graders: ResolvedGrader[];
217
+ repeats: number;
218
+ passThreshold: number;
219
+ concurrency: number;
220
+ maxCostUsd: number;
221
+ modelOverride?: string;
222
+ budget?: {
223
+ latencyMs?: number;
224
+ costUsd?: number;
225
+ };
226
+ /** Agent plans only: the revision pinned at plan time and the conversation settings. */
227
+ agent?: {
228
+ definitionHash: string;
229
+ definition: Record<string, unknown>;
230
+ };
231
+ conversation?: ResolvedConversation;
232
+ /** Agent plans only: null when no selected case is simulated. */
233
+ userModel?: string | null;
234
+ };
235
+ export type EnvelopeOutput = {
236
+ kind: "envelope";
237
+ status: "success" | "failure";
238
+ summary: string;
239
+ payload?: Record<string, unknown>;
240
+ artifacts: string[];
241
+ };
242
+ export type ConversationStopReason = "script_end" | "resolved" | "abandoned" | "max_turns" | "no_progress" | "empty_message" | "error" | "timeout";
243
+ export type ConversationMessage = {
244
+ role: "user" | "assistant";
245
+ text: string;
246
+ turn: number;
247
+ };
248
+ export type ConversationToolCall = {
249
+ id: string;
250
+ turn: number;
251
+ name: string;
252
+ args: Record<string, unknown>;
253
+ result_summary: string;
254
+ status: "ok" | "error" | "not_run";
255
+ policy_outcome: "ran" | "stubbed" | "mocked" | "would_ask_first" | "denied";
256
+ session_id?: string;
257
+ };
258
+ export type ConversationUsage = {
259
+ cost_usd: number;
260
+ agent_cost_usd: number;
261
+ simulator_cost_usd: number;
262
+ tokens_in: number;
263
+ tokens_out: number;
264
+ simulator_tokens_in: number;
265
+ simulator_tokens_out: number;
266
+ simulator_estimated: boolean;
267
+ agent_calls: number;
268
+ simulator_calls: number;
269
+ };
270
+ /** The stored JSON shape (snake_case), which graders, the judge renderer and the console read as is. */
271
+ export type ConversationOutput = {
272
+ kind: "conversation";
273
+ status: "success" | "failure";
274
+ summary: string;
275
+ messages: ConversationMessage[];
276
+ tool_calls: ConversationToolCall[];
277
+ final_reply: string;
278
+ turns: number;
279
+ stop_reason: ConversationStopReason;
280
+ usage: ConversationUsage;
281
+ truncated?: Record<string, number>;
282
+ /** Transport only: the bounded rendered transcript for the judge. The manager drops it before storing. */
283
+ transcript?: string;
284
+ };
285
+ export type CaseOutput = EnvelopeOutput | ConversationOutput;
286
+ export type RunCaseRequest = {
287
+ evalRunId: string;
288
+ caseId: string;
289
+ repeatIndex: number;
290
+ };
291
+ export type RunCaseFailureCode = "EVAL_STAGE_FAILED" | "EVAL_STAGE_NEEDS_INPUT" | "EVAL_STAGE_TIMEOUT" | "EVAL_BUDGET_EXCEEDED" | "EVAL_CANCELLED" | "EVAL_ENGINE_ERROR" | "EVAL_AGENT_FAILED" | "EVAL_AGENT_EMPTY_REPLY" | "EVAL_AGENT_TIMEOUT" | "EVAL_SIMULATOR_ERROR";
292
+ export type RunCaseResult = {
293
+ ok: true;
294
+ /** Root subject id: the stage run id (set by the manager when absent) or the root agent session id. */
295
+ subjectRef?: string;
296
+ stageRunId?: string;
297
+ output: CaseOutput;
298
+ latencyMs: number;
299
+ costUsd: number;
300
+ tokensIn: number;
301
+ tokensOut: number;
302
+ } | {
303
+ ok: false;
304
+ code: RunCaseFailureCode;
305
+ message: string;
306
+ subjectRef?: string;
307
+ stageRunId?: string;
308
+ output?: ConversationOutput;
309
+ latencyMs?: number;
310
+ costUsd?: number;
311
+ };
312
+ export type NormalizedGrade = {
313
+ graderId: string;
314
+ graderType: string;
315
+ status: "graded" | "error";
316
+ passed?: boolean;
317
+ score?: number;
318
+ rawValue?: number;
319
+ reason?: string;
320
+ errorCode?: string;
321
+ details?: unknown;
322
+ judgeModel?: string;
323
+ judgeCostUsd?: number;
324
+ judgeTokensIn?: number;
325
+ judgeTokensOut?: number;
326
+ };
327
+ export type NormalizedResult = {
328
+ caseId: string;
329
+ repeatIndex: number;
330
+ run: RunCaseResult;
331
+ grades: NormalizedGrade[];
332
+ };
333
+ export interface EngineIo {
334
+ runCase(req: RunCaseRequest): Promise<RunCaseResult>;
335
+ onResult(result: NormalizedResult): void;
336
+ reportJudgeCost(usd: number): void;
337
+ onPhase(caseId: string, repeatIndex: number, phase: "stage_running" | "agent_running" | "grading" | "done"): void;
338
+ log(level: "info" | "warn" | "error", message: string): void;
339
+ }
340
+ export interface EvalEngine {
341
+ readonly name: string;
342
+ version(): Promise<string>;
343
+ /** Throws an Error with a `code` when the plan can never run on this engine (checked before a run row exists). */
344
+ validatePlan?(plan: EvalPlan): void;
345
+ run(plan: EvalPlan, io: EngineIo, signal: AbortSignal): Promise<void>;
346
+ }
347
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -5,6 +5,9 @@ import type { ConversationBackend } from "../agents/sessions.js";
5
5
  import type { AgentService } from "../agents/service.js";
6
6
  import type { RunStore } from "../runstore/port.js";
7
7
  import type { RunChangeBus } from "../runtime/runChangeBus.js";
8
+ import type { EngineStatus } from "../evals/engine/promptfoo/status.js";
9
+ import type { EvalRunManager } from "../evals/manager.js";
10
+ import type { TriageDeps } from "../evals/triage/service.js";
8
11
  import type { RunManager } from "../runtime/runManager.js";
9
12
  export type McpToolDeps = {
10
13
  manager: RunManager;
@@ -17,6 +20,10 @@ export type McpToolDeps = {
17
20
  agentService?: AgentService;
18
21
  agentChat?: AgentChatController;
19
22
  agentConversationBackend?: ConversationBackend;
23
+ evalRunManager?: EvalRunManager;
24
+ evalEngineStatus?: () => EngineStatus;
25
+ /** Failure sorting seams: key lookup, fetch and base URL for the Jev classifier, or a judge model (tests). */
26
+ triage?: Omit<TriageDeps, "store">;
20
27
  };
21
28
  export type McpHttpDeps = McpToolDeps & {
22
29
  runChangeBus: RunChangeBus;
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/server";
2
+ import type { McpToolDeps } from "./deps.js";
3
+ export declare function registerEvalTools(server: McpServer, deps: McpToolDeps): void;
@@ -0,0 +1,280 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { z } from "zod";
4
+ import { CatalogPathError, catalogPathErrorBody, resolveCatalogStartInput, } from "../config/catalogRelativePath.js";
5
+ import { assertInsideProject, manifestForWrite, resolveWriteDir } from "../config/evalWriteRoots.js";
6
+ import { CompareMismatchError } from "../evals/compare.js";
7
+ import { EVAL_LIMITS, EVAL_RUN_STATUSES } from "../evals/constants.js";
8
+ import { engineStatus as promptfooEngineStatus } from "../evals/engine/promptfoo/status.js";
9
+ import { EvalRunError } from "../evals/manager.js";
10
+ import { buildEvalDetail, buildResultDetail, buildResultsPage, DEFAULT_LIMIT, engineCheck, EvalApiError, locateEval, loadAggregatedEvalList, listEvalRunsPage, MAX_LIMIT, runByIdOr404, validateEvalText, } from "../evals/read/api.js";
11
+ import { buildCompare } from "../evals/read/compare.js";
12
+ import { buildRunDetail } from "../evals/read/runs.js";
13
+ import { EvalStoreError } from "../evals/storeError.js";
14
+ import { SUGGESTION_STATUSES, TRIAGE_MAX_LIMIT } from "../evals/triage/constants.js";
15
+ import { TriageService } from "../evals/triage/service.js";
16
+ import { writeAudit } from "../logging/audit.js";
17
+ import { logger as rootLogger } from "../logging/logger.js";
18
+ import { globalStageflowHome } from "../project/globalHome.js";
19
+ import { callerIdFromRequestAuth, getRequestAuth } from "../server/requestAuthContext.js";
20
+ import { textResult } from "./toolResults.js";
21
+ const auditLog = rootLogger.child({ component: "audit" });
22
+ const CANCEL_WAIT_MS = 5000;
23
+ const DEFAULT_WAIT_MS = 600_000;
24
+ const MAX_WAIT_MS = 1_800_000;
25
+ const projectRootField = z
26
+ .string()
27
+ .optional()
28
+ .describe("Catalog project root; omit to use the host default");
29
+ function audit(action, outcome, target, code) {
30
+ writeAudit(auditLog, {
31
+ caller_id: callerIdFromRequestAuth(),
32
+ surface: getRequestAuth()?.surface ?? "mcp",
33
+ action,
34
+ outcome,
35
+ ...(target !== undefined ? { target_run_id: target } : {}),
36
+ ...(code !== undefined ? { error_code: code } : {}),
37
+ });
38
+ }
39
+ function errorResult(err) {
40
+ if (err instanceof EvalApiError) {
41
+ return textResult({ error: err.message, code: err.code, ...err.extra }, true);
42
+ }
43
+ if (err instanceof CatalogPathError)
44
+ return textResult(catalogPathErrorBody(err), true);
45
+ if (err instanceof EvalRunError) {
46
+ return textResult({ error: err.message, code: err.code, ...(err.findings ? { findings: err.findings } : {}) }, true);
47
+ }
48
+ if (err instanceof EvalStoreError || err instanceof CompareMismatchError) {
49
+ return textResult({ error: err.message, code: err.code }, true);
50
+ }
51
+ return textResult({ error: "Internal error", code: "internal_error" }, true);
52
+ }
53
+ export function registerEvalTools(server, deps) {
54
+ const { store, cwd } = deps;
55
+ const manager = () => {
56
+ if (deps.evalRunManager === undefined) {
57
+ throw new EvalApiError(503, "EVAL_ENGINE_UNAVAILABLE", "Evals are not available on this Host");
58
+ }
59
+ return deps.evalRunManager;
60
+ };
61
+ const projectRootFor = async (wire) => {
62
+ const { wireRoot } = await resolveCatalogStartInput({ store, bootCwd: cwd }, wire);
63
+ return wireRoot.path;
64
+ };
65
+ const agentsInput = () => (deps.agentService !== undefined ? { agents: deps.agentService } : {});
66
+ const runDetail = (row) => buildRunDetail({ store, row, live: manager().getLive(row.evalRunId), ...agentsInput() });
67
+ const tool = (name, description, inputSchema, handler) => {
68
+ const run = async (input) => {
69
+ try {
70
+ return textResult(await handler(input));
71
+ }
72
+ catch (err) {
73
+ return errorResult(err);
74
+ }
75
+ };
76
+ server.registerTool(name, { description, inputSchema }, run);
77
+ };
78
+ tool("list_evals", "List the evals (*.eval.yaml) in a project with target (a stage, or an agent with its revision), dataset size, last-run headline and trend. Read-only, free. Same items as GET /api/evals.", z.object({ project_root: projectRootField }), async ({ project_root }) => {
79
+ manager();
80
+ const { items } = await loadAggregatedEvalList({
81
+ options: { store, bootCwd: cwd },
82
+ wire: project_root,
83
+ now: new Date(),
84
+ ...agentsInput(),
85
+ });
86
+ return { items };
87
+ });
88
+ tool("get_eval", "Get one eval: parsed definition, raw YAML, file hash, validation findings, run count and last run; for an agent eval also the live agent (model, revision, tools with risk, ask-first list). Read-only, free. Same as GET /api/evals/:id. Fails EVAL_NOT_FOUND for an unknown id.", z.object({ eval_id: z.string(), project_root: projectRootField }), async ({ eval_id, project_root }) => {
89
+ manager();
90
+ const { projectRoot, ref } = await locateEval({ store, bootCwd: cwd }, project_root, eval_id);
91
+ return buildEvalDetail({ store, projectRoot, absPath: ref.absPath, ...agentsInput() });
92
+ });
93
+ tool("validate_eval", "Validate an eval without running it: pass eval_id for a file in the catalog, or yaml for unsaved text (exactly one). Checks schema, target, dataset and graders and returns { findings }. Read-only, free, calls no model; writes no files.", z.object({
94
+ eval_id: z.string().optional(),
95
+ yaml: z.string().optional(),
96
+ project_root: projectRootField,
97
+ }), async ({ eval_id, yaml, project_root }) => {
98
+ manager();
99
+ if ((eval_id === undefined) === (yaml === undefined)) {
100
+ throw new EvalApiError(400, "invalid_request", "Provide exactly one of eval_id or yaml");
101
+ }
102
+ const located = eval_id !== undefined ? await locateEval({ store, bootCwd: cwd }, project_root, eval_id) : undefined;
103
+ const projectRoot = located?.projectRoot ?? (await projectRootFor(project_root));
104
+ let evalAbs;
105
+ let derived;
106
+ if (located !== undefined) {
107
+ evalAbs = located.ref.absPath;
108
+ derived = await readFile(evalAbs, "utf8");
109
+ }
110
+ else {
111
+ const manifest = await manifestForWrite(projectRoot);
112
+ const dir = await resolveWriteDir(projectRoot, manifest, "evals");
113
+ const rawId = /^id:\s*["']?([A-Za-z0-9][A-Za-z0-9._-]{0,63})["']?\s*$/m.exec(yaml)?.[1];
114
+ evalAbs = path.join(dir.absDir, `${rawId ?? "new"}.eval.yaml`);
115
+ assertInsideProject(projectRoot, evalAbs, "eval path");
116
+ derived = yaml.endsWith("\n") ? yaml : `${yaml}\n`;
117
+ }
118
+ const { findings } = await validateEvalText({ projectRoot, evalAbs, derived, ...agentsInput() });
119
+ return { findings };
120
+ });
121
+ tool("run_eval", "Run an eval: executes every case of the eval's dataset through its target (a stage, or an agent held as a conversation; repeats times each) and grades the results. This calls models and may cost real money (stage calls plus judge calls for rubric graders), bounded by max_cost_usd. Agent runs call the agent's model, a simulator model that plays the user (simulated cases) and the judge, and can cost much more than stage runs. Agent tools are read-only by default: write, shell and unlisted tools are stubbed and recorded, never executed. An eval with conversation.tools: real needs allow_real_tools true on top of allow_real_tools in the eval file, else it fails EVAL_REAL_TOOLS_NOT_CONFIRMED. Only one eval run is active per host; a second start fails EVAL_RUN_ACTIVE. With wait false (default) it returns { eval_run_id } immediately and the run continues in the background: poll get_eval_run. With wait true it blocks up to timeout_ms (default 600000, max 1800000) and returns the finished run detail, or { eval_run_id, status: \"timeout\" } if still running (the run keeps going; cancel_eval_run stops it). An invalid eval returns an error result with findings.", z.object({
122
+ eval_id: z.string(),
123
+ repeats: z.number().int().min(EVAL_LIMITS.repeats.min).max(EVAL_LIMITS.repeats.max).optional().describe("Override executions per case"),
124
+ judge: z.string().optional().describe("Override the judge model for rubric graders"),
125
+ model: z.string().optional().describe("Override the stage or agent model"),
126
+ user_model: z.string().optional().describe("Agent evals: override the model that plays the simulated user"),
127
+ allow_real_tools: z.boolean().optional().describe("Agent evals: consent to conversation.tools real; no effect on other policies"),
128
+ tools: z.enum(["read_only", "mocked"]).optional().describe("Agent evals: run with a safer tool policy than the file (never riskier)"),
129
+ approvals: z.enum(["deny", "record_only"]).optional().describe("Agent evals: run with a safer approval policy than the file (never riskier)"),
130
+ max_cost_usd: z.number().positive().optional().describe("Override the cost cap in USD"),
131
+ case_ids: z.array(z.string()).optional().describe("Run only these case ids"),
132
+ tags: z.array(z.string()).optional().describe("Run only cases with these tags"),
133
+ label: z.string().max(EVAL_LIMITS.labelMaxChars).optional().describe("Free-text label for the run"),
134
+ wait: z.boolean().optional().describe("Block until finished (default false)"),
135
+ timeout_ms: z.number().int().min(1).max(MAX_WAIT_MS).optional().describe(`Wait budget in ms when wait is true (default ${DEFAULT_WAIT_MS}, max ${MAX_WAIT_MS})`),
136
+ project_root: projectRootField,
137
+ }), async (input) => {
138
+ const evalRunManager = manager();
139
+ const { projectRoot, ref } = await locateEval({ store, bootCwd: cwd }, input.project_root, input.eval_id);
140
+ const overrides = {};
141
+ if (input.user_model !== undefined)
142
+ overrides.user_model = input.user_model;
143
+ if (input.tools !== undefined)
144
+ overrides.tools = input.tools;
145
+ if (input.approvals !== undefined)
146
+ overrides.approvals = input.approvals;
147
+ if (input.allow_real_tools === true)
148
+ overrides.allow_real_tools = true;
149
+ if (input.repeats !== undefined)
150
+ overrides.repeats = input.repeats;
151
+ if (input.judge !== undefined)
152
+ overrides.judge = input.judge;
153
+ if (input.model !== undefined)
154
+ overrides.model = input.model;
155
+ if (input.max_cost_usd !== undefined)
156
+ overrides.max_cost_usd = input.max_cost_usd;
157
+ if (input.case_ids !== undefined)
158
+ overrides.case_ids = input.case_ids;
159
+ if (input.tags !== undefined)
160
+ overrides.tags = input.tags;
161
+ const callerId = callerIdFromRequestAuth();
162
+ const startInput = {
163
+ projectRoot,
164
+ evalPath: ref.absPath,
165
+ overrides,
166
+ startedVia: "mcp",
167
+ ...(input.label !== undefined ? { label: input.label } : {}),
168
+ ...(callerId !== null ? { callerId } : {}),
169
+ };
170
+ try {
171
+ await evalRunManager.preflightAgentStart(startInput);
172
+ }
173
+ catch (err) {
174
+ audit("eval_run_start", "error", undefined, err instanceof EvalRunError ? err.code : "internal_error");
175
+ throw err;
176
+ }
177
+ engineCheck((deps.evalEngineStatus ?? (() => promptfooEngineStatus({ home: globalStageflowHome() })))());
178
+ let started;
179
+ try {
180
+ started = await evalRunManager.start(startInput);
181
+ }
182
+ catch (err) {
183
+ audit("eval_run_start", "error", undefined, err instanceof EvalRunError ? err.code : "internal_error");
184
+ throw err;
185
+ }
186
+ audit("eval_run_start", "ok", started.evalRunId);
187
+ if (input.wait !== true)
188
+ return { eval_run_id: started.evalRunId };
189
+ const { finished, run } = await evalRunManager.awaitFinished(started.evalRunId, input.timeout_ms ?? DEFAULT_WAIT_MS);
190
+ if (!finished)
191
+ return { eval_run_id: started.evalRunId, status: "timeout" };
192
+ return runDetail(run);
193
+ });
194
+ tool("get_eval_run", "Get one eval run: status, progress, stats, cost, config snapshot and per-grader summary. Read-only, free. Same as GET /api/eval-runs/:id. Fails EVAL_RUN_NOT_FOUND for an unknown id.", z.object({ eval_run_id: z.string() }), async ({ eval_run_id }) => {
195
+ manager();
196
+ return runDetail(await runByIdOr404(store, eval_run_id));
197
+ });
198
+ tool("list_eval_runs", "List eval runs, newest first, as headlines (status, pass rate, cost). Filter by eval_id or status. Read-only, free. Same items as GET /api/eval-runs.", z.object({
199
+ eval_id: z.string().optional(),
200
+ status: z.enum(EVAL_RUN_STATUSES).optional(),
201
+ limit: z.number().int().min(1).max(MAX_LIMIT).optional().describe(`Default ${DEFAULT_LIMIT}`),
202
+ }), async ({ eval_id, status, limit }) => {
203
+ manager();
204
+ const page = await listEvalRunsPage(store, {
205
+ ...(eval_id !== undefined ? { evalId: eval_id } : {}),
206
+ ...(status !== undefined ? { status } : {}),
207
+ limit: limit ?? DEFAULT_LIMIT,
208
+ });
209
+ return { items: page.items };
210
+ });
211
+ tool("get_eval_results", "List the result rows (one per case) of an eval run with pass state, score and failing graders. Filter by status or grader; page with cursor. Read-only, free. Same as GET /api/eval-runs/:id/results.", z.object({
212
+ eval_run_id: z.string(),
213
+ status: z.string().optional().describe("Result row status filter, for example failed or passed"),
214
+ grader: z.string().optional().describe("Only results this grader failed"),
215
+ limit: z.number().int().min(1).max(MAX_LIMIT).optional().describe(`Default ${DEFAULT_LIMIT}`),
216
+ cursor: z.string().optional(),
217
+ }), async ({ eval_run_id, status, grader, limit, cursor }) => {
218
+ manager();
219
+ const page = await buildResultsPage(store, eval_run_id, {
220
+ ...(status !== undefined ? { status } : {}),
221
+ ...(grader !== undefined ? { grader } : {}),
222
+ limit: limit ?? DEFAULT_LIMIT,
223
+ ...(cursor !== undefined ? { cursor } : {}),
224
+ });
225
+ return { items: page.items, next_cursor: page.next_cursor };
226
+ });
227
+ tool("get_eval_result", "Get one case of an eval run in full: the case input and expected value plus every repeat's stage output (for an agent eval, the conversation subject: messages, tool calls with policy outcomes, usage), grades, cost and review. Read-only, free. Same as GET /api/eval-runs/:id/results/:case_id.", z.object({ eval_run_id: z.string(), case_id: z.string() }), async ({ eval_run_id, case_id }) => {
228
+ manager();
229
+ return buildResultDetail(store, eval_run_id, case_id);
230
+ });
231
+ const triage = () => new TriageService({
232
+ store,
233
+ judgeBackend: () => deps.agentConversationBackend,
234
+ ...(deps.triage ?? {}),
235
+ });
236
+ tool("sort_eval_failures", "Sort the failed and errored results of a finished eval run into suggested buckets (agent_wrong, grader_wrong, case_wrong, unclear) and failure types, each with a confidence. This sends each failed result's case, output and grader reasons to an external classifier: TypeSafe Jev (about $0.04 per million input tokens; needs a TypeSafe key) or the run's judge model (a model call per result, real cost). It is manual only and never runs on its own. Nothing is tagged: suggestions are reviewed and accepted by the author in the console or API. Eval sessions of the run stay hidden. classifier auto uses jev when a key exists, else judge, else fails EVAL_CLASSIFIER_UNAVAILABLE. By default results that already have a suggestion are skipped (only_unsorted). Fails EVAL_RUN_ACTIVE while the run is running. Same as POST /api/eval-runs/:id/failures/sort.", z.object({
237
+ eval_run_id: z.string(),
238
+ classifier: z.enum(["auto", "jev", "judge"]).optional().describe("Default auto"),
239
+ only_unsorted: z.boolean().optional().describe("Skip results that already have a suggestion (default true)"),
240
+ limit: z.number().int().min(1).max(TRIAGE_MAX_LIMIT).optional().describe("Most results to sort (default 200)"),
241
+ }), async ({ eval_run_id, classifier, only_unsorted, limit }) => {
242
+ manager();
243
+ const row = await runByIdOr404(store, eval_run_id);
244
+ try {
245
+ const result = await triage().sort(row, {
246
+ classifier: classifier ?? "auto",
247
+ ...(only_unsorted !== undefined ? { onlyUnsorted: only_unsorted } : {}),
248
+ ...(limit !== undefined ? { limit } : {}),
249
+ });
250
+ audit("eval_failures_sort", "ok", eval_run_id);
251
+ return result;
252
+ }
253
+ catch (err) {
254
+ audit("eval_failures_sort", "error", eval_run_id, err instanceof EvalApiError ? err.code : "internal_error");
255
+ throw err;
256
+ }
257
+ });
258
+ tool("get_eval_failure_suggestions", "List the suggested buckets and failure types for an eval run's failed results, with a summary (counts by bucket, by failure type, by status, low confidence). Read-only, free, calls no model. Filter by status (pending, accepted, dismissed). Same as GET /api/eval-runs/:id/failures/suggestions.", z.object({ eval_run_id: z.string(), status: z.enum(SUGGESTION_STATUSES).optional() }), async ({ eval_run_id, status }) => {
259
+ manager();
260
+ return triage().list(await runByIdOr404(store, eval_run_id), status);
261
+ });
262
+ tool("get_eval_failure_sort_status", "Report which failure classifiers can run for an eval run: jev (needs a TypeSafe key; reports only the names of the sources that hold one, never the key) and judge (the run's judge model), and which one auto would use. Read-only, free. Same as GET /api/eval-runs/:id/failures/status.", z.object({ eval_run_id: z.string() }), async ({ eval_run_id }) => {
263
+ manager();
264
+ return triage().status(await runByIdOr404(store, eval_run_id));
265
+ });
266
+ tool("cancel_eval_run", "Cancel an eval run in progress and return its detail once it has stopped (waits up to 5 seconds). Stops spending; completed results are kept. A run that already finished is returned unchanged. Same as POST /api/eval-runs/:id/cancel.", z.object({ eval_run_id: z.string() }), async ({ eval_run_id }) => {
267
+ const evalRunManager = manager();
268
+ await runByIdOr404(store, eval_run_id);
269
+ await evalRunManager.cancel(eval_run_id);
270
+ const { run } = await evalRunManager.awaitFinished(eval_run_id, CANCEL_WAIT_MS);
271
+ audit("eval_run_cancel", "ok", eval_run_id);
272
+ return runDetail(run);
273
+ });
274
+ tool("compare_eval_runs", "Compare two eval runs of the same eval: per-grader and per-case differences, regressions and improvements, cost delta. a is the baseline, b the candidate. Read-only, free. Same as GET /api/eval-compare. Fails 400 when the runs are not comparable.", z.object({ a: z.string(), b: z.string() }), async ({ a, b }) => {
275
+ manager();
276
+ const left = await runByIdOr404(store, a);
277
+ const right = await runByIdOr404(store, b);
278
+ return buildCompare(store, left, right);
279
+ });
280
+ }
@@ -49,6 +49,7 @@ function toolDepsFrom(deps) {
49
49
  ...(deps.agentConversationBackend !== undefined
50
50
  ? { agentConversationBackend: deps.agentConversationBackend }
51
51
  : {}),
52
+ ...(deps.evalRunManager !== undefined ? { evalRunManager: deps.evalRunManager } : {}),
52
53
  };
53
54
  }
54
55
  function attachBusNotifications(server, bus) {
package/dist/mcp/tools.js CHANGED
@@ -5,6 +5,7 @@ import { registerControlTools } from "./controlTools.js";
5
5
  import { registerProjectMcpTools } from "./projectMcpTools.js";
6
6
  import { registerPipelineAuthorTools } from "./pipelineAuthorTools.js";
7
7
  import { registerProviderTools } from "./providerTools.js";
8
+ import { registerEvalTools } from "./evalTools.js";
8
9
  import { registerTriggerTools } from "./triggerTools.js";
9
10
  import { textResult } from "./toolResults.js";
10
11
  import { DEFAULT_TIMEOUT_MS, waitRun } from "./waitRun.js";
@@ -19,6 +20,7 @@ export function registerMcpTools(server, deps) {
19
20
  registerProviderTools(server, deps);
20
21
  registerProjectMcpTools(server, deps);
21
22
  registerTriggerTools(server, deps);
23
+ registerEvalTools(server, deps);
22
24
  registerPipelineAuthorTools(server, deps);
23
25
  registerAgentTools(server, deps);
24
26
  server.registerTool("wait_run", {