stageflow 0.25.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (501) hide show
  1. package/README.md +10 -3
  2. package/dist/a2a/contracts.d.ts +1 -0
  3. package/dist/a2a/contracts.js +1 -0
  4. package/dist/a2a/registry.d.ts +2 -0
  5. package/dist/a2a/registry.js +20 -1
  6. package/dist/a2a/server.d.ts +7 -0
  7. package/dist/a2a/server.js +5 -2
  8. package/dist/a2a/service.d.ts +1 -1
  9. package/dist/a2a/service.js +57 -8
  10. package/dist/a2a/store.d.ts +8 -5
  11. package/dist/a2a/store.js +24 -67
  12. package/dist/agent/activity.d.ts +3 -0
  13. package/dist/agent/bootProviderConfig.d.ts +21 -0
  14. package/dist/agent/bootProviderConfig.js +74 -0
  15. package/dist/agent/piAdapter.d.ts +9 -1
  16. package/dist/agent/piAdapter.js +58 -3
  17. package/dist/agent/piIsolatedMcp.d.ts +2 -0
  18. package/dist/agent/piIsolatedMcp.js +10 -2
  19. package/dist/agent/port.d.ts +6 -0
  20. package/dist/agent/streamLogRedact.d.ts +1 -1
  21. package/dist/agent/streamLogRedact.js +4 -11
  22. package/dist/cli/artifactCommand.js +9 -16
  23. package/dist/cli/backupCommand.d.ts +9 -0
  24. package/dist/cli/backupCommand.js +111 -0
  25. package/dist/cli/debugRunCommand.d.ts +10 -0
  26. package/dist/cli/debugRunCommand.js +109 -0
  27. package/dist/cli/doctorCommand.d.ts +32 -0
  28. package/dist/cli/doctorCommand.js +453 -0
  29. package/dist/cli/envelopeCommand.js +9 -2
  30. package/dist/cli/exportAllCommand.d.ts +55 -0
  31. package/dist/cli/exportAllCommand.js +218 -0
  32. package/dist/cli/exportRunCommand.js +23 -21
  33. package/dist/cli/hostClient.d.ts +21 -3
  34. package/dist/cli/hostClient.js +93 -12
  35. package/dist/cli/initTemplates.d.ts +1 -1
  36. package/dist/cli/initTemplates.js +8 -1
  37. package/dist/cli/resolveSafeOutPath.d.ts +2 -0
  38. package/dist/cli/resolveSafeOutPath.js +40 -0
  39. package/dist/cli/restoreCommand.d.ts +10 -0
  40. package/dist/cli/restoreCommand.js +90 -0
  41. package/dist/cli/runCommand.d.ts +3 -2
  42. package/dist/cli/runCommand.js +93 -11
  43. package/dist/cli/runOutput.d.ts +4 -0
  44. package/dist/cli/runOutput.js +33 -7
  45. package/dist/cli/runStageCommand.d.ts +1 -0
  46. package/dist/cli/runStageCommand.js +44 -25
  47. package/dist/cli/runsCommand.d.ts +1 -1
  48. package/dist/cli/runsCommand.js +162 -8
  49. package/dist/cli.js +171 -30
  50. package/dist/config/catalogRelativePath.d.ts +62 -0
  51. package/dist/config/catalogRelativePath.js +118 -0
  52. package/dist/config/configOrigin.d.ts +27 -0
  53. package/dist/config/configOrigin.js +59 -0
  54. package/dist/config/hostConfig.d.ts +55 -0
  55. package/dist/config/hostConfig.js +333 -0
  56. package/dist/config/listSkills.d.ts +26 -0
  57. package/dist/config/listSkills.js +190 -3
  58. package/dist/config/loadOutcome.d.ts +1 -1
  59. package/dist/config/loadPipeline.js +23 -2
  60. package/dist/config/loadStage.js +18 -0
  61. package/dist/config/loadTask.d.ts +2 -0
  62. package/dist/config/loadTask.js +57 -18
  63. package/dist/config/materializeStageModels.js +5 -1
  64. package/dist/config/mergePipelineIncludes.d.ts +3 -0
  65. package/dist/config/mergePipelineIncludes.js +19 -0
  66. package/dist/config/migrateYaml.js +6 -11
  67. package/dist/config/multiProjectCatalog.d.ts +30 -0
  68. package/dist/config/multiProjectCatalog.js +142 -0
  69. package/dist/config/normalizePipelineStageEntry.js +20 -1
  70. package/dist/config/pipelineStageKeys.js +2 -0
  71. package/dist/config/resolveCatalogRoots.d.ts +27 -0
  72. package/dist/config/resolveCatalogRoots.js +76 -0
  73. package/dist/config/resolveModel.d.ts +7 -1
  74. package/dist/config/resolveModel.js +11 -1
  75. package/dist/config/resolveStageMcpServers.d.ts +9 -1
  76. package/dist/config/resolveStageMcpServers.js +36 -9
  77. package/dist/config/secretFromEnvOrFile.d.ts +9 -0
  78. package/dist/config/secretFromEnvOrFile.js +34 -0
  79. package/dist/config/seededCatalog.d.ts +11 -0
  80. package/dist/config/seededCatalog.js +29 -0
  81. package/dist/config/toolRequires.d.ts +24 -0
  82. package/dist/config/toolRequires.js +115 -0
  83. package/dist/config/validateCatalog.d.ts +5 -1
  84. package/dist/config/validateCatalog.js +140 -1
  85. package/dist/diagnostics/checks.d.ts +39 -0
  86. package/dist/diagnostics/checks.js +135 -0
  87. package/dist/errors/codes.d.ts +14 -0
  88. package/dist/errors/codes.js +59 -0
  89. package/dist/git/cache.d.ts +14 -0
  90. package/dist/git/cache.js +150 -0
  91. package/dist/git/credentials.d.ts +9 -0
  92. package/dist/git/credentials.js +88 -0
  93. package/dist/git/errors.d.ts +20 -0
  94. package/dist/git/errors.js +94 -0
  95. package/dist/git/exec.d.ts +16 -0
  96. package/dist/git/exec.js +135 -0
  97. package/dist/git/operations.d.ts +29 -0
  98. package/dist/git/operations.js +153 -0
  99. package/dist/git/version.d.ts +1 -0
  100. package/dist/git/version.js +10 -0
  101. package/dist/logging/audit.d.ts +16 -0
  102. package/dist/logging/audit.js +19 -0
  103. package/dist/logging/commonWords.d.ts +2 -0
  104. package/dist/logging/commonWords.js +13 -0
  105. package/dist/logging/logger.d.ts +49 -0
  106. package/dist/logging/logger.js +193 -0
  107. package/dist/logging/namedSecrets.d.ts +6 -0
  108. package/dist/logging/namedSecrets.js +58 -0
  109. package/dist/logging/redact.d.ts +13 -0
  110. package/dist/logging/redact.js +88 -0
  111. package/dist/mcp/catalogTools.js +456 -80
  112. package/dist/mcp/checkoutTools.d.ts +52 -0
  113. package/dist/mcp/checkoutTools.js +332 -0
  114. package/dist/mcp/controlTools.js +179 -5
  115. package/dist/mcp/deps.d.ts +1 -0
  116. package/dist/mcp/getStartedTool.d.ts +3 -0
  117. package/dist/mcp/getStartedTool.js +62 -0
  118. package/dist/mcp/readArtifact.d.ts +11 -0
  119. package/dist/mcp/readArtifact.js +46 -30
  120. package/dist/mcp/server.js +1 -0
  121. package/dist/mcp/tools.js +4 -0
  122. package/dist/net/proxy.d.ts +14 -0
  123. package/dist/net/proxy.js +78 -0
  124. package/dist/package-meta.d.ts +2 -1
  125. package/dist/package-meta.js +2 -1
  126. package/dist/preflight/bash.d.ts +8 -0
  127. package/dist/preflight/bash.js +34 -0
  128. package/dist/preflight/claudeRoot.d.ts +11 -0
  129. package/dist/preflight/claudeRoot.js +23 -0
  130. package/dist/preflight/mcpCommands.d.ts +8 -0
  131. package/dist/preflight/mcpCommands.js +36 -0
  132. package/dist/preflight/pipelinePreflight.d.ts +40 -0
  133. package/dist/preflight/pipelinePreflight.js +192 -0
  134. package/dist/preflight/tls.d.ts +6 -0
  135. package/dist/preflight/tls.js +18 -0
  136. package/dist/preflight/toolchain.d.ts +45 -0
  137. package/dist/preflight/toolchain.js +219 -0
  138. package/dist/project/findProjectRoot.js +3 -4
  139. package/dist/project/globalHome.d.ts +7 -0
  140. package/dist/project/globalHome.js +63 -2
  141. package/dist/project/resolveProjectContext.js +2 -2
  142. package/dist/project/resolveStageflowContext.js +2 -2
  143. package/dist/projection/projectRun.d.ts +2 -1
  144. package/dist/projection/projectRun.js +1 -0
  145. package/dist/runstore/assertTmpdir.d.ts +1 -0
  146. package/dist/runstore/assertTmpdir.js +21 -0
  147. package/dist/runstore/backup.d.ts +52 -0
  148. package/dist/runstore/backup.js +292 -0
  149. package/dist/runstore/createStore.d.ts +18 -1
  150. package/dist/runstore/createStore.js +64 -5
  151. package/dist/runstore/debugBundle.d.ts +60 -0
  152. package/dist/runstore/debugBundle.js +143 -0
  153. package/dist/runstore/diskUsage.d.ts +50 -0
  154. package/dist/runstore/diskUsage.js +214 -0
  155. package/dist/runstore/exportRunPayload.d.ts +8 -0
  156. package/dist/runstore/exportRunPayload.js +23 -0
  157. package/dist/runstore/normalizeCatalogPath.d.ts +1 -0
  158. package/dist/runstore/normalizeCatalogPath.js +10 -0
  159. package/dist/runstore/paths.d.ts +5 -0
  160. package/dist/runstore/paths.js +145 -1
  161. package/dist/runstore/port.d.ts +111 -13
  162. package/dist/runstore/port.js +8 -1
  163. package/dist/runstore/restore.d.ts +81 -0
  164. package/dist/runstore/restore.js +355 -0
  165. package/dist/runstore/retention.d.ts +27 -0
  166. package/dist/runstore/retention.js +74 -0
  167. package/dist/runstore/runManifest.d.ts +146 -0
  168. package/dist/runstore/runManifest.js +288 -0
  169. package/dist/runstore/runProjection.d.ts +3 -1
  170. package/dist/runstore/runProjection.js +63 -1
  171. package/dist/runstore/sqlite/SqliteRunStore.d.ts +28 -3
  172. package/dist/runstore/sqlite/SqliteRunStore.js +352 -219
  173. package/dist/runstore/sqlite/applyStorePragmas.d.ts +39 -0
  174. package/dist/runstore/sqlite/applyStorePragmas.js +145 -0
  175. package/dist/runstore/sqlite/migrations/001-baseline.d.ts +7 -0
  176. package/dist/runstore/sqlite/migrations/001-baseline.js +10 -0
  177. package/dist/runstore/sqlite/migrations/002-repository-binding.d.ts +7 -0
  178. package/dist/runstore/sqlite/migrations/002-repository-binding.js +29 -0
  179. package/dist/runstore/sqlite/migrations/003-run-lifecycle.d.ts +7 -0
  180. package/dist/runstore/sqlite/migrations/003-run-lifecycle.js +28 -0
  181. package/dist/runstore/sqlite/migrations/004-auto-resume-count.d.ts +7 -0
  182. package/dist/runstore/sqlite/migrations/004-auto-resume-count.js +19 -0
  183. package/dist/runstore/sqlite/migrations/005-config-origins.d.ts +7 -0
  184. package/dist/runstore/sqlite/migrations/005-config-origins.js +19 -0
  185. package/dist/runstore/sqlite/migrations/006-pipeline-body-and-caller.d.ts +7 -0
  186. package/dist/runstore/sqlite/migrations/006-pipeline-body-and-caller.js +28 -0
  187. package/dist/runstore/sqlite/migrations/007-projects-registry.d.ts +7 -0
  188. package/dist/runstore/sqlite/migrations/007-projects-registry.js +31 -0
  189. package/dist/runstore/sqlite/migrations/index.d.ts +21 -0
  190. package/dist/runstore/sqlite/migrations/index.js +72 -0
  191. package/dist/runstore/sqlite/schema.d.ts +20 -1
  192. package/dist/runstore/sqlite/schema.js +277 -1
  193. package/dist/runstore/sqlite/storeOpenError.d.ts +6 -0
  194. package/dist/runstore/sqlite/storeOpenError.js +11 -0
  195. package/dist/runstore/sqlite/storeSchemaError.d.ts +5 -0
  196. package/dist/runstore/sqlite/storeSchemaError.js +8 -0
  197. package/dist/runstore/stageExecution.js +3 -0
  198. package/dist/runstore/storeFilesystem.d.ts +32 -0
  199. package/dist/runstore/storeFilesystem.js +148 -0
  200. package/dist/runstore/trackProjection.js +2 -0
  201. package/dist/runstore/workspaceLayout.d.ts +13 -0
  202. package/dist/runstore/workspaceLayout.js +76 -0
  203. package/dist/runtime/completionCheckRunner.d.ts +3 -0
  204. package/dist/runtime/completionCheckRunner.js +45 -7
  205. package/dist/runtime/containerLimits.d.ts +16 -0
  206. package/dist/runtime/containerLimits.js +84 -0
  207. package/dist/runtime/credentialMaterialisation.d.ts +28 -0
  208. package/dist/runtime/credentialMaterialisation.js +128 -0
  209. package/dist/runtime/gitCheckoutCapability.js +7 -13
  210. package/dist/runtime/pipelineRunner.d.ts +36 -1
  211. package/dist/runtime/pipelineRunner.js +178 -17
  212. package/dist/runtime/pipelineScheduler.d.ts +15 -0
  213. package/dist/runtime/pipelineScheduler.js +135 -27
  214. package/dist/runtime/reloadRunCatalog.js +6 -1
  215. package/dist/runtime/repositoryMaterialize.d.ts +47 -0
  216. package/dist/runtime/repositoryMaterialize.js +235 -0
  217. package/dist/runtime/resumeTimedOut.d.ts +1 -1
  218. package/dist/runtime/resumeTimedOut.js +5 -2
  219. package/dist/runtime/runChangeBus.js +9 -0
  220. package/dist/runtime/runDeletion.d.ts +7 -0
  221. package/dist/runtime/runDeletion.js +37 -0
  222. package/dist/runtime/runManager.d.ts +158 -4
  223. package/dist/runtime/runManager.js +1596 -79
  224. package/dist/runtime/runRetentionSweep.d.ts +31 -0
  225. package/dist/runtime/runRetentionSweep.js +328 -0
  226. package/dist/runtime/runRetryCoordinator.d.ts +2 -0
  227. package/dist/runtime/runRetryCoordinator.js +19 -2
  228. package/dist/runtime/runSkills.d.ts +40 -0
  229. package/dist/runtime/runSkills.js +173 -0
  230. package/dist/runtime/stageAttemptBootstrap.d.ts +5 -1
  231. package/dist/runtime/stageAttemptBootstrap.js +195 -24
  232. package/dist/runtime/stageCacheEnv.d.ts +6 -0
  233. package/dist/runtime/stageCacheEnv.js +34 -0
  234. package/dist/runtime/stageConcurrency.d.ts +2 -1
  235. package/dist/runtime/stageConcurrency.js +4 -2
  236. package/dist/runtime/stageEnvironment.d.ts +38 -0
  237. package/dist/runtime/stageEnvironment.js +193 -0
  238. package/dist/runtime/stageProcessLauncher.d.ts +18 -0
  239. package/dist/runtime/stageProcessLauncher.js +194 -38
  240. package/dist/runtime/stageRecovery.d.ts +3 -1
  241. package/dist/runtime/stageRecovery.js +19 -4
  242. package/dist/runtime/stageRoots.d.ts +38 -1
  243. package/dist/runtime/stageRoots.js +118 -0
  244. package/dist/runtime/stageRunner.d.ts +1 -0
  245. package/dist/runtime/stageRunner.js +3 -1
  246. package/dist/runtime/stageSecretDecl.d.ts +9 -0
  247. package/dist/runtime/stageSecretDecl.js +102 -0
  248. package/dist/runtime/stageSecrets.d.ts +40 -0
  249. package/dist/runtime/stageSecrets.js +181 -0
  250. package/dist/runtime/stageWorker.d.ts +3 -2
  251. package/dist/runtime/stageWorker.js +58 -10
  252. package/dist/runtime/stageWorkerProtocol.d.ts +5 -0
  253. package/dist/runtime/stageWorkerProtocol.js +8 -0
  254. package/dist/runtime/startPayload.d.ts +32 -0
  255. package/dist/runtime/startPayload.js +59 -0
  256. package/dist/runtime/taskInput.js +14 -5
  257. package/dist/runtime/verifiedStageExecution.d.ts +1 -0
  258. package/dist/runtime/verifiedStageExecution.js +2 -1
  259. package/dist/runtime/workspaceBinding.d.ts +20 -0
  260. package/dist/runtime/workspaceBinding.js +60 -0
  261. package/dist/server/allowedHosts.d.ts +19 -0
  262. package/dist/server/allowedHosts.js +179 -0
  263. package/dist/server/bootstrap.d.ts +36 -2
  264. package/dist/server/bootstrap.js +209 -5
  265. package/dist/server/controlToken.d.ts +47 -0
  266. package/dist/server/controlToken.js +241 -0
  267. package/dist/server/createHttpHost.d.ts +11 -2
  268. package/dist/server/createHttpHost.js +103 -10
  269. package/dist/server/ensureGlobalService.d.ts +5 -1
  270. package/dist/server/ensureGlobalService.js +35 -1
  271. package/dist/server/healthSurfaces.d.ts +6 -0
  272. package/dist/server/healthSurfaces.js +98 -0
  273. package/dist/server/http.d.ts +12 -1
  274. package/dist/server/http.js +1085 -485
  275. package/dist/server/listenHost.d.ts +5 -0
  276. package/dist/server/listenHost.js +56 -0
  277. package/dist/server/mcpHost.d.ts +10 -1
  278. package/dist/server/mcpHost.js +36 -6
  279. package/dist/server/operatorResults.d.ts +2 -2
  280. package/dist/server/operatorResults.js +40 -29
  281. package/dist/server/requestAuthContext.d.ts +12 -0
  282. package/dist/server/requestAuthContext.js +21 -0
  283. package/dist/server/shutdown.d.ts +69 -0
  284. package/dist/server/shutdown.js +219 -0
  285. package/dist/types/pipeline.d.ts +7 -0
  286. package/dist/types/stage.d.ts +8 -0
  287. package/dist/types/task.d.ts +8 -0
  288. package/dist/ui/assets/index-B6hlSXXn.js +120 -0
  289. package/dist/ui/index.html +1 -1
  290. package/examples/README.md +98 -0
  291. package/examples/archify-on-pr/README.md +213 -0
  292. package/examples/archify-on-pr/archify-on-pr.pipeline.yaml +19 -0
  293. package/examples/archify-on-pr/archify-on-pr.task.yaml +19 -0
  294. package/examples/archify-on-pr/author-diagrams.yaml +106 -0
  295. package/examples/archify-on-pr/detect-changes.yaml +89 -0
  296. package/examples/ci-validate/README.md +35 -0
  297. package/examples/ci-validate/check.yaml +13 -0
  298. package/examples/ci-validate/ci-demo.pipeline.yaml +4 -0
  299. package/examples/ci-validate/validate.sh +5 -0
  300. package/examples/compliance-review-demo/README.md +125 -0
  301. package/examples/compliance-review-demo/compliance-review-demo.pipeline.yaml +29 -0
  302. package/examples/compliance-review-demo/connect-account.yaml +47 -0
  303. package/examples/compliance-review-demo/map-to-control.yaml +123 -0
  304. package/examples/compliance-review-demo/poll-bucket-config.yaml +103 -0
  305. package/examples/compliance-review-demo/publish-report.yaml +79 -0
  306. package/examples/compliance-review-demo/review-verdict.yaml +122 -0
  307. package/examples/compliance-review-demo/run-compliance-check.yaml +96 -0
  308. package/examples/compliance-review-demo/store-evidence.yaml +143 -0
  309. package/examples/conditional-fork/README.md +89 -0
  310. package/examples/conditional-fork/branch-a.yaml +13 -0
  311. package/examples/conditional-fork/branch-b.yaml +13 -0
  312. package/examples/conditional-fork/decide.yaml +35 -0
  313. package/examples/conditional-fork/fork-demo.pipeline.yaml +20 -0
  314. package/examples/conditional-fork/fork-demo.task.yaml +8 -0
  315. package/examples/context7-mcp/README.md +42 -0
  316. package/examples/context7-mcp/context7-mcp.pipeline.yaml +15 -0
  317. package/examples/context7-mcp/context7-mcp.task.yaml +4 -0
  318. package/examples/context7-mcp/fetch-docs.yaml +52 -0
  319. package/examples/context7-mcp/resolve-library.yaml +31 -0
  320. package/examples/context7-mcp/write-brief.yaml +52 -0
  321. package/examples/feature-loop/README.md +104 -0
  322. package/examples/feature-loop/address-feedback.yaml +148 -0
  323. package/examples/feature-loop/align.yaml +116 -0
  324. package/examples/feature-loop/decompose.yaml +86 -0
  325. package/examples/feature-loop/default-model-hierarchy.task.yaml +58 -0
  326. package/examples/feature-loop/feature-loop.pipeline.yaml +140 -0
  327. package/examples/feature-loop/feature-loop.task.yaml +25 -0
  328. package/examples/feature-loop/implement.yaml +128 -0
  329. package/examples/feature-loop/plan.yaml +111 -0
  330. package/examples/feature-loop/publish.yaml +167 -0
  331. package/examples/feature-loop/review.yaml +146 -0
  332. package/examples/feature-loop/verify.yaml +106 -0
  333. package/examples/feedback-loop/README.md +72 -0
  334. package/examples/feedback-loop/VERIFY.md +208 -0
  335. package/examples/feedback-loop/continue-only.task.yaml +4 -0
  336. package/examples/feedback-loop/exhaust-require-continue.task.yaml +6 -0
  337. package/examples/feedback-loop/exhaust-wait-human.task.yaml +11 -0
  338. package/examples/feedback-loop/feedback-loop-wait-human.pipeline.yaml +23 -0
  339. package/examples/feedback-loop/feedback-loop.pipeline.yaml +23 -0
  340. package/examples/feedback-loop/feedback-loop.task.yaml +4 -0
  341. package/examples/feedback-loop/implement.yaml +14 -0
  342. package/examples/feedback-loop/plan.yaml +14 -0
  343. package/examples/feedback-loop/review.yaml +15 -0
  344. package/examples/feedback-loop/send-back-once.task.yaml +6 -0
  345. package/examples/feedback-loop/submit.yaml +12 -0
  346. package/examples/generic-fan-in/README.md +151 -0
  347. package/examples/generic-fan-in/clarify.yaml +18 -0
  348. package/examples/generic-fan-in/generic-fan-in-accepted.pipeline.yaml +19 -0
  349. package/examples/generic-fan-in/generic-fan-in-accepted.task.yaml +8 -0
  350. package/examples/generic-fan-in/generic-fan-in.pipeline.yaml +18 -0
  351. package/examples/generic-fan-in/generic-fan-in.task.yaml +9 -0
  352. package/examples/generic-fan-in/research.yaml +45 -0
  353. package/examples/generic-fan-in/synthesize.yaml +66 -0
  354. package/examples/generic-fan-in/validation.yaml +45 -0
  355. package/examples/github-release/README.md +78 -0
  356. package/examples/github-release/draft-release-notes.yaml +64 -0
  357. package/examples/github-release/github-release.pipeline.yaml +11 -0
  358. package/examples/github-release/github-release.task.yaml +13 -0
  359. package/examples/github-release/publish-github-release.yaml +64 -0
  360. package/examples/hello-world/README.md +28 -0
  361. package/examples/hello-world/hello.pipeline.yaml +4 -0
  362. package/examples/hello-world/my-task.task.yaml +6 -0
  363. package/examples/hello-world/research.yaml +18 -0
  364. package/examples/mcp-hitl-tour/README.md +113 -0
  365. package/examples/mcp-hitl-tour/draft-angle.yaml +33 -0
  366. package/examples/mcp-hitl-tour/draft-short.yaml +32 -0
  367. package/examples/mcp-hitl-tour/emit-angles.yaml +45 -0
  368. package/examples/mcp-hitl-tour/gather.yaml +31 -0
  369. package/examples/mcp-hitl-tour/intake.yaml +62 -0
  370. package/examples/mcp-hitl-tour/mcp-hitl-tour-exhaust.task.yaml +13 -0
  371. package/examples/mcp-hitl-tour/mcp-hitl-tour.pipeline.yaml +54 -0
  372. package/examples/mcp-hitl-tour/mcp-hitl-tour.task.yaml +16 -0
  373. package/examples/mcp-hitl-tour/polish.yaml +33 -0
  374. package/examples/mcp-hitl-tour/review.yaml +17 -0
  375. package/examples/mcp-hitl-tour/ship.yaml +25 -0
  376. package/examples/oss-issue-contribution/README.md +218 -0
  377. package/examples/oss-issue-contribution/oss-address-review-feedback.yaml +117 -0
  378. package/examples/oss-issue-contribution/oss-approve-contribution.yaml +181 -0
  379. package/examples/oss-issue-contribution/oss-approve-plan.yaml +111 -0
  380. package/examples/oss-issue-contribution/oss-explain-fix.yaml +108 -0
  381. package/examples/oss-issue-contribution/oss-explain-issue.yaml +114 -0
  382. package/examples/oss-issue-contribution/oss-implement-source-fix.yaml +171 -0
  383. package/examples/oss-issue-contribution/oss-investigate-area.yaml +116 -0
  384. package/examples/oss-issue-contribution/oss-issue-contribution.pipeline.yaml +125 -0
  385. package/examples/oss-issue-contribution/oss-issue-intake.yaml +106 -0
  386. package/examples/oss-issue-contribution/oss-plan-investigation.yaml +112 -0
  387. package/examples/oss-issue-contribution/oss-plan-review.yaml +109 -0
  388. package/examples/oss-issue-contribution/oss-reproduce-issue.yaml +140 -0
  389. package/examples/oss-issue-contribution/oss-review-change.yaml +107 -0
  390. package/examples/oss-issue-contribution/oss-verify-fix.yaml +130 -0
  391. package/examples/oss-issue-contribution/oss-write-regression-test.yaml +135 -0
  392. package/examples/oss-issue-contribution/pi-7629.task.yaml +79 -0
  393. package/examples/oss-issue-contribution/pi-8919.task.yaml +75 -0
  394. package/examples/oss-issue-contribution/pydantic-ai-7971.task.yaml +53 -0
  395. package/examples/oss-quick-fix/README.md +65 -0
  396. package/examples/oss-quick-fix/implement-fix.yaml +84 -0
  397. package/examples/oss-quick-fix/oss-quick-fix.pipeline.yaml +35 -0
  398. package/examples/oss-quick-fix/oss-quick-fix.task.yaml +19 -0
  399. package/examples/oss-quick-fix/ship-fix.yaml +81 -0
  400. package/examples/oss-quick-fix/understand-issue.yaml +67 -0
  401. package/examples/oss-quick-fix/verify-fix.yaml +73 -0
  402. package/examples/plan-review/README.md +37 -0
  403. package/examples/plan-review/plan-review-followup.yaml +13 -0
  404. package/examples/plan-review/plan-review.pipeline.yaml +9 -0
  405. package/examples/plan-review/plan-review.task.yaml +9 -0
  406. package/examples/plan-review/plan-review.yaml +25 -0
  407. package/examples/playwright-mcp/README.md +39 -0
  408. package/examples/playwright-mcp/playwright-mcp.pipeline.yaml +5 -0
  409. package/examples/playwright-mcp/playwright-mcp.task.yaml +4 -0
  410. package/examples/playwright-mcp/screenshot.yaml +40 -0
  411. package/examples/retry-tour/README.md +75 -0
  412. package/examples/retry-tour/retry-tour.pipeline.yaml +122 -0
  413. package/examples/retry-tour/retry-tour.task.yaml +9 -0
  414. package/examples/route-if-tour/README.md +202 -0
  415. package/examples/route-if-tour/route-if-tour.pipeline.yaml +950 -0
  416. package/examples/route-if-tour/route-if-tour.task.yaml +4 -0
  417. package/examples/route-wiring-smoke-test/01-core-routing.pipeline.yaml +168 -0
  418. package/examples/route-wiring-smoke-test/02-on-gating.pipeline.yaml +44 -0
  419. package/examples/route-wiring-smoke-test/03-two-entry-points.pipeline.yaml +28 -0
  420. package/examples/route-wiring-smoke-test/04-entry-false-equivalent-to-omitted.pipeline.yaml +27 -0
  421. package/examples/route-wiring-smoke-test/05-fork-choice.pipeline.yaml +110 -0
  422. package/examples/route-wiring-smoke-test/06-fan-out-fan-in.pipeline.yaml +82 -0
  423. package/examples/route-wiring-smoke-test/07-loop-human-decision.pipeline.yaml +82 -0
  424. package/examples/route-wiring-smoke-test/08-skip-fallback-and-multi-on-gate.pipeline.yaml +77 -0
  425. package/examples/route-wiring-smoke-test/09-multi-loop-targets.pipeline.yaml +102 -0
  426. package/examples/route-wiring-smoke-test/10-uses-dialect-fork.pipeline.yaml +23 -0
  427. package/examples/route-wiring-smoke-test/11-sequential-io-handoff.pipeline.yaml +37 -0
  428. package/examples/route-wiring-smoke-test/12-complex-io-schemas.pipeline.yaml +138 -0
  429. package/examples/route-wiring-smoke-test/13-ref-io-handoff.pipeline.yaml +84 -0
  430. package/examples/route-wiring-smoke-test/14-if-eq-gating.pipeline.yaml +50 -0
  431. package/examples/route-wiring-smoke-test/15-if-all-gated.pipeline.yaml +37 -0
  432. package/examples/route-wiring-smoke-test/16-if-composition.pipeline.yaml +65 -0
  433. package/examples/route-wiring-smoke-test/17-if-join.pipeline.yaml +80 -0
  434. package/examples/route-wiring-smoke-test/README.md +377 -0
  435. package/examples/route-wiring-smoke-test/rejected/01-reject-legacy-needs.pipeline.yaml +26 -0
  436. package/examples/route-wiring-smoke-test/rejected/02-reject-legacy-fork.pipeline.yaml +25 -0
  437. package/examples/route-wiring-smoke-test/rejected/03-reject-legacy-feedback-loop.pipeline.yaml +30 -0
  438. package/examples/route-wiring-smoke-test/rejected/04-reject-missing-entry.pipeline.yaml +24 -0
  439. package/examples/route-wiring-smoke-test/rejected/05-reject-unreachable.pipeline.yaml +35 -0
  440. package/examples/route-wiring-smoke-test/rejected/06-reject-allow-none-without-route-select.pipeline.yaml +37 -0
  441. package/examples/route-wiring-smoke-test/rejected/07-reject-cycle.pipeline.yaml +27 -0
  442. package/examples/route-wiring-smoke-test/rejected/08-reject-loop-non-ancestor.pipeline.yaml +42 -0
  443. package/examples/route-wiring-smoke-test/rejected/09-reject-route-select-leaf.pipeline.yaml +26 -0
  444. package/examples/route-wiring-smoke-test/rejected/12-reject-loop-replay-unsafe.pipeline.yaml +55 -0
  445. package/examples/route-wiring-smoke-test/rejected/13-reject-multiple-loop-entries.pipeline.yaml +51 -0
  446. package/examples/route-wiring-smoke-test/rejected/14-reject-duplicate-stage-id.pipeline.yaml +35 -0
  447. package/examples/route-wiring-smoke-test/rejected/15-reject-unknown-route-target.pipeline.yaml +15 -0
  448. package/examples/route-wiring-smoke-test/rejected/16-reject-malformed-route-item.pipeline.yaml +15 -0
  449. package/examples/route-wiring-smoke-test/rejected/17-reject-io-incompatible.pipeline.yaml +33 -0
  450. package/examples/route-wiring-smoke-test/rejected/18-reject-nested-io.pipeline.yaml +51 -0
  451. package/examples/route-wiring-smoke-test/rejected/19-reject-array-item-io.pipeline.yaml +55 -0
  452. package/examples/route-wiring-smoke-test/rejected/20-reject-closed-io.pipeline.yaml +36 -0
  453. package/examples/route-wiring-smoke-test/rejected/21-reject-ref-io.pipeline.yaml +42 -0
  454. package/examples/route-wiring-smoke-test/rejected/22-reject-if-unknown-field.pipeline.yaml +33 -0
  455. package/examples/route-wiring-smoke-test/rejected/23-reject-if-optional-field.pipeline.yaml +32 -0
  456. package/examples/route-wiring-smoke-test/rejected/24-reject-if-empty-all.pipeline.yaml +31 -0
  457. package/examples/route-wiring-smoke-test/rejected/25-reject-if-gt-on-string.pipeline.yaml +33 -0
  458. package/examples/route-wiring-smoke-test/rejected/26-reject-if-optional-nested.pipeline.yaml +36 -0
  459. package/examples/route-wiring-smoke-test/rejected/27-reject-if-on-loop.pipeline.yaml +63 -0
  460. package/examples/route-wiring-smoke-test/rejected/29-reject-if-on-failed.pipeline.yaml +46 -0
  461. package/examples/route-wiring-smoke-test/smoke-test.task.yaml +4 -0
  462. package/examples/route-wiring-smoke-test/stages/branch-a.yaml +12 -0
  463. package/examples/route-wiring-smoke-test/stages/branch-b.yaml +12 -0
  464. package/examples/route-wiring-smoke-test/stages/decide.yaml +12 -0
  465. package/examples/route-wiring-smoke-test/stages/finish.yaml +12 -0
  466. package/examples/ship-feature/README.md +190 -0
  467. package/examples/ship-feature/feat-address-feedback.yaml +119 -0
  468. package/examples/ship-feature/feat-implement.yaml +180 -0
  469. package/examples/ship-feature/feat-plan.yaml +120 -0
  470. package/examples/ship-feature/feat-review.yaml +109 -0
  471. package/examples/ship-feature/feat-ship.yaml +203 -0
  472. package/examples/ship-feature/ship-feature.pipeline.yaml +44 -0
  473. package/examples/ship-feature/ship-feature.task.yaml +78 -0
  474. package/examples/stage-mcp/README.md +144 -0
  475. package/examples/stage-mcp/echo-mcp.mjs +20 -0
  476. package/examples/stage-mcp/stage-mcp-dead.pipeline.yaml +5 -0
  477. package/examples/stage-mcp/stage-mcp-dead.task.yaml +3 -0
  478. package/examples/stage-mcp/stage-mcp-missing-var.pipeline.yaml +5 -0
  479. package/examples/stage-mcp/stage-mcp-missing-var.task.yaml +3 -0
  480. package/examples/stage-mcp/stage-mcp.pipeline.yaml +5 -0
  481. package/examples/stage-mcp/stage-mcp.task.yaml +4 -0
  482. package/examples/stage-mcp/use-echo.yaml +20 -0
  483. package/examples/verify-tour/README.md +80 -0
  484. package/examples/verify-tour/verify-tour.pipeline.yaml +162 -0
  485. package/examples/verify-tour/verify-tour.task.yaml +11 -0
  486. package/package.json +5 -1
  487. package/skills/stageflow/SKILL.md +6 -6
  488. package/skills/stageflow/references/control-surface.md +11 -5
  489. package/skills/stageflow/scripts/detect-host.mjs +14 -1
  490. package/skills/stageflow/scripts/detect-host.test.mjs +29 -1
  491. package/skills/stageflow-author/SKILL.md +4 -3
  492. package/skills/stageflow-author/references/catalog-mapping.md +16 -0
  493. package/skills/stageflow-author/references/catalog-write-conventions.md +1 -1
  494. package/skills/stageflow-run/SKILL.md +20 -12
  495. package/skills/stageflow-run/references/mcp-call.md +9 -1
  496. package/skills/stageflow-run/references/task-and-pipeline-selection.md +16 -5
  497. package/skills/stageflow-run/scripts/mcp-call.mjs +46 -6
  498. package/skills/stageflow-run/scripts/mcp-call.test.mjs +64 -0
  499. package/skills/stageflow-session-capture/references/catalog-authoring.md +2 -0
  500. package/skills/stageflow-setup/SKILL.md +40 -8
  501. package/dist/ui/assets/index-Da5EHU6M.js +0 -119
@@ -0,0 +1,55 @@
1
+ export declare class HostConfigError extends Error {
2
+ readonly code: "config_unknown_key" | "config_invalid";
3
+ readonly key?: string;
4
+ constructor(message: string, code: "config_unknown_key" | "config_invalid", key?: string);
5
+ }
6
+ /** Env keys that are known STAGEFLOW_* (or dynamic families) so unknown-key check accepts them. */
7
+ export declare const KNOWN_STAGEFLOW_ENV_KEYS: Set<string>;
8
+ /** Per-caller concurrent-active quota from host `callers:` config. */
9
+ export type CallerQuotaConfig = {
10
+ maxConcurrent: number;
11
+ };
12
+ export type HostConfig = {
13
+ maxConcurrentRuns: number;
14
+ maxConcurrentRunsPerProject: number | undefined;
15
+ maxQueued: number;
16
+ requireProviders: string[];
17
+ trustWorkspaceConfig: string[];
18
+ allowUnknownConfig: boolean;
19
+ controlToken: string | undefined;
20
+ readToken: string | undefined;
21
+ /** caller_id → quota; empty when unset. */
22
+ callers: Record<string, CallerQuotaConfig>;
23
+ /** Absolute path of config.yaml when loaded; undefined if absent. */
24
+ configFilePath: string | undefined;
25
+ warnings: string[];
26
+ /** True when maxConcurrentRuns came from override, env, or file (not bare default). */
27
+ maxConcurrentRunsExplicit: boolean;
28
+ };
29
+ export type HostConfigOverrides = {
30
+ maxConcurrentRuns?: number;
31
+ };
32
+ export type HostConfigSecretsEcho = {
33
+ controlToken: "set" | "unset";
34
+ readToken: "set" | "unset";
35
+ };
36
+ export type HostConfigPublicEcho = {
37
+ maxConcurrentRuns: number;
38
+ maxConcurrentRunsPerProject: number | undefined;
39
+ maxQueued: number;
40
+ requireProviders: string[];
41
+ trustWorkspaceConfig: string[];
42
+ allowUnknownConfig: boolean;
43
+ callers: Record<string, CallerQuotaConfig>;
44
+ secrets: HostConfigSecretsEcho;
45
+ };
46
+ /**
47
+ * Assemble HostConfig once at boot. Precedence: overrides (CLI flags) > env > file > default.
48
+ */
49
+ export declare function loadHostConfig(options?: {
50
+ env?: NodeJS.ProcessEnv;
51
+ homeDir?: string;
52
+ overrides?: HostConfigOverrides;
53
+ configFilePath?: string | null;
54
+ }): HostConfig;
55
+ export declare function redactHostConfig(config: HostConfig): HostConfigPublicEcho;
@@ -0,0 +1,333 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { parse as parseYaml } from "yaml";
4
+ import { globalStageflowHome } from "../project/globalHome.js";
5
+ import { readSecretFromEnvOrFile } from "./secretFromEnvOrFile.js";
6
+ export class HostConfigError extends Error {
7
+ code;
8
+ key;
9
+ constructor(message, code, key) {
10
+ super(message);
11
+ this.name = "HostConfigError";
12
+ this.code = code;
13
+ this.key = key;
14
+ }
15
+ }
16
+ /** Env keys that are known STAGEFLOW_* (or dynamic families) so unknown-key check accepts them. */
17
+ export const KNOWN_STAGEFLOW_ENV_KEYS = new Set([
18
+ "STAGEFLOW_HOME",
19
+ "STAGEFLOW_BIND",
20
+ "STAGEFLOW_ALLOWED_HOSTS",
21
+ "STAGEFLOW_CONTROL_TOKEN",
22
+ "STAGEFLOW_CONTROL_TOKEN_FILE",
23
+ "STAGEFLOW_READ_TOKEN",
24
+ "STAGEFLOW_READ_TOKEN_FILE",
25
+ "STAGEFLOW_ALLOW_UNKNOWN_CONFIG",
26
+ "STAGEFLOW_MAX_CONCURRENT_RUNS",
27
+ "STAGEFLOW_MAX_CONCURRENT_RUNS_PER_PROJECT",
28
+ "STAGEFLOW_MAX_QUEUED",
29
+ "STAGEFLOW_REQUIRE_PROVIDERS",
30
+ "STAGEFLOW_A2A_CONFIG",
31
+ "STAGEFLOW_ACTIVITY_TEXT_LIMIT",
32
+ "STAGEFLOW_ACTIVITY_VERBOSE",
33
+ "STAGEFLOW_CURSOR_EXTENSION",
34
+ "STAGEFLOW_NO_OPEN",
35
+ "STAGEFLOW_OPERATOR_CWD",
36
+ "STAGEFLOW_OPERATOR_AGENT_DIR",
37
+ "STAGEFLOW_LEGACY_YAML",
38
+ "STAGEFLOW_LOG_FORMAT",
39
+ "STAGEFLOW_LOG_LEVEL",
40
+ "STAGEFLOW_LOG_MAX_LINE_BYTES",
41
+ "STAGEFLOW_MCP_STATELESS",
42
+ "STAGEFLOW_DISK_WARN_BYTES",
43
+ "STAGEFLOW_MIN_FREE_DISK_BYTES",
44
+ "STAGEFLOW_BARE_CACHE_TTL_MS",
45
+ "STAGEFLOW_SLIM_ARTIFACT_MAX_BYTES",
46
+ "STAGEFLOW_SQLITE_BUSY_TIMEOUT_MS",
47
+ "STAGEFLOW_RUN_BRANCH_TEMPLATE",
48
+ "STAGEFLOW_AUTO_RESUME_INTERRUPTED",
49
+ "STAGEFLOW_MAX_AUTO_RESUMES",
50
+ "STAGEFLOW_RETRY_ROOT_WAIT_TIMEOUT_MS",
51
+ "STAGEFLOW_MAX_ACTIVE_STAGES_PER_RUN",
52
+ "STAGEFLOW_MAX_ACTIVE_STAGE_PROCESSES",
53
+ "STAGEFLOW_STAGE_EXECUTION",
54
+ "STAGEFLOW_STAGE_ENV_ALLOW",
55
+ "STAGEFLOW_STAGE_ENV_PASSTHROUGH",
56
+ "STAGEFLOW_GC_INTERVAL_MS",
57
+ "STAGEFLOW_MAX_CONNECTIONS",
58
+ "STAGEFLOW_REQUEST_TIMEOUT_MS",
59
+ "STAGEFLOW_AUTOSTART_TIMEOUT_MS",
60
+ "STAGEFLOW_NO_AUTOSTART",
61
+ "STAGEFLOW_SERVICE_PORT",
62
+ "STAGEFLOW_SHUTDOWN_GRACE_MS",
63
+ "STAGEFLOW_TOOLCHAIN_MANIFEST",
64
+ "STAGEFLOW_SECRET_REGISTRY",
65
+ "STAGEFLOW_BUILD_SHA",
66
+ ]);
67
+ const KNOWN_ENV_PREFIXES = [
68
+ "STAGEFLOW_PROVIDER_",
69
+ "STAGEFLOW_SLIM_",
70
+ "STAGEFLOW_PURGE_",
71
+ "STAGEFLOW_CONTROL_TOKEN_",
72
+ ];
73
+ const FILE_KEY_TO_FIELD = {
74
+ max_concurrent_runs: "maxConcurrentRuns",
75
+ max_concurrent_runs_per_project: "maxConcurrentRunsPerProject",
76
+ max_queued: "maxQueued",
77
+ require_providers: "requireProviders",
78
+ trust_workspace_config: "trustWorkspaceConfig",
79
+ allow_unknown_config: "allowUnknownConfig",
80
+ callers: "callers",
81
+ };
82
+ const DEFAULT_MAX_CONCURRENT = 3;
83
+ const DEFAULT_MAX_QUEUED = 32;
84
+ function isKnownStageflowEnvKey(key) {
85
+ if (KNOWN_STAGEFLOW_ENV_KEYS.has(key))
86
+ return true;
87
+ return KNOWN_ENV_PREFIXES.some((prefix) => key.startsWith(prefix));
88
+ }
89
+ function parsePositiveInt(raw, key, opts) {
90
+ const n = Number.parseInt(raw, 10);
91
+ const min = opts?.allowZero ? 0 : 1;
92
+ if (!Number.isFinite(n) || n < min || String(n) !== raw.trim()) {
93
+ throw new HostConfigError(`Invalid value for ${key}: ${JSON.stringify(raw)}`, "config_invalid", key);
94
+ }
95
+ return n;
96
+ }
97
+ function parseStringList(raw) {
98
+ return raw
99
+ .split(",")
100
+ .map((s) => s.trim())
101
+ .filter((s) => s.length > 0);
102
+ }
103
+ function parseBool(raw, key) {
104
+ const normalized = raw.trim().toLowerCase();
105
+ if (normalized === "1" || normalized === "true")
106
+ return true;
107
+ if (normalized === "0" || normalized === "false" || normalized === "") {
108
+ return false;
109
+ }
110
+ throw new HostConfigError(`Invalid boolean for ${key}: ${JSON.stringify(raw)}`, "config_invalid", key);
111
+ }
112
+ function assertUnknownEnvKeys(env, allowUnknown, warnings) {
113
+ for (const key of Object.keys(env)) {
114
+ if (!key.startsWith("STAGEFLOW_"))
115
+ continue;
116
+ if (isKnownStageflowEnvKey(key))
117
+ continue;
118
+ const message = `Unknown environment variable ${key}`;
119
+ if (allowUnknown) {
120
+ warnings.push(message);
121
+ continue;
122
+ }
123
+ throw new HostConfigError(message, "config_unknown_key", key);
124
+ }
125
+ }
126
+ function loadFileLayers(filePath) {
127
+ const warnings = [];
128
+ if (filePath === undefined || !existsSync(filePath)) {
129
+ return { values: {}, warnings };
130
+ }
131
+ let raw;
132
+ try {
133
+ raw = parseYaml(readFileSync(filePath, "utf8"));
134
+ }
135
+ catch (err) {
136
+ const detail = err instanceof Error ? err.message : String(err);
137
+ throw new HostConfigError(`Invalid config file ${filePath}: ${detail}`, "config_invalid");
138
+ }
139
+ if (raw === null || raw === undefined) {
140
+ return { values: {}, warnings };
141
+ }
142
+ if (typeof raw !== "object" || Array.isArray(raw)) {
143
+ throw new HostConfigError(`config.yaml must be a mapping`, "config_invalid");
144
+ }
145
+ const values = {};
146
+ for (const [key, value] of Object.entries(raw)) {
147
+ const field = FILE_KEY_TO_FIELD[key];
148
+ if (field === undefined) {
149
+ throw new HostConfigError(`Unknown config key "${key}" in ${filePath}`, "config_unknown_key", key);
150
+ }
151
+ values[field] = value;
152
+ }
153
+ return { values, warnings };
154
+ }
155
+ function coerceFileNumber(value, key) {
156
+ if (typeof value === "number" && Number.isFinite(value)) {
157
+ return value;
158
+ }
159
+ if (typeof value === "string") {
160
+ return parsePositiveInt(value, key, {
161
+ allowZero: key.includes("queued") || key.includes("per_project"),
162
+ });
163
+ }
164
+ throw new HostConfigError(`Invalid value for ${key}`, "config_invalid", key);
165
+ }
166
+ function coerceFileStringList(value, key) {
167
+ if (Array.isArray(value) && value.every((v) => typeof v === "string")) {
168
+ return value.map((v) => v.trim()).filter((v) => v.length > 0);
169
+ }
170
+ if (typeof value === "string")
171
+ return parseStringList(value);
172
+ throw new HostConfigError(`Invalid value for ${key}; expected string list`, "config_invalid", key);
173
+ }
174
+ function coerceFileBool(value, key) {
175
+ if (typeof value === "boolean")
176
+ return value;
177
+ if (typeof value === "string")
178
+ return parseBool(value, key);
179
+ throw new HostConfigError(`Invalid boolean for ${key}`, "config_invalid", key);
180
+ }
181
+ function coerceCallers(value, key) {
182
+ if (value === null || value === undefined)
183
+ return {};
184
+ if (typeof value !== "object" || Array.isArray(value)) {
185
+ throw new HostConfigError(`Invalid value for ${key}; expected mapping of caller_id → { max_concurrent }`, "config_invalid", key);
186
+ }
187
+ const out = {};
188
+ for (const [rawId, rawEntry] of Object.entries(value)) {
189
+ const callerId = rawId.trim().toLowerCase();
190
+ if (callerId.length === 0) {
191
+ throw new HostConfigError(`Invalid empty caller id in ${key}`, "config_invalid", key);
192
+ }
193
+ if (rawEntry === null ||
194
+ typeof rawEntry !== "object" ||
195
+ Array.isArray(rawEntry)) {
196
+ throw new HostConfigError(`Invalid value for ${key}.${rawId}; expected { max_concurrent }`, "config_invalid", key);
197
+ }
198
+ const entry = rawEntry;
199
+ if (entry.max_concurrent === undefined) {
200
+ throw new HostConfigError(`Missing max_concurrent for ${key}.${rawId}`, "config_invalid", key);
201
+ }
202
+ const n = coerceFileNumber(entry.max_concurrent, `${key}.${rawId}.max_concurrent`);
203
+ if (!Number.isInteger(n) || n < 1) {
204
+ throw new HostConfigError(`Invalid max_concurrent for ${key}.${rawId}`, "config_invalid", key);
205
+ }
206
+ for (const sub of Object.keys(entry)) {
207
+ if (sub !== "max_concurrent") {
208
+ throw new HostConfigError(`Unknown key "${sub}" under ${key}.${rawId}`, "config_unknown_key", sub);
209
+ }
210
+ }
211
+ out[callerId] = { maxConcurrent: n };
212
+ }
213
+ return out;
214
+ }
215
+ /**
216
+ * Assemble HostConfig once at boot. Precedence: overrides (CLI flags) > env > file > default.
217
+ */
218
+ export function loadHostConfig(options) {
219
+ const env = options?.env ?? process.env;
220
+ const warnings = [];
221
+ const homeDir = options?.homeDir ?? globalStageflowHome();
222
+ const configFilePath = options?.configFilePath === null
223
+ ? undefined
224
+ : (options?.configFilePath ?? path.join(homeDir, "config.yaml"));
225
+ const allowUnknownFromEnv = env.STAGEFLOW_ALLOW_UNKNOWN_CONFIG;
226
+ const allowUnknownConfig = allowUnknownFromEnv !== undefined && allowUnknownFromEnv.length > 0
227
+ ? parseBool(allowUnknownFromEnv, "STAGEFLOW_ALLOW_UNKNOWN_CONFIG")
228
+ : false;
229
+ if (allowUnknownConfig) {
230
+ warnings.push("STAGEFLOW_ALLOW_UNKNOWN_CONFIG=1: unknown STAGEFLOW_* keys will warn instead of failing boot");
231
+ }
232
+ assertUnknownEnvKeys(env, allowUnknownConfig, warnings);
233
+ const file = loadFileLayers(configFilePath !== undefined && existsSync(configFilePath)
234
+ ? configFilePath
235
+ : undefined);
236
+ warnings.push(...file.warnings);
237
+ let maxConcurrentRuns = DEFAULT_MAX_CONCURRENT;
238
+ let maxConcurrentRunsExplicit = false;
239
+ if (file.values.maxConcurrentRuns !== undefined) {
240
+ const n = coerceFileNumber(file.values.maxConcurrentRuns, "max_concurrent_runs");
241
+ if (!Number.isInteger(n) || n < 1) {
242
+ throw new HostConfigError(`Invalid value for max_concurrent_runs`, "config_invalid", "max_concurrent_runs");
243
+ }
244
+ maxConcurrentRuns = n;
245
+ maxConcurrentRunsExplicit = true;
246
+ }
247
+ if (env.STAGEFLOW_MAX_CONCURRENT_RUNS !== undefined) {
248
+ maxConcurrentRuns = parsePositiveInt(env.STAGEFLOW_MAX_CONCURRENT_RUNS, "STAGEFLOW_MAX_CONCURRENT_RUNS");
249
+ maxConcurrentRunsExplicit = true;
250
+ }
251
+ if (options?.overrides?.maxConcurrentRuns !== undefined) {
252
+ maxConcurrentRuns = options.overrides.maxConcurrentRuns;
253
+ maxConcurrentRunsExplicit = true;
254
+ }
255
+ let maxConcurrentRunsPerProject;
256
+ if (file.values.maxConcurrentRunsPerProject !== undefined) {
257
+ const n = coerceFileNumber(file.values.maxConcurrentRunsPerProject, "max_concurrent_runs_per_project");
258
+ if (!Number.isInteger(n) || n < 1) {
259
+ throw new HostConfigError(`Invalid value for max_concurrent_runs_per_project`, "config_invalid", "max_concurrent_runs_per_project");
260
+ }
261
+ maxConcurrentRunsPerProject = n;
262
+ }
263
+ if (env.STAGEFLOW_MAX_CONCURRENT_RUNS_PER_PROJECT !== undefined) {
264
+ maxConcurrentRunsPerProject = parsePositiveInt(env.STAGEFLOW_MAX_CONCURRENT_RUNS_PER_PROJECT, "STAGEFLOW_MAX_CONCURRENT_RUNS_PER_PROJECT");
265
+ }
266
+ let maxQueued = DEFAULT_MAX_QUEUED;
267
+ if (file.values.maxQueued !== undefined) {
268
+ const n = coerceFileNumber(file.values.maxQueued, "max_queued");
269
+ if (!Number.isInteger(n) || n < 0) {
270
+ throw new HostConfigError(`Invalid value for max_queued`, "config_invalid", "max_queued");
271
+ }
272
+ maxQueued = n;
273
+ }
274
+ if (env.STAGEFLOW_MAX_QUEUED !== undefined) {
275
+ maxQueued = parsePositiveInt(env.STAGEFLOW_MAX_QUEUED, "STAGEFLOW_MAX_QUEUED", {
276
+ allowZero: true,
277
+ });
278
+ }
279
+ let requireProviders = [];
280
+ if (file.values.requireProviders !== undefined) {
281
+ requireProviders = coerceFileStringList(file.values.requireProviders, "require_providers");
282
+ }
283
+ if (env.STAGEFLOW_REQUIRE_PROVIDERS !== undefined) {
284
+ requireProviders = parseStringList(env.STAGEFLOW_REQUIRE_PROVIDERS);
285
+ }
286
+ let trustWorkspaceConfig = [];
287
+ if (file.values.trustWorkspaceConfig !== undefined) {
288
+ trustWorkspaceConfig = coerceFileStringList(file.values.trustWorkspaceConfig, "trust_workspace_config");
289
+ }
290
+ let allowUnknownFromFile = allowUnknownConfig;
291
+ if (file.values.allowUnknownConfig !== undefined) {
292
+ allowUnknownFromFile = coerceFileBool(file.values.allowUnknownConfig, "allow_unknown_config");
293
+ }
294
+ // Env already applied above for the unknown-key scan; file cannot enable after the fact.
295
+ void allowUnknownFromFile;
296
+ const controlToken = readSecretFromEnvOrFile(env, "STAGEFLOW_CONTROL_TOKEN");
297
+ const readToken = readSecretFromEnvOrFile(env, "STAGEFLOW_READ_TOKEN");
298
+ let callers = {};
299
+ if (file.values.callers !== undefined) {
300
+ callers = coerceCallers(file.values.callers, "callers");
301
+ }
302
+ return {
303
+ maxConcurrentRuns,
304
+ maxConcurrentRunsPerProject,
305
+ maxQueued,
306
+ requireProviders,
307
+ trustWorkspaceConfig,
308
+ allowUnknownConfig,
309
+ controlToken,
310
+ readToken,
311
+ callers,
312
+ configFilePath: configFilePath !== undefined && existsSync(configFilePath)
313
+ ? configFilePath
314
+ : undefined,
315
+ warnings,
316
+ maxConcurrentRunsExplicit,
317
+ };
318
+ }
319
+ export function redactHostConfig(config) {
320
+ return {
321
+ maxConcurrentRuns: config.maxConcurrentRuns,
322
+ maxConcurrentRunsPerProject: config.maxConcurrentRunsPerProject,
323
+ maxQueued: config.maxQueued,
324
+ requireProviders: [...config.requireProviders],
325
+ trustWorkspaceConfig: [...config.trustWorkspaceConfig],
326
+ allowUnknownConfig: config.allowUnknownConfig,
327
+ callers: { ...config.callers },
328
+ secrets: {
329
+ controlToken: config.controlToken ? "set" : "unset",
330
+ readToken: config.readToken ? "set" : "unset",
331
+ },
332
+ };
333
+ }
@@ -1,4 +1,6 @@
1
1
  export type SkillScope = "user" | "project" | "temporary";
2
+ /** Skill resolution origin — separate from ConfigOriginKind. */
3
+ export type SkillOrigin = "run" | "checkout" | "host";
2
4
  export type SkillListing = {
3
5
  name: string;
4
6
  description: string;
@@ -7,6 +9,7 @@ export type SkillListing = {
7
9
  scope: SkillScope;
8
10
  source: string;
9
11
  disableModelInvocation: boolean;
12
+ origin?: SkillOrigin;
10
13
  };
11
14
  export type SkillDiagnostic = {
12
15
  message: string;
@@ -19,11 +22,34 @@ export type SkillCatalog = {
19
22
  export type ListSkillsOptions = {
20
23
  cwd: string;
21
24
  agentDir: string;
25
+ /** Materialized run skills root: `$STAGEFLOW_HOME/runs/<runId>/skills`. */
26
+ runSkillsDir?: string;
27
+ /** Checkout / worktree root whose `.pi/skills` is the checkout tier. */
28
+ checkoutRoot?: string;
22
29
  };
23
30
  export type ResolvedSkill = {
24
31
  name: string;
25
32
  filePath: string;
26
33
  baseDir: string;
34
+ scope: SkillScope;
35
+ source: string;
36
+ origin: SkillOrigin;
37
+ };
38
+ export type SkillOriginListing = {
39
+ name: string;
40
+ description: string;
41
+ origin: SkillOrigin;
27
42
  };
43
+ /**
44
+ * Resolve a skill by name. Search order: run skills → checkout `.pi/skills` → host agentDir.
45
+ */
28
46
  export declare function resolveSkillByName(name: string, options: ListSkillsOptions): Promise<ResolvedSkill | undefined>;
29
47
  export declare function listSkills(options: ListSkillsOptions): Promise<SkillCatalog>;
48
+ /**
49
+ * List skills with SkillOrigin. When runSkillsDir is set, run-tier skills win on name.
50
+ * Precedence for the merged view: run → checkout → host.
51
+ */
52
+ export declare function listSkillsWithOrigin(options: ListSkillsOptions): Promise<{
53
+ skills: SkillOriginListing[];
54
+ diagnostics: SkillDiagnostic[];
55
+ }>;
@@ -1,6 +1,7 @@
1
- import { access } from "node:fs/promises";
1
+ import { access, readFile, readdir } from "node:fs/promises";
2
+ import path from "node:path";
2
3
  import { DefaultResourceLoader, SettingsManager, } from "@earendil-works/pi-coding-agent";
3
- function toListing(skill) {
4
+ function toListing(skill, origin) {
4
5
  return {
5
6
  name: skill.name,
6
7
  description: skill.description,
@@ -9,8 +10,39 @@ function toListing(skill) {
9
10
  scope: skill.sourceInfo.scope,
10
11
  source: skill.sourceInfo.source,
11
12
  disableModelInvocation: skill.disableModelInvocation,
13
+ ...(origin !== undefined ? { origin } : {}),
12
14
  };
13
15
  }
16
+ function isUnderRoot(candidate, root) {
17
+ const resolvedRoot = path.resolve(root);
18
+ const resolved = path.resolve(candidate);
19
+ const rel = path.relative(resolvedRoot, resolved);
20
+ return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
21
+ }
22
+ async function pathExists(filePath) {
23
+ try {
24
+ await access(filePath);
25
+ return true;
26
+ }
27
+ catch {
28
+ return false;
29
+ }
30
+ }
31
+ function descriptionFromSkillMd(content) {
32
+ const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
33
+ if (!match)
34
+ return undefined;
35
+ const block = match[1];
36
+ const descLine = /^description:\s*(.*)$/m.exec(block);
37
+ if (!descLine)
38
+ return undefined;
39
+ let value = descLine[1].trim();
40
+ if ((value.startsWith('"') && value.endsWith('"')) ||
41
+ (value.startsWith("'") && value.endsWith("'"))) {
42
+ value = value.slice(1, -1);
43
+ }
44
+ return value.length > 0 ? value : undefined;
45
+ }
14
46
  async function openSkillCatalogLoader(options) {
15
47
  const settingsManager = SettingsManager.create(options.cwd, options.agentDir, {
16
48
  projectTrusted: true,
@@ -23,7 +55,67 @@ async function openSkillCatalogLoader(options) {
23
55
  await loader.reload();
24
56
  return loader;
25
57
  }
58
+ async function resolveRunSkill(name, runSkillsDir) {
59
+ if (name.includes("/") || name.includes("\\") || name.includes("\0")) {
60
+ return undefined;
61
+ }
62
+ const baseDir = path.join(runSkillsDir, name);
63
+ const filePath = path.join(baseDir, "SKILL.md");
64
+ if (!(await pathExists(filePath)))
65
+ return undefined;
66
+ return {
67
+ name,
68
+ filePath,
69
+ baseDir,
70
+ scope: "temporary",
71
+ source: "run",
72
+ origin: "run",
73
+ };
74
+ }
75
+ async function resolveCheckoutSkill(name, checkoutRoot) {
76
+ if (name.includes("/") || name.includes("\\") || name.includes("\0")) {
77
+ return undefined;
78
+ }
79
+ const baseDir = path.join(checkoutRoot, ".pi", "skills", name);
80
+ const filePath = path.join(baseDir, "SKILL.md");
81
+ if (!(await pathExists(filePath)))
82
+ return undefined;
83
+ return {
84
+ name,
85
+ filePath,
86
+ baseDir,
87
+ scope: "project",
88
+ source: "checkout",
89
+ origin: "checkout",
90
+ };
91
+ }
92
+ function originForLoaderSkill(skill, options) {
93
+ if (options.checkoutRoot !== undefined &&
94
+ isUnderRoot(skill.filePath, path.join(options.checkoutRoot, ".pi", "skills"))) {
95
+ return "checkout";
96
+ }
97
+ if (isUnderRoot(skill.filePath, path.join(options.agentDir, "skills"))) {
98
+ return "host";
99
+ }
100
+ if (skill.sourceInfo.scope === "project") {
101
+ return "checkout";
102
+ }
103
+ return "host";
104
+ }
105
+ /**
106
+ * Resolve a skill by name. Search order: run skills → checkout `.pi/skills` → host agentDir.
107
+ */
26
108
  export async function resolveSkillByName(name, options) {
109
+ if (options.runSkillsDir !== undefined) {
110
+ const run = await resolveRunSkill(name, options.runSkillsDir);
111
+ if (run)
112
+ return run;
113
+ }
114
+ if (options.checkoutRoot !== undefined) {
115
+ const checkout = await resolveCheckoutSkill(name, options.checkoutRoot);
116
+ if (checkout)
117
+ return checkout;
118
+ }
27
119
  const loader = await openSkillCatalogLoader(options);
28
120
  const match = loader.getSkills().skills.find((skill) => skill.name === name);
29
121
  if (!match)
@@ -34,10 +126,24 @@ export async function resolveSkillByName(name, options) {
34
126
  catch {
35
127
  return undefined;
36
128
  }
129
+ if (options.checkoutRoot !== undefined &&
130
+ isUnderRoot(match.filePath, path.join(options.checkoutRoot, ".pi", "skills"))) {
131
+ return {
132
+ name: match.name,
133
+ filePath: match.filePath,
134
+ baseDir: match.baseDir,
135
+ scope: match.sourceInfo.scope,
136
+ source: match.sourceInfo.source,
137
+ origin: "checkout",
138
+ };
139
+ }
37
140
  return {
38
141
  name: match.name,
39
142
  filePath: match.filePath,
40
143
  baseDir: match.baseDir,
144
+ scope: match.sourceInfo.scope,
145
+ source: match.sourceInfo.source,
146
+ origin: originForLoaderSkill(match, options),
41
147
  };
42
148
  }
43
149
  export async function listSkills(options) {
@@ -45,7 +151,7 @@ export async function listSkills(options) {
45
151
  const { skills, diagnostics } = loader.getSkills();
46
152
  return {
47
153
  skills: skills
48
- .map(toListing)
154
+ .map((skill) => toListing(skill, originForLoaderSkill(skill, options)))
49
155
  .sort((a, b) => a.name.localeCompare(b.name)),
50
156
  diagnostics: diagnostics.map((d) => ({
51
157
  message: d.message,
@@ -53,3 +159,84 @@ export async function listSkills(options) {
53
159
  })),
54
160
  };
55
161
  }
162
+ async function listRunSkillOrigins(runSkillsDir) {
163
+ let names;
164
+ try {
165
+ names = await readdir(runSkillsDir);
166
+ }
167
+ catch {
168
+ return [];
169
+ }
170
+ const out = [];
171
+ for (const name of names) {
172
+ if (name.includes("/") || name.includes("\\") || name.includes("\0")) {
173
+ continue;
174
+ }
175
+ const filePath = path.join(runSkillsDir, name, "SKILL.md");
176
+ try {
177
+ const content = await readFile(filePath, "utf8");
178
+ const description = descriptionFromSkillMd(content);
179
+ if (description === undefined)
180
+ continue;
181
+ out.push({ name, description, origin: "run" });
182
+ }
183
+ catch {
184
+ // skip unreadable / incomplete packs
185
+ }
186
+ }
187
+ return out;
188
+ }
189
+ /**
190
+ * List skills with SkillOrigin. When runSkillsDir is set, run-tier skills win on name.
191
+ * Precedence for the merged view: run → checkout → host.
192
+ */
193
+ export async function listSkillsWithOrigin(options) {
194
+ const byName = new Map();
195
+ const hostCatalog = await listSkills({
196
+ cwd: options.cwd,
197
+ agentDir: options.agentDir,
198
+ ...(options.checkoutRoot !== undefined
199
+ ? { checkoutRoot: options.checkoutRoot }
200
+ : {}),
201
+ });
202
+ for (const skill of hostCatalog.skills) {
203
+ const origin = skill.origin ??
204
+ (skill.scope === "project" ? "checkout" : "host");
205
+ byName.set(skill.name, {
206
+ name: skill.name,
207
+ description: skill.description,
208
+ origin,
209
+ });
210
+ }
211
+ if (options.checkoutRoot !== undefined) {
212
+ let checkoutNames = [];
213
+ try {
214
+ checkoutNames = await readdir(path.join(options.checkoutRoot, ".pi", "skills"));
215
+ }
216
+ catch {
217
+ checkoutNames = [];
218
+ }
219
+ for (const name of checkoutNames) {
220
+ const filePath = path.join(options.checkoutRoot, ".pi", "skills", name, "SKILL.md");
221
+ try {
222
+ const content = await readFile(filePath, "utf8");
223
+ const description = descriptionFromSkillMd(content);
224
+ if (description === undefined)
225
+ continue;
226
+ byName.set(name, { name, description, origin: "checkout" });
227
+ }
228
+ catch {
229
+ // skip
230
+ }
231
+ }
232
+ }
233
+ if (options.runSkillsDir !== undefined) {
234
+ for (const skill of await listRunSkillOrigins(options.runSkillsDir)) {
235
+ byName.set(skill.name, skill);
236
+ }
237
+ }
238
+ return {
239
+ skills: [...byName.values()].sort((a, b) => a.name.localeCompare(b.name)),
240
+ diagnostics: hostCatalog.diagnostics,
241
+ };
242
+ }
@@ -1,5 +1,5 @@
1
1
  import type { ValidationFindingCode } from "./validateCatalog.js";
2
- export type TaskLoadCode = "task.invalid_shape" | "task.load_error";
2
+ export type TaskLoadCode = "task.invalid_shape" | "task.load_error" | "task.binding_conflict" | "task.repository_ref_required" | "task.ref_without_repository" | "task.repository_invalid";
3
3
  export type LoadIssue = {
4
4
  code: ValidationFindingCode;
5
5
  message: string;