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
@@ -7,20 +7,32 @@ import { mapProviderAuthError } from "../agent/providerInspect.js";
7
7
  import { handleProviderRoutes } from "./providerRoutes.js";
8
8
  import { handleProjectMcpRoutes } from "./projectMcpRoutes.js";
9
9
  import { handleTriggerRoutes } from "./triggerRoutes.js";
10
+ import { readCatalogFileForHttp, writeCatalogFileForHttp, } from "./readCatalogFile.js";
11
+ import { applyLiveViewHeaders, createLiveViewRoutes, isLiveViewDialogPath, isLiveViewInputPath, isLiveViewPath, isLiveViewReopenTabPath, } from "./liveViewRoutes.js";
10
12
  import { createPipeline, parseCreatePipelineBody } from "../config/createPipeline.js";
11
13
  import { createStage, parseCreateStageBody } from "../config/createStage.js";
12
- import { loadDraftPackage, loadTaskArtifact, parseAttachTaskBody, parseCreateDraftPackageBody, parseDraftPackageBody, parseOpenDraftPackageBody, parseOverwriteDraftPackageBody, validateDraftPackage, } from "../config/draftPackage.js";
14
+ import { createTask, parseCreateTaskBody } from "../config/createTask.js";
15
+ import { importTaskFromIssue, parseImportTaskBody } from "../config/importTask.js";
16
+ import { parseUpdateTaskBody, updateTask } from "../config/updateTask.js";
17
+ import { loadDraftPackage, loadTaskArtifact, parseAttachTaskBody, parseCreateDraftPackageBody, parseDraftPackageBody, parseOpenDraftPackageBody, parseOverwriteDraftPackageBody, parsePlanDraftPackageBody, planDraftPackage, validateDraftPackage, } from "../config/draftPackage.js";
13
18
  import { publishDraftPackage } from "../config/publishDraftPackage.js";
14
19
  import { clearWorkshopAutosave, detectDiskChange, draftPackageDiskRelativePaths, fingerprintPackageFiles, parseWorkshopAutosaveRecord, readWorkshopAutosave, resolveWorkshopAutosaveStoreRoot, workshopAutosaveSlotKey, writeWorkshopAutosave, } from "../workshop/autosave.js";
15
20
  import { acceptWorkshopSessionMutation, iterateWorkshopChatStreamFrames, runWorkshopChatTurn, stopWorkshopChatTurn, undoWorkshopSessionMutation, WorkshopChatSessionRegistry, WorkshopSessionStoreError, } from "../workshop/chatTurn.js";
21
+ import { parseWorkshopChatAttachments, parseWorkshopChatContext, } from "../workshop/chatAttachments.js";
16
22
  import { readWorkshopOpenModel, resolveWorkshopModel, } from "../workshop/modelSettings.js";
17
23
  import { createWorkshopSession, getWorkshopSession, listWorkshopSessions, resolveWorkshopSessionStoreRoot, updateWorkshopSessionActiveBuildId, } from "../workshop/sessionStore.js";
18
24
  import { createWorkshopBuild, focusUnboundWorkshopBuild, getWorkshopBuild, listWorkshopBuilds, listWorkshopPickerRows, resolvePickerCatalogPipelines, resolveWorkshopBuildStoreRoot, updateWorkshopBuild, WorkshopBuildStoreError, } from "../workshop/buildStore.js";
19
- import { createLiveWorkshopOperatorHost, } from "../operatorAgent/index.js";
25
+ import { resolveAgentPort } from "../operatorAgent/index.js";
26
+ import { listPipelinesForContext } from "../config/browseCatalog.js";
27
+ import { catalogContextFromStageflow } from "../config/resolveCatalogContext.js";
20
28
  import { listModelsMultiProject, listPipelinesMultiProject, listTasksMultiProject, } from "../config/multiProjectCatalog.js";
21
- import { catalogPathErrorBody, CatalogPathError, resolveCatalogRelativePath, resolveCatalogStartInput, resolveWritableCatalogRoot, } from "../config/catalogRelativePath.js";
29
+ import { catalogPathErrorBody, CatalogPathError, resolveCatalogRelativePath, resolveCatalogStartInput, resolveDefaultWriteRoot, resolveWritableCatalogRoot, } from "../config/catalogRelativePath.js";
22
30
  import { listExtensions } from "../config/listExtensions.js";
23
31
  import { listSkills } from "../config/listSkills.js";
32
+ import { findTaskInCatalogMulti } from "../config/findTaskInCatalog.js";
33
+ import { buildSkillUsageIndex } from "../config/skillUsageIndex.js";
34
+ import { installSkillFromCommand, parseInstallSkillBody, } from "../config/installSkillRequest.js";
35
+ import { validateCatalog } from "../config/validateCatalog.js";
24
36
  import { artifactMediaType, classifyArtifactContent, readRunArtifact, readRunArtifactBytes, } from "../mcp/readArtifact.js";
25
37
  import { getRunDiff, listCheckoutChanges, readCheckoutFileBytes, } from "../mcp/checkoutTools.js";
26
38
  import { readStageVerificationHistory } from "../runstore/verificationHistory.js";
@@ -39,17 +51,25 @@ import { isTaskFile } from "../runtime/taskInput.js";
39
51
  import { findTokenShapedField, tokenRejectedPayload, } from "../runtime/startPayload.js";
40
52
  import { parseAskOperatorAnswer } from "../tools/askOperator.js";
41
53
  import { handleEmailRoutes } from "./emailRoutes.js";
54
+ import { handleChannelRoutes } from "./channelRoutes.js";
42
55
  import { bootstrapStageflowHost, } from "./bootstrap.js";
43
56
  import { assertAllowedHttpAccess, isTrustedLocalHttpRequest, resolveAllowedHosts, } from "./allowedHosts.js";
44
57
  import { enforceBearerAuth, loadControlTokens, requiredScopeFor, } from "./controlToken.js";
45
58
  import { createHttpHost, DEFAULT_PORT, json, } from "./createHttpHost.js";
46
- import { handleApiHealth } from "./healthSurfaces.js";
59
+ import { handleApiHealth, listCatalogRootsForApi } from "./healthSurfaces.js";
47
60
  import { mapRetryStageFailure, mapStartFailure, mapStoreLookupError, } from "./operatorResults.js";
48
61
  import { installShutdownController, makeDrainableHostFromOptional, } from "./shutdown.js";
49
62
  import { writeAudit } from "../logging/audit.js";
50
63
  import { logger as rootLogger } from "../logging/logger.js";
51
64
  import { callerIdFromRequestAuth, getRequestAuth, requestAuthFromBearer, runWithRequestAuth, } from "./requestAuthContext.js";
52
65
  const auditLog = rootLogger.child({ component: "audit" });
66
+ function draftCatalogRootWire(root) {
67
+ return {
68
+ project_root: root.project_root,
69
+ root_kind: root.kind,
70
+ read_only: root.read_only,
71
+ };
72
+ }
53
73
  function textPlain(res, status, body) {
54
74
  res.writeHead(status, {
55
75
  "Content-Type": "text/plain; charset=utf-8",
@@ -137,7 +157,10 @@ async function serveStatic(res, uiDistDir, urlPath) {
137
157
  function isCredentialMutatingApi(method, pathname) {
138
158
  if (method !== "POST")
139
159
  return false;
140
- return (/^\/api\/providers\/[^/]+\/login$/.test(pathname) ||
160
+ return (isLiveViewInputPath(method, pathname) ||
161
+ isLiveViewDialogPath(method, pathname) ||
162
+ isLiveViewReopenTabPath(method, pathname) ||
163
+ /^\/api\/providers\/[^/]+\/login$/.test(pathname) ||
141
164
  /^\/api\/providers\/[^/]+\/login\/[^/]+\/answer$/.test(pathname) ||
142
165
  /^\/api\/providers\/[^/]+\/login\/[^/]+\/cancel$/.test(pathname) ||
143
166
  /^\/api\/providers\/[^/]+\/logout$/.test(pathname));
@@ -149,18 +172,11 @@ export function defaultUiDistDir() {
149
172
  }
150
173
  return path.resolve(here, "../ui");
151
174
  }
152
- /**
153
- * The full operator-console route surface: REST API (runs, stages,
154
- * catalog, settings, providers) plus, when `uiDistDir` is given, static
155
- * console UI file serving. Shared by `startUiServer` (browser-facing) and
156
- * `startMcpServer` (headless global-service daemon) — both need the REST
157
- * API; only the former needs the UI files.
158
- */
159
175
  export function createOperatorRoutes(deps) {
160
176
  const { manager, store, cwd, agentDir, rootDir, providerAuthContext, uiDistDir, workshopOperatorHost, } = deps;
161
177
  const workshopChatRegistry = deps.workshopChatRegistry ??
162
178
  new WorkshopChatSessionRegistry(workshopOperatorHost ??
163
- createLiveWorkshopOperatorHost({
179
+ resolveAgentPort({
164
180
  cwd,
165
181
  projectRoot: cwd,
166
182
  }));
@@ -168,7 +184,11 @@ export function createOperatorRoutes(deps) {
168
184
  const workshopBuildStoreRoot = () => resolveWorkshopBuildStoreRoot();
169
185
  const allowedHosts = deps.allowedHosts ?? resolveAllowedHosts();
170
186
  const controlTokens = deps.controlTokens ?? loadControlTokens();
171
- return async ({ req, res, url, pathname, method, boot }) => {
187
+ const liveView = createLiveViewRoutes({ ...deps.liveView, store, controlTokens });
188
+ liveView.attach(manager);
189
+ const handler = async ({ req, res, url, pathname, method, boot }) => {
190
+ if (isLiveViewPath(pathname))
191
+ applyLiveViewHeaders(res);
172
192
  if (boot.serveBlocked !== undefined && pathname.startsWith("/api/")) {
173
193
  json(res, 503, {
174
194
  error: boot.serveBlocked.reason,
@@ -193,6 +213,18 @@ export function createOperatorRoutes(deps) {
193
213
  return handleOperatorRequest();
194
214
  async function handleOperatorRequest() {
195
215
  try {
216
+ if (await liveView.handle(req, res))
217
+ return true;
218
+ if (pathname === "/api/connections" || pathname.startsWith("/api/connections/")) {
219
+ if (!assertAllowedHttpAccess({ entries: [] }, req, res, { requireOrigin: method !== "GET" }))
220
+ return true;
221
+ if (!boot.channels) {
222
+ json(res, 503, { code: "CHANNEL_CONNECTION_FAILED" });
223
+ return true;
224
+ }
225
+ if (await handleChannelRoutes(req, method, pathname, boot.channels, res, json))
226
+ return true;
227
+ }
196
228
  if (pathname.startsWith("/api/email/")) {
197
229
  if (!assertAllowedHttpAccess({ entries: [] }, req, res, { requireOrigin: ["POST", "PATCH", "DELETE"].includes(method) }))
198
230
  return true;
@@ -1044,6 +1076,356 @@ export function createOperatorRoutes(deps) {
1044
1076
  });
1045
1077
  return true;
1046
1078
  }
1079
+ if (method === "GET" && pathname === "/api/catalog/roots") {
1080
+ const roots = await listCatalogRootsForApi({ store, bootCwd: cwd });
1081
+ json(res, 200, { roots });
1082
+ return true;
1083
+ }
1084
+ if (method === "POST" && pathname === "/api/catalog/validate") {
1085
+ let body;
1086
+ try {
1087
+ body = await readJsonBody(req);
1088
+ }
1089
+ catch {
1090
+ json(res, 400, { error: "Invalid JSON body" });
1091
+ return true;
1092
+ }
1093
+ if (body !== null && typeof body === "object" && !Array.isArray(body)) {
1094
+ const record = body;
1095
+ let pipeline = typeof record.pipeline === "string" ? record.pipeline : undefined;
1096
+ let task = typeof record.task === "string" ? record.task : undefined;
1097
+ let stage = typeof record.stage === "string" ? record.stage : undefined;
1098
+ const strict = record.strict === true;
1099
+ const projectRoot = typeof record.project_root === "string" && record.project_root.trim()
1100
+ ? record.project_root.trim()
1101
+ : undefined;
1102
+ const scopedCount = [pipeline, task, stage].filter((v) => v !== undefined).length;
1103
+ if (scopedCount > 1) {
1104
+ json(res, 400, {
1105
+ error: "Use at most one of pipeline, task, or stage",
1106
+ });
1107
+ return true;
1108
+ }
1109
+ let validateCwd = cwd;
1110
+ if (pipeline !== undefined || task !== undefined || stage !== undefined) {
1111
+ try {
1112
+ const { wireRoot, roots } = await resolveCatalogStartInput({ store, bootCwd: cwd }, projectRoot);
1113
+ validateCwd = wireRoot.path;
1114
+ if (pipeline !== undefined) {
1115
+ pipeline = resolveCatalogRelativePath({
1116
+ inputPath: pipeline.trim(),
1117
+ projectRoot: wireRoot.project_root,
1118
+ roots,
1119
+ fieldName: "pipeline",
1120
+ }).absolutePath;
1121
+ }
1122
+ if (task !== undefined) {
1123
+ task = resolveCatalogRelativePath({
1124
+ inputPath: task.trim(),
1125
+ projectRoot: wireRoot.project_root,
1126
+ roots,
1127
+ fieldName: "task",
1128
+ }).absolutePath;
1129
+ }
1130
+ if (stage !== undefined) {
1131
+ stage = resolveCatalogRelativePath({
1132
+ inputPath: stage.trim(),
1133
+ projectRoot: wireRoot.project_root,
1134
+ roots,
1135
+ fieldName: "stage",
1136
+ }).absolutePath;
1137
+ }
1138
+ }
1139
+ catch (err) {
1140
+ if (err instanceof CatalogPathError) {
1141
+ json(res, 400, catalogPathErrorBody(err));
1142
+ return true;
1143
+ }
1144
+ throw err;
1145
+ }
1146
+ }
1147
+ const scope = pipeline
1148
+ ? "pipeline"
1149
+ : task
1150
+ ? "task"
1151
+ : stage
1152
+ ? "stage"
1153
+ : "full";
1154
+ const stageContent = typeof record.content === "string" ? record.content : undefined;
1155
+ if (stageContent !== undefined && stage === undefined) {
1156
+ json(res, 400, {
1157
+ error: "content is only supported with stage",
1158
+ });
1159
+ return true;
1160
+ }
1161
+ const result = await validateCatalog({
1162
+ scope,
1163
+ cwd: validateCwd,
1164
+ ...(pipeline !== undefined ||
1165
+ task !== undefined ||
1166
+ stage !== undefined
1167
+ ? { projectRoot: validateCwd }
1168
+ : {}),
1169
+ pipeline,
1170
+ task,
1171
+ stage,
1172
+ ...(stageContent !== undefined ? { stageContent } : {}),
1173
+ strict,
1174
+ });
1175
+ json(res, 200, result);
1176
+ return true;
1177
+ }
1178
+ json(res, 400, { error: "Request body must be an object" });
1179
+ return true;
1180
+ }
1181
+ if (method === "GET" && pathname === "/api/catalog/file") {
1182
+ const relPath = url.searchParams.get("path");
1183
+ if (relPath === null || relPath.length === 0) {
1184
+ json(res, 400, { error: "path query parameter is required" });
1185
+ return true;
1186
+ }
1187
+ const projectRoot = url.searchParams.get("project_root") ?? undefined;
1188
+ try {
1189
+ const { roots } = await resolveCatalogStartInput({ store, bootCwd: cwd }, projectRoot);
1190
+ const result = await readCatalogFileForHttp({
1191
+ inputPath: relPath,
1192
+ projectRoot,
1193
+ roots,
1194
+ fieldName: "path",
1195
+ });
1196
+ if (!result.ok) {
1197
+ json(res, result.status, { error: result.error, code: result.code });
1198
+ return true;
1199
+ }
1200
+ json(res, 200, { path: result.path, content: result.content });
1201
+ return true;
1202
+ }
1203
+ catch (err) {
1204
+ if (err instanceof CatalogPathError) {
1205
+ json(res, 400, catalogPathErrorBody(err));
1206
+ return true;
1207
+ }
1208
+ throw err;
1209
+ }
1210
+ }
1211
+ if (method === "PUT" && pathname === "/api/catalog/file") {
1212
+ let body;
1213
+ try {
1214
+ body = await readJsonBody(req);
1215
+ }
1216
+ catch {
1217
+ json(res, 400, { error: "Invalid JSON body" });
1218
+ return true;
1219
+ }
1220
+ if (body === null || typeof body !== "object" || Array.isArray(body)) {
1221
+ json(res, 400, { error: "Request body must be an object" });
1222
+ return true;
1223
+ }
1224
+ const record = body;
1225
+ const relPath = typeof record.path === "string" ? record.path.trim() : "";
1226
+ if (!relPath) {
1227
+ json(res, 400, { error: "path is required" });
1228
+ return true;
1229
+ }
1230
+ if (typeof record.content !== "string") {
1231
+ json(res, 400, { error: "content must be a string" });
1232
+ return true;
1233
+ }
1234
+ const writeRoot = typeof record.project_root === "string"
1235
+ ? record.project_root
1236
+ : undefined;
1237
+ try {
1238
+ const { wireRoot, roots } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
1239
+ const result = await writeCatalogFileForHttp({
1240
+ inputPath: relPath,
1241
+ content: record.content,
1242
+ projectRoot: wireRoot.project_root,
1243
+ roots,
1244
+ fieldName: "path",
1245
+ });
1246
+ if (!result.ok) {
1247
+ json(res, result.status, {
1248
+ error: result.error,
1249
+ code: result.code,
1250
+ ...(result.findings !== undefined
1251
+ ? { findings: result.findings }
1252
+ : {}),
1253
+ });
1254
+ return true;
1255
+ }
1256
+ json(res, 200, { path: result.path, content: result.content });
1257
+ return true;
1258
+ }
1259
+ catch (err) {
1260
+ if (err instanceof CatalogPathError) {
1261
+ json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
1262
+ return true;
1263
+ }
1264
+ throw err;
1265
+ }
1266
+ }
1267
+ if (method === "POST" && pathname === "/api/tasks/import") {
1268
+ let body;
1269
+ try {
1270
+ body = await readJsonBody(req);
1271
+ }
1272
+ catch {
1273
+ json(res, 400, { error: "Invalid JSON body" });
1274
+ return true;
1275
+ }
1276
+ const parsed = parseImportTaskBody(body);
1277
+ if ("ok" in parsed) {
1278
+ json(res, parsed.status, { error: parsed.error });
1279
+ return true;
1280
+ }
1281
+ const writeRoot = body !== null &&
1282
+ typeof body === "object" &&
1283
+ !Array.isArray(body) &&
1284
+ typeof body.project_root === "string"
1285
+ ? body.project_root
1286
+ : undefined;
1287
+ let taskWriteRoot;
1288
+ try {
1289
+ const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
1290
+ taskWriteRoot = selected.path;
1291
+ }
1292
+ catch (err) {
1293
+ if (err instanceof CatalogPathError) {
1294
+ json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
1295
+ return true;
1296
+ }
1297
+ throw err;
1298
+ }
1299
+ const stageflowCtx = await resolveStageflowContext(taskWriteRoot);
1300
+ if (stageflowCtx.manifestStatus !== "ok") {
1301
+ json(res, 400, {
1302
+ error: "Project root not found; initialize stageflow.yaml",
1303
+ });
1304
+ return true;
1305
+ }
1306
+ const result = await importTaskFromIssue(stageflowCtx.projectRoot, parsed);
1307
+ if (!result.ok) {
1308
+ json(res, result.status, { error: result.error });
1309
+ return true;
1310
+ }
1311
+ json(res, 201, { task: result.task });
1312
+ return true;
1313
+ }
1314
+ if (method === "POST" && pathname === "/api/tasks") {
1315
+ let body;
1316
+ try {
1317
+ body = await readJsonBody(req);
1318
+ }
1319
+ catch {
1320
+ json(res, 400, { error: "Invalid JSON body" });
1321
+ return true;
1322
+ }
1323
+ const writeRoot = body !== null &&
1324
+ typeof body === "object" &&
1325
+ !Array.isArray(body) &&
1326
+ typeof body.project_root === "string"
1327
+ ? body.project_root
1328
+ : undefined;
1329
+ let taskWriteRoot;
1330
+ try {
1331
+ const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
1332
+ taskWriteRoot = selected.path;
1333
+ }
1334
+ catch (err) {
1335
+ if (err instanceof CatalogPathError) {
1336
+ json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
1337
+ return true;
1338
+ }
1339
+ throw err;
1340
+ }
1341
+ const parsed = parseCreateTaskBody(body);
1342
+ if ("ok" in parsed) {
1343
+ json(res, parsed.status, { error: parsed.error });
1344
+ return true;
1345
+ }
1346
+ const stageflowCtx = await resolveStageflowContext(taskWriteRoot);
1347
+ if (stageflowCtx.manifestStatus !== "ok") {
1348
+ json(res, 400, {
1349
+ error: "Project root not found; initialize stageflow.yaml",
1350
+ });
1351
+ return true;
1352
+ }
1353
+ const result = await createTask(stageflowCtx.projectRoot, parsed);
1354
+ if (!result.ok) {
1355
+ json(res, result.status, { error: result.error });
1356
+ return true;
1357
+ }
1358
+ json(res, 201, { task: result.task });
1359
+ return true;
1360
+ }
1361
+ const taskDetailMatch = pathname.match(/^\/api\/tasks\/([^/]+)$/);
1362
+ if (method === "GET" && taskDetailMatch) {
1363
+ const taskId = decodeURIComponent(taskDetailMatch[1] ?? "");
1364
+ const projectRootFilter = url.searchParams.get("project_root") ?? undefined;
1365
+ const lookup = await findTaskInCatalogMulti({
1366
+ store,
1367
+ bootCwd: cwd,
1368
+ taskId,
1369
+ projectRootFilter,
1370
+ });
1371
+ if (!lookup.ok) {
1372
+ if (lookup.status === 409) {
1373
+ json(res, 409, {
1374
+ error: lookup.error,
1375
+ code: lookup.code,
1376
+ project_roots: lookup.project_roots,
1377
+ });
1378
+ return true;
1379
+ }
1380
+ if (lookup.status === 400) {
1381
+ json(res, 400, {
1382
+ error: lookup.error,
1383
+ code: lookup.code,
1384
+ });
1385
+ return true;
1386
+ }
1387
+ json(res, 404, { error: lookup.error });
1388
+ return true;
1389
+ }
1390
+ json(res, 200, { task: lookup.task });
1391
+ return true;
1392
+ }
1393
+ if (method === "PUT" && taskDetailMatch) {
1394
+ const taskId = decodeURIComponent(taskDetailMatch[1] ?? "");
1395
+ const projectRootFilter = url.searchParams.get("project_root") ?? undefined;
1396
+ let body;
1397
+ try {
1398
+ body = await readJsonBody(req);
1399
+ }
1400
+ catch {
1401
+ json(res, 400, { error: "Invalid JSON body" });
1402
+ return true;
1403
+ }
1404
+ const parsed = parseUpdateTaskBody(body);
1405
+ if ("ok" in parsed) {
1406
+ json(res, parsed.status, { error: parsed.error });
1407
+ return true;
1408
+ }
1409
+ const result = await updateTask({
1410
+ store,
1411
+ bootCwd: cwd,
1412
+ taskId,
1413
+ input: parsed,
1414
+ projectRootFilter,
1415
+ });
1416
+ if (!result.ok) {
1417
+ json(res, result.status, {
1418
+ error: result.error,
1419
+ ...(result.code !== undefined ? { code: result.code } : {}),
1420
+ ...(result.project_roots !== undefined
1421
+ ? { project_roots: result.project_roots }
1422
+ : {}),
1423
+ });
1424
+ return true;
1425
+ }
1426
+ json(res, 200, { task: result.task });
1427
+ return true;
1428
+ }
1047
1429
  if (method === "GET" && pathname === "/api/tasks") {
1048
1430
  const filter = url.searchParams.get("project_root") ?? undefined;
1049
1431
  const result = await listTasksMultiProject({
@@ -1210,19 +1592,17 @@ export function createOperatorRoutes(deps) {
1210
1592
  typeof body.project_root === "string"
1211
1593
  ? body.project_root
1212
1594
  : undefined;
1213
- let validateRoot = cwd;
1214
- if (writeRoot) {
1215
- try {
1216
- const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
1217
- validateRoot = selected.path;
1218
- }
1219
- catch (err) {
1220
- if (err instanceof CatalogPathError) {
1221
- json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
1222
- return true;
1223
- }
1224
- throw err;
1595
+ let validateRoot;
1596
+ try {
1597
+ const { wireRoot: selected } = await resolveDefaultWriteRoot({ store, bootCwd: cwd }, writeRoot);
1598
+ validateRoot = selected.path;
1599
+ }
1600
+ catch (err) {
1601
+ if (err instanceof CatalogPathError) {
1602
+ json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
1603
+ return true;
1225
1604
  }
1605
+ throw err;
1226
1606
  }
1227
1607
  const ctx = await resolveStageflowContext(validateRoot);
1228
1608
  const result = await validateDraftPackage(draftParsed, {
@@ -1249,9 +1629,11 @@ export function createOperatorRoutes(deps) {
1249
1629
  ? body.project_root
1250
1630
  : undefined;
1251
1631
  let draftWriteRoot;
1632
+ let draftWriteWireRoot;
1252
1633
  try {
1253
1634
  const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
1254
1635
  draftWriteRoot = selected.path;
1636
+ draftWriteWireRoot = selected;
1255
1637
  }
1256
1638
  catch (err) {
1257
1639
  if (err instanceof CatalogPathError) {
@@ -1266,13 +1648,15 @@ export function createOperatorRoutes(deps) {
1266
1648
  return true;
1267
1649
  }
1268
1650
  const ctx = await resolveStageflowContext(draftWriteRoot);
1269
- if (!ctx.isGitProject) {
1651
+ if (ctx.manifestStatus !== "ok") {
1270
1652
  json(res, 400, {
1271
- error: "Project root not found; initialize stageflow.yaml in a git repo",
1653
+ error: "Project root not found; initialize stageflow.yaml",
1272
1654
  });
1273
1655
  return true;
1274
1656
  }
1275
- const result = (await publishDraftPackage(ctx.projectRoot, parsed, "create")).write;
1657
+ const result = (await publishDraftPackage(ctx.projectRoot, parsed, "create", {
1658
+ catalogRootKind: draftWriteWireRoot.kind,
1659
+ })).write;
1276
1660
  if (!result.ok) {
1277
1661
  json(res, result.status, {
1278
1662
  error: result.error,
@@ -1284,6 +1668,8 @@ export function createOperatorRoutes(deps) {
1284
1668
  pipeline: result.pipeline,
1285
1669
  pipelinePath: result.pipelinePath,
1286
1670
  stagePaths: result.stagePaths,
1671
+ catalog_root_path: ctx.projectRoot,
1672
+ ...draftCatalogRootWire(draftWriteWireRoot),
1287
1673
  ...(result.taskPath !== undefined ? { taskPath: result.taskPath } : {}),
1288
1674
  });
1289
1675
  return true;
@@ -1304,9 +1690,11 @@ export function createOperatorRoutes(deps) {
1304
1690
  ? body.project_root
1305
1691
  : undefined;
1306
1692
  let draftWriteRoot;
1693
+ let draftWriteWireRoot;
1307
1694
  try {
1308
1695
  const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
1309
1696
  draftWriteRoot = selected.path;
1697
+ draftWriteWireRoot = selected;
1310
1698
  }
1311
1699
  catch (err) {
1312
1700
  if (err instanceof CatalogPathError) {
@@ -1321,13 +1709,15 @@ export function createOperatorRoutes(deps) {
1321
1709
  return true;
1322
1710
  }
1323
1711
  const ctx = await resolveStageflowContext(draftWriteRoot);
1324
- if (!ctx.isGitProject) {
1712
+ if (ctx.manifestStatus !== "ok") {
1325
1713
  json(res, 400, {
1326
- error: "Project root not found; initialize stageflow.yaml in a git repo",
1714
+ error: "Project root not found; initialize stageflow.yaml",
1327
1715
  });
1328
1716
  return true;
1329
1717
  }
1330
- const result = (await publishDraftPackage(ctx.projectRoot, parsed, "overwrite")).write;
1718
+ const result = (await publishDraftPackage(ctx.projectRoot, parsed, "overwrite", {
1719
+ catalogRootKind: draftWriteWireRoot.kind,
1720
+ })).write;
1331
1721
  if (!result.ok) {
1332
1722
  json(res, result.status, {
1333
1723
  error: result.error,
@@ -1339,10 +1729,70 @@ export function createOperatorRoutes(deps) {
1339
1729
  pipeline: result.pipeline,
1340
1730
  pipelinePath: result.pipelinePath,
1341
1731
  stagePaths: result.stagePaths,
1732
+ catalog_root_path: ctx.projectRoot,
1733
+ ...draftCatalogRootWire(draftWriteWireRoot),
1342
1734
  ...(result.taskPath !== undefined ? { taskPath: result.taskPath } : {}),
1343
1735
  });
1344
1736
  return true;
1345
1737
  }
1738
+ if (method === "POST" && pathname === "/api/drafts/plan") {
1739
+ let body;
1740
+ try {
1741
+ body = await readJsonBody(req);
1742
+ }
1743
+ catch {
1744
+ json(res, 400, { error: "Invalid JSON body" });
1745
+ return true;
1746
+ }
1747
+ const writeRoot = body !== null &&
1748
+ typeof body === "object" &&
1749
+ !Array.isArray(body) &&
1750
+ typeof body.project_root === "string"
1751
+ ? body.project_root
1752
+ : undefined;
1753
+ let draftWriteRoot;
1754
+ try {
1755
+ const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
1756
+ draftWriteRoot = selected.path;
1757
+ }
1758
+ catch (err) {
1759
+ if (err instanceof CatalogPathError) {
1760
+ json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
1761
+ return true;
1762
+ }
1763
+ throw err;
1764
+ }
1765
+ const parsed = parsePlanDraftPackageBody(body);
1766
+ if ("ok" in parsed) {
1767
+ json(res, parsed.status, { error: parsed.error });
1768
+ return true;
1769
+ }
1770
+ const ctx = await resolveStageflowContext(draftWriteRoot);
1771
+ if (ctx.manifestStatus !== "ok") {
1772
+ json(res, 400, {
1773
+ error: "Project root not found; initialize stageflow.yaml",
1774
+ });
1775
+ return true;
1776
+ }
1777
+ const catalogPipelines = parsed.mode === "create"
1778
+ ? await listPipelinesForContext(catalogContextFromStageflow(ctx))
1779
+ : [];
1780
+ const plan = await planDraftPackage(ctx.projectRoot, parsed, {
1781
+ mode: parsed.mode,
1782
+ catalogPipelines,
1783
+ });
1784
+ if (!plan.ok) {
1785
+ json(res, plan.status, { error: plan.error });
1786
+ return true;
1787
+ }
1788
+ json(res, 200, {
1789
+ pipelinePath: plan.pipelinePath,
1790
+ directory: plan.directory,
1791
+ files: plan.files,
1792
+ pipelineIdTaken: plan.pipelineIdTaken,
1793
+ });
1794
+ return true;
1795
+ }
1346
1796
  if (method === "POST" && pathname === "/api/drafts/open") {
1347
1797
  let body;
1348
1798
  try {
@@ -1412,6 +1862,7 @@ export function createOperatorRoutes(deps) {
1412
1862
  draft: result.draft,
1413
1863
  destination: result.destination,
1414
1864
  pipelinePath: result.pipelinePath,
1865
+ ...draftCatalogRootWire(wireRoot),
1415
1866
  ...(result.taskPath !== undefined ? { taskPath: result.taskPath } : {}),
1416
1867
  });
1417
1868
  return true;
@@ -1481,23 +1932,8 @@ export function createOperatorRoutes(deps) {
1481
1932
  const keyParam = url.searchParams.get("key");
1482
1933
  const key = workshopAutosaveSlotKey(keyParam);
1483
1934
  const writeRoot = url.searchParams.get("project_root") ?? undefined;
1484
- let storeRoot;
1485
- try {
1486
- if (writeRoot) {
1487
- const { wireRoot } = await resolveCatalogStartInput({ store, bootCwd: cwd }, writeRoot);
1488
- const ctx = await resolveStageflowContext(wireRoot.path);
1489
- storeRoot = resolveWorkshopAutosaveStoreRoot({
1490
- projectRoot: ctx.projectRoot,
1491
- isGitProject: ctx.isGitProject,
1492
- });
1493
- }
1494
- else {
1495
- const ctx = await resolveStageflowContext(cwd);
1496
- storeRoot = resolveWorkshopAutosaveStoreRoot({
1497
- projectRoot: ctx.isGitProject ? ctx.projectRoot : null,
1498
- isGitProject: ctx.isGitProject,
1499
- });
1500
- }
1935
+ try {
1936
+ await resolveDefaultWriteRoot({ store, bootCwd: cwd }, writeRoot);
1501
1937
  }
1502
1938
  catch (err) {
1503
1939
  if (err instanceof CatalogPathError) {
@@ -1506,6 +1942,7 @@ export function createOperatorRoutes(deps) {
1506
1942
  }
1507
1943
  throw err;
1508
1944
  }
1945
+ const storeRoot = resolveWorkshopAutosaveStoreRoot();
1509
1946
  const record = readWorkshopAutosave(storeRoot, key);
1510
1947
  json(res, 200, { key, autosave: record });
1511
1948
  return true;
@@ -1528,23 +1965,8 @@ export function createOperatorRoutes(deps) {
1528
1965
  const writeRoot = typeof body.project_root === "string"
1529
1966
  ? body.project_root
1530
1967
  : undefined;
1531
- let storeRoot;
1532
- try {
1533
- if (writeRoot) {
1534
- const { wireRoot } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
1535
- const ctx = await resolveStageflowContext(wireRoot.path);
1536
- storeRoot = resolveWorkshopAutosaveStoreRoot({
1537
- projectRoot: ctx.projectRoot,
1538
- isGitProject: ctx.isGitProject,
1539
- });
1540
- }
1541
- else {
1542
- const ctx = await resolveStageflowContext(cwd);
1543
- storeRoot = resolveWorkshopAutosaveStoreRoot({
1544
- projectRoot: ctx.isGitProject ? ctx.projectRoot : null,
1545
- isGitProject: ctx.isGitProject,
1546
- });
1547
- }
1968
+ try {
1969
+ await resolveDefaultWriteRoot({ store, bootCwd: cwd }, writeRoot);
1548
1970
  }
1549
1971
  catch (err) {
1550
1972
  if (err instanceof CatalogPathError) {
@@ -1553,6 +1975,7 @@ export function createOperatorRoutes(deps) {
1553
1975
  }
1554
1976
  throw err;
1555
1977
  }
1978
+ const storeRoot = resolveWorkshopAutosaveStoreRoot();
1556
1979
  const candidate = {
1557
1980
  version: 1,
1558
1981
  key,
@@ -1583,6 +2006,9 @@ export function createOperatorRoutes(deps) {
1583
2006
  diskFingerprints: body.diskFingerprints,
1584
2007
  }
1585
2008
  : {}),
2009
+ ...(body.projectRoot === null || typeof body.projectRoot === "string"
2010
+ ? { projectRoot: body.projectRoot }
2011
+ : {}),
1586
2012
  };
1587
2013
  const parsed = parseWorkshopAutosaveRecord(candidate);
1588
2014
  if (!parsed) {
@@ -1611,23 +2037,8 @@ export function createOperatorRoutes(deps) {
1611
2037
  const writeRoot = typeof body.project_root === "string"
1612
2038
  ? body.project_root
1613
2039
  : undefined;
1614
- let storeRoot;
1615
- try {
1616
- if (writeRoot) {
1617
- const { wireRoot } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
1618
- const ctx = await resolveStageflowContext(wireRoot.path);
1619
- storeRoot = resolveWorkshopAutosaveStoreRoot({
1620
- projectRoot: ctx.projectRoot,
1621
- isGitProject: ctx.isGitProject,
1622
- });
1623
- }
1624
- else {
1625
- const ctx = await resolveStageflowContext(cwd);
1626
- storeRoot = resolveWorkshopAutosaveStoreRoot({
1627
- projectRoot: ctx.isGitProject ? ctx.projectRoot : null,
1628
- isGitProject: ctx.isGitProject,
1629
- });
1630
- }
2040
+ try {
2041
+ await resolveDefaultWriteRoot({ store, bootCwd: cwd }, writeRoot);
1631
2042
  }
1632
2043
  catch (err) {
1633
2044
  if (err instanceof CatalogPathError) {
@@ -1636,6 +2047,7 @@ export function createOperatorRoutes(deps) {
1636
2047
  }
1637
2048
  throw err;
1638
2049
  }
2050
+ const storeRoot = resolveWorkshopAutosaveStoreRoot();
1639
2051
  const cleared = clearWorkshopAutosave(storeRoot, key);
1640
2052
  json(res, 200, { key, cleared });
1641
2053
  return true;
@@ -2064,22 +2476,52 @@ export function createOperatorRoutes(deps) {
2064
2476
  json(res, draftParsed.status, { error: draftParsed.error });
2065
2477
  return true;
2066
2478
  }
2479
+ const attachmentsParsed = parseWorkshopChatAttachments(body.attachments);
2480
+ if (!attachmentsParsed.ok) {
2481
+ json(res, 400, {
2482
+ error: attachmentsParsed.error,
2483
+ code: "workshop_attachment_invalid",
2484
+ });
2485
+ return true;
2486
+ }
2067
2487
  const settingsDefault = await readWorkshopOpenModel(cwd);
2068
2488
  const accept = String(req.headers.accept ?? "");
2069
2489
  const wantsStream = body.stream === true ||
2070
2490
  accept.includes("application/x-ndjson") ||
2071
2491
  accept.includes("text/event-stream");
2492
+ const projectRootWire = typeof body.project_root === "string"
2493
+ ? body.project_root.trim()
2494
+ : typeof body.projectRoot === "string"
2495
+ ? body.projectRoot.trim()
2496
+ : "";
2497
+ let chatProjectRoot;
2498
+ if (projectRootWire) {
2499
+ try {
2500
+ const { wireRoot } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, projectRootWire);
2501
+ chatProjectRoot = wireRoot.path;
2502
+ }
2503
+ catch (err) {
2504
+ if (err instanceof CatalogPathError) {
2505
+ json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
2506
+ return true;
2507
+ }
2508
+ throw err;
2509
+ }
2510
+ }
2072
2511
  const chatTurnBase = {
2073
2512
  sessionId: body.sessionId,
2074
2513
  draft: draftParsed,
2075
2514
  message: body.message,
2076
2515
  autoApply: body.autoApply === true,
2516
+ attachments: attachmentsParsed.attachments,
2517
+ context: parseWorkshopChatContext(body.context),
2077
2518
  model: typeof body.model === "string" || body.model === null
2078
2519
  ? body.model
2079
2520
  : undefined,
2080
2521
  settingsDefault: settingsDefault ?? null,
2081
2522
  registry: workshopChatRegistry,
2082
2523
  storeRoot: workshopSessionStoreRoot(),
2524
+ ...(chatProjectRoot ? { projectRoot: chatProjectRoot } : {}),
2083
2525
  };
2084
2526
  // Fail closed on missing sessions before opening an NDJSON body.
2085
2527
  try {
@@ -2199,7 +2641,7 @@ export function createOperatorRoutes(deps) {
2199
2641
  events: [errorEvent],
2200
2642
  draft: draftParsed,
2201
2643
  pending: null,
2202
- autoApply: false,
2644
+ autoApply: chatTurnBase.autoApply,
2203
2645
  model: resolvedModel,
2204
2646
  buildId: workshopChatRegistry.getPinnedBuildId(body.sessionId),
2205
2647
  });
@@ -2336,8 +2778,94 @@ export function createOperatorRoutes(deps) {
2336
2778
  })) {
2337
2779
  return true;
2338
2780
  }
2781
+ if (method === "GET" && pathname === "/api/skills/usage") {
2782
+ const projectRoot = url.searchParams.get("project_root") ?? undefined;
2783
+ let usageRoot;
2784
+ try {
2785
+ const { wireRoot } = projectRoot === undefined
2786
+ ? await resolveWritableCatalogRoot({ store, bootCwd: cwd }, undefined)
2787
+ : await resolveCatalogStartInput({ store, bootCwd: cwd }, projectRoot);
2788
+ usageRoot = wireRoot.path;
2789
+ }
2790
+ catch (err) {
2791
+ if (err instanceof CatalogPathError) {
2792
+ json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
2793
+ return true;
2794
+ }
2795
+ throw err;
2796
+ }
2797
+ const usages = await buildSkillUsageIndex(usageRoot);
2798
+ if (usages === null) {
2799
+ json(res, 404, { error: "No Stageflow catalog found" });
2800
+ return true;
2801
+ }
2802
+ json(res, 200, { usages });
2803
+ return true;
2804
+ }
2339
2805
  if (method === "GET" && pathname === "/api/skills") {
2340
- json(res, 200, await listSkills({ cwd, agentDir }));
2806
+ const projectRoot = url.searchParams.get("project_root") ?? undefined;
2807
+ try {
2808
+ const { wireRoot } = projectRoot === undefined
2809
+ ? await resolveWritableCatalogRoot({ store, bootCwd: cwd }, undefined)
2810
+ : await resolveCatalogStartInput({ store, bootCwd: cwd }, projectRoot);
2811
+ json(res, 200, await listSkills({ cwd: wireRoot.path, agentDir }));
2812
+ }
2813
+ catch (err) {
2814
+ if (err instanceof CatalogPathError) {
2815
+ json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
2816
+ return true;
2817
+ }
2818
+ throw err;
2819
+ }
2820
+ return true;
2821
+ }
2822
+ if (method === "POST" && pathname === "/api/skills/install") {
2823
+ let body;
2824
+ try {
2825
+ body = await readJsonBody(req);
2826
+ }
2827
+ catch {
2828
+ json(res, 400, { error: "Invalid JSON body" });
2829
+ return true;
2830
+ }
2831
+ const parsed = parseInstallSkillBody(body);
2832
+ if ("ok" in parsed) {
2833
+ json(res, parsed.status, { error: parsed.error });
2834
+ return true;
2835
+ }
2836
+ let skillWriteRoot;
2837
+ try {
2838
+ const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, undefined);
2839
+ skillWriteRoot = selected.path;
2840
+ }
2841
+ catch (err) {
2842
+ if (err instanceof CatalogPathError) {
2843
+ json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
2844
+ return true;
2845
+ }
2846
+ throw err;
2847
+ }
2848
+ const stageflowCtx = await resolveStageflowContext(skillWriteRoot);
2849
+ if (stageflowCtx.manifestStatus !== "ok") {
2850
+ json(res, 400, {
2851
+ error: "Project root not found; initialize stageflow.yaml",
2852
+ });
2853
+ return true;
2854
+ }
2855
+ const result = await installSkillFromCommand({
2856
+ projectRoot: stageflowCtx.projectRoot,
2857
+ command: parsed.command,
2858
+ });
2859
+ if (!result.ok) {
2860
+ json(res, result.status ?? 400, { error: result.error });
2861
+ return true;
2862
+ }
2863
+ json(res, 201, {
2864
+ ok: true,
2865
+ skill_name: result.skill_name,
2866
+ dest_path: result.dest_path,
2867
+ project_root: result.project_root,
2868
+ });
2341
2869
  return true;
2342
2870
  }
2343
2871
  if (method === "GET" && pathname === "/api/extensions") {
@@ -2444,6 +2972,7 @@ export function createOperatorRoutes(deps) {
2444
2972
  }
2445
2973
  }
2446
2974
  };
2975
+ return Object.assign(handler, { dispose: () => liveView.dispose() });
2447
2976
  }
2448
2977
  export async function startUiServer(options) {
2449
2978
  const host = options.host ?? "127.0.0.1";
@@ -2492,6 +3021,8 @@ export async function startUiServer(options) {
2492
3021
  });
2493
3022
  envelope.server.on("close", () => {
2494
3023
  shutdown?.uninstall();
3024
+ if ("dispose" in routes)
3025
+ void routes.dispose();
2495
3026
  });
2496
3027
  return { ...envelope, shutdown };
2497
3028
  }