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,289 @@
1
+ import path from "node:path";
2
+ import { loadDatasetFile } from "../../config/loadDataset.js";
3
+ import { loadEvalOutcome, resolveEvalPaths } from "../../config/loadEval.js";
4
+ import { resolveAgentTarget } from "../../config/resolveAgentTarget.js";
5
+ import { resolveEvalTarget } from "../../config/resolveEvalTarget.js";
6
+ import { computeStats, lowestCases } from "../stats.js";
7
+ import { toGradeRecords, toResultRecord } from "../statsAdapter.js";
8
+ import { resultKey } from "../statsTypes.js";
9
+ export function conversationView(output) {
10
+ const rec = asRecord(output);
11
+ if (rec.kind !== "conversation")
12
+ return null;
13
+ if (!Array.isArray(rec.messages) || !Array.isArray(rec.tool_calls))
14
+ return null;
15
+ return {
16
+ messages: rec.messages,
17
+ tool_calls: rec.tool_calls,
18
+ final_reply: typeof rec.final_reply === "string" ? rec.final_reply : "",
19
+ stop_reason: typeof rec.stop_reason === "string" ? rec.stop_reason : "",
20
+ };
21
+ }
22
+ function asRecord(value) {
23
+ return typeof value === "object" && value !== null && !Array.isArray(value)
24
+ ? value
25
+ : {};
26
+ }
27
+ export function runHeadline(row) {
28
+ const summary = (row.summary ?? {});
29
+ return {
30
+ eval_run_id: row.evalRunId,
31
+ eval_id: row.evalId,
32
+ run_number: row.runNumber,
33
+ status: row.status,
34
+ passed: row.passed,
35
+ pass_rate: summary.pass_rate ?? null,
36
+ pass_rate_ci: summary.pass_rate_ci ?? null,
37
+ results_executed: summary.results_executed ?? row.resultsDone,
38
+ cost_total: summary.cost_total ?? row.costUsd,
39
+ finished_at: row.finishedAt,
40
+ fingerprint: row.configFingerprint,
41
+ label: row.label,
42
+ };
43
+ }
44
+ /** The newest queued or running run of an eval, or null. */
45
+ export async function activeRunOf(store, projectRoot, evalId) {
46
+ const found = [];
47
+ for (const status of ["running", "queued"]) {
48
+ const page = await store.listEvalRuns({ projectRoot, evalId, status, limit: 50 });
49
+ found.push(...page.items);
50
+ }
51
+ const row = found.sort((a, b) => b.runNumber - a.runNumber)[0];
52
+ if (!row || (row.status !== "queued" && row.status !== "running"))
53
+ return null;
54
+ return {
55
+ run_number: row.runNumber,
56
+ eval_run_id: row.evalRunId,
57
+ status: row.status,
58
+ results_done: row.resultsDone,
59
+ results_total: row.resultsTotal,
60
+ };
61
+ }
62
+ export function graderRefsOf(row) {
63
+ const graders = asRecord(row.config).graders;
64
+ if (!Array.isArray(graders))
65
+ return [];
66
+ return graders.flatMap((g) => {
67
+ const rec = asRecord(g);
68
+ return typeof rec.id === "string" && typeof rec.hash === "string"
69
+ ? [{ id: rec.id, type: typeof rec.type === "string" ? rec.type : "", hash: rec.hash }]
70
+ : [];
71
+ });
72
+ }
73
+ export function reviewTagMap(reviews) {
74
+ const map = new Map();
75
+ for (const review of reviews) {
76
+ if (review.tags.length > 0)
77
+ map.set(resultKey(review.caseId, review.repeatIndex), review.tags);
78
+ }
79
+ return map;
80
+ }
81
+ export function statsOfRun(row, results, extra = {}) {
82
+ return computeStats({
83
+ ...(row.targetKind === "agent" ? { targetKind: "agent" } : {}),
84
+ results: results.map(toResultRecord),
85
+ grades: results.flatMap(toGradeRecords),
86
+ graders: graderRefsOf(row),
87
+ repeats: row.repeats,
88
+ passThreshold: row.passThreshold,
89
+ ...(extra.reviews ? { reviewsTags: reviewTagMap(extra.reviews) } : {}),
90
+ ...(extra.previousRun !== undefined ? { previousRun: extra.previousRun } : {}),
91
+ });
92
+ }
93
+ export async function completedRunsNewestFirst(store, projectRoot, evalId, limit = 50) {
94
+ const page = await store.listEvalRuns({ projectRoot, evalId, status: "completed", limit });
95
+ return [...page.items].sort((a, b) => b.runNumber - a.runNumber);
96
+ }
97
+ export async function previousRunLite(store, row, graders) {
98
+ const earlier = (await completedRunsNewestFirst(store, row.projectRoot, row.evalId)).filter((r) => r.runNumber < row.runNumber);
99
+ if (earlier.length === 0)
100
+ return null;
101
+ const rates = new Map();
102
+ const rateOf = async (run, graderId) => {
103
+ let byGrader = rates.get(run.evalRunId);
104
+ if (!byGrader) {
105
+ const stats = statsOfRun(run, await store.listEvalResults(run.evalRunId));
106
+ byGrader = new Map(stats.by_grader.map((g) => [g.grader_id, g.pass_rate]));
107
+ rates.set(run.evalRunId, byGrader);
108
+ }
109
+ return byGrader.get(graderId) ?? null;
110
+ };
111
+ const lite = [];
112
+ for (const g of graders) {
113
+ let changedHash;
114
+ let match;
115
+ for (const run of earlier) {
116
+ const found = graderRefsOf(run).find((x) => x.id === g.id);
117
+ if (!found)
118
+ continue;
119
+ if (found.hash === g.hash) {
120
+ match = run;
121
+ break;
122
+ }
123
+ changedHash ??= found.hash;
124
+ }
125
+ if (match)
126
+ lite.push({ graderId: g.id, hash: g.hash, passRate: await rateOf(match, g.id) });
127
+ else if (changedHash !== undefined)
128
+ lite.push({ graderId: g.id, hash: changedHash, passRate: null });
129
+ }
130
+ return { graders: lite };
131
+ }
132
+ export function gradeToWire(g) {
133
+ return {
134
+ grader_id: g.graderId,
135
+ grader_type: g.graderType,
136
+ status: g.status,
137
+ passed: g.passed,
138
+ score: g.score,
139
+ raw_value: g.rawValue,
140
+ reason: g.reason,
141
+ details: g.details,
142
+ error_code: g.errorCode,
143
+ judge_model: g.judgeModel,
144
+ judge_cost_usd: g.judgeCostUsd,
145
+ judge_tokens_in: g.judgeTokensIn,
146
+ judge_tokens_out: g.judgeTokensOut,
147
+ };
148
+ }
149
+ export function resultToWire(r, review, agentId) {
150
+ const hasReview = review !== undefined && (review.label !== null || review.note !== null);
151
+ return {
152
+ case_id: r.caseId,
153
+ repeat_index: r.repeatIndex,
154
+ status: r.status,
155
+ error_code: r.errorCode,
156
+ error_message: r.errorMessage,
157
+ stage_run_id: r.stageRunId,
158
+ subject_ref: r.subjectRef,
159
+ turns: r.turns,
160
+ tool_call_count: r.toolCallCount,
161
+ ...(agentId !== undefined
162
+ ? { agent: r.subjectRef !== null ? { id: agentId, session_id: r.subjectRef } : null }
163
+ : {}),
164
+ output: r.output,
165
+ latency_ms: r.latencyMs,
166
+ cost_usd: r.costUsd,
167
+ tokens_in: r.tokensIn,
168
+ tokens_out: r.tokensOut,
169
+ score: r.score,
170
+ passed: r.passed,
171
+ grades: r.grades.map(gradeToWire),
172
+ budget_warnings: r.budgetWarnings ?? [],
173
+ review: hasReview ? { label: review.label, note: review.note } : null,
174
+ tags: review?.tags ?? [],
175
+ };
176
+ }
177
+ export async function currentHashes(row, agents) {
178
+ const isAgent = row.targetKind === "agent";
179
+ const none = {
180
+ stage_version_hash: null,
181
+ dataset_hash: null,
182
+ grader_hashes: null,
183
+ ...(isAgent ? { agent_revision: null, agent_definition_hash: null } : {}),
184
+ };
185
+ const evalAbs = path.resolve(row.projectRoot, row.evalPath);
186
+ try {
187
+ const outcome = await loadEvalOutcome(evalAbs, row.projectRoot);
188
+ if (!outcome.ok)
189
+ return none;
190
+ const loaded = outcome.value;
191
+ const paths = resolveEvalPaths(loaded.file, loaded.path);
192
+ const datasetHashOf = async () => {
193
+ const dataset = await loadDatasetFile(paths.datasetPath, { root: row.projectRoot });
194
+ return dataset.findings.some((f) => f.severity === "error") ? null : dataset.contentHash;
195
+ };
196
+ const graderHashes = Object.fromEntries(loaded.graders.map((g) => [g.id, g.hash]));
197
+ if (isAgent) {
198
+ const resolved = agents ? await resolveAgentTarget(loaded.file, row.projectRoot, agents) : null;
199
+ return {
200
+ stage_version_hash: null,
201
+ dataset_hash: await datasetHashOf(),
202
+ grader_hashes: graderHashes,
203
+ agent_revision: resolved?.ok ? resolved.value.revision : null,
204
+ agent_definition_hash: resolved?.ok ? resolved.value.definitionHash : null,
205
+ };
206
+ }
207
+ const [target, datasetHash] = await Promise.all([
208
+ resolveEvalTarget(loaded.file, loaded.path, row.projectRoot),
209
+ datasetHashOf(),
210
+ ]);
211
+ return {
212
+ stage_version_hash: target.ok ? target.value.stageVersionHash : null,
213
+ dataset_hash: datasetHash,
214
+ grader_hashes: graderHashes,
215
+ };
216
+ }
217
+ catch {
218
+ return none;
219
+ }
220
+ }
221
+ export async function buildRunDetail(input) {
222
+ const { store, row } = input;
223
+ const [results, reviews] = await Promise.all([
224
+ store.listEvalResults(row.evalRunId),
225
+ store.getEvalReviews(row.evalRunId),
226
+ ]);
227
+ const graders = graderRefsOf(row);
228
+ const grades = results.flatMap(toGradeRecords);
229
+ const executed = results.some((r) => ["completed", "stage_failed", "needs_input", "error"].includes(r.status));
230
+ const stats = executed
231
+ ? statsOfRun(row, results, { reviews, previousRun: await previousRunLite(store, row, graders) })
232
+ : null;
233
+ const detail = {
234
+ eval_run_id: row.evalRunId,
235
+ run_number: row.runNumber,
236
+ eval_id: row.evalId,
237
+ eval_path: row.evalPath,
238
+ status: row.status,
239
+ status_reason: row.statusReason,
240
+ passed: row.passed,
241
+ label: row.label,
242
+ started_via: row.startedVia,
243
+ created_at: row.createdAt,
244
+ started_at: row.startedAt,
245
+ finished_at: row.finishedAt,
246
+ progress: {
247
+ results_done: row.resultsDone,
248
+ results_total: row.resultsTotal,
249
+ cost_usd: row.costUsd,
250
+ max_cost_usd: row.maxCostUsd,
251
+ cases_total: row.casesTotal,
252
+ },
253
+ config: row.config,
254
+ fingerprint: row.configFingerprint,
255
+ components: row.configComponents,
256
+ stats,
257
+ lowest_cases_by_grader: executed
258
+ ? Object.fromEntries(graders.map((g) => [g.id, lowestCases(grades.filter((x) => x.graderId === g.id))]))
259
+ : {},
260
+ current: await currentHashes(row, input.agents),
261
+ };
262
+ if (input.live && (row.status === "running" || row.status === "queued")) {
263
+ detail.live = input.live.phases.map((p) => ({
264
+ case_id: p.caseId,
265
+ repeat_index: p.repeatIndex,
266
+ phase: p.phase,
267
+ }));
268
+ }
269
+ return detail;
270
+ }
271
+ export async function buildExport(input) {
272
+ const { store, row, what } = input;
273
+ const filename = `${row.evalId}-run-${row.runNumber}-${what}.json`;
274
+ if (what === "config")
275
+ return { filename, body: row.config };
276
+ const [detail, results, reviews] = await Promise.all([
277
+ buildRunDetail({ store, row }),
278
+ store.listEvalResults(row.evalRunId),
279
+ store.getEvalReviews(row.evalRunId),
280
+ ]);
281
+ const reviewByKey = new Map(reviews.map((r) => [resultKey(r.caseId, r.repeatIndex), r]));
282
+ return {
283
+ filename,
284
+ body: {
285
+ ...detail,
286
+ results: results.map((r) => resultToWire(r, reviewByKey.get(resultKey(r.caseId, r.repeatIndex)))),
287
+ },
288
+ };
289
+ }
@@ -0,0 +1,43 @@
1
+ import { type TriageBucket } from "../triage/constants.js";
2
+ import type { SuggestionRow } from "../triage/types.js";
3
+ export type WireSuggestion = {
4
+ id: string;
5
+ case_id: string;
6
+ repeat_index: number;
7
+ source: "jev" | "judge";
8
+ bucket: TriageBucket;
9
+ failure_type: string | null;
10
+ confidence: number;
11
+ bucket_probabilities: Record<TriageBucket, number>;
12
+ grader_false_alarms: Array<{
13
+ grader_id: string;
14
+ probability: number;
15
+ }>;
16
+ model: string;
17
+ prompt_version: number;
18
+ status: "pending" | "accepted" | "dismissed";
19
+ created_at: string;
20
+ };
21
+ export type SuggestionSummary = {
22
+ by_bucket: Record<TriageBucket, number>;
23
+ by_type: Record<string, number>;
24
+ pending: number;
25
+ accepted: number;
26
+ dismissed: number;
27
+ low_confidence: number;
28
+ };
29
+ export declare function suggestionToWire(row: SuggestionRow): WireSuggestion;
30
+ export declare function isLowConfidence(row: Pick<SuggestionRow, "bucket" | "confidence">): boolean;
31
+ /**
32
+ * One suggestion stands for a result: the newest that is not dismissed, else the newest dismissed. A result sorted by
33
+ * both backends therefore counts once. Ties on time keep the later source name so the choice is stable.
34
+ */
35
+ export declare function representatives(rows: readonly SuggestionRow[]): SuggestionRow[];
36
+ /**
37
+ * Counts per result (see `representatives`). `by_bucket`, `by_type` and `low_confidence` count suggestions that are not
38
+ * dismissed; `pending`, `accepted` and `dismissed` count every representative by status. A null failure type is not
39
+ * counted in `by_type`. Low confidence means the bucket is `unclear` or its confidence is below 0.7.
40
+ */
41
+ export declare function buildSuggestionSummary(rows: readonly SuggestionRow[]): SuggestionSummary;
42
+ /** The suggestion shown for one result in the case drawer: its representative, or null. */
43
+ export declare function suggestionFor(rows: readonly SuggestionRow[], caseId: string, repeatIndex: number): WireSuggestion | null;
@@ -0,0 +1,73 @@
1
+ import { LOW_CONFIDENCE_THRESHOLD, TRIAGE_BUCKETS } from "../triage/constants.js";
2
+ export function suggestionToWire(row) {
3
+ return {
4
+ id: row.id,
5
+ case_id: row.caseId,
6
+ repeat_index: row.repeatIndex,
7
+ source: row.source,
8
+ bucket: row.bucket,
9
+ failure_type: row.failureType,
10
+ confidence: row.confidence,
11
+ bucket_probabilities: row.bucketProbabilities,
12
+ grader_false_alarms: row.graderFalseAlarms.map((a) => ({ grader_id: a.graderId, probability: a.probability })),
13
+ model: row.model,
14
+ prompt_version: row.promptVersion,
15
+ status: row.status,
16
+ created_at: row.createdAt,
17
+ };
18
+ }
19
+ export function isLowConfidence(row) {
20
+ return row.bucket === "unclear" || row.confidence < LOW_CONFIDENCE_THRESHOLD;
21
+ }
22
+ /**
23
+ * One suggestion stands for a result: the newest that is not dismissed, else the newest dismissed. A result sorted by
24
+ * both backends therefore counts once. Ties on time keep the later source name so the choice is stable.
25
+ */
26
+ export function representatives(rows) {
27
+ const best = new Map();
28
+ const rank = (row) => [row.status === "dismissed" ? 0 : 1, row.createdAt, row.source];
29
+ for (const row of rows) {
30
+ const key = `${row.caseId}\u0000${row.repeatIndex}`;
31
+ const current = best.get(key);
32
+ if (current === undefined) {
33
+ best.set(key, row);
34
+ continue;
35
+ }
36
+ const [a, b] = [rank(row), rank(current)];
37
+ if (a[0] > b[0] || (a[0] === b[0] && (a[1] > b[1] || (a[1] === b[1] && a[2] > b[2]))))
38
+ best.set(key, row);
39
+ }
40
+ return [...best.values()];
41
+ }
42
+ /**
43
+ * Counts per result (see `representatives`). `by_bucket`, `by_type` and `low_confidence` count suggestions that are not
44
+ * dismissed; `pending`, `accepted` and `dismissed` count every representative by status. A null failure type is not
45
+ * counted in `by_type`. Low confidence means the bucket is `unclear` or its confidence is below 0.7.
46
+ */
47
+ export function buildSuggestionSummary(rows) {
48
+ const summary = {
49
+ by_bucket: Object.fromEntries(TRIAGE_BUCKETS.map((b) => [b, 0])),
50
+ by_type: {},
51
+ pending: 0,
52
+ accepted: 0,
53
+ dismissed: 0,
54
+ low_confidence: 0,
55
+ };
56
+ for (const row of representatives(rows)) {
57
+ summary[row.status] += 1;
58
+ if (row.status === "dismissed")
59
+ continue;
60
+ summary.by_bucket[row.bucket] += 1;
61
+ if (row.failureType !== null)
62
+ summary.by_type[row.failureType] = (summary.by_type[row.failureType] ?? 0) + 1;
63
+ if (isLowConfidence(row))
64
+ summary.low_confidence += 1;
65
+ }
66
+ return summary;
67
+ }
68
+ /** The suggestion shown for one result in the case drawer: its representative, or null. */
69
+ export function suggestionFor(rows, caseId, repeatIndex) {
70
+ const own = rows.filter((r) => r.caseId === caseId && r.repeatIndex === repeatIndex);
71
+ const [rep] = representatives(own);
72
+ return rep === undefined ? null : suggestionToWire(rep);
73
+ }
@@ -0,0 +1,40 @@
1
+ import type { CompleteInput, CompleteResult, ConversationBackend, RuntimeUsage } from "../../agents/runtime/types.js";
2
+ export type FakeToolStep = {
3
+ tool: string;
4
+ input?: Record<string, unknown>;
5
+ /** What the tool does when the gate lets it run. */
6
+ body?: () => string | Promise<string>;
7
+ /** Report the tool as failed when it ran. */
8
+ fails?: boolean;
9
+ };
10
+ export type FakeTurnScript = {
11
+ text?: string;
12
+ tools?: FakeToolStep[];
13
+ usage?: RuntimeUsage;
14
+ /** Wait before replying; an abort ends the wait early. */
15
+ delayMs?: number;
16
+ /** Never reply until the turn is aborted. */
17
+ hang?: boolean;
18
+ /** Throw this from the backend. */
19
+ error?: Error;
20
+ };
21
+ export type FakeTurnReply = FakeTurnScript | ((input: CompleteInput, index: number) => FakeTurnScript);
22
+ export declare const FAKE_USAGE: RuntimeUsage;
23
+ /**
24
+ * A scriptable conversation backend for eval tests. Each model call takes the next scripted turn; tool calls go
25
+ * through the same gate and hooks the real runtime uses, so tool policy is exercised.
26
+ */
27
+ export declare class FakeAgentBackend implements ConversationBackend {
28
+ private readonly script;
29
+ readonly id: "pi";
30
+ readonly inputs: CompleteInput[];
31
+ readonly released: string[];
32
+ /** Tool bodies that actually ran, by grant id. */
33
+ readonly executed: string[];
34
+ private index;
35
+ private callSeq;
36
+ constructor(script?: readonly FakeTurnReply[] | ((input: CompleteInput, index: number) => FakeTurnScript));
37
+ complete(input: CompleteInput): Promise<CompleteResult>;
38
+ release(sessionId: string): Promise<void>;
39
+ get calls(): number;
40
+ }
@@ -0,0 +1,82 @@
1
+ export const FAKE_USAGE = { inputTokens: 100, outputTokens: 20, costUsd: 0.01 };
2
+ function waitOrAbort(ms, signal) {
3
+ return new Promise((resolve) => {
4
+ if (signal.aborted)
5
+ return resolve();
6
+ const done = () => {
7
+ clearTimeout(timer);
8
+ signal.removeEventListener("abort", done);
9
+ resolve();
10
+ };
11
+ const timer = ms === undefined ? undefined : setTimeout(done, ms);
12
+ signal.addEventListener("abort", done, { once: true });
13
+ });
14
+ }
15
+ /**
16
+ * A scriptable conversation backend for eval tests. Each model call takes the next scripted turn; tool calls go
17
+ * through the same gate and hooks the real runtime uses, so tool policy is exercised.
18
+ */
19
+ export class FakeAgentBackend {
20
+ script;
21
+ id = "pi";
22
+ inputs = [];
23
+ released = [];
24
+ /** Tool bodies that actually ran, by grant id. */
25
+ executed = [];
26
+ index = 0;
27
+ callSeq = 0;
28
+ constructor(script = []) {
29
+ this.script = script;
30
+ }
31
+ async complete(input) {
32
+ const index = this.index++;
33
+ this.inputs.push(input);
34
+ const reply = typeof this.script === "function" ? this.script : this.script[index];
35
+ const turn = typeof reply === "function" ? reply(input, index) : (reply ?? { text: `reply ${index + 1}` });
36
+ if (turn.error)
37
+ throw turn.error;
38
+ const usage = turn.usage ?? FAKE_USAGE;
39
+ const toolOutputs = [];
40
+ const startedAt = new Date();
41
+ if (turn.hang)
42
+ await waitOrAbort(undefined, input.signal);
43
+ else if (turn.delayMs !== undefined)
44
+ await waitOrAbort(turn.delayMs, input.signal);
45
+ if (input.signal.aborted)
46
+ return { text: "", usage, toolOutputs };
47
+ for (const step of turn.tools ?? []) {
48
+ const call = { id: `call_${(this.callSeq += 1)}`, tool: step.tool, input: step.input ?? {} };
49
+ const decision = await input.hooks.gate(call);
50
+ if (!decision.allow)
51
+ continue;
52
+ input.hooks.toolStarted(call);
53
+ let output;
54
+ let ok = step.fails !== true;
55
+ if (step.tool.startsWith("agent:")) {
56
+ output = await input.hooks.delegate(step.tool.slice(6), String(call.input.message ?? ""), call.id);
57
+ }
58
+ else {
59
+ try {
60
+ this.executed.push(step.tool);
61
+ output = await (step.body ? step.body() : `ran ${step.tool}`);
62
+ }
63
+ catch (error) {
64
+ ok = false;
65
+ output = error instanceof Error ? error.message : String(error);
66
+ }
67
+ }
68
+ toolOutputs.push(output);
69
+ input.hooks.toolFinished(call, { ok, output });
70
+ }
71
+ const text = turn.text ?? `reply ${index + 1}`;
72
+ input.onDelta(text);
73
+ input.hooks.modelFinished({ startedAt, endedAt: new Date(), ...usage });
74
+ return { text, usage, toolOutputs };
75
+ }
76
+ async release(sessionId) {
77
+ this.released.push(sessionId);
78
+ }
79
+ get calls() {
80
+ return this.index;
81
+ }
82
+ }
@@ -0,0 +1,24 @@
1
+ import type { AgentHostContext } from "../../agents/hostContext.js";
2
+ import type { AgentDefinition } from "../../config/loadAgent.js";
3
+ import { type ModelPrice, type SimulatorModel } from "./simulatorModel.js";
4
+ import type { StageCaseRunner } from "./stageCaseRunner.js";
5
+ export declare const EVAL_CASE_WALL_CLOCK_GRACE_MS = 60000;
6
+ export declare const EVAL_TURN_STOP_GRACE_MS = 10000;
7
+ export type AgentRunnerHost = Pick<AgentHostContext, "service" | "chat" | "evalGates" | "backend">;
8
+ export type AgentCaseRunnerDeps = {
9
+ /** Lazy: the manager is built before the agent host. Undefined means this Host cannot run agents. */
10
+ host: () => AgentRunnerHost | undefined;
11
+ /** Builds the simulated user's model; defaults to the agent backend's one-shot call on `plan.userModel`. */
12
+ simulatorModel?: (input: {
13
+ projectRoot: string;
14
+ definition: AgentDefinition;
15
+ modelId: string;
16
+ host: AgentRunnerHost;
17
+ }) => SimulatorModel;
18
+ /** USD per million tokens by model id, for pricing simulator calls the backend reports no cost for. */
19
+ loadPrices?: (modelIds: readonly string[], projectRoot: string) => Promise<Map<string, ModelPrice>>;
20
+ now?: () => number;
21
+ wallClockGraceMs?: number;
22
+ stopGraceMs?: number;
23
+ };
24
+ export declare function createAgentCaseRunner(deps: AgentCaseRunnerDeps): StageCaseRunner;