@straiffi/archon 1.8.15 → 1.8.17

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 (227) hide show
  1. package/dist/client/assets/ActivityView-XT8l-IRo.js +1 -0
  2. package/dist/client/assets/GitHubActionsDialog-C1nmgELc.js +1 -0
  3. package/dist/client/assets/VerificationDialog-Cg5XNaPd.js +4 -0
  4. package/dist/client/assets/{arc-C83vRtlz.js → arc-D2Nn9xIj.js} +1 -1
  5. package/dist/client/assets/architecture-7EHR7CIX-C-4Cp4ml.js +1 -0
  6. package/dist/client/assets/{architectureDiagram-3BPJPVTR-Dx7MJofM.js → architectureDiagram-3BPJPVTR-YDD_HNbm.js} +1 -1
  7. package/dist/client/assets/{blockDiagram-GPEHLZMM-CSSJR8F_.js → blockDiagram-GPEHLZMM-BCgBIlJR.js} +1 -1
  8. package/dist/client/assets/{c4Diagram-AAUBKEIU-C4F9969r.js → c4Diagram-AAUBKEIU-BLfk5bKl.js} +1 -1
  9. package/dist/client/assets/channel-3_WpUZue.js +1 -0
  10. package/dist/client/assets/{chunk-2J33WTMH-23HW6i-y.js → chunk-2J33WTMH-DvwqUfn2.js} +1 -1
  11. package/dist/client/assets/{chunk-3OPIFGDE-C3ZmRDMb.js → chunk-3OPIFGDE-D9R3JLGz.js} +1 -1
  12. package/dist/client/assets/{chunk-4BX2VUAB-C5pU9ApX.js → chunk-4BX2VUAB-CtLS8D71.js} +1 -1
  13. package/dist/client/assets/{chunk-4EGX6M5U-CAjWPjND.js → chunk-4EGX6M5U-Btnm0QNe.js} +1 -1
  14. package/dist/client/assets/{chunk-55IACEB6-DhO4-AG4.js → chunk-55IACEB6-74CcJaub.js} +1 -1
  15. package/dist/client/assets/{chunk-5DO6E6H7-BDKA-zHS.js → chunk-5DO6E6H7-WLZ8L4ko.js} +1 -1
  16. package/dist/client/assets/{chunk-5ZQYHXKU-B_-UQvCg.js → chunk-5ZQYHXKU-CZeWOab2.js} +1 -1
  17. package/dist/client/assets/{chunk-727SXJPM-5Dua6u1B.js → chunk-727SXJPM-YdTt6zCi.js} +1 -1
  18. package/dist/client/assets/{chunk-AQP2D5EJ-DcpcaFRO.js → chunk-AQP2D5EJ-BFHbP_eb.js} +1 -1
  19. package/dist/client/assets/{chunk-BR22UD5L-12D2GWh4.js → chunk-BR22UD5L-Cou5beGe.js} +1 -1
  20. package/dist/client/assets/{chunk-BSJP7CBP-DMi77e84.js → chunk-BSJP7CBP-DJ57XcV5.js} +1 -1
  21. package/dist/client/assets/{chunk-CSCIHK7Q-CLCpOw52.js → chunk-CSCIHK7Q-C0BPXL2C.js} +2 -2
  22. package/dist/client/assets/{chunk-FHYWG6QK-BhSajKiB.js → chunk-FHYWG6QK-Ba10e2kj.js} +1 -1
  23. package/dist/client/assets/{chunk-FMBD7UC4-Drmlc0wM.js → chunk-FMBD7UC4-CEo2wZMx.js} +1 -1
  24. package/dist/client/assets/{chunk-KSCS5N6A-uOZFe2JI.js → chunk-KSCS5N6A-DqC4tO6g.js} +1 -1
  25. package/dist/client/assets/{chunk-L5ZTLDWV-DNnzSNoX.js → chunk-L5ZTLDWV-BERUWsJ6.js} +1 -1
  26. package/dist/client/assets/{chunk-LZXEDZCA-CSA3Xnoh.js → chunk-LZXEDZCA-BUHwhuWm.js} +2 -2
  27. package/dist/client/assets/{chunk-MPE355IW-Cw8YmjTC.js → chunk-MPE355IW-MqEt-J8G.js} +1 -1
  28. package/dist/client/assets/{chunk-MZUSXYTE-jvxRCZ7x.js → chunk-MZUSXYTE-DdNi9lhx.js} +1 -1
  29. package/dist/client/assets/{chunk-N66VUXT2-00i4y0yD.js → chunk-N66VUXT2-CjemTW4E.js} +1 -1
  30. package/dist/client/assets/{chunk-ND2GUHAM-BQR5rD7w.js → chunk-ND2GUHAM-DCfmNJ41.js} +1 -1
  31. package/dist/client/assets/{chunk-NZK2D7GU-DyIwcQYQ.js → chunk-NZK2D7GU-CHOkPepJ.js} +1 -1
  32. package/dist/client/assets/{chunk-O5CBEL6O-DfW60w5V.js → chunk-O5CBEL6O-ByMuL2EO.js} +1 -1
  33. package/dist/client/assets/{chunk-PUPMXCY4-DfWourL7.js → chunk-PUPMXCY4-ZvwTw0To.js} +1 -1
  34. package/dist/client/assets/chunk-QZHKN3VN-LI_RZca2.js +1 -0
  35. package/dist/client/assets/{chunk-UIBZB4QT-v-a5ZfN6.js → chunk-UIBZB4QT-BTY9be9s.js} +1 -1
  36. package/dist/client/assets/{chunk-WCWK7LTN-CtYzhvvb.js → chunk-WCWK7LTN-Db1n3n3-.js} +1 -1
  37. package/dist/client/assets/chunk-WU5MYG2G-BGsB3vTj.js +1 -0
  38. package/dist/client/assets/{chunk-XPW4576I-CvnAuXH2.js → chunk-XPW4576I-DObFPxjB.js} +1 -1
  39. package/dist/client/assets/classDiagram-4FO5ZUOK--L8rxPgF.js +1 -0
  40. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-uW5z5Qib.js +1 -0
  41. package/dist/client/assets/{cose-bilkent-S5V4N54A-fchD-dH2.js → cose-bilkent-S5V4N54A-MlOPiG6P.js} +1 -1
  42. package/dist/client/assets/{dagre-BM42HDAG-Ba7Pz-ZI.js → dagre-BM42HDAG-CtmlVYvf.js} +1 -1
  43. package/dist/client/assets/{dagre-DSrzm-Gq.js → dagre-BWNHuDdf.js} +1 -1
  44. package/dist/client/assets/{diagram-2AECGRRQ-BxYfgpZ6.js → diagram-2AECGRRQ-DmQ2-tFR.js} +1 -1
  45. package/dist/client/assets/{diagram-5GNKFQAL-Bc4-O-UJ.js → diagram-5GNKFQAL-l49VXoBJ.js} +1 -1
  46. package/dist/client/assets/{diagram-KO2AKTUF-Ds1Lmrzm.js → diagram-KO2AKTUF-0-U37RyJ.js} +1 -1
  47. package/dist/client/assets/{diagram-LMA3HP47-VFQabxKS.js → diagram-LMA3HP47-DupuMWgW.js} +1 -1
  48. package/dist/client/assets/{diagram-OG6HWLK6-Baff59Zc.js → diagram-OG6HWLK6-D5DoIHW2.js} +1 -1
  49. package/dist/client/assets/{erDiagram-TEJ5UH35-Da5zHjOK.js → erDiagram-TEJ5UH35-DO-3-WYF.js} +1 -1
  50. package/dist/client/assets/eventmodeling-FCH6USID-Dqb6QKoI.js +1 -0
  51. package/dist/client/assets/{flowDiagram-I6XJVG4X-pdGMAuog.js → flowDiagram-I6XJVG4X-CG1viGSO.js} +1 -1
  52. package/dist/client/assets/{ganttDiagram-6RSMTGT7-DjJGX0nm.js → ganttDiagram-6RSMTGT7-CV-6ALMZ.js} +1 -1
  53. package/dist/client/assets/gitGraph-WXDBUCRP-DYEsqh93.js +1 -0
  54. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-kErlwtcT.js → gitGraphDiagram-PVQCEYII-CHnyP-d3.js} +1 -1
  55. package/dist/client/assets/index-CsZfTdMO.css +2 -0
  56. package/dist/client/assets/index-DQ7z6ZWk.js +275 -0
  57. package/dist/client/assets/info-J43DQDTF-CfjXFZai.js +1 -0
  58. package/dist/client/assets/{infoDiagram-5YYISTIA-Bx78HhDi.js → infoDiagram-5YYISTIA-iKC-l4xs.js} +1 -1
  59. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-BP0YQCMl.js → ishikawaDiagram-YF4QCWOH-zi-y6jRI.js} +1 -1
  60. package/dist/client/assets/{journeyDiagram-JHISSGLW-DUgrCOuD.js → journeyDiagram-JHISSGLW-Da4wMeC4.js} +1 -1
  61. package/dist/client/assets/{kanban-definition-UN3LZRKU-K0Zc5_M8.js → kanban-definition-UN3LZRKU-CZQ1yL5y.js} +1 -1
  62. package/dist/client/assets/{line-Dtg-krjk.js → line-B-FS1zIN.js} +1 -1
  63. package/dist/client/assets/{linear-BOwW57yX.js → linear-DT4rSZvy.js} +1 -1
  64. package/dist/client/assets/{mermaid-parser.core-Dw5BI77W.js → mermaid-parser.core-DPXl3axs.js} +2 -2
  65. package/dist/client/assets/{mermaid.core-BE-qlWNj.js → mermaid.core-DjqrtE_A.js} +3 -3
  66. package/dist/client/assets/{mindmap-definition-RKZ34NQL-1Piz4H9I.js → mindmap-definition-RKZ34NQL-Day2-dLJ.js} +1 -1
  67. package/dist/client/assets/{ordinal-CZ33lFpK.js → ordinal-k--hYEme.js} +1 -1
  68. package/dist/client/assets/packet-YPE3B663-Dv0HkRHU.js +1 -0
  69. package/dist/client/assets/pie-LRSECV5Y-S5zPzZFN.js +1 -0
  70. package/dist/client/assets/{pieDiagram-4H26LBE5-zlWdJ7lL.js → pieDiagram-4H26LBE5-BmZiJzvi.js} +1 -1
  71. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-DRQY8Adp.js → quadrantDiagram-W4KKPZXB-DJgAcycV.js} +1 -1
  72. package/dist/client/assets/radar-GUYGQ44K-B7psLv22.js +1 -0
  73. package/dist/client/assets/{requirementDiagram-4Y6WPE33-B8_GJFxl.js → requirementDiagram-4Y6WPE33-y94ek5ya.js} +1 -1
  74. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-BaI0Njo8.js → sankeyDiagram-5OEKKPKP-ChuulS6b.js} +1 -1
  75. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-DjqAXbRF.js → sequenceDiagram-3UESZ5HK-C7eiUu5p.js} +1 -1
  76. package/dist/client/assets/{stateDiagram-AJRCARHV-Ovr_3LeO.js → stateDiagram-AJRCARHV-Dy4QkKUV.js} +1 -1
  77. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-HdcReOP7.js +1 -0
  78. package/dist/client/assets/{timeline-definition-PNZ67QCA-DkczYKeZ.js → timeline-definition-PNZ67QCA-CTtZLJWJ.js} +1 -1
  79. package/dist/client/assets/treeView-BLDUP644-B8H0kT67.js +1 -0
  80. package/dist/client/assets/treemap-LRROVOQU-DPLhFHnh.js +1 -0
  81. package/dist/client/assets/{vennDiagram-CIIHVFJN-BZyo4ESi.js → vennDiagram-CIIHVFJN-C5matxMS.js} +1 -1
  82. package/dist/client/assets/wardley-L42UT6IY-B5-Vb08V.js +1 -0
  83. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-BEISrECX.js → wardleyDiagram-YWT4CUSO-m_iOvfzY.js} +1 -1
  84. package/dist/client/assets/{xychartDiagram-2RQKCTM6-CyNzGK7f.js → xychartDiagram-2RQKCTM6-BmOxTt9K.js} +1 -1
  85. package/dist/client/index.html +2 -2
  86. package/dist/server/db.js +235 -0
  87. package/dist/server/db.js.map +1 -1
  88. package/dist/server/index.js +200 -45
  89. package/dist/server/index.js.map +1 -1
  90. package/dist/server/lib/agent.js +139 -90
  91. package/dist/server/lib/agent.js.map +1 -1
  92. package/dist/server/lib/agentProvider.js +1 -6
  93. package/dist/server/lib/agentProvider.js.map +1 -1
  94. package/dist/server/lib/buildExecutionEnvironment.js +535 -0
  95. package/dist/server/lib/buildExecutionEnvironment.js.map +1 -0
  96. package/dist/server/lib/buildOpenQuestionOperations.js +9 -3
  97. package/dist/server/lib/buildOpenQuestionOperations.js.map +1 -1
  98. package/dist/server/lib/checkAutoFix.js +1 -0
  99. package/dist/server/lib/checkAutoFix.js.map +1 -1
  100. package/dist/server/lib/checkRepairDispatch.js +3 -1
  101. package/dist/server/lib/checkRepairDispatch.js.map +1 -1
  102. package/dist/server/lib/dockerSandboxCredentials.js +62 -0
  103. package/dist/server/lib/dockerSandboxCredentials.js.map +1 -0
  104. package/dist/server/lib/dockerSandboxEnvironmentOperations.js +54 -0
  105. package/dist/server/lib/dockerSandboxEnvironmentOperations.js.map +1 -0
  106. package/dist/server/lib/dockerSandboxExecutionEnvironment.js +725 -0
  107. package/dist/server/lib/dockerSandboxExecutionEnvironment.js.map +1 -0
  108. package/dist/server/lib/dockerSandboxExecutionProvider.js +196 -0
  109. package/dist/server/lib/dockerSandboxExecutionProvider.js.map +1 -0
  110. package/dist/server/lib/dockerSandboxOpencodeSessionOperations.js +29 -0
  111. package/dist/server/lib/dockerSandboxOpencodeSessionOperations.js.map +1 -0
  112. package/dist/server/lib/dockerSandboxRuntimeController.js +187 -0
  113. package/dist/server/lib/dockerSandboxRuntimeController.js.map +1 -0
  114. package/dist/server/lib/executionEnvironment.js +22 -0
  115. package/dist/server/lib/executionEnvironment.js.map +1 -1
  116. package/dist/server/lib/executionEnvironmentContexts.js +144 -0
  117. package/dist/server/lib/executionEnvironmentContexts.js.map +1 -0
  118. package/dist/server/lib/executionEnvironmentLifecycle.js +63 -0
  119. package/dist/server/lib/executionEnvironmentLifecycle.js.map +1 -0
  120. package/dist/server/lib/executionEnvironmentReconciliation.js +782 -0
  121. package/dist/server/lib/executionEnvironmentReconciliation.js.map +1 -0
  122. package/dist/server/lib/executionEnvironmentRoutes.js +375 -0
  123. package/dist/server/lib/executionEnvironmentRoutes.js.map +1 -0
  124. package/dist/server/lib/executionEnvironmentValidation.js +540 -0
  125. package/dist/server/lib/executionEnvironmentValidation.js.map +1 -0
  126. package/dist/server/lib/executionEnvironmentValidationOperations.js +636 -0
  127. package/dist/server/lib/executionEnvironmentValidationOperations.js.map +1 -0
  128. package/dist/server/lib/executionEnvironments.js +726 -0
  129. package/dist/server/lib/executionEnvironments.js.map +1 -0
  130. package/dist/server/lib/executionReconciliation.js +77 -0
  131. package/dist/server/lib/executionReconciliation.js.map +1 -1
  132. package/dist/server/lib/executionRetry.js +3 -0
  133. package/dist/server/lib/executionRetry.js.map +1 -1
  134. package/dist/server/lib/executionRuns.js +1 -0
  135. package/dist/server/lib/executionRuns.js.map +1 -1
  136. package/dist/server/lib/executionTargets.js +107 -0
  137. package/dist/server/lib/executionTargets.js.map +1 -0
  138. package/dist/server/lib/executionTimeline.js +3 -0
  139. package/dist/server/lib/executionTimeline.js.map +1 -1
  140. package/dist/server/lib/hostExecutionEnvironment.js +47 -1
  141. package/dist/server/lib/hostExecutionEnvironment.js.map +1 -1
  142. package/dist/server/lib/mobileAccess.js +2 -2
  143. package/dist/server/lib/mobileAccess.js.map +1 -1
  144. package/dist/server/lib/models.js +11 -0
  145. package/dist/server/lib/models.js.map +1 -1
  146. package/dist/server/lib/opencodeErrors.js +31 -3
  147. package/dist/server/lib/opencodeErrors.js.map +1 -1
  148. package/dist/server/lib/opencodeSessionOperations.js +189 -9
  149. package/dist/server/lib/opencodeSessionOperations.js.map +1 -1
  150. package/dist/server/lib/opencodeTaskSessions.js +7 -12
  151. package/dist/server/lib/opencodeTaskSessions.js.map +1 -1
  152. package/dist/server/lib/postBuildChecks.js +2 -1
  153. package/dist/server/lib/postBuildChecks.js.map +1 -1
  154. package/dist/server/lib/projects.js +10 -0
  155. package/dist/server/lib/projects.js.map +1 -1
  156. package/dist/server/lib/specArtifact.js +1 -1
  157. package/dist/server/lib/specArtifact.js.map +1 -1
  158. package/dist/server/lib/specDraft.js +1 -5
  159. package/dist/server/lib/specDraft.js.map +1 -1
  160. package/dist/server/lib/specExport.js +1 -6
  161. package/dist/server/lib/specExport.js.map +1 -1
  162. package/dist/server/lib/specGraphFeature.js +3 -0
  163. package/dist/server/lib/specGraphFeature.js.map +1 -0
  164. package/dist/server/lib/specRepositoryEvidenceFormatting.js +2 -1
  165. package/dist/server/lib/specRepositoryEvidenceFormatting.js.map +1 -1
  166. package/dist/server/lib/specRepositoryImpact.js +4 -0
  167. package/dist/server/lib/specRepositoryImpact.js.map +1 -1
  168. package/dist/server/lib/specReviewContext.js +6 -20
  169. package/dist/server/lib/specReviewContext.js.map +1 -1
  170. package/dist/server/lib/specSnapshotEvidence.js +4 -0
  171. package/dist/server/lib/specSnapshotEvidence.js.map +1 -1
  172. package/dist/server/lib/specTicketMaterialization.js +4 -1
  173. package/dist/server/lib/specTicketMaterialization.js.map +1 -1
  174. package/dist/server/lib/ticketFollowUpOperations.js +8 -1
  175. package/dist/server/lib/ticketFollowUpOperations.js.map +1 -1
  176. package/dist/server/lib/ticketRunOperations.js +0 -3
  177. package/dist/server/lib/ticketRunOperations.js.map +1 -1
  178. package/dist/server/lib/types.js +1 -0
  179. package/dist/server/lib/types.js.map +1 -1
  180. package/dist/server/lib/workspaceIdentity.js +3 -0
  181. package/dist/server/lib/workspaceIdentity.js.map +1 -1
  182. package/dist/server/workers/build.js +115 -8
  183. package/dist/server/workers/build.js.map +1 -1
  184. package/dist/server/workers/followUp.js +119 -3
  185. package/dist/server/workers/followUp.js.map +1 -1
  186. package/dist/server/workers/plan.js +34 -22
  187. package/dist/server/workers/plan.js.map +1 -1
  188. package/dist/server/workers/review.js +14 -13
  189. package/dist/server/workers/review.js.map +1 -1
  190. package/dist/server/workers/specReview.js +0 -1
  191. package/dist/server/workers/specReview.js.map +1 -1
  192. package/dist/server/workers/specSession.js +46 -122
  193. package/dist/server/workers/specSession.js.map +1 -1
  194. package/package.json +1 -1
  195. package/dist/client/assets/ActivityView-DpY9GZez.js +0 -1
  196. package/dist/client/assets/GitHubActionsDialog-BCr-hjvp.js +0 -1
  197. package/dist/client/assets/RepositoryGraphDialog-_hm-x1Dx.js +0 -1
  198. package/dist/client/assets/VerificationDialog-CsjDzpE1.js +0 -4
  199. package/dist/client/assets/architecture-7EHR7CIX-Dm8H7N6Z.js +0 -1
  200. package/dist/client/assets/channel-DVkhJzgx.js +0 -1
  201. package/dist/client/assets/chunk-QZHKN3VN-CtbsU1Bj.js +0 -1
  202. package/dist/client/assets/chunk-WU5MYG2G-CzMMPYdi.js +0 -1
  203. package/dist/client/assets/classDiagram-4FO5ZUOK-6oZ93s5p.js +0 -1
  204. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DYO_YnkU.js +0 -1
  205. package/dist/client/assets/eventmodeling-FCH6USID-BWhw0UJ8.js +0 -1
  206. package/dist/client/assets/gitGraph-WXDBUCRP-BSnxAa1J.js +0 -1
  207. package/dist/client/assets/index-CS4TzG41.css +0 -2
  208. package/dist/client/assets/index-D5TEfELZ.js +0 -275
  209. package/dist/client/assets/info-J43DQDTF-DcFwF1-h.js +0 -1
  210. package/dist/client/assets/packet-YPE3B663-1VZiO_9r.js +0 -1
  211. package/dist/client/assets/pie-LRSECV5Y-BX1-tQCu.js +0 -1
  212. package/dist/client/assets/radar-GUYGQ44K-B2HRrCR4.js +0 -1
  213. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-9pmeAorV.js +0 -1
  214. package/dist/client/assets/treeView-BLDUP644-CDfUVbUi.js +0 -1
  215. package/dist/client/assets/treemap-LRROVOQU-DUWqu8sK.js +0 -1
  216. package/dist/client/assets/wardley-L42UT6IY-BEFuPUCy.js +0 -1
  217. /package/dist/client/assets/{array-B2f2amhX.js → array-BNor45A1.js} +0 -0
  218. /package/dist/client/assets/{chunk-NNHCCRGN-DgKrp8T-.js → chunk-NNHCCRGN-CZB-V-Gv.js} +0 -0
  219. /package/dist/client/assets/{cytoscape.esm-BInZCgaJ.js → cytoscape.esm-B2lJcMSI.js} +0 -0
  220. /package/dist/client/assets/{defaultLocale-sPXJ5urm.js → defaultLocale-DPzUsThw.js} +0 -0
  221. /package/dist/client/assets/{dist-DfHDMDTt.js → dist-CERhmQhR.js} +0 -0
  222. /package/dist/client/assets/{graphlib-DacNv4Jd.js → graphlib-BHOW-eWz.js} +0 -0
  223. /package/dist/client/assets/{init-BVCnG1lj.js → init-C0L3woqb.js} +0 -0
  224. /package/dist/client/assets/{katex-BlkrXOwm.js → katex-CpjQsUQe.js} +0 -0
  225. /package/dist/client/assets/{path-Bw5AvBNC.js → path-sMK4d_s9.js} +0 -0
  226. /package/dist/client/assets/{rough.esm-DeLgKbOI.js → rough.esm-BoTisKeL.js} +0 -0
  227. /package/dist/client/assets/{src-CFGsdLiG.js → src-BRjoYtUW.js} +0 -0
@@ -10,6 +10,8 @@ import { adjectives, animals, uniqueNamesGenerator } from 'unique-names-generato
10
10
  import db from './db.js';
11
11
  import { pruneAllRepositoryGraphScans } from './lib/repositoryGraphRetention.js';
12
12
  import { registerRepositoryGraphRoutes } from './lib/repositoryGraphRoutes.js';
13
+ import { registerExecutionEnvironmentRoutes } from './lib/executionEnvironmentRoutes.js';
14
+ import { dockerSandboxAdapter, dockerSandboxRunner, dockerSandboxRuntimeController, } from './lib/dockerSandboxEnvironmentOperations.js';
13
15
  import { deleteChatSession, getChatSession, listChatSessions } from './lib/chats.js';
14
16
  import { deleteSpecSession, getCreatedTicketIdsForSnapshot, getSpecSession, getSpecSnapshot, listSpecSessions } from './lib/specs.js';
15
17
  import { getSpecSnapshotRepositoryEvidenceFreshness } from './lib/specSnapshotEvidence.js';
@@ -82,6 +84,9 @@ import { buildImportedIssueSessionTitle, importProviderHostedIssueImages } from
82
84
  import { dispatchGitHubWorkflow, getGitHubWorkflowDispatchConfig, getGitHubWorkflowRun, getGitHubWorkflowRunJobs, listGitHubWorkflows, listGitHubWorkflowRuns, } from './lib/integrations/githubActions.js';
83
85
  import { clearAllGitHubActionsSessions, getActiveTrackedRunCount, getTrackedGitHubActionsRuns, startGitHubActionsPolling, trackGitHubActionsRun, } from './lib/integrations/githubActionsSessions.js';
84
86
  import { reconcileStartupExecutions } from './lib/executionReconciliation.js';
87
+ import { cleanupProjectExecutionEnvironments, cleanupExecutionEnvironmentsForTarget, ExecutionEnvironmentCleanupError, reconcileOrphanedExecutionEnvironments, reconcileExecutionEnvironments, reconcileProductionExecutionEnvironmentBindings, } from './lib/executionEnvironmentReconciliation.js';
88
+ import { createExecutionTargetKey } from './lib/executionTargets.js';
89
+ import { getExecutionEnvironmentContextForRun } from './lib/executionEnvironmentContexts.js';
85
90
  import { PlanningReferenceError, resolveExternalPlanningDescription } from './lib/integrations/planning.js';
86
91
  import { refreshIfNeeded as refreshAssignedIssues, clearCachedAssignedIssues } from './lib/integrations/assignedIssuesCache.js';
87
92
  import { buildCommitMessagePrompt, normalizeGeneratedCommitMessage } from './lib/commitMessage.js';
@@ -453,6 +458,7 @@ const buildExecutionRunDetailResponse = async (project, run) => {
453
458
  details,
454
459
  agent_runs: listAgentRunsForExecutionRun(run.id),
455
460
  ...artifacts,
461
+ environment_context: getExecutionEnvironmentContextForRun(project.id, run.id),
456
462
  };
457
463
  };
458
464
  const formatWorktreeDeleteError = (target, branch, error) => {
@@ -506,6 +512,25 @@ const getBundlePlanBlockerError = () => {
506
512
  const hasActiveBundleCheckWork = (projectId, bundleId) => Boolean(getActiveBundleRepairBlocker(bundleId)
507
513
  || findActiveCheckBatchForTarget(projectId, 'bundle', bundleId)
508
514
  || findActiveCheckAutoFixForTarget(projectId, 'bundle', bundleId));
515
+ const cleanupWorktreeExecutionTarget = async ({ project, targetKind, targetKey, targetLabel, worktreeTarget, branch, }) => {
516
+ try {
517
+ await cleanupExecutionEnvironmentsForTarget({
518
+ projectId: project.id,
519
+ targetKind,
520
+ targetKey,
521
+ });
522
+ }
523
+ catch (error) {
524
+ const message = error instanceof Error ? error.message : 'Docker Sandbox cleanup could not be verified.';
525
+ throw new HttpError(409, `Cannot delete ${targetLabel} until its Docker Sandbox cleanup completes: ${message}`);
526
+ }
527
+ try {
528
+ deleteWorktree(branch, project);
529
+ }
530
+ catch (error) {
531
+ throw new HttpError(500, formatWorktreeDeleteError(worktreeTarget, branch, error));
532
+ }
533
+ };
509
534
  const deleteTicketRecord = async (ticket) => {
510
535
  if (ticket.project_id
511
536
  && ticket.worktree_bundle_id
@@ -524,19 +549,54 @@ const deleteTicketRecord = async (ticket) => {
524
549
  const project = getEffectiveProject(ticket);
525
550
  if (project) {
526
551
  try {
527
- deleteWorktree(branch, project);
552
+ await cleanupWorktreeExecutionTarget({
553
+ project,
554
+ targetKind: 'branch-worktree',
555
+ targetKey: createExecutionTargetKey(project.id, { kind: 'branch-worktree', bundleId: null, branch }),
556
+ targetLabel: 'ticket worktree',
557
+ worktreeTarget: 'ticket',
558
+ branch,
559
+ });
528
560
  }
529
561
  catch (error) {
530
562
  return {
531
563
  ok: false,
532
- status: 500,
533
- error: formatWorktreeDeleteError('ticket', branch, error),
564
+ status: error instanceof HttpError ? error.status : 500,
565
+ error: error instanceof Error ? error.message : 'Unable to delete the ticket worktree',
534
566
  };
535
567
  }
536
568
  }
537
569
  }
538
570
  const bundledProject = isBundledTicket(ticket) ? getEffectiveProject(ticket) : null;
539
571
  const deletedBundleId = ticket.worktree_bundle_id;
572
+ const deletedBundle = bundledProject && deletedBundleId ? getBundle(deletedBundleId, bundledProject.id) : null;
573
+ const bundleResourcesWereCleaned = Boolean(bundledProject
574
+ && deletedBundle
575
+ && !isProjectRootBundle(deletedBundle)
576
+ && countBundleTickets(deletedBundle.id, bundledProject.id) === 1);
577
+ if (bundleResourcesWereCleaned && bundledProject && deletedBundle) {
578
+ try {
579
+ await cleanupWorktreeExecutionTarget({
580
+ project: bundledProject,
581
+ targetKind: 'bundle-worktree',
582
+ targetKey: createExecutionTargetKey(bundledProject.id, {
583
+ kind: 'bundle-worktree',
584
+ bundleId: deletedBundle.id,
585
+ branch: deletedBundle.branch,
586
+ }),
587
+ targetLabel: 'bundle worktree',
588
+ worktreeTarget: 'bundle',
589
+ branch: deletedBundle.branch,
590
+ });
591
+ }
592
+ catch (error) {
593
+ return {
594
+ ok: false,
595
+ status: error instanceof HttpError ? error.status : 500,
596
+ error: error instanceof Error ? error.message : 'Unable to delete the bundle worktree',
597
+ };
598
+ }
599
+ }
540
600
  let nextBuildTicketId = null;
541
601
  let nextBuildSequenceId = null;
542
602
  let affectedTicketIds = [];
@@ -557,7 +617,7 @@ const deleteTicketRecord = async (ticket) => {
557
617
  }
558
618
  if (bundledProject && deletedBundleId) {
559
619
  try {
560
- cleanupBundleIfEmpty(deletedBundleId, bundledProject);
620
+ await cleanupBundleIfEmpty(deletedBundleId, bundledProject, { resourcesAlreadyCleaned: bundleResourcesWereCleaned });
561
621
  }
562
622
  catch (error) {
563
623
  return {
@@ -870,7 +930,7 @@ const createAutoBundleForName = (name, projectId, baseBranch = null) => {
870
930
  }
871
931
  return bundle;
872
932
  };
873
- const cleanupBundleIfEmpty = (bundleId, project) => {
933
+ const cleanupBundleIfEmpty = async (bundleId, project, options = {}) => {
874
934
  if (!bundleId) {
875
935
  return;
876
936
  }
@@ -884,11 +944,19 @@ const cleanupBundleIfEmpty = (bundleId, project) => {
884
944
  if (hasActiveBundleCheckWork(project.id, bundle.id)) {
885
945
  throw new HttpError(409, 'Cannot delete a bundle while check work is active');
886
946
  }
887
- try {
888
- deleteWorktree(bundle.branch, project);
889
- }
890
- catch (error) {
891
- throw new HttpError(500, formatWorktreeDeleteError('bundle', bundle.branch, error));
947
+ if (!options.resourcesAlreadyCleaned) {
948
+ await cleanupWorktreeExecutionTarget({
949
+ project,
950
+ targetKind: 'bundle-worktree',
951
+ targetKey: createExecutionTargetKey(project.id, {
952
+ kind: 'bundle-worktree',
953
+ bundleId: bundle.id,
954
+ branch: bundle.branch,
955
+ }),
956
+ targetLabel: 'bundle worktree',
957
+ worktreeTarget: 'bundle',
958
+ branch: bundle.branch,
959
+ });
892
960
  }
893
961
  deleteBundle(bundle.id, project.id);
894
962
  };
@@ -1358,6 +1426,8 @@ const listDetailedBundleTickets = (projectId, bundleId) => {
1358
1426
  const representative = getBundleRepresentativeTicketContext(projectId, bundleId);
1359
1427
  return representative ? sortBundleTicketsByExecutionOrder(listTicketsInRunContext(representative)) : [];
1360
1428
  };
1429
+ const listBoardBundleTickets = (projectId, bundleId) => listTickets(projectId, { detail: 'board' })
1430
+ .filter(ticket => ticket.worktree_bundle_id === bundleId);
1361
1431
  const listBundleConversationTickets = (projectId, bundleId) => {
1362
1432
  const representative = getBundleRepresentativeTicketContext(projectId, bundleId);
1363
1433
  return representative
@@ -2480,6 +2550,12 @@ const emitRunStateForContext = (ticket, isRunningValue) => {
2480
2550
  };
2481
2551
  app.use(express.json({ limit: '5mb' }));
2482
2552
  registerRepositoryGraphRoutes(app, { emitter: io });
2553
+ registerExecutionEnvironmentRoutes(app, {
2554
+ emitter: io,
2555
+ runner: dockerSandboxRunner,
2556
+ adapter: dockerSandboxAdapter,
2557
+ runtimeController: dockerSandboxRuntimeController,
2558
+ });
2483
2559
  const requireDesktopMobileAccessRequest = (req, res) => {
2484
2560
  const rejectionReason = validateDesktopMobileAccessRequest(req);
2485
2561
  if (!rejectionReason) {
@@ -2598,7 +2674,7 @@ app.post('/projects/:id/chat-sessions', (req, res) => {
2598
2674
  }
2599
2675
  return res.status(result.status).json(result.value);
2600
2676
  });
2601
- app.get('/projects/:id/chat-sessions/:sessionId', (req, res) => {
2677
+ app.get('/projects/:id/chat-sessions/:sessionId', async (req, res) => {
2602
2678
  const project = getProjectById(req.params.id);
2603
2679
  if (!project) {
2604
2680
  return res.status(404).json({ error: 'Not found' });
@@ -2607,7 +2683,7 @@ app.get('/projects/:id/chat-sessions/:sessionId', (req, res) => {
2607
2683
  if (!chatSession) {
2608
2684
  return res.status(404).json({ error: 'Not found' });
2609
2685
  }
2610
- const refreshedChatSession = refreshChatSessionUsageSnapshot(chatSession.id, { includeMessages: true });
2686
+ const refreshedChatSession = await refreshChatSessionUsageSnapshot(chatSession.id, { includeMessages: true });
2611
2687
  if (!refreshedChatSession || refreshedChatSession.project_id !== project.id) {
2612
2688
  return res.json(chatSession);
2613
2689
  }
@@ -2908,7 +2984,7 @@ app.post('/projects/:id/spec-sessions', (req, res) => {
2908
2984
  }
2909
2985
  return res.status(result.status).json(result.value);
2910
2986
  });
2911
- app.get('/projects/:id/spec-sessions/:sessionId', (req, res) => {
2987
+ app.get('/projects/:id/spec-sessions/:sessionId', async (req, res) => {
2912
2988
  const project = getProjectById(req.params.id);
2913
2989
  if (!project) {
2914
2990
  return res.status(404).json({ error: 'Not found' });
@@ -2917,7 +2993,7 @@ app.get('/projects/:id/spec-sessions/:sessionId', (req, res) => {
2917
2993
  if (!specSession) {
2918
2994
  return res.status(404).json({ error: 'Not found' });
2919
2995
  }
2920
- const refreshedSpecSession = refreshSpecSessionUsageSnapshot(specSession.id, { includeMessages: true });
2996
+ const refreshedSpecSession = await refreshSpecSessionUsageSnapshot(specSession.id, { includeMessages: true });
2921
2997
  if (!refreshedSpecSession || refreshedSpecSession.project_id !== project.id) {
2922
2998
  return res.json(specSession);
2923
2999
  }
@@ -3848,7 +3924,7 @@ app.post('/bundles', async (req, res) => {
3848
3924
  }
3849
3925
  res.status(201).json(serializeBundleRow(result.project.id, bundle, [], result.project));
3850
3926
  });
3851
- app.delete('/bundles/:id', (req, res) => {
3927
+ app.delete('/bundles/:id', async (req, res) => {
3852
3928
  const result = getRequiredProject(req);
3853
3929
  if ('error' in result) {
3854
3930
  return res.status(400).json({ error: result.error });
@@ -3882,11 +3958,22 @@ app.delete('/bundles/:id', (req, res) => {
3882
3958
  .filter(specSession => specSession.target_bundle_id === bundle.id)
3883
3959
  .map(specSession => specSession.id);
3884
3960
  try {
3885
- deleteWorktree(bundle.branch, result.project);
3961
+ await cleanupWorktreeExecutionTarget({
3962
+ project: result.project,
3963
+ targetKind: 'bundle-worktree',
3964
+ targetKey: createExecutionTargetKey(result.project.id, {
3965
+ kind: 'bundle-worktree',
3966
+ bundleId: bundle.id,
3967
+ branch: bundle.branch,
3968
+ }),
3969
+ targetLabel: 'bundle worktree',
3970
+ worktreeTarget: 'bundle',
3971
+ branch: bundle.branch,
3972
+ });
3886
3973
  }
3887
3974
  catch (error) {
3888
- return res.status(500).json({
3889
- error: formatWorktreeDeleteError('bundle', bundle.branch, error),
3975
+ return res.status(error instanceof HttpError ? error.status : 500).json({
3976
+ error: error instanceof Error ? error.message : 'Unable to delete the bundle worktree',
3890
3977
  });
3891
3978
  }
3892
3979
  deleteBundle(bundle.id, result.project.id);
@@ -4171,12 +4258,16 @@ app.post('/bundles/:id/build-open-questions/resolve', async (req, res) => {
4171
4258
  progressionRunId: accepted.progressionRunId,
4172
4259
  broadcaster: io,
4173
4260
  });
4174
- for (const ticket of listDetailedBundleTickets(result.project.id, bundle.id)) {
4261
+ for (const ticket of listBoardBundleTickets(result.project.id, bundle.id)) {
4175
4262
  io.emit('ticket:updated', ticket);
4176
4263
  }
4177
- return res.json(serializeBundleDetails(result.project, bundle));
4264
+ return res.json({
4265
+ kind: accepted.kind,
4266
+ batch_id: accepted.batchId,
4267
+ target_ticket_id: null,
4268
+ });
4178
4269
  }
4179
- const ticketsBeforeReview = listDetailedBundleTickets(result.project.id, bundle.id);
4270
+ const ticketsBeforeReview = listBoardBundleTickets(result.project.id, bundle.id);
4180
4271
  if (!areBundleTicketsReadyForAutomaticReview(ticketsBeforeReview, bundle.id)) {
4181
4272
  completeAutomationProgression({
4182
4273
  projectId: result.project.id,
@@ -4187,7 +4278,11 @@ app.post('/bundles/:id/build-open-questions/resolve', async (req, res) => {
4187
4278
  for (const ticket of ticketsBeforeReview) {
4188
4279
  io.emit('ticket:updated', ticket);
4189
4280
  }
4190
- return res.json(serializeBundleDetails(result.project, bundle));
4281
+ return res.json({
4282
+ kind: accepted.kind,
4283
+ batch_id: accepted.batchId,
4284
+ target_ticket_id: null,
4285
+ });
4191
4286
  }
4192
4287
  let reviewStartFailureStatus = null;
4193
4288
  let reviewStartFailureError = null;
@@ -4218,24 +4313,27 @@ app.post('/bundles/:id/build-open-questions/resolve', async (req, res) => {
4218
4313
  });
4219
4314
  return res.status(reviewStartFailureStatus).json({ error: reviewStartFailureError });
4220
4315
  }
4221
- const updatedTickets = listDetailedBundleTickets(result.project.id, bundle.id);
4316
+ const updatedTickets = listBoardBundleTickets(result.project.id, bundle.id);
4222
4317
  for (const ticket of updatedTickets) {
4223
4318
  io.emit('ticket:updated', ticket);
4224
4319
  }
4225
4320
  const reviewOwner = reviewStarted
4226
4321
  ? updatedTickets.find(ticket => ticket.state === 'review' && ticket.parked_at === null)
4227
4322
  : null;
4228
- return res.json(serializeBundleDetails(result.project, bundle, {
4229
- review_owner_ticket_id: reviewOwner?.id,
4230
- target_ticket_id: reviewOwner?.id,
4231
- }));
4323
+ return res.json({
4324
+ kind: accepted.kind,
4325
+ batch_id: accepted.batchId,
4326
+ target_ticket_id: reviewOwner?.id ?? null,
4327
+ });
4232
4328
  }
4233
- for (const ticket of listDetailedBundleTickets(result.project.id, bundle.id)) {
4329
+ for (const ticket of listBoardBundleTickets(result.project.id, bundle.id)) {
4234
4330
  io.emit('ticket:updated', ticket);
4235
4331
  }
4236
- return res.status(accepted.status).json(serializeBundleDetails(result.project, bundle, {
4332
+ return res.status(accepted.status).json({
4333
+ kind: accepted.kind,
4334
+ batch_id: accepted.batchId,
4237
4335
  target_ticket_id: accepted.targetTicket.id,
4238
- }));
4336
+ });
4239
4337
  });
4240
4338
  app.post('/bundles/:id/undo-review', (req, res) => {
4241
4339
  const result = getRequiredProject(req);
@@ -6002,7 +6100,7 @@ app.post('/projects', (req, res) => {
6002
6100
  return res.status(400).json({ error: validation.error });
6003
6101
  }
6004
6102
  const projectCount = Number(db.prepare('SELECT COUNT(*) AS count FROM projects').get()?.count ?? 0);
6005
- const { name, repo_path, linked_project_ids, run_setup, run_services, check_definitions, required_check_definition_ids, pre_review_check_definition_ids, post_build_check_definition_ids, auto_fix_check_definition_ids, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_variant, reviewer_model, reviewer_variant, commit_message_rules, commit_after_each_ticket, auto_park_stale_tickets, memory_enabled, scan_include_globs_json, scan_exclude_globs_json, worktree_sync, spec_aware_code_review_enabled, automation_depth, automation_review_fix_enabled, automation_pause_on_open_questions, automation_review_severity, automation_review_max_iterations, } = validation.values;
6103
+ const { name, repo_path, build_execution_provider, linked_project_ids, run_setup, run_services, check_definitions, required_check_definition_ids, pre_review_check_definition_ids, post_build_check_definition_ids, auto_fix_check_definition_ids, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_variant, reviewer_model, reviewer_variant, commit_message_rules, commit_after_each_ticket, auto_park_stale_tickets, memory_enabled, scan_include_globs_json, scan_exclude_globs_json, worktree_sync, spec_aware_code_review_enabled, automation_depth, automation_review_fix_enabled, automation_pause_on_open_questions, automation_review_severity, automation_review_max_iterations, } = validation.values;
6006
6104
  if (!repo_path) {
6007
6105
  return res.status(400).json({ error: 'repo_path must be a non-empty string' });
6008
6106
  }
@@ -6020,6 +6118,7 @@ app.post('/projects', (req, res) => {
6020
6118
  id,
6021
6119
  name,
6022
6120
  repo_path,
6121
+ build_execution_provider,
6023
6122
  run_setup,
6024
6123
  run_services,
6025
6124
  check_definitions,
@@ -6050,8 +6149,8 @@ app.post('/projects', (req, res) => {
6050
6149
  automation_review_severity,
6051
6150
  automation_review_max_iterations
6052
6151
  )
6053
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
6054
- `).run(id, name, normalizedRepoPath, run_setup, run_services, check_definitions, required_check_definition_ids ?? '[]', pre_review_check_definition_ids ?? '[]', post_build_check_definition_ids ?? '[]', auto_fix_check_definition_ids ?? '[]', test_commands, run_ide ?? null, preview_service_name ?? null, preview_path ?? null, preview_capability_mode ?? null, helper_model ?? null, helper_variant ?? null, reviewer_model ?? null, reviewer_model ? reviewer_variant ?? null : null, commit_message_rules ?? null, commit_after_each_ticket ?? 0, auto_park_stale_tickets ?? 0, memory_enabled ?? 0, scan_include_globs_json ?? null, scan_exclude_globs_json ?? null, worktree_sync ?? null, spec_aware_code_review_enabled ?? 0, automation_depth ?? 'manual', automation_review_fix_enabled ?? 0, automation_pause_on_open_questions ?? 0, automation_review_severity ?? 'medium', automation_review_max_iterations ?? 3);
6152
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
6153
+ `).run(id, name, normalizedRepoPath, build_execution_provider ?? 'host', run_setup, run_services, check_definitions, required_check_definition_ids ?? '[]', pre_review_check_definition_ids ?? '[]', post_build_check_definition_ids ?? '[]', auto_fix_check_definition_ids ?? '[]', test_commands, run_ide ?? null, preview_service_name ?? null, preview_path ?? null, preview_capability_mode ?? null, helper_model ?? null, helper_variant ?? null, reviewer_model ?? null, reviewer_model ? reviewer_variant ?? null : null, commit_message_rules ?? null, commit_after_each_ticket ?? 0, auto_park_stale_tickets ?? 0, memory_enabled ?? 0, scan_include_globs_json ?? null, scan_exclude_globs_json ?? null, worktree_sync ?? null, spec_aware_code_review_enabled ?? 0, automation_depth ?? 'manual', automation_review_fix_enabled ?? 0, automation_pause_on_open_questions ?? 0, automation_review_severity ?? 'medium', automation_review_max_iterations ?? 3);
6055
6154
  replaceProjectLinks(id, linked_project_ids ?? []);
6056
6155
  ensureProjectRootBundle(id);
6057
6156
  if (projectCount === 0) {
@@ -7258,11 +7357,12 @@ app.patch('/projects/:id', (req, res) => {
7258
7357
  });
7259
7358
  }
7260
7359
  }
7261
- const { name, repo_path, linked_project_ids, run_setup, run_services, check_definitions, required_check_definition_ids, pre_review_check_definition_ids, post_build_check_definition_ids, auto_fix_check_definition_ids, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_variant, reviewer_model, reviewer_variant, commit_message_rules, commit_after_each_ticket, auto_park_stale_tickets, memory_enabled, scan_include_globs_json, scan_exclude_globs_json, worktree_sync, spec_aware_code_review_enabled, automation_depth, automation_review_fix_enabled, automation_pause_on_open_questions, automation_review_severity, automation_review_max_iterations, } = validation.values;
7360
+ const { name, repo_path, build_execution_provider, linked_project_ids, run_setup, run_services, check_definitions, required_check_definition_ids, pre_review_check_definition_ids, post_build_check_definition_ids, auto_fix_check_definition_ids, test_commands, run_ide, preview_service_name, preview_path, preview_capability_mode, helper_model, helper_variant, reviewer_model, reviewer_variant, commit_message_rules, commit_after_each_ticket, auto_park_stale_tickets, memory_enabled, scan_include_globs_json, scan_exclude_globs_json, worktree_sync, spec_aware_code_review_enabled, automation_depth, automation_review_fix_enabled, automation_pause_on_open_questions, automation_review_severity, automation_review_max_iterations, } = validation.values;
7262
7361
  const updateProject = db.prepare(`
7263
7362
  UPDATE projects SET
7264
7363
  name = ?,
7265
7364
  repo_path = ?,
7365
+ build_execution_provider = ?,
7266
7366
  run_setup = ?,
7267
7367
  run_services = ?,
7268
7368
  check_definitions = ?,
@@ -7324,7 +7424,7 @@ app.patch('/projects/:id', (req, res) => {
7324
7424
  if ('error' in policyReferences) {
7325
7425
  return policyReferences;
7326
7426
  }
7327
- updateProject.run(name ?? project.name, repo_path ?? project.repo_path, hasOwn(validation.values, 'run_setup') ? run_setup : project.run_setup, hasOwn(validation.values, 'run_services') ? run_services : project.run_services, nextCheckDefinitions, nextRequiredCheckDefinitionIds, nextPreReviewCheckDefinitionIds, nextPostBuildCheckDefinitionIds, nextAutoFixCheckDefinitionIds, hasOwn(validation.values, 'test_commands') ? test_commands : project.test_commands, hasOwn(validation.values, 'run_ide') ? run_ide : project.run_ide, hasOwn(validation.values, 'preview_service_name') ? preview_service_name : project.preview_service_name, hasOwn(validation.values, 'preview_path') ? preview_path : project.preview_path, hasOwn(validation.values, 'preview_capability_mode') ? preview_capability_mode : project.preview_capability_mode, hasOwn(validation.values, 'helper_model') ? helper_model : project.helper_model, hasOwn(validation.values, 'helper_variant') ? helper_variant : project.helper_variant, nextReviewerModel, nextReviewerVariant, hasOwn(validation.values, 'commit_message_rules') ? commit_message_rules : project.commit_message_rules, hasOwn(validation.values, 'commit_after_each_ticket') ? commit_after_each_ticket : project.commit_after_each_ticket, hasOwn(validation.values, 'auto_park_stale_tickets') ? auto_park_stale_tickets : project.auto_park_stale_tickets, hasOwn(validation.values, 'memory_enabled') ? memory_enabled : project.memory_enabled, hasOwn(validation.values, 'scan_include_globs_json') ? scan_include_globs_json : project.scan_include_globs_json, hasOwn(validation.values, 'scan_exclude_globs_json') ? scan_exclude_globs_json : project.scan_exclude_globs_json, hasOwn(validation.values, 'worktree_sync') ? worktree_sync : project.worktree_sync, hasOwn(validation.values, 'spec_aware_code_review_enabled') ? spec_aware_code_review_enabled : project.spec_aware_code_review_enabled, hasOwn(validation.values, 'automation_depth') ? automation_depth : project.automation_depth, hasOwn(validation.values, 'automation_review_fix_enabled') ? automation_review_fix_enabled : project.automation_review_fix_enabled, hasOwn(validation.values, 'automation_pause_on_open_questions') ? automation_pause_on_open_questions : project.automation_pause_on_open_questions, hasOwn(validation.values, 'automation_review_severity') ? automation_review_severity : project.automation_review_severity, hasOwn(validation.values, 'automation_review_max_iterations') ? automation_review_max_iterations : project.automation_review_max_iterations, req.params.id);
7427
+ updateProject.run(name ?? project.name, repo_path ?? project.repo_path, hasOwn(validation.values, 'build_execution_provider') ? build_execution_provider : project.build_execution_provider, hasOwn(validation.values, 'run_setup') ? run_setup : project.run_setup, hasOwn(validation.values, 'run_services') ? run_services : project.run_services, nextCheckDefinitions, nextRequiredCheckDefinitionIds, nextPreReviewCheckDefinitionIds, nextPostBuildCheckDefinitionIds, nextAutoFixCheckDefinitionIds, hasOwn(validation.values, 'test_commands') ? test_commands : project.test_commands, hasOwn(validation.values, 'run_ide') ? run_ide : project.run_ide, hasOwn(validation.values, 'preview_service_name') ? preview_service_name : project.preview_service_name, hasOwn(validation.values, 'preview_path') ? preview_path : project.preview_path, hasOwn(validation.values, 'preview_capability_mode') ? preview_capability_mode : project.preview_capability_mode, hasOwn(validation.values, 'helper_model') ? helper_model : project.helper_model, hasOwn(validation.values, 'helper_variant') ? helper_variant : project.helper_variant, nextReviewerModel, nextReviewerVariant, hasOwn(validation.values, 'commit_message_rules') ? commit_message_rules : project.commit_message_rules, hasOwn(validation.values, 'commit_after_each_ticket') ? commit_after_each_ticket : project.commit_after_each_ticket, hasOwn(validation.values, 'auto_park_stale_tickets') ? auto_park_stale_tickets : project.auto_park_stale_tickets, hasOwn(validation.values, 'memory_enabled') ? memory_enabled : project.memory_enabled, hasOwn(validation.values, 'scan_include_globs_json') ? scan_include_globs_json : project.scan_include_globs_json, hasOwn(validation.values, 'scan_exclude_globs_json') ? scan_exclude_globs_json : project.scan_exclude_globs_json, hasOwn(validation.values, 'worktree_sync') ? worktree_sync : project.worktree_sync, hasOwn(validation.values, 'spec_aware_code_review_enabled') ? spec_aware_code_review_enabled : project.spec_aware_code_review_enabled, hasOwn(validation.values, 'automation_depth') ? automation_depth : project.automation_depth, hasOwn(validation.values, 'automation_review_fix_enabled') ? automation_review_fix_enabled : project.automation_review_fix_enabled, hasOwn(validation.values, 'automation_pause_on_open_questions') ? automation_pause_on_open_questions : project.automation_pause_on_open_questions, hasOwn(validation.values, 'automation_review_severity') ? automation_review_severity : project.automation_review_severity, hasOwn(validation.values, 'automation_review_max_iterations') ? automation_review_max_iterations : project.automation_review_max_iterations, req.params.id);
7328
7428
  if (hasOwn(validation.values, 'linked_project_ids')) {
7329
7429
  replaceProjectLinks(project.id, linked_project_ids ?? []);
7330
7430
  }
@@ -7373,15 +7473,23 @@ const assertProjectDeletionEligible = (projectId, claimId) => {
7373
7473
  }
7374
7474
  };
7375
7475
  const completeClaimedProjectDeletion = async (projectId, claimId) => {
7476
+ const marked = db.prepare(`
7477
+ UPDATE projects
7478
+ SET deletion_cleanup_started_at = COALESCE(deletion_cleanup_started_at, CURRENT_TIMESTAMP)
7479
+ WHERE id = ? AND deletion_claim_id = ?
7480
+ `).run(projectId, claimId);
7481
+ if (marked.changes !== 1) {
7482
+ throw new ProjectDeletionError(409, 'Project deletion ownership was lost');
7483
+ }
7484
+ await cleanupProjectExecutionEnvironments(projectId, claimId);
7376
7485
  await runExclusiveExecutionArtifactMaintenance(() => reconcileProjectExecutionArtifacts(projectId, {
7377
7486
  emitter: io,
7378
7487
  beginCleanup: () => {
7379
- const marked = db.prepare(`
7380
- UPDATE projects
7381
- SET deletion_cleanup_started_at = COALESCE(deletion_cleanup_started_at, CURRENT_TIMESTAMP)
7382
- WHERE id = ? AND deletion_claim_id = ?
7383
- `).run(projectId, claimId);
7384
- if (marked.changes !== 1) {
7488
+ const owned = db.prepare(`
7489
+ SELECT 1 FROM projects
7490
+ WHERE id = ? AND deletion_claim_id = ? AND deletion_cleanup_started_at IS NOT NULL
7491
+ `).get(projectId, claimId);
7492
+ if (!owned) {
7385
7493
  throw new ProjectDeletionError(409, 'Project deletion ownership was lost');
7386
7494
  }
7387
7495
  },
@@ -7426,6 +7534,7 @@ const completeClaimedProjectDeletion = async (projectId, claimId) => {
7426
7534
  SET parent_run_id = NULL, retry_of_run_id = NULL
7427
7535
  WHERE project_id = ?
7428
7536
  `).run(projectId);
7537
+ db.prepare("DELETE FROM execution_environments WHERE project_id = ? AND lifecycle_state = 'retired'").run(projectId);
7429
7538
  const deleted = db.prepare('DELETE FROM projects WHERE id = ? AND deletion_claim_id = ?').run(projectId, claimId);
7430
7539
  if (deleted.changes !== 1) {
7431
7540
  throw new ProjectDeletionError(409, 'Project deletion ownership was lost');
@@ -7477,6 +7586,9 @@ app.delete('/projects/:id', async (req, res) => {
7477
7586
  if (error instanceof ExecutionArtifactCleanupError) {
7478
7587
  return res.status(409).json({ error: error.message });
7479
7588
  }
7589
+ if (error instanceof ExecutionEnvironmentCleanupError) {
7590
+ return res.status(409).json({ error: error.message });
7591
+ }
7480
7592
  if (error instanceof ProjectDeletionError) {
7481
7593
  return res.status(error.status).json({ error: error.message });
7482
7594
  }
@@ -7543,7 +7655,7 @@ app.post('/tickets', (req, res) => {
7543
7655
  return res.status(500).json({ error: getErrorMessage(error) || 'Unable to create ticket' });
7544
7656
  }
7545
7657
  });
7546
- app.patch('/tickets/:id', (req, res) => {
7658
+ app.patch('/tickets/:id', async (req, res) => {
7547
7659
  const ticket = getTicket(req.params.id);
7548
7660
  if (!ticket) {
7549
7661
  return res.status(404).json({ error: 'Not found' });
@@ -7670,16 +7782,46 @@ app.patch('/tickets/:id', (req, res) => {
7670
7782
  return res.status(404).json({ error: 'Bundle not found' });
7671
7783
  }
7672
7784
  }
7785
+ const bundleChanged = previousBundleId !== resolvedBundleId || ticket.project_id !== nextProjectId;
7786
+ let previousBundleResourcesCleaned = false;
7787
+ if (bundleChanged && previousBundleId && previousProject) {
7788
+ const previousBundle = getBundle(previousBundleId, previousProject.id);
7789
+ if (previousBundle
7790
+ && !isProjectRootBundle(previousBundle)
7791
+ && countBundleTickets(previousBundle.id, previousProject.id) === 1) {
7792
+ try {
7793
+ await cleanupWorktreeExecutionTarget({
7794
+ project: previousProject,
7795
+ targetKind: 'bundle-worktree',
7796
+ targetKey: createExecutionTargetKey(previousProject.id, {
7797
+ kind: 'bundle-worktree',
7798
+ bundleId: previousBundle.id,
7799
+ branch: previousBundle.branch,
7800
+ }),
7801
+ targetLabel: 'bundle worktree',
7802
+ worktreeTarget: 'bundle',
7803
+ branch: previousBundle.branch,
7804
+ });
7805
+ previousBundleResourcesCleaned = true;
7806
+ }
7807
+ catch (error) {
7808
+ return res.status(error instanceof HttpError ? error.status : 500).json({
7809
+ error: error instanceof Error ? error.message : 'Unable to clean up the empty bundle',
7810
+ });
7811
+ }
7812
+ }
7813
+ }
7673
7814
  if (isProjectChanging) {
7674
7815
  deleteTicketDependenciesForTicket(ticket.id);
7675
7816
  }
7676
7817
  db.prepare('UPDATE tickets SET title = ?, description = ?, model = ?, variant = ?, skills_json = ?, worktree_bundle_id = ?, project_id = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?').run(title?.trim() ?? ticket.title, description ?? ticket.description, resolvedModel, resolvedVariant, JSON.stringify(resolvedSkills), resolvedBundleId, nextProjectId, req.params.id);
7677
7818
  upsertTicketDescriptionMessage(req.params.id, description ?? ticket.description);
7678
7819
  const updated = getTicket(req.params.id);
7679
- const bundleChanged = previousBundleId !== resolvedBundleId || ticket.project_id !== nextProjectId;
7680
7820
  if (bundleChanged && previousBundleId && previousProject) {
7681
7821
  try {
7682
- cleanupBundleIfEmpty(previousBundleId, previousProject);
7822
+ await cleanupBundleIfEmpty(previousBundleId, previousProject, {
7823
+ resourcesAlreadyCleaned: previousBundleResourcesCleaned,
7824
+ });
7683
7825
  }
7684
7826
  catch (error) {
7685
7827
  return res.status(error instanceof HttpError ? error.status : 500).json({
@@ -7911,6 +8053,10 @@ app.post('/tickets/:id/retry', (req, res) => {
7911
8053
  if (!acceptance.sequenceId) {
7912
8054
  throw new Error('Build sequence no longer exists');
7913
8055
  }
8056
+ const project = getEffectiveProject(acceptance.ticket);
8057
+ if (!project) {
8058
+ throw new Error('Build project no longer exists');
8059
+ }
7914
8060
  transaction.createExecutionRun({
7915
8061
  projectId: ticket.project_id,
7916
8062
  kind: 'build',
@@ -7920,6 +8066,9 @@ app.post('/tickets/:id/retry', (req, res) => {
7920
8066
  { entityType: 'bundle', entityId: ticket.worktree_bundle_id, relationship: 'context' },
7921
8067
  { entityType: 'build_sequence', entityId: acceptance.sequenceId, relationship: 'context' },
7922
8068
  ],
8069
+ metadata: {
8070
+ build_execution_provider: project.build_execution_provider ?? 'host',
8071
+ },
7923
8072
  initiatedBy: 'user',
7924
8073
  retryOfRunId: retrySource?.id ?? null,
7925
8074
  eventType: 'build_created',
@@ -9261,6 +9410,10 @@ export const runExecutionArtifactCleanup = (mode) => {
9261
9410
  if (mode === 'periodic') {
9262
9411
  await resumeInterruptedProjectDeletions();
9263
9412
  }
9413
+ const orphanedEnvironments = await reconcileOrphanedExecutionEnvironments();
9414
+ if (orphanedEnvironments.scanned > 0) {
9415
+ console.log(`Reconciled ${orphanedEnvironments.cleaned} orphaned execution environment(s); ${orphanedEnvironments.failed} cleanup(s) retained for retry`);
9416
+ }
9264
9417
  await runExclusiveExecutionArtifactMaintenance(async () => {
9265
9418
  const intents = await reconcileExecutionArtifactRegistrationIntents({ mode });
9266
9419
  const artifacts = await reconcileFinalizedExecutionArtifacts({ emitter: io });
@@ -9316,8 +9469,10 @@ export const startServer = ({ port = DEFAULT_PORT, host } = {}) => {
9316
9469
  }
9317
9470
  startServerPromise = (async () => {
9318
9471
  releaseInterruptedProjectDeletionClaims();
9319
- await resumeInterruptedProjectDeletions();
9472
+ await reconcileProductionExecutionEnvironmentBindings();
9473
+ await reconcileExecutionEnvironments();
9320
9474
  await reconcileStartupExecutions({ getGitHubConfig: getGitHubConnectionConfig });
9475
+ await resumeInterruptedProjectDeletions();
9321
9476
  const repositoryGraphSchemaExists = db.prepare(`
9322
9477
  SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'repository_graph_scans'
9323
9478
  `).get();