stageflow 0.25.0 → 0.27.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 (501) hide show
  1. package/README.md +10 -3
  2. package/dist/a2a/contracts.d.ts +1 -0
  3. package/dist/a2a/contracts.js +1 -0
  4. package/dist/a2a/registry.d.ts +2 -0
  5. package/dist/a2a/registry.js +20 -1
  6. package/dist/a2a/server.d.ts +7 -0
  7. package/dist/a2a/server.js +5 -2
  8. package/dist/a2a/service.d.ts +1 -1
  9. package/dist/a2a/service.js +57 -8
  10. package/dist/a2a/store.d.ts +8 -5
  11. package/dist/a2a/store.js +24 -67
  12. package/dist/agent/activity.d.ts +3 -0
  13. package/dist/agent/bootProviderConfig.d.ts +21 -0
  14. package/dist/agent/bootProviderConfig.js +74 -0
  15. package/dist/agent/piAdapter.d.ts +9 -1
  16. package/dist/agent/piAdapter.js +58 -3
  17. package/dist/agent/piIsolatedMcp.d.ts +2 -0
  18. package/dist/agent/piIsolatedMcp.js +10 -2
  19. package/dist/agent/port.d.ts +6 -0
  20. package/dist/agent/streamLogRedact.d.ts +1 -1
  21. package/dist/agent/streamLogRedact.js +4 -11
  22. package/dist/cli/artifactCommand.js +9 -16
  23. package/dist/cli/backupCommand.d.ts +9 -0
  24. package/dist/cli/backupCommand.js +111 -0
  25. package/dist/cli/debugRunCommand.d.ts +10 -0
  26. package/dist/cli/debugRunCommand.js +109 -0
  27. package/dist/cli/doctorCommand.d.ts +32 -0
  28. package/dist/cli/doctorCommand.js +453 -0
  29. package/dist/cli/envelopeCommand.js +9 -2
  30. package/dist/cli/exportAllCommand.d.ts +55 -0
  31. package/dist/cli/exportAllCommand.js +218 -0
  32. package/dist/cli/exportRunCommand.js +23 -21
  33. package/dist/cli/hostClient.d.ts +21 -3
  34. package/dist/cli/hostClient.js +93 -12
  35. package/dist/cli/initTemplates.d.ts +1 -1
  36. package/dist/cli/initTemplates.js +8 -1
  37. package/dist/cli/resolveSafeOutPath.d.ts +2 -0
  38. package/dist/cli/resolveSafeOutPath.js +40 -0
  39. package/dist/cli/restoreCommand.d.ts +10 -0
  40. package/dist/cli/restoreCommand.js +90 -0
  41. package/dist/cli/runCommand.d.ts +3 -2
  42. package/dist/cli/runCommand.js +93 -11
  43. package/dist/cli/runOutput.d.ts +4 -0
  44. package/dist/cli/runOutput.js +33 -7
  45. package/dist/cli/runStageCommand.d.ts +1 -0
  46. package/dist/cli/runStageCommand.js +44 -25
  47. package/dist/cli/runsCommand.d.ts +1 -1
  48. package/dist/cli/runsCommand.js +162 -8
  49. package/dist/cli.js +171 -30
  50. package/dist/config/catalogRelativePath.d.ts +62 -0
  51. package/dist/config/catalogRelativePath.js +118 -0
  52. package/dist/config/configOrigin.d.ts +27 -0
  53. package/dist/config/configOrigin.js +59 -0
  54. package/dist/config/hostConfig.d.ts +55 -0
  55. package/dist/config/hostConfig.js +333 -0
  56. package/dist/config/listSkills.d.ts +26 -0
  57. package/dist/config/listSkills.js +190 -3
  58. package/dist/config/loadOutcome.d.ts +1 -1
  59. package/dist/config/loadPipeline.js +23 -2
  60. package/dist/config/loadStage.js +18 -0
  61. package/dist/config/loadTask.d.ts +2 -0
  62. package/dist/config/loadTask.js +57 -18
  63. package/dist/config/materializeStageModels.js +5 -1
  64. package/dist/config/mergePipelineIncludes.d.ts +3 -0
  65. package/dist/config/mergePipelineIncludes.js +19 -0
  66. package/dist/config/migrateYaml.js +6 -11
  67. package/dist/config/multiProjectCatalog.d.ts +30 -0
  68. package/dist/config/multiProjectCatalog.js +142 -0
  69. package/dist/config/normalizePipelineStageEntry.js +20 -1
  70. package/dist/config/pipelineStageKeys.js +2 -0
  71. package/dist/config/resolveCatalogRoots.d.ts +27 -0
  72. package/dist/config/resolveCatalogRoots.js +76 -0
  73. package/dist/config/resolveModel.d.ts +7 -1
  74. package/dist/config/resolveModel.js +11 -1
  75. package/dist/config/resolveStageMcpServers.d.ts +9 -1
  76. package/dist/config/resolveStageMcpServers.js +36 -9
  77. package/dist/config/secretFromEnvOrFile.d.ts +9 -0
  78. package/dist/config/secretFromEnvOrFile.js +34 -0
  79. package/dist/config/seededCatalog.d.ts +11 -0
  80. package/dist/config/seededCatalog.js +29 -0
  81. package/dist/config/toolRequires.d.ts +24 -0
  82. package/dist/config/toolRequires.js +115 -0
  83. package/dist/config/validateCatalog.d.ts +5 -1
  84. package/dist/config/validateCatalog.js +140 -1
  85. package/dist/diagnostics/checks.d.ts +39 -0
  86. package/dist/diagnostics/checks.js +135 -0
  87. package/dist/errors/codes.d.ts +14 -0
  88. package/dist/errors/codes.js +59 -0
  89. package/dist/git/cache.d.ts +14 -0
  90. package/dist/git/cache.js +150 -0
  91. package/dist/git/credentials.d.ts +9 -0
  92. package/dist/git/credentials.js +88 -0
  93. package/dist/git/errors.d.ts +20 -0
  94. package/dist/git/errors.js +94 -0
  95. package/dist/git/exec.d.ts +16 -0
  96. package/dist/git/exec.js +135 -0
  97. package/dist/git/operations.d.ts +29 -0
  98. package/dist/git/operations.js +153 -0
  99. package/dist/git/version.d.ts +1 -0
  100. package/dist/git/version.js +10 -0
  101. package/dist/logging/audit.d.ts +16 -0
  102. package/dist/logging/audit.js +19 -0
  103. package/dist/logging/commonWords.d.ts +2 -0
  104. package/dist/logging/commonWords.js +13 -0
  105. package/dist/logging/logger.d.ts +49 -0
  106. package/dist/logging/logger.js +193 -0
  107. package/dist/logging/namedSecrets.d.ts +6 -0
  108. package/dist/logging/namedSecrets.js +58 -0
  109. package/dist/logging/redact.d.ts +13 -0
  110. package/dist/logging/redact.js +88 -0
  111. package/dist/mcp/catalogTools.js +456 -80
  112. package/dist/mcp/checkoutTools.d.ts +52 -0
  113. package/dist/mcp/checkoutTools.js +332 -0
  114. package/dist/mcp/controlTools.js +179 -5
  115. package/dist/mcp/deps.d.ts +1 -0
  116. package/dist/mcp/getStartedTool.d.ts +3 -0
  117. package/dist/mcp/getStartedTool.js +62 -0
  118. package/dist/mcp/readArtifact.d.ts +11 -0
  119. package/dist/mcp/readArtifact.js +46 -30
  120. package/dist/mcp/server.js +1 -0
  121. package/dist/mcp/tools.js +4 -0
  122. package/dist/net/proxy.d.ts +14 -0
  123. package/dist/net/proxy.js +78 -0
  124. package/dist/package-meta.d.ts +2 -1
  125. package/dist/package-meta.js +2 -1
  126. package/dist/preflight/bash.d.ts +8 -0
  127. package/dist/preflight/bash.js +34 -0
  128. package/dist/preflight/claudeRoot.d.ts +11 -0
  129. package/dist/preflight/claudeRoot.js +23 -0
  130. package/dist/preflight/mcpCommands.d.ts +8 -0
  131. package/dist/preflight/mcpCommands.js +36 -0
  132. package/dist/preflight/pipelinePreflight.d.ts +40 -0
  133. package/dist/preflight/pipelinePreflight.js +192 -0
  134. package/dist/preflight/tls.d.ts +6 -0
  135. package/dist/preflight/tls.js +18 -0
  136. package/dist/preflight/toolchain.d.ts +45 -0
  137. package/dist/preflight/toolchain.js +219 -0
  138. package/dist/project/findProjectRoot.js +3 -4
  139. package/dist/project/globalHome.d.ts +7 -0
  140. package/dist/project/globalHome.js +63 -2
  141. package/dist/project/resolveProjectContext.js +2 -2
  142. package/dist/project/resolveStageflowContext.js +2 -2
  143. package/dist/projection/projectRun.d.ts +2 -1
  144. package/dist/projection/projectRun.js +1 -0
  145. package/dist/runstore/assertTmpdir.d.ts +1 -0
  146. package/dist/runstore/assertTmpdir.js +21 -0
  147. package/dist/runstore/backup.d.ts +52 -0
  148. package/dist/runstore/backup.js +292 -0
  149. package/dist/runstore/createStore.d.ts +18 -1
  150. package/dist/runstore/createStore.js +64 -5
  151. package/dist/runstore/debugBundle.d.ts +60 -0
  152. package/dist/runstore/debugBundle.js +143 -0
  153. package/dist/runstore/diskUsage.d.ts +50 -0
  154. package/dist/runstore/diskUsage.js +214 -0
  155. package/dist/runstore/exportRunPayload.d.ts +8 -0
  156. package/dist/runstore/exportRunPayload.js +23 -0
  157. package/dist/runstore/normalizeCatalogPath.d.ts +1 -0
  158. package/dist/runstore/normalizeCatalogPath.js +10 -0
  159. package/dist/runstore/paths.d.ts +5 -0
  160. package/dist/runstore/paths.js +145 -1
  161. package/dist/runstore/port.d.ts +111 -13
  162. package/dist/runstore/port.js +8 -1
  163. package/dist/runstore/restore.d.ts +81 -0
  164. package/dist/runstore/restore.js +355 -0
  165. package/dist/runstore/retention.d.ts +27 -0
  166. package/dist/runstore/retention.js +74 -0
  167. package/dist/runstore/runManifest.d.ts +146 -0
  168. package/dist/runstore/runManifest.js +288 -0
  169. package/dist/runstore/runProjection.d.ts +3 -1
  170. package/dist/runstore/runProjection.js +63 -1
  171. package/dist/runstore/sqlite/SqliteRunStore.d.ts +28 -3
  172. package/dist/runstore/sqlite/SqliteRunStore.js +352 -219
  173. package/dist/runstore/sqlite/applyStorePragmas.d.ts +39 -0
  174. package/dist/runstore/sqlite/applyStorePragmas.js +145 -0
  175. package/dist/runstore/sqlite/migrations/001-baseline.d.ts +7 -0
  176. package/dist/runstore/sqlite/migrations/001-baseline.js +10 -0
  177. package/dist/runstore/sqlite/migrations/002-repository-binding.d.ts +7 -0
  178. package/dist/runstore/sqlite/migrations/002-repository-binding.js +29 -0
  179. package/dist/runstore/sqlite/migrations/003-run-lifecycle.d.ts +7 -0
  180. package/dist/runstore/sqlite/migrations/003-run-lifecycle.js +28 -0
  181. package/dist/runstore/sqlite/migrations/004-auto-resume-count.d.ts +7 -0
  182. package/dist/runstore/sqlite/migrations/004-auto-resume-count.js +19 -0
  183. package/dist/runstore/sqlite/migrations/005-config-origins.d.ts +7 -0
  184. package/dist/runstore/sqlite/migrations/005-config-origins.js +19 -0
  185. package/dist/runstore/sqlite/migrations/006-pipeline-body-and-caller.d.ts +7 -0
  186. package/dist/runstore/sqlite/migrations/006-pipeline-body-and-caller.js +28 -0
  187. package/dist/runstore/sqlite/migrations/007-projects-registry.d.ts +7 -0
  188. package/dist/runstore/sqlite/migrations/007-projects-registry.js +31 -0
  189. package/dist/runstore/sqlite/migrations/index.d.ts +21 -0
  190. package/dist/runstore/sqlite/migrations/index.js +72 -0
  191. package/dist/runstore/sqlite/schema.d.ts +20 -1
  192. package/dist/runstore/sqlite/schema.js +277 -1
  193. package/dist/runstore/sqlite/storeOpenError.d.ts +6 -0
  194. package/dist/runstore/sqlite/storeOpenError.js +11 -0
  195. package/dist/runstore/sqlite/storeSchemaError.d.ts +5 -0
  196. package/dist/runstore/sqlite/storeSchemaError.js +8 -0
  197. package/dist/runstore/stageExecution.js +3 -0
  198. package/dist/runstore/storeFilesystem.d.ts +32 -0
  199. package/dist/runstore/storeFilesystem.js +148 -0
  200. package/dist/runstore/trackProjection.js +2 -0
  201. package/dist/runstore/workspaceLayout.d.ts +13 -0
  202. package/dist/runstore/workspaceLayout.js +76 -0
  203. package/dist/runtime/completionCheckRunner.d.ts +3 -0
  204. package/dist/runtime/completionCheckRunner.js +45 -7
  205. package/dist/runtime/containerLimits.d.ts +16 -0
  206. package/dist/runtime/containerLimits.js +84 -0
  207. package/dist/runtime/credentialMaterialisation.d.ts +28 -0
  208. package/dist/runtime/credentialMaterialisation.js +128 -0
  209. package/dist/runtime/gitCheckoutCapability.js +7 -13
  210. package/dist/runtime/pipelineRunner.d.ts +36 -1
  211. package/dist/runtime/pipelineRunner.js +178 -17
  212. package/dist/runtime/pipelineScheduler.d.ts +15 -0
  213. package/dist/runtime/pipelineScheduler.js +135 -27
  214. package/dist/runtime/reloadRunCatalog.js +6 -1
  215. package/dist/runtime/repositoryMaterialize.d.ts +47 -0
  216. package/dist/runtime/repositoryMaterialize.js +235 -0
  217. package/dist/runtime/resumeTimedOut.d.ts +1 -1
  218. package/dist/runtime/resumeTimedOut.js +5 -2
  219. package/dist/runtime/runChangeBus.js +9 -0
  220. package/dist/runtime/runDeletion.d.ts +7 -0
  221. package/dist/runtime/runDeletion.js +37 -0
  222. package/dist/runtime/runManager.d.ts +158 -4
  223. package/dist/runtime/runManager.js +1596 -79
  224. package/dist/runtime/runRetentionSweep.d.ts +31 -0
  225. package/dist/runtime/runRetentionSweep.js +328 -0
  226. package/dist/runtime/runRetryCoordinator.d.ts +2 -0
  227. package/dist/runtime/runRetryCoordinator.js +19 -2
  228. package/dist/runtime/runSkills.d.ts +40 -0
  229. package/dist/runtime/runSkills.js +173 -0
  230. package/dist/runtime/stageAttemptBootstrap.d.ts +5 -1
  231. package/dist/runtime/stageAttemptBootstrap.js +195 -24
  232. package/dist/runtime/stageCacheEnv.d.ts +6 -0
  233. package/dist/runtime/stageCacheEnv.js +34 -0
  234. package/dist/runtime/stageConcurrency.d.ts +2 -1
  235. package/dist/runtime/stageConcurrency.js +4 -2
  236. package/dist/runtime/stageEnvironment.d.ts +38 -0
  237. package/dist/runtime/stageEnvironment.js +193 -0
  238. package/dist/runtime/stageProcessLauncher.d.ts +18 -0
  239. package/dist/runtime/stageProcessLauncher.js +194 -38
  240. package/dist/runtime/stageRecovery.d.ts +3 -1
  241. package/dist/runtime/stageRecovery.js +19 -4
  242. package/dist/runtime/stageRoots.d.ts +38 -1
  243. package/dist/runtime/stageRoots.js +118 -0
  244. package/dist/runtime/stageRunner.d.ts +1 -0
  245. package/dist/runtime/stageRunner.js +3 -1
  246. package/dist/runtime/stageSecretDecl.d.ts +9 -0
  247. package/dist/runtime/stageSecretDecl.js +102 -0
  248. package/dist/runtime/stageSecrets.d.ts +40 -0
  249. package/dist/runtime/stageSecrets.js +181 -0
  250. package/dist/runtime/stageWorker.d.ts +3 -2
  251. package/dist/runtime/stageWorker.js +58 -10
  252. package/dist/runtime/stageWorkerProtocol.d.ts +5 -0
  253. package/dist/runtime/stageWorkerProtocol.js +8 -0
  254. package/dist/runtime/startPayload.d.ts +32 -0
  255. package/dist/runtime/startPayload.js +59 -0
  256. package/dist/runtime/taskInput.js +14 -5
  257. package/dist/runtime/verifiedStageExecution.d.ts +1 -0
  258. package/dist/runtime/verifiedStageExecution.js +2 -1
  259. package/dist/runtime/workspaceBinding.d.ts +20 -0
  260. package/dist/runtime/workspaceBinding.js +60 -0
  261. package/dist/server/allowedHosts.d.ts +19 -0
  262. package/dist/server/allowedHosts.js +179 -0
  263. package/dist/server/bootstrap.d.ts +36 -2
  264. package/dist/server/bootstrap.js +209 -5
  265. package/dist/server/controlToken.d.ts +47 -0
  266. package/dist/server/controlToken.js +241 -0
  267. package/dist/server/createHttpHost.d.ts +11 -2
  268. package/dist/server/createHttpHost.js +103 -10
  269. package/dist/server/ensureGlobalService.d.ts +5 -1
  270. package/dist/server/ensureGlobalService.js +35 -1
  271. package/dist/server/healthSurfaces.d.ts +6 -0
  272. package/dist/server/healthSurfaces.js +98 -0
  273. package/dist/server/http.d.ts +12 -1
  274. package/dist/server/http.js +1085 -485
  275. package/dist/server/listenHost.d.ts +5 -0
  276. package/dist/server/listenHost.js +56 -0
  277. package/dist/server/mcpHost.d.ts +10 -1
  278. package/dist/server/mcpHost.js +36 -6
  279. package/dist/server/operatorResults.d.ts +2 -2
  280. package/dist/server/operatorResults.js +40 -29
  281. package/dist/server/requestAuthContext.d.ts +12 -0
  282. package/dist/server/requestAuthContext.js +21 -0
  283. package/dist/server/shutdown.d.ts +69 -0
  284. package/dist/server/shutdown.js +219 -0
  285. package/dist/types/pipeline.d.ts +7 -0
  286. package/dist/types/stage.d.ts +8 -0
  287. package/dist/types/task.d.ts +8 -0
  288. package/dist/ui/assets/index-B6hlSXXn.js +120 -0
  289. package/dist/ui/index.html +1 -1
  290. package/examples/README.md +98 -0
  291. package/examples/archify-on-pr/README.md +213 -0
  292. package/examples/archify-on-pr/archify-on-pr.pipeline.yaml +19 -0
  293. package/examples/archify-on-pr/archify-on-pr.task.yaml +19 -0
  294. package/examples/archify-on-pr/author-diagrams.yaml +106 -0
  295. package/examples/archify-on-pr/detect-changes.yaml +89 -0
  296. package/examples/ci-validate/README.md +35 -0
  297. package/examples/ci-validate/check.yaml +13 -0
  298. package/examples/ci-validate/ci-demo.pipeline.yaml +4 -0
  299. package/examples/ci-validate/validate.sh +5 -0
  300. package/examples/compliance-review-demo/README.md +125 -0
  301. package/examples/compliance-review-demo/compliance-review-demo.pipeline.yaml +29 -0
  302. package/examples/compliance-review-demo/connect-account.yaml +47 -0
  303. package/examples/compliance-review-demo/map-to-control.yaml +123 -0
  304. package/examples/compliance-review-demo/poll-bucket-config.yaml +103 -0
  305. package/examples/compliance-review-demo/publish-report.yaml +79 -0
  306. package/examples/compliance-review-demo/review-verdict.yaml +122 -0
  307. package/examples/compliance-review-demo/run-compliance-check.yaml +96 -0
  308. package/examples/compliance-review-demo/store-evidence.yaml +143 -0
  309. package/examples/conditional-fork/README.md +89 -0
  310. package/examples/conditional-fork/branch-a.yaml +13 -0
  311. package/examples/conditional-fork/branch-b.yaml +13 -0
  312. package/examples/conditional-fork/decide.yaml +35 -0
  313. package/examples/conditional-fork/fork-demo.pipeline.yaml +20 -0
  314. package/examples/conditional-fork/fork-demo.task.yaml +8 -0
  315. package/examples/context7-mcp/README.md +42 -0
  316. package/examples/context7-mcp/context7-mcp.pipeline.yaml +15 -0
  317. package/examples/context7-mcp/context7-mcp.task.yaml +4 -0
  318. package/examples/context7-mcp/fetch-docs.yaml +52 -0
  319. package/examples/context7-mcp/resolve-library.yaml +31 -0
  320. package/examples/context7-mcp/write-brief.yaml +52 -0
  321. package/examples/feature-loop/README.md +104 -0
  322. package/examples/feature-loop/address-feedback.yaml +148 -0
  323. package/examples/feature-loop/align.yaml +116 -0
  324. package/examples/feature-loop/decompose.yaml +86 -0
  325. package/examples/feature-loop/default-model-hierarchy.task.yaml +58 -0
  326. package/examples/feature-loop/feature-loop.pipeline.yaml +140 -0
  327. package/examples/feature-loop/feature-loop.task.yaml +25 -0
  328. package/examples/feature-loop/implement.yaml +128 -0
  329. package/examples/feature-loop/plan.yaml +111 -0
  330. package/examples/feature-loop/publish.yaml +167 -0
  331. package/examples/feature-loop/review.yaml +146 -0
  332. package/examples/feature-loop/verify.yaml +106 -0
  333. package/examples/feedback-loop/README.md +72 -0
  334. package/examples/feedback-loop/VERIFY.md +208 -0
  335. package/examples/feedback-loop/continue-only.task.yaml +4 -0
  336. package/examples/feedback-loop/exhaust-require-continue.task.yaml +6 -0
  337. package/examples/feedback-loop/exhaust-wait-human.task.yaml +11 -0
  338. package/examples/feedback-loop/feedback-loop-wait-human.pipeline.yaml +23 -0
  339. package/examples/feedback-loop/feedback-loop.pipeline.yaml +23 -0
  340. package/examples/feedback-loop/feedback-loop.task.yaml +4 -0
  341. package/examples/feedback-loop/implement.yaml +14 -0
  342. package/examples/feedback-loop/plan.yaml +14 -0
  343. package/examples/feedback-loop/review.yaml +15 -0
  344. package/examples/feedback-loop/send-back-once.task.yaml +6 -0
  345. package/examples/feedback-loop/submit.yaml +12 -0
  346. package/examples/generic-fan-in/README.md +151 -0
  347. package/examples/generic-fan-in/clarify.yaml +18 -0
  348. package/examples/generic-fan-in/generic-fan-in-accepted.pipeline.yaml +19 -0
  349. package/examples/generic-fan-in/generic-fan-in-accepted.task.yaml +8 -0
  350. package/examples/generic-fan-in/generic-fan-in.pipeline.yaml +18 -0
  351. package/examples/generic-fan-in/generic-fan-in.task.yaml +9 -0
  352. package/examples/generic-fan-in/research.yaml +45 -0
  353. package/examples/generic-fan-in/synthesize.yaml +66 -0
  354. package/examples/generic-fan-in/validation.yaml +45 -0
  355. package/examples/github-release/README.md +78 -0
  356. package/examples/github-release/draft-release-notes.yaml +64 -0
  357. package/examples/github-release/github-release.pipeline.yaml +11 -0
  358. package/examples/github-release/github-release.task.yaml +13 -0
  359. package/examples/github-release/publish-github-release.yaml +64 -0
  360. package/examples/hello-world/README.md +28 -0
  361. package/examples/hello-world/hello.pipeline.yaml +4 -0
  362. package/examples/hello-world/my-task.task.yaml +6 -0
  363. package/examples/hello-world/research.yaml +18 -0
  364. package/examples/mcp-hitl-tour/README.md +113 -0
  365. package/examples/mcp-hitl-tour/draft-angle.yaml +33 -0
  366. package/examples/mcp-hitl-tour/draft-short.yaml +32 -0
  367. package/examples/mcp-hitl-tour/emit-angles.yaml +45 -0
  368. package/examples/mcp-hitl-tour/gather.yaml +31 -0
  369. package/examples/mcp-hitl-tour/intake.yaml +62 -0
  370. package/examples/mcp-hitl-tour/mcp-hitl-tour-exhaust.task.yaml +13 -0
  371. package/examples/mcp-hitl-tour/mcp-hitl-tour.pipeline.yaml +54 -0
  372. package/examples/mcp-hitl-tour/mcp-hitl-tour.task.yaml +16 -0
  373. package/examples/mcp-hitl-tour/polish.yaml +33 -0
  374. package/examples/mcp-hitl-tour/review.yaml +17 -0
  375. package/examples/mcp-hitl-tour/ship.yaml +25 -0
  376. package/examples/oss-issue-contribution/README.md +218 -0
  377. package/examples/oss-issue-contribution/oss-address-review-feedback.yaml +117 -0
  378. package/examples/oss-issue-contribution/oss-approve-contribution.yaml +181 -0
  379. package/examples/oss-issue-contribution/oss-approve-plan.yaml +111 -0
  380. package/examples/oss-issue-contribution/oss-explain-fix.yaml +108 -0
  381. package/examples/oss-issue-contribution/oss-explain-issue.yaml +114 -0
  382. package/examples/oss-issue-contribution/oss-implement-source-fix.yaml +171 -0
  383. package/examples/oss-issue-contribution/oss-investigate-area.yaml +116 -0
  384. package/examples/oss-issue-contribution/oss-issue-contribution.pipeline.yaml +125 -0
  385. package/examples/oss-issue-contribution/oss-issue-intake.yaml +106 -0
  386. package/examples/oss-issue-contribution/oss-plan-investigation.yaml +112 -0
  387. package/examples/oss-issue-contribution/oss-plan-review.yaml +109 -0
  388. package/examples/oss-issue-contribution/oss-reproduce-issue.yaml +140 -0
  389. package/examples/oss-issue-contribution/oss-review-change.yaml +107 -0
  390. package/examples/oss-issue-contribution/oss-verify-fix.yaml +130 -0
  391. package/examples/oss-issue-contribution/oss-write-regression-test.yaml +135 -0
  392. package/examples/oss-issue-contribution/pi-7629.task.yaml +79 -0
  393. package/examples/oss-issue-contribution/pi-8919.task.yaml +75 -0
  394. package/examples/oss-issue-contribution/pydantic-ai-7971.task.yaml +53 -0
  395. package/examples/oss-quick-fix/README.md +65 -0
  396. package/examples/oss-quick-fix/implement-fix.yaml +84 -0
  397. package/examples/oss-quick-fix/oss-quick-fix.pipeline.yaml +35 -0
  398. package/examples/oss-quick-fix/oss-quick-fix.task.yaml +19 -0
  399. package/examples/oss-quick-fix/ship-fix.yaml +81 -0
  400. package/examples/oss-quick-fix/understand-issue.yaml +67 -0
  401. package/examples/oss-quick-fix/verify-fix.yaml +73 -0
  402. package/examples/plan-review/README.md +37 -0
  403. package/examples/plan-review/plan-review-followup.yaml +13 -0
  404. package/examples/plan-review/plan-review.pipeline.yaml +9 -0
  405. package/examples/plan-review/plan-review.task.yaml +9 -0
  406. package/examples/plan-review/plan-review.yaml +25 -0
  407. package/examples/playwright-mcp/README.md +39 -0
  408. package/examples/playwright-mcp/playwright-mcp.pipeline.yaml +5 -0
  409. package/examples/playwright-mcp/playwright-mcp.task.yaml +4 -0
  410. package/examples/playwright-mcp/screenshot.yaml +40 -0
  411. package/examples/retry-tour/README.md +75 -0
  412. package/examples/retry-tour/retry-tour.pipeline.yaml +122 -0
  413. package/examples/retry-tour/retry-tour.task.yaml +9 -0
  414. package/examples/route-if-tour/README.md +202 -0
  415. package/examples/route-if-tour/route-if-tour.pipeline.yaml +950 -0
  416. package/examples/route-if-tour/route-if-tour.task.yaml +4 -0
  417. package/examples/route-wiring-smoke-test/01-core-routing.pipeline.yaml +168 -0
  418. package/examples/route-wiring-smoke-test/02-on-gating.pipeline.yaml +44 -0
  419. package/examples/route-wiring-smoke-test/03-two-entry-points.pipeline.yaml +28 -0
  420. package/examples/route-wiring-smoke-test/04-entry-false-equivalent-to-omitted.pipeline.yaml +27 -0
  421. package/examples/route-wiring-smoke-test/05-fork-choice.pipeline.yaml +110 -0
  422. package/examples/route-wiring-smoke-test/06-fan-out-fan-in.pipeline.yaml +82 -0
  423. package/examples/route-wiring-smoke-test/07-loop-human-decision.pipeline.yaml +82 -0
  424. package/examples/route-wiring-smoke-test/08-skip-fallback-and-multi-on-gate.pipeline.yaml +77 -0
  425. package/examples/route-wiring-smoke-test/09-multi-loop-targets.pipeline.yaml +102 -0
  426. package/examples/route-wiring-smoke-test/10-uses-dialect-fork.pipeline.yaml +23 -0
  427. package/examples/route-wiring-smoke-test/11-sequential-io-handoff.pipeline.yaml +37 -0
  428. package/examples/route-wiring-smoke-test/12-complex-io-schemas.pipeline.yaml +138 -0
  429. package/examples/route-wiring-smoke-test/13-ref-io-handoff.pipeline.yaml +84 -0
  430. package/examples/route-wiring-smoke-test/14-if-eq-gating.pipeline.yaml +50 -0
  431. package/examples/route-wiring-smoke-test/15-if-all-gated.pipeline.yaml +37 -0
  432. package/examples/route-wiring-smoke-test/16-if-composition.pipeline.yaml +65 -0
  433. package/examples/route-wiring-smoke-test/17-if-join.pipeline.yaml +80 -0
  434. package/examples/route-wiring-smoke-test/README.md +377 -0
  435. package/examples/route-wiring-smoke-test/rejected/01-reject-legacy-needs.pipeline.yaml +26 -0
  436. package/examples/route-wiring-smoke-test/rejected/02-reject-legacy-fork.pipeline.yaml +25 -0
  437. package/examples/route-wiring-smoke-test/rejected/03-reject-legacy-feedback-loop.pipeline.yaml +30 -0
  438. package/examples/route-wiring-smoke-test/rejected/04-reject-missing-entry.pipeline.yaml +24 -0
  439. package/examples/route-wiring-smoke-test/rejected/05-reject-unreachable.pipeline.yaml +35 -0
  440. package/examples/route-wiring-smoke-test/rejected/06-reject-allow-none-without-route-select.pipeline.yaml +37 -0
  441. package/examples/route-wiring-smoke-test/rejected/07-reject-cycle.pipeline.yaml +27 -0
  442. package/examples/route-wiring-smoke-test/rejected/08-reject-loop-non-ancestor.pipeline.yaml +42 -0
  443. package/examples/route-wiring-smoke-test/rejected/09-reject-route-select-leaf.pipeline.yaml +26 -0
  444. package/examples/route-wiring-smoke-test/rejected/12-reject-loop-replay-unsafe.pipeline.yaml +55 -0
  445. package/examples/route-wiring-smoke-test/rejected/13-reject-multiple-loop-entries.pipeline.yaml +51 -0
  446. package/examples/route-wiring-smoke-test/rejected/14-reject-duplicate-stage-id.pipeline.yaml +35 -0
  447. package/examples/route-wiring-smoke-test/rejected/15-reject-unknown-route-target.pipeline.yaml +15 -0
  448. package/examples/route-wiring-smoke-test/rejected/16-reject-malformed-route-item.pipeline.yaml +15 -0
  449. package/examples/route-wiring-smoke-test/rejected/17-reject-io-incompatible.pipeline.yaml +33 -0
  450. package/examples/route-wiring-smoke-test/rejected/18-reject-nested-io.pipeline.yaml +51 -0
  451. package/examples/route-wiring-smoke-test/rejected/19-reject-array-item-io.pipeline.yaml +55 -0
  452. package/examples/route-wiring-smoke-test/rejected/20-reject-closed-io.pipeline.yaml +36 -0
  453. package/examples/route-wiring-smoke-test/rejected/21-reject-ref-io.pipeline.yaml +42 -0
  454. package/examples/route-wiring-smoke-test/rejected/22-reject-if-unknown-field.pipeline.yaml +33 -0
  455. package/examples/route-wiring-smoke-test/rejected/23-reject-if-optional-field.pipeline.yaml +32 -0
  456. package/examples/route-wiring-smoke-test/rejected/24-reject-if-empty-all.pipeline.yaml +31 -0
  457. package/examples/route-wiring-smoke-test/rejected/25-reject-if-gt-on-string.pipeline.yaml +33 -0
  458. package/examples/route-wiring-smoke-test/rejected/26-reject-if-optional-nested.pipeline.yaml +36 -0
  459. package/examples/route-wiring-smoke-test/rejected/27-reject-if-on-loop.pipeline.yaml +63 -0
  460. package/examples/route-wiring-smoke-test/rejected/29-reject-if-on-failed.pipeline.yaml +46 -0
  461. package/examples/route-wiring-smoke-test/smoke-test.task.yaml +4 -0
  462. package/examples/route-wiring-smoke-test/stages/branch-a.yaml +12 -0
  463. package/examples/route-wiring-smoke-test/stages/branch-b.yaml +12 -0
  464. package/examples/route-wiring-smoke-test/stages/decide.yaml +12 -0
  465. package/examples/route-wiring-smoke-test/stages/finish.yaml +12 -0
  466. package/examples/ship-feature/README.md +190 -0
  467. package/examples/ship-feature/feat-address-feedback.yaml +119 -0
  468. package/examples/ship-feature/feat-implement.yaml +180 -0
  469. package/examples/ship-feature/feat-plan.yaml +120 -0
  470. package/examples/ship-feature/feat-review.yaml +109 -0
  471. package/examples/ship-feature/feat-ship.yaml +203 -0
  472. package/examples/ship-feature/ship-feature.pipeline.yaml +44 -0
  473. package/examples/ship-feature/ship-feature.task.yaml +78 -0
  474. package/examples/stage-mcp/README.md +144 -0
  475. package/examples/stage-mcp/echo-mcp.mjs +20 -0
  476. package/examples/stage-mcp/stage-mcp-dead.pipeline.yaml +5 -0
  477. package/examples/stage-mcp/stage-mcp-dead.task.yaml +3 -0
  478. package/examples/stage-mcp/stage-mcp-missing-var.pipeline.yaml +5 -0
  479. package/examples/stage-mcp/stage-mcp-missing-var.task.yaml +3 -0
  480. package/examples/stage-mcp/stage-mcp.pipeline.yaml +5 -0
  481. package/examples/stage-mcp/stage-mcp.task.yaml +4 -0
  482. package/examples/stage-mcp/use-echo.yaml +20 -0
  483. package/examples/verify-tour/README.md +80 -0
  484. package/examples/verify-tour/verify-tour.pipeline.yaml +162 -0
  485. package/examples/verify-tour/verify-tour.task.yaml +11 -0
  486. package/package.json +5 -1
  487. package/skills/stageflow/SKILL.md +6 -6
  488. package/skills/stageflow/references/control-surface.md +11 -5
  489. package/skills/stageflow/scripts/detect-host.mjs +14 -1
  490. package/skills/stageflow/scripts/detect-host.test.mjs +29 -1
  491. package/skills/stageflow-author/SKILL.md +4 -3
  492. package/skills/stageflow-author/references/catalog-mapping.md +16 -0
  493. package/skills/stageflow-author/references/catalog-write-conventions.md +1 -1
  494. package/skills/stageflow-run/SKILL.md +20 -12
  495. package/skills/stageflow-run/references/mcp-call.md +9 -1
  496. package/skills/stageflow-run/references/task-and-pipeline-selection.md +16 -5
  497. package/skills/stageflow-run/scripts/mcp-call.mjs +46 -6
  498. package/skills/stageflow-run/scripts/mcp-call.test.mjs +64 -0
  499. package/skills/stageflow-session-capture/references/catalog-authoring.md +2 -0
  500. package/skills/stageflow-setup/SKILL.md +40 -8
  501. package/dist/ui/assets/index-Da5EHU6M.js +0 -119
@@ -0,0 +1,20 @@
1
+ import { type LoadOutcome } from "../config/loadOutcome.js";
2
+ export type WorkspaceBinding = {
3
+ kind: "repository";
4
+ repository: string;
5
+ ref: string;
6
+ } | {
7
+ kind: "checkout";
8
+ path: string;
9
+ } | {
10
+ kind: "unbound";
11
+ };
12
+ export type WorkspaceBindingInput = {
13
+ repository?: string;
14
+ ref?: string;
15
+ checkout?: string;
16
+ };
17
+ export type ResolveWorkspaceBindingOptions = {
18
+ checkoutOverride?: string;
19
+ };
20
+ export declare function resolveWorkspaceBinding(task: WorkspaceBindingInput, options?: ResolveWorkspaceBindingOptions): LoadOutcome<WorkspaceBinding>;
@@ -0,0 +1,60 @@
1
+ import { loadFailure, loadSuccess } from "../config/loadOutcome.js";
2
+ const OWNER_REPO_RE = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
3
+ function nonEmptyTrimmed(value) {
4
+ if (value === undefined)
5
+ return undefined;
6
+ const trimmed = value.trim();
7
+ return trimmed.length > 0 ? trimmed : undefined;
8
+ }
9
+ function isGithubOwnerRepo(repository) {
10
+ return OWNER_REPO_RE.test(repository);
11
+ }
12
+ export function resolveWorkspaceBinding(task, options) {
13
+ const repository = nonEmptyTrimmed(task.repository);
14
+ const ref = nonEmptyTrimmed(task.ref);
15
+ const checkoutPath = nonEmptyTrimmed(options?.checkoutOverride ?? task.checkout);
16
+ const hasRepositoryField = repository !== undefined;
17
+ if (hasRepositoryField && checkoutPath !== undefined) {
18
+ return loadFailure([
19
+ {
20
+ code: "task.binding_conflict",
21
+ message: "Task cannot declare both repository and checkout (exactly one workspace binding)",
22
+ category: "task",
23
+ },
24
+ ]);
25
+ }
26
+ if (hasRepositoryField && ref === undefined) {
27
+ return loadFailure([
28
+ {
29
+ code: "task.repository_ref_required",
30
+ message: "Task repository requires a non-empty ref",
31
+ category: "task",
32
+ },
33
+ ]);
34
+ }
35
+ if (ref !== undefined && repository === undefined) {
36
+ return loadFailure([
37
+ {
38
+ code: "task.ref_without_repository",
39
+ message: "Task ref requires repository",
40
+ category: "task",
41
+ },
42
+ ]);
43
+ }
44
+ if (repository !== undefined && ref !== undefined) {
45
+ if (!isGithubOwnerRepo(repository)) {
46
+ return loadFailure([
47
+ {
48
+ code: "task.repository_invalid",
49
+ message: "Task repository must be a GitHub owner/repo (e.g. acme/api); hosts and URLs are not accepted",
50
+ category: "task",
51
+ },
52
+ ]);
53
+ }
54
+ return loadSuccess({ kind: "repository", repository, ref });
55
+ }
56
+ if (checkoutPath !== undefined) {
57
+ return loadSuccess({ kind: "checkout", path: checkoutPath });
58
+ }
59
+ return loadSuccess({ kind: "unbound" });
60
+ }
@@ -0,0 +1,19 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ export type AllowedHostEntry = {
3
+ hostname: string;
4
+ port?: string;
5
+ };
6
+ export type AllowedHosts = {
7
+ entries: AllowedHostEntry[];
8
+ };
9
+ export declare function hostnameFromHostHeader(hostHeader: string): string;
10
+ export declare function portFromHostHeader(hostHeader: string): string | undefined;
11
+ export declare function isLoopbackHostname(hostname: string): boolean;
12
+ export declare function isLoopbackRemoteAddress(addr: string | undefined): boolean;
13
+ /** Loopback peer plus Host (and Origin, when present) — trusted local plane for ensure. */
14
+ export declare function isTrustedLocalHttpRequest(req: IncomingMessage): boolean;
15
+ export declare function resolveAllowedHosts(env?: NodeJS.ProcessEnv): AllowedHosts;
16
+ export declare function isHostAllowed(allowed: AllowedHosts, hostname: string, port?: string): boolean;
17
+ export declare function assertAllowedHttpAccess(allowed: AllowedHosts, req: IncomingMessage, res: ServerResponse, options?: {
18
+ requireOrigin?: boolean;
19
+ }): boolean;
@@ -0,0 +1,179 @@
1
+ import { isIP } from "node:net";
2
+ function writeForbidden(res, body) {
3
+ const payload = JSON.stringify(body);
4
+ res.writeHead(403, {
5
+ "Content-Type": "application/json; charset=utf-8",
6
+ "Content-Length": Buffer.byteLength(payload),
7
+ });
8
+ res.end(payload);
9
+ }
10
+ export function hostnameFromHostHeader(hostHeader) {
11
+ if (hostHeader.startsWith("[")) {
12
+ const end = hostHeader.indexOf("]");
13
+ return end === -1 ? hostHeader : hostHeader.slice(1, end);
14
+ }
15
+ const colon = hostHeader.lastIndexOf(":");
16
+ if (colon > 0 && /^\d+$/.test(hostHeader.slice(colon + 1))) {
17
+ return hostHeader.slice(0, colon);
18
+ }
19
+ return hostHeader;
20
+ }
21
+ export function portFromHostHeader(hostHeader) {
22
+ if (hostHeader.startsWith("[")) {
23
+ const end = hostHeader.indexOf("]");
24
+ if (end === -1)
25
+ return undefined;
26
+ const rest = hostHeader.slice(end + 1);
27
+ if (rest.startsWith(":") && /^\d+$/.test(rest.slice(1))) {
28
+ return rest.slice(1);
29
+ }
30
+ return undefined;
31
+ }
32
+ const colon = hostHeader.lastIndexOf(":");
33
+ if (colon > 0 && /^\d+$/.test(hostHeader.slice(colon + 1))) {
34
+ return hostHeader.slice(colon + 1);
35
+ }
36
+ return undefined;
37
+ }
38
+ export function isLoopbackHostname(hostname) {
39
+ const h = hostname.toLowerCase();
40
+ if (h === "localhost" || h === "::1")
41
+ return true;
42
+ if (isIP(h) === 4) {
43
+ const first = Number(h.split(".")[0]);
44
+ return first === 127;
45
+ }
46
+ return false;
47
+ }
48
+ export function isLoopbackRemoteAddress(addr) {
49
+ if (addr === undefined || addr.length === 0)
50
+ return false;
51
+ let normalized = addr;
52
+ if (normalized.toLowerCase().startsWith("::ffff:")) {
53
+ normalized = normalized.slice(7);
54
+ }
55
+ if (normalized === "::1")
56
+ return true;
57
+ if (isIP(normalized) === 4) {
58
+ const first = Number(normalized.split(".")[0]);
59
+ return first === 127;
60
+ }
61
+ return false;
62
+ }
63
+ /** Loopback peer plus Host (and Origin, when present) — trusted local plane for ensure. */
64
+ export function isTrustedLocalHttpRequest(req) {
65
+ const hostHeader = req.headers.host;
66
+ if (typeof hostHeader !== "string" || hostHeader.length === 0)
67
+ return false;
68
+ if (!isLoopbackHostname(hostnameFromHostHeader(hostHeader)))
69
+ return false;
70
+ const origin = req.headers.origin;
71
+ if (typeof origin === "string" && origin.length > 0) {
72
+ try {
73
+ const originUrl = new URL(origin);
74
+ if (!isLoopbackHostname(originUrl.hostname))
75
+ return false;
76
+ }
77
+ catch {
78
+ return false;
79
+ }
80
+ }
81
+ return isLoopbackRemoteAddress(req.socket?.remoteAddress);
82
+ }
83
+ export function resolveAllowedHosts(env = process.env) {
84
+ const raw = env.STAGEFLOW_ALLOWED_HOSTS;
85
+ const entries = [
86
+ { hostname: "localhost" },
87
+ { hostname: "127.0.0.1" },
88
+ { hostname: "::1" },
89
+ ];
90
+ if (raw === undefined || raw.trim() === "") {
91
+ return { entries };
92
+ }
93
+ for (const part of raw.split(",")) {
94
+ const token = part.trim();
95
+ if (token.length === 0)
96
+ continue;
97
+ if (token === "*") {
98
+ throw new Error("STAGEFLOW_ALLOWED_HOSTS must not contain '*'; list explicit hostnames instead.");
99
+ }
100
+ const parsed = parseAllowedHostEntry(token);
101
+ if (!entries.some((e) => e.hostname === parsed.hostname &&
102
+ (e.port ?? "") === (parsed.port ?? ""))) {
103
+ entries.push(parsed);
104
+ }
105
+ }
106
+ return { entries };
107
+ }
108
+ function parseAllowedHostEntry(token) {
109
+ if (token.startsWith("[")) {
110
+ const end = token.indexOf("]");
111
+ if (end === -1) {
112
+ throw new Error(`Invalid STAGEFLOW_ALLOWED_HOSTS entry: ${token}`);
113
+ }
114
+ const hostname = token.slice(1, end).toLowerCase();
115
+ const rest = token.slice(end + 1);
116
+ if (rest.length === 0)
117
+ return { hostname };
118
+ if (rest.startsWith(":") && /^\d+$/.test(rest.slice(1))) {
119
+ return { hostname, port: rest.slice(1) };
120
+ }
121
+ throw new Error(`Invalid STAGEFLOW_ALLOWED_HOSTS entry: ${token}`);
122
+ }
123
+ const colon = token.lastIndexOf(":");
124
+ if (colon > 0 && /^\d+$/.test(token.slice(colon + 1))) {
125
+ return {
126
+ hostname: token.slice(0, colon).toLowerCase(),
127
+ port: token.slice(colon + 1),
128
+ };
129
+ }
130
+ return { hostname: token.toLowerCase() };
131
+ }
132
+ export function isHostAllowed(allowed, hostname, port) {
133
+ const h = hostname.toLowerCase();
134
+ if (isLoopbackHostname(h))
135
+ return true;
136
+ for (const entry of allowed.entries) {
137
+ if (entry.hostname !== h)
138
+ continue;
139
+ if (entry.port === undefined)
140
+ return true;
141
+ if (port !== undefined && entry.port === port)
142
+ return true;
143
+ }
144
+ return false;
145
+ }
146
+ export function assertAllowedHttpAccess(allowed, req, res, options = {}) {
147
+ const hostHeader = req.headers.host;
148
+ if (typeof hostHeader !== "string" || hostHeader.length === 0) {
149
+ writeForbidden(res, { error: "Forbidden host" });
150
+ return false;
151
+ }
152
+ const hostname = hostnameFromHostHeader(hostHeader);
153
+ const port = portFromHostHeader(hostHeader);
154
+ if (!isHostAllowed(allowed, hostname, port)) {
155
+ writeForbidden(res, { error: "Forbidden host", host: hostname });
156
+ return false;
157
+ }
158
+ const origin = req.headers.origin;
159
+ if (typeof origin === "string" && origin.length > 0) {
160
+ try {
161
+ const originUrl = new URL(origin);
162
+ const originHost = originUrl.hostname;
163
+ const originPort = originUrl.port || undefined;
164
+ if (!isHostAllowed(allowed, originHost, originPort)) {
165
+ writeForbidden(res, { error: "Forbidden origin", host: originHost });
166
+ return false;
167
+ }
168
+ }
169
+ catch {
170
+ writeForbidden(res, { error: "Forbidden origin" });
171
+ return false;
172
+ }
173
+ }
174
+ else if (options.requireOrigin) {
175
+ writeForbidden(res, { error: "Origin required" });
176
+ return false;
177
+ }
178
+ return true;
179
+ }
@@ -3,9 +3,13 @@ import type { AgentPort } from "../agent/port.js";
3
3
  import type { ProviderAuthContext } from "../agent/providerAuth.js";
4
4
  import { type RunStoreKind } from "../runstore/createStore.js";
5
5
  import type { RunStore } from "../runstore/port.js";
6
+ import type { StoreFilesystemClassification } from "../runstore/storeFilesystem.js";
6
7
  import { RunManager } from "../runtime/runManager.js";
7
8
  import { type RunChangeBus } from "../runtime/runChangeBus.js";
8
9
  import { type McpHttpHandler } from "../mcp/server.js";
10
+ import { type HostConfig } from "../config/hostConfig.js";
11
+ import { bootProviderConfig } from "../agent/bootProviderConfig.js";
12
+ export declare const DEFAULT_GC_INTERVAL_MS: number;
9
13
  export type StageflowHostOptions = {
10
14
  agent: AgentPort;
11
15
  cwd?: string;
@@ -17,6 +21,10 @@ export type StageflowHostOptions = {
17
21
  providerAuthContext?: ProviderAuthContext;
18
22
  mcpStateless?: boolean;
19
23
  runChangeBus?: RunChangeBus;
24
+ env?: NodeJS.ProcessEnv;
25
+ hostConfig?: HostConfig;
26
+ /** Skip HostConfig load (tests that inject store/manager pieces only). */
27
+ skipHostConfig?: boolean;
20
28
  };
21
29
  export type StageflowHostBootstrap = {
22
30
  a2a?: A2aHost;
@@ -24,11 +32,37 @@ export type StageflowHostBootstrap = {
24
32
  agentDir: string;
25
33
  rootDir: string;
26
34
  isGitProject: boolean;
27
- store: RunStore;
28
- manager: RunManager;
35
+ /** Absent when `serveBlocked` — store must not be opened after a failed restore. */
36
+ store?: RunStore;
37
+ /** Absent when `serveBlocked` — no RunManager lifecycle/resume. */
38
+ manager?: RunManager;
29
39
  runChangeBus: RunChangeBus;
30
40
  mcpStateless: boolean;
31
41
  providerAuthContext: ProviderAuthContext | undefined;
32
42
  mcpHandler: McpHttpHandler;
43
+ hostConfig?: HostConfig;
44
+ providerBoot?: Awaited<ReturnType<typeof bootProviderConfig>>;
45
+ /** Periodic retention GC handle when enabled; already `.unref()`'d. */
46
+ gcInterval?: NodeJS.Timeout;
47
+ stopGcInterval: () => void;
48
+ /** Filesystem classification for `$STAGEFLOW_HOME` (Slot 8). */
49
+ storeFilesystem?: StoreFilesystemClassification;
50
+ /**
51
+ * When set, Host must not serve API/MCP (restore.failed or failed boot apply).
52
+ * /livez still answers; /readyz fails.
53
+ */
54
+ serveBlocked?: {
55
+ code: string;
56
+ reason: string;
57
+ };
33
58
  };
59
+ /** Parse `STAGEFLOW_GC_INTERVAL_MS`; default 1h; `0` disables. */
60
+ export declare function gcIntervalMsFromEnv(env?: NodeJS.ProcessEnv): number;
61
+ /**
62
+ * Start the unattended retention sweep. First fire is one interval after start
63
+ * (`setInterval` semantics). Returns undefined when intervalMs is 0.
64
+ */
65
+ export declare function startPeriodicRunGc(manager: RunManager, intervalMs: number, options?: {
66
+ logError?: (message: string) => void;
67
+ }): NodeJS.Timeout | undefined;
34
68
  export declare function bootstrapStageflowHost(options: StageflowHostOptions): Promise<StageflowHostBootstrap>;
@@ -1,31 +1,205 @@
1
+ import path from "node:path";
1
2
  import { getAgentDir } from "@earendil-works/pi-coding-agent";
2
3
  import { createA2aHost } from "../a2a/server.js";
4
+ import { A2aStore } from "../a2a/store.js";
3
5
  import { createRunStoreWithConnection } from "../runstore/createStore.js";
4
6
  import { resolveA2aConfigPath } from "../a2a/configDiscovery.js";
7
+ import { ensureGlobalHome } from "../project/globalHome.js";
5
8
  import { resolveStageflowContext } from "../project/resolveStageflowContext.js";
6
9
  import { findProjectRoot } from "../project/findProjectRoot.js";
10
+ import { warnDurableRootDiskIfNeeded } from "../runstore/diskUsage.js";
11
+ import { assertStoreFilesystemSupported } from "../runstore/storeFilesystem.js";
12
+ import { assertStoreQuickCheck, isSqliteCorruptError, maybeCheckpointResidualWal, rethrowAsStoreIntegrityFailed, } from "../runstore/sqlite/applyStorePragmas.js";
7
13
  import { RunManager } from "../runtime/runManager.js";
14
+ import { PI_CODING_AGENT_DIR_ENV } from "../runtime/stageRoots.js";
8
15
  import { createRunChangeBus, getRunChangeBusFromWrappedStore, isRunStoreWrapped, wrapRunStoreWithChangeBus, } from "../runtime/runChangeBus.js";
9
16
  import { createMcpHttpHandler, resolveMcpStateless, } from "../mcp/server.js";
17
+ import { loadHostConfig, } from "../config/hostConfig.js";
18
+ import { bootProviderConfig } from "../agent/bootProviderConfig.js";
19
+ import { applyPendingRestoreAtBoot, } from "../runstore/restore.js";
20
+ import { assertTmpdirUsable } from "../runstore/assertTmpdir.js";
21
+ import { logger as rootLogger } from "../logging/logger.js";
22
+ export const DEFAULT_GC_INTERVAL_MS = 60 * 60 * 1000;
23
+ function sqliteConnectionFromStore(store) {
24
+ const connection = store.connection;
25
+ if (connection !== undefined &&
26
+ connection !== null &&
27
+ typeof connection.prepare === "function") {
28
+ return connection;
29
+ }
30
+ return undefined;
31
+ }
32
+ /** Parse `STAGEFLOW_GC_INTERVAL_MS`; default 1h; `0` disables. */
33
+ export function gcIntervalMsFromEnv(env = process.env) {
34
+ const raw = env.STAGEFLOW_GC_INTERVAL_MS;
35
+ if (raw === undefined || raw.trim() === "")
36
+ return DEFAULT_GC_INTERVAL_MS;
37
+ const parsed = Number(raw);
38
+ if (!Number.isFinite(parsed) || parsed < 0)
39
+ return DEFAULT_GC_INTERVAL_MS;
40
+ return parsed;
41
+ }
42
+ /**
43
+ * Start the unattended retention sweep. First fire is one interval after start
44
+ * (`setInterval` semantics). Returns undefined when intervalMs is 0.
45
+ */
46
+ export function startPeriodicRunGc(manager, intervalMs, options) {
47
+ if (intervalMs <= 0)
48
+ return undefined;
49
+ const logError = options?.logError ??
50
+ ((message) => {
51
+ console.error(message);
52
+ });
53
+ let inFlight = false;
54
+ return setInterval(() => {
55
+ if (inFlight)
56
+ return;
57
+ inFlight = true;
58
+ void manager
59
+ .gcRuns({ execute: true, channel: "periodic" })
60
+ .then((result) => {
61
+ if (!result.ok) {
62
+ logError(`periodic run GC failed: ${result.reason}`);
63
+ }
64
+ })
65
+ .catch((err) => {
66
+ logError(`periodic run GC failed: ${err instanceof Error ? err.message : String(err)}`);
67
+ })
68
+ .finally(() => {
69
+ inFlight = false;
70
+ });
71
+ }, intervalMs).unref();
72
+ }
10
73
  export async function bootstrapStageflowHost(options) {
74
+ const env = options.env ?? process.env;
11
75
  const invocationCwd = options.cwd ?? process.cwd();
12
76
  const ctx = await resolveStageflowContext(invocationCwd);
13
77
  const cwd = ctx.invocationCwd;
78
+ ensureGlobalHome();
79
+ assertTmpdirUsable(env);
80
+ const hostConfig = options.hostConfig ??
81
+ (options.skipHostConfig
82
+ ? undefined
83
+ : loadHostConfig({
84
+ env,
85
+ homeDir: ctx.globalHome,
86
+ overrides: options.maxConcurrent !== undefined
87
+ ? { maxConcurrentRuns: options.maxConcurrent }
88
+ : undefined,
89
+ }));
90
+ for (const warning of hostConfig?.warnings ?? []) {
91
+ console.warn(`stageflow: ${warning}`);
92
+ }
93
+ const providerBoot = options.skipHostConfig
94
+ ? undefined
95
+ : await bootProviderConfig({
96
+ cwd: invocationCwd,
97
+ env,
98
+ requireProviders: hostConfig?.requireProviders,
99
+ authContext: options.providerAuthContext,
100
+ });
101
+ process.env[PI_CODING_AGENT_DIR_ENV] = path.join(ctx.globalHome, "agent");
14
102
  const agentDir = options.agentDir ?? getAgentDir();
15
103
  const rootDir = options.rootDir ?? ctx.projectRoot;
16
104
  const isGitProject = options.rootDir !== undefined
17
105
  ? findProjectRoot(rootDir) !== null
18
106
  : ctx.isGitProject;
107
+ const storeFilesystem = assertStoreFilesystemSupported(ctx.globalHome, {
108
+ env,
109
+ warn: (message) => console.warn(message),
110
+ });
111
+ const bootLog = rootLogger.child({ component: "bootstrap" });
112
+ let serveBlocked;
113
+ let restoreOutcome = { status: "none" };
114
+ if (!options.store) {
115
+ restoreOutcome = await applyPendingRestoreAtBoot(ctx.globalHome);
116
+ if (restoreOutcome.status === "blocked") {
117
+ serveBlocked = {
118
+ code: "restore_failed",
119
+ reason: restoreOutcome.reason,
120
+ };
121
+ bootLog.error("restore.blocked", `restore.failed: ${restoreOutcome.reason}`);
122
+ }
123
+ else if (restoreOutcome.status === "failed") {
124
+ serveBlocked = {
125
+ code: "restore_failed",
126
+ reason: restoreOutcome.reason,
127
+ };
128
+ bootLog.error("restore.apply_failed", `restore apply failed (attempts=${restoreOutcome.attempts}): ${restoreOutcome.reason}`);
129
+ }
130
+ else if (restoreOutcome.status === "applied") {
131
+ bootLog.info("restore.applied", `restore applied schema_version=${restoreOutcome.result.schema_version}`);
132
+ }
133
+ }
134
+ if (serveBlocked !== undefined) {
135
+ const runChangeBus = options.runChangeBus ?? createRunChangeBus();
136
+ const mcpStateless = resolveMcpStateless({
137
+ mcpStateless: options.mcpStateless,
138
+ });
139
+ return {
140
+ cwd,
141
+ agentDir,
142
+ rootDir,
143
+ isGitProject,
144
+ runChangeBus,
145
+ mcpStateless,
146
+ providerAuthContext: options.providerAuthContext,
147
+ mcpHandler: {
148
+ handle: async () => { },
149
+ close: async () => { },
150
+ },
151
+ stopGcInterval: () => { },
152
+ storeFilesystem,
153
+ serveBlocked,
154
+ ...(hostConfig !== undefined ? { hostConfig } : {}),
155
+ ...(providerBoot !== undefined ? { providerBoot } : {}),
156
+ };
157
+ }
19
158
  let rawStore;
20
159
  let sqliteConnection;
160
+ const storeRootDir = options.store
161
+ ? (options.rootDir ?? ctx.globalHome)
162
+ : ctx.globalHome;
21
163
  if (options.store) {
22
164
  rawStore = options.store;
165
+ sqliteConnection = sqliteConnectionFromStore(rawStore);
23
166
  }
24
167
  else {
25
- const created = createRunStoreWithConnection({ rootDir: ctx.globalHome, kind: options.storeKind });
26
- rawStore = created.store;
27
- sqliteConnection = created.connection;
168
+ try {
169
+ const created = createRunStoreWithConnection({
170
+ rootDir: ctx.globalHome,
171
+ kind: options.storeKind,
172
+ openerMode: "migrate",
173
+ });
174
+ rawStore = created.store;
175
+ sqliteConnection = created.connection;
176
+ }
177
+ catch (err) {
178
+ if (isSqliteCorruptError(err)) {
179
+ rethrowAsStoreIntegrityFailed(err);
180
+ }
181
+ throw err;
182
+ }
183
+ }
184
+ if (sqliteConnection !== undefined) {
185
+ try {
186
+ assertStoreQuickCheck(sqliteConnection);
187
+ }
188
+ catch (err) {
189
+ if (isSqliteCorruptError(err)) {
190
+ rethrowAsStoreIntegrityFailed(err);
191
+ }
192
+ throw err;
193
+ }
194
+ maybeCheckpointResidualWal(storeRootDir, sqliteConnection, {
195
+ log: (event, fields) => {
196
+ bootLog.info(event, event, fields);
197
+ },
198
+ });
28
199
  }
200
+ const a2aStore = sqliteConnection !== undefined
201
+ ? new A2aStore(storeRootDir, sqliteConnection)
202
+ : undefined;
29
203
  const boundBus = isRunStoreWrapped(rawStore)
30
204
  ? getRunChangeBusFromWrappedStore(rawStore)
31
205
  : undefined;
@@ -42,12 +216,30 @@ export async function bootstrapStageflowHost(options) {
42
216
  projectRoot: rootDir,
43
217
  isGitProject,
44
218
  store,
45
- maxConcurrent: options.maxConcurrent,
219
+ maxConcurrent: options.maxConcurrent ??
220
+ (hostConfig?.maxConcurrentRunsExplicit
221
+ ? hostConfig.maxConcurrentRuns
222
+ : undefined),
223
+ maxQueued: hostConfig?.maxQueued,
224
+ maxConcurrentPerProject: hostConfig?.maxConcurrentRunsPerProject,
225
+ callerQuotas: Object.fromEntries(Object.entries(hostConfig?.callers ?? {}).map(([id, cfg]) => [
226
+ id,
227
+ cfg.maxConcurrent,
228
+ ])),
46
229
  operatorCatalog: { cwd, agentDir },
230
+ a2aStore,
47
231
  });
48
232
  await manager.attachWaitingStages();
49
233
  await manager.reconcileOrphanedStages();
234
+ await manager.autoResumeInterruptedStages();
50
235
  await manager.resumeStalledSchedules();
236
+ await manager.reenqueuePersistedQueuedRuns();
237
+ await warnDurableRootDiskIfNeeded(ctx.globalHome, { env });
238
+ const gcInterval = startPeriodicRunGc(manager, gcIntervalMsFromEnv(env));
239
+ const stopGcInterval = () => {
240
+ if (gcInterval !== undefined)
241
+ clearInterval(gcInterval);
242
+ };
51
243
  const mcpStateless = resolveMcpStateless({
52
244
  mcpStateless: options.mcpStateless,
53
245
  });
@@ -55,12 +247,19 @@ export async function bootstrapStageflowHost(options) {
55
247
  manager,
56
248
  store,
57
249
  cwd,
250
+ agentDir,
58
251
  runChangeBus,
59
252
  providerAuthContext: options.providerAuthContext,
60
253
  projectRoot: rootDir,
61
254
  }, { mcpStateless });
62
255
  return {
63
- a2a: await createA2aHost({ manager, runStore: store, rootDir, connection: sqliteConnection }, resolveA2aConfigPath(rootDir)),
256
+ a2a: await createA2aHost({
257
+ manager,
258
+ runStore: store,
259
+ rootDir: storeRootDir,
260
+ connection: sqliteConnection,
261
+ a2aStore,
262
+ }, resolveA2aConfigPath(rootDir)),
64
263
  cwd,
65
264
  agentDir,
66
265
  rootDir,
@@ -71,5 +270,10 @@ export async function bootstrapStageflowHost(options) {
71
270
  mcpStateless,
72
271
  providerAuthContext: options.providerAuthContext,
73
272
  mcpHandler,
273
+ ...(hostConfig !== undefined ? { hostConfig } : {}),
274
+ ...(providerBoot !== undefined ? { providerBoot } : {}),
275
+ ...(gcInterval !== undefined ? { gcInterval } : {}),
276
+ stopGcInterval,
277
+ storeFilesystem,
74
278
  };
75
279
  }
@@ -0,0 +1,47 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ export type ControlScope = "read" | "drive";
3
+ export type BearerAuth = {
4
+ scope: ControlScope;
5
+ caller_id: string;
6
+ };
7
+ export type NamedDriveToken = {
8
+ caller_id: string;
9
+ digest: Buffer;
10
+ };
11
+ export type ControlTokens = {
12
+ driveDigest: Buffer | undefined;
13
+ readDigest: Buffer | undefined;
14
+ namedDrive: readonly NamedDriveToken[];
15
+ };
16
+ /** Parse named drive callers from STAGEFLOW_CONTROL_TOKEN_<NAME> / _FILE. */
17
+ export declare function listNamedControlTokenEnvKeys(env?: NodeJS.ProcessEnv): Array<{
18
+ envKey: string;
19
+ caller_id: string;
20
+ isFile: boolean;
21
+ }>;
22
+ export declare function loadControlTokens(env?: NodeJS.ProcessEnv): ControlTokens;
23
+ /** Plain bearer for CLI→host calls. Drive preferred; read only for GET/HEAD. */
24
+ export declare function resolveClientBearerToken(method: string, env?: NodeJS.ProcessEnv): string | undefined;
25
+ export declare function clientAuthorizationHeaders(method: string, env?: NodeJS.ProcessEnv): Record<string, string>;
26
+ export declare function hasDriveToken(tokens: ControlTokens): boolean;
27
+ export declare function hasAnyToken(tokens: ControlTokens): boolean;
28
+ export declare function isLoopbackBind(bind: string): boolean;
29
+ export declare function refuseBindMessage(bind: string): string;
30
+ export declare function assertBindAllowed(bind: string, tokens: ControlTokens): void;
31
+ export declare class BindRefusedError extends Error {
32
+ readonly exitCode = 1;
33
+ constructor(message: string);
34
+ }
35
+ export declare function requiredScopeFor(method: string, pathname: string): ControlScope | null;
36
+ /**
37
+ * Digest once; timingSafeEqual against every loaded digest (no early-return oracle).
38
+ * Named control tokens are drive-only; READ stays the singular global token.
39
+ */
40
+ export declare function authenticateBearer(tokens: ControlTokens, authorization: string | undefined): BearerAuth | undefined;
41
+ export type EnforceBearerResult = {
42
+ ok: true;
43
+ auth: BearerAuth | undefined;
44
+ } | {
45
+ ok: false;
46
+ };
47
+ export declare function enforceBearerAuth(tokens: ControlTokens, req: IncomingMessage, res: ServerResponse, required: ControlScope): EnforceBearerResult;