stageflow 0.39.0 → 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (360) hide show
  1. package/README.md +46 -19
  2. package/dist/agent/claudeAdapter.js +15 -1
  3. package/dist/agent/claudeTools.d.ts +13 -0
  4. package/dist/agent/claudeTools.js +98 -0
  5. package/dist/agent/piAdapter.js +38 -16
  6. package/dist/agent/sandboxPiTools.d.ts +99 -0
  7. package/dist/agent/sandboxPiTools.js +109 -0
  8. package/dist/agents/authorChat.d.ts +4 -0
  9. package/dist/agents/authorChat.js +113 -12
  10. package/dist/agents/authorSessionStore.d.ts +35 -0
  11. package/dist/agents/authorSessionStore.js +152 -0
  12. package/dist/agents/chatTurn.d.ts +2 -0
  13. package/dist/agents/chatTurn.js +17 -1
  14. package/dist/agents/errors.d.ts +1 -1
  15. package/dist/agents/errors.js +1 -0
  16. package/dist/agents/evalGate.d.ts +61 -0
  17. package/dist/agents/evalGate.js +103 -0
  18. package/dist/agents/evalSession.d.ts +26 -0
  19. package/dist/agents/evalSession.js +29 -0
  20. package/dist/agents/hostContext.d.ts +3 -0
  21. package/dist/agents/hostContext.js +4 -1
  22. package/dist/agents/runtime/piRuntime.d.ts +1 -1
  23. package/dist/agents/runtime/piRuntime.js +4 -4
  24. package/dist/agents/runtime/types.d.ts +3 -1
  25. package/dist/agents/service.d.ts +27 -2
  26. package/dist/agents/service.js +88 -13
  27. package/dist/agents/sessions.d.ts +8 -0
  28. package/dist/agents/sessions.js +50 -10
  29. package/dist/agents/store.d.ts +14 -3
  30. package/dist/agents/store.js +45 -7
  31. package/dist/agents/toolRisk.d.ts +21 -0
  32. package/dist/agents/toolRisk.js +50 -0
  33. package/dist/cli/doctorCommand.js +26 -0
  34. package/dist/cli/evalCommand.d.ts +20 -0
  35. package/dist/cli/evalCommand.js +720 -0
  36. package/dist/cli/exportAllCommand.js +1 -1
  37. package/dist/cli/hostClient.d.ts +60 -0
  38. package/dist/cli/hostClient.js +71 -0
  39. package/dist/cli/validateOutput.d.ts +2 -2
  40. package/dist/cli/validateOutput.js +2 -2
  41. package/dist/cli.js +10 -0
  42. package/dist/config/atomicWrite.d.ts +15 -0
  43. package/dist/config/atomicWrite.js +43 -0
  44. package/dist/config/browseCatalog.d.ts +2 -0
  45. package/dist/config/browseCatalog.js +4 -2
  46. package/dist/config/csv.d.ts +9 -0
  47. package/dist/config/csv.js +81 -0
  48. package/dist/config/datasetAgentCheck.d.ts +18 -0
  49. package/dist/config/datasetAgentCheck.js +109 -0
  50. package/dist/config/datasetCandidates.d.ts +22 -0
  51. package/dist/config/datasetCandidates.js +86 -0
  52. package/dist/config/datasetFromSessions.d.ts +34 -0
  53. package/dist/config/datasetFromSessions.js +105 -0
  54. package/dist/config/datasetHash.d.ts +1 -0
  55. package/dist/config/datasetHash.js +4 -0
  56. package/dist/config/datasetImport.d.ts +21 -0
  57. package/dist/config/datasetImport.js +120 -0
  58. package/dist/config/datasetStageCheck.d.ts +9 -0
  59. package/dist/config/datasetStageCheck.js +17 -0
  60. package/dist/config/datasetTypes.d.ts +27 -0
  61. package/dist/config/datasetTypes.js +1 -0
  62. package/dist/config/ensureCatalogScanRoot.d.ts +2 -1
  63. package/dist/config/ensureCatalogScanRoot.js +2 -6
  64. package/dist/config/evalCatalog.d.ts +22 -0
  65. package/dist/config/evalCatalog.js +67 -0
  66. package/dist/config/evalWriteRoots.d.ts +17 -0
  67. package/dist/config/evalWriteRoots.js +109 -0
  68. package/dist/config/hostConfig.js +3 -0
  69. package/dist/config/installEvalSample.d.ts +34 -0
  70. package/dist/config/installEvalSample.js +182 -0
  71. package/dist/config/loadDataset.d.ts +16 -0
  72. package/dist/config/loadDataset.js +318 -0
  73. package/dist/config/loadEval.d.ts +32 -0
  74. package/dist/config/loadEval.js +1085 -0
  75. package/dist/config/loadOutcome.d.ts +5 -0
  76. package/dist/config/loadStageflowManifest.js +64 -0
  77. package/dist/config/loadTask.js +34 -1
  78. package/dist/config/resolveAgentTarget.d.ts +32 -0
  79. package/dist/config/resolveAgentTarget.js +118 -0
  80. package/dist/config/resolveEvalTarget.d.ts +21 -0
  81. package/dist/config/resolveEvalTarget.js +89 -0
  82. package/dist/config/scanCatalogPaths.d.ts +1 -1
  83. package/dist/config/scanCatalogPaths.js +17 -5
  84. package/dist/config/validateCatalog.d.ts +1 -1
  85. package/dist/config/validateCatalog.js +9 -1
  86. package/dist/config/validateEval.d.ts +36 -0
  87. package/dist/config/validateEval.js +405 -0
  88. package/dist/config/writeDataset.d.ts +54 -0
  89. package/dist/config/writeDataset.js +186 -0
  90. package/dist/config/writeEval.d.ts +25 -0
  91. package/dist/config/writeEval.js +398 -0
  92. package/dist/diagnostics/checks.js +1 -1
  93. package/dist/evals/agentDiff.d.ts +13 -0
  94. package/dist/evals/agentDiff.js +11 -0
  95. package/dist/evals/canonical.d.ts +3 -0
  96. package/dist/evals/canonical.js +24 -0
  97. package/dist/evals/caseTask.d.ts +3 -0
  98. package/dist/evals/caseTask.js +9 -0
  99. package/dist/evals/compare.d.ts +94 -0
  100. package/dist/evals/compare.js +201 -0
  101. package/dist/evals/constants.d.ts +60 -0
  102. package/dist/evals/constants.js +95 -0
  103. package/dist/evals/engine/FakeEngine.d.ts +33 -0
  104. package/dist/evals/engine/FakeEngine.js +176 -0
  105. package/dist/evals/engine/index.d.ts +9 -0
  106. package/dist/evals/engine/index.js +19 -0
  107. package/dist/evals/engine/promptfoo/PromptfooEngine.d.ts +52 -0
  108. package/dist/evals/engine/promptfoo/PromptfooEngine.js +311 -0
  109. package/dist/evals/engine/promptfoo/errors.d.ts +5 -0
  110. package/dist/evals/engine/promptfoo/errors.js +8 -0
  111. package/dist/evals/engine/promptfoo/generate.d.ts +30 -0
  112. package/dist/evals/engine/promptfoo/generate.js +342 -0
  113. package/dist/evals/engine/promptfoo/guard.d.ts +10 -0
  114. package/dist/evals/engine/promptfoo/guard.js +116 -0
  115. package/dist/evals/engine/promptfoo/install.d.ts +17 -0
  116. package/dist/evals/engine/promptfoo/install.js +73 -0
  117. package/dist/evals/engine/promptfoo/judgeProviders.d.ts +2 -0
  118. package/dist/evals/engine/promptfoo/judgeProviders.js +9 -0
  119. package/dist/evals/engine/promptfoo/judgeServices.d.ts +6 -0
  120. package/dist/evals/engine/promptfoo/judgeServices.js +41 -0
  121. package/dist/evals/engine/promptfoo/locate.d.ts +21 -0
  122. package/dist/evals/engine/promptfoo/locate.js +68 -0
  123. package/dist/evals/engine/promptfoo/nodeVersion.d.ts +7 -0
  124. package/dist/evals/engine/promptfoo/nodeVersion.js +14 -0
  125. package/dist/evals/engine/promptfoo/normalize.d.ts +66 -0
  126. package/dist/evals/engine/promptfoo/normalize.js +261 -0
  127. package/dist/evals/engine/promptfoo/pinned.d.ts +5 -0
  128. package/dist/evals/engine/promptfoo/pinned.js +5 -0
  129. package/dist/evals/engine/promptfoo/rubricPrompt.d.ts +14 -0
  130. package/dist/evals/engine/promptfoo/rubricPrompt.js +103 -0
  131. package/dist/evals/engine/promptfoo/runTokens.d.ts +18 -0
  132. package/dist/evals/engine/promptfoo/runTokens.js +31 -0
  133. package/dist/evals/engine/promptfoo/status.d.ts +13 -0
  134. package/dist/evals/engine/promptfoo/status.js +16 -0
  135. package/dist/evals/estimate.d.ts +56 -0
  136. package/dist/evals/estimate.js +66 -0
  137. package/dist/evals/graders/codeGrader.d.ts +6 -0
  138. package/dist/evals/graders/codeGrader.js +85 -0
  139. package/dist/evals/graders/conversationCheckGrader.d.ts +6 -0
  140. package/dist/evals/graders/conversationCheckGrader.js +62 -0
  141. package/dist/evals/graders/fieldGrader.d.ts +11 -0
  142. package/dist/evals/graders/fieldGrader.js +117 -0
  143. package/dist/evals/graders/goal.d.ts +3 -0
  144. package/dist/evals/graders/goal.js +14 -0
  145. package/dist/evals/graders/index.d.ts +18 -0
  146. package/dist/evals/graders/index.js +34 -0
  147. package/dist/evals/graders/notApplicable.d.ts +5 -0
  148. package/dist/evals/graders/notApplicable.js +9 -0
  149. package/dist/evals/graders/path.d.ts +26 -0
  150. package/dist/evals/graders/path.js +75 -0
  151. package/dist/evals/graders/referenceGrader.d.ts +5 -0
  152. package/dist/evals/graders/referenceGrader.js +59 -0
  153. package/dist/evals/graders/schemaGrader.d.ts +5 -0
  154. package/dist/evals/graders/schemaGrader.js +29 -0
  155. package/dist/evals/graders/subject.d.ts +6 -0
  156. package/dist/evals/graders/subject.js +33 -0
  157. package/dist/evals/graders/toolCallsGrader.d.ts +6 -0
  158. package/dist/evals/graders/toolCallsGrader.js +100 -0
  159. package/dist/evals/graders/transcript.d.ts +12 -0
  160. package/dist/evals/graders/transcript.js +57 -0
  161. package/dist/evals/graders/types.d.ts +20 -0
  162. package/dist/evals/graders/types.js +1 -0
  163. package/dist/evals/judgeProviders.d.ts +16 -0
  164. package/dist/evals/judgeProviders.js +27 -0
  165. package/dist/evals/lineDiff.d.ts +5 -0
  166. package/dist/evals/lineDiff.js +34 -0
  167. package/dist/evals/listStatus.d.ts +31 -0
  168. package/dist/evals/listStatus.js +48 -0
  169. package/dist/evals/manager.d.ts +174 -0
  170. package/dist/evals/manager.js +787 -0
  171. package/dist/evals/plan.d.ts +58 -0
  172. package/dist/evals/plan.js +602 -0
  173. package/dist/evals/providerKeys.d.ts +1 -0
  174. package/dist/evals/providerKeys.js +21 -0
  175. package/dist/evals/read/agentTargets.d.ts +37 -0
  176. package/dist/evals/read/agentTargets.js +101 -0
  177. package/dist/evals/read/api.d.ts +196 -0
  178. package/dist/evals/read/api.js +358 -0
  179. package/dist/evals/read/compare.d.ts +4 -0
  180. package/dist/evals/read/compare.js +39 -0
  181. package/dist/evals/read/datasets.d.ts +105 -0
  182. package/dist/evals/read/datasets.js +318 -0
  183. package/dist/evals/read/list.d.ts +66 -0
  184. package/dist/evals/read/list.js +173 -0
  185. package/dist/evals/read/results.d.ts +46 -0
  186. package/dist/evals/read/results.js +107 -0
  187. package/dist/evals/read/runs.d.ts +149 -0
  188. package/dist/evals/read/runs.js +289 -0
  189. package/dist/evals/read/suggestions.d.ts +43 -0
  190. package/dist/evals/read/suggestions.js +73 -0
  191. package/dist/evals/runner/FakeAgentBackend.d.ts +40 -0
  192. package/dist/evals/runner/FakeAgentBackend.js +82 -0
  193. package/dist/evals/runner/agentCaseRunner.d.ts +24 -0
  194. package/dist/evals/runner/agentCaseRunner.js +388 -0
  195. package/dist/evals/runner/caseRunner.d.ts +7 -0
  196. package/dist/evals/runner/caseRunner.js +17 -0
  197. package/dist/evals/runner/conversationSubject.d.ts +18 -0
  198. package/dist/evals/runner/conversationSubject.js +123 -0
  199. package/dist/evals/runner/simulatorModel.d.ts +51 -0
  200. package/dist/evals/runner/simulatorModel.js +58 -0
  201. package/dist/evals/runner/simulatorPrompt.d.ts +29 -0
  202. package/dist/evals/runner/simulatorPrompt.js +59 -0
  203. package/dist/evals/runner/stageCaseRunner.d.ts +30 -0
  204. package/dist/evals/runner/stageCaseRunner.js +279 -0
  205. package/dist/evals/runner/userDriver.d.ts +71 -0
  206. package/dist/evals/runner/userDriver.js +104 -0
  207. package/dist/evals/scoring.d.ts +34 -0
  208. package/dist/evals/scoring.js +67 -0
  209. package/dist/evals/snapshot.d.ts +183 -0
  210. package/dist/evals/snapshot.js +65 -0
  211. package/dist/evals/stageDiff.d.ts +8 -0
  212. package/dist/evals/stageDiff.js +48 -0
  213. package/dist/evals/stats.d.ts +95 -0
  214. package/dist/evals/stats.js +220 -0
  215. package/dist/evals/statsAdapter.d.ts +4 -0
  216. package/dist/evals/statsAdapter.js +30 -0
  217. package/dist/evals/statsTypes.d.ts +51 -0
  218. package/dist/evals/statsTypes.js +18 -0
  219. package/dist/evals/storeError.d.ts +5 -0
  220. package/dist/evals/storeError.js +8 -0
  221. package/dist/evals/storeTypes.d.ts +207 -0
  222. package/dist/evals/storeTypes.js +1 -0
  223. package/dist/evals/tTable.d.ts +3 -0
  224. package/dist/evals/tTable.js +15 -0
  225. package/dist/evals/triage/constants.d.ts +26 -0
  226. package/dist/evals/triage/constants.js +33 -0
  227. package/dist/evals/triage/credentials.d.ts +13 -0
  228. package/dist/evals/triage/credentials.js +20 -0
  229. package/dist/evals/triage/jevClassifier.d.ts +14 -0
  230. package/dist/evals/triage/jevClassifier.js +124 -0
  231. package/dist/evals/triage/judgeClassifier.d.ts +10 -0
  232. package/dist/evals/triage/judgeClassifier.js +76 -0
  233. package/dist/evals/triage/prompts.d.ts +16 -0
  234. package/dist/evals/triage/prompts.js +64 -0
  235. package/dist/evals/triage/service.d.ts +79 -0
  236. package/dist/evals/triage/service.js +217 -0
  237. package/dist/evals/triage/stateBuilder.d.ts +19 -0
  238. package/dist/evals/triage/stateBuilder.js +125 -0
  239. package/dist/evals/triage/types.d.ts +60 -0
  240. package/dist/evals/triage/types.js +8 -0
  241. package/dist/evals/types.d.ts +347 -0
  242. package/dist/evals/types.js +1 -0
  243. package/dist/mcp/catalogTools.js +11 -1
  244. package/dist/mcp/deps.d.ts +7 -0
  245. package/dist/mcp/evalTools.d.ts +3 -0
  246. package/dist/mcp/evalTools.js +280 -0
  247. package/dist/mcp/server.js +1 -0
  248. package/dist/mcp/tools.js +2 -0
  249. package/dist/models/response.d.ts +7 -2
  250. package/dist/models/response.js +6 -1
  251. package/dist/operatorAgent/agentAuthorContext.d.ts +1 -1
  252. package/dist/operatorAgent/agentAuthorContext.js +1 -1
  253. package/dist/operatorAgent/piModel.js +17 -4
  254. package/dist/operatorAgent/profiles/agentAuthor.d.ts +3 -1
  255. package/dist/operatorAgent/profiles/agentAuthor.js +145 -20
  256. package/dist/operatorAgent/profiles/agentAuthorPlaybook.d.ts +1 -1
  257. package/dist/operatorAgent/profiles/agentAuthorPlaybook.js +48 -15
  258. package/dist/operatorAgent/profiles/authorReadable.d.ts +2 -0
  259. package/dist/operatorAgent/profiles/authorReadable.js +26 -0
  260. package/dist/package-meta.d.ts +1 -1
  261. package/dist/package-meta.js +1 -1
  262. package/dist/runstore/port.d.ts +43 -0
  263. package/dist/runstore/sqlite/SqliteRunStore.d.ts +28 -0
  264. package/dist/runstore/sqlite/SqliteRunStore.js +99 -5
  265. package/dist/runstore/sqlite/evalStore.d.ts +20 -0
  266. package/dist/runstore/sqlite/evalStore.js +541 -0
  267. package/dist/runstore/sqlite/migrations/015-evals.d.ts +12 -0
  268. package/dist/runstore/sqlite/migrations/015-evals.js +157 -0
  269. package/dist/runstore/sqlite/migrations/016-eval-subject-ref.d.ts +12 -0
  270. package/dist/runstore/sqlite/migrations/016-eval-subject-ref.js +20 -0
  271. package/dist/runstore/sqlite/migrations/017-eval-result-suggestions.d.ts +11 -0
  272. package/dist/runstore/sqlite/migrations/017-eval-result-suggestions.js +36 -0
  273. package/dist/runstore/sqlite/migrations/index.js +6 -0
  274. package/dist/runstore/sqlite/suggestionStore.d.ts +14 -0
  275. package/dist/runstore/sqlite/suggestionStore.js +132 -0
  276. package/dist/runtime/gitCheckoutCapability.d.ts +1 -0
  277. package/dist/runtime/gitCheckoutCapability.js +1 -1
  278. package/dist/runtime/gitWorktreeCheckout.d.ts +22 -0
  279. package/dist/runtime/gitWorktreeCheckout.js +120 -0
  280. package/dist/runtime/pipelineRunner.d.ts +2 -1
  281. package/dist/runtime/pipelineRunner.js +2 -0
  282. package/dist/runtime/runManager.d.ts +2 -1
  283. package/dist/runtime/runManager.js +8 -6
  284. package/dist/runtime/runRetentionSweep.js +2 -2
  285. package/dist/runtime/sandboxContainer.d.ts +62 -0
  286. package/dist/runtime/sandboxContainer.js +151 -0
  287. package/dist/runtime/sandboxDesk.d.ts +28 -0
  288. package/dist/runtime/sandboxDesk.js +242 -0
  289. package/dist/runtime/sandboxFiles.d.ts +20 -0
  290. package/dist/runtime/sandboxFiles.js +150 -0
  291. package/dist/runtime/stageEnvironment.js +2 -0
  292. package/dist/runtime/stageRoots.d.ts +9 -0
  293. package/dist/runtime/stageRoots.js +14 -0
  294. package/dist/runtime/stageRunner.d.ts +5 -0
  295. package/dist/runtime/stageRunner.js +38 -4
  296. package/dist/runtime/stageSandbox.d.ts +16 -0
  297. package/dist/runtime/stageSandbox.js +54 -0
  298. package/dist/runtime/stageWorker.js +138 -105
  299. package/dist/runtime/workspaceBinding.d.ts +2 -1
  300. package/dist/runtime/workspaceBinding.js +20 -4
  301. package/dist/server/agentAuthorRoutes.js +50 -1
  302. package/dist/server/bootstrap.d.ts +8 -0
  303. package/dist/server/bootstrap.js +56 -0
  304. package/dist/server/controlToken.js +3 -0
  305. package/dist/server/evalInternalRoutes.d.ts +14 -0
  306. package/dist/server/evalInternalRoutes.js +87 -0
  307. package/dist/server/evalRouteContext.d.ts +46 -0
  308. package/dist/server/evalRouteContext.js +43 -0
  309. package/dist/server/evalRoutes.d.ts +5 -0
  310. package/dist/server/evalRoutes.js +1427 -0
  311. package/dist/server/evalTriageRoutes.d.ts +2 -0
  312. package/dist/server/evalTriageRoutes.js +95 -0
  313. package/dist/server/http.d.ts +5 -0
  314. package/dist/server/http.js +32 -2
  315. package/dist/server/mcpHost.js +3 -0
  316. package/dist/toolkit/meta.d.ts +1 -1
  317. package/dist/toolkit/meta.js +15 -0
  318. package/dist/toolkit/profiles.js +1 -1
  319. package/dist/tools/readEmail.d.ts +3 -3
  320. package/dist/types/stageflowManifest.d.ts +6 -0
  321. package/dist/types/task.d.ts +11 -1
  322. package/dist/ui/assets/AssistantRuntimeProvider-qIX1xNoH.js +9 -0
  323. package/dist/ui/assets/{MarkdownText-D7tYaIFO.js → MarkdownText-BTrOqu5J.js} +1 -1
  324. package/dist/ui/assets/chat-Df_wCpak.js +5 -0
  325. package/dist/ui/assets/chat-P7KQznHm.css +2 -0
  326. package/dist/ui/assets/{main-BFo79krC.css → main-CHlmTNJe.css} +1 -1
  327. package/dist/ui/assets/main-CvVfxmMd.js +271 -0
  328. package/dist/ui/chat.html +3 -3
  329. package/dist/ui/index.html +3 -3
  330. package/examples/README.md +2 -0
  331. package/examples/document-policy-review/README.md +35 -0
  332. package/examples/document-policy-review/agents/compliance.agent.yaml +42 -0
  333. package/examples/document-policy-review/documents/acme-vendor-sow.md +15 -0
  334. package/examples/document-policy-review/documents/support-runbook.md +13 -0
  335. package/examples/document-policy-review/policies/acceptable-use.md +13 -0
  336. package/examples/document-policy-review/policies/data-handling.md +17 -0
  337. package/examples/document-policy-review/policies/vendor-security.md +17 -0
  338. package/examples/evals/README.md +123 -0
  339. package/examples/evals/critique-sample.dataset.jsonl +8 -0
  340. package/examples/evals/critique-sample.eval.yaml +34 -0
  341. package/examples/evals/critique-sample.task.yaml +4 -0
  342. package/examples/evals/eval-sample.pipeline.yaml +35 -0
  343. package/examples/evals/support-sample.agent.yaml +20 -0
  344. package/examples/evals/support-sample.dataset.jsonl +6 -0
  345. package/examples/evals/support-sample.eval.yaml +38 -0
  346. package/examples/memory/README.md +2 -0
  347. package/examples/memory/probe-bash.task.yaml +3 -0
  348. package/examples/memory/sandbox-memory-bash.pipeline.yaml +28 -0
  349. package/examples/ship-feature/README.md +15 -0
  350. package/examples/stageflow.yaml +6 -0
  351. package/package.json +1 -1
  352. package/skills/stageflow/SKILL.md +5 -2
  353. package/skills/stageflow/references/control-surface.md +4 -2
  354. package/skills/stageflow/references/evals.md +68 -0
  355. package/skills/stageflow-author/SKILL.md +4 -0
  356. package/skills/stageflow-run/SKILL.md +4 -0
  357. package/dist/ui/assets/AssistantRuntimeProvider-CsiPFhCT.js +0 -9
  358. package/dist/ui/assets/chat-BbLukq9W.js +0 -5
  359. package/dist/ui/assets/chat-zIZU_bP1.css +0 -2
  360. package/dist/ui/assets/main-CijYMeM0.js +0 -259
@@ -49,6 +49,7 @@ function toolDepsFrom(deps) {
49
49
  ...(deps.agentConversationBackend !== undefined
50
50
  ? { agentConversationBackend: deps.agentConversationBackend }
51
51
  : {}),
52
+ ...(deps.evalRunManager !== undefined ? { evalRunManager: deps.evalRunManager } : {}),
52
53
  };
53
54
  }
54
55
  function attachBusNotifications(server, bus) {
package/dist/mcp/tools.js CHANGED
@@ -5,6 +5,7 @@ import { registerControlTools } from "./controlTools.js";
5
5
  import { registerProjectMcpTools } from "./projectMcpTools.js";
6
6
  import { registerPipelineAuthorTools } from "./pipelineAuthorTools.js";
7
7
  import { registerProviderTools } from "./providerTools.js";
8
+ import { registerEvalTools } from "./evalTools.js";
8
9
  import { registerTriggerTools } from "./triggerTools.js";
9
10
  import { textResult } from "./toolResults.js";
10
11
  import { DEFAULT_TIMEOUT_MS, waitRun } from "./waitRun.js";
@@ -19,6 +20,7 @@ export function registerMcpTools(server, deps) {
19
20
  registerProviderTools(server, deps);
20
21
  registerProjectMcpTools(server, deps);
21
22
  registerTriggerTools(server, deps);
23
+ registerEvalTools(server, deps);
22
24
  registerPipelineAuthorTools(server, deps);
23
25
  registerAgentTools(server, deps);
24
26
  server.registerTool("wait_run", {
@@ -1,11 +1,16 @@
1
1
  import type { RunStore } from "../runstore/port.js";
2
2
  import { listModelsMultiProject } from "../config/multiProjectCatalog.js";
3
- import { type CatalogProvider } from "./catalog.js";
3
+ import { type CatalogModel, type CatalogProvider } from "./catalog.js";
4
+ export type ModelsResponseProvider = Omit<CatalogProvider, "models"> & {
5
+ models: Array<CatalogModel & {
6
+ judge_capable: boolean;
7
+ }>;
8
+ };
4
9
  export type ModelsResponse = {
5
10
  /** Catalog ids plus ids pipelines already use, sorted. Back-compatible with the old flat list. */
6
11
  models: string[];
7
12
  /** Connected providers with their models. */
8
- providers: CatalogProvider[];
13
+ providers: ModelsResponseProvider[];
9
14
  /** The model a new chat, stage or agent starts on. */
10
15
  default: string;
11
16
  fetchedAt: string;
@@ -1,4 +1,5 @@
1
1
  import { listModelsMultiProject } from "../config/multiProjectCatalog.js";
2
+ import { judgeCapableModels } from "../evals/judgeProviders.js";
2
3
  import { getModelCatalog } from "./catalog.js";
3
4
  /** The shared answer for GET /api/models, the MCP `list_models` tool and every picker. */
4
5
  export async function buildModelsResponse(options) {
@@ -9,12 +10,16 @@ export async function buildModelsResponse(options) {
9
10
  catalog.defaultModel(),
10
11
  ]);
11
12
  const listed = new Set(snapshot.models);
13
+ const judgeCapable = new Set(judgeCapableModels(snapshot.providers.flatMap((p) => p.models.map((m) => m.id))));
12
14
  const unlisted = used.models.filter((id) => !listed.has(id));
13
15
  return {
14
16
  used,
15
17
  response: {
16
18
  models: [...new Set([...snapshot.models, ...used.models])].sort((a, b) => a.localeCompare(b)),
17
- providers: snapshot.providers,
19
+ providers: snapshot.providers.map((provider) => ({
20
+ ...provider,
21
+ models: provider.models.map((model) => ({ ...model, judge_capable: judgeCapable.has(model.id) })),
22
+ })),
18
23
  default: defaultModel,
19
24
  fetchedAt: snapshot.fetchedAt,
20
25
  unlisted,
@@ -10,7 +10,7 @@ export type AgentAuthorContext = {
10
10
  projectRoot?: string;
11
11
  draft: AgentAuthorDraft;
12
12
  };
13
- export declare const AGENT_AUTHOR_GREETING = "What should this agent do? Tell me the job, the tools it needs, and anything it must never do without asking.";
13
+ export declare const AGENT_AUTHOR_GREETING = "Who is this agent for, and what does a good result look like? I'll ask a few questions before I fill the config.";
14
14
  export declare function emptyAgentAuthorDraft(): AgentAuthorDraft;
15
15
  export declare function isAgentAuthorContext(value: unknown): value is AgentAuthorContext;
16
16
  export declare function readAgentAuthorDraft(context: unknown): AgentAuthorDraft;
@@ -1,4 +1,4 @@
1
- export const AGENT_AUTHOR_GREETING = "What should this agent do? Tell me the job, the tools it needs, and anything it must never do without asking.";
1
+ export const AGENT_AUTHOR_GREETING = "Who is this agent for, and what does a good result look like? I'll ask a few questions before I fill the config.";
2
2
  export function emptyAgentAuthorDraft() {
3
3
  return {};
4
4
  }
@@ -11,7 +11,7 @@ import path from "node:path";
11
11
  import { defineTool, ModelRuntime, resolveCliModel, SessionManager, SettingsManager, } from "@earendil-works/pi-coding-agent";
12
12
  import { Type } from "typebox";
13
13
  import { createPiAgentSession, createSealedResourceLoader, resolveWorkshopToolNames, } from "../agent/piSessionFactory.js";
14
- import { bindCursorApiKey, cursorBridgePrompt } from "../agent/cursorProvider.js";
14
+ import { bindCursorApiKey, cursorBridgePrompt, isCursorModelRef } from "../agent/cursorProvider.js";
15
15
  import { findProviderSupport } from "../agent/providerSupport.js";
16
16
  import "../agent/cursorProvider.js";
17
17
  import { logger as rootLogger } from "../logging/logger.js";
@@ -19,7 +19,7 @@ import { globalStageflowHome } from "../project/globalHome.js";
19
19
  import { isUsableAuthFile, resolveCredentialBinding, } from "../runtime/credentialBinding.js";
20
20
  import { resolveWorkshopModel } from "../workshop/modelSettings.js";
21
21
  import { createOperatorAgentHost, invokeProfileTool, } from "./host.js";
22
- import { createAgentAuthorProfile } from "./profiles/agentAuthor.js";
22
+ import { AGENT_AUTHOR_PROFILE_ID, agentAuthorBridgeHint, createAgentAuthorProfile, formatAgentAuthorTurnPrompt, } from "./profiles/agentAuthor.js";
23
23
  import { createWorkshopAuthorProfile, } from "./profiles/workshopAuthor.js";
24
24
  import { readDraftFromContext } from "./draftContext.js";
25
25
  export function sessionNeedsExtensionReload(loaded, needed) {
@@ -434,7 +434,7 @@ export function createPiOperatorAgentModel(options) {
434
434
  syncAgentMessages(state.handle.session, state.handle.sessionManager);
435
435
  state.replayed = true;
436
436
  },
437
- async complete({ profile, message, tools, onDelta, onActivity, modelId }) {
437
+ async complete({ profile, message, contextSnapshot, tools, onDelta, onActivity, modelId }) {
438
438
  let state;
439
439
  try {
440
440
  state = await ensureBound(profile, tools, modelId);
@@ -472,8 +472,21 @@ export function createPiOperatorAgentModel(options) {
472
472
  }
473
473
  }
474
474
  });
475
+ const toolNames = profile.tools.map((tool) => tool.name);
476
+ let prompt = message;
477
+ if (profile.id === AGENT_AUTHOR_PROFILE_ID) {
478
+ prompt = formatAgentAuthorTurnPrompt(message, contextSnapshot);
479
+ prompt = `${prompt}\n\n${agentAuthorBridgeHint(toolNames)}`;
480
+ if (modelId && isCursorModelRef(modelId) && toolNames.length > 0) {
481
+ const aliasLines = toolNames.map((name) => `- pi__${name} (playbook name: ${name})`);
482
+ prompt = `${prompt}\n${aliasLines.join("\n")}`;
483
+ }
484
+ }
485
+ else {
486
+ prompt = cursorBridgePrompt(prompt, modelId, toolNames);
487
+ }
475
488
  try {
476
- await state.handle.session.prompt(cursorBridgePrompt(message, modelId, profile.tools.map((tool) => tool.name)));
489
+ await state.handle.session.prompt(prompt);
477
490
  }
478
491
  catch (err) {
479
492
  unsubscribe();
@@ -1,9 +1,11 @@
1
1
  import { type AgentAuthorContext, type AgentAuthorDraft } from "../agentAuthorContext.js";
2
2
  import type { OperatorAgentProfile } from "../types.js";
3
3
  export declare const AGENT_AUTHOR_PROFILE_ID = "agent-author";
4
+ export declare function formatAgentAuthorTurnPrompt(message: string, contextSnapshot: unknown): string;
5
+ export declare function agentAuthorBridgeHint(toolNames: readonly string[]): string;
4
6
  /** Objects merge key by key; arrays and scalars replace; `null` removes the key. */
5
7
  export declare function mergeDraftPatch(draft: AgentAuthorDraft, patch: Record<string, unknown>): AgentAuthorDraft;
6
- export declare const AGENT_AUTHOR_TOOL_NAMES: readonly ["read_project_file", "list_project_files", "list_mcp_servers", "update_agent_draft"];
8
+ export declare const AGENT_AUTHOR_TOOL_NAMES: readonly ["read_agent_draft", "read_project_file", "list_project_files", "list_mcp_servers", "list_author_catalog", "update_agent_draft"];
7
9
  export declare function createAgentAuthorProfile(): OperatorAgentProfile;
8
10
  export declare function createAgentAuthorContext(input: {
9
11
  projectRoot: string;
@@ -1,9 +1,15 @@
1
1
  import { readdir, readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
+ import { parse as parseYaml } from "yaml";
3
4
  import { agentAuthorContextAdapter, emptyAgentAuthorDraft, isAgentAuthorContext, readAgentAuthorDraft, } from "../agentAuthorContext.js";
4
5
  import { AGENT_ID_PATTERN, parseAgentDefinition } from "../../config/loadAgent.js";
5
6
  import { listProjectMcpCatalog } from "../../config/resolveStageMcpServers.js";
6
7
  import { AGENT_AUTHOR_PLAYBOOK } from "./agentAuthorPlaybook.js";
8
+ import { authorReadRefusal } from "./authorReadable.js";
9
+ import { connectionsFilePath } from "../../channels/registry.js";
10
+ import { loadConnectionsFile } from "../../channels/config.js";
11
+ import { listSkills } from "../../config/listSkills.js";
12
+ import { globalStageflowHome } from "../../project/globalHome.js";
7
13
  export const AGENT_AUTHOR_PROFILE_ID = "agent-author";
8
14
  const MAX_READ_CHARS = 32_000;
9
15
  let proposalSeq = 0;
@@ -38,6 +44,60 @@ function stringArg(args, key) {
38
44
  function isPlainObject(value) {
39
45
  return value !== null && typeof value === "object" && !Array.isArray(value);
40
46
  }
47
+ function coercePlainObject(value) {
48
+ if (isPlainObject(value))
49
+ return value;
50
+ if (typeof value !== "string")
51
+ return null;
52
+ const trimmed = value.trim();
53
+ if (!trimmed)
54
+ return null;
55
+ try {
56
+ const parsed = JSON.parse(trimmed);
57
+ if (isPlainObject(parsed))
58
+ return parsed;
59
+ }
60
+ catch {
61
+ /* not JSON */
62
+ }
63
+ try {
64
+ const parsed = parseYaml(trimmed);
65
+ if (isPlainObject(parsed))
66
+ return parsed;
67
+ }
68
+ catch {
69
+ /* not YAML */
70
+ }
71
+ return null;
72
+ }
73
+ export function formatAgentAuthorTurnPrompt(message, contextSnapshot) {
74
+ const draft = readAgentAuthorDraft(contextSnapshot);
75
+ const draftJson = JSON.stringify(draft, null, 2);
76
+ return [
77
+ "Current agent draft JSON. Ask until the job is clear. Call update_agent_draft only when you draft or revise; do not reply with YAML instead:",
78
+ draftJson,
79
+ "",
80
+ "Operator message:",
81
+ message.trim(),
82
+ ].join("\n");
83
+ }
84
+ export function agentAuthorBridgeHint(toolNames) {
85
+ return [
86
+ "Agent author: a thin first request is not a spec. Ask what you still need, or offer one suggestion when the answer is vague. Do not call update_agent_draft until they confirm the job or tell you to go ahead.",
87
+ "When you do change the draft, call update_agent_draft before you finish. Chat YAML does not update the panel.",
88
+ "You may read only docs/, policies/, AGENTS.md, and README.md. Do not grant a connection, MCP server, or skill that list_author_catalog or list_mcp_servers did not return. Do not set tools.browser.",
89
+ `Tools: ${toolNames.join(", ")}`,
90
+ ].join("\n");
91
+ }
92
+ function patchFromToolArgs(args) {
93
+ const patch = coercePlainObject(args.patch);
94
+ if (patch)
95
+ return patch;
96
+ const { summary: _summary, patch: _patch, ...rest } = args;
97
+ if (Object.keys(rest).length === 0)
98
+ return null;
99
+ return coercePlainObject(rest) ?? (isPlainObject(rest) ? rest : null);
100
+ }
41
101
  /** Objects merge key by key; arrays and scalars replace; `null` removes the key. */
42
102
  export function mergeDraftPatch(draft, patch) {
43
103
  const next = structuredClone(draft);
@@ -75,6 +135,9 @@ const readProjectFile = {
75
135
  const rejected = rejectEscape(root, rel);
76
136
  if (rejected)
77
137
  return { ok: false, content: null, error: rejected };
138
+ const docsOnly = authorReadRefusal(rel);
139
+ if (docsOnly)
140
+ return { ok: false, content: null, error: docsOnly };
78
141
  try {
79
142
  const bytes = await readFile(path.resolve(root, rel));
80
143
  if (bytes.includes(0))
@@ -95,32 +158,61 @@ const readProjectFile = {
95
158
  };
96
159
  const listProjectFiles = {
97
160
  name: "list_project_files",
98
- description: "List readable project files: AGENTS.md when present, and YAML files under agents/. Does not write.",
161
+ description: "List documentation the author may read: docs/ and policies/ (.md and .txt), plus AGENTS.md and README.md at the project root. Does not list source code.",
99
162
  async handler(_args, ctx) {
100
163
  const root = projectRootOf(ctx);
101
164
  if (!root)
102
165
  return { ok: false, content: null, error: "project root is not set" };
103
166
  const files = [];
104
- try {
105
- await readFile(path.join(root, "AGENTS.md"));
106
- files.push("AGENTS.md");
107
- }
108
- catch {
109
- /* absent */
167
+ for (const name of ["AGENTS.md", "README.md"]) {
168
+ try {
169
+ await readFile(path.join(root, name));
170
+ files.push(name);
171
+ }
172
+ catch {
173
+ /* absent */
174
+ }
110
175
  }
111
- try {
112
- const entries = await readdir(path.join(root, "agents"), { withFileTypes: true });
176
+ async function walk(rel) {
177
+ let entries;
178
+ try {
179
+ entries = await readdir(path.join(root, rel), { withFileTypes: true });
180
+ }
181
+ catch {
182
+ return;
183
+ }
113
184
  for (const entry of entries) {
114
- if (entry.isFile() && /\.ya?ml$/i.test(entry.name))
115
- files.push(`agents/${entry.name}`);
185
+ if (entry.name.startsWith(".") || entry.name === "node_modules")
186
+ continue;
187
+ const child = `${rel}/${entry.name}`;
188
+ if (entry.isDirectory())
189
+ await walk(child);
190
+ else if (!authorReadRefusal(child))
191
+ files.push(child);
116
192
  }
117
193
  }
118
- catch {
119
- /* no agents dir */
120
- }
194
+ await walk("docs");
195
+ await walk("policies");
196
+ files.sort();
121
197
  return { ok: true, content: { files } };
122
198
  },
123
199
  };
200
+ const readAgentDraft = {
201
+ name: "read_agent_draft",
202
+ description: "Read the current unsaved agent draft (agents/<id>.agent.yaml shape). Use before update_agent_draft when you need the latest fields.",
203
+ handler(_args, ctx) {
204
+ const draft = readAgentAuthorDraft(ctx.getContext());
205
+ const context = ctx.getContext();
206
+ const projectRoot = isAgentAuthorContext(context) ? context.projectRoot : undefined;
207
+ return {
208
+ ok: true,
209
+ content: {
210
+ ...(projectRoot ? { projectRoot } : {}),
211
+ draft,
212
+ },
213
+ };
214
+ },
215
+ };
124
216
  const listMcpServers = {
125
217
  name: "list_mcp_servers",
126
218
  description: "List MCP server names declared in the project's .mcp.json. Use these names under tools.mcp. Does not connect.",
@@ -132,6 +224,32 @@ const listMcpServers = {
132
224
  return { ok: true, content: { status: catalog.status, servers: catalog.servers.map((server) => server.name) } };
133
225
  },
134
226
  };
227
+ const listAuthorCatalog = {
228
+ name: "list_author_catalog",
229
+ description: "List host connections (id and provider only) and installed skill names. No secrets. Use these ids in channel.connection_id and tools.skills. If a name is not listed, do not grant it.",
230
+ async handler(_args, ctx) {
231
+ const root = projectRootOf(ctx);
232
+ if (!root)
233
+ return { ok: false, content: null, error: "project root is not set" };
234
+ let connections = [];
235
+ try {
236
+ const loaded = loadConnectionsFile(connectionsFilePath(root));
237
+ connections = loaded.connections.map((entry) => ({ id: entry.id, provider: entry.provider }));
238
+ }
239
+ catch {
240
+ connections = [];
241
+ }
242
+ let skills = [];
243
+ try {
244
+ const catalog = await listSkills({ cwd: root, agentDir: path.join(globalStageflowHome(), "workshop", "agent") });
245
+ skills = catalog.skills.map((skill) => skill.name);
246
+ }
247
+ catch {
248
+ skills = [];
249
+ }
250
+ return { ok: true, content: { connections, skills } };
251
+ },
252
+ };
135
253
  const updateAgentDraft = {
136
254
  name: "update_agent_draft",
137
255
  description: [
@@ -140,9 +258,9 @@ const updateAgentDraft = {
140
258
  "The result lists validation issues; fix them in a follow-up patch. Does not write YAML.",
141
259
  ].join(" "),
142
260
  handler(args, ctx) {
143
- const patch = args.patch;
144
- if (!isPlainObject(patch))
145
- return { ok: false, content: null, error: "patch must be an object" };
261
+ const patch = patchFromToolArgs(args);
262
+ if (!patch)
263
+ return { ok: false, content: null, error: "patch must be an object (JSON or YAML)" };
146
264
  const currentContext = ctx.getContext();
147
265
  const current = readAgentAuthorDraft(currentContext);
148
266
  const next = mergeDraftPatch(current, patch);
@@ -172,14 +290,21 @@ const updateAgentDraft = {
172
290
  };
173
291
  },
174
292
  };
175
- export const AGENT_AUTHOR_TOOL_NAMES = ["read_project_file", "list_project_files", "list_mcp_servers", "update_agent_draft"];
293
+ export const AGENT_AUTHOR_TOOL_NAMES = [
294
+ "read_agent_draft",
295
+ "read_project_file",
296
+ "list_project_files",
297
+ "list_mcp_servers",
298
+ "list_author_catalog",
299
+ "update_agent_draft",
300
+ ];
176
301
  export function createAgentAuthorProfile() {
177
302
  return {
178
303
  id: AGENT_AUTHOR_PROFILE_ID,
179
304
  title: "Agent author",
180
305
  playbook: AGENT_AUTHOR_PLAYBOOK,
181
- greeting: "What should this agent do?",
182
- tools: [readProjectFile, listProjectFiles, listMcpServers, updateAgentDraft],
306
+ greeting: "Who is this agent for, and what does a good result look like? I'll ask a few questions before I fill the config.",
307
+ tools: [readAgentDraft, readProjectFile, listProjectFiles, listMcpServers, listAuthorCatalog, updateAgentDraft],
183
308
  contextAdapter: agentAuthorContextAdapter(),
184
309
  };
185
310
  }
@@ -1 +1 @@
1
- export declare const AGENT_AUTHOR_PLAYBOOK = "You are the Stageflow Agent author. You draft one sealed project agent with the operator. You are not a task runner, and you do not write files.\n\n## How you talk\n- Short turns, plain words, present tense. One to four sentences.\n- Do not narrate tool names. Say what changed in the config.\n- The config panel shows the draft. The operator can edit it directly; your next patch builds on what they changed.\n\n## Tools\n- read_project_file and list_project_files are read-only. Use them when the operator points at the project, an existing agent file, or AGENTS.md.\n- list_mcp_servers names the servers in .mcp.json. Only grant MCP servers that exist there; if the operator wants one that isn't there, say so.\n- update_agent_draft applies a patch to the draft immediately. Call it as soon as you know enough. Do not wait for Keep. If it reports issues, fix them with another patch.\n- Never use bash, write, or edit. Save is the operator's button, not a tool.\n\n## The draft (agents/<id>.agent.yaml schema)\n- id (snake_case, starts with a letter; derived from name when omitted), name, description, greeting, instructions (required).\n- model: { backend: pi | claude, id?: \"provider/model\", thinking: low | medium | high }. Leave id out to use the project default unless they name one, and say so.\n- tools: { builtin: [read | write | shell] (whole groups), builtin_tools: [read | grep | find | ls | write | edit | bash] (single tools), stageflow: { groups: [...], tools: [...] }, mcp: { <server>: [tool names] or [\"*\"] }, skills: [names], browser: bool }.\n- approvals: { ask_first: [tool ids], approvers: { operators: bool, channel_ids: [] }, timeout_minutes, on_timeout: skip | deny }.\n Tool ids: builtin names (read, grep, find, ls, write, edit, bash), \"<server>.<tool>\" for MCP, \"agent:<id>\" for sub-agents. ask_first may only name granted tools.\n- sub_agents: [agent ids], observability: { capture: off | metadata | full }, limits: { session_minutes, max_turns, cost_usd | null, long_context: summarize | truncate }, guardrails: { block_patterns: [JavaScript regex] }, schedule: { cron, timezone?, message? }.\n\n## Defaults\n- A new agent saves disabled and starts sealed: read only. Write and shell stay off unless the job needs them.\n- Anything that sends, posts, writes, merges or deletes goes in approvals.ask_first.\n- Capture stays metadata unless they ask for full content.\n\n## After an edit\n- One sentence on what the agent will do now. Mention any assumption or open question. Do not paste YAML unless they ask.\n";
1
+ export declare const AGENT_AUTHOR_PLAYBOOK = "You are the Stageflow Agent author. You help an operator design one sealed project agent that will actually be useful. You are not a task runner, and you do not write files.\n\nA first message is a starting point, not a spec. Your job is to understand the use case before you fill the config.\n\n## How you talk\n- Short turns, plain words, present tense.\n- Two or three questions a turn. Do not dump a questionnaire.\n- Do not narrate tool names.\n- The config panel shows the draft. The operator can edit it directly; your next patch builds on what they changed.\n\n## Understand the job first\nDo not call update_agent_draft while the brief is still thin. Asking questions is a complete turn.\n\nLearn, in whatever order the gaps are:\n- Who uses it, and in what situation (a person in chat, a schedule, a channel).\n- What a good result looks like, and what a bad one looks like.\n- What it may read, and what it may change.\n- What it must never do without asking.\n- Where the source of truth lives. You may read only documentation: docs/, policies/, AGENTS.md, and README.md (.md and .txt). You cannot read source code. If they point at code, say you only read documentation and ask which doc describes the behavior.\n\nUse what they already said. Ask only what would change the draft.\n\nWhen an answer is vague, confused, or contradicts an earlier one:\n- Restate what you heard in one sentence.\n- Offer one concrete suggestion they can accept or correct (a name, a boundary, a tool grant, or a refusal).\n- Ask which part is wrong.\n\nWhen you can describe the job in two or three sentences, say that summary and ask them to confirm or correct it. Do not draft before they confirm, unless they tell you to go ahead anyway.\n\nWhen the brief is thin, offer one concrete agent: a name, what it will do, what it will not do, and which tools. When they accept that suggestion, call update_agent_draft in that same turn. Do not wait for another question.\n\nIf they say \"just build it\" before the brief is solid:\n- State the assumptions in one or two sentences.\n- Offer the narrowest useful version.\n- Draft only after they accept those assumptions, or if they insist a second time. Name every assumption in the reply after you draft.\n\n## When you draft\n- Call update_agent_draft in that turn. Chat text does not update the panel. Do not paste YAML unless they ask to see it.\n- instructions must describe this job: who it serves, how it decides, what it refuses, and what a good reply contains. Do not write a generic \"be helpful\" prompt.\n- Grant only the tools the job needs. A new agent stays sealed: read only, until the job needs write or shell.\n- Anything that sends, posts, writes, merges, or deletes goes in approvals.ask_first.\n- Leave model.id unset unless they name a model, and say you did that.\n- Capture stays metadata unless they ask for full content.\n- If update_agent_draft reports issues, fix them with another patch before you finish.\n\nAfter the patch, one sentence on what the agent will do. Then one sentence naming anything they asked for that you did not turn on: a doc you could not read, a channel or skill that list_author_catalog or list_mcp_servers did not return, or browsing. Do not set tools.browser. Do not invent a connection id, MCP server, or skill name.\n\n## Tools\n- read_agent_draft returns the current unsaved draft JSON. Each turn also includes that draft in the operator message.\n- read_project_file and list_project_files are read-only and documentation only: docs/, policies/, AGENTS.md, and README.md, and only .md or .txt. A refused path means you tell them you cannot read code or other files.\n- list_mcp_servers names the servers in .mcp.json. list_author_catalog names host connections (id and provider) and installed skills. Grant only names those tools return. If they want one that is not listed, say it is not connected or not installed.\n- update_agent_draft applies a patch immediately. Objects merge key by key, arrays replace, null removes a key. Do not wait for Keep.\n- Never use bash, write, or edit. Save is the operator's button, not a tool.\n\n## The draft (agents/<id>.agent.yaml schema)\n- id (snake_case, starts with a letter; derived from name when omitted), name, description, greeting, instructions (required).\n- model: { backend: pi | claude, id?: \"provider/model\", thinking: low | medium | high }.\n- tools: { builtin: [read | write | shell] (whole groups), builtin_tools: [read | grep | find | ls | write | edit | bash] (single tools), stageflow: { groups: [...], tools: [...] }, mcp: { <server>: [tool names] or [\"*\"] }, skills: [names], browser: bool }.\n- approvals: { ask_first: [tool ids], approvers: { operators: bool, channel_ids: [] }, timeout_minutes, on_timeout: skip | deny }.\n Tool ids: builtin names (read, grep, find, ls, write, edit, bash), \"<server>.<tool>\" for MCP, \"agent:<id>\" for sub-agents. ask_first may only name granted tools.\n- sub_agents: [agent ids], observability: { capture: off | metadata | full }, limits: { session_minutes, max_turns, cost_usd | null, long_context: summarize | truncate }, guardrails: { block_patterns: [JavaScript regex] }, schedule: { cron, timezone?, message? }.\n";
@@ -1,29 +1,62 @@
1
- export const AGENT_AUTHOR_PLAYBOOK = `You are the Stageflow Agent author. You draft one sealed project agent with the operator. You are not a task runner, and you do not write files.
1
+ export const AGENT_AUTHOR_PLAYBOOK = `You are the Stageflow Agent author. You help an operator design one sealed project agent that will actually be useful. You are not a task runner, and you do not write files.
2
+
3
+ A first message is a starting point, not a spec. Your job is to understand the use case before you fill the config.
2
4
 
3
5
  ## How you talk
4
- - Short turns, plain words, present tense. One to four sentences.
5
- - Do not narrate tool names. Say what changed in the config.
6
+ - Short turns, plain words, present tense.
7
+ - Two or three questions a turn. Do not dump a questionnaire.
8
+ - Do not narrate tool names.
6
9
  - The config panel shows the draft. The operator can edit it directly; your next patch builds on what they changed.
7
10
 
11
+ ## Understand the job first
12
+ Do not call update_agent_draft while the brief is still thin. Asking questions is a complete turn.
13
+
14
+ Learn, in whatever order the gaps are:
15
+ - Who uses it, and in what situation (a person in chat, a schedule, a channel).
16
+ - What a good result looks like, and what a bad one looks like.
17
+ - What it may read, and what it may change.
18
+ - What it must never do without asking.
19
+ - Where the source of truth lives. You may read only documentation: docs/, policies/, AGENTS.md, and README.md (.md and .txt). You cannot read source code. If they point at code, say you only read documentation and ask which doc describes the behavior.
20
+
21
+ Use what they already said. Ask only what would change the draft.
22
+
23
+ When an answer is vague, confused, or contradicts an earlier one:
24
+ - Restate what you heard in one sentence.
25
+ - Offer one concrete suggestion they can accept or correct (a name, a boundary, a tool grant, or a refusal).
26
+ - Ask which part is wrong.
27
+
28
+ When you can describe the job in two or three sentences, say that summary and ask them to confirm or correct it. Do not draft before they confirm, unless they tell you to go ahead anyway.
29
+
30
+ When the brief is thin, offer one concrete agent: a name, what it will do, what it will not do, and which tools. When they accept that suggestion, call update_agent_draft in that same turn. Do not wait for another question.
31
+
32
+ If they say "just build it" before the brief is solid:
33
+ - State the assumptions in one or two sentences.
34
+ - Offer the narrowest useful version.
35
+ - Draft only after they accept those assumptions, or if they insist a second time. Name every assumption in the reply after you draft.
36
+
37
+ ## When you draft
38
+ - Call update_agent_draft in that turn. Chat text does not update the panel. Do not paste YAML unless they ask to see it.
39
+ - instructions must describe this job: who it serves, how it decides, what it refuses, and what a good reply contains. Do not write a generic "be helpful" prompt.
40
+ - Grant only the tools the job needs. A new agent stays sealed: read only, until the job needs write or shell.
41
+ - Anything that sends, posts, writes, merges, or deletes goes in approvals.ask_first.
42
+ - Leave model.id unset unless they name a model, and say you did that.
43
+ - Capture stays metadata unless they ask for full content.
44
+ - If update_agent_draft reports issues, fix them with another patch before you finish.
45
+
46
+ After the patch, one sentence on what the agent will do. Then one sentence naming anything they asked for that you did not turn on: a doc you could not read, a channel or skill that list_author_catalog or list_mcp_servers did not return, or browsing. Do not set tools.browser. Do not invent a connection id, MCP server, or skill name.
47
+
8
48
  ## Tools
9
- - read_project_file and list_project_files are read-only. Use them when the operator points at the project, an existing agent file, or AGENTS.md.
10
- - list_mcp_servers names the servers in .mcp.json. Only grant MCP servers that exist there; if the operator wants one that isn't there, say so.
11
- - update_agent_draft applies a patch to the draft immediately. Call it as soon as you know enough. Do not wait for Keep. If it reports issues, fix them with another patch.
49
+ - read_agent_draft returns the current unsaved draft JSON. Each turn also includes that draft in the operator message.
50
+ - read_project_file and list_project_files are read-only and documentation only: docs/, policies/, AGENTS.md, and README.md, and only .md or .txt. A refused path means you tell them you cannot read code or other files.
51
+ - list_mcp_servers names the servers in .mcp.json. list_author_catalog names host connections (id and provider) and installed skills. Grant only names those tools return. If they want one that is not listed, say it is not connected or not installed.
52
+ - update_agent_draft applies a patch immediately. Objects merge key by key, arrays replace, null removes a key. Do not wait for Keep.
12
53
  - Never use bash, write, or edit. Save is the operator's button, not a tool.
13
54
 
14
55
  ## The draft (agents/<id>.agent.yaml schema)
15
56
  - id (snake_case, starts with a letter; derived from name when omitted), name, description, greeting, instructions (required).
16
- - model: { backend: pi | claude, id?: "provider/model", thinking: low | medium | high }. Leave id out to use the project default unless they name one, and say so.
57
+ - model: { backend: pi | claude, id?: "provider/model", thinking: low | medium | high }.
17
58
  - tools: { builtin: [read | write | shell] (whole groups), builtin_tools: [read | grep | find | ls | write | edit | bash] (single tools), stageflow: { groups: [...], tools: [...] }, mcp: { <server>: [tool names] or ["*"] }, skills: [names], browser: bool }.
18
59
  - approvals: { ask_first: [tool ids], approvers: { operators: bool, channel_ids: [] }, timeout_minutes, on_timeout: skip | deny }.
19
60
  Tool ids: builtin names (read, grep, find, ls, write, edit, bash), "<server>.<tool>" for MCP, "agent:<id>" for sub-agents. ask_first may only name granted tools.
20
61
  - sub_agents: [agent ids], observability: { capture: off | metadata | full }, limits: { session_minutes, max_turns, cost_usd | null, long_context: summarize | truncate }, guardrails: { block_patterns: [JavaScript regex] }, schedule: { cron, timezone?, message? }.
21
-
22
- ## Defaults
23
- - A new agent saves disabled and starts sealed: read only. Write and shell stay off unless the job needs them.
24
- - Anything that sends, posts, writes, merges or deletes goes in approvals.ask_first.
25
- - Capture stays metadata unless they ask for full content.
26
-
27
- ## After an edit
28
- - One sentence on what the agent will do now. Mention any assumption or open question. Do not paste YAML unless they ask.
29
62
  `;
@@ -0,0 +1,2 @@
1
+ /** Relative project path the agent author may read. Null when it is documentation. */
2
+ export declare function authorReadRefusal(rel: string): string | null;
@@ -0,0 +1,26 @@
1
+ const DOC_ROOTS = new Set(["docs", "policies"]);
2
+ const ROOT_FILES = new Set(["AGENTS.md", "README.md"]);
3
+ /** Relative project path the agent author may read. Null when it is documentation. */
4
+ export function authorReadRefusal(rel) {
5
+ const normalized = rel.replace(/\\/g, "/").replace(/^\.\//, "");
6
+ if (!normalized || normalized.startsWith("/") || normalized.split("/").includes("..")) {
7
+ return "path is outside the readable project surface";
8
+ }
9
+ const segments = normalized.split("/").filter(Boolean);
10
+ if (segments.some((part) => part === ".git" || part === "node_modules" || part.startsWith("."))) {
11
+ return "path is outside the readable project surface";
12
+ }
13
+ if (ROOT_FILES.has(normalized))
14
+ return null;
15
+ const [head, ...rest] = segments;
16
+ if (!head || !DOC_ROOTS.has(head)) {
17
+ return "only documentation under docs/ and policies/, plus AGENTS.md and README.md, can be read";
18
+ }
19
+ if (rest.length === 0)
20
+ return "path must be a documentation file";
21
+ const base = rest[rest.length - 1] ?? "";
22
+ if (!base.endsWith(".md") && !base.endsWith(".txt")) {
23
+ return "only .md and .txt documentation can be read";
24
+ }
25
+ return null;
26
+ }
@@ -1,3 +1,3 @@
1
1
  export declare const PACKAGE_NAME = "stageflow";
2
- export declare const PACKAGE_VERSION = "0.39.0";
2
+ export declare const PACKAGE_VERSION = "0.42.0";
3
3
  export declare const BUILD_SHA: string;
@@ -1,3 +1,3 @@
1
1
  export const PACKAGE_NAME = "stageflow";
2
- export const PACKAGE_VERSION = "0.39.0";
2
+ export const PACKAGE_VERSION = "0.42.0";
3
3
  export const BUILD_SHA = process.env.STAGEFLOW_BUILD_SHA ?? "unknown";
@@ -1,4 +1,5 @@
1
1
  import type { StageLogLine } from "../agent/activity.js";
2
+ import type { EvalTargetKind } from "../evals/constants.js";
2
3
  import type { ConfigOriginRecord } from "../config/configOrigin.js";
3
4
  import type { RunSubmission, RunSubmissionRecord } from "./submission.js";
4
5
  import type { AskOperatorPrompt } from "../tools/askOperator.js";
@@ -8,6 +9,9 @@ import type { CompletionCheck } from "../types/completion.js";
8
9
  import type { StageGateKind } from "../types/stage.js";
9
10
  import type { StageUsage } from "../types/usage.js";
10
11
  import type { OverviewRunRow } from "./overviewSummary.js";
12
+ import type { CreateEvalRunInput, EvalCaseInput, EvalCaseRow, EvalGraderVersionInput, EvalResultInput, EvalResultWithGrades, EvalReviewPatch, EvalReviewRow, EvalRunPage, EvalRunProgress, EvalRunRow, FinishEvalRunInput, ListEvalRunsQuery } from "../evals/storeTypes.js";
13
+ import type { SuggestionStatus } from "../evals/triage/constants.js";
14
+ import type { SuggestionInput, SuggestionRow } from "../evals/triage/types.js";
11
15
  export type { ConfigOriginRecord };
12
16
  export type RunStatus = "created" | "queued" | "running" | "succeeded" | "failed" | "cancelled";
13
17
  export type RunPipelineDagSnapshot = ResolvedPipelineDag & {
@@ -40,6 +44,7 @@ export type PipelineTrackProjection = {
40
44
  nodes: PipelineTrackNode[];
41
45
  edges: PipelineTrackEdge[];
42
46
  };
47
+ export type RunOrigin = "eval";
43
48
  export type RunMeta = {
44
49
  run_id: string;
45
50
  pipeline_id: string;
@@ -69,6 +74,8 @@ export type RunMeta = {
69
74
  config_origins?: ConfigOriginRecord[];
70
75
  pipeline_source?: "inline" | "path";
71
76
  caller_id?: string;
77
+ /** `eval` for stage runs started by an eval run; absent for normal runs. */
78
+ origin?: RunOrigin;
72
79
  run_manifest?: unknown;
73
80
  skip_gates?: boolean;
74
81
  /**
@@ -372,6 +379,7 @@ export type CreateRunInput = {
372
379
  */
373
380
  inlinePipeline?: InlinePipelineDefinition;
374
381
  callerId?: string;
382
+ origin?: RunOrigin;
375
383
  runManifest?: unknown;
376
384
  skipGates?: boolean;
377
385
  };
@@ -382,6 +390,8 @@ export type ListRunsFilter = {
382
390
  /** Match pipeline_id or pipeline_path */
383
391
  pipeline?: string;
384
392
  caller_id?: string;
393
+ /** Default (undefined) hides eval stage runs; `"any"` includes them; `"eval"` returns only them. */
394
+ origin?: RunOrigin | "any";
385
395
  };
386
396
  /**
387
397
  * Persistence port for pipeline run state.
@@ -466,6 +476,39 @@ export interface RunStore {
466
476
  listRuns(filter?: ListRunsFilter): Promise<RunSummary[]>;
467
477
  /** One light row per run created at or after `since` (ISO), newest first, with summed stage cost. No stages are loaded. */
468
478
  listRunOverviewRows(since: string): Promise<OverviewRunRow[]>;
479
+ createEvalRun(input: CreateEvalRunInput): Promise<EvalRunRow>;
480
+ insertEvalCases(evalRunId: string, cases: EvalCaseInput[]): Promise<void>;
481
+ /** Result row and its grade rows in one transaction; replaces any earlier grades for the same result. */
482
+ upsertEvalResult(result: EvalResultInput): Promise<void>;
483
+ updateEvalRunProgress(evalRunId: string, progress: EvalRunProgress): Promise<void>;
484
+ finishEvalRun(evalRunId: string, input: FinishEvalRunInput): Promise<void>;
485
+ getEvalRun(evalRunId: string): Promise<EvalRunRow | null>;
486
+ getEvalRunByNumber(projectRoot: string, evalId: string, runNumber: number): Promise<EvalRunRow | null>;
487
+ listEvalRuns(query: ListEvalRunsQuery): Promise<EvalRunPage>;
488
+ listEvalResults(evalRunId: string): Promise<EvalResultWithGrades[]>;
489
+ getEvalResultsForCase(evalRunId: string, caseId: string): Promise<EvalResultWithGrades[]>;
490
+ getEvalCases(evalRunId: string): Promise<EvalCaseRow[]>;
491
+ /** `tags` replaces the tag set and is accepted only for results with `passed = 0`. */
492
+ putEvalReview(evalRunId: string, caseId: string, repeatIndex: number, patch: EvalReviewPatch): Promise<EvalReviewRow>;
493
+ getEvalReviews(evalRunId: string): Promise<EvalReviewRow[]>;
494
+ /** The eval's failure-type vocabulary, most recently used first. */
495
+ listFailureTypes(projectRoot: string, evalId: string): Promise<string[]>;
496
+ /** Inserts unseen grader hashes with the next ordinal per grader id; returns grader id to ordinal. */
497
+ recordGraderVersions(projectRoot: string, evalId: string, graders: EvalGraderVersionInput[]): Promise<Record<string, number>>;
498
+ /** Deletes the eval run and its rows; returns the stage run ids it started so the caller can delete those runs. */
499
+ deleteEvalRun(evalRunId: string): Promise<string[]>;
500
+ /** Distinct non-null result subject refs (stage run ids or agent session ids) when the run's target kind matches. */
501
+ listEvalSubjectRefs(evalRunId: string, targetKind: EvalTargetKind): Promise<string[]>;
502
+ /** Inserts or replaces pending suggestions (accepted and dismissed are kept); returns the rows written. */
503
+ upsertEvalSuggestions(evalRunId: string, inputs: SuggestionInput[]): Promise<SuggestionRow[]>;
504
+ listEvalSuggestions(evalRunId: string, status?: SuggestionStatus): Promise<SuggestionRow[]>;
505
+ /** Pending only; returns how many changed. */
506
+ dismissEvalSuggestions(evalRunId: string, ids: string[]): Promise<number>;
507
+ /** Pending only; adds the failure type and `bucket:` tags through the review write path. */
508
+ acceptEvalSuggestions(evalRunId: string, ids: string[]): Promise<{
509
+ accepted: number;
510
+ tagged: number;
511
+ }>;
469
512
  /** Idempotent upsert; returns the realpath/resolve-normalized absolute key. */
470
513
  ensureProject(absPath: string): Promise<string>;
471
514
  /** Absolute roots from the durable projects registry. */