stageflow 0.23.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 (511) hide show
  1. package/README.md +24 -5
  2. package/dist/a2a/configDiscovery.d.ts +9 -0
  3. package/dist/a2a/configDiscovery.js +18 -0
  4. package/dist/a2a/contracts.d.ts +94 -0
  5. package/dist/a2a/contracts.js +164 -0
  6. package/dist/a2a/limits.d.ts +14 -0
  7. package/dist/a2a/limits.js +29 -0
  8. package/dist/a2a/registry.d.ts +46 -0
  9. package/dist/a2a/registry.js +178 -0
  10. package/dist/a2a/server.d.ts +36 -0
  11. package/dist/a2a/server.js +344 -0
  12. package/dist/a2a/service.d.ts +97 -0
  13. package/dist/a2a/service.js +545 -0
  14. package/dist/a2a/store.d.ts +109 -0
  15. package/dist/a2a/store.js +202 -0
  16. package/dist/agent/activity.d.ts +3 -0
  17. package/dist/agent/bootProviderConfig.d.ts +21 -0
  18. package/dist/agent/bootProviderConfig.js +74 -0
  19. package/dist/agent/piAdapter.d.ts +9 -1
  20. package/dist/agent/piAdapter.js +58 -3
  21. package/dist/agent/piIsolatedMcp.d.ts +2 -0
  22. package/dist/agent/piIsolatedMcp.js +10 -2
  23. package/dist/agent/port.d.ts +6 -0
  24. package/dist/agent/streamLogRedact.d.ts +1 -1
  25. package/dist/agent/streamLogRedact.js +4 -11
  26. package/dist/cli/a2aCommand.d.ts +8 -0
  27. package/dist/cli/a2aCommand.js +131 -0
  28. package/dist/cli/artifactCommand.js +9 -16
  29. package/dist/cli/backupCommand.d.ts +9 -0
  30. package/dist/cli/backupCommand.js +111 -0
  31. package/dist/cli/debugRunCommand.d.ts +10 -0
  32. package/dist/cli/debugRunCommand.js +109 -0
  33. package/dist/cli/doctorCommand.d.ts +32 -0
  34. package/dist/cli/doctorCommand.js +453 -0
  35. package/dist/cli/envelopeCommand.js +9 -2
  36. package/dist/cli/exportAllCommand.d.ts +55 -0
  37. package/dist/cli/exportAllCommand.js +218 -0
  38. package/dist/cli/exportRunCommand.js +23 -21
  39. package/dist/cli/hostClient.d.ts +21 -3
  40. package/dist/cli/hostClient.js +93 -12
  41. package/dist/cli/initTemplates.d.ts +1 -1
  42. package/dist/cli/initTemplates.js +8 -1
  43. package/dist/cli/resolveSafeOutPath.d.ts +2 -0
  44. package/dist/cli/resolveSafeOutPath.js +40 -0
  45. package/dist/cli/restoreCommand.d.ts +10 -0
  46. package/dist/cli/restoreCommand.js +90 -0
  47. package/dist/cli/runCommand.d.ts +3 -2
  48. package/dist/cli/runCommand.js +93 -11
  49. package/dist/cli/runOutput.d.ts +4 -0
  50. package/dist/cli/runOutput.js +33 -7
  51. package/dist/cli/runStageCommand.d.ts +34 -0
  52. package/dist/cli/runStageCommand.js +426 -0
  53. package/dist/cli/runsCommand.d.ts +1 -1
  54. package/dist/cli/runsCommand.js +162 -8
  55. package/dist/cli.js +190 -30
  56. package/dist/config/catalogRelativePath.d.ts +62 -0
  57. package/dist/config/catalogRelativePath.js +118 -0
  58. package/dist/config/configOrigin.d.ts +27 -0
  59. package/dist/config/configOrigin.js +59 -0
  60. package/dist/config/hostConfig.d.ts +55 -0
  61. package/dist/config/hostConfig.js +333 -0
  62. package/dist/config/listSkills.d.ts +26 -0
  63. package/dist/config/listSkills.js +190 -3
  64. package/dist/config/loadOutcome.d.ts +1 -1
  65. package/dist/config/loadPipeline.js +23 -2
  66. package/dist/config/loadStage.js +18 -0
  67. package/dist/config/loadTask.d.ts +2 -0
  68. package/dist/config/loadTask.js +57 -18
  69. package/dist/config/materializeStageModels.js +5 -1
  70. package/dist/config/mergePipelineIncludes.d.ts +3 -0
  71. package/dist/config/mergePipelineIncludes.js +19 -0
  72. package/dist/config/migrateYaml.js +6 -11
  73. package/dist/config/multiProjectCatalog.d.ts +30 -0
  74. package/dist/config/multiProjectCatalog.js +142 -0
  75. package/dist/config/normalizePipelineStageEntry.js +20 -1
  76. package/dist/config/pipelineStageKeys.js +2 -0
  77. package/dist/config/resolveCatalogRoots.d.ts +27 -0
  78. package/dist/config/resolveCatalogRoots.js +76 -0
  79. package/dist/config/resolveModel.d.ts +7 -1
  80. package/dist/config/resolveModel.js +11 -1
  81. package/dist/config/resolveStageMcpServers.d.ts +9 -1
  82. package/dist/config/resolveStageMcpServers.js +36 -9
  83. package/dist/config/secretFromEnvOrFile.d.ts +9 -0
  84. package/dist/config/secretFromEnvOrFile.js +34 -0
  85. package/dist/config/seededCatalog.d.ts +11 -0
  86. package/dist/config/seededCatalog.js +29 -0
  87. package/dist/config/toolRequires.d.ts +24 -0
  88. package/dist/config/toolRequires.js +115 -0
  89. package/dist/config/validateCatalog.d.ts +5 -1
  90. package/dist/config/validateCatalog.js +140 -1
  91. package/dist/diagnostics/checks.d.ts +39 -0
  92. package/dist/diagnostics/checks.js +135 -0
  93. package/dist/errors/codes.d.ts +14 -0
  94. package/dist/errors/codes.js +59 -0
  95. package/dist/git/cache.d.ts +14 -0
  96. package/dist/git/cache.js +150 -0
  97. package/dist/git/credentials.d.ts +9 -0
  98. package/dist/git/credentials.js +88 -0
  99. package/dist/git/errors.d.ts +20 -0
  100. package/dist/git/errors.js +94 -0
  101. package/dist/git/exec.d.ts +16 -0
  102. package/dist/git/exec.js +135 -0
  103. package/dist/git/operations.d.ts +29 -0
  104. package/dist/git/operations.js +153 -0
  105. package/dist/git/version.d.ts +1 -0
  106. package/dist/git/version.js +10 -0
  107. package/dist/logging/audit.d.ts +16 -0
  108. package/dist/logging/audit.js +19 -0
  109. package/dist/logging/commonWords.d.ts +2 -0
  110. package/dist/logging/commonWords.js +13 -0
  111. package/dist/logging/logger.d.ts +49 -0
  112. package/dist/logging/logger.js +193 -0
  113. package/dist/logging/namedSecrets.d.ts +6 -0
  114. package/dist/logging/namedSecrets.js +58 -0
  115. package/dist/logging/redact.d.ts +13 -0
  116. package/dist/logging/redact.js +88 -0
  117. package/dist/mcp/catalogTools.js +591 -76
  118. package/dist/mcp/checkoutTools.d.ts +52 -0
  119. package/dist/mcp/checkoutTools.js +332 -0
  120. package/dist/mcp/controlTools.js +179 -5
  121. package/dist/mcp/deps.d.ts +1 -0
  122. package/dist/mcp/getStartedTool.d.ts +3 -0
  123. package/dist/mcp/getStartedTool.js +62 -0
  124. package/dist/mcp/readArtifact.d.ts +11 -0
  125. package/dist/mcp/readArtifact.js +46 -30
  126. package/dist/mcp/server.js +1 -0
  127. package/dist/mcp/tools.js +4 -0
  128. package/dist/net/proxy.d.ts +14 -0
  129. package/dist/net/proxy.js +78 -0
  130. package/dist/package-meta.d.ts +2 -1
  131. package/dist/package-meta.js +2 -1
  132. package/dist/preflight/bash.d.ts +8 -0
  133. package/dist/preflight/bash.js +34 -0
  134. package/dist/preflight/claudeRoot.d.ts +11 -0
  135. package/dist/preflight/claudeRoot.js +23 -0
  136. package/dist/preflight/mcpCommands.d.ts +8 -0
  137. package/dist/preflight/mcpCommands.js +36 -0
  138. package/dist/preflight/pipelinePreflight.d.ts +40 -0
  139. package/dist/preflight/pipelinePreflight.js +192 -0
  140. package/dist/preflight/tls.d.ts +6 -0
  141. package/dist/preflight/tls.js +18 -0
  142. package/dist/preflight/toolchain.d.ts +45 -0
  143. package/dist/preflight/toolchain.js +219 -0
  144. package/dist/project/findProjectRoot.js +3 -4
  145. package/dist/project/globalHome.d.ts +7 -0
  146. package/dist/project/globalHome.js +63 -2
  147. package/dist/project/resolveProjectContext.js +2 -2
  148. package/dist/project/resolveStageflowContext.js +2 -2
  149. package/dist/projection/projectRun.d.ts +2 -1
  150. package/dist/projection/projectRun.js +1 -0
  151. package/dist/runstore/assertTmpdir.d.ts +1 -0
  152. package/dist/runstore/assertTmpdir.js +21 -0
  153. package/dist/runstore/backup.d.ts +52 -0
  154. package/dist/runstore/backup.js +292 -0
  155. package/dist/runstore/createStore.d.ts +27 -0
  156. package/dist/runstore/createStore.js +75 -7
  157. package/dist/runstore/debugBundle.d.ts +60 -0
  158. package/dist/runstore/debugBundle.js +143 -0
  159. package/dist/runstore/diskUsage.d.ts +50 -0
  160. package/dist/runstore/diskUsage.js +214 -0
  161. package/dist/runstore/exportRunPayload.d.ts +8 -0
  162. package/dist/runstore/exportRunPayload.js +23 -0
  163. package/dist/runstore/normalizeCatalogPath.d.ts +1 -0
  164. package/dist/runstore/normalizeCatalogPath.js +10 -0
  165. package/dist/runstore/paths.d.ts +5 -0
  166. package/dist/runstore/paths.js +145 -1
  167. package/dist/runstore/port.d.ts +119 -8
  168. package/dist/runstore/port.js +8 -1
  169. package/dist/runstore/restore.d.ts +81 -0
  170. package/dist/runstore/restore.js +355 -0
  171. package/dist/runstore/retention.d.ts +27 -0
  172. package/dist/runstore/retention.js +74 -0
  173. package/dist/runstore/runManifest.d.ts +146 -0
  174. package/dist/runstore/runManifest.js +288 -0
  175. package/dist/runstore/runProjection.d.ts +3 -1
  176. package/dist/runstore/runProjection.js +63 -1
  177. package/dist/runstore/sqlite/SqliteRunStore.d.ts +37 -3
  178. package/dist/runstore/sqlite/SqliteRunStore.js +401 -227
  179. package/dist/runstore/sqlite/applyStorePragmas.d.ts +39 -0
  180. package/dist/runstore/sqlite/applyStorePragmas.js +145 -0
  181. package/dist/runstore/sqlite/migrations/001-baseline.d.ts +7 -0
  182. package/dist/runstore/sqlite/migrations/001-baseline.js +10 -0
  183. package/dist/runstore/sqlite/migrations/002-repository-binding.d.ts +7 -0
  184. package/dist/runstore/sqlite/migrations/002-repository-binding.js +29 -0
  185. package/dist/runstore/sqlite/migrations/003-run-lifecycle.d.ts +7 -0
  186. package/dist/runstore/sqlite/migrations/003-run-lifecycle.js +28 -0
  187. package/dist/runstore/sqlite/migrations/004-auto-resume-count.d.ts +7 -0
  188. package/dist/runstore/sqlite/migrations/004-auto-resume-count.js +19 -0
  189. package/dist/runstore/sqlite/migrations/005-config-origins.d.ts +7 -0
  190. package/dist/runstore/sqlite/migrations/005-config-origins.js +19 -0
  191. package/dist/runstore/sqlite/migrations/006-pipeline-body-and-caller.d.ts +7 -0
  192. package/dist/runstore/sqlite/migrations/006-pipeline-body-and-caller.js +28 -0
  193. package/dist/runstore/sqlite/migrations/007-projects-registry.d.ts +7 -0
  194. package/dist/runstore/sqlite/migrations/007-projects-registry.js +31 -0
  195. package/dist/runstore/sqlite/migrations/index.d.ts +21 -0
  196. package/dist/runstore/sqlite/migrations/index.js +72 -0
  197. package/dist/runstore/sqlite/schema.d.ts +20 -1
  198. package/dist/runstore/sqlite/schema.js +283 -1
  199. package/dist/runstore/sqlite/storeOpenError.d.ts +6 -0
  200. package/dist/runstore/sqlite/storeOpenError.js +11 -0
  201. package/dist/runstore/sqlite/storeSchemaError.d.ts +5 -0
  202. package/dist/runstore/sqlite/storeSchemaError.js +8 -0
  203. package/dist/runstore/stageExecution.js +3 -0
  204. package/dist/runstore/storeFilesystem.d.ts +32 -0
  205. package/dist/runstore/storeFilesystem.js +148 -0
  206. package/dist/runstore/submission.d.ts +11 -0
  207. package/dist/runstore/submission.js +7 -0
  208. package/dist/runstore/trackProjection.js +2 -0
  209. package/dist/runstore/workspaceLayout.d.ts +13 -0
  210. package/dist/runstore/workspaceLayout.js +76 -0
  211. package/dist/runtime/completionCheckRunner.d.ts +3 -0
  212. package/dist/runtime/completionCheckRunner.js +45 -7
  213. package/dist/runtime/containerLimits.d.ts +16 -0
  214. package/dist/runtime/containerLimits.js +84 -0
  215. package/dist/runtime/credentialMaterialisation.d.ts +28 -0
  216. package/dist/runtime/credentialMaterialisation.js +128 -0
  217. package/dist/runtime/gitCheckoutCapability.js +7 -13
  218. package/dist/runtime/pipelineRunner.d.ts +38 -1
  219. package/dist/runtime/pipelineRunner.js +179 -15
  220. package/dist/runtime/pipelineScheduler.d.ts +15 -0
  221. package/dist/runtime/pipelineScheduler.js +167 -42
  222. package/dist/runtime/reloadRunCatalog.js +12 -1
  223. package/dist/runtime/repositoryMaterialize.d.ts +47 -0
  224. package/dist/runtime/repositoryMaterialize.js +235 -0
  225. package/dist/runtime/resolveEnvelopeRef.d.ts +30 -0
  226. package/dist/runtime/resolveEnvelopeRef.js +79 -0
  227. package/dist/runtime/resumeTimedOut.d.ts +1 -1
  228. package/dist/runtime/resumeTimedOut.js +5 -2
  229. package/dist/runtime/runChangeBus.js +9 -0
  230. package/dist/runtime/runDeletion.d.ts +7 -0
  231. package/dist/runtime/runDeletion.js +37 -0
  232. package/dist/runtime/runManager.d.ts +179 -4
  233. package/dist/runtime/runManager.js +1644 -83
  234. package/dist/runtime/runRetentionSweep.d.ts +31 -0
  235. package/dist/runtime/runRetentionSweep.js +328 -0
  236. package/dist/runtime/runRetryCoordinator.d.ts +2 -0
  237. package/dist/runtime/runRetryCoordinator.js +19 -2
  238. package/dist/runtime/runSkills.d.ts +40 -0
  239. package/dist/runtime/runSkills.js +173 -0
  240. package/dist/runtime/stageAttemptBootstrap.d.ts +5 -1
  241. package/dist/runtime/stageAttemptBootstrap.js +195 -24
  242. package/dist/runtime/stageCacheEnv.d.ts +6 -0
  243. package/dist/runtime/stageCacheEnv.js +34 -0
  244. package/dist/runtime/stageConcurrency.d.ts +2 -1
  245. package/dist/runtime/stageConcurrency.js +4 -2
  246. package/dist/runtime/stageEnvironment.d.ts +38 -0
  247. package/dist/runtime/stageEnvironment.js +193 -0
  248. package/dist/runtime/stageProcessLauncher.d.ts +18 -0
  249. package/dist/runtime/stageProcessLauncher.js +194 -38
  250. package/dist/runtime/stageRecovery.d.ts +3 -1
  251. package/dist/runtime/stageRecovery.js +19 -4
  252. package/dist/runtime/stageRoots.d.ts +38 -1
  253. package/dist/runtime/stageRoots.js +118 -0
  254. package/dist/runtime/stageRunner.d.ts +1 -0
  255. package/dist/runtime/stageRunner.js +3 -1
  256. package/dist/runtime/stageSecretDecl.d.ts +9 -0
  257. package/dist/runtime/stageSecretDecl.js +102 -0
  258. package/dist/runtime/stageSecrets.d.ts +40 -0
  259. package/dist/runtime/stageSecrets.js +181 -0
  260. package/dist/runtime/stageWorker.d.ts +3 -2
  261. package/dist/runtime/stageWorker.js +58 -10
  262. package/dist/runtime/stageWorkerProtocol.d.ts +5 -0
  263. package/dist/runtime/stageWorkerProtocol.js +8 -0
  264. package/dist/runtime/startPayload.d.ts +32 -0
  265. package/dist/runtime/startPayload.js +59 -0
  266. package/dist/runtime/taskInput.js +14 -5
  267. package/dist/runtime/verifiedStageExecution.d.ts +1 -0
  268. package/dist/runtime/verifiedStageExecution.js +2 -1
  269. package/dist/runtime/workspaceBinding.d.ts +20 -0
  270. package/dist/runtime/workspaceBinding.js +60 -0
  271. package/dist/server/allowedHosts.d.ts +19 -0
  272. package/dist/server/allowedHosts.js +179 -0
  273. package/dist/server/bootstrap.d.ts +38 -2
  274. package/dist/server/bootstrap.js +219 -4
  275. package/dist/server/controlToken.d.ts +47 -0
  276. package/dist/server/controlToken.js +241 -0
  277. package/dist/server/createHttpHost.d.ts +11 -2
  278. package/dist/server/createHttpHost.js +109 -10
  279. package/dist/server/ensureGlobalService.d.ts +5 -1
  280. package/dist/server/ensureGlobalService.js +35 -1
  281. package/dist/server/healthSurfaces.d.ts +6 -0
  282. package/dist/server/healthSurfaces.js +98 -0
  283. package/dist/server/http.d.ts +12 -1
  284. package/dist/server/http.js +1085 -485
  285. package/dist/server/listenHost.d.ts +5 -0
  286. package/dist/server/listenHost.js +56 -0
  287. package/dist/server/mcpHost.d.ts +10 -1
  288. package/dist/server/mcpHost.js +36 -6
  289. package/dist/server/operatorResults.d.ts +2 -2
  290. package/dist/server/operatorResults.js +40 -29
  291. package/dist/server/requestAuthContext.d.ts +12 -0
  292. package/dist/server/requestAuthContext.js +21 -0
  293. package/dist/server/shutdown.d.ts +69 -0
  294. package/dist/server/shutdown.js +219 -0
  295. package/dist/types/pipeline.d.ts +7 -0
  296. package/dist/types/stage.d.ts +8 -0
  297. package/dist/types/task.d.ts +8 -0
  298. package/dist/ui/assets/index-B6hlSXXn.js +120 -0
  299. package/dist/ui/index.html +1 -1
  300. package/examples/README.md +98 -0
  301. package/examples/archify-on-pr/README.md +213 -0
  302. package/examples/archify-on-pr/archify-on-pr.pipeline.yaml +19 -0
  303. package/examples/archify-on-pr/archify-on-pr.task.yaml +19 -0
  304. package/examples/archify-on-pr/author-diagrams.yaml +106 -0
  305. package/examples/archify-on-pr/detect-changes.yaml +89 -0
  306. package/examples/ci-validate/README.md +35 -0
  307. package/examples/ci-validate/check.yaml +13 -0
  308. package/examples/ci-validate/ci-demo.pipeline.yaml +4 -0
  309. package/examples/ci-validate/validate.sh +5 -0
  310. package/examples/compliance-review-demo/README.md +125 -0
  311. package/examples/compliance-review-demo/compliance-review-demo.pipeline.yaml +29 -0
  312. package/examples/compliance-review-demo/connect-account.yaml +47 -0
  313. package/examples/compliance-review-demo/map-to-control.yaml +123 -0
  314. package/examples/compliance-review-demo/poll-bucket-config.yaml +103 -0
  315. package/examples/compliance-review-demo/publish-report.yaml +79 -0
  316. package/examples/compliance-review-demo/review-verdict.yaml +122 -0
  317. package/examples/compliance-review-demo/run-compliance-check.yaml +96 -0
  318. package/examples/compliance-review-demo/store-evidence.yaml +143 -0
  319. package/examples/conditional-fork/README.md +89 -0
  320. package/examples/conditional-fork/branch-a.yaml +13 -0
  321. package/examples/conditional-fork/branch-b.yaml +13 -0
  322. package/examples/conditional-fork/decide.yaml +35 -0
  323. package/examples/conditional-fork/fork-demo.pipeline.yaml +20 -0
  324. package/examples/conditional-fork/fork-demo.task.yaml +8 -0
  325. package/examples/context7-mcp/README.md +42 -0
  326. package/examples/context7-mcp/context7-mcp.pipeline.yaml +15 -0
  327. package/examples/context7-mcp/context7-mcp.task.yaml +4 -0
  328. package/examples/context7-mcp/fetch-docs.yaml +52 -0
  329. package/examples/context7-mcp/resolve-library.yaml +31 -0
  330. package/examples/context7-mcp/write-brief.yaml +52 -0
  331. package/examples/feature-loop/README.md +104 -0
  332. package/examples/feature-loop/address-feedback.yaml +148 -0
  333. package/examples/feature-loop/align.yaml +116 -0
  334. package/examples/feature-loop/decompose.yaml +86 -0
  335. package/examples/feature-loop/default-model-hierarchy.task.yaml +58 -0
  336. package/examples/feature-loop/feature-loop.pipeline.yaml +140 -0
  337. package/examples/feature-loop/feature-loop.task.yaml +25 -0
  338. package/examples/feature-loop/implement.yaml +128 -0
  339. package/examples/feature-loop/plan.yaml +111 -0
  340. package/examples/feature-loop/publish.yaml +167 -0
  341. package/examples/feature-loop/review.yaml +146 -0
  342. package/examples/feature-loop/verify.yaml +106 -0
  343. package/examples/feedback-loop/README.md +72 -0
  344. package/examples/feedback-loop/VERIFY.md +208 -0
  345. package/examples/feedback-loop/continue-only.task.yaml +4 -0
  346. package/examples/feedback-loop/exhaust-require-continue.task.yaml +6 -0
  347. package/examples/feedback-loop/exhaust-wait-human.task.yaml +11 -0
  348. package/examples/feedback-loop/feedback-loop-wait-human.pipeline.yaml +23 -0
  349. package/examples/feedback-loop/feedback-loop.pipeline.yaml +23 -0
  350. package/examples/feedback-loop/feedback-loop.task.yaml +4 -0
  351. package/examples/feedback-loop/implement.yaml +14 -0
  352. package/examples/feedback-loop/plan.yaml +14 -0
  353. package/examples/feedback-loop/review.yaml +15 -0
  354. package/examples/feedback-loop/send-back-once.task.yaml +6 -0
  355. package/examples/feedback-loop/submit.yaml +12 -0
  356. package/examples/generic-fan-in/README.md +151 -0
  357. package/examples/generic-fan-in/clarify.yaml +18 -0
  358. package/examples/generic-fan-in/generic-fan-in-accepted.pipeline.yaml +19 -0
  359. package/examples/generic-fan-in/generic-fan-in-accepted.task.yaml +8 -0
  360. package/examples/generic-fan-in/generic-fan-in.pipeline.yaml +18 -0
  361. package/examples/generic-fan-in/generic-fan-in.task.yaml +9 -0
  362. package/examples/generic-fan-in/research.yaml +45 -0
  363. package/examples/generic-fan-in/synthesize.yaml +66 -0
  364. package/examples/generic-fan-in/validation.yaml +45 -0
  365. package/examples/github-release/README.md +78 -0
  366. package/examples/github-release/draft-release-notes.yaml +64 -0
  367. package/examples/github-release/github-release.pipeline.yaml +11 -0
  368. package/examples/github-release/github-release.task.yaml +13 -0
  369. package/examples/github-release/publish-github-release.yaml +64 -0
  370. package/examples/hello-world/README.md +28 -0
  371. package/examples/hello-world/hello.pipeline.yaml +4 -0
  372. package/examples/hello-world/my-task.task.yaml +6 -0
  373. package/examples/hello-world/research.yaml +18 -0
  374. package/examples/mcp-hitl-tour/README.md +113 -0
  375. package/examples/mcp-hitl-tour/draft-angle.yaml +33 -0
  376. package/examples/mcp-hitl-tour/draft-short.yaml +32 -0
  377. package/examples/mcp-hitl-tour/emit-angles.yaml +45 -0
  378. package/examples/mcp-hitl-tour/gather.yaml +31 -0
  379. package/examples/mcp-hitl-tour/intake.yaml +62 -0
  380. package/examples/mcp-hitl-tour/mcp-hitl-tour-exhaust.task.yaml +13 -0
  381. package/examples/mcp-hitl-tour/mcp-hitl-tour.pipeline.yaml +54 -0
  382. package/examples/mcp-hitl-tour/mcp-hitl-tour.task.yaml +16 -0
  383. package/examples/mcp-hitl-tour/polish.yaml +33 -0
  384. package/examples/mcp-hitl-tour/review.yaml +17 -0
  385. package/examples/mcp-hitl-tour/ship.yaml +25 -0
  386. package/examples/oss-issue-contribution/README.md +218 -0
  387. package/examples/oss-issue-contribution/oss-address-review-feedback.yaml +117 -0
  388. package/examples/oss-issue-contribution/oss-approve-contribution.yaml +181 -0
  389. package/examples/oss-issue-contribution/oss-approve-plan.yaml +111 -0
  390. package/examples/oss-issue-contribution/oss-explain-fix.yaml +108 -0
  391. package/examples/oss-issue-contribution/oss-explain-issue.yaml +114 -0
  392. package/examples/oss-issue-contribution/oss-implement-source-fix.yaml +171 -0
  393. package/examples/oss-issue-contribution/oss-investigate-area.yaml +116 -0
  394. package/examples/oss-issue-contribution/oss-issue-contribution.pipeline.yaml +125 -0
  395. package/examples/oss-issue-contribution/oss-issue-intake.yaml +106 -0
  396. package/examples/oss-issue-contribution/oss-plan-investigation.yaml +112 -0
  397. package/examples/oss-issue-contribution/oss-plan-review.yaml +109 -0
  398. package/examples/oss-issue-contribution/oss-reproduce-issue.yaml +140 -0
  399. package/examples/oss-issue-contribution/oss-review-change.yaml +107 -0
  400. package/examples/oss-issue-contribution/oss-verify-fix.yaml +130 -0
  401. package/examples/oss-issue-contribution/oss-write-regression-test.yaml +135 -0
  402. package/examples/oss-issue-contribution/pi-7629.task.yaml +79 -0
  403. package/examples/oss-issue-contribution/pi-8919.task.yaml +75 -0
  404. package/examples/oss-issue-contribution/pydantic-ai-7971.task.yaml +53 -0
  405. package/examples/oss-quick-fix/README.md +65 -0
  406. package/examples/oss-quick-fix/implement-fix.yaml +84 -0
  407. package/examples/oss-quick-fix/oss-quick-fix.pipeline.yaml +35 -0
  408. package/examples/oss-quick-fix/oss-quick-fix.task.yaml +19 -0
  409. package/examples/oss-quick-fix/ship-fix.yaml +81 -0
  410. package/examples/oss-quick-fix/understand-issue.yaml +67 -0
  411. package/examples/oss-quick-fix/verify-fix.yaml +73 -0
  412. package/examples/plan-review/README.md +37 -0
  413. package/examples/plan-review/plan-review-followup.yaml +13 -0
  414. package/examples/plan-review/plan-review.pipeline.yaml +9 -0
  415. package/examples/plan-review/plan-review.task.yaml +9 -0
  416. package/examples/plan-review/plan-review.yaml +25 -0
  417. package/examples/playwright-mcp/README.md +39 -0
  418. package/examples/playwright-mcp/playwright-mcp.pipeline.yaml +5 -0
  419. package/examples/playwright-mcp/playwright-mcp.task.yaml +4 -0
  420. package/examples/playwright-mcp/screenshot.yaml +40 -0
  421. package/examples/retry-tour/README.md +75 -0
  422. package/examples/retry-tour/retry-tour.pipeline.yaml +122 -0
  423. package/examples/retry-tour/retry-tour.task.yaml +9 -0
  424. package/examples/route-if-tour/README.md +202 -0
  425. package/examples/route-if-tour/route-if-tour.pipeline.yaml +950 -0
  426. package/examples/route-if-tour/route-if-tour.task.yaml +4 -0
  427. package/examples/route-wiring-smoke-test/01-core-routing.pipeline.yaml +168 -0
  428. package/examples/route-wiring-smoke-test/02-on-gating.pipeline.yaml +44 -0
  429. package/examples/route-wiring-smoke-test/03-two-entry-points.pipeline.yaml +28 -0
  430. package/examples/route-wiring-smoke-test/04-entry-false-equivalent-to-omitted.pipeline.yaml +27 -0
  431. package/examples/route-wiring-smoke-test/05-fork-choice.pipeline.yaml +110 -0
  432. package/examples/route-wiring-smoke-test/06-fan-out-fan-in.pipeline.yaml +82 -0
  433. package/examples/route-wiring-smoke-test/07-loop-human-decision.pipeline.yaml +82 -0
  434. package/examples/route-wiring-smoke-test/08-skip-fallback-and-multi-on-gate.pipeline.yaml +77 -0
  435. package/examples/route-wiring-smoke-test/09-multi-loop-targets.pipeline.yaml +102 -0
  436. package/examples/route-wiring-smoke-test/10-uses-dialect-fork.pipeline.yaml +23 -0
  437. package/examples/route-wiring-smoke-test/11-sequential-io-handoff.pipeline.yaml +37 -0
  438. package/examples/route-wiring-smoke-test/12-complex-io-schemas.pipeline.yaml +138 -0
  439. package/examples/route-wiring-smoke-test/13-ref-io-handoff.pipeline.yaml +84 -0
  440. package/examples/route-wiring-smoke-test/14-if-eq-gating.pipeline.yaml +50 -0
  441. package/examples/route-wiring-smoke-test/15-if-all-gated.pipeline.yaml +37 -0
  442. package/examples/route-wiring-smoke-test/16-if-composition.pipeline.yaml +65 -0
  443. package/examples/route-wiring-smoke-test/17-if-join.pipeline.yaml +80 -0
  444. package/examples/route-wiring-smoke-test/README.md +377 -0
  445. package/examples/route-wiring-smoke-test/rejected/01-reject-legacy-needs.pipeline.yaml +26 -0
  446. package/examples/route-wiring-smoke-test/rejected/02-reject-legacy-fork.pipeline.yaml +25 -0
  447. package/examples/route-wiring-smoke-test/rejected/03-reject-legacy-feedback-loop.pipeline.yaml +30 -0
  448. package/examples/route-wiring-smoke-test/rejected/04-reject-missing-entry.pipeline.yaml +24 -0
  449. package/examples/route-wiring-smoke-test/rejected/05-reject-unreachable.pipeline.yaml +35 -0
  450. package/examples/route-wiring-smoke-test/rejected/06-reject-allow-none-without-route-select.pipeline.yaml +37 -0
  451. package/examples/route-wiring-smoke-test/rejected/07-reject-cycle.pipeline.yaml +27 -0
  452. package/examples/route-wiring-smoke-test/rejected/08-reject-loop-non-ancestor.pipeline.yaml +42 -0
  453. package/examples/route-wiring-smoke-test/rejected/09-reject-route-select-leaf.pipeline.yaml +26 -0
  454. package/examples/route-wiring-smoke-test/rejected/12-reject-loop-replay-unsafe.pipeline.yaml +55 -0
  455. package/examples/route-wiring-smoke-test/rejected/13-reject-multiple-loop-entries.pipeline.yaml +51 -0
  456. package/examples/route-wiring-smoke-test/rejected/14-reject-duplicate-stage-id.pipeline.yaml +35 -0
  457. package/examples/route-wiring-smoke-test/rejected/15-reject-unknown-route-target.pipeline.yaml +15 -0
  458. package/examples/route-wiring-smoke-test/rejected/16-reject-malformed-route-item.pipeline.yaml +15 -0
  459. package/examples/route-wiring-smoke-test/rejected/17-reject-io-incompatible.pipeline.yaml +33 -0
  460. package/examples/route-wiring-smoke-test/rejected/18-reject-nested-io.pipeline.yaml +51 -0
  461. package/examples/route-wiring-smoke-test/rejected/19-reject-array-item-io.pipeline.yaml +55 -0
  462. package/examples/route-wiring-smoke-test/rejected/20-reject-closed-io.pipeline.yaml +36 -0
  463. package/examples/route-wiring-smoke-test/rejected/21-reject-ref-io.pipeline.yaml +42 -0
  464. package/examples/route-wiring-smoke-test/rejected/22-reject-if-unknown-field.pipeline.yaml +33 -0
  465. package/examples/route-wiring-smoke-test/rejected/23-reject-if-optional-field.pipeline.yaml +32 -0
  466. package/examples/route-wiring-smoke-test/rejected/24-reject-if-empty-all.pipeline.yaml +31 -0
  467. package/examples/route-wiring-smoke-test/rejected/25-reject-if-gt-on-string.pipeline.yaml +33 -0
  468. package/examples/route-wiring-smoke-test/rejected/26-reject-if-optional-nested.pipeline.yaml +36 -0
  469. package/examples/route-wiring-smoke-test/rejected/27-reject-if-on-loop.pipeline.yaml +63 -0
  470. package/examples/route-wiring-smoke-test/rejected/29-reject-if-on-failed.pipeline.yaml +46 -0
  471. package/examples/route-wiring-smoke-test/smoke-test.task.yaml +4 -0
  472. package/examples/route-wiring-smoke-test/stages/branch-a.yaml +12 -0
  473. package/examples/route-wiring-smoke-test/stages/branch-b.yaml +12 -0
  474. package/examples/route-wiring-smoke-test/stages/decide.yaml +12 -0
  475. package/examples/route-wiring-smoke-test/stages/finish.yaml +12 -0
  476. package/examples/ship-feature/README.md +190 -0
  477. package/examples/ship-feature/feat-address-feedback.yaml +119 -0
  478. package/examples/ship-feature/feat-implement.yaml +180 -0
  479. package/examples/ship-feature/feat-plan.yaml +120 -0
  480. package/examples/ship-feature/feat-review.yaml +109 -0
  481. package/examples/ship-feature/feat-ship.yaml +203 -0
  482. package/examples/ship-feature/ship-feature.pipeline.yaml +44 -0
  483. package/examples/ship-feature/ship-feature.task.yaml +78 -0
  484. package/examples/stage-mcp/README.md +144 -0
  485. package/examples/stage-mcp/echo-mcp.mjs +20 -0
  486. package/examples/stage-mcp/stage-mcp-dead.pipeline.yaml +5 -0
  487. package/examples/stage-mcp/stage-mcp-dead.task.yaml +3 -0
  488. package/examples/stage-mcp/stage-mcp-missing-var.pipeline.yaml +5 -0
  489. package/examples/stage-mcp/stage-mcp-missing-var.task.yaml +3 -0
  490. package/examples/stage-mcp/stage-mcp.pipeline.yaml +5 -0
  491. package/examples/stage-mcp/stage-mcp.task.yaml +4 -0
  492. package/examples/stage-mcp/use-echo.yaml +20 -0
  493. package/examples/verify-tour/README.md +80 -0
  494. package/examples/verify-tour/verify-tour.pipeline.yaml +162 -0
  495. package/examples/verify-tour/verify-tour.task.yaml +11 -0
  496. package/package.json +6 -1
  497. package/skills/stageflow/SKILL.md +6 -6
  498. package/skills/stageflow/references/control-surface.md +11 -5
  499. package/skills/stageflow/scripts/detect-host.mjs +14 -1
  500. package/skills/stageflow/scripts/detect-host.test.mjs +29 -1
  501. package/skills/stageflow-author/SKILL.md +4 -3
  502. package/skills/stageflow-author/references/catalog-mapping.md +16 -0
  503. package/skills/stageflow-author/references/catalog-write-conventions.md +1 -1
  504. package/skills/stageflow-run/SKILL.md +20 -12
  505. package/skills/stageflow-run/references/mcp-call.md +9 -1
  506. package/skills/stageflow-run/references/task-and-pipeline-selection.md +16 -5
  507. package/skills/stageflow-run/scripts/mcp-call.mjs +46 -6
  508. package/skills/stageflow-run/scripts/mcp-call.test.mjs +64 -0
  509. package/skills/stageflow-session-capture/references/catalog-authoring.md +2 -0
  510. package/skills/stageflow-setup/SKILL.md +40 -8
  511. package/dist/ui/assets/index-Da5EHU6M.js +0 -119
@@ -1,7 +1,52 @@
1
- import { fork } from "node:child_process";
1
+ import * as childProcess from "node:child_process";
2
2
  import { fileURLToPath } from "node:url";
3
+ import { logger as rootLogger, resolveLogMaxLineBytes, } from "../logging/logger.js";
4
+ import { PACKAGE_VERSION } from "../package-meta.js";
5
+ import { redactString } from "../logging/redact.js";
6
+ import { getNamedSecrets } from "../logging/namedSecrets.js";
3
7
  import { readMaxActiveStageProcesses, } from "./stageConcurrency.js";
8
+ import { getContainerLimits } from "./containerLimits.js";
9
+ import { buildStageEnvironment, isAmbientBlockedEnv, isForeverDeniedSecret, } from "./stageEnvironment.js";
10
+ import { ensureStageCacheDirs } from "./stageCacheEnv.js";
4
11
  import { SF_STAGE_WORKER, STAGE_WORKER_EXIT, } from "./stageWorkerProtocol.js";
12
+ import { overlayStageBindingEnv, } from "./stageRoots.js";
13
+ function flushCappedPartial(log, event, text, maxLineBytes) {
14
+ let remaining = redactString(text, { namedSecrets: getNamedSecrets() });
15
+ while (Buffer.byteLength(remaining, "utf8") > maxLineBytes) {
16
+ const bytes = Buffer.from(remaining, "utf8");
17
+ const originalBytes = bytes.byteLength;
18
+ const piece = bytes.subarray(0, maxLineBytes).toString("utf8");
19
+ log.info(event, piece, {
20
+ truncated: true,
21
+ original_bytes: originalBytes,
22
+ });
23
+ remaining = bytes.subarray(maxLineBytes).toString("utf8");
24
+ }
25
+ return remaining;
26
+ }
27
+ function attachStreamLineLogger(stream, log, event, maxLineBytes) {
28
+ if (!stream)
29
+ return;
30
+ let buffer = "";
31
+ stream.on("data", (chunk) => {
32
+ buffer += chunk.toString();
33
+ const lines = buffer.split("\n");
34
+ buffer = lines.pop() ?? "";
35
+ for (const line of lines) {
36
+ log.info(event, line);
37
+ }
38
+ buffer = flushCappedPartial(log, event, buffer, maxLineBytes);
39
+ });
40
+ stream.on("end", () => {
41
+ if (buffer.length > 0) {
42
+ buffer = flushCappedPartial(log, event, buffer, maxLineBytes);
43
+ if (buffer.length > 0) {
44
+ log.info(event, buffer);
45
+ }
46
+ buffer = "";
47
+ }
48
+ });
49
+ }
5
50
  function activeKey(runId, stageId) {
6
51
  return `${runId}\0${stageId}`;
7
52
  }
@@ -11,7 +56,7 @@ function isStageWorkerResult(value) {
11
56
  const type = value.type;
12
57
  return type === "succeeded" || type === "failed" || type === "waiting";
13
58
  }
14
- function resultFromExitCode(code) {
59
+ function resultFromExitCode(code, signal, hostInitiatedKill) {
15
60
  if (code === STAGE_WORKER_EXIT.SUCCEEDED) {
16
61
  return { type: "succeeded" };
17
62
  }
@@ -21,9 +66,16 @@ function resultFromExitCode(code) {
21
66
  if (code === STAGE_WORKER_EXIT.FAILED) {
22
67
  return { type: "failed", reason: "stage failed" };
23
68
  }
69
+ if (signal === "SIGKILL" && !hostInitiatedKill) {
70
+ return { type: "failed", reason: "worker_oom_killed" };
71
+ }
24
72
  return {
25
73
  type: "failed",
26
- reason: code === null ? "stage process exited" : `stage process exit ${code}`,
74
+ reason: code === null
75
+ ? signal
76
+ ? `stage process exited (${signal})`
77
+ : "stage process exited"
78
+ : `stage process exit ${code}`,
27
79
  };
28
80
  }
29
81
  function resultFromWorkerMessage(msg) {
@@ -35,19 +87,51 @@ function resultFromWorkerMessage(msg) {
35
87
  }
36
88
  return { type: "failed", reason: msg.reason };
37
89
  }
90
+ export function signalProcessGroup(pid, signal) {
91
+ if (pid === undefined)
92
+ return;
93
+ try {
94
+ if (process.platform === "win32") {
95
+ process.kill(pid, signal);
96
+ return;
97
+ }
98
+ process.kill(-pid, signal);
99
+ }
100
+ catch (error) {
101
+ const code = error.code;
102
+ if (code !== "ESRCH" && code !== "EPERM") {
103
+ throw error;
104
+ }
105
+ }
106
+ }
38
107
  export class StageProcessLauncher {
39
108
  maxActive;
40
109
  env;
110
+ explicitChildExtras;
41
111
  cliEntry;
112
+ logger;
113
+ forkFn;
42
114
  active = new Map();
43
115
  waitQueue = [];
116
+ cancelledRuns = new Set();
44
117
  slotsHeld = 0;
118
+ heapMb;
45
119
  constructor(options = {}) {
46
120
  this.env = options.env ?? process.env;
121
+ this.explicitChildExtras =
122
+ options.env !== undefined && options.env !== process.env
123
+ ? Object.fromEntries(Object.entries(options.env).filter((entry) => entry[1] !== undefined &&
124
+ !isForeverDeniedSecret(entry[0]) &&
125
+ !isAmbientBlockedEnv(entry[0])))
126
+ : undefined;
47
127
  this.maxActive = readMaxActiveStageProcesses(this.env, options.maxActiveStageProcesses);
128
+ this.heapMb = getContainerLimits().maxOldSpaceSizeMb;
48
129
  this.cliEntry =
49
130
  options.cliEntry ??
50
131
  fileURLToPath(new URL("../cli.js", import.meta.url));
132
+ this.logger =
133
+ options.logger ?? rootLogger.child({ component: "runtime" });
134
+ this.forkFn = options.forkFn ?? childProcess.fork;
51
135
  }
52
136
  activeCount() {
53
137
  return this.active.size;
@@ -59,47 +143,102 @@ export class StageProcessLauncher {
59
143
  startedAt,
60
144
  }));
61
145
  }
146
+ signalAllActive(signal) {
147
+ for (const entry of this.active.values()) {
148
+ entry.hostInitiatedKill = true;
149
+ signalProcessGroup(entry.child.pid, signal);
150
+ }
151
+ }
62
152
  async launch(input) {
63
- await this.waitForCapacity();
64
- return this.spawnAndWait(input);
153
+ const acquired = await this.waitForCapacity(input.runId);
154
+ if (!acquired) {
155
+ return { type: "failed", reason: "cancelled" };
156
+ }
157
+ let slotOwnedBySpawn = false;
158
+ try {
159
+ if (this.cancelledRuns.has(input.runId)) {
160
+ return { type: "failed", reason: "cancelled" };
161
+ }
162
+ const resultPromise = this.spawnAndWait(input);
163
+ slotOwnedBySpawn = true;
164
+ return await resultPromise;
165
+ }
166
+ finally {
167
+ if (!slotOwnedBySpawn) {
168
+ this.releaseCapacity();
169
+ }
170
+ }
65
171
  }
66
172
  async cancelRun(runId, killAfterMs = 5000) {
173
+ this.cancelledRuns.add(runId);
174
+ const remaining = [];
175
+ for (const entry of this.waitQueue) {
176
+ if (entry.runId === runId) {
177
+ entry.resolve(true);
178
+ }
179
+ else {
180
+ remaining.push(entry);
181
+ }
182
+ }
183
+ this.waitQueue.length = 0;
184
+ this.waitQueue.push(...remaining);
67
185
  const children = [...this.active.values()].filter((entry) => entry.runId === runId);
68
186
  if (children.length === 0) {
187
+ this.cancelledRuns.delete(runId);
69
188
  return;
70
189
  }
190
+ for (const entry of children) {
191
+ entry.hostInitiatedKill = true;
192
+ }
71
193
  await Promise.all(children.map((entry) => new Promise((resolve) => {
72
194
  const child = entry.child;
195
+ const pid = child.pid;
73
196
  let settled = false;
197
+ let exited = false;
198
+ let escalateTimer;
74
199
  const finish = () => {
75
200
  if (settled)
76
201
  return;
77
202
  settled = true;
203
+ if (escalateTimer !== undefined)
204
+ clearTimeout(escalateTimer);
78
205
  resolve();
79
206
  };
80
- child.once("exit", finish);
81
- child.kill("SIGTERM");
207
+ child.once("exit", () => {
208
+ exited = true;
209
+ // Parent may exit on SIGTERM while SIGTERM-proof grandchildren remain.
210
+ signalProcessGroup(pid, "SIGKILL");
211
+ finish();
212
+ });
213
+ signalProcessGroup(pid, "SIGTERM");
82
214
  if (killAfterMs > 0) {
83
- setTimeout(() => {
84
- if (!child.killed) {
85
- child.kill("SIGKILL");
215
+ escalateTimer = setTimeout(() => {
216
+ if (!exited) {
217
+ signalProcessGroup(pid, "SIGKILL");
86
218
  }
87
219
  }, killAfterMs);
88
220
  }
89
221
  })));
222
+ this.cancelledRuns.delete(runId);
90
223
  }
91
- async waitForCapacity() {
224
+ async waitForCapacity(runId) {
225
+ if (this.cancelledRuns.has(runId)) {
226
+ return false;
227
+ }
92
228
  if (!Number.isFinite(this.maxActive)) {
93
- return;
229
+ return true;
94
230
  }
95
231
  if (this.slotsHeld < this.maxActive) {
96
232
  this.slotsHeld += 1;
97
- return;
233
+ return true;
98
234
  }
99
- await new Promise((resolve) => {
100
- this.waitQueue.push(resolve);
235
+ const cancelled = await new Promise((resolve) => {
236
+ this.waitQueue.push({ runId, resolve });
101
237
  });
102
- return this.waitForCapacity();
238
+ if (cancelled) {
239
+ return false;
240
+ }
241
+ return this.waitForCapacity(runId);
103
242
  }
104
243
  releaseCapacity() {
105
244
  if (!Number.isFinite(this.maxActive)) {
@@ -108,7 +247,7 @@ export class StageProcessLauncher {
108
247
  this.slotsHeld -= 1;
109
248
  if (this.waitQueue.length > 0) {
110
249
  const next = this.waitQueue.shift();
111
- next?.();
250
+ next?.resolve(false);
112
251
  }
113
252
  }
114
253
  spawnAndWait(input) {
@@ -141,10 +280,35 @@ export class StageProcessLauncher {
141
280
  if (input.skipGates) {
142
281
  args.push("--skip-gates");
143
282
  }
144
- const child = fork(this.cliEntry, args, {
283
+ if (this.cancelledRuns.has(input.runId)) {
284
+ this.releaseCapacity();
285
+ return Promise.resolve({ type: "failed", reason: "cancelled" });
286
+ }
287
+ const hostEnv = { ...process.env, ...this.env };
288
+ const built = buildStageEnvironment({
289
+ hostEnv,
290
+ cacheVars: ensureStageCacheDirs(),
291
+ grants: input.grants,
292
+ attemptHome: input.attemptHome,
293
+ packageVersion: PACKAGE_VERSION,
294
+ });
295
+ for (const warning of built.warnings) {
296
+ this.logger.warn("stage.env.passthrough", warning, {
297
+ run_id: input.runId,
298
+ stage_id: input.stageId,
299
+ });
300
+ }
301
+ const withExtras = this.explicitChildExtras !== undefined
302
+ ? { ...built.env, ...this.explicitChildExtras }
303
+ : built.env;
304
+ const childEnv = overlayStageBindingEnv(withExtras, input.env ?? {}, input.bindingKind ?? "unbound");
305
+ const child = this.forkFn(this.cliEntry, args, {
145
306
  cwd: input.rootDir,
146
- env: { ...process.env, ...this.env, [SF_STAGE_WORKER]: "1" },
307
+ env: { ...childEnv, [SF_STAGE_WORKER]: "1" },
147
308
  stdio: ["pipe", "pipe", "pipe", "ipc"],
309
+ detached: true,
310
+ // Explicit: do not inherit Host execArgv; set heap from cgroup budget.
311
+ execArgv: [`--max-old-space-size=${this.heapMb}`],
148
312
  });
149
313
  const key = activeKey(input.runId, input.stageId);
150
314
  const tracked = {
@@ -152,25 +316,17 @@ export class StageProcessLauncher {
152
316
  runId: input.runId,
153
317
  stageId: input.stageId,
154
318
  startedAt: Date.now(),
319
+ hostInitiatedKill: false,
155
320
  };
156
321
  this.active.set(key, tracked);
157
- if (child.stderr) {
158
- let stderrBuffer = "";
159
- child.stderr.on("data", (chunk) => {
160
- stderrBuffer += chunk.toString();
161
- const lines = stderrBuffer.split("\n");
162
- stderrBuffer = lines.pop() ?? "";
163
- for (const line of lines) {
164
- process.stderr.write(`[stage:${input.stageId}] ${line}\n`);
165
- }
166
- });
167
- child.stderr.on("end", () => {
168
- if (stderrBuffer.length > 0) {
169
- process.stderr.write(`[stage:${input.stageId}] ${stderrBuffer}\n`);
170
- stderrBuffer = "";
171
- }
172
- });
173
- }
322
+ const stageLog = this.logger.child({
323
+ run_id: input.runId,
324
+ stage_id: input.stageId,
325
+ ...(input.attempt !== undefined ? { attempt: input.attempt } : {}),
326
+ });
327
+ const maxLineBytes = resolveLogMaxLineBytes(this.env);
328
+ attachStreamLineLogger(child.stdout, stageLog, "stage.stdout", maxLineBytes);
329
+ attachStreamLineLogger(child.stderr, stageLog, "stage.stderr", maxLineBytes);
174
330
  return new Promise((resolve) => {
175
331
  let settled = false;
176
332
  const cleanup = () => {
@@ -189,10 +345,10 @@ export class StageProcessLauncher {
189
345
  return;
190
346
  finish(resultFromWorkerMessage(message));
191
347
  });
192
- child.on("exit", (code) => {
348
+ child.on("exit", (code, signal) => {
193
349
  if (settled)
194
350
  return;
195
- finish(resultFromExitCode(code));
351
+ finish(resultFromExitCode(code, signal, tracked.hostInitiatedKill));
196
352
  });
197
353
  child.on("error", (err) => {
198
354
  finish({
@@ -1,10 +1,12 @@
1
1
  import type { RunStore } from "../runstore/port.js";
2
2
  import { type StageAttemptContext } from "./stageAttemptContext.js";
3
- export declare function failStageAsInterrupted(options: {
3
+ export declare const OPERATOR_CANCEL_REASON = "process_interrupted: operator cancelled run";
4
+ export declare function markStageInterrupted(options: {
4
5
  store: RunStore;
5
6
  runId: string;
6
7
  stageId: string;
7
8
  reason: string;
9
+ status: "failed" | "interrupted";
8
10
  attemptCtx?: StageAttemptContext;
9
11
  }): Promise<void>;
10
12
  export declare function syncRunStatusFromStages(store: RunStore, runId: string): Promise<void>;
@@ -1,8 +1,12 @@
1
1
  import { deriveStatusFromStages } from "../runstore/port.js";
2
+ import { refreshRunDiskUsage } from "../runstore/diskUsage.js";
3
+ import { finaliseStoredRunManifest } from "../runstore/runManifest.js";
2
4
  import { deriveExecutionPatchFromEvent } from "../runstore/stageExecution.js";
5
+ import { reclaimWorkspaceOnRunSucceeded } from "./repositoryMaterialize.js";
3
6
  import { attemptContext, } from "./stageAttemptContext.js";
4
- export async function failStageAsInterrupted(options) {
5
- const { store, runId, stageId, reason, attemptCtx } = options;
7
+ export const OPERATOR_CANCEL_REASON = "process_interrupted: operator cancelled run";
8
+ export async function markStageInterrupted(options) {
9
+ const { store, runId, stageId, reason, status, attemptCtx } = options;
6
10
  let attemptNum;
7
11
  let attemptOpt;
8
12
  if (attemptCtx !== undefined) {
@@ -20,7 +24,9 @@ export async function failStageAsInterrupted(options) {
20
24
  attemptOpt = undefined;
21
25
  }
22
26
  }
23
- const event = { event: "failed", reason };
27
+ const event = status === "interrupted"
28
+ ? { event: "interrupted", reason }
29
+ : { event: "failed", reason };
24
30
  await store.appendStageEvent(runId, stageId, event, attemptOpt);
25
31
  try {
26
32
  const execution = await store.getStageExecution(runId, stageId, attemptNum);
@@ -36,10 +42,19 @@ export async function failStageAsInterrupted(options) {
36
42
  export async function syncRunStatusFromStages(store, runId) {
37
43
  const meta = await store.readRunMeta(runId);
38
44
  const run = await store.readRun(runId);
39
- const derived = deriveStatusFromStages(run.stages, meta.pipeline_dag);
45
+ const derived = deriveStatusFromStages(run.stages, meta.pipeline_dag, meta.status);
40
46
  if (meta.status === "succeeded" && derived !== "running")
41
47
  return;
42
48
  if ((meta.status ?? "created") !== derived) {
43
49
  await store.updateRunStatus(runId, derived);
50
+ if (derived === "succeeded" ||
51
+ derived === "failed" ||
52
+ derived === "cancelled") {
53
+ await finaliseStoredRunManifest(store, runId).catch(() => undefined);
54
+ await refreshRunDiskUsage(store, runId).catch(() => undefined);
55
+ }
56
+ if (derived === "succeeded") {
57
+ await reclaimWorkspaceOnRunSucceeded(store, runId);
58
+ }
44
59
  }
45
60
  }
@@ -1,5 +1,7 @@
1
+ import type { RunMeta } from "../runstore/port.js";
1
2
  import type { StageAttemptContext } from "./stageAttemptContext.js";
2
- import type { TaskFile } from "../types/task.js";
3
+ import type { TaskFile, TaskGitIdentity } from "../types/task.js";
4
+ export type DerivedBindingKind = "repository" | "checkout" | "unbound";
3
5
  export type StageRoots = {
4
6
  mode: "bound" | "unbound";
5
7
  cwd: string;
@@ -10,8 +12,43 @@ export type StageRoots = {
10
12
  /** Durable Pi auth.json path for ModelRuntime.create({ authPath }). */
11
13
  authPath?: string;
12
14
  };
15
+ export declare const STAGEFLOW_CHECKOUT = "STAGEFLOW_CHECKOUT";
13
16
  export declare const STAGEFLOW_RUN_WORKSPACE = "STAGEFLOW_RUN_WORKSPACE";
17
+ export declare const STAGEFLOW_REPOSITORY = "STAGEFLOW_REPOSITORY";
18
+ export declare const STAGEFLOW_REF = "STAGEFLOW_REF";
19
+ export declare const STAGEFLOW_BASE_SHA = "STAGEFLOW_BASE_SHA";
20
+ export declare const STAGEFLOW_RUN_BRANCH = "STAGEFLOW_RUN_BRANCH";
21
+ export declare const REPOSITORY_ONLY_STAGEFLOW_VARS: readonly ["STAGEFLOW_REPOSITORY", "STAGEFLOW_REF", "STAGEFLOW_BASE_SHA", "STAGEFLOW_RUN_BRANCH"];
14
22
  export declare const PI_CODING_AGENT_DIR_ENV = "PI_CODING_AGENT_DIR";
23
+ export type EffectiveGitIdentity = {
24
+ name: string;
25
+ email: string;
26
+ };
27
+ export type BuildStageBindingEnvInput = {
28
+ kind: DerivedBindingKind;
29
+ runWorkspaceDir: string;
30
+ checkoutRoot?: string;
31
+ repository?: string;
32
+ ref?: string;
33
+ resolvedSha?: string;
34
+ runBranch?: string;
35
+ gitIdentity: EffectiveGitIdentity;
36
+ hostEnv?: NodeJS.ProcessEnv;
37
+ };
38
+ export declare function resolveEffectiveGitIdentity(hostEnv: NodeJS.ProcessEnv, taskIdentity?: TaskGitIdentity): EffectiveGitIdentity;
39
+ export declare function buildStageBindingEnv(input: BuildStageBindingEnvInput): Record<string, string>;
40
+ export declare function overlayStageBindingEnv(base: NodeJS.ProcessEnv, bindingEnv: Record<string, string>, kind: DerivedBindingKind): Record<string, string | undefined>;
41
+ export declare function bindingKindFromMeta(meta: RunMeta): DerivedBindingKind;
42
+ export declare function stageBindingEnvFromRun(options: {
43
+ meta: RunMeta;
44
+ task: TaskFile;
45
+ runWorkspaceDir: string;
46
+ hostEnv?: NodeJS.ProcessEnv;
47
+ }): {
48
+ kind: DerivedBindingKind;
49
+ env: Record<string, string>;
50
+ identity: EffectiveGitIdentity;
51
+ };
15
52
  export declare function withResolvedAuthPath(roots: StageRoots, factoryCwd: string): StageRoots;
16
53
  export declare function rootsForStageWorker(runWorkspaceDir: string, stageId: string, model: string, checkoutRoot?: string, attemptCtx?: StageAttemptContext): StageRoots;
17
54
  export declare function bindPiAgentDirEnv(agentDirPath: string): () => void;
@@ -4,8 +4,126 @@ import { isCursorModelRef } from "../agent/cursorProvider.js";
4
4
  import { stageDir } from "../runstore/paths.js";
5
5
  import { noAttemptContext } from "./stageAttemptContext.js";
6
6
  import { resolveCredentialBinding } from "./credentialBinding.js";
7
+ export const STAGEFLOW_CHECKOUT = "STAGEFLOW_CHECKOUT";
7
8
  export const STAGEFLOW_RUN_WORKSPACE = "STAGEFLOW_RUN_WORKSPACE";
9
+ export const STAGEFLOW_REPOSITORY = "STAGEFLOW_REPOSITORY";
10
+ export const STAGEFLOW_REF = "STAGEFLOW_REF";
11
+ export const STAGEFLOW_BASE_SHA = "STAGEFLOW_BASE_SHA";
12
+ export const STAGEFLOW_RUN_BRANCH = "STAGEFLOW_RUN_BRANCH";
13
+ export const REPOSITORY_ONLY_STAGEFLOW_VARS = [
14
+ STAGEFLOW_REPOSITORY,
15
+ STAGEFLOW_REF,
16
+ STAGEFLOW_BASE_SHA,
17
+ STAGEFLOW_RUN_BRANCH,
18
+ ];
8
19
  export const PI_CODING_AGENT_DIR_ENV = "PI_CODING_AGENT_DIR";
20
+ function nonEmptyTrimmed(value) {
21
+ if (value === undefined)
22
+ return undefined;
23
+ const trimmed = value.trim();
24
+ return trimmed.length > 0 ? trimmed : undefined;
25
+ }
26
+ export function resolveEffectiveGitIdentity(hostEnv, taskIdentity) {
27
+ const name = nonEmptyTrimmed(taskIdentity?.name) ??
28
+ nonEmptyTrimmed(hostEnv.STAGEFLOW_GIT_AUTHOR_NAME) ??
29
+ "Stageflow";
30
+ const email = nonEmptyTrimmed(taskIdentity?.email) ??
31
+ nonEmptyTrimmed(hostEnv.STAGEFLOW_GIT_AUTHOR_EMAIL) ??
32
+ "stageflow@localhost";
33
+ return { name, email };
34
+ }
35
+ export function buildStageBindingEnv(input) {
36
+ const hostEnv = input.hostEnv ?? {};
37
+ const authorName = input.gitIdentity.name;
38
+ const authorEmail = input.gitIdentity.email;
39
+ const committerName = nonEmptyTrimmed(hostEnv.STAGEFLOW_GIT_COMMITTER_NAME) ?? authorName;
40
+ const committerEmail = nonEmptyTrimmed(hostEnv.STAGEFLOW_GIT_COMMITTER_EMAIL) ?? authorEmail;
41
+ const env = {
42
+ [STAGEFLOW_RUN_WORKSPACE]: input.runWorkspaceDir,
43
+ GIT_AUTHOR_NAME: authorName,
44
+ GIT_AUTHOR_EMAIL: authorEmail,
45
+ GIT_COMMITTER_NAME: committerName,
46
+ GIT_COMMITTER_EMAIL: committerEmail,
47
+ };
48
+ if (input.checkoutRoot !== undefined && input.kind !== "unbound") {
49
+ env[STAGEFLOW_CHECKOUT] = input.checkoutRoot;
50
+ env.GIT_CONFIG_COUNT = "1";
51
+ env.GIT_CONFIG_KEY_0 = "safe.directory";
52
+ env.GIT_CONFIG_VALUE_0 = input.checkoutRoot;
53
+ }
54
+ if (input.kind === "repository") {
55
+ if (input.repository !== undefined) {
56
+ env[STAGEFLOW_REPOSITORY] = input.repository;
57
+ }
58
+ if (input.ref !== undefined) {
59
+ env[STAGEFLOW_REF] = input.ref;
60
+ }
61
+ if (input.resolvedSha !== undefined) {
62
+ env[STAGEFLOW_BASE_SHA] = input.resolvedSha;
63
+ }
64
+ if (input.runBranch !== undefined) {
65
+ env[STAGEFLOW_RUN_BRANCH] = input.runBranch;
66
+ }
67
+ }
68
+ return env;
69
+ }
70
+ export function overlayStageBindingEnv(base, bindingEnv, kind) {
71
+ const out = { ...base, ...bindingEnv };
72
+ if (kind !== "repository") {
73
+ for (const key of REPOSITORY_ONLY_STAGEFLOW_VARS) {
74
+ delete out[key];
75
+ }
76
+ }
77
+ if (kind === "unbound" || !Object.hasOwn(bindingEnv, STAGEFLOW_CHECKOUT)) {
78
+ delete out[STAGEFLOW_CHECKOUT];
79
+ }
80
+ if (!Object.hasOwn(bindingEnv, "GIT_CONFIG_COUNT")) {
81
+ delete out.GIT_CONFIG_COUNT;
82
+ delete out.GIT_CONFIG_KEY_0;
83
+ delete out.GIT_CONFIG_VALUE_0;
84
+ }
85
+ return out;
86
+ }
87
+ export function bindingKindFromMeta(meta) {
88
+ if (meta.repository != null && meta.repository !== "") {
89
+ return "repository";
90
+ }
91
+ if (meta.checkout_root != null && meta.checkout_root !== "") {
92
+ return "checkout";
93
+ }
94
+ return "unbound";
95
+ }
96
+ export function stageBindingEnvFromRun(options) {
97
+ const hostEnv = options.hostEnv ?? process.env;
98
+ const kind = bindingKindFromMeta(options.meta);
99
+ const identity = options.meta.git_author_name !== undefined &&
100
+ options.meta.git_author_email !== undefined
101
+ ? {
102
+ name: options.meta.git_author_name,
103
+ email: options.meta.git_author_email,
104
+ }
105
+ : resolveEffectiveGitIdentity(hostEnv, options.task.git_identity);
106
+ const env = buildStageBindingEnv({
107
+ kind,
108
+ runWorkspaceDir: options.runWorkspaceDir,
109
+ ...(options.meta.checkout_root !== undefined
110
+ ? { checkoutRoot: options.meta.checkout_root }
111
+ : {}),
112
+ ...(options.meta.repository !== undefined
113
+ ? { repository: options.meta.repository }
114
+ : {}),
115
+ ...(options.meta.ref !== undefined ? { ref: options.meta.ref } : {}),
116
+ ...(options.meta.resolved_sha !== undefined
117
+ ? { resolvedSha: options.meta.resolved_sha }
118
+ : {}),
119
+ ...(options.meta.run_branch !== undefined
120
+ ? { runBranch: options.meta.run_branch }
121
+ : {}),
122
+ gitIdentity: identity,
123
+ hostEnv,
124
+ });
125
+ return { kind, env, identity };
126
+ }
9
127
  export function withResolvedAuthPath(roots, factoryCwd) {
10
128
  if (roots.authPath)
11
129
  return roots;
@@ -48,6 +48,7 @@ export type RunStageOptions = {
48
48
  sessionMode?: StageSessionMode;
49
49
  feedbackLoopContext?: FeedbackLoopContext;
50
50
  resumeToken?: string;
51
+ stageEnv?: Record<string, string>;
51
52
  };
52
53
  /**
53
54
  * Yield loop: next() until completed; on wait, coordinator enterWait (KTD6).
@@ -115,7 +115,7 @@ export async function runStageYieldLoop(options) {
115
115
  }
116
116
  }
117
117
  export async function runStage(options) {
118
- const { agent, store, runId, stage, task, dag, checkoutRoot, workspaceDir, hitl, skipStarted, existingHandle, workerMode, roots: rootsOverride, attemptCtx, factoryCwd, operatorCatalog, completedEnvelopes, skipGates, sessionMode, feedbackLoopContext, resumeToken, } = options;
118
+ const { agent, store, runId, stage, task, dag, checkoutRoot, workspaceDir, hitl, skipStarted, existingHandle, workerMode, roots: rootsOverride, attemptCtx, factoryCwd, operatorCatalog, completedEnvelopes, skipGates, sessionMode, feedbackLoopContext, resumeToken, stageEnv, } = options;
119
119
  const stageId = options.stageId ?? stage.id;
120
120
  const attemptOpt = attemptCtx?.eventOptions();
121
121
  const baseRoots = rootsOverride ??
@@ -143,6 +143,7 @@ export async function runStage(options) {
143
143
  stage,
144
144
  dag,
145
145
  roots,
146
+ commandEnv: stageEnv ?? process.env,
146
147
  });
147
148
  await verifiedExecution.prepare();
148
149
  console.error(`Running stage ${stageId} (${stage.model})...`);
@@ -179,6 +180,7 @@ export async function runStage(options) {
179
180
  ...(sessionMode !== undefined ? { sessionMode } : {}),
180
181
  ...(feedbackLoopContext !== undefined ? { feedbackLoopContext } : {}),
181
182
  ...(resumeToken !== undefined ? { resumeToken } : {}),
183
+ ...(stageEnv !== undefined ? { stageEnv } : {}),
182
184
  onActivity: (event) => {
183
185
  enqueueActivity(event);
184
186
  },
@@ -0,0 +1,9 @@
1
+ import { type LoadOutcome } from "../config/loadOutcome.js";
2
+ export type StageSecretAs = "env" | "helper" | "file";
3
+ export type StageSecretDecl = {
4
+ name: string;
5
+ /** Default: helper for git/gh token names, else env for env-kind registry entries / file for file-kind. */
6
+ as?: "env";
7
+ };
8
+ export declare function defaultSecretDelivery(name: string): "helper" | "env";
9
+ export declare function parseStageSecrets(raw: unknown, label: string, stageId?: string): LoadOutcome<StageSecretDecl[] | undefined>;