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
@@ -0,0 +1,157 @@
1
+ import { PACKAGE_VERSION } from "../../../package-meta.js";
2
+ /**
3
+ * Eval runs, their cases, results, grades and the author's reviews, plus `runs.origin` so stage runs
4
+ * started by an eval can be hidden from normal run views. `eval_run_counters` keeps `run_number`
5
+ * from being reused after an eval run is deleted.
6
+ */
7
+ export const MIGRATION_015 = {
8
+ version: 15,
9
+ name: "015_evals",
10
+ minStageflowVersion: PACKAGE_VERSION,
11
+ up(db) {
12
+ db.exec(`
13
+ ALTER TABLE runs ADD COLUMN origin TEXT;
14
+ CREATE INDEX runs_origin ON runs (origin);
15
+
16
+ CREATE TABLE eval_runs (
17
+ eval_run_id TEXT PRIMARY KEY,
18
+ project_root TEXT NOT NULL,
19
+ eval_id TEXT NOT NULL,
20
+ run_number INTEGER NOT NULL,
21
+ eval_path TEXT NOT NULL,
22
+ eval_hash TEXT NOT NULL,
23
+ target_kind TEXT NOT NULL CHECK (target_kind IN ('stage','agent')),
24
+ target_ref_json TEXT NOT NULL,
25
+ target_version_hash TEXT NOT NULL,
26
+ dataset_path TEXT NOT NULL,
27
+ dataset_hash TEXT NOT NULL,
28
+ config_json TEXT NOT NULL,
29
+ config_fingerprint TEXT NOT NULL,
30
+ config_components_json TEXT NOT NULL,
31
+ status TEXT NOT NULL CHECK (status IN ('queued','running','completed','failed','cancelled','budget_stopped')),
32
+ status_reason TEXT,
33
+ passed INTEGER,
34
+ repeats INTEGER NOT NULL,
35
+ pass_threshold REAL NOT NULL,
36
+ max_cost_usd REAL NOT NULL,
37
+ cases_total INTEGER NOT NULL,
38
+ results_total INTEGER NOT NULL,
39
+ results_done INTEGER NOT NULL DEFAULT 0,
40
+ cost_usd REAL NOT NULL DEFAULT 0,
41
+ engine_name TEXT NOT NULL,
42
+ engine_version TEXT NOT NULL,
43
+ stageflow_version TEXT NOT NULL,
44
+ label TEXT,
45
+ started_via TEXT NOT NULL CHECK (started_via IN ('console','cli','mcp')),
46
+ caller_id TEXT,
47
+ summary_json TEXT,
48
+ created_at TEXT NOT NULL,
49
+ started_at TEXT,
50
+ finished_at TEXT
51
+ );
52
+ CREATE UNIQUE INDEX eval_runs_number ON eval_runs (project_root, eval_id, run_number);
53
+ CREATE INDEX eval_runs_eval ON eval_runs (project_root, eval_id, created_at DESC);
54
+ CREATE INDEX eval_runs_status ON eval_runs (status);
55
+
56
+ CREATE TABLE eval_run_counters (
57
+ project_root TEXT NOT NULL,
58
+ eval_id TEXT NOT NULL,
59
+ last_number INTEGER NOT NULL,
60
+ PRIMARY KEY (project_root, eval_id)
61
+ );
62
+
63
+ CREATE TABLE eval_cases (
64
+ eval_run_id TEXT NOT NULL REFERENCES eval_runs (eval_run_id) ON DELETE CASCADE,
65
+ case_id TEXT NOT NULL,
66
+ position INTEGER NOT NULL,
67
+ case_json TEXT NOT NULL,
68
+ PRIMARY KEY (eval_run_id, case_id)
69
+ );
70
+
71
+ CREATE TABLE eval_results (
72
+ eval_run_id TEXT NOT NULL REFERENCES eval_runs (eval_run_id) ON DELETE CASCADE,
73
+ case_id TEXT NOT NULL,
74
+ repeat_index INTEGER NOT NULL,
75
+ status TEXT NOT NULL CHECK (status IN ('completed','stage_failed','needs_input','error','skipped_budget','cancelled')),
76
+ error_code TEXT,
77
+ error_message TEXT,
78
+ stage_run_id TEXT,
79
+ output_json TEXT,
80
+ latency_ms INTEGER,
81
+ cost_usd REAL,
82
+ tokens_in INTEGER,
83
+ tokens_out INTEGER,
84
+ score REAL,
85
+ passed INTEGER NOT NULL DEFAULT 0,
86
+ has_grader_errors INTEGER NOT NULL DEFAULT 0,
87
+ budget_warnings_json TEXT,
88
+ started_at TEXT,
89
+ finished_at TEXT,
90
+ PRIMARY KEY (eval_run_id, case_id, repeat_index),
91
+ FOREIGN KEY (eval_run_id, case_id) REFERENCES eval_cases (eval_run_id, case_id) ON DELETE CASCADE
92
+ );
93
+ CREATE INDEX eval_results_stage_run ON eval_results (stage_run_id);
94
+
95
+ CREATE TABLE eval_grades (
96
+ eval_run_id TEXT NOT NULL,
97
+ case_id TEXT NOT NULL,
98
+ repeat_index INTEGER NOT NULL,
99
+ grader_id TEXT NOT NULL,
100
+ grader_type TEXT NOT NULL,
101
+ status TEXT NOT NULL CHECK (status IN ('graded','error')),
102
+ passed INTEGER,
103
+ score REAL,
104
+ raw_value REAL,
105
+ reason TEXT,
106
+ details_json TEXT,
107
+ error_code TEXT,
108
+ judge_model TEXT,
109
+ judge_cost_usd REAL,
110
+ judge_tokens_in INTEGER,
111
+ judge_tokens_out INTEGER,
112
+ PRIMARY KEY (eval_run_id, case_id, repeat_index, grader_id),
113
+ FOREIGN KEY (eval_run_id, case_id, repeat_index) REFERENCES eval_results (eval_run_id, case_id, repeat_index) ON DELETE CASCADE
114
+ );
115
+
116
+ CREATE TABLE eval_grader_versions (
117
+ project_root TEXT NOT NULL,
118
+ eval_id TEXT NOT NULL,
119
+ grader_id TEXT NOT NULL,
120
+ grader_hash TEXT NOT NULL,
121
+ ordinal INTEGER NOT NULL,
122
+ first_seen_at TEXT NOT NULL,
123
+ PRIMARY KEY (project_root, eval_id, grader_id, grader_hash)
124
+ );
125
+
126
+ CREATE TABLE eval_failure_types (
127
+ project_root TEXT NOT NULL,
128
+ eval_id TEXT NOT NULL,
129
+ name TEXT NOT NULL,
130
+ created_at TEXT NOT NULL,
131
+ PRIMARY KEY (project_root, eval_id, name COLLATE NOCASE)
132
+ );
133
+
134
+ CREATE TABLE eval_result_reviews (
135
+ eval_run_id TEXT NOT NULL,
136
+ case_id TEXT NOT NULL,
137
+ repeat_index INTEGER NOT NULL,
138
+ label TEXT NOT NULL CHECK (label IN ('pass','fail')),
139
+ note TEXT,
140
+ created_at TEXT NOT NULL,
141
+ updated_at TEXT NOT NULL,
142
+ PRIMARY KEY (eval_run_id, case_id, repeat_index),
143
+ FOREIGN KEY (eval_run_id, case_id, repeat_index) REFERENCES eval_results (eval_run_id, case_id, repeat_index) ON DELETE CASCADE
144
+ );
145
+
146
+ CREATE TABLE eval_result_tags (
147
+ eval_run_id TEXT NOT NULL,
148
+ case_id TEXT NOT NULL,
149
+ repeat_index INTEGER NOT NULL,
150
+ tag TEXT NOT NULL,
151
+ created_at TEXT NOT NULL,
152
+ PRIMARY KEY (eval_run_id, case_id, repeat_index, tag COLLATE NOCASE),
153
+ FOREIGN KEY (eval_run_id, case_id, repeat_index) REFERENCES eval_results (eval_run_id, case_id, repeat_index) ON DELETE CASCADE
154
+ );
155
+ `);
156
+ },
157
+ };
@@ -0,0 +1,12 @@
1
+ import type Database from "better-sqlite3";
2
+ /**
3
+ * Agent eval results point at an agent session, not a stage run: `subject_ref` is the root subject id
4
+ * (stage run id for stage results, agent session id for agent results). `turns` and `tool_call_count`
5
+ * are agent summaries. Additive; stage rows keep `stage_run_id` and are backfilled.
6
+ */
7
+ export declare const MIGRATION_016: {
8
+ version: number;
9
+ name: string;
10
+ minStageflowVersion: string;
11
+ up(db: Database.Database): void;
12
+ };
@@ -0,0 +1,20 @@
1
+ import { PACKAGE_VERSION } from "../../../package-meta.js";
2
+ /**
3
+ * Agent eval results point at an agent session, not a stage run: `subject_ref` is the root subject id
4
+ * (stage run id for stage results, agent session id for agent results). `turns` and `tool_call_count`
5
+ * are agent summaries. Additive; stage rows keep `stage_run_id` and are backfilled.
6
+ */
7
+ export const MIGRATION_016 = {
8
+ version: 16,
9
+ name: "016_eval_subject_ref",
10
+ minStageflowVersion: PACKAGE_VERSION,
11
+ up(db) {
12
+ db.exec(`
13
+ ALTER TABLE eval_results ADD COLUMN subject_ref TEXT;
14
+ ALTER TABLE eval_results ADD COLUMN turns INTEGER;
15
+ ALTER TABLE eval_results ADD COLUMN tool_call_count INTEGER;
16
+ UPDATE eval_results SET subject_ref = stage_run_id WHERE stage_run_id IS NOT NULL;
17
+ CREATE INDEX eval_results_subject_ref ON eval_results (subject_ref);
18
+ `);
19
+ },
20
+ };
@@ -0,0 +1,11 @@
1
+ import type Database from "better-sqlite3";
2
+ /**
3
+ * Suggested bucket and failure type per failed eval result, produced on request by a classifier
4
+ * (`jev` or `judge`). A suggestion becomes a tag only when the author accepts it. Additive.
5
+ */
6
+ export declare const MIGRATION_017: {
7
+ version: number;
8
+ name: string;
9
+ minStageflowVersion: string;
10
+ up(db: Database.Database): void;
11
+ };
@@ -0,0 +1,36 @@
1
+ import { PACKAGE_VERSION } from "../../../package-meta.js";
2
+ /**
3
+ * Suggested bucket and failure type per failed eval result, produced on request by a classifier
4
+ * (`jev` or `judge`). A suggestion becomes a tag only when the author accepts it. Additive.
5
+ */
6
+ export const MIGRATION_017 = {
7
+ version: 17,
8
+ name: "017_eval_result_suggestions",
9
+ minStageflowVersion: PACKAGE_VERSION,
10
+ up(db) {
11
+ db.exec(`
12
+ CREATE TABLE eval_result_suggestions (
13
+ id TEXT PRIMARY KEY,
14
+ eval_run_id TEXT NOT NULL,
15
+ case_id TEXT NOT NULL,
16
+ repeat_index INTEGER NOT NULL,
17
+ source TEXT NOT NULL CHECK (source IN ('jev','judge')),
18
+ bucket TEXT NOT NULL CHECK (bucket IN ('agent_wrong','grader_wrong','case_wrong','unclear')),
19
+ failure_type TEXT,
20
+ confidence REAL NOT NULL,
21
+ probabilities_json TEXT NOT NULL,
22
+ grader_false_alarms_json TEXT NOT NULL,
23
+ model TEXT NOT NULL,
24
+ prompt_version INTEGER NOT NULL,
25
+ status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','accepted','dismissed')),
26
+ batch_id TEXT NOT NULL,
27
+ cost_usd REAL NOT NULL DEFAULT 0,
28
+ state_truncated INTEGER NOT NULL DEFAULT 0,
29
+ created_at TEXT NOT NULL,
30
+ UNIQUE (eval_run_id, case_id, repeat_index, source),
31
+ FOREIGN KEY (eval_run_id, case_id, repeat_index) REFERENCES eval_results (eval_run_id, case_id, repeat_index) ON DELETE CASCADE
32
+ );
33
+ CREATE INDEX eval_result_suggestions_run ON eval_result_suggestions (eval_run_id, status);
34
+ `);
35
+ },
36
+ };
@@ -13,6 +13,9 @@ import { MIGRATION_011 } from "./011-email-dispatch-key.js";
13
13
  import { MIGRATION_012 } from "./012-trigger-fires.js";
14
14
  import { MIGRATION_013 } from "./013-agents.js";
15
15
  import { MIGRATION_014 } from "./014-agent-deliveries.js";
16
+ import { MIGRATION_015 } from "./015-evals.js";
17
+ import { MIGRATION_016 } from "./016-eval-subject-ref.js";
18
+ import { MIGRATION_017 } from "./017-eval-result-suggestions.js";
16
19
  const DEFAULT_MIGRATIONS = [
17
20
  MIGRATION_001,
18
21
  MIGRATION_002,
@@ -28,6 +31,9 @@ const DEFAULT_MIGRATIONS = [
28
31
  MIGRATION_012,
29
32
  MIGRATION_013,
30
33
  MIGRATION_014,
34
+ MIGRATION_015,
35
+ MIGRATION_016,
36
+ MIGRATION_017,
31
37
  ];
32
38
  export const CURRENT_SCHEMA_VERSION = DEFAULT_MIGRATIONS[DEFAULT_MIGRATIONS.length - 1].version;
33
39
  function throwStoreSchemaTooNew(db, version, maxKnown) {
@@ -0,0 +1,14 @@
1
+ import type Database from "better-sqlite3";
2
+ import type { SuggestionInput, SuggestionRow } from "../../evals/triage/types.js";
3
+ /** Inserts or replaces pending suggestions; accepted and dismissed ones are kept. Returns the rows this call wrote. */
4
+ export declare function upsertEvalSuggestions(db: Database.Database, evalRunId: string, inputs: SuggestionInput[]): SuggestionRow[];
5
+ export declare function listEvalSuggestions(db: Database.Database, evalRunId: string, status?: SuggestionRow["status"]): SuggestionRow[];
6
+ export declare function dismissEvalSuggestions(db: Database.Database, evalRunId: string, ids: string[]): number;
7
+ /**
8
+ * Marks pending suggestions accepted and adds their failure type and `bucket:<bucket>` tags to the result
9
+ * through `putEvalReview`, keeping tags already there. Returns how many were accepted and how many results gained a tag.
10
+ */
11
+ export declare function acceptEvalSuggestions(db: Database.Database, evalRunId: string, ids: string[]): {
12
+ accepted: number;
13
+ tagged: number;
14
+ };
@@ -0,0 +1,132 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { EvalStoreError } from "../../evals/storeError.js";
3
+ import { BUCKET_TAG_PREFIX } from "../../evals/triage/constants.js";
4
+ import { getEvalReviews, putEvalReview } from "./evalStore.js";
5
+ const now = () => new Date().toISOString();
6
+ function suggestionFromDb(row) {
7
+ return {
8
+ id: row.id,
9
+ evalRunId: row.eval_run_id,
10
+ caseId: row.case_id,
11
+ repeatIndex: row.repeat_index,
12
+ source: row.source,
13
+ bucket: row.bucket,
14
+ failureType: row.failure_type,
15
+ confidence: row.confidence,
16
+ bucketProbabilities: JSON.parse(row.probabilities_json),
17
+ graderFalseAlarms: JSON.parse(row.grader_false_alarms_json),
18
+ model: row.model,
19
+ promptVersion: row.prompt_version,
20
+ status: row.status,
21
+ batchId: row.batch_id,
22
+ costUsd: row.cost_usd,
23
+ stateTruncated: row.state_truncated === 1,
24
+ createdAt: row.created_at,
25
+ };
26
+ }
27
+ /** Inserts or replaces pending suggestions; accepted and dismissed ones are kept. Returns the rows this call wrote. */
28
+ export function upsertEvalSuggestions(db, evalRunId, inputs) {
29
+ const written = [];
30
+ const at = now();
31
+ db.transaction(() => {
32
+ for (const input of inputs) {
33
+ const info = db
34
+ .prepare(`INSERT INTO eval_result_suggestions
35
+ (id, eval_run_id, case_id, repeat_index, source, bucket, failure_type, confidence, probabilities_json,
36
+ grader_false_alarms_json, model, prompt_version, status, batch_id, cost_usd, state_truncated, created_at)
37
+ VALUES (@id, @eval_run_id, @case_id, @repeat_index, @source, @bucket, @failure_type, @confidence, @probabilities_json,
38
+ @grader_false_alarms_json, @model, @prompt_version, 'pending', @batch_id, @cost_usd, @state_truncated, @at)
39
+ ON CONFLICT (eval_run_id, case_id, repeat_index, source) DO UPDATE SET
40
+ bucket = excluded.bucket, failure_type = excluded.failure_type, confidence = excluded.confidence,
41
+ probabilities_json = excluded.probabilities_json, grader_false_alarms_json = excluded.grader_false_alarms_json,
42
+ model = excluded.model, prompt_version = excluded.prompt_version, batch_id = excluded.batch_id,
43
+ cost_usd = excluded.cost_usd, state_truncated = excluded.state_truncated, created_at = excluded.created_at
44
+ WHERE eval_result_suggestions.status = 'pending'`)
45
+ .run({
46
+ id: `sg_${randomBytes(8).toString("hex")}`,
47
+ eval_run_id: evalRunId,
48
+ case_id: input.caseId,
49
+ repeat_index: input.repeatIndex,
50
+ source: input.source,
51
+ bucket: input.bucket,
52
+ failure_type: input.failureType,
53
+ confidence: input.confidence,
54
+ probabilities_json: JSON.stringify(input.bucketProbabilities),
55
+ grader_false_alarms_json: JSON.stringify(input.graderFalseAlarms),
56
+ model: input.model,
57
+ prompt_version: input.promptVersion,
58
+ batch_id: input.batchId,
59
+ cost_usd: input.costUsd,
60
+ state_truncated: input.stateTruncated ? 1 : 0,
61
+ at,
62
+ });
63
+ if (info.changes === 0)
64
+ continue;
65
+ const row = db
66
+ .prepare(`SELECT * FROM eval_result_suggestions WHERE eval_run_id = ? AND case_id = ? AND repeat_index = ? AND source = ?`)
67
+ .get(evalRunId, input.caseId, input.repeatIndex, input.source);
68
+ written.push(suggestionFromDb(row));
69
+ }
70
+ })();
71
+ return written;
72
+ }
73
+ export function listEvalSuggestions(db, evalRunId, status) {
74
+ const rows = (status !== undefined
75
+ ? db
76
+ .prepare(`SELECT * FROM eval_result_suggestions WHERE eval_run_id = ? AND status = ? ORDER BY case_id, repeat_index, source`)
77
+ .all(evalRunId, status)
78
+ : db
79
+ .prepare(`SELECT * FROM eval_result_suggestions WHERE eval_run_id = ? ORDER BY case_id, repeat_index, source`)
80
+ .all(evalRunId));
81
+ return rows.map(suggestionFromDb);
82
+ }
83
+ export function dismissEvalSuggestions(db, evalRunId, ids) {
84
+ let changed = 0;
85
+ db.transaction(() => {
86
+ for (const id of ids) {
87
+ changed += db
88
+ .prepare(`UPDATE eval_result_suggestions SET status = 'dismissed' WHERE eval_run_id = ? AND id = ? AND status = 'pending'`)
89
+ .run(evalRunId, id).changes;
90
+ }
91
+ })();
92
+ return changed;
93
+ }
94
+ /**
95
+ * Marks pending suggestions accepted and adds their failure type and `bucket:<bucket>` tags to the result
96
+ * through `putEvalReview`, keeping tags already there. Returns how many were accepted and how many results gained a tag.
97
+ */
98
+ export function acceptEvalSuggestions(db, evalRunId, ids) {
99
+ let accepted = 0;
100
+ let tagged = 0;
101
+ db.transaction(() => {
102
+ for (const id of ids) {
103
+ const found = db
104
+ .prepare(`SELECT * FROM eval_result_suggestions WHERE eval_run_id = ? AND id = ? AND status = 'pending'`)
105
+ .get(evalRunId, id);
106
+ if (found === undefined)
107
+ continue;
108
+ const suggestion = suggestionFromDb(found);
109
+ const add = [
110
+ ...(suggestion.failureType !== null ? [suggestion.failureType] : []),
111
+ `${BUCKET_TAG_PREFIX}${suggestion.bucket}`,
112
+ ];
113
+ const have = getEvalReviews(db, evalRunId).find((r) => r.caseId === suggestion.caseId && r.repeatIndex === suggestion.repeatIndex)?.tags ?? [];
114
+ const haveKeys = new Set(have.map((t) => t.toLowerCase()));
115
+ const fresh = add.filter((t) => !haveKeys.has(t.toLowerCase()));
116
+ if (fresh.length > 0) {
117
+ try {
118
+ putEvalReview(db, evalRunId, suggestion.caseId, suggestion.repeatIndex, { tags: [...have, ...fresh] });
119
+ tagged += 1;
120
+ }
121
+ catch (err) {
122
+ if (err instanceof EvalStoreError && err.code === "EVAL_TAGS_ON_PASSED_RESULT")
123
+ continue;
124
+ throw err;
125
+ }
126
+ }
127
+ db.prepare(`UPDATE eval_result_suggestions SET status = 'accepted' WHERE id = ?`).run(id);
128
+ accepted += 1;
129
+ }
130
+ })();
131
+ return { accepted, tagged };
132
+ }
@@ -1,4 +1,5 @@
1
1
  import type { CheckoutCapability } from "./completionCheckRunner.js";
2
+ export declare function runGit(root: string, args: string[]): Promise<Buffer>;
2
3
  /**
3
4
  * Git-backed checkout observation. It fingerprints tracked and untracked
4
5
  * files before the agent runs, so pre-existing changes are not attributed to
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
2
2
  import { lstat, readFile, readlink } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { runGit as execGit } from "../git/exec.js";
5
- async function runGit(root, args) {
5
+ export async function runGit(root, args) {
6
6
  const result = await execGit({
7
7
  cwd: root,
8
8
  args,
@@ -0,0 +1,22 @@
1
+ import { sanitizeContainerNameSegment } from "./sandboxContainer.js";
2
+ export declare const sanitizeBranchForPath: typeof sanitizeContainerNameSegment;
3
+ export declare function defaultBranchNameForTask(taskId: string): string;
4
+ export declare function worktreePathFor(projectRoot: string, branch: string): string;
5
+ /**
6
+ * Ensures a git worktree checked out to `branch` (creating the branch from
7
+ * `base` if it doesn't exist yet) exists at a deterministic path derived
8
+ * from the branch name, and returns that path.
9
+ *
10
+ * Deterministic (not per-run/random) on purpose: `resolveAndValidateCheckout`
11
+ * is called twice per run start (runManager.ts, for the checkout lease key,
12
+ * then pipelineRunner.ts, for the durable run record) — a random path would
13
+ * create two worktrees for one run. A stable path also means rerunning the
14
+ * same task/branch reuses the same worktree, matching the "leave it in
15
+ * place" behavior an operator gets from making one by hand.
16
+ */
17
+ export declare function ensureWorktreeCheckout(params: {
18
+ projectRoot: string;
19
+ branch?: string;
20
+ base?: string;
21
+ taskId: string;
22
+ }): Promise<string>;
@@ -0,0 +1,120 @@
1
+ import { access, mkdir, realpath } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { runGit } from "./gitCheckoutCapability.js";
4
+ import { sanitizeContainerNameSegment } from "./sandboxContainer.js";
5
+ /**
6
+ * Auto-created worktrees live as siblings of the project root (not nested
7
+ * inside it) — the same place `examples/ship-feature/README.md` already
8
+ * tells operators to put a hand-made one, just automated.
9
+ */
10
+ const WORKTREES_DIR_NAME = ".stageflow-worktrees";
11
+ export const sanitizeBranchForPath = sanitizeContainerNameSegment;
12
+ export function defaultBranchNameForTask(taskId) {
13
+ return `stageflow/${sanitizeBranchForPath(taskId)}`;
14
+ }
15
+ export function worktreePathFor(projectRoot, branch) {
16
+ return path.join(path.dirname(projectRoot), WORKTREES_DIR_NAME, sanitizeBranchForPath(branch));
17
+ }
18
+ async function git(projectRoot, args) {
19
+ const stdout = await runGit(projectRoot, args);
20
+ return stdout.toString("utf8");
21
+ }
22
+ async function registeredWorktreePaths(projectRoot) {
23
+ const stdout = await git(projectRoot, ["worktree", "list", "--porcelain"]);
24
+ const paths = new Set();
25
+ for (const line of stdout.split("\n")) {
26
+ const match = /^worktree (.+)$/.exec(line);
27
+ if (match)
28
+ paths.add(match[1]);
29
+ }
30
+ return paths;
31
+ }
32
+ async function branchExists(projectRoot, branch) {
33
+ try {
34
+ await git(projectRoot, ["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`]);
35
+ return true;
36
+ }
37
+ catch {
38
+ return false;
39
+ }
40
+ }
41
+ async function pathExists(target) {
42
+ try {
43
+ await access(target);
44
+ return true;
45
+ }
46
+ catch {
47
+ return false;
48
+ }
49
+ }
50
+ // A ref/branch value starting with `-` can get reinterpreted by git as a
51
+ // flag instead of a literal name (e.g. `-b --upload-pack=...`) — task YAML
52
+ // is operator-authored, not attacker input, but rejecting this outright is
53
+ // free and closes the class of mistake/footgun entirely.
54
+ function assertNotFlagLike(value, label) {
55
+ if (value.startsWith("-")) {
56
+ throw new Error(`Invalid ${label} "${value}": must not start with "-"`);
57
+ }
58
+ }
59
+ /**
60
+ * Ensures a git worktree checked out to `branch` (creating the branch from
61
+ * `base` if it doesn't exist yet) exists at a deterministic path derived
62
+ * from the branch name, and returns that path.
63
+ *
64
+ * Deterministic (not per-run/random) on purpose: `resolveAndValidateCheckout`
65
+ * is called twice per run start (runManager.ts, for the checkout lease key,
66
+ * then pipelineRunner.ts, for the durable run record) — a random path would
67
+ * create two worktrees for one run. A stable path also means rerunning the
68
+ * same task/branch reuses the same worktree, matching the "leave it in
69
+ * place" behavior an operator gets from making one by hand.
70
+ */
71
+ export async function ensureWorktreeCheckout(params) {
72
+ // `git worktree list --porcelain` reports canonicalized paths (symlinks
73
+ // resolved, e.g. macOS's /var -> /private/var) — realpath the project
74
+ // root up front so every path this function builds or compares matches
75
+ // what git itself reports, instead of drifting apart on a symlinked tmp
76
+ // dir or similar.
77
+ const projectRoot = await realpath(params.projectRoot);
78
+ const branch = params.branch ?? defaultBranchNameForTask(params.taskId);
79
+ assertNotFlagLike(branch, "checkout.branch");
80
+ if (params.base !== undefined)
81
+ assertNotFlagLike(params.base, "checkout.base");
82
+ const targetPath = worktreePathFor(projectRoot, branch);
83
+ const registered = await registeredWorktreePaths(projectRoot);
84
+ if (registered.has(targetPath)) {
85
+ if (await pathExists(targetPath)) {
86
+ return targetPath;
87
+ }
88
+ // Registered but the directory is gone (e.g. deleted with `rm -rf`
89
+ // instead of `git worktree remove`) — clear the stale registration so
90
+ // `worktree add` below doesn't refuse to reuse the path.
91
+ await git(projectRoot, ["worktree", "prune"]);
92
+ }
93
+ else if (await pathExists(targetPath)) {
94
+ throw new Error(`Worktree path already exists but is not a registered git worktree: ${targetPath}. Remove it or use a different branch name.`);
95
+ }
96
+ await mkdir(path.dirname(targetPath), { recursive: true });
97
+ if (await branchExists(projectRoot, branch)) {
98
+ await git(projectRoot, ["worktree", "add", targetPath, branch]);
99
+ }
100
+ else {
101
+ await git(projectRoot, [
102
+ "worktree",
103
+ "add",
104
+ targetPath,
105
+ "-b",
106
+ branch,
107
+ params.base ?? "HEAD",
108
+ ]);
109
+ }
110
+ // Two concurrent run-starts auto-creating the same branch/worktree can
111
+ // race inside `git worktree add` itself — for the "branch already
112
+ // exists" path this has been observed to exit 0 while leaving no
113
+ // directory behind (the loser silently no-ops instead of erroring).
114
+ // Turn that into a loud, unambiguous failure rather than handing back a
115
+ // "successful" path that doesn't exist.
116
+ if (!(await pathExists(targetPath))) {
117
+ throw new Error(`git worktree add reported success but ${targetPath} does not exist — likely a concurrent worktree creation for the same branch ("${branch}"); retry.`);
118
+ }
119
+ return targetPath;
120
+ }
@@ -1,7 +1,7 @@
1
1
  import type { RunSubmission } from "../runstore/submission.js";
2
2
  import type { StagePort } from "../agent/port.js";
3
3
  import { type ValidationFinding } from "../config/validateCatalog.js";
4
- import type { RunStore } from "../runstore/port.js";
4
+ import type { RunOrigin, RunStore } from "../runstore/port.js";
5
5
  import type { StageEnvelope } from "../types/envelope.js";
6
6
  import type { StageHitlController } from "./stageHitl.js";
7
7
  import type { InlinePipelineDefinition, LoadedPipeline } from "../types/pipeline.js";
@@ -130,6 +130,7 @@ export declare function startPipeline(options: {
130
130
  halted: boolean;
131
131
  };
132
132
  callerId?: string | null;
133
+ origin?: RunOrigin;
133
134
  skills?: SkillsPayload;
134
135
  }): Promise<StartedPipeline>;
135
136
  export declare function resolveTaskPath(taskArg: string, cwd?: string): string;
@@ -224,6 +224,7 @@ async function preparePipeline(options) {
224
224
  ? { pipelineBody: persistence.fields.pipelineBody }
225
225
  : {}),
226
226
  ...(callerId !== null ? { callerId } : {}),
227
+ ...(options.origin !== undefined ? { origin: options.origin } : {}),
227
228
  runManifest,
228
229
  skipGates: options.skipGates,
229
230
  });
@@ -329,6 +330,7 @@ export async function startPipeline(options) {
329
330
  operatorCatalog: options.operatorCatalog,
330
331
  skipGates: options.skipGates,
331
332
  callerId: options.callerId,
333
+ ...(options.origin !== undefined ? { origin: options.origin } : {}),
332
334
  ...(options.skills !== undefined ? { skills: options.skills } : {}),
333
335
  });
334
336
  if (prepared.run.created === false) {
@@ -2,7 +2,7 @@ import { type RunSubmission } from "../runstore/submission.js";
2
2
  import type { StagePort, OpaqueAnswer } from "../agent/port.js";
3
3
  import type { InlinePipelineDefinition } from "../types/pipeline.js";
4
4
  import { type DiskBreakdown, type FreeSpaceReader } from "../runstore/diskUsage.js";
5
- import { type RunStore } from "../runstore/port.js";
5
+ import { type RunOrigin, type RunStore } from "../runstore/port.js";
6
6
  import type { TaskFile } from "../types/task.js";
7
7
  import { type PipelineRunResult } from "./pipelineRunner.js";
8
8
  import { type SkillsPayload } from "./runSkills.js";
@@ -314,6 +314,7 @@ export declare class RunManager {
314
314
  projectRoot?: string;
315
315
  /** Attribution from auth only — never from request body/query. */
316
316
  callerId?: string | null;
317
+ origin?: RunOrigin;
317
318
  skills?: SkillsPayload;
318
319
  }, submission?: RunSubmission): Promise<StartRunResult>;
319
320
  rerun(runId: string, options?: {