@straiffi/archon 1.8.15 → 1.8.16

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-p8gBUxYN.js +1 -0
  2. package/dist/client/assets/GitHubActionsDialog-BRxGcSyq.js +1 -0
  3. package/dist/client/assets/VerificationDialog-B6l5TYOS.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-DnuKO1RY.js} +1 -1
  7. package/dist/client/assets/{blockDiagram-GPEHLZMM-CSSJR8F_.js → blockDiagram-GPEHLZMM-CnQ7FwEp.js} +1 -1
  8. package/dist/client/assets/{c4Diagram-AAUBKEIU-C4F9969r.js → c4Diagram-AAUBKEIU-BF1hFXwp.js} +1 -1
  9. package/dist/client/assets/channel-TJtf4cBJ.js +1 -0
  10. package/dist/client/assets/{chunk-2J33WTMH-23HW6i-y.js → chunk-2J33WTMH-UvCFvkZO.js} +1 -1
  11. package/dist/client/assets/{chunk-3OPIFGDE-C3ZmRDMb.js → chunk-3OPIFGDE-z1l5nBD0.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-Bis0Pw2_.js} +1 -1
  17. package/dist/client/assets/{chunk-727SXJPM-5Dua6u1B.js → chunk-727SXJPM-B3gUO8rD.js} +1 -1
  18. package/dist/client/assets/{chunk-AQP2D5EJ-DcpcaFRO.js → chunk-AQP2D5EJ-hPBzrrh5.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-BfOng_VY.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-B2Jg7PW5.js} +1 -1
  25. package/dist/client/assets/{chunk-L5ZTLDWV-DNnzSNoX.js → chunk-L5ZTLDWV-CUYujZxt.js} +1 -1
  26. package/dist/client/assets/{chunk-LZXEDZCA-CSA3Xnoh.js → chunk-LZXEDZCA-CH_eV8jH.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-BVF5evM9.js} +1 -1
  31. package/dist/client/assets/{chunk-NZK2D7GU-DyIwcQYQ.js → chunk-NZK2D7GU-WfG-2AOS.js} +1 -1
  32. package/dist/client/assets/{chunk-O5CBEL6O-DfW60w5V.js → chunk-O5CBEL6O-CSF9ozqy.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-DoVpb2oe.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-CnKIRKWC.js +1 -0
  40. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-5ZgjUwTv.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-rekDIYnC.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-Z4mXdhW0.js} +1 -1
  45. package/dist/client/assets/{diagram-5GNKFQAL-Bc4-O-UJ.js → diagram-5GNKFQAL-DqeVe5Vc.js} +1 -1
  46. package/dist/client/assets/{diagram-KO2AKTUF-Ds1Lmrzm.js → diagram-KO2AKTUF-ijppTuNc.js} +1 -1
  47. package/dist/client/assets/{diagram-LMA3HP47-VFQabxKS.js → diagram-LMA3HP47-CDGLx0-N.js} +1 -1
  48. package/dist/client/assets/{diagram-OG6HWLK6-Baff59Zc.js → diagram-OG6HWLK6-DDlnjzF8.js} +1 -1
  49. package/dist/client/assets/{erDiagram-TEJ5UH35-Da5zHjOK.js → erDiagram-TEJ5UH35-V_axe9oz.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-GH5q79jg.js} +1 -1
  52. package/dist/client/assets/{ganttDiagram-6RSMTGT7-DjJGX0nm.js → ganttDiagram-6RSMTGT7-BMWVdLeQ.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-VrV8jMUg.js} +1 -1
  55. package/dist/client/assets/index-B7J4DYDu.js +275 -0
  56. package/dist/client/assets/index-CsZfTdMO.css +2 -0
  57. package/dist/client/assets/info-J43DQDTF-CfjXFZai.js +1 -0
  58. package/dist/client/assets/{infoDiagram-5YYISTIA-Bx78HhDi.js → infoDiagram-5YYISTIA-EGiVbp17.js} +1 -1
  59. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-BP0YQCMl.js → ishikawaDiagram-YF4QCWOH-Dw3CiXbm.js} +1 -1
  60. package/dist/client/assets/{journeyDiagram-JHISSGLW-DUgrCOuD.js → journeyDiagram-JHISSGLW-2S631RY5.js} +1 -1
  61. package/dist/client/assets/{kanban-definition-UN3LZRKU-K0Zc5_M8.js → kanban-definition-UN3LZRKU-pc9vzUtB.js} +1 -1
  62. package/dist/client/assets/{line-Dtg-krjk.js → line-BBMBmEjI.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-ClNeolx0.js} +2 -2
  65. package/dist/client/assets/{mermaid.core-BE-qlWNj.js → mermaid.core-Q4IJbm5V.js} +3 -3
  66. package/dist/client/assets/{mindmap-definition-RKZ34NQL-1Piz4H9I.js → mindmap-definition-RKZ34NQL-DUTZxR8H.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-RW5hNtJq.js} +1 -1
  71. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-DRQY8Adp.js → quadrantDiagram-W4KKPZXB-CK60c7X9.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-Dz35FvcC.js} +1 -1
  74. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-BaI0Njo8.js → sankeyDiagram-5OEKKPKP-ByyluoXu.js} +1 -1
  75. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-DjqAXbRF.js → sequenceDiagram-3UESZ5HK-C-879UFe.js} +1 -1
  76. package/dist/client/assets/{stateDiagram-AJRCARHV-Ovr_3LeO.js → stateDiagram-AJRCARHV-B8neDlUu.js} +1 -1
  77. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-B4Mq8Abu.js +1 -0
  78. package/dist/client/assets/{timeline-definition-PNZ67QCA-DkczYKeZ.js → timeline-definition-PNZ67QCA-iAwrltn1.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-DHN61m8d.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-XDVo7Jy1.js} +1 -1
  84. package/dist/client/assets/{xychartDiagram-2RQKCTM6-CyNzGK7f.js → xychartDiagram-2RQKCTM6-Cb-HLpaN.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 +517 -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 +631 -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 +354 -0
  123. package/dist/server/lib/executionEnvironmentRoutes.js.map +1 -0
  124. package/dist/server/lib/executionEnvironmentValidation.js +486 -0
  125. package/dist/server/lib/executionEnvironmentValidation.js.map +1 -0
  126. package/dist/server/lib/executionEnvironmentValidationOperations.js +635 -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 +114 -8
  183. package/dist/server/workers/build.js.map +1 -1
  184. package/dist/server/workers/followUp.js +118 -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
@@ -0,0 +1,726 @@
1
+ import { randomUUID } from 'crypto';
2
+ import db from '../db.js';
3
+ import { canonicalizeEnvironmentValidationFingerprintInput, createEnvironmentValidationFingerprint, } from './executionEnvironmentLifecycle.js';
4
+ const MAX_JSON_BYTES = 16 * 1024;
5
+ const MAX_EVENT_MESSAGE_LENGTH = 4_000;
6
+ const REUSABLE_STATES = ['ready', 'running', 'stopped'];
7
+ const CLEANUP_CLAIMABLE_STATES = ['retiring', 'cleanup_pending', 'failed', 'unavailable'];
8
+ const ALLOWED_TRANSITIONS = {
9
+ queued: new Set(['preparing', 'retiring', 'failed', 'unavailable']),
10
+ preparing: new Set(['ready', 'stopped', 'retiring', 'cleanup_pending', 'failed', 'unavailable']),
11
+ ready: new Set(['running', 'stopped', 'retiring', 'failed', 'unavailable']),
12
+ running: new Set(['ready', 'stopped', 'retiring', 'failed', 'unavailable']),
13
+ stopped: new Set(['preparing', 'ready', 'running', 'retiring', 'failed', 'unavailable']),
14
+ retiring: new Set(['cleanup_pending', 'retired', 'failed', 'unavailable']),
15
+ cleanup_pending: new Set(['retired', 'failed', 'unavailable']),
16
+ retired: new Set(),
17
+ failed: new Set(['preparing', 'retiring', 'cleanup_pending']),
18
+ unavailable: new Set(['preparing', 'retiring', 'cleanup_pending']),
19
+ };
20
+ const getEnvironmentStatement = db.prepare(`
21
+ SELECT * FROM execution_environments WHERE id = ? AND project_id = ?
22
+ `);
23
+ const projectExistsStatement = db.prepare(`
24
+ SELECT 1 FROM projects WHERE id = ? AND deletion_started_at IS NULL
25
+ `);
26
+ const insertEnvironmentStatement = db.prepare(`
27
+ INSERT INTO execution_environments (
28
+ id, project_id, provider_kind, provider_version, external_id, target_kind, target_key,
29
+ target_workspace_path, environment_revision, workspace_mode,
30
+ environment_validation_fingerprint, template_identity, template_digest, agent_identity,
31
+ compatible_agents_json, capabilities_json, lifecycle_state, created_at, updated_at
32
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'direct', ?, ?, ?, ?, ?, ?, 'queued', ?, ?)
33
+ `);
34
+ const updateEnvironmentStatement = db.prepare(`
35
+ UPDATE execution_environments
36
+ SET lifecycle_state = ?, external_id = ?, capabilities_json = ?, failure_code = ?,
37
+ failure_message = ?, failure_details_json = ?, preparation_started_at = ?, ready_at = ?,
38
+ last_validated_at = ?, last_used_at = ?, stopped_at = ?, retirement_started_at = ?,
39
+ cleanup_pending_at = ?, retired_at = ?, failed_at = ?, unavailable_at = ?, updated_at = ?
40
+ WHERE id = ? AND project_id = ? AND lifecycle_state = ?
41
+ `);
42
+ const insertEventStatement = db.prepare(`
43
+ INSERT INTO execution_environment_events (
44
+ id, project_id, environment_id, sequence, event_type, from_state, to_state, message,
45
+ metadata_schema_version, metadata_json, created_at
46
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
47
+ `);
48
+ const nextEventSequenceStatement = db.prepare(`
49
+ SELECT COALESCE(MAX(sequence), 0) + 1 AS sequence
50
+ FROM execution_environment_events WHERE environment_id = ?
51
+ `);
52
+ const getEventStatement = db.prepare(`
53
+ SELECT * FROM execution_environment_events WHERE id = ? AND project_id = ?
54
+ `);
55
+ const insertBindingStatement = db.prepare(`
56
+ INSERT INTO execution_environment_run_bindings (
57
+ environment_id, execution_run_id, project_id, relationship, environment_revision,
58
+ environment_validation_fingerprint, authority_json, bound_at
59
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
60
+ `);
61
+ const getBindingStatement = db.prepare(`
62
+ SELECT * FROM execution_environment_run_bindings
63
+ WHERE environment_id = ? AND execution_run_id = ? AND relationship = ?
64
+ `);
65
+ const releaseBindingStatement = db.prepare(`
66
+ UPDATE execution_environment_run_bindings
67
+ SET released_at = ?
68
+ WHERE environment_id = ? AND execution_run_id = ? AND project_id = ?
69
+ AND relationship = ? AND released_at IS NULL
70
+ `);
71
+ const capturePlacementStatement = db.prepare(`
72
+ UPDATE execution_environment_run_bindings
73
+ SET placement_json = ?
74
+ WHERE environment_id = ? AND execution_run_id = ? AND project_id = ?
75
+ AND relationship = 'execution' AND released_at IS NULL AND placement_json IS NULL
76
+ `);
77
+ const assertBoundedString = (value, name, maxBytes) => {
78
+ const bytes = Buffer.byteLength(value, 'utf8');
79
+ if (bytes < 1 || bytes > maxBytes) {
80
+ throw new Error(`${name} must contain between 1 and ${maxBytes} bytes`);
81
+ }
82
+ };
83
+ const serializeBoundedObject = (value, name) => {
84
+ let serialized;
85
+ try {
86
+ serialized = JSON.stringify(value);
87
+ }
88
+ catch {
89
+ throw new Error(`${name} must be JSON serializable`);
90
+ }
91
+ if (Buffer.byteLength(serialized, 'utf8') > MAX_JSON_BYTES) {
92
+ throw new Error(`${name} exceeds ${MAX_JSON_BYTES} bytes`);
93
+ }
94
+ return serialized;
95
+ };
96
+ const parseStoredObject = (value) => {
97
+ if (value === null || Buffer.byteLength(value, 'utf8') > MAX_JSON_BYTES) {
98
+ return null;
99
+ }
100
+ try {
101
+ const parsed = JSON.parse(value);
102
+ return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)
103
+ ? parsed
104
+ : null;
105
+ }
106
+ catch {
107
+ return null;
108
+ }
109
+ };
110
+ const createDefaultAuthority = (environment) => {
111
+ if (environment.provider_kind !== 'docker_sandbox' || !environment.agent_identity) {
112
+ throw new Error('Execution bindings require an explicit Docker Sandbox authority snapshot');
113
+ }
114
+ return {
115
+ schema_version: 1,
116
+ provider: 'docker_sandbox',
117
+ provider_version: environment.provider_version,
118
+ environment_id: environment.id,
119
+ environment_revision: environment.environment_revision,
120
+ environment_validation_fingerprint: environment.environment_validation_fingerprint,
121
+ target: {
122
+ kind: environment.target_kind,
123
+ key: environment.target_key,
124
+ workspace_path: environment.target_workspace_path,
125
+ },
126
+ capabilities: environment.capabilities,
127
+ degradation_reasons: environment.workspace_mode === 'direct' ? ['direct_workspace_writable'] : [],
128
+ agent: environment.agent_identity,
129
+ authentication_mode: 'validated_guest',
130
+ credential_delivery: 'sandbox_template',
131
+ };
132
+ };
133
+ const assertAuthorityMatchesEnvironment = (authority, environment) => {
134
+ if (authority.schema_version !== 1
135
+ || authority.provider !== 'docker_sandbox'
136
+ || authority.provider_version !== environment.provider_version
137
+ || authority.environment_id !== environment.id
138
+ || authority.environment_revision !== environment.environment_revision
139
+ || authority.environment_validation_fingerprint !== environment.environment_validation_fingerprint
140
+ || authority.target.kind !== environment.target_kind
141
+ || authority.target.key !== environment.target_key
142
+ || authority.target.workspace_path !== environment.target_workspace_path
143
+ || authority.agent !== environment.agent_identity
144
+ || authority.authentication_mode !== 'validated_guest'
145
+ || authority.credential_delivery !== 'sandbox_template') {
146
+ throw new Error('Execution environment authority does not match the durable environment identity');
147
+ }
148
+ };
149
+ const parseCompatibleAgents = (value) => {
150
+ if (Buffer.byteLength(value, 'utf8') > 100) {
151
+ return null;
152
+ }
153
+ try {
154
+ const parsed = JSON.parse(value);
155
+ if (!Array.isArray(parsed)
156
+ || parsed.length === 0
157
+ || parsed.some(agent => agent !== 'opencode' && agent !== 'claude')
158
+ || new Set(parsed).size !== parsed.length
159
+ || [...parsed].sort().some((agent, index) => agent !== parsed[index])) {
160
+ return null;
161
+ }
162
+ return parsed;
163
+ }
164
+ catch {
165
+ return null;
166
+ }
167
+ };
168
+ const serializeEnvironment = (row) => {
169
+ const compatibleAgents = parseCompatibleAgents(row.compatible_agents_json);
170
+ const capabilities = parseStoredObject(row.capabilities_json);
171
+ if (compatibleAgents === null
172
+ || capabilities === null
173
+ || !/^[0-9a-f]{64}$/.test(row.environment_validation_fingerprint)) {
174
+ return null;
175
+ }
176
+ return {
177
+ ...row,
178
+ compatible_agents: compatibleAgents,
179
+ capabilities,
180
+ failure_details: parseStoredObject(row.failure_details_json),
181
+ };
182
+ };
183
+ const serializeEvent = (row) => ({
184
+ ...row,
185
+ metadata: parseStoredObject(row.metadata_json),
186
+ });
187
+ const requireEnvironment = (projectId, environmentId) => {
188
+ const row = getEnvironmentStatement.get(environmentId, projectId);
189
+ if (!row) {
190
+ throw new Error(`Execution environment ${environmentId} was not found in project ${projectId}`);
191
+ }
192
+ const environment = serializeEnvironment(row);
193
+ if (!environment) {
194
+ throw new Error(`Execution environment ${environmentId} contains invalid durable identity data`);
195
+ }
196
+ return environment;
197
+ };
198
+ const appendEventInTransaction = (input) => {
199
+ assertBoundedString(input.eventType, 'Environment event type', 100);
200
+ const metadataSchemaVersion = input.metadataSchemaVersion ?? 1;
201
+ if (!Number.isSafeInteger(metadataSchemaVersion) || metadataSchemaVersion < 1) {
202
+ throw new Error('Environment event metadata schema version must be a positive integer');
203
+ }
204
+ const message = input.message ?? null;
205
+ if (message && message.length > MAX_EVENT_MESSAGE_LENGTH) {
206
+ throw new Error(`Environment event message exceeds ${MAX_EVENT_MESSAGE_LENGTH} characters`);
207
+ }
208
+ const id = randomUUID();
209
+ const sequence = nextEventSequenceStatement.get(input.environmentId).sequence;
210
+ insertEventStatement.run(id, input.projectId, input.environmentId, sequence, input.eventType, input.fromState, input.toState, message, metadataSchemaVersion, input.metadata ? serializeBoundedObject(input.metadata, 'Environment event metadata') : null, input.createdAt ?? new Date().toISOString());
211
+ return serializeEvent(getEventStatement.get(id, input.projectId));
212
+ };
213
+ const createEnvironmentInTransaction = (input) => {
214
+ if (!projectExistsStatement.get(input.projectId)) {
215
+ throw new Error(`Project ${input.projectId} was not found`);
216
+ }
217
+ const id = input.id ?? randomUUID();
218
+ assertBoundedString(id, 'Environment ID', 128);
219
+ assertBoundedString(input.providerKind, 'Provider kind', 100);
220
+ assertBoundedString(input.providerVersion, 'Provider version', 200);
221
+ assertBoundedString(input.target.kind, 'Target kind', 100);
222
+ assertBoundedString(input.target.key, 'Target key', 1_000);
223
+ assertBoundedString(input.target.workspacePath, 'Target workspace path', 4_096);
224
+ if (!Number.isSafeInteger(input.environmentRevision) || input.environmentRevision < 1) {
225
+ throw new Error('Environment revision must be a positive integer');
226
+ }
227
+ if (input.externalId) {
228
+ assertBoundedString(input.externalId, 'Provider external ID', 2_048);
229
+ }
230
+ const validation = canonicalizeEnvironmentValidationFingerprintInput(input.validation);
231
+ if (input.agentIdentity && !validation.compatibleAgents.includes(input.agentIdentity)) {
232
+ throw new Error('Agent identity must be included in the compatible agent set');
233
+ }
234
+ const capabilitiesJson = serializeBoundedObject(input.capabilities, 'Environment capabilities');
235
+ const createdAt = input.createdAt ?? new Date().toISOString();
236
+ insertEnvironmentStatement.run(id, input.projectId, input.providerKind, input.providerVersion, input.externalId ?? null, input.target.kind, input.target.key, input.target.workspacePath, input.environmentRevision, createEnvironmentValidationFingerprint(validation), validation.template.kind === 'identity' ? validation.template.value : null, validation.template.kind === 'digest' ? validation.template.value : null, input.agentIdentity ?? null, JSON.stringify(validation.compatibleAgents), capabilitiesJson, createdAt, createdAt);
237
+ const environment = requireEnvironment(input.projectId, id);
238
+ const event = appendEventInTransaction({
239
+ projectId: input.projectId,
240
+ environmentId: id,
241
+ eventType: 'environment_created',
242
+ fromState: null,
243
+ toState: 'queued',
244
+ createdAt,
245
+ });
246
+ return { environment, event };
247
+ };
248
+ const stateTimestampUpdates = (environment, state, occurredAt) => ({
249
+ preparationStartedAt: state === 'preparing' ? occurredAt : environment.preparation_started_at,
250
+ readyAt: state === 'ready' ? occurredAt : environment.ready_at,
251
+ lastValidatedAt: environment.last_validated_at,
252
+ lastUsedAt: state === 'running' ? occurredAt : environment.last_used_at,
253
+ stoppedAt: state === 'stopped' ? occurredAt : environment.stopped_at,
254
+ retirementStartedAt: state === 'retiring' ? occurredAt : environment.retirement_started_at,
255
+ cleanupPendingAt: state === 'cleanup_pending' ? occurredAt : environment.cleanup_pending_at,
256
+ retiredAt: state === 'retired' ? occurredAt : environment.retired_at,
257
+ failedAt: state === 'failed' ? occurredAt : environment.failed_at,
258
+ unavailableAt: state === 'unavailable' ? occurredAt : environment.unavailable_at,
259
+ });
260
+ const transitionEnvironmentInTransaction = (input) => {
261
+ const environment = requireEnvironment(input.projectId, input.environmentId);
262
+ if (environment.lifecycle_state === input.state) {
263
+ return { environment, event: null };
264
+ }
265
+ if (!ALLOWED_TRANSITIONS[environment.lifecycle_state].has(input.state)) {
266
+ throw new Error(`Invalid execution environment transition: ${environment.lifecycle_state} -> ${input.state}`);
267
+ }
268
+ if (environment.external_id && input.externalId !== undefined && input.externalId !== environment.external_id) {
269
+ throw new Error('Provider external ID cannot be replaced');
270
+ }
271
+ if (input.failure && input.state !== 'failed' && input.state !== 'unavailable' && input.state !== 'cleanup_pending') {
272
+ throw new Error('Environment failure evidence requires a failed, unavailable, or cleanup-pending state');
273
+ }
274
+ const failure = input.failure ?? null;
275
+ if (failure) {
276
+ assertBoundedString(failure.code, 'Environment failure code', 100);
277
+ if (failure.message.length > MAX_EVENT_MESSAGE_LENGTH) {
278
+ throw new Error(`Environment failure message exceeds ${MAX_EVENT_MESSAGE_LENGTH} characters`);
279
+ }
280
+ }
281
+ const occurredAt = input.occurredAt ?? new Date().toISOString();
282
+ const timestamps = stateTimestampUpdates(environment, input.state, occurredAt);
283
+ const capabilitiesJson = input.capabilities
284
+ ? serializeBoundedObject(input.capabilities, 'Environment capabilities')
285
+ : JSON.stringify(environment.capabilities);
286
+ const result = updateEnvironmentStatement.run(input.state, input.externalId === undefined ? environment.external_id : input.externalId, capabilitiesJson, failure?.code ?? null, failure?.message ?? null, failure?.details ? serializeBoundedObject(failure.details, 'Environment failure details') : null, timestamps.preparationStartedAt, timestamps.readyAt, timestamps.lastValidatedAt, timestamps.lastUsedAt, timestamps.stoppedAt, timestamps.retirementStartedAt, timestamps.cleanupPendingAt, timestamps.retiredAt, timestamps.failedAt, timestamps.unavailableAt, occurredAt, input.environmentId, input.projectId, environment.lifecycle_state);
287
+ if (result.changes !== 1) {
288
+ throw new Error(`Execution environment ${input.environmentId} changed concurrently`);
289
+ }
290
+ const updated = requireEnvironment(input.projectId, input.environmentId);
291
+ const event = appendEventInTransaction({
292
+ projectId: input.projectId,
293
+ environmentId: input.environmentId,
294
+ eventType: input.eventType ?? `environment_${input.state}`,
295
+ fromState: environment.lifecycle_state,
296
+ toState: input.state,
297
+ message: input.message ?? failure?.message,
298
+ metadata: input.metadata,
299
+ metadataSchemaVersion: input.metadataSchemaVersion,
300
+ createdAt: occurredAt,
301
+ });
302
+ return { environment: updated, event };
303
+ };
304
+ const bindRunInTransaction = (input) => {
305
+ const environment = requireEnvironment(input.projectId, input.environmentId);
306
+ const authority = input.relationship === 'execution'
307
+ ? input.authority ?? createDefaultAuthority(environment)
308
+ : null;
309
+ if (authority) {
310
+ assertAuthorityMatchesEnvironment(authority, environment);
311
+ }
312
+ const boundAt = input.boundAt ?? new Date().toISOString();
313
+ insertBindingStatement.run(input.environmentId, input.executionRunId, input.projectId, input.relationship, environment.environment_revision, environment.environment_validation_fingerprint, authority ? serializeBoundedObject(authority, 'Execution environment authority') : null, boundAt);
314
+ const binding = getBindingStatement.get(input.environmentId, input.executionRunId, input.relationship);
315
+ appendEventInTransaction({
316
+ projectId: input.projectId,
317
+ environmentId: input.environmentId,
318
+ eventType: 'execution_run_bound',
319
+ fromState: environment.lifecycle_state,
320
+ toState: environment.lifecycle_state,
321
+ metadata: { executionRunId: input.executionRunId, relationship: input.relationship },
322
+ createdAt: boundAt,
323
+ });
324
+ return binding;
325
+ };
326
+ const releaseRunInTransaction = (input) => {
327
+ const environment = requireEnvironment(input.projectId, input.environmentId);
328
+ const releasedAt = input.releasedAt ?? new Date().toISOString();
329
+ const result = releaseBindingStatement.run(releasedAt, input.environmentId, input.executionRunId, input.projectId, input.relationship);
330
+ if (result.changes !== 1) {
331
+ throw new Error('Execution environment run binding was not found or was already released');
332
+ }
333
+ const binding = getBindingStatement.get(input.environmentId, input.executionRunId, input.relationship);
334
+ appendEventInTransaction({
335
+ projectId: input.projectId,
336
+ environmentId: input.environmentId,
337
+ eventType: 'execution_run_released',
338
+ fromState: environment.lifecycle_state,
339
+ toState: environment.lifecycle_state,
340
+ metadata: { executionRunId: input.executionRunId, relationship: input.relationship },
341
+ createdAt: releasedAt,
342
+ });
343
+ return binding;
344
+ };
345
+ const isPromiseLike = (value) => {
346
+ if ((typeof value !== 'object' || value === null) && typeof value !== 'function') {
347
+ return false;
348
+ }
349
+ return typeof value.then === 'function';
350
+ };
351
+ export const withExecutionEnvironmentTransaction = (callback) => {
352
+ const transaction = db.transaction(() => {
353
+ const operations = {
354
+ createExecutionEnvironment: input => createEnvironmentInTransaction(input).environment,
355
+ transitionExecutionEnvironment: input => transitionEnvironmentInTransaction(input).environment,
356
+ bindExecutionRunToEnvironment: bindRunInTransaction,
357
+ releaseExecutionRunFromEnvironment: releaseRunInTransaction,
358
+ appendExecutionEnvironmentEvent: appendEventInTransaction,
359
+ };
360
+ const result = callback(operations);
361
+ if (isPromiseLike(result)) {
362
+ throw new Error('Execution environment transactions require a synchronous callback');
363
+ }
364
+ return result;
365
+ });
366
+ return transaction();
367
+ };
368
+ export const createExecutionEnvironment = (input) => (withExecutionEnvironmentTransaction(transaction => transaction.createExecutionEnvironment(input)));
369
+ export const transitionExecutionEnvironment = (input) => (withExecutionEnvironmentTransaction(transaction => transaction.transitionExecutionEnvironment(input)));
370
+ export const bindExecutionRunToEnvironment = (input) => (withExecutionEnvironmentTransaction(transaction => transaction.bindExecutionRunToEnvironment(input)));
371
+ export const acquireExecutionEnvironmentLease = (input) => {
372
+ try {
373
+ return db.transaction(() => {
374
+ const active = db.prepare(`
375
+ SELECT 1 FROM execution_environment_run_bindings
376
+ WHERE environment_id = ? AND released_at IS NULL
377
+ LIMIT 1
378
+ `).get(input.environmentId);
379
+ if (active) {
380
+ throw new Error(`Execution environment ${input.environmentId} is already leased`);
381
+ }
382
+ return bindRunInTransaction({
383
+ ...input,
384
+ relationship: 'execution',
385
+ });
386
+ }).immediate();
387
+ }
388
+ catch (error) {
389
+ if (error instanceof Error && error.message.includes('UNIQUE constraint failed')) {
390
+ throw new Error(`Execution environment ${input.environmentId} is already leased`);
391
+ }
392
+ throw error;
393
+ }
394
+ };
395
+ const placementMatchesBinding = (placement, binding) => (placement.provider === 'docker_sandbox'
396
+ && placement.environmentId === binding.environment_id
397
+ && placement.environmentRevision === binding.environment_revision
398
+ && placement.executionRunId === binding.execution_run_id
399
+ && placement.scope.kind === 'exact-sandbox');
400
+ export const captureExecutionEnvironmentPlacement = (input) => withExecutionEnvironmentTransaction(transaction => {
401
+ const binding = getBindingStatement.get(input.environmentId, input.executionRunId, 'execution');
402
+ if (!binding || binding.project_id !== input.projectId || binding.released_at !== null) {
403
+ throw new Error('Active execution environment lease was not found');
404
+ }
405
+ if (!placementMatchesBinding(input.placement, binding)) {
406
+ throw new Error('Execution placement does not match its durable lease');
407
+ }
408
+ if (binding.placement_json !== null) {
409
+ const existingPlacement = parseStoredObject(binding.placement_json);
410
+ if (existingPlacement && JSON.stringify(existingPlacement) === JSON.stringify(input.placement)) {
411
+ return binding;
412
+ }
413
+ throw new Error('Conflicting execution placement evidence was already captured');
414
+ }
415
+ const result = capturePlacementStatement.run(serializeBoundedObject(input.placement, 'Execution placement evidence'), input.environmentId, input.executionRunId, input.projectId);
416
+ if (result.changes !== 1) {
417
+ throw new Error('Execution placement evidence was already captured');
418
+ }
419
+ transaction.appendExecutionEnvironmentEvent({
420
+ projectId: input.projectId,
421
+ environmentId: input.environmentId,
422
+ eventType: 'execution_placement_captured',
423
+ fromState: requireEnvironment(input.projectId, input.environmentId).lifecycle_state,
424
+ toState: requireEnvironment(input.projectId, input.environmentId).lifecycle_state,
425
+ metadata: { executionRunId: input.executionRunId },
426
+ });
427
+ return getBindingStatement.get(input.environmentId, input.executionRunId, 'execution');
428
+ });
429
+ export const getExecutionEnvironmentRunBinding = (input) => getBindingStatement.get(input.environmentId, input.executionRunId, input.relationship) ?? null;
430
+ export const getExecutionEnvironmentBindingForRun = (projectId, executionRunId) => db.prepare(`
431
+ SELECT * FROM execution_environment_run_bindings
432
+ WHERE project_id = ? AND execution_run_id = ? AND relationship = 'execution'
433
+ ORDER BY bound_at DESC LIMIT 1
434
+ `).get(projectId, executionRunId) ?? null;
435
+ export const releaseExecutionEnvironmentLease = (input) => withExecutionEnvironmentTransaction(transaction => {
436
+ const binding = getExecutionEnvironmentRunBinding({
437
+ environmentId: input.environmentId,
438
+ executionRunId: input.executionRunId,
439
+ relationship: 'execution',
440
+ });
441
+ if (!binding || binding.project_id !== input.projectId || binding.released_at !== null) {
442
+ throw new Error('Active execution environment lease was not found');
443
+ }
444
+ if (input.proof.kind === 'terminal_local_execution') {
445
+ const run = db.prepare('SELECT status FROM execution_runs WHERE id = ? AND project_id = ?').get(input.executionRunId, input.projectId);
446
+ if (!run || ['queued', 'running', 'waiting'].includes(run.status)) {
447
+ throw new Error('Execution environment lease release requires a terminal execution run');
448
+ }
449
+ }
450
+ else if (input.proof.kind === 'placement_terminated') {
451
+ const placement = parseStoredObject(binding.placement_json);
452
+ if (!placement
453
+ || !placementMatchesBinding(placement, binding)
454
+ || input.proof.inspection.state === 'running'
455
+ || JSON.stringify(input.proof.inspection.identity) !== JSON.stringify(placement)) {
456
+ throw new Error('Execution environment lease release requires exact terminated placement proof');
457
+ }
458
+ }
459
+ else {
460
+ const environment = requireEnvironment(input.projectId, input.environmentId);
461
+ if (!environment.external_id || environment.external_id !== input.proof.externalId) {
462
+ throw new Error('Execution environment lease release requires the exact stopped sandbox identity');
463
+ }
464
+ }
465
+ if (input.cleanupCompletion) {
466
+ const environment = requireEnvironment(input.projectId, input.environmentId);
467
+ const targetState = input.cleanupCompletion.state === 'stopped' ? 'stopped' : 'unavailable';
468
+ if (environment.lifecycle_state === targetState
469
+ || ['failed', 'unavailable', 'retired'].includes(environment.lifecycle_state)) {
470
+ transaction.appendExecutionEnvironmentEvent({
471
+ projectId: input.projectId,
472
+ environmentId: input.environmentId,
473
+ eventType: input.cleanupCompletion.eventType,
474
+ fromState: environment.lifecycle_state,
475
+ toState: environment.lifecycle_state,
476
+ message: input.cleanupCompletion.message,
477
+ metadata: input.cleanupCompletion.metadata,
478
+ });
479
+ }
480
+ else {
481
+ transaction.transitionExecutionEnvironment({
482
+ projectId: input.projectId,
483
+ environmentId: input.environmentId,
484
+ state: targetState,
485
+ eventType: input.cleanupCompletion.eventType,
486
+ message: input.cleanupCompletion.message,
487
+ metadata: input.cleanupCompletion.metadata,
488
+ failure: input.cleanupCompletion.failure,
489
+ });
490
+ }
491
+ }
492
+ return releaseRunInTransaction({
493
+ projectId: input.projectId,
494
+ environmentId: input.environmentId,
495
+ executionRunId: input.executionRunId,
496
+ relationship: 'execution',
497
+ releasedAt: input.releasedAt,
498
+ });
499
+ });
500
+ export const releaseExecutionRunFromEnvironment = (input) => (withExecutionEnvironmentTransaction(transaction => transaction.releaseExecutionRunFromEnvironment(input)));
501
+ export const appendExecutionEnvironmentEvent = (input) => (withExecutionEnvironmentTransaction(transaction => transaction.appendExecutionEnvironmentEvent(input)));
502
+ export const retireExecutionEnvironment = (input) => transitionExecutionEnvironment({ ...input, state: 'retiring', eventType: input.eventType ?? 'environment_retirement_started' });
503
+ export const getExecutionEnvironment = (projectId, environmentId) => {
504
+ const row = getEnvironmentStatement.get(environmentId, projectId);
505
+ return row ? serializeEnvironment(row) : null;
506
+ };
507
+ export const listExecutionEnvironments = (projectId) => {
508
+ const rows = db.prepare(`
509
+ SELECT * FROM execution_environments
510
+ WHERE project_id = ?
511
+ ORDER BY environment_revision DESC, created_at DESC, id DESC
512
+ `).all(projectId);
513
+ return rows.flatMap(row => {
514
+ const environment = serializeEnvironment(row);
515
+ return environment ? [environment] : [];
516
+ });
517
+ };
518
+ export const listExecutionEnvironmentEvents = (projectId, environmentId) => (db.prepare(`
519
+ SELECT * FROM execution_environment_events
520
+ WHERE project_id = ? AND environment_id = ?
521
+ ORDER BY sequence ASC
522
+ `).all(projectId, environmentId).map(row => serializeEvent(row)));
523
+ export const findReusableExecutionEnvironment = (input) => {
524
+ const validation = canonicalizeEnvironmentValidationFingerprintInput(input.validation);
525
+ if (input.agentIdentity && !validation.compatibleAgents.includes(input.agentIdentity)) {
526
+ return null;
527
+ }
528
+ const rows = db.prepare(`
529
+ SELECT * FROM execution_environments
530
+ WHERE project_id = ?
531
+ AND provider_kind = ?
532
+ AND provider_version = ?
533
+ AND target_kind = ?
534
+ AND target_key = ?
535
+ AND target_workspace_path = ?
536
+ AND environment_revision = ?
537
+ AND workspace_mode = 'direct'
538
+ AND environment_validation_fingerprint = ?
539
+ AND template_identity IS ?
540
+ AND template_digest IS ?
541
+ AND compatible_agents_json = ?
542
+ AND (agent_identity IS NULL OR agent_identity IS ?)
543
+ AND lifecycle_state IN (${REUSABLE_STATES.map(() => '?').join(', ')})
544
+ ORDER BY last_validated_at DESC, created_at DESC, id DESC
545
+ `).all(input.projectId, input.providerKind, input.providerVersion, input.target.kind, input.target.key, input.target.workspacePath, input.environmentRevision, createEnvironmentValidationFingerprint(validation), validation.template.kind === 'identity' ? validation.template.value : null, validation.template.kind === 'digest' ? validation.template.value : null, JSON.stringify(validation.compatibleAgents), input.agentIdentity ?? null, ...REUSABLE_STATES);
546
+ for (const row of rows) {
547
+ const environment = serializeEnvironment(row);
548
+ if (environment) {
549
+ return environment;
550
+ }
551
+ }
552
+ return null;
553
+ };
554
+ export const claimExecutionEnvironmentPreparation = (input) => {
555
+ const transaction = db.transaction(() => {
556
+ const validation = canonicalizeEnvironmentValidationFingerprintInput(input.validation);
557
+ if (input.agentIdentity && !validation.compatibleAgents.includes(input.agentIdentity)) {
558
+ throw new Error('Agent identity must be included in the compatible agent set');
559
+ }
560
+ const fingerprint = createEnvironmentValidationFingerprint(validation);
561
+ const identityValues = [
562
+ input.projectId,
563
+ input.providerKind,
564
+ input.providerVersion,
565
+ input.target.kind,
566
+ input.target.key,
567
+ input.target.workspacePath,
568
+ fingerprint,
569
+ validation.template.kind === 'identity' ? validation.template.value : null,
570
+ validation.template.kind === 'digest' ? validation.template.value : null,
571
+ JSON.stringify(validation.compatibleAgents),
572
+ input.agentIdentity ?? null,
573
+ ];
574
+ const exactRows = db.prepare(`
575
+ SELECT * FROM execution_environments
576
+ WHERE project_id = ? AND provider_kind = ? AND provider_version = ?
577
+ AND target_kind = ? AND target_key = ? AND target_workspace_path = ?
578
+ AND workspace_mode = 'direct' AND environment_validation_fingerprint = ?
579
+ AND template_identity IS ? AND template_digest IS ?
580
+ AND compatible_agents_json = ? AND agent_identity IS ?
581
+ ORDER BY environment_revision DESC, created_at DESC, id DESC
582
+ `).all(...identityValues);
583
+ for (const row of exactRows) {
584
+ const environment = serializeEnvironment(row);
585
+ if (!environment) {
586
+ continue;
587
+ }
588
+ if (REUSABLE_STATES.includes(environment.lifecycle_state)) {
589
+ return { kind: 'reusable', environment };
590
+ }
591
+ if (environment.lifecycle_state === 'queued' || environment.lifecycle_state === 'preparing') {
592
+ return { kind: 'preparing', environment };
593
+ }
594
+ }
595
+ const revision = db.prepare(`
596
+ SELECT COALESCE(MAX(environment_revision), 0) + 1 AS revision
597
+ FROM execution_environments
598
+ WHERE project_id = ? AND provider_kind = ? AND target_kind = ? AND target_key = ?
599
+ `).get(input.projectId, input.providerKind, input.target.kind, input.target.key).revision;
600
+ const externalId = input.resolveExternalId(revision, fingerprint);
601
+ const environment = createEnvironmentInTransaction({
602
+ ...input,
603
+ environmentRevision: revision,
604
+ externalId,
605
+ }).environment;
606
+ const preparing = transitionEnvironmentInTransaction({
607
+ projectId: input.projectId,
608
+ environmentId: environment.id,
609
+ state: 'preparing',
610
+ eventType: 'environment_preparing',
611
+ }).environment;
612
+ return { kind: 'created', environment: preparing };
613
+ });
614
+ return transaction.immediate();
615
+ };
616
+ export const markExecutionEnvironmentValidated = (input) => withExecutionEnvironmentTransaction(() => {
617
+ const environment = requireEnvironment(input.projectId, input.environmentId);
618
+ const validatedAt = input.validatedAt ?? new Date().toISOString();
619
+ const result = db.prepare(`
620
+ UPDATE execution_environments
621
+ SET last_validated_at = ?, updated_at = ?
622
+ WHERE id = ? AND project_id = ?
623
+ `).run(validatedAt, validatedAt, input.environmentId, input.projectId);
624
+ if (result.changes !== 1) {
625
+ throw new Error(`Execution environment ${input.environmentId} changed concurrently`);
626
+ }
627
+ return { ...environment, last_validated_at: validatedAt, updated_at: validatedAt };
628
+ });
629
+ export const claimExecutionEnvironmentsForCleanup = (input = {}) => withExecutionEnvironmentTransaction(transaction => {
630
+ const limit = input.limit ?? 50;
631
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > 200) {
632
+ throw new Error('Environment cleanup claim limit must be between 1 and 200');
633
+ }
634
+ const claimId = input.claimId ?? randomUUID();
635
+ assertBoundedString(claimId, 'Environment cleanup claim ID', 128);
636
+ const claimedAt = input.claimedAt ?? new Date().toISOString();
637
+ const rows = db.prepare(`
638
+ SELECT * FROM execution_environments
639
+ WHERE lifecycle_state IN (${CLEANUP_CLAIMABLE_STATES.map(() => '?').join(', ')})
640
+ AND (? IS NULL OR project_id = ?)
641
+ AND (? IS NULL OR target_kind = ?)
642
+ AND (? IS NULL OR target_key = ?)
643
+ AND (cleanup_claim_id IS NULL OR (? IS NOT NULL AND cleanup_claimed_at < ?))
644
+ ORDER BY updated_at ASC, id ASC
645
+ LIMIT ?
646
+ `).all(...CLEANUP_CLAIMABLE_STATES, input.projectId ?? null, input.projectId ?? null, input.targetKind ?? null, input.targetKind ?? null, input.targetKey ?? null, input.targetKey ?? null, input.staleBefore ?? null, input.staleBefore ?? null, limit);
647
+ const claimed = [];
648
+ for (const row of rows) {
649
+ const result = db.prepare(`
650
+ UPDATE execution_environments
651
+ SET lifecycle_state = 'cleanup_pending', cleanup_claim_id = ?, cleanup_claimed_at = ?,
652
+ cleanup_attempts = cleanup_attempts + 1,
653
+ cleanup_pending_at = COALESCE(cleanup_pending_at, ?), updated_at = ?
654
+ WHERE id = ? AND project_id = ?
655
+ AND lifecycle_state = ?
656
+ AND (cleanup_claim_id IS NULL OR (? IS NOT NULL AND cleanup_claimed_at < ?))
657
+ `).run(claimId, claimedAt, claimedAt, claimedAt, row.id, row.project_id, row.lifecycle_state, input.staleBefore ?? null, input.staleBefore ?? null);
658
+ if (result.changes !== 1) {
659
+ continue;
660
+ }
661
+ transaction.appendExecutionEnvironmentEvent({
662
+ projectId: row.project_id,
663
+ environmentId: row.id,
664
+ eventType: 'environment_cleanup_claimed',
665
+ fromState: row.lifecycle_state,
666
+ toState: 'cleanup_pending',
667
+ metadata: { claimId },
668
+ createdAt: claimedAt,
669
+ });
670
+ claimed.push(requireEnvironment(row.project_id, row.id));
671
+ }
672
+ return claimed;
673
+ });
674
+ export const completeExecutionEnvironmentCleanup = (input) => withExecutionEnvironmentTransaction(transaction => {
675
+ const environment = requireEnvironment(input.projectId, input.environmentId);
676
+ if (environment.lifecycle_state !== 'cleanup_pending' || environment.cleanup_claim_id !== input.claimId) {
677
+ throw new Error(`Execution environment ${input.environmentId} cleanup claim is not owned by ${input.claimId}`);
678
+ }
679
+ const completedAt = input.completedAt ?? new Date().toISOString();
680
+ const result = db.prepare(`
681
+ UPDATE execution_environments
682
+ SET lifecycle_state = 'retired', cleanup_claim_id = NULL, cleanup_claimed_at = NULL,
683
+ retired_at = ?, updated_at = ?
684
+ WHERE id = ? AND project_id = ? AND lifecycle_state = 'cleanup_pending' AND cleanup_claim_id = ?
685
+ `).run(completedAt, completedAt, input.environmentId, input.projectId, input.claimId);
686
+ if (result.changes !== 1) {
687
+ throw new Error(`Execution environment ${input.environmentId} cleanup claim was lost`);
688
+ }
689
+ transaction.appendExecutionEnvironmentEvent({
690
+ projectId: input.projectId,
691
+ environmentId: input.environmentId,
692
+ eventType: 'environment_cleanup_completed',
693
+ fromState: 'cleanup_pending',
694
+ toState: 'retired',
695
+ createdAt: completedAt,
696
+ });
697
+ return requireEnvironment(input.projectId, input.environmentId);
698
+ });
699
+ export const failExecutionEnvironmentCleanup = (input) => withExecutionEnvironmentTransaction(transaction => {
700
+ assertBoundedString(input.failure.code, 'Environment cleanup failure code', 100);
701
+ if (input.failure.message.length > MAX_EVENT_MESSAGE_LENGTH) {
702
+ throw new Error(`Environment cleanup failure message exceeds ${MAX_EVENT_MESSAGE_LENGTH} characters`);
703
+ }
704
+ const failedAt = input.failedAt ?? new Date().toISOString();
705
+ const result = db.prepare(`
706
+ UPDATE execution_environments
707
+ SET cleanup_claim_id = NULL, cleanup_claimed_at = NULL, failure_code = ?, failure_message = ?,
708
+ failure_details_json = ?, updated_at = ?
709
+ WHERE id = ? AND project_id = ? AND lifecycle_state = 'cleanup_pending' AND cleanup_claim_id = ?
710
+ `).run(input.failure.code, input.failure.message, input.failure.details ? serializeBoundedObject(input.failure.details, 'Environment cleanup failure details') : null, failedAt, input.environmentId, input.projectId, input.claimId);
711
+ if (result.changes !== 1) {
712
+ throw new Error(`Execution environment ${input.environmentId} cleanup claim was lost`);
713
+ }
714
+ transaction.appendExecutionEnvironmentEvent({
715
+ projectId: input.projectId,
716
+ environmentId: input.environmentId,
717
+ eventType: 'environment_cleanup_failed',
718
+ fromState: 'cleanup_pending',
719
+ toState: 'cleanup_pending',
720
+ message: input.failure.message,
721
+ metadata: { failureCode: input.failure.code },
722
+ createdAt: failedAt,
723
+ });
724
+ return requireEnvironment(input.projectId, input.environmentId);
725
+ });
726
+ //# sourceMappingURL=executionEnvironments.js.map