specrails-desktop 2.12.0 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (401) hide show
  1. package/README.md +3 -2
  2. package/client/dist/assets/ActivityFeedPage-D0-P3PsD.js +1 -0
  3. package/client/dist/assets/AgentsPage-BZa3lhQM.js +86 -0
  4. package/client/dist/assets/AnalyticsPage-CgaEg-lx.js +1 -0
  5. package/client/dist/assets/BarChart-D9vfeilz.js +33 -0
  6. package/client/dist/assets/CodePage-2GUsF_JS.js +2 -0
  7. package/client/dist/assets/DesktopAnalyticsPage-C6Qccm5E.js +1 -0
  8. package/client/dist/assets/DocsDialog-BAmtk9Jc.js +11 -0
  9. package/client/dist/assets/DocsPage-CJwnpB57.js +11 -0
  10. package/client/dist/assets/{ExportDropdown-I_8R48Yq.js → ExportDropdown-DMJ__Yui.js} +1 -1
  11. package/client/dist/assets/IntegrationsPage-CP2VcOpr.js +3 -0
  12. package/client/dist/assets/JobDetailPage-Bk6tQTew.js +16 -0
  13. package/client/dist/assets/JobsPage-BvbeqWjS.js +1 -0
  14. package/client/dist/assets/LoopBuilderPage-BS3enu9N.js +7 -0
  15. package/client/dist/assets/LoopBuilderPage-DtxX27Jz.css +1 -0
  16. package/client/dist/assets/LoopsPage-B0pgvoky.js +2 -0
  17. package/client/dist/assets/analytics-BHApBnUn.js +1 -0
  18. package/client/dist/assets/analytics-BUxrug9J.js +1 -0
  19. package/client/dist/assets/analytics-Cy6DTfO-.js +1 -0
  20. package/client/dist/assets/analytics-DMJxD9FD.js +1 -0
  21. package/client/dist/assets/analytics-DgrBuxla.js +1 -0
  22. package/client/dist/assets/analytics-DwxT1CS8.js +1 -0
  23. package/client/dist/assets/analytics-GrLc-SMr.js +1 -0
  24. package/client/dist/assets/analytics-qvPq4T4h.js +1 -0
  25. package/client/dist/assets/atom-one-dark-DrWoEEbZ.js +1 -0
  26. package/client/dist/assets/auth-Cvsb7-Ek.js +1 -0
  27. package/client/dist/assets/{cssMode-DzNPAYFh.js → cssMode-DSHXamUH.js} +1 -1
  28. package/client/dist/assets/dashboard-8FSyxKGV.js +1 -0
  29. package/client/dist/assets/dashboard-A9XQUfdS.js +1 -0
  30. package/client/dist/assets/dashboard-BFktSDsV.js +1 -0
  31. package/client/dist/assets/dashboard-C1PKCXLs.js +1 -0
  32. package/client/dist/assets/dashboard-DNf0czU-.js +1 -0
  33. package/client/dist/assets/dashboard-DQQCOVfu.js +1 -0
  34. package/client/dist/assets/dashboard-DxFqeHZk.js +1 -0
  35. package/client/dist/assets/dashboard-WvPGhbbi.js +1 -0
  36. package/client/dist/assets/{dist-js-DXfIbU9U.js → dist-js-_oLAsZQ4.js} +1 -1
  37. package/client/dist/assets/{dist-js-CtdZvl0R.js → dist-js-o4iwbCuN.js} +1 -1
  38. package/client/dist/assets/{editor.main-C7Rmw-hR.js → editor.main-Cd8qBRAT.js} +2 -2
  39. package/client/dist/assets/{freemarker2-Cszs4SVo.js → freemarker2-CkEKTBm2.js} +1 -1
  40. package/client/dist/assets/{handlebars-Dp7Lsuym.js → handlebars-BRGx0JpL.js} +1 -1
  41. package/client/dist/assets/{html-BURidrEm.js → html-BfRIaGgN.js} +1 -1
  42. package/client/dist/assets/{htmlMode--k5M7GjZ.js → htmlMode-BTpSnsDE.js} +1 -1
  43. package/client/dist/assets/index-DrFJinsI.css +2 -0
  44. package/client/dist/assets/index-QEeqwz4v.js +143 -0
  45. package/client/dist/assets/{javascript-kJQz__44.js → javascript-DMmE2kKN.js} +1 -1
  46. package/client/dist/assets/{jsonMode-v5JYPpnz.js → jsonMode-hYuaUleY.js} +1 -1
  47. package/client/dist/assets/{lib-BBoUS-5a.js → lib-CVrpo1ac.js} +2 -2
  48. package/client/dist/assets/{liquid-Dl9I6gWt.js → liquid-C-VbuTr7.js} +1 -1
  49. package/client/dist/assets/loops-B1ZQCKEk.js +1 -0
  50. package/client/dist/assets/loops-Bp5yTSic.js +1 -0
  51. package/client/dist/assets/loops-BvoL_LAc.js +1 -0
  52. package/client/dist/assets/loops-BwgehdXY.js +1 -0
  53. package/client/dist/assets/loops-CMLUqUpc.js +1 -0
  54. package/client/dist/assets/loops-CW69VvTI.js +1 -0
  55. package/client/dist/assets/loops-DkV2RhEb.js +1 -0
  56. package/client/dist/assets/loops-arQ602Zm.js +1 -0
  57. package/client/dist/assets/{lspLanguageFeatures-CPlEe0NK.js → lspLanguageFeatures-BALx-vjn.js} +1 -1
  58. package/client/dist/assets/{mdx-Byl7TtzQ.js → mdx-Cs8MNCzn.js} +1 -1
  59. package/client/dist/assets/{monaco.contribution-YMAkHQcQ.js → monaco.contribution-CIvCAJs4.js} +2 -2
  60. package/client/dist/assets/nav-B5W9IMnf.js +1 -0
  61. package/client/dist/assets/nav-CCz4krPP.js +1 -0
  62. package/client/dist/assets/nav-CrG_mYRr.js +1 -0
  63. package/client/dist/assets/nav-Cw3_40fA.js +1 -0
  64. package/client/dist/assets/nav-DNDJ3H-_.js +1 -0
  65. package/client/dist/assets/nav-DVp1nEsi.js +1 -0
  66. package/client/dist/assets/nav-DbAbG5_k.js +1 -0
  67. package/client/dist/assets/nav-_0vAgZ_k.js +1 -0
  68. package/client/dist/assets/{python-jWQwT6j2.js → python-CGShLxuI.js} +1 -1
  69. package/client/dist/assets/{razor-BWS3sP-E.js → razor-CvOBQGd5.js} +1 -1
  70. package/client/dist/assets/{setup-Cpu17hJv.js → setup-BOmQ4vh2.js} +1 -1
  71. package/client/dist/assets/setup-BwO2-kuK.js +1 -0
  72. package/client/dist/assets/{setup-D-1r0uSx.js → setup-CUTNWz-0.js} +1 -1
  73. package/client/dist/assets/{setup-BHroXlke.js → setup-Cf3hjChU.js} +1 -1
  74. package/client/dist/assets/{setup-BIXsWUp1.js → setup-CsuXrbJ2.js} +1 -1
  75. package/client/dist/assets/{setup-B6egeeTM.js → setup-Df4cJOqu.js} +1 -1
  76. package/client/dist/assets/{setup-C0rVGnCy.js → setup-Do0fWx6W.js} +1 -1
  77. package/client/dist/assets/{setup-BJRdg1iE.js → setup-GlkDhBlJ.js} +1 -1
  78. package/client/dist/assets/{tsMode-BbOGOuSV.js → tsMode-04uOpiiP.js} +1 -1
  79. package/client/dist/assets/{typescript-eBtFQJLs.js → typescript-CX0CwyIZ.js} +1 -1
  80. package/client/dist/assets/upload-Dn9N1ESQ.js +1 -0
  81. package/client/dist/assets/value-Bd3Cf9Sr.js +1 -0
  82. package/client/dist/assets/{workers-BvicOoDf.js → workers-yr2u4c3C.js} +1 -1
  83. package/client/dist/assets/{xml-BJepAPyM.js → xml-C0OFlt5X.js} +1 -1
  84. package/client/dist/assets/{yaml-DabgV-eA.js → yaml-DgabaPs5.js} +1 -1
  85. package/client/dist/index.html +28 -26
  86. package/docs/getting-started.md +3 -2
  87. package/docs/guide/de/pipeline/1-rails-and-jobs.md +27 -22
  88. package/docs/guide/de/pipeline/5-the-loop-builder.md +91 -0
  89. package/docs/guide/en/pipeline/1-rails-and-jobs.md +11 -6
  90. package/docs/guide/en/pipeline/5-the-loop-builder.md +91 -0
  91. package/docs/guide/es/pipeline/1-rails-and-jobs.md +11 -6
  92. package/docs/guide/es/pipeline/5-the-loop-builder.md +91 -0
  93. package/docs/guide/fr/pipeline/1-rails-and-jobs.md +40 -35
  94. package/docs/guide/fr/pipeline/5-the-loop-builder.md +91 -0
  95. package/docs/guide/it/pipeline/1-rails-and-jobs.md +11 -6
  96. package/docs/guide/it/pipeline/5-the-loop-builder.md +91 -0
  97. package/docs/guide/ja/pipeline/1-rails-and-jobs.md +11 -6
  98. package/docs/guide/ja/pipeline/5-the-loop-builder.md +91 -0
  99. package/docs/guide/pt/pipeline/1-rails-and-jobs.md +50 -45
  100. package/docs/guide/pt/pipeline/5-the-loop-builder.md +91 -0
  101. package/docs/guide/zh/pipeline/1-rails-and-jobs.md +13 -8
  102. package/docs/guide/zh/pipeline/5-the-loop-builder.md +91 -0
  103. package/docs/internals/companion-rails-as-loops-contract.md +199 -0
  104. package/docs/internals/global-artifacts-alignment-contract.md +2 -0
  105. package/docs/running-pipelines.md +25 -14
  106. package/package.json +1 -1
  107. package/server/dist/ai-invocations.js +5 -3
  108. package/server/dist/db.js +45 -0
  109. package/server/dist/desktop-db.js +48 -0
  110. package/server/dist/desktop-router.js +10 -0
  111. package/server/dist/feature-flags.js +13 -0
  112. package/server/dist/loop-command-catalog.js +145 -0
  113. package/server/dist/loop-constants.js +82 -0
  114. package/server/dist/loop-decider.js +66 -0
  115. package/server/dist/loop-executors.js +193 -0
  116. package/server/dist/loop-factory.js +56 -0
  117. package/server/dist/loop-graph.js +144 -0
  118. package/server/dist/loop-preview.js +69 -0
  119. package/server/dist/loop-run-manager.js +407 -0
  120. package/server/dist/loop-runs-store.js +69 -0
  121. package/server/dist/loop-templates.js +137 -0
  122. package/server/dist/loops-router.js +302 -0
  123. package/server/dist/loops-store.js +137 -0
  124. package/server/dist/mobile/mobile-router.js +22 -0
  125. package/server/dist/mobile/mobile-ws.js +17 -1
  126. package/server/dist/project-registry.js +103 -1
  127. package/server/dist/project-router-jobs.js +11 -1
  128. package/server/dist/project-router-loop-runs.js +102 -0
  129. package/server/dist/project-router.js +2 -0
  130. package/server/dist/providers/claude-adapter.js +4 -0
  131. package/server/dist/providers/codex-adapter.js +21 -0
  132. package/server/dist/providers/gemini-adapter.js +4 -0
  133. package/server/dist/rails-router.js +175 -6
  134. package/server/dist/spending.js +5 -2
  135. package/server/dist/util/cli-prompt.js +6 -3
  136. package/client/dist/assets/ActivityFeedPage-B9-UAGS7.js +0 -1
  137. package/client/dist/assets/AgentsPage-Bz9rophT.js +0 -86
  138. package/client/dist/assets/AnalyticsPage-D3ynQwGd.js +0 -1
  139. package/client/dist/assets/BarChart-Cwp6VuED.js +0 -33
  140. package/client/dist/assets/CodePage-CO_TLOvi.js +0 -2
  141. package/client/dist/assets/DesktopAnalyticsPage-BMDqiAbw.js +0 -1
  142. package/client/dist/assets/DocsDialog-B-chJn-f.js +0 -11
  143. package/client/dist/assets/DocsPage-BcJwYo20.js +0 -11
  144. package/client/dist/assets/IntegrationsPage-Bd4l3L6u.js +0 -3
  145. package/client/dist/assets/JobDetailPage-Dt3jLjS4.js +0 -16
  146. package/client/dist/assets/JobsPage-DcWpJbBc.js +0 -1
  147. package/client/dist/assets/analytics-BeTyviO8.js +0 -1
  148. package/client/dist/assets/analytics-C4eEO260.js +0 -1
  149. package/client/dist/assets/analytics-C67cIA1b.js +0 -1
  150. package/client/dist/assets/analytics-CAguvW28.js +0 -1
  151. package/client/dist/assets/analytics-DBtt8Mgk.js +0 -1
  152. package/client/dist/assets/analytics-DMCto-TF.js +0 -1
  153. package/client/dist/assets/analytics-DUPtODxX.js +0 -1
  154. package/client/dist/assets/analytics-YIpQvPAc.js +0 -1
  155. package/client/dist/assets/dashboard--Ahnvfr3.js +0 -1
  156. package/client/dist/assets/dashboard-BN1C2pEh.js +0 -1
  157. package/client/dist/assets/dashboard-BZs_EzAn.js +0 -1
  158. package/client/dist/assets/dashboard-Bsw44L8_.js +0 -1
  159. package/client/dist/assets/dashboard-Bw3VECgY.js +0 -1
  160. package/client/dist/assets/dashboard-CuOshSHn.js +0 -1
  161. package/client/dist/assets/dashboard-DfouCM3_.js +0 -1
  162. package/client/dist/assets/dashboard-Pp5hwnZB.js +0 -1
  163. package/client/dist/assets/index-BEGU2TCx.js +0 -143
  164. package/client/dist/assets/index-B_Zqeoa5.css +0 -2
  165. package/client/dist/assets/nav-B05EYB0b.js +0 -1
  166. package/client/dist/assets/nav-BNGCq-0y.js +0 -1
  167. package/client/dist/assets/nav-BRInPX8a.js +0 -1
  168. package/client/dist/assets/nav-Bf87DRHD.js +0 -1
  169. package/client/dist/assets/nav-BkVzzFpc.js +0 -1
  170. package/client/dist/assets/nav-BzFLtS1W.js +0 -1
  171. package/client/dist/assets/nav-DBDbQOYn.js +0 -1
  172. package/client/dist/assets/nav-X9sVtUWC.js +0 -1
  173. package/client/dist/assets/setup-Dn2-veYO.js +0 -1
  174. /package/client/dist/assets/{abap-s65oMlhi.js → abap-CK68cr9-.js} +0 -0
  175. /package/client/dist/assets/{activity-Cyy07Tgo.js → activity-B2MvEH54.js} +0 -0
  176. /package/client/dist/assets/{activity-qFTcMyW9.js → activity-BbNkUmW4.js} +0 -0
  177. /package/client/dist/assets/{activity-DlbWCa4y.js → activity-C7VdmeGb.js} +0 -0
  178. /package/client/dist/assets/{activity-Dv6H7wEr.js → activity-Ca2XoBi-.js} +0 -0
  179. /package/client/dist/assets/{activity-C8qqEIoP.js → activity-D4dhNEwO.js} +0 -0
  180. /package/client/dist/assets/{activity-Dwq0heud.js → activity-DHjfmAyT.js} +0 -0
  181. /package/client/dist/assets/{activity-CZVM4nlJ.js → activity-Di2f50_Z.js} +0 -0
  182. /package/client/dist/assets/{activity-BqqwnH_h.js → activity-usnNa7FX.js} +0 -0
  183. /package/client/dist/assets/{addon-image-CpF0L0jM.js → addon-image-biWX2UUm.js} +0 -0
  184. /package/client/dist/assets/{addon-ligatures-hXysGZrA.js → addon-ligatures-D4EiDb5r.js} +0 -0
  185. /package/client/dist/assets/{addon-webgl-Cn1slavz.js → addon-webgl-CDCIZQ03.js} +0 -0
  186. /package/client/dist/assets/{addspec-RuL8Zd7w.js → addspec-1HEtmLv1.js} +0 -0
  187. /package/client/dist/assets/{addspec-DDvvnE6N.js → addspec-BxMn39wn.js} +0 -0
  188. /package/client/dist/assets/{addspec-xjDbYZWL.js → addspec-CNyAUGz_.js} +0 -0
  189. /package/client/dist/assets/{addspec-CIGb34PS.js → addspec-CO1xv6BL.js} +0 -0
  190. /package/client/dist/assets/{addspec-BErjOdNK.js → addspec-DAveBiCE.js} +0 -0
  191. /package/client/dist/assets/{addspec-C_3NBarY.js → addspec-DE_VfIyP.js} +0 -0
  192. /package/client/dist/assets/{addspec-B-BKlvDj.js → addspec-DfuR6EAl.js} +0 -0
  193. /package/client/dist/assets/{addspec-rmhOaH7N.js → addspec-IozoiiGs.js} +0 -0
  194. /package/client/dist/assets/{agents-BDx1RXcl.js → agents-1lhqEcXH.js} +0 -0
  195. /package/client/dist/assets/{agents-BFr3kUhK.js → agents-BYjUOrLD.js} +0 -0
  196. /package/client/dist/assets/{agents-CMxtJMLD.js → agents-CIhRSOna.js} +0 -0
  197. /package/client/dist/assets/{agents-DcxZHzNr.js → agents-CQlu1nOm.js} +0 -0
  198. /package/client/dist/assets/{agents-BlPnx-mz.js → agents-DCX9D3-Q.js} +0 -0
  199. /package/client/dist/assets/{agents-23iPejcA.js → agents-DVYpVGur.js} +0 -0
  200. /package/client/dist/assets/{agents-B_1L9xRg.js → agents-DcoUUFZl.js} +0 -0
  201. /package/client/dist/assets/{agents-G3shOewU.js → agents-Jc-CjBDB.js} +0 -0
  202. /package/client/dist/assets/{agentstudio-kk9RB7Se.js → agentstudio-1E-bag77.js} +0 -0
  203. /package/client/dist/assets/{agentstudio-Bk1eZcv4.js → agentstudio-9NbsoGXk.js} +0 -0
  204. /package/client/dist/assets/{agentstudio-Y3G0ddJ2.js → agentstudio-B-jQgYV-.js} +0 -0
  205. /package/client/dist/assets/{agentstudio-B-CMAQqy.js → agentstudio-B1y0JWY-.js} +0 -0
  206. /package/client/dist/assets/{agentstudio-DpP9caEE.js → agentstudio-Bwa72b1S.js} +0 -0
  207. /package/client/dist/assets/{agentstudio-CxlUllqI.js → agentstudio-DCTpIqOx.js} +0 -0
  208. /package/client/dist/assets/{agentstudio-DNlme601.js → agentstudio-DCyDW8sy.js} +0 -0
  209. /package/client/dist/assets/{agentstudio-ChxNuGAu.js → agentstudio-Q0Sq_jTg.js} +0 -0
  210. /package/client/dist/assets/{aiedit-DrfzQroF.js → aiedit-CGxeml8D.js} +0 -0
  211. /package/client/dist/assets/{aiedit-De0SOH6S.js → aiedit-CfBNanHE.js} +0 -0
  212. /package/client/dist/assets/{aiedit-BBCrOpHq.js → aiedit-CvSspQmE.js} +0 -0
  213. /package/client/dist/assets/{aiedit-fMltW101.js → aiedit-DSLtsYIu.js} +0 -0
  214. /package/client/dist/assets/{aiedit-BMtcGYNE.js → aiedit-DUpe7NxI.js} +0 -0
  215. /package/client/dist/assets/{aiedit-TTwzL1TS.js → aiedit-Dirqf3Bm.js} +0 -0
  216. /package/client/dist/assets/{aiedit-5ETerMK1.js → aiedit-kAU7KADg.js} +0 -0
  217. /package/client/dist/assets/{aiedit-D9ddlgkM.js → aiedit-qTGI_PGs.js} +0 -0
  218. /package/client/dist/assets/{apex-BLUBIldB.js → apex-BHHyAVNb.js} +0 -0
  219. /package/client/dist/assets/{attachments-CHaDUfjB.js → attachments-3CuBBRFu.js} +0 -0
  220. /package/client/dist/assets/{attachments-DazTVJbH.js → attachments-BLqqPGyy.js} +0 -0
  221. /package/client/dist/assets/{attachments-CCWasu-P.js → attachments-BNgH6Tcu.js} +0 -0
  222. /package/client/dist/assets/{attachments-BIsSSnHJ.js → attachments-C2JPppoP.js} +0 -0
  223. /package/client/dist/assets/{attachments-CVSAbGNl.js → attachments-CppM7de8.js} +0 -0
  224. /package/client/dist/assets/{attachments-Chg5poG1.js → attachments-D5fVsMgb.js} +0 -0
  225. /package/client/dist/assets/{attachments-Dn-JImAK.js → attachments-UJtEq892.js} +0 -0
  226. /package/client/dist/assets/{attachments-LDA9kp2X.js → attachments-pW3zm9nu.js} +0 -0
  227. /package/client/dist/assets/{azcli-DuWxh9mO.js → azcli-C56lfIEP.js} +0 -0
  228. /package/client/dist/assets/{bat-UKoTejQm.js → bat-BLamSaf-.js} +0 -0
  229. /package/client/dist/assets/{bicep-4sTT4B3D.js → bicep-lrOcEItS.js} +0 -0
  230. /package/client/dist/assets/{browser-D2Y_UAKA.js → browser-B7i7Ysc8.js} +0 -0
  231. /package/client/dist/assets/{browser-BDd1dbFa.js → browser-BVQgd1_1.js} +0 -0
  232. /package/client/dist/assets/{browser-Dxc_VIRK.js → browser-C91MQBxx.js} +0 -0
  233. /package/client/dist/assets/{browser-DH9SGVfM.js → browser-C__Vz7KR.js} +0 -0
  234. /package/client/dist/assets/{browser-lTQwcDCI.js → browser-DhwTc0ry.js} +0 -0
  235. /package/client/dist/assets/{browser-Qya9cARy.js → browser-Dpo_ug4c.js} +0 -0
  236. /package/client/dist/assets/{browser-BWSgbfdX.js → browser-DwE289IS.js} +0 -0
  237. /package/client/dist/assets/{browser-DWOVYMlg.js → browser-EFmPpkvO.js} +0 -0
  238. /package/client/dist/assets/{cameligo-CAAryRYO.js → cameligo-BK_p0bgU.js} +0 -0
  239. /package/client/dist/assets/{chat-UVVZqA57.js → chat-9RcD5Lcn.js} +0 -0
  240. /package/client/dist/assets/{chat-CUrG1eVg.js → chat-BXVx6rb5.js} +0 -0
  241. /package/client/dist/assets/{chat-CPgmgZOj.js → chat-BnJPyeUK.js} +0 -0
  242. /package/client/dist/assets/{chat-CvOOKB2s.js → chat-C5nJF6L0.js} +0 -0
  243. /package/client/dist/assets/{chat-BO9MvVID.js → chat-CE8bx6Rz.js} +0 -0
  244. /package/client/dist/assets/{chat-DIh3hr6y.js → chat-PVakYp92.js} +0 -0
  245. /package/client/dist/assets/{chat-mPn3UlMl.js → chat-UMl6H55K.js} +0 -0
  246. /package/client/dist/assets/{chat-BsbNGPW9.js → chat-mq1x9cxZ.js} +0 -0
  247. /package/client/dist/assets/{clojure-BlMERO1w.js → clojure-BQ7JOykv.js} +0 -0
  248. /package/client/dist/assets/{clsx-CnH-HMk3.js → clsx-CjLeUJfG.js} +0 -0
  249. /package/client/dist/assets/{code-BwIz8agY.js → code-BPMR8_Vp.js} +0 -0
  250. /package/client/dist/assets/{code-CDFlxUFC.js → code-BsOBm1Fb.js} +0 -0
  251. /package/client/dist/assets/{code-CY85RXZU.js → code-DtIatVES.js} +0 -0
  252. /package/client/dist/assets/{code-DtZBQTi9.js → code-Lg4awa35.js} +0 -0
  253. /package/client/dist/assets/{code-CD7yNSK0.js → code-OoLqhjoI.js} +0 -0
  254. /package/client/dist/assets/{code-D24e1Crx.js → code-aCUqkVN_.js} +0 -0
  255. /package/client/dist/assets/{code-Cp3Fdng-.js → code-eh4vO2-_.js} +0 -0
  256. /package/client/dist/assets/{code-nKa0fkm_.js → code-u9Sg9ohM.js} +0 -0
  257. /package/client/dist/assets/{coffee-Cj8D-Wl1.js → coffee-BD3DiL5s.js} +0 -0
  258. /package/client/dist/assets/{commands-t4frzhB0.js → commands-B9XEyMj-.js} +0 -0
  259. /package/client/dist/assets/{commands-BR1kDkHQ.js → commands-BI1Ta-E7.js} +0 -0
  260. /package/client/dist/assets/{commands-DWgp-8W1.js → commands-BdI0CtOL.js} +0 -0
  261. /package/client/dist/assets/{commands-ddsl1V91.js → commands-C2ewlyXo.js} +0 -0
  262. /package/client/dist/assets/{commands-IXMOKBYt.js → commands-CwypztRT.js} +0 -0
  263. /package/client/dist/assets/{commands-Cb21pDlG.js → commands-DSX3yJyr.js} +0 -0
  264. /package/client/dist/assets/{commands-B-MVT-2F.js → commands-Ic1DJDxm.js} +0 -0
  265. /package/client/dist/assets/{commands-B0yFTp7e.js → commands-yNn4IA4C.js} +0 -0
  266. /package/client/dist/assets/{common-CTEbWVZS.js → common-BhFasU8N.js} +0 -0
  267. /package/client/dist/assets/{common-CALKUpYm.js → common-CnrZ9gCZ.js} +0 -0
  268. /package/client/dist/assets/{common-B4sqsKp7.js → common-CwhHWbTD.js} +0 -0
  269. /package/client/dist/assets/{common-wA36jmj1.js → common-D9GAPxeN.js} +0 -0
  270. /package/client/dist/assets/{common-5ilvMOcH.js → common-DI4jE5y-.js} +0 -0
  271. /package/client/dist/assets/{common-DQiza2Xp.js → common-DS8zKLQT.js} +0 -0
  272. /package/client/dist/assets/{common-BKpVwUIf.js → common-DTHzyfYC.js} +0 -0
  273. /package/client/dist/assets/{common-BzEC3kJU.js → common-DY4nzbWC.js} +0 -0
  274. /package/client/dist/assets/{cpp-BPfKnaj_.js → cpp-splnCwCh.js} +0 -0
  275. /package/client/dist/assets/{csharp-gX-x5uD6.js → csharp-BCpsl-o3.js} +0 -0
  276. /package/client/dist/assets/{csp-DKGVt8SM.js → csp-tkCWrZmb.js} +0 -0
  277. /package/client/dist/assets/{css-CPMdnAVq.js → css-B_xdnReq.js} +0 -0
  278. /package/client/dist/assets/{cypher-ClMDrj9S.js → cypher-OL1zq-y3.js} +0 -0
  279. /package/client/dist/assets/{dart-C4zbzpVv.js → dart-CtTlOkrm.js} +0 -0
  280. /package/client/dist/assets/{dockerfile-D9xw73D1.js → dockerfile-DcQ5CNiW.js} +0 -0
  281. /package/client/dist/assets/{ecl-gqO8tIR9.js → ecl-DZpiDRt6.js} +0 -0
  282. /package/client/dist/assets/{editor.api2-BPnIxMjz.js → editor.api2-Cwla1nvV.js} +0 -0
  283. /package/client/dist/assets/{elixir-DSAhVF3_.js → elixir-Dzj2lqtK.js} +0 -0
  284. /package/client/dist/assets/{explore-8gMBVmkp.js → explore-4SD1DxR0.js} +0 -0
  285. /package/client/dist/assets/{explore-vdBm_Re8.js → explore-B2n8r9Ye.js} +0 -0
  286. /package/client/dist/assets/{explore-BkWTnLi3.js → explore-BxR9q8Hq.js} +0 -0
  287. /package/client/dist/assets/{explore-CQUCw9ci.js → explore-CBYU76JD.js} +0 -0
  288. /package/client/dist/assets/{explore-Dbac0ErD.js → explore-CCcPXrHg.js} +0 -0
  289. /package/client/dist/assets/{explore-DUU36tcH.js → explore-CZt4Ncpa.js} +0 -0
  290. /package/client/dist/assets/{explore-DlKgN7D-.js → explore-DQ0XEjed.js} +0 -0
  291. /package/client/dist/assets/{explore-COaUKI0t.js → explore-xQoaak6e.js} +0 -0
  292. /package/client/dist/assets/{flow9-DeQCSPOd.js → flow9-CFENCHIZ.js} +0 -0
  293. /package/client/dist/assets/{format-command-CZsy-vHq.js → format-command-CMsBTOPN.js} +0 -0
  294. /package/client/dist/assets/{fsharp-CEfaXL-S.js → fsharp-D-4bv7FB.js} +0 -0
  295. /package/client/dist/assets/{go-Xp1OkZCh.js → go-djxbTJq7.js} +0 -0
  296. /package/client/dist/assets/{graphql-BwRXrUwe.js → graphql-DHIFrkZ6.js} +0 -0
  297. /package/client/dist/assets/{hcl-u06DtVFk.js → hcl-CXVoowNb.js} +0 -0
  298. /package/client/dist/assets/{ini-AmeIpFND.js → ini-Crzc5kly.js} +0 -0
  299. /package/client/dist/assets/{integrations-B9CEpNF0.js → integrations-9qxUNhPD.js} +0 -0
  300. /package/client/dist/assets/{integrations-DWz1eU_K.js → integrations-A9f08E0o.js} +0 -0
  301. /package/client/dist/assets/{integrations-BlvAdewo.js → integrations-B17KyA-i.js} +0 -0
  302. /package/client/dist/assets/{integrations-Bw8UM9Xd.js → integrations-B46ac3sf.js} +0 -0
  303. /package/client/dist/assets/{integrations-7YyTBuU9.js → integrations-C7u7SInG.js} +0 -0
  304. /package/client/dist/assets/{integrations-DiPR8Fzp.js → integrations-DkBi-Gsh.js} +0 -0
  305. /package/client/dist/assets/{integrations-C5SxNKnG.js → integrations-DwSVdCpf.js} +0 -0
  306. /package/client/dist/assets/{integrations-CJQKMmdW.js → integrations-jJGaKwff.js} +0 -0
  307. /package/client/dist/assets/{java-CyDbRQjX.js → java-CYLaJrdE.js} +0 -0
  308. /package/client/dist/assets/{jira-DOw8bkIR.js → jira-01H1RfQj.js} +0 -0
  309. /package/client/dist/assets/{jira-DKImM1YH.js → jira-0ldfMByG.js} +0 -0
  310. /package/client/dist/assets/{jira-Fob8EGxN.js → jira-4Fin3fu7.js} +0 -0
  311. /package/client/dist/assets/{jira-xZA2lixb.js → jira-BpnpUFlb.js} +0 -0
  312. /package/client/dist/assets/{jira-C-ATCti0.js → jira-BwACz875.js} +0 -0
  313. /package/client/dist/assets/{jira-DlA-wGp-.js → jira-CBU0200d.js} +0 -0
  314. /package/client/dist/assets/{jira-D7bkKAX8.js → jira-Dht8qNck.js} +0 -0
  315. /package/client/dist/assets/{jira-CmVfRM-b.js → jira-DzIVvNmy.js} +0 -0
  316. /package/client/dist/assets/{jobs-DAF8AGy5.js → jobs-BF9raUkL.js} +0 -0
  317. /package/client/dist/assets/{jobs-D93lG6If.js → jobs-Bw4hqEro.js} +0 -0
  318. /package/client/dist/assets/{jobs-F5PGJwbW.js → jobs-DM9owF1j.js} +0 -0
  319. /package/client/dist/assets/{jobs-Db3xrsp_.js → jobs-DRInW0AE.js} +0 -0
  320. /package/client/dist/assets/{jobs-fYWWxCUV.js → jobs-DcCONARH.js} +0 -0
  321. /package/client/dist/assets/{jobs-Brp44JDd.js → jobs-Oz3UtLQn.js} +0 -0
  322. /package/client/dist/assets/{jobs-Do4Ltqdj.js → jobs-jEQidSIb.js} +0 -0
  323. /package/client/dist/assets/{jobs-BGkI19S_.js → jobs-ym72SIR9.js} +0 -0
  324. /package/client/dist/assets/{julia-BqialFRG.js → julia-BJ7tUU-T.js} +0 -0
  325. /package/client/dist/assets/{kotlin-Dzz8TWAt.js → kotlin-CeWcU3lC.js} +0 -0
  326. /package/client/dist/assets/{less-DHRJD3TR.js → less-CviwWNG4.js} +0 -0
  327. /package/client/dist/assets/{lexon-5Y3QgTmT.js → lexon-Dt28jqDS.js} +0 -0
  328. /package/client/dist/assets/{lua-sKvhfPn5.js → lua-B6AOaJSi.js} +0 -0
  329. /package/client/dist/assets/{m3-DWDVwkFG.js → m3-VnMkHJcy.js} +0 -0
  330. /package/client/dist/assets/{markdown-CD_aSBxW.js → markdown-8vKNdvIM.js} +0 -0
  331. /package/client/dist/assets/{mips-687T03hg.js → mips-DoEcuzan.js} +0 -0
  332. /package/client/dist/assets/{msdax-C1St-dIV.js → msdax-CNzvHyEp.js} +0 -0
  333. /package/client/dist/assets/{mysql-BG7r8oBS.js → mysql-Buk3vUtL.js} +0 -0
  334. /package/client/dist/assets/{objective-c-Ds1-m05L.js → objective-c-DfOmda-5.js} +0 -0
  335. /package/client/dist/assets/{pascal-BKK9FpIi.js → pascal-DoAA2yjy.js} +0 -0
  336. /package/client/dist/assets/{pascaligo-SRS3nwtO.js → pascaligo-C65Vn1fu.js} +0 -0
  337. /package/client/dist/assets/{perl-B2hTOlrF.js → perl-INlfQ_ro.js} +0 -0
  338. /package/client/dist/assets/{pgsql-DIQJYNpL.js → pgsql-DtwRzglx.js} +0 -0
  339. /package/client/dist/assets/{php-BEaVe8X2.js → php-DfHqVNet.js} +0 -0
  340. /package/client/dist/assets/{pla-oPLHpZ-Q.js → pla-D3dNVRYk.js} +0 -0
  341. /package/client/dist/assets/{postiats-D_vzrAzD.js → postiats-UKXD1rM4.js} +0 -0
  342. /package/client/dist/assets/{powerquery-BKG6w-FH.js → powerquery-zE3E3O7s.js} +0 -0
  343. /package/client/dist/assets/{powershell-B3dLhDt4.js → powershell-Df9b3zQl.js} +0 -0
  344. /package/client/dist/assets/{protobuf-DC8SGjcl.js → protobuf-BF0nUVrW.js} +0 -0
  345. /package/client/dist/assets/{pug-D5E-4fI0.js → pug-Bj6Yikv_.js} +0 -0
  346. /package/client/dist/assets/{qsharp-6vJAWv0x.js → qsharp-BI4h-raZ.js} +0 -0
  347. /package/client/dist/assets/{r-CDwsEcbM.js → r-DhpnmaXx.js} +0 -0
  348. /package/client/dist/assets/{redis-CuQbbESS.js → redis-DwYYPr4d.js} +0 -0
  349. /package/client/dist/assets/{redshift-B9e1k-qI.js → redshift-D2f3Bc-4.js} +0 -0
  350. /package/client/dist/assets/{restructuredtext-BiJ5IwaU.js → restructuredtext-Dkiif6fe.js} +0 -0
  351. /package/client/dist/assets/{ruby-B0UAHY9b.js → ruby-C6hLmHy0.js} +0 -0
  352. /package/client/dist/assets/{rust-Dg_spmFr.js → rust-X8WAGtxB.js} +0 -0
  353. /package/client/dist/assets/{sb-DjU66I8Q.js → sb-4M2la0Kr.js} +0 -0
  354. /package/client/dist/assets/{scala-qvStIdfG.js → scala-CaCXz58E.js} +0 -0
  355. /package/client/dist/assets/{scheme-FstEk5Rh.js → scheme-CMdjpmP7.js} +0 -0
  356. /package/client/dist/assets/{scss-w0U3rQLK.js → scss-QopmvPyp.js} +0 -0
  357. /package/client/dist/assets/{settings-CewqtD9V.js → settings-0m2iuTzd.js} +0 -0
  358. /package/client/dist/assets/{settings-Puea5c8v.js → settings-BA1V6KdK.js} +0 -0
  359. /package/client/dist/assets/{settings-Cx97tHs5.js → settings-BN7OusER.js} +0 -0
  360. /package/client/dist/assets/{settings-BwMQ1lx5.js → settings-Bi5FFkIw.js} +0 -0
  361. /package/client/dist/assets/{settings-EMaKBwE0.js → settings-CRJxyTNN.js} +0 -0
  362. /package/client/dist/assets/{settings-DkB9Wh-f.js → settings-DyBxunCh.js} +0 -0
  363. /package/client/dist/assets/{settings-CqCY8gZq.js → settings-fk-WlnUt.js} +0 -0
  364. /package/client/dist/assets/{settings-CyixAx0X.js → settings-iQe5Dim-.js} +0 -0
  365. /package/client/dist/assets/{shell-DJ78wREd.js → shell-CDBchS00.js} +0 -0
  366. /package/client/dist/assets/{solidity-1aGIVsdX.js → solidity-DvBfi5mC.js} +0 -0
  367. /package/client/dist/assets/{sophia-40LqcGjB.js → sophia-HSnXR17u.js} +0 -0
  368. /package/client/dist/assets/{sparql-Cz5dqG_g.js → sparql-DhG_ppup.js} +0 -0
  369. /package/client/dist/assets/{specs-DfwDeADE.js → specs-Bku8SypT.js} +0 -0
  370. /package/client/dist/assets/{specs-B4GuOzuZ.js → specs-BvsFNKTs.js} +0 -0
  371. /package/client/dist/assets/{specs-ghyBMnib.js → specs-C2wW5bq8.js} +0 -0
  372. /package/client/dist/assets/{specs-VK-zXv7x.js → specs-CUE3xSFM.js} +0 -0
  373. /package/client/dist/assets/{specs-D-Sb6dre.js → specs-C_8j4ELZ.js} +0 -0
  374. /package/client/dist/assets/{specs-C62F2CDv.js → specs-CnH7dOiF.js} +0 -0
  375. /package/client/dist/assets/{specs-DFSkAeK8.js → specs-D4ORogcT.js} +0 -0
  376. /package/client/dist/assets/{specs-BVLKe2n5.js → specs-D6Y76Bgi.js} +0 -0
  377. /package/client/dist/assets/{sql-64f62Ni4.js → sql-DHeIS-tf.js} +0 -0
  378. /package/client/dist/assets/{st-gJe2yG8J.js → st-CMWchiRU.js} +0 -0
  379. /package/client/dist/assets/{swift-C6ME22mv.js → swift-CDXvBefj.js} +0 -0
  380. /package/client/dist/assets/{systemverilog-CEWz259w.js → systemverilog-0RTukYUE.js} +0 -0
  381. /package/client/dist/assets/{tcl-CcLVIi3m.js → tcl-CbBF_Ttl.js} +0 -0
  382. /package/client/dist/assets/{terminal-CPpK58RC.js → terminal-BLh00ouM.js} +0 -0
  383. /package/client/dist/assets/{terminal-CdxkpafL.js → terminal-BTanJIt_.js} +0 -0
  384. /package/client/dist/assets/{terminal-DHIkiWcs.js → terminal-CB33IsO1.js} +0 -0
  385. /package/client/dist/assets/{terminal-Ciia0wh2.js → terminal-CR6t-Oey.js} +0 -0
  386. /package/client/dist/assets/{terminal-DY42QANg.js → terminal-CY_sPfdD.js} +0 -0
  387. /package/client/dist/assets/{terminal-C0xx0SjA.js → terminal-Cj4HzP3p.js} +0 -0
  388. /package/client/dist/assets/{terminal-BYtreaaF.js → terminal-Cz_Puz0q.js} +0 -0
  389. /package/client/dist/assets/{terminal-DoxtVdma.js → terminal-DFEH9XTQ.js} +0 -0
  390. /package/client/dist/assets/{tickets-D5MSAPe_.js → tickets-BFrVcXAI.js} +0 -0
  391. /package/client/dist/assets/{tickets-DBV3wgQZ.js → tickets-C3gG--yT.js} +0 -0
  392. /package/client/dist/assets/{tickets-r4-oNV0R.js → tickets-Cfhwbdxj.js} +0 -0
  393. /package/client/dist/assets/{tickets-RZ0LyeQe.js → tickets-DcggvP3k.js} +0 -0
  394. /package/client/dist/assets/{tickets-d1A6EOHa.js → tickets-EnP6m_Ao.js} +0 -0
  395. /package/client/dist/assets/{tickets-Q0_pONEh.js → tickets-IbYhlr-6.js} +0 -0
  396. /package/client/dist/assets/{tickets-CVJQ-vRm.js → tickets-ZS5EsDxn.js} +0 -0
  397. /package/client/dist/assets/{tickets-CG_mo-Bg.js → tickets-lJR7pWNI.js} +0 -0
  398. /package/client/dist/assets/{twig-DvsO-WjW.js → twig-DCcGd8FE.js} +0 -0
  399. /package/client/dist/assets/{typespec-Brkt3IAA.js → typespec-HTphp-Uh.js} +0 -0
  400. /package/client/dist/assets/{vb-r121Uzxt.js → vb-Cb_yUcVd.js} +0 -0
  401. /package/client/dist/assets/{wgsl-BRX8uYh4.js → wgsl-DrKU3YXj.js} +0 -0
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ /**
3
+ * Loop graph model + validation — the pure core shared by the canvas builder
4
+ * (`loop-builder-canvas`) and the publish gate (`loops-store.publishLoop`).
5
+ *
6
+ * A loop is a directed graph of typed nodes connected by edges. Cycles are
7
+ * allowed (a loop routes back to an earlier node); the run is bounded by the
8
+ * graph's `config.maxIterations` + `timeoutMinutes`. This module has NO I/O and
9
+ * NO provider knowledge — it only describes and validates the shape, so it is
10
+ * trivially unit-testable and reused verbatim by both the store and the engine.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.emptyLoopGraph = emptyLoopGraph;
14
+ exports.validateLoopGraph = validateLoopGraph;
15
+ exports.nodesById = nodesById;
16
+ exports.findStartNode = findStartNode;
17
+ exports.successors = successors;
18
+ exports.interpolateSpec = interpolateSpec;
19
+ /** A minimal, structurally-valid empty graph (used to seed a new Draft). */
20
+ function emptyLoopGraph() {
21
+ return { nodes: [], edges: [], config: { maxIterations: 10, timeoutMinutes: 30 } };
22
+ }
23
+ /**
24
+ * Validate a loop graph against the publish-time rules:
25
+ * - exactly one `start` node,
26
+ * - at least one `end` node (the explicit exit),
27
+ * - every edge references existing nodes (no dangling edges),
28
+ * - every node is reachable from `start` (no orphan / disconnected nodes),
29
+ * - a sane config (maxIterations ≥ 1, timeoutMinutes ≥ 1).
30
+ *
31
+ * Returns all errors found (not just the first) so the builder can highlight
32
+ * every problem at once.
33
+ */
34
+ function validateLoopGraph(graph) {
35
+ const errors = [];
36
+ const nodes = graph.nodes ?? [];
37
+ const edges = graph.edges ?? [];
38
+ // ── Start cardinality ──────────────────────────────────────────────────────
39
+ const starts = nodes.filter((n) => n.type === 'start');
40
+ if (starts.length === 0) {
41
+ errors.push({ code: 'NO_START', message: 'The loop must have a Start node.' });
42
+ }
43
+ else if (starts.length > 1) {
44
+ for (const extra of starts.slice(1)) {
45
+ errors.push({
46
+ code: 'MULTIPLE_START',
47
+ message: 'The loop must have exactly one Start node.',
48
+ nodeId: extra.id,
49
+ });
50
+ }
51
+ }
52
+ // ── End presence ────────────────────────────────────────────────────────────
53
+ if (!nodes.some((n) => n.type === 'end')) {
54
+ errors.push({ code: 'NO_END', message: 'The loop must have at least one End node.' });
55
+ }
56
+ // ── Config sanity ─────────────────────────────────────────────────────────────
57
+ const cfg = graph.config;
58
+ if (!cfg ||
59
+ !Number.isInteger(cfg.maxIterations) ||
60
+ cfg.maxIterations < 1 ||
61
+ typeof cfg.timeoutMinutes !== 'number' ||
62
+ cfg.timeoutMinutes < 1) {
63
+ errors.push({
64
+ code: 'INVALID_CONFIG',
65
+ message: 'maxIterations and timeoutMinutes must both be ≥ 1.',
66
+ });
67
+ }
68
+ // ── Dangling edges ────────────────────────────────────────────────────────────
69
+ const nodeIds = new Set(nodes.map((n) => n.id));
70
+ for (const e of edges) {
71
+ if (!nodeIds.has(e.source) || !nodeIds.has(e.target)) {
72
+ errors.push({
73
+ code: 'DANGLING_EDGE',
74
+ message: `Edge "${e.id}" references a node that does not exist.`,
75
+ edgeId: e.id,
76
+ });
77
+ }
78
+ }
79
+ // ── Reachability from Start (orphan detection) ──────────────────────────────
80
+ // Only meaningful with exactly one Start; otherwise the start-cardinality
81
+ // errors above already explain the shape problem.
82
+ if (starts.length === 1) {
83
+ const adjacency = new Map();
84
+ for (const e of edges) {
85
+ if (nodeIds.has(e.source) && nodeIds.has(e.target)) {
86
+ const list = adjacency.get(e.source) ?? [];
87
+ list.push(e.target);
88
+ adjacency.set(e.source, list);
89
+ }
90
+ }
91
+ const visited = new Set();
92
+ const queue = [starts[0].id];
93
+ visited.add(starts[0].id);
94
+ while (queue.length > 0) {
95
+ const current = queue.shift();
96
+ for (const next of adjacency.get(current) ?? []) {
97
+ if (!visited.has(next)) {
98
+ visited.add(next);
99
+ queue.push(next);
100
+ }
101
+ }
102
+ }
103
+ for (const n of nodes) {
104
+ if (!visited.has(n.id)) {
105
+ errors.push({
106
+ code: 'ORPHAN_NODE',
107
+ message: `Node "${n.id}" is not reachable from Start.`,
108
+ nodeId: n.id,
109
+ });
110
+ }
111
+ }
112
+ }
113
+ return { valid: errors.length === 0, errors };
114
+ }
115
+ // ── Traversal helpers (used by the execution engine) ─────────────────────────
116
+ function nodesById(graph) {
117
+ return new Map(graph.nodes.map((n) => [n.id, n]));
118
+ }
119
+ function findStartNode(graph) {
120
+ return graph.nodes.find((n) => n.type === 'start');
121
+ }
122
+ /** The nodes directly reachable from `nodeId` via an outgoing edge (in edge order). */
123
+ function successors(graph, nodeId) {
124
+ const byId = nodesById(graph);
125
+ return graph.edges
126
+ .filter((e) => e.source === nodeId)
127
+ .map((e) => byId.get(e.target))
128
+ .filter((n) => n !== undefined);
129
+ }
130
+ function interpolateSpec(text, spec) {
131
+ return text.replace(/\{\{\s*spec\.(\w+)\s*\}\}/g, (_match, key) => {
132
+ // `{{spec.ids}}` → all rail ticket ids as `#1 #2 #3` (used by implement/batch).
133
+ if (key === 'ids') {
134
+ const ids = spec?.ticketIds ?? (spec?.id != null ? [spec.id] : []);
135
+ return ids.map((id) => `#${id}`).join(' ');
136
+ }
137
+ const value = spec ? spec[key] : undefined;
138
+ if (value == null)
139
+ return '';
140
+ if (Array.isArray(value))
141
+ return value.join(', ');
142
+ return String(value);
143
+ });
144
+ }
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SAMPLE_SPEC = void 0;
4
+ exports.previewLoop = previewLoop;
5
+ /**
6
+ * Dry-run preview: resolve every step's tokens (`{{cmd:*}}`, `{{spec.*}}`,
7
+ * `{{const:*}}`) and return the EXACT prompt/command text that WOULD be sent —
8
+ * without spawning anything. Lets the user debug a loop before burning tokens.
9
+ *
10
+ * Spec tokens are filled from a fixed SAMPLE spec (the real ticket is only known
11
+ * at launch), so the preview is labelled "sample data" on the client. Commands +
12
+ * constants resolve to their real run-time values.
13
+ */
14
+ const loop_command_catalog_1 = require("./loop-command-catalog");
15
+ const loop_graph_1 = require("./loop-graph");
16
+ const loop_constants_1 = require("./loop-constants");
17
+ /** Stand-in ticket so `{{spec.*}}` resolves to something readable in the preview. */
18
+ exports.SAMPLE_SPEC = {
19
+ id: 1,
20
+ ticketIds: [1],
21
+ title: 'Example spec',
22
+ description: 'Example description',
23
+ status: 'todo',
24
+ priority: 'medium',
25
+ labels: ['example'],
26
+ jira_key: 'PROJ-1',
27
+ jira_url: 'https://example.atlassian.net/browse/PROJ-1',
28
+ };
29
+ /** Reachable-first visit order (then any unreachable nodes), mirroring the run. */
30
+ function visitOrder(graph) {
31
+ const start = (0, loop_graph_1.findStartNode)(graph);
32
+ const ordered = [];
33
+ const seen = new Set();
34
+ const queue = start ? [start] : [];
35
+ while (queue.length) {
36
+ const node = queue.shift();
37
+ if (seen.has(node.id))
38
+ continue;
39
+ seen.add(node.id);
40
+ ordered.push(node);
41
+ for (const next of (0, loop_graph_1.successors)(graph, node.id))
42
+ if (!seen.has(next.id))
43
+ queue.push(next);
44
+ }
45
+ for (const n of graph.nodes ?? [])
46
+ if (!seen.has(n.id))
47
+ ordered.push(n);
48
+ return ordered;
49
+ }
50
+ function previewLoop(graph, opts) {
51
+ const spec = opts.spec ?? exports.SAMPLE_SPEC;
52
+ const steps = [];
53
+ for (const node of visitOrder(graph)) {
54
+ const label = typeof node.data?.label === 'string' && node.data.label.trim() ? node.data.label.trim() : undefined;
55
+ let text = null;
56
+ if (node.type === 'ai-step') {
57
+ text = (0, loop_constants_1.resolveConstants)((0, loop_graph_1.interpolateSpec)((0, loop_command_catalog_1.expandCommands)(String(node.data?.prompt ?? ''), { provider: opts.provider, ticketIds: spec.ticketIds, specId: spec.id }), spec), opts.constants);
58
+ }
59
+ else if (node.type === 'shell') {
60
+ text = (0, loop_constants_1.resolveConstants)((0, loop_graph_1.interpolateSpec)(String(node.data?.command ?? ''), spec), opts.constants);
61
+ }
62
+ else if (node.type === 'decider') {
63
+ text = (0, loop_constants_1.resolveConstants)((0, loop_graph_1.interpolateSpec)(String(node.data?.goal ?? ''), spec), opts.constants);
64
+ }
65
+ if (text !== null)
66
+ steps.push({ nodeId: node.id, kind: node.type, label, text });
67
+ }
68
+ return { steps };
69
+ }
@@ -0,0 +1,407 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.LoopRunManager = void 0;
7
+ const tree_kill_1 = __importDefault(require("tree-kill"));
8
+ const db_1 = require("./db");
9
+ const loop_graph_1 = require("./loop-graph");
10
+ const loop_command_catalog_1 = require("./loop-command-catalog");
11
+ const loop_constants_1 = require("./loop-constants");
12
+ const loop_decider_1 = require("./loop-decider");
13
+ const loop_runs_store_1 = require("./loop-runs-store");
14
+ const ai_invocations_1 = require("./ai-invocations");
15
+ const ids_1 = require("./ids");
16
+ const HISTORY_MAX_CHARS = 1500;
17
+ function truncate(s, max = 600) {
18
+ return s.length > max ? s.slice(0, max) + '…' : s;
19
+ }
20
+ class LoopRunManager {
21
+ db;
22
+ broadcast;
23
+ executors;
24
+ now;
25
+ _cancelled = new Set();
26
+ /** The currently-spawned child per run, so cancel/stop can actually KILL a
27
+ * blocked spawn (the cooperative `_cancelled` flag can't interrupt an await). */
28
+ _activeChild = new Map();
29
+ constructor(db, broadcast, executors, now = () => Date.now()) {
30
+ this.db = db;
31
+ this.broadcast = broadcast;
32
+ this.executors = executors;
33
+ this.now = now;
34
+ }
35
+ /** Cancel an in-flight run: flag it AND kill the active spawned child so a
36
+ * blocked AI Step / Shell returns immediately (the engine then settles
37
+ * 'stopped' at the next boundary). */
38
+ cancel(runId) {
39
+ this._cancelled.add(runId);
40
+ const child = this._activeChild.get(runId);
41
+ if (child?.pid) {
42
+ try {
43
+ (0, tree_kill_1.default)(child.pid, 'SIGKILL', () => { });
44
+ }
45
+ catch { /* already gone */ }
46
+ }
47
+ }
48
+ async run(req) {
49
+ const runId = req.runId ?? (0, ids_1.newId)();
50
+ const maxIterations = req.graph.config.maxIterations;
51
+ const timeoutMs = req.graph.config.timeoutMinutes * 60_000;
52
+ const deadline = this.now() + timeoutMs;
53
+ // Optional cost cap (USD). Like maxIterations/timeout, it's a BETWEEN-STEPS
54
+ // guard: per-step cost is only known when that step's process exits, so the
55
+ // loop is stopped before the NEXT step once the accumulated total crosses the
56
+ // cap (may overshoot by at most one step). Honest accuracy caveat: for
57
+ // non-Claude providers the per-step cost is ESTIMATED (rate card) — see the
58
+ // fail-open warning below when a step yields no priced cost.
59
+ const maxCostUsd = typeof req.graph.config.maxCostUsd === 'number' && req.graph.config.maxCostUsd > 0
60
+ ? req.graph.config.maxCostUsd
61
+ : undefined;
62
+ (0, loop_runs_store_1.createLoopRun)(this.db, {
63
+ id: runId,
64
+ projectId: req.projectId,
65
+ loopId: req.loopId,
66
+ loopName: req.loopName ?? null,
67
+ railIndex: req.railIndex ?? null,
68
+ ticketId: req.ticketId ?? null,
69
+ provider: req.provider,
70
+ model: req.model,
71
+ reasoningEffort: req.effort ?? null,
72
+ iterationLimit: maxIterations,
73
+ startedAt: new Date(this.now()).toISOString(),
74
+ });
75
+ this.broadcast({
76
+ type: 'loop.run_started',
77
+ projectId: req.projectId,
78
+ loopRunId: runId,
79
+ loopId: req.loopId,
80
+ railIndex: req.railIndex ?? null,
81
+ });
82
+ // Surface the run as a JOB so the full session streams live in the Jobs list
83
+ // + JobDetail, exactly like implement/ultracode. The job id IS the run id;
84
+ // createJob is idempotent. Synchronous (before the first await) so the row
85
+ // exists by the time the launch responds → "View Log" never 404s.
86
+ const jobCommand = `loop: ${req.loopName ?? req.loopId}${req.ticketId != null ? ` #${req.ticketId}` : ''}`;
87
+ try {
88
+ (0, db_1.createJob)(this.db, { id: runId, command: jobCommand, started_at: new Date(this.now()).toISOString() });
89
+ }
90
+ catch { /* best-effort; the run still proceeds */ }
91
+ let seq = 0;
92
+ let stepNum = 0;
93
+ // Emit a structured step-boundary event (for a segmented/collapsible client
94
+ // view) AND a visual divider line in the flat log, so each loop step is a
95
+ // clearly-delimited section.
96
+ const emitStep = (kind, title) => {
97
+ stepNum += 1;
98
+ try {
99
+ (0, db_1.appendEvent)(this.db, runId, seq, { event_type: 'loop_step', source: 'stdout', payload: JSON.stringify({ index: stepNum, kind, title }) });
100
+ }
101
+ catch { /* best-effort */ }
102
+ this.broadcast({ type: 'event', jobId: runId, event_type: 'loop_step', source: 'stdout', payload: JSON.stringify({ index: stepNum, kind, title }), seq, timestamp: new Date(this.now()).toISOString() });
103
+ seq += 1;
104
+ logLine(`\n━━━━━━ Step ${stepNum} · ${title} ━━━━━━`);
105
+ };
106
+ const logLine = (line, source = 'stdout') => {
107
+ try {
108
+ (0, db_1.appendEvent)(this.db, runId, seq, { event_type: 'log', source, payload: JSON.stringify({ line }) });
109
+ }
110
+ catch { /* events table best-effort */ }
111
+ this.broadcast({ type: 'log', source, line, timestamp: new Date(this.now()).toISOString(), processId: runId });
112
+ seq += 1;
113
+ };
114
+ // Forward a RAW provider stdout line (claude/codex JSONL) as an `event` —
115
+ // identical to QueueManager — so JobStatusPanel parses real activity
116
+ // (thinking/reading/editing/tool steps) instead of staying on "Connecting…".
117
+ // Non-JSON lines fall back to a plain log line.
118
+ const onRawLine = (line) => {
119
+ if (!line)
120
+ return;
121
+ let eventType;
122
+ try {
123
+ const parsed = JSON.parse(line);
124
+ if (typeof parsed.type === 'string')
125
+ eventType = parsed.type;
126
+ }
127
+ catch { /* not JSON */ }
128
+ if (!eventType) {
129
+ logLine(line);
130
+ return;
131
+ }
132
+ try {
133
+ (0, db_1.appendEvent)(this.db, runId, seq, { event_type: eventType, source: 'stdout', payload: line });
134
+ }
135
+ catch { /* best-effort */ }
136
+ this.broadcast({ type: 'event', jobId: runId, event_type: eventType, source: 'stdout', payload: line, seq, timestamp: new Date(this.now()).toISOString() });
137
+ seq += 1;
138
+ };
139
+ logLine(`▶ Loop "${req.loopName ?? req.loopId}" started${req.spec?.title ? ` — spec: ${req.spec.title}` : ''}`);
140
+ console.log(`[loop] start run=${runId} loop=${req.loopId} provider=${req.provider} model=${req.model} nodes=${req.graph.nodes.length} cwd=${req.cwd}`);
141
+ const byId = (0, loop_graph_1.nodesById)(req.graph);
142
+ const start = (0, loop_graph_1.findStartNode)(req.graph);
143
+ let outcome = 'failed';
144
+ let iteration = 0;
145
+ let totalCost = 0;
146
+ let totalTokens = 0;
147
+ let totalDuration = 0;
148
+ let aiSessionId;
149
+ // Fail-open honesty: flips true the first time a cost-bearing step yields no
150
+ // priced cost while a cap is set (non-Claude estimate / unknown model / a
151
+ // failed step) → the cap can under-count, so we warn once in the run log.
152
+ let costUnknownWarned = false;
153
+ // Built-ins always resolve even if the caller omitted `constants`; custom
154
+ // values layer on top. Used to expand `{{const:*}}` in every node's text.
155
+ const constMap = { ...loop_constants_1.BUILTIN_CONSTANTS, ...(req.constants ?? {}) };
156
+ const history = [];
157
+ // Backstop against a cycle with no Decider (would otherwise never increment
158
+ // `iteration`): cap total node executions well above any honest run.
159
+ const stepCap = (maxIterations + 1) * (req.graph.nodes.length + 2) + 16;
160
+ let steps = 0;
161
+ const record = (refSuffix, r) => {
162
+ totalCost += r.cost ?? 0;
163
+ // Honest cost-cap caveat: a cost-bearing step that reports no figure means
164
+ // the cap can't be enforced precisely from here on. Warn once (live).
165
+ if (maxCostUsd !== undefined && r.cost == null && !costUnknownWarned) {
166
+ costUnknownWarned = true;
167
+ logLine('⚠️ A step reported no priced cost (non-Claude estimate / unknown model / failed step) — the cost cap may under-count.', 'stderr');
168
+ }
169
+ totalTokens += r.tokens ?? 0;
170
+ totalDuration += r.durationMs ?? 0;
171
+ (0, ai_invocations_1.recordInvocation)(this.db, {
172
+ id: (0, ids_1.newId)(),
173
+ project_id: req.projectId,
174
+ provider: r.provider ?? req.provider,
175
+ surface: 'loop',
176
+ surface_ref_id: refSuffix,
177
+ ticket_id: req.ticketId ?? null,
178
+ status: 'success',
179
+ started_at: new Date(this.now()).toISOString(),
180
+ total_cost_usd: r.cost,
181
+ total_cost_usd_estimated: r.estimated ?? false,
182
+ tokens_out: r.tokens,
183
+ model: r.model ?? req.model,
184
+ loop_run_id: runId,
185
+ });
186
+ };
187
+ const composeHistory = () => {
188
+ const joined = history.join('\n');
189
+ return joined.length > HISTORY_MAX_CHARS ? '…' + joined.slice(-HISTORY_MAX_CHARS) : joined;
190
+ };
191
+ try {
192
+ let nodeId = start?.id;
193
+ let settled = !start; // no start node → fall through to settle as failed
194
+ if (!start)
195
+ outcome = 'failed';
196
+ while (nodeId && !settled) {
197
+ if (this._cancelled.has(runId)) {
198
+ outcome = 'stopped';
199
+ break;
200
+ }
201
+ if (this.now() > deadline) {
202
+ outcome = 'failed';
203
+ break;
204
+ }
205
+ if (++steps > stepCap) {
206
+ outcome = 'max-iterations';
207
+ break;
208
+ }
209
+ // Cost cap: stop BEFORE the next step once prior steps crossed the budget.
210
+ if (maxCostUsd !== undefined && totalCost >= maxCostUsd) {
211
+ logLine(`\n■ Cost cap reached: $${totalCost.toFixed(4)} ≥ $${maxCostUsd.toFixed(2)} — stopping the loop.`, 'stderr');
212
+ outcome = 'max-cost';
213
+ break;
214
+ }
215
+ const node = byId.get(nodeId);
216
+ if (!node) {
217
+ outcome = 'failed';
218
+ break;
219
+ }
220
+ const succs = (0, loop_graph_1.successors)(req.graph, node.id);
221
+ // The RAIL governs provider/model/effort for the whole run — nodes do
222
+ // NOT override (decided in design: "lo que pongamos en el rail manda").
223
+ const nodeProvider = req.provider;
224
+ const nodeModel = req.model;
225
+ const nodeEffort = req.effort;
226
+ // User-given step name (shown in the run log header); falls back per-kind.
227
+ const nodeLabel = String(node.data?.label ?? '').trim();
228
+ switch (node.type) {
229
+ case 'start':
230
+ nodeId = succs[0]?.id;
231
+ break;
232
+ case 'end':
233
+ outcome = node.data?.outcome === 'failure' ? 'failed' : 'success';
234
+ settled = true;
235
+ break;
236
+ case 'ai-step': {
237
+ // Expand magic commands FIRST — `{{cmd:implement}}` becomes the
238
+ // NATIVE per-provider invocation (claude `/specrails:implement #<id>
239
+ // --yes`, codex `$implement #<id> --yes`) — then resolve `{{spec.*}}`
240
+ // data tokens and finally `{{const:*}}` library constants.
241
+ const rawTemplate = String(node.data?.prompt ?? '');
242
+ const base = (0, loop_constants_1.resolveConstants)((0, loop_graph_1.interpolateSpec)((0, loop_command_catalog_1.expandCommands)(rawTemplate, { provider: nodeProvider, ticketIds: req.spec?.ticketIds, specId: req.spec?.id }), req.spec), constMap);
243
+ const prompt = history.length > 0 ? `${base}\n\n---\nContext from previous iterations:\n${composeHistory()}` : base;
244
+ emitStep('ai-step', `🤖 ${nodeLabel || 'AI Step'} (${nodeProvider}/${nodeModel}${nodeEffort ? `, effort: ${nodeEffort}` : ''})`);
245
+ // Show the authored template AND the actual COMMAND sent. For magic
246
+ // commands this is the short native invocation (so it's plainly visible
247
+ // that `/specrails:implement` ran); a long free-text prompt is capped.
248
+ logLine(`Template: ${rawTemplate.length > 1000 ? rawTemplate.slice(0, 1000) + '…' : rawTemplate}`);
249
+ logLine(`Command: ${base.length > 2000 ? base.slice(0, 2000) + '…(truncated)' : base}`);
250
+ const res = await this.executors.runAiStep({ prompt, sessionId: aiSessionId, provider: nodeProvider, model: nodeModel, effort: nodeEffort, cwd: req.cwd, repoDir: req.repoDir, onLine: logLine, onRawLine, onSpawn: (c) => this._activeChild.set(runId, c) });
251
+ this._activeChild.delete(runId);
252
+ if (res.sessionId)
253
+ aiSessionId = res.sessionId;
254
+ history.push(`AI Step: ${truncate(res.text)}`);
255
+ record(`loop:${runId}`, res);
256
+ nodeId = succs[0]?.id;
257
+ break;
258
+ }
259
+ case 'shell': {
260
+ const command = (0, loop_constants_1.resolveConstants)((0, loop_graph_1.interpolateSpec)(String(node.data?.command ?? ''), req.spec), constMap);
261
+ emitStep('shell', `⚡ ${nodeLabel || 'Shell'}`);
262
+ logLine(`$ ${command}`);
263
+ const sh = await this.executors.runShell({ command, cwd: req.cwd, onLine: logLine, onSpawn: (c) => this._activeChild.set(runId, c) });
264
+ this._activeChild.delete(runId);
265
+ logLine(`(exit ${sh.exitCode})`);
266
+ totalDuration += sh.durationMs ?? 0;
267
+ history.push(`Shell \`${command}\` exit=${sh.exitCode}: ${truncate(sh.stdout || sh.stderr)}`);
268
+ nodeId = succs[0]?.id;
269
+ break;
270
+ }
271
+ case 'decider': {
272
+ if (iteration >= maxIterations) {
273
+ outcome = 'max-iterations';
274
+ settled = true;
275
+ break;
276
+ }
277
+ iteration += 1;
278
+ const goal = (0, loop_constants_1.resolveConstants)(String(node.data?.goal ?? 'The loop goal is met.'), constMap);
279
+ emitStep('decider', `🔍 ${nodeLabel || 'Loop Decider'} (iteration ${iteration})`);
280
+ logLine(`Goal: ${goal}`);
281
+ const dec = await this.executors.runDecider({
282
+ systemPrompt: (0, loop_decider_1.buildDeciderSystemPrompt)(),
283
+ userPrompt: (0, loop_decider_1.buildDeciderUserPrompt)({ goal, history }),
284
+ provider: nodeProvider,
285
+ model: nodeModel,
286
+ effort: nodeEffort,
287
+ cwd: req.cwd,
288
+ repoDir: req.repoDir,
289
+ onLine: logLine,
290
+ onRawLine,
291
+ onSpawn: (c) => this._activeChild.set(runId, c),
292
+ });
293
+ this._activeChild.delete(runId);
294
+ record(`loop:${runId}:decider`, dec);
295
+ logLine(`Decision: ${dec.continue ? 'continue' : 'stop'} — ${dec.reasoning}`);
296
+ history.push(`Decider: ${dec.continue ? 'continue' : 'stop'} — ${dec.reasoning}`);
297
+ (0, loop_runs_store_1.updateLoopRunCounters)(this.db, runId, {
298
+ iterationCount: iteration,
299
+ totalCostUsd: totalCost,
300
+ totalTokens,
301
+ totalDurationMs: totalDuration,
302
+ });
303
+ this.broadcast({
304
+ type: 'loop.run_progress',
305
+ projectId: req.projectId,
306
+ loopRunId: runId,
307
+ iteration,
308
+ activeNode: node.id,
309
+ reasoning: dec.reasoning,
310
+ });
311
+ // Route by the edge's explicit `branch` (the Decider's two named
312
+ // source handles: 'continue' / 'stop'). Legacy graphs authored before
313
+ // labeled branches carry no `branch` — fall back to the original
314
+ // successor-node-type heuristic (continue → first non-end, stop →
315
+ // first end) so old loops keep running identically.
316
+ const out = req.graph.edges.filter((e) => e.source === node.id);
317
+ const branchTarget = (b) => out.find((e) => e.branch === b)?.target;
318
+ const ends = succs.filter((n) => n.type === 'end');
319
+ const conts = succs.filter((n) => n.type !== 'end');
320
+ if (dec.continue) {
321
+ const target = branchTarget('continue') ?? conts[0]?.id;
322
+ if (target)
323
+ nodeId = target;
324
+ else {
325
+ outcome = 'success';
326
+ settled = true;
327
+ }
328
+ }
329
+ else {
330
+ const target = branchTarget('stop') ?? ends[0]?.id;
331
+ if (target)
332
+ nodeId = target;
333
+ else {
334
+ outcome = 'success';
335
+ settled = true;
336
+ }
337
+ }
338
+ break;
339
+ }
340
+ case 'condition':
341
+ // v1: AND/OR branching is not yet evaluated — follow the first edge.
342
+ nodeId = succs[0]?.id;
343
+ break;
344
+ default:
345
+ nodeId = succs[0]?.id;
346
+ }
347
+ // Ran off the end of the graph without an explicit End node.
348
+ if (nodeId === undefined && !settled) {
349
+ outcome = 'success';
350
+ settled = true;
351
+ }
352
+ }
353
+ }
354
+ catch (err) {
355
+ outcome = 'failed';
356
+ console.error(`[loop] run=${runId} traversal threw:`, err);
357
+ logLine(`error: ${err?.message ?? String(err)}`, 'stderr');
358
+ }
359
+ console.log(`[loop] settle run=${runId} outcome=${outcome} iterations=${iteration} cost=$${totalCost.toFixed(4)}`);
360
+ (0, loop_runs_store_1.finishLoopRun)(this.db, runId, {
361
+ outcome,
362
+ finishedAt: new Date(this.now()).toISOString(),
363
+ counters: { iterationCount: iteration, totalCostUsd: totalCost, totalTokens, totalDurationMs: totalDuration },
364
+ });
365
+ // Settle the backing job so the Jobs list + JobDetail reflect the final
366
+ // status, and emit job.finalized so an open JobDetail re-fetches + stops the
367
+ // live stream (mirrors QueueManager).
368
+ const jobStatus = outcome === 'success' ? 'completed' : outcome === 'stopped' ? 'canceled' : 'failed';
369
+ logLine(`\n■ Loop finished: ${outcome} — ${iteration} iteration${iteration === 1 ? '' : 's'}, $${totalCost.toFixed(4)}`);
370
+ try {
371
+ (0, db_1.finishJob)(this.db, runId, {
372
+ exit_code: outcome === 'success' ? 0 : 1,
373
+ status: jobStatus,
374
+ total_cost_usd: totalCost,
375
+ tokens_out: totalTokens,
376
+ duration_ms: totalDuration,
377
+ num_turns: iteration,
378
+ });
379
+ }
380
+ catch { /* best-effort */ }
381
+ this.broadcast({
382
+ type: 'job.finalized',
383
+ projectId: req.projectId,
384
+ jobId: runId,
385
+ status: jobStatus,
386
+ totals: {
387
+ tokens_in: 0,
388
+ tokens_out: totalTokens,
389
+ tokens_cache_read: 0,
390
+ tokens_cache_create: 0,
391
+ total_cost_usd: totalCost,
392
+ num_turns: iteration,
393
+ },
394
+ timestamp: new Date(this.now()).toISOString(),
395
+ });
396
+ this.broadcast({
397
+ type: 'loop.run_completed',
398
+ projectId: req.projectId,
399
+ loopRunId: runId,
400
+ railIndex: req.railIndex ?? null,
401
+ status: outcome,
402
+ ticketIds: req.ticketId != null ? [req.ticketId] : [],
403
+ });
404
+ return { runId, outcome, iterations: iteration, totalCostUsd: totalCost };
405
+ }
406
+ }
407
+ exports.LoopRunManager = LoopRunManager;