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,52 @@
1
+ import type { McpServer } from "@modelcontextprotocol/server";
2
+ import type { RunMeta, RunStore } from "../runstore/port.js";
3
+ import type { McpToolDeps } from "./deps.js";
4
+ export declare const CHECKOUT_CHANGES_CAP = 500;
5
+ export declare const DEFAULT_DIFF_MAX_BYTES = 262144;
6
+ export type CheckoutChangeStatus = "added" | "untracked" | "modified" | "deleted";
7
+ export type CheckoutChangeEntry = {
8
+ path: string;
9
+ status: CheckoutChangeStatus;
10
+ };
11
+ export type CheckoutVisibilityCode = "run_not_bound" | "checkout_reclaimed" | "not_a_git_repository";
12
+ export type CheckoutVisibilityError = {
13
+ ok: false;
14
+ code: CheckoutVisibilityCode;
15
+ error: string;
16
+ status: number;
17
+ };
18
+ export type BoundCheckout = {
19
+ ok: true;
20
+ runId: string;
21
+ meta: RunMeta;
22
+ checkoutRoot: string;
23
+ isGit: boolean;
24
+ };
25
+ export declare function resolveBoundCheckout(store: RunStore, runId: string): Promise<BoundCheckout | CheckoutVisibilityError>;
26
+ export declare function parseStatusPorcelainZ(output: string): CheckoutChangeEntry[];
27
+ export declare function listCheckoutChanges(store: RunStore, runId: string): Promise<{
28
+ ok: true;
29
+ changes: CheckoutChangeEntry[];
30
+ truncated: boolean;
31
+ } | CheckoutVisibilityError>;
32
+ export type GetRunDiffInput = {
33
+ mode?: "stat" | "patch";
34
+ path?: string;
35
+ maxBytes?: number;
36
+ };
37
+ export type GetRunDiffSuccess = {
38
+ ok: true;
39
+ mode: "stat" | "patch";
40
+ base_sha: string;
41
+ base_source: "resolved_sha" | "HEAD";
42
+ content: string;
43
+ truncated: boolean;
44
+ untracked: string[];
45
+ };
46
+ export declare function getRunDiff(store: RunStore, runId: string, input?: GetRunDiffInput): Promise<GetRunDiffSuccess | CheckoutVisibilityError>;
47
+ export declare function readCheckoutFileBytes(store: RunStore, runId: string, relativePath: string): Promise<{
48
+ ok: true;
49
+ bytes: Buffer;
50
+ path: string;
51
+ } | CheckoutVisibilityError>;
52
+ export declare function registerCheckoutTools(server: McpServer, deps: McpToolDeps): void;
@@ -0,0 +1,332 @@
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync } from "node:fs";
3
+ import { lstat, readdir, readFile, readlink } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { z } from "zod";
6
+ import { diff, lsFilesOthersZ, revParse, statusPorcelainZ, } from "../git/operations.js";
7
+ import { projectRunBindingDetail } from "../runstore/runProjection.js";
8
+ import { mapStoreLookupError } from "../server/operatorResults.js";
9
+ import { assertSafeRunId, CHECKOUT_PATH_MESSAGES, classifyArtifactContent, resolveContainedRelativePath, } from "./readArtifact.js";
10
+ import { imageResult, textResult } from "./toolResults.js";
11
+ export const CHECKOUT_CHANGES_CAP = 500;
12
+ export const DEFAULT_DIFF_MAX_BYTES = 262_144;
13
+ async function detectGitCheckout(root) {
14
+ try {
15
+ const inside = await revParse(root, ["--is-inside-work-tree"]);
16
+ return inside === "true";
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ }
22
+ export async function resolveBoundCheckout(store, runId) {
23
+ assertSafeRunId(runId);
24
+ let meta;
25
+ try {
26
+ meta = await store.readRunMeta(runId);
27
+ }
28
+ catch (err) {
29
+ const mapped = mapStoreLookupError(err, { policy: "run" });
30
+ throw new Error(mapped.error);
31
+ }
32
+ const binding = projectRunBindingDetail(meta);
33
+ if (binding.kind === "unbound") {
34
+ return {
35
+ ok: false,
36
+ code: "run_not_bound",
37
+ error: "run is not bound to a checkout",
38
+ status: 400,
39
+ };
40
+ }
41
+ const checkoutRoot = meta.checkout_root?.trim();
42
+ if (!checkoutRoot) {
43
+ return {
44
+ ok: false,
45
+ code: "checkout_reclaimed",
46
+ error: "checkout has been reclaimed",
47
+ status: 404,
48
+ };
49
+ }
50
+ if (!existsSync(checkoutRoot)) {
51
+ return {
52
+ ok: false,
53
+ code: "checkout_reclaimed",
54
+ error: "checkout has been reclaimed",
55
+ status: 404,
56
+ };
57
+ }
58
+ const isGit = await detectGitCheckout(checkoutRoot);
59
+ return { ok: true, runId, meta, checkoutRoot, isGit };
60
+ }
61
+ function nulSplit(output) {
62
+ return output.split("\0").filter((part) => part.length > 0);
63
+ }
64
+ export function parseStatusPorcelainZ(output) {
65
+ const parts = nulSplit(output);
66
+ const changes = [];
67
+ for (let i = 0; i < parts.length; i += 1) {
68
+ const entry = parts[i];
69
+ if (entry.length < 3)
70
+ continue;
71
+ const xy = entry.slice(0, 2);
72
+ const firstPath = entry.slice(3);
73
+ const isRenameOrCopy = xy[0] === "R" || xy[0] === "C";
74
+ let filePath = firstPath;
75
+ if (isRenameOrCopy) {
76
+ const next = parts[i + 1];
77
+ if (next !== undefined) {
78
+ filePath = next;
79
+ i += 1;
80
+ }
81
+ }
82
+ if (xy === "??") {
83
+ changes.push({ path: filePath, status: "untracked" });
84
+ continue;
85
+ }
86
+ if (xy === "!!")
87
+ continue;
88
+ if (xy.includes("D") && !isRenameOrCopy) {
89
+ changes.push({ path: filePath, status: "deleted" });
90
+ continue;
91
+ }
92
+ if (xy[0] === "A" || xy[1] === "A" || isRenameOrCopy) {
93
+ changes.push({ path: filePath, status: "added" });
94
+ continue;
95
+ }
96
+ changes.push({ path: filePath, status: "modified" });
97
+ }
98
+ return changes;
99
+ }
100
+ function capChanges(changes) {
101
+ if (changes.length <= CHECKOUT_CHANGES_CAP) {
102
+ return { changes, truncated: false };
103
+ }
104
+ return {
105
+ changes: changes.slice(0, CHECKOUT_CHANGES_CAP),
106
+ truncated: true,
107
+ };
108
+ }
109
+ async function fingerprintFile(root, relativePath) {
110
+ const target = path.join(root, relativePath);
111
+ try {
112
+ const stats = await lstat(target);
113
+ if (stats.isSymbolicLink()) {
114
+ const targetPath = await readlink(target);
115
+ return `link:${createHash("sha256").update(targetPath).digest("hex")}`;
116
+ }
117
+ if (!stats.isFile())
118
+ return `other:${stats.mode}:${stats.size}`;
119
+ const contents = await readFile(target);
120
+ return `file:${createHash("sha256").update(contents).digest("hex")}`;
121
+ }
122
+ catch (error) {
123
+ if (error.code === "ENOENT")
124
+ return "missing";
125
+ throw error;
126
+ }
127
+ }
128
+ async function listNonGitFiles(root, relativeDir = "") {
129
+ const absDir = relativeDir ? path.join(root, relativeDir) : root;
130
+ let entries;
131
+ try {
132
+ entries = await readdir(absDir, { withFileTypes: true });
133
+ }
134
+ catch (error) {
135
+ if (error.code === "ENOENT")
136
+ return [];
137
+ throw error;
138
+ }
139
+ const files = [];
140
+ for (const entry of entries) {
141
+ if (entry.name === ".pi-agent" || entry.name === ".git")
142
+ continue;
143
+ const rel = relativeDir ? path.join(relativeDir, entry.name) : entry.name;
144
+ if (entry.name === "auth.json")
145
+ continue;
146
+ if (entry.isSymbolicLink() || entry.isFile()) {
147
+ files.push(rel.split(path.sep).join("/"));
148
+ continue;
149
+ }
150
+ if (entry.isDirectory()) {
151
+ files.push(...(await listNonGitFiles(root, rel)));
152
+ }
153
+ }
154
+ return files;
155
+ }
156
+ async function listNonGitChanges(checkoutRoot) {
157
+ const files = (await listNonGitFiles(checkoutRoot)).sort();
158
+ const changes = [];
159
+ for (const file of files) {
160
+ const fingerprint = await fingerprintFile(checkoutRoot, file);
161
+ if (fingerprint === "missing")
162
+ continue;
163
+ changes.push({ path: file, status: "untracked" });
164
+ if (changes.length > CHECKOUT_CHANGES_CAP)
165
+ break;
166
+ }
167
+ return capChanges(changes);
168
+ }
169
+ export async function listCheckoutChanges(store, runId) {
170
+ const bound = await resolveBoundCheckout(store, runId);
171
+ if (!bound.ok)
172
+ return bound;
173
+ if (!bound.isGit) {
174
+ const listed = await listNonGitChanges(bound.checkoutRoot);
175
+ return { ok: true, ...listed };
176
+ }
177
+ const porcelain = await statusPorcelainZ(bound.checkoutRoot);
178
+ return { ok: true, ...capChanges(parseStatusPorcelainZ(porcelain)) };
179
+ }
180
+ export async function getRunDiff(store, runId, input = {}) {
181
+ const bound = await resolveBoundCheckout(store, runId);
182
+ if (!bound.ok)
183
+ return bound;
184
+ if (!bound.isGit) {
185
+ return {
186
+ ok: false,
187
+ code: "not_a_git_repository",
188
+ error: "checkout is not a git repository",
189
+ status: 400,
190
+ };
191
+ }
192
+ const mode = input.mode ?? "stat";
193
+ const maxBytes = typeof input.maxBytes === "number" && Number.isFinite(input.maxBytes)
194
+ ? Math.max(0, Math.floor(input.maxBytes))
195
+ : DEFAULT_DIFF_MAX_BYTES;
196
+ let baseSha = bound.meta.resolved_sha?.trim() || undefined;
197
+ let baseSource = "resolved_sha";
198
+ if (!baseSha) {
199
+ baseSha = await revParse(bound.checkoutRoot, ["HEAD"]);
200
+ baseSource = "HEAD";
201
+ }
202
+ const pathArgs = input.path !== undefined && input.path.trim().length > 0
203
+ ? ["--", input.path]
204
+ : ["--"];
205
+ const diffArgs = mode === "patch"
206
+ ? ["--patch", "--no-color", baseSha, ...pathArgs]
207
+ : ["--stat", baseSha, ...pathArgs];
208
+ let content = await diff(bound.checkoutRoot, diffArgs);
209
+ const othersRaw = await lsFilesOthersZ(bound.checkoutRoot);
210
+ let untracked = nulSplit(othersRaw);
211
+ if (input.path !== undefined && input.path.trim().length > 0) {
212
+ const filter = input.path.replace(/\\/g, "/");
213
+ untracked = untracked.filter((file) => file === filter || file.startsWith(`${filter}/`));
214
+ }
215
+ let truncated = false;
216
+ if (mode === "patch" && content.length > maxBytes) {
217
+ content = content.slice(0, maxBytes);
218
+ truncated = true;
219
+ }
220
+ if (untracked.length > CHECKOUT_CHANGES_CAP) {
221
+ untracked = untracked.slice(0, CHECKOUT_CHANGES_CAP);
222
+ truncated = true;
223
+ }
224
+ return {
225
+ ok: true,
226
+ mode,
227
+ base_sha: baseSha,
228
+ base_source: baseSource,
229
+ content,
230
+ truncated,
231
+ untracked,
232
+ };
233
+ }
234
+ export async function readCheckoutFileBytes(store, runId, relativePath) {
235
+ const bound = await resolveBoundCheckout(store, runId);
236
+ if (!bound.ok)
237
+ return bound;
238
+ const realPath = await resolveContainedRelativePath(bound.checkoutRoot, relativePath, CHECKOUT_PATH_MESSAGES);
239
+ return { ok: true, bytes: await readFile(realPath), path: relativePath };
240
+ }
241
+ function visibilityErrorResult(err) {
242
+ return textResult({ error: err.error, code: err.code, status: err.status }, true);
243
+ }
244
+ export function registerCheckoutTools(server, deps) {
245
+ const { store } = deps;
246
+ server.registerTool("list_checkout_changes", {
247
+ description: "List dirty/untracked paths in the run's bound checkout. Repository and git path bindings use git status; non-git path bindings use filesystem fingerprints. Unbound → run_not_bound; reclaimed checkout → checkout_reclaimed. Caps at 500 entries with truncated: true.",
248
+ inputSchema: z.object({
249
+ runId: z.string(),
250
+ }),
251
+ }, async ({ runId }) => {
252
+ try {
253
+ const result = await listCheckoutChanges(store, runId);
254
+ if (!result.ok)
255
+ return visibilityErrorResult(result);
256
+ return textResult({
257
+ runId,
258
+ changes: result.changes,
259
+ truncated: result.truncated,
260
+ });
261
+ }
262
+ catch (err) {
263
+ const mapped = mapStoreLookupError(err, { policy: "run" });
264
+ return textResult({ error: mapped.error, status: mapped.status }, true);
265
+ }
266
+ });
267
+ server.registerTool("get_run_diff", {
268
+ description: "Diff the run's bound git checkout against its recorded base SHA (resolved_sha). mode defaults to stat; patch enforces maxBytes (default 262144) with truncated: true. Unbound → run_not_bound; non-git path → not_a_git_repository; reclaimed → checkout_reclaimed.",
269
+ inputSchema: z.object({
270
+ runId: z.string(),
271
+ mode: z.enum(["stat", "patch"]).optional(),
272
+ path: z.string().optional(),
273
+ maxBytes: z.number().optional(),
274
+ }),
275
+ }, async ({ runId, mode, path: filterPath, maxBytes }) => {
276
+ try {
277
+ const result = await getRunDiff(store, runId, {
278
+ mode,
279
+ path: filterPath,
280
+ maxBytes,
281
+ });
282
+ if (!result.ok)
283
+ return visibilityErrorResult(result);
284
+ return textResult({
285
+ runId,
286
+ mode: result.mode,
287
+ base_sha: result.base_sha,
288
+ base_source: result.base_source,
289
+ content: result.content,
290
+ truncated: result.truncated,
291
+ untracked: result.untracked,
292
+ });
293
+ }
294
+ catch (err) {
295
+ const mapped = mapStoreLookupError(err, { policy: "run" });
296
+ return textResult({ error: mapped.error, status: mapped.status }, true);
297
+ }
298
+ });
299
+ server.registerTool("read_checkout_file", {
300
+ description: "Read a file from the run's bound checkout by relative path (contained under checkout_root). Same deny-list as read_artifact (.pi-agent, auth.json, traversal). Images return an MCP image block; UTF-8 returns JSON { runId, path, content }.",
301
+ inputSchema: z.object({
302
+ runId: z.string(),
303
+ path: z.string(),
304
+ }),
305
+ }, async ({ runId, path: relativePath }) => {
306
+ try {
307
+ const result = await readCheckoutFileBytes(store, runId, relativePath);
308
+ if (!result.ok)
309
+ return visibilityErrorResult(result);
310
+ const classified = classifyArtifactContent(relativePath, result.bytes);
311
+ if (classified.kind === "image") {
312
+ return imageResult(classified.mimeType, result.bytes, {
313
+ runId,
314
+ path: relativePath,
315
+ mimeType: classified.mimeType,
316
+ });
317
+ }
318
+ if (classified.kind === "utf8") {
319
+ return textResult({
320
+ runId,
321
+ path: relativePath,
322
+ content: result.bytes.toString("utf8"),
323
+ });
324
+ }
325
+ return textResult({ error: "Checkout file is not valid UTF-8 text", status: 400 }, true);
326
+ }
327
+ catch (err) {
328
+ const mapped = mapStoreLookupError(err, { policy: "artifact" });
329
+ return textResult({ error: mapped.error, status: mapped.status }, true);
330
+ }
331
+ });
332
+ }
@@ -1,13 +1,17 @@
1
1
  import path from "node:path";
2
2
  import { z } from "zod";
3
+ import { writeAudit } from "../logging/audit.js";
4
+ import { logger as rootLogger } from "../logging/logger.js";
3
5
  import { findProjectRoot } from "../project/findProjectRoot.js";
4
6
  import { mapRetryStageFailure, mapStartFailure, mapStoreLookupError, } from "../server/operatorResults.js";
7
+ import { callerIdFromRequestAuth, getRequestAuth, } from "../server/requestAuthContext.js";
5
8
  import { attemptStreamLogPath } from "../runstore/workspaceLayout.js";
6
9
  import { parseAskOperatorAnswer } from "../tools/askOperator.js";
7
10
  import { readStreamLogTail } from "./tailStreamLog.js";
8
11
  import { textResult } from "./toolResults.js";
9
12
  import { projectWaitingGates } from "./waitingGates.js";
10
13
  import { readStageVerificationHistory } from "../runstore/verificationHistory.js";
14
+ const auditLog = rootLogger.child({ component: "audit" });
11
15
  export function registerControlTools(server, deps) {
12
16
  const { manager, store, cwd } = deps;
13
17
  server.registerTool("list_waiting", {
@@ -51,12 +55,33 @@ export function registerControlTools(server, deps) {
51
55
  }
52
56
  catch (err) {
53
57
  const message = err instanceof Error ? err.message : String(err);
58
+ writeAudit(auditLog, {
59
+ caller_id: callerIdFromRequestAuth(),
60
+ surface: getRequestAuth()?.surface ?? "mcp",
61
+ action: "answer_gate",
62
+ target_run_id: runId,
63
+ outcome: "error",
64
+ });
54
65
  return textResult({ error: message, status: 400 }, true);
55
66
  }
56
67
  const result = await manager.deliverAnswer(runId, stageId, parsed);
57
68
  if (!result.ok) {
69
+ writeAudit(auditLog, {
70
+ caller_id: callerIdFromRequestAuth(),
71
+ surface: getRequestAuth()?.surface ?? "mcp",
72
+ action: "answer_gate",
73
+ target_run_id: runId,
74
+ outcome: "error",
75
+ });
58
76
  return textResult({ error: result.reason, status: result.status }, true);
59
77
  }
78
+ writeAudit(auditLog, {
79
+ caller_id: callerIdFromRequestAuth(),
80
+ surface: getRequestAuth()?.surface ?? "mcp",
81
+ action: "answer_gate",
82
+ target_run_id: runId,
83
+ outcome: "ok",
84
+ });
60
85
  return textResult({ ok: true });
61
86
  });
62
87
  server.registerTool("decide_feedback_loop", {
@@ -233,8 +258,23 @@ export function registerControlTools(server, deps) {
233
258
  }, async ({ runId, stageId }) => {
234
259
  const result = await manager.retryStage(runId, stageId);
235
260
  if (!result.ok) {
261
+ writeAudit(auditLog, {
262
+ caller_id: callerIdFromRequestAuth(),
263
+ surface: getRequestAuth()?.surface ?? "mcp",
264
+ action: "retry_stage",
265
+ target_run_id: runId,
266
+ outcome: "error",
267
+ ...(result.code !== undefined ? { error_code: result.code } : {}),
268
+ });
236
269
  return textResult({ ...mapRetryStageFailure(result), status: result.status }, true);
237
270
  }
271
+ writeAudit(auditLog, {
272
+ caller_id: callerIdFromRequestAuth(),
273
+ surface: getRequestAuth()?.surface ?? "mcp",
274
+ action: "retry_stage",
275
+ target_run_id: runId,
276
+ outcome: "ok",
277
+ });
238
278
  return textResult({
239
279
  runId: result.runId,
240
280
  stageId: result.stageId,
@@ -242,7 +282,7 @@ export function registerControlTools(server, deps) {
242
282
  });
243
283
  });
244
284
  server.registerTool("resume_stage", {
245
- description: "Resume a stage that failed because it timed out, continuing the same attempt/session (same as POST .../resume). Does not start a new attempt — use retry_stage to start over.",
285
+ description: "Resume an interrupted stage or a stage that failed because it timed out, continuing the same attempt/session (same as POST .../resume). Does not start a new attempt — use retry_stage to start over.",
246
286
  inputSchema: z.object({
247
287
  runId: z.string(),
248
288
  stageId: z.string(),
@@ -250,8 +290,23 @@ export function registerControlTools(server, deps) {
250
290
  }, async ({ runId, stageId }) => {
251
291
  const result = await manager.resumeTimedOutStage(runId, stageId);
252
292
  if (!result.ok) {
293
+ writeAudit(auditLog, {
294
+ caller_id: callerIdFromRequestAuth(),
295
+ surface: getRequestAuth()?.surface ?? "mcp",
296
+ action: "resume_stage",
297
+ target_run_id: runId,
298
+ outcome: "error",
299
+ ...(result.code !== undefined ? { error_code: result.code } : {}),
300
+ });
253
301
  return textResult({ ...mapRetryStageFailure(result), status: result.status }, true);
254
302
  }
303
+ writeAudit(auditLog, {
304
+ caller_id: callerIdFromRequestAuth(),
305
+ surface: getRequestAuth()?.surface ?? "mcp",
306
+ action: "resume_stage",
307
+ target_run_id: runId,
308
+ outcome: "ok",
309
+ });
255
310
  return textResult({
256
311
  runId: result.runId,
257
312
  stageId: result.stageId,
@@ -259,7 +314,7 @@ export function registerControlTools(server, deps) {
259
314
  });
260
315
  });
261
316
  server.registerTool("abandon_stage", {
262
- description: "Abandon a running stage (marks it failed/interrupted). Does not dismiss HITL — waiting stages return 409; answer them with answer_gate. There is no run-level cancel tool.",
317
+ description: "Abandon a running stage (marks it failed). Does not dismiss HITL — waiting stages return 409; answer them with answer_gate. Prefer cancel_run to stop an entire run.",
263
318
  inputSchema: z.object({
264
319
  runId: z.string(),
265
320
  stageId: z.string(),
@@ -268,27 +323,146 @@ export function registerControlTools(server, deps) {
268
323
  const result = await manager.abandonStage(runId, stageId);
269
324
  if (!result.ok) {
270
325
  const fail = result;
326
+ writeAudit(auditLog, {
327
+ caller_id: callerIdFromRequestAuth(),
328
+ surface: getRequestAuth()?.surface ?? "mcp",
329
+ action: "abandon_stage",
330
+ target_run_id: runId,
331
+ outcome: "error",
332
+ });
271
333
  return textResult({ error: fail.reason, status: fail.status }, true);
272
334
  }
335
+ writeAudit(auditLog, {
336
+ caller_id: callerIdFromRequestAuth(),
337
+ surface: getRequestAuth()?.surface ?? "mcp",
338
+ action: "abandon_stage",
339
+ target_run_id: runId,
340
+ outcome: "ok",
341
+ });
273
342
  return textResult({
274
343
  ok: true,
275
344
  runId: result.runId,
276
345
  stageId: result.stageId,
277
346
  });
278
347
  });
348
+ server.registerTool("cancel_run", {
349
+ description: "Cancel a non-terminal run (marks it cancelled, terminalizes pending/running/waiting stages, releases the checkout lease). Signals live stage workers via process-group kill (SIGTERM, then SIGKILL escalation) so agent grandchildren are included. Reason is required free-text and stored on the run as cancel_reason.",
350
+ inputSchema: z.object({
351
+ runId: z.string(),
352
+ reason: z.string().min(1),
353
+ }),
354
+ }, async ({ runId, reason }) => {
355
+ const result = await manager.cancelRun(runId, reason);
356
+ if (!result.ok) {
357
+ const fail = result;
358
+ writeAudit(auditLog, {
359
+ caller_id: callerIdFromRequestAuth(),
360
+ surface: getRequestAuth()?.surface ?? "mcp",
361
+ action: "cancel_run",
362
+ target_run_id: runId,
363
+ outcome: "error",
364
+ });
365
+ return textResult({ error: fail.reason, status: fail.status }, true);
366
+ }
367
+ writeAudit(auditLog, {
368
+ caller_id: callerIdFromRequestAuth(),
369
+ surface: getRequestAuth()?.surface ?? "mcp",
370
+ action: "cancel_run",
371
+ target_run_id: runId,
372
+ outcome: "ok",
373
+ });
374
+ return textResult({
375
+ ok: true,
376
+ runId: result.runId,
377
+ });
378
+ });
379
+ server.registerTool("delete_run", {
380
+ description: "Hard-delete a terminal run (store rows, workspace, worktree, run branch, and A2A tasks/artifacts). Active runs (created/queued/running) require force: true, which cancels first then deletes. Irreversible.",
381
+ inputSchema: z.object({
382
+ runId: z.string(),
383
+ force: z.boolean().optional(),
384
+ }),
385
+ }, async ({ runId, force }) => {
386
+ const result = await manager.deleteRun(runId, {
387
+ force,
388
+ channel: "mcp",
389
+ });
390
+ if (!result.ok) {
391
+ const fail = result;
392
+ writeAudit(auditLog, {
393
+ caller_id: callerIdFromRequestAuth(),
394
+ surface: getRequestAuth()?.surface ?? "mcp",
395
+ action: "delete_run",
396
+ target_run_id: runId,
397
+ outcome: "error",
398
+ });
399
+ return textResult({ error: fail.reason, status: fail.status }, true);
400
+ }
401
+ writeAudit(auditLog, {
402
+ caller_id: callerIdFromRequestAuth(),
403
+ surface: getRequestAuth()?.surface ?? "mcp",
404
+ action: "delete_run",
405
+ target_run_id: runId,
406
+ outcome: "ok",
407
+ });
408
+ return textResult({
409
+ ok: true,
410
+ runId: result.runId,
411
+ });
412
+ });
413
+ server.registerTool("gc_runs", {
414
+ description: "Run retention GC (SLIM then PURGE then bare-cache eviction). Default execute: false is dry-run (report candidates only). execute: true is irreversible bulk reclaim — slimmed/purged runIds and evicted bare caches are returned in the report.",
415
+ inputSchema: z.object({
416
+ execute: z.boolean().optional(),
417
+ }),
418
+ }, async ({ execute }) => {
419
+ const result = await manager.gcRuns({
420
+ execute: execute === true,
421
+ channel: "mcp",
422
+ });
423
+ if (!result.ok) {
424
+ const fail = result;
425
+ return textResult({ error: fail.reason, status: fail.status }, true);
426
+ }
427
+ return textResult({
428
+ slimmed: result.slimmed,
429
+ purged: result.purged,
430
+ bareCachesEvicted: result.bareCachesEvicted,
431
+ });
432
+ });
279
433
  server.registerTool("rerun", {
280
- description: "Start a new run from a completed or failed run's pipeline/task locators. Returns { runId } for the new run.",
434
+ description: "Start a new run from a completed or failed run's pipeline/task locators. Optional pinned: true replays the prior resolved_sha for repository bindings. Returns { runId } for the new run.",
281
435
  inputSchema: z.object({
282
436
  runId: z.string(),
437
+ pinned: z.boolean().optional(),
283
438
  }),
284
- }, async ({ runId }) => {
285
- const result = await manager.rerun(runId);
439
+ }, async ({ runId, pinned }) => {
440
+ const callerId = callerIdFromRequestAuth();
441
+ const result = await manager.rerun(runId, {
442
+ ...(pinned !== undefined ? { pinned } : {}),
443
+ callerId,
444
+ });
286
445
  if (!result.ok) {
446
+ writeAudit(auditLog, {
447
+ caller_id: callerId,
448
+ surface: getRequestAuth()?.surface ?? "mcp",
449
+ action: "rerun",
450
+ target_run_id: runId,
451
+ outcome: "error",
452
+ ...(result.code !== undefined ? { error_code: result.code } : {}),
453
+ });
287
454
  return textResult({
288
455
  ...mapStartFailure(result),
289
456
  ...(result.status !== undefined ? { status: result.status } : {}),
290
457
  }, true);
291
458
  }
459
+ writeAudit(auditLog, {
460
+ caller_id: callerId,
461
+ surface: getRequestAuth()?.surface ?? "mcp",
462
+ action: "rerun",
463
+ target_run_id: result.runId,
464
+ outcome: "ok",
465
+ });
292
466
  return textResult({ runId: result.runId });
293
467
  });
294
468
  }
@@ -6,6 +6,7 @@ export type McpToolDeps = {
6
6
  manager: RunManager;
7
7
  store: RunStore;
8
8
  cwd: string;
9
+ agentDir?: string;
9
10
  providerAuthContext?: ProviderAuthContext;
10
11
  projectRoot?: string;
11
12
  };
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/server";
2
+ import type { McpToolDeps } from "./deps.js";
3
+ export declare function registerGetStartedTool(server: McpServer, deps: McpToolDeps): void;