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,62 @@
1
+ import { z } from "zod";
2
+ import { PACKAGE_VERSION } from "../package-meta.js";
3
+ import { listPipelinesMultiProject, listTasksMultiProject } from "../config/multiProjectCatalog.js";
4
+ import { resolveCatalogRoots } from "../config/resolveCatalogRoots.js";
5
+ import { textResult } from "./toolResults.js";
6
+ import { toolchainHealthMap } from "../preflight/toolchain.js";
7
+ export function registerGetStartedTool(server, deps) {
8
+ server.registerTool("get_started", {
9
+ description: "First-run orientation for remote harnesses: host version, providers, toolchain hints, catalog roots with counts, and a three-call next_steps list. No arguments.",
10
+ inputSchema: z.object({}),
11
+ }, async () => {
12
+ const roots = await resolveCatalogRoots({
13
+ store: deps.store,
14
+ bootCwd: deps.cwd,
15
+ });
16
+ const pipelines = await listPipelinesMultiProject({
17
+ store: deps.store,
18
+ bootCwd: deps.cwd,
19
+ });
20
+ const tasks = await listTasksMultiProject({
21
+ store: deps.store,
22
+ bootCwd: deps.cwd,
23
+ });
24
+ const catalog_roots = roots.map((root) => ({
25
+ project_root: root.project_root,
26
+ kind: root.kind,
27
+ read_only: root.read_only,
28
+ pipeline_count: pipelines.items.filter((p) => p.project_root === root.project_root).length,
29
+ task_count: tasks.items.filter((t) => t.project_root === root.project_root)
30
+ .length,
31
+ }));
32
+ const firstRoot = catalog_roots.find((r) => r.kind === "seeded") ?? catalog_roots[0];
33
+ const samplePipeline = pipelines.items.find((p) => p.project_root === firstRoot?.project_root);
34
+ const sampleTask = tasks.items.find((t) => t.project_root === firstRoot?.project_root);
35
+ return textResult({
36
+ host: { version: PACKAGE_VERSION },
37
+ providers: deps.providerAuthContext
38
+ ? { note: "Use list_providers for status" }
39
+ : { configured: [] },
40
+ toolchain: toolchainHealthMap(),
41
+ catalog_roots,
42
+ next_steps: [
43
+ {
44
+ tool: "list_pipelines",
45
+ args: firstRoot ? { project_root: firstRoot.project_root } : {},
46
+ },
47
+ {
48
+ tool: "list_tasks",
49
+ args: firstRoot ? { project_root: firstRoot.project_root } : {},
50
+ },
51
+ {
52
+ tool: "start_run",
53
+ args: {
54
+ ...(firstRoot ? { project_root: firstRoot.project_root } : {}),
55
+ pipeline: samplePipeline?.path ?? "pipelines/demo.pipeline.yaml",
56
+ task_path: sampleTask?.path ?? "tasks/demo.task.yaml",
57
+ },
58
+ },
59
+ ],
60
+ });
61
+ });
62
+ }
@@ -1,4 +1,15 @@
1
1
  import type { RunStore } from "../runstore/port.js";
2
+ export declare function assertSafeRunId(runId: string): void;
3
+ export declare const PATH_DENIED_MESSAGE = "Artifact path denied";
4
+ export type ContainedPathMessages = {
5
+ relativeRequired: string;
6
+ escapes: string;
7
+ notFound: (relativePath: string) => string;
8
+ denied?: string;
9
+ };
10
+ export declare const CHECKOUT_PATH_MESSAGES: ContainedPathMessages;
11
+ export declare function assertSafeRelativePath(relativePath: string, messages: ContainedPathMessages): string[];
12
+ export declare function resolveContainedRelativePath(containerDir: string, relativePath: string, messages: ContainedPathMessages): Promise<string>;
2
13
  export declare function artifactMediaType(relativePath: string): string | undefined;
3
14
  export type ClassifiedArtifactContent = {
4
15
  kind: "image";
@@ -1,8 +1,8 @@
1
1
  import { isUtf8 } from "node:buffer";
2
- import { readFile, realpath } from "node:fs/promises";
2
+ import { readFile } from "node:fs/promises";
3
3
  import path from "node:path";
4
- import { isInsideDir } from "../runstore/workspaceLayout.js";
5
- function assertSafeRunId(runId) {
4
+ import { classifyRealPathContainment } from "../runstore/workspaceLayout.js";
5
+ export function assertSafeRunId(runId) {
6
6
  if (typeof runId !== "string" || runId.trim().length === 0) {
7
7
  throw new Error("runId must be a non-empty string");
8
8
  }
@@ -13,6 +13,48 @@ function assertSafeRunId(runId) {
13
13
  throw new Error("runId must not contain path separators or ..");
14
14
  }
15
15
  }
16
+ export const PATH_DENIED_MESSAGE = "Artifact path denied";
17
+ const ARTIFACT_MESSAGES = {
18
+ relativeRequired: "path must be relative to the run workspace",
19
+ escapes: "path escapes the run workspace",
20
+ notFound: (relativePath) => `Artifact not found: ${relativePath}`,
21
+ denied: PATH_DENIED_MESSAGE,
22
+ };
23
+ export const CHECKOUT_PATH_MESSAGES = {
24
+ relativeRequired: "path must be relative to the checkout",
25
+ escapes: "path escapes the checkout",
26
+ notFound: (relativePath) => `Checkout file not found: ${relativePath}`,
27
+ denied: PATH_DENIED_MESSAGE,
28
+ };
29
+ export function assertSafeRelativePath(relativePath, messages) {
30
+ if (typeof relativePath !== "string" || relativePath.trim().length === 0) {
31
+ throw new Error("path must be a non-empty string");
32
+ }
33
+ if (path.isAbsolute(relativePath)) {
34
+ throw new Error(messages.relativeRequired);
35
+ }
36
+ const segments = relativePath.split(/[/\\]/);
37
+ if (segments.some((segment) => segment === "..")) {
38
+ throw new Error("path must not contain .. segments");
39
+ }
40
+ if (segments.some((segment) => segment === ".pi-agent") ||
41
+ path.basename(relativePath) === "auth.json") {
42
+ throw new Error(messages.denied ?? PATH_DENIED_MESSAGE);
43
+ }
44
+ return segments;
45
+ }
46
+ export async function resolveContainedRelativePath(containerDir, relativePath, messages) {
47
+ assertSafeRelativePath(relativePath, messages);
48
+ const candidate = path.resolve(containerDir, relativePath);
49
+ const containment = await classifyRealPathContainment(candidate, containerDir);
50
+ if (containment.status === "missing") {
51
+ throw new Error(messages.notFound(relativePath));
52
+ }
53
+ if (containment.status === "outside") {
54
+ throw new Error(messages.escapes);
55
+ }
56
+ return containment.realPath;
57
+ }
16
58
  export function artifactMediaType(relativePath) {
17
59
  const ext = path.extname(relativePath).toLowerCase();
18
60
  switch (ext) {
@@ -41,35 +83,9 @@ export function classifyArtifactContent(relativePath, bytes) {
41
83
  }
42
84
  async function resolveRunArtifactFile(store, runId, relativePath) {
43
85
  assertSafeRunId(runId);
44
- if (typeof relativePath !== "string" || relativePath.trim().length === 0) {
45
- throw new Error("path must be a non-empty string");
46
- }
47
- if (path.isAbsolute(relativePath)) {
48
- throw new Error("path must be relative to the run workspace");
49
- }
50
- const segments = relativePath.split(/[/\\]/);
51
- if (segments.some((segment) => segment === "..")) {
52
- throw new Error("path must not contain .. segments");
53
- }
54
- if (segments.some((segment) => segment === ".pi-agent") ||
55
- path.basename(relativePath) === "auth.json") {
56
- throw new Error("Artifact path denied");
57
- }
58
86
  await store.readRunMeta(runId);
59
87
  const workspaceDir = store.getWorkspaceDir(runId);
60
- const workspaceReal = await realpath(workspaceDir);
61
- const candidate = path.resolve(workspaceDir, relativePath);
62
- let fileReal;
63
- try {
64
- fileReal = await realpath(candidate);
65
- }
66
- catch {
67
- throw new Error(`Artifact not found: ${relativePath}`);
68
- }
69
- if (!isInsideDir(fileReal, workspaceReal)) {
70
- throw new Error("path escapes the run workspace");
71
- }
72
- return fileReal;
88
+ return resolveContainedRelativePath(workspaceDir, relativePath, ARTIFACT_MESSAGES);
73
89
  }
74
90
  export async function readRunArtifact(store, runId, relativePath) {
75
91
  const fileReal = await resolveRunArtifactFile(store, runId, relativePath);
@@ -38,6 +38,7 @@ function toolDepsFrom(deps) {
38
38
  manager: deps.manager,
39
39
  store: deps.store,
40
40
  cwd: deps.cwd,
41
+ ...(deps.agentDir !== undefined ? { agentDir: deps.agentDir } : {}),
41
42
  ...(deps.providerAuthContext !== undefined
42
43
  ? { providerAuthContext: deps.providerAuthContext }
43
44
  : {}),
package/dist/mcp/tools.js CHANGED
@@ -1,14 +1,18 @@
1
1
  import { z } from "zod";
2
2
  import { registerCatalogTools } from "./catalogTools.js";
3
+ import { registerCheckoutTools } from "./checkoutTools.js";
3
4
  import { registerControlTools } from "./controlTools.js";
4
5
  import { registerProjectMcpTools } from "./projectMcpTools.js";
5
6
  import { registerProviderTools } from "./providerTools.js";
6
7
  import { textResult } from "./toolResults.js";
7
8
  import { DEFAULT_TIMEOUT_MS, waitRun } from "./waitRun.js";
9
+ import { registerGetStartedTool } from "./getStartedTool.js";
8
10
  export function registerMcpTools(server, deps) {
9
11
  const { store } = deps;
12
+ registerGetStartedTool(server, deps);
10
13
  registerCatalogTools(server, deps);
11
14
  registerControlTools(server, deps);
15
+ registerCheckoutTools(server, deps);
12
16
  registerProviderTools(server, deps);
13
17
  registerProjectMcpTools(server, deps);
14
18
  server.registerTool("wait_run", {
@@ -0,0 +1,14 @@
1
+ export type ProxyInstallResult = {
2
+ installed: boolean;
3
+ httpProxyHost?: string;
4
+ httpsProxyHost?: string;
5
+ noProxy: string;
6
+ };
7
+ export declare function effectiveNoProxy(env: NodeJS.ProcessEnv, bindHosts?: readonly string[]): string;
8
+ export declare function installProxyDispatcher(env?: NodeJS.ProcessEnv, options?: {
9
+ bindHosts?: readonly string[];
10
+ log?: (msg: string) => void;
11
+ }): ProxyInstallResult;
12
+ export declare function resetProxyDispatcherForTests(): void;
13
+ export declare function buildEgressHealth(env?: NodeJS.ProcessEnv): Record<string, unknown>;
14
+ export declare function proxyHealthFields(env?: NodeJS.ProcessEnv): Record<string, unknown>;
@@ -0,0 +1,78 @@
1
+ import { EnvHttpProxyAgent, setGlobalDispatcher } from "undici";
2
+ import { stripUrlUserinfo } from "../logging/redact.js";
3
+ let installed = false;
4
+ function firstProxy(env) {
5
+ return (env.HTTPS_PROXY ||
6
+ env.https_proxy ||
7
+ env.HTTP_PROXY ||
8
+ env.http_proxy ||
9
+ undefined);
10
+ }
11
+ function parseProxyHost(raw) {
12
+ if (!raw)
13
+ return undefined;
14
+ try {
15
+ const u = new URL(raw);
16
+ return u.host;
17
+ }
18
+ catch {
19
+ return stripUrlUserinfo(raw);
20
+ }
21
+ }
22
+ export function effectiveNoProxy(env, bindHosts = ["127.0.0.1", "::1", "localhost"]) {
23
+ const existing = env.NO_PROXY ?? env.no_proxy ?? "";
24
+ const parts = existing
25
+ .split(",")
26
+ .map((s) => s.trim())
27
+ .filter(Boolean);
28
+ const set = new Set(parts.map((p) => p.toLowerCase()));
29
+ for (const host of bindHosts) {
30
+ if (!set.has(host.toLowerCase())) {
31
+ parts.push(host);
32
+ set.add(host.toLowerCase());
33
+ }
34
+ }
35
+ return parts.join(",");
36
+ }
37
+ export function installProxyDispatcher(env = process.env, options = {}) {
38
+ const proxy = firstProxy(env);
39
+ if (!proxy) {
40
+ return { installed: false, noProxy: env.NO_PROXY ?? env.no_proxy ?? "" };
41
+ }
42
+ const noProxy = effectiveNoProxy(env, options.bindHosts);
43
+ process.env.NO_PROXY = noProxy;
44
+ if (!installed) {
45
+ setGlobalDispatcher(new EnvHttpProxyAgent({ noProxy }));
46
+ installed = true;
47
+ options.log?.(`Installed EnvHttpProxyAgent; appended loopback to NO_PROXY (${noProxy})`);
48
+ }
49
+ return {
50
+ installed: true,
51
+ httpProxyHost: parseProxyHost(env.HTTP_PROXY ?? env.http_proxy),
52
+ httpsProxyHost: parseProxyHost(env.HTTPS_PROXY ?? env.https_proxy),
53
+ noProxy,
54
+ };
55
+ }
56
+ export function resetProxyDispatcherForTests() {
57
+ installed = false;
58
+ }
59
+ export function buildEgressHealth(env = process.env) {
60
+ const httpSet = Boolean(env.HTTP_PROXY || env.http_proxy);
61
+ const httpsSet = Boolean(env.HTTPS_PROXY || env.https_proxy);
62
+ const noProxySet = Boolean(env.NO_PROXY || env.no_proxy);
63
+ const caSet = Boolean(env.NODE_EXTRA_CA_CERTS || env.SSL_CERT_FILE || env.REQUESTS_CA_BUNDLE);
64
+ const proxyHost = parseProxyHost(env.HTTPS_PROXY ?? env.https_proxy) ??
65
+ parseProxyHost(env.HTTP_PROXY ?? env.http_proxy) ??
66
+ null;
67
+ return {
68
+ http_proxy: httpSet ? "set" : "unset",
69
+ https_proxy: httpsSet ? "set" : "unset",
70
+ no_proxy: noProxySet ? "set" : "unset",
71
+ proxy_host: proxyHost,
72
+ extra_ca_certs: caSet ? "set" : "unset",
73
+ dispatcher_installed: installed,
74
+ };
75
+ }
76
+ export function proxyHealthFields(env = process.env) {
77
+ return buildEgressHealth(env);
78
+ }
@@ -1,2 +1,3 @@
1
1
  export declare const PACKAGE_NAME = "stageflow";
2
- export declare const PACKAGE_VERSION = "0.25.0";
2
+ export declare const PACKAGE_VERSION = "0.27.0";
3
+ export declare const BUILD_SHA: string;
@@ -1,2 +1,3 @@
1
1
  export const PACKAGE_NAME = "stageflow";
2
- export const PACKAGE_VERSION = "0.25.0";
2
+ export const PACKAGE_VERSION = "0.27.0";
3
+ export const BUILD_SHA = process.env.STAGEFLOW_BUILD_SHA ?? "unknown";
@@ -0,0 +1,8 @@
1
+ export type BashPreflightErrorCode = "bash_not_found";
2
+ export declare class BashPreflightError extends Error {
3
+ readonly code: BashPreflightErrorCode;
4
+ constructor(message: string, code?: BashPreflightErrorCode);
5
+ }
6
+ /** Resolve `bash` on PATH (never hardcode /bin/bash). */
7
+ export declare function resolveBashPath(env?: NodeJS.ProcessEnv): string;
8
+ export declare function assertBashAvailable(env?: NodeJS.ProcessEnv): string;
@@ -0,0 +1,34 @@
1
+ import { accessSync, constants } from "node:fs";
2
+ import path from "node:path";
3
+ export class BashPreflightError extends Error {
4
+ code;
5
+ constructor(message, code = "bash_not_found") {
6
+ super(message);
7
+ this.name = "BashPreflightError";
8
+ this.code = code;
9
+ }
10
+ }
11
+ function isExecutable(filePath) {
12
+ try {
13
+ accessSync(filePath, constants.X_OK);
14
+ return true;
15
+ }
16
+ catch {
17
+ return false;
18
+ }
19
+ }
20
+ /** Resolve `bash` on PATH (never hardcode /bin/bash). */
21
+ export function resolveBashPath(env = process.env) {
22
+ const pathValue = env.PATH ?? "";
23
+ const parts = pathValue.split(path.delimiter).filter((p) => p.length > 0);
24
+ for (const dir of parts) {
25
+ const candidate = path.join(dir, "bash");
26
+ if (isExecutable(candidate)) {
27
+ return candidate;
28
+ }
29
+ }
30
+ throw new BashPreflightError('bash was not found on PATH; Stageflow verify requires bash (install bash or fix PATH)', "bash_not_found");
31
+ }
32
+ export function assertBashAvailable(env = process.env) {
33
+ return resolveBashPath(env);
34
+ }
@@ -0,0 +1,11 @@
1
+ export type ClaudeRootErrorCode = "claude_as_root";
2
+ export declare class ClaudeRootError extends Error {
3
+ readonly code: ClaudeRootErrorCode;
4
+ readonly euid: number;
5
+ constructor(euid: number);
6
+ }
7
+ export declare function isEffectiveRoot(geteuid?: (() => number) | undefined): boolean;
8
+ export declare function assertClaudeNotRoot(options: {
9
+ backendId: string | undefined;
10
+ geteuid?: (() => number) | undefined;
11
+ }): void;
@@ -0,0 +1,23 @@
1
+ export class ClaudeRootError extends Error {
2
+ code = "claude_as_root";
3
+ euid;
4
+ constructor(euid) {
5
+ super(`Claude backend cannot run as root (euid=${euid}). Use a non-root USER or docker --user.`);
6
+ this.name = "ClaudeRootError";
7
+ this.euid = euid;
8
+ }
9
+ }
10
+ export function isEffectiveRoot(geteuid = process.geteuid) {
11
+ if (typeof geteuid !== "function")
12
+ return false;
13
+ return geteuid() === 0;
14
+ }
15
+ export function assertClaudeNotRoot(options) {
16
+ if (options.backendId !== "claude")
17
+ return;
18
+ const geteuid = options.geteuid ?? process.geteuid;
19
+ if (isEffectiveRoot(geteuid)) {
20
+ const euid = typeof geteuid === "function" ? geteuid() : 0;
21
+ throw new ClaudeRootError(euid);
22
+ }
23
+ }
@@ -0,0 +1,8 @@
1
+ export type McpCommandMissing = {
2
+ command: string;
3
+ serverName: string;
4
+ };
5
+ export declare function resolveCommandOnPath(command: string, env?: NodeJS.ProcessEnv): string | undefined;
6
+ export declare function findMissingMcpCommands(servers: Record<string, {
7
+ command?: string;
8
+ }>, env?: NodeJS.ProcessEnv): McpCommandMissing[];
@@ -0,0 +1,36 @@
1
+ import { accessSync, constants } from "node:fs";
2
+ import path from "node:path";
3
+ export function resolveCommandOnPath(command, env = process.env) {
4
+ if (command.includes("/") || command.includes("\\")) {
5
+ try {
6
+ accessSync(command, constants.X_OK);
7
+ return command;
8
+ }
9
+ catch {
10
+ return undefined;
11
+ }
12
+ }
13
+ const pathValue = env.PATH ?? "";
14
+ for (const dir of pathValue.split(path.delimiter).filter(Boolean)) {
15
+ const candidate = path.join(dir, command);
16
+ try {
17
+ accessSync(candidate, constants.X_OK);
18
+ return candidate;
19
+ }
20
+ catch {
21
+ // continue
22
+ }
23
+ }
24
+ return undefined;
25
+ }
26
+ export function findMissingMcpCommands(servers, env = process.env) {
27
+ const missing = [];
28
+ for (const [serverName, server] of Object.entries(servers)) {
29
+ if (!server.command)
30
+ continue;
31
+ if (resolveCommandOnPath(server.command, env) === undefined) {
32
+ missing.push({ command: server.command, serverName });
33
+ }
34
+ }
35
+ return missing;
36
+ }
@@ -0,0 +1,40 @@
1
+ import type { LoadedPipeline } from "../types/pipeline.js";
2
+ import { type ToolRequirement } from "../config/toolRequires.js";
3
+ import { type CheckToolchainOptions, type ToolchainCheckResult } from "./toolchain.js";
4
+ export type PreflightCheckKind = "toolchain" | "secret" | "mcp";
5
+ export type PreflightItem = {
6
+ kind: PreflightCheckKind;
7
+ status: "ok" | "missing_tool" | "tool_version_mismatch" | "unknown_version" | "secret_unavailable" | "stage.unknown_secret" | "stage.denied_secret" | "unresolved_var" | "missing_catalog" | "unknown_server" | "invalid_config" | "untrusted_config_origin";
8
+ tool?: string;
9
+ required?: string;
10
+ found?: string;
11
+ path?: string;
12
+ secret?: string;
13
+ stageId?: string;
14
+ server?: string;
15
+ message?: string;
16
+ };
17
+ export type PipelinePreflightResult = {
18
+ ok: boolean;
19
+ checks: PreflightItem[];
20
+ requirements: ToolRequirement[];
21
+ toolchain: ToolchainCheckResult;
22
+ };
23
+ export declare function checkStageSecretsPresence(loaded: LoadedPipeline, hostEnv?: NodeJS.ProcessEnv): PreflightItem[];
24
+ export declare function checkStageMcpResolution(loaded: LoadedPipeline, options: {
25
+ projectRoot: string;
26
+ hostEnv?: NodeJS.ProcessEnv;
27
+ }): Promise<PreflightItem[]>;
28
+ export declare function runPipelinePreflight(loaded: LoadedPipeline, options: {
29
+ projectRoot: string;
30
+ hostEnv?: NodeJS.ProcessEnv;
31
+ toolchain?: CheckToolchainOptions;
32
+ /** When true (doctor --strict / explicit), unknown_version fails. */
33
+ strict?: boolean;
34
+ /** start_run: unknown_version passes; missing/mismatch fail. */
35
+ forStart?: boolean;
36
+ }): Promise<PipelinePreflightResult>;
37
+ export declare function preflightFailureCode(result: PipelinePreflightResult, options?: {
38
+ strict?: boolean;
39
+ forStart?: boolean;
40
+ }): string | undefined;
@@ -0,0 +1,192 @@
1
+ import { collectEffectiveRequires, } from "../config/toolRequires.js";
2
+ import { resolveStageMcpServers, StageMcpError, } from "../config/resolveStageMcpServers.js";
3
+ import { assertSecretPresent, loadSecretRegistry, SecretUnavailableError, } from "../runtime/stageSecrets.js";
4
+ import { isForeverDeniedSecret } from "../runtime/stageEnvironment.js";
5
+ import { checkToolchainRequirements, curatedStageEnv, } from "./toolchain.js";
6
+ function toolchainItems(checks) {
7
+ return checks.map((c) => ({
8
+ kind: "toolchain",
9
+ status: c.status,
10
+ tool: c.tool,
11
+ required: c.required,
12
+ found: c.found,
13
+ path: c.path,
14
+ }));
15
+ }
16
+ export function checkStageSecretsPresence(loaded, hostEnv = process.env) {
17
+ const registry = loadSecretRegistry(hostEnv);
18
+ const items = [];
19
+ for (const stage of loaded.stages) {
20
+ for (const decl of stage.secrets ?? []) {
21
+ if (isForeverDeniedSecret(decl.name)) {
22
+ items.push({
23
+ kind: "secret",
24
+ status: "stage.denied_secret",
25
+ secret: decl.name,
26
+ stageId: stage.id,
27
+ message: `secret "${decl.name}" is permanently denied`,
28
+ });
29
+ continue;
30
+ }
31
+ try {
32
+ assertSecretPresent(decl.name, registry, hostEnv);
33
+ items.push({
34
+ kind: "secret",
35
+ status: "ok",
36
+ secret: decl.name,
37
+ stageId: stage.id,
38
+ });
39
+ }
40
+ catch (err) {
41
+ if (err instanceof SecretUnavailableError) {
42
+ items.push({
43
+ kind: "secret",
44
+ status: "secret_unavailable",
45
+ secret: decl.name,
46
+ stageId: stage.id,
47
+ message: err.message,
48
+ });
49
+ continue;
50
+ }
51
+ const message = err instanceof Error ? err.message : String(err);
52
+ if (message.startsWith("stage.unknown_secret")) {
53
+ items.push({
54
+ kind: "secret",
55
+ status: "stage.unknown_secret",
56
+ secret: decl.name,
57
+ stageId: stage.id,
58
+ message: `unknown secret "${decl.name}" is not in the Host secret registry`,
59
+ });
60
+ continue;
61
+ }
62
+ items.push({
63
+ kind: "secret",
64
+ status: "secret_unavailable",
65
+ secret: decl.name,
66
+ stageId: stage.id,
67
+ message,
68
+ });
69
+ }
70
+ }
71
+ }
72
+ return items;
73
+ }
74
+ export async function checkStageMcpResolution(loaded, options) {
75
+ const hostEnv = options.hostEnv ?? process.env;
76
+ const stageEnv = { ...curatedStageEnv(hostEnv) };
77
+ const registry = loadSecretRegistry(hostEnv);
78
+ for (const stage of loaded.stages) {
79
+ for (const decl of stage.secrets ?? []) {
80
+ try {
81
+ assertSecretPresent(decl.name, registry, hostEnv);
82
+ const entry = registry.get(decl.name);
83
+ if (entry?.kind === "env") {
84
+ const plain = hostEnv[decl.name];
85
+ const fileKey = `${decl.name}_FILE`;
86
+ const filePath = hostEnv[fileKey];
87
+ if (plain !== undefined && plain.length > 0) {
88
+ stageEnv[decl.name] = plain;
89
+ }
90
+ else if (filePath !== undefined && filePath.length > 0) {
91
+ try {
92
+ const { readFileSync } = await import("node:fs");
93
+ stageEnv[decl.name] = readFileSync(filePath, "utf8").replace(/\r?\n$/, "");
94
+ }
95
+ catch {
96
+ /* presence already checked */
97
+ }
98
+ }
99
+ }
100
+ }
101
+ catch {
102
+ /* secret presence reported separately */
103
+ }
104
+ }
105
+ }
106
+ const items = [];
107
+ for (const stage of loaded.stages) {
108
+ const allowlist = stage.mcp ?? [];
109
+ if (allowlist.length === 0)
110
+ continue;
111
+ try {
112
+ await resolveStageMcpServers({
113
+ projectRoot: options.projectRoot,
114
+ allowlist,
115
+ env: stageEnv,
116
+ stageId: stage.id,
117
+ });
118
+ for (const server of allowlist) {
119
+ items.push({
120
+ kind: "mcp",
121
+ status: "ok",
122
+ server,
123
+ stageId: stage.id,
124
+ });
125
+ }
126
+ }
127
+ catch (err) {
128
+ if (err instanceof StageMcpError) {
129
+ items.push({
130
+ kind: "mcp",
131
+ status: err.code,
132
+ stageId: stage.id,
133
+ message: err.message,
134
+ });
135
+ continue;
136
+ }
137
+ items.push({
138
+ kind: "mcp",
139
+ status: "invalid_config",
140
+ stageId: stage.id,
141
+ message: err instanceof Error ? err.message : String(err),
142
+ });
143
+ }
144
+ }
145
+ return items;
146
+ }
147
+ export async function runPipelinePreflight(loaded, options) {
148
+ const hostEnv = options.hostEnv ?? process.env;
149
+ const strict = options.strict === true;
150
+ const forStart = options.forStart === true;
151
+ const reqOutcome = collectEffectiveRequires({
152
+ pipelineRequires: loaded.pipeline.requires,
153
+ stageRequires: loaded.stages.map((s) => s.requires),
154
+ pipelineId: loaded.pipeline.id,
155
+ });
156
+ const requirements = reqOutcome.ok ? reqOutcome.value : [];
157
+ const toolchain = checkToolchainRequirements(requirements, {
158
+ ...options.toolchain,
159
+ env: hostEnv,
160
+ strict: forStart ? false : strict,
161
+ failOnUnknownVersion: forStart ? false : strict,
162
+ });
163
+ const secretChecks = checkStageSecretsPresence(loaded, hostEnv);
164
+ const mcpChecks = await checkStageMcpResolution(loaded, {
165
+ projectRoot: options.projectRoot,
166
+ hostEnv,
167
+ });
168
+ const checks = [
169
+ ...toolchainItems(toolchain.checks),
170
+ ...secretChecks,
171
+ ...mcpChecks,
172
+ ];
173
+ const ok = toolchain.ok &&
174
+ !secretChecks.some((c) => c.status !== "ok") &&
175
+ !mcpChecks.some((c) => c.status !== "ok");
176
+ return { ok, checks, requirements, toolchain };
177
+ }
178
+ export function preflightFailureCode(result, options = {}) {
179
+ for (const check of result.checks) {
180
+ if (check.status === "ok")
181
+ continue;
182
+ if (check.status === "unknown_version") {
183
+ if (options.forStart)
184
+ continue;
185
+ if (!options.strict)
186
+ continue;
187
+ return "unknown_version";
188
+ }
189
+ return check.status;
190
+ }
191
+ return undefined;
192
+ }