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
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CUSTOM_MODEL_ALIAS_MAX_LENGTH = void 0;
4
+ exports.isSafeCustomModelAlias = isSafeCustomModelAlias;
5
+ exports.isModelAvailableForAdapter = isModelAvailableForAdapter;
6
+ exports.supportsToolPolicy = supportsToolPolicy;
7
+ exports.pureOutputToolPolicy = pureOutputToolPolicy;
8
+ exports.requireToolPolicy = requireToolPolicy;
9
+ exports.reasoningEffortsForModel = reasoningEffortsForModel;
10
+ exports.defaultReasoningEffortForModel = defaultReasoningEffortForModel;
11
+ exports.isReasoningEffortValidForModel = isReasoningEffortValidForModel;
12
+ exports.buildProviderEnv = buildProviderEnv;
13
+ exports.formatProviderCommand = formatProviderCommand;
14
+ exports.buildProviderRepoAccessArgs = buildProviderRepoAccessArgs;
15
+ exports.parseStreamEvents = parseStreamEvents;
16
+ /**
17
+ * Conservative grammar for provider-configured model aliases passed as the
18
+ * value immediately following a CLI model flag (for example Kimi's `-m`).
19
+ *
20
+ * Keep aliases shell-neutral even though Desktop spawns with argv arrays:
21
+ * no leading `-`, whitespace, controls, quoting, or unbounded values. Common
22
+ * provider/model forms such as `kimi-code/k3`, `team/model:v2`, and dotted
23
+ * registry ids remain valid and are preserved byte-for-byte after validation.
24
+ */
25
+ exports.CUSTOM_MODEL_ALIAS_MAX_LENGTH = 128;
26
+ const SAFE_CUSTOM_MODEL_ALIAS_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._/:-]*$/;
27
+ function isSafeCustomModelAlias(value) {
28
+ return typeof value === 'string'
29
+ && value.length > 0
30
+ && value.length <= exports.CUSTOM_MODEL_ALIAS_MAX_LENGTH
31
+ && SAFE_CUSTOM_MODEL_ALIAS_PATTERN.test(value);
32
+ }
33
+ /**
34
+ * Provider-aware model validation.
35
+ *
36
+ * Catalog entries are always accepted. Off-catalog values are accepted only
37
+ * when the adapter explicitly advertises configured aliases and the value
38
+ * satisfies the safe argv grammar above.
39
+ */
40
+ function isModelAvailableForAdapter(adapter, value) {
41
+ if (typeof value !== 'string' || value.length === 0)
42
+ return false;
43
+ if (adapter.modelCatalog().some((model) => model.value === value))
44
+ return true;
45
+ return adapter.capabilities.customModelAliases === true
46
+ && isSafeCustomModelAlias(value);
47
+ }
48
+ /** Whether an adapter natively enforces the requested non-default boundary. */
49
+ function supportsToolPolicy(adapter, policy) {
50
+ return (adapter.capabilities.toolPolicies ?? []).includes(policy);
51
+ }
52
+ /**
53
+ * Safest boundary for a pure-output transform. Prefer a true no-tools mode;
54
+ * read-only is an acceptable fallback when the CLI has no native `none`.
55
+ * `null` means the action must fail closed before a child is spawned.
56
+ */
57
+ function pureOutputToolPolicy(adapter) {
58
+ if (supportsToolPolicy(adapter, 'none'))
59
+ return 'none';
60
+ if (supportsToolPolicy(adapter, 'read-only'))
61
+ return 'read-only';
62
+ return null;
63
+ }
64
+ /** Throw a stable, provider-identifying error for an unsupported boundary. */
65
+ function requireToolPolicy(adapter, policy) {
66
+ if (!supportsToolPolicy(adapter, policy)) {
67
+ throw new Error(`provider_tool_policy_unsupported:${adapter.id}:${policy}`);
68
+ }
69
+ return policy;
70
+ }
71
+ /** Exact effort tiers accepted by a provider for the effective model. */
72
+ function reasoningEffortsForModel(adapter, model) {
73
+ if (adapter.capabilities.supportsReasoningEffort !== true)
74
+ return [];
75
+ return adapter.reasoningEffortsForModel?.(model)
76
+ ?? adapter.capabilities.reasoningEfforts
77
+ ?? [];
78
+ }
79
+ /**
80
+ * Safe app default for a model: medium where available, otherwise high where
81
+ * available, then the first tier. Kimi K3's native default is high; preferring
82
+ * it over low keeps an omitted Desktop selection semantically identical to the
83
+ * upstream CLI without changing the ordered selector catalog.
84
+ */
85
+ function defaultReasoningEffortForModel(adapter, model) {
86
+ const efforts = reasoningEffortsForModel(adapter, model);
87
+ if (efforts.includes('medium'))
88
+ return 'medium';
89
+ if (efforts.includes('high'))
90
+ return 'high';
91
+ return efforts[0];
92
+ }
93
+ /** Narrow an untrusted effort value against the model-specific catalog. */
94
+ function isReasoningEffortValidForModel(adapter, model, value) {
95
+ return typeof value === 'string'
96
+ && reasoningEffortsForModel(adapter, model).includes(value);
97
+ }
98
+ /** Merge provider runtime overrides without mutating the caller or process.env. */
99
+ function buildProviderEnv(adapter, opts, baseEnv = process.env) {
100
+ const merged = { ...baseEnv };
101
+ for (const [key, value] of Object.entries(adapter.buildEnv?.(opts) ?? {})) {
102
+ // Windows treats environment names case-insensitively. Remove every
103
+ // inherited casing before applying an adapter-owned override/deletion so
104
+ // `kimi_code_experimental_flag` cannot survive a scrub of the canonical
105
+ // `KIMI_CODE_EXPERIMENTAL_FLAG` (and likewise for reasoning effort).
106
+ const foldedKey = key.toLowerCase();
107
+ for (const inheritedKey of Object.keys(merged)) {
108
+ if (inheritedKey.toLowerCase() === foldedKey)
109
+ delete merged[inheritedKey];
110
+ }
111
+ if (value !== undefined)
112
+ merged[key] = value;
113
+ }
114
+ return merged;
115
+ }
116
+ /**
117
+ * Provider-native Core command/prompt, identity when no hook is declared.
118
+ * `cwd` points at the provider artifact root for adapters that must load the
119
+ * headless workflow content before spawn.
120
+ */
121
+ function formatProviderCommand(adapter, command, cwd, sessionId) {
122
+ return adapter.formatCoreCommand?.(command, cwd, sessionId) ?? command;
123
+ }
124
+ /** Flatten provider-native access flags for additional repository roots. */
125
+ function buildProviderRepoAccessArgs(adapter, paths) {
126
+ return adapter.buildRepoAccessArgs?.(paths) ?? [];
127
+ }
128
+ /**
129
+ * Normalize an adapter's single-or-batch parser contract into an event list.
130
+ * One JSONL record may contain assistant text and several tool calls (Kimi).
131
+ */
132
+ function parseStreamEvents(adapter, line) {
133
+ const parsed = adapter.parseStreamLine(line);
134
+ if (parsed == null)
135
+ return [];
136
+ return Array.isArray(parsed) ? parsed : [parsed];
137
+ }
@@ -24,6 +24,7 @@ const file_provenance_1 = require("./file-provenance");
24
24
  const result_event_1 = require("./result-event");
25
25
  const crypto_1 = require("crypto");
26
26
  const providers_1 = require("./providers");
27
+ const runtime_1 = require("./providers/runtime");
27
28
  const codex_otel_bridge_1 = require("./codex-otel-bridge");
28
29
  const db_1 = require("./db");
29
30
  const interactive_job_session_1 = require("./interactive-job-session");
@@ -2155,7 +2156,7 @@ class QueueManager {
2155
2156
  // true forces interactive where capable, undefined = default ON. Derived
2156
2157
  // HERE (spawn time), not at enqueue, so legacy queued rows with no explicit
2157
2158
  // selection still receive the current default after restart.
2158
- const isFreestyle = adapter.id === 'claude' && exports.FREESTYLE_COMMAND_RE.test(commandToRun);
2159
+ const isFreestyle = adapter.capabilities.freestyle === true && exports.FREESTYLE_COMMAND_RE.test(commandToRun);
2159
2160
  const interactiveOverride = this._jobInteractiveSelection.get(jobId);
2160
2161
  const spawnInteractive = (0, feature_flags_1.isInteractiveJobsEnabled)() &&
2161
2162
  adapter.capabilities.persistentStdin &&
@@ -2259,36 +2260,26 @@ class QueueManager {
2259
2260
  // claude slash command — propose-spec, implement, batch-implement,
2260
2261
  // explore-spec, retry, …). This is the rail equivalent of the
2261
2262
  // user typing `$implement #1 --yes` themselves in `codex`.
2262
- // Freestyle (Claude only): skip the slash command entirely and send the
2263
- // pre-prompt + spec text directly as the prompt. The server route guards
2264
- // that freestyle never reaches a non-claude adapter; defensively, a codex
2265
- // adapter still falls through to its skill-translation path below.
2263
+ // Freestyle (capability-gated; currently Claude and Kimi): skip the slash
2264
+ // command entirely and send the pre-prompt + spec text directly as the
2265
+ // prompt. The server route rejects adapters that do not advertise
2266
+ // `freestyle` before this point.
2266
2267
  // (`isFreestyle` itself is resolved above, before the systemAppend build.)
2267
2268
  const railPrompt = isFreestyle
2268
2269
  ? this._buildFreestylePrompt(commandToRun)
2269
- : adapter.id === 'codex'
2270
- ? commandToRun.replace(/^\/(specrails|sr):([\w-]+)/, '$$$2')
2271
- : commandToRun;
2270
+ : (0, runtime_1.formatProviderCommand)(adapter, commandToRun, execution.cwd);
2272
2271
  // Per-job model override (consumed once) takes precedence — used by the
2273
2272
  // freestyle model picker so the user can choose haiku/sonnet/opus per launch.
2274
2273
  const modelOverride = this._jobModelSelection.get(jobId);
2275
- const railModel = modelOverride
2276
- ? modelOverride
2277
- : adapter.id === 'claude' && this._db
2278
- ? (0, db_1.getProjectSettings)(this._db).orchestratorModel
2279
- : (this._resolvedModel ?? adapter.defaultModel());
2274
+ const fallbackRailModel = adapter.id === 'claude' && this._db
2275
+ ? (0, db_1.getProjectSettings)(this._db).orchestratorModel
2276
+ : (this._resolvedModel ?? adapter.defaultModel());
2280
2277
  // Relocate-artifacts: when relocated, claude is spawned from the workspace
2281
2278
  // so add `--add-dir <repoDir>` so its tools can still reach repo files by
2282
2279
  // absolute path. (gemini/codex get env-only tweaks at spawn time below.)
2283
- const railExtraArgs = execution.relocated && adapter.id === 'claude'
2284
- ? ['--add-dir', execution.repoDir]
2285
- : undefined;
2286
- const args = adapter.buildArgs('rail-job', {
2287
- prompt: railPrompt,
2288
- systemPrompt: systemAppend || undefined,
2289
- model: railModel,
2290
- extraArgs: railExtraArgs,
2291
- });
2280
+ const railExtraArgs = execution.relocated
2281
+ ? (0, runtime_1.buildProviderRepoAccessArgs)(adapter, [execution.repoDir])
2282
+ : [];
2292
2283
  // Resolve agent profile (if any) and snapshot per-job before spawn.
2293
2284
  // Super mode only (projectId + projectSlug + cwd all present).
2294
2285
  // Skipped when the adapter does not honour `SPECRAILS_PROFILE_PATH` AND
@@ -2299,6 +2290,7 @@ class QueueManager {
2299
2290
  // tracked in OpenSpec change task §13.
2300
2291
  let profileSnapshotPath = null;
2301
2292
  let profileName = null;
2293
+ let profileOrchestratorModel = null;
2302
2294
  if (adapter.capabilities.profileEnvSupport && this._projectId && this._projectSlug && this._cwd) {
2303
2295
  try {
2304
2296
  const selection = this._jobProfileSelection.get(jobId); // undefined|null|string
@@ -2312,6 +2304,7 @@ class QueueManager {
2312
2304
  if (resolved) {
2313
2305
  profileSnapshotPath = snapshotForJob(this._projectSlug, jobId, resolved);
2314
2306
  profileName = resolved.name;
2307
+ profileOrchestratorModel = resolved.profile.orchestrator.model;
2315
2308
  if (this._db) {
2316
2309
  persistJobProfile(this._db, jobId, resolved);
2317
2310
  }
@@ -2324,6 +2317,17 @@ class QueueManager {
2324
2317
  console.warn(`[queue-manager] profile resolution failed for job ${jobId}: ${err.message}`);
2325
2318
  }
2326
2319
  }
2320
+ // One authoritative model for both transports:
2321
+ // explicit per-job override > resolved profile orchestrator > project/default.
2322
+ // resolveProfile validates the profile model against this adapter's catalog.
2323
+ const railModel = modelOverride ?? profileOrchestratorModel ?? fallbackRailModel;
2324
+ const railSpawnOptions = {
2325
+ prompt: railPrompt,
2326
+ systemPrompt: systemAppend || undefined,
2327
+ model: railModel,
2328
+ extraArgs: railExtraArgs.length > 0 ? railExtraArgs : undefined,
2329
+ };
2330
+ const args = adapter.buildArgs('rail-job', railSpawnOptions);
2327
2331
  // Read pipelineTelemetryEnabled at spawn time (not constructor time) so
2328
2332
  // toggling the setting takes effect on the next job without restarting.
2329
2333
  // OTEL env injection is gated on `adapter.capabilities.nativeOtelEnv`:
@@ -2369,10 +2373,9 @@ class QueueManager {
2369
2373
  // or timed out. Degraded does NOT block spawn — rail proceeds, UI gets
2370
2374
  // a `plugin.degraded` event so the user can reinstall.
2371
2375
  //
2372
- // Today PluginManager only supports the `project-json` MCP registration
2373
- // (claude). Codex (`cli-add`) is covered by tasks §14 — until that lands
2374
- // we skip plugin resolution for non-`project-json` adapters so the rail
2375
- // spawns cleanly without errors.
2376
+ // Project-json providers (Claude, Kimi, Gemini) resolve only the plugin
2377
+ // installations scoped to this rail's effective provider. CLI-managed MCP
2378
+ // registries remain outside this snapshot path.
2376
2379
  let pluginActive = [];
2377
2380
  let pluginDegraded = [];
2378
2381
  let pluginSnapshotPath = null;
@@ -2386,7 +2389,7 @@ class QueueManager {
2386
2389
  snapshotter = pluginIntegration.snapshotPluginsForJob;
2387
2390
  }
2388
2391
  // Relocated ⇒ `.mcp.json`/plugin state live in the workspace (execution.cwd).
2389
- const resolution = await resolver(execution.cwd, this._projectId, jobId);
2392
+ const resolution = await resolver(execution.cwd, this._projectId, jobId, adapter.id, this._adapter.id, this._projectSlug);
2390
2393
  // shutdown() may have run while plugin verification was awaiting. Never
2391
2394
  // snapshot, broadcast, or spawn for a manager generation that no longer
2392
2395
  // owns this active slot.
@@ -2405,6 +2408,7 @@ class QueueManager {
2405
2408
  type: 'plugin.degraded',
2406
2409
  projectId: this._projectId,
2407
2410
  name: d.name,
2411
+ providerId: adapter.id,
2408
2412
  reason: d.reason,
2409
2413
  jobId,
2410
2414
  timestamp: new Date().toISOString(),
@@ -2525,7 +2529,7 @@ class QueueManager {
2525
2529
  console.warn(`[queue-manager] provenance snapshot failed: ${err.message}`);
2526
2530
  }
2527
2531
  }
2528
- const interactiveArgs = adapter.buildArgs('chat-stream', {
2532
+ const interactiveSpawnOptions = {
2529
2533
  // chat-stream feeds the prompt over stdin per-turn, so the argv `prompt`
2530
2534
  // is unused — pass empty to satisfy the shared SpawnOptions shape.
2531
2535
  prompt: '',
@@ -2538,9 +2542,10 @@ class QueueManager {
2538
2542
  model: railModel,
2539
2543
  extraArgs: !isFreestyle && systemAppend
2540
2544
  ? ['--append-system-prompt', systemAppend, ...(railExtraArgs ?? [])]
2541
- : railExtraArgs,
2542
- });
2543
- this._startInteractiveJob(jobId, job, adapter, { binary, args: interactiveArgs, cwd: spawnCwd, env: spawnEnv }, railPrompt, interactiveSettleMode);
2545
+ : (railExtraArgs.length > 0 ? railExtraArgs : undefined),
2546
+ };
2547
+ const interactiveArgs = adapter.buildArgs('chat-stream', interactiveSpawnOptions);
2548
+ this._startInteractiveJob(jobId, job, adapter, { binary, args: interactiveArgs, cwd: spawnCwd, env: (0, runtime_1.buildProviderEnv)(adapter, interactiveSpawnOptions, spawnEnv) }, railPrompt, interactiveSettleMode);
2544
2549
  return;
2545
2550
  }
2546
2551
  // Code-Explorer pre-spawn snapshot. Captures the working-tree state via
@@ -2579,7 +2584,7 @@ class QueueManager {
2579
2584
  this._consumePendingSelections(jobId);
2580
2585
  // spawnAiCli reroutes multi-line argv values through stdin on Windows.
2581
2586
  const child = (0, cli_prompt_1.spawnAiCli)(binary, args, {
2582
- env: spawnEnv,
2587
+ env: (0, runtime_1.buildProviderEnv)(adapter, railSpawnOptions, spawnEnv),
2583
2588
  stdio: ['ignore', 'pipe', 'pipe'],
2584
2589
  cwd: spawnCwd,
2585
2590
  });
@@ -2729,13 +2734,13 @@ class QueueManager {
2729
2734
  // alongside the raw event persistence below, NOT in place of it: the
2730
2735
  // raw event log is what feeds the live Job Detail UI and the
2731
2736
  // telemetry export ZIP for non-bridge providers.
2732
- const adapterEv = adapter.parseStreamLine(line);
2733
- if (adapterEv) {
2737
+ const adapterEventsForLine = (0, runtime_1.parseStreamEvents)(adapter, line);
2738
+ for (const adapterEv of adapterEventsForLine) {
2734
2739
  adapterEvents.push(adapterEv);
2735
2740
  otelBridge?.consumeEvent(adapterEv);
2736
2741
  }
2737
2742
  if (parsed) {
2738
- const eventType = parsed.type ?? 'unknown';
2743
+ const eventType = parsed.type ?? parsed.role ?? 'unknown';
2739
2744
  const persisted = persistEvent({
2740
2745
  event_type: eventType,
2741
2746
  source: 'stdout',
@@ -2774,8 +2779,9 @@ class QueueManager {
2774
2779
  // For adapters whose stream is JSONL (claude, codex), a non-parseable
2775
2780
  // line is unexpected noise. For future plain-text adapters this is
2776
2781
  // their normal output. emitLine surfaces it either way.
2777
- if (adapterEv?.kind === 'text-delta') {
2778
- emitLine('stdout', adapterEv.text);
2782
+ const textEvent = adapterEventsForLine.find((event) => event.kind === 'text-delta');
2783
+ if (textEvent?.kind === 'text-delta') {
2784
+ emitLine('stdout', textEvent.text);
2779
2785
  }
2780
2786
  else {
2781
2787
  emitLine('stdout', line);
@@ -2878,6 +2884,12 @@ class QueueManager {
2878
2884
  const wasZombie = this._zombieJobs.has(jobId);
2879
2885
  const wasCanceling = this._cancelingJobs.has(jobId);
2880
2886
  const wasPersistenceFailed = this._persistenceFailedJobs.has(jobId);
2887
+ // A provider may report a semantic turn failure while its CLI still exits
2888
+ // successfully (code 0). AdapterEvent is the provider-neutral contract for
2889
+ // that condition, so do not let the process exit code relabel it completed.
2890
+ // Cancellation/zombie outcomes retain precedence because they describe the
2891
+ // user/manager-owned termination of the whole job.
2892
+ const providerReportedError = adapterEvents.some((event) => event.kind === 'error');
2881
2893
  let finalStatus;
2882
2894
  if (wasPersistenceFailed) {
2883
2895
  finalStatus = 'failed';
@@ -2888,15 +2900,26 @@ class QueueManager {
2888
2900
  else if (wasCanceling) {
2889
2901
  finalStatus = 'canceled';
2890
2902
  }
2903
+ else if (providerReportedError) {
2904
+ finalStatus = 'failed';
2905
+ }
2891
2906
  else if (code === 0) {
2892
2907
  finalStatus = 'completed';
2893
2908
  }
2894
2909
  else {
2895
2910
  finalStatus = 'failed';
2896
2911
  }
2912
+ const finishedAt = new Date().toISOString();
2913
+ const startedAtMs = job.startedAt ? new Date(job.startedAt).getTime() : Number.NaN;
2914
+ const finishedAtMs = new Date(finishedAt).getTime();
2915
+ const wallDurationMs = Number.isFinite(startedAtMs)
2916
+ ? Math.max(0, finishedAtMs - startedAtMs)
2917
+ : undefined;
2897
2918
  // Adapter-driven finalisation must happen before staging so the immutable
2898
- // intent owns the same usage that lands on jobs and ai_invocations.
2899
- const { result: normalised, estimated } = (0, result_event_1.finaliseInvocationResult)(adapter, adapterEvents, { fallbackModel: spawnedModel });
2919
+ // intent owns the same usage that lands on jobs and ai_invocations. The
2920
+ // manager wall clock fills providers such as Kimi that emit no duration;
2921
+ // a provider-native duration retains precedence inside the finaliser.
2922
+ const { result: normalised, estimated } = (0, result_event_1.finaliseInvocationResult)(adapter, adapterEvents, { fallbackModel: spawnedModel, durationMs: wallDurationMs });
2900
2923
  const tokenData = lastResultEvent || adapterEvents.length > 0
2901
2924
  ? {
2902
2925
  tokens_in: normalised.tokens_in,
@@ -2912,7 +2935,6 @@ class QueueManager {
2912
2935
  session_id: normalised.session_id,
2913
2936
  }
2914
2937
  : {};
2915
- const finishedAt = new Date().toISOString();
2916
2938
  const invStatus = finalStatus === 'completed'
2917
2939
  ? 'success'
2918
2940
  : (finalStatus === 'canceled' || finalStatus === 'zombie_terminated')
@@ -3765,11 +3787,11 @@ class QueueManager {
3765
3787
  }
3766
3788
  const parse = (row) => {
3767
3789
  try {
3768
- return adapter.parseStreamLine(row.payload);
3790
+ return (0, runtime_1.parseStreamEvents)(adapter, row.payload);
3769
3791
  }
3770
3792
  catch (err) {
3771
3793
  console.warn(`[queue-manager] ignored malformed durable event for ${jobId}:`, err);
3772
- return null;
3794
+ return [];
3773
3795
  }
3774
3796
  };
3775
3797
  const makeUsageAccumulator = () => {
@@ -3844,18 +3866,19 @@ class QueueManager {
3844
3866
  ORDER BY seq, id
3845
3867
  `).iterate(jobId);
3846
3868
  for (const row of events) {
3847
- const event = parse(row);
3848
- if (event?.kind === 'result') {
3849
- const finalised = (0, result_event_1.finaliseInvocationResult)(adapter, [event], {
3850
- fallbackModel: fallbackModel ?? undefined,
3851
- });
3852
- lastValidResult = {
3853
- ...finalised,
3854
- result: sanitizeRecoveredResult(finalised.result),
3855
- };
3856
- }
3857
- else if (event) {
3858
- accumulator.add(event, row.seq);
3869
+ for (const event of parse(row)) {
3870
+ if (event.kind === 'result') {
3871
+ const finalised = (0, result_event_1.finaliseInvocationResult)(adapter, [event], {
3872
+ fallbackModel: fallbackModel ?? undefined,
3873
+ });
3874
+ lastValidResult = {
3875
+ ...finalised,
3876
+ result: sanitizeRecoveredResult(finalised.result),
3877
+ };
3878
+ }
3879
+ else {
3880
+ accumulator.add(event, row.seq);
3881
+ }
3859
3882
  }
3860
3883
  }
3861
3884
  const fallback = accumulator.result();
@@ -3905,27 +3928,28 @@ class QueueManager {
3905
3928
  ORDER BY seq, id
3906
3929
  `).iterate(jobId);
3907
3930
  for (const row of resultRows) {
3908
- const event = parse(row);
3909
- if (event?.kind !== 'result')
3910
- continue;
3911
- const finalised = (0, result_event_1.finaliseInvocationResult)(adapter, [event], { fallbackModel: model });
3912
- const result = sanitizeRecoveredResult(finalised.result);
3913
- totals.tokens_in += result.tokens_in ?? 0;
3914
- totals.tokens_out += result.tokens_out ?? 0;
3915
- totals.tokens_cache_read += result.tokens_cache_read ?? 0;
3916
- totals.tokens_cache_create += result.tokens_cache_create ?? 0;
3917
- const cumulativeCost = result.total_cost_usd ?? baselineCost;
3918
- totals.total_cost_usd += Math.max(0, cumulativeCost - baselineCost);
3919
- baselineCost = cumulativeCost;
3920
- const cumulativeTurns = result.num_turns ?? (baselineTurns + 1);
3921
- totals.num_turns += Math.max(0, cumulativeTurns - baselineTurns);
3922
- baselineTurns = cumulativeTurns;
3923
- totals.duration_ms += result.duration_ms ?? 0;
3924
- totals.duration_api_ms += result.duration_api_ms ?? 0;
3925
- model = result.model ?? model;
3926
- sessionId = result.session_id ?? sessionId;
3927
- estimated = estimated || finalised.estimated;
3928
- lastResultSeq = row.seq;
3931
+ for (const event of parse(row)) {
3932
+ if (event.kind !== 'result')
3933
+ continue;
3934
+ const finalised = (0, result_event_1.finaliseInvocationResult)(adapter, [event], { fallbackModel: model });
3935
+ const result = sanitizeRecoveredResult(finalised.result);
3936
+ totals.tokens_in += result.tokens_in ?? 0;
3937
+ totals.tokens_out += result.tokens_out ?? 0;
3938
+ totals.tokens_cache_read += result.tokens_cache_read ?? 0;
3939
+ totals.tokens_cache_create += result.tokens_cache_create ?? 0;
3940
+ const cumulativeCost = result.total_cost_usd ?? baselineCost;
3941
+ totals.total_cost_usd += Math.max(0, cumulativeCost - baselineCost);
3942
+ baselineCost = cumulativeCost;
3943
+ const cumulativeTurns = result.num_turns ?? (baselineTurns + 1);
3944
+ totals.num_turns += Math.max(0, cumulativeTurns - baselineTurns);
3945
+ baselineTurns = cumulativeTurns;
3946
+ totals.duration_ms += result.duration_ms ?? 0;
3947
+ totals.duration_api_ms += result.duration_api_ms ?? 0;
3948
+ model = result.model ?? model;
3949
+ sessionId = result.session_id ?? sessionId;
3950
+ estimated = estimated || finalised.estimated;
3951
+ lastResultSeq = row.seq;
3952
+ }
3929
3953
  }
3930
3954
  const tail = makeUsageAccumulator();
3931
3955
  const tailRows = db.prepare(`
@@ -3934,9 +3958,10 @@ class QueueManager {
3934
3958
  ORDER BY seq, id
3935
3959
  `).iterate(jobId, lastResultSeq);
3936
3960
  for (const row of tailRows) {
3937
- const event = parse(row);
3938
- if (event && event.kind !== 'result')
3939
- tail.add(event, row.seq);
3961
+ for (const event of parse(row)) {
3962
+ if (event.kind !== 'result')
3963
+ tail.add(event, row.seq);
3964
+ }
3940
3965
  }
3941
3966
  const tailUsage = tail.result();
3942
3967
  const tailResult = tailUsage.result;
@@ -1479,7 +1479,7 @@ function recoveryOverlayExcludes(repoDir, worktreePath) {
1479
1479
  return false;
1480
1480
  if (value === worktree_overlay_1.OVERLAY_MANIFEST)
1481
1481
  return true;
1482
- if (!/^\.(?:claude|codex|gemini)\/[^/]+(?:\/[^/]+)*$/.test(value))
1482
+ if (!/^\.(?:claude|codex|gemini|kimi-code)\/[^/]+(?:\/[^/]+)*$/.test(value))
1483
1483
  return false;
1484
1484
  try {
1485
1485
  // Manifest contents are worktree-writable and therefore not authority.
@@ -32,12 +32,6 @@ const repo_lock_1 = require("./repo-lock");
32
32
  const agent_chat_registry_1 = require("./agent-chat-registry");
33
33
  const process_admission_1 = require("./process-admission");
34
34
  const VALID_MODES = new Set(['implement', 'batch-implement', 'freestyle', 'loop']);
35
- // Models the freestyle picker exposes (Claude aliases). Mirrors the client
36
- // RailModelSelector options and the project-router orchestrator-model allow-list.
37
- const VALID_FREESTYLE_MODELS = new Set(['haiku', 'sonnet', 'opus', 'fable']);
38
- // Reasoning-effort tiers a loop launch may request (mirrors the client selector
39
- // + the provider adapters' supported values).
40
- const VALID_REASONING_EFFORTS = new Set(['low', 'medium', 'high']);
41
35
  function prDeliveryContinuesTickets(delivery, ticketIds) {
42
36
  if (delivery.decision !== 'pr_draft' && delivery.decision !== 'pr_ready')
43
37
  return false;
@@ -446,15 +440,7 @@ function createRailsRouter() {
446
440
  res.status(400).json({ error: 'target_pr_requires_pr_mode', detail: 'targetPrNumber requires loops and PR delivery to be enabled' });
447
441
  return;
448
442
  }
449
- // Freestyle model picker: optional, validated against the allow-list.
450
- // Ignored for non-freestyle modes (they use the orchestrator model).
451
- if (mode === 'freestyle' && model !== undefined && model !== null) {
452
- if (typeof model !== 'string' || !VALID_FREESTYLE_MODELS.has(model)) {
453
- res.status(400).json({ error: 'model must be one of: haiku, sonnet, opus, fable' });
454
- return;
455
- }
456
- }
457
- // Interactive in-job chat is ON by default for EVERY claude job — the
443
+ // Interactive in-job chat is ON by default for providers that expose the
458
444
  // spawn-time gate in QueueManager (kill-switch + persistent-stdin
459
445
  // capability) decides, so the launch no longer passes an explicit flag. A
460
446
  // legacy `interactive` body param is accepted and ignored (wire compat).
@@ -503,21 +489,33 @@ function createRailsRouter() {
503
489
  // Only pass a provider override when one was actually requested (keeps
504
490
  // single-provider rails on the legacy code path).
505
491
  const railProvider = requestedEngine ? engineCheck.provider : undefined;
506
- // Freestyle bypasses the OpenSpec pipeline and hands the raw spec to
507
- // Claude. It is Claude-only — reject when the effective engine is not claude.
508
- if (mode === 'freestyle' && engineCheck.provider !== 'claude') {
509
- res.status(400).json({ error: 'Freestyle requires the Claude provider' });
492
+ // Freestyle bypasses the OpenSpec pipeline and hands the raw spec to a
493
+ // provider that explicitly advertises autonomous freestyle support.
494
+ if (mode === 'freestyle' && (0, providers_1.getAdapter)(engineCheck.provider).capabilities.freestyle !== true) {
495
+ res.status(400).json({ error: `Freestyle is not supported by provider '${engineCheck.provider}'` });
496
+ return;
497
+ }
498
+ // Freestyle is provider-owned. Validate its optional model against the
499
+ // selected engine instead of a Claude-only alias list.
500
+ if (mode === 'freestyle' &&
501
+ model !== undefined &&
502
+ model !== null &&
503
+ (typeof model !== 'string' || !(0, spec_models_1.isValidModelForProvider)(model, engineCheck.provider))) {
504
+ res.status(400).json({
505
+ error: `model is not valid for provider "${engineCheck.provider}"`,
506
+ allowed: (0, spec_models_1.getModelsForProvider)(engineCheck.provider),
507
+ });
510
508
  return;
511
509
  }
512
510
  // Profile selection precedence: explicit body param > stored rail profile > default resolution.
513
- // `null` in the body explicitly forces legacy mode. Codex has no agent
514
- // profiles, so force legacy mode whenever the chosen engine is not claude.
511
+ // `null` in the body explicitly forces legacy mode. Providers without
512
+ // profile support run in legacy mode.
515
513
  let resolvedProfile;
516
514
  if (mode === 'freestyle') {
517
515
  // Freestyle runs no agent pipeline, so profiles do not apply.
518
516
  resolvedProfile = null;
519
517
  }
520
- else if (railProvider && railProvider !== 'claude') {
518
+ else if (railProvider && (0, providers_1.getAdapter)(railProvider).capabilities.profiles !== true) {
521
519
  resolvedProfile = null;
522
520
  }
523
521
  else if (profileName === null) {
@@ -573,27 +571,44 @@ function createRailsRouter() {
573
571
  loopGraph = loop.graph;
574
572
  loopName = loop.name;
575
573
  }
574
+ const loopProvider = railProvider ?? c.project.provider ?? 'claude';
575
+ const loopAdapter = (0, providers_1.getAdapter)(loopProvider);
576
+ if (loopGraph.nodes.some((node) => node.type === 'decider')
577
+ && !(0, providers_1.supportsToolPolicy)(loopAdapter, 'read-only')) {
578
+ res.status(409).json({
579
+ code: 'provider_tool_policy_unsupported',
580
+ provider: loopProvider,
581
+ requiredPolicy: 'read-only',
582
+ error: `Provider '${loopProvider}' cannot run Loop Deciders because its headless CLI ` +
583
+ 'does not enforce a read-only tool policy.',
584
+ });
585
+ return;
586
+ }
587
+ if (typeof model === 'string' && model && !(0, spec_models_1.isValidModelForProvider)(model, loopProvider)) {
588
+ res.status(400).json({ error: `model is not valid for provider "${loopProvider}"`, allowed: (0, spec_models_1.getModelsForProvider)(loopProvider) });
589
+ return;
590
+ }
591
+ const loopModel = typeof model === 'string' && model ? model : loopAdapter.defaultModel();
576
592
  let effort;
577
593
  if (reasoning_effort !== undefined && reasoning_effort !== null) {
578
- if (typeof reasoning_effort !== 'string' || !VALID_REASONING_EFFORTS.has(reasoning_effort)) {
579
- res.status(400).json({ error: 'reasoning_effort must be one of: low, medium, high' });
594
+ const allowed = (0, providers_1.reasoningEffortsForModel)(loopAdapter, loopModel);
595
+ if (typeof reasoning_effort !== 'string' ||
596
+ !allowed.includes(reasoning_effort)) {
597
+ res.status(400).json({
598
+ error: `reasoning_effort is not valid for provider "${loopProvider}" and model "${loopModel}"`,
599
+ allowed,
600
+ });
580
601
  return;
581
602
  }
582
603
  effort = reasoning_effort;
583
604
  }
584
- const loopProvider = railProvider ?? c.project.provider ?? 'claude';
585
- if (typeof model === 'string' && model && !(0, spec_models_1.isValidModelForProvider)(model, loopProvider)) {
586
- res.status(400).json({ error: `model is not valid for provider "${loopProvider}"`, allowed: (0, spec_models_1.getModelsForProvider)(loopProvider) });
587
- return;
588
- }
589
- const loopModel = typeof model === 'string' && model ? model : (0, providers_1.getAdapter)(loopProvider).defaultModel();
590
- // The loop's command(s) declare ticket scope + claude-only-ness.
605
+ // The loop's command(s) declare ticket scope + required capabilities.
591
606
  const promptsText = loopGraph.nodes
592
607
  .filter((n) => n.type === 'ai-step')
593
608
  .map((n) => String(n.data?.prompt ?? ''))
594
609
  .join('\n');
595
- if ((0, loop_command_catalog_1.referencesClaudeOnlyCommand)(promptsText) && loopProvider !== 'claude') {
596
- res.status(400).json({ error: 'This loop uses a Claude-only command and requires the Claude provider' });
610
+ if ((0, loop_command_catalog_1.referencesUnsupportedProviderCommand)(promptsText, loopProvider)) {
611
+ res.status(400).json({ error: `This loop uses a command unsupported by provider '${loopProvider}'` });
597
612
  return;
598
613
  }
599
614
  const scope = (0, loop_command_catalog_1.dominantTicketScope)(promptsText);
@@ -831,13 +846,11 @@ function createRailsRouter() {
831
846
  }
832
847
  let jobId;
833
848
  if (mode === 'freestyle') {
834
- // Freestyle launches ONE independent Claude job per ticket — each gets
849
+ // Freestyle launches ONE independent provider-owned job per ticket — each gets
835
850
  // its own log and runs the spec autonomously (no pipeline). The rail UI
836
851
  // tracks the first job as its representative active job; every job is
837
852
  // registered so its ticket is marked done on completion.
838
- // `provider: 'claude'` is explicit so the spawn resolves the claude
839
- // adapter regardless of the project's primary.
840
- const freestyleModel = mode === 'freestyle' && typeof model === 'string' && VALID_FREESTYLE_MODELS.has(model)
853
+ const freestyleModel = mode === 'freestyle' && typeof model === 'string'
841
854
  ? model
842
855
  : undefined;
843
856
  const jobIds = [];
@@ -845,7 +858,7 @@ function createRailsRouter() {
845
858
  const command = `/specrails:freestyle #${ticketId} --yes`;
846
859
  const job = c.queueManager.enqueue(command, 'normal', {
847
860
  profileName: null,
848
- provider: 'claude',
861
+ provider: engineCheck.provider,
849
862
  ...(freestyleModel ? { model: freestyleModel } : {}),
850
863
  // No explicit `interactive` — QueueManager's spawn-time default
851
864
  // (kill-switch + persistent-stdin capability) covers it, and the