specrails-desktop 2.17.0 → 2.18.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 (389) hide show
  1. package/client/dist/assets/{ActivityFeedPage-BLDMBgwL.js → ActivityFeedPage-mGnv5SOe.js} +1 -1
  2. package/client/dist/assets/AgentsPage-BH1isg5j.js +86 -0
  3. package/client/dist/assets/AnalyticsPage-ByyshYRI.js +1 -0
  4. package/client/dist/assets/{BarChart-ouivqe5L.js → BarChart-BqHUOnl9.js} +4 -4
  5. package/client/dist/assets/CodePage-CU7T_HYs.js +2 -0
  6. package/client/dist/assets/{DesktopAnalyticsPage-DIMhuuv0.js → DesktopAnalyticsPage-CR-m97Ui.js} +1 -1
  7. package/client/dist/assets/DocsDialog-CEmZp_8V.js +11 -0
  8. package/client/dist/assets/DocsPage-DYbAQ1AT.js +11 -0
  9. package/client/dist/assets/{ExportDropdown-2XwQ08sz.js → ExportDropdown-C-o1Q4ef.js} +1 -1
  10. package/client/dist/assets/IntegrationsPage-C6fKdwVH.js +3 -0
  11. package/client/dist/assets/JobDetailPage-CKfIfBgE.js +16 -0
  12. package/client/dist/assets/JobsPage-nBng0riZ.js +1 -0
  13. package/client/dist/assets/LoopBuilderPage-Co_TYqYk.js +7 -0
  14. package/client/dist/assets/LoopsPage-a2hd37Ya.js +1 -0
  15. package/client/dist/assets/{activity-Ca2XoBi-.js → activity-D8vQJrpW.js} +1 -1
  16. package/client/dist/assets/{addon-image-bPlduuUZ.js → addon-image-Ba-TY3wr.js} +1 -1
  17. package/client/dist/assets/{addon-ligatures-BexpF7xb.js → addon-ligatures-T4ARLiuu.js} +1 -1
  18. package/client/dist/assets/{addon-webgl-Zy4IOieH.js → addon-webgl-Cd73n8eh.js} +1 -1
  19. package/client/dist/assets/{addspec-DE_VfIyP.js → addspec-BloGn_ey.js} +1 -1
  20. package/client/dist/assets/agent-9WdSxPU5.js +1 -0
  21. package/client/dist/assets/agent-ASIyVcg0.js +1 -0
  22. package/client/dist/assets/agent-Bm2tPrdr.js +1 -0
  23. package/client/dist/assets/agent-C4bO1l_v.js +1 -0
  24. package/client/dist/assets/agent-CgKsh88Z2.js +1 -0
  25. package/client/dist/assets/agent-DMUrz9ZS2.js +1 -0
  26. package/client/dist/assets/agent-Dwkz5mPS.js +1 -0
  27. package/client/dist/assets/agent-pO8x4jbR.js +1 -0
  28. package/client/dist/assets/{agents-DzhSlNZd.js → agents-BjT_xLjv.js} +1 -1
  29. package/client/dist/assets/{agentstudio-DCTpIqOx.js → agentstudio-CZm2rOzt.js} +1 -1
  30. package/client/dist/assets/{aiedit-Dirqf3Bm.js → aiedit-DYtnZU9H.js} +1 -1
  31. package/client/dist/assets/{analytics-BE49LbUk.js → analytics-B4FJ_pry.js} +1 -1
  32. package/client/dist/assets/{attachments-C2JPppoP.js → attachments-Ci6_42Kh.js} +1 -1
  33. package/client/dist/assets/{browser-Dpo_ug4c.js → browser-ZB1afzjZ.js} +1 -1
  34. package/client/dist/assets/{chat-mq1x9cxZ.js → chat-VMflhzxn.js} +1 -1
  35. package/client/dist/assets/chunk-BNv3lrIs.js +1 -0
  36. package/client/dist/assets/{code-DtIatVES.js → code-wtcs0ivb.js} +1 -1
  37. package/client/dist/assets/commands-Bc8y9eNo2.js +1 -0
  38. package/client/dist/assets/commands-Bl3-KVfj.js +1 -0
  39. package/client/dist/assets/commands-C5WQ9blx2.js +1 -0
  40. package/client/dist/assets/commands-CM99Upsz2.js +1 -0
  41. package/client/dist/assets/commands-Cc2WpuXa.js +1 -0
  42. package/client/dist/assets/commands-CckaBWdV.js +1 -0
  43. package/client/dist/assets/commands-DASXJD-l.js +1 -0
  44. package/client/dist/assets/commands-k9wbNYoi.js +1 -0
  45. package/client/dist/assets/{common-kftoE0Q-.js → common-BPQQkgJZ.js} +1 -1
  46. package/client/dist/assets/{cssMode-BijCvCxs.js → cssMode-DsArsSCT.js} +1 -1
  47. package/client/dist/assets/{dashboard-BQ3Oa8CE.js → dashboard-Cyzp4ox9.js} +1 -1
  48. package/client/dist/assets/{dist-js-D7m9--SN.js → dist-js-BHhtSerX.js} +1 -1
  49. package/client/dist/assets/{dist-js-nZhsq1qo.js → dist-js-CUegfF6k.js} +1 -1
  50. package/client/dist/assets/{editor.api2-CrNrMVfe.js → editor.api2-T7u8kQWG.js} +1 -1
  51. package/client/dist/assets/{editor.main-DMIcqwXb.js → editor.main-G3Sl9C67.js} +2 -2
  52. package/client/dist/assets/{explore-CBYU76JD.js → explore-BKVV5JSu.js} +1 -1
  53. package/client/dist/assets/{freemarker2-CP2AizpZ.js → freemarker2-pBzQNgFi.js} +1 -1
  54. package/client/dist/assets/{handlebars-8ElanPOm.js → handlebars-CtBTCnBI.js} +1 -1
  55. package/client/dist/assets/{html-Mu3b73tG.js → html-Dx3DYBzY.js} +1 -1
  56. package/client/dist/assets/{htmlMode-tHprGCEv.js → htmlMode-In7frFOo.js} +1 -1
  57. package/client/dist/assets/index-B0FxQALR.css +2 -0
  58. package/client/dist/assets/index-B7vB20Jw.js +152 -0
  59. package/client/dist/assets/{integrations-jJGaKwff.js → integrations-CuWnMR5L.js} +1 -1
  60. package/client/dist/assets/{javascript-DlG0apUe.js → javascript-BKcn-YkN.js} +1 -1
  61. package/client/dist/assets/{jira-BwACz875.js → jira-DXMoAKWD.js} +1 -1
  62. package/client/dist/assets/{jobs-DRInW0AE.js → jobs---dwa4Nf.js} +1 -1
  63. package/client/dist/assets/{jsonMode-Cr0T4SXO.js → jsonMode-jNREoQ3C.js} +1 -1
  64. package/client/dist/assets/{lib-Bu76JzdB.js → lib-PQoHlR5u.js} +1 -1
  65. package/client/dist/assets/{liquid-QgTgbClC.js → liquid-CtRkGqBu.js} +1 -1
  66. package/client/dist/assets/{loops-DfGadne0.js → loops-D8Iu6Jss.js} +1 -1
  67. package/client/dist/assets/{lspLanguageFeatures-D-BxkRza.js → lspLanguageFeatures-Cl3gvD_W.js} +1 -1
  68. package/client/dist/assets/{mcp-D-zQCO5E.js → mcp-CIH0pH1K.js} +1 -1
  69. package/client/dist/assets/{mdx-B2QTaeRe.js → mdx-B_nJldHz.js} +1 -1
  70. package/client/dist/assets/{monaco.contribution-CkdGfXCC.js → monaco.contribution-PUeaZk8T.js} +2 -2
  71. package/client/dist/assets/{nav-CyHKTZxn.js → nav-DE3yWQw6.js} +1 -1
  72. package/client/dist/assets/{python-BlF0qnMQ.js → python-DS1Ce-A6.js} +1 -1
  73. package/client/dist/assets/{razor--3PSy3lb.js → razor-L41Qg9OK.js} +1 -1
  74. package/client/dist/assets/settings-BKmORxlB2.js +1 -0
  75. package/client/dist/assets/settings-BVv2JgQ8.js +1 -0
  76. package/client/dist/assets/settings-Bghm3pAV.js +1 -0
  77. package/client/dist/assets/settings-BuC_aplX.js +1 -0
  78. package/client/dist/assets/settings-Bw_Qk8E32.js +1 -0
  79. package/client/dist/assets/settings-CAKgJv7p.js +1 -0
  80. package/client/dist/assets/settings-CEUgk1Ck.js +1 -0
  81. package/client/dist/assets/settings-C_PDE20s2.js +1 -0
  82. package/client/dist/assets/setup-4us-Bz3H.js +1 -0
  83. package/client/dist/assets/setup-BYoDYoPY2.js +1 -0
  84. package/client/dist/assets/setup-Bf9eLKxU.js +1 -0
  85. package/client/dist/assets/setup-C5UbM0RP.js +1 -0
  86. package/client/dist/assets/setup-CqGrWb6G.js +1 -0
  87. package/client/dist/assets/setup-JFkWWZnR.js +1 -0
  88. package/client/dist/assets/setup-faMApo0U2.js +1 -0
  89. package/client/dist/assets/setup-tloaTobG2.js +1 -0
  90. package/client/dist/assets/{specs-Dslng_yW.js → specs-BYeAbIEy.js} +1 -1
  91. package/client/dist/assets/{terminal-XciDq6VV.js → terminal-DFPNRLKp.js} +1 -1
  92. package/client/dist/assets/{tickets-DbcMWq5L.js → tickets-CkfbISuK.js} +1 -1
  93. package/client/dist/assets/{tsMode-CHfsvcOI.js → tsMode-BC1g186U.js} +1 -1
  94. package/client/dist/assets/{typescript-Bjxf6BOp.js → typescript-BGlRGvJW.js} +1 -1
  95. package/client/dist/assets/upload-PnJDPHZH.js +1 -0
  96. package/client/dist/assets/{value-fEDhg5Bd.js → value-BTDefcOV.js} +1 -1
  97. package/client/dist/assets/{workers-CAAvSda9.js → workers-CXcXtO3b.js} +1 -1
  98. package/client/dist/assets/{xml-iYCSJ4oL.js → xml-JdZZaIhJ.js} +1 -1
  99. package/client/dist/assets/{yaml-DtO-_yDY.js → yaml-Cm82BVBt.js} +1 -1
  100. package/client/dist/index.html +30 -29
  101. package/docs/guide/de/integrations/6-agent-chat.md +64 -0
  102. package/docs/guide/en/integrations/6-agent-chat.md +64 -0
  103. package/docs/guide/es/integrations/6-agent-chat.md +64 -0
  104. package/docs/guide/fr/integrations/6-agent-chat.md +64 -0
  105. package/docs/guide/it/integrations/6-agent-chat.md +64 -0
  106. package/docs/guide/ja/integrations/6-agent-chat.md +64 -0
  107. package/docs/guide/pt/integrations/6-agent-chat.md +64 -0
  108. package/docs/guide/zh/integrations/6-agent-chat.md +64 -0
  109. package/package.json +1 -1
  110. package/server/dist/agent-chat-manager.js +224 -0
  111. package/server/dist/agent-chat-router.js +148 -0
  112. package/server/dist/agent-cwd-manager.js +94 -0
  113. package/server/dist/agent-mcp-config.js +208 -0
  114. package/server/dist/agent-store.js +59 -0
  115. package/server/dist/agent-tier.js +86 -0
  116. package/server/dist/desktop-db.js +27 -0
  117. package/server/dist/index.js +8 -0
  118. package/server/dist/mcp/tools/setup.js +30 -50
  119. package/server/dist/mcp/tools/types.js +21 -2
  120. package/server/dist/project-router-setup.js +15 -1
  121. package/server/dist/providers/codex-adapter.js +19 -3
  122. package/server/dist/setup-manager.js +28 -0
  123. package/server/dist/spawn-lifecycle.js +21 -13
  124. package/server/dist/workspace-manager.js +11 -0
  125. package/client/dist/assets/AgentsPage-CFaIiyAA.js +0 -86
  126. package/client/dist/assets/AnalyticsPage-DY8ECq2R.js +0 -1
  127. package/client/dist/assets/CodePage-DAFtRafz.js +0 -2
  128. package/client/dist/assets/DocsDialog-DBYhv3SM.js +0 -11
  129. package/client/dist/assets/DocsPage-DeLRn8W_.js +0 -11
  130. package/client/dist/assets/IntegrationsPage-DB-a4FDV.js +0 -3
  131. package/client/dist/assets/JobDetailPage-C3lljMiV.js +0 -16
  132. package/client/dist/assets/JobsPage-CzquwJ6G.js +0 -1
  133. package/client/dist/assets/LoopBuilderPage-TkoalfW5.js +0 -7
  134. package/client/dist/assets/LoopsPage-ypooVAQ5.js +0 -1
  135. package/client/dist/assets/chunk-CilyBKbf.js +0 -1
  136. package/client/dist/assets/commands-CwypztRT.js +0 -1
  137. package/client/dist/assets/commands-CxReviyc.js +0 -1
  138. package/client/dist/assets/commands-CzP1qQyD.js +0 -1
  139. package/client/dist/assets/commands-DGP46UxM.js +0 -1
  140. package/client/dist/assets/commands-DQvoMqa7.js +0 -1
  141. package/client/dist/assets/commands-DeKaz04Q.js +0 -1
  142. package/client/dist/assets/commands-DwYhwQsF.js +0 -1
  143. package/client/dist/assets/commands-lm6Vlght.js +0 -1
  144. package/client/dist/assets/index-BRMyOS1B.css +0 -2
  145. package/client/dist/assets/index-CDEQ4aU_.js +0 -144
  146. package/client/dist/assets/settings-BN4dufu6.js +0 -1
  147. package/client/dist/assets/settings-Bap_q3rt.js +0 -1
  148. package/client/dist/assets/settings-BlYjWMy5.js +0 -1
  149. package/client/dist/assets/settings-C7t_uUz4.js +0 -1
  150. package/client/dist/assets/settings-CIFrUsKt.js +0 -1
  151. package/client/dist/assets/settings-Cbj9VWmI.js +0 -1
  152. package/client/dist/assets/settings-D7saHMU8.js +0 -1
  153. package/client/dist/assets/settings-DrRjM5e8.js +0 -1
  154. package/client/dist/assets/setup-BiQ7ut8d.js +0 -1
  155. package/client/dist/assets/setup-BxvjG5Xp.js +0 -1
  156. package/client/dist/assets/setup-CQ-F6Ciq.js +0 -1
  157. package/client/dist/assets/setup-D2JlpJEz.js +0 -1
  158. package/client/dist/assets/setup-DG1R6J6o.js +0 -1
  159. package/client/dist/assets/setup-DuFJZJsm.js +0 -1
  160. package/client/dist/assets/setup-DyJszDqm.js +0 -1
  161. package/client/dist/assets/setup-Ynw19V8w.js +0 -1
  162. package/client/dist/assets/upload-x42OZJr2.js +0 -1
  163. /package/client/dist/assets/{abap-BFJA_Joo.js → abap-CpHUwCVK.js} +0 -0
  164. /package/client/dist/assets/{activity-oLgwaVyt.js → activity-B82z3Pna.js} +0 -0
  165. /package/client/dist/assets/{activity-D62CDlQz.js → activity-BId0pwuS2.js} +0 -0
  166. /package/client/dist/assets/{activity-BXBMi28x.js → activity-BVpZrs0J.js} +0 -0
  167. /package/client/dist/assets/{activity-DAFuSwnt.js → activity-BySXFT2J2.js} +0 -0
  168. /package/client/dist/assets/{activity-By6I70Ws.js → activity-DIWnV0Pl.js} +0 -0
  169. /package/client/dist/assets/{activity-Did0FnrH.js → activity-Uj576ckT.js} +0 -0
  170. /package/client/dist/assets/{activity-CfedQaof.js → activity-w1qMFwxh.js} +0 -0
  171. /package/client/dist/assets/{addspec-Cvm06hGe.js → addspec-B9dY_F5T.js} +0 -0
  172. /package/client/dist/assets/{addspec-gthH-H1g.js → addspec-B9nhMikb.js} +0 -0
  173. /package/client/dist/assets/{addspec-DwoLTocx.js → addspec-BtNaJHu2.js} +0 -0
  174. /package/client/dist/assets/{addspec-B2fTENuq.js → addspec-D43TlMVp2.js} +0 -0
  175. /package/client/dist/assets/{addspec-CFsLeQKV.js → addspec-DfgIZ5lO.js} +0 -0
  176. /package/client/dist/assets/{addspec-CkHI5c5H.js → addspec-W0r16BOC2.js} +0 -0
  177. /package/client/dist/assets/{addspec-BmggyuCD.js → addspec-tbxr3sHa.js} +0 -0
  178. /package/client/dist/assets/{agents-B-rVIhuw.js → agents-52t8NVpS.js} +0 -0
  179. /package/client/dist/assets/{agents-CZQvKDIb.js → agents-BAxAwHy-.js} +0 -0
  180. /package/client/dist/assets/{agents-D7nWnLg6.js → agents-BUYBb825.js} +0 -0
  181. /package/client/dist/assets/{agents-CuRY-Zg_.js → agents-C07al8rI.js} +0 -0
  182. /package/client/dist/assets/{agents-E-42N3b1.js → agents-DfGvYtpq2.js} +0 -0
  183. /package/client/dist/assets/{agents-4fnKlwcG.js → agents-Dli56gr82.js} +0 -0
  184. /package/client/dist/assets/{agents-BRUz6RJs.js → agents-Eqw_AQ6H.js} +0 -0
  185. /package/client/dist/assets/{agentstudio-Dq-bZrI3.js → agentstudio-0o3XKrcy.js} +0 -0
  186. /package/client/dist/assets/{agentstudio-CIe9C892.js → agentstudio-Bu8m5jDw.js} +0 -0
  187. /package/client/dist/assets/{agentstudio-D0bQBWpI.js → agentstudio-C7xxqVWn.js} +0 -0
  188. /package/client/dist/assets/{agentstudio-n0pmwz2I.js → agentstudio-D-UoNAIA.js} +0 -0
  189. /package/client/dist/assets/{agentstudio-CYGk-jjo.js → agentstudio-OJjjz6Uf2.js} +0 -0
  190. /package/client/dist/assets/{agentstudio-BtASTJl7.js → agentstudio-ObTpVbBP.js} +0 -0
  191. /package/client/dist/assets/{agentstudio-CwiS3BTp.js → agentstudio-PzFB3vfh2.js} +0 -0
  192. /package/client/dist/assets/{aiedit-s1T1EMK8.js → aiedit-BIQqv2XM2.js} +0 -0
  193. /package/client/dist/assets/{aiedit-CunYxmW0.js → aiedit-BaLZalLa.js} +0 -0
  194. /package/client/dist/assets/{aiedit-CZBZAyeK.js → aiedit-BjlUzasF.js} +0 -0
  195. /package/client/dist/assets/{aiedit-uIDh2tlc.js → aiedit-BmDufyGU.js} +0 -0
  196. /package/client/dist/assets/{aiedit-1Z-UYKFb.js → aiedit-DAM_WRAl2.js} +0 -0
  197. /package/client/dist/assets/{aiedit-CYnsp1Iv.js → aiedit-DRrNiaB4.js} +0 -0
  198. /package/client/dist/assets/{aiedit-BAJM9Cch.js → aiedit-sP_iao2j.js} +0 -0
  199. /package/client/dist/assets/{analytics-DPIkvuTS.js → analytics-BDqqCzN9.js} +0 -0
  200. /package/client/dist/assets/{analytics-DVFNjjAv.js → analytics-B_rxNC9X.js} +0 -0
  201. /package/client/dist/assets/{analytics-DRexho82.js → analytics-C4z_NgJZ.js} +0 -0
  202. /package/client/dist/assets/{analytics-DPmCEzvJ.js → analytics-DL2vJCbq.js} +0 -0
  203. /package/client/dist/assets/{analytics-g9ASAR_r.js → analytics-Ds_PNxtO2.js} +0 -0
  204. /package/client/dist/assets/{analytics-DZKGAUdm.js → analytics-MVJRjcY52.js} +0 -0
  205. /package/client/dist/assets/{analytics-B95FiMuD.js → analytics-e1mkNTgy2.js} +0 -0
  206. /package/client/dist/assets/{apex-C1f-z6NO.js → apex-DlD7oJX6.js} +0 -0
  207. /package/client/dist/assets/{atom-one-dark-CT4DolNR.js → atom-one-dark-BrRN8wBy.js} +0 -0
  208. /package/client/dist/assets/{attachments-Bj3HHTXV.js → attachments-8QCKHCEF2.js} +0 -0
  209. /package/client/dist/assets/{attachments-FFb2xHc_.js → attachments-BP0Ac1yt.js} +0 -0
  210. /package/client/dist/assets/{attachments-CnPS8YWu.js → attachments-CJaO1vCH.js} +0 -0
  211. /package/client/dist/assets/{attachments-C3skjFyH.js → attachments-CPBVeHX72.js} +0 -0
  212. /package/client/dist/assets/{attachments--jr2QLGv.js → attachments-CV2uaTGE.js} +0 -0
  213. /package/client/dist/assets/{attachments-DejzxE0C.js → attachments-CaOjdN332.js} +0 -0
  214. /package/client/dist/assets/{attachments-BgqfeoOx.js → attachments-Db7JfA8a.js} +0 -0
  215. /package/client/dist/assets/{azcli-Dr0WBJTP.js → azcli-CmXFAbc-.js} +0 -0
  216. /package/client/dist/assets/{bat-BKG3k49W.js → bat-DdNJq4hH.js} +0 -0
  217. /package/client/dist/assets/{bicep-DXs2wTcS.js → bicep-Bppz7WOA.js} +0 -0
  218. /package/client/dist/assets/{browser-DS6xnywt.js → browser-0xeW8fUa2.js} +0 -0
  219. /package/client/dist/assets/{browser-Dm_Of27q.js → browser-543jbxE4.js} +0 -0
  220. /package/client/dist/assets/{browser-Dm6F9bqQ.js → browser-C9rUUxo4.js} +0 -0
  221. /package/client/dist/assets/{browser-BS9t1qMc.js → browser-CEqx11iy.js} +0 -0
  222. /package/client/dist/assets/{browser-BpMo-amf.js → browser-CLC0etB_2.js} +0 -0
  223. /package/client/dist/assets/{browser-CsC3NOZ-.js → browser-DyG2l9Wa.js} +0 -0
  224. /package/client/dist/assets/{browser-U3iouSXn.js → browser-ExmELH702.js} +0 -0
  225. /package/client/dist/assets/{cameligo-Detknuxp.js → cameligo-0MRMdSmg.js} +0 -0
  226. /package/client/dist/assets/{chat-DyTbH-0F.js → chat-CCytD3P8.js} +0 -0
  227. /package/client/dist/assets/{chat-D78VMi-v.js → chat-CN8BMAMc.js} +0 -0
  228. /package/client/dist/assets/{chat-De1RgOXU.js → chat-Cu6F3d6g2.js} +0 -0
  229. /package/client/dist/assets/{chat-Cy78lw1n.js → chat-DagUyoVA.js} +0 -0
  230. /package/client/dist/assets/{chat-B2tWTbZL.js → chat-IjutwlBg.js} +0 -0
  231. /package/client/dist/assets/{chat-sqZyn0C4.js → chat-NQPCBQo72.js} +0 -0
  232. /package/client/dist/assets/{chat-CXOJHDUZ.js → chat-vVpwkOp52.js} +0 -0
  233. /package/client/dist/assets/{clojure-CCu4UNKg.js → clojure-B5dKj863.js} +0 -0
  234. /package/client/dist/assets/{clsx-pVRQNNR0.js → clsx-1dnHRn8W.js} +0 -0
  235. /package/client/dist/assets/{code-CLTuhuvM.js → code-32l6Naxu2.js} +0 -0
  236. /package/client/dist/assets/{code-CwGlPuwb.js → code-BaG05zK62.js} +0 -0
  237. /package/client/dist/assets/{code-DFCDV5e1.js → code-C5cibF2F.js} +0 -0
  238. /package/client/dist/assets/{code-Do27r8WG.js → code-Ce-nBIwj.js} +0 -0
  239. /package/client/dist/assets/{code-Drc7f197.js → code-Dap54vOb.js} +0 -0
  240. /package/client/dist/assets/{code-6LokPmIE.js → code-Dhb6jbAU.js} +0 -0
  241. /package/client/dist/assets/{code-Djq48g0s.js → code-_NOCo-Ol2.js} +0 -0
  242. /package/client/dist/assets/{coffee-Bam7Y9IR.js → coffee-DSbWjzk7.js} +0 -0
  243. /package/client/dist/assets/{common-DH9A-wTo.js → common-0aY9HQBP.js} +0 -0
  244. /package/client/dist/assets/{common-C_-0fZsp.js → common-5jScBJqt.js} +0 -0
  245. /package/client/dist/assets/{common-SyTaRc_K.js → common-BdaRciTu.js} +0 -0
  246. /package/client/dist/assets/{common-B0crwfK0.js → common-CHARW6MZ2.js} +0 -0
  247. /package/client/dist/assets/{common-BlqmiTj4.js → common-CQCJzAQq2.js} +0 -0
  248. /package/client/dist/assets/{common-DJM41wFl.js → common-DNhhqjlJ.js} +0 -0
  249. /package/client/dist/assets/{common-CIUPtPdu.js → common-YdyZtmaI2.js} +0 -0
  250. /package/client/dist/assets/{cpp-BhOkAe-9.js → cpp-SBF8SAh9.js} +0 -0
  251. /package/client/dist/assets/{csharp-BgHArJNx.js → csharp-Lzuq_1t-.js} +0 -0
  252. /package/client/dist/assets/{csp-Xzh56_3-.js → csp-BEzxZqi1.js} +0 -0
  253. /package/client/dist/assets/{css-CD7-RYRS.js → css-C3D3PKln.js} +0 -0
  254. /package/client/dist/assets/{cypher-l2xAdF3X.js → cypher-CQ3dEPmv.js} +0 -0
  255. /package/client/dist/assets/{dart-CDOWeyzt.js → dart-DCPmxolI.js} +0 -0
  256. /package/client/dist/assets/{dashboard-Dxgb47lO.js → dashboard-BTiH361F.js} +0 -0
  257. /package/client/dist/assets/{dashboard-B1rs1wki.js → dashboard-COSm_14E.js} +0 -0
  258. /package/client/dist/assets/{dashboard-D9QPCmv3.js → dashboard-CfOs70FN2.js} +0 -0
  259. /package/client/dist/assets/{dashboard-ajT8usAi.js → dashboard-CzHDp14_2.js} +0 -0
  260. /package/client/dist/assets/{dashboard-TuElRCZX.js → dashboard-D6Woy2B5.js} +0 -0
  261. /package/client/dist/assets/{dashboard-zwN2xswo.js → dashboard-DwbH4oVH.js} +0 -0
  262. /package/client/dist/assets/{dashboard-UWXs9_wy.js → dashboard-cWhU3krA2.js} +0 -0
  263. /package/client/dist/assets/{dockerfile-Dv8atx7v.js → dockerfile-B_2_aaTO.js} +0 -0
  264. /package/client/dist/assets/{ecl-DIYTlbD_.js → ecl-rADUeXsV.js} +0 -0
  265. /package/client/dist/assets/{elixir-CR7PLJja.js → elixir-DOd-TuJO.js} +0 -0
  266. /package/client/dist/assets/{explore-Jkw93usp.js → explore-B82VMUJ_.js} +0 -0
  267. /package/client/dist/assets/{explore-CvM08PpD.js → explore-BGz1rvQE.js} +0 -0
  268. /package/client/dist/assets/{explore-CE0wSNV1.js → explore-BzdMEpuf.js} +0 -0
  269. /package/client/dist/assets/{explore-mLI5eXks.js → explore-CbW0381i2.js} +0 -0
  270. /package/client/dist/assets/{explore-BYFCCka4.js → explore-ChtxIiSM.js} +0 -0
  271. /package/client/dist/assets/{explore-kK239uoZ.js → explore-DfcUfiPw2.js} +0 -0
  272. /package/client/dist/assets/{explore-CSDcRUNj.js → explore-yRF7wFSp2.js} +0 -0
  273. /package/client/dist/assets/{flow9-DCK5-6NG.js → flow9-Bf7zg_j0.js} +0 -0
  274. /package/client/dist/assets/{format-command-rVyGPxWE.js → format-command-D96da4fZ.js} +0 -0
  275. /package/client/dist/assets/{fsharp-DJ1xiUVU.js → fsharp-BqROZjbA.js} +0 -0
  276. /package/client/dist/assets/{go-BEvbHcbb.js → go-oQTP5uLe.js} +0 -0
  277. /package/client/dist/assets/{graphql-DN1ktXnz.js → graphql-CSHfTU3q.js} +0 -0
  278. /package/client/dist/assets/{hcl-DJaIjIEr.js → hcl-CeG-RoFJ.js} +0 -0
  279. /package/client/dist/assets/{ini-PlKfLEnb.js → ini-B2Dv4ohc.js} +0 -0
  280. /package/client/dist/assets/{integrations-TwQV3sLw.js → integrations-B0OBPU9_.js} +0 -0
  281. /package/client/dist/assets/{integrations-D8M8eJH2.js → integrations-BA5pM05t.js} +0 -0
  282. /package/client/dist/assets/{integrations-TJuDBl-9.js → integrations-BJvrlcc7.js} +0 -0
  283. /package/client/dist/assets/{integrations-Bdk5dMyv.js → integrations-CPKYkG36.js} +0 -0
  284. /package/client/dist/assets/{integrations-B8hT0Qf_.js → integrations-CnrX7vqw2.js} +0 -0
  285. /package/client/dist/assets/{integrations-Cj_BsXxJ.js → integrations-DBou159M2.js} +0 -0
  286. /package/client/dist/assets/{integrations-CYqz7YFS.js → integrations-DeJFRyXz2.js} +0 -0
  287. /package/client/dist/assets/{java-3PUwMlx-.js → java-aUDDeufb.js} +0 -0
  288. /package/client/dist/assets/{jira-DHFnvEm8.js → jira-BDgxAFTF2.js} +0 -0
  289. /package/client/dist/assets/{jira-NhkbrDXe.js → jira-BwLBgmfI2.js} +0 -0
  290. /package/client/dist/assets/{jira-wiIvGma5.js → jira-D3GGzXOI.js} +0 -0
  291. /package/client/dist/assets/{jira-C95FN3yk.js → jira-DeotPx6P2.js} +0 -0
  292. /package/client/dist/assets/{jira-CNUUgTs4.js → jira-NwlUCcQD.js} +0 -0
  293. /package/client/dist/assets/{jira-BoRyckbb.js → jira-Srx2P5zS.js} +0 -0
  294. /package/client/dist/assets/{jira--rI5PfuE.js → jira-mi37H4Kt.js} +0 -0
  295. /package/client/dist/assets/{jobs-Bjt5PyVv.js → jobs-B56vQEkh2.js} +0 -0
  296. /package/client/dist/assets/{jobs-D2QVTPbz.js → jobs-BYIk6Tg_.js} +0 -0
  297. /package/client/dist/assets/{jobs-LK1ImiDH.js → jobs-C-lM-iax2.js} +0 -0
  298. /package/client/dist/assets/{jobs-BmTs4Bn3.js → jobs-CYdevGgT.js} +0 -0
  299. /package/client/dist/assets/{jobs-yPz4Dvi-.js → jobs-D8adm2BX.js} +0 -0
  300. /package/client/dist/assets/{jobs-Cchy-18B.js → jobs-K_Y8KAZ2.js} +0 -0
  301. /package/client/dist/assets/{jobs-Bjp7v9aA.js → jobs-OwM0ZWhI2.js} +0 -0
  302. /package/client/dist/assets/{julia-DC6LnY15.js → julia-C5LbruO5.js} +0 -0
  303. /package/client/dist/assets/{kotlin-CoGntbZF.js → kotlin-C6mOuSKk.js} +0 -0
  304. /package/client/dist/assets/{less-DCFflTXL.js → less-D5qC3caW.js} +0 -0
  305. /package/client/dist/assets/{lexon-BTOivnjP.js → lexon-Dr2La-fj.js} +0 -0
  306. /package/client/dist/assets/{loops-C3OaXou9.js → loops-BDhEfnx82.js} +0 -0
  307. /package/client/dist/assets/{loops-Cv6Aeszq.js → loops-BM5QggwH.js} +0 -0
  308. /package/client/dist/assets/{loops-BU314kSd.js → loops-CPjLAAxj.js} +0 -0
  309. /package/client/dist/assets/{loops-LqZ4MC6J.js → loops-DKnk79TB2.js} +0 -0
  310. /package/client/dist/assets/{loops-SbkOdciY.js → loops-DLW0aFMm.js} +0 -0
  311. /package/client/dist/assets/{loops-BFIcYvpy.js → loops-Dcv3dFy_2.js} +0 -0
  312. /package/client/dist/assets/{loops-IQe23ef6.js → loops-y7wFRx6M.js} +0 -0
  313. /package/client/dist/assets/{lua-ClKCZMmP.js → lua-Zy6u5z6Z.js} +0 -0
  314. /package/client/dist/assets/{m3-25hKFWMb.js → m3-C7XHeDz_.js} +0 -0
  315. /package/client/dist/assets/{markdown-BGAj9q0K.js → markdown-PiF7r-2h.js} +0 -0
  316. /package/client/dist/assets/{mcp-DyERgHA2.js → mcp-B4irfLJ2.js} +0 -0
  317. /package/client/dist/assets/{mcp-BSCAtU6Y.js → mcp-B6nwJJmR.js} +0 -0
  318. /package/client/dist/assets/{mcp-DFG9OEXM.js → mcp-BOoEGTSQ.js} +0 -0
  319. /package/client/dist/assets/{mcp-Cb6fUaFJ.js → mcp-BP7PpbDr2.js} +0 -0
  320. /package/client/dist/assets/{mcp-CyP0quZ7.js → mcp-CNgFiTEh.js} +0 -0
  321. /package/client/dist/assets/{mcp-DCTNWpfB.js → mcp-D3E4_Mmk2.js} +0 -0
  322. /package/client/dist/assets/{mcp-DEFGJ1OJ.js → mcp-mupPuPDO2.js} +0 -0
  323. /package/client/dist/assets/{mips-BrVgY2Pf.js → mips-nn07GiyE.js} +0 -0
  324. /package/client/dist/assets/{msdax-7EhcRsm8.js → msdax-DaKFVMST.js} +0 -0
  325. /package/client/dist/assets/{mysql-B5_atCBH.js → mysql-C6d6xrbU.js} +0 -0
  326. /package/client/dist/assets/{nav-Br2AivTc.js → nav-1hj2izqB2.js} +0 -0
  327. /package/client/dist/assets/{nav-CFofO7QO.js → nav-BBUKMAu-2.js} +0 -0
  328. /package/client/dist/assets/{nav-_-siCrRv.js → nav-CLqkP-26.js} +0 -0
  329. /package/client/dist/assets/{nav-zyBhf29M.js → nav-Cv46L837.js} +0 -0
  330. /package/client/dist/assets/{nav-rO4Nqnh-.js → nav-DgA-rjLk2.js} +0 -0
  331. /package/client/dist/assets/{nav-DqyLGJmF.js → nav-EG2pr6vN.js} +0 -0
  332. /package/client/dist/assets/{nav-B2Cv1vjz.js → nav-r3mIJVfU.js} +0 -0
  333. /package/client/dist/assets/{objective-c-CLUvUl9T.js → objective-c-B6KMtI-k.js} +0 -0
  334. /package/client/dist/assets/{pascal-BnU1HU42.js → pascal-DYr8MPE7.js} +0 -0
  335. /package/client/dist/assets/{pascaligo-dsKvjFzi.js → pascaligo-B3pd-jJ_.js} +0 -0
  336. /package/client/dist/assets/{perl-CZne1GYG.js → perl-z2xUzoaL.js} +0 -0
  337. /package/client/dist/assets/{pgsql-CMa5W_iF.js → pgsql-DUvT-09-.js} +0 -0
  338. /package/client/dist/assets/{php-Cu4mS35J.js → php-CGyOmJfy.js} +0 -0
  339. /package/client/dist/assets/{pla-B7COwnnm.js → pla-CMqgvyD0.js} +0 -0
  340. /package/client/dist/assets/{postiats-GLeNtnBQ.js → postiats-Dx5LUn4Z.js} +0 -0
  341. /package/client/dist/assets/{powerquery-BqPf3R1W.js → powerquery-DazYxvJ6.js} +0 -0
  342. /package/client/dist/assets/{powershell-D9btb5su.js → powershell-B3nwlDka.js} +0 -0
  343. /package/client/dist/assets/{protobuf-6GHSuaNm.js → protobuf-BGuu2u2s.js} +0 -0
  344. /package/client/dist/assets/{pug-ct6rYKQd.js → pug-Dbte1MXA.js} +0 -0
  345. /package/client/dist/assets/{qsharp-CIrAQNPy.js → qsharp-DJLwBOvG.js} +0 -0
  346. /package/client/dist/assets/{r-Bk-a0sON.js → r-DmGBRSHj.js} +0 -0
  347. /package/client/dist/assets/{redis-C9yfaWYh.js → redis-VvUYn0a4.js} +0 -0
  348. /package/client/dist/assets/{redshift-DaOT1jRU.js → redshift-6GdDrxg3.js} +0 -0
  349. /package/client/dist/assets/{restructuredtext-C1t9LQfS.js → restructuredtext-CfAB8aRU.js} +0 -0
  350. /package/client/dist/assets/{ruby-COiPZJjc.js → ruby-CIYK6MXY.js} +0 -0
  351. /package/client/dist/assets/{rust--gZOJZzj.js → rust-CTHZfjj9.js} +0 -0
  352. /package/client/dist/assets/{sb-NHf3iazF.js → sb-CkKTdkz_.js} +0 -0
  353. /package/client/dist/assets/{scala-D1nO1TBd.js → scala-Bx-Wr9TM.js} +0 -0
  354. /package/client/dist/assets/{scheme-Bref42s5.js → scheme-Dcfi6f83.js} +0 -0
  355. /package/client/dist/assets/{scss-BS9CCLr3.js → scss-ByL-7PFQ.js} +0 -0
  356. /package/client/dist/assets/{shell-DT3joMBB.js → shell-Csnb9Yqd.js} +0 -0
  357. /package/client/dist/assets/{solidity-o2Fm9dvg.js → solidity-Dbw5-0QP.js} +0 -0
  358. /package/client/dist/assets/{sophia-Dj1N6Xqb.js → sophia-ZQ3saZQx.js} +0 -0
  359. /package/client/dist/assets/{sparql-D2fjpTWm.js → sparql-BEtOel8u.js} +0 -0
  360. /package/client/dist/assets/{specs-CYA5Bv7Q.js → specs-B5XOoH-02.js} +0 -0
  361. /package/client/dist/assets/{specs-DFCFg51O.js → specs-B8mwScoM2.js} +0 -0
  362. /package/client/dist/assets/{specs-DzlROLy7.js → specs-Bj_3fDrQ.js} +0 -0
  363. /package/client/dist/assets/{specs-CCXV-eSF.js → specs-CWNKAScX2.js} +0 -0
  364. /package/client/dist/assets/{specs-C-WXGBqy.js → specs-D4z5d8uq.js} +0 -0
  365. /package/client/dist/assets/{specs-C57koADY.js → specs-DgvIRZtM.js} +0 -0
  366. /package/client/dist/assets/{specs-DwRPiJTc.js → specs-DuRzE5wE.js} +0 -0
  367. /package/client/dist/assets/{sql-BArhsPUz.js → sql-DGPDbzB7.js} +0 -0
  368. /package/client/dist/assets/{st-Bd2-tALj.js → st-GeW2MOgh.js} +0 -0
  369. /package/client/dist/assets/{swift-DvEpXBKj.js → swift-BVYTN8jq.js} +0 -0
  370. /package/client/dist/assets/{systemverilog-mHEbsnVH.js → systemverilog-Deg2a8Lr.js} +0 -0
  371. /package/client/dist/assets/{tcl-DePutp1e.js → tcl-6DnLmROF.js} +0 -0
  372. /package/client/dist/assets/{terminal-CnObBCAh.js → terminal-B2sExUis.js} +0 -0
  373. /package/client/dist/assets/{terminal-BRQMrU74.js → terminal-CH0fdHvP.js} +0 -0
  374. /package/client/dist/assets/{terminal-BTiemy98.js → terminal-CPMigUX7.js} +0 -0
  375. /package/client/dist/assets/{terminal-2S-Wsk6L.js → terminal-DOwfkxqa2.js} +0 -0
  376. /package/client/dist/assets/{terminal-DcPKgKMN.js → terminal-DjSSa5qP.js} +0 -0
  377. /package/client/dist/assets/{terminal-CmP5riTU.js → terminal-DoMTHedv2.js} +0 -0
  378. /package/client/dist/assets/{terminal-CjQeWfpp.js → terminal-w0i-OCp32.js} +0 -0
  379. /package/client/dist/assets/{tickets-D75vyZJj.js → tickets-CQTana1u.js} +0 -0
  380. /package/client/dist/assets/{tickets-CcTcvwKS.js → tickets-D00mSt832.js} +0 -0
  381. /package/client/dist/assets/{tickets-BSyaZI8d.js → tickets-D7CSuWjO.js} +0 -0
  382. /package/client/dist/assets/{tickets-7kl_0jgM.js → tickets-Dq7HDy8n.js} +0 -0
  383. /package/client/dist/assets/{tickets-D79ZT2SZ.js → tickets-DvbFg_-c2.js} +0 -0
  384. /package/client/dist/assets/{tickets-mxd23fWX.js → tickets-heDAaj8C2.js} +0 -0
  385. /package/client/dist/assets/{tickets-ZA_2iPd1.js → tickets-onKZz6Dd.js} +0 -0
  386. /package/client/dist/assets/{twig-DQ2zIX0b.js → twig-XbbXW9Yz.js} +0 -0
  387. /package/client/dist/assets/{typespec-DPoaiGTJ.js → typespec-CRlV8GhI.js} +0 -0
  388. /package/client/dist/assets/{vb-Jo98wdMr.js → vb-BHE2izPH.js} +0 -0
  389. /package/client/dist/assets/{wgsl-DZc4h_eS.js → wgsl-Bf5vkfmY.js} +0 -0
@@ -0,0 +1,208 @@
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.resolveBridgeScript = resolveBridgeScript;
7
+ exports.resolveNodeCommand = resolveNodeCommand;
8
+ exports.buildSpecrailsMcpEntry = buildSpecrailsMcpEntry;
9
+ exports.mergeSpecrailsIntoWorkspaceMcp = mergeSpecrailsIntoWorkspaceMcp;
10
+ exports.buildAgentMcpArgs = buildAgentMcpArgs;
11
+ exports.prepareAgentMcp = prepareAgentMcp;
12
+ const fs_1 = __importDefault(require("fs"));
13
+ const path_1 = __importDefault(require("path"));
14
+ const os_1 = __importDefault(require("os"));
15
+ const path_resolver_1 = require("./path-resolver");
16
+ const agent_tier_1 = require("./agent-tier");
17
+ // ─── Agent MCP wiring (design D8 / D1) ────────────────────────────────────────
18
+ //
19
+ // The in-app agent drives the app by talking to its OWN embedded MCP server. We
20
+ // spawn the AI CLI with `--mcp-config` pointing at a generated config whose sole
21
+ // server is the bundled `specrails-mcp` stdio bridge, which relays to
22
+ // `127.0.0.1:<port>/api/mcp` and reads the scoped token from disk (no token is
23
+ // ever written into the config file). The current Shift+Tab ladder level is
24
+ // forwarded via env → the bridge sets it as a loopback-only header so the tool
25
+ // guard enforces the agent ladder independently of the external Settings tiers.
26
+ function homeDir() {
27
+ return process.env.SPECRAILS_REGISTRY_HOME || os_1.default.homedir();
28
+ }
29
+ function fileExists(p) {
30
+ try {
31
+ return fs_1.default.existsSync(p);
32
+ }
33
+ catch {
34
+ return false;
35
+ }
36
+ }
37
+ /**
38
+ * Absolute path to the `specrails-mcp` bridge JS. Resolution order:
39
+ * 1. SPECRAILS_BUNDLED_MCP_BRIDGE_PATH (set by the Tauri host in the packaged app)
40
+ * 2. the staged copy under src-tauri/binaries/ (dev + bundled layouts)
41
+ * 3. the mcp-bridge build output (mcp-bridge/dist/)
42
+ * Climbs from this module so it works whether running from server/ or server/dist/.
43
+ */
44
+ function resolveBridgeScript() {
45
+ const fromEnv = process.env.SPECRAILS_BUNDLED_MCP_BRIDGE_PATH;
46
+ if (fromEnv && fileExists(fromEnv))
47
+ return fromEnv;
48
+ const roots = [
49
+ path_1.default.resolve(__dirname, '..'),
50
+ path_1.default.resolve(__dirname, '..', '..'),
51
+ process.cwd(),
52
+ ];
53
+ const rels = [
54
+ path_1.default.join('src-tauri', 'binaries', 'specrails-mcp.js'),
55
+ path_1.default.join('mcp-bridge', 'dist', 'specrails-mcp.js'),
56
+ ];
57
+ for (const root of roots) {
58
+ for (const rel of rels) {
59
+ const candidate = path_1.default.join(root, rel);
60
+ if (fileExists(candidate))
61
+ return candidate;
62
+ }
63
+ }
64
+ return null;
65
+ }
66
+ /** The node executable that should run the bridge (bundled in the packaged app, else PATH `node`). */
67
+ function resolveNodeCommand() {
68
+ return (0, path_resolver_1.resolveBundledNodeExe)() ?? 'node';
69
+ }
70
+ /** Build the `mcpServers.specrails` entry the agent (or a project workspace) uses. */
71
+ function buildSpecrailsMcpEntry(opts) {
72
+ const bridge = resolveBridgeScript();
73
+ if (!bridge)
74
+ return null;
75
+ const env = {
76
+ SPECRAILS_MCP_PORT: String(opts.port),
77
+ };
78
+ // Preserve the test-home override so the bridge reads the right token file.
79
+ if (process.env.SPECRAILS_REGISTRY_HOME)
80
+ env.SPECRAILS_REGISTRY_HOME = process.env.SPECRAILS_REGISTRY_HOME;
81
+ if (opts.tierLevel != null)
82
+ env[agent_tier_1.AGENT_TIER_ENV] = (0, agent_tier_1.tierNameForLevel)(opts.tierLevel);
83
+ if (opts.activeProjectId)
84
+ env[agent_tier_1.AGENT_PROJECT_ENV] = opts.activeProjectId;
85
+ return { command: resolveNodeCommand(), args: [bridge], env };
86
+ }
87
+ /**
88
+ * Part A (design D8): surgically merge the `specrails` MCP server into a
89
+ * project's app-managed WORKSPACE `.mcp.json` so the project's own rails/explore/
90
+ * chat spawns can also call the Specrails MCP. NEVER touches the pristine repo.
91
+ * Additive + idempotent: preserves any existing (e.g. plugin) mcpServers entries,
92
+ * writes atomically (temp + rename), inlines NO token. Returns true when written.
93
+ */
94
+ function mergeSpecrailsIntoWorkspaceMcp(workspaceDir, port) {
95
+ const entry = buildSpecrailsMcpEntry({ port });
96
+ if (!entry)
97
+ return false;
98
+ const file = path_1.default.join(workspaceDir, '.mcp.json');
99
+ let current = {};
100
+ try {
101
+ if (fs_1.default.existsSync(file)) {
102
+ const parsed = JSON.parse(fs_1.default.readFileSync(file, 'utf-8'));
103
+ if (parsed && typeof parsed === 'object')
104
+ current = parsed;
105
+ }
106
+ }
107
+ catch {
108
+ // Corrupt file → start fresh rather than crash assembly.
109
+ current = {};
110
+ }
111
+ const mcpServers = { ...(current.mcpServers ?? {}), specrails: entry };
112
+ const next = { ...current, mcpServers };
113
+ const tmp = `${file}.tmp-${process.pid}`;
114
+ fs_1.default.mkdirSync(workspaceDir, { recursive: true });
115
+ fs_1.default.writeFileSync(tmp, JSON.stringify(next, null, 2));
116
+ fs_1.default.renameSync(tmp, file);
117
+ return true;
118
+ }
119
+ /**
120
+ * Writes a per-conversation `--mcp-config` file for the agent and returns the
121
+ * `['--mcp-config', <file>]` argv to inject into SpawnOptions.extraArgs. Uses the
122
+ * bundled `specrails-mcp` stdio bridge (shipped inside the app — the user installs
123
+ * nothing; 100% unattended) which relays to this process's `/api/mcp`. Returns
124
+ * `[]` when the bridge can't be located (agent then runs tool-less / degraded).
125
+ */
126
+ function buildAgentMcpArgs(opts) {
127
+ const entry = buildSpecrailsMcpEntry({ port: opts.port, tierLevel: opts.tierLevel, activeProjectId: opts.activeProjectId });
128
+ if (!entry)
129
+ return [];
130
+ if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(opts.conversationId)) {
131
+ throw new Error(`unsafe agent conversation id: ${opts.conversationId}`);
132
+ }
133
+ const dir = path_1.default.join(homeDir(), '.specrails', 'agent', opts.conversationId);
134
+ fs_1.default.mkdirSync(dir, { recursive: true });
135
+ const file = path_1.default.join(dir, 'mcp.json');
136
+ fs_1.default.writeFileSync(file, JSON.stringify({ mcpServers: { specrails: entry } }, null, 2), { mode: 0o600 });
137
+ try {
138
+ fs_1.default.chmodSync(file, 0o600);
139
+ }
140
+ catch {
141
+ /* best-effort on platforms without chmod */
142
+ }
143
+ return ['--mcp-config', file];
144
+ }
145
+ function agentDir(conversationId) {
146
+ if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(conversationId)) {
147
+ throw new Error(`unsafe agent conversation id: ${conversationId}`);
148
+ }
149
+ const dir = path_1.default.join(homeDir(), '.specrails', 'agent', conversationId);
150
+ fs_1.default.mkdirSync(dir, { recursive: true });
151
+ return dir;
152
+ }
153
+ /**
154
+ * Codex registers the specrails MCP via inline `-c mcp_servers.specrails.*`
155
+ * config overrides. This keeps codex on the USER's real CODEX_HOME (auth + model
156
+ * settings intact — no all-or-nothing 401 from a CODEX_HOME override) and cannot
157
+ * break codex startup the way an unknown `--mcp-config` flag does: `-c` is a known
158
+ * flag and unknown config keys are simply set. TOML scalar values reuse JSON
159
+ * escaping (compatible for our command/path/env inputs).
160
+ */
161
+ function codexMcpOverrides(entry) {
162
+ const c = (kv) => ['-c', kv];
163
+ const args = [
164
+ ...c(`mcp_servers.specrails.command=${JSON.stringify(entry.command)}`),
165
+ ...c(`mcp_servers.specrails.args=[${entry.args.map((a) => JSON.stringify(a)).join(', ')}]`),
166
+ ];
167
+ for (const [k, v] of Object.entries(entry.env)) {
168
+ args.push(...c(`mcp_servers.specrails.env.${k}=${JSON.stringify(v)}`));
169
+ }
170
+ return args;
171
+ }
172
+ /**
173
+ * Prepares the specrails MCP for the given provider and returns the spawn
174
+ * extraArgs + env to merge. Returns empty wiring when the bridge is unavailable
175
+ * (agent then runs tool-less / degraded).
176
+ */
177
+ function prepareAgentMcp(opts) {
178
+ const entry = buildSpecrailsMcpEntry({
179
+ port: opts.port,
180
+ tierLevel: opts.tierLevel,
181
+ activeProjectId: opts.activeProjectId,
182
+ });
183
+ if (!entry)
184
+ return { extraArgs: [], env: {} };
185
+ if (opts.adapterId === 'codex') {
186
+ // Inline -c overrides — no CODEX_HOME override, no file mutation; auth intact.
187
+ return { extraArgs: codexMcpOverrides(entry), env: {} };
188
+ }
189
+ if (opts.adapterId === 'claude') {
190
+ return { extraArgs: buildAgentMcpArgs(opts), env: {} };
191
+ }
192
+ // gemini + any other project-json provider: write .mcp.json in the spawn cwd.
193
+ const file = path_1.default.join(opts.cwd, '.mcp.json');
194
+ let current = {};
195
+ try {
196
+ if (fs_1.default.existsSync(file)) {
197
+ const parsed = JSON.parse(fs_1.default.readFileSync(file, 'utf-8'));
198
+ if (parsed && typeof parsed === 'object')
199
+ current = parsed;
200
+ }
201
+ }
202
+ catch {
203
+ current = {};
204
+ }
205
+ const next = { ...current, mcpServers: { ...(current.mcpServers ?? {}), specrails: entry } };
206
+ fs_1.default.writeFileSync(file, JSON.stringify(next, null, 2), { mode: 0o600 });
207
+ return { extraArgs: [], env: {} };
208
+ }
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAgentConversation = createAgentConversation;
4
+ exports.getAgentConversation = getAgentConversation;
5
+ exports.listAgentConversations = listAgentConversations;
6
+ exports.updateAgentConversation = updateAgentConversation;
7
+ exports.deleteAgentConversation = deleteAgentConversation;
8
+ exports.addAgentMessage = addAgentMessage;
9
+ exports.listAgentMessages = listAgentMessages;
10
+ const crypto_1 = require("crypto");
11
+ const agent_tier_1 = require("./agent-tier");
12
+ function mapConversation(row) {
13
+ if (!row)
14
+ return undefined;
15
+ return { ...row, tier_level: (0, agent_tier_1.normalizeLevel)(row.tier_level) };
16
+ }
17
+ function createAgentConversation(db, input = {}) {
18
+ const id = (0, crypto_1.randomUUID)();
19
+ db.prepare(`INSERT INTO agent_conversations (id, provider, model, pinned_project_id, tier_level)
20
+ VALUES (?, ?, ?, ?, ?)`).run(id, input.provider ?? 'claude', input.model ?? null, input.pinnedProjectId ?? null, input.tierLevel ?? 0);
21
+ return getAgentConversation(db, id);
22
+ }
23
+ function getAgentConversation(db, id) {
24
+ return mapConversation(db.prepare('SELECT * FROM agent_conversations WHERE id = ?').get(id));
25
+ }
26
+ function listAgentConversations(db, limit = 100) {
27
+ return db
28
+ .prepare('SELECT * FROM agent_conversations ORDER BY updated_at DESC LIMIT ?')
29
+ .all(limit).map((r) => mapConversation(r));
30
+ }
31
+ /** Patch a conversation. Only the provided fields change; `updated_at` always bumps. */
32
+ function updateAgentConversation(db, id, patch) {
33
+ const sets = [];
34
+ const vals = [];
35
+ for (const [k, v] of Object.entries(patch)) {
36
+ sets.push(`${k} = ?`);
37
+ vals.push(v);
38
+ }
39
+ sets.push("updated_at = datetime('now')");
40
+ if (sets.length > 0) {
41
+ db.prepare(`UPDATE agent_conversations SET ${sets.join(', ')} WHERE id = ?`).run(...vals, id);
42
+ }
43
+ return getAgentConversation(db, id);
44
+ }
45
+ function deleteAgentConversation(db, id) {
46
+ db.prepare('DELETE FROM agent_conversations WHERE id = ?').run(id);
47
+ }
48
+ function addAgentMessage(db, input) {
49
+ const id = (0, crypto_1.randomUUID)();
50
+ db.prepare('INSERT INTO agent_messages (id, conversation_id, role, content) VALUES (?, ?, ?, ?)').run(id, input.conversationId, input.role, input.content);
51
+ // Touch the parent so the list stays ordered by latest activity.
52
+ db.prepare("UPDATE agent_conversations SET updated_at = datetime('now') WHERE id = ?").run(input.conversationId);
53
+ return db.prepare('SELECT * FROM agent_messages WHERE id = ?').get(id);
54
+ }
55
+ function listAgentMessages(db, conversationId) {
56
+ return db
57
+ .prepare('SELECT * FROM agent_messages WHERE conversation_id = ? ORDER BY created_at ASC, rowid ASC')
58
+ .all(conversationId);
59
+ }
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AGENT_PROJECT_ENV = exports.AGENT_PROJECT_HEADER = exports.AGENT_TIER_ENV = exports.AGENT_TIER_HEADER = exports.AGENT_TIER_NAMES = void 0;
4
+ exports.tierNameForLevel = tierNameForLevel;
5
+ exports.levelForTierName = levelForTierName;
6
+ exports.normalizeLevel = normalizeLevel;
7
+ exports.nextLevel = nextLevel;
8
+ exports.levelAllowsTier = levelAllowsTier;
9
+ exports.levelFromHeader = levelFromHeader;
10
+ exports.tierNeedsApproval = tierNeedsApproval;
11
+ exports.AGENT_TIER_NAMES = [
12
+ 'observe',
13
+ 'edit',
14
+ 'operate',
15
+ 'autonomous',
16
+ ];
17
+ /** The HTTP header the agent's MCP bridge sets so the tool guard can read the level. */
18
+ exports.AGENT_TIER_HEADER = 'x-specrails-agent-tier';
19
+ /** Env var the AgentChatManager sets on the bridge spawn; the bridge forwards it as the header. */
20
+ exports.AGENT_TIER_ENV = 'SPECRAILS_AGENT_TIER';
21
+ /** Header carrying the pinned project id so tools resolve it per-request. */
22
+ exports.AGENT_PROJECT_HEADER = 'x-specrails-active-project';
23
+ /** Env var the manager sets on the bridge spawn; the bridge forwards it as the project header. */
24
+ exports.AGENT_PROJECT_ENV = 'SPECRAILS_ACTIVE_PROJECT';
25
+ /** Which MCP tiers a given ladder level unlocks (cumulative). */
26
+ const TIERS_BY_LEVEL = {
27
+ 0: new Set(['read']),
28
+ 1: new Set(['read', 'write']),
29
+ 2: new Set(['read', 'write', 'ai-spawn']),
30
+ 3: new Set(['read', 'write', 'ai-spawn', 'destructive']),
31
+ };
32
+ function tierNameForLevel(level) {
33
+ return exports.AGENT_TIER_NAMES[level];
34
+ }
35
+ function levelForTierName(name) {
36
+ const idx = exports.AGENT_TIER_NAMES.indexOf(name);
37
+ return idx === -1 ? null : idx;
38
+ }
39
+ /** Clamp an arbitrary number/string to a valid ladder level (default observe). */
40
+ function normalizeLevel(value) {
41
+ const n = typeof value === 'string' ? Number(value) : value;
42
+ if (typeof n === 'number' && Number.isInteger(n) && n >= 0 && n <= 3) {
43
+ return n;
44
+ }
45
+ // Allow the name form too.
46
+ if (typeof value === 'string') {
47
+ const byName = levelForTierName(value);
48
+ if (byName !== null)
49
+ return byName;
50
+ }
51
+ return 0;
52
+ }
53
+ /** Cycle Shift+Tab: 0→1→2→3→0. */
54
+ function nextLevel(level) {
55
+ return ((level + 1) % 4);
56
+ }
57
+ /** True when the given MCP tier is permitted at the given ladder level. */
58
+ function levelAllowsTier(level, tier) {
59
+ return TIERS_BY_LEVEL[level].has(tier);
60
+ }
61
+ /**
62
+ * Parse the agent-tier header value into a level, or null when absent/invalid.
63
+ * The header carries the tier NAME (observe/edit/operate/autonomous) or the
64
+ * numeric level; anything else yields null (→ fall back to Settings tiers).
65
+ */
66
+ function levelFromHeader(headerValue) {
67
+ if (headerValue == null)
68
+ return null;
69
+ const raw = Array.isArray(headerValue) ? headerValue[0] : headerValue;
70
+ if (typeof raw !== 'string' || raw.trim() === '')
71
+ return null;
72
+ const byName = levelForTierName(raw.trim());
73
+ if (byName !== null)
74
+ return byName;
75
+ const n = Number(raw.trim());
76
+ if (Number.isInteger(n) && n >= 0 && n <= 3)
77
+ return n;
78
+ return null;
79
+ }
80
+ /**
81
+ * Whether a cost/destructive action needs an explicit inline approval (Option C):
82
+ * reversible writes (read/write) run silently; ai-spawn and destructive prompt.
83
+ */
84
+ function tierNeedsApproval(tier) {
85
+ return tier === 'ai-spawn' || tier === 'destructive';
86
+ }
@@ -351,6 +351,33 @@ function applyDesktopMigrations(db) {
351
351
  ('seed-min-coverage', 'MIN_COVERAGE', '80');
352
352
  `);
353
353
  },
354
+ // 17: app-global agent chat (design D2). The agent chat lives ABOVE projects,
355
+ // so its conversations are stored in the app registry DB (not any per-project
356
+ // jobs.sqlite). `pinned_project_id` records the Cursor-style selector target
357
+ // (NULL = Home / app-global); `tier_level` records the Shift+Tab ladder.
358
+ () => {
359
+ db.exec(`
360
+ CREATE TABLE IF NOT EXISTS agent_conversations (
361
+ id TEXT PRIMARY KEY,
362
+ title TEXT,
363
+ provider TEXT NOT NULL DEFAULT 'claude',
364
+ model TEXT,
365
+ session_id TEXT,
366
+ pinned_project_id TEXT,
367
+ tier_level INTEGER NOT NULL DEFAULT 0,
368
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
369
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
370
+ );
371
+ CREATE TABLE IF NOT EXISTS agent_messages (
372
+ id TEXT PRIMARY KEY,
373
+ conversation_id TEXT NOT NULL REFERENCES agent_conversations(id) ON DELETE CASCADE,
374
+ role TEXT NOT NULL,
375
+ content TEXT NOT NULL,
376
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
377
+ );
378
+ CREATE INDEX IF NOT EXISTS idx_agent_messages_conv ON agent_messages(conversation_id, created_at);
379
+ `);
380
+ },
354
381
  ];
355
382
  for (let i = 0; i < migrations.length; i++) {
356
383
  const version = i + 1;
@@ -24,6 +24,8 @@ const terminal_shell_integration_1 = require("./terminal-shell-integration");
24
24
  const feature_flags_1 = require("./feature-flags");
25
25
  const mobile_1 = require("./mobile");
26
26
  const mcp_1 = require("./mcp");
27
+ const agent_chat_manager_1 = require("./agent-chat-manager");
28
+ const agent_chat_router_1 = require("./agent-chat-router");
27
29
  const browser_playwright_1 = require("./browser-playwright");
28
30
  const telemetry_receiver_1 = require("./telemetry-receiver");
29
31
  const telemetry_compactor_1 = require("./telemetry-compactor");
@@ -235,6 +237,7 @@ let _registry = null;
235
237
  /** The mobile companion gateway (off by default); torn down on shutdown. */
236
238
  let _mobileGateway = null;
237
239
  let _mcpManager = null;
240
+ let _agentChatManager = null;
238
241
  server.on('upgrade', (request, socket, head) => {
239
242
  const urlStr = request.url ?? '/';
240
243
  const auth = authorizeUpgrade(request);
@@ -536,6 +539,10 @@ function applyPtyWsRateLimiting(ws) {
536
539
  if (mcpManager.isEnabledSetting()) {
537
540
  mcpManager.start().catch((err) => console.error('[mcp] boot start failed:', err));
538
541
  }
542
+ // ─── App-global agent chat (drives the app via its own MCP) ────────────────
543
+ const agentChatManager = new agent_chat_manager_1.AgentChatManager(broadcast, registry.desktopDb, port);
544
+ _agentChatManager = agentChatManager;
545
+ app.use('/api/agent', (0, agent_chat_router_1.createAgentChatRouter)({ manager: agentChatManager, desktopDb: registry.desktopDb }));
539
546
  // App-level routes. CRITICAL mount order: the desktop router is mounted at
540
547
  // '/api' BEFORE the project router below so its exact routes (e.g.
541
548
  // GET /api/projects, DELETE /api/projects/:id) are handled here, while
@@ -641,6 +648,7 @@ async function shutdown() {
641
648
  try {
642
649
  await _mobileGateway?.stop();
643
650
  await _mcpManager?.stop();
651
+ await _agentChatManager?.shutdown();
644
652
  }
645
653
  catch { /* ignore */ }
646
654
  try {
@@ -7,20 +7,23 @@ const types_1 = require("./types");
7
7
  * The setup-wizard domain facade. Mixes APP-LEVEL operations (prerequisites,
8
8
  * available_providers, add_project — mounted at `/api` with NO `:projectId`)
9
9
  * with PER-PROJECT operations (install-config, install, checkpoints, abort,
10
- * integration-contract, enrich — under `/api/projects/:projectId/*`).
10
+ * integration-contract — under `/api/projects/:projectId/*`).
11
+ *
12
+ * QUICK-only: the agent path provisions the quick (offline) tier and installs
13
+ * every registered provider in ONE `install` call (the server chains them). The
14
+ * legacy full/enrich AI flow is intentionally NOT exposed through the MCP.
11
15
  *
12
16
  * LIFECYCLE the caller must respect:
13
17
  * 1. prerequisites → must be ok before adding a project
14
18
  * 2. add_project → creates the ProjectContext + slug + registry mirror + workspace
15
- * 3. install_config (one POST per provider for multi-provider projects)
16
- * 4. install → 202 async; the real result arrives only via WS / checkpoints
19
+ * 3. install_config (one POST per provider for multi-provider projects; tier forced to quick)
20
+ * 4. install → 202 async; installs ALL providers; result arrives via WS / checkpoints
17
21
  * 5. poll checkpoints (or stream WS) until isInstalling === false
18
- * 6. optional enrich / enrich_message for the legacy full AI flow
19
22
  *
20
- * The async actions (install, enrich, enrich_message) return immediately (HTTP
21
- * 202) and do the real, long-running work in a detached child whose only output
22
- * channel is WebSocket — so they return a hint to poll `checkpoints` (which also
23
- * exposes isInstalling/isEnriching/logLines/summary) for true completion.
23
+ * The async `install` action returns immediately (HTTP 202) and does the real,
24
+ * long-running work in a detached child whose only output channel is WebSocket —
25
+ * so it returns a hint to poll `checkpoints` (which also exposes
26
+ * isInstalling/logLines/summary) for true completion.
24
27
  */
25
28
  function setupTools() {
26
29
  return [
@@ -31,15 +34,15 @@ function setupTools() {
31
34
  'App-level actions ignore projectId: prerequisites (probe node/npm/npx/git + provider CLIs), ' +
32
35
  'available_providers (which AI CLIs are detected + install tiers), add_project (register a repo path). ' +
33
36
  'Per-project actions: install_config (persist install-config.yaml), install (spawn offline framework scaffold — async/202), ' +
34
- 'checkpoints (poll live setup/install/enrich state — the canonical completion signal when not streaming WS), ' +
35
- 'abort (SIGTERM/SIGKILL the in-progress install+enrich child), integration_contract (read the ticket-provider contract), ' +
36
- 'enrich (spawn the legacy full AI flow — async/202, COST-INCURRING), enrich_message (resume an enrich session with a reply — async, COST-INCURRING).',
37
+ 'checkpoints (poll live setup/install state — the canonical completion signal when not streaming WS), ' +
38
+ 'abort (SIGTERM/SIGKILL the in-progress install child), integration_contract (read the ticket-provider contract). ' +
39
+ 'Install is QUICK-only and provisions every registered provider in one shot; the legacy full/enrich AI flow is not exposed here.',
37
40
  hintTier: 'read',
38
41
  tier: (a) => {
39
42
  const action = a.action;
40
43
  if (action === 'abort')
41
44
  return 'destructive';
42
- if (action === 'install' || action === 'enrich' || action === 'enrich_message')
45
+ if (action === 'install')
43
46
  return 'ai-spawn';
44
47
  if (action === 'add_project' || action === 'install_config')
45
48
  return 'write';
@@ -56,14 +59,12 @@ function setupTools() {
56
59
  'checkpoints',
57
60
  'abort',
58
61
  'integration_contract',
59
- 'enrich',
60
- 'enrich_message',
61
62
  ])
62
63
  .describe('Setup operation to perform'),
63
64
  projectId: zod_1.z
64
65
  .string()
65
66
  .optional()
66
- .describe('Project id for per-project actions (install_config/install/checkpoints/abort/integration_contract/enrich/enrich_message). Ignored by app-level actions.'),
67
+ .describe('Project id for per-project actions (install_config/install/checkpoints/abort/integration_contract). Ignored by app-level actions.'),
67
68
  // prerequisites
68
69
  diagnostic: zod_1.z
69
70
  .boolean()
@@ -81,7 +82,7 @@ function setupTools() {
81
82
  provider: zod_1.z
82
83
  .string()
83
84
  .optional()
84
- .describe('add_project: legacy single primary provider; install/enrich: which per-provider install-config to read'),
85
+ .describe('add_project: legacy single primary provider; install: which per-provider install-config to read'),
85
86
  providers: zod_1.z
86
87
  .array(zod_1.z.string())
87
88
  .optional()
@@ -91,15 +92,6 @@ function setupTools() {
91
92
  .record(zod_1.z.unknown())
92
93
  .optional()
93
94
  .describe('install_config: the install-config object to serialize (e.g. { tier:"quick", provider, agents }); required for install_config'),
94
- // enrich_message
95
- sessionId: zod_1.z
96
- .string()
97
- .optional()
98
- .describe('enrich_message: the enrich session id to resume (required for enrich_message)'),
99
- message: zod_1.z
100
- .string()
101
- .optional()
102
- .describe('enrich_message: the user reply to send into the enrich session (required for enrich_message)'),
103
95
  },
104
96
  async handler(ctx, args) {
105
97
  const action = args.action;
@@ -131,17 +123,27 @@ function setupTools() {
131
123
  if (config === undefined || typeof config !== 'object' || Array.isArray(config)) {
132
124
  throw new Error('install_config requires a "config" object.');
133
125
  }
134
- return (0, types_1.apiCall)(ctx, 'POST', `${base}/setup/install-config`, config);
126
+ // The MCP/agent path is QUICK-only — force the tier so a full/enrich
127
+ // install can never be provisioned through the agent (deprecated).
128
+ const quickConfig = { ...config, tier: 'quick' };
129
+ return (0, types_1.apiCall)(ctx, 'POST', `${base}/setup/install-config`, quickConfig);
135
130
  }
136
131
  case 'install': {
137
132
  const base = (0, types_1.projectPath)(ctx, args.projectId);
133
+ // Quick-only, all-provider install: by default provision EVERY provider
134
+ // the project is registered with in one shot (server chains them). An
135
+ // explicit `providers` list narrows it; a single `provider` still works.
138
136
  const body = {};
139
- if (typeof args.provider === 'string')
137
+ if (Array.isArray(args.providers))
138
+ body.providers = args.providers;
139
+ else if (typeof args.provider === 'string')
140
140
  body.provider = args.provider;
141
+ else
142
+ body.all = true;
141
143
  const r = await (0, types_1.apiCall)(ctx, 'POST', `${base}/setup/install`, body);
142
144
  return {
143
145
  ...(typeof r === 'object' && r !== null ? r : { result: r }),
144
- hint: 'Install is async (HTTP 202). Poll specrails_setup(checkpoints) until isInstalling === false (and watch summary for the result), or stream the setup_install_done / setup_error WS events.',
146
+ hint: 'Install is async (HTTP 202). Installs ALL registered providers (quick tier) in one shot; poll specrails_setup(checkpoints) until isInstalling === false, or stream the setup_install_done / setup_error WS events.',
145
147
  };
146
148
  }
147
149
  case 'checkpoints': {
@@ -156,28 +158,6 @@ function setupTools() {
156
158
  const base = (0, types_1.projectPath)(ctx, args.projectId);
157
159
  return (0, types_1.apiCall)(ctx, 'GET', `${base}/integration-contract`);
158
160
  }
159
- case 'enrich': {
160
- const base = (0, types_1.projectPath)(ctx, args.projectId);
161
- const r = await (0, types_1.apiCall)(ctx, 'POST', `${base}/enrich/start`, {});
162
- return {
163
- ...(typeof r === 'object' && r !== null ? r : { result: r }),
164
- hint: 'Enrich is async (HTTP 202) and COST-INCURRING. Poll specrails_setup(checkpoints) until isEnriching === false, or stream the setup_complete / setup_turn_done / setup_error WS events. Use enrich_message to answer clarifying questions.',
165
- };
166
- }
167
- case 'enrich_message': {
168
- const base = (0, types_1.projectPath)(ctx, args.projectId);
169
- const sessionId = args.sessionId;
170
- const message = args.message;
171
- if (!sessionId)
172
- throw new Error('enrich_message requires a "sessionId".');
173
- if (!message || !message.trim())
174
- throw new Error('enrich_message requires a non-empty "message".');
175
- const r = await (0, types_1.apiCall)(ctx, 'POST', `${base}/enrich/message`, { sessionId, message });
176
- return {
177
- ...(typeof r === 'object' && r !== null ? r : { result: r }),
178
- hint: 'Enrich-message is async (HTTP 202) and COST-INCURRING. Poll specrails_setup(checkpoints) until isEnriching === false, or stream the setup_turn_done / setup_error WS events.',
179
- };
180
- }
181
161
  default:
182
162
  throw new Error(`Unknown action "${action}".`);
183
163
  }
@@ -8,6 +8,7 @@ exports.getActiveProject = getActiveProject;
8
8
  exports.registerTieredTool = registerTieredTool;
9
9
  const mcp_tiers_1 = require("../mcp-tiers");
10
10
  const auth_1 = require("../../auth");
11
+ const agent_tier_1 = require("../../agent-tier");
11
12
  /**
12
13
  * Calls the app's own REST API over loopback using the MASTER token (held
13
14
  * server-side, never exposed to the MCP client). This reuses every router's
@@ -119,9 +120,27 @@ function registerTieredTool(server, ctx, spec) {
119
120
  description: spec.description,
120
121
  inputSchema: spec.inputSchema,
121
122
  annotations: tierAnnotations(hintTier),
122
- }, async (args) => {
123
+ }, async (args, extra) => {
124
+ // In-app agent: a per-request active-project header (loopback-only, set by
125
+ // the agent's bridge from the Cursor-style selector) pins the project for
126
+ // this call so project-scoped tools resolve without specrails_select_project.
127
+ const projectHeader = extra?.requestInfo?.headers?.[agent_tier_1.AGENT_PROJECT_HEADER];
128
+ if (projectHeader != null) {
129
+ const pid = Array.isArray(projectHeader) ? projectHeader[0] : projectHeader;
130
+ if (typeof pid === 'string' && pid.trim())
131
+ setActiveProject(pid.trim());
132
+ }
123
133
  const tier = typeof spec.tier === 'function' ? spec.tier(args ?? {}) : spec.tier;
124
- if (!(0, mcp_tiers_1.isTierEnabled)(ctx.desktopDb, tier)) {
134
+ // In-app agent: when the request carries the loopback-only agent-tier header,
135
+ // enforce the cumulative ladder instead of the external Settings checkboxes
136
+ // (design D4). Absent header → external client → unchanged Settings gate.
137
+ const agentLevel = (0, agent_tier_1.levelFromHeader)(extra?.requestInfo?.headers?.[agent_tier_1.AGENT_TIER_HEADER]);
138
+ if (agentLevel !== null) {
139
+ if (!(0, agent_tier_1.levelAllowsTier)(agentLevel, tier)) {
140
+ return errorResult((0, mcp_tiers_1.tierRefusalMessage)(tier));
141
+ }
142
+ }
143
+ else if (!(0, mcp_tiers_1.isTierEnabled)(ctx.desktopDb, tier)) {
125
144
  return errorResult((0, mcp_tiers_1.tierRefusalMessage)(tier));
126
145
  }
127
146
  try {
@@ -67,7 +67,21 @@ function registerSetupRoutes(deps) {
67
67
  const requested = (req.body ?? {});
68
68
  const provider = typeof requested.provider === 'string' ? requested.provider : undefined;
69
69
  res.status(202).json({ ok: true });
70
- setupManager.startInstall(project.id, project.path, project.slug, provider);
70
+ // One-shot multi-provider install: an explicit `providers` list, or `all:true`
71
+ // to install every provider the project is registered with (used by the MCP/
72
+ // agent so a single `install` provisions them all). Else single-provider.
73
+ const explicit = Array.isArray(requested.providers)
74
+ ? requested.providers.filter((p) => typeof p === 'string')
75
+ : undefined;
76
+ if (explicit && explicit.length > 0) {
77
+ setupManager.startInstallProviders(project.id, project.path, project.slug, explicit);
78
+ }
79
+ else if (requested.all === true) {
80
+ setupManager.startInstallProviders(project.id, project.path, project.slug, project.providers);
81
+ }
82
+ else {
83
+ setupManager.startInstall(project.id, project.path, project.slug, provider);
84
+ }
71
85
  });
72
86
  router.post('/:projectId/enrich/start', (req, res) => {
73
87
  const { project, setupManager } = ctx(req);