specrails-desktop 2.30.2 → 2.31.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 (462) hide show
  1. package/README.md +28 -24
  2. package/client/dist/assets/{ActivityFeedPage-9DZOvIcY.js → ActivityFeedPage-CAhFRUO4.js} +1 -1
  3. package/client/dist/assets/{AgentBrowserCapture-DqyHbzk7.js → AgentBrowserCapture-Ct4G5lsE.js} +1 -1
  4. package/client/dist/assets/AgentModeCodePane-DQt9WIvu.js +2 -0
  5. package/client/dist/assets/{AgentModeJobsPane-CTIHMUKN.js → AgentModeJobsPane-podaJVl9.js} +1 -1
  6. package/client/dist/assets/AgentsPage-DTZdbW80.js +87 -0
  7. package/client/dist/assets/AnalyticsPage-BskgGPm0.js +1 -0
  8. package/client/dist/assets/CodePage-CvtYo1DU.js +2 -0
  9. package/client/dist/assets/{DesktopAnalyticsPage-CkXUFj3F.js → DesktopAnalyticsPage-Dnf99E8e.js} +1 -1
  10. package/client/dist/assets/{DocsDialog-PhdCJNVj.js → DocsDialog-DHi36rZ4.js} +1 -1
  11. package/client/dist/assets/{DocsPage-BY-wDthx.js → DocsPage-DIdVrQz1.js} +1 -1
  12. package/client/dist/assets/{InteractiveJobComposer-C9I0A4KR.js → InteractiveJobComposer-BHeB2inv.js} +1 -1
  13. package/client/dist/assets/{JobDetailModal-I_RoQJV4.js → JobDetailModal-CF1SKUHH.js} +1 -1
  14. package/client/dist/assets/JobDetailPage-De3rY18h.js +1 -0
  15. package/client/dist/assets/{JobsPage-DcllM4Ip.js → JobsPage-CfAqqJTx.js} +1 -1
  16. package/client/dist/assets/{LoopBuilderPage-DGm_iuff.js → LoopBuilderPage-DSBMuTb3.js} +2 -2
  17. package/client/dist/assets/{LoopPreviewModal-BC-TeFdD.js → LoopPreviewModal-B-c37kA0.js} +1 -1
  18. package/client/dist/assets/LoopsPage-DjnNXdmm.js +1 -0
  19. package/client/dist/assets/{MinimizedChatsContext-Cs34FPTz.js → MinimizedChatsContext-DIsnQMVz.js} +1 -1
  20. package/client/dist/assets/PluginsPage-DA5pQ_6u.js +2 -0
  21. package/client/dist/assets/{ProjectSettingsDialog-nEs-ENrE.js → ProjectSettingsDialog-DA23PSi6.js} +1 -1
  22. package/client/dist/assets/{TemplatePreviewModal-D4qW-J9B.js → TemplatePreviewModal-Cp7IQ-g2.js} +1 -1
  23. package/client/dist/assets/TicketDetailModal-BPD2i_0R.js +14 -0
  24. package/client/dist/assets/{Trans-CPISVji8.js → Trans-cWtExGkC.js} +1 -1
  25. package/client/dist/assets/addspec-4WmLJmBG.js +1 -0
  26. package/client/dist/assets/addspec-BRlk8jON.js +1 -0
  27. package/client/dist/assets/addspec-C7XRejU4.js +1 -0
  28. package/client/dist/assets/addspec-CHW9xf_X.js +1 -0
  29. package/client/dist/assets/addspec-DTmRrWrF.js +1 -0
  30. package/client/dist/assets/addspec-DhmnXER8.js +1 -0
  31. package/client/dist/assets/addspec-Di41DpYV.js +1 -0
  32. package/client/dist/assets/addspec-x3mizDxc.js +1 -0
  33. package/client/dist/assets/agent-BKsuIgIT.js +1 -0
  34. package/client/dist/assets/agent-Bs9NilfI.js +1 -0
  35. package/client/dist/assets/agent-C23h684q.js +1 -0
  36. package/client/dist/assets/agent-CqEjy3Z2.js +1 -0
  37. package/client/dist/assets/agent-D5DZTks1.js +1 -0
  38. package/client/dist/assets/agent-DE8hUYPl.js +1 -0
  39. package/client/dist/assets/agent-DsTuJGSc.js +1 -0
  40. package/client/dist/assets/agent-DwZM05-x.js +1 -0
  41. package/client/dist/assets/agents-45GbINfh.js +1 -0
  42. package/client/dist/assets/agents-BHp0_FQt.js +1 -0
  43. package/client/dist/assets/agents-Bvyi3y6R.js +1 -0
  44. package/client/dist/assets/agents-DMKFStHL.js +1 -0
  45. package/client/dist/assets/agents-DaYcxAr1.js +1 -0
  46. package/client/dist/assets/agents-IUOgqT5t.js +1 -0
  47. package/client/dist/assets/agents-ms7TwG16.js +1 -0
  48. package/client/dist/assets/agents-rzPnqoBR.js +1 -0
  49. package/client/dist/assets/agentstudio-8TQqBenW.js +1 -0
  50. package/client/dist/assets/agentstudio-B8Z_mmEX.js +1 -0
  51. package/client/dist/assets/agentstudio-BZR9YmLs.js +1 -0
  52. package/client/dist/assets/agentstudio-Bge9pAWb.js +1 -0
  53. package/client/dist/assets/agentstudio-Bz169Vzt.js +1 -0
  54. package/client/dist/assets/agentstudio-D6L2jpNi.js +1 -0
  55. package/client/dist/assets/agentstudio-K2-Z7CL0.js +1 -0
  56. package/client/dist/assets/agentstudio-Zmgzthv8.js +1 -0
  57. package/client/dist/assets/analytics-B8Qtptbl.js +1 -0
  58. package/client/dist/assets/analytics-BHqUazhP.js +1 -0
  59. package/client/dist/assets/analytics-BPptRY4f.js +1 -0
  60. package/client/dist/assets/analytics-BxDGOIwf.js +1 -0
  61. package/client/dist/assets/analytics-C9RaQcE3.js +1 -0
  62. package/client/dist/assets/analytics-CJIwqrEl.js +1 -0
  63. package/client/dist/assets/analytics-D_OZdQ9O.js +1 -0
  64. package/client/dist/assets/analytics-We6lUMI8.js +1 -0
  65. package/client/dist/assets/builder-3bygQtZi.js +1 -0
  66. package/client/dist/assets/builder-BiUG9Z5K.js +1 -0
  67. package/client/dist/assets/builder-CGRz_-zP.js +1 -0
  68. package/client/dist/assets/builder-CWYPw7Cw.js +1 -0
  69. package/client/dist/assets/builder-CX1437hF.js +1 -0
  70. package/client/dist/assets/{builder-CwA4T7gP.js → builder-Cowk9jtL.js} +1 -1
  71. package/client/dist/assets/builder-DwDexYRE.js +1 -0
  72. package/client/dist/assets/builder-skfnZ1uC.js +1 -0
  73. package/client/dist/assets/code-B0O8-p8k.js +1 -0
  74. package/client/dist/assets/code-B1VLZDhS.js +1 -0
  75. package/client/dist/assets/code-B86IMAfz.js +1 -0
  76. package/client/dist/assets/code-Bpj4Ddml.js +1 -0
  77. package/client/dist/assets/code-BykBZ5-e.js +1 -0
  78. package/client/dist/assets/code-CHPkOkDS.js +1 -0
  79. package/client/dist/assets/code-CkUuW4m9.js +1 -0
  80. package/client/dist/assets/code-cAqaqMTx.js +1 -0
  81. package/client/dist/assets/{dashboard-Dy2Um-US.js → dashboard-BVq6kRJe.js} +1 -1
  82. package/client/dist/assets/{dashboard-CHCEkds4.js → dashboard-BaKPPmLH.js} +1 -1
  83. package/client/dist/assets/{dashboard-CrlTV1ms.js → dashboard-Bm8e4zYG.js} +1 -1
  84. package/client/dist/assets/{dashboard-WhzOQw1S.js → dashboard-Bt6IesnM.js} +1 -1
  85. package/client/dist/assets/{dashboard-Cslu_GCH.js → dashboard-C_NQZle_.js} +1 -1
  86. package/client/dist/assets/{dashboard-DweT3Y4R.js → dashboard-CyZKoGoI.js} +1 -1
  87. package/client/dist/assets/{dashboard-DUU8-_Rg.js → dashboard-HIUdUBQ0.js} +1 -1
  88. package/client/dist/assets/{dashboard-Cqmmbh3l.js → dashboard-pm4Mr229.js} +1 -1
  89. package/client/dist/assets/{dist-js-jwoXo0YD.js → dist-js-BaP-xLOy.js} +1 -1
  90. package/client/dist/assets/{dist-js-Dj72W6qo.js → dist-js-CLlgOa5a.js} +1 -1
  91. package/client/dist/assets/format-command-C0D9IPmV.js +1 -0
  92. package/client/dist/assets/{formatDistanceToNow-ByKli7Q_.js → formatDistanceToNow-DUyzS4rr.js} +1 -1
  93. package/client/dist/assets/{getTimezoneOffsetInMilliseconds-qrcBaLW1.js → getTimezoneOffsetInMilliseconds-BCIwmmTj.js} +1 -1
  94. package/client/dist/assets/{i18n-Bf9Ckd10.js → i18n-D21Pr8Sj.js} +2 -2
  95. package/client/dist/assets/index-BdSDUFnA.css +2 -0
  96. package/client/dist/assets/index-Ci4-JKDr.js +77 -0
  97. package/client/dist/assets/{jobs-CVFD8rar.js → jobs-BAls3bWq.js} +1 -1
  98. package/client/dist/assets/{jobs-DoSaFzAa.js → jobs-B_TeSQxb.js} +1 -1
  99. package/client/dist/assets/jobs-Bgq-PZkC.js +1 -0
  100. package/client/dist/assets/jobs-Bpks6doU.js +1 -0
  101. package/client/dist/assets/jobs-CN_zv4Gr.js +1 -0
  102. package/client/dist/assets/jobs-DUlWSYq2.js +1 -0
  103. package/client/dist/assets/{jobs-B-sDHEay.js → jobs-DhxKgWFq.js} +1 -1
  104. package/client/dist/assets/{jobs-lk1_Dq1U.js → jobs-SwYS2M-B.js} +1 -1
  105. package/client/dist/assets/nav-BHDiSIVu.js +1 -0
  106. package/client/dist/assets/nav-CMiYAq4i.js +1 -0
  107. package/client/dist/assets/nav-CqS7NrDW.js +1 -0
  108. package/client/dist/assets/nav-D-0M7xfv.js +1 -0
  109. package/client/dist/assets/nav-D65O6u9I.js +1 -0
  110. package/client/dist/assets/nav-D_LKYvBr.js +1 -0
  111. package/client/dist/assets/nav-FDTJmU79.js +1 -0
  112. package/client/dist/assets/nav-VaB-C4k5.js +1 -0
  113. package/client/dist/assets/provider-capabilities-BvoExILy.js +1 -0
  114. package/client/dist/assets/settings-BveOJoaI.js +1 -0
  115. package/client/dist/assets/settings-Cj1C6F5k.js +1 -0
  116. package/client/dist/assets/settings-CuVwOpe0.js +1 -0
  117. package/client/dist/assets/settings-D5X4DfRc.js +1 -0
  118. package/client/dist/assets/settings-DXwxe-BW.js +1 -0
  119. package/client/dist/assets/settings-Di81Ba_V.js +1 -0
  120. package/client/dist/assets/settings-jbLjCYC_.js +1 -0
  121. package/client/dist/assets/settings-wIZNPFnn.js +1 -0
  122. package/client/dist/assets/setup-BBiHEO7h.js +1 -0
  123. package/client/dist/assets/setup-BlTWhNIQ.js +1 -0
  124. package/client/dist/assets/setup-Bp2L_oiP.js +1 -0
  125. package/client/dist/assets/setup-CInYXp1-.js +1 -0
  126. package/client/dist/assets/{setup-CITtPjed.js → setup-CYH3bP6K.js} +1 -1
  127. package/client/dist/assets/setup-DwcQ921P.js +1 -0
  128. package/client/dist/assets/setup-PBLjuDSs.js +1 -0
  129. package/client/dist/assets/setup-bx6FtoBp.js +1 -0
  130. package/client/dist/assets/{spending-DMnCXy84.js → spending-BCyUxoOI.js} +1 -1
  131. package/client/dist/assets/{tickets-B8BuAaj5.js → tickets-BL9r1G0e.js} +1 -1
  132. package/client/dist/assets/{tickets-DpmdE8tv.js → tickets-B_NyHJ8f.js} +1 -1
  133. package/client/dist/assets/{tickets-B_yh4qQk.js → tickets-BarF1wuG.js} +1 -1
  134. package/client/dist/assets/{tickets-pAUJkDTu.js → tickets-CclVhpQA.js} +1 -1
  135. package/client/dist/assets/{tickets-D_yDquEw.js → tickets-CqqOHbli.js} +1 -1
  136. package/client/dist/assets/{tickets-CFk2n2Or.js → tickets-DCkFSiqt.js} +1 -1
  137. package/client/dist/assets/{tickets-DQp7JS2R.js → tickets-Djn3ccfT.js} +1 -1
  138. package/client/dist/assets/{tickets-Bs37scwY.js → tickets-MiHBPsy_.js} +1 -1
  139. package/client/dist/assets/{useBrowserCaptureSession-CkBJShUw.js → useBrowserCaptureSession-CkVCDzer.js} +1 -1
  140. package/client/dist/assets/{useDesktop-B4pSBIDd.js → useDesktop-DCP_MHp9.js} +1 -1
  141. package/client/dist/index.html +25 -24
  142. package/docs/README.md +7 -3
  143. package/docs/cli.md +3 -3
  144. package/docs/codex.md +51 -64
  145. package/docs/creating-specs.md +36 -4
  146. package/docs/customizing.md +5 -5
  147. package/docs/gemini.md +35 -47
  148. package/docs/getting-started.md +15 -9
  149. package/docs/guide/de/agents/2-profiles-and-the-balanced-default.md +5 -1
  150. package/docs/guide/de/agents/4-custom-agents-catalog.md +7 -4
  151. package/docs/guide/de/getting-started/1-what-is-specrails.md +1 -0
  152. package/docs/guide/de/getting-started/2-installing-and-first-run.md +1 -0
  153. package/docs/guide/de/getting-started/4-the-dashboard-tour.md +4 -4
  154. package/docs/guide/de/integrations/1-ai-providers.md +29 -18
  155. package/docs/guide/de/integrations/2-plugins.md +3 -3
  156. package/docs/guide/de/integrations/6-agent-chat.md +8 -1
  157. package/docs/guide/de/pipeline/1-rails-and-jobs.md +9 -6
  158. package/docs/guide/de/pipeline/2-the-job-detail-view.md +3 -1
  159. package/docs/guide/de/pipeline/3-batch-implement-and-multi-feature.md +1 -1
  160. package/docs/guide/de/pipeline/4-picking-an-engine-per-rail.md +13 -13
  161. package/docs/guide/de/pipeline/5-the-loop-builder.md +6 -2
  162. package/docs/guide/de/specs/2-add-spec-quick-mode.md +6 -1
  163. package/docs/guide/en/agents/2-profiles-and-the-balanced-default.md +7 -1
  164. package/docs/guide/en/agents/4-custom-agents-catalog.md +17 -4
  165. package/docs/guide/en/getting-started/1-what-is-specrails.md +2 -1
  166. package/docs/guide/en/getting-started/2-installing-and-first-run.md +2 -1
  167. package/docs/guide/en/getting-started/4-the-dashboard-tour.md +5 -5
  168. package/docs/guide/en/insights/3-code-explorer.md +5 -0
  169. package/docs/guide/en/integrations/1-ai-providers.md +63 -22
  170. package/docs/guide/en/integrations/2-plugins.md +3 -3
  171. package/docs/guide/en/integrations/6-agent-chat.md +14 -2
  172. package/docs/guide/en/pipeline/1-rails-and-jobs.md +19 -7
  173. package/docs/guide/en/pipeline/2-the-job-detail-view.md +3 -1
  174. package/docs/guide/en/pipeline/3-batch-implement-and-multi-feature.md +1 -1
  175. package/docs/guide/en/pipeline/4-picking-an-engine-per-rail.md +25 -11
  176. package/docs/guide/en/pipeline/5-the-loop-builder.md +7 -2
  177. package/docs/guide/en/specs/2-add-spec-quick-mode.md +10 -1
  178. package/docs/guide/es/agents/2-profiles-and-the-balanced-default.md +5 -1
  179. package/docs/guide/es/agents/4-custom-agents-catalog.md +7 -4
  180. package/docs/guide/es/getting-started/1-what-is-specrails.md +1 -0
  181. package/docs/guide/es/getting-started/2-installing-and-first-run.md +1 -0
  182. package/docs/guide/es/getting-started/4-the-dashboard-tour.md +4 -4
  183. package/docs/guide/es/integrations/1-ai-providers.md +30 -18
  184. package/docs/guide/es/integrations/2-plugins.md +3 -3
  185. package/docs/guide/es/integrations/6-agent-chat.md +8 -1
  186. package/docs/guide/es/pipeline/1-rails-and-jobs.md +9 -6
  187. package/docs/guide/es/pipeline/2-the-job-detail-view.md +3 -1
  188. package/docs/guide/es/pipeline/3-batch-implement-and-multi-feature.md +1 -1
  189. package/docs/guide/es/pipeline/4-picking-an-engine-per-rail.md +15 -13
  190. package/docs/guide/es/pipeline/5-the-loop-builder.md +6 -2
  191. package/docs/guide/es/specs/2-add-spec-quick-mode.md +6 -1
  192. package/docs/guide/fr/agents/2-profiles-and-the-balanced-default.md +5 -1
  193. package/docs/guide/fr/agents/4-custom-agents-catalog.md +7 -4
  194. package/docs/guide/fr/getting-started/1-what-is-specrails.md +1 -0
  195. package/docs/guide/fr/getting-started/2-installing-and-first-run.md +1 -0
  196. package/docs/guide/fr/getting-started/4-the-dashboard-tour.md +4 -4
  197. package/docs/guide/fr/integrations/1-ai-providers.md +29 -18
  198. package/docs/guide/fr/integrations/2-plugins.md +3 -3
  199. package/docs/guide/fr/integrations/6-agent-chat.md +8 -1
  200. package/docs/guide/fr/pipeline/1-rails-and-jobs.md +9 -6
  201. package/docs/guide/fr/pipeline/2-the-job-detail-view.md +3 -1
  202. package/docs/guide/fr/pipeline/3-batch-implement-and-multi-feature.md +1 -1
  203. package/docs/guide/fr/pipeline/4-picking-an-engine-per-rail.md +15 -13
  204. package/docs/guide/fr/pipeline/5-the-loop-builder.md +6 -2
  205. package/docs/guide/fr/specs/2-add-spec-quick-mode.md +6 -1
  206. package/docs/guide/it/agents/2-profiles-and-the-balanced-default.md +5 -1
  207. package/docs/guide/it/agents/4-custom-agents-catalog.md +7 -4
  208. package/docs/guide/it/getting-started/1-what-is-specrails.md +1 -0
  209. package/docs/guide/it/getting-started/2-installing-and-first-run.md +1 -0
  210. package/docs/guide/it/getting-started/4-the-dashboard-tour.md +4 -4
  211. package/docs/guide/it/integrations/1-ai-providers.md +28 -18
  212. package/docs/guide/it/integrations/2-plugins.md +3 -3
  213. package/docs/guide/it/integrations/6-agent-chat.md +8 -1
  214. package/docs/guide/it/pipeline/1-rails-and-jobs.md +9 -6
  215. package/docs/guide/it/pipeline/2-the-job-detail-view.md +3 -1
  216. package/docs/guide/it/pipeline/3-batch-implement-and-multi-feature.md +1 -1
  217. package/docs/guide/it/pipeline/4-picking-an-engine-per-rail.md +13 -13
  218. package/docs/guide/it/pipeline/5-the-loop-builder.md +6 -2
  219. package/docs/guide/it/specs/2-add-spec-quick-mode.md +6 -1
  220. package/docs/guide/ja/agents/2-profiles-and-the-balanced-default.md +6 -1
  221. package/docs/guide/ja/agents/4-custom-agents-catalog.md +7 -4
  222. package/docs/guide/ja/getting-started/1-what-is-specrails.md +1 -0
  223. package/docs/guide/ja/getting-started/2-installing-and-first-run.md +1 -0
  224. package/docs/guide/ja/getting-started/4-the-dashboard-tour.md +4 -4
  225. package/docs/guide/ja/integrations/1-ai-providers.md +28 -18
  226. package/docs/guide/ja/integrations/2-plugins.md +3 -3
  227. package/docs/guide/ja/integrations/6-agent-chat.md +7 -1
  228. package/docs/guide/ja/pipeline/1-rails-and-jobs.md +9 -6
  229. package/docs/guide/ja/pipeline/2-the-job-detail-view.md +3 -1
  230. package/docs/guide/ja/pipeline/3-batch-implement-and-multi-feature.md +1 -1
  231. package/docs/guide/ja/pipeline/4-picking-an-engine-per-rail.md +14 -13
  232. package/docs/guide/ja/pipeline/5-the-loop-builder.md +6 -2
  233. package/docs/guide/ja/specs/2-add-spec-quick-mode.md +6 -1
  234. package/docs/guide/pt/agents/2-profiles-and-the-balanced-default.md +5 -1
  235. package/docs/guide/pt/agents/4-custom-agents-catalog.md +7 -4
  236. package/docs/guide/pt/getting-started/1-what-is-specrails.md +1 -0
  237. package/docs/guide/pt/getting-started/2-installing-and-first-run.md +1 -0
  238. package/docs/guide/pt/getting-started/4-the-dashboard-tour.md +4 -4
  239. package/docs/guide/pt/integrations/1-ai-providers.md +28 -18
  240. package/docs/guide/pt/integrations/2-plugins.md +3 -3
  241. package/docs/guide/pt/integrations/6-agent-chat.md +7 -1
  242. package/docs/guide/pt/pipeline/1-rails-and-jobs.md +9 -6
  243. package/docs/guide/pt/pipeline/2-the-job-detail-view.md +2 -1
  244. package/docs/guide/pt/pipeline/3-batch-implement-and-multi-feature.md +1 -1
  245. package/docs/guide/pt/pipeline/4-picking-an-engine-per-rail.md +14 -13
  246. package/docs/guide/pt/pipeline/5-the-loop-builder.md +6 -2
  247. package/docs/guide/pt/specs/2-add-spec-quick-mode.md +6 -1
  248. package/docs/guide/zh/agents/2-profiles-and-the-balanced-default.md +4 -1
  249. package/docs/guide/zh/agents/4-custom-agents-catalog.md +7 -4
  250. package/docs/guide/zh/getting-started/1-what-is-specrails.md +1 -0
  251. package/docs/guide/zh/getting-started/2-installing-and-first-run.md +1 -0
  252. package/docs/guide/zh/getting-started/4-the-dashboard-tour.md +4 -4
  253. package/docs/guide/zh/integrations/1-ai-providers.md +26 -18
  254. package/docs/guide/zh/integrations/2-plugins.md +3 -3
  255. package/docs/guide/zh/integrations/6-agent-chat.md +6 -1
  256. package/docs/guide/zh/pipeline/1-rails-and-jobs.md +9 -6
  257. package/docs/guide/zh/pipeline/2-the-job-detail-view.md +2 -1
  258. package/docs/guide/zh/pipeline/3-batch-implement-and-multi-feature.md +1 -1
  259. package/docs/guide/zh/pipeline/4-picking-an-engine-per-rail.md +12 -12
  260. package/docs/guide/zh/pipeline/5-the-loop-builder.md +6 -2
  261. package/docs/guide/zh/specs/2-add-spec-quick-mode.md +6 -1
  262. package/docs/internals/README.md +4 -1
  263. package/docs/internals/adding-a-provider.md +32 -28
  264. package/docs/internals/api-reference.md +37 -13
  265. package/docs/internals/architecture.md +5 -5
  266. package/docs/internals/bundled-framework-build-plan.md +3 -3
  267. package/docs/internals/code-explorer-story.md +4 -0
  268. package/docs/internals/configuration.md +17 -5
  269. package/docs/internals/global-artifacts-alignment-contract.md +1 -1
  270. package/docs/internals/operations-runbook.md +1 -1
  271. package/docs/internals/profiles.md +37 -45
  272. package/docs/internals/project-builder.md +16 -0
  273. package/docs/kimi.md +291 -0
  274. package/docs/mcp.md +9 -2
  275. package/docs/platforms/macos.md +17 -7
  276. package/docs/platforms/windows.md +9 -4
  277. package/docs/running-pipelines.md +46 -12
  278. package/docs/terminal.md +1 -1
  279. package/docs/tracking-cost.md +25 -6
  280. package/package.json +4 -2
  281. package/server/dist/agent-chat-manager.js +74 -32
  282. package/server/dist/agent-chat-router.js +40 -17
  283. package/server/dist/agent-cwd-manager.js +14 -7
  284. package/server/dist/agent-generator.js +69 -85
  285. package/server/dist/agent-mcp-config.js +17 -11
  286. package/server/dist/agent-operator-prompt.js +29 -20
  287. package/server/dist/agent-refine-manager.js +58 -38
  288. package/server/dist/ai-invocations.js +65 -20
  289. package/server/dist/attachment-manager.js +49 -15
  290. package/server/dist/blueprint-chat-manager.js +41 -16
  291. package/server/dist/blueprint-router.js +40 -6
  292. package/server/dist/chat-manager.js +165 -89
  293. package/server/dist/code-explorer-router.js +26 -7
  294. package/server/dist/contract-refine-runner.js +146 -113
  295. package/server/dist/core-compat.js +57 -2
  296. package/server/dist/core-package.js +3 -3
  297. package/server/dist/core-update-manager.js +17 -1
  298. package/server/dist/db.js +60 -5
  299. package/server/dist/desktop-analytics.js +44 -6
  300. package/server/dist/desktop-router.js +127 -11
  301. package/server/dist/explore-cwd-manager.js +19 -3
  302. package/server/dist/feature-flags.js +1 -1
  303. package/server/dist/file-story-manager.js +3 -3
  304. package/server/dist/file-summary-generator.js +21 -15
  305. package/server/dist/file-summary-manager.js +6 -6
  306. package/server/dist/framework-manager.js +19 -2
  307. package/server/dist/framework-migration.js +27 -12
  308. package/server/dist/install-config-path.js +1 -1
  309. package/server/dist/interactive-job-session.js +8 -6
  310. package/server/dist/loop-command-catalog.js +27 -10
  311. package/server/dist/loop-executors.js +59 -27
  312. package/server/dist/loop-factory.js +2 -2
  313. package/server/dist/loop-run-manager.js +100 -39
  314. package/server/dist/loops-router.js +1 -1
  315. package/server/dist/mcp/guide.js +23 -16
  316. package/server/dist/mcp/tools/agents.js +47 -41
  317. package/server/dist/mcp/tools/analytics.js +2 -2
  318. package/server/dist/mcp/tools/loops.js +2 -2
  319. package/server/dist/mcp/tools/plugins.js +14 -9
  320. package/server/dist/mcp/tools/rails.js +12 -13
  321. package/server/dist/mcp/tools/support.js +1 -1
  322. package/server/dist/mobile/mobile-router.js +5 -1
  323. package/server/dist/offline-assemble.js +13 -1
  324. package/server/dist/path-resolver.js +2 -2
  325. package/server/dist/plugin-manager.js +391 -106
  326. package/server/dist/plugins/claude-md-mutation.js +5 -4
  327. package/server/dist/plugins/drift.js +32 -29
  328. package/server/dist/plugins/rail-integration.js +13 -2
  329. package/server/dist/plugins/serena/install.js +25 -17
  330. package/server/dist/plugins/serena/manifest.js +22 -5
  331. package/server/dist/plugins/serena/verify.js +46 -16
  332. package/server/dist/plugins-router.js +59 -8
  333. package/server/dist/profile-manager.js +148 -36
  334. package/server/dist/profiles-router.js +311 -82
  335. package/server/dist/project-registry.js +2 -2
  336. package/server/dist/project-router-chat.js +17 -8
  337. package/server/dist/project-router-helpers.js +93 -5
  338. package/server/dist/project-router-jobs.js +76 -12
  339. package/server/dist/project-router-loop-runs.js +42 -14
  340. package/server/dist/project-router-settings.js +31 -10
  341. package/server/dist/project-router-setup.js +4 -5
  342. package/server/dist/project-router-spending.js +15 -3
  343. package/server/dist/project-router-tickets.js +187 -181
  344. package/server/dist/project-router.js +1 -0
  345. package/server/dist/proposal-manager.js +125 -85
  346. package/server/dist/providers/claude-adapter.js +14 -0
  347. package/server/dist/providers/codex-adapter.js +20 -0
  348. package/server/dist/providers/gemini-adapter.js +14 -0
  349. package/server/dist/providers/index.js +18 -1
  350. package/server/dist/providers/kimi-adapter.js +361 -0
  351. package/server/dist/providers/kimi-skill-prompt.js +332 -0
  352. package/server/dist/providers/runtime.js +137 -0
  353. package/server/dist/queue-manager.js +103 -78
  354. package/server/dist/rail-isolated-launch.js +1 -1
  355. package/server/dist/rails-router.js +51 -38
  356. package/server/dist/result-event.js +5 -0
  357. package/server/dist/schemas/profile.v1.json +3 -4
  358. package/server/dist/setup-manager.js +222 -101
  359. package/server/dist/setup-prerequisites.js +7 -1
  360. package/server/dist/smash-runner.js +104 -57
  361. package/server/dist/spawn-lifecycle.js +18 -14
  362. package/server/dist/spec-launcher-manager.js +66 -54
  363. package/server/dist/spec-models.js +15 -6
  364. package/server/dist/spending.js +110 -17
  365. package/server/dist/util/cli-prompt.js +142 -5
  366. package/server/dist/util/stream-display.js +46 -0
  367. package/server/dist/workspace-manager.js +100 -37
  368. package/server/dist/worktree-manager.js +4 -0
  369. package/server/dist/worktree-overlay.js +12 -3
  370. package/client/dist/assets/AgentModeCodePane-BAYyFRmY.js +0 -2
  371. package/client/dist/assets/AgentsPage-DuyJuaFs.js +0 -86
  372. package/client/dist/assets/AnalyticsPage-BY7gtCx6.js +0 -1
  373. package/client/dist/assets/CodePage-vyQr40Vl.js +0 -2
  374. package/client/dist/assets/JobDetailPage-CU4FEnUC.js +0 -1
  375. package/client/dist/assets/LoopsPage-SqglnXlp.js +0 -1
  376. package/client/dist/assets/PluginsPage-B89S8o1a.js +0 -2
  377. package/client/dist/assets/TicketDetailModal-CI47Dq9E.js +0 -14
  378. package/client/dist/assets/addspec-BXjvkh5q.js +0 -1
  379. package/client/dist/assets/addspec-C4bwrmpi.js +0 -1
  380. package/client/dist/assets/addspec-CArHXUDM.js +0 -1
  381. package/client/dist/assets/addspec-DTKJcXiR.js +0 -1
  382. package/client/dist/assets/addspec-DbNBbrha.js +0 -1
  383. package/client/dist/assets/addspec-Rw8uVBC1.js +0 -1
  384. package/client/dist/assets/addspec-htHGZHOe.js +0 -1
  385. package/client/dist/assets/addspec-lr_LP6jk.js +0 -1
  386. package/client/dist/assets/agent-BABjRt0P.js +0 -1
  387. package/client/dist/assets/agent-BuUpgqVx.js +0 -1
  388. package/client/dist/assets/agent-CCDZesgU.js +0 -1
  389. package/client/dist/assets/agent-CVn64zWH.js +0 -1
  390. package/client/dist/assets/agent-CWeOny0E.js +0 -1
  391. package/client/dist/assets/agent-DOysmad_.js +0 -1
  392. package/client/dist/assets/agent-DSsjTBhn.js +0 -1
  393. package/client/dist/assets/agent-DlZMxfM9.js +0 -1
  394. package/client/dist/assets/agents-6ZOJXWVk.js +0 -1
  395. package/client/dist/assets/agents-BZ0XQKjg.js +0 -1
  396. package/client/dist/assets/agents-BqWklnHR.js +0 -1
  397. package/client/dist/assets/agents-Bz-thHkB.js +0 -1
  398. package/client/dist/assets/agents-CJclElx6.js +0 -1
  399. package/client/dist/assets/agents-D_l9L0VA.js +0 -1
  400. package/client/dist/assets/agents-DhG7mg6-.js +0 -1
  401. package/client/dist/assets/agents-u_C0jPB5.js +0 -1
  402. package/client/dist/assets/agentstudio-BF19hbXj.js +0 -1
  403. package/client/dist/assets/agentstudio-BIUe1Im2.js +0 -1
  404. package/client/dist/assets/agentstudio-BLGvxWBx.js +0 -1
  405. package/client/dist/assets/agentstudio-D9UKiWTj.js +0 -1
  406. package/client/dist/assets/agentstudio-Drs37nIM.js +0 -1
  407. package/client/dist/assets/agentstudio-nEnKkcXM.js +0 -1
  408. package/client/dist/assets/agentstudio-pyFcSBKy.js +0 -1
  409. package/client/dist/assets/agentstudio-zfTzQh3U.js +0 -1
  410. package/client/dist/assets/analytics-BC7mwR4s.js +0 -1
  411. package/client/dist/assets/analytics-BiNg8edP.js +0 -1
  412. package/client/dist/assets/analytics-C0s_T2GN.js +0 -1
  413. package/client/dist/assets/analytics-CdsAPAwZ.js +0 -1
  414. package/client/dist/assets/analytics-Cr1_q-lC.js +0 -1
  415. package/client/dist/assets/analytics-DZ8Aoq3I.js +0 -1
  416. package/client/dist/assets/analytics-IEsKZvpW.js +0 -1
  417. package/client/dist/assets/analytics-SRPf84OT.js +0 -1
  418. package/client/dist/assets/builder-BH8Fua-g.js +0 -1
  419. package/client/dist/assets/builder-BoV7DgKd.js +0 -1
  420. package/client/dist/assets/builder-Cc_Lf79C.js +0 -1
  421. package/client/dist/assets/builder-CwZ7Bme2.js +0 -1
  422. package/client/dist/assets/builder-CxW-JFsR.js +0 -1
  423. package/client/dist/assets/builder-D94lrV7x.js +0 -1
  424. package/client/dist/assets/builder-cqHpCyGa.js +0 -1
  425. package/client/dist/assets/code-76gS0IdL.js +0 -1
  426. package/client/dist/assets/code-BDXEM4ml.js +0 -1
  427. package/client/dist/assets/code-C1VwxlML.js +0 -1
  428. package/client/dist/assets/code-CnMZyJpr.js +0 -1
  429. package/client/dist/assets/code-D20ef9Cf.js +0 -1
  430. package/client/dist/assets/code-DdOXaWRY.js +0 -1
  431. package/client/dist/assets/code-Duohuf9l.js +0 -1
  432. package/client/dist/assets/code-geHmknB9.js +0 -1
  433. package/client/dist/assets/format-command-Dfa1o2d-.js +0 -1
  434. package/client/dist/assets/index-Dh9i4CnN.css +0 -2
  435. package/client/dist/assets/index-F9r8mWP_.js +0 -76
  436. package/client/dist/assets/jobs-BCtGfgUA.js +0 -1
  437. package/client/dist/assets/jobs-BHR_EnDI.js +0 -1
  438. package/client/dist/assets/jobs-BMbZJjJJ.js +0 -1
  439. package/client/dist/assets/jobs-DzSIR8V6.js +0 -1
  440. package/client/dist/assets/nav-BRJBxaGt.js +0 -1
  441. package/client/dist/assets/nav-CAGKv3_F.js +0 -1
  442. package/client/dist/assets/nav-CNskRJKQ.js +0 -1
  443. package/client/dist/assets/nav-DY93irBL.js +0 -1
  444. package/client/dist/assets/nav-DhjvYgqt.js +0 -1
  445. package/client/dist/assets/nav-Gb-r4wop.js +0 -1
  446. package/client/dist/assets/nav-TvumAFD2.js +0 -1
  447. package/client/dist/assets/nav-myFaXRHD.js +0 -1
  448. package/client/dist/assets/settings-B0ypw9uw.js +0 -1
  449. package/client/dist/assets/settings-B9ilOSYO.js +0 -1
  450. package/client/dist/assets/settings-BqlWHRHA.js +0 -1
  451. package/client/dist/assets/settings-D8dlqt4d.js +0 -1
  452. package/client/dist/assets/settings-DAZqjX2a.js +0 -1
  453. package/client/dist/assets/settings-DRh39v1S.js +0 -1
  454. package/client/dist/assets/settings-Mj1OJbfd.js +0 -1
  455. package/client/dist/assets/settings-lRReaSYm.js +0 -1
  456. package/client/dist/assets/setup-3832BJCo.js +0 -1
  457. package/client/dist/assets/setup-B8J-gECe.js +0 -1
  458. package/client/dist/assets/setup-BCIOxVsc.js +0 -1
  459. package/client/dist/assets/setup-BciLj_tU.js +0 -1
  460. package/client/dist/assets/setup-CTkE3cfh.js +0 -1
  461. package/client/dist/assets/setup-ajB4rYm-.js +0 -1
  462. package/client/dist/assets/setup-xFO1wNl0.js +0 -1
@@ -25,7 +25,8 @@ const node_readline_1 = require("node:readline");
25
25
  const tree_kill_1 = __importDefault(require("tree-kill"));
26
26
  const cli_prompt_1 = require("./util/cli-prompt");
27
27
  const spawn_lifecycle_1 = require("./spawn-lifecycle");
28
- const registry_1 = require("./providers/registry");
28
+ const providers_1 = require("./providers");
29
+ const runtime_1 = require("./providers/runtime");
29
30
  const explore_contract_refine_1 = require("./explore-contract-refine");
30
31
  const db_1 = require("./db");
31
32
  const explore_cwd_manager_1 = require("./explore-cwd-manager");
@@ -65,29 +66,30 @@ function isResultErrorEvent(resultEvent) {
65
66
  // `error_*` subtype as a model-side error rather than malformed output.
66
67
  return subtype === 'error_max_turns' || subtype.startsWith('error_');
67
68
  }
68
- function normalizeClaudeCodeModel(model) {
69
- if (!model || typeof model !== 'string')
70
- return 'sonnet';
71
- return model;
72
- }
73
- function buildRefineArgs(model, systemPrompt, sessionId) {
74
- return [
75
- '--model', normalizeClaudeCodeModel(model),
76
- // Contract Refine receives all required spec context in the prompt. An
77
- // explicit no-tool allowlist is stronger than trying to enumerate every
78
- // present and future tool in --disallowedTools.
79
- '--tools', '__none__',
80
- '--output-format', 'stream-json',
81
- '--verbose',
82
- '--system-prompt', systemPrompt,
83
- '--resume', sessionId,
84
- '--max-turns', '1',
85
- '-p', explore_contract_refine_1.CONTRACT_MARKER_USER_MESSAGE,
86
- ];
69
+ function buildRefineArgs(adapter, model, systemPrompt, sessionId) {
70
+ const action = sessionId && adapter.capabilities.nativeResume ? 'chat-resume' : 'spec-gen';
71
+ // Chat adapters intentionally keep resumed Explore prompts short because the
72
+ // app-managed cwd already carries the normal Explore stance. Contract Refine
73
+ // is different: its schema is invocation-specific and must be present on the
74
+ // resumed turn. Providers without a native system-prompt flag therefore need
75
+ // the schema folded here, exactly once, before their chat-resume builder
76
+ // deliberately ignores `systemPrompt`.
77
+ const foldForResume = action === 'chat-resume' && !adapter.capabilities.systemPromptArg;
78
+ const options = {
79
+ prompt: foldForResume
80
+ ? `${systemPrompt}\n\n---\n\n${explore_contract_refine_1.CONTRACT_MARKER_USER_MESSAGE}`
81
+ : explore_contract_refine_1.CONTRACT_MARKER_USER_MESSAGE,
82
+ model,
83
+ systemPrompt: foldForResume ? undefined : systemPrompt,
84
+ sessionId,
85
+ toolPolicy: 'none',
86
+ maxTurns: 1,
87
+ };
88
+ return { args: adapter.buildArgs(action, options), options };
87
89
  }
88
90
  /** Build the pure-output, no-resume invocation used by Quick Refine and by the
89
91
  * one-time compatibility recovery for cwd-scoped Explore sessions. */
90
- function buildFreshRefineArgs(model, title, description) {
92
+ function buildFreshRefineArgs(adapter, model, title, description) {
91
93
  const systemPrompt = [
92
94
  (0, explore_contract_refine_1.buildContractRefineSystemPrompt)(),
93
95
  '',
@@ -99,15 +101,14 @@ function buildFreshRefineArgs(model, title, description) {
99
101
  '### Description',
100
102
  description,
101
103
  ].join('\n');
102
- return [
103
- '--model', model,
104
- '--tools', '__none__',
105
- '--output-format', 'stream-json',
106
- '--verbose',
107
- '--system-prompt', systemPrompt,
108
- '--max-turns', '1',
109
- '-p', explore_contract_refine_1.CONTRACT_MARKER_USER_MESSAGE,
110
- ];
104
+ const options = {
105
+ prompt: explore_contract_refine_1.CONTRACT_MARKER_USER_MESSAGE,
106
+ model,
107
+ systemPrompt,
108
+ toolPolicy: 'none',
109
+ maxTurns: 1,
110
+ };
111
+ return { args: adapter.buildArgs('spec-gen', options), options };
111
112
  }
112
113
  /** Match only Claude's missing cwd-scoped session diagnostic. The CLI has
113
114
  * emitted it both as stderr and inside structured result payloads, so inspect
@@ -180,8 +181,10 @@ function prepareContractRefineSpawn(deps, conversation) {
180
181
  cwd = deps.projectPath;
181
182
  }
182
183
  }
183
- const args = buildRefineArgs(conversation.model ?? 'sonnet', systemPrompt, conversation.session_id ?? '');
184
- return { args, cwd, systemPrompt, env };
184
+ const adapter = (0, providers_1.getAdapter)(conversation.provider ?? deps.providerId ?? 'claude');
185
+ const model = conversation.model ?? adapter.defaultModel();
186
+ const built = buildRefineArgs(adapter, model, systemPrompt, conversation.session_id ?? '');
187
+ return { args: built.args, cwd, systemPrompt, env, options: built.options, adapter };
185
188
  }
186
189
  /** Grace before SIGKILL-escalating a child that swallowed SIGTERM. */
187
190
  const REFINE_SIGKILL_GRACE_MS = 2_000;
@@ -358,13 +361,9 @@ async function runContractRefine(deps, conversationId, ticketId) {
358
361
  console.log(`[contract-refine-runner] skip: conversation missing or not explore (kind=${conversation?.kind})`);
359
362
  return { ok: false, reason: 'not-explore', ticketId, conversationId };
360
363
  }
361
- // Contract Layer refinement is a Claude-only capability (it `--resume`s the
362
- // Explore session and invokes the `/specrails:contract-refine` slash command,
363
- // neither of which Codex supports). Skip defensively when the conversation
364
- // ran on a non-claude engine; the Add Spec UI already hides the toggle for
365
- // those, but a manually-crafted scope must never spawn the wrong CLI.
366
- if (conversation.provider && conversation.provider !== 'claude') {
367
- console.log(`[contract-refine-runner] skip: provider '${conversation.provider}' does not support contract refine`);
364
+ const adapter = (0, providers_1.getAdapter)(conversation.provider ?? deps.providerId ?? 'claude');
365
+ if (adapter.capabilities.structuredActions !== true) {
366
+ console.log(`[contract-refine-runner] skip: provider '${adapter.id}' does not support structured actions`);
368
367
  return { ok: false, reason: 'provider-unsupported', ticketId, conversationId };
369
368
  }
370
369
  // Per-conversation gating: contractRefine on the conversation's stored
@@ -394,13 +393,15 @@ async function runContractRefine(deps, conversationId, ticketId) {
394
393
  deps.broadcast({
395
394
  type: 'explore.contract_refine_started',
396
395
  projectId: deps.projectId,
396
+ provider: adapter.id,
397
397
  ticketId,
398
398
  timestamp: now().toISOString(),
399
399
  });
400
- const { args, cwd, env: refineEnv } = prepareContractRefineSpawn({
400
+ const { args, cwd, env: refineEnv, options: refineOptions } = prepareContractRefineSpawn({
401
401
  projectSlug: deps.projectSlug,
402
402
  projectPath: deps.projectPath,
403
403
  projectName: deps.projectName,
404
+ providerId: adapter.id,
404
405
  }, conversation);
405
406
  const startedAt = now().toISOString();
406
407
  // Spawn/stream/timeout/settlement is owned by the shared spawn-lifecycle; the
@@ -408,38 +409,24 @@ async function runContractRefine(deps, conversationId, ticketId) {
408
409
  // the raw result event) and all finalize/record/broadcast logic stay here.
409
410
  // Keeping the invocation result in one shape also lets the narrowly-gated
410
411
  // missing-session recovery re-enter the exact same finalization path.
411
- const invoke = async (invocationArgs) => {
412
+ const invoke = async (invocationArgs, invocationOptions) => {
412
413
  let fullText = '';
413
414
  let resultEvent = null;
414
415
  const run = await (0, spawn_lifecycle_1.runAiCliInvocation)({
415
- adapter: (0, registry_1.getAdapter)('claude'),
416
- binary: 'claude',
416
+ adapter,
417
+ binary: adapter.binary,
417
418
  argv: invocationArgs,
418
419
  cwd,
419
- env: refineEnv ?? process.env,
420
+ env: (0, runtime_1.buildProviderEnv)(adapter, invocationOptions, refineEnv ?? process.env),
420
421
  spawn,
421
422
  timeoutMs,
422
423
  onSpawn: (child) => (0, transient_children_1.trackTransientChild)(deps.projectId, child),
423
- onStdoutLine: (line) => {
424
- let parsed = null;
425
- try {
426
- parsed = JSON.parse(line);
424
+ onEvent: (event) => {
425
+ if (event.kind === 'text-delta') {
426
+ fullText += event.text;
427
427
  }
428
- catch {
429
- return;
430
- }
431
- if (!parsed)
432
- return;
433
- const type = parsed.type;
434
- if (type === 'result') {
435
- resultEvent = parsed;
436
- }
437
- else if (type === 'assistant') {
438
- const message = parsed.message;
439
- for (const b of (message?.content ?? [])) {
440
- if (b.type === 'text' && typeof b.text === 'string')
441
- fullText += b.text;
442
- }
428
+ else if (event.kind === 'result') {
429
+ resultEvent = event.payload;
443
430
  }
444
431
  },
445
432
  });
@@ -450,9 +437,10 @@ async function runContractRefine(deps, conversationId, ticketId) {
450
437
  timedOut: run.timedOut,
451
438
  spawnFailed: run.spawnFailed,
452
439
  stderrTail: run.stderrTail,
440
+ events: run.events,
453
441
  };
454
442
  };
455
- let result = await invoke(args);
443
+ let result = await invoke(args, refineOptions);
456
444
  if (!admission.isCurrent()) {
457
445
  return { ok: false, reason: 'aborted', ticketId, conversationId };
458
446
  }
@@ -468,7 +456,7 @@ async function runContractRefine(deps, conversationId, ticketId) {
468
456
  const missingSessionDiagnostic = containsMissingClaudeSessionDiagnostic(result.fullText) ||
469
457
  containsMissingClaudeSessionDiagnostic(result.resultEvent) ||
470
458
  containsMissingClaudeSessionDiagnostic(result.stderrTail);
471
- if (resumedFromRelocatedWorkspace && resumeFailed && missingSessionDiagnostic) {
459
+ if (adapter.id === 'claude' && resumedFromRelocatedWorkspace && resumeFailed && missingSessionDiagnostic) {
472
460
  let ticket = null;
473
461
  try {
474
462
  ticket = (0, ticket_store_1.readStore)(resolveContractTicketsPath(deps.projectPath)).tickets[String(ticketId)] ?? null;
@@ -478,7 +466,8 @@ async function runContractRefine(deps, conversationId, ticketId) {
478
466
  }
479
467
  if (ticket) {
480
468
  console.warn(`[contract-refine-runner] resume session unavailable; retrying once fresh from workspace ticket=${ticketId}`);
481
- result = await invoke(buildFreshRefineArgs(conversation.model ?? 'sonnet', ticket.title, ticket.description));
469
+ const fresh = buildFreshRefineArgs(adapter, conversation.model ?? adapter.defaultModel(), ticket.title, ticket.description);
470
+ result = await invoke(fresh.args, fresh.options);
482
471
  if (!admission.isCurrent()) {
483
472
  return { ok: false, reason: 'aborted', ticketId, conversationId };
484
473
  }
@@ -486,10 +475,11 @@ async function runContractRefine(deps, conversationId, ticketId) {
486
475
  }
487
476
  const finishedAt = now().toISOString();
488
477
  if (result.spawnFailed) {
489
- recordSafely(deps, conversationId, ticketId, conversation.model, startedAt, finishedAt, 'failed', null);
478
+ recordSafely(deps, adapter, conversationId, ticketId, conversation.model, startedAt, finishedAt, 'failed', result.events);
490
479
  deps.broadcast({
491
480
  type: 'explore.contract_refine_failed',
492
481
  projectId: deps.projectId,
482
+ provider: adapter.id,
493
483
  ticketId,
494
484
  reason: 'crashed',
495
485
  timestamp: finishedAt,
@@ -498,17 +488,19 @@ async function runContractRefine(deps, conversationId, ticketId) {
498
488
  }
499
489
  console.log(`[contract-refine-runner] spawn done code=${result.code} timedOut=${result.timedOut} hasResult=${!!result.resultEvent} textBytes=${result.fullText.length}`);
500
490
  if (result.timedOut) {
501
- recordSafely(deps, conversationId, ticketId, conversation.model, startedAt, finishedAt, 'aborted', result.resultEvent);
491
+ recordSafely(deps, adapter, conversationId, ticketId, conversation.model, startedAt, finishedAt, 'aborted', result.events);
502
492
  deps.broadcast({
503
493
  type: 'explore.contract_refine_failed',
504
494
  projectId: deps.projectId,
495
+ provider: adapter.id,
505
496
  ticketId,
506
497
  reason: 'timeout',
507
498
  timestamp: finishedAt,
508
499
  });
509
500
  return { ok: false, reason: 'timeout', ticketId, conversationId };
510
501
  }
511
- if (result.code !== 0 || !result.resultEvent) {
502
+ const providerError = result.events.some((event) => event.kind === 'error');
503
+ if (result.code !== 0 || providerError || !result.fullText.trim()) {
512
504
  const r = result.resultEvent;
513
505
  console.log(`[contract-refine-runner] non-zero exit code=${result.code} ` +
514
506
  `subtype=${r?.subtype ?? '-'} is_error=${r?.is_error ?? '-'} ` +
@@ -516,25 +508,27 @@ async function runContractRefine(deps, conversationId, ticketId) {
516
508
  `textTail=${JSON.stringify(result.fullText.slice(-400))}`);
517
509
  if (r)
518
510
  console.log(`[contract-refine-runner] result event: ${JSON.stringify(r).slice(0, 2000)}`);
519
- recordSafely(deps, conversationId, ticketId, conversation.model, startedAt, finishedAt, 'failed', result.resultEvent);
511
+ recordSafely(deps, adapter, conversationId, ticketId, conversation.model, startedAt, finishedAt, 'failed', result.events);
520
512
  deps.broadcast({
521
513
  type: 'explore.contract_refine_failed',
522
514
  projectId: deps.projectId,
515
+ provider: adapter.id,
523
516
  ticketId,
524
- reason: result.resultEvent ? 'model_error' : 'crashed',
517
+ reason: providerError || result.resultEvent ? 'model_error' : 'crashed',
525
518
  timestamp: finishedAt,
526
519
  });
527
- return { ok: false, reason: result.resultEvent ? 'model_error' : 'crashed', ticketId, conversationId };
520
+ return { ok: false, reason: providerError || result.resultEvent ? 'model_error' : 'crashed', ticketId, conversationId };
528
521
  }
529
522
  // BUG-PARSER-04: exit-0 but the result event flags an error / max-turns
530
523
  // truncation — classify as model_error, not malformed.
531
- if (isResultErrorEvent(result.resultEvent)) {
524
+ if (result.resultEvent && isResultErrorEvent(result.resultEvent)) {
532
525
  const r = result.resultEvent;
533
526
  console.log(`[contract-refine-runner] result error event subtype=${r.subtype ?? '-'} is_error=${r.is_error ?? '-'}`);
534
- recordSafely(deps, conversationId, ticketId, conversation.model, startedAt, finishedAt, 'failed', result.resultEvent);
527
+ recordSafely(deps, adapter, conversationId, ticketId, conversation.model, startedAt, finishedAt, 'failed', result.events);
535
528
  deps.broadcast({
536
529
  type: 'explore.contract_refine_failed',
537
530
  projectId: deps.projectId,
531
+ provider: adapter.id,
538
532
  ticketId,
539
533
  reason: 'model_error',
540
534
  timestamp: finishedAt,
@@ -547,10 +541,11 @@ async function runContractRefine(deps, conversationId, ticketId) {
547
541
  const reason = parse.reason === 'parser-error'
548
542
  ? 'parser_error'
549
543
  : 'malformed';
550
- recordSafely(deps, conversationId, ticketId, conversation.model, startedAt, finishedAt, 'failed', result.resultEvent);
544
+ recordSafely(deps, adapter, conversationId, ticketId, conversation.model, startedAt, finishedAt, 'failed', result.events);
551
545
  deps.broadcast({
552
546
  type: 'explore.contract_refine_failed',
553
547
  projectId: deps.projectId,
548
+ provider: adapter.id,
554
549
  ticketId,
555
550
  reason,
556
551
  timestamp: finishedAt,
@@ -565,17 +560,18 @@ async function runContractRefine(deps, conversationId, ticketId) {
565
560
  }
566
561
  catch (err) {
567
562
  console.error('[contract-refine-runner] PATCH failed:', err);
568
- recordSafely(deps, conversationId, ticketId, conversation.model, startedAt, finishedAt, 'failed', result.resultEvent);
563
+ recordSafely(deps, adapter, conversationId, ticketId, conversation.model, startedAt, finishedAt, 'failed', result.events);
569
564
  deps.broadcast({
570
565
  type: 'explore.contract_refine_failed',
571
566
  projectId: deps.projectId,
567
+ provider: adapter.id,
572
568
  ticketId,
573
569
  reason: 'parser_error',
574
570
  timestamp: finishedAt,
575
571
  });
576
572
  return { ok: false, reason: 'parser_error', ticketId, conversationId };
577
573
  }
578
- recordSafely(deps, conversationId, ticketId, conversation.model, startedAt, finishedAt, 'success', result.resultEvent);
574
+ recordSafely(deps, adapter, conversationId, ticketId, conversation.model, startedAt, finishedAt, 'success', result.events);
579
575
  if (updated) {
580
576
  deps.broadcast({
581
577
  type: 'ticket_updated',
@@ -605,79 +601,106 @@ async function runContractRefineForQuick(deps, ticketId, generatedTitle, generat
605
601
  return { ok: false, reason: 'disabled', ticketId, conversationId: '' };
606
602
  }
607
603
  const admission = (0, process_admission_1.captureProcessAdmission)(deps.projectId);
608
- const args = buildFreshRefineArgs(model ?? 'sonnet', generatedTitle, generatedDescription);
604
+ const adapter = (0, providers_1.getAdapter)(deps.providerId ?? 'claude');
605
+ if (adapter.capabilities.structuredActions !== true) {
606
+ return { ok: false, reason: 'provider-unsupported', ticketId, conversationId: '' };
607
+ }
608
+ const resolvedModel = model ?? adapter.defaultModel();
609
+ const built = buildFreshRefineArgs(adapter, resolvedModel, generatedTitle, generatedDescription);
609
610
  const startedAt = now().toISOString();
610
611
  deps.broadcast({
611
612
  type: 'explore.contract_refine_started',
612
613
  projectId: deps.projectId,
614
+ provider: adapter.id,
613
615
  ticketId,
614
616
  timestamp: startedAt,
615
617
  });
616
618
  // Relocate-artifacts gate: spawn from the workspace + SPECRAILS_REPO_DIR when
617
619
  // relocated, else cwd = project.path + process.env (byte-identical legacy).
618
620
  const quickExec = (0, workspace_resolution_1.resolveProjectExecution)({ slug: deps.projectSlug, path: deps.projectPath });
619
- let child;
620
- try {
621
- child = spawn('claude', args, {
622
- env: quickExec.relocated ? { ...process.env, ...quickExec.env } : process.env,
623
- stdio: ['ignore', 'pipe', 'pipe'],
624
- cwd: quickExec.cwd,
625
- });
626
- (0, transient_children_1.trackTransientChild)(deps.projectId, child);
621
+ let fullText = '';
622
+ let resultEvent = null;
623
+ const run = await (0, spawn_lifecycle_1.runAiCliInvocation)({
624
+ adapter,
625
+ binary: adapter.binary,
626
+ argv: built.args,
627
+ cwd: quickExec.cwd,
628
+ env: (0, runtime_1.buildProviderEnv)(adapter, built.options, quickExec.relocated ? { ...process.env, ...quickExec.env } : process.env),
629
+ spawn,
630
+ timeoutMs,
631
+ onSpawn: (child) => (0, transient_children_1.trackTransientChild)(deps.projectId, child),
632
+ onEvent: (event) => {
633
+ if (event.kind === 'text-delta')
634
+ fullText += event.text;
635
+ else if (event.kind === 'result')
636
+ resultEvent = event.payload;
637
+ },
638
+ });
639
+ const result = {
640
+ fullText,
641
+ resultEvent,
642
+ code: run.code,
643
+ timedOut: run.timedOut,
644
+ spawnFailed: run.spawnFailed,
645
+ events: run.events,
646
+ };
647
+ if (!admission.isCurrent()) {
648
+ return { ok: false, reason: 'aborted', ticketId, conversationId: '' };
627
649
  }
628
- catch (err) {
629
- recordSafelyQuick(deps, ticketId, model, startedAt, now().toISOString(), 'failed', null);
650
+ const finishedAt = now().toISOString();
651
+ console.log(`[contract-refine-runner] quick spawn done code=${result.code} timedOut=${result.timedOut} textBytes=${result.fullText.length}`);
652
+ if (result.spawnFailed) {
653
+ recordSafelyQuick(deps, adapter, ticketId, resolvedModel, startedAt, finishedAt, 'failed', result.events);
630
654
  deps.broadcast({
631
655
  type: 'explore.contract_refine_failed',
632
656
  projectId: deps.projectId,
657
+ provider: adapter.id,
633
658
  ticketId,
634
659
  reason: 'crashed',
635
- timestamp: now().toISOString(),
660
+ timestamp: finishedAt,
636
661
  });
637
662
  return { ok: false, reason: 'crashed', ticketId, conversationId: '' };
638
663
  }
639
- const result = await readRefineChildOutput(child, timeoutMs);
640
- if (!admission.isCurrent()) {
641
- return { ok: false, reason: 'aborted', ticketId, conversationId: '' };
642
- }
643
- const finishedAt = now().toISOString();
644
- console.log(`[contract-refine-runner] quick spawn done code=${result.code} timedOut=${result.timedOut} textBytes=${result.fullText.length}`);
645
664
  if (result.timedOut) {
646
- recordSafelyQuick(deps, ticketId, model, startedAt, finishedAt, 'aborted', result.resultEvent);
665
+ recordSafelyQuick(deps, adapter, ticketId, resolvedModel, startedAt, finishedAt, 'aborted', result.events);
647
666
  deps.broadcast({
648
667
  type: 'explore.contract_refine_failed',
649
668
  projectId: deps.projectId,
669
+ provider: adapter.id,
650
670
  ticketId,
651
671
  reason: 'timeout',
652
672
  timestamp: finishedAt,
653
673
  });
654
674
  return { ok: false, reason: 'timeout', ticketId, conversationId: '' };
655
675
  }
656
- if (result.code !== 0 || !result.resultEvent) {
676
+ const providerError = result.events.some((event) => event.kind === 'error');
677
+ if (result.code !== 0 || providerError || !result.fullText.trim()) {
657
678
  const r = result.resultEvent;
658
679
  console.log(`[contract-refine-runner] quick non-zero exit code=${result.code} ` +
659
680
  `subtype=${r?.subtype ?? '-'} is_error=${r?.is_error ?? '-'} ` +
660
681
  `num_turns=${r?.num_turns ?? '-'} ` +
661
682
  `textTail=${JSON.stringify(result.fullText.slice(-400))}`);
662
- recordSafelyQuick(deps, ticketId, model, startedAt, finishedAt, 'failed', result.resultEvent);
683
+ recordSafelyQuick(deps, adapter, ticketId, resolvedModel, startedAt, finishedAt, 'failed', result.events);
663
684
  deps.broadcast({
664
685
  type: 'explore.contract_refine_failed',
665
686
  projectId: deps.projectId,
687
+ provider: adapter.id,
666
688
  ticketId,
667
- reason: result.resultEvent ? 'model_error' : 'crashed',
689
+ reason: providerError || result.resultEvent ? 'model_error' : 'crashed',
668
690
  timestamp: finishedAt,
669
691
  });
670
- return { ok: false, reason: result.resultEvent ? 'model_error' : 'crashed', ticketId, conversationId: '' };
692
+ return { ok: false, reason: providerError || result.resultEvent ? 'model_error' : 'crashed', ticketId, conversationId: '' };
671
693
  }
672
694
  // BUG-PARSER-04: exit-0 but the result event flags an error / max-turns
673
695
  // truncation — classify as model_error, not malformed.
674
- if (isResultErrorEvent(result.resultEvent)) {
696
+ if (result.resultEvent && isResultErrorEvent(result.resultEvent)) {
675
697
  const r = result.resultEvent;
676
698
  console.log(`[contract-refine-runner] quick result error event subtype=${r.subtype ?? '-'} is_error=${r.is_error ?? '-'}`);
677
- recordSafelyQuick(deps, ticketId, model, startedAt, finishedAt, 'failed', result.resultEvent);
699
+ recordSafelyQuick(deps, adapter, ticketId, resolvedModel, startedAt, finishedAt, 'failed', result.events);
678
700
  deps.broadcast({
679
701
  type: 'explore.contract_refine_failed',
680
702
  projectId: deps.projectId,
703
+ provider: adapter.id,
681
704
  ticketId,
682
705
  reason: 'model_error',
683
706
  timestamp: finishedAt,
@@ -687,10 +710,11 @@ async function runContractRefineForQuick(deps, ticketId, generatedTitle, generat
687
710
  const parse = (0, explore_contract_refine_1.parseContractLayerBlock)(result.fullText);
688
711
  if (!parse.ok) {
689
712
  const reason = parse.reason === 'parser-error' ? 'parser_error' : 'malformed';
690
- recordSafelyQuick(deps, ticketId, model, startedAt, finishedAt, 'failed', result.resultEvent);
713
+ recordSafelyQuick(deps, adapter, ticketId, resolvedModel, startedAt, finishedAt, 'failed', result.events);
691
714
  deps.broadcast({
692
715
  type: 'explore.contract_refine_failed',
693
716
  projectId: deps.projectId,
717
+ provider: adapter.id,
694
718
  ticketId,
695
719
  reason,
696
720
  timestamp: finishedAt,
@@ -704,17 +728,18 @@ async function runContractRefineForQuick(deps, ticketId, generatedTitle, generat
704
728
  }
705
729
  catch (err) {
706
730
  console.error('[contract-refine-runner] quick PATCH failed:', err);
707
- recordSafelyQuick(deps, ticketId, model, startedAt, finishedAt, 'failed', result.resultEvent);
731
+ recordSafelyQuick(deps, adapter, ticketId, resolvedModel, startedAt, finishedAt, 'failed', result.events);
708
732
  deps.broadcast({
709
733
  type: 'explore.contract_refine_failed',
710
734
  projectId: deps.projectId,
735
+ provider: adapter.id,
711
736
  ticketId,
712
737
  reason: 'parser_error',
713
738
  timestamp: finishedAt,
714
739
  });
715
740
  return { ok: false, reason: 'parser_error', ticketId, conversationId: '' };
716
741
  }
717
- recordSafelyQuick(deps, ticketId, model, startedAt, finishedAt, 'success', result.resultEvent);
742
+ recordSafelyQuick(deps, adapter, ticketId, resolvedModel, startedAt, finishedAt, 'success', result.events);
718
743
  if (updated) {
719
744
  deps.broadcast({
720
745
  type: 'ticket_updated',
@@ -726,13 +751,16 @@ async function runContractRefineForQuick(deps, ticketId, generatedTitle, generat
726
751
  deps.broadcast({ type: 'spending.invalidated', projectId: deps.projectId });
727
752
  return { ok: true, ticketId, conversationId: '' };
728
753
  }
729
- function recordSafelyQuick(deps, ticketId, model, startedAt, finishedAt, status, resultEvent) {
754
+ function recordSafelyQuick(deps, adapter, ticketId, model, startedAt, finishedAt, status, events) {
730
755
  try {
731
- const normalised = resultEvent ? (0, result_event_1.normaliseResultEvent)(resultEvent, 'claude') : {};
756
+ const { result: normalised, estimated } = (0, result_event_1.finaliseInvocationResult)(adapter, events, {
757
+ fallbackModel: model ?? adapter.defaultModel(),
758
+ durationMs: Math.max(0, Date.parse(finishedAt) - Date.parse(startedAt)),
759
+ });
732
760
  (0, ai_invocations_1.recordInvocation)(deps.db, {
733
761
  id: (0, node_crypto_1.randomUUID)(),
734
762
  project_id: deps.projectId,
735
- provider: 'claude',
763
+ provider: adapter.id,
736
764
  surface: 'quick-spec',
737
765
  surface_ref_id: `contract-refine:${ticketId}`,
738
766
  conversation_id: null,
@@ -740,21 +768,25 @@ function recordSafelyQuick(deps, ticketId, model, startedAt, finishedAt, status,
740
768
  status,
741
769
  started_at: startedAt,
742
770
  finished_at: finishedAt,
771
+ total_cost_usd_estimated: estimated,
743
772
  ...normalised,
744
- model: resultEvent?.model ?? model ?? undefined,
773
+ model: normalised.model ?? model ?? undefined,
745
774
  });
746
775
  }
747
776
  catch (err) {
748
777
  console.error('[contract-refine-runner] quick recordInvocation failed:', err);
749
778
  }
750
779
  }
751
- function recordSafely(deps, conversationId, ticketId, model, startedAt, finishedAt, status, resultEvent) {
780
+ function recordSafely(deps, adapter, conversationId, ticketId, model, startedAt, finishedAt, status, events) {
752
781
  try {
753
- const normalised = resultEvent ? (0, result_event_1.normaliseResultEvent)(resultEvent, 'claude') : {};
782
+ const { result: normalised, estimated } = (0, result_event_1.finaliseInvocationResult)(adapter, events, {
783
+ fallbackModel: model ?? adapter.defaultModel(),
784
+ durationMs: Math.max(0, Date.parse(finishedAt) - Date.parse(startedAt)),
785
+ });
754
786
  (0, ai_invocations_1.recordInvocation)(deps.db, {
755
787
  id: (0, node_crypto_1.randomUUID)(),
756
788
  project_id: deps.projectId,
757
- provider: 'claude',
789
+ provider: adapter.id,
758
790
  surface: 'explore-spec',
759
791
  surface_ref_id: `contract-refine:${conversationId}`,
760
792
  conversation_id: conversationId,
@@ -762,8 +794,9 @@ function recordSafely(deps, conversationId, ticketId, model, startedAt, finished
762
794
  status,
763
795
  started_at: startedAt,
764
796
  finished_at: finishedAt,
797
+ total_cost_usd_estimated: estimated,
765
798
  ...normalised,
766
- model: resultEvent?.model ?? model ?? undefined,
799
+ model: normalised.model ?? model ?? undefined,
767
800
  });
768
801
  }
769
802
  catch (err) {
@@ -9,10 +9,12 @@ exports.detectAvailableCLIs = detectAvailableCLIs;
9
9
  exports.detectCLI = detectCLI;
10
10
  exports.getCLIStatus = getCLIStatus;
11
11
  exports.checkCoreCompat = checkCoreCompat;
12
+ exports.coreCompatSupportsProvider = coreCompatSupportsProvider;
12
13
  const child_process_1 = require("child_process");
13
14
  const fs_1 = __importDefault(require("fs"));
14
15
  const path_1 = __importDefault(require("path"));
15
16
  const setup_manager_1 = require("./setup-manager");
17
+ const bundled_core_1 = require("./bundled-core");
16
18
  // Windows has no `which`; probe PATH via `where` instead.
17
19
  const WHICH_CMD = process.platform === 'win32' ? 'where' : 'which';
18
20
  // These must mirror KNOWN_VERBS in cli/specrails-desktop.ts
@@ -27,15 +29,55 @@ const DESKTOP_KNOWN_COMMANDS = new Set([
27
29
  'compat-check',
28
30
  'enrich',
29
31
  ]);
32
+ function isRecord(value) {
33
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
34
+ }
35
+ function isStringArray(value) {
36
+ return Array.isArray(value) && value.every((item) => typeof item === 'string');
37
+ }
38
+ /**
39
+ * A provider key alone is not proof that Core can render or launch it. Require
40
+ * the common workflow surface and, for Kimi, the managed materialization runner
41
+ * that Desktop depends on for every headless Core command.
42
+ */
43
+ function isRenderedProviderContract(provider, value) {
44
+ if (!isRecord(value))
45
+ return false;
46
+ if (typeof value.enrichCommand !== 'string' ||
47
+ value.enrichCommand.length === 0 ||
48
+ !isStringArray(value.enrichArgs) ||
49
+ typeof value.updateCommand !== 'string' ||
50
+ value.updateCommand.length === 0 ||
51
+ !isStringArray(value.updateArgs) ||
52
+ !isRecord(value.cli)) {
53
+ return false;
54
+ }
55
+ if (provider !== 'kimi')
56
+ return true;
57
+ const cli = value.cli;
58
+ const skillRunner = cli.skillRunner;
59
+ return cli.providerBinary === 'kimi' &&
60
+ typeof skillRunner === 'string' &&
61
+ skillRunner === '.kimi-code/specrails/run-skill.mjs' &&
62
+ isStringArray(cli.enrichArgs) &&
63
+ cli.enrichArgs.includes(skillRunner);
64
+ }
30
65
  async function findCoreContract() {
31
- // Strategy 1: Try require.resolve (works for local installs)
66
+ // Strategy 1: the packaged Desktop's bundled Core is authoritative.
67
+ const bundledRoot = (0, bundled_core_1.getBundledCoreRoot)();
68
+ if (bundledRoot) {
69
+ const contractPath = path_1.default.join(bundledRoot, 'integration-contract.json');
70
+ if (fs_1.default.existsSync(contractPath))
71
+ return contractPath;
72
+ }
73
+ // Strategy 2: Try require.resolve (works for local installs)
32
74
  try {
33
75
  const contractPath = require.resolve('specrails-core/integration-contract.json');
34
76
  if (fs_1.default.existsSync(contractPath))
35
77
  return contractPath;
36
78
  }
37
79
  catch { /* not locally installed */ }
38
- // Strategy 2: npm root -g
80
+ // Strategy 3: npm root -g
39
81
  try {
40
82
  const globalRoot = (0, child_process_1.execSync)('npm root -g', {
41
83
  encoding: 'utf-8',
@@ -149,9 +191,15 @@ async function checkCoreCompat() {
149
191
  missingCommands: [],
150
192
  extraCommands: [],
151
193
  contractFound: false,
194
+ supportedProviders: [],
152
195
  };
153
196
  }
154
197
  const contract = JSON.parse(fs_1.default.readFileSync(contractPath, 'utf-8'));
198
+ const supportedProviders = contract.providers && typeof contract.providers === 'object'
199
+ ? Object.entries(contract.providers)
200
+ .filter(([provider, providerContract]) => isRenderedProviderContract(provider, providerContract))
201
+ .map(([provider]) => provider)
202
+ : [];
155
203
  const desktopCheckpointKeys = setup_manager_1.CHECKPOINTS.map((cp) => cp.key);
156
204
  // v1/v2 store checkpoints as a string[]; v3+ stores them as a description map.
157
205
  const contractCheckpoints = Array.isArray(contract.checkpoints)
@@ -182,8 +230,15 @@ async function checkCoreCompat() {
182
230
  extraCommands,
183
231
  contractFound: true,
184
232
  contractSchemaVersion: contract.schemaVersion,
233
+ supportedProviders,
185
234
  };
186
235
  }
236
+ /** True only when the located Core contract explicitly renders this provider. */
237
+ function coreCompatSupportsProvider(result, provider) {
238
+ return result.contractFound &&
239
+ Array.isArray(result.supportedProviders) &&
240
+ result.supportedProviders.includes(provider);
241
+ }
187
242
  // v3 contracts no longer include `coreVersion` at the top level — fall back
188
243
  // to reading it from the package.json next to the contract file.
189
244
  function readCoreVersionNear(contractPath) {