stageflow 0.42.0 → 0.46.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 (315) hide show
  1. package/README.md +12 -3
  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 +1 -0
  23. package/dist/agents/authorChat.js +7 -0
  24. package/dist/agents/authorSessionStore.d.ts +3 -0
  25. package/dist/agents/authorSessionStore.js +17 -2
  26. package/dist/agents/evalGate.js +1 -0
  27. package/dist/agents/hostContext.d.ts +1 -1
  28. package/dist/agents/runtime/piRuntime.d.ts +5 -1
  29. package/dist/agents/runtime/piRuntime.js +46 -12
  30. package/dist/agents/service.js +3 -2
  31. package/dist/agents/sessions.js +3 -1
  32. package/dist/cli/evalCommand.d.ts +1 -1
  33. package/dist/cli/evalCommand.js +39 -2
  34. package/dist/cli/hostClient.d.ts +6 -0
  35. package/dist/cli/hostClient.js +4 -0
  36. package/dist/compaction/directives.d.ts +18 -0
  37. package/dist/compaction/directives.js +119 -0
  38. package/dist/compaction/extension.d.ts +20 -0
  39. package/dist/compaction/extension.js +78 -0
  40. package/dist/compaction/mask.d.ts +18 -0
  41. package/dist/compaction/mask.js +155 -0
  42. package/dist/compaction/pin.d.ts +21 -0
  43. package/dist/compaction/pin.js +270 -0
  44. package/dist/compaction/scrub.d.ts +7 -0
  45. package/dist/compaction/scrub.js +26 -0
  46. package/dist/compaction/sessionWiring.d.ts +22 -0
  47. package/dist/compaction/sessionWiring.js +26 -0
  48. package/dist/compaction/summarize.d.ts +79 -0
  49. package/dist/compaction/summarize.js +171 -0
  50. package/dist/compaction/toolPolicy.d.ts +3 -0
  51. package/dist/compaction/toolPolicy.js +33 -0
  52. package/dist/compaction/types.d.ts +45 -0
  53. package/dist/compaction/types.js +7 -0
  54. package/dist/compaction/workingState.d.ts +56 -0
  55. package/dist/compaction/workingState.js +198 -0
  56. package/dist/config/createPipeline.js +2 -0
  57. package/dist/config/createTrigger.js +7 -1
  58. package/dist/config/ensureCatalogScanRoot.d.ts +2 -0
  59. package/dist/config/ensureCatalogScanRoot.js +6 -0
  60. package/dist/config/evalCatalog.js +30 -2
  61. package/dist/config/githubTriggerAction.d.ts +8 -0
  62. package/dist/config/githubTriggerAction.js +29 -0
  63. package/dist/config/hostConfig.js +2 -0
  64. package/dist/config/loadEval.d.ts +3 -1
  65. package/dist/config/loadEval.js +96 -10
  66. package/dist/config/pipelineNeeds.d.ts +2 -0
  67. package/dist/config/pipelineNeeds.js +16 -3
  68. package/dist/config/pipelineRoute.js +5 -4
  69. package/dist/config/validateCatalog.d.ts +1 -1
  70. package/dist/config/validateCatalog.js +12 -0
  71. package/dist/config/validateEval.js +60 -3
  72. package/dist/config/writeEval.js +2 -2
  73. package/dist/envelope/assertEmittedArtifacts.d.ts +12 -0
  74. package/dist/envelope/assertEmittedArtifacts.js +43 -0
  75. package/dist/envelope/preEmitChecks.d.ts +3 -0
  76. package/dist/evals/calibration.d.ts +21 -0
  77. package/dist/evals/calibration.js +69 -0
  78. package/dist/evals/calibrationSuggest.d.ts +29 -0
  79. package/dist/evals/calibrationSuggest.js +72 -0
  80. package/dist/evals/calibrator.d.ts +46 -0
  81. package/dist/evals/calibrator.js +148 -0
  82. package/dist/evals/compare.d.ts +1 -1
  83. package/dist/evals/compare.js +3 -0
  84. package/dist/evals/constants.d.ts +7 -3
  85. package/dist/evals/constants.js +7 -1
  86. package/dist/evals/engine/FakeEngine.d.ts +2 -1
  87. package/dist/evals/engine/FakeEngine.js +9 -3
  88. package/dist/evals/engine/promptfoo/PromptfooEngine.d.ts +1 -1
  89. package/dist/evals/engine/promptfoo/PromptfooEngine.js +4 -2
  90. package/dist/evals/engine/promptfoo/generate.d.ts +3 -1
  91. package/dist/evals/engine/promptfoo/generate.js +43 -23
  92. package/dist/evals/engine/promptfoo/judgeProviders.js +2 -1
  93. package/dist/evals/engine/promptfoo/normalize.d.ts +4 -1
  94. package/dist/evals/engine/promptfoo/normalize.js +16 -3
  95. package/dist/evals/engine/promptfoo/rubricPrompt.d.ts +3 -1
  96. package/dist/evals/engine/promptfoo/rubricPrompt.js +15 -5
  97. package/dist/evals/errors.d.ts +7 -0
  98. package/dist/evals/errors.js +10 -0
  99. package/dist/evals/judgeProviders.d.ts +6 -0
  100. package/dist/evals/judgeProviders.js +14 -0
  101. package/dist/evals/judged.d.ts +7 -0
  102. package/dist/evals/judged.js +7 -0
  103. package/dist/evals/manager.d.ts +27 -8
  104. package/dist/evals/manager.js +73 -12
  105. package/dist/evals/plan.d.ts +10 -1
  106. package/dist/evals/plan.js +33 -13
  107. package/dist/evals/planInternalAgent.d.ts +19 -0
  108. package/dist/evals/planInternalAgent.js +201 -0
  109. package/dist/evals/read/api.d.ts +18 -0
  110. package/dist/evals/read/api.js +10 -1
  111. package/dist/evals/read/labelQueue.d.ts +45 -0
  112. package/dist/evals/read/labelQueue.js +83 -0
  113. package/dist/evals/read/list.js +10 -1
  114. package/dist/evals/read/runs.d.ts +5 -0
  115. package/dist/evals/read/runs.js +7 -1
  116. package/dist/evals/runner/caseRunner.d.ts +2 -1
  117. package/dist/evals/runner/caseRunner.js +16 -5
  118. package/dist/evals/runner/internalAgentCaseRunner.d.ts +6 -0
  119. package/dist/evals/runner/internalAgentCaseRunner.js +250 -0
  120. package/dist/evals/snapshot.d.ts +10 -2
  121. package/dist/evals/snapshot.js +8 -3
  122. package/dist/evals/stats.d.ts +40 -0
  123. package/dist/evals/stats.js +69 -0
  124. package/dist/evals/storeTypes.d.ts +42 -0
  125. package/dist/evals/types.d.ts +25 -2
  126. package/dist/evals/types.js +4 -1
  127. package/dist/evals/votes.d.ts +10 -0
  128. package/dist/evals/votes.js +60 -0
  129. package/dist/guide/cardContract.d.ts +133 -0
  130. package/dist/guide/cardContract.js +156 -0
  131. package/dist/guide/chat.d.ts +45 -0
  132. package/dist/guide/chat.js +378 -0
  133. package/dist/guide/knowledge/index.d.ts +14 -0
  134. package/dist/guide/knowledge/index.js +109 -0
  135. package/dist/guide/knowledge/topics/agents.d.ts +2 -0
  136. package/dist/guide/knowledge/topics/agents.js +58 -0
  137. package/dist/guide/knowledge/topics/bestPractices.d.ts +2 -0
  138. package/dist/guide/knowledge/topics/bestPractices.js +67 -0
  139. package/dist/guide/knowledge/topics/connections.d.ts +2 -0
  140. package/dist/guide/knowledge/topics/connections.js +66 -0
  141. package/dist/guide/knowledge/topics/consoleMap.d.ts +2 -0
  142. package/dist/guide/knowledge/topics/consoleMap.js +55 -0
  143. package/dist/guide/knowledge/topics/evals.d.ts +2 -0
  144. package/dist/guide/knowledge/topics/evals.js +72 -0
  145. package/dist/guide/knowledge/topics/limitations.d.ts +2 -0
  146. package/dist/guide/knowledge/topics/limitations.js +84 -0
  147. package/dist/guide/knowledge/topics/mcpSkillsBrowser.d.ts +2 -0
  148. package/dist/guide/knowledge/topics/mcpSkillsBrowser.js +57 -0
  149. package/dist/guide/knowledge/topics/memories.d.ts +2 -0
  150. package/dist/guide/knowledge/topics/memories.js +63 -0
  151. package/dist/guide/knowledge/topics/modelsAndProviders.d.ts +2 -0
  152. package/dist/guide/knowledge/topics/modelsAndProviders.js +56 -0
  153. package/dist/guide/knowledge/topics/overview.d.ts +2 -0
  154. package/dist/guide/knowledge/topics/overview.js +56 -0
  155. package/dist/guide/knowledge/topics/pipelines.d.ts +2 -0
  156. package/dist/guide/knowledge/topics/pipelines.js +93 -0
  157. package/dist/guide/knowledge/topics/stages.d.ts +2 -0
  158. package/dist/guide/knowledge/topics/stages.js +54 -0
  159. package/dist/guide/knowledge/topics/tasks.d.ts +2 -0
  160. package/dist/guide/knowledge/topics/tasks.js +52 -0
  161. package/dist/guide/knowledge/topics/triggers.d.ts +2 -0
  162. package/dist/guide/knowledge/topics/triggers.js +68 -0
  163. package/dist/guide/knowledge/topics/troubleshooting.d.ts +2 -0
  164. package/dist/guide/knowledge/topics/troubleshooting.js +85 -0
  165. package/dist/guide/knowledgeTools.d.ts +2 -0
  166. package/dist/guide/knowledgeTools.js +56 -0
  167. package/dist/guide/showUi.d.ts +6 -0
  168. package/dist/guide/showUi.js +546 -0
  169. package/dist/guide/threadStore.d.ts +48 -0
  170. package/dist/guide/threadStore.js +189 -0
  171. package/dist/guide/writeRoot.d.ts +11 -0
  172. package/dist/guide/writeRoot.js +70 -0
  173. package/dist/hosted/translate.js +2 -0
  174. package/dist/mcp/agentTools.js +12 -0
  175. package/dist/mcp/catalogTools.js +18 -20
  176. package/dist/mcp/catalogWriteTools.d.ts +8 -0
  177. package/dist/mcp/catalogWriteTools.js +225 -0
  178. package/dist/mcp/connectionTools.d.ts +3 -0
  179. package/dist/mcp/connectionTools.js +85 -0
  180. package/dist/mcp/controlTools.js +34 -1
  181. package/dist/mcp/deps.d.ts +9 -0
  182. package/dist/mcp/evalTools.js +723 -21
  183. package/dist/mcp/memoryHostTools.d.ts +3 -0
  184. package/dist/mcp/memoryHostTools.js +72 -0
  185. package/dist/mcp/pipelineAuthorTools.js +14 -1
  186. package/dist/mcp/server.js +5 -0
  187. package/dist/mcp/tools.js +6 -0
  188. package/dist/mcp/triggerTools.js +77 -31
  189. package/dist/operatorAgent/evalGate.d.ts +22 -0
  190. package/dist/operatorAgent/evalGate.js +77 -0
  191. package/dist/operatorAgent/fakeHost.js +10 -2
  192. package/dist/operatorAgent/host.js +18 -2
  193. package/dist/operatorAgent/index.d.ts +1 -0
  194. package/dist/operatorAgent/index.js +1 -0
  195. package/dist/operatorAgent/internalAgents.d.ts +11 -0
  196. package/dist/operatorAgent/internalAgents.js +13 -0
  197. package/dist/operatorAgent/piModel.d.ts +5 -1
  198. package/dist/operatorAgent/piModel.js +15 -10
  199. package/dist/operatorAgent/profiles/agentAuthor.js +66 -22
  200. package/dist/operatorAgent/profiles/agentAuthorPlaybook.d.ts +1 -1
  201. package/dist/operatorAgent/profiles/agentAuthorPlaybook.js +1 -1
  202. package/dist/operatorAgent/profiles/guideConfirm.d.ts +21 -0
  203. package/dist/operatorAgent/profiles/guideConfirm.js +100 -0
  204. package/dist/operatorAgent/profiles/guideTools.d.ts +7 -0
  205. package/dist/operatorAgent/profiles/guideTools.js +123 -0
  206. package/dist/operatorAgent/profiles/stageflowGuide.d.ts +7 -0
  207. package/dist/operatorAgent/profiles/stageflowGuide.js +133 -0
  208. package/dist/operatorAgent/profiles/workshopAuthor.js +23 -0
  209. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +1 -1
  210. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +9 -1
  211. package/dist/operatorAgent/profiles/workshopAuthorTools.d.ts +2 -0
  212. package/dist/operatorAgent/profiles/workshopAuthorTools.js +197 -9
  213. package/dist/operatorAgent/types.d.ts +23 -0
  214. package/dist/package-meta.d.ts +1 -1
  215. package/dist/package-meta.js +1 -1
  216. package/dist/runstore/port.d.ts +13 -1
  217. package/dist/runstore/retention.d.ts +2 -1
  218. package/dist/runstore/retention.js +3 -7
  219. package/dist/runstore/runStatusClock.d.ts +17 -0
  220. package/dist/runstore/runStatusClock.js +19 -0
  221. package/dist/runstore/sqlite/SqliteRunStore.d.ts +11 -1
  222. package/dist/runstore/sqlite/SqliteRunStore.js +70 -45
  223. package/dist/runstore/sqlite/calibrationStore.d.ts +10 -0
  224. package/dist/runstore/sqlite/calibrationStore.js +106 -0
  225. package/dist/runstore/sqlite/evalStore.d.ts +5 -0
  226. package/dist/runstore/sqlite/evalStore.js +7 -0
  227. package/dist/runstore/sqlite/migrations/018-eval-grader-labels.d.ts +11 -0
  228. package/dist/runstore/sqlite/migrations/018-eval-grader-labels.js +43 -0
  229. package/dist/runstore/sqlite/migrations/index.js +2 -0
  230. package/dist/runstore/workspaceLayout.js +6 -1
  231. package/dist/runtime/artifactDiskInspect.d.ts +14 -0
  232. package/dist/runtime/artifactDiskInspect.js +52 -0
  233. package/dist/runtime/artifactPath.d.ts +24 -0
  234. package/dist/runtime/artifactPath.js +84 -0
  235. package/dist/runtime/completionCheckRunner.js +15 -43
  236. package/dist/runtime/githubPollSource.d.ts +22 -4
  237. package/dist/runtime/githubPollSource.js +93 -8
  238. package/dist/runtime/priorArtifactGrants.d.ts +8 -0
  239. package/dist/runtime/priorArtifactGrants.js +105 -0
  240. package/dist/runtime/runActionEligibility.d.ts +46 -0
  241. package/dist/runtime/runActionEligibility.js +123 -0
  242. package/dist/runtime/runManager.d.ts +4 -1
  243. package/dist/runtime/runManager.js +52 -10
  244. package/dist/runtime/runRetryCoordinator.d.ts +2 -14
  245. package/dist/runtime/runRetryCoordinator.js +3 -67
  246. package/dist/runtime/sandboxContainer.d.ts +7 -0
  247. package/dist/runtime/sandboxContainer.js +48 -6
  248. package/dist/runtime/sandboxDesk.d.ts +2 -1
  249. package/dist/runtime/sandboxDesk.js +11 -12
  250. package/dist/runtime/stageEnvironment.js +1 -0
  251. package/dist/runtime/stageRoots.d.ts +12 -0
  252. package/dist/runtime/stageSandbox.d.ts +2 -1
  253. package/dist/runtime/stageSandbox.js +18 -2
  254. package/dist/runtime/stageWorker.js +20 -0
  255. package/dist/server/agentAuthorRoutes.js +12 -3
  256. package/dist/server/bootstrap.d.ts +5 -0
  257. package/dist/server/bootstrap.js +14 -1
  258. package/dist/server/evalRoutes.js +251 -12
  259. package/dist/server/guideRoutes.d.ts +5 -0
  260. package/dist/server/guideRoutes.js +166 -0
  261. package/dist/server/http.d.ts +10 -0
  262. package/dist/server/http.js +35 -1
  263. package/dist/server/internalAgentRoutes.d.ts +6 -0
  264. package/dist/server/internalAgentRoutes.js +12 -0
  265. package/dist/server/mcpHost.js +2 -0
  266. package/dist/toolkit/meta.d.ts +1 -1
  267. package/dist/toolkit/meta.js +41 -1
  268. package/dist/toolkit/profiles.d.ts +5 -0
  269. package/dist/toolkit/profiles.js +12 -0
  270. package/dist/tools/askOperator.js +2 -1
  271. package/dist/tools/emitStageEnvelope.d.ts +17 -16
  272. package/dist/tools/emitStageEnvelope.js +46 -32
  273. package/dist/tools/stageToolNames.d.ts +4 -0
  274. package/dist/tools/stageToolNames.js +4 -0
  275. package/dist/tools/workingState.d.ts +40 -0
  276. package/dist/tools/workingState.js +108 -0
  277. package/dist/tools/writeStageArtifact.d.ts +0 -9
  278. package/dist/tools/writeStageArtifact.js +2 -1
  279. package/dist/ui/assets/{MarkdownText-BTrOqu5J.js → MarkdownText-BS2BBbCx.js} +2 -2
  280. package/dist/ui/assets/adapters-DPD-cAsy.js +17 -0
  281. package/dist/ui/assets/chat-BD1-VTT6.js +2 -0
  282. package/dist/ui/assets/chat-DXnQv9st.css +2 -0
  283. package/dist/ui/assets/main-AX5XNLtX.js +278 -0
  284. package/dist/ui/assets/{main-CHlmTNJe.css → main-CFX0_n8J.css} +1 -1
  285. package/dist/ui/chat.html +3 -4
  286. package/dist/ui/index.html +3 -4
  287. package/dist/workshop/chatTurn.js +1 -1
  288. package/dist/workshop/workshopPin.d.ts +3 -3
  289. package/dist/workshop/workshopPin.js +16 -15
  290. package/examples/compaction-stress/README.md +30 -0
  291. package/examples/compaction-stress/check-run.mjs +53 -0
  292. package/examples/compaction-stress/collect-evidence.yaml +60 -0
  293. package/examples/compaction-stress/compaction-stress.pipeline.yaml +141 -0
  294. package/examples/compaction-stress/compaction-stress.task.yaml +15 -0
  295. package/examples/compaction-stress/generate-evidence.mjs +110 -0
  296. package/examples/compaction-stress/root-cause.yaml +26 -0
  297. package/examples/compaction-stress/stageflow.yaml +9 -0
  298. package/examples/compaction-stress/write-postmortem.yaml +40 -0
  299. package/examples/evals/README.md +18 -0
  300. package/examples/evals/guide-behavior.dataset.jsonl +15 -0
  301. package/examples/evals/guide-behavior.eval.yaml +35 -0
  302. package/examples/evals/guide-depth.dataset.jsonl +35 -0
  303. package/examples/evals/guide-depth.eval.yaml +169 -0
  304. package/examples/memory/evals/guide-behavior.dataset.jsonl +15 -0
  305. package/examples/memory/evals/guide-behavior.eval.yaml +35 -0
  306. package/examples/memory/evals/guide-depth.dataset.jsonl +35 -0
  307. package/examples/memory/evals/guide-depth.eval.yaml +169 -0
  308. package/package.json +1 -1
  309. package/skills/stageflow/references/control-surface.md +1 -1
  310. package/skills/stageflow/references/evals.md +1 -0
  311. package/dist/ui/assets/AssistantRuntimeProvider-qIX1xNoH.js +0 -9
  312. package/dist/ui/assets/ReadonlyStore-BfXqHykm.js +0 -6
  313. package/dist/ui/assets/chat-Df_wCpak.js +0 -5
  314. package/dist/ui/assets/chat-P7KQznHm.css +0 -2
  315. package/dist/ui/assets/main-CvVfxmMd.js +0 -271
@@ -11,6 +11,9 @@ import { EVAL_LARGE_RUN, EVAL_TOO_LARGE_RUN, evalFinding, evalFindingsFromIssues
11
11
  import { canonicalJson, sha256Hex } from "./canonical.js";
12
12
  import { EVAL_LIMITS, SIMULATOR_PROMPT_VERSION } from "./constants.js";
13
13
  import { buildComponents, buildSnapshot, computeFingerprint, } from "./snapshot.js";
14
+ import { planInternalAgent } from "./planInternalAgent.js";
15
+ import { isConversationTarget } from "./types.js";
16
+ import { isJudged, isJudgedType } from "./judged.js";
14
17
  const SELECTION_KEYS = ["case_ids", "tags", "only_failed_from"];
15
18
  const RUN_SIZE_CODES = new Set(["eval.large_run", "eval.too_large_run"]);
16
19
  function posix(projectRoot, abs) {
@@ -58,7 +61,7 @@ function checkConversationOverrides(relPath, file, overrides) {
58
61
  const keys = ["user_model", "tools", "approvals"].filter((key) => overrides[key] !== undefined);
59
62
  if (keys.length === 0)
60
63
  return out;
61
- if (file.target.kind !== "agent") {
64
+ if (!isConversationTarget(file.target.kind)) {
62
65
  out.push(evalFinding(relPath, "error", "eval.conversation_not_applicable", `Override ${keys.map((k) => `"${k}"`).join(", ")} applies to agent targets only`));
63
66
  return out;
64
67
  }
@@ -104,10 +107,10 @@ function withOverrides(loaded, overrides) {
104
107
  ...(overrides.max_cost_usd !== undefined ? { max_cost_usd: overrides.max_cost_usd } : {}),
105
108
  };
106
109
  const graders = loaded.graders.map((grader) => {
107
- if ((grader.type !== "rubric" && grader.type !== "goal_reached") || overrides.judge === undefined)
110
+ if (!isJudged(grader) || overrides.judge === undefined)
108
111
  return grader;
109
112
  const raw = loaded.file.graders.find((g) => g.id === grader.id);
110
- if ((raw?.type === "rubric" || raw?.type === "goal_reached") && raw.judge !== undefined)
113
+ if (raw !== undefined && isJudgedType(raw.type) && "judge" in raw && raw.judge !== undefined)
111
114
  return grader;
112
115
  const { hash: _old, ...rest } = { ...grader, judge: overrides.judge };
113
116
  return { ...rest, hash: graderHash(rest) };
@@ -131,7 +134,7 @@ function selectCases(cases, overrides, failedIds) {
131
134
  return cases.filter((c) => failedIds.has(c.id));
132
135
  return [...cases];
133
136
  }
134
- function evalDefinition(loaded, projectRoot) {
137
+ export function evalDefinition(loaded, projectRoot) {
135
138
  const { file } = loaded;
136
139
  const paths = resolveEvalPaths(file, loaded.path);
137
140
  const rel = (abs) => abs === undefined ? undefined : posix(projectRoot, abs);
@@ -145,12 +148,14 @@ function evalDefinition(loaded, projectRoot) {
145
148
  type: file.type ?? "capability",
146
149
  target: file.target.kind === "agent"
147
150
  ? { kind: "agent", agent: file.target.agent }
148
- : {
149
- kind: file.target.kind,
150
- pipeline: rel(paths.pipelinePath),
151
- stage: stageTarget?.stage,
152
- file: rel(paths.stageFile),
153
- },
151
+ : file.target.kind === "internal_agent"
152
+ ? { kind: "internal_agent", profile: file.target.profile }
153
+ : {
154
+ kind: file.target.kind,
155
+ pipeline: rel(paths.pipelinePath),
156
+ stage: stageTarget?.stage,
157
+ file: rel(paths.stageFile),
158
+ },
154
159
  dataset: rel(paths.datasetPath),
155
160
  task: file.task,
156
161
  defaults: {
@@ -160,9 +165,9 @@ function evalDefinition(loaded, projectRoot) {
160
165
  max_cost_usd: defaults.max_cost_usd ?? EVAL_DEFAULT_MAX_COST_USD,
161
166
  concurrency: defaults.concurrency ?? EVAL_DEFAULT_CONCURRENCY,
162
167
  model: defaults.model,
163
- ...(file.target.kind === "agent" && defaults.user_model !== undefined ? { user_model: defaults.user_model } : {}),
168
+ ...(isConversationTarget(file.target.kind) && defaults.user_model !== undefined ? { user_model: defaults.user_model } : {}),
164
169
  },
165
- ...(file.target.kind === "agent" ? { conversation: file.conversation } : {}),
170
+ ...(isConversationTarget(file.target.kind) ? { conversation: file.conversation } : {}),
166
171
  graders: file.graders.map((g) => ({ ...g, weight: g.weight ?? 1, must_pass: g.must_pass ?? false })),
167
172
  budget: file.budget,
168
173
  }));
@@ -227,6 +232,21 @@ export async function buildEvalPlan(input) {
227
232
  const findings = [...validation.filter((f) => !RUN_SIZE_CODES.has(f.code)), ...dataset.findings];
228
233
  if (findings.some((f) => f.severity === "error"))
229
234
  return fail(findings);
235
+ if (file.target.kind === "internal_agent") {
236
+ return planInternalAgent({
237
+ input,
238
+ loaded,
239
+ dataset: {
240
+ path: paths.datasetPath,
241
+ contentHash: dataset.contentHash,
242
+ format: dataset.format,
243
+ cases: dataset.cases,
244
+ },
245
+ findings,
246
+ overrides,
247
+ ...(failedIds ? { failedIds } : {}),
248
+ });
249
+ }
230
250
  let agentTarget;
231
251
  let stageTarget;
232
252
  if (file.target.kind === "agent") {
@@ -489,7 +509,7 @@ async function buildAgentResult(a) {
489
509
  });
490
510
  const fingerprint = computeFingerprint(components);
491
511
  const { overrides } = a;
492
- const hasRubric = loaded.graders.some((g) => g.type === "rubric" || g.type === "goal_reached");
512
+ const hasRubric = loaded.graders.some(isJudged);
493
513
  const plainDefinition = JSON.parse(JSON.stringify(definition));
494
514
  const snapshot = buildSnapshot({
495
515
  evalId: loaded.id,
@@ -0,0 +1,19 @@
1
+ import type { DatasetCase } from "../config/datasetTypes.js";
2
+ import { type LoadedEval } from "../config/loadEval.js";
3
+ import type { ValidationFinding } from "../config/validateCatalog.js";
4
+ import { type BuildEvalPlanInput, type BuildEvalPlanResult, type EvalOverrides } from "./plan.js";
5
+ type Dataset = {
6
+ path: string;
7
+ contentHash: string;
8
+ format?: string;
9
+ cases: DatasetCase[];
10
+ };
11
+ export declare function planInternalAgent(args: {
12
+ input: BuildEvalPlanInput;
13
+ loaded: LoadedEval;
14
+ dataset: Dataset;
15
+ findings: ValidationFinding[];
16
+ overrides: EvalOverrides;
17
+ failedIds?: Set<string>;
18
+ }): BuildEvalPlanResult;
19
+ export {};
@@ -0,0 +1,201 @@
1
+ import path from "node:path";
2
+ import { checkDatasetAgainstAgent } from "../config/datasetAgentCheck.js";
3
+ import { EVAL_AGENT_DEFAULT_CONCURRENCY, EVAL_AGENT_DEFAULT_MAX_COST_USD, EVAL_DEFAULT_PASS_THRESHOLD, EVAL_DEFAULT_REPEATS, resolveConversation, resolveEvalPaths, } from "../config/loadEval.js";
4
+ import { EVAL_LARGE_RUN, EVAL_TOO_LARGE_RUN, evalFinding } from "../config/validateEval.js";
5
+ import { PACKAGE_VERSION } from "../package-meta.js";
6
+ import { sha256Hex } from "./canonical.js";
7
+ import { EVAL_LIMITS, SIMULATOR_PROMPT_VERSION } from "./constants.js";
8
+ import { evalDefinition } from "./plan.js";
9
+ import { buildComponents, buildSnapshot, computeFingerprint } from "./snapshot.js";
10
+ function posix(projectRoot, abs) {
11
+ return path.relative(projectRoot, abs).split(path.sep).join("/");
12
+ }
13
+ function selectCases(cases, overrides, failedIds) {
14
+ if (overrides.case_ids !== undefined)
15
+ return cases.filter((c) => overrides.case_ids.includes(c.id));
16
+ if (overrides.tags !== undefined) {
17
+ const wanted = new Set(overrides.tags);
18
+ return cases.filter((c) => (c.tags ?? []).some((tag) => wanted.has(tag)));
19
+ }
20
+ if (failedIds !== undefined)
21
+ return cases.filter((c) => failedIds.has(c.id));
22
+ return [...cases];
23
+ }
24
+ export function planInternalAgent(args) {
25
+ const { input, loaded, dataset, overrides, failedIds } = args;
26
+ const findings = [...args.findings];
27
+ const fail = (extra) => ({ ok: false, code: "EVAL_INVALID", findings: [...findings, ...extra] });
28
+ if (loaded.file.target.kind !== "internal_agent")
29
+ return fail([]);
30
+ const profileId = loaded.file.target.profile;
31
+ const profiles = input.internalAgents ?? [];
32
+ const profile = profiles.find((item) => item.id === profileId);
33
+ if (!profile) {
34
+ return fail([
35
+ evalFinding(loaded.relPath, "error", profiles.length === 0 ? "eval.internal_agent_unavailable" : "eval.internal_agent_unknown", profiles.length === 0
36
+ ? "This Host is not showing internal agents, so this eval cannot run"
37
+ : `This Host has no internal agent "${profileId}"`),
38
+ ]);
39
+ }
40
+ if (overrides.case_ids !== undefined) {
41
+ const known = new Set(dataset.cases.map((c) => c.id));
42
+ const unknown = overrides.case_ids.filter((id) => !known.has(id));
43
+ if (unknown.length > 0) {
44
+ return fail([
45
+ evalFinding(loaded.relPath, "error", "eval.invalid_field", `Override "case_ids" names ${unknown.length} case(s) not in the dataset; first: ${unknown[0]}`),
46
+ ]);
47
+ }
48
+ }
49
+ const conversation = resolveConversation(loaded.file.conversation);
50
+ if (conversation.tools === "real") {
51
+ return fail([
52
+ evalFinding(loaded.relPath, "error", "eval.internal_agent_real_tools", "An internal agent eval cannot use real tools"),
53
+ ]);
54
+ }
55
+ const checks = new Map(checkDatasetAgainstAgent(dataset.cases, { mode: conversation.mode }).map((r) => [r.caseId, r.valid]));
56
+ const validCases = dataset.cases.filter((c) => checks.get(c.id) !== false);
57
+ const selected = selectCases(dataset.cases, overrides, failedIds);
58
+ const skippedCaseIds = selected.filter((c) => checks.get(c.id) === false).map((c) => c.id);
59
+ const runCases = selected.filter((c) => checks.get(c.id) !== false);
60
+ const positionOf = new Map(dataset.cases.map((c, i) => [c.id, i]));
61
+ const planCases = runCases.map((c) => ({
62
+ id: c.id,
63
+ position: positionOf.get(c.id) ?? 0,
64
+ ...(c.title !== undefined ? { title: c.title } : {}),
65
+ ...(c.goal !== undefined ? { goal: c.goal } : {}),
66
+ input: c.input,
67
+ ...(c.expected !== undefined ? { expected: c.expected } : {}),
68
+ tags: c.tags ?? [],
69
+ ...(c.metadata !== undefined ? { metadata: c.metadata } : {}),
70
+ }));
71
+ const repeats = loaded.file.defaults?.repeats ?? EVAL_DEFAULT_REPEATS;
72
+ const results = planCases.length * repeats;
73
+ if (planCases.length === 0) {
74
+ return fail([evalFinding(loaded.relPath, "error", "eval.no_valid_cases", "No valid cases are left to run after case selection and skipping")]);
75
+ }
76
+ if (results > EVAL_LIMITS.maxResultsPerRun) {
77
+ return fail([
78
+ evalFinding(loaded.relPath, "error", "eval.too_large_run", `${planCases.length} case(s) x ${repeats} repeat(s) = ${results} results, over the ${EVAL_TOO_LARGE_RUN} limit`),
79
+ ]);
80
+ }
81
+ if (results > EVAL_LARGE_RUN) {
82
+ findings.push(evalFinding(loaded.relPath, "warning", "eval.large_run", `${planCases.length} case(s) x ${repeats} repeat(s) = ${results} results, over ${EVAL_LARGE_RUN}`));
83
+ }
84
+ const defaults = loaded.file.defaults ?? {};
85
+ const passThreshold = defaults.pass_threshold ?? EVAL_DEFAULT_PASS_THRESHOLD;
86
+ const concurrency = defaults.concurrency ?? EVAL_AGENT_DEFAULT_CONCURRENCY;
87
+ const maxCostUsd = defaults.max_cost_usd ?? EVAL_AGENT_DEFAULT_MAX_COST_USD;
88
+ const judge = defaults.judge ?? "";
89
+ const model = defaults.model ?? "";
90
+ const playbookHash = sha256Hex(profile.playbook);
91
+ const definitionHash = sha256Hex(JSON.stringify({ playbook: profile.playbook, tools: profile.tools }));
92
+ const userModel = planCases.some((c) => c.input.mode === "simulated") ? (defaults.user_model ?? null) : null;
93
+ const paths = resolveEvalPaths(loaded.file, loaded.path);
94
+ const projectRoot = input.projectRoot;
95
+ const components = buildComponents({
96
+ stageVersionHash: definitionHash,
97
+ model,
98
+ datasetHash: dataset.contentHash,
99
+ caseIds: runCases.length < validCases.length ? runCases.map((c) => c.id) : null,
100
+ graders: loaded.graders,
101
+ judge,
102
+ repeats,
103
+ passThreshold,
104
+ engine: input.engine,
105
+ agent: {
106
+ revision: 0,
107
+ mode: conversation.mode,
108
+ max_turns: conversation.maxTurns,
109
+ turn_timeout_ms: conversation.turnTimeoutMs,
110
+ tools: conversation.tools,
111
+ approvals: conversation.approvals,
112
+ read_only_tools: conversation.readOnlyTools,
113
+ user_model: userModel,
114
+ simulator_prompt_version: SIMULATOR_PROMPT_VERSION,
115
+ },
116
+ });
117
+ const fingerprint = computeFingerprint(components);
118
+ const snapshotOverrides = {
119
+ repeats: overrides.repeats ?? null,
120
+ judge: overrides.judge ?? null,
121
+ model: overrides.model ?? null,
122
+ max_cost_usd: overrides.max_cost_usd ?? null,
123
+ case_ids: overrides.case_ids ?? null,
124
+ tags: overrides.tags ?? null,
125
+ only_failed_from: overrides.only_failed_from ?? null,
126
+ };
127
+ const snapshot = buildSnapshot({
128
+ evalId: loaded.id,
129
+ evalPath: loaded.relPath,
130
+ evalHash: loaded.fileHash,
131
+ evalDefinition: evalDefinition(loaded, projectRoot),
132
+ overrides: snapshotOverrides,
133
+ target: {
134
+ profile_id: profileId,
135
+ title: profile.title,
136
+ playbook_hash: playbookHash,
137
+ tools: profile.tools,
138
+ model,
139
+ },
140
+ dataset: {
141
+ path: posix(projectRoot, paths.datasetPath),
142
+ hash: dataset.contentHash,
143
+ format: dataset.format ?? "jsonl",
144
+ case_count: dataset.cases.length,
145
+ cases_run: planCases.length,
146
+ cases_skipped: skippedCaseIds.length,
147
+ skipped_case_ids: skippedCaseIds,
148
+ },
149
+ graders: loaded.graders.map((g) => ({
150
+ id: g.id,
151
+ type: g.type,
152
+ hash: g.hash,
153
+ version_ordinal: input.graderOrdinals?.[g.id] ?? 1,
154
+ weight: g.weight,
155
+ must_pass: g.mustPass,
156
+ })),
157
+ settings: {
158
+ repeats,
159
+ pass_threshold: passThreshold,
160
+ judge,
161
+ judge_same_as_stage: false,
162
+ max_cost_usd: maxCostUsd,
163
+ concurrency,
164
+ suite_type: loaded.file.type ?? "capability",
165
+ conversation: {
166
+ mode: conversation.mode,
167
+ max_turns: conversation.maxTurns,
168
+ turn_timeout_ms: conversation.turnTimeoutMs,
169
+ tools: conversation.tools,
170
+ approvals: conversation.approvals,
171
+ read_only_tools: conversation.readOnlyTools,
172
+ real_tools_confirmed: false,
173
+ user_model: userModel ?? "",
174
+ user_model_from_judge: false,
175
+ simulator_prompt_version: SIMULATOR_PROMPT_VERSION,
176
+ },
177
+ },
178
+ engine: input.engine,
179
+ stageflowVersion: PACKAGE_VERSION,
180
+ });
181
+ const plan = {
182
+ evalRunId: input.evalRunId,
183
+ projectRoot,
184
+ evalId: loaded.id,
185
+ target: { kind: "internal_agent", profileId },
186
+ targetVersionHash: definitionHash,
187
+ stageModel: model,
188
+ task: {},
189
+ cases: planCases,
190
+ graders: loaded.graders,
191
+ repeats,
192
+ passThreshold,
193
+ concurrency,
194
+ maxCostUsd,
195
+ ...(model !== "" ? { modelOverride: model } : {}),
196
+ conversation,
197
+ userModel,
198
+ agent: { definitionHash, definition: { id: profileId, title: profile.title, tools: profile.tools } },
199
+ };
200
+ return { ok: true, plan, snapshot, fingerprint, components, skippedCaseIds, findings };
201
+ }
@@ -54,6 +54,12 @@ export declare function planFindings(input: {
54
54
  datasetCases?: readonly unknown[];
55
55
  isKnownModel?: IsKnownModel;
56
56
  agents?: AgentCatalogPort;
57
+ internalAgents?: ReadonlyArray<{
58
+ id: string;
59
+ title: string;
60
+ playbook: string;
61
+ tools: string[];
62
+ }>;
57
63
  }): Promise<{
58
64
  ok: boolean;
59
65
  findings: ValidationFinding[];
@@ -65,6 +71,12 @@ export declare function validateEvalText(input: {
65
71
  datasetCases?: readonly unknown[];
66
72
  isKnownModel?: IsKnownModel;
67
73
  agents?: AgentCatalogPort;
74
+ internalAgents?: ReadonlyArray<{
75
+ id: string;
76
+ title: string;
77
+ playbook: string;
78
+ tools: string[];
79
+ }>;
68
80
  }): Promise<{
69
81
  findings: ValidationFinding[];
70
82
  parsed: import("../../config/loadOutcome.js").LoadOutcome<LoadedEval>;
@@ -147,6 +159,12 @@ export declare function buildResultDetail(store: RunStore, id: string, caseId: s
147
159
  } | null;
148
160
  tags: string[];
149
161
  suggestion: import("./suggestions.js").WireSuggestion | null;
162
+ grader_labels: {
163
+ [k: string]: {
164
+ label: "fail" | "pass";
165
+ note: string | null;
166
+ };
167
+ };
150
168
  }[];
151
169
  }>;
152
170
  export declare function loadEvalListItems(input: {
@@ -99,6 +99,9 @@ export async function buildEvalDetail(input) {
99
99
  agent = await liveAgentOf(input.agents, projectRoot, file.target.agent);
100
100
  targetModel = file.defaults?.model ?? agent?.model ?? null;
101
101
  }
102
+ else if (file.target.kind === "internal_agent") {
103
+ targetModel = file.defaults?.model ?? null;
104
+ }
102
105
  else {
103
106
  const target = await resolveEvalTarget(file, entry.outcome.value.path, projectRoot);
104
107
  if (target.ok)
@@ -130,6 +133,7 @@ export async function planFindings(input) {
130
133
  engine: { name: ENGINE_NAME, version: PINNED_PROMPTFOO_VERSION, nodeVersion: process.versions.node },
131
134
  ...(input.isKnownModel ? { isKnownModel: input.isKnownModel } : {}),
132
135
  ...(input.agents ? { agents: input.agents } : {}),
136
+ ...(input.internalAgents ? { internalAgents: input.internalAgents } : {}),
133
137
  });
134
138
  return { ok: built.ok, findings: built.findings };
135
139
  }
@@ -143,6 +147,7 @@ export async function validateEvalText(input) {
143
147
  ...(input.datasetCases !== undefined ? { datasetCases: input.datasetCases } : {}),
144
148
  ...(input.isKnownModel ? { isKnownModel: input.isKnownModel } : {}),
145
149
  ...(input.agents ? { agents: input.agents } : {}),
150
+ ...(input.internalAgents ? { internalAgents: input.internalAgents } : {}),
146
151
  });
147
152
  const findings = checked.findings.length > 0 || parsed.ok
148
153
  ? checked.findings
@@ -222,11 +227,12 @@ export async function buildResultsPage(store, id, input) {
222
227
  }
223
228
  export async function buildResultDetail(store, id, caseId) {
224
229
  const row = await runByIdOr404(store, id);
225
- const [cases, results, reviews, suggestions] = await Promise.all([
230
+ const [cases, results, reviews, suggestions, graderLabels] = await Promise.all([
226
231
  store.getEvalCases(row.evalRunId),
227
232
  store.getEvalResultsForCase(row.evalRunId, caseId),
228
233
  store.getEvalReviews(row.evalRunId),
229
234
  store.listEvalSuggestions(row.evalRunId),
235
+ store.listEvalGraderLabels(row.evalRunId),
230
236
  ]);
231
237
  const caseRow = cases.find((x) => x.caseId === caseId);
232
238
  if (!caseRow || results.length === 0) {
@@ -240,6 +246,9 @@ export async function buildResultDetail(store, id, caseId) {
240
246
  .map((r) => ({
241
247
  ...resultToWire(r, reviewByKey.get(resultKey(r.caseId, r.repeatIndex)), row.targetKind === "agent" ? (row.targetRef.agent_id ?? "") : undefined),
242
248
  suggestion: suggestionFor(suggestions, r.caseId, r.repeatIndex),
249
+ grader_labels: Object.fromEntries(graderLabels
250
+ .filter((l) => l.caseId === r.caseId && l.repeatIndex === r.repeatIndex)
251
+ .map((l) => [l.graderId, { label: l.label, note: l.note }])),
243
252
  })),
244
253
  };
245
254
  }
@@ -0,0 +1,45 @@
1
+ import type { RunStore } from "../../runstore/port.js";
2
+ export type LabelCounts = {
3
+ pass: number;
4
+ fail: number;
5
+ };
6
+ /**
7
+ * Everything the console's labeling screen needs in one read: each result the judge graded for one grader, with the
8
+ * conversation, what the case expects, the judge's answer and the author's label so far. Results the judge errored on
9
+ * are left out (there is no verdict to compare with), and so are results that never ran.
10
+ */
11
+ export declare function buildLabelQueue(store: RunStore, id: string, graderId: string): Promise<{
12
+ grader_id: string;
13
+ items: {
14
+ case: {
15
+ position: number;
16
+ };
17
+ result: import("./runs.js").WireResultDetail;
18
+ judge: {
19
+ passed: boolean;
20
+ score: number | null;
21
+ reason: string | null;
22
+ };
23
+ label: {
24
+ label: "fail" | "pass";
25
+ note: string | null;
26
+ } | null;
27
+ }[];
28
+ labeled_here: LabelCounts;
29
+ labeled_eval: LabelCounts;
30
+ }>;
31
+ /**
32
+ * The label counts across the eval's runs, and which results of this run the judge graded that still have no label
33
+ * from you. No conversations: this is for a progress line and the Cases filter.
34
+ */
35
+ export declare function buildLabelSummary(store: RunStore, id: string, graderId: string): Promise<{
36
+ grader_id: string;
37
+ labeled_eval: {
38
+ pass: number;
39
+ fail: number;
40
+ };
41
+ unlabeled: {
42
+ case_id: string;
43
+ repeat_index: number;
44
+ }[];
45
+ }>;
@@ -0,0 +1,83 @@
1
+ import { resultKey } from "../statsTypes.js";
2
+ import { EvalApiError, runByIdOr404 } from "./api.js";
3
+ import { resultToWire } from "./runs.js";
4
+ import { isJudgedType } from "../judged.js";
5
+ /**
6
+ * Everything the console's labeling screen needs in one read: each result the judge graded for one grader, with the
7
+ * conversation, what the case expects, the judge's answer and the author's label so far. Results the judge errored on
8
+ * are left out (there is no verdict to compare with), and so are results that never ran.
9
+ */
10
+ export async function buildLabelQueue(store, id, graderId) {
11
+ const row = await runByIdOr404(store, id);
12
+ const [cases, results, labels, evalLabels] = await Promise.all([
13
+ store.getEvalCases(row.evalRunId),
14
+ store.listEvalResults(row.evalRunId),
15
+ store.listEvalGraderLabels(row.evalRunId),
16
+ store.listEvalGraderLabelsForEval(row.projectRoot, row.evalId, graderId),
17
+ ]);
18
+ const caseById = new Map(cases.map((c) => [c.caseId, c]));
19
+ const labelByKey = new Map(labels.filter((l) => l.graderId === graderId).map((l) => [resultKey(l.caseId, l.repeatIndex), l]));
20
+ const gradeOf = (grades) => grades.find((g) => g.graderId === graderId);
21
+ const known = results.some((r) => gradeOf(r.grades) !== undefined);
22
+ if (!known) {
23
+ throw new EvalApiError(404, "EVAL_GRADER_NOT_FOUND", `Grader ${graderId} has no grades in run ${id}`);
24
+ }
25
+ const judged = results.find((r) => {
26
+ const g = gradeOf(r.grades);
27
+ return g !== undefined && isJudgedType(g.graderType);
28
+ });
29
+ if (!judged) {
30
+ throw new EvalApiError(400, "invalid_request", `Grader ${graderId} is not judged by a model, so it has nothing to label`);
31
+ }
32
+ const items = [...results]
33
+ .sort((a, b) => (caseById.get(a.caseId)?.position ?? 0) - (caseById.get(b.caseId)?.position ?? 0) || a.repeatIndex - b.repeatIndex)
34
+ .flatMap((r) => {
35
+ const grade = gradeOf(r.grades);
36
+ const caseRow = caseById.get(r.caseId);
37
+ if (!grade || grade.status !== "graded" || !caseRow)
38
+ return [];
39
+ const label = labelByKey.get(resultKey(r.caseId, r.repeatIndex));
40
+ return [
41
+ {
42
+ case: { ...caseRow.case, position: caseRow.position },
43
+ result: resultToWire(r, undefined),
44
+ judge: { passed: grade.passed === true, score: grade.score, reason: grade.reason },
45
+ label: label ? { label: label.label, note: label.note } : null,
46
+ },
47
+ ];
48
+ });
49
+ const count = (rows) => ({
50
+ pass: rows.filter((l) => l.label === "pass").length,
51
+ fail: rows.filter((l) => l.label === "fail").length,
52
+ });
53
+ return {
54
+ grader_id: graderId,
55
+ items,
56
+ labeled_here: count(labels.filter((l) => l.graderId === graderId)),
57
+ labeled_eval: count(evalLabels),
58
+ };
59
+ }
60
+ /**
61
+ * The label counts across the eval's runs, and which results of this run the judge graded that still have no label
62
+ * from you. No conversations: this is for a progress line and the Cases filter.
63
+ */
64
+ export async function buildLabelSummary(store, id, graderId) {
65
+ const row = await runByIdOr404(store, id);
66
+ const [labels, runLabels, graded] = await Promise.all([
67
+ store.listEvalGraderLabelsForEval(row.projectRoot, row.evalId, graderId),
68
+ store.listEvalGraderLabels(row.evalRunId),
69
+ store.listGradedResultKeys(row.evalRunId, graderId),
70
+ ]);
71
+ const labeled = new Set(runLabels.filter((l) => l.graderId === graderId).map((l) => resultKey(l.caseId, l.repeatIndex)));
72
+ const unlabeled = graded
73
+ .filter((r) => !labeled.has(resultKey(r.caseId, r.repeatIndex)))
74
+ .map((r) => ({ case_id: r.caseId, repeat_index: r.repeatIndex }));
75
+ return {
76
+ grader_id: graderId,
77
+ labeled_eval: {
78
+ pass: labels.filter((l) => l.label === "pass").length,
79
+ fail: labels.filter((l) => l.label === "fail").length,
80
+ },
81
+ unlabeled,
82
+ };
83
+ }
@@ -103,7 +103,16 @@ export async function buildEvalListItems(input) {
103
103
  bannerInputs.push({ evalId: loaded.id, status, last: lastSummary, previous: previousSummary });
104
104
  const agentTarget = file.target.kind === "agent"
105
105
  ? await agentListTarget(file.target.agent, last, input.agents, projectRoot)
106
- : undefined;
106
+ : file.target.kind === "internal_agent"
107
+ ? {
108
+ kind: "agent",
109
+ agent_id: file.target.profile,
110
+ agent_name: file.target.profile,
111
+ model: file.defaults?.model ?? null,
112
+ revision: null,
113
+ label: `${file.target.profile} (internal)`,
114
+ }
115
+ : undefined;
107
116
  const conv = agentTarget ? resolveConversation(file.conversation) : undefined;
108
117
  const pipelinePath = paths.pipelinePath !== undefined ? posix(projectRoot, paths.pipelinePath) : undefined;
109
118
  const stageFile = paths.stageFile !== undefined ? posix(projectRoot, paths.stageFile) : undefined;
@@ -106,6 +106,11 @@ export type EvalRunDetail = {
106
106
  stats: Stats | null;
107
107
  lowest_cases_by_grader: Record<string, LowestCase[]>;
108
108
  current: RunCurrent;
109
+ cases: Array<{
110
+ id: string;
111
+ title: string | null;
112
+ position: number;
113
+ }>;
109
114
  live?: Array<{
110
115
  case_id: string;
111
116
  repeat_index: number;
@@ -218,11 +218,16 @@ export async function currentHashes(row, agents) {
218
218
  return none;
219
219
  }
220
220
  }
221
+ function plannedCase(row) {
222
+ const title = row.case.title;
223
+ return { id: row.caseId, title: typeof title === "string" && title !== "" ? title : null, position: row.position };
224
+ }
221
225
  export async function buildRunDetail(input) {
222
226
  const { store, row } = input;
223
- const [results, reviews] = await Promise.all([
227
+ const [results, reviews, storedCases] = await Promise.all([
224
228
  store.listEvalResults(row.evalRunId),
225
229
  store.getEvalReviews(row.evalRunId),
230
+ store.getEvalCases(row.evalRunId),
226
231
  ]);
227
232
  const graders = graderRefsOf(row);
228
233
  const grades = results.flatMap(toGradeRecords);
@@ -258,6 +263,7 @@ export async function buildRunDetail(input) {
258
263
  ? Object.fromEntries(graders.map((g) => [g.id, lowestCases(grades.filter((x) => x.graderId === g.id))]))
259
264
  : {},
260
265
  current: await currentHashes(row, input.agents),
266
+ cases: storedCases.map(plannedCase),
261
267
  };
262
268
  if (input.live && (row.status === "running" || row.status === "queued")) {
263
269
  detail.live = input.live.phases.map((p) => ({
@@ -1,7 +1,8 @@
1
1
  import type { StageCaseRunner } from "./stageCaseRunner.js";
2
2
  export type EvalCaseRunner = StageCaseRunner;
3
- /** Routes each case to the stage or agent runner by the plan's target kind. */
3
+ /** Routes each case to the stage, agent, or internal-agent runner by the plan's target kind. */
4
4
  export declare function createEvalCaseRunner(deps: {
5
5
  stage: StageCaseRunner;
6
6
  agent: StageCaseRunner;
7
+ internalAgent?: StageCaseRunner;
7
8
  }): EvalCaseRunner;
@@ -1,16 +1,27 @@
1
- /** Routes each case to the stage or agent runner by the plan's target kind. */
1
+ import { isConversationTarget } from "../types.js";
2
+ /** Routes each case to the stage, agent, or internal-agent runner by the plan's target kind. */
2
3
  export function createEvalCaseRunner(deps) {
3
4
  const kinds = new Map();
4
- const runnerOf = (evalRunId) => (kinds.get(evalRunId) === "agent" ? deps.agent : deps.stage);
5
+ const runnerOf = (evalRunId) => {
6
+ const kind = kinds.get(evalRunId);
7
+ if (kind === "internal_agent" && deps.internalAgent)
8
+ return deps.internalAgent;
9
+ return kind === "agent" ? deps.agent : deps.stage;
10
+ };
5
11
  return {
6
12
  runCase(plan, req) {
7
- kinds.set(req.evalRunId, plan.target.kind === "agent" ? "agent" : "stage");
13
+ const kind = plan.target.kind === "internal_agent" ? "internal_agent" : isConversationTarget(plan.target.kind) ? "agent" : "stage";
14
+ kinds.set(req.evalRunId, kind);
8
15
  return runnerOf(req.evalRunId).runCase(plan, req);
9
16
  },
10
17
  async cancelAll(evalRunId) {
11
- await Promise.all([deps.stage.cancelAll(evalRunId), deps.agent.cancelAll(evalRunId)]);
18
+ await Promise.all([
19
+ deps.stage.cancelAll(evalRunId),
20
+ deps.agent.cancelAll(evalRunId),
21
+ deps.internalAgent?.cancelAll(evalRunId),
22
+ ]);
12
23
  },
13
- spend: (evalRunId) => deps.stage.spend(evalRunId) + deps.agent.spend(evalRunId),
24
+ spend: (evalRunId) => deps.stage.spend(evalRunId) + deps.agent.spend(evalRunId) + (deps.internalAgent?.spend(evalRunId) ?? 0),
14
25
  addSpend: (evalRunId, usd) => runnerOf(evalRunId).addSpend(evalRunId, usd),
15
26
  stopReason: (evalRunId) => runnerOf(evalRunId).stopReason?.(evalRunId),
16
27
  };
@@ -0,0 +1,6 @@
1
+ import type { OperatorAgentHost } from "../../operatorAgent/types.js";
2
+ import type { StageCaseRunner } from "./stageCaseRunner.js";
3
+ export declare function createInternalAgentCaseRunner(deps: {
4
+ host: () => OperatorAgentHost | undefined;
5
+ now?: () => number;
6
+ }): StageCaseRunner;