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
@@ -0,0 +1,214 @@
1
+ import { readdir, stat, statfs } from "node:fs/promises";
2
+ import path from "node:path";
3
+ const BLOCK_SIZE = 512;
4
+ const STATE_DB_NAMES = ["state.db", "state.db-wal", "state.db-shm"];
5
+ export const DISK_WARN_LOG_PREFIX = "stageflow.disk_warn";
6
+ let walkCallCount = 0;
7
+ let freeSpaceReaderOverride = null;
8
+ export function getDiskUsageWalkCallCount() {
9
+ return walkCallCount;
10
+ }
11
+ export function resetDiskUsageWalkCallCount() {
12
+ walkCallCount = 0;
13
+ }
14
+ export function setFreeSpaceReaderForTests(reader) {
15
+ freeSpaceReaderOverride = reader;
16
+ }
17
+ function allocatedBytes(st) {
18
+ return st.blocks * BLOCK_SIZE;
19
+ }
20
+ async function pathExists(target) {
21
+ try {
22
+ await stat(target);
23
+ return true;
24
+ }
25
+ catch (err) {
26
+ if (err.code === "ENOENT")
27
+ return false;
28
+ throw err;
29
+ }
30
+ }
31
+ /** Walk `rootPath` summing `stat().blocks * 512`. Missing path → 0. */
32
+ export async function diskUsageOf(rootPath) {
33
+ walkCallCount += 1;
34
+ if (!(await pathExists(rootPath)))
35
+ return 0;
36
+ let total = 0;
37
+ async function walk(current) {
38
+ let st;
39
+ try {
40
+ st = await stat(current);
41
+ }
42
+ catch (err) {
43
+ if (err.code === "ENOENT")
44
+ return;
45
+ throw err;
46
+ }
47
+ total += allocatedBytes(st);
48
+ if (!st.isDirectory())
49
+ return;
50
+ let entries;
51
+ try {
52
+ entries = await readdir(current, { withFileTypes: true });
53
+ }
54
+ catch (err) {
55
+ if (err.code === "ENOENT")
56
+ return;
57
+ throw err;
58
+ }
59
+ for (const entry of entries) {
60
+ await walk(path.join(current, entry.name));
61
+ }
62
+ }
63
+ await walk(rootPath);
64
+ return total;
65
+ }
66
+ export async function readFilesystemSize(rootPath) {
67
+ if (freeSpaceReaderOverride !== null) {
68
+ return freeSpaceReaderOverride(rootPath);
69
+ }
70
+ let target = path.resolve(rootPath);
71
+ for (;;) {
72
+ try {
73
+ const fsStats = await statfs(target);
74
+ const blockSize = fsStats.bsize;
75
+ return {
76
+ freeBytes: fsStats.bavail * blockSize,
77
+ totalBytes: fsStats.blocks * blockSize,
78
+ };
79
+ }
80
+ catch (err) {
81
+ if (err.code !== "ENOENT")
82
+ throw err;
83
+ const parent = path.dirname(target);
84
+ if (parent === target)
85
+ throw err;
86
+ target = parent;
87
+ }
88
+ }
89
+ }
90
+ async function usageOfMissingOk(target) {
91
+ try {
92
+ return await diskUsageOf(target);
93
+ }
94
+ catch (err) {
95
+ if (err.code === "ENOENT")
96
+ return 0;
97
+ throw err;
98
+ }
99
+ }
100
+ async function stateDbBytes(durableRoot) {
101
+ let total = 0;
102
+ for (const name of STATE_DB_NAMES) {
103
+ const filePath = path.join(durableRoot, name);
104
+ try {
105
+ const st = await stat(filePath);
106
+ if (st.isFile()) {
107
+ walkCallCount += 1;
108
+ total += allocatedBytes(st);
109
+ }
110
+ }
111
+ catch (err) {
112
+ if (err.code === "ENOENT")
113
+ continue;
114
+ throw err;
115
+ }
116
+ }
117
+ return total;
118
+ }
119
+ /** Category breakdown for the durable root, reusing `diskUsageOf` for directory trees. */
120
+ export async function durableRootDiskBreakdown(durableRoot, options) {
121
+ const freeSpace = options?.freeSpace ?? readFilesystemSize;
122
+ const [runs_bytes, worktrees_bytes, repos_bytes, state_db_bytes, a2a_artifacts_bytes, cache_bytes, size] = await Promise.all([
123
+ usageOfMissingOk(path.join(durableRoot, "runs")),
124
+ usageOfMissingOk(path.join(durableRoot, "worktrees")),
125
+ usageOfMissingOk(path.join(durableRoot, "repos")),
126
+ stateDbBytes(durableRoot),
127
+ usageOfMissingOk(path.join(durableRoot, "a2a-artifacts")),
128
+ usageOfMissingOk(path.join(durableRoot, "cache")),
129
+ freeSpace(durableRoot),
130
+ ]);
131
+ return {
132
+ runs_bytes,
133
+ worktrees_bytes,
134
+ repos_bytes,
135
+ state_db_bytes,
136
+ a2a_artifacts_bytes,
137
+ cache_bytes,
138
+ free_bytes: size.freeBytes,
139
+ };
140
+ }
141
+ /**
142
+ * Parse `STAGEFLOW_DISK_WARN_BYTES` / floor-style env: integer bytes or `N%` of filesystem.
143
+ * Empty / unset / invalid → undefined (no threshold).
144
+ */
145
+ export function parseDiskBytesThreshold(raw, filesystemTotalBytes) {
146
+ if (raw === undefined)
147
+ return undefined;
148
+ const trimmed = raw.trim();
149
+ if (trimmed === "")
150
+ return undefined;
151
+ if (trimmed.endsWith("%")) {
152
+ const pct = Number.parseFloat(trimmed.slice(0, -1));
153
+ if (!Number.isFinite(pct) || pct < 0 || pct > 100)
154
+ return undefined;
155
+ if (!Number.isFinite(filesystemTotalBytes) || filesystemTotalBytes < 0) {
156
+ return undefined;
157
+ }
158
+ return Math.floor((filesystemTotalBytes * pct) / 100);
159
+ }
160
+ const n = Number.parseInt(trimmed, 10);
161
+ if (!Number.isFinite(n) || n < 0)
162
+ return undefined;
163
+ return n;
164
+ }
165
+ const DEFAULT_MIN_FREE_DISK_BYTES = 2 * 1024 * 1024 * 1024;
166
+ /**
167
+ * Resolve `STAGEFLOW_MIN_FREE_DISK_BYTES`: bytes or `%` of filesystem.
168
+ * Unset / empty / invalid → `max(2 GiB, 10% of filesystemTotalBytes)`.
169
+ */
170
+ export function resolveMinFreeDiskFloor(raw, filesystemTotalBytes) {
171
+ const parsed = parseDiskBytesThreshold(raw, filesystemTotalBytes);
172
+ if (parsed !== undefined)
173
+ return parsed;
174
+ const total = Number.isFinite(filesystemTotalBytes) && filesystemTotalBytes > 0
175
+ ? filesystemTotalBytes
176
+ : 0;
177
+ const tenPercent = Math.floor(total * 0.1);
178
+ return Math.max(DEFAULT_MIN_FREE_DISK_BYTES, tenPercent);
179
+ }
180
+ /**
181
+ * One named warning when free space is below `STAGEFLOW_DISK_WARN_BYTES`. Never throws.
182
+ * @returns true when a warning line was emitted
183
+ */
184
+ export async function warnDurableRootDiskIfNeeded(durableRoot, options) {
185
+ try {
186
+ const env = options?.env ?? process.env;
187
+ const raw = env.STAGEFLOW_DISK_WARN_BYTES;
188
+ if (raw === undefined || raw.trim() === "")
189
+ return false;
190
+ const freeSpace = options?.freeSpace ?? readFilesystemSize;
191
+ const size = await freeSpace(durableRoot);
192
+ const threshold = parseDiskBytesThreshold(raw, size.totalBytes);
193
+ if (threshold === undefined)
194
+ return false;
195
+ if (size.freeBytes >= threshold)
196
+ return false;
197
+ const log = options?.log ?? ((line) => console.warn(line));
198
+ log(`${DISK_WARN_LOG_PREFIX}: durable root free space ${size.freeBytes} bytes is below threshold ${threshold} bytes (${raw.trim()}); run sf runs gc`);
199
+ return true;
200
+ }
201
+ catch (err) {
202
+ const log = options?.log ?? ((line) => console.warn(line));
203
+ log(`${DISK_WARN_LOG_PREFIX}: free-space measurement failed: ${err instanceof Error ? err.message : String(err)}`);
204
+ return false;
205
+ }
206
+ }
207
+ /** Measure one run workspace and persist `disk_bytes` / `disk_measured_at`. */
208
+ export async function refreshRunDiskUsage(store, runId, options) {
209
+ const workspaceDir = store.getWorkspaceDir(runId);
210
+ const bytes = await diskUsageOf(workspaceDir);
211
+ const measuredAt = (options?.now ?? new Date()).toISOString();
212
+ await store.setRunDiskUsage(runId, bytes, measuredAt);
213
+ return bytes;
214
+ }
@@ -0,0 +1,8 @@
1
+ import { type RunProjection } from "../projection/projectRun.js";
2
+ import type { RunDetail } from "./port.js";
3
+ import { type RunManifest } from "./runManifest.js";
4
+ export type RunExportPayload = RunProjection & {
5
+ run_manifest: RunManifest | null;
6
+ };
7
+ export declare function buildRunExportPayload(detail: RunDetail): RunExportPayload;
8
+ export declare function runDetailWithRedactedManifest(detail: RunDetail): RunDetail;
@@ -0,0 +1,23 @@
1
+ import { projectRun } from "../projection/projectRun.js";
2
+ import { redactRunManifestForRead, } from "./runManifest.js";
3
+ import { getNamedSecrets } from "../logging/namedSecrets.js";
4
+ export function buildRunExportPayload(detail) {
5
+ return {
6
+ ...projectRun(detail),
7
+ run_manifest: redactRunManifestForRead(detail.run_manifest, {
8
+ namedSecrets: getNamedSecrets(),
9
+ }),
10
+ };
11
+ }
12
+ export function runDetailWithRedactedManifest(detail) {
13
+ const run_manifest = redactRunManifestForRead(detail.run_manifest, {
14
+ namedSecrets: getNamedSecrets(),
15
+ });
16
+ if (run_manifest === null) {
17
+ if (detail.run_manifest === undefined)
18
+ return detail;
19
+ const { run_manifest: _drop, ...rest } = detail;
20
+ return rest;
21
+ }
22
+ return { ...detail, run_manifest };
23
+ }
@@ -1 +1,2 @@
1
1
  export declare function normalizeCatalogPath(raw: string): string;
2
+ export declare function normalizeProjectRoot(raw: string): string;
@@ -1,4 +1,14 @@
1
+ import { realpathSync } from "node:fs";
1
2
  import path from "node:path";
2
3
  export function normalizeCatalogPath(raw) {
3
4
  return path.resolve(raw);
4
5
  }
6
+ export function normalizeProjectRoot(raw) {
7
+ const resolved = path.resolve(raw);
8
+ try {
9
+ return realpathSync(resolved);
10
+ }
11
+ catch {
12
+ return resolved;
13
+ }
14
+ }
@@ -1,6 +1,11 @@
1
1
  export declare function newRunId(): string;
2
2
  /** Root for factory state under a project: `<rootDir>/.stageflow`. */
3
3
  export declare function storeRootFor(rootDir: string): string;
4
+ export declare function isGlobalStageflowHome(rootDir: string): boolean;
5
+ /** Project store root or the durable home when `rootDir` is the global home. */
6
+ export declare function resolveStoreRoot(rootDir: string): string;
7
+ export declare const NESTED_GLOBAL_STORE_CONFLICT = "Cannot open the global store: flat state.db has no runs table while nested .stageflow/state.db still holds run data. Resolve the conflict manually.";
8
+ export declare function flattenNestedGlobalStore(home: string): void;
4
9
  export declare function migrateLegacyStoreRoot(rootDir: string): void;
5
10
  export declare function runsDir(storeRoot: string): string;
6
11
  export declare function runWorkspaceDir(storeRoot: string, runId: string): string;
@@ -1,6 +1,8 @@
1
1
  import { randomBytes } from "node:crypto";
2
- import { readdirSync, renameSync, rmdirSync, statSync } from "node:fs";
2
+ import { existsSync, readdirSync, renameSync, rmdirSync, statSync, } from "node:fs";
3
3
  import path from "node:path";
4
+ import Database from "better-sqlite3";
5
+ import { globalStageflowHome } from "../project/globalHome.js";
4
6
  import { guardStageId } from "./workspaceLayout.js";
5
7
  export function newRunId() {
6
8
  const stamp = new Date().toISOString().replace(/[:.]/g, "-");
@@ -10,6 +12,148 @@ export function newRunId() {
10
12
  export function storeRootFor(rootDir) {
11
13
  return path.join(rootDir, ".stageflow");
12
14
  }
15
+ export function isGlobalStageflowHome(rootDir) {
16
+ return path.resolve(rootDir) === globalStageflowHome();
17
+ }
18
+ /** Project store root or the durable home when `rootDir` is the global home. */
19
+ export function resolveStoreRoot(rootDir) {
20
+ if (isGlobalStageflowHome(rootDir)) {
21
+ return globalStageflowHome();
22
+ }
23
+ return storeRootFor(rootDir);
24
+ }
25
+ const NESTED_GLOBAL_STORE_DIR = ".stageflow";
26
+ const STATE_DB_BASENAME = "state.db";
27
+ const STATE_DB_SIDECARS = ["state.db-wal", "state.db-shm"];
28
+ const NESTED_STORE_MOVE_DIRS = ["runs", "a2a-artifacts"];
29
+ export const NESTED_GLOBAL_STORE_CONFLICT = "Cannot open the global store: flat state.db has no runs table while nested .stageflow/state.db still holds run data. Resolve the conflict manually.";
30
+ function sqliteHasRunsTable(dbPath) {
31
+ if (!existsSync(dbPath)) {
32
+ return false;
33
+ }
34
+ let db;
35
+ try {
36
+ db = new Database(dbPath, { readonly: true, fileMustExist: true });
37
+ const row = db
38
+ .prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'runs'")
39
+ .get();
40
+ return row !== undefined;
41
+ }
42
+ catch {
43
+ return false;
44
+ }
45
+ finally {
46
+ db?.close();
47
+ }
48
+ }
49
+ function assertNoNestedGlobalStoreConflict(home) {
50
+ const flatDb = path.join(home, STATE_DB_BASENAME);
51
+ const nestedDb = path.join(home, NESTED_GLOBAL_STORE_DIR, STATE_DB_BASENAME);
52
+ if (!existsSync(flatDb) || !existsSync(nestedDb)) {
53
+ return;
54
+ }
55
+ if (!sqliteHasRunsTable(flatDb) && sqliteHasRunsTable(nestedDb)) {
56
+ throw new Error(NESTED_GLOBAL_STORE_CONFLICT);
57
+ }
58
+ }
59
+ function renameIfExists(from, to) {
60
+ if (!existsSync(from) || existsSync(to)) {
61
+ return;
62
+ }
63
+ renameSync(from, to);
64
+ }
65
+ function rollbackStateDbSet(home, nested, moved) {
66
+ for (const name of [...moved].reverse()) {
67
+ const from = path.join(home, name);
68
+ const to = path.join(nested, name);
69
+ if (existsSync(from) && !existsSync(to)) {
70
+ renameSync(from, to);
71
+ }
72
+ }
73
+ }
74
+ function moveStateDbSet(home, nested) {
75
+ const flatMain = path.join(home, STATE_DB_BASENAME);
76
+ const nestedMain = path.join(nested, STATE_DB_BASENAME);
77
+ const flatHasMain = existsSync(flatMain);
78
+ const nestedHasMain = existsSync(nestedMain);
79
+ if (flatHasMain && nestedHasMain) {
80
+ return;
81
+ }
82
+ if (flatHasMain && !nestedHasMain) {
83
+ for (const sidecar of STATE_DB_SIDECARS) {
84
+ renameIfExists(path.join(nested, sidecar), path.join(home, sidecar));
85
+ }
86
+ return;
87
+ }
88
+ if (!flatHasMain && nestedHasMain) {
89
+ const names = [STATE_DB_BASENAME, ...STATE_DB_SIDECARS];
90
+ const moved = [];
91
+ try {
92
+ for (const name of names) {
93
+ const from = path.join(nested, name);
94
+ const to = path.join(home, name);
95
+ if (existsSync(from) && !existsSync(to)) {
96
+ renameSync(from, to);
97
+ moved.push(name);
98
+ }
99
+ }
100
+ }
101
+ catch (err) {
102
+ rollbackStateDbSet(home, nested, moved);
103
+ throw err;
104
+ }
105
+ }
106
+ }
107
+ function flatStorePathAbsent(home, name) {
108
+ const target = path.join(home, name);
109
+ const state = directoryState(target);
110
+ return state === "missing";
111
+ }
112
+ function moveNestedStoreDirs(home, nested) {
113
+ for (const dirName of NESTED_STORE_MOVE_DIRS) {
114
+ const from = path.join(nested, dirName);
115
+ const to = path.join(home, dirName);
116
+ if (directoryState(from) === "missing") {
117
+ continue;
118
+ }
119
+ if (!flatStorePathAbsent(home, dirName)) {
120
+ continue;
121
+ }
122
+ renameSync(from, to);
123
+ }
124
+ }
125
+ function removeNestedGlobalStoreDirIfEmpty(home) {
126
+ const nested = path.join(home, NESTED_GLOBAL_STORE_DIR);
127
+ const state = directoryState(nested);
128
+ if (state === "missing" || state === "other") {
129
+ return;
130
+ }
131
+ const inventory = [
132
+ STATE_DB_BASENAME,
133
+ ...STATE_DB_SIDECARS,
134
+ ...NESTED_STORE_MOVE_DIRS,
135
+ ];
136
+ for (const name of inventory) {
137
+ if (existsSync(path.join(nested, name))) {
138
+ return;
139
+ }
140
+ }
141
+ if (state === "empty") {
142
+ rmdirSync(nested);
143
+ }
144
+ }
145
+ export function flattenNestedGlobalStore(home) {
146
+ const nested = path.join(home, NESTED_GLOBAL_STORE_DIR);
147
+ const nestedState = directoryState(nested);
148
+ if (nestedState === "missing" || nestedState === "other") {
149
+ assertNoNestedGlobalStoreConflict(home);
150
+ return;
151
+ }
152
+ assertNoNestedGlobalStoreConflict(home);
153
+ moveStateDbSet(home, nested);
154
+ moveNestedStoreDirs(home, nested);
155
+ removeNestedGlobalStoreDirIfEmpty(home);
156
+ }
13
157
  function directoryState(dirPath) {
14
158
  try {
15
159
  const st = statSync(dirPath);
@@ -1,18 +1,21 @@
1
1
  import type { StageLogLine } from "../agent/activity.js";
2
+ import type { ConfigOriginRecord } from "../config/configOrigin.js";
3
+ import type { RunSubmission, RunSubmissionRecord } from "./submission.js";
2
4
  import type { AskOperatorPrompt } from "../tools/askOperator.js";
3
5
  import type { StageEnvelope } from "../types/envelope.js";
4
- import type { FeedbackLoopConfig, ResolvedPipelineDag } from "../types/pipeline.js";
6
+ import type { FeedbackLoopConfig, InlinePipelineDefinition, ResolvedPipelineDag } from "../types/pipeline.js";
5
7
  import type { CompletionCheck } from "../types/completion.js";
6
8
  import type { StageGateKind } from "../types/stage.js";
7
9
  import type { StageUsage } from "../types/usage.js";
8
- export type RunStatus = "created" | "running" | "succeeded" | "failed";
10
+ export type { ConfigOriginRecord };
11
+ export type RunStatus = "created" | "queued" | "running" | "succeeded" | "failed" | "cancelled";
9
12
  export type RunPipelineDagSnapshot = ResolvedPipelineDag & {
10
13
  stage_ids: string[];
11
14
  gate_kinds?: Record<string, StageGateKind[]>;
12
15
  /** IR: per-stage clone assignment schema. YAML: `io.input.schema`. */
13
16
  clone_input_schema?: Record<string, unknown>;
14
17
  };
15
- export type StageReadiness = "blocked" | "ready" | "running" | "waiting" | "succeeded" | "failed" | "skipped";
18
+ export type StageReadiness = "blocked" | "ready" | "running" | "waiting" | "interrupted" | "succeeded" | "failed" | "skipped";
16
19
  export type PipelineTrackNode = {
17
20
  stage_id: string;
18
21
  status: StageSnapshot["status"];
@@ -51,6 +54,27 @@ export type RunMeta = {
51
54
  pipeline_path?: string;
52
55
  task_path?: string;
53
56
  project_root?: string;
57
+ repository?: string;
58
+ ref?: string;
59
+ resolved_sha?: string;
60
+ run_branch?: string;
61
+ git_author_name?: string;
62
+ git_author_email?: string;
63
+ cancel_reason?: string;
64
+ finished_at?: string;
65
+ slimmed_at?: string;
66
+ disk_bytes?: number;
67
+ disk_measured_at?: string;
68
+ config_origins?: ConfigOriginRecord[];
69
+ pipeline_source?: "inline" | "path";
70
+ caller_id?: string;
71
+ run_manifest?: unknown;
72
+ skip_gates?: boolean;
73
+ /**
74
+ * Parsed inline pipeline body for stage-worker reload when there is no
75
+ * pipeline_path. Derived from stored pipeline_body.
76
+ */
77
+ inline_pipeline?: InlinePipelineDefinition;
54
78
  };
55
79
  export type CreatedRun = {
56
80
  runId: string;
@@ -64,7 +88,7 @@ export type StageLogEvent = StageLogLine & {
64
88
  export type StageSnapshot = {
65
89
  stage_id: string;
66
90
  definition_id?: string;
67
- status: "pending" | "running" | "waiting_for_input" | "succeeded" | "failed" | "skipped";
91
+ status: "pending" | "running" | "waiting_for_input" | "interrupted" | "succeeded" | "failed" | "skipped";
68
92
  events: StageLogEvent[];
69
93
  envelope: StageEnvelope | null;
70
94
  artifacts: string[];
@@ -85,6 +109,7 @@ export type StageExecution = {
85
109
  envelope: StageEnvelope | null;
86
110
  cost_usd?: number;
87
111
  usage?: StageUsage;
112
+ auto_resume_count: number;
88
113
  };
89
114
  export type StageExecutionPatch = {
90
115
  status?: StageSnapshot["status"];
@@ -94,6 +119,7 @@ export type StageExecutionPatch = {
94
119
  envelope?: StageEnvelope | null;
95
120
  cost_usd?: number;
96
121
  usage?: StageUsage;
122
+ auto_resume_count?: number;
97
123
  };
98
124
  /** The durable disposition of completion verification for one stage attempt. */
99
125
  export type VerificationOutcome = "not_run" | "passed" | "failed" | "error";
@@ -229,6 +255,16 @@ export type CompactStage = {
229
255
  definition_id?: string;
230
256
  cost_usd?: number;
231
257
  };
258
+ export type RunBindingCompact = {
259
+ kind: "repository" | "checkout" | "unbound";
260
+ repository?: string;
261
+ ref?: string;
262
+ resolved_sha?: string;
263
+ };
264
+ export type RunBindingDetail = RunBindingCompact & {
265
+ run_branch?: string;
266
+ checkout_root?: string;
267
+ };
232
268
  export type RunSummary = {
233
269
  run_id: string;
234
270
  pipeline_id: string;
@@ -236,9 +272,11 @@ export type RunSummary = {
236
272
  pipeline_path?: string;
237
273
  task_path?: string;
238
274
  project_root?: string;
275
+ pipeline_source?: "inline" | "path";
239
276
  status: RunStatus;
240
277
  created_at: string;
241
278
  updated_at?: string;
279
+ binding: RunBindingCompact;
242
280
  stages: CompactStage[];
243
281
  /** Present when a stage is waiting_for_input (first such stage). */
244
282
  waiting_stage_id?: string;
@@ -256,13 +294,22 @@ export type RunSummary = {
256
294
  active_feedback_loop?: FeedbackLoopRecord;
257
295
  /** Sum of every stage's cost_usd; omitted when no stage reported usage. */
258
296
  total_cost_usd?: number;
297
+ cancel_reason?: string;
298
+ finished_at?: string;
299
+ slimmed_at?: string;
300
+ disk_bytes?: number;
301
+ disk_measured_at?: string;
259
302
  };
260
- export type RunDetail = Omit<RunSummary, "stages"> & {
303
+ export type RunDetail = Omit<RunSummary, "stages" | "binding"> & {
304
+ binding: RunBindingDetail;
261
305
  task_yaml: string;
262
306
  stages: StageSnapshot[];
263
307
  pipeline_track: PipelineTrackProjection;
264
308
  /** Append-only feedback-loop and replay history, ordered by creation. */
265
309
  feedback_loops: FeedbackLoopHistory[];
310
+ config_origins?: ConfigOriginRecord[];
311
+ caller_id?: string;
312
+ run_manifest?: unknown;
266
313
  };
267
314
  export type FeedbackLoopHistory = {
268
315
  loop: FeedbackLoopRecord;
@@ -275,6 +322,8 @@ export type FeedbackLoopHistory = {
275
322
  fork_generations: ForkGenerationRecord[];
276
323
  };
277
324
  export type CreateRunInput = {
325
+ submission?: RunSubmission;
326
+ runId?: string;
278
327
  pipelineId: string;
279
328
  taskYaml: string;
280
329
  taskId?: string;
@@ -286,6 +335,24 @@ export type CreateRunInput = {
286
335
  pipelinePath?: string;
287
336
  taskPath?: string;
288
337
  projectRoot?: string;
338
+ repository?: string;
339
+ ref?: string;
340
+ resolvedSha?: string;
341
+ runBranch?: string;
342
+ gitAuthorName?: string;
343
+ gitAuthorEmail?: string;
344
+ status?: RunStatus;
345
+ pipelineSource?: "inline" | "path";
346
+ /** Durable JSON string stored in pipeline_body. Prefer this for slots writers. */
347
+ pipelineBody?: string;
348
+ /**
349
+ * Convenience for callers/tests that have a parsed inline pipeline.
350
+ * Stored as pipeline_body JSON; surfaced back as meta.inline_pipeline on read.
351
+ */
352
+ inlinePipeline?: InlinePipelineDefinition;
353
+ callerId?: string;
354
+ runManifest?: unknown;
355
+ skipGates?: boolean;
289
356
  };
290
357
  export type ListRunsFilter = {
291
358
  status?: RunStatus;
@@ -293,6 +360,7 @@ export type ListRunsFilter = {
293
360
  since?: string;
294
361
  /** Match pipeline_id or pipeline_path */
295
362
  pipeline?: string;
363
+ caller_id?: string;
296
364
  };
297
365
  /**
298
366
  * Persistence port for pipeline run state.
@@ -301,10 +369,49 @@ export type ListRunsFilter = {
301
369
  * run id is all you have (catalog / rehydrate).
302
370
  */
303
371
  export interface RunStore {
372
+ /** Checkpoint WAL (when applicable) and release the store connection. */
373
+ close(): Promise<void>;
374
+ /**
375
+ * Consistent compacted snapshot of the live DB via `VACUUM INTO`.
376
+ * Only SqliteRunStore implements this; callers must not open a second raw connection.
377
+ */
378
+ snapshotInto(destPath: string): Promise<{
379
+ userVersion: number;
380
+ }>;
381
+ getRunBySubmission(key: string): Promise<RunSubmissionRecord | null>;
304
382
  createRun(input: CreateRunInput): Promise<CreatedRun>;
305
383
  updateRunStatus(runId: string, status: RunStatus): Promise<void>;
384
+ /**
385
+ * Conditional status update (CAS): sets `status` only when the row currently
386
+ * has `expectedStatus`. Returns false when the run exists but status differs;
387
+ * throws when the run is missing.
388
+ */
389
+ tryUpdateRunStatus(runId: string, status: RunStatus, expectedStatus: RunStatus): Promise<boolean>;
390
+ /** Patch workspace-binding columns on an existing run (queued → materialize). */
391
+ patchRunWorkspaceBinding(runId: string, patch: {
392
+ checkoutRoot?: string;
393
+ repository?: string;
394
+ ref?: string;
395
+ resolvedSha?: string;
396
+ runBranch?: string;
397
+ }): Promise<void>;
398
+ /** Append resolved config origin records (MCP / skill / verify). Dedupes by name+origin+path. */
399
+ appendConfigOrigins(runId: string, origins: ConfigOriginRecord[]): Promise<void>;
400
+ setCancelReason(runId: string, reason: string): Promise<void>;
401
+ /** Persist cached disk usage for `listRuns` (never measured inside listRuns). */
402
+ setRunDiskUsage(runId: string, diskBytes: number, measuredAt: string): Promise<void>;
403
+ /** Record that SLIM reclaim completed for this run (R18 idempotency gate). */
404
+ setSlimmedAt(runId: string, slimmedAt: string): Promise<void>;
405
+ /** Hard-delete every run-scoped row. Throws `Run not found: …` when missing. */
406
+ deleteRun(runId: string): Promise<void>;
306
407
  readRunMeta(runId: string): Promise<RunMeta>;
307
408
  readTaskYaml(runId: string): Promise<string>;
409
+ readPipelineBody(runId: string): Promise<string | null>;
410
+ /**
411
+ * Atomic replace of the `run_manifest` JSON blob (build → redact → write).
412
+ * Pass `null` to clear.
413
+ */
414
+ updateRunManifest(runId: string, manifest: unknown): Promise<void>;
308
415
  /** Opaque run workspace root for agents and artifact tools. */
309
416
  getWorkspaceDir(runId: string): string;
310
417
  ensureStageWorkspace(runId: string, stageId: string): Promise<void>;
@@ -314,6 +421,8 @@ export interface RunStore {
314
421
  getLatestStageExecution(runId: string, stageId: string): Promise<StageExecution | null>;
315
422
  countStageAttempts(runId: string, stageId: string): Promise<number>;
316
423
  getStageExecution(runId: string, stageId: string, attempt: number): Promise<StageExecution>;
424
+ /** Latest attempt rows whose status is `interrupted` (targeted scan; not listRuns). */
425
+ listInterruptedStageExecutions(): Promise<StageExecution[]>;
317
426
  updateStageExecution(runId: string, stageId: string, attempt: number, patch: StageExecutionPatch): Promise<void>;
318
427
  /** Create or update evidence for one check in a stage execution attempt. */
319
428
  upsertVerificationCheckResult(runId: string, stageId: string, result: VerificationCheckResultPatch, options?: {
@@ -333,8 +442,10 @@ export interface RunStore {
333
442
  }): Promise<void>;
334
443
  listStageEvents(runId: string, stageId: string, attempt?: number): Promise<StageLogEvent[]>;
335
444
  listRuns(filter?: ListRunsFilter): Promise<RunSummary[]>;
336
- /** Every distinct non-empty project_root recorded across all runs. */
337
- listProjectRoots(): Promise<string[]>;
445
+ /** Idempotent upsert; returns the realpath/resolve-normalized absolute key. */
446
+ ensureProject(absPath: string): Promise<string>;
447
+ /** Absolute roots from the durable projects registry. */
448
+ listRegisteredProjects(): Promise<string[]>;
338
449
  readRun(runId: string): Promise<RunDetail>;
339
450
  updatePipelineDag(runId: string, dag: RunPipelineDagSnapshot): Promise<void>;
340
451
  createFeedbackLoop(runId: string, input: CreateFeedbackLoopInput): Promise<FeedbackLoopRecord>;
@@ -364,4 +475,4 @@ export interface RunStore {
364
475
  }
365
476
  export declare function stageStatusFromEvents(events: StageLogEvent[]): StageSnapshot["status"];
366
477
  export declare function findUnhandledFailedStage(stages: StageSnapshot[], dag?: Pick<RunPipelineDagSnapshot, "nodes"> | null): StageSnapshot | undefined;
367
- export declare function deriveStatusFromStages(stages: StageSnapshot[], dag?: Pick<RunPipelineDagSnapshot, "nodes"> | null): RunStatus;
478
+ export declare function deriveStatusFromStages(stages: StageSnapshot[], dag?: Pick<RunPipelineDagSnapshot, "nodes"> | null, currentStatus?: RunStatus): RunStatus;