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
@@ -59,6 +59,8 @@ const node_crypto_1 = require("node:crypto");
59
59
  const node_readline_1 = require("node:readline");
60
60
  const tree_kill_1 = __importDefault(require("tree-kill"));
61
61
  const cli_prompt_1 = require("./util/cli-prompt");
62
+ const spawn_lifecycle_1 = require("./spawn-lifecycle");
63
+ const providers_1 = require("./providers");
62
64
  const explore_smash_1 = require("./explore-smash");
63
65
  const explore_cwd_manager_1 = require("./explore-cwd-manager");
64
66
  const workspace_resolution_1 = require("./workspace-resolution");
@@ -66,6 +68,8 @@ const ai_invocations_1 = require("./ai-invocations");
66
68
  const result_event_1 = require("./result-event");
67
69
  const ticket_store_1 = require("./ticket-store");
68
70
  const ticket_store_2 = require("./ticket-store");
71
+ const runtime_1 = require("./providers/runtime");
72
+ const transient_children_1 = require("./transient-children");
69
73
  /** Resolve the tickets-store path for a SMASH run: the gated `ticketsPath` when
70
74
  * the caller threaded it (relocation-aware), else the legacy repo-relative
71
75
  * derivation. */
@@ -104,21 +108,18 @@ function checkSmashEligibility(store, ticketId) {
104
108
  return { ok: true, context: { ticket, childCount } };
105
109
  }
106
110
  // ─── Spawn helpers ───────────────────────────────────────────────────────────
107
- function buildSmashArgs(model, systemPrompt, userPrompt, mode) {
108
- // Simple: deny all tools, single turn — fast, no codebase access.
109
- // Full: allow Read/Grep/Glob (read-only), multi-turn — slower, grounded.
110
- const disallowed = mode === 'full' ? 'Bash,Edit,Write,NotebookEdit' : 'Read,Grep,Glob,Bash';
111
+ function buildSmashArgs(adapter, model, systemPrompt, userPrompt, mode) {
112
+ // Simple: deny all tools, single turn — fast, no codebase access. Full:
113
+ // request the adapter's verified read-only boundary.
111
114
  const maxTurns = mode === 'full' ? SMASH_MAX_TURNS_FULL : SMASH_MAX_TURNS_SIMPLE;
112
- return [
113
- '--model', model,
114
- '--dangerously-skip-permissions',
115
- '--output-format', 'stream-json',
116
- '--verbose',
117
- '--system-prompt', systemPrompt,
118
- '--disallowedTools', disallowed,
119
- '--max-turns', String(maxTurns),
120
- '-p', userPrompt,
121
- ];
115
+ const options = {
116
+ prompt: userPrompt,
117
+ model,
118
+ systemPrompt,
119
+ toolPolicy: mode === 'full' ? 'read-only' : 'none',
120
+ maxTurns,
121
+ };
122
+ return { args: adapter.buildArgs('spec-gen', options), options };
122
123
  }
123
124
  /**
124
125
  * Build the spawn argv + cwd. Exported for tests.
@@ -152,8 +153,10 @@ function prepareSmashSpawn(deps, ticket) {
152
153
  cwd = deps.projectPath;
153
154
  }
154
155
  }
155
- const model = deps.model ?? 'sonnet';
156
- return { args: buildSmashArgs(model, systemPrompt, userPrompt, mode), cwd, env, systemPrompt, userPrompt, mode };
156
+ const adapter = (0, providers_1.getAdapter)(deps.providerId ?? 'claude');
157
+ const model = deps.model ?? adapter.defaultModel();
158
+ const built = buildSmashArgs(adapter, model, systemPrompt, userPrompt, mode);
159
+ return { args: built.args, cwd, env, systemPrompt, userPrompt, mode, options: built.options, adapter };
157
160
  }
158
161
  /** Grace before SIGKILL-escalating a child that swallowed SIGTERM. */
159
162
  const SMASH_SIGKILL_GRACE_MS = 2_000;
@@ -389,13 +392,16 @@ function applyDeleteEpicChildren(filePath, epicId) {
389
392
  return { deletedChildren, revision: store.revision };
390
393
  }
391
394
  // ─── recordInvocation wrapper ────────────────────────────────────────────────
392
- function recordSafely(deps, ticketId, runId, startedAt, finishedAt, status, resultEvent, model) {
395
+ function recordSafely(deps, adapter, ticketId, runId, startedAt, finishedAt, status, events, model) {
393
396
  try {
394
- const normalised = resultEvent ? (0, result_event_1.normaliseResultEvent)(resultEvent, 'claude') : {};
397
+ const { result: normalised, estimated } = (0, result_event_1.finaliseInvocationResult)(adapter, events, {
398
+ fallbackModel: model ?? adapter.defaultModel(),
399
+ durationMs: Math.max(0, Date.parse(finishedAt) - Date.parse(startedAt)),
400
+ });
395
401
  (0, ai_invocations_1.recordInvocation)(deps.db, {
396
402
  id: (0, node_crypto_1.randomUUID)(),
397
403
  project_id: deps.projectId,
398
- provider: 'claude',
404
+ provider: adapter.id,
399
405
  surface: 'smash',
400
406
  surface_ref_id: `smash:${runId}`,
401
407
  conversation_id: null,
@@ -403,8 +409,9 @@ function recordSafely(deps, ticketId, runId, startedAt, finishedAt, status, resu
403
409
  status,
404
410
  started_at: startedAt,
405
411
  finished_at: finishedAt,
412
+ total_cost_usd_estimated: estimated,
406
413
  ...normalised,
407
- model: resultEvent?.model ?? model ?? undefined,
414
+ model: normalised.model ?? model ?? undefined,
408
415
  });
409
416
  }
410
417
  catch (err) {
@@ -451,10 +458,13 @@ function distributeInt(total, n) {
451
458
  * are split via largest-remainder so the per-child rows sum EXACTLY back to the
452
459
  * spawn total (LOW-9).
453
460
  */
454
- function recordChildrenInvocations(deps, childrenIds, runId, startedAt, finishedAt, resultEvent, model) {
455
- if (childrenIds.length === 0 || !resultEvent)
461
+ function recordChildrenInvocations(deps, adapter, childrenIds, runId, startedAt, finishedAt, events, model) {
462
+ if (childrenIds.length === 0)
456
463
  return;
457
- const normalised = (0, result_event_1.normaliseResultEvent)(resultEvent, 'claude');
464
+ const { result: normalised, estimated } = (0, result_event_1.finaliseInvocationResult)(adapter, events, {
465
+ fallbackModel: model ?? adapter.defaultModel(),
466
+ durationMs: Math.max(0, Date.parse(finishedAt) - Date.parse(startedAt)),
467
+ });
458
468
  const n = childrenIds.length;
459
469
  const split = (v) => {
460
470
  if (v === null || v === undefined)
@@ -474,7 +484,7 @@ function recordChildrenInvocations(deps, childrenIds, runId, startedAt, finished
474
484
  (0, ai_invocations_1.recordInvocation)(deps.db, {
475
485
  id: (0, node_crypto_1.randomUUID)(),
476
486
  project_id: deps.projectId,
477
- provider: 'claude',
487
+ provider: adapter.id,
478
488
  surface: 'smash',
479
489
  surface_ref_id: `smash:${runId}:child:${childId}`,
480
490
  conversation_id: null,
@@ -482,6 +492,7 @@ function recordChildrenInvocations(deps, childrenIds, runId, startedAt, finished
482
492
  status: 'success',
483
493
  started_at: startedAt,
484
494
  finished_at: finishedAt,
495
+ total_cost_usd_estimated: estimated,
485
496
  duration_ms: split(normalised.duration_ms),
486
497
  duration_api_ms: split(normalised.duration_api_ms),
487
498
  tokens_in: tokensIn[i],
@@ -491,7 +502,7 @@ function recordChildrenInvocations(deps, childrenIds, runId, startedAt, finished
491
502
  total_cost_usd: split(normalised.total_cost_usd),
492
503
  num_turns: numTurns[i],
493
504
  session_id: normalised.session_id,
494
- model: resultEvent.model ?? model ?? undefined,
505
+ model: normalised.model ?? model ?? undefined,
495
506
  });
496
507
  }
497
508
  catch (err) {
@@ -519,6 +530,10 @@ async function runSmash(deps, ticketId) {
519
530
  if ((0, explore_smash_1.isSpecsSmashKillSwitchActive)()) {
520
531
  return { ok: false, reason: 'disabled', ticketId, runId };
521
532
  }
533
+ const adapter = (0, providers_1.getAdapter)(deps.providerId ?? 'claude');
534
+ if (adapter.capabilities.structuredActions !== true) {
535
+ return { ok: false, reason: 'provider-unsupported', ticketId, runId };
536
+ }
522
537
  // B60: reject a concurrent SMASH of the same ticket. The has-check + add are
523
538
  // synchronous (no await between them), so they close the TOCTOU window the
524
539
  // pre-flight eligibility check leaves open. Released in the finally below.
@@ -566,36 +581,14 @@ async function runSmash(deps, ticketId) {
566
581
  ticketTitle: ticket.title,
567
582
  timestamp: startedAt,
568
583
  });
569
- const { args, cwd, env: spawnEnv } = prepareSmashSpawn({
584
+ const { args, cwd, env: spawnEnv, options: spawnOptions } = prepareSmashSpawn({
570
585
  projectSlug: deps.projectSlug,
571
586
  projectPath: deps.projectPath,
572
587
  projectName: deps.projectName,
573
588
  model: deps.model,
574
589
  mode,
590
+ providerId: adapter.id,
575
591
  }, ticket);
576
- let child;
577
- try {
578
- child = spawn('claude', args, {
579
- env: spawnEnv ? { ...process.env, ...spawnEnv } : process.env,
580
- stdio: ['ignore', 'pipe', 'pipe'],
581
- cwd,
582
- });
583
- }
584
- catch (err) {
585
- const finishedAt = now().toISOString();
586
- recordSafely(deps, ticketId, runId, startedAt, finishedAt, 'failed', null, deps.model);
587
- deps.broadcast({
588
- type: 'smash.failed',
589
- projectId: deps.projectId,
590
- ticketId,
591
- runId,
592
- reason: 'crashed',
593
- timestamp: finishedAt,
594
- });
595
- // LOW-10: a row was recorded — invalidate open dashboards.
596
- deps.broadcast({ type: 'spending.invalidated', projectId: deps.projectId });
597
- return { ok: false, reason: 'crashed', ticketId, runId };
598
- }
599
592
  // Synthesize progress pills as the spawn proceeds. The runner uses fixed
600
593
  // offsets — cheap and deterministic vs trying to read tool-use events.
601
594
  const progressTimers = [];
@@ -612,12 +605,50 @@ async function runSmash(deps, ticketId) {
612
605
  });
613
606
  }, 200 + i * 1500));
614
607
  });
615
- const result = await readSmashChildOutput(child, timeoutMs);
608
+ let fullText = '';
609
+ let resultEvent = null;
610
+ const run = await (0, spawn_lifecycle_1.runAiCliInvocation)({
611
+ adapter,
612
+ binary: adapter.binary,
613
+ argv: args,
614
+ cwd,
615
+ env: (0, runtime_1.buildProviderEnv)(adapter, spawnOptions, spawnEnv ? { ...process.env, ...spawnEnv } : process.env),
616
+ spawn,
617
+ timeoutMs,
618
+ onSpawn: (child) => (0, transient_children_1.trackTransientChild)(deps.projectId, child),
619
+ onEvent: (event) => {
620
+ if (event.kind === 'text-delta')
621
+ fullText += event.text;
622
+ else if (event.kind === 'result')
623
+ resultEvent = event.payload;
624
+ },
625
+ });
626
+ const result = {
627
+ fullText,
628
+ resultEvent,
629
+ code: run.code,
630
+ timedOut: run.timedOut,
631
+ spawnFailed: run.spawnFailed,
632
+ events: run.events,
633
+ };
616
634
  progressTimers.forEach((t) => clearTimeout(t));
617
635
  const finishedAt = now().toISOString();
618
636
  console.log(`[smash-runner] spawn done code=${result.code} timedOut=${result.timedOut} hasResult=${!!result.resultEvent} textBytes=${result.fullText.length}`);
637
+ if (result.spawnFailed) {
638
+ recordSafely(deps, adapter, ticketId, runId, startedAt, finishedAt, 'failed', result.events, deps.model);
639
+ deps.broadcast({
640
+ type: 'smash.failed',
641
+ projectId: deps.projectId,
642
+ ticketId,
643
+ runId,
644
+ reason: 'crashed',
645
+ timestamp: finishedAt,
646
+ });
647
+ deps.broadcast({ type: 'spending.invalidated', projectId: deps.projectId });
648
+ return { ok: false, reason: 'crashed', ticketId, runId };
649
+ }
619
650
  if (result.timedOut) {
620
- recordSafely(deps, ticketId, runId, startedAt, finishedAt, 'aborted', result.resultEvent, deps.model);
651
+ recordSafely(deps, adapter, ticketId, runId, startedAt, finishedAt, 'aborted', result.events, deps.model);
621
652
  deps.broadcast({
622
653
  type: 'smash.failed',
623
654
  projectId: deps.projectId,
@@ -630,9 +661,10 @@ async function runSmash(deps, ticketId) {
630
661
  deps.broadcast({ type: 'spending.invalidated', projectId: deps.projectId });
631
662
  return { ok: false, reason: 'timeout', ticketId, runId };
632
663
  }
633
- if (result.code !== 0 || !result.resultEvent) {
634
- recordSafely(deps, ticketId, runId, startedAt, finishedAt, 'failed', result.resultEvent, deps.model);
635
- const reason = result.resultEvent ? 'model_error' : 'crashed';
664
+ const providerError = result.events.some((event) => event.kind === 'error');
665
+ if (result.code !== 0 || providerError || !result.fullText.trim()) {
666
+ recordSafely(deps, adapter, ticketId, runId, startedAt, finishedAt, 'failed', result.events, deps.model);
667
+ const reason = providerError || result.resultEvent ? 'model_error' : 'crashed';
636
668
  deps.broadcast({
637
669
  type: 'smash.failed',
638
670
  projectId: deps.projectId,
@@ -648,7 +680,7 @@ async function runSmash(deps, ticketId) {
648
680
  const parse = (0, explore_smash_1.parseSmashOutput)(result.fullText);
649
681
  if (!parse.ok) {
650
682
  console.log(`[smash-runner] parse failed reason=${parse.reason} detail=${parse.detail ?? '-'}`);
651
- recordSafely(deps, ticketId, runId, startedAt, finishedAt, 'failed', result.resultEvent, deps.model);
683
+ recordSafely(deps, adapter, ticketId, runId, startedAt, finishedAt, 'failed', result.events, deps.model);
652
684
  deps.broadcast({
653
685
  type: 'smash.failed',
654
686
  projectId: deps.projectId,
@@ -670,7 +702,7 @@ async function runSmash(deps, ticketId) {
670
702
  }
671
703
  catch (err) {
672
704
  console.error('[smash-runner] mutation failed:', err);
673
- recordSafely(deps, ticketId, runId, startedAt, finishedAt, 'failed', result.resultEvent, deps.model);
705
+ recordSafely(deps, adapter, ticketId, runId, startedAt, finishedAt, 'failed', result.events, deps.model);
674
706
  deps.broadcast({
675
707
  type: 'smash.failed',
676
708
  projectId: deps.projectId,
@@ -687,7 +719,22 @@ async function runSmash(deps, ticketId) {
687
719
  // (cost split evenly). The Epic itself does not accrue cost here — the
688
720
  // operation logically birthed the Sub-Specs and the cost belongs to them.
689
721
  // Total project cost remains the original spawn cost (sum of N × X/N = X).
690
- recordChildrenInvocations(deps, applied.children.map((c) => c.id), runId, startedAt, finishedAt, result.resultEvent, deps.model);
722
+ const reported = (0, result_event_1.finaliseInvocationResult)(adapter, result.events, {
723
+ fallbackModel: deps.model ?? adapter.defaultModel(),
724
+ }).result;
725
+ const hasAttributableUsage = reported.total_cost_usd !== undefined ||
726
+ reported.tokens_in !== undefined ||
727
+ reported.tokens_out !== undefined ||
728
+ reported.tokens_cache_read !== undefined ||
729
+ reported.tokens_cache_create !== undefined;
730
+ if (hasAttributableUsage) {
731
+ recordChildrenInvocations(deps, adapter, applied.children.map((c) => c.id), runId, startedAt, finishedAt, result.events, deps.model);
732
+ }
733
+ else {
734
+ // Kimi's stream has no usage/cost envelope. Record the actual process once
735
+ // with null cost instead of fabricating N zero-cost child invocations.
736
+ recordSafely(deps, adapter, ticketId, runId, startedAt, finishedAt, 'success', result.events, deps.model);
737
+ }
691
738
  // Broadcast underlying state changes first, then the SMASH-specific event.
692
739
  deps.broadcast({
693
740
  type: 'ticket_updated',
@@ -19,6 +19,7 @@ exports.runAiCliInvocation = runAiCliInvocation;
19
19
  const node_readline_1 = require("node:readline");
20
20
  const tree_kill_1 = __importDefault(require("tree-kill"));
21
21
  const cli_prompt_1 = require("./util/cli-prompt");
22
+ const runtime_1 = require("./providers/runtime");
22
23
  const STDERR_TAIL_CAP = 64 * 1024;
23
24
  /**
24
25
  * Run one AI-CLI invocation: spawn → stream → settle. Resolves exactly once on
@@ -31,6 +32,9 @@ function runAiCliInvocation(hooks) {
31
32
  const binary = hooks.binary ?? hooks.adapter.binary;
32
33
  const args = hooks.argv ?? hooks.adapter.buildArgs(hooks.action, hooks.buildOpts);
33
34
  const spawn = hooks.spawn ?? cli_prompt_1.spawnAiCli;
35
+ const env = hooks.buildOpts
36
+ ? (0, runtime_1.buildProviderEnv)(hooks.adapter, hooks.buildOpts, hooks.env ?? process.env)
37
+ : hooks.env ?? process.env;
34
38
  const events = [];
35
39
  let lastResultEvent = null;
36
40
  let sessionId = null;
@@ -51,7 +55,7 @@ function runAiCliInvocation(hooks) {
51
55
  let child;
52
56
  try {
53
57
  child = spawn(binary, args, {
54
- env: hooks.env ?? process.env,
58
+ env,
55
59
  stdio: hooks.stdio ?? ['ignore', 'pipe', 'pipe'],
56
60
  cwd: hooks.cwd,
57
61
  });
@@ -100,20 +104,20 @@ function runAiCliInvocation(hooks) {
100
104
  // uncaught exception that takes the sidecar down.
101
105
  try {
102
106
  hooks.onStdoutLine?.(line);
103
- const ev = hooks.adapter.parseStreamLine(line);
104
- if (!ev)
105
- return;
106
- events.push(ev);
107
- if (ev.kind === 'session-started') {
108
- sessionId = ev.sessionId;
109
- }
110
- else if (ev.kind === 'result') {
111
- lastResultEvent = ev;
112
- const sid = ev.payload.session_id;
113
- if (sid)
114
- sessionId = sid;
107
+ const parsedEvents = (0, runtime_1.parseStreamEvents)(hooks.adapter, line);
108
+ for (const ev of parsedEvents) {
109
+ events.push(ev);
110
+ if (ev.kind === 'session-started') {
111
+ sessionId = ev.sessionId;
112
+ }
113
+ else if (ev.kind === 'result') {
114
+ lastResultEvent = ev;
115
+ const sid = ev.payload.session_id;
116
+ if (sid)
117
+ sessionId = sid;
118
+ }
119
+ hooks.onEvent?.(ev);
115
120
  }
116
- hooks.onEvent?.(ev);
117
121
  }
118
122
  catch (err) {
119
123
  console.error('[spawn-lifecycle] stdout line handler error (ignored):', err);
@@ -12,6 +12,9 @@ const cli_prompt_1 = require("./util/cli-prompt");
12
12
  const providers_1 = require("./providers");
13
13
  const result_event_1 = require("./result-event");
14
14
  const ai_invocations_1 = require("./ai-invocations");
15
+ const runtime_1 = require("./providers/runtime");
16
+ const loop_command_catalog_1 = require("./loop-command-catalog");
17
+ const workspace_resolution_1 = require("./workspace-resolution");
15
18
  // ─── SpecLauncherManager ──────────────────────────────────────────────────────
16
19
  class SpecLauncherManager {
17
20
  _broadcast;
@@ -36,21 +39,27 @@ class SpecLauncherManager {
36
39
  * fires when BOTH are present. */
37
40
  _db;
38
41
  _projectId;
39
- /** claude adapter — the launcher only ever spawns claude. Used to parse the
40
- * stream into AdapterEvents for cost finalisation. */
41
- _adapter = (0, providers_1.getAdapter)('claude');
42
+ _projectSlug;
43
+ _adapter;
42
44
  /** Accumulated adapter events per active launch, drained at close. */
43
45
  _events = new Map();
44
46
  /** Spawn timestamp per active launch (ISO), for the invocation row. */
45
47
  _startedAt = new Map();
46
- constructor(broadcast, cwd, db, projectId) {
48
+ constructor(broadcast, cwd, db, projectId, providerId = 'claude', projectSlug) {
47
49
  this._broadcast = broadcast;
48
50
  this._cwd = cwd;
49
51
  this._db = db;
50
52
  this._projectId = projectId;
53
+ this._projectSlug = projectSlug;
54
+ this._adapter = (0, providers_1.getAdapter)(providerId);
51
55
  this._activeProcesses = new Map();
52
56
  this._buffers = new Map();
53
57
  }
58
+ /** Resolve lazily because Core may populate the relocated workspace after
59
+ * this project-scoped manager was constructed. */
60
+ _execution() {
61
+ return (0, workspace_resolution_1.resolveProjectExecution)({ slug: this._projectSlug, path: this._cwd });
62
+ }
54
63
  /**
55
64
  * Persist one surface='spec-launcher' ai_invocations row. Cost is the native
56
65
  * `total_cost_usd` when the run produced a terminal `result` event, else the
@@ -66,8 +75,10 @@ class SpecLauncherManager {
66
75
  if (!db || !projectId)
67
76
  return;
68
77
  try {
78
+ const finishedAt = new Date().toISOString();
69
79
  const { result, estimated } = (0, result_event_1.finaliseInvocationResult)(this._adapter, events, {
70
80
  fallbackModel: this._adapter.defaultModel(),
81
+ durationMs: Math.max(0, Date.parse(finishedAt) - Date.parse(startedAt)),
71
82
  });
72
83
  (0, ai_invocations_1.recordInvocation)(db, {
73
84
  id: (0, crypto_1.randomUUID)(),
@@ -77,7 +88,7 @@ class SpecLauncherManager {
77
88
  surface_ref_id: launchId,
78
89
  status,
79
90
  started_at: startedAt,
80
- finished_at: new Date().toISOString(),
91
+ finished_at: finishedAt,
81
92
  total_cost_usd_estimated: estimated,
82
93
  ...result,
83
94
  });
@@ -139,24 +150,42 @@ class SpecLauncherManager {
139
150
  this._startedAt.clear();
140
151
  }
141
152
  async launch(launchId, description) {
153
+ const execution = this._execution();
142
154
  const rawCommand = `/opsx:ff ${description}`;
143
- const prompt = (0, command_resolver_1.resolveCommand)(rawCommand, this._cwd);
144
- if (prompt === rawCommand) {
155
+ const resolvedPrompt = this._adapter.capabilities.materializeHeadlessSkills
156
+ ? rawCommand
157
+ : (0, command_resolver_1.resolveCommand)(rawCommand, execution.cwd);
158
+ const providerCommand = resolvedPrompt === rawCommand
159
+ ? `${(0, loop_command_catalog_1.expandCommands)('{{cmd:opsx:ff}}', { provider: this._adapter.id })} ${description}`
160
+ : resolvedPrompt;
161
+ if (providerCommand === rawCommand) {
145
162
  this._broadcastError(launchId, 'This project does not have the /opsx:ff command installed. Run "npx specrails-core@latest" to install it.');
146
163
  return;
147
164
  }
148
- const args = [
149
- '--dangerously-skip-permissions',
150
- '--tools', 'default',
151
- '--output-format', 'stream-json',
152
- '--verbose',
153
- '-p', prompt,
154
- ];
155
- // spawnClaude reroutes multi-line argv values through stdin on Windows.
156
- const child = (0, cli_prompt_1.spawnClaude)(args, {
157
- env: process.env,
165
+ let prompt;
166
+ try {
167
+ prompt = (0, runtime_1.formatProviderCommand)(this._adapter, providerCommand, execution.cwd);
168
+ }
169
+ catch (error) {
170
+ this._broadcastError(launchId, error instanceof Error ? error.message : String(error));
171
+ return;
172
+ }
173
+ const spawnOptions = {
174
+ prompt,
175
+ model: this._adapter.defaultModel(),
176
+ toolPolicy: 'default',
177
+ extraArgs: execution.relocated
178
+ ? (0, runtime_1.buildProviderRepoAccessArgs)(this._adapter, [execution.repoDir])
179
+ : [],
180
+ };
181
+ const args = this._adapter.buildArgs('spec-gen', spawnOptions);
182
+ const child = (0, cli_prompt_1.spawnAiCli)(this._adapter.binary, args, {
183
+ env: (0, runtime_1.buildProviderEnv)(this._adapter, spawnOptions, {
184
+ ...process.env,
185
+ ...execution.env,
186
+ }),
158
187
  stdio: ['ignore', 'pipe', 'pipe'],
159
- cwd: this._cwd,
188
+ cwd: execution.cwd,
160
189
  });
161
190
  this._activeProcesses.set(launchId, child);
162
191
  this._buffers.set(launchId, '');
@@ -178,7 +207,7 @@ class SpecLauncherManager {
178
207
  type: 'spec_launcher_error',
179
208
  projectId: '',
180
209
  launchId,
181
- error: `Failed to launch claude: ${err.message}`,
210
+ error: `Failed to launch ${this._adapter.binary}: ${err.message}`,
182
211
  timestamp: new Date().toISOString(),
183
212
  });
184
213
  });
@@ -188,28 +217,12 @@ class SpecLauncherManager {
188
217
  const stdoutReader = (0, readline_1.createInterface)({ input: child.stdout, crlfDelay: Infinity });
189
218
  stdoutReader.on('line', (line) => {
190
219
  // Accumulate adapter events so a killed/failed launch is still costed from
191
- // the per-assistant-event usage snapshots (HIGH-5). Only when DB-wired.
192
- if (this._db && this._projectId) {
193
- const ev = this._adapter.parseStreamLine(line);
194
- if (ev)
195
- this._events.get(launchId)?.push(ev);
196
- }
197
- let parsed = null;
198
- try {
199
- parsed = JSON.parse(line);
200
- }
201
- catch { /* skip non-JSON */ }
202
- if (!parsed)
203
- return;
204
- const eventType = parsed.type;
205
- if (eventType === 'assistant') {
206
- const msg = parsed.message;
207
- const blocks = msg?.content ?? [];
208
- const texts = blocks
209
- .filter((c) => c.type === 'text')
210
- .map((c) => c.text ?? '');
211
- const newText = texts.join('');
212
- if (newText) {
220
+ // the per-assistant-event usage snapshots (HIGH-5), and so explicit
221
+ // provider failures remain terminal even when a CLI anomalously exits 0.
222
+ for (const event of (0, runtime_1.parseStreamEvents)(this._adapter, line)) {
223
+ this._events.get(launchId)?.push(event);
224
+ if (event.kind === 'text-delta') {
225
+ const newText = event.text;
213
226
  // Try to detect change ID from output (look for "openspec/changes/<id>" pattern)
214
227
  const changeMatch = newText.match(/openspec\/changes\/([^\s/]+)/);
215
228
  if (changeMatch)
@@ -224,16 +237,14 @@ class SpecLauncherManager {
224
237
  timestamp: new Date().toISOString(),
225
238
  });
226
239
  }
227
- for (const block of blocks) {
228
- if (block.type === 'tool_use' && block.name) {
229
- this._broadcast({
230
- type: 'spec_launcher_stream',
231
- projectId: '',
232
- launchId,
233
- delta: `<!--tool:${block.name}-->`,
234
- timestamp: new Date().toISOString(),
235
- });
236
- }
240
+ else if (event.kind === 'tool-use') {
241
+ this._broadcast({
242
+ type: 'spec_launcher_stream',
243
+ projectId: '',
244
+ launchId,
245
+ delta: `<!--tool:${event.name}-->`,
246
+ timestamp: new Date().toISOString(),
247
+ });
237
248
  }
238
249
  }
239
250
  });
@@ -244,13 +255,14 @@ class SpecLauncherManager {
244
255
  this._activeProcesses.delete(launchId);
245
256
  this._buffers.delete(launchId);
246
257
  const wasCancelled = this._cancelledIds.delete(launchId);
258
+ const providerError = this._events.get(launchId)?.find((event) => event.kind === 'error')?.message;
247
259
  // Record the invocation regardless of cancel/done outcome (the spend is
248
260
  // real either way) — but NOT when the project is being torn down, since
249
261
  // its DB is closing (recordInvocation would throw). A cancelled/killed
250
262
  // launch is 'aborted'; a non-zero exit is 'failed'; a clean exit is
251
263
  // 'success' (HIGH-5).
252
264
  if (!this._disposed) {
253
- const status = wasCancelled ? 'aborted' : code === 0 ? 'success' : 'failed';
265
+ const status = wasCancelled ? 'aborted' : code === 0 && !providerError ? 'success' : 'failed';
254
266
  this._recordInvocation(launchId, status);
255
267
  }
256
268
  this._events.delete(launchId);
@@ -262,7 +274,7 @@ class SpecLauncherManager {
262
274
  resolve();
263
275
  return;
264
276
  }
265
- if (code === 0) {
277
+ if (code === 0 && !providerError) {
266
278
  // Also try to extract change ID from full text
267
279
  if (!detectedChangeId) {
268
280
  const match = fullText.match(/openspec\/changes\/([^\s/]+)/);
@@ -278,7 +290,7 @@ class SpecLauncherManager {
278
290
  });
279
291
  }
280
292
  else {
281
- this._broadcastError(launchId, 'Spec generation failed');
293
+ this._broadcastError(launchId, providerError ?? 'Spec generation failed');
282
294
  }
283
295
  resolve();
284
296
  });
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PROVIDER_DEFAULT_MODEL = exports.GEMINI_MODELS = exports.CODEX_MODELS = exports.CLAUDE_MODELS = void 0;
3
+ exports.PROVIDER_DEFAULT_MODEL = exports.KIMI_MODELS = exports.GEMINI_MODELS = exports.CODEX_MODELS = exports.CLAUDE_MODELS = void 0;
4
4
  exports.getModelsForProvider = getModelsForProvider;
5
5
  exports.isValidModelForProvider = isValidModelForProvider;
6
6
  exports.getProviderDefault = getProviderDefault;
7
+ const providers_1 = require("./providers");
7
8
  exports.CLAUDE_MODELS = [
8
9
  { value: 'sonnet', label: 'Claude Sonnet' },
9
10
  { value: 'fable', label: 'Claude Fable' },
@@ -26,25 +27,33 @@ exports.GEMINI_MODELS = [
26
27
  { value: 'gemini-3.1-flash-lite', label: 'Gemini 3.1 Flash Lite' },
27
28
  { value: 'gemini-2.5-flash-lite', label: 'Gemini 2.5 Flash Lite' },
28
29
  ];
29
- /** Per-provider model catalog. Lookup, not a branch — fallback is Claude's. */
30
+ // Mirrors KIMI_MODELS in server/providers/kimi-adapter.ts.
31
+ exports.KIMI_MODELS = [
32
+ { value: 'k3', label: 'Kimi K3' },
33
+ { value: 'kimi-for-coding', label: 'Kimi for Coding' },
34
+ { value: 'kimi-for-coding-highspeed', label: 'Kimi for Coding Highspeed' },
35
+ ];
36
+ /** Per-provider model catalog. Unknown providers intentionally return none. */
30
37
  const PROVIDER_MODELS = {
31
38
  claude: exports.CLAUDE_MODELS,
32
39
  codex: exports.CODEX_MODELS,
33
40
  gemini: exports.GEMINI_MODELS,
41
+ kimi: exports.KIMI_MODELS,
34
42
  };
35
43
  exports.PROVIDER_DEFAULT_MODEL = {
36
44
  claude: 'sonnet',
37
45
  codex: 'gpt-5.5',
38
46
  gemini: 'gemini-3.5-flash',
47
+ kimi: 'k3',
39
48
  };
40
49
  function getModelsForProvider(provider) {
41
- return PROVIDER_MODELS[provider] ?? exports.CLAUDE_MODELS;
50
+ return PROVIDER_MODELS[provider] ?? [];
42
51
  }
43
52
  function isValidModelForProvider(model, provider) {
44
- if (typeof model !== 'string' || model.length === 0)
53
+ if (!(0, providers_1.hasAdapter)(provider))
45
54
  return false;
46
- return getModelsForProvider(provider).some((m) => m.value === model);
55
+ return (0, providers_1.isModelAvailableForAdapter)((0, providers_1.getAdapter)(provider), model);
47
56
  }
48
57
  function getProviderDefault(provider) {
49
- return exports.PROVIDER_DEFAULT_MODEL[provider] ?? exports.PROVIDER_DEFAULT_MODEL.claude;
58
+ return exports.PROVIDER_DEFAULT_MODEL[provider] ?? '';
50
59
  }