stageflow 0.32.0 → 0.35.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 (335) hide show
  1. package/README.md +4 -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 +50 -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/fakeAgent.js +0 -6
  12. package/dist/agent/piAdapter.d.ts +2 -2
  13. package/dist/agent/piAdapter.js +116 -15
  14. package/dist/agent/piIsolatedMcp.d.ts +1 -0
  15. package/dist/agent/piIsolatedMcp.js +19 -1
  16. package/dist/agent/piIsolatedMcpProbe.d.ts +11 -0
  17. package/dist/agent/piIsolatedMcpProbe.js +20 -0
  18. package/dist/agent/port.d.ts +2 -2
  19. package/dist/agent/providerAuth.d.ts +6 -0
  20. package/dist/agent/providerAuth.js +86 -17
  21. package/dist/agent/{resolveAgentPort.d.ts → resolveStagePort.d.ts} +8 -6
  22. package/dist/agent/{resolveAgentPort.js → resolveStagePort.js} +13 -9
  23. package/dist/agent/stageTurnDriver.d.ts +33 -0
  24. package/dist/agent/stageTurnDriver.js +64 -0
  25. package/dist/agent/toolOutputBudget.d.ts +46 -0
  26. package/dist/agent/toolOutputBudget.js +97 -0
  27. package/dist/agent/toolOutputBudgetExtension.d.ts +22 -0
  28. package/dist/agent/toolOutputBudgetExtension.js +141 -0
  29. package/dist/agent/toolOutputSpill.d.ts +34 -0
  30. package/dist/agent/toolOutputSpill.js +85 -0
  31. package/dist/browser/agentBrowserLiveViewRelay.d.ts +41 -0
  32. package/dist/browser/agentBrowserLiveViewRelay.js +801 -0
  33. package/dist/browser/anchor.d.ts +2 -0
  34. package/dist/browser/anchor.js +2 -1
  35. package/dist/browser/auditSink.d.ts +8 -0
  36. package/dist/browser/browserHost.d.ts +29 -0
  37. package/dist/browser/browserSweep.d.ts +4 -1
  38. package/dist/browser/browserSweep.js +7 -0
  39. package/dist/browser/browserTeardown.d.ts +1 -0
  40. package/dist/browser/browserTeardown.js +32 -2
  41. package/dist/browser/containerBrowserEndpoint.d.ts +11 -0
  42. package/dist/browser/containerBrowserEndpoint.js +57 -0
  43. package/dist/browser/containerBrowserHost.d.ts +24 -0
  44. package/dist/browser/containerBrowserHost.js +147 -0
  45. package/dist/browser/dockerSandboxOrchestrator.d.ts +26 -0
  46. package/dist/browser/dockerSandboxOrchestrator.js +142 -0
  47. package/dist/browser/fakeLiveViewRelay.d.ts +15 -0
  48. package/dist/browser/fakeLiveViewRelay.js +94 -0
  49. package/dist/browser/fakeLiveViewSource.d.ts +6 -0
  50. package/dist/browser/fakeLiveViewSource.js +16 -0
  51. package/dist/browser/fakeRemoteBrowserHost.d.ts +2 -1
  52. package/dist/browser/fakeRemoteBrowserHost.js +2 -1
  53. package/dist/browser/fakeSandboxOrchestrator.d.ts +9 -0
  54. package/dist/browser/fakeSandboxOrchestrator.js +44 -0
  55. package/dist/browser/gateHandoff.d.ts +0 -2
  56. package/dist/browser/gateHandoff.js +5 -11
  57. package/dist/browser/hostCapabilities.d.ts +23 -0
  58. package/dist/browser/hostCapabilities.js +23 -0
  59. package/dist/browser/humanLogin.d.ts +2 -2
  60. package/dist/browser/humanLogin.js +11 -2
  61. package/dist/browser/liveViewPersisted.d.ts +11 -0
  62. package/dist/browser/liveViewPersisted.js +22 -0
  63. package/dist/browser/liveViewRelay.d.ts +76 -0
  64. package/dist/browser/liveViewRelay.js +1 -0
  65. package/dist/browser/liveViewSessions.d.ts +25 -0
  66. package/dist/browser/liveViewSessions.js +139 -0
  67. package/dist/browser/liveViewSource.d.ts +25 -0
  68. package/dist/browser/liveViewSource.js +7 -0
  69. package/dist/browser/liveViewTickets.d.ts +46 -0
  70. package/dist/browser/liveViewTickets.js +122 -0
  71. package/dist/browser/localBrowserHost.d.ts +10 -0
  72. package/dist/browser/localBrowserHost.js +53 -7
  73. package/dist/browser/ownerScope.d.ts +10 -0
  74. package/dist/browser/ownerScope.js +6 -0
  75. package/dist/browser/profileStore.d.ts +5 -2
  76. package/dist/browser/profileStore.js +3 -5
  77. package/dist/browser/sandboxOrchestrator.d.ts +60 -0
  78. package/dist/browser/sandboxOrchestrator.js +8 -0
  79. package/dist/browser/sessionApiSandboxOrchestrator.d.ts +34 -0
  80. package/dist/browser/sessionApiSandboxOrchestrator.js +108 -0
  81. package/dist/browser/stageBrowserEnv.d.ts +7 -0
  82. package/dist/browser/stageBrowserEnv.js +53 -15
  83. package/dist/browser/stageHandoff.d.ts +47 -0
  84. package/dist/browser/stageHandoff.js +101 -0
  85. package/dist/browser/stageProfileLock.d.ts +1 -0
  86. package/dist/browser/stageProfileLock.js +1 -3
  87. package/dist/browser/volumeProfileStore.d.ts +7 -0
  88. package/dist/browser/volumeProfileStore.js +35 -0
  89. package/dist/cli/browserCommand.d.ts +3 -1
  90. package/dist/cli/browserCommand.js +25 -20
  91. package/dist/cli/resolveCliProjectRoot.d.ts +7 -0
  92. package/dist/cli/resolveCliProjectRoot.js +27 -0
  93. package/dist/cli/runCommand.d.ts +1 -1
  94. package/dist/cli/runCommand.js +59 -17
  95. package/dist/cli/skillsCommand.d.ts +20 -0
  96. package/dist/cli/skillsCommand.js +74 -31
  97. package/dist/cli/validateCommand.d.ts +1 -1
  98. package/dist/cli/validateCommand.js +22 -4
  99. package/dist/cli.js +4 -4
  100. package/dist/config/browseCatalog.d.ts +2 -0
  101. package/dist/config/browseCatalog.js +6 -0
  102. package/dist/config/catalogRelativePath.d.ts +4 -0
  103. package/dist/config/catalogRelativePath.js +37 -9
  104. package/dist/config/createStage.d.ts +1 -0
  105. package/dist/config/createStage.js +11 -2
  106. package/dist/config/createTask.d.ts +30 -0
  107. package/dist/config/createTask.js +161 -0
  108. package/dist/config/createTrigger.d.ts +15 -0
  109. package/dist/config/createTrigger.js +32 -25
  110. package/dist/config/draftPackage.d.ts +41 -0
  111. package/dist/config/draftPackage.js +216 -23
  112. package/dist/config/ensureCatalogScanRoot.d.ts +2 -1
  113. package/dist/config/ensureCatalogScanRoot.js +6 -2
  114. package/dist/config/findTaskInCatalog.d.ts +33 -0
  115. package/dist/config/findTaskInCatalog.js +69 -0
  116. package/dist/config/hostConfig.d.ts +21 -0
  117. package/dist/config/hostConfig.js +113 -1
  118. package/dist/config/importTask.d.ts +19 -0
  119. package/dist/config/importTask.js +133 -0
  120. package/dist/config/installSkillRequest.d.ts +40 -0
  121. package/dist/config/installSkillRequest.js +286 -0
  122. package/dist/config/listConfig.d.ts +0 -16
  123. package/dist/config/listConfig.js +0 -3
  124. package/dist/config/loadOutcome.d.ts +11 -6
  125. package/dist/config/loadPipeline.js +0 -1
  126. package/dist/config/loadStage.js +2 -0
  127. package/dist/config/loadStageflowManifest.js +4 -1
  128. package/dist/config/loadTask.js +2 -0
  129. package/dist/config/loadTrigger.js +2 -0
  130. package/dist/config/mergePipelineIncludes.js +3 -0
  131. package/dist/config/normalizePipelineStageEntry.js +10 -1
  132. package/dist/config/printTargetYaml.js +0 -1
  133. package/dist/config/publishDraftPackage.d.ts +5 -1
  134. package/dist/config/publishDraftPackage.js +4 -1
  135. package/dist/config/resolveCatalogRoots.d.ts +4 -1
  136. package/dist/config/resolveCatalogRoots.js +18 -1
  137. package/dist/config/skillUsageIndex.d.ts +6 -0
  138. package/dist/config/skillUsageIndex.js +39 -0
  139. package/dist/config/stageBrowser.js +13 -0
  140. package/dist/config/triggerAdapterStatus.d.ts +11 -0
  141. package/dist/config/triggerAdapterStatus.js +90 -0
  142. package/dist/config/triggerCatalog.d.ts +41 -2
  143. package/dist/config/triggerCatalog.js +107 -15
  144. package/dist/config/updateTask.d.ts +30 -0
  145. package/dist/config/updateTask.js +119 -0
  146. package/dist/config/updateTrigger.d.ts +38 -0
  147. package/dist/config/updateTrigger.js +220 -0
  148. package/dist/config/validateCatalog.d.ts +6 -2
  149. package/dist/config/validateCatalog.js +48 -7
  150. package/dist/config/workshopCatalogFilenames.d.ts +37 -0
  151. package/dist/config/workshopCatalogFilenames.js +125 -0
  152. package/dist/config/workshopCatalogRepair.d.ts +7 -0
  153. package/dist/config/workshopCatalogRepair.js +144 -0
  154. package/dist/config/yamlParsePosition.d.ts +4 -0
  155. package/dist/config/yamlParsePosition.js +9 -0
  156. package/dist/hosted/containment.d.ts +3 -0
  157. package/dist/hosted/containment.js +36 -0
  158. package/dist/hosted/errors.d.ts +21 -0
  159. package/dist/hosted/errors.js +30 -0
  160. package/dist/hosted/packageSkills.d.ts +3 -0
  161. package/dist/hosted/packageSkills.js +80 -0
  162. package/dist/hosted/runtime.d.ts +12 -0
  163. package/dist/hosted/runtime.js +596 -0
  164. package/dist/hosted/sequencer.d.ts +28 -0
  165. package/dist/hosted/sequencer.js +129 -0
  166. package/dist/hosted/translate.d.ts +3 -0
  167. package/dist/hosted/translate.js +43 -0
  168. package/dist/hosted/types.d.ts +108 -0
  169. package/dist/hosted/types.js +10 -0
  170. package/dist/index.d.ts +9 -1
  171. package/dist/index.js +6 -0
  172. package/dist/mcp/getStartedTool.js +15 -6
  173. package/dist/mcp/triggerTools.js +2 -8
  174. package/dist/operatorAgent/agentPort.d.ts +11 -0
  175. package/dist/operatorAgent/agentPort.js +12 -0
  176. package/dist/operatorAgent/index.d.ts +1 -0
  177. package/dist/operatorAgent/index.js +1 -0
  178. package/dist/operatorAgent/piModel.js +26 -6
  179. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +1 -1
  180. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +4 -2
  181. package/dist/operatorAgent/profiles/workshopAuthorTools.js +103 -16
  182. package/dist/package-meta.d.ts +1 -1
  183. package/dist/package-meta.js +1 -1
  184. package/dist/project/ensureProjectRootsFromEnv.d.ts +3 -0
  185. package/dist/project/ensureProjectRootsFromEnv.js +22 -0
  186. package/dist/project/globalHome.d.ts +1 -0
  187. package/dist/project/globalHome.js +18 -0
  188. package/dist/project/workspaceCatalog.d.ts +10 -0
  189. package/dist/project/workspaceCatalog.js +91 -0
  190. package/dist/projection/projectRun.d.ts +0 -1
  191. package/dist/projection/projectRun.js +0 -1
  192. package/dist/runstore/backup.js +14 -4
  193. package/dist/runstore/port.d.ts +11 -1
  194. package/dist/runstore/restore.js +5 -2
  195. package/dist/runstore/sqlite/SqliteRunStore.d.ts +8 -0
  196. package/dist/runstore/sqlite/SqliteRunStore.js +29 -9
  197. package/dist/runstore/sqlite/migrations/012-trigger-fires.d.ts +7 -0
  198. package/dist/runstore/sqlite/migrations/012-trigger-fires.js +18 -0
  199. package/dist/runstore/sqlite/migrations/index.js +2 -0
  200. package/dist/runstore/stageSnapshot.d.ts +3 -1
  201. package/dist/runstore/stageSnapshot.js +19 -0
  202. package/dist/runstore/workspaceLayout.js +27 -0
  203. package/dist/runstore/writeContainedArtifact.d.ts +1 -0
  204. package/dist/runstore/writeContainedArtifact.js +52 -0
  205. package/dist/runtime/containerLimits.d.ts +0 -3
  206. package/dist/runtime/containerLimits.js +0 -5
  207. package/dist/runtime/credentialBinding.d.ts +3 -0
  208. package/dist/runtime/credentialBinding.js +23 -6
  209. package/dist/runtime/emailSource.js +20 -28
  210. package/dist/runtime/githubPollSource.js +20 -28
  211. package/dist/runtime/pipelineRunner.d.ts +4 -4
  212. package/dist/runtime/pipelineScheduler.d.ts +2 -2
  213. package/dist/runtime/pipelineScheduler.js +3 -0
  214. package/dist/runtime/resumeReconstruct.d.ts +2 -2
  215. package/dist/runtime/resumeTimedOut.d.ts +2 -2
  216. package/dist/runtime/runManager.d.ts +18 -2
  217. package/dist/runtime/runManager.js +95 -30
  218. package/dist/runtime/runRetentionSweep.d.ts +0 -2
  219. package/dist/runtime/runRetentionSweep.js +0 -8
  220. package/dist/runtime/runRetryCoordinator.d.ts +2 -2
  221. package/dist/runtime/scheduleSource.js +15 -24
  222. package/dist/runtime/stageAttemptBootstrap.d.ts +2 -2
  223. package/dist/runtime/stageAttemptBootstrap.js +10 -1
  224. package/dist/runtime/stageProcessLauncher.js +2 -2
  225. package/dist/runtime/stageRoots.js +12 -1
  226. package/dist/runtime/stageRunner.d.ts +2 -2
  227. package/dist/runtime/stageRunner.js +8 -2
  228. package/dist/runtime/stageWorker.js +2 -2
  229. package/dist/runtime/triggerPort.d.ts +5 -3
  230. package/dist/runtime/triggerRunner.d.ts +1 -0
  231. package/dist/runtime/triggerRunner.js +27 -20
  232. package/dist/runtime/webhookAccept.d.ts +25 -0
  233. package/dist/runtime/webhookAccept.js +56 -0
  234. package/dist/server/bootstrap.d.ts +7 -2
  235. package/dist/server/bootstrap.js +15 -1
  236. package/dist/server/controlToken.d.ts +1 -1
  237. package/dist/server/controlToken.js +18 -2
  238. package/dist/server/healthSurfaces.d.ts +12 -0
  239. package/dist/server/healthSurfaces.js +27 -6
  240. package/dist/server/http.d.ts +19 -10
  241. package/dist/server/http.js +605 -85
  242. package/dist/server/liveViewPath.d.ts +9 -0
  243. package/dist/server/liveViewPath.js +8 -0
  244. package/dist/server/liveViewRoutes.d.ts +58 -0
  245. package/dist/server/liveViewRoutes.js +565 -0
  246. package/dist/server/mcpHost.d.ts +4 -2
  247. package/dist/server/mcpHost.js +2 -0
  248. package/dist/server/readCatalogFile.d.ts +29 -0
  249. package/dist/server/readCatalogFile.js +215 -0
  250. package/dist/server/triggerRoutes.d.ts +1 -0
  251. package/dist/server/triggerRoutes.js +200 -73
  252. package/dist/tools/readEmail.d.ts +3 -3
  253. package/dist/tools/writeStageArtifact.d.ts +9 -0
  254. package/dist/tools/writeStageArtifact.js +2 -47
  255. package/dist/types/pipeline.d.ts +1 -1
  256. package/dist/types/stage.d.ts +1 -1
  257. package/dist/types/stageflowManifest.d.ts +1 -1
  258. package/dist/ui/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  259. package/dist/ui/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  260. package/dist/ui/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  261. package/dist/ui/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  262. package/dist/ui/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
  263. package/dist/ui/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
  264. package/dist/ui/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
  265. package/dist/ui/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  266. package/dist/ui/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
  267. package/dist/ui/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
  268. package/dist/ui/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  269. package/dist/ui/assets/index-BoFPkKFg.js +256 -0
  270. package/dist/ui/assets/{index-Bv4p1aPK.css → index-CIPbbBUi.css} +1 -1
  271. package/dist/ui/index.html +4 -4
  272. package/dist/workshop/autosave.d.ts +2 -2
  273. package/dist/workshop/autosave.js +4 -6
  274. package/dist/workshop/buildStore.d.ts +2 -0
  275. package/dist/workshop/chatAttachments.d.ts +27 -0
  276. package/dist/workshop/chatAttachments.js +98 -0
  277. package/dist/workshop/chatTurn.d.ts +5 -2
  278. package/dist/workshop/chatTurn.js +22 -18
  279. package/dist/workshop/sessionMutations.d.ts +17 -0
  280. package/dist/workshop/sessionMutations.js +107 -0
  281. package/dist/workshop/sessionStore.d.ts +10 -0
  282. package/dist/workshop/sessionStore.js +52 -0
  283. package/dist/workshop/workshopPin.d.ts +4 -3
  284. package/dist/workshop/workshopPin.js +26 -6
  285. package/dist/workshop/workshopTranscriptTurn.d.ts +8 -0
  286. package/dist/workshop/workshopTranscriptTurn.js +46 -0
  287. package/examples/README.md +1 -1
  288. package/examples/archify-on-pr/README.md +1 -1
  289. package/examples/archify-on-pr/archify-on-pr.pipeline.yaml +0 -1
  290. package/examples/archify-on-pr/author-diagrams.yaml +1 -0
  291. package/examples/conditional-fork/branch-a.yaml +1 -1
  292. package/examples/conditional-fork/decide.yaml +2 -1
  293. package/examples/feature-loop/{implement.yaml → feature-implement.yaml} +1 -1
  294. package/examples/feature-loop/feature-loop.pipeline.yaml +10 -10
  295. package/examples/feature-loop/{plan.yaml → feature-plan.yaml} +1 -1
  296. package/examples/feature-loop/{review.yaml → feature-review.yaml} +1 -1
  297. package/examples/feedback-loop/{implement.yaml → feedback-implement.yaml} +1 -1
  298. package/examples/feedback-loop/feedback-loop-wait-human.pipeline.yaml +9 -9
  299. package/examples/feedback-loop/feedback-loop.pipeline.yaml +9 -9
  300. package/examples/feedback-loop/{plan.yaml → feedback-plan.yaml} +1 -1
  301. package/examples/feedback-loop/{review.yaml → feedback-review.yaml} +1 -1
  302. package/examples/generic-fan-in/{research.yaml → fan-in-research.yaml} +1 -1
  303. package/examples/generic-fan-in/generic-fan-in-accepted.pipeline.yaml +3 -3
  304. package/examples/generic-fan-in/generic-fan-in.pipeline.yaml +3 -3
  305. package/examples/hello-world/{research.yaml → hello-research.yaml} +1 -1
  306. package/examples/hello-world/hello.pipeline.yaml +2 -2
  307. package/examples/mcp-hitl-tour/{review.yaml → mcp-hitl-review.yaml} +1 -1
  308. package/examples/mcp-hitl-tour/mcp-hitl-tour.pipeline.yaml +3 -3
  309. package/examples/oss-issue-contribution/oss-explain-fix.yaml +1 -0
  310. package/examples/oss-issue-contribution/oss-explain-issue.yaml +1 -0
  311. package/examples/oss-issue-contribution/oss-issue-contribution.pipeline.yaml +0 -2
  312. package/examples/stageflow.yaml +6 -0
  313. package/examples/triggers/sample-github-pr.trigger.yaml +12 -0
  314. package/examples/triggers/sample-inbox-email.trigger.yaml +14 -0
  315. package/examples/triggers/sample-manual-hello.trigger.yaml +5 -0
  316. package/examples/triggers/sample-nightly-hello.trigger.yaml +8 -0
  317. package/examples/triggers/sample-signed-webhook.trigger.yaml +11 -0
  318. package/examples/triggers-readiness/README.md +48 -0
  319. package/examples/triggers-readiness/readiness-emit.yaml +18 -0
  320. package/examples/triggers-readiness/readiness.pipeline.yaml +4 -0
  321. package/examples/triggers-readiness/readiness.task.yaml +6 -0
  322. package/examples/triggers-readiness/stageflow.yaml +13 -0
  323. package/examples/triggers-readiness/triggers/readiness-dynamic-manual.trigger.yaml +4 -0
  324. package/examples/triggers-readiness/triggers/readiness-email.trigger.yaml +12 -0
  325. package/examples/triggers-readiness/triggers/readiness-github-pr.trigger.yaml +12 -0
  326. package/examples/triggers-readiness/triggers/readiness-manual.trigger.yaml +5 -0
  327. package/examples/triggers-readiness/triggers/readiness-schedule.trigger.yaml +8 -0
  328. package/examples/triggers-readiness/triggers/readiness-webhook.trigger.yaml +11 -0
  329. package/package.json +9 -1
  330. package/skills/stageflow/references/control-surface.md +1 -1
  331. package/skills/stageflow-author/references/catalog-write-conventions.md +2 -2
  332. package/skills/stageflow-run/SKILL.md +1 -1
  333. package/skills/stageflow-run/references/mcp-call.md +1 -1
  334. package/skills/stageflow-setup/SKILL.md +1 -1
  335. 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";
@@ -43,13 +55,20 @@ import { bootstrapStageflowHost, } from "./bootstrap.js";
43
55
  import { assertAllowedHttpAccess, isTrustedLocalHttpRequest, resolveAllowedHosts, } from "./allowedHosts.js";
44
56
  import { enforceBearerAuth, loadControlTokens, requiredScopeFor, } from "./controlToken.js";
45
57
  import { createHttpHost, DEFAULT_PORT, json, } from "./createHttpHost.js";
46
- import { handleApiHealth } from "./healthSurfaces.js";
58
+ import { handleApiHealth, listCatalogRootsForApi } from "./healthSurfaces.js";
47
59
  import { mapRetryStageFailure, mapStartFailure, mapStoreLookupError, } from "./operatorResults.js";
48
60
  import { installShutdownController, makeDrainableHostFromOptional, } from "./shutdown.js";
49
61
  import { writeAudit } from "../logging/audit.js";
50
62
  import { logger as rootLogger } from "../logging/logger.js";
51
63
  import { callerIdFromRequestAuth, getRequestAuth, requestAuthFromBearer, runWithRequestAuth, } from "./requestAuthContext.js";
52
64
  const auditLog = rootLogger.child({ component: "audit" });
65
+ function draftCatalogRootWire(root) {
66
+ return {
67
+ project_root: root.project_root,
68
+ root_kind: root.kind,
69
+ read_only: root.read_only,
70
+ };
71
+ }
53
72
  function textPlain(res, status, body) {
54
73
  res.writeHead(status, {
55
74
  "Content-Type": "text/plain; charset=utf-8",
@@ -137,7 +156,10 @@ async function serveStatic(res, uiDistDir, urlPath) {
137
156
  function isCredentialMutatingApi(method, pathname) {
138
157
  if (method !== "POST")
139
158
  return false;
140
- return (/^\/api\/providers\/[^/]+\/login$/.test(pathname) ||
159
+ return (isLiveViewInputPath(method, pathname) ||
160
+ isLiveViewDialogPath(method, pathname) ||
161
+ isLiveViewReopenTabPath(method, pathname) ||
162
+ /^\/api\/providers\/[^/]+\/login$/.test(pathname) ||
141
163
  /^\/api\/providers\/[^/]+\/login\/[^/]+\/answer$/.test(pathname) ||
142
164
  /^\/api\/providers\/[^/]+\/login\/[^/]+\/cancel$/.test(pathname) ||
143
165
  /^\/api\/providers\/[^/]+\/logout$/.test(pathname));
@@ -149,18 +171,11 @@ export function defaultUiDistDir() {
149
171
  }
150
172
  return path.resolve(here, "../ui");
151
173
  }
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
174
  export function createOperatorRoutes(deps) {
160
175
  const { manager, store, cwd, agentDir, rootDir, providerAuthContext, uiDistDir, workshopOperatorHost, } = deps;
161
176
  const workshopChatRegistry = deps.workshopChatRegistry ??
162
177
  new WorkshopChatSessionRegistry(workshopOperatorHost ??
163
- createLiveWorkshopOperatorHost({
178
+ resolveAgentPort({
164
179
  cwd,
165
180
  projectRoot: cwd,
166
181
  }));
@@ -168,7 +183,11 @@ export function createOperatorRoutes(deps) {
168
183
  const workshopBuildStoreRoot = () => resolveWorkshopBuildStoreRoot();
169
184
  const allowedHosts = deps.allowedHosts ?? resolveAllowedHosts();
170
185
  const controlTokens = deps.controlTokens ?? loadControlTokens();
171
- return async ({ req, res, url, pathname, method, boot }) => {
186
+ const liveView = createLiveViewRoutes({ ...deps.liveView, store, controlTokens });
187
+ liveView.attach(manager);
188
+ const handler = async ({ req, res, url, pathname, method, boot }) => {
189
+ if (isLiveViewPath(pathname))
190
+ applyLiveViewHeaders(res);
172
191
  if (boot.serveBlocked !== undefined && pathname.startsWith("/api/")) {
173
192
  json(res, 503, {
174
193
  error: boot.serveBlocked.reason,
@@ -193,6 +212,8 @@ export function createOperatorRoutes(deps) {
193
212
  return handleOperatorRequest();
194
213
  async function handleOperatorRequest() {
195
214
  try {
215
+ if (await liveView.handle(req, res))
216
+ return true;
196
217
  if (pathname.startsWith("/api/email/")) {
197
218
  if (!assertAllowedHttpAccess({ entries: [] }, req, res, { requireOrigin: ["POST", "PATCH", "DELETE"].includes(method) }))
198
219
  return true;
@@ -1044,6 +1065,356 @@ export function createOperatorRoutes(deps) {
1044
1065
  });
1045
1066
  return true;
1046
1067
  }
1068
+ if (method === "GET" && pathname === "/api/catalog/roots") {
1069
+ const roots = await listCatalogRootsForApi({ store, bootCwd: cwd });
1070
+ json(res, 200, { roots });
1071
+ return true;
1072
+ }
1073
+ if (method === "POST" && pathname === "/api/catalog/validate") {
1074
+ let body;
1075
+ try {
1076
+ body = await readJsonBody(req);
1077
+ }
1078
+ catch {
1079
+ json(res, 400, { error: "Invalid JSON body" });
1080
+ return true;
1081
+ }
1082
+ if (body !== null && typeof body === "object" && !Array.isArray(body)) {
1083
+ const record = body;
1084
+ let pipeline = typeof record.pipeline === "string" ? record.pipeline : undefined;
1085
+ let task = typeof record.task === "string" ? record.task : undefined;
1086
+ let stage = typeof record.stage === "string" ? record.stage : undefined;
1087
+ const strict = record.strict === true;
1088
+ const projectRoot = typeof record.project_root === "string" && record.project_root.trim()
1089
+ ? record.project_root.trim()
1090
+ : undefined;
1091
+ const scopedCount = [pipeline, task, stage].filter((v) => v !== undefined).length;
1092
+ if (scopedCount > 1) {
1093
+ json(res, 400, {
1094
+ error: "Use at most one of pipeline, task, or stage",
1095
+ });
1096
+ return true;
1097
+ }
1098
+ let validateCwd = cwd;
1099
+ if (pipeline !== undefined || task !== undefined || stage !== undefined) {
1100
+ try {
1101
+ const { wireRoot, roots } = await resolveCatalogStartInput({ store, bootCwd: cwd }, projectRoot);
1102
+ validateCwd = wireRoot.path;
1103
+ if (pipeline !== undefined) {
1104
+ pipeline = resolveCatalogRelativePath({
1105
+ inputPath: pipeline.trim(),
1106
+ projectRoot: wireRoot.project_root,
1107
+ roots,
1108
+ fieldName: "pipeline",
1109
+ }).absolutePath;
1110
+ }
1111
+ if (task !== undefined) {
1112
+ task = resolveCatalogRelativePath({
1113
+ inputPath: task.trim(),
1114
+ projectRoot: wireRoot.project_root,
1115
+ roots,
1116
+ fieldName: "task",
1117
+ }).absolutePath;
1118
+ }
1119
+ if (stage !== undefined) {
1120
+ stage = resolveCatalogRelativePath({
1121
+ inputPath: stage.trim(),
1122
+ projectRoot: wireRoot.project_root,
1123
+ roots,
1124
+ fieldName: "stage",
1125
+ }).absolutePath;
1126
+ }
1127
+ }
1128
+ catch (err) {
1129
+ if (err instanceof CatalogPathError) {
1130
+ json(res, 400, catalogPathErrorBody(err));
1131
+ return true;
1132
+ }
1133
+ throw err;
1134
+ }
1135
+ }
1136
+ const scope = pipeline
1137
+ ? "pipeline"
1138
+ : task
1139
+ ? "task"
1140
+ : stage
1141
+ ? "stage"
1142
+ : "full";
1143
+ const stageContent = typeof record.content === "string" ? record.content : undefined;
1144
+ if (stageContent !== undefined && stage === undefined) {
1145
+ json(res, 400, {
1146
+ error: "content is only supported with stage",
1147
+ });
1148
+ return true;
1149
+ }
1150
+ const result = await validateCatalog({
1151
+ scope,
1152
+ cwd: validateCwd,
1153
+ ...(pipeline !== undefined ||
1154
+ task !== undefined ||
1155
+ stage !== undefined
1156
+ ? { projectRoot: validateCwd }
1157
+ : {}),
1158
+ pipeline,
1159
+ task,
1160
+ stage,
1161
+ ...(stageContent !== undefined ? { stageContent } : {}),
1162
+ strict,
1163
+ });
1164
+ json(res, 200, result);
1165
+ return true;
1166
+ }
1167
+ json(res, 400, { error: "Request body must be an object" });
1168
+ return true;
1169
+ }
1170
+ if (method === "GET" && pathname === "/api/catalog/file") {
1171
+ const relPath = url.searchParams.get("path");
1172
+ if (relPath === null || relPath.length === 0) {
1173
+ json(res, 400, { error: "path query parameter is required" });
1174
+ return true;
1175
+ }
1176
+ const projectRoot = url.searchParams.get("project_root") ?? undefined;
1177
+ try {
1178
+ const { roots } = await resolveCatalogStartInput({ store, bootCwd: cwd }, projectRoot);
1179
+ const result = await readCatalogFileForHttp({
1180
+ inputPath: relPath,
1181
+ projectRoot,
1182
+ roots,
1183
+ fieldName: "path",
1184
+ });
1185
+ if (!result.ok) {
1186
+ json(res, result.status, { error: result.error, code: result.code });
1187
+ return true;
1188
+ }
1189
+ json(res, 200, { path: result.path, content: result.content });
1190
+ return true;
1191
+ }
1192
+ catch (err) {
1193
+ if (err instanceof CatalogPathError) {
1194
+ json(res, 400, catalogPathErrorBody(err));
1195
+ return true;
1196
+ }
1197
+ throw err;
1198
+ }
1199
+ }
1200
+ if (method === "PUT" && pathname === "/api/catalog/file") {
1201
+ let body;
1202
+ try {
1203
+ body = await readJsonBody(req);
1204
+ }
1205
+ catch {
1206
+ json(res, 400, { error: "Invalid JSON body" });
1207
+ return true;
1208
+ }
1209
+ if (body === null || typeof body !== "object" || Array.isArray(body)) {
1210
+ json(res, 400, { error: "Request body must be an object" });
1211
+ return true;
1212
+ }
1213
+ const record = body;
1214
+ const relPath = typeof record.path === "string" ? record.path.trim() : "";
1215
+ if (!relPath) {
1216
+ json(res, 400, { error: "path is required" });
1217
+ return true;
1218
+ }
1219
+ if (typeof record.content !== "string") {
1220
+ json(res, 400, { error: "content must be a string" });
1221
+ return true;
1222
+ }
1223
+ const writeRoot = typeof record.project_root === "string"
1224
+ ? record.project_root
1225
+ : undefined;
1226
+ try {
1227
+ const { wireRoot, roots } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
1228
+ const result = await writeCatalogFileForHttp({
1229
+ inputPath: relPath,
1230
+ content: record.content,
1231
+ projectRoot: wireRoot.project_root,
1232
+ roots,
1233
+ fieldName: "path",
1234
+ });
1235
+ if (!result.ok) {
1236
+ json(res, result.status, {
1237
+ error: result.error,
1238
+ code: result.code,
1239
+ ...(result.findings !== undefined
1240
+ ? { findings: result.findings }
1241
+ : {}),
1242
+ });
1243
+ return true;
1244
+ }
1245
+ json(res, 200, { path: result.path, content: result.content });
1246
+ return true;
1247
+ }
1248
+ catch (err) {
1249
+ if (err instanceof CatalogPathError) {
1250
+ json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
1251
+ return true;
1252
+ }
1253
+ throw err;
1254
+ }
1255
+ }
1256
+ if (method === "POST" && pathname === "/api/tasks/import") {
1257
+ let body;
1258
+ try {
1259
+ body = await readJsonBody(req);
1260
+ }
1261
+ catch {
1262
+ json(res, 400, { error: "Invalid JSON body" });
1263
+ return true;
1264
+ }
1265
+ const parsed = parseImportTaskBody(body);
1266
+ if ("ok" in parsed) {
1267
+ json(res, parsed.status, { error: parsed.error });
1268
+ return true;
1269
+ }
1270
+ const writeRoot = body !== null &&
1271
+ typeof body === "object" &&
1272
+ !Array.isArray(body) &&
1273
+ typeof body.project_root === "string"
1274
+ ? body.project_root
1275
+ : undefined;
1276
+ let taskWriteRoot;
1277
+ try {
1278
+ const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
1279
+ taskWriteRoot = selected.path;
1280
+ }
1281
+ catch (err) {
1282
+ if (err instanceof CatalogPathError) {
1283
+ json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
1284
+ return true;
1285
+ }
1286
+ throw err;
1287
+ }
1288
+ const stageflowCtx = await resolveStageflowContext(taskWriteRoot);
1289
+ if (stageflowCtx.manifestStatus !== "ok") {
1290
+ json(res, 400, {
1291
+ error: "Project root not found; initialize stageflow.yaml",
1292
+ });
1293
+ return true;
1294
+ }
1295
+ const result = await importTaskFromIssue(stageflowCtx.projectRoot, parsed);
1296
+ if (!result.ok) {
1297
+ json(res, result.status, { error: result.error });
1298
+ return true;
1299
+ }
1300
+ json(res, 201, { task: result.task });
1301
+ return true;
1302
+ }
1303
+ if (method === "POST" && pathname === "/api/tasks") {
1304
+ let body;
1305
+ try {
1306
+ body = await readJsonBody(req);
1307
+ }
1308
+ catch {
1309
+ json(res, 400, { error: "Invalid JSON body" });
1310
+ return true;
1311
+ }
1312
+ const writeRoot = body !== null &&
1313
+ typeof body === "object" &&
1314
+ !Array.isArray(body) &&
1315
+ typeof body.project_root === "string"
1316
+ ? body.project_root
1317
+ : undefined;
1318
+ let taskWriteRoot;
1319
+ try {
1320
+ const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
1321
+ taskWriteRoot = selected.path;
1322
+ }
1323
+ catch (err) {
1324
+ if (err instanceof CatalogPathError) {
1325
+ json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
1326
+ return true;
1327
+ }
1328
+ throw err;
1329
+ }
1330
+ const parsed = parseCreateTaskBody(body);
1331
+ if ("ok" in parsed) {
1332
+ json(res, parsed.status, { error: parsed.error });
1333
+ return true;
1334
+ }
1335
+ const stageflowCtx = await resolveStageflowContext(taskWriteRoot);
1336
+ if (stageflowCtx.manifestStatus !== "ok") {
1337
+ json(res, 400, {
1338
+ error: "Project root not found; initialize stageflow.yaml",
1339
+ });
1340
+ return true;
1341
+ }
1342
+ const result = await createTask(stageflowCtx.projectRoot, parsed);
1343
+ if (!result.ok) {
1344
+ json(res, result.status, { error: result.error });
1345
+ return true;
1346
+ }
1347
+ json(res, 201, { task: result.task });
1348
+ return true;
1349
+ }
1350
+ const taskDetailMatch = pathname.match(/^\/api\/tasks\/([^/]+)$/);
1351
+ if (method === "GET" && taskDetailMatch) {
1352
+ const taskId = decodeURIComponent(taskDetailMatch[1] ?? "");
1353
+ const projectRootFilter = url.searchParams.get("project_root") ?? undefined;
1354
+ const lookup = await findTaskInCatalogMulti({
1355
+ store,
1356
+ bootCwd: cwd,
1357
+ taskId,
1358
+ projectRootFilter,
1359
+ });
1360
+ if (!lookup.ok) {
1361
+ if (lookup.status === 409) {
1362
+ json(res, 409, {
1363
+ error: lookup.error,
1364
+ code: lookup.code,
1365
+ project_roots: lookup.project_roots,
1366
+ });
1367
+ return true;
1368
+ }
1369
+ if (lookup.status === 400) {
1370
+ json(res, 400, {
1371
+ error: lookup.error,
1372
+ code: lookup.code,
1373
+ });
1374
+ return true;
1375
+ }
1376
+ json(res, 404, { error: lookup.error });
1377
+ return true;
1378
+ }
1379
+ json(res, 200, { task: lookup.task });
1380
+ return true;
1381
+ }
1382
+ if (method === "PUT" && taskDetailMatch) {
1383
+ const taskId = decodeURIComponent(taskDetailMatch[1] ?? "");
1384
+ const projectRootFilter = url.searchParams.get("project_root") ?? undefined;
1385
+ let body;
1386
+ try {
1387
+ body = await readJsonBody(req);
1388
+ }
1389
+ catch {
1390
+ json(res, 400, { error: "Invalid JSON body" });
1391
+ return true;
1392
+ }
1393
+ const parsed = parseUpdateTaskBody(body);
1394
+ if ("ok" in parsed) {
1395
+ json(res, parsed.status, { error: parsed.error });
1396
+ return true;
1397
+ }
1398
+ const result = await updateTask({
1399
+ store,
1400
+ bootCwd: cwd,
1401
+ taskId,
1402
+ input: parsed,
1403
+ projectRootFilter,
1404
+ });
1405
+ if (!result.ok) {
1406
+ json(res, result.status, {
1407
+ error: result.error,
1408
+ ...(result.code !== undefined ? { code: result.code } : {}),
1409
+ ...(result.project_roots !== undefined
1410
+ ? { project_roots: result.project_roots }
1411
+ : {}),
1412
+ });
1413
+ return true;
1414
+ }
1415
+ json(res, 200, { task: result.task });
1416
+ return true;
1417
+ }
1047
1418
  if (method === "GET" && pathname === "/api/tasks") {
1048
1419
  const filter = url.searchParams.get("project_root") ?? undefined;
1049
1420
  const result = await listTasksMultiProject({
@@ -1210,19 +1581,17 @@ export function createOperatorRoutes(deps) {
1210
1581
  typeof body.project_root === "string"
1211
1582
  ? body.project_root
1212
1583
  : 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;
1584
+ let validateRoot;
1585
+ try {
1586
+ const { wireRoot: selected } = await resolveDefaultWriteRoot({ store, bootCwd: cwd }, writeRoot);
1587
+ validateRoot = selected.path;
1588
+ }
1589
+ catch (err) {
1590
+ if (err instanceof CatalogPathError) {
1591
+ json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
1592
+ return true;
1225
1593
  }
1594
+ throw err;
1226
1595
  }
1227
1596
  const ctx = await resolveStageflowContext(validateRoot);
1228
1597
  const result = await validateDraftPackage(draftParsed, {
@@ -1249,9 +1618,11 @@ export function createOperatorRoutes(deps) {
1249
1618
  ? body.project_root
1250
1619
  : undefined;
1251
1620
  let draftWriteRoot;
1621
+ let draftWriteWireRoot;
1252
1622
  try {
1253
1623
  const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
1254
1624
  draftWriteRoot = selected.path;
1625
+ draftWriteWireRoot = selected;
1255
1626
  }
1256
1627
  catch (err) {
1257
1628
  if (err instanceof CatalogPathError) {
@@ -1266,13 +1637,15 @@ export function createOperatorRoutes(deps) {
1266
1637
  return true;
1267
1638
  }
1268
1639
  const ctx = await resolveStageflowContext(draftWriteRoot);
1269
- if (!ctx.isGitProject) {
1640
+ if (ctx.manifestStatus !== "ok") {
1270
1641
  json(res, 400, {
1271
- error: "Project root not found; initialize stageflow.yaml in a git repo",
1642
+ error: "Project root not found; initialize stageflow.yaml",
1272
1643
  });
1273
1644
  return true;
1274
1645
  }
1275
- const result = (await publishDraftPackage(ctx.projectRoot, parsed, "create")).write;
1646
+ const result = (await publishDraftPackage(ctx.projectRoot, parsed, "create", {
1647
+ catalogRootKind: draftWriteWireRoot.kind,
1648
+ })).write;
1276
1649
  if (!result.ok) {
1277
1650
  json(res, result.status, {
1278
1651
  error: result.error,
@@ -1284,6 +1657,8 @@ export function createOperatorRoutes(deps) {
1284
1657
  pipeline: result.pipeline,
1285
1658
  pipelinePath: result.pipelinePath,
1286
1659
  stagePaths: result.stagePaths,
1660
+ catalog_root_path: ctx.projectRoot,
1661
+ ...draftCatalogRootWire(draftWriteWireRoot),
1287
1662
  ...(result.taskPath !== undefined ? { taskPath: result.taskPath } : {}),
1288
1663
  });
1289
1664
  return true;
@@ -1304,9 +1679,11 @@ export function createOperatorRoutes(deps) {
1304
1679
  ? body.project_root
1305
1680
  : undefined;
1306
1681
  let draftWriteRoot;
1682
+ let draftWriteWireRoot;
1307
1683
  try {
1308
1684
  const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
1309
1685
  draftWriteRoot = selected.path;
1686
+ draftWriteWireRoot = selected;
1310
1687
  }
1311
1688
  catch (err) {
1312
1689
  if (err instanceof CatalogPathError) {
@@ -1321,13 +1698,15 @@ export function createOperatorRoutes(deps) {
1321
1698
  return true;
1322
1699
  }
1323
1700
  const ctx = await resolveStageflowContext(draftWriteRoot);
1324
- if (!ctx.isGitProject) {
1701
+ if (ctx.manifestStatus !== "ok") {
1325
1702
  json(res, 400, {
1326
- error: "Project root not found; initialize stageflow.yaml in a git repo",
1703
+ error: "Project root not found; initialize stageflow.yaml",
1327
1704
  });
1328
1705
  return true;
1329
1706
  }
1330
- const result = (await publishDraftPackage(ctx.projectRoot, parsed, "overwrite")).write;
1707
+ const result = (await publishDraftPackage(ctx.projectRoot, parsed, "overwrite", {
1708
+ catalogRootKind: draftWriteWireRoot.kind,
1709
+ })).write;
1331
1710
  if (!result.ok) {
1332
1711
  json(res, result.status, {
1333
1712
  error: result.error,
@@ -1339,10 +1718,70 @@ export function createOperatorRoutes(deps) {
1339
1718
  pipeline: result.pipeline,
1340
1719
  pipelinePath: result.pipelinePath,
1341
1720
  stagePaths: result.stagePaths,
1721
+ catalog_root_path: ctx.projectRoot,
1722
+ ...draftCatalogRootWire(draftWriteWireRoot),
1342
1723
  ...(result.taskPath !== undefined ? { taskPath: result.taskPath } : {}),
1343
1724
  });
1344
1725
  return true;
1345
1726
  }
1727
+ if (method === "POST" && pathname === "/api/drafts/plan") {
1728
+ let body;
1729
+ try {
1730
+ body = await readJsonBody(req);
1731
+ }
1732
+ catch {
1733
+ json(res, 400, { error: "Invalid JSON body" });
1734
+ return true;
1735
+ }
1736
+ const writeRoot = body !== null &&
1737
+ typeof body === "object" &&
1738
+ !Array.isArray(body) &&
1739
+ typeof body.project_root === "string"
1740
+ ? body.project_root
1741
+ : undefined;
1742
+ let draftWriteRoot;
1743
+ try {
1744
+ const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, writeRoot);
1745
+ draftWriteRoot = selected.path;
1746
+ }
1747
+ catch (err) {
1748
+ if (err instanceof CatalogPathError) {
1749
+ json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
1750
+ return true;
1751
+ }
1752
+ throw err;
1753
+ }
1754
+ const parsed = parsePlanDraftPackageBody(body);
1755
+ if ("ok" in parsed) {
1756
+ json(res, parsed.status, { error: parsed.error });
1757
+ return true;
1758
+ }
1759
+ const ctx = await resolveStageflowContext(draftWriteRoot);
1760
+ if (ctx.manifestStatus !== "ok") {
1761
+ json(res, 400, {
1762
+ error: "Project root not found; initialize stageflow.yaml",
1763
+ });
1764
+ return true;
1765
+ }
1766
+ const catalogPipelines = parsed.mode === "create"
1767
+ ? await listPipelinesForContext(catalogContextFromStageflow(ctx))
1768
+ : [];
1769
+ const plan = await planDraftPackage(ctx.projectRoot, parsed, {
1770
+ mode: parsed.mode,
1771
+ catalogPipelines,
1772
+ });
1773
+ if (!plan.ok) {
1774
+ json(res, plan.status, { error: plan.error });
1775
+ return true;
1776
+ }
1777
+ json(res, 200, {
1778
+ pipelinePath: plan.pipelinePath,
1779
+ directory: plan.directory,
1780
+ files: plan.files,
1781
+ pipelineIdTaken: plan.pipelineIdTaken,
1782
+ });
1783
+ return true;
1784
+ }
1346
1785
  if (method === "POST" && pathname === "/api/drafts/open") {
1347
1786
  let body;
1348
1787
  try {
@@ -1412,6 +1851,7 @@ export function createOperatorRoutes(deps) {
1412
1851
  draft: result.draft,
1413
1852
  destination: result.destination,
1414
1853
  pipelinePath: result.pipelinePath,
1854
+ ...draftCatalogRootWire(wireRoot),
1415
1855
  ...(result.taskPath !== undefined ? { taskPath: result.taskPath } : {}),
1416
1856
  });
1417
1857
  return true;
@@ -1481,23 +1921,8 @@ export function createOperatorRoutes(deps) {
1481
1921
  const keyParam = url.searchParams.get("key");
1482
1922
  const key = workshopAutosaveSlotKey(keyParam);
1483
1923
  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
- }
1924
+ try {
1925
+ await resolveDefaultWriteRoot({ store, bootCwd: cwd }, writeRoot);
1501
1926
  }
1502
1927
  catch (err) {
1503
1928
  if (err instanceof CatalogPathError) {
@@ -1506,6 +1931,7 @@ export function createOperatorRoutes(deps) {
1506
1931
  }
1507
1932
  throw err;
1508
1933
  }
1934
+ const storeRoot = resolveWorkshopAutosaveStoreRoot();
1509
1935
  const record = readWorkshopAutosave(storeRoot, key);
1510
1936
  json(res, 200, { key, autosave: record });
1511
1937
  return true;
@@ -1528,23 +1954,8 @@ export function createOperatorRoutes(deps) {
1528
1954
  const writeRoot = typeof body.project_root === "string"
1529
1955
  ? body.project_root
1530
1956
  : 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
- }
1957
+ try {
1958
+ await resolveDefaultWriteRoot({ store, bootCwd: cwd }, writeRoot);
1548
1959
  }
1549
1960
  catch (err) {
1550
1961
  if (err instanceof CatalogPathError) {
@@ -1553,6 +1964,7 @@ export function createOperatorRoutes(deps) {
1553
1964
  }
1554
1965
  throw err;
1555
1966
  }
1967
+ const storeRoot = resolveWorkshopAutosaveStoreRoot();
1556
1968
  const candidate = {
1557
1969
  version: 1,
1558
1970
  key,
@@ -1583,6 +1995,9 @@ export function createOperatorRoutes(deps) {
1583
1995
  diskFingerprints: body.diskFingerprints,
1584
1996
  }
1585
1997
  : {}),
1998
+ ...(body.projectRoot === null || typeof body.projectRoot === "string"
1999
+ ? { projectRoot: body.projectRoot }
2000
+ : {}),
1586
2001
  };
1587
2002
  const parsed = parseWorkshopAutosaveRecord(candidate);
1588
2003
  if (!parsed) {
@@ -1611,23 +2026,8 @@ export function createOperatorRoutes(deps) {
1611
2026
  const writeRoot = typeof body.project_root === "string"
1612
2027
  ? body.project_root
1613
2028
  : 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
- }
2029
+ try {
2030
+ await resolveDefaultWriteRoot({ store, bootCwd: cwd }, writeRoot);
1631
2031
  }
1632
2032
  catch (err) {
1633
2033
  if (err instanceof CatalogPathError) {
@@ -1636,6 +2036,7 @@ export function createOperatorRoutes(deps) {
1636
2036
  }
1637
2037
  throw err;
1638
2038
  }
2039
+ const storeRoot = resolveWorkshopAutosaveStoreRoot();
1639
2040
  const cleared = clearWorkshopAutosave(storeRoot, key);
1640
2041
  json(res, 200, { key, cleared });
1641
2042
  return true;
@@ -2064,22 +2465,52 @@ export function createOperatorRoutes(deps) {
2064
2465
  json(res, draftParsed.status, { error: draftParsed.error });
2065
2466
  return true;
2066
2467
  }
2468
+ const attachmentsParsed = parseWorkshopChatAttachments(body.attachments);
2469
+ if (!attachmentsParsed.ok) {
2470
+ json(res, 400, {
2471
+ error: attachmentsParsed.error,
2472
+ code: "workshop_attachment_invalid",
2473
+ });
2474
+ return true;
2475
+ }
2067
2476
  const settingsDefault = await readWorkshopOpenModel(cwd);
2068
2477
  const accept = String(req.headers.accept ?? "");
2069
2478
  const wantsStream = body.stream === true ||
2070
2479
  accept.includes("application/x-ndjson") ||
2071
2480
  accept.includes("text/event-stream");
2481
+ const projectRootWire = typeof body.project_root === "string"
2482
+ ? body.project_root.trim()
2483
+ : typeof body.projectRoot === "string"
2484
+ ? body.projectRoot.trim()
2485
+ : "";
2486
+ let chatProjectRoot;
2487
+ if (projectRootWire) {
2488
+ try {
2489
+ const { wireRoot } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, projectRootWire);
2490
+ chatProjectRoot = wireRoot.path;
2491
+ }
2492
+ catch (err) {
2493
+ if (err instanceof CatalogPathError) {
2494
+ json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
2495
+ return true;
2496
+ }
2497
+ throw err;
2498
+ }
2499
+ }
2072
2500
  const chatTurnBase = {
2073
2501
  sessionId: body.sessionId,
2074
2502
  draft: draftParsed,
2075
2503
  message: body.message,
2076
2504
  autoApply: body.autoApply === true,
2505
+ attachments: attachmentsParsed.attachments,
2506
+ context: parseWorkshopChatContext(body.context),
2077
2507
  model: typeof body.model === "string" || body.model === null
2078
2508
  ? body.model
2079
2509
  : undefined,
2080
2510
  settingsDefault: settingsDefault ?? null,
2081
2511
  registry: workshopChatRegistry,
2082
2512
  storeRoot: workshopSessionStoreRoot(),
2513
+ ...(chatProjectRoot ? { projectRoot: chatProjectRoot } : {}),
2083
2514
  };
2084
2515
  // Fail closed on missing sessions before opening an NDJSON body.
2085
2516
  try {
@@ -2199,7 +2630,7 @@ export function createOperatorRoutes(deps) {
2199
2630
  events: [errorEvent],
2200
2631
  draft: draftParsed,
2201
2632
  pending: null,
2202
- autoApply: false,
2633
+ autoApply: chatTurnBase.autoApply,
2203
2634
  model: resolvedModel,
2204
2635
  buildId: workshopChatRegistry.getPinnedBuildId(body.sessionId),
2205
2636
  });
@@ -2336,8 +2767,94 @@ export function createOperatorRoutes(deps) {
2336
2767
  })) {
2337
2768
  return true;
2338
2769
  }
2770
+ if (method === "GET" && pathname === "/api/skills/usage") {
2771
+ const projectRoot = url.searchParams.get("project_root") ?? undefined;
2772
+ let usageRoot;
2773
+ try {
2774
+ const { wireRoot } = projectRoot === undefined
2775
+ ? await resolveWritableCatalogRoot({ store, bootCwd: cwd }, undefined)
2776
+ : await resolveCatalogStartInput({ store, bootCwd: cwd }, projectRoot);
2777
+ usageRoot = wireRoot.path;
2778
+ }
2779
+ catch (err) {
2780
+ if (err instanceof CatalogPathError) {
2781
+ json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
2782
+ return true;
2783
+ }
2784
+ throw err;
2785
+ }
2786
+ const usages = await buildSkillUsageIndex(usageRoot);
2787
+ if (usages === null) {
2788
+ json(res, 404, { error: "No Stageflow catalog found" });
2789
+ return true;
2790
+ }
2791
+ json(res, 200, { usages });
2792
+ return true;
2793
+ }
2339
2794
  if (method === "GET" && pathname === "/api/skills") {
2340
- json(res, 200, await listSkills({ cwd, agentDir }));
2795
+ const projectRoot = url.searchParams.get("project_root") ?? undefined;
2796
+ try {
2797
+ const { wireRoot } = projectRoot === undefined
2798
+ ? await resolveWritableCatalogRoot({ store, bootCwd: cwd }, undefined)
2799
+ : await resolveCatalogStartInput({ store, bootCwd: cwd }, projectRoot);
2800
+ json(res, 200, await listSkills({ cwd: wireRoot.path, agentDir }));
2801
+ }
2802
+ catch (err) {
2803
+ if (err instanceof CatalogPathError) {
2804
+ json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
2805
+ return true;
2806
+ }
2807
+ throw err;
2808
+ }
2809
+ return true;
2810
+ }
2811
+ if (method === "POST" && pathname === "/api/skills/install") {
2812
+ let body;
2813
+ try {
2814
+ body = await readJsonBody(req);
2815
+ }
2816
+ catch {
2817
+ json(res, 400, { error: "Invalid JSON body" });
2818
+ return true;
2819
+ }
2820
+ const parsed = parseInstallSkillBody(body);
2821
+ if ("ok" in parsed) {
2822
+ json(res, parsed.status, { error: parsed.error });
2823
+ return true;
2824
+ }
2825
+ let skillWriteRoot;
2826
+ try {
2827
+ const { wireRoot: selected } = await resolveWritableCatalogRoot({ store, bootCwd: cwd }, undefined);
2828
+ skillWriteRoot = selected.path;
2829
+ }
2830
+ catch (err) {
2831
+ if (err instanceof CatalogPathError) {
2832
+ json(res, err.code === "catalog_root_read_only" ? 403 : 400, catalogPathErrorBody(err));
2833
+ return true;
2834
+ }
2835
+ throw err;
2836
+ }
2837
+ const stageflowCtx = await resolveStageflowContext(skillWriteRoot);
2838
+ if (stageflowCtx.manifestStatus !== "ok") {
2839
+ json(res, 400, {
2840
+ error: "Project root not found; initialize stageflow.yaml",
2841
+ });
2842
+ return true;
2843
+ }
2844
+ const result = await installSkillFromCommand({
2845
+ projectRoot: stageflowCtx.projectRoot,
2846
+ command: parsed.command,
2847
+ });
2848
+ if (!result.ok) {
2849
+ json(res, result.status ?? 400, { error: result.error });
2850
+ return true;
2851
+ }
2852
+ json(res, 201, {
2853
+ ok: true,
2854
+ skill_name: result.skill_name,
2855
+ dest_path: result.dest_path,
2856
+ project_root: result.project_root,
2857
+ });
2341
2858
  return true;
2342
2859
  }
2343
2860
  if (method === "GET" && pathname === "/api/extensions") {
@@ -2444,6 +2961,7 @@ export function createOperatorRoutes(deps) {
2444
2961
  }
2445
2962
  }
2446
2963
  };
2964
+ return Object.assign(handler, { dispose: () => liveView.dispose() });
2447
2965
  }
2448
2966
  export async function startUiServer(options) {
2449
2967
  const host = options.host ?? "127.0.0.1";
@@ -2492,6 +3010,8 @@ export async function startUiServer(options) {
2492
3010
  });
2493
3011
  envelope.server.on("close", () => {
2494
3012
  shutdown?.uninstall();
3013
+ if ("dispose" in routes)
3014
+ void routes.dispose();
2495
3015
  });
2496
3016
  return { ...envelope, shutdown };
2497
3017
  }