stageflow 0.33.0 → 0.36.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 (379) hide show
  1. package/README.md +3 -3
  2. package/dist/agent/adapterSpill.d.ts +14 -0
  3. package/dist/agent/adapterSpill.js +83 -0
  4. package/dist/agent/agentBackend.d.ts +2 -2
  5. package/dist/agent/agentBackend.js +2 -2
  6. package/dist/agent/claudeAdapter.d.ts +13 -2
  7. package/dist/agent/claudeAdapter.js +56 -6
  8. package/dist/agent/cursorProvider.d.ts +3 -0
  9. package/dist/agent/cursorProvider.js +23 -6
  10. package/dist/agent/fakeAgent.d.ts +3 -3
  11. package/dist/agent/piAdapter.d.ts +2 -2
  12. package/dist/agent/piAdapter.js +125 -15
  13. package/dist/agent/piIsolatedMcp.d.ts +1 -0
  14. package/dist/agent/piIsolatedMcp.js +19 -1
  15. package/dist/agent/piIsolatedMcpProbe.d.ts +11 -0
  16. package/dist/agent/piIsolatedMcpProbe.js +20 -0
  17. package/dist/agent/port.d.ts +4 -2
  18. package/dist/agent/providerAuth.js +36 -3
  19. package/dist/agent/{resolveAgentPort.d.ts → resolveStagePort.d.ts} +8 -8
  20. package/dist/agent/{resolveAgentPort.js → resolveStagePort.js} +11 -11
  21. package/dist/agent/stageTurnDriver.d.ts +33 -0
  22. package/dist/agent/stageTurnDriver.js +64 -0
  23. package/dist/agent/toolOutputBudget.d.ts +46 -0
  24. package/dist/agent/toolOutputBudget.js +97 -0
  25. package/dist/agent/toolOutputBudgetExtension.d.ts +22 -0
  26. package/dist/agent/toolOutputBudgetExtension.js +141 -0
  27. package/dist/agent/toolOutputSpill.d.ts +34 -0
  28. package/dist/agent/toolOutputSpill.js +85 -0
  29. package/dist/browser/agentBrowserLiveViewRelay.d.ts +41 -0
  30. package/dist/browser/agentBrowserLiveViewRelay.js +801 -0
  31. package/dist/browser/anchor.d.ts +2 -0
  32. package/dist/browser/anchor.js +2 -1
  33. package/dist/browser/auditSink.d.ts +8 -0
  34. package/dist/browser/browserHost.d.ts +29 -0
  35. package/dist/browser/browserSweep.d.ts +4 -1
  36. package/dist/browser/browserSweep.js +7 -0
  37. package/dist/browser/browserTeardown.d.ts +1 -0
  38. package/dist/browser/browserTeardown.js +32 -2
  39. package/dist/browser/containerBrowserEndpoint.d.ts +11 -0
  40. package/dist/browser/containerBrowserEndpoint.js +57 -0
  41. package/dist/browser/containerBrowserHost.d.ts +24 -0
  42. package/dist/browser/containerBrowserHost.js +147 -0
  43. package/dist/browser/dockerSandboxOrchestrator.d.ts +26 -0
  44. package/dist/browser/dockerSandboxOrchestrator.js +142 -0
  45. package/dist/browser/fakeLiveViewRelay.d.ts +15 -0
  46. package/dist/browser/fakeLiveViewRelay.js +94 -0
  47. package/dist/browser/fakeLiveViewSource.d.ts +6 -0
  48. package/dist/browser/fakeLiveViewSource.js +16 -0
  49. package/dist/browser/fakeRemoteBrowserHost.d.ts +2 -1
  50. package/dist/browser/fakeRemoteBrowserHost.js +2 -1
  51. package/dist/browser/fakeSandboxOrchestrator.d.ts +9 -0
  52. package/dist/browser/fakeSandboxOrchestrator.js +44 -0
  53. package/dist/browser/gateHandoff.d.ts +0 -2
  54. package/dist/browser/gateHandoff.js +5 -11
  55. package/dist/browser/hostCapabilities.d.ts +23 -0
  56. package/dist/browser/hostCapabilities.js +23 -0
  57. package/dist/browser/humanLogin.d.ts +2 -2
  58. package/dist/browser/humanLogin.js +11 -2
  59. package/dist/browser/liveViewPersisted.d.ts +11 -0
  60. package/dist/browser/liveViewPersisted.js +22 -0
  61. package/dist/browser/liveViewRelay.d.ts +76 -0
  62. package/dist/browser/liveViewRelay.js +1 -0
  63. package/dist/browser/liveViewSessions.d.ts +25 -0
  64. package/dist/browser/liveViewSessions.js +139 -0
  65. package/dist/browser/liveViewSource.d.ts +25 -0
  66. package/dist/browser/liveViewSource.js +7 -0
  67. package/dist/browser/liveViewTickets.d.ts +46 -0
  68. package/dist/browser/liveViewTickets.js +122 -0
  69. package/dist/browser/localBrowserHost.d.ts +10 -0
  70. package/dist/browser/localBrowserHost.js +53 -7
  71. package/dist/browser/ownerScope.d.ts +10 -0
  72. package/dist/browser/ownerScope.js +6 -0
  73. package/dist/browser/profileStore.d.ts +5 -2
  74. package/dist/browser/profileStore.js +3 -5
  75. package/dist/browser/sandboxOrchestrator.d.ts +60 -0
  76. package/dist/browser/sandboxOrchestrator.js +8 -0
  77. package/dist/browser/sessionApiSandboxOrchestrator.d.ts +34 -0
  78. package/dist/browser/sessionApiSandboxOrchestrator.js +108 -0
  79. package/dist/browser/stageBrowserEnv.d.ts +7 -0
  80. package/dist/browser/stageBrowserEnv.js +53 -15
  81. package/dist/browser/stageHandoff.d.ts +47 -0
  82. package/dist/browser/stageHandoff.js +101 -0
  83. package/dist/browser/stageProfileLock.d.ts +1 -0
  84. package/dist/browser/stageProfileLock.js +1 -3
  85. package/dist/browser/volumeProfileStore.d.ts +7 -0
  86. package/dist/browser/volumeProfileStore.js +35 -0
  87. package/dist/channels/config.d.ts +11 -0
  88. package/dist/channels/config.js +95 -0
  89. package/dist/channels/configWriter.d.ts +40 -0
  90. package/dist/channels/configWriter.js +148 -0
  91. package/dist/channels/connectionEdits.d.ts +34 -0
  92. package/dist/channels/connectionEdits.js +53 -0
  93. package/dist/channels/errorInfo.d.ts +18 -0
  94. package/dist/channels/errorInfo.js +71 -0
  95. package/dist/channels/errors.d.ts +12 -0
  96. package/dist/channels/errors.js +13 -0
  97. package/dist/channels/health.d.ts +41 -0
  98. package/dist/channels/health.js +99 -0
  99. package/dist/channels/host.d.ts +55 -0
  100. package/dist/channels/host.js +185 -0
  101. package/dist/channels/http.d.ts +31 -0
  102. package/dist/channels/http.js +79 -0
  103. package/dist/channels/index.d.ts +10 -0
  104. package/dist/channels/index.js +9 -0
  105. package/dist/channels/ledger.d.ts +37 -0
  106. package/dist/channels/ledger.js +95 -0
  107. package/dist/channels/providers/agentmail.d.ts +26 -0
  108. package/dist/channels/providers/agentmail.js +81 -0
  109. package/dist/channels/providers/kit.d.ts +40 -0
  110. package/dist/channels/providers/kit.js +78 -0
  111. package/dist/channels/providers/photonImessage.d.ts +45 -0
  112. package/dist/channels/providers/photonImessage.js +152 -0
  113. package/dist/channels/providers/photonWhatsapp.d.ts +49 -0
  114. package/dist/channels/providers/photonWhatsapp.js +146 -0
  115. package/dist/channels/providers/slack.d.ts +25 -0
  116. package/dist/channels/providers/slack.js +101 -0
  117. package/dist/channels/providers/telegram.d.ts +21 -0
  118. package/dist/channels/providers/telegram.js +100 -0
  119. package/dist/channels/providers.d.ts +5 -0
  120. package/dist/channels/providers.js +22 -0
  121. package/dist/channels/registry.d.ts +54 -0
  122. package/dist/channels/registry.js +186 -0
  123. package/dist/channels/secretStore.d.ts +21 -0
  124. package/dist/channels/secretStore.js +75 -0
  125. package/dist/channels/secrets.d.ts +15 -0
  126. package/dist/channels/secrets.js +50 -0
  127. package/dist/channels/service.d.ts +54 -0
  128. package/dist/channels/service.js +307 -0
  129. package/dist/channels/stageChannels.d.ts +45 -0
  130. package/dist/channels/stageChannels.js +110 -0
  131. package/dist/channels/types.d.ts +142 -0
  132. package/dist/channels/types.js +1 -0
  133. package/dist/cli/browserCommand.d.ts +3 -1
  134. package/dist/cli/browserCommand.js +25 -20
  135. package/dist/cli/doctorCommand.js +3 -1
  136. package/dist/cli/resolveCliProjectRoot.d.ts +7 -0
  137. package/dist/cli/resolveCliProjectRoot.js +27 -0
  138. package/dist/cli/runCommand.d.ts +1 -1
  139. package/dist/cli/runCommand.js +59 -17
  140. package/dist/cli/skillsCommand.d.ts +20 -0
  141. package/dist/cli/skillsCommand.js +74 -31
  142. package/dist/cli/validateCommand.d.ts +1 -1
  143. package/dist/cli/validateCommand.js +22 -4
  144. package/dist/cli.js +4 -4
  145. package/dist/config/browseCatalog.d.ts +2 -0
  146. package/dist/config/browseCatalog.js +6 -0
  147. package/dist/config/catalogRelativePath.d.ts +4 -0
  148. package/dist/config/catalogRelativePath.js +37 -9
  149. package/dist/config/createStage.d.ts +1 -0
  150. package/dist/config/createStage.js +11 -2
  151. package/dist/config/createTask.d.ts +30 -0
  152. package/dist/config/createTask.js +161 -0
  153. package/dist/config/createTrigger.d.ts +15 -0
  154. package/dist/config/createTrigger.js +32 -25
  155. package/dist/config/draftPackage.d.ts +42 -0
  156. package/dist/config/draftPackage.js +220 -23
  157. package/dist/config/ensureCatalogScanRoot.d.ts +2 -1
  158. package/dist/config/ensureCatalogScanRoot.js +6 -2
  159. package/dist/config/findTaskInCatalog.d.ts +33 -0
  160. package/dist/config/findTaskInCatalog.js +69 -0
  161. package/dist/config/hostConfig.d.ts +21 -0
  162. package/dist/config/hostConfig.js +114 -1
  163. package/dist/config/importTask.d.ts +19 -0
  164. package/dist/config/importTask.js +133 -0
  165. package/dist/config/installSkillRequest.d.ts +40 -0
  166. package/dist/config/installSkillRequest.js +286 -0
  167. package/dist/config/loadOutcome.d.ts +11 -6
  168. package/dist/config/loadPipeline.js +27 -4
  169. package/dist/config/loadStage.js +17 -0
  170. package/dist/config/loadStageflowManifest.js +4 -1
  171. package/dist/config/loadTask.js +2 -0
  172. package/dist/config/loadTrigger.js +2 -0
  173. package/dist/config/mergePipelineIncludes.d.ts +3 -0
  174. package/dist/config/mergePipelineIncludes.js +30 -0
  175. package/dist/config/normalizePipelineStageEntry.js +10 -1
  176. package/dist/config/pipelineStageKeys.js +1 -0
  177. package/dist/config/printTargetYaml.js +0 -1
  178. package/dist/config/publishDraftPackage.d.ts +5 -1
  179. package/dist/config/publishDraftPackage.js +4 -1
  180. package/dist/config/resolveCatalogRoots.d.ts +4 -1
  181. package/dist/config/resolveCatalogRoots.js +18 -1
  182. package/dist/config/skillUsageIndex.d.ts +6 -0
  183. package/dist/config/skillUsageIndex.js +39 -0
  184. package/dist/config/stageBrowser.js +13 -0
  185. package/dist/config/triggerAdapterStatus.d.ts +11 -0
  186. package/dist/config/triggerAdapterStatus.js +90 -0
  187. package/dist/config/triggerCatalog.d.ts +41 -2
  188. package/dist/config/triggerCatalog.js +107 -15
  189. package/dist/config/updateTask.d.ts +30 -0
  190. package/dist/config/updateTask.js +119 -0
  191. package/dist/config/updateTrigger.d.ts +38 -0
  192. package/dist/config/updateTrigger.js +220 -0
  193. package/dist/config/validateCatalog.d.ts +9 -2
  194. package/dist/config/validateCatalog.js +71 -7
  195. package/dist/config/workshopCatalogFilenames.d.ts +37 -0
  196. package/dist/config/workshopCatalogFilenames.js +125 -0
  197. package/dist/config/workshopCatalogRepair.d.ts +7 -0
  198. package/dist/config/workshopCatalogRepair.js +144 -0
  199. package/dist/config/yamlParsePosition.d.ts +4 -0
  200. package/dist/config/yamlParsePosition.js +9 -0
  201. package/dist/errors/codes.d.ts +1 -1
  202. package/dist/errors/codes.js +1 -0
  203. package/dist/hosted/runtime.d.ts +2 -2
  204. package/dist/hosted/types.d.ts +1 -1
  205. package/dist/hosted/types.js +1 -1
  206. package/dist/index.d.ts +3 -2
  207. package/dist/index.js +2 -1
  208. package/dist/mcp/catalogTools.js +1 -0
  209. package/dist/mcp/getStartedTool.js +15 -6
  210. package/dist/mcp/triggerTools.js +2 -8
  211. package/dist/operatorAgent/agentPort.d.ts +11 -0
  212. package/dist/operatorAgent/agentPort.js +12 -0
  213. package/dist/operatorAgent/index.d.ts +1 -0
  214. package/dist/operatorAgent/index.js +1 -0
  215. package/dist/operatorAgent/piModel.js +26 -6
  216. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +1 -1
  217. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +4 -2
  218. package/dist/operatorAgent/profiles/workshopAuthorTools.js +103 -16
  219. package/dist/package-meta.d.ts +1 -1
  220. package/dist/package-meta.js +1 -1
  221. package/dist/preflight/channelChecks.d.ts +17 -0
  222. package/dist/preflight/channelChecks.js +106 -0
  223. package/dist/preflight/pipelinePreflight.d.ts +9 -2
  224. package/dist/preflight/pipelinePreflight.js +9 -1
  225. package/dist/project/ensureProjectRootsFromEnv.d.ts +3 -0
  226. package/dist/project/ensureProjectRootsFromEnv.js +22 -0
  227. package/dist/project/globalHome.d.ts +1 -0
  228. package/dist/project/globalHome.js +18 -0
  229. package/dist/project/workspaceCatalog.d.ts +10 -0
  230. package/dist/project/workspaceCatalog.js +91 -0
  231. package/dist/runstore/backup.js +14 -4
  232. package/dist/runstore/port.d.ts +11 -1
  233. package/dist/runstore/restore.js +5 -2
  234. package/dist/runstore/sqlite/SqliteRunStore.d.ts +8 -0
  235. package/dist/runstore/sqlite/SqliteRunStore.js +29 -9
  236. package/dist/runstore/sqlite/migrations/012-trigger-fires.d.ts +7 -0
  237. package/dist/runstore/sqlite/migrations/012-trigger-fires.js +18 -0
  238. package/dist/runstore/sqlite/migrations/index.js +2 -0
  239. package/dist/runstore/stageSnapshot.d.ts +3 -1
  240. package/dist/runstore/stageSnapshot.js +19 -0
  241. package/dist/runstore/workspaceLayout.js +27 -0
  242. package/dist/runstore/writeContainedArtifact.d.ts +1 -0
  243. package/dist/runstore/writeContainedArtifact.js +52 -0
  244. package/dist/runtime/credentialBinding.js +14 -6
  245. package/dist/runtime/emailSource.js +20 -28
  246. package/dist/runtime/githubPollSource.js +20 -28
  247. package/dist/runtime/pipelineRunner.d.ts +4 -4
  248. package/dist/runtime/pipelineRunner.js +3 -1
  249. package/dist/runtime/pipelineScheduler.d.ts +2 -2
  250. package/dist/runtime/pipelineScheduler.js +3 -0
  251. package/dist/runtime/repositoryMaterialize.d.ts +1 -1
  252. package/dist/runtime/resumeReconstruct.d.ts +2 -2
  253. package/dist/runtime/resumeTimedOut.d.ts +2 -2
  254. package/dist/runtime/runManager.d.ts +18 -2
  255. package/dist/runtime/runManager.js +99 -31
  256. package/dist/runtime/runRetryCoordinator.d.ts +2 -2
  257. package/dist/runtime/scheduleSource.js +15 -24
  258. package/dist/runtime/stageAttemptBootstrap.d.ts +2 -2
  259. package/dist/runtime/stageAttemptBootstrap.js +26 -1
  260. package/dist/runtime/stageProcessLauncher.js +33 -3
  261. package/dist/runtime/stageRoots.js +8 -1
  262. package/dist/runtime/stageRunner.d.ts +2 -2
  263. package/dist/runtime/stageRunner.js +8 -2
  264. package/dist/runtime/stageWorker.js +2 -2
  265. package/dist/runtime/triggerPort.d.ts +5 -3
  266. package/dist/runtime/triggerRunner.d.ts +1 -0
  267. package/dist/runtime/triggerRunner.js +27 -20
  268. package/dist/runtime/webhookAccept.d.ts +25 -0
  269. package/dist/runtime/webhookAccept.js +56 -0
  270. package/dist/server/bootstrap.d.ts +10 -2
  271. package/dist/server/bootstrap.js +19 -1
  272. package/dist/server/channelRoutes.d.ts +9 -0
  273. package/dist/server/channelRoutes.js +88 -0
  274. package/dist/server/controlToken.d.ts +1 -1
  275. package/dist/server/controlToken.js +18 -2
  276. package/dist/server/healthSurfaces.d.ts +12 -0
  277. package/dist/server/healthSurfaces.js +27 -6
  278. package/dist/server/http.d.ts +19 -10
  279. package/dist/server/http.js +616 -85
  280. package/dist/server/liveViewPath.d.ts +9 -0
  281. package/dist/server/liveViewPath.js +8 -0
  282. package/dist/server/liveViewRoutes.d.ts +58 -0
  283. package/dist/server/liveViewRoutes.js +565 -0
  284. package/dist/server/mcpHost.d.ts +4 -2
  285. package/dist/server/mcpHost.js +2 -0
  286. package/dist/server/readCatalogFile.d.ts +29 -0
  287. package/dist/server/readCatalogFile.js +215 -0
  288. package/dist/server/triggerRoutes.d.ts +1 -0
  289. package/dist/server/triggerRoutes.js +200 -73
  290. package/dist/tools/sendMessage.d.ts +21 -0
  291. package/dist/tools/sendMessage.js +25 -0
  292. package/dist/tools/writeStageArtifact.d.ts +9 -0
  293. package/dist/tools/writeStageArtifact.js +2 -47
  294. package/dist/types/pipeline.d.ts +5 -2
  295. package/dist/types/stage.d.ts +14 -1
  296. package/dist/types/stageflowManifest.d.ts +1 -1
  297. package/dist/ui/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  298. package/dist/ui/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  299. package/dist/ui/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  300. package/dist/ui/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  301. package/dist/ui/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
  302. package/dist/ui/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
  303. package/dist/ui/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
  304. package/dist/ui/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  305. package/dist/ui/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
  306. package/dist/ui/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
  307. package/dist/ui/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  308. package/dist/ui/assets/index-BOhqnPNg.js +260 -0
  309. package/dist/ui/assets/{index-Bv4p1aPK.css → index-Clv4sJpU.css} +1 -1
  310. package/dist/ui/index.html +4 -4
  311. package/dist/workshop/autosave.d.ts +2 -2
  312. package/dist/workshop/autosave.js +4 -6
  313. package/dist/workshop/buildStore.d.ts +2 -0
  314. package/dist/workshop/chatAttachments.d.ts +27 -0
  315. package/dist/workshop/chatAttachments.js +98 -0
  316. package/dist/workshop/chatTurn.d.ts +5 -2
  317. package/dist/workshop/chatTurn.js +22 -18
  318. package/dist/workshop/sessionMutations.d.ts +17 -0
  319. package/dist/workshop/sessionMutations.js +107 -0
  320. package/dist/workshop/sessionStore.d.ts +10 -0
  321. package/dist/workshop/sessionStore.js +52 -0
  322. package/dist/workshop/workshopPin.d.ts +4 -3
  323. package/dist/workshop/workshopPin.js +26 -6
  324. package/dist/workshop/workshopTranscriptTurn.d.ts +8 -0
  325. package/dist/workshop/workshopTranscriptTurn.js +46 -0
  326. package/examples/README.md +1 -1
  327. package/examples/archify-on-pr/README.md +1 -1
  328. package/examples/archify-on-pr/archify-on-pr.pipeline.yaml +0 -1
  329. package/examples/archify-on-pr/author-diagrams.yaml +1 -0
  330. package/examples/channels-smoke/README.md +12 -0
  331. package/examples/channels-smoke/channels-smoke.pipeline.yaml +11 -0
  332. package/examples/channels-smoke/channels-smoke.task.yaml +3 -0
  333. package/examples/channels-smoke/send-email.yaml +32 -0
  334. package/examples/channels-smoke/send-text.yaml +31 -0
  335. package/examples/conditional-fork/branch-a.yaml +1 -1
  336. package/examples/conditional-fork/decide.yaml +2 -1
  337. package/examples/feature-loop/{implement.yaml → feature-implement.yaml} +1 -1
  338. package/examples/feature-loop/feature-loop.pipeline.yaml +10 -10
  339. package/examples/feature-loop/{plan.yaml → feature-plan.yaml} +1 -1
  340. package/examples/feature-loop/{review.yaml → feature-review.yaml} +1 -1
  341. package/examples/feedback-loop/{implement.yaml → feedback-implement.yaml} +1 -1
  342. package/examples/feedback-loop/feedback-loop-wait-human.pipeline.yaml +9 -9
  343. package/examples/feedback-loop/feedback-loop.pipeline.yaml +9 -9
  344. package/examples/feedback-loop/{plan.yaml → feedback-plan.yaml} +1 -1
  345. package/examples/feedback-loop/{review.yaml → feedback-review.yaml} +1 -1
  346. package/examples/generic-fan-in/{research.yaml → fan-in-research.yaml} +1 -1
  347. package/examples/generic-fan-in/generic-fan-in-accepted.pipeline.yaml +3 -3
  348. package/examples/generic-fan-in/generic-fan-in.pipeline.yaml +3 -3
  349. package/examples/hello-world/{research.yaml → hello-research.yaml} +1 -1
  350. package/examples/hello-world/hello.pipeline.yaml +2 -2
  351. package/examples/mcp-hitl-tour/{review.yaml → mcp-hitl-review.yaml} +1 -1
  352. package/examples/mcp-hitl-tour/mcp-hitl-tour.pipeline.yaml +3 -3
  353. package/examples/oss-issue-contribution/oss-explain-fix.yaml +1 -0
  354. package/examples/oss-issue-contribution/oss-explain-issue.yaml +1 -0
  355. package/examples/oss-issue-contribution/oss-issue-contribution.pipeline.yaml +0 -2
  356. package/examples/stageflow.yaml +6 -0
  357. package/examples/triggers/sample-github-pr.trigger.yaml +12 -0
  358. package/examples/triggers/sample-inbox-email.trigger.yaml +14 -0
  359. package/examples/triggers/sample-manual-hello.trigger.yaml +5 -0
  360. package/examples/triggers/sample-nightly-hello.trigger.yaml +8 -0
  361. package/examples/triggers/sample-signed-webhook.trigger.yaml +11 -0
  362. package/examples/triggers-readiness/README.md +48 -0
  363. package/examples/triggers-readiness/readiness-emit.yaml +18 -0
  364. package/examples/triggers-readiness/readiness.pipeline.yaml +4 -0
  365. package/examples/triggers-readiness/readiness.task.yaml +6 -0
  366. package/examples/triggers-readiness/stageflow.yaml +13 -0
  367. package/examples/triggers-readiness/triggers/readiness-dynamic-manual.trigger.yaml +4 -0
  368. package/examples/triggers-readiness/triggers/readiness-email.trigger.yaml +12 -0
  369. package/examples/triggers-readiness/triggers/readiness-github-pr.trigger.yaml +12 -0
  370. package/examples/triggers-readiness/triggers/readiness-manual.trigger.yaml +5 -0
  371. package/examples/triggers-readiness/triggers/readiness-schedule.trigger.yaml +8 -0
  372. package/examples/triggers-readiness/triggers/readiness-webhook.trigger.yaml +11 -0
  373. package/package.json +8 -3
  374. package/skills/stageflow/references/control-surface.md +1 -1
  375. package/skills/stageflow-author/references/catalog-write-conventions.md +2 -2
  376. package/skills/stageflow-run/SKILL.md +1 -1
  377. package/skills/stageflow-run/references/mcp-call.md +1 -1
  378. package/skills/stageflow-setup/SKILL.md +1 -1
  379. package/dist/ui/assets/index-59cjF0OA.js +0 -133
@@ -21,6 +21,7 @@ import { hydrateScheduleFromStore, hydratedScheduleHasRunnableWork, resumeRun, r
21
21
  import { resolveFeedbackLoopDecision, } from "./feedbackLoopDecision.js";
22
22
  import { RunRetryCoordinator, readRetryRootWaitTimeoutMs, } from "./runRetryCoordinator.js";
23
23
  import { readMaxActiveStageProcesses, readMaxActiveStagesPerRun, readStageExecutionMode, } from "./stageConcurrency.js";
24
+ import { runOwnerScope } from "../browser/ownerScope.js";
24
25
  import { isHumanLoginStage } from "../browser/humanLogin.js";
25
26
  import { defaultStageBrowserSupport, resolveStageBrowserEnv, } from "../browser/stageBrowserEnv.js";
26
27
  import { teardownRunBrowsers, teardownStageBrowser, } from "../browser/browserTeardown.js";
@@ -40,6 +41,7 @@ import { getContainerLimits } from "./containerLimits.js";
40
41
  import { stageflowCacheRoot } from "./stageCacheEnv.js";
41
42
  import { assertClaudeNotRoot, ClaudeRootError } from "../preflight/claudeRoot.js";
42
43
  import { preflightFailureCode, runPipelinePreflight, } from "../preflight/pipelinePreflight.js";
44
+ import { channelFailureSummary } from "../preflight/channelChecks.js";
43
45
  import { asAgentBackendId } from "../agent/agentBackend.js";
44
46
  import { INVALID_SLOT_COUNT_MESSAGE, parseSlotCount, readMaxConcurrentFromGlobal, writeMaxConcurrentToGlobal, } from "./settingsFile.js";
45
47
  import { StageHitlController, waitKey, } from "./stageHitl.js";
@@ -267,6 +269,9 @@ export class RunManager {
267
269
  queuedDone = new Map();
268
270
  admissionDrainInFlight = false;
269
271
  resumeInFlight = new Set();
272
+ gateClosedListeners = new Set();
273
+ beforeBrowserTeardownListeners = new Set();
274
+ shutdownListeners = new Set();
270
275
  retryInFlight = new Set();
271
276
  retryStartOwner = new Map();
272
277
  retryStartWaiters = new Map();
@@ -357,7 +362,18 @@ export class RunManager {
357
362
  return this.active.size;
358
363
  }
359
364
  stopAcceptingWork() {
365
+ const wasAccepting = this.acceptingWork;
360
366
  this.acceptingWork = false;
367
+ if (wasAccepting) {
368
+ for (const listener of [...this.shutdownListeners]) {
369
+ try {
370
+ listener();
371
+ }
372
+ catch {
373
+ // listeners must not affect shutdown
374
+ }
375
+ }
376
+ }
361
377
  for (const halt of this.schedulingHalts.values()) {
362
378
  halt.halted = true;
363
379
  halt.hostShutdown = true;
@@ -592,13 +608,14 @@ export class RunManager {
592
608
  .some((entry) => entry.runId === runId && entry.stageId === stageId);
593
609
  }
594
610
  async sweepBrowserSessions() {
595
- const support = this.options.browser ?? defaultStageBrowserSupport();
611
+ const support = this.browserSupport();
596
612
  const isRunLive = createRunLiveness(this.options.store);
597
613
  await stageProfileLock(support)
598
614
  .reclaimStale(isRunLive)
599
615
  .catch(() => 0);
600
616
  return sweepOrphanBrowserSessions({
601
617
  isRunLive,
618
+ host: support.host,
602
619
  ...(support.runner !== undefined ? { runner: support.runner } : {}),
603
620
  ...(support.closeWaitMs !== undefined
604
621
  ? { closeWaitMs: support.closeWaitMs }
@@ -833,6 +850,64 @@ export class RunManager {
833
850
  }
834
851
  await this.drainAdmissionQueue();
835
852
  }
853
+ /** Called when a stage's gate stops accepting operator input (answered, abandoned) or a run is cancelled (no stage id). */
854
+ onGateClosed(listener) {
855
+ this.gateClosedListeners.add(listener);
856
+ return () => {
857
+ this.gateClosedListeners.delete(listener);
858
+ };
859
+ }
860
+ /** Awaited (bounded by the browser support) before a stage's or run's browser is torn down; stageId absent means the whole run. */
861
+ beforeBrowserTeardown(listener) {
862
+ this.beforeBrowserTeardownListeners.add(listener);
863
+ return () => {
864
+ this.beforeBrowserTeardownListeners.delete(listener);
865
+ };
866
+ }
867
+ /** Called once the Host stops accepting work (drain start). */
868
+ onShutdown(listener) {
869
+ this.shutdownListeners.add(listener);
870
+ return () => {
871
+ this.shutdownListeners.delete(listener);
872
+ };
873
+ }
874
+ browserSupport() {
875
+ const base = this.options.browser ?? defaultStageBrowserSupport();
876
+ if (this.beforeBrowserTeardownListeners.size === 0)
877
+ return base;
878
+ return {
879
+ ...base,
880
+ beforeTeardown: async (input) => {
881
+ await Promise.all([
882
+ base.beforeTeardown?.(input),
883
+ ...[...this.beforeBrowserTeardownListeners].map(async (listener) => {
884
+ try {
885
+ await listener(input);
886
+ }
887
+ catch {
888
+ // listeners must not block teardown
889
+ }
890
+ }),
891
+ ]);
892
+ },
893
+ };
894
+ }
895
+ browserOption() {
896
+ if (this.options.browser === undefined && this.beforeBrowserTeardownListeners.size === 0) {
897
+ return {};
898
+ }
899
+ return { browser: this.browserSupport() };
900
+ }
901
+ notifyGateClosed(runId, stageId) {
902
+ for (const listener of [...this.gateClosedListeners]) {
903
+ try {
904
+ listener(runId, stageId);
905
+ }
906
+ catch {
907
+ // listeners must not affect run control
908
+ }
909
+ }
910
+ }
836
911
  async abandonStage(runId, stageId) {
837
912
  let detail;
838
913
  try {
@@ -863,6 +938,7 @@ export class RunManager {
863
938
  status: 409,
864
939
  };
865
940
  }
941
+ this.notifyGateClosed(runId, stageId);
866
942
  if (this.stageProcessLauncher !== undefined) {
867
943
  const hasActive = this.stageProcessLauncher
868
944
  .getActiveStageProcesses()
@@ -871,7 +947,7 @@ export class RunManager {
871
947
  await this.stageProcessLauncher.cancelRun(runId);
872
948
  }
873
949
  }
874
- await teardownStageBrowser(this.options.browser ?? defaultStageBrowserSupport(), {
950
+ await teardownStageBrowser(this.browserSupport(), {
875
951
  runId,
876
952
  runDir: this.options.store.getWorkspaceDir(runId),
877
953
  stageId,
@@ -893,7 +969,7 @@ export class RunManager {
893
969
  const hasWaiting = after.stages.some((s) => s.status === "waiting_for_input");
894
970
  if (!hasWaiting &&
895
971
  !(await createRunLiveness(this.options.store)(runId).catch(() => true))) {
896
- await teardownRunBrowsers(this.options.browser ?? defaultStageBrowserSupport(), {
972
+ await teardownRunBrowsers(this.browserSupport(), {
897
973
  runId,
898
974
  runDir: this.options.store.getWorkspaceDir(runId),
899
975
  events: (stageId) => this.options.store.listStageEvents(runId, stageId),
@@ -931,6 +1007,7 @@ export class RunManager {
931
1007
  };
932
1008
  }
933
1009
  await this.options.store.updateRunStatus(runId, "cancelled");
1010
+ this.notifyGateClosed(runId);
934
1011
  await this.options.store.setCancelReason(runId, trimmedReason);
935
1012
  await finaliseStoredRunManifest(this.options.store, runId).catch(() => undefined);
936
1013
  await this.refreshRunDiskBytes(runId).catch(() => undefined);
@@ -950,7 +1027,7 @@ export class RunManager {
950
1027
  if (this.stageProcessLauncher !== undefined) {
951
1028
  await this.stageProcessLauncher.cancelRun(runId);
952
1029
  }
953
- await teardownRunBrowsers(this.options.browser ?? defaultStageBrowserSupport(), {
1030
+ await teardownRunBrowsers(this.browserSupport(), {
954
1031
  runId,
955
1032
  runDir: this.options.store.getWorkspaceDir(runId),
956
1033
  events: (stageId) => this.options.store.listStageEvents(runId, stageId),
@@ -1530,9 +1607,7 @@ export class RunManager {
1530
1607
  agent: this.options.agent,
1531
1608
  cwd: meta.project_root ?? this.projectRoot,
1532
1609
  operatorCatalog: this.options.operatorCatalog,
1533
- ...(this.options.browser !== undefined
1534
- ? { browser: this.options.browser }
1535
- : {}),
1610
+ ...this.browserOption(),
1536
1611
  maxActiveStagesPerRun: this.maxActiveStagesPerRun,
1537
1612
  executionMode: this.executionMode,
1538
1613
  stageProcessLauncher: this.stageProcessLauncher,
@@ -1757,6 +1832,7 @@ export class RunManager {
1757
1832
  }
1758
1833
  return prefix;
1759
1834
  }
1835
+ this.notifyGateClosed(runId, stageId);
1760
1836
  return await orchestrateAnswerResume({
1761
1837
  prefixMode: prefix.mode,
1762
1838
  executionMode: this.executionMode,
@@ -1896,9 +1972,7 @@ export class RunManager {
1896
1972
  checkoutRoot: meta.checkout_root,
1897
1973
  hitl: this.hitl,
1898
1974
  operatorCatalog: this.options.operatorCatalog,
1899
- ...(this.options.browser !== undefined
1900
- ? { browser: this.options.browser }
1901
- : {}),
1975
+ ...this.browserOption(),
1902
1976
  },
1903
1977
  maxActiveStagesPerRun: this.maxActiveStagesPerRun,
1904
1978
  executionMode: this.executionMode,
@@ -1957,7 +2031,8 @@ export class RunManager {
1957
2031
  });
1958
2032
  const resumedStage = loaded.stages.find((s) => s.id === definitionIdForInstance(meta.pipeline_dag, stageId));
1959
2033
  if (resumedStage?.browser?.profile !== undefined) {
1960
- await acquireStageProfile(this.options.browser ?? defaultStageBrowserSupport(), {
2034
+ await acquireStageProfile(this.browserSupport(), {
2035
+ scope: runOwnerScope(this.browserSupport(), { runId }),
1961
2036
  profile: resumedStage.browser.profile,
1962
2037
  owner: { runId, stageId },
1963
2038
  isRunLive: createRunLiveness(store),
@@ -1968,9 +2043,10 @@ export class RunManager {
1968
2043
  }
1969
2044
  });
1970
2045
  }
1971
- const browserEnv = await resolveStageBrowserEnv(this.options.browser ?? defaultStageBrowserSupport(), {
2046
+ const browserEnv = await resolveStageBrowserEnv(this.browserSupport(), {
1972
2047
  runId,
1973
2048
  stageId,
2049
+ scope: runOwnerScope(this.browserSupport(), { runId }),
1974
2050
  runDir: workspaceDir,
1975
2051
  browser: resumedStage?.browser,
1976
2052
  attempt,
@@ -1999,7 +2075,7 @@ export class RunManager {
1999
2075
  if (launchResult.type === "waiting") {
2000
2076
  return { ok: true };
2001
2077
  }
2002
- await teardownStageBrowser(this.options.browser ?? defaultStageBrowserSupport(), {
2078
+ await teardownStageBrowser(this.browserSupport(), {
2003
2079
  runId,
2004
2080
  runDir: workspaceDir,
2005
2081
  stageId,
@@ -2021,9 +2097,7 @@ export class RunManager {
2021
2097
  checkoutRoot: meta.checkout_root,
2022
2098
  hitl: this.hitl,
2023
2099
  operatorCatalog: this.options.operatorCatalog,
2024
- ...(this.options.browser !== undefined
2025
- ? { browser: this.options.browser }
2026
- : {}),
2100
+ ...this.browserOption(),
2027
2101
  },
2028
2102
  retryRoots: new Map([[stageId, nextRepairAttempt]]),
2029
2103
  maxActiveStagesPerRun: this.maxActiveStagesPerRun,
@@ -2041,7 +2115,7 @@ export class RunManager {
2041
2115
  reason: launchResult.reason,
2042
2116
  }, eventOptions);
2043
2117
  await store.updateRunStatus(runId, "failed");
2044
- await teardownRunBrowsers(this.options.browser ?? defaultStageBrowserSupport(), {
2118
+ await teardownRunBrowsers(this.browserSupport(), {
2045
2119
  runId,
2046
2120
  runDir: workspaceDir,
2047
2121
  events: (stageId) => store.listStageEvents(runId, stageId),
@@ -2067,9 +2141,7 @@ export class RunManager {
2067
2141
  checkoutRoot: meta.checkout_root,
2068
2142
  hitl: this.hitl,
2069
2143
  operatorCatalog: this.options.operatorCatalog,
2070
- ...(this.options.browser !== undefined
2071
- ? { browser: this.options.browser }
2072
- : {}),
2144
+ ...this.browserOption(),
2073
2145
  },
2074
2146
  maxActiveStagesPerRun: this.maxActiveStagesPerRun,
2075
2147
  resumeFromStageId: stageId,
@@ -2093,7 +2165,7 @@ export class RunManager {
2093
2165
  reason,
2094
2166
  }, eventOptions);
2095
2167
  await store.updateRunStatus(runId, "failed");
2096
- await teardownRunBrowsers(this.options.browser ?? defaultStageBrowserSupport(), {
2168
+ await teardownRunBrowsers(this.browserSupport(), {
2097
2169
  runId,
2098
2170
  runDir: store.getWorkspaceDir(runId),
2099
2171
  events: (stageId) => store.listStageEvents(runId, stageId),
@@ -2260,7 +2332,9 @@ export class RunManager {
2260
2332
  "missing_tool";
2261
2333
  return {
2262
2334
  ok: false,
2263
- reason: `Pipeline preflight failed: ${code}`,
2335
+ reason: `Pipeline preflight failed: ${code}${code === "channel_check_failed" && channelFailureSummary(preflight.checks)
2336
+ ? `: ${channelFailureSummary(preflight.checks)}`
2337
+ : ""}`,
2264
2338
  status: 400,
2265
2339
  code,
2266
2340
  };
@@ -2406,9 +2480,7 @@ export class RunManager {
2406
2480
  const started = await startPipeline({
2407
2481
  dispatchKey,
2408
2482
  submission,
2409
- ...(this.options.browser !== undefined
2410
- ? { browser: this.options.browser }
2411
- : {}),
2483
+ ...this.browserOption(),
2412
2484
  agent: this.options.agent,
2413
2485
  store: this.options.store,
2414
2486
  taskYaml,
@@ -2911,9 +2983,7 @@ export class RunManager {
2911
2983
  const schedulingHalt = this.ensureSchedulingHalt(runId);
2912
2984
  const started = await startPipeline({
2913
2985
  submission,
2914
- ...(this.options.browser !== undefined
2915
- ? { browser: this.options.browser }
2916
- : {}),
2986
+ ...this.browserOption(),
2917
2987
  agent: this.options.agent,
2918
2988
  store: this.options.store,
2919
2989
  taskYaml,
@@ -3018,9 +3088,7 @@ export class RunManager {
3018
3088
  checkoutRoot: loadedMeta.checkout_root,
3019
3089
  hitl: this.hitl,
3020
3090
  operatorCatalog: this.options.operatorCatalog,
3021
- ...(this.options.browser !== undefined
3022
- ? { browser: this.options.browser }
3023
- : {}),
3091
+ ...this.browserOption(),
3024
3092
  },
3025
3093
  maxActiveStagesPerRun: this.maxActiveStagesPerRun,
3026
3094
  executionMode: this.executionMode,
@@ -1,4 +1,4 @@
1
- import type { AgentPort } from "../agent/port.js";
1
+ import type { StagePort } from "../agent/port.js";
2
2
  import { type RunDetail, type RunPipelineDagSnapshot, type RunStatus, type RunStore, type StageSnapshot } from "../runstore/port.js";
3
3
  import type { PipelineRunResult } from "./pipelineRunner.js";
4
4
  import { type SchedulerPreparedPipeline } from "./pipelineScheduler.js";
@@ -56,7 +56,7 @@ export type RetryStageRequest = {
56
56
  runId: string;
57
57
  stageId: string;
58
58
  store: RunStore;
59
- agent: AgentPort;
59
+ agent: StagePort;
60
60
  cwd: string;
61
61
  operatorCatalog?: OperatorCatalog;
62
62
  browser?: StageBrowserSupport;
@@ -1,9 +1,5 @@
1
- import path from "node:path";
2
1
  import { Cron } from "croner";
3
- import { getCatalogScanPaths } from "../config/browseCatalog.js";
4
- import { loadTriggerOutcome } from "../config/loadTrigger.js";
5
- import { catalogContextFromStageflow } from "../config/resolveCatalogContext.js";
6
- import { resolveStageflowContext } from "../project/resolveStageflowContext.js";
2
+ import { listCatalogTriggerMatches, readTriggerStoreRecord, triggerStoreId, } from "../config/triggerCatalog.js";
7
3
  export const DEFAULT_TRIGGER_TICK_INTERVAL_MS = 30_000;
8
4
  /** Parse `STAGEFLOW_TRIGGER_TICK_INTERVAL_MS`; default 30s; `0` disables. */
9
5
  export function triggerTickIntervalMsFromEnv(env = process.env) {
@@ -72,17 +68,14 @@ export class ScheduleSource {
72
68
  * against an injected clock instead of waiting on real interval timers.
73
69
  */
74
70
  async tick(onFire) {
75
- const ctx = catalogContextFromStageflow(await resolveStageflowContext(this.cwd));
76
- const scanPaths = await getCatalogScanPaths(ctx);
77
- if (!scanPaths)
78
- return;
79
71
  const now = this.now();
80
- const projectRoot = ctx.projectRoot ?? undefined;
81
- for (const triggerPath of scanPaths.triggerPaths) {
82
- const outcome = await loadTriggerOutcome(triggerPath);
83
- if (!outcome.ok)
84
- continue;
85
- const definition = outcome.value;
72
+ const catalogTriggers = await listCatalogTriggerMatches({
73
+ store: this.store,
74
+ bootCwd: this.cwd,
75
+ sourceRootsOnly: true,
76
+ });
77
+ for (const match of catalogTriggers) {
78
+ const definition = match.definition;
86
79
  if (definition.kind !== "schedule" || !definition.enabled || !definition.schedule) {
87
80
  continue;
88
81
  }
@@ -94,25 +87,23 @@ export class ScheduleSource {
94
87
  this.logError(`invalid schedule for trigger "${definition.id}": ${err instanceof Error ? err.message : String(err)}`);
95
88
  continue;
96
89
  }
97
- const definitionRef = projectRoot !== undefined
98
- ? path.relative(projectRoot, triggerPath).replace(/\\/g, "/")
99
- : triggerPath;
90
+ const storeId = triggerStoreId(match.project_root, definition.id);
100
91
  await this.store.upsertTrigger({
101
- id: definition.id,
102
- definitionRef,
92
+ id: storeId,
93
+ definitionRef: match.definition_ref,
103
94
  enabled: true,
104
95
  });
105
- const record = await this.store.getTrigger(definition.id);
96
+ const record = await readTriggerStoreRecord(this.store, match.project_root, definition.id);
106
97
  if (!record?.next_run_at) {
107
98
  if (next)
108
- await this.store.setTriggerNextRun(definition.id, next.toISOString());
99
+ await this.store.setTriggerNextRun(storeId, next.toISOString());
109
100
  continue;
110
101
  }
111
102
  const dueAt = new Date(record.next_run_at);
112
103
  if (dueAt.getTime() <= now.getTime()) {
113
- await onFire({ triggerId: definition.id });
104
+ await onFire({ triggerId: definition.id, project_root: match.project_root });
114
105
  if (next)
115
- await this.store.setTriggerNextRun(definition.id, next.toISOString());
106
+ await this.store.setTriggerNextRun(storeId, next.toISOString());
116
107
  }
117
108
  }
118
109
  }
@@ -1,4 +1,4 @@
1
- import type { AgentPort, FeedbackLoopContext, StageHandle, StageRunInput, StageSessionMode } from "../agent/port.js";
1
+ import type { StagePort, FeedbackLoopContext, StageHandle, StageRunInput, StageSessionMode } from "../agent/port.js";
2
2
  import { type StageStreamLogWriter } from "./stageStreamLog.js";
3
3
  import type { RunStore } from "../runstore/port.js";
4
4
  import type { StageEnvelope } from "../types/envelope.js";
@@ -12,7 +12,7 @@ export type OperatorCatalog = {
12
12
  agentDir?: string;
13
13
  };
14
14
  export type StageAttemptOpenInput = {
15
- agent: Pick<AgentPort, "openStage">;
15
+ agent: Pick<StagePort, "openStage">;
16
16
  store: RunStore;
17
17
  runId: string;
18
18
  stage: LoadedStageConfig;
@@ -1,7 +1,10 @@
1
1
  import { emailHostFor, stageEmail, workerStageEmail, validateStageEmailAccounts } from "../email/host.js";
2
+ import { ChannelError } from "../channels/errors.js";
3
+ import { channelHostFor, stageChannels, validateStageChannelConnections, workerStageChannels } from "../channels/host.js";
2
4
  import { EmailError } from "../email/port.js";
3
5
  import { bindingKindForOrigin, catalogOrSeededOrigin, decideWorkspaceConfigTrust, } from "../config/configOrigin.js";
4
6
  import { loginCheckPromptBlock, readStageLoginCheck, } from "../browser/loginCheck.js";
7
+ import { stageGateHandoff } from "../browser/stageHandoff.js";
5
8
  import { humanLoginPromptBlock, isHumanLoginStage } from "../browser/humanLogin.js";
6
9
  import { BROWSER_SKILL_NAME, resolveBuiltinSkillFile, } from "../config/builtinSkills.js";
7
10
  import { resolveSkillByName, } from "../config/listSkills.js";
@@ -239,12 +242,20 @@ async function openStageWithOperatorCatalog(agent, input, catalog, factoryCwd, a
239
242
  return { ok: false, reason: "browser login check result is missing" };
240
243
  }
241
244
  }
245
+ const humanLoginHandoff = humanLogin
246
+ ? await stageGateHandoff({
247
+ runDir: resolveOptions.workspaceDir,
248
+ runId: resolveOptions.runId,
249
+ stageId: input.stage.id,
250
+ dirStageId: stageIdForManifest,
251
+ })
252
+ : undefined;
242
253
  try {
243
254
  const handle = agent.openStage({
244
255
  ...input,
245
256
  stage: {
246
257
  ...input.stage,
247
- system_prompt: `${stampStagePromptArtifactsDir(input.stage.system_prompt, artifactsDir)}${loginCheck !== undefined ? `\n\n${loginCheckPromptBlock(loginCheck)}` : ""}${humanLogin ? `\n\n${humanLoginPromptBlock(input.stage.browser?.login_url ?? input.stage.browser?.check?.url)}` : ""}`,
258
+ system_prompt: `${stampStagePromptArtifactsDir(input.stage.system_prompt, artifactsDir)}${loginCheck !== undefined ? `\n\n${loginCheckPromptBlock(loginCheck)}` : ""}${humanLogin ? `\n\n${humanLoginPromptBlock(input.stage.browser?.login_url ?? input.stage.browser?.check?.url, humanLoginHandoff?.kind === "live_view" ? "live_view" : "local_window")}` : ""}`,
248
259
  ...(loginCheck !== undefined
249
260
  ? {
250
261
  pre_emit_checks: [
@@ -441,6 +452,16 @@ export async function openStageAttempt(input) {
441
452
  throw error;
442
453
  }
443
454
  }
455
+ if (process.env.SF_STAGE_WORKER !== "1" && input.stage.channels?.length) {
456
+ try {
457
+ validateStageChannelConnections(channelHostFor(input.factoryCwd ?? process.cwd()), input.stage);
458
+ }
459
+ catch (error) {
460
+ if (error instanceof ChannelError)
461
+ return { ok: false, reason: error.code };
462
+ throw error;
463
+ }
464
+ }
444
465
  const opened = await openStageWithOperatorCatalog(input.agent, {
445
466
  roots,
446
467
  stage: input.stage,
@@ -456,6 +477,10 @@ export async function openStageAttempt(input) {
456
477
  resumeToken,
457
478
  email: input.stage.email?.length ? (process.env.SF_STAGE_WORKER === "1" ? workerStageEmail()
458
479
  : stageEmail(emailHostFor(input.factoryCwd ?? process.cwd()).mailbox, input.stage, input.runId, { workspaceDir: input.workspaceDir, attempt })) : undefined,
480
+ ...(input.stage.channels?.length ? {
481
+ channels: process.env.SF_STAGE_WORKER === "1" ? workerStageChannels()
482
+ : stageChannels(channelHostFor(input.factoryCwd ?? process.cwd()), input.stage, input.runId),
483
+ } : {}),
459
484
  ...(input.sessionMode !== undefined ? { sessionMode: input.sessionMode } : {}),
460
485
  onActivity: (event) => {
461
486
  input.onActivity?.(input.stage.browser !== undefined
@@ -4,7 +4,7 @@ import { logger as rootLogger, resolveLogMaxLineBytes, } from "../logging/logger
4
4
  import { isCursorModelRef, readCursorApiKey } from "../agent/cursorProvider.js";
5
5
  import { BROWSER_ENV_PREFIX } from "../browser/browserHost.js";
6
6
  import { PACKAGE_VERSION } from "../package-meta.js";
7
- import { PI_HOME_AUTH_PATH_ENV, stageflowAgentAuthPath } from "./credentialBinding.js";
7
+ import { PI_HOME_AUTH_PATH_ENV, sfOwnedAuthPath } from "./credentialBinding.js";
8
8
  import { redactString } from "../logging/redact.js";
9
9
  import { getNamedSecrets } from "../logging/namedSecrets.js";
10
10
  import { readMaxActiveStageProcesses, } from "./stageConcurrency.js";
@@ -13,6 +13,8 @@ import { buildStageEnvironment, isAmbientBlockedEnv, isForeverDeniedSecret, } fr
13
13
  import { ensureStageCacheDirs } from "./stageCacheEnv.js";
14
14
  import { SF_STAGE_WORKER, STAGE_WORKER_EXIT, } from "./stageWorkerProtocol.js";
15
15
  import { emailHostFor, emailWorkerEnvironment, stageEmail, validateStageEmailAccounts } from "../email/host.js";
16
+ import { ChannelError } from "../channels/errors.js";
17
+ import { channelFault, channelHostFor, channelsWorkerEnvironment, stageChannels, validateStageChannelConnections } from "../channels/host.js";
16
18
  import { EmailError } from "../email/port.js";
17
19
  import { overlayStageBindingEnv, } from "./stageRoots.js";
18
20
  function defaultStageCliEntry() {
@@ -189,6 +191,15 @@ export class StageProcessLauncher {
189
191
  return { type: "failed", reason: error.code };
190
192
  throw error;
191
193
  }
194
+ try {
195
+ if (input.stage?.channels?.length)
196
+ validateStageChannelConnections(channelHostFor(input.factoryCwd ?? input.operatorCatalog?.cwd ?? input.rootDir), input.stage);
197
+ }
198
+ catch (error) {
199
+ if (error instanceof ChannelError)
200
+ return { type: "failed", reason: error.code };
201
+ throw error;
202
+ }
192
203
  input = { ...input, stage: input.stage ? structuredClone(input.stage) : undefined };
193
204
  const acquired = await this.waitForCapacity(input.runId);
194
205
  if (!acquired) {
@@ -352,7 +363,7 @@ export class StageProcessLauncher {
352
363
  : overlaid;
353
364
  const childEnv = {
354
365
  ...withBrowser,
355
- [PI_HOME_AUTH_PATH_ENV]: stageflowAgentAuthPath(),
366
+ [PI_HOME_AUTH_PATH_ENV]: sfOwnedAuthPath(),
356
367
  };
357
368
  const cursorApiKey = readCursorApiKey(hostEnv);
358
369
  if (isCursorModelRef(input.model ?? "") &&
@@ -363,7 +374,7 @@ export class StageProcessLauncher {
363
374
  }
364
375
  const child = this.forkFn(this.cliEntry, args, {
365
376
  cwd: input.rootDir,
366
- env: emailWorkerEnvironment({ ...childEnv, [SF_STAGE_WORKER]: "1" }, emailHostFor(factoryCwd).accounts),
377
+ env: channelsWorkerEnvironment(emailWorkerEnvironment({ ...childEnv, [SF_STAGE_WORKER]: "1" }, emailHostFor(factoryCwd).accounts), factoryCwd),
367
378
  stdio: ["pipe", "pipe", "pipe", "ipc"],
368
379
  detached: true,
369
380
  // Explicit: do not inherit Host execArgv; set heap from cgroup budget.
@@ -432,6 +443,25 @@ export class StageProcessLauncher {
432
443
  })();
433
444
  return;
434
445
  }
446
+ if (!settled && request?.type === "channel.send") {
447
+ void (async () => {
448
+ if (typeof request.requestId !== "string" || request.requestId.length > 100)
449
+ return;
450
+ let response;
451
+ try {
452
+ const stage = input.stage;
453
+ if (!stage || settled)
454
+ throw new ChannelError("CHANNEL_UNAUTHORIZED");
455
+ response = { receipt: await stageChannels(channelHostFor(factoryCwd), stage, input.runId).send(request.input) };
456
+ }
457
+ catch (error) {
458
+ response = { error: channelFault(error) };
459
+ }
460
+ if (child.connected && !settled)
461
+ child.send({ type: "channel.response", requestId: request.requestId, ...response });
462
+ })();
463
+ return;
464
+ }
435
465
  if (!isStageWorkerResult(message))
436
466
  return;
437
467
  finish(resultFromWorkerMessage(message));
@@ -3,7 +3,8 @@ import path from "node:path";
3
3
  import { isCursorModelRef } from "../agent/cursorProvider.js";
4
4
  import { stageDir } from "../runstore/paths.js";
5
5
  import { noAttemptContext } from "./stageAttemptContext.js";
6
- import { resolveCredentialBinding, currentExplicitAuthPath } from "./credentialBinding.js";
6
+ import { resolveCredentialBinding, STAGEFLOW_AGENT_AUTH_PATH_ENV, currentExplicitAuthPath, } from "./credentialBinding.js";
7
+ import { SF_STAGE_WORKER } from "./stageWorkerProtocol.js";
7
8
  export const STAGEFLOW_CHECKOUT = "STAGEFLOW_CHECKOUT";
8
9
  export const STAGEFLOW_RUN_WORKSPACE = "STAGEFLOW_RUN_WORKSPACE";
9
10
  export const STAGEFLOW_REPOSITORY = "STAGEFLOW_REPOSITORY";
@@ -131,6 +132,12 @@ export function withResolvedAuthPath(roots, factoryCwd) {
131
132
  if (explicit !== undefined) {
132
133
  return { ...roots, authPath: explicit };
133
134
  }
135
+ if (process.env[SF_STAGE_WORKER] === "1") {
136
+ const stamped = process.env[STAGEFLOW_AGENT_AUTH_PATH_ENV]?.trim();
137
+ if (!stamped)
138
+ return roots;
139
+ return { ...roots, authPath: path.resolve(stamped) };
140
+ }
134
141
  const binding = resolveCredentialBinding(factoryCwd);
135
142
  return { ...roots, authPath: binding.authPath };
136
143
  }
@@ -1,4 +1,4 @@
1
- import type { AgentPort, FeedbackLoopContext, StageHandle, StageRunResult, StageSessionMode } from "../agent/port.js";
1
+ import type { StagePort, FeedbackLoopContext, StageHandle, StageRunResult, StageSessionMode } from "../agent/port.js";
2
2
  import { type HostGateContext } from "../browser/gateHandoff.js";
3
3
  import type { StageBrowserSupport } from "../browser/browserHost.js";
4
4
  import type { StageEnvelope } from "../types/envelope.js";
@@ -20,7 +20,7 @@ export declare function isRunStageWaiting(outcome: RunStageOutcome): outcome is
20
20
  waiting: true;
21
21
  };
22
22
  export type RunStageOptions = {
23
- agent: AgentPort;
23
+ agent: StagePort;
24
24
  store: RunStore;
25
25
  runId: string;
26
26
  stage: LoadedStageConfig;
@@ -1,4 +1,5 @@
1
- import { hostGateContextFor, stampGateRequest, } from "../browser/gateHandoff.js";
1
+ import { stampGateRequest } from "../browser/gateHandoff.js";
2
+ import { stageGateContext } from "../browser/stageHandoff.js";
2
3
  import { deriveExecutionPatchFromEvent } from "../runstore/stageExecution.js";
3
4
  import { buildStageRoots, withResolvedAuthPath, } from "./stageRoots.js";
4
5
  import { createDefaultHitlQaHooks, recordWaitEnter, } from "./stageHitl.js";
@@ -231,7 +232,12 @@ export async function runStage(options) {
231
232
  store: workerMode ? store : undefined,
232
233
  attemptCtx,
233
234
  skipGates,
234
- gateContext: hostGateContextFor(stage.browser),
235
+ gateContext: await stageGateContext({
236
+ runDir: workspaceDir ?? store.getWorkspaceDir(runId),
237
+ runId,
238
+ stageId,
239
+ browser: stage.browser,
240
+ }),
235
241
  });
236
242
  }
237
243
  catch (err) {
@@ -1,4 +1,4 @@
1
- import { resolveAgentPort } from "../agent/resolveAgentPort.js";
1
+ import { resolveStagePort } from "../agent/resolveStagePort.js";
2
2
  import { asAgentBackendId } from "../agent/agentBackend.js";
3
3
  import { loadStageflowManifestOutcome } from "../config/loadStageflowManifest.js";
4
4
  import { loadNamedSecretsFromAttemptDir } from "../logging/namedSecrets.js";
@@ -29,7 +29,7 @@ export async function runStageWorker(input) {
29
29
  }
30
30
  const dag = meta.pipeline_dag ?? loaded.dag;
31
31
  const manifestOutcome = await loadStageflowManifestOutcome(input.rootDir);
32
- const agent = resolveAgentPort({
32
+ const agent = resolveStagePort({
33
33
  global: manifestOutcome.ok
34
34
  ? asAgentBackendId(manifestOutcome.value.manifest.agent)
35
35
  : undefined,
@@ -1,12 +1,14 @@
1
1
  import type { TaskFile } from "../types/task.js";
2
2
  export type TriggerFireEvent = {
3
3
  triggerId: string;
4
+ project_root?: string;
4
5
  task?: TaskFile;
5
6
  };
6
7
  /**
7
- * Abstract seam for anything that decides "fire trigger X now" — a croner-backed
8
- * schedule loop, a GitHub/email poller, a webhook receiver. No adapter implements
9
- * this yet; it exists so those later adapters build against a locked contract.
8
+ * Long-running sources that decide "fire trigger X now". ScheduleSource,
9
+ * GithubPollSource, and EmailSource implement this. Webhook deliveries are
10
+ * request-driven: acceptWebhook shares TriggerFireEvent's optional task, and
11
+ * the HTTP route fires; a webhook is not a start/stop loop.
10
12
  */
11
13
  export interface TriggerSourcePort {
12
14
  start(onFire: (event: TriggerFireEvent) => Promise<void>): Promise<void>;
@@ -13,5 +13,6 @@ import type { TaskFile } from "../types/task.js";
13
13
  */
14
14
  export declare function fireTrigger(triggerId: string, store: RunStore, runManager: RunManager, options?: {
15
15
  cwd?: string;
16
+ project_root?: string;
16
17
  task?: TaskFile;
17
18
  }): Promise<StartRunResult>;