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
@@ -16,10 +16,12 @@ import { resolveSeededExamplesPath } from "../config/seededCatalog.js";
16
16
  import { applyCaseOps, appendOrReplaceCases, convertDatasetToJsonl, createDatasetFile, } from "../config/writeDataset.js";
17
17
  import { createEvalFile, previewEvalUpdate, renderEvalCreateYaml, updateEvalFile, } from "../config/writeEval.js";
18
18
  import { CompareMismatchError } from "../evals/compare.js";
19
+ import { suggestExamples } from "../evals/calibrationSuggest.js";
19
20
  import { EVAL_LIMITS, EVAL_REVIEW_LABELS, EVAL_STARTED_VIA } from "../evals/constants.js";
20
21
  import { PINNED_PROMPTFOO_VERSION } from "../evals/engine/promptfoo/pinned.js";
21
22
  import { AGENT_ESTIMATE_ASSUMPTIONS, estimateRun, } from "../evals/estimate.js";
22
23
  import { EvalRunError, } from "../evals/manager.js";
24
+ import { buildLabelQueue, buildLabelSummary } from "../evals/read/labelQueue.js";
23
25
  import { buildEvalPlan } from "../evals/plan.js";
24
26
  import { buildEvalDetail, buildResultDetail, buildResultsPage, DEFAULT_LIMIT, ENGINE_NAME, engineCheck as engineCheckStatus, EvalApiError, evalScopeRoots, listEvalRunsPage, loadAggregatedEvalList, locateDataset, locateEval, locateEvalRoot, wireRootOf, MAX_LIMIT, planFindings as planFindingsShared, rel, validateEvalText, } from "../evals/read/api.js";
25
27
  import { listAgentTargets } from "../evals/read/agentTargets.js";
@@ -31,6 +33,7 @@ import { EvalStoreError } from "../evals/storeError.js";
31
33
  import { audit, bad, isRecord, optStringArray, readOptionalBody, runByIdOr404, } from "./evalRouteContext.js";
32
34
  import { matchEvalTriageRoute } from "./evalTriageRoutes.js";
33
35
  import { callerIdFromRequestAuth } from "./requestAuthContext.js";
36
+ import { isJudged, isJudgedType } from "../evals/judged.js";
34
37
  const ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
35
38
  const CANCEL_WAIT_MS = 5000;
36
39
  const WRITE_ERROR_STATUS = {
@@ -57,6 +60,8 @@ const RUN_ERROR_STATUS = {
57
60
  EVAL_ENGINE_UNAVAILABLE: 503,
58
61
  EVAL_NODE_TOO_OLD: 503,
59
62
  EVAL_TAGS_ON_PASSED_RESULT: 400,
63
+ EVAL_CALIBRATION_NO_LABELS: 422,
64
+ EVAL_CALIBRATION_CANCELLED: 409,
60
65
  };
61
66
  function sendError(c, status, code, message, extra = {}) {
62
67
  c.json(c.res, status, { error: message, code, ...extra });
@@ -141,7 +146,8 @@ function agentsOf(c) {
141
146
  }
142
147
  function agentsInput(c) {
143
148
  const agents = agentsOf(c);
144
- return agents ? { agents } : {};
149
+ const internalAgents = c.evalRunManager.visibleInternalAgents();
150
+ return { ...(agents ? { agents } : {}), ...(internalAgents.length > 0 ? { internalAgents } : {}) };
145
151
  }
146
152
  function engineCheck(c) {
147
153
  engineCheckStatus(c.engineStatus());
@@ -254,11 +260,30 @@ function parseDefinitionOrYaml(body) {
254
260
  throw bad(`"definition" must be an object`);
255
261
  return { definition: body.definition };
256
262
  }
263
+ function validateEvalId(input) {
264
+ if (isRecord(input.definition) && typeof input.definition.id === "string")
265
+ return input.definition.id;
266
+ if (typeof input.yaml !== "string")
267
+ return undefined;
268
+ const match = /^id:\s*["']?([A-Za-z0-9][A-Za-z0-9._-]{0,63})["']?\s*$/m.exec(input.yaml);
269
+ return match?.[1];
270
+ }
271
+ async function existingEvalFile(c, wire, id) {
272
+ try {
273
+ const located = await evalRootFor(c, wire, id);
274
+ return { projectRoot: located.projectRoot, absPath: located.ref.absPath };
275
+ }
276
+ catch (err) {
277
+ if (err instanceof EvalApiError && err.status === 404)
278
+ return undefined;
279
+ throw err;
280
+ }
281
+ }
257
282
  async function handleEvalValidate(c) {
258
283
  const body = await readBody(c);
259
284
  const input = parseDefinitionOrYaml(body);
260
- const projectRoot = await projectRootFor(c, wireFromBody(body));
261
- const manifest = await manifestForWrite(projectRoot);
285
+ const wire = wireFromBody(body);
286
+ let projectRoot = await projectRootFor(c, wire);
262
287
  const suppliedPath = optString(body, "path");
263
288
  const datasetCases = optDatasetCases(body);
264
289
  let evalAbs;
@@ -267,10 +292,18 @@ async function handleEvalValidate(c) {
267
292
  assertInsideProject(projectRoot, evalAbs, "eval path");
268
293
  }
269
294
  else {
270
- const rawId = isRecord(input.definition) ? input.definition.id : undefined;
271
- const dir = await resolveWriteDir(projectRoot, manifest, "evals");
272
- const fileName = typeof rawId === "string" && ID_RE.test(rawId) ? `${rawId}.eval.yaml` : "new.eval.yaml";
273
- evalAbs = path.join(dir.absDir, fileName);
295
+ const rawId = validateEvalId(input);
296
+ const located = rawId !== undefined && ID_RE.test(rawId) ? await existingEvalFile(c, wire, rawId) : undefined;
297
+ if (located) {
298
+ projectRoot = located.projectRoot;
299
+ evalAbs = located.absPath;
300
+ }
301
+ else {
302
+ const manifest = await manifestForWrite(projectRoot);
303
+ const dir = await resolveWriteDir(projectRoot, manifest, "evals");
304
+ const fileName = rawId !== undefined && ID_RE.test(rawId) ? `${rawId}.eval.yaml` : "new.eval.yaml";
305
+ evalAbs = path.join(dir.absDir, fileName);
306
+ }
274
307
  }
275
308
  let derived;
276
309
  if (input.yaml !== undefined) {
@@ -639,7 +672,7 @@ async function handleRunEstimate(c, id) {
639
672
  resultsExecuted: stats.results_executed,
640
673
  rubricGradesGraded: results
641
674
  .flatMap((r) => r.grades)
642
- .filter((g) => (g.graderType === "rubric" || g.graderType === "goal_reached") && g.status === "graded").length,
675
+ .filter((g) => isJudgedType(g.graderType) && g.status === "graded").length,
643
676
  wallSeconds: last.startedAt !== null && last.finishedAt !== null
644
677
  ? Math.max(0, (Date.parse(last.finishedAt) - Date.parse(last.startedAt)) / 1000)
645
678
  : null,
@@ -664,7 +697,7 @@ async function agentEstimateBlock(c, plan) {
664
697
  }
665
698
  }
666
699
  const count = Math.max(1, plan.cases.length);
667
- const judge = plan.graders.find((g) => g.type === "rubric" || g.type === "goal_reached");
700
+ const judge = plan.graders.find(isJudged);
668
701
  const judgeModel = judge !== undefined && "judge" in judge && judge.judge !== "" ? judge.judge : undefined;
669
702
  const wanted = [plan.stageModel, plan.userModel ?? undefined, judgeModel].filter((id) => typeof id === "string" && id !== "");
670
703
  const prices = (await c.loadPrices?.([...new Set(wanted)], plan.projectRoot).catch(() => undefined)) ?? new Map();
@@ -681,7 +714,7 @@ async function sendEstimate(c, plan, previous) {
681
714
  const agent = plan.target.kind === "agent" ? await agentEstimateBlock(c, plan) : undefined;
682
715
  const estimate = estimateRun({
683
716
  resultsTotal: plan.cases.length * plan.repeats,
684
- rubricGraderCount: plan.graders.filter((g) => g.type === "rubric" || g.type === "goal_reached").length,
717
+ rubricGraderCount: plan.graders.reduce((calls, g) => (isJudged(g) ? calls + (g.judgeSettings?.votes ?? 1) : calls), 0),
685
718
  previous,
686
719
  recentStageCosts: await recentStageCosts(c, {
687
720
  ...(plan.target.kind === "stage" && plan.target.pipelinePath !== undefined ? { pipelinePath: plan.target.pipelinePath } : {}),
@@ -806,6 +839,10 @@ async function handleResultsList(c, id) {
806
839
  async function handleResultDetail(c, id, caseId) {
807
840
  c.json(c.res, 200, await buildResultDetail(c.store, id, caseId));
808
841
  }
842
+ async function handleLabelQueue(c, id, graderId) {
843
+ const summary = c.url.searchParams.get("summary") === "true";
844
+ c.json(c.res, 200, summary ? await buildLabelSummary(c.store, id, graderId) : await buildLabelQueue(c.store, id, graderId));
845
+ }
809
846
  async function handleReviewPut(c, id, caseId, repeat) {
810
847
  const body = await readBody(c);
811
848
  await runByIdOr404(c, id);
@@ -842,6 +879,191 @@ async function handleReviewPut(c, id, caseId, repeat) {
842
879
  throw err;
843
880
  }
844
881
  }
882
+ const CALIBRATION_SPLITS = ["all", "dev", "held_out"];
883
+ function candidateFrom(raw) {
884
+ if (raw === undefined || raw === null)
885
+ return undefined;
886
+ if (!isRecord(raw))
887
+ throw bad(`"candidate" must be an object`);
888
+ const out = {};
889
+ if (raw.judge_settings !== undefined) {
890
+ if (!isRecord(raw.judge_settings))
891
+ throw bad(`"candidate.judge_settings" must be an object`);
892
+ const js = raw.judge_settings;
893
+ const settings = {};
894
+ if (js.temperature !== undefined) {
895
+ if (typeof js.temperature !== "number" || js.temperature < 0 || js.temperature > 1) {
896
+ throw bad(`"candidate.judge_settings.temperature" must be a number from 0 to 1`);
897
+ }
898
+ settings.temperature = js.temperature;
899
+ }
900
+ if (js.reasoning !== undefined) {
901
+ if (typeof js.reasoning !== "boolean")
902
+ throw bad(`"candidate.judge_settings.reasoning" must be a boolean`);
903
+ settings.reasoning = js.reasoning;
904
+ }
905
+ if (js.votes !== undefined) {
906
+ if (typeof js.votes !== "number" || ![1, 3, 5].includes(js.votes))
907
+ throw bad(`"candidate.judge_settings.votes" must be 1, 3 or 5`);
908
+ settings.votes = js.votes;
909
+ }
910
+ out.judgeSettings = settings;
911
+ }
912
+ if (raw.rubric !== undefined) {
913
+ if (typeof raw.rubric !== "string" || raw.rubric.trim() === "")
914
+ throw bad(`"candidate.rubric" must be a non-empty string`);
915
+ out.rubric = raw.rubric;
916
+ }
917
+ if (raw.judge !== undefined) {
918
+ if (typeof raw.judge !== "string" || raw.judge.trim() === "")
919
+ throw bad(`"candidate.judge" must be a non-empty string`);
920
+ out.judge = raw.judge;
921
+ }
922
+ if (raw.examples !== undefined) {
923
+ if (!Array.isArray(raw.examples) || raw.examples.length > EVAL_LIMITS.rubricExamplesMax) {
924
+ throw bad(`"candidate.examples" must be an array of at most ${EVAL_LIMITS.rubricExamplesMax}`);
925
+ }
926
+ out.examples = raw.examples.map((e) => {
927
+ if (!isRecord(e) || typeof e.output !== "string")
928
+ throw bad(`Each candidate example needs an "output" string`);
929
+ const reason = typeof e.reason === "string" ? { reason: e.reason } : {};
930
+ if (e.verdict === "pass" || e.verdict === "fail")
931
+ return { verdict: e.verdict, output: e.output, ...reason };
932
+ if (typeof e.score === "number" && Number.isInteger(e.score) && e.score >= 1 && e.score <= 5) {
933
+ return { score: e.score, output: e.output, ...reason };
934
+ }
935
+ throw bad(`Each candidate example needs a verdict (pass or fail) or a score from 1 to 5`);
936
+ });
937
+ }
938
+ return out;
939
+ }
940
+ async function handleGraderLabelPut(c, id, caseId, repeat, graderId) {
941
+ const body = await readBody(c);
942
+ await runByIdOr404(c, id);
943
+ let label;
944
+ if (body.label === null)
945
+ label = null;
946
+ else if (typeof body.label === "string" && EVAL_REVIEW_LABELS.includes(body.label)) {
947
+ label = body.label;
948
+ }
949
+ else
950
+ throw bad(`"label" must be pass, fail or null`);
951
+ if (body.note !== undefined && body.note !== null) {
952
+ if (typeof body.note !== "string" || body.note.length > EVAL_LIMITS.reviewNoteMaxChars) {
953
+ throw bad(`"note" must be a string of at most ${EVAL_LIMITS.reviewNoteMaxChars} characters or null`);
954
+ }
955
+ }
956
+ const results = await c.store.getEvalResultsForCase(id, caseId);
957
+ const result = results.find((r) => r.repeatIndex === repeat);
958
+ if (!result)
959
+ throw new EvalStoreError("EVAL_RUN_NOT_FOUND", `No result for ${caseId}#${repeat} in ${id}`);
960
+ const grade = result.grades.find((g) => g.graderId === graderId);
961
+ if (!grade || !isJudgedType(grade.graderType)) {
962
+ throw bad(`Grader ${graderId} is not a judged grader on this result`);
963
+ }
964
+ const row = await c.store.putEvalGraderLabel(id, caseId, repeat, graderId, {
965
+ label,
966
+ ...(body.note !== undefined ? { note: body.note } : {}),
967
+ });
968
+ audit(c, "eval_grader_label", "ok", id);
969
+ c.json(c.res, 200, row === null ? { label: null, note: null } : { label: row.label, note: row.note });
970
+ }
971
+ function calibrationToWire(r) {
972
+ return {
973
+ id: r.id,
974
+ grader_id: r.graderId,
975
+ grader_hash: r.graderHash,
976
+ judge: r.judge,
977
+ prompt_version: r.promptVersion,
978
+ split: r.split,
979
+ candidate: r.candidate,
980
+ stats: r.stats,
981
+ disagreements: r.disagreements.map((d) => ({
982
+ ...(d.evalRunId !== undefined ? { eval_run_id: d.evalRunId } : {}),
983
+ case_id: d.caseId,
984
+ repeat_index: d.repeatIndex,
985
+ label: d.label,
986
+ judge: d.judge,
987
+ reason: d.reason,
988
+ })),
989
+ cost_usd: r.costUsd,
990
+ created_at: r.createdAt,
991
+ };
992
+ }
993
+ async function handleCalibrationList(c, id, graderId) {
994
+ const root = await locateEvalRoot(rootOptions(c), wireFromQuery(c), id);
995
+ const candidates = c.url.searchParams.get("include_candidates") === "true";
996
+ const items = await c.store.listEvalCalibrations(root.path, id, graderId, candidates);
997
+ c.json(c.res, 200, { items: items.map(calibrationToWire), next_cursor: null });
998
+ }
999
+ async function handleSuggestExamples(c, id, graderId) {
1000
+ const body = await readBody(c);
1001
+ if (body.calibration_id !== undefined && typeof body.calibration_id !== "string") {
1002
+ throw bad(`"calibration_id" must be a string`);
1003
+ }
1004
+ const { projectRoot, ref } = await evalRootFor(c, wireFromBody(body), id);
1005
+ const outcome = await loadEvalOutcome(ref.absPath, projectRoot);
1006
+ if (!outcome.ok)
1007
+ throw new EvalApiError(422, "EVAL_INVALID", "Eval definition is invalid");
1008
+ const grader = outcome.value.graders.find((g) => g.id === graderId);
1009
+ if (!grader || grader.type !== "rubric") {
1010
+ throw new EvalApiError(422, "EVAL_INVALID", `Grader ${graderId} is not a rubric grader of this eval`);
1011
+ }
1012
+ const calibrations = await c.store.listEvalCalibrations(projectRoot, id, graderId, true, 50);
1013
+ const calibration = body.calibration_id !== undefined
1014
+ ? calibrations.find((x) => x.id === body.calibration_id)
1015
+ : calibrations.find((x) => !x.candidate);
1016
+ if (!calibration) {
1017
+ throw new EvalApiError(404, "EVAL_RUN_NOT_FOUND", "No calibration to suggest examples from; check the judge against your labels first");
1018
+ }
1019
+ const items = await suggestExamples(c.store, {
1020
+ projectRoot,
1021
+ evalId: id,
1022
+ graderId,
1023
+ scale: grader.scale,
1024
+ calibration,
1025
+ });
1026
+ c.json(c.res, 200, { calibration_id: calibration.id, items });
1027
+ }
1028
+ async function handleCalibrate(c, id, graderId) {
1029
+ const body = await readBody(c);
1030
+ const split = body.split === undefined ? "all" : body.split;
1031
+ if (typeof split !== "string" || !CALIBRATION_SPLITS.includes(split)) {
1032
+ throw bad(`"split" must be one of ${CALIBRATION_SPLITS.join(", ")}`);
1033
+ }
1034
+ if (body.max_cost_usd !== undefined && (typeof body.max_cost_usd !== "number" || !(body.max_cost_usd > 0))) {
1035
+ throw bad(`"max_cost_usd" must be a positive number`);
1036
+ }
1037
+ const candidate = candidateFrom(body.candidate);
1038
+ if (body.stability !== undefined && typeof body.stability !== "boolean")
1039
+ throw bad(`"stability" must be a boolean`);
1040
+ const { projectRoot, ref } = await evalRootFor(c, wireFromBody(body), id);
1041
+ engineCheck(c);
1042
+ const closed = new AbortController();
1043
+ const onClose = () => closed.abort();
1044
+ c.res.once("close", onClose);
1045
+ try {
1046
+ const out = await c.evalRunManager.calibrate({
1047
+ signal: closed.signal,
1048
+ projectRoot,
1049
+ evalPath: ref.absPath,
1050
+ graderId,
1051
+ split: split,
1052
+ ...(candidate !== undefined ? { candidate } : {}),
1053
+ ...(body.max_cost_usd !== undefined ? { maxCostUsd: body.max_cost_usd } : {}),
1054
+ ...(body.stability === true ? { stability: true } : {}),
1055
+ });
1056
+ audit(c, "eval_calibrate", "ok");
1057
+ c.json(c.res, 200, calibrationToWire(out.calibration));
1058
+ }
1059
+ catch (err) {
1060
+ audit(c, "eval_calibrate", "error", undefined, err instanceof EvalRunError ? err.code : "internal_error");
1061
+ throw err;
1062
+ }
1063
+ finally {
1064
+ c.res.off("close", onClose);
1065
+ }
1066
+ }
845
1067
  async function handleFailureTypes(c, id) {
846
1068
  const root = await locateEvalRoot(rootOptions(c), wireFromQuery(c), id);
847
1069
  c.json(c.res, 200, { items: await c.store.listFailureTypes(root.path, id), next_cursor: null });
@@ -955,7 +1177,7 @@ async function datasetDetailFor(c, projectRoot, id, absPath, evalId, query) {
955
1177
  let inputSchema;
956
1178
  let agent;
957
1179
  if (useEval !== undefined) {
958
- if (useEval.loaded.file.target.kind === "agent") {
1180
+ if (useEval.loaded.file.target.kind === "agent" || useEval.loaded.file.target.kind === "internal_agent") {
959
1181
  agent = { mode: resolveConversation(useEval.loaded.file.conversation).mode };
960
1182
  }
961
1183
  else {
@@ -1014,7 +1236,8 @@ async function conversationModeOf(c, body) {
1014
1236
  return undefined;
1015
1237
  const { projectRoot, ref } = await evalRootFor(c, wireFromBody(body), evalId);
1016
1238
  const outcome = await loadEvalOutcome(ref.absPath, projectRoot);
1017
- return outcome.ok && outcome.value.file.target.kind === "agent"
1239
+ const kind = outcome.ok ? outcome.value.file.target.kind : undefined;
1240
+ return outcome.ok && (kind === "agent" || kind === "internal_agent")
1018
1241
  ? resolveConversation(outcome.value.file.conversation).mode
1019
1242
  : undefined;
1020
1243
  }
@@ -1370,6 +1593,15 @@ export async function handleEvalRoutes(req, res, deps) {
1370
1593
  match = m(/^\/api\/evals\/([^/]+)\/runs$/);
1371
1594
  if (method === "POST" && match)
1372
1595
  return run(() => handleRunStart(c, seg(match[1])));
1596
+ match = m(/^\/api\/evals\/([^/]+)\/graders\/([^/]+)\/calibrations$/);
1597
+ if (method === "GET" && match)
1598
+ return run(() => handleCalibrationList(c, seg(match[1]), seg(match[2])));
1599
+ match = m(/^\/api\/evals\/([^/]+)\/graders\/([^/]+)\/suggest-examples$/);
1600
+ if (method === "POST" && match)
1601
+ return run(() => handleSuggestExamples(c, seg(match[1]), seg(match[2])));
1602
+ match = m(/^\/api\/evals\/([^/]+)\/graders\/([^/]+)\/calibrate$/);
1603
+ if (method === "POST" && match)
1604
+ return run(() => handleCalibrate(c, seg(match[1]), seg(match[2])));
1373
1605
  match = m(/^\/api\/evals\/([^/]+)\/failure-types$/);
1374
1606
  if (method === "GET" && match)
1375
1607
  return run(() => handleFailureTypes(c, seg(match[1])));
@@ -1401,6 +1633,13 @@ export async function handleEvalRoutes(req, res, deps) {
1401
1633
  if (method === "PUT" && match) {
1402
1634
  return run(() => handleReviewPut(c, seg(match[1]), seg(match[2]), Number(match[3])));
1403
1635
  }
1636
+ match = m(/^\/api\/eval-runs\/([^/]+)\/results\/([^/]+)\/(\d+)\/graders\/([^/]+)\/label$/);
1637
+ if (method === "PUT" && match) {
1638
+ return run(() => handleGraderLabelPut(c, seg(match[1]), seg(match[2]), Number(match[3]), seg(match[4])));
1639
+ }
1640
+ match = m(/^\/api\/eval-runs\/([^/]+)\/graders\/([^/]+)\/label-queue$/);
1641
+ if (method === "GET" && match)
1642
+ return run(() => handleLabelQueue(c, seg(match[1]), seg(match[2])));
1404
1643
  const triageRoute = matchEvalTriageRoute(c, seg);
1405
1644
  if (triageRoute)
1406
1645
  return run(triageRoute);
@@ -0,0 +1,5 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import type { OperatorAgentHost } from "../operatorAgent/index.js";
3
+ type Json = (res: ServerResponse, status: number, body: unknown) => void;
4
+ export declare function handleGuideRoutes(req: IncomingMessage, method: string, pathname: string, host: OperatorAgentHost, res: ServerResponse, json: Json): Promise<boolean>;
5
+ export {};
@@ -0,0 +1,166 @@
1
+ import { abortGuideTurn, GuideError, guideAgentInfo, isGuideThreadRunning, streamGuideTurn, } from "../guide/chat.js";
2
+ import { createGuideThread, listGuideThreads, readGuideThread, setGuideThreadModel, } from "../guide/threadStore.js";
3
+ async function readBody(req) {
4
+ const chunks = [];
5
+ let size = 0;
6
+ for await (const chunk of req) {
7
+ const buffer = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
8
+ size += buffer.length;
9
+ if (size > 1_500_000)
10
+ return { __tooLarge: true };
11
+ chunks.push(buffer);
12
+ }
13
+ if (chunks.length === 0)
14
+ return {};
15
+ try {
16
+ const parsed = JSON.parse(Buffer.concat(chunks).toString("utf8"));
17
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)
18
+ ? parsed
19
+ : { __invalid: true };
20
+ }
21
+ catch {
22
+ return { __invalid: true };
23
+ }
24
+ }
25
+ function threadSummary(record) {
26
+ return { id: record.id, title: record.title, updatedAt: record.updatedAt };
27
+ }
28
+ function canWrite(res) {
29
+ return !res.writableEnded && !res.destroyed;
30
+ }
31
+ export async function handleGuideRoutes(req, method, pathname, host, res, json) {
32
+ const root = pathname === "/api/guide";
33
+ const threads = pathname === "/api/guide/threads";
34
+ const threadMatch = /^\/api\/guide\/threads\/([^/]+)$/.exec(pathname);
35
+ const messageMatch = /^\/api\/guide\/threads\/([^/]+)\/messages$/.exec(pathname);
36
+ const stopMatch = /^\/api\/guide\/threads\/([^/]+)\/stop$/.exec(pathname);
37
+ if (!root && !threads && !threadMatch && !messageMatch && !stopMatch)
38
+ return false;
39
+ if (root && method === "GET") {
40
+ json(res, 200, { agent: guideAgentInfo(host) });
41
+ return true;
42
+ }
43
+ if (threads && method === "GET") {
44
+ json(res, 200, {
45
+ threads: listGuideThreads().map((thread) => ({
46
+ ...thread,
47
+ status: isGuideThreadRunning(thread.id) ? "running" : "open",
48
+ })),
49
+ });
50
+ return true;
51
+ }
52
+ if (threads && method === "POST") {
53
+ const record = createGuideThread();
54
+ json(res, 201, { thread: threadSummary(record) });
55
+ return true;
56
+ }
57
+ if (stopMatch && method === "POST") {
58
+ const threadId = decodeURIComponent(stopMatch[1] ?? "");
59
+ const record = readGuideThread(threadId);
60
+ if (!record) {
61
+ json(res, 404, { code: "guide.not_found", message: "" });
62
+ return true;
63
+ }
64
+ json(res, 200, { ok: true, stopped: abortGuideTurn(threadId) });
65
+ return true;
66
+ }
67
+ if (threadMatch && method === "GET") {
68
+ const threadId = decodeURIComponent(threadMatch[1] ?? "");
69
+ const record = readGuideThread(threadId);
70
+ if (!record) {
71
+ json(res, 404, { code: "guide.not_found", message: "" });
72
+ return true;
73
+ }
74
+ json(res, 200, {
75
+ thread: threadSummary(record),
76
+ status: isGuideThreadRunning(record.id) ? "running" : "open",
77
+ messages: record.messages,
78
+ model: record.model ?? null,
79
+ });
80
+ return true;
81
+ }
82
+ if (threadMatch && method === "PATCH") {
83
+ const threadId = decodeURIComponent(threadMatch[1] ?? "");
84
+ const body = await readBody(req);
85
+ if (body.__tooLarge || body.__invalid) {
86
+ json(res, 400, { code: "guide.invalid", message: "" });
87
+ return true;
88
+ }
89
+ let record = readGuideThread(threadId);
90
+ if (!record) {
91
+ json(res, 404, { code: "guide.not_found", message: "" });
92
+ return true;
93
+ }
94
+ if ("model" in body) {
95
+ if (typeof body.model === "string") {
96
+ const next = setGuideThreadModel(threadId, body.model.trim() || null);
97
+ if (!next) {
98
+ json(res, 404, { code: "guide.not_found", message: "" });
99
+ return true;
100
+ }
101
+ record = next;
102
+ }
103
+ else if (body.model === null) {
104
+ const next = setGuideThreadModel(threadId, null);
105
+ if (!next) {
106
+ json(res, 404, { code: "guide.not_found", message: "" });
107
+ return true;
108
+ }
109
+ record = next;
110
+ }
111
+ else {
112
+ json(res, 400, { code: "guide.invalid", message: "" });
113
+ return true;
114
+ }
115
+ }
116
+ json(res, 200, { thread: threadSummary(record), model: record.model ?? null });
117
+ return true;
118
+ }
119
+ if (messageMatch && method === "POST") {
120
+ const threadId = decodeURIComponent(messageMatch[1] ?? "");
121
+ const body = await readBody(req);
122
+ if (body.__tooLarge || body.__invalid) {
123
+ json(res, 400, { code: "guide.invalid", message: "" });
124
+ return true;
125
+ }
126
+ const text = typeof body.text === "string" ? body.text : "";
127
+ let started = false;
128
+ try {
129
+ for await (const frame of streamGuideTurn({
130
+ host,
131
+ threadId,
132
+ text,
133
+ attachments: body.attachments,
134
+ model: typeof body.model === "string" ? body.model : undefined,
135
+ })) {
136
+ if (!canWrite(res))
137
+ continue;
138
+ if (!started) {
139
+ started = true;
140
+ res.writeHead(200, {
141
+ "Content-Type": "application/x-ndjson; charset=utf-8",
142
+ "Cache-Control": "no-store",
143
+ });
144
+ }
145
+ res.write(`${JSON.stringify(frame)}\n`);
146
+ }
147
+ }
148
+ catch (err) {
149
+ const code = err instanceof GuideError ? err.code : "guide.failed";
150
+ const status = err instanceof GuideError ? err.status : 500;
151
+ if (!started && canWrite(res))
152
+ json(res, status, { code, message: "" });
153
+ else if (started && canWrite(res))
154
+ res.write(`${JSON.stringify({ type: "error", code, message: "" })}\n`);
155
+ }
156
+ finally {
157
+ if (!res.headersSent && canWrite(res))
158
+ json(res, 200, { code: "guide.failed", message: "" });
159
+ else if (canWrite(res))
160
+ res.end();
161
+ }
162
+ return true;
163
+ }
164
+ json(res, 405, { code: "guide.invalid", message: "" });
165
+ return true;
166
+ }
@@ -1,6 +1,8 @@
1
1
  import type { StagePort } from "../agent/port.js";
2
2
  import { type ProviderAuthContext } from "../agent/providerAuth.js";
3
+ import type { ChannelService } from "../channels/service.js";
3
4
  import type { EvalRunManager } from "../evals/manager.js";
5
+ import type { MemoryHost } from "../memory/host.js";
4
6
  import { type LiveViewRoutesOptions } from "./liveViewRoutes.js";
5
7
  import { WorkshopChatSessionRegistry } from "../workshop/chatTurn.js";
6
8
  import { type AgentPort } from "../operatorAgent/index.js";
@@ -70,9 +72,17 @@ export type OperatorRouteDeps = {
70
72
  agentConversationBackend?: ConversationBackend;
71
73
  /** Shared with MCP when set by bootstrap. */
72
74
  agentHost?: AgentHostContext;
75
+ /** Bootstrap fills this once the operator host exists, so evals can run against it. */
76
+ operatorHostSlot?: {
77
+ current?: import("../operatorAgent/types.js").OperatorAgentHost;
78
+ };
73
79
  evalRunManager?: EvalRunManager;
74
80
  /** Wired by the eval manager; until then the internal eval callback answers 404. */
75
81
  evalServices?: EvalServices;
82
+ /** Host connections. Guide and MCP use the same service the console does. */
83
+ channels?: ChannelService;
84
+ /** Host memories. Guide and MCP use the same host the console does. */
85
+ memories?: MemoryHost;
76
86
  };
77
87
  /**
78
88
  * The full operator-console route surface: REST API (runs, stages,
@@ -8,6 +8,7 @@ import { mapProviderAuthError } from "../agent/providerInspect.js";
8
8
  import { handleProviderRoutes } from "./providerRoutes.js";
9
9
  import { handleProjectMcpRoutes } from "./projectMcpRoutes.js";
10
10
  import { handleTriggerRoutes } from "./triggerRoutes.js";
11
+ import { guideWriteRoot } from "../guide/writeRoot.js";
11
12
  import { handleEvalRoutes } from "./evalRoutes.js";
12
13
  import { defaultLoadJudgePrices } from "../evals/engine/promptfoo/judgeServices.js";
13
14
  import { engineStatus as promptfooEngineStatus } from "../evals/engine/promptfoo/status.js";
@@ -62,6 +63,8 @@ import { buildModelsResponse } from "../models/response.js";
62
63
  import { createAgentHostContext } from "../agents/hostContext.js";
63
64
  import { AgentScheduleSource, agentScheduleTickIntervalMsFromEnv, } from "../agents/scheduleSource.js";
64
65
  import { handleAgentAuthorRoutes } from "./agentAuthorRoutes.js";
66
+ import { handleGuideRoutes } from "./guideRoutes.js";
67
+ import { handleInternalAgentRoutes } from "./internalAgentRoutes.js";
65
68
  import { handleAgentRoutes } from "./agentRoutes.js";
66
69
  import { handleChannelRoutes } from "./channelRoutes.js";
67
70
  import { handleEvalInternalRoutes, isEvalInternalPath } from "./evalInternalRoutes.js";
@@ -217,7 +220,27 @@ export function createOperatorRoutes(deps) {
217
220
  resolveAgentPort({
218
221
  cwd,
219
222
  projectRoot: cwd,
223
+ guideDeps: {
224
+ manager,
225
+ store,
226
+ cwd,
227
+ agentDir,
228
+ projectRoot: guideWriteRoot(rootDir),
229
+ ...(providerAuthContext ? { providerAuthContext } : {}),
230
+ ...(deps.evalRunManager ? { evalRunManager: deps.evalRunManager } : {}),
231
+ ...(deps.channels ? { channels: deps.channels } : {}),
232
+ ...(deps.memories ? { memories: deps.memories } : {}),
233
+ ...(agentHost
234
+ ? {
235
+ agentService: agentHost.service,
236
+ agentChat: agentHost.chat,
237
+ agentConversationBackend: agentHost.backend,
238
+ }
239
+ : {}),
240
+ },
220
241
  });
242
+ if (deps.operatorHostSlot)
243
+ deps.operatorHostSlot.current = operatorHost;
221
244
  const workshopChatRegistry = deps.workshopChatRegistry ?? new WorkshopChatSessionRegistry(operatorHost);
222
245
  const workshopSessionStoreRoot = () => resolveWorkshopSessionStoreRoot();
223
246
  const workshopBuildStoreRoot = () => resolveWorkshopBuildStoreRoot();
@@ -263,6 +286,12 @@ export function createOperatorRoutes(deps) {
263
286
  if (await handleAgentAuthorRoutes(req, method, pathname, cwd, operatorHost, res, json))
264
287
  return true;
265
288
  }
289
+ if (pathname === "/api/guide" || pathname.startsWith("/api/guide/")) {
290
+ if (await handleGuideRoutes(req, method, pathname, operatorHost, res, json))
291
+ return true;
292
+ }
293
+ if (handleInternalAgentRoutes(method, pathname, operatorHost, res, json))
294
+ return true;
266
295
  if (PublicWebChat.owns(pathname)) {
267
296
  if (!agentHost) {
268
297
  json(res, 503, { code: "web_chat.unavailable" });
@@ -1064,7 +1093,9 @@ export function createOperatorRoutes(deps) {
1064
1093
  await readJsonBody(req);
1065
1094
  const runId = decodeURIComponent(retryMatch[1] ?? "");
1066
1095
  const stageId = decodeURIComponent(retryMatch[2] ?? "");
1067
- const result = await manager.retryStage(runId, stageId);
1096
+ const result = await manager.retryStage(runId, stageId, {
1097
+ awaitRoot: false,
1098
+ });
1068
1099
  if (!result.ok) {
1069
1100
  json(res, result.status ?? 500, mapRetryStageFailure(result));
1070
1101
  return true;
@@ -3178,8 +3209,11 @@ export async function startUiServer(options) {
3178
3209
  ? { workshopOperatorHost: options.workshopOperatorHost }
3179
3210
  : {}),
3180
3211
  ...(boot.agentHost ? { agentHost: boot.agentHost } : {}),
3212
+ ...(boot.operatorHostSlot ? { operatorHostSlot: boot.operatorHostSlot } : {}),
3181
3213
  ...(boot.evalRunManager ? { evalRunManager: boot.evalRunManager } : {}),
3182
3214
  ...(boot.evalServices ? { evalServices: boot.evalServices } : {}),
3215
+ ...(boot.channels ? { channels: boot.channels } : {}),
3216
+ ...(boot.memories ? { memories: boot.memories } : {}),
3183
3217
  ...(options.agentConversationBackend
3184
3218
  ? { agentConversationBackend: options.agentConversationBackend }
3185
3219
  : {}),
@@ -0,0 +1,6 @@
1
+ import type { ServerResponse } from "node:http";
2
+ import type { OperatorAgentHost } from "../operatorAgent/index.js";
3
+ type Json = (res: ServerResponse, status: number, body: unknown) => void;
4
+ export declare const INTERNAL_AGENTS_PATH = "/api/internal-agents";
5
+ export declare function handleInternalAgentRoutes(method: string, pathname: string, host: OperatorAgentHost, res: ServerResponse, json: Json, env?: NodeJS.ProcessEnv): boolean;
6
+ export {};