stageflow 0.40.0 → 0.44.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 (519) hide show
  1. package/README.md +15 -4
  2. package/dist/agent/activity.d.ts +18 -0
  3. package/dist/agent/claudeAdapter.js +7 -0
  4. package/dist/agent/claudeTools.d.ts +27 -18
  5. package/dist/agent/claudeTools.js +54 -15
  6. package/dist/agent/compactionEvents.d.ts +34 -0
  7. package/dist/agent/compactionEvents.js +37 -0
  8. package/dist/agent/openRouterRouting.d.ts +7 -0
  9. package/dist/agent/openRouterRouting.js +23 -0
  10. package/dist/agent/piAdapter.d.ts +6 -0
  11. package/dist/agent/piAdapter.js +128 -27
  12. package/dist/agent/piSettings.d.ts +7 -0
  13. package/dist/agent/piSettings.js +9 -0
  14. package/dist/agent/sandboxPiTools.d.ts +3 -1
  15. package/dist/agent/sandboxPiTools.js +8 -2
  16. package/dist/agent/stageTurnDriver.d.ts +11 -2
  17. package/dist/agent/stageTurnDriver.js +45 -4
  18. package/dist/agent/toolOutputBudget.d.ts +15 -1
  19. package/dist/agent/toolOutputBudget.js +18 -0
  20. package/dist/agent/toolOutputBudgetExtension.d.ts +2 -0
  21. package/dist/agent/toolOutputBudgetExtension.js +40 -3
  22. package/dist/agents/authorChat.d.ts +5 -0
  23. package/dist/agents/authorChat.js +120 -12
  24. package/dist/agents/authorSessionStore.d.ts +38 -0
  25. package/dist/agents/authorSessionStore.js +167 -0
  26. package/dist/agents/chatTurn.d.ts +2 -0
  27. package/dist/agents/chatTurn.js +17 -1
  28. package/dist/agents/errors.d.ts +1 -1
  29. package/dist/agents/errors.js +1 -0
  30. package/dist/agents/evalGate.d.ts +61 -0
  31. package/dist/agents/evalGate.js +103 -0
  32. package/dist/agents/evalSession.d.ts +26 -0
  33. package/dist/agents/evalSession.js +29 -0
  34. package/dist/agents/hostContext.d.ts +4 -1
  35. package/dist/agents/hostContext.js +4 -1
  36. package/dist/agents/runtime/piRuntime.d.ts +6 -2
  37. package/dist/agents/runtime/piRuntime.js +50 -16
  38. package/dist/agents/runtime/types.d.ts +3 -1
  39. package/dist/agents/service.d.ts +27 -2
  40. package/dist/agents/service.js +91 -15
  41. package/dist/agents/sessions.d.ts +8 -0
  42. package/dist/agents/sessions.js +53 -11
  43. package/dist/agents/store.d.ts +14 -3
  44. package/dist/agents/store.js +45 -7
  45. package/dist/agents/toolRisk.d.ts +21 -0
  46. package/dist/agents/toolRisk.js +50 -0
  47. package/dist/cli/doctorCommand.js +26 -0
  48. package/dist/cli/evalCommand.d.ts +20 -0
  49. package/dist/cli/evalCommand.js +720 -0
  50. package/dist/cli/exportAllCommand.js +1 -1
  51. package/dist/cli/hostClient.d.ts +60 -0
  52. package/dist/cli/hostClient.js +71 -0
  53. package/dist/cli/validateOutput.d.ts +2 -2
  54. package/dist/cli/validateOutput.js +2 -2
  55. package/dist/cli.js +10 -0
  56. package/dist/compaction/directives.d.ts +18 -0
  57. package/dist/compaction/directives.js +119 -0
  58. package/dist/compaction/extension.d.ts +20 -0
  59. package/dist/compaction/extension.js +78 -0
  60. package/dist/compaction/mask.d.ts +18 -0
  61. package/dist/compaction/mask.js +155 -0
  62. package/dist/compaction/pin.d.ts +21 -0
  63. package/dist/compaction/pin.js +270 -0
  64. package/dist/compaction/scrub.d.ts +7 -0
  65. package/dist/compaction/scrub.js +26 -0
  66. package/dist/compaction/sessionWiring.d.ts +22 -0
  67. package/dist/compaction/sessionWiring.js +26 -0
  68. package/dist/compaction/summarize.d.ts +79 -0
  69. package/dist/compaction/summarize.js +171 -0
  70. package/dist/compaction/toolPolicy.d.ts +3 -0
  71. package/dist/compaction/toolPolicy.js +33 -0
  72. package/dist/compaction/types.d.ts +45 -0
  73. package/dist/compaction/types.js +7 -0
  74. package/dist/compaction/workingState.d.ts +56 -0
  75. package/dist/compaction/workingState.js +198 -0
  76. package/dist/config/atomicWrite.d.ts +15 -0
  77. package/dist/config/atomicWrite.js +43 -0
  78. package/dist/config/browseCatalog.d.ts +2 -0
  79. package/dist/config/browseCatalog.js +4 -2
  80. package/dist/config/createPipeline.js +2 -0
  81. package/dist/config/createTrigger.js +7 -1
  82. package/dist/config/csv.d.ts +9 -0
  83. package/dist/config/csv.js +81 -0
  84. package/dist/config/datasetAgentCheck.d.ts +18 -0
  85. package/dist/config/datasetAgentCheck.js +109 -0
  86. package/dist/config/datasetCandidates.d.ts +22 -0
  87. package/dist/config/datasetCandidates.js +86 -0
  88. package/dist/config/datasetFromSessions.d.ts +34 -0
  89. package/dist/config/datasetFromSessions.js +105 -0
  90. package/dist/config/datasetHash.d.ts +1 -0
  91. package/dist/config/datasetHash.js +4 -0
  92. package/dist/config/datasetImport.d.ts +21 -0
  93. package/dist/config/datasetImport.js +120 -0
  94. package/dist/config/datasetStageCheck.d.ts +9 -0
  95. package/dist/config/datasetStageCheck.js +17 -0
  96. package/dist/config/datasetTypes.d.ts +27 -0
  97. package/dist/config/datasetTypes.js +1 -0
  98. package/dist/config/ensureCatalogScanRoot.d.ts +4 -1
  99. package/dist/config/ensureCatalogScanRoot.js +8 -6
  100. package/dist/config/evalCatalog.d.ts +22 -0
  101. package/dist/config/evalCatalog.js +95 -0
  102. package/dist/config/evalWriteRoots.d.ts +17 -0
  103. package/dist/config/evalWriteRoots.js +109 -0
  104. package/dist/config/githubTriggerAction.d.ts +8 -0
  105. package/dist/config/githubTriggerAction.js +29 -0
  106. package/dist/config/hostConfig.js +3 -0
  107. package/dist/config/installEvalSample.d.ts +34 -0
  108. package/dist/config/installEvalSample.js +182 -0
  109. package/dist/config/loadDataset.d.ts +16 -0
  110. package/dist/config/loadDataset.js +318 -0
  111. package/dist/config/loadEval.d.ts +32 -0
  112. package/dist/config/loadEval.js +1103 -0
  113. package/dist/config/loadOutcome.d.ts +5 -0
  114. package/dist/config/loadStageflowManifest.js +64 -0
  115. package/dist/config/pipelineNeeds.d.ts +2 -0
  116. package/dist/config/pipelineNeeds.js +16 -3
  117. package/dist/config/pipelineRoute.js +5 -4
  118. package/dist/config/resolveAgentTarget.d.ts +32 -0
  119. package/dist/config/resolveAgentTarget.js +118 -0
  120. package/dist/config/resolveEvalTarget.d.ts +21 -0
  121. package/dist/config/resolveEvalTarget.js +89 -0
  122. package/dist/config/scanCatalogPaths.d.ts +1 -1
  123. package/dist/config/scanCatalogPaths.js +17 -5
  124. package/dist/config/validateCatalog.d.ts +2 -2
  125. package/dist/config/validateCatalog.js +21 -1
  126. package/dist/config/validateEval.d.ts +36 -0
  127. package/dist/config/validateEval.js +446 -0
  128. package/dist/config/writeDataset.d.ts +54 -0
  129. package/dist/config/writeDataset.js +186 -0
  130. package/dist/config/writeEval.d.ts +25 -0
  131. package/dist/config/writeEval.js +398 -0
  132. package/dist/diagnostics/checks.js +1 -1
  133. package/dist/envelope/assertEmittedArtifacts.d.ts +12 -0
  134. package/dist/envelope/assertEmittedArtifacts.js +43 -0
  135. package/dist/envelope/preEmitChecks.d.ts +3 -0
  136. package/dist/evals/agentDiff.d.ts +13 -0
  137. package/dist/evals/agentDiff.js +11 -0
  138. package/dist/evals/canonical.d.ts +3 -0
  139. package/dist/evals/canonical.js +24 -0
  140. package/dist/evals/caseTask.d.ts +3 -0
  141. package/dist/evals/caseTask.js +9 -0
  142. package/dist/evals/compare.d.ts +94 -0
  143. package/dist/evals/compare.js +201 -0
  144. package/dist/evals/constants.d.ts +62 -0
  145. package/dist/evals/constants.js +97 -0
  146. package/dist/evals/engine/FakeEngine.d.ts +33 -0
  147. package/dist/evals/engine/FakeEngine.js +177 -0
  148. package/dist/evals/engine/index.d.ts +9 -0
  149. package/dist/evals/engine/index.js +19 -0
  150. package/dist/evals/engine/promptfoo/PromptfooEngine.d.ts +52 -0
  151. package/dist/evals/engine/promptfoo/PromptfooEngine.js +312 -0
  152. package/dist/evals/engine/promptfoo/errors.d.ts +5 -0
  153. package/dist/evals/engine/promptfoo/errors.js +8 -0
  154. package/dist/evals/engine/promptfoo/generate.d.ts +30 -0
  155. package/dist/evals/engine/promptfoo/generate.js +345 -0
  156. package/dist/evals/engine/promptfoo/guard.d.ts +10 -0
  157. package/dist/evals/engine/promptfoo/guard.js +116 -0
  158. package/dist/evals/engine/promptfoo/install.d.ts +17 -0
  159. package/dist/evals/engine/promptfoo/install.js +73 -0
  160. package/dist/evals/engine/promptfoo/judgeProviders.d.ts +2 -0
  161. package/dist/evals/engine/promptfoo/judgeProviders.js +9 -0
  162. package/dist/evals/engine/promptfoo/judgeServices.d.ts +6 -0
  163. package/dist/evals/engine/promptfoo/judgeServices.js +41 -0
  164. package/dist/evals/engine/promptfoo/locate.d.ts +21 -0
  165. package/dist/evals/engine/promptfoo/locate.js +68 -0
  166. package/dist/evals/engine/promptfoo/nodeVersion.d.ts +7 -0
  167. package/dist/evals/engine/promptfoo/nodeVersion.js +14 -0
  168. package/dist/evals/engine/promptfoo/normalize.d.ts +66 -0
  169. package/dist/evals/engine/promptfoo/normalize.js +261 -0
  170. package/dist/evals/engine/promptfoo/pinned.d.ts +5 -0
  171. package/dist/evals/engine/promptfoo/pinned.js +5 -0
  172. package/dist/evals/engine/promptfoo/rubricPrompt.d.ts +14 -0
  173. package/dist/evals/engine/promptfoo/rubricPrompt.js +105 -0
  174. package/dist/evals/engine/promptfoo/runTokens.d.ts +18 -0
  175. package/dist/evals/engine/promptfoo/runTokens.js +31 -0
  176. package/dist/evals/engine/promptfoo/status.d.ts +13 -0
  177. package/dist/evals/engine/promptfoo/status.js +16 -0
  178. package/dist/evals/estimate.d.ts +56 -0
  179. package/dist/evals/estimate.js +66 -0
  180. package/dist/evals/graders/codeGrader.d.ts +6 -0
  181. package/dist/evals/graders/codeGrader.js +85 -0
  182. package/dist/evals/graders/conversationCheckGrader.d.ts +6 -0
  183. package/dist/evals/graders/conversationCheckGrader.js +62 -0
  184. package/dist/evals/graders/fieldGrader.d.ts +11 -0
  185. package/dist/evals/graders/fieldGrader.js +117 -0
  186. package/dist/evals/graders/goal.d.ts +3 -0
  187. package/dist/evals/graders/goal.js +14 -0
  188. package/dist/evals/graders/index.d.ts +18 -0
  189. package/dist/evals/graders/index.js +34 -0
  190. package/dist/evals/graders/notApplicable.d.ts +5 -0
  191. package/dist/evals/graders/notApplicable.js +9 -0
  192. package/dist/evals/graders/path.d.ts +26 -0
  193. package/dist/evals/graders/path.js +75 -0
  194. package/dist/evals/graders/referenceGrader.d.ts +5 -0
  195. package/dist/evals/graders/referenceGrader.js +59 -0
  196. package/dist/evals/graders/schemaGrader.d.ts +5 -0
  197. package/dist/evals/graders/schemaGrader.js +29 -0
  198. package/dist/evals/graders/subject.d.ts +6 -0
  199. package/dist/evals/graders/subject.js +33 -0
  200. package/dist/evals/graders/toolCallsGrader.d.ts +6 -0
  201. package/dist/evals/graders/toolCallsGrader.js +100 -0
  202. package/dist/evals/graders/transcript.d.ts +12 -0
  203. package/dist/evals/graders/transcript.js +57 -0
  204. package/dist/evals/graders/types.d.ts +20 -0
  205. package/dist/evals/graders/types.js +1 -0
  206. package/dist/evals/judgeProviders.d.ts +16 -0
  207. package/dist/evals/judgeProviders.js +27 -0
  208. package/dist/evals/lineDiff.d.ts +5 -0
  209. package/dist/evals/lineDiff.js +34 -0
  210. package/dist/evals/listStatus.d.ts +31 -0
  211. package/dist/evals/listStatus.js +48 -0
  212. package/dist/evals/manager.d.ts +187 -0
  213. package/dist/evals/manager.js +795 -0
  214. package/dist/evals/plan.d.ts +65 -0
  215. package/dist/evals/plan.js +621 -0
  216. package/dist/evals/planInternalAgent.d.ts +19 -0
  217. package/dist/evals/planInternalAgent.js +205 -0
  218. package/dist/evals/providerKeys.d.ts +1 -0
  219. package/dist/evals/providerKeys.js +21 -0
  220. package/dist/evals/read/agentTargets.d.ts +37 -0
  221. package/dist/evals/read/agentTargets.js +101 -0
  222. package/dist/evals/read/api.d.ts +208 -0
  223. package/dist/evals/read/api.js +363 -0
  224. package/dist/evals/read/compare.d.ts +4 -0
  225. package/dist/evals/read/compare.js +39 -0
  226. package/dist/evals/read/datasets.d.ts +105 -0
  227. package/dist/evals/read/datasets.js +318 -0
  228. package/dist/evals/read/list.d.ts +66 -0
  229. package/dist/evals/read/list.js +182 -0
  230. package/dist/evals/read/results.d.ts +46 -0
  231. package/dist/evals/read/results.js +107 -0
  232. package/dist/evals/read/runs.d.ts +154 -0
  233. package/dist/evals/read/runs.js +295 -0
  234. package/dist/evals/read/suggestions.d.ts +43 -0
  235. package/dist/evals/read/suggestions.js +73 -0
  236. package/dist/evals/runner/FakeAgentBackend.d.ts +40 -0
  237. package/dist/evals/runner/FakeAgentBackend.js +82 -0
  238. package/dist/evals/runner/agentCaseRunner.d.ts +24 -0
  239. package/dist/evals/runner/agentCaseRunner.js +388 -0
  240. package/dist/evals/runner/caseRunner.d.ts +8 -0
  241. package/dist/evals/runner/caseRunner.js +28 -0
  242. package/dist/evals/runner/conversationSubject.d.ts +18 -0
  243. package/dist/evals/runner/conversationSubject.js +123 -0
  244. package/dist/evals/runner/internalAgentCaseRunner.d.ts +6 -0
  245. package/dist/evals/runner/internalAgentCaseRunner.js +250 -0
  246. package/dist/evals/runner/simulatorModel.d.ts +51 -0
  247. package/dist/evals/runner/simulatorModel.js +58 -0
  248. package/dist/evals/runner/simulatorPrompt.d.ts +29 -0
  249. package/dist/evals/runner/simulatorPrompt.js +59 -0
  250. package/dist/evals/runner/stageCaseRunner.d.ts +30 -0
  251. package/dist/evals/runner/stageCaseRunner.js +279 -0
  252. package/dist/evals/runner/userDriver.d.ts +71 -0
  253. package/dist/evals/runner/userDriver.js +104 -0
  254. package/dist/evals/scoring.d.ts +34 -0
  255. package/dist/evals/scoring.js +67 -0
  256. package/dist/evals/snapshot.d.ts +191 -0
  257. package/dist/evals/snapshot.js +70 -0
  258. package/dist/evals/stageDiff.d.ts +8 -0
  259. package/dist/evals/stageDiff.js +48 -0
  260. package/dist/evals/stats.d.ts +95 -0
  261. package/dist/evals/stats.js +220 -0
  262. package/dist/evals/statsAdapter.d.ts +4 -0
  263. package/dist/evals/statsAdapter.js +30 -0
  264. package/dist/evals/statsTypes.d.ts +51 -0
  265. package/dist/evals/statsTypes.js +18 -0
  266. package/dist/evals/storeError.d.ts +5 -0
  267. package/dist/evals/storeError.js +8 -0
  268. package/dist/evals/storeTypes.d.ts +207 -0
  269. package/dist/evals/storeTypes.js +1 -0
  270. package/dist/evals/tTable.d.ts +3 -0
  271. package/dist/evals/tTable.js +15 -0
  272. package/dist/evals/triage/constants.d.ts +26 -0
  273. package/dist/evals/triage/constants.js +33 -0
  274. package/dist/evals/triage/credentials.d.ts +13 -0
  275. package/dist/evals/triage/credentials.js +20 -0
  276. package/dist/evals/triage/jevClassifier.d.ts +14 -0
  277. package/dist/evals/triage/jevClassifier.js +124 -0
  278. package/dist/evals/triage/judgeClassifier.d.ts +10 -0
  279. package/dist/evals/triage/judgeClassifier.js +76 -0
  280. package/dist/evals/triage/prompts.d.ts +16 -0
  281. package/dist/evals/triage/prompts.js +64 -0
  282. package/dist/evals/triage/service.d.ts +79 -0
  283. package/dist/evals/triage/service.js +217 -0
  284. package/dist/evals/triage/stateBuilder.d.ts +19 -0
  285. package/dist/evals/triage/stateBuilder.js +125 -0
  286. package/dist/evals/triage/types.d.ts +60 -0
  287. package/dist/evals/triage/types.js +8 -0
  288. package/dist/evals/types.d.ts +357 -0
  289. package/dist/evals/types.js +4 -0
  290. package/dist/guide/cardContract.d.ts +133 -0
  291. package/dist/guide/cardContract.js +156 -0
  292. package/dist/guide/chat.d.ts +45 -0
  293. package/dist/guide/chat.js +378 -0
  294. package/dist/guide/knowledge/index.d.ts +14 -0
  295. package/dist/guide/knowledge/index.js +109 -0
  296. package/dist/guide/knowledge/topics/agents.d.ts +2 -0
  297. package/dist/guide/knowledge/topics/agents.js +58 -0
  298. package/dist/guide/knowledge/topics/bestPractices.d.ts +2 -0
  299. package/dist/guide/knowledge/topics/bestPractices.js +67 -0
  300. package/dist/guide/knowledge/topics/connections.d.ts +2 -0
  301. package/dist/guide/knowledge/topics/connections.js +66 -0
  302. package/dist/guide/knowledge/topics/consoleMap.d.ts +2 -0
  303. package/dist/guide/knowledge/topics/consoleMap.js +55 -0
  304. package/dist/guide/knowledge/topics/evals.d.ts +2 -0
  305. package/dist/guide/knowledge/topics/evals.js +72 -0
  306. package/dist/guide/knowledge/topics/limitations.d.ts +2 -0
  307. package/dist/guide/knowledge/topics/limitations.js +84 -0
  308. package/dist/guide/knowledge/topics/mcpSkillsBrowser.d.ts +2 -0
  309. package/dist/guide/knowledge/topics/mcpSkillsBrowser.js +57 -0
  310. package/dist/guide/knowledge/topics/memories.d.ts +2 -0
  311. package/dist/guide/knowledge/topics/memories.js +63 -0
  312. package/dist/guide/knowledge/topics/modelsAndProviders.d.ts +2 -0
  313. package/dist/guide/knowledge/topics/modelsAndProviders.js +56 -0
  314. package/dist/guide/knowledge/topics/overview.d.ts +2 -0
  315. package/dist/guide/knowledge/topics/overview.js +56 -0
  316. package/dist/guide/knowledge/topics/pipelines.d.ts +2 -0
  317. package/dist/guide/knowledge/topics/pipelines.js +93 -0
  318. package/dist/guide/knowledge/topics/stages.d.ts +2 -0
  319. package/dist/guide/knowledge/topics/stages.js +54 -0
  320. package/dist/guide/knowledge/topics/tasks.d.ts +2 -0
  321. package/dist/guide/knowledge/topics/tasks.js +52 -0
  322. package/dist/guide/knowledge/topics/triggers.d.ts +2 -0
  323. package/dist/guide/knowledge/topics/triggers.js +68 -0
  324. package/dist/guide/knowledge/topics/troubleshooting.d.ts +2 -0
  325. package/dist/guide/knowledge/topics/troubleshooting.js +85 -0
  326. package/dist/guide/knowledgeTools.d.ts +2 -0
  327. package/dist/guide/knowledgeTools.js +56 -0
  328. package/dist/guide/showUi.d.ts +6 -0
  329. package/dist/guide/showUi.js +546 -0
  330. package/dist/guide/threadStore.d.ts +48 -0
  331. package/dist/guide/threadStore.js +189 -0
  332. package/dist/guide/writeRoot.d.ts +11 -0
  333. package/dist/guide/writeRoot.js +70 -0
  334. package/dist/hosted/translate.js +2 -0
  335. package/dist/mcp/agentTools.js +12 -0
  336. package/dist/mcp/catalogTools.js +18 -20
  337. package/dist/mcp/catalogWriteTools.d.ts +8 -0
  338. package/dist/mcp/catalogWriteTools.js +225 -0
  339. package/dist/mcp/connectionTools.d.ts +3 -0
  340. package/dist/mcp/connectionTools.js +85 -0
  341. package/dist/mcp/controlTools.js +34 -1
  342. package/dist/mcp/deps.d.ts +16 -0
  343. package/dist/mcp/evalTools.d.ts +3 -0
  344. package/dist/mcp/evalTools.js +943 -0
  345. package/dist/mcp/memoryHostTools.d.ts +3 -0
  346. package/dist/mcp/memoryHostTools.js +72 -0
  347. package/dist/mcp/pipelineAuthorTools.js +14 -1
  348. package/dist/mcp/server.js +6 -0
  349. package/dist/mcp/tools.js +8 -0
  350. package/dist/mcp/triggerTools.js +77 -31
  351. package/dist/models/response.d.ts +7 -2
  352. package/dist/models/response.js +6 -1
  353. package/dist/operatorAgent/agentAuthorContext.d.ts +1 -1
  354. package/dist/operatorAgent/agentAuthorContext.js +1 -1
  355. package/dist/operatorAgent/evalGate.d.ts +22 -0
  356. package/dist/operatorAgent/evalGate.js +77 -0
  357. package/dist/operatorAgent/fakeHost.js +10 -2
  358. package/dist/operatorAgent/host.js +18 -2
  359. package/dist/operatorAgent/index.d.ts +1 -0
  360. package/dist/operatorAgent/index.js +1 -0
  361. package/dist/operatorAgent/internalAgents.d.ts +11 -0
  362. package/dist/operatorAgent/internalAgents.js +13 -0
  363. package/dist/operatorAgent/piModel.d.ts +5 -1
  364. package/dist/operatorAgent/piModel.js +32 -14
  365. package/dist/operatorAgent/profiles/agentAuthor.d.ts +3 -1
  366. package/dist/operatorAgent/profiles/agentAuthor.js +201 -32
  367. package/dist/operatorAgent/profiles/agentAuthorPlaybook.d.ts +1 -1
  368. package/dist/operatorAgent/profiles/agentAuthorPlaybook.js +48 -15
  369. package/dist/operatorAgent/profiles/authorReadable.d.ts +2 -0
  370. package/dist/operatorAgent/profiles/authorReadable.js +26 -0
  371. package/dist/operatorAgent/profiles/guideConfirm.d.ts +21 -0
  372. package/dist/operatorAgent/profiles/guideConfirm.js +100 -0
  373. package/dist/operatorAgent/profiles/guideTools.d.ts +7 -0
  374. package/dist/operatorAgent/profiles/guideTools.js +123 -0
  375. package/dist/operatorAgent/profiles/stageflowGuide.d.ts +7 -0
  376. package/dist/operatorAgent/profiles/stageflowGuide.js +132 -0
  377. package/dist/operatorAgent/profiles/workshopAuthor.js +23 -0
  378. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +1 -1
  379. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +9 -1
  380. package/dist/operatorAgent/profiles/workshopAuthorTools.d.ts +2 -0
  381. package/dist/operatorAgent/profiles/workshopAuthorTools.js +197 -9
  382. package/dist/operatorAgent/types.d.ts +23 -0
  383. package/dist/package-meta.d.ts +1 -1
  384. package/dist/package-meta.js +1 -1
  385. package/dist/runstore/port.d.ts +43 -0
  386. package/dist/runstore/retention.d.ts +2 -1
  387. package/dist/runstore/retention.js +3 -7
  388. package/dist/runstore/runStatusClock.d.ts +17 -0
  389. package/dist/runstore/runStatusClock.js +19 -0
  390. package/dist/runstore/sqlite/SqliteRunStore.d.ts +29 -0
  391. package/dist/runstore/sqlite/SqliteRunStore.js +144 -50
  392. package/dist/runstore/sqlite/evalStore.d.ts +20 -0
  393. package/dist/runstore/sqlite/evalStore.js +541 -0
  394. package/dist/runstore/sqlite/migrations/015-evals.d.ts +12 -0
  395. package/dist/runstore/sqlite/migrations/015-evals.js +157 -0
  396. package/dist/runstore/sqlite/migrations/016-eval-subject-ref.d.ts +12 -0
  397. package/dist/runstore/sqlite/migrations/016-eval-subject-ref.js +20 -0
  398. package/dist/runstore/sqlite/migrations/017-eval-result-suggestions.d.ts +11 -0
  399. package/dist/runstore/sqlite/migrations/017-eval-result-suggestions.js +36 -0
  400. package/dist/runstore/sqlite/migrations/index.js +6 -0
  401. package/dist/runstore/sqlite/suggestionStore.d.ts +14 -0
  402. package/dist/runstore/sqlite/suggestionStore.js +132 -0
  403. package/dist/runstore/workspaceLayout.js +6 -1
  404. package/dist/runtime/artifactDiskInspect.d.ts +14 -0
  405. package/dist/runtime/artifactDiskInspect.js +52 -0
  406. package/dist/runtime/artifactPath.d.ts +24 -0
  407. package/dist/runtime/artifactPath.js +84 -0
  408. package/dist/runtime/completionCheckRunner.js +15 -43
  409. package/dist/runtime/githubPollSource.d.ts +22 -4
  410. package/dist/runtime/githubPollSource.js +93 -8
  411. package/dist/runtime/pipelineRunner.d.ts +2 -1
  412. package/dist/runtime/pipelineRunner.js +2 -0
  413. package/dist/runtime/priorArtifactGrants.d.ts +8 -0
  414. package/dist/runtime/priorArtifactGrants.js +105 -0
  415. package/dist/runtime/runActionEligibility.d.ts +46 -0
  416. package/dist/runtime/runActionEligibility.js +123 -0
  417. package/dist/runtime/runManager.d.ts +6 -2
  418. package/dist/runtime/runManager.js +60 -16
  419. package/dist/runtime/runRetentionSweep.js +2 -2
  420. package/dist/runtime/runRetryCoordinator.d.ts +2 -14
  421. package/dist/runtime/runRetryCoordinator.js +3 -67
  422. package/dist/runtime/sandboxContainer.d.ts +7 -0
  423. package/dist/runtime/sandboxContainer.js +48 -6
  424. package/dist/runtime/sandboxDesk.d.ts +2 -1
  425. package/dist/runtime/sandboxDesk.js +11 -12
  426. package/dist/runtime/stageEnvironment.js +1 -0
  427. package/dist/runtime/stageRoots.d.ts +12 -0
  428. package/dist/runtime/stageSandbox.d.ts +2 -1
  429. package/dist/runtime/stageSandbox.js +18 -2
  430. package/dist/runtime/stageWorker.js +20 -0
  431. package/dist/server/agentAuthorRoutes.js +59 -1
  432. package/dist/server/bootstrap.d.ts +13 -0
  433. package/dist/server/bootstrap.js +69 -0
  434. package/dist/server/controlToken.js +3 -0
  435. package/dist/server/evalInternalRoutes.d.ts +14 -0
  436. package/dist/server/evalInternalRoutes.js +87 -0
  437. package/dist/server/evalRouteContext.d.ts +46 -0
  438. package/dist/server/evalRouteContext.js +43 -0
  439. package/dist/server/evalRoutes.d.ts +5 -0
  440. package/dist/server/evalRoutes.js +1456 -0
  441. package/dist/server/evalTriageRoutes.d.ts +2 -0
  442. package/dist/server/evalTriageRoutes.js +95 -0
  443. package/dist/server/guideRoutes.d.ts +5 -0
  444. package/dist/server/guideRoutes.js +166 -0
  445. package/dist/server/http.d.ts +15 -0
  446. package/dist/server/http.js +67 -3
  447. package/dist/server/internalAgentRoutes.d.ts +6 -0
  448. package/dist/server/internalAgentRoutes.js +12 -0
  449. package/dist/server/mcpHost.js +5 -0
  450. package/dist/toolkit/meta.d.ts +1 -1
  451. package/dist/toolkit/meta.js +53 -0
  452. package/dist/toolkit/profiles.d.ts +5 -0
  453. package/dist/toolkit/profiles.js +13 -1
  454. package/dist/tools/askOperator.js +2 -1
  455. package/dist/tools/emitStageEnvelope.d.ts +17 -16
  456. package/dist/tools/emitStageEnvelope.js +46 -32
  457. package/dist/tools/stageToolNames.d.ts +4 -0
  458. package/dist/tools/stageToolNames.js +4 -0
  459. package/dist/tools/workingState.d.ts +40 -0
  460. package/dist/tools/workingState.js +108 -0
  461. package/dist/tools/writeStageArtifact.d.ts +0 -9
  462. package/dist/tools/writeStageArtifact.js +2 -1
  463. package/dist/types/stageflowManifest.d.ts +6 -0
  464. package/dist/ui/assets/{MarkdownText-D7tYaIFO.js → MarkdownText-S5C_vemc.js} +2 -2
  465. package/dist/ui/assets/adapters-C0l4ruiR.js +17 -0
  466. package/dist/ui/assets/chat-CjnsmvqR.js +2 -0
  467. package/dist/ui/assets/chat-DFDd4UjS.css +2 -0
  468. package/dist/ui/assets/{main-BFo79krC.css → main-Del1GeLf.css} +1 -1
  469. package/dist/ui/assets/main-_yXVqcW9.js +277 -0
  470. package/dist/ui/chat.html +3 -4
  471. package/dist/ui/index.html +3 -4
  472. package/dist/workshop/chatTurn.js +1 -1
  473. package/dist/workshop/workshopPin.d.ts +3 -3
  474. package/dist/workshop/workshopPin.js +16 -15
  475. package/examples/README.md +2 -0
  476. package/examples/compaction-stress/README.md +30 -0
  477. package/examples/compaction-stress/check-run.mjs +53 -0
  478. package/examples/compaction-stress/collect-evidence.yaml +60 -0
  479. package/examples/compaction-stress/compaction-stress.pipeline.yaml +141 -0
  480. package/examples/compaction-stress/compaction-stress.task.yaml +15 -0
  481. package/examples/compaction-stress/generate-evidence.mjs +110 -0
  482. package/examples/compaction-stress/root-cause.yaml +26 -0
  483. package/examples/compaction-stress/stageflow.yaml +9 -0
  484. package/examples/compaction-stress/write-postmortem.yaml +40 -0
  485. package/examples/document-policy-review/README.md +35 -0
  486. package/examples/document-policy-review/agents/compliance.agent.yaml +42 -0
  487. package/examples/document-policy-review/documents/acme-vendor-sow.md +15 -0
  488. package/examples/document-policy-review/documents/support-runbook.md +13 -0
  489. package/examples/document-policy-review/policies/acceptable-use.md +13 -0
  490. package/examples/document-policy-review/policies/data-handling.md +17 -0
  491. package/examples/document-policy-review/policies/vendor-security.md +17 -0
  492. package/examples/evals/README.md +141 -0
  493. package/examples/evals/critique-sample.dataset.jsonl +8 -0
  494. package/examples/evals/critique-sample.eval.yaml +34 -0
  495. package/examples/evals/critique-sample.task.yaml +4 -0
  496. package/examples/evals/eval-sample.pipeline.yaml +35 -0
  497. package/examples/evals/guide-behavior.dataset.jsonl +15 -0
  498. package/examples/evals/guide-behavior.eval.yaml +35 -0
  499. package/examples/evals/guide-depth.dataset.jsonl +35 -0
  500. package/examples/evals/guide-depth.eval.yaml +169 -0
  501. package/examples/evals/support-sample.agent.yaml +20 -0
  502. package/examples/evals/support-sample.dataset.jsonl +6 -0
  503. package/examples/evals/support-sample.eval.yaml +38 -0
  504. package/examples/memory/evals/guide-behavior.dataset.jsonl +15 -0
  505. package/examples/memory/evals/guide-behavior.eval.yaml +35 -0
  506. package/examples/memory/evals/guide-depth.dataset.jsonl +35 -0
  507. package/examples/memory/evals/guide-depth.eval.yaml +169 -0
  508. package/examples/stageflow.yaml +6 -0
  509. package/package.json +1 -1
  510. package/skills/stageflow/SKILL.md +5 -2
  511. package/skills/stageflow/references/control-surface.md +4 -2
  512. package/skills/stageflow/references/evals.md +68 -0
  513. package/skills/stageflow-author/SKILL.md +4 -0
  514. package/skills/stageflow-run/SKILL.md +4 -0
  515. package/dist/ui/assets/AssistantRuntimeProvider-CsiPFhCT.js +0 -9
  516. package/dist/ui/assets/ReadonlyStore-BfXqHykm.js +0 -6
  517. package/dist/ui/assets/chat-BbLukq9W.js +0 -5
  518. package/dist/ui/assets/chat-zIZU_bP1.css +0 -2
  519. package/dist/ui/assets/main-CijYMeM0.js +0 -259
@@ -0,0 +1,943 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { z } from "zod";
4
+ import { EvalsWriteError, FileChangedError, contentHash } from "../config/atomicWrite.js";
5
+ import { CatalogPathError, catalogPathErrorBody, resolveCatalogStartInput, resolveWritableCatalogRoot, } from "../config/catalogRelativePath.js";
6
+ import { formatId, nextIdNumber } from "../config/datasetImport.js";
7
+ import { casesFromRuns } from "../config/datasetCandidates.js";
8
+ import { casesFromSessions, SessionCaseError } from "../config/datasetFromSessions.js";
9
+ import { checkDatasetAgainstStage } from "../config/datasetStageCheck.js";
10
+ import { listEvalFiles } from "../config/evalCatalog.js";
11
+ import { assertInsideProject, findEvalFile, manifestForWrite, resolveWriteDir, } from "../config/evalWriteRoots.js";
12
+ import { EVAL_SAMPLE_FILES, installEvalSample } from "../config/installEvalSample.js";
13
+ import { loadDatasetFile, parseDatasetText } from "../config/loadDataset.js";
14
+ import { loadEvalOutcome, resolveConversation, resolveEvalPaths } from "../config/loadEval.js";
15
+ import { resolveEvalTarget } from "../config/resolveEvalTarget.js";
16
+ import { resolveSeededExamplesPath } from "../config/seededCatalog.js";
17
+ import { appendOrReplaceCases, applyCaseOps, convertDatasetToJsonl, createDatasetFile, } from "../config/writeDataset.js";
18
+ import { createEvalFile, previewEvalUpdate, renderEvalCreateYaml, updateEvalFile } from "../config/writeEval.js";
19
+ import { CompareMismatchError } from "../evals/compare.js";
20
+ import { EVAL_LIMITS, EVAL_REVIEW_LABELS, EVAL_RUN_STATUSES } from "../evals/constants.js";
21
+ import { engineStatus as promptfooEngineStatus } from "../evals/engine/promptfoo/status.js";
22
+ import { EvalRunError } from "../evals/manager.js";
23
+ import { buildEvalDetail, buildResultDetail, buildResultsPage, DEFAULT_LIMIT, engineCheck, EvalApiError, locateDataset, locateEval, loadAggregatedEvalList, listEvalRunsPage, MAX_LIMIT, planFindings, runByIdOr404, validateEvalText, } from "../evals/read/api.js";
24
+ import { buildCompare } from "../evals/read/compare.js";
25
+ import { buildDatasetDetail, resolveDatasetTarget, } from "../evals/read/datasets.js";
26
+ import { buildExport, buildRunDetail } from "../evals/read/runs.js";
27
+ import { EvalStoreError } from "../evals/storeError.js";
28
+ import { SUGGESTION_STATUSES, TRIAGE_BUCKETS, TRIAGE_MAX_LIMIT } from "../evals/triage/constants.js";
29
+ import { isTriageBucket, TriageService } from "../evals/triage/service.js";
30
+ import { writeAudit } from "../logging/audit.js";
31
+ import { logger as rootLogger } from "../logging/logger.js";
32
+ import { globalStageflowHome } from "../project/globalHome.js";
33
+ import { callerIdFromRequestAuth, getRequestAuth } from "../server/requestAuthContext.js";
34
+ import { textResult } from "./toolResults.js";
35
+ const auditLog = rootLogger.child({ component: "audit" });
36
+ const CANCEL_WAIT_MS = 5000;
37
+ const DEFAULT_WAIT_MS = 600_000;
38
+ const MAX_WAIT_MS = 1_800_000;
39
+ const ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
40
+ const CASE_OPS = new Set(["add", "update", "delete", "set_tags"]);
41
+ const datasetTargetObject = z
42
+ .object({
43
+ pipeline_path: z.string().optional(),
44
+ stage_id: z.string().optional(),
45
+ file: z.string().optional(),
46
+ agent_id: z.string().optional(),
47
+ })
48
+ .describe("Stage (pipeline_path + stage_id, or file) or agent_id the imported cases must match");
49
+ const datasetTargetField = datasetTargetObject.optional();
50
+ const projectRootField = z
51
+ .string()
52
+ .optional()
53
+ .describe("Catalog project root; omit to use the host default");
54
+ const evalPathField = z
55
+ .string()
56
+ .optional()
57
+ .describe("Eval path relative to the project, used to resolve dataset and target paths");
58
+ const datasetCasesField = z
59
+ .array(z.unknown())
60
+ .optional()
61
+ .describe("Inline cases used instead of the dataset file");
62
+ const validateEvalSchema = z.union([
63
+ z.strictObject({
64
+ eval_id: z.string(),
65
+ path: evalPathField,
66
+ dataset_cases: datasetCasesField,
67
+ project_root: projectRootField,
68
+ }),
69
+ z.strictObject({
70
+ yaml: z.string(),
71
+ path: evalPathField,
72
+ dataset_cases: datasetCasesField,
73
+ project_root: projectRootField,
74
+ }),
75
+ ]);
76
+ const definitionField = z.record(z.string(), z.unknown());
77
+ const updateEvalSchema = z.union([
78
+ z.strictObject({
79
+ eval_id: z.string(),
80
+ base_hash: z.string().min(1),
81
+ definition: definitionField,
82
+ project_root: projectRootField,
83
+ }),
84
+ z.strictObject({
85
+ eval_id: z.string(),
86
+ base_hash: z.string().min(1),
87
+ yaml: z.string(),
88
+ project_root: projectRootField,
89
+ }),
90
+ ]);
91
+ const caseRecord = z.record(z.string(), z.unknown());
92
+ const caseOpSchema = z.discriminatedUnion("op", [
93
+ z.strictObject({ op: z.literal("add"), case: caseRecord }),
94
+ z.strictObject({ op: z.literal("update"), id: z.string(), case: caseRecord }),
95
+ z.strictObject({ op: z.literal("delete"), id: z.string() }),
96
+ z.strictObject({ op: z.literal("set_tags"), id: z.string(), tags: z.array(z.string()) }),
97
+ ]);
98
+ const datasetDirectoryField = z
99
+ .string()
100
+ .optional()
101
+ .describe("Catalog directory for a new dataset, relative to the project");
102
+ const datasetCreateShared = {
103
+ directory: datasetDirectoryField,
104
+ project_root: projectRootField,
105
+ };
106
+ const createDatasetSchema = z.union([
107
+ z.strictObject({
108
+ dataset_id: z.string().min(1),
109
+ mode: z.literal("append"),
110
+ base_hash: z.string().min(1),
111
+ source: z.literal("runs"),
112
+ target: datasetTargetObject,
113
+ run_ids: z.array(z.string()).min(1),
114
+ project_root: projectRootField,
115
+ }),
116
+ z.strictObject({
117
+ dataset_id: z.string().min(1),
118
+ mode: z.literal("append"),
119
+ base_hash: z.string().min(1),
120
+ source: z.literal("sessions"),
121
+ target: datasetTargetObject,
122
+ session_ids: z.array(z.string()).min(1),
123
+ project_root: projectRootField,
124
+ }),
125
+ z.strictObject({
126
+ dataset_id: z.string().min(1),
127
+ mode: z.enum(["append", "replace"]),
128
+ base_hash: z.string().min(1),
129
+ cases: z.array(caseRecord),
130
+ project_root: projectRootField,
131
+ }),
132
+ z.strictObject({
133
+ name: z.string().min(1),
134
+ source: z.literal("sample"),
135
+ target: datasetTargetObject,
136
+ ...datasetCreateShared,
137
+ }),
138
+ z.strictObject({
139
+ name: z.string().min(1),
140
+ source: z.literal("runs"),
141
+ target: datasetTargetObject,
142
+ run_ids: z.array(z.string()).min(1),
143
+ ...datasetCreateShared,
144
+ }),
145
+ z.strictObject({
146
+ name: z.string().min(1),
147
+ source: z.literal("sessions"),
148
+ target: datasetTargetObject,
149
+ session_ids: z.array(z.string()).min(1),
150
+ ...datasetCreateShared,
151
+ }),
152
+ z.strictObject({
153
+ name: z.string().min(1),
154
+ cases: z.array(caseRecord),
155
+ target: datasetTargetField,
156
+ ...datasetCreateShared,
157
+ }),
158
+ ]);
159
+ const tryShared = {
160
+ dataset_cases: datasetCasesField,
161
+ allow_real_tools: z.boolean().optional().describe("Agent evals: consent to conversation.tools real; no effect on other policies"),
162
+ project_root: projectRootField,
163
+ };
164
+ const tryCaseField = caseRecord.describe("Inline case; must include an input object");
165
+ const tryEvalCaseSchema = z.union([
166
+ z.strictObject({ eval_id: z.string(), case_id: z.string(), ...tryShared }),
167
+ z.strictObject({ eval_id: z.string(), case: tryCaseField, ...tryShared }),
168
+ z.strictObject({ definition: definitionField, case_id: z.string(), ...tryShared }),
169
+ z.strictObject({ definition: definitionField, case: tryCaseField, ...tryShared }),
170
+ ]);
171
+ function audit(action, outcome, target, code) {
172
+ writeAudit(auditLog, {
173
+ caller_id: callerIdFromRequestAuth(),
174
+ surface: getRequestAuth()?.surface ?? "mcp",
175
+ action,
176
+ outcome,
177
+ ...(target !== undefined ? { target_run_id: target } : {}),
178
+ ...(code !== undefined ? { error_code: code } : {}),
179
+ });
180
+ }
181
+ function bad(message) {
182
+ return new EvalApiError(400, "invalid_request", message);
183
+ }
184
+ function isRecord(value) {
185
+ return value !== null && typeof value === "object" && !Array.isArray(value);
186
+ }
187
+ function withAssignedIds(cases) {
188
+ const hasId = (c) => isRecord(c) && typeof c.id === "string" && c.id !== "";
189
+ const existing = cases.filter(hasId).map((c) => c.id);
190
+ let next = nextIdNumber(existing);
191
+ return cases.map((c) => (isRecord(c) && !hasId(c) ? { ...c, id: formatId(next++) } : c));
192
+ }
193
+ function optDatasetCases(value) {
194
+ if (value === undefined)
195
+ return undefined;
196
+ if (!Array.isArray(value))
197
+ throw bad(`"dataset_cases" must be an array`);
198
+ return withAssignedIds(value);
199
+ }
200
+ function targetFrom(value) {
201
+ if (!isRecord(value))
202
+ throw bad(`"target" must be an object`);
203
+ const target = {};
204
+ for (const key of ["pipeline_path", "stage_id", "file", "agent_id"]) {
205
+ const v = value[key];
206
+ if (v !== undefined) {
207
+ if (typeof v !== "string" || v === "")
208
+ throw bad(`target.${key} must be a non-empty string`);
209
+ target[key] = v;
210
+ }
211
+ }
212
+ return target;
213
+ }
214
+ function tryCaseFrom(value) {
215
+ if (!isRecord(value) || !isRecord(value.input))
216
+ throw bad(`"case" must be an object with an input object`);
217
+ return value;
218
+ }
219
+ function tryResultToWire(result) {
220
+ const r = result.result;
221
+ return {
222
+ result: {
223
+ case_id: r.caseId,
224
+ repeat_index: 0,
225
+ status: r.status,
226
+ error_code: r.errorCode,
227
+ error_message: r.errorMessage,
228
+ stage_run_id: r.stageRunId,
229
+ subject_ref: null,
230
+ turns: null,
231
+ tool_call_count: null,
232
+ output: r.output,
233
+ latency_ms: r.latencyMs,
234
+ cost_usd: r.costUsd,
235
+ tokens_in: r.tokensIn,
236
+ tokens_out: r.tokensOut,
237
+ score: r.score,
238
+ passed: r.passed,
239
+ grades: r.grades.map((g) => ({
240
+ grader_id: g.graderId,
241
+ grader_type: g.graderType,
242
+ status: g.status,
243
+ passed: g.passed,
244
+ score: g.score,
245
+ raw_value: g.rawValue,
246
+ reason: g.reason,
247
+ details: g.details,
248
+ error_code: g.errorCode,
249
+ judge_model: null,
250
+ judge_cost_usd: g.judgeCostUsd,
251
+ judge_tokens_in: null,
252
+ judge_tokens_out: null,
253
+ })),
254
+ budget_warnings: r.budgetWarnings,
255
+ review: null,
256
+ tags: [],
257
+ },
258
+ stage_run_id: result.stageRunId,
259
+ cost_usd: result.costUsd,
260
+ };
261
+ }
262
+ async function sampleCases() {
263
+ const examples = resolveSeededExamplesPath();
264
+ const dir = examples === undefined ? undefined : path.join(examples, "evals");
265
+ if (dir === undefined)
266
+ throw new EvalsWriteError("SAMPLE_SOURCE_MISSING", "The packaged sample files were not found");
267
+ let text;
268
+ try {
269
+ text = await readFile(path.join(dir, EVAL_SAMPLE_FILES.dataset), "utf8");
270
+ }
271
+ catch {
272
+ throw new EvalsWriteError("SAMPLE_SOURCE_MISSING", "The packaged sample dataset was not found");
273
+ }
274
+ return parseDatasetText(text, "jsonl").cases;
275
+ }
276
+ async function datasetDetailFor(projectRoot, id, absPath) {
277
+ const loaded = await loadDatasetFile(absPath, { root: projectRoot });
278
+ const manifest = await manifestForWrite(projectRoot);
279
+ const refs = await listEvalFiles(manifest);
280
+ const usedBy = [];
281
+ let inputSchema;
282
+ let agent;
283
+ for (const ref of refs) {
284
+ const outcome = await loadEvalOutcome(ref.absPath, projectRoot);
285
+ if (!outcome.ok)
286
+ continue;
287
+ const { datasetPath } = resolveEvalPaths(outcome.value.file, outcome.value.path);
288
+ if (path.resolve(datasetPath) !== path.resolve(absPath))
289
+ continue;
290
+ usedBy.push(outcome.value.id);
291
+ if (usedBy.length > 1)
292
+ continue;
293
+ if (outcome.value.file.target.kind === "agent") {
294
+ agent = { mode: resolveConversation(outcome.value.file.conversation).mode };
295
+ }
296
+ else {
297
+ const target = await resolveEvalTarget(outcome.value.file, outcome.value.path, projectRoot);
298
+ if (target.ok)
299
+ inputSchema = target.value.inputSchema;
300
+ }
301
+ }
302
+ return buildDatasetDetail({
303
+ id,
304
+ loaded,
305
+ inputSchema,
306
+ ...(agent !== undefined ? { agent } : {}),
307
+ hasEval: usedBy.length > 0,
308
+ usedBy,
309
+ query: { offset: 0, limit: MAX_LIMIT, filter: "all" },
310
+ });
311
+ }
312
+ function errorResult(err) {
313
+ if (err instanceof EvalApiError) {
314
+ return textResult({ error: err.message, code: err.code, ...err.extra }, true);
315
+ }
316
+ if (err instanceof CatalogPathError)
317
+ return textResult(catalogPathErrorBody(err), true);
318
+ if (err instanceof FileChangedError) {
319
+ return textResult({ error: err.message, code: err.code, current_hash: err.currentHash }, true);
320
+ }
321
+ if (err instanceof EvalsWriteError) {
322
+ return textResult({ error: err.message, code: err.code, ...(err.findings.length > 0 ? { findings: err.findings } : {}) }, true);
323
+ }
324
+ if (err instanceof EvalRunError) {
325
+ return textResult({ error: err.message, code: err.code, ...(err.findings ? { findings: err.findings } : {}) }, true);
326
+ }
327
+ if (err instanceof EvalStoreError || err instanceof CompareMismatchError) {
328
+ return textResult({ error: err.message, code: err.code }, true);
329
+ }
330
+ return textResult({ error: "Internal error", code: "internal_error" }, true);
331
+ }
332
+ export function registerEvalTools(server, deps) {
333
+ const { store, cwd } = deps;
334
+ const manager = () => {
335
+ if (deps.evalRunManager === undefined) {
336
+ throw new EvalApiError(503, "EVAL_ENGINE_UNAVAILABLE", "Evals are not available on this Host");
337
+ }
338
+ return deps.evalRunManager;
339
+ };
340
+ const roots = { store, bootCwd: cwd };
341
+ const projectRootFor = async (wire) => {
342
+ const { wireRoot } = await resolveCatalogStartInput(roots, wire);
343
+ return wireRoot.path;
344
+ };
345
+ const writableRoot = async (wire) => {
346
+ const { wireRoot } = await resolveWritableCatalogRoot(roots, wire);
347
+ return wireRoot.path;
348
+ };
349
+ const agentsInput = () => (deps.agentService !== undefined ? { agents: deps.agentService } : {});
350
+ const sampleAgents = () => {
351
+ const agents = deps.agentService;
352
+ if (agents?.create === undefined || agents.setEnabled === undefined)
353
+ return undefined;
354
+ return {
355
+ get: (projectRoot, agentId) => agents.get(projectRoot, agentId),
356
+ create: (projectRoot, raw, actor) => agents.create(projectRoot, raw, actor),
357
+ setEnabled: (projectRoot, agentId, enabled, actor) => agents.setEnabled(projectRoot, agentId, enabled, actor),
358
+ };
359
+ };
360
+ const sessionsOf = () => deps.agentService;
361
+ const requireEngine = () => {
362
+ engineCheck((deps.evalEngineStatus ?? (() => promptfooEngineStatus({ home: globalStageflowHome() })))());
363
+ };
364
+ const runDetail = (row) => buildRunDetail({ store, row, live: manager().getLive(row.evalRunId), ...agentsInput() });
365
+ const tool = (name, description, inputSchema, handler) => {
366
+ const run = async (input) => {
367
+ try {
368
+ return textResult(await handler(input));
369
+ }
370
+ catch (err) {
371
+ return errorResult(err);
372
+ }
373
+ };
374
+ server.registerTool(name, { description, inputSchema }, run);
375
+ };
376
+ 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 }) => {
377
+ manager();
378
+ const { items } = await loadAggregatedEvalList({
379
+ options: { store, bootCwd: cwd },
380
+ wire: project_root,
381
+ now: new Date(),
382
+ ...agentsInput(),
383
+ });
384
+ return { items };
385
+ });
386
+ 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 }) => {
387
+ manager();
388
+ const { projectRoot, ref } = await locateEval({ store, bootCwd: cwd }, project_root, eval_id);
389
+ return buildEvalDetail({ store, projectRoot, absPath: ref.absPath, ...agentsInput() });
390
+ });
391
+ 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). Optional path is the file location used to resolve relative paths (required only to check an unsaved eval against a specific path). Optional dataset_cases stand in for the dataset file, the same as POST /api/evals/validate. Checks schema, target, dataset and graders and returns { findings }. Read-only, free, calls no model; writes no files.", validateEvalSchema, async (input) => {
392
+ const { eval_id, yaml, path: evalPath, dataset_cases, project_root } = input;
393
+ manager();
394
+ if ((eval_id === undefined) === (yaml === undefined)) {
395
+ throw new EvalApiError(400, "invalid_request", "Provide exactly one of eval_id or yaml");
396
+ }
397
+ const datasetCases = optDatasetCases(dataset_cases);
398
+ const located = eval_id !== undefined ? await locateEval(roots, project_root, eval_id) : undefined;
399
+ const projectRoot = located?.projectRoot ?? (await projectRootFor(project_root));
400
+ let evalAbs;
401
+ if (evalPath !== undefined) {
402
+ evalAbs = path.resolve(projectRoot, evalPath);
403
+ assertInsideProject(projectRoot, evalAbs, "eval path");
404
+ }
405
+ else if (located !== undefined) {
406
+ evalAbs = located.ref.absPath;
407
+ }
408
+ else {
409
+ const manifest = await manifestForWrite(projectRoot);
410
+ const dir = await resolveWriteDir(projectRoot, manifest, "evals");
411
+ const rawId = /^id:\s*["']?([A-Za-z0-9][A-Za-z0-9._-]{0,63})["']?\s*$/m.exec(yaml)?.[1];
412
+ evalAbs = path.join(dir.absDir, `${rawId ?? "new"}.eval.yaml`);
413
+ assertInsideProject(projectRoot, evalAbs, "eval path");
414
+ }
415
+ const derived = yaml !== undefined
416
+ ? yaml.endsWith("\n")
417
+ ? yaml
418
+ : `${yaml}\n`
419
+ : await readFile(located.ref.absPath, "utf8");
420
+ const { findings } = await validateEvalText({
421
+ projectRoot,
422
+ evalAbs,
423
+ derived,
424
+ ...(datasetCases !== undefined ? { datasetCases } : {}),
425
+ ...agentsInput(),
426
+ });
427
+ return { findings };
428
+ });
429
+ 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({
430
+ eval_id: z.string(),
431
+ repeats: z.number().int().min(EVAL_LIMITS.repeats.min).max(EVAL_LIMITS.repeats.max).optional().describe("Override executions per case"),
432
+ judge: z.string().optional().describe("Override the judge model for rubric graders"),
433
+ model: z.string().optional().describe("Override the stage or agent model"),
434
+ user_model: z.string().optional().describe("Agent evals: override the model that plays the simulated user"),
435
+ allow_real_tools: z.boolean().optional().describe("Agent evals: consent to conversation.tools real; no effect on other policies"),
436
+ tools: z.enum(["read_only", "mocked"]).optional().describe("Agent evals: run with a safer tool policy than the file (never riskier)"),
437
+ approvals: z.enum(["deny", "record_only"]).optional().describe("Agent evals: run with a safer approval policy than the file (never riskier)"),
438
+ max_cost_usd: z.number().positive().optional().describe("Override the cost cap in USD"),
439
+ case_ids: z.array(z.string()).optional().describe("Run only these case ids"),
440
+ tags: z.array(z.string()).optional().describe("Run only cases with these tags"),
441
+ only_failed_from: z.string().optional().describe("Run only cases that failed in this earlier eval run of the same eval"),
442
+ label: z.string().max(EVAL_LIMITS.labelMaxChars).optional().describe("Free-text label for the run"),
443
+ wait: z.boolean().optional().describe("Block until finished (default false)"),
444
+ 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})`),
445
+ project_root: projectRootField,
446
+ }), async (input) => {
447
+ const evalRunManager = manager();
448
+ const { projectRoot, ref } = await locateEval(roots, input.project_root, input.eval_id);
449
+ const overrides = {};
450
+ if (input.user_model !== undefined)
451
+ overrides.user_model = input.user_model;
452
+ if (input.tools !== undefined)
453
+ overrides.tools = input.tools;
454
+ if (input.approvals !== undefined)
455
+ overrides.approvals = input.approvals;
456
+ if (input.allow_real_tools === true)
457
+ overrides.allow_real_tools = true;
458
+ if (input.repeats !== undefined)
459
+ overrides.repeats = input.repeats;
460
+ if (input.judge !== undefined)
461
+ overrides.judge = input.judge;
462
+ if (input.model !== undefined)
463
+ overrides.model = input.model;
464
+ if (input.max_cost_usd !== undefined)
465
+ overrides.max_cost_usd = input.max_cost_usd;
466
+ if (input.case_ids !== undefined)
467
+ overrides.case_ids = input.case_ids;
468
+ if (input.tags !== undefined)
469
+ overrides.tags = input.tags;
470
+ if (input.only_failed_from !== undefined)
471
+ overrides.only_failed_from = input.only_failed_from;
472
+ const callerId = callerIdFromRequestAuth();
473
+ const startInput = {
474
+ projectRoot,
475
+ evalPath: ref.absPath,
476
+ overrides,
477
+ startedVia: "mcp",
478
+ ...(input.label !== undefined ? { label: input.label } : {}),
479
+ ...(callerId !== null ? { callerId } : {}),
480
+ };
481
+ try {
482
+ await evalRunManager.preflightAgentStart(startInput);
483
+ }
484
+ catch (err) {
485
+ audit("eval_run_start", "error", undefined, err instanceof EvalRunError ? err.code : "internal_error");
486
+ throw err;
487
+ }
488
+ requireEngine();
489
+ let started;
490
+ try {
491
+ started = await evalRunManager.start(startInput);
492
+ }
493
+ catch (err) {
494
+ audit("eval_run_start", "error", undefined, err instanceof EvalRunError ? err.code : "internal_error");
495
+ throw err;
496
+ }
497
+ audit("eval_run_start", "ok", started.evalRunId);
498
+ if (input.wait !== true)
499
+ return { eval_run_id: started.evalRunId };
500
+ const { finished, run } = await evalRunManager.awaitFinished(started.evalRunId, input.timeout_ms ?? DEFAULT_WAIT_MS);
501
+ if (!finished)
502
+ return { eval_run_id: started.evalRunId, status: "timeout" };
503
+ return runDetail(run);
504
+ });
505
+ 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 }) => {
506
+ manager();
507
+ return runDetail(await runByIdOr404(store, eval_run_id));
508
+ });
509
+ 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({
510
+ eval_id: z.string().optional(),
511
+ status: z.enum(EVAL_RUN_STATUSES).optional(),
512
+ limit: z.number().int().min(1).max(MAX_LIMIT).optional().describe(`Default ${DEFAULT_LIMIT}`),
513
+ }), async ({ eval_id, status, limit }) => {
514
+ manager();
515
+ const page = await listEvalRunsPage(store, {
516
+ ...(eval_id !== undefined ? { evalId: eval_id } : {}),
517
+ ...(status !== undefined ? { status } : {}),
518
+ limit: limit ?? DEFAULT_LIMIT,
519
+ });
520
+ return { items: page.items };
521
+ });
522
+ 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({
523
+ eval_run_id: z.string(),
524
+ status: z.string().optional().describe("Result row status filter, for example failed or passed"),
525
+ grader: z.string().optional().describe("Only results this grader failed"),
526
+ limit: z.number().int().min(1).max(MAX_LIMIT).optional().describe(`Default ${DEFAULT_LIMIT}`),
527
+ cursor: z.string().optional(),
528
+ }), async ({ eval_run_id, status, grader, limit, cursor }) => {
529
+ manager();
530
+ const page = await buildResultsPage(store, eval_run_id, {
531
+ ...(status !== undefined ? { status } : {}),
532
+ ...(grader !== undefined ? { grader } : {}),
533
+ limit: limit ?? DEFAULT_LIMIT,
534
+ ...(cursor !== undefined ? { cursor } : {}),
535
+ });
536
+ return { items: page.items, next_cursor: page.next_cursor };
537
+ });
538
+ 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 }) => {
539
+ manager();
540
+ return buildResultDetail(store, eval_run_id, case_id);
541
+ });
542
+ const triage = () => new TriageService({
543
+ store,
544
+ judgeBackend: () => deps.agentConversationBackend,
545
+ ...(deps.triage ?? {}),
546
+ });
547
+ 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({
548
+ eval_run_id: z.string(),
549
+ classifier: z.enum(["auto", "jev", "judge"]).optional().describe("Default auto"),
550
+ only_unsorted: z.boolean().optional().describe("Skip results that already have a suggestion (default true)"),
551
+ limit: z.number().int().min(1).max(TRIAGE_MAX_LIMIT).optional().describe("Most results to sort (default 200)"),
552
+ }), async ({ eval_run_id, classifier, only_unsorted, limit }) => {
553
+ manager();
554
+ const row = await runByIdOr404(store, eval_run_id);
555
+ try {
556
+ const result = await triage().sort(row, {
557
+ classifier: classifier ?? "auto",
558
+ ...(only_unsorted !== undefined ? { onlyUnsorted: only_unsorted } : {}),
559
+ ...(limit !== undefined ? { limit } : {}),
560
+ });
561
+ audit("eval_failures_sort", "ok", eval_run_id);
562
+ return result;
563
+ }
564
+ catch (err) {
565
+ audit("eval_failures_sort", "error", eval_run_id, err instanceof EvalApiError ? err.code : "internal_error");
566
+ throw err;
567
+ }
568
+ });
569
+ 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 }) => {
570
+ manager();
571
+ return triage().list(await runByIdOr404(store, eval_run_id), status);
572
+ });
573
+ 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 }) => {
574
+ manager();
575
+ return triage().status(await runByIdOr404(store, eval_run_id));
576
+ });
577
+ 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 }) => {
578
+ const evalRunManager = manager();
579
+ await runByIdOr404(store, eval_run_id);
580
+ await evalRunManager.cancel(eval_run_id);
581
+ const { run } = await evalRunManager.awaitFinished(eval_run_id, CANCEL_WAIT_MS);
582
+ audit("eval_run_cancel", "ok", eval_run_id);
583
+ return runDetail(run);
584
+ });
585
+ 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 }) => {
586
+ manager();
587
+ const left = await runByIdOr404(store, a);
588
+ const right = await runByIdOr404(store, b);
589
+ return buildCompare(store, left, right);
590
+ });
591
+ const evalDetail = (projectRoot, absPath) => buildEvalDetail({ store, projectRoot, absPath, ...agentsInput() });
592
+ tool("create_eval", "Create an eval file from a definition object. Validates the full plan first (target, dataset, graders) and writes only when that passes. Same as POST /api/evals. Fails EVAL_EXISTS when the id is already in the catalog, EVAL_INVALID with findings when the definition is not runnable. Does not call a model.", z.object({
593
+ definition: z.record(z.string(), z.unknown()).describe("Eval definition object, the same shape as an eval file"),
594
+ directory: z.string().optional().describe("Catalog directory to write into, relative to the project"),
595
+ project_root: projectRootField,
596
+ }), async ({ definition, directory, project_root }) => {
597
+ const file = definition;
598
+ const projectRoot = await writableRoot(project_root);
599
+ const manifest = await manifestForWrite(projectRoot);
600
+ const dir = await resolveWriteDir(projectRoot, manifest, "evals", directory);
601
+ const rawId = file.id;
602
+ const fileName = typeof rawId === "string" && ID_RE.test(rawId) ? `${rawId}.eval.yaml` : "new.eval.yaml";
603
+ const absPath = path.join(dir.absDir, fileName);
604
+ assertInsideProject(projectRoot, absPath, "eval path");
605
+ if (typeof rawId === "string" && (await findEvalFile(projectRoot, rawId, manifest)) !== undefined) {
606
+ throw new EvalsWriteError("EVAL_EXISTS", `Eval "${rawId}" already exists`);
607
+ }
608
+ const text = renderEvalCreateYaml(file);
609
+ const checked = await planFindings({ projectRoot, evalPath: absPath, evalText: text, ...agentsInput() });
610
+ if (!checked.ok) {
611
+ audit("eval_create", "error", undefined, "EVAL_INVALID");
612
+ throw new EvalApiError(422, "EVAL_INVALID", "Eval definition is invalid", { findings: checked.findings });
613
+ }
614
+ const written = await createEvalFile({ projectRoot, definition: file, ...(directory !== undefined ? { directory } : {}) });
615
+ audit("eval_create", "ok");
616
+ return evalDetail(projectRoot, written.path);
617
+ });
618
+ tool("update_eval", "Replace an eval file. Pass exactly one of definition (merged into the current file) or yaml (the full file text), plus base_hash from get_eval. Refuses with FILE_CHANGED and current_hash when the file moved, and EVAL_INVALID with findings when the result is not runnable. Same as PUT /api/evals/:id. Does not call a model.", updateEvalSchema, async (input) => {
619
+ const { eval_id, base_hash, definition, yaml, project_root } = input;
620
+ if ((definition === undefined) === (yaml === undefined))
621
+ throw bad("Provide exactly one of definition or yaml");
622
+ if (base_hash === "")
623
+ throw bad(`"base_hash" is required`);
624
+ const { projectRoot, ref } = await locateEval(roots, project_root, eval_id, true);
625
+ const current = await readFile(ref.absPath);
626
+ const currentHash = contentHash(current);
627
+ if (currentHash !== base_hash)
628
+ throw new FileChangedError(currentHash);
629
+ const text = yaml !== undefined ? (yaml.endsWith("\n") ? yaml : `${yaml}\n`) : previewEvalUpdate(current.toString("utf8"), definition);
630
+ const checked = await planFindings({ projectRoot, evalPath: ref.absPath, evalText: text, ...agentsInput() });
631
+ if (!checked.ok) {
632
+ audit("eval_update", "error", undefined, "EVAL_INVALID");
633
+ throw new EvalApiError(422, "EVAL_INVALID", "Eval definition is invalid", { findings: checked.findings });
634
+ }
635
+ await updateEvalFile({
636
+ projectRoot,
637
+ id: eval_id,
638
+ baseHash: base_hash,
639
+ ...(yaml !== undefined ? { yaml } : { definition }),
640
+ });
641
+ audit("eval_update", "ok");
642
+ return evalDetail(projectRoot, ref.absPath);
643
+ });
644
+ const casesFromRunsInput = async (projectRoot, target, runIds) => {
645
+ const resolved = await resolveDatasetTarget(projectRoot, targetFrom(target));
646
+ if (!resolved.ok)
647
+ throw new EvalApiError(422, "EVAL_INVALID", resolved.message);
648
+ if (runIds === undefined || runIds.length === 0)
649
+ throw bad(`"run_ids" must be a non-empty array`);
650
+ if (resolved.value.stageId === undefined)
651
+ throw bad("target does not name a stage");
652
+ return casesFromRuns({
653
+ store,
654
+ runIds,
655
+ target: {
656
+ stageId: resolved.value.stageId,
657
+ ...(resolved.value.pipelineAbsPath !== undefined ? { pipelinePath: resolved.value.pipelineAbsPath } : {}),
658
+ },
659
+ });
660
+ };
661
+ const casesFromSessionsInput = async (projectRoot, target, sessionIds) => {
662
+ const resolved = await resolveDatasetTarget(projectRoot, targetFrom(target), deps.agentService);
663
+ if (!resolved.ok)
664
+ throw new EvalApiError(422, "EVAL_INVALID", resolved.message);
665
+ if (resolved.value.agentId === undefined)
666
+ throw bad(`source "sessions" needs target.agent_id`);
667
+ if (sessionIds === undefined || sessionIds.length === 0)
668
+ throw bad(`"session_ids" must be a non-empty array`);
669
+ const sessions = sessionsOf();
670
+ if (sessions === undefined)
671
+ throw new EvalApiError(503, "EVAL_ENGINE_UNAVAILABLE", "Agents are not available on this Host");
672
+ try {
673
+ return await casesFromSessions({ sessions, projectRoot, agentId: resolved.value.agentId, sessionIds });
674
+ }
675
+ catch (err) {
676
+ if (err instanceof SessionCaseError)
677
+ throw bad(err.message);
678
+ throw err;
679
+ }
680
+ };
681
+ tool("create_dataset", "Create a dataset, or append or replace cases in one. Pass name and cases for a new file. source sample copies the packaged sample cases (they must match target). source runs copies recorded stage inputs (run_ids, append only when updating). source sessions copies finished agent sessions (session_ids and target.agent_id, append only when updating; needs the agent service). To update an existing file pass dataset_id, mode append or replace, and base_hash. Same as POST /api/datasets. Does not call a model.", createDatasetSchema, async (raw) => {
682
+ const input = raw;
683
+ const datasetId = input.dataset_id !== undefined && input.dataset_id !== "" ? input.dataset_id : undefined;
684
+ const projectRoot = datasetId !== undefined
685
+ ? (await locateDataset(roots, input.project_root, datasetId, { write: true, missing: "default" })).projectRoot
686
+ : await writableRoot(input.project_root);
687
+ let absPath;
688
+ let id;
689
+ if (datasetId !== undefined) {
690
+ id = datasetId;
691
+ if (input.mode !== "append" && input.mode !== "replace")
692
+ throw bad(`"mode" must be append or replace`);
693
+ if (typeof input.base_hash !== "string" || input.base_hash === "")
694
+ throw bad(`"base_hash" is required`);
695
+ let newCases;
696
+ if (input.source === "runs") {
697
+ if (input.mode !== "append")
698
+ throw bad(`source "runs" requires mode "append"`);
699
+ newCases = await casesFromRunsInput(projectRoot, input.target, input.run_ids);
700
+ }
701
+ else if (input.source === "sessions") {
702
+ if (input.mode !== "append")
703
+ throw bad(`source "sessions" requires mode "append"`);
704
+ newCases = await casesFromSessionsInput(projectRoot, input.target, input.session_ids);
705
+ }
706
+ else {
707
+ if (!Array.isArray(input.cases))
708
+ throw bad(`"cases" must be an array`);
709
+ newCases = input.cases;
710
+ }
711
+ const written = await appendOrReplaceCases({
712
+ projectRoot,
713
+ id,
714
+ baseHash: input.base_hash,
715
+ mode: input.mode,
716
+ cases: newCases,
717
+ });
718
+ absPath = written.path;
719
+ }
720
+ else {
721
+ const name = input.name;
722
+ if (name === undefined || name === "")
723
+ throw bad(`"name" is required`);
724
+ id = name;
725
+ let cases;
726
+ if (input.source === "sessions") {
727
+ cases = await casesFromSessionsInput(projectRoot, input.target, input.session_ids);
728
+ }
729
+ else if (input.source === "sample" || input.source === "runs") {
730
+ const resolved = await resolveDatasetTarget(projectRoot, targetFrom(input.target), deps.agentService);
731
+ if (!resolved.ok)
732
+ throw new EvalApiError(422, "EVAL_INVALID", resolved.message);
733
+ if (resolved.value.agentId !== undefined)
734
+ throw bad(`source "${input.source}" is not available for an agent target`);
735
+ if (input.source === "sample") {
736
+ const sample = await sampleCases();
737
+ const failing = checkDatasetAgainstStage(sample, resolved.value.inputSchema).find((row) => !row.valid);
738
+ if (failing)
739
+ throw new EvalsWriteError("DATASET_INVALID", "The sample cases do not match this stage's input");
740
+ cases = sample;
741
+ }
742
+ else {
743
+ cases = await casesFromRunsInput(projectRoot, input.target, input.run_ids);
744
+ }
745
+ }
746
+ else {
747
+ if (!Array.isArray(input.cases))
748
+ throw bad(`"cases" must be an array`);
749
+ cases = input.cases;
750
+ }
751
+ const written = await createDatasetFile({
752
+ projectRoot,
753
+ name,
754
+ cases,
755
+ ...(input.directory !== undefined ? { directory: input.directory } : {}),
756
+ });
757
+ absPath = written.path;
758
+ }
759
+ audit("dataset_write", "ok");
760
+ return datasetDetailFor(projectRoot, id, absPath);
761
+ });
762
+ tool("update_dataset_cases", "Apply add, update, delete, and set_tags operations to a .dataset.jsonl file. base_hash must be the current file hash. The whole list is one write: a bad op changes nothing. Same as PATCH /api/datasets/:id/cases. Fails DATASET_READ_ONLY for json or csv (convert_dataset first). Does not call a model.", z.object({
763
+ dataset_id: z.string(),
764
+ base_hash: z.string().min(1),
765
+ ops: z.array(caseOpSchema),
766
+ project_root: projectRootField,
767
+ }), async ({ dataset_id, base_hash, ops, project_root }) => {
768
+ if (base_hash === "")
769
+ throw bad(`"base_hash" is required`);
770
+ if (ops.some((op) => !CASE_OPS.has(String(op.op)))) {
771
+ throw bad(`"ops" must be an array of add, update, delete or set_tags operations`);
772
+ }
773
+ const { projectRoot } = await locateDataset(roots, project_root, dataset_id, { write: true, missing: "default" });
774
+ const written = await applyCaseOps({ projectRoot, id: dataset_id, baseHash: base_hash, ops: ops });
775
+ audit("dataset_write", "ok");
776
+ return datasetDetailFor(projectRoot, dataset_id, written.path);
777
+ });
778
+ tool("convert_dataset", "Copy a .dataset.json or .dataset.csv file to an editable .dataset.jsonl sibling and return that file. The original is left in place. base_hash is the current source hash. Same as POST /api/datasets/:id/convert. Does not call a model.", z.object({
779
+ dataset_id: z.string(),
780
+ base_hash: z.string(),
781
+ project_root: projectRootField,
782
+ }), async ({ dataset_id, base_hash, project_root }) => {
783
+ if (base_hash === "")
784
+ throw bad(`"base_hash" is required`);
785
+ const { projectRoot } = await locateDataset(roots, project_root, dataset_id, { write: true, missing: "default" });
786
+ const written = await convertDatasetToJsonl({ projectRoot, id: dataset_id, baseHash: base_hash });
787
+ audit("dataset_write", "ok");
788
+ return datasetDetailFor(projectRoot, written.id, written.path);
789
+ });
790
+ tool("install_eval_sample", "Install the packaged sample eval into the project. kind stage (default) writes the critique sample pipeline, dataset, and eval. kind agent writes the support sample and creates its agent; without the agent service it fails SAMPLE_AGENTS_UNAVAILABLE. Same as POST /api/evals/sample. Fails SAMPLE_EXISTS when that sample is already installed. Does not call a model.", z.object({
791
+ kind: z.enum(["stage", "agent"]).optional().describe('Default "stage"'),
792
+ project_root: projectRootField,
793
+ }), async ({ kind, project_root }) => {
794
+ const projectRoot = await writableRoot(project_root);
795
+ try {
796
+ const port = sampleAgents();
797
+ const result = await installEvalSample({
798
+ projectRoot,
799
+ ...(kind === "agent" ? { kind } : {}),
800
+ ...(port ? { agents: port } : {}),
801
+ });
802
+ audit("eval_sample_install", "ok");
803
+ return { eval_id: result.evalId };
804
+ }
805
+ catch (err) {
806
+ if (err instanceof EvalsWriteError)
807
+ audit("eval_sample_install", "error", undefined, err.code);
808
+ throw err;
809
+ }
810
+ });
811
+ tool("try_eval_case", "Run one case of an eval and return its grade. Pass exactly one of eval_id or definition, and exactly one of case_id or case. dataset_cases stand in for the dataset file. This calls the target model once and may cost real money, bounded by the eval's max_cost_usd; graders that need a judge call the judge too. Agent tools stay read-only unless the eval file sets conversation.tools real and allow_real_tools is true. Nothing is saved as an eval run, and no eval session is returned. Same as POST /api/evals/try.", tryEvalCaseSchema, async (input) => {
812
+ const { eval_id, definition, case_id, case: caseInput, dataset_cases, allow_real_tools, project_root } = input;
813
+ const evalRunManager = manager();
814
+ if ((eval_id === undefined) === (definition === undefined))
815
+ throw bad("Provide exactly one of eval_id or definition");
816
+ if ((case_id === undefined) === (caseInput === undefined))
817
+ throw bad("Provide exactly one of case_id or case");
818
+ const datasetCases = optDatasetCases(dataset_cases);
819
+ const located = eval_id !== undefined ? await locateEval(roots, project_root, eval_id) : undefined;
820
+ const projectRoot = located?.projectRoot ?? (await projectRootFor(project_root));
821
+ const manifest = await manifestForWrite(projectRoot);
822
+ let evalPath;
823
+ let file;
824
+ if (located !== undefined) {
825
+ evalPath = located.ref.absPath;
826
+ }
827
+ else {
828
+ file = definition;
829
+ const rawId = file.id;
830
+ const dir = await resolveWriteDir(projectRoot, manifest, "evals");
831
+ evalPath = path.join(dir.absDir, typeof rawId === "string" && ID_RE.test(rawId) ? `${rawId}.eval.yaml` : "new.eval.yaml");
832
+ assertInsideProject(projectRoot, evalPath, "eval path");
833
+ }
834
+ requireEngine();
835
+ const tried = await evalRunManager.tryOne({
836
+ projectRoot,
837
+ evalPath,
838
+ ...(file !== undefined ? { definition: file } : {}),
839
+ ...(datasetCases !== undefined ? { datasetCases } : {}),
840
+ ...(case_id !== undefined ? { caseId: case_id } : { case: tryCaseFrom(caseInput) }),
841
+ ...(allow_real_tools === true ? { allowRealTools: true } : {}),
842
+ });
843
+ audit("eval_try", "ok");
844
+ return tryResultToWire(tried);
845
+ });
846
+ tool("get_eval_run_export", "Export one eval run. what results (default) is the run detail plus every result row. what config is the config snapshot written when the run started. Read-only, free. Same body as GET /api/eval-runs/:id/export. Grader failures are codes.", z.object({
847
+ eval_run_id: z.string(),
848
+ what: z.enum(["results", "config"]).optional().describe('Default "results"'),
849
+ }), async ({ eval_run_id, what }) => {
850
+ manager();
851
+ const row = await runByIdOr404(store, eval_run_id);
852
+ return buildExport({ store, row, what: what ?? "results" });
853
+ });
854
+ tool("put_eval_review", "Set the review label, note, or failure-type tags on one repeat of an eval result. Tags are stored only on results that failed; tagging a passed result fails EVAL_TAGS_ON_PASSED_RESULT. Same as PUT /api/eval-runs/:id/results/:case_id/:repeat/review. Does not call a model.", z.object({
855
+ eval_run_id: z.string(),
856
+ case_id: z.string(),
857
+ repeat_index: z.number().int().min(0),
858
+ label: z.string().optional().describe("pass or fail"),
859
+ note: z.string().nullable().optional(),
860
+ tags: z.array(z.string()).optional().describe("Failure types; only on a failed result"),
861
+ }), async ({ eval_run_id, case_id, repeat_index, label, note, tags }) => {
862
+ manager();
863
+ await runByIdOr404(store, eval_run_id);
864
+ const patch = {};
865
+ if (label !== undefined) {
866
+ if (!EVAL_REVIEW_LABELS.includes(label))
867
+ throw bad(`"label" must be pass or fail`);
868
+ patch.label = label;
869
+ }
870
+ if (note !== undefined) {
871
+ if (note !== null && note.length > EVAL_LIMITS.reviewNoteMaxChars) {
872
+ throw bad(`"note" must be a string of at most ${EVAL_LIMITS.reviewNoteMaxChars} characters or null`);
873
+ }
874
+ patch.note = note;
875
+ }
876
+ if (tags !== undefined) {
877
+ if (tags.some((t) => t.length > EVAL_LIMITS.failureTypeNameMaxChars)) {
878
+ throw bad(`Each tag must be at most ${EVAL_LIMITS.failureTypeNameMaxChars} characters`);
879
+ }
880
+ patch.tags = tags;
881
+ }
882
+ try {
883
+ const review = await store.putEvalReview(eval_run_id, case_id, repeat_index, patch);
884
+ audit("eval_review", "ok", eval_run_id);
885
+ return { label: review.label, note: review.note, tags: review.tags };
886
+ }
887
+ catch (err) {
888
+ if (err instanceof EvalStoreError)
889
+ throw err;
890
+ if (err instanceof Error)
891
+ throw bad(err.message);
892
+ throw err;
893
+ }
894
+ });
895
+ tool("accept_eval_failure_suggestions", "Accept pending failure suggestions for an eval run and write their failure types as tags. Pass ids to accept those rows, or min_confidence and bucket to accept every pending suggestion that matches (unclear is never included). Tags land only on failed results. Same as POST /api/eval-runs/:id/failures/suggestions/accept. Does not call a model.", z.object({
896
+ eval_run_id: z.string(),
897
+ ids: z.array(z.string()).optional().describe("Suggestion ids to accept; omit to accept by confidence and bucket"),
898
+ min_confidence: z.number().optional().describe("Lowest confidence to accept when ids is omitted, from 0 to 1"),
899
+ bucket: z.string().optional().describe(`Only this bucket when ids is omitted: ${TRIAGE_BUCKETS.join(", ")}`),
900
+ }), async ({ eval_run_id, ids, min_confidence, bucket }) => {
901
+ manager();
902
+ const row = await runByIdOr404(store, eval_run_id);
903
+ if (min_confidence !== undefined && (min_confidence < 0 || min_confidence > 1)) {
904
+ throw bad(`"min_confidence" must be a number from 0 to 1`);
905
+ }
906
+ if (bucket !== undefined && !isTriageBucket(bucket))
907
+ throw bad(`"bucket" must be one of ${TRIAGE_BUCKETS.join(", ")}`);
908
+ const result = await triage().accept(row, {
909
+ ...(ids !== undefined ? { ids } : {}),
910
+ ...(min_confidence !== undefined ? { minConfidence: min_confidence } : {}),
911
+ ...(isTriageBucket(bucket) ? { bucket } : {}),
912
+ });
913
+ audit("eval_suggestions_accept", "ok", eval_run_id);
914
+ return result;
915
+ });
916
+ tool("dismiss_eval_failure_suggestions", "Dismiss failure suggestions so they are not tagged. ids are required. Same as POST /api/eval-runs/:id/failures/suggestions/dismiss. Does not call a model.", z.object({
917
+ eval_run_id: z.string(),
918
+ ids: z.array(z.string()).describe("Suggestion ids to dismiss"),
919
+ }), async ({ eval_run_id, ids }) => {
920
+ manager();
921
+ const row = await runByIdOr404(store, eval_run_id);
922
+ const result = await triage().dismiss(row, ids);
923
+ audit("eval_suggestions_dismiss", "ok", eval_run_id);
924
+ return result;
925
+ });
926
+ tool("delete_eval_run", "Delete a finished eval run, its results, and the stage runs it recorded. A queued or running run fails EVAL_RUN_ACTIVE; cancel it first. Agent eval sessions for the run are deleted with it and are not listed anywhere else. Same as DELETE /api/eval-runs/:id.", z.object({ eval_run_id: z.string() }), async ({ eval_run_id }) => {
927
+ manager();
928
+ const row = await runByIdOr404(store, eval_run_id);
929
+ if (row.status === "queued" || row.status === "running") {
930
+ throw new EvalApiError(409, "EVAL_RUN_ACTIVE", "Eval run is still running; cancel it first");
931
+ }
932
+ const sessionIds = row.targetKind === "agent" ? await store.listEvalSubjectRefs(eval_run_id, "agent") : [];
933
+ const stageRunIds = await store.deleteEvalRun(eval_run_id);
934
+ for (const runId of stageRunIds) {
935
+ await deps.manager.deleteRun(runId, { channel: "rest" }).catch(() => undefined);
936
+ }
937
+ if (sessionIds.length > 0) {
938
+ await deps.agentService?.deleteEvalSessions(row.projectRoot, sessionIds).catch(() => undefined);
939
+ }
940
+ audit("eval_run_delete", "ok", eval_run_id);
941
+ return { deleted: true };
942
+ });
943
+ }