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,102 @@
1
+ import { loadFailure, loadSuccess } from "../config/loadOutcome.js";
2
+ import { isForeverDeniedSecret } from "./stageEnvironment.js";
3
+ const GIT_HELPER_DEFAULT_NAMES = new Set([
4
+ "GITHUB_TOKEN",
5
+ "GH_TOKEN",
6
+ ]);
7
+ export function defaultSecretDelivery(name) {
8
+ return GIT_HELPER_DEFAULT_NAMES.has(name) ? "helper" : "env";
9
+ }
10
+ export function parseStageSecrets(raw, label, stageId) {
11
+ if (raw === undefined)
12
+ return loadSuccess(undefined);
13
+ if (!Array.isArray(raw)) {
14
+ return loadFailure([
15
+ {
16
+ code: "stage.invalid_secrets",
17
+ message: `Invalid stage ${label}: secrets must be an array`,
18
+ category: "stage",
19
+ stageId,
20
+ },
21
+ ]);
22
+ }
23
+ const decls = [];
24
+ const seen = new Set();
25
+ for (const item of raw) {
26
+ let name;
27
+ let asEnv = false;
28
+ if (typeof item === "string") {
29
+ name = item.trim();
30
+ }
31
+ else if (item !== null && typeof item === "object" && !Array.isArray(item)) {
32
+ const obj = item;
33
+ if (typeof obj.name !== "string" || obj.name.trim() === "") {
34
+ return loadFailure([
35
+ {
36
+ code: "stage.invalid_secrets",
37
+ message: `Invalid stage ${label}: secrets entries must be strings or { name, as? }`,
38
+ category: "stage",
39
+ stageId,
40
+ },
41
+ ]);
42
+ }
43
+ name = obj.name.trim();
44
+ if (obj.as !== undefined) {
45
+ if (obj.as !== "env") {
46
+ return loadFailure([
47
+ {
48
+ code: "stage.invalid_secrets",
49
+ message: `Invalid stage ${label}: secrets as must be "env" when set`,
50
+ category: "stage",
51
+ stageId,
52
+ },
53
+ ]);
54
+ }
55
+ asEnv = true;
56
+ }
57
+ }
58
+ else {
59
+ return loadFailure([
60
+ {
61
+ code: "stage.invalid_secrets",
62
+ message: `Invalid stage ${label}: secrets entries must be strings or { name, as? }`,
63
+ category: "stage",
64
+ stageId,
65
+ },
66
+ ]);
67
+ }
68
+ if (name === "") {
69
+ return loadFailure([
70
+ {
71
+ code: "stage.invalid_secrets",
72
+ message: `Invalid stage ${label}: secrets names must be non-empty`,
73
+ category: "stage",
74
+ stageId,
75
+ },
76
+ ]);
77
+ }
78
+ if (isForeverDeniedSecret(name)) {
79
+ return loadFailure([
80
+ {
81
+ code: "stage.denied_secret",
82
+ message: `Invalid stage ${label}: secret "${name}" is permanently denied and cannot be granted`,
83
+ category: "stage",
84
+ stageId,
85
+ },
86
+ ]);
87
+ }
88
+ if (seen.has(name)) {
89
+ return loadFailure([
90
+ {
91
+ code: "stage.invalid_secrets",
92
+ message: `Invalid stage ${label}: secrets contains duplicate name "${name}"`,
93
+ category: "stage",
94
+ stageId,
95
+ },
96
+ ]);
97
+ }
98
+ seen.add(name);
99
+ decls.push(asEnv ? { name, as: "env" } : { name });
100
+ }
101
+ return loadSuccess(decls);
102
+ }
@@ -0,0 +1,40 @@
1
+ import { cleanupCredentialsDir } from "./credentialMaterialisation.js";
2
+ import { type StageSecretDecl } from "./stageSecretDecl.js";
3
+ import type { ResolvedStageGrants } from "./stageEnvironment.js";
4
+ export type SecretRegistryEntry = {
5
+ kind: "env";
6
+ name: string;
7
+ } | {
8
+ kind: "file";
9
+ name: string;
10
+ source: string;
11
+ pointerVar: string;
12
+ };
13
+ export type SecretRegistry = Map<string, SecretRegistryEntry>;
14
+ export declare class SecretUnavailableError extends Error {
15
+ readonly code: "secret_unavailable";
16
+ readonly secretName: string;
17
+ constructor(secretName: string);
18
+ }
19
+ /** Host-owned secret registry: env NAME/_FILE plus file-credentials under $STAGEFLOW_HOME. */
20
+ export declare function loadSecretRegistry(env?: NodeJS.ProcessEnv, home?: string): SecretRegistry;
21
+ export type ResolveStageSecretsInput = {
22
+ decls: readonly StageSecretDecl[] | undefined;
23
+ registry: SecretRegistry;
24
+ hostEnv: NodeJS.ProcessEnv;
25
+ attemptDir: string;
26
+ home?: string;
27
+ };
28
+ export type ResolveStageSecretsResult = {
29
+ grants: ResolvedStageGrants;
30
+ warnings: string[];
31
+ knownValues: Array<{
32
+ name: string;
33
+ value: string;
34
+ }>;
35
+ credentialDirs: string[];
36
+ };
37
+ /** Presence-only check (no materialisation) for preflight / start gates. */
38
+ export declare function assertSecretPresent(name: string, registry: SecretRegistry, hostEnv: NodeJS.ProcessEnv): void;
39
+ export declare function resolveStageSecrets(input: ResolveStageSecretsInput): ResolveStageSecretsResult;
40
+ export { cleanupCredentialsDir };
@@ -0,0 +1,181 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { shouldRegisterValue } from "../logging/redact.js";
3
+ import { globalStageflowHome } from "../project/globalHome.js";
4
+ import { attemptCredentialsDir, cleanupCredentialsDir, ensureStageAskpassHelper, materialiseCredentialFile, materialiseSecretBytes, readFileCredentialRegistry, } from "./credentialMaterialisation.js";
5
+ import { defaultSecretDelivery, } from "./stageSecretDecl.js";
6
+ import { isForeverDeniedSecret } from "./stageEnvironment.js";
7
+ export class SecretUnavailableError extends Error {
8
+ code = "secret_unavailable";
9
+ secretName;
10
+ constructor(secretName) {
11
+ super(`secret_unavailable: registered secret "${secretName}" has no value on this Host`);
12
+ this.name = "SecretUnavailableError";
13
+ this.secretName = secretName;
14
+ }
15
+ }
16
+ function readEnvSecretValue(name, env) {
17
+ const plain = env[name];
18
+ const fileKey = `${name}_FILE`;
19
+ const filePath = env[fileKey];
20
+ const hasPlain = plain !== undefined && plain.length > 0;
21
+ const hasFile = filePath !== undefined && filePath.length > 0;
22
+ if (hasPlain && hasFile) {
23
+ throw new Error(`Set only one of ${name} or ${fileKey}, not both`);
24
+ }
25
+ if (hasFile) {
26
+ return readFileSync(filePath, "utf8").replace(/\r?\n$/, "");
27
+ }
28
+ if (hasPlain)
29
+ return plain;
30
+ return undefined;
31
+ }
32
+ /** Host-owned secret registry: env NAME/_FILE plus file-credentials under $STAGEFLOW_HOME. */
33
+ export function loadSecretRegistry(env = process.env, home = globalStageflowHome()) {
34
+ const registry = new Map();
35
+ const fileCreds = readFileCredentialRegistry(home);
36
+ for (const [name, entry] of Object.entries(fileCreds)) {
37
+ if (isForeverDeniedSecret(name))
38
+ continue;
39
+ registry.set(name, {
40
+ kind: "file",
41
+ name,
42
+ source: entry.source,
43
+ pointerVar: entry.pointerVar,
44
+ });
45
+ }
46
+ // Well-known env secrets are always registered when present or when declared
47
+ // (presence checked at resolve time). Register common names so validate can
48
+ // distinguish unknown vs unavailable.
49
+ for (const name of [
50
+ "GITHUB_TOKEN",
51
+ "GH_TOKEN",
52
+ "NPM_TOKEN",
53
+ "NODE_AUTH_TOKEN",
54
+ ]) {
55
+ if (!registry.has(name)) {
56
+ registry.set(name, { kind: "env", name });
57
+ }
58
+ }
59
+ // Any Host env var with a sibling `_FILE` or non-empty value that looks like a
60
+ // registered secret name from STAGEFLOW_SECRET_REGISTRY (comma list) is also
61
+ // env-kind.
62
+ const extra = env.STAGEFLOW_SECRET_REGISTRY?.split(",") ?? [];
63
+ for (const raw of extra) {
64
+ const name = raw.trim();
65
+ if (!name || isForeverDeniedSecret(name) || registry.has(name))
66
+ continue;
67
+ registry.set(name, { kind: "env", name });
68
+ }
69
+ return registry;
70
+ }
71
+ /** Presence-only check (no materialisation) for preflight / start gates. */
72
+ export function assertSecretPresent(name, registry, hostEnv) {
73
+ if (isForeverDeniedSecret(name)) {
74
+ throw new Error(`secret "${name}" is permanently denied`);
75
+ }
76
+ const entry = registry.get(name);
77
+ if (entry === undefined) {
78
+ throw new Error(`stage.unknown_secret: "${name}"`);
79
+ }
80
+ if (entry.kind === "file") {
81
+ try {
82
+ readFileSync(entry.source, "utf8");
83
+ }
84
+ catch {
85
+ throw new SecretUnavailableError(name);
86
+ }
87
+ return;
88
+ }
89
+ const value = readEnvSecretValue(name, hostEnv);
90
+ if (value === undefined || value === "") {
91
+ throw new SecretUnavailableError(name);
92
+ }
93
+ }
94
+ function materialiseGitAskpassGrant(grantEnv, credentialDirs, opts) {
95
+ const destDir = attemptCredentialsDir(opts.attemptDir, opts.secretName);
96
+ const tokenFile = materialiseSecretBytes({
97
+ contents: opts.value,
98
+ destDir,
99
+ destBasename: "token",
100
+ });
101
+ credentialDirs.push(destDir);
102
+ grantEnv.GIT_ASKPASS = ensureStageAskpassHelper(opts.home);
103
+ grantEnv.GIT_TERMINAL_PROMPT = "0";
104
+ grantEnv.STAGEFLOW_GIT_ASKPASS_TOKEN_FILE = tokenFile.destPath;
105
+ }
106
+ export function resolveStageSecrets(input) {
107
+ const decls = input.decls ?? [];
108
+ const warnings = [];
109
+ const grantEnv = {};
110
+ const knownValues = [];
111
+ const credentialDirs = [];
112
+ const registeredNames = [...input.registry.keys()];
113
+ const declaredNames = decls.map((d) => d.name);
114
+ for (const decl of decls) {
115
+ if (isForeverDeniedSecret(decl.name)) {
116
+ throw new Error(`secret "${decl.name}" is permanently denied`);
117
+ }
118
+ const entry = input.registry.get(decl.name);
119
+ if (entry === undefined) {
120
+ throw new Error(`stage.unknown_secret: "${decl.name}"`);
121
+ }
122
+ if (entry.kind === "file") {
123
+ const destDir = attemptCredentialsDir(input.attemptDir, decl.name);
124
+ const homeRoot = input.home ?? globalStageflowHome();
125
+ const copied = materialiseCredentialFile({
126
+ sourcePath: entry.source,
127
+ destDir,
128
+ allowedRoot: homeRoot,
129
+ });
130
+ credentialDirs.push(destDir);
131
+ grantEnv[entry.pointerVar] = copied.destPath;
132
+ const contents = readFileSync(copied.destPath, "utf8");
133
+ if (shouldRegisterValue(contents)) {
134
+ knownValues.push({ name: decl.name, value: contents });
135
+ }
136
+ if (decl.as === "env") {
137
+ warnings.push(`secrets: ${decl.name} granted as: env (value present in stage environment)`);
138
+ grantEnv[decl.name] = contents;
139
+ }
140
+ continue;
141
+ }
142
+ const value = readEnvSecretValue(decl.name, input.hostEnv);
143
+ if (value === undefined || value === "") {
144
+ throw new SecretUnavailableError(decl.name);
145
+ }
146
+ knownValues.push({ name: decl.name, value });
147
+ const delivery = decl.as === "env" ? "env" : defaultSecretDelivery(decl.name);
148
+ if (delivery === "env") {
149
+ if (decl.as === "env") {
150
+ warnings.push(`secrets: ${decl.name} granted as: env (value present in stage environment)`);
151
+ }
152
+ grantEnv[decl.name] = value;
153
+ if (defaultSecretDelivery(decl.name) === "helper") {
154
+ materialiseGitAskpassGrant(grantEnv, credentialDirs, {
155
+ attemptDir: input.attemptDir,
156
+ secretName: decl.name,
157
+ value,
158
+ home: input.home,
159
+ });
160
+ }
161
+ continue;
162
+ }
163
+ materialiseGitAskpassGrant(grantEnv, credentialDirs, {
164
+ attemptDir: input.attemptDir,
165
+ secretName: decl.name,
166
+ value,
167
+ home: input.home,
168
+ });
169
+ }
170
+ return {
171
+ grants: {
172
+ env: grantEnv,
173
+ registeredSecretNames: registeredNames,
174
+ declaredSecretNames: declaredNames,
175
+ },
176
+ warnings,
177
+ knownValues,
178
+ credentialDirs,
179
+ };
180
+ }
181
+ export { cleanupCredentialsDir };
@@ -2,5 +2,6 @@ import { type RunStageOutcome } from "./stageRunner.js";
2
2
  import { type StageWorkerInput, type StageWorkerResult } from "./stageWorkerProtocol.js";
3
3
  export type { StageWorkerInput, StageWorkerResult } from "./stageWorkerProtocol.js";
4
4
  export declare function runStageWorker(input: StageWorkerInput): Promise<RunStageOutcome>;
5
- export declare function sendWorkerResult(result: StageWorkerResult): void;
6
- export declare function exitForOutcome(outcome: RunStageOutcome): never;
5
+ export declare function sendWorkerResult(result: StageWorkerResult): Promise<void>;
6
+ export declare function exitCodeForOutcome(outcome: RunStageOutcome): number;
7
+ export declare function exitForOutcome(outcome: RunStageOutcome): Promise<number>;
@@ -1,16 +1,21 @@
1
1
  import { resolveAgentPort } from "../agent/resolveAgentPort.js";
2
2
  import { asAgentBackendId } from "../agent/agentBackend.js";
3
3
  import { loadStageflowManifestOutcome } from "../config/loadStageflowManifest.js";
4
+ import { loadNamedSecretsFromAttemptDir } from "../logging/namedSecrets.js";
4
5
  import { definitionIdForInstance } from "../runstore/stageInstanceId.js";
6
+ import { attemptWorkspaceDir } from "../runstore/workspaceLayout.js";
5
7
  import { createRunStore } from "../runstore/createStore.js";
6
8
  import { globalStageflowHome } from "../project/globalHome.js";
7
9
  import { loadRunContext } from "./resumeReconstruct.js";
8
- import { bindPiAgentDirEnv, rootsForStageWorker, withResolvedAuthPath, } from "./stageRoots.js";
10
+ import { bindPiAgentDirEnv, overlayStageBindingEnv, rootsForStageWorker, stageBindingEnvFromRun, withResolvedAuthPath, } from "./stageRoots.js";
11
+ import { buildStageEnvironment } from "./stageEnvironment.js";
12
+ import { ensureStageCacheDirs } from "./stageCacheEnv.js";
13
+ import { PACKAGE_VERSION } from "../package-meta.js";
9
14
  import { runStage, isRunStageWaiting, } from "./stageRunner.js";
10
15
  import { openStageAttempt } from "./stageAttemptBootstrap.js";
11
16
  import { attemptContext } from "./stageAttemptContext.js";
12
17
  import { loadActiveFeedbackLoopContext } from "./feedbackLoopCoordinator.js";
13
- import { outcomeToWorkerResult, STAGE_WORKER_EXIT, } from "./stageWorkerProtocol.js";
18
+ import { outcomeToWorkerResult, SF_STAGE_WORKER, STAGE_WORKER_EXIT, } from "./stageWorkerProtocol.js";
14
19
  export async function runStageWorker(input) {
15
20
  const store = createRunStore({ rootDir: globalStageflowHome() });
16
21
  const { meta, task, loaded } = await loadRunContext(store, input.runId, input.rootDir);
@@ -33,8 +38,29 @@ export async function runStageWorker(input) {
33
38
  });
34
39
  const workspaceDir = store.getWorkspaceDir(input.runId);
35
40
  const checkoutRoot = meta.checkout_root;
41
+ const binding = input.env !== undefined
42
+ ? {
43
+ env: input.env,
44
+ kind: input.bindingKind ?? "unbound",
45
+ }
46
+ : stageBindingEnvFromRun({
47
+ meta,
48
+ task,
49
+ runWorkspaceDir: workspaceDir,
50
+ hostEnv: process.env,
51
+ });
52
+ const baseEnv = process.env[SF_STAGE_WORKER] === "1"
53
+ ? Object.fromEntries(Object.entries(process.env).filter((e) => e[1] !== undefined))
54
+ : buildStageEnvironment({
55
+ hostEnv: process.env,
56
+ cacheVars: ensureStageCacheDirs(),
57
+ packageVersion: PACKAGE_VERSION,
58
+ }).env;
59
+ const stageEnv = Object.fromEntries(Object.entries(overlayStageBindingEnv(baseEnv, binding.env, binding.kind))
60
+ .filter((e) => e[1] !== undefined));
36
61
  const mode = input.mode ?? "run";
37
62
  const attempt = input.attempt ?? 1;
63
+ loadNamedSecretsFromAttemptDir(attemptWorkspaceDir(workspaceDir, input.stageId, attempt));
38
64
  const attemptCtx = input.attempt !== undefined ? attemptContext(input.attempt) : undefined;
39
65
  const eventOptions = { attempt };
40
66
  const roots = withResolvedAuthPath(rootsForStageWorker(workspaceDir, input.stageId, stage.model, checkoutRoot, attemptCtx), input.rootDir);
@@ -57,6 +83,7 @@ export async function runStageWorker(input) {
57
83
  roots,
58
84
  resumeToken: input.sessionFilePath,
59
85
  sessionMode: "waiting_resume",
86
+ stageEnv,
60
87
  onActivity: (event) => {
61
88
  void store.appendStageEvent(input.runId, input.stageId, event, eventOptions);
62
89
  },
@@ -83,6 +110,7 @@ export async function runStageWorker(input) {
83
110
  factoryCwd: input.rootDir,
84
111
  operatorCatalog: input.operatorCatalog,
85
112
  skipGates: input.skipGates,
113
+ stageEnv,
86
114
  });
87
115
  return outcome;
88
116
  }
@@ -116,6 +144,7 @@ export async function runStageWorker(input) {
116
144
  ...(resumeToken !== undefined ? { resumeToken } : {}),
117
145
  sessionMode: mode,
118
146
  ...(feedbackLoopContext !== undefined ? { feedbackLoopContext } : {}),
147
+ stageEnv,
119
148
  onActivity: (event) => {
120
149
  void store.appendStageEvent(input.runId, input.stageId, event, eventOptions);
121
150
  },
@@ -140,6 +169,7 @@ export async function runStageWorker(input) {
140
169
  factoryCwd: input.rootDir,
141
170
  operatorCatalog: input.operatorCatalog,
142
171
  skipGates: input.skipGates,
172
+ stageEnv,
143
173
  });
144
174
  }
145
175
  return runStage({
@@ -158,6 +188,7 @@ export async function runStageWorker(input) {
158
188
  factoryCwd: input.rootDir,
159
189
  operatorCatalog: input.operatorCatalog,
160
190
  skipGates: input.skipGates,
191
+ stageEnv,
161
192
  });
162
193
  }
163
194
  finally {
@@ -165,18 +196,35 @@ export async function runStageWorker(input) {
165
196
  }
166
197
  }
167
198
  export function sendWorkerResult(result) {
168
- if (typeof process.send === "function") {
169
- process.send(result);
199
+ if (typeof process.send !== "function") {
200
+ return Promise.resolve();
170
201
  }
202
+ return new Promise((resolve) => {
203
+ try {
204
+ const ok = process.send(result, (err) => {
205
+ void err;
206
+ resolve();
207
+ });
208
+ if (ok === false) {
209
+ resolve();
210
+ }
211
+ }
212
+ catch {
213
+ resolve();
214
+ }
215
+ });
171
216
  }
172
- export function exitForOutcome(outcome) {
173
- const message = outcomeToWorkerResult(outcome);
174
- sendWorkerResult(message);
217
+ export function exitCodeForOutcome(outcome) {
175
218
  if (isRunStageWaiting(outcome)) {
176
- process.exit(STAGE_WORKER_EXIT.WAITING);
219
+ return STAGE_WORKER_EXIT.WAITING;
177
220
  }
178
221
  if (outcome.ok) {
179
- process.exit(STAGE_WORKER_EXIT.SUCCEEDED);
222
+ return STAGE_WORKER_EXIT.SUCCEEDED;
180
223
  }
181
- process.exit(STAGE_WORKER_EXIT.FAILED);
224
+ return STAGE_WORKER_EXIT.FAILED;
225
+ }
226
+ export async function exitForOutcome(outcome) {
227
+ const message = outcomeToWorkerResult(outcome);
228
+ await sendWorkerResult(message);
229
+ return exitCodeForOutcome(outcome);
182
230
  }
@@ -1,11 +1,14 @@
1
1
  import type { RunStageOutcome } from "./stageRunner.js";
2
2
  import type { OperatorCatalog } from "./stageAttemptBootstrap.js";
3
+ import type { DerivedBindingKind } from "./stageRoots.js";
3
4
  export declare const SF_STAGE_WORKER = "SF_STAGE_WORKER";
4
5
  export declare const STAGE_WORKER_EXIT: {
5
6
  readonly SUCCEEDED: 0;
6
7
  readonly FAILED: 1;
7
8
  readonly WAITING: 2;
8
9
  };
10
+ export declare const PROCESS_EXIT_FORCE_MS = 1000;
11
+ export declare function scheduleExitWithDrain(code: number): void;
9
12
  export type StageWorkerInput = {
10
13
  runId: string;
11
14
  stageId: string;
@@ -16,6 +19,8 @@ export type StageWorkerInput = {
16
19
  sessionFilePath?: string;
17
20
  operatorCatalog?: OperatorCatalog;
18
21
  skipGates?: boolean;
22
+ env?: Record<string, string>;
23
+ bindingKind?: DerivedBindingKind;
19
24
  };
20
25
  export type StageWorkerResult = {
21
26
  type: "succeeded";
@@ -4,6 +4,14 @@ export const STAGE_WORKER_EXIT = {
4
4
  FAILED: 1,
5
5
  WAITING: 2,
6
6
  };
7
+ export const PROCESS_EXIT_FORCE_MS = 1_000;
8
+ export function scheduleExitWithDrain(code) {
9
+ process.exitCode = code;
10
+ const timer = setTimeout(() => {
11
+ process.exit(code);
12
+ }, PROCESS_EXIT_FORCE_MS);
13
+ timer.unref();
14
+ }
7
15
  export function outcomeToWorkerResult(outcome) {
8
16
  if ("waiting" in outcome && outcome.waiting) {
9
17
  return { type: "waiting" };
@@ -0,0 +1,32 @@
1
+ import type { InlinePipelineDefinition } from "../types/pipeline.js";
2
+ /** Cap for inline pipeline body + skills start payload (1 MiB). */
3
+ export declare const START_PAYLOAD_MAX_BYTES = 1048576;
4
+ export declare const START_TOKEN_REJECTED: "start.token_rejected";
5
+ export declare const INLINE_PIPELINE_TOO_LARGE: "inline_pipeline_too_large";
6
+ export type PipelinePersistenceFields = {
7
+ pipelineSource: "inline" | "path";
8
+ pipelineBody?: string;
9
+ };
10
+ export declare function pipelineBodyBytes(fields: PipelinePersistenceFields): number;
11
+ export type PipelinePersistenceResult = {
12
+ ok: true;
13
+ fields: PipelinePersistenceFields;
14
+ } | {
15
+ ok: false;
16
+ code: typeof INLINE_PIPELINE_TOO_LARGE;
17
+ bytes: number;
18
+ maxBytes: number;
19
+ };
20
+ export declare function pipelinePersistenceForStart(pipeline: string | InlinePipelineDefinition): PipelinePersistenceResult;
21
+ /** MCP prefers `checkout`; `checkout_override` remains a temporary alias. */
22
+ export declare function pickCheckoutOverride(args: {
23
+ checkout?: string;
24
+ checkout_override?: string;
25
+ }): string | undefined;
26
+ export declare function isTokenShapedKey(key: string): boolean;
27
+ export declare function findTokenShapedField(value: unknown, pathPrefix?: string): string | undefined;
28
+ export declare function tokenRejectedPayload(field: string): {
29
+ error: string;
30
+ code: typeof START_TOKEN_REJECTED;
31
+ field: string;
32
+ };
@@ -0,0 +1,59 @@
1
+ /** Cap for inline pipeline body + skills start payload (1 MiB). */
2
+ export const START_PAYLOAD_MAX_BYTES = 1_048_576;
3
+ export const START_TOKEN_REJECTED = "start.token_rejected";
4
+ export const INLINE_PIPELINE_TOO_LARGE = "inline_pipeline_too_large";
5
+ export function pipelineBodyBytes(fields) {
6
+ if (fields.pipelineBody === undefined)
7
+ return 0;
8
+ return Buffer.byteLength(fields.pipelineBody, "utf8");
9
+ }
10
+ export function pipelinePersistenceForStart(pipeline) {
11
+ if (typeof pipeline === "string") {
12
+ return { ok: true, fields: { pipelineSource: "path" } };
13
+ }
14
+ const body = JSON.stringify(pipeline);
15
+ const bytes = Buffer.byteLength(body, "utf8");
16
+ if (bytes > START_PAYLOAD_MAX_BYTES) {
17
+ return {
18
+ ok: false,
19
+ code: INLINE_PIPELINE_TOO_LARGE,
20
+ bytes,
21
+ maxBytes: START_PAYLOAD_MAX_BYTES,
22
+ };
23
+ }
24
+ return {
25
+ ok: true,
26
+ fields: { pipelineSource: "inline", pipelineBody: body },
27
+ };
28
+ }
29
+ /** MCP prefers `checkout`; `checkout_override` remains a temporary alias. */
30
+ export function pickCheckoutOverride(args) {
31
+ return args.checkout ?? args.checkout_override;
32
+ }
33
+ const TOKEN_SHAPED_KEY = /^(token|github_token|gh_token|access_token|personal_access_token|pat|clone_token|git_token|api_token|auth_token)$/i;
34
+ export function isTokenShapedKey(key) {
35
+ return TOKEN_SHAPED_KEY.test(key);
36
+ }
37
+ export function findTokenShapedField(value, pathPrefix = "") {
38
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
39
+ return undefined;
40
+ }
41
+ for (const [key, child] of Object.entries(value)) {
42
+ const path = pathPrefix ? `${pathPrefix}.${key}` : key;
43
+ if (isTokenShapedKey(key))
44
+ return path;
45
+ if (key === "task" && child !== null && typeof child === "object") {
46
+ const nested = findTokenShapedField(child, path);
47
+ if (nested !== undefined)
48
+ return nested;
49
+ }
50
+ }
51
+ return undefined;
52
+ }
53
+ export function tokenRejectedPayload(field) {
54
+ return {
55
+ error: `Clone credentials must stay on the Host; reject token-shaped start field "${field}"`,
56
+ code: START_TOKEN_REJECTED,
57
+ field,
58
+ };
59
+ }
@@ -1,11 +1,11 @@
1
1
  import path from "node:path";
2
2
  import { stringify as stringifyYaml } from "yaml";
3
- import { parseTaskFile } from "../config/loadTask.js";
3
+ import { coerceTaskFile } from "../config/loadTask.js";
4
4
  import { relPath } from "../config/validateCatalog.js";
5
5
  import { payloadInstanceMismatch } from "../envelope/payloadSchema.js";
6
6
  import { resolveTaskPath } from "./pipelineRunner.js";
7
7
  export function isTaskFile(value) {
8
- return parseTaskFile(value, "task").ok;
8
+ return coerceTaskFile(value) !== undefined;
9
9
  }
10
10
  export function taskFileToYaml(task) {
11
11
  const doc = {
@@ -18,6 +18,15 @@ export function taskFileToYaml(task) {
18
18
  doc.constraints = task.constraints;
19
19
  if (task.checkout !== undefined)
20
20
  doc.checkout = task.checkout;
21
+ if (task.repository !== undefined)
22
+ doc.repository = task.repository;
23
+ if (task.ref !== undefined)
24
+ doc.ref = task.ref;
25
+ if (task.run_branch_template !== undefined) {
26
+ doc.run_branch_template = task.run_branch_template;
27
+ }
28
+ if (task.git_identity !== undefined)
29
+ doc.git_identity = task.git_identity;
21
30
  if (task.input !== undefined)
22
31
  doc.input = task.input;
23
32
  return stringifyYaml(doc);
@@ -78,9 +87,9 @@ export function resolveStartTaskInput(input, cwd) {
78
87
  }
79
88
  return { kind: "path", taskPath: resolveTaskPath(input.task, cwd) };
80
89
  }
81
- const parsed = parseTaskFile(input.task, "task");
82
- if (parsed.ok) {
83
- return { kind: "yaml", taskYaml: taskFileToYaml(parsed.value) };
90
+ const coerced = coerceTaskFile(input.task);
91
+ if (coerced !== undefined) {
92
+ return { kind: "yaml", taskYaml: taskFileToYaml(coerced) };
84
93
  }
85
94
  throw new Error("task path, task object, or taskYaml is required");
86
95
  }
@@ -28,4 +28,5 @@ export declare function createVerifiedStageExecution(options: {
28
28
  stage: StageConfig;
29
29
  dag?: ResolvedPipelineDag;
30
30
  roots: StageRoots;
31
+ commandEnv?: NodeJS.ProcessEnv;
31
32
  }): VerifiedStageExecution;
@@ -42,7 +42,7 @@ function gateDecisionsForAttempt(options) {
42
42
  * module in stageRunner.
43
43
  */
44
44
  export function createVerifiedStageExecution(options) {
45
- const { store, runId, stageId, attempt, stage, dag, roots } = options;
45
+ const { store, runId, stageId, attempt, stage, dag, roots, commandEnv } = options;
46
46
  const contract = dag?.nodes.find((node) => node.id === stageId)?.completion;
47
47
  let checkout;
48
48
  let checkoutBefore;
@@ -82,6 +82,7 @@ export function createVerifiedStageExecution(options) {
82
82
  payloadSchema: stage.payload_schema,
83
83
  artifactsDir,
84
84
  commandWorkingDirectory: roots.cwd,
85
+ ...(commandEnv !== undefined ? { commandEnv } : {}),
85
86
  gates: gateDecisionsForAttempt({ store, runId, stageId, attempt }),
86
87
  checkout,
87
88
  checkoutBefore,