specrails-desktop 2.46.0 → 2.48.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 (259) hide show
  1. package/README.md +2 -0
  2. package/client/dist/assets/{ActivityFeedPage-CsHQPpvl.js → ActivityFeedPage-CGq6QhP5.js} +1 -1
  3. package/client/dist/assets/AgentBrowserCapture-C4GZ-kdf.js +1 -0
  4. package/client/dist/assets/AgentModeAnalyticsPane-_IPmL0ZS.js +2 -0
  5. package/client/dist/assets/AgentModeCodePane-B5WG5DOP.js +2 -0
  6. package/client/dist/assets/{AgentModeJobsPane-rnD-EPMd.js → AgentModeJobsPane-w1N_HIXh.js} +1 -1
  7. package/client/dist/assets/AgentsPage-CmwnnT46.js +87 -0
  8. package/client/dist/assets/AnalyticsPage-BUMsKtsJ.js +1 -0
  9. package/client/dist/assets/CodePage-PjgywJPS.js +3 -0
  10. package/client/dist/assets/DesktopAnalyticsPage-BXY1pH4y.js +1 -0
  11. package/client/dist/assets/DocsDialog-ccQETHKB.js +11 -0
  12. package/client/dist/assets/DocsPage-DzEgplB3.js +11 -0
  13. package/client/dist/assets/{ExportDropdown-pWZGYCv3.js → ExportDropdown-B8bBGopr.js} +1 -1
  14. package/client/dist/assets/InteractiveJobComposer-CEwGkJ8F.js +19 -0
  15. package/client/dist/assets/JobDetailModal-BYXmuTDe.js +1 -0
  16. package/client/dist/assets/JobDetailPage-CWp6MwJj.js +1 -0
  17. package/client/dist/assets/JobsPage-D1O1uyG8.js +1 -0
  18. package/client/dist/assets/{LoopBuilderPage-CtmOE4-b.js → LoopBuilderPage-DQE-7Vby.js} +2 -2
  19. package/client/dist/assets/{LoopPreviewModal-BYeLjQbr.js → LoopPreviewModal-Bk7zIXD5.js} +1 -1
  20. package/client/dist/assets/LoopsPage-CqG7mx56.js +1 -0
  21. package/client/dist/assets/MinimizedChatsContext-UpnqccPx.js +1 -0
  22. package/client/dist/assets/PluginsPage-CjgVvmpW.js +2 -0
  23. package/client/dist/assets/{ProjectSettingsDialog-DsCRIc4v.js → ProjectSettingsDialog-D6vBCoay.js} +1 -1
  24. package/client/dist/assets/{RepositoryDeliveries-BQm_3TO8.js → RepositoryDeliveries-C36ipKp_.js} +1 -1
  25. package/client/dist/assets/{RepositoryScopeSelector-DnAFtjTz.js → RepositoryScopeSelector-0Gz_fR1E.js} +1 -1
  26. package/client/dist/assets/ReviewPacketPage-Dr9RYs7q.js +1 -0
  27. package/client/dist/assets/TemplatePreviewModal-sCMHYFJo.js +1 -0
  28. package/client/dist/assets/TicketDetailModalContext-C5GgJlEt.js +14 -0
  29. package/client/dist/assets/{Trans-BwLgX6n7.js → Trans-9pHTy2fH.js} +1 -1
  30. package/client/dist/assets/{addspec-D2YIQ0GU.js → addspec-BOvgmcgk.js} +1 -1
  31. package/client/dist/assets/{addspec-B_TPJt-R.js → addspec-Clmyfn_R.js} +1 -1
  32. package/client/dist/assets/{addspec-A1lDQa4M.js → addspec-CsNaBpHp.js} +1 -1
  33. package/client/dist/assets/{addspec-CQBjzt24.js → addspec-D2tvWmmV.js} +1 -1
  34. package/client/dist/assets/{addspec-CLYLvaVP.js → addspec-D3nFwvD0.js} +1 -1
  35. package/client/dist/assets/{addspec-BPpZ_olp.js → addspec-DVLEhK3w.js} +1 -1
  36. package/client/dist/assets/{addspec-iFdQ6D99.js → addspec-DXbK-tb0.js} +1 -1
  37. package/client/dist/assets/{addspec-BHZd0N5F.js → addspec-OHG8DFEn.js} +1 -1
  38. package/client/dist/assets/agentRuntime-BUimiX__.js +1 -0
  39. package/client/dist/assets/agentRuntime-BcN97sAB.js +1 -0
  40. package/client/dist/assets/agentRuntime-Ckyg-ahT.js +1 -0
  41. package/client/dist/assets/agentRuntime-DJCwyX4t.js +1 -0
  42. package/client/dist/assets/agentRuntime-DVzPJOl0.js +1 -0
  43. package/client/dist/assets/agentRuntime-V9AZTG3c.js +1 -0
  44. package/client/dist/assets/agentRuntime-bAAjw1vw.js +1 -0
  45. package/client/dist/assets/agentRuntime-v4qKAYH-.js +1 -0
  46. package/client/dist/assets/{agents-45GbINfh.js → agents-CA5eT1d0.js} +1 -1
  47. package/client/dist/assets/{agents-DMKFStHL.js → agents-CAgy-5uS.js} +1 -1
  48. package/client/dist/assets/{agents-IUOgqT5t.js → agents-CNm6Qzlv.js} +1 -1
  49. package/client/dist/assets/{agents-ms7TwG16.js → agents-CTwbx-fb.js} +1 -1
  50. package/client/dist/assets/{agents-DaYcxAr1.js → agents-DEGV1Vao.js} +1 -1
  51. package/client/dist/assets/{agents-BHp0_FQt.js → agents-HsDF1aQU.js} +1 -1
  52. package/client/dist/assets/{agents-Bvyi3y6R.js → agents-IQjdA-z9.js} +1 -1
  53. package/client/dist/assets/{agents-rzPnqoBR.js → agents-RkGoteXh.js} +1 -1
  54. package/client/dist/assets/analytics-BNgZ1Nj7.js +1 -0
  55. package/client/dist/assets/analytics-BlypLLR6.js +1 -0
  56. package/client/dist/assets/analytics-CoEzWPx4.js +1 -0
  57. package/client/dist/assets/analytics-DFh5lSOl.js +1 -0
  58. package/client/dist/assets/analytics-DZp-DS2U.js +1 -0
  59. package/client/dist/assets/analytics-DjLlE7jS.js +1 -0
  60. package/client/dist/assets/analytics-jzCkOec3.js +1 -0
  61. package/client/dist/assets/analytics-nZIjjtCc.js +1 -0
  62. package/client/dist/assets/{dashboard-BK_mez-z.js → dashboard-C29Qwa-L.js} +1 -1
  63. package/client/dist/assets/{dashboard-BwkjxI6H.js → dashboard-CSJzf5OT.js} +1 -1
  64. package/client/dist/assets/{dashboard-C3bDKf7s.js → dashboard-CTfEtkqX.js} +1 -1
  65. package/client/dist/assets/{dashboard-Bt0cCzeG.js → dashboard-Cr4D8oMn.js} +1 -1
  66. package/client/dist/assets/{dashboard-fRrP0Kv0.js → dashboard-Csf1yGSN.js} +1 -1
  67. package/client/dist/assets/{dashboard-CdvjGfog.js → dashboard-D40HP2ks.js} +1 -1
  68. package/client/dist/assets/dashboard-DBAP2_SQ.js +1 -0
  69. package/client/dist/assets/{dashboard-B14uYXkG.js → dashboard-DMklepmz.js} +1 -1
  70. package/client/dist/assets/file-text-CFT-xXKN.js +1 -0
  71. package/client/dist/assets/{formatDistanceToNow-CeZv93iR.js → formatDistanceToNow-w_2U5pm0.js} +1 -1
  72. package/client/dist/assets/{getTimezoneOffsetInMilliseconds-DxAkjvJb.js → getTimezoneOffsetInMilliseconds-BRymg1Kk.js} +1 -1
  73. package/client/dist/assets/index-D0ScFZvR.css +2 -0
  74. package/client/dist/assets/index-DORCrgbI.js +77 -0
  75. package/client/dist/assets/jira-api-BqHbSdxK.js +1 -0
  76. package/client/dist/assets/jobs-C6qmvUAi.js +1 -0
  77. package/client/dist/assets/jobs-C9j2PuwW.js +1 -0
  78. package/client/dist/assets/jobs-DLHdDkHc.js +1 -0
  79. package/client/dist/assets/jobs-DSp9DoD_.js +1 -0
  80. package/client/dist/assets/jobs-DTQO08nJ.js +1 -0
  81. package/client/dist/assets/jobs-DoFaEl9g.js +1 -0
  82. package/client/dist/assets/jobs-Dpw6AEOP.js +1 -0
  83. package/client/dist/assets/jobs-DrF8Do8s.js +1 -0
  84. package/client/dist/assets/{narration-BSldQfjd.js → narration--hUhjM5v.js} +1 -1
  85. package/client/dist/assets/{narration-DKLjXIFI.js → narration-CHI06czm.js} +1 -1
  86. package/client/dist/assets/{narration-4OSg0b5-.js → narration-DjKB3pQZ.js} +1 -1
  87. package/client/dist/assets/{narration-BPMOQPFe.js → narration-DtCYvRLM.js} +1 -1
  88. package/client/dist/assets/{narration-D-e9ssxA.js → narration-DvLQBW9U.js} +1 -1
  89. package/client/dist/assets/{narration-DnrKC3mF.js → narration-eSx3gR0W.js} +1 -1
  90. package/client/dist/assets/{narration-Dypjxeel.js → narration-n3hOA7LY.js} +1 -1
  91. package/client/dist/assets/{narration-CvQdWdR5.js → narration-thJOeT2-.js} +1 -1
  92. package/client/dist/assets/packet-BNW8ODQw.js +1 -0
  93. package/client/dist/assets/packet-CXYvfdjy.js +1 -0
  94. package/client/dist/assets/packet-CtIlB8YE.js +1 -0
  95. package/client/dist/assets/packet-DA1SJkMH.js +1 -0
  96. package/client/dist/assets/packet-DYcWfBip.js +1 -0
  97. package/client/dist/assets/packet-DseX9yKC.js +1 -0
  98. package/client/dist/assets/packet-_m5aOy_d.js +1 -0
  99. package/client/dist/assets/packet-qOemUAm3.js +1 -0
  100. package/client/dist/assets/{project-repositories-BvBj9XH9.js → project-repositories-BsCs1PPA.js} +1 -1
  101. package/client/dist/assets/provider-capabilities-Vi1ZGvAK.js +1 -0
  102. package/client/dist/assets/settings-BsehKpty.js +1 -0
  103. package/client/dist/assets/settings-CQWob3SB.js +1 -0
  104. package/client/dist/assets/settings-CgCl0hzy.js +1 -0
  105. package/client/dist/assets/settings-DLRLd22L.js +1 -0
  106. package/client/dist/assets/settings-Dn5prAFl.js +1 -0
  107. package/client/dist/assets/settings-HZSu-K4l.js +1 -0
  108. package/client/dist/assets/{settings-DEgcA0jd.js → settings-p4OVIyRh.js} +1 -1
  109. package/client/dist/assets/settings-rddaldUp.js +1 -0
  110. package/client/dist/assets/setup-B5FHuxvB.js +1 -0
  111. package/client/dist/assets/setup-BihzXadx.js +1 -0
  112. package/client/dist/assets/setup-BtC6Hg4K.js +1 -0
  113. package/client/dist/assets/setup-C9Fy9PKQ.js +1 -0
  114. package/client/dist/assets/setup-CROyqD5N.js +1 -0
  115. package/client/dist/assets/setup-DpqzaEEA.js +1 -0
  116. package/client/dist/assets/setup-Dwzt5nO1.js +1 -0
  117. package/client/dist/assets/setup-zLEDnG7N.js +1 -0
  118. package/client/dist/assets/{spending-DNM6NAoA.js → spending-hcgxHP4O.js} +1 -1
  119. package/client/dist/assets/{useDesktop-mxHVQOdx.js → useDesktop-CAisjKWC.js} +1 -1
  120. package/client/dist/assets/{useSharedWebSocket-DnxHj8jP.js → useSharedWebSocket-BHIfW4QT.js} +2 -2
  121. package/client/dist/assets/wrench-BfmNO9Vw.js +1 -0
  122. package/client/dist/index.html +27 -26
  123. package/docs/guide/de/integrations/7-local-engines.md +37 -0
  124. package/docs/guide/en/integrations/7-local-engines.md +37 -0
  125. package/docs/guide/es/integrations/7-local-engines.md +37 -0
  126. package/docs/guide/fr/integrations/7-local-engines.md +37 -0
  127. package/docs/guide/it/integrations/7-local-engines.md +37 -0
  128. package/docs/guide/ja/integrations/7-local-engines.md +37 -0
  129. package/docs/guide/pt/integrations/7-local-engines.md +37 -0
  130. package/docs/guide/zh/integrations/7-local-engines.md +37 -0
  131. package/docs/internals/adding-a-provider.md +12 -0
  132. package/docs/internals/local-agent-runner.md +153 -0
  133. package/docs/internals/review-packet.md +8 -0
  134. package/docs/local-providers.md +215 -0
  135. package/docs/plans/implementation-efficiency-artifact.json +18 -0
  136. package/docs/plans/implementation-efficiency-evaluation.json +1817 -0
  137. package/docs/plans/implementation-efficiency-evaluation.md +41 -0
  138. package/docs/plans/implementation-efficiency-progress.md +48 -0
  139. package/docs/plans/implementation-efficiency-release.md +41 -0
  140. package/docs/plans/implementation-efficiency.md +124 -0
  141. package/local-runner/dist/specrails-local-runner.js +15815 -0
  142. package/package.json +7 -5
  143. package/server/dist/agent-chat-manager.js +8 -1
  144. package/server/dist/agent-chat-router.js +5 -2
  145. package/server/dist/agent-fence-promotion.js +53 -0
  146. package/server/dist/agent-mcp-config.js +4 -1
  147. package/server/dist/agent-runtime-bridge.js +35 -26
  148. package/server/dist/agent-runtime-controls-router.js +20 -1
  149. package/server/dist/agent-runtime-controls.js +94 -12
  150. package/server/dist/agent-runtime-effective-config.js +53 -0
  151. package/server/dist/agent-runtime-events.js +46 -0
  152. package/server/dist/agent-runtime-history.js +47 -0
  153. package/server/dist/agent-runtime-loader.js +84 -5
  154. package/server/dist/agent-runtime-metrics.js +77 -1
  155. package/server/dist/agent-runtime-package.js +205 -0
  156. package/server/dist/agent-runtime-settings-router.js +96 -5
  157. package/server/dist/agent-runtime-settings.js +163 -7
  158. package/server/dist/agent-spec-framing.js +10 -4
  159. package/server/dist/binary-probe.js +12 -0
  160. package/server/dist/blueprint-router.js +3 -2
  161. package/server/dist/chat-manager.js +5 -1
  162. package/server/dist/desktop-router.js +87 -4
  163. package/server/dist/index.js +13 -0
  164. package/server/dist/interactive-job-session.js +3 -1
  165. package/server/dist/local-engine-detection.js +118 -0
  166. package/server/dist/loop-executors.js +4 -3
  167. package/server/dist/loop-role-engines.js +129 -0
  168. package/server/dist/loop-run-manager.js +14 -6
  169. package/server/dist/mcp/tools/app-settings.js +30 -4
  170. package/server/dist/mcp/tools/rails.js +2 -1
  171. package/server/dist/pr-body.js +11 -2
  172. package/server/dist/pricing.js +15 -0
  173. package/server/dist/provider-detection.js +63 -1
  174. package/server/dist/provider-selection.js +26 -1
  175. package/server/dist/providers/index.js +4 -1
  176. package/server/dist/providers/local-adapter-registry.js +56 -0
  177. package/server/dist/providers/local-adapter.js +426 -0
  178. package/server/dist/providers/registry.js +39 -4
  179. package/server/dist/rail-isolated-launch.js +4 -3
  180. package/server/dist/rails-router.js +90 -8
  181. package/server/dist/result-event.js +17 -0
  182. package/server/dist/review-packet.js +1 -0
  183. package/server/dist/schemas/agent-runtime.schema.json +28 -3
  184. package/server/dist/schemas/fixtures/runtime-efficiency-summary.v1.json +790 -0
  185. package/server/dist/setup-manager.js +5 -1
  186. package/server/dist/spec-models.js +14 -2
  187. package/server/dist/worktree-node-modules.js +97 -65
  188. package/client/dist/assets/AgentBrowserCapture-BVcCK7x0.js +0 -1
  189. package/client/dist/assets/AgentModeAnalyticsPane-C5F3m29G.js +0 -2
  190. package/client/dist/assets/AgentModeCodePane-C5QuKsu0.js +0 -2
  191. package/client/dist/assets/AgentsPage-DiyJWZcU.js +0 -87
  192. package/client/dist/assets/AnalyticsPage-Dkmb0dnd.js +0 -1
  193. package/client/dist/assets/CodePage-C0dH99-t.js +0 -3
  194. package/client/dist/assets/DesktopAnalyticsPage-DuTjr2n8.js +0 -1
  195. package/client/dist/assets/DocsDialog-BVZJZHVR.js +0 -11
  196. package/client/dist/assets/DocsPage-D6KHTv8V.js +0 -11
  197. package/client/dist/assets/InteractiveJobComposer-Ck3PQxjN.js +0 -19
  198. package/client/dist/assets/JobDetailModal-DkoMH3ie.js +0 -1
  199. package/client/dist/assets/JobDetailPage-CBVivcl2.js +0 -1
  200. package/client/dist/assets/JobsPage-CzxEeca9.js +0 -1
  201. package/client/dist/assets/LoopsPage-6iW7g25k.js +0 -1
  202. package/client/dist/assets/MinimizedChatsContext-B6wVevPq.js +0 -1
  203. package/client/dist/assets/PluginsPage-Bf1z3WPF.js +0 -2
  204. package/client/dist/assets/ReviewPacketPage-CaXAwtR7.js +0 -1
  205. package/client/dist/assets/TemplatePreviewModal-9aRFpJ_J.js +0 -1
  206. package/client/dist/assets/TicketDetailModalContext-B1X5glzV.js +0 -14
  207. package/client/dist/assets/agentRuntime-BUagD2I_.js +0 -1
  208. package/client/dist/assets/agentRuntime-BkL70WtX.js +0 -1
  209. package/client/dist/assets/agentRuntime-CWXVqgqE.js +0 -1
  210. package/client/dist/assets/agentRuntime-CvNBdUAe.js +0 -1
  211. package/client/dist/assets/agentRuntime-D4RmeuNY.js +0 -1
  212. package/client/dist/assets/agentRuntime-DTidBWKg.js +0 -1
  213. package/client/dist/assets/agentRuntime-DhKYfyaU.js +0 -1
  214. package/client/dist/assets/agentRuntime-LL8_GLx9.js +0 -1
  215. package/client/dist/assets/analytics-47pYygtV.js +0 -1
  216. package/client/dist/assets/analytics-B0vt1g5U.js +0 -1
  217. package/client/dist/assets/analytics-BMzCvrCU.js +0 -1
  218. package/client/dist/assets/analytics-BlFN3Zwx.js +0 -1
  219. package/client/dist/assets/analytics-CZYzL-nR.js +0 -1
  220. package/client/dist/assets/analytics-DAsb9MJ8.js +0 -1
  221. package/client/dist/assets/analytics-FXQlQ2Yt.js +0 -1
  222. package/client/dist/assets/analytics-RnFCNkah.js +0 -1
  223. package/client/dist/assets/dashboard-BfBir_6o.js +0 -1
  224. package/client/dist/assets/file-text-BcLCfcWZ.js +0 -1
  225. package/client/dist/assets/index-Cfh9aaep.css +0 -2
  226. package/client/dist/assets/index-DYoyDWim.js +0 -76
  227. package/client/dist/assets/jira-api-ChjVGJx_.js +0 -1
  228. package/client/dist/assets/jobs-5PQJbdOf.js +0 -1
  229. package/client/dist/assets/jobs-B2B08Oy9.js +0 -1
  230. package/client/dist/assets/jobs-BJZtBbwb.js +0 -1
  231. package/client/dist/assets/jobs-BlWEsQb7.js +0 -1
  232. package/client/dist/assets/jobs-ConfD8xD.js +0 -1
  233. package/client/dist/assets/jobs-DMPU64ms.js +0 -1
  234. package/client/dist/assets/jobs-DojASiTp.js +0 -1
  235. package/client/dist/assets/jobs-kVHkNpB-.js +0 -1
  236. package/client/dist/assets/packet-BQ2tXfJI.js +0 -1
  237. package/client/dist/assets/packet-BdpOJ1Cy.js +0 -1
  238. package/client/dist/assets/packet-C3Ievb9R.js +0 -1
  239. package/client/dist/assets/packet-C4zJIG0Y.js +0 -1
  240. package/client/dist/assets/packet-Cx-zRuWn.js +0 -1
  241. package/client/dist/assets/packet-DUTGX52M.js +0 -1
  242. package/client/dist/assets/packet-DhsKtUPB.js +0 -1
  243. package/client/dist/assets/packet-z3LJ8Zyy.js +0 -1
  244. package/client/dist/assets/provider-capabilities-CXipwrXJ.js +0 -1
  245. package/client/dist/assets/settings-B3rWEwOx.js +0 -1
  246. package/client/dist/assets/settings-BcVoiMyw.js +0 -1
  247. package/client/dist/assets/settings-BlYvaQ7w.js +0 -1
  248. package/client/dist/assets/settings-CD5uEBiL.js +0 -1
  249. package/client/dist/assets/settings-CyhS0DLJ.js +0 -1
  250. package/client/dist/assets/settings-DCMFUGDs.js +0 -1
  251. package/client/dist/assets/settings-DmbYKyMJ.js +0 -1
  252. package/client/dist/assets/setup-2xGgu4Sk.js +0 -1
  253. package/client/dist/assets/setup-Ba11l2JA.js +0 -1
  254. package/client/dist/assets/setup-BzW4kMok.js +0 -1
  255. package/client/dist/assets/setup-COzwYhkJ.js +0 -1
  256. package/client/dist/assets/setup-CRVULhtF.js +0 -1
  257. package/client/dist/assets/setup-DgvlxwpE.js +0 -1
  258. package/client/dist/assets/setup-Dw_wYeEY.js +0 -1
  259. package/client/dist/assets/setup-ZPtEeJPW.js +0 -1
@@ -7,7 +7,7 @@
7
7
  //
8
8
  // Spec: openspec/specs/multi-provider-architecture/spec.md
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.UnknownProviderError = exports.CUSTOM_MODEL_ALIAS_MAX_LENGTH = exports.isSafeCustomModelAlias = exports.isModelAvailableForAdapter = exports.isReasoningEffortValidForModel = exports.defaultReasoningEffortForModel = exports.reasoningEffortsForModel = exports.requireToolPolicy = exports.pureOutputToolPolicy = exports.supportsToolPolicy = exports.parseStreamEvents = exports.buildProviderRepoAccessArgs = exports.formatProviderCommand = exports.buildProviderEnv = exports.kimiAdapter = exports.geminiAdapter = exports.codexAdapter = exports.claudeAdapter = exports.listAdapters = exports.hasAdapter = exports.getAdapter = void 0;
10
+ exports.UnknownProviderError = exports.CUSTOM_MODEL_ALIAS_MAX_LENGTH = exports.isSafeCustomModelAlias = exports.isModelAvailableForAdapter = exports.isReasoningEffortValidForModel = exports.defaultReasoningEffortForModel = exports.reasoningEffortsForModel = exports.requireToolPolicy = exports.pureOutputToolPolicy = exports.supportsToolPolicy = exports.parseStreamEvents = exports.buildProviderRepoAccessArgs = exports.formatProviderCommand = exports.buildProviderEnv = exports.kimiAdapter = exports.geminiAdapter = exports.codexAdapter = exports.claudeAdapter = exports.adapterKind = exports.isLocalAdapterId = exports.unregisterAdapter = exports.listAdapters = exports.hasAdapter = exports.getAdapter = void 0;
11
11
  const registry_1 = require("./registry");
12
12
  const claude_adapter_1 = require("./claude-adapter");
13
13
  Object.defineProperty(exports, "claudeAdapter", { enumerable: true, get: function () { return claude_adapter_1.claudeAdapter; } });
@@ -25,6 +25,9 @@ var registry_2 = require("./registry");
25
25
  Object.defineProperty(exports, "getAdapter", { enumerable: true, get: function () { return registry_2.getAdapter; } });
26
26
  Object.defineProperty(exports, "hasAdapter", { enumerable: true, get: function () { return registry_2.hasAdapter; } });
27
27
  Object.defineProperty(exports, "listAdapters", { enumerable: true, get: function () { return registry_2.listAdapters; } });
28
+ Object.defineProperty(exports, "unregisterAdapter", { enumerable: true, get: function () { return registry_2.unregisterAdapter; } });
29
+ Object.defineProperty(exports, "isLocalAdapterId", { enumerable: true, get: function () { return registry_2.isLocalAdapterId; } });
30
+ Object.defineProperty(exports, "adapterKind", { enumerable: true, get: function () { return registry_2.adapterKind; } });
28
31
  var runtime_1 = require("./runtime");
29
32
  Object.defineProperty(exports, "buildProviderEnv", { enumerable: true, get: function () { return runtime_1.buildProviderEnv; } });
30
33
  Object.defineProperty(exports, "formatProviderCommand", { enumerable: true, get: function () { return runtime_1.formatProviderCommand; } });
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ // Dynamic registration of local (OpenAI-compatible) adapters — one per
3
+ // connection in `~/.specrails/runtime-providers.json`. Called at boot (after
4
+ // the connections load) and after every successful `PUT /api/runtime-providers`.
5
+ //
6
+ // Kill switch: SPECRAILS_LOCAL_ENGINES=0|false|off ⇒ no registration, and any
7
+ // previously registered local adapter is removed (byte-identical legacy).
8
+ //
9
+ // Spec: openspec/changes/local-ai-engines/specs/local-ai-engines/spec.md
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.isLocalEnginesEnabled = isLocalEnginesEnabled;
12
+ exports.listLocalAdapters = listLocalAdapters;
13
+ exports.listLocalConnections = listLocalConnections;
14
+ exports.syncLocalAdapters = syncLocalAdapters;
15
+ const registry_1 = require("./registry");
16
+ const local_adapter_1 = require("./local-adapter");
17
+ function isLocalEnginesEnabled() {
18
+ const raw = (process.env.SPECRAILS_LOCAL_ENGINES ?? '').trim().toLowerCase();
19
+ return !(raw === '0' || raw === 'false' || raw === 'off');
20
+ }
21
+ /** Every currently registered local adapter, in registry order. */
22
+ function listLocalAdapters() {
23
+ return (0, registry_1.listAdapters)().filter(local_adapter_1.isLocalAdapter);
24
+ }
25
+ /** The connections behind the registered local adapters. */
26
+ function listLocalConnections() {
27
+ return listLocalAdapters().map((a) => a.localConnection);
28
+ }
29
+ function syncLocalAdapters(connections) {
30
+ const result = { registered: [], unregistered: [], skipped: [] };
31
+ const wanted = new Map();
32
+ if (isLocalEnginesEnabled()) {
33
+ for (const conn of connections) {
34
+ if (conn.kind !== 'openai-compatible' || typeof conn.baseUrl !== 'string')
35
+ continue;
36
+ if ((0, registry_1.hasAdapter)(conn.id) && (0, registry_1.adapterKind)(conn.id) !== 'local') {
37
+ result.skipped.push(conn.id);
38
+ continue;
39
+ }
40
+ wanted.set(conn.id, conn);
41
+ }
42
+ }
43
+ for (const existing of listLocalAdapters()) {
44
+ if (!wanted.has(existing.id)) {
45
+ if ((0, registry_1.unregisterAdapter)(existing.id))
46
+ result.unregistered.push(existing.id);
47
+ }
48
+ }
49
+ for (const [id, conn] of wanted) {
50
+ // Re-register on every sync so a changed baseUrl/rates/label takes effect;
51
+ // in-flight jobs keep the adapter instance they resolved earlier.
52
+ (0, registry_1.register)((0, local_adapter_1.createLocalAdapter)({ ...conn }), 'local');
53
+ result.registered.push(id);
54
+ }
55
+ return result;
56
+ }
@@ -0,0 +1,426 @@
1
+ "use strict";
2
+ // Local (OpenAI-compatible) engine adapter.
3
+ //
4
+ // One adapter instance PER CONNECTION (`~/.specrails/runtime-providers.json`,
5
+ // `kind: 'openai-compatible'`), registered dynamically by
6
+ // `local-adapter-registry.ts`. The engine is the bundled local agent runner
7
+ // (`local-runner/` → `src-tauri/binaries/specrails-local-runner.js`) executed
8
+ // by the bundled Node — spawned exactly like a CLI so every manager's
9
+ // spawn/stream/settle path is reused unchanged (design D1).
10
+ //
11
+ // The runner speaks a claude-shaped `stream-json` dialect (design D2). This
12
+ // adapter owns its OWN frame parser — deliberately NOT an import of the claude
13
+ // parser — so claude-specific heuristics (notification frames, alias pinning,
14
+ // background tasks) never leak in and a claude parser change can't silently
15
+ // break local engines. `total_cost_usd` is never present.
16
+ //
17
+ // Spec: openspec/changes/local-ai-engines/specs/local-ai-engines/spec.md
18
+ var __importDefault = (this && this.__importDefault) || function (mod) {
19
+ return (mod && mod.__esModule) ? mod : { "default": mod };
20
+ };
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.LOCAL_REASONING_EFFORTS = exports.LOCAL_PROJECT_DIR = void 0;
23
+ exports.isLocalAdapter = isLocalAdapter;
24
+ exports.resolveLocalRunnerScript = resolveLocalRunnerScript;
25
+ exports.resolveLocalRunnerNode = resolveLocalRunnerNode;
26
+ exports.filterLocalExtraArgs = filterLocalExtraArgs;
27
+ exports.parseLocalStreamLine = parseLocalStreamLine;
28
+ exports.extractLocalResult = extractLocalResult;
29
+ exports.createLocalAdapter = createLocalAdapter;
30
+ exports.localConnectionRates = localConnectionRates;
31
+ const fs_1 = __importDefault(require("fs"));
32
+ const path_1 = __importDefault(require("path"));
33
+ const path_resolver_1 = require("../path-resolver");
34
+ const win_spawn_1 = require("../util/win-spawn");
35
+ const local_engine_detection_1 = require("../local-engine-detection");
36
+ function isLocalAdapter(adapter) {
37
+ return typeof adapter.localConnection === 'object'
38
+ && adapter.localConnection !== null;
39
+ }
40
+ exports.LOCAL_PROJECT_DIR = '.specrails-local';
41
+ exports.LOCAL_REASONING_EFFORTS = ['low', 'medium', 'high'];
42
+ function fileExists(p) {
43
+ try {
44
+ return fs_1.default.statSync(p).isFile();
45
+ }
46
+ catch {
47
+ return false;
48
+ }
49
+ }
50
+ const RUNNER_RELS = [
51
+ path_1.default.join('src-tauri', 'binaries', 'specrails-local-runner.js'),
52
+ path_1.default.join('local-runner', 'dist', 'specrails-local-runner.js'),
53
+ ];
54
+ /**
55
+ * Absolute path to the bundled runner script (mirrors the MCP bridge resolution
56
+ * in agent-mcp-config.ts): `SPECRAILS_BUNDLED_LOCAL_RUNNER_PATH`, then the
57
+ * staged `src-tauri/binaries/` copy, then the `local-runner/dist/` build
58
+ * output. Returns null when none exists.
59
+ */
60
+ function resolveLocalRunnerScript() {
61
+ const fromEnv = process.env.SPECRAILS_BUNDLED_LOCAL_RUNNER_PATH;
62
+ if (fromEnv) {
63
+ const cleaned = (0, win_spawn_1.stripWindowsVerbatimPrefix)(fromEnv);
64
+ if (fileExists(cleaned))
65
+ return cleaned;
66
+ }
67
+ const roots = [
68
+ path_1.default.resolve(__dirname, '..', '..'),
69
+ path_1.default.resolve(__dirname, '..', '..', '..'),
70
+ process.cwd(),
71
+ ];
72
+ for (const root of roots) {
73
+ for (const rel of RUNNER_RELS) {
74
+ const candidate = path_1.default.join(root, rel);
75
+ if (fileExists(candidate))
76
+ return candidate;
77
+ }
78
+ }
79
+ return null;
80
+ }
81
+ /** Deterministic argv[0] even when the build output is absent (spawn then
82
+ * fails with a clear "no such file" instead of an undefined argument). */
83
+ function runnerScriptOrDefault() {
84
+ return resolveLocalRunnerScript() ?? path_1.default.resolve(__dirname, '..', '..', RUNNER_RELS[0]);
85
+ }
86
+ /** The node executable that runs the runner (bundled in the packaged app, else PATH `node`). */
87
+ function resolveLocalRunnerNode() {
88
+ return (0, path_resolver_1.resolveBundledNodeExe)() ?? 'node';
89
+ }
90
+ // ─── extraArgs allowlist ─────────────────────────────────────────────────────
91
+ //
92
+ // Managers forward provider-shaped extras through `extraArgs` (context-scope
93
+ // tool flags, `--mcp-config`, repo access, codex plugin `-c` pairs, the claude
94
+ // openspec `--plugin-dir`…). The runner fails fast on unknown flags, so only
95
+ // the flags it understands pass; everything else is dropped WITH its value.
96
+ const RUNNER_VALUE_FLAGS = new Set([
97
+ '--tools', '--disallowedTools', '--mcp-config', '--add-dir', '--max-turns',
98
+ '--model', '--resume', '--system-prompt', '--append-system-prompt',
99
+ '--reasoning-effort', '--base-url', '--api-key-env',
100
+ ]);
101
+ /** Flags foreign to the runner that carry a value (drop flag + value). */
102
+ const FOREIGN_VALUE_FLAGS = new Set([
103
+ '--setting-sources', '--permission-mode', '--effort', '--plugin-dir', '-c',
104
+ '--allowedTools', '--output-format', '--input-format', '--approval-mode',
105
+ '--sandbox', '-i', '--image', '-m', '-S', '--session',
106
+ ]);
107
+ function filterLocalExtraArgs(extra) {
108
+ if (!extra || extra.length === 0)
109
+ return [];
110
+ const out = [];
111
+ for (let i = 0; i < extra.length; i++) {
112
+ const token = extra[i];
113
+ if (RUNNER_VALUE_FLAGS.has(token)) {
114
+ if (i + 1 < extra.length) {
115
+ out.push(token, extra[i + 1]);
116
+ i++;
117
+ }
118
+ continue;
119
+ }
120
+ if (FOREIGN_VALUE_FLAGS.has(token)) {
121
+ if (i + 1 < extra.length && !extra[i + 1].startsWith('-'))
122
+ i++;
123
+ continue;
124
+ }
125
+ // Any other flag (`--dangerously-skip-permissions`, `--safe-mode`,
126
+ // `--verbose`, `--yolo`, …) or stray positional token is dropped.
127
+ }
128
+ return out;
129
+ }
130
+ // ─── argv ────────────────────────────────────────────────────────────────────
131
+ function toolPolicyArgs(policy) {
132
+ if (policy === 'none')
133
+ return ['--tools', '__none__'];
134
+ if (policy === 'read-only')
135
+ return ['--tools', 'Read,Grep,Glob'];
136
+ return [];
137
+ }
138
+ function buildLocalArgs(connection, action, opts) {
139
+ const script = runnerScriptOrDefault();
140
+ const args = [script];
141
+ const effectiveOpts = action === 'auto-title' ? { ...opts, toolPolicy: 'none' } : opts;
142
+ const common = () => [
143
+ '--model', effectiveOpts.model || connection.defaultModel || 'default',
144
+ '--base-url', connection.baseUrl,
145
+ ...(connection.apiKeyEnv ? ['--api-key-env', connection.apiKeyEnv] : []),
146
+ ...toolPolicyArgs(effectiveOpts.toolPolicy),
147
+ ...(connection.supportsReasoningEffort && effectiveOpts.reasoning_effort
148
+ ? ['--reasoning-effort', effectiveOpts.reasoning_effort]
149
+ : []),
150
+ '--output-format', 'stream-json',
151
+ ];
152
+ const extras = filterLocalExtraArgs(effectiveOpts.extraArgs);
153
+ const maxTurns = effectiveOpts.maxTurns != null ? ['--max-turns', String(effectiveOpts.maxTurns)] : [];
154
+ switch (action) {
155
+ case 'chat-turn':
156
+ args.push(...common());
157
+ if (opts.systemPrompt)
158
+ args.push('--system-prompt', opts.systemPrompt);
159
+ args.push(...maxTurns, '-p', opts.prompt, ...extras);
160
+ return args;
161
+ case 'chat-resume':
162
+ if (!opts.sessionId)
163
+ throw new Error('chat-resume requires sessionId');
164
+ args.push(...common());
165
+ if (opts.systemPrompt)
166
+ args.push('--system-prompt', opts.systemPrompt);
167
+ args.push('--resume', opts.sessionId, ...maxTurns, '-p', opts.prompt, ...extras);
168
+ return args;
169
+ case 'chat-stream':
170
+ // Persistent multi-turn: the prompt arrives over stdin, one line per turn.
171
+ // `--max-turns` omitted on purpose (it would end the whole session).
172
+ args.push(...common());
173
+ if (opts.systemPrompt)
174
+ args.push('--system-prompt', opts.systemPrompt);
175
+ if (opts.sessionId)
176
+ args.push('--resume', opts.sessionId);
177
+ args.push('--input-format', 'stream-json', ...extras);
178
+ return args;
179
+ case 'rail-job':
180
+ // The slash command carries its own framing; ADD to it, never overwrite.
181
+ args.push(...common());
182
+ if (opts.systemPrompt)
183
+ args.push('--append-system-prompt', opts.systemPrompt);
184
+ args.push('-p', opts.prompt, ...extras);
185
+ return args;
186
+ case 'spec-gen':
187
+ args.push(...common(), ...maxTurns);
188
+ if (opts.systemPrompt)
189
+ args.push('--system-prompt', opts.systemPrompt);
190
+ args.push('-p', opts.prompt, ...extras);
191
+ return args;
192
+ case 'agent-refine':
193
+ args.push(...common());
194
+ if (opts.sessionId)
195
+ args.push('--resume', opts.sessionId);
196
+ args.push('-p', opts.prompt, ...extras);
197
+ return args;
198
+ case 'setup-enrich':
199
+ args.push('-p', opts.prompt, ...common(), ...extras);
200
+ return args;
201
+ case 'setup-enrich-resume':
202
+ if (!opts.sessionId)
203
+ throw new Error('setup-enrich-resume requires sessionId');
204
+ args.push('--resume', opts.sessionId, ...common(), '-p', opts.prompt, ...extras);
205
+ return args;
206
+ case 'auto-title':
207
+ args.push(...common(), '-p', opts.prompt);
208
+ return args;
209
+ }
210
+ }
211
+ function readNumber(value) {
212
+ return typeof value === 'number' && Number.isFinite(value) ? value : 0;
213
+ }
214
+ function withUsage(ev, msg) {
215
+ const usage = msg?.usage;
216
+ if (!usage || typeof usage !== 'object')
217
+ return ev;
218
+ const carrier = ev;
219
+ carrier.usage = usage;
220
+ if (typeof msg?.id === 'string')
221
+ carrier.messageId = msg.id;
222
+ if (typeof msg?.model === 'string')
223
+ carrier.model = msg.model;
224
+ return ev;
225
+ }
226
+ function parseLocalStreamLine(line) {
227
+ if (line.length === 0)
228
+ return null;
229
+ let parsed;
230
+ try {
231
+ parsed = JSON.parse(line);
232
+ }
233
+ catch {
234
+ return null;
235
+ }
236
+ const type = parsed.type;
237
+ if (!type)
238
+ return { kind: 'other', type: '<missing>', raw: parsed };
239
+ if (type === 'system' || type === 'init') {
240
+ const sid = parsed.session_id;
241
+ if (sid)
242
+ return { kind: 'session-started', sessionId: sid };
243
+ return { kind: 'other', type, raw: parsed };
244
+ }
245
+ if (type === 'result') {
246
+ const result = { kind: 'result', payload: parsed, ...(parsed.is_error === true ? { isError: true } : {}) };
247
+ // The runner puts the endpoint/loop failure text in `result` when
248
+ // `is_error` is set (HTTP 4xx/5xx body, connection refused, max_turns…).
249
+ // Surface it as an explicit error event too, so every manager reports the
250
+ // real reason instead of a generic "returned no output".
251
+ const message = parsed.is_error === true && typeof parsed.result === 'string' && parsed.result.trim()
252
+ ? parsed.result.trim().slice(0, 600)
253
+ : null;
254
+ return message ? [{ kind: 'error', message }, result] : result;
255
+ }
256
+ if (type === 'error') {
257
+ const message = typeof parsed.message === 'string' ? parsed.message
258
+ : typeof parsed.error === 'string' ? parsed.error : 'local runner error';
259
+ return { kind: 'error', message };
260
+ }
261
+ if (type === 'assistant') {
262
+ const msg = parsed.message;
263
+ const blocks = Array.isArray(msg?.content) ? msg.content : [];
264
+ const text = blocks
265
+ .filter((b) => b.type === 'text')
266
+ .map((b) => (typeof b.text === 'string' ? b.text : ''))
267
+ .join('');
268
+ if (text)
269
+ return withUsage({ kind: 'text-delta', text }, msg);
270
+ const tool = blocks.find((b) => b.type === 'tool_use');
271
+ if (tool && typeof tool.name === 'string') {
272
+ const input = JSON.stringify(tool.input ?? {});
273
+ return withUsage({
274
+ kind: 'tool-use',
275
+ name: tool.name,
276
+ inputPreview: input.slice(0, 200),
277
+ ...(typeof tool.id === 'string' ? { toolUseId: tool.id } : {}),
278
+ }, msg);
279
+ }
280
+ return withUsage({ kind: 'other', type, raw: parsed }, msg);
281
+ }
282
+ if (type === 'user') {
283
+ const msg = parsed.message;
284
+ const blocks = Array.isArray(msg?.content) ? msg.content : [];
285
+ const resultBlock = blocks.find((b) => b.type === 'tool_result');
286
+ if (resultBlock) {
287
+ const content = resultBlock.content;
288
+ const text = typeof content === 'string'
289
+ ? content
290
+ : Array.isArray(content)
291
+ ? content
292
+ .filter((c) => c.type === 'text' && typeof c.text === 'string')
293
+ .map((c) => c.text)
294
+ .join('\n')
295
+ : '';
296
+ return {
297
+ kind: 'tool-result',
298
+ outputPreview: text.slice(0, 600),
299
+ ...(typeof resultBlock.tool_use_id === 'string' ? { toolUseId: resultBlock.tool_use_id } : {}),
300
+ ...(resultBlock.is_error === true ? { isError: true } : {}),
301
+ };
302
+ }
303
+ }
304
+ return { kind: 'other', type, raw: parsed };
305
+ }
306
+ function extractLocalResult(events) {
307
+ let resultPayload = null;
308
+ let sessionId;
309
+ for (const ev of events) {
310
+ if (ev.kind === 'result')
311
+ resultPayload = ev.payload;
312
+ else if (ev.kind === 'session-started')
313
+ sessionId = ev.sessionId;
314
+ }
315
+ const usageFromAssistant = () => {
316
+ // No terminal `result` (killed/aborted). Reconstruct per message id,
317
+ // last snapshot wins; each distinct message is a separate API call.
318
+ const perMessage = new Map();
319
+ let model;
320
+ let anonymous = 0;
321
+ for (const ev of events) {
322
+ const carrier = ev;
323
+ if (!carrier.usage)
324
+ continue;
325
+ perMessage.set(carrier.messageId ?? `__anon-${anonymous++}`, carrier.usage);
326
+ if (carrier.model)
327
+ model = carrier.model;
328
+ }
329
+ if (perMessage.size === 0)
330
+ return { session_id: sessionId };
331
+ let tokensIn = 0, tokensOut = 0, cacheRead = 0, cacheCreate = 0;
332
+ for (const u of perMessage.values()) {
333
+ tokensIn += readNumber(u.input_tokens);
334
+ tokensOut += readNumber(u.output_tokens);
335
+ cacheRead += readNumber(u.cache_read_input_tokens);
336
+ cacheCreate += readNumber(u.cache_creation_input_tokens);
337
+ }
338
+ return { tokens_in: tokensIn, tokens_out: tokensOut, tokens_cache_read: cacheRead, tokens_cache_create: cacheCreate, model, session_id: sessionId };
339
+ };
340
+ if (!resultPayload)
341
+ return usageFromAssistant();
342
+ const usage = resultPayload.usage;
343
+ const finalSessionId = (typeof resultPayload.session_id === 'string' ? resultPayload.session_id : undefined) ?? sessionId;
344
+ const tokens = usage && typeof usage === 'object'
345
+ ? {
346
+ tokens_in: typeof usage.input_tokens === 'number' ? usage.input_tokens : undefined,
347
+ tokens_out: typeof usage.output_tokens === 'number' ? usage.output_tokens : undefined,
348
+ tokens_cache_read: typeof usage.cache_read_input_tokens === 'number' ? usage.cache_read_input_tokens : undefined,
349
+ tokens_cache_create: typeof usage.cache_creation_input_tokens === 'number' ? usage.cache_creation_input_tokens : undefined,
350
+ }
351
+ : (() => { const r = usageFromAssistant(); return { tokens_in: r.tokens_in, tokens_out: r.tokens_out, tokens_cache_read: r.tokens_cache_read, tokens_cache_create: r.tokens_cache_create }; })();
352
+ return {
353
+ ...tokens,
354
+ // NEVER total_cost_usd: local engines report no cost (design D9).
355
+ num_turns: typeof resultPayload.num_turns === 'number' ? resultPayload.num_turns : undefined,
356
+ model: typeof resultPayload.model === 'string' ? resultPayload.model : undefined,
357
+ duration_ms: typeof resultPayload.duration_ms === 'number' ? resultPayload.duration_ms : undefined,
358
+ duration_api_ms: typeof resultPayload.duration_api_ms === 'number' ? resultPayload.duration_api_ms : undefined,
359
+ session_id: finalSessionId,
360
+ };
361
+ }
362
+ // ─── factory ─────────────────────────────────────────────────────────────────
363
+ function createLocalAdapter(connection) {
364
+ const supportsEffort = connection.supportsReasoningEffort === true;
365
+ const displayName = connection.label?.trim() || connection.id;
366
+ const adapter = {
367
+ id: connection.id,
368
+ displayName,
369
+ binary: resolveLocalRunnerNode(),
370
+ minCliVersion: null,
371
+ projectDirName: exports.LOCAL_PROJECT_DIR,
372
+ instructionsFilename: 'AGENTS.md',
373
+ mcpRegistration: 'project-json',
374
+ localConnection: connection,
375
+ capabilities: {
376
+ nativeResume: true,
377
+ nativeStreamJson: true,
378
+ nativeCostUsd: false,
379
+ reportsUsage: true,
380
+ nativeOtelEnv: false,
381
+ profileEnvSupport: false,
382
+ systemPromptArg: true,
383
+ persistentStdin: true,
384
+ supportsReasoningEffort: supportsEffort,
385
+ ...(supportsEffort ? { reasoningEfforts: exports.LOCAL_REASONING_EFFORTS } : {}),
386
+ supportsImageInput: false,
387
+ structuredActions: false,
388
+ toolPolicies: ['none', 'read-only'],
389
+ profiles: false,
390
+ customModelAliases: true,
391
+ customRoles: false,
392
+ freestyle: true,
393
+ userMcp: false,
394
+ },
395
+ modelCatalog: () => {
396
+ const cached = (0, local_engine_detection_1.getCachedModels)(connection.id);
397
+ if (cached.length === 0) {
398
+ const value = connection.defaultModel ?? 'default';
399
+ return [{ value, label: value, default: true }];
400
+ }
401
+ const stored = connection.defaultModel && cached.includes(connection.defaultModel) ? connection.defaultModel : cached[0];
402
+ return cached.map((value) => ({ value, label: value, ...(value === stored ? { default: true } : {}) }));
403
+ },
404
+ defaultModel: () => connection.defaultModel ?? (0, local_engine_detection_1.getCachedModels)(connection.id)[0] ?? 'default',
405
+ buildArgs: (action, opts) => buildLocalArgs(connection, action, opts),
406
+ parseStreamLine: parseLocalStreamLine,
407
+ extractResult: extractLocalResult,
408
+ formatCoreCommand: (command) => command,
409
+ buildRepoAccessArgs: (paths) => paths.flatMap((p) => ['--add-dir', p]),
410
+ projectMcpPath: (root) => path_1.default.join(root, exports.LOCAL_PROJECT_DIR, 'mcp.json'),
411
+ baselineAgents: () => ['sr-architect', 'sr-developer', 'sr-reviewer'],
412
+ detectInstalled: async () => {
413
+ const probe = await (0, local_engine_detection_1.probeConnection)({ baseUrl: connection.baseUrl, apiKeyEnv: connection.apiKeyEnv });
414
+ return {
415
+ installed: probe.installed,
416
+ executable: probe.executable,
417
+ ...(probe.error ? { error: probe.error } : {}),
418
+ };
419
+ },
420
+ };
421
+ return adapter;
422
+ }
423
+ /** Rates of a registered local adapter (null for CLI/unknown ids or no rates). */
424
+ function localConnectionRates(adapter) {
425
+ return isLocalAdapter(adapter) ? adapter.localConnection.rates ?? null : null;
426
+ }
@@ -1,19 +1,40 @@
1
1
  "use strict";
2
- // In-process registry of ProviderAdapter instances. Adapter modules call
3
- // `register(this)` on module load; managers look up by id via `getAdapter`.
2
+ // In-process registry of ProviderAdapter instances. CLI adapter modules call
3
+ // `register(this)` on module load; local (OpenAI-compatible) adapters are
4
+ // registered/unregistered DYNAMICALLY by `syncLocalAdapters` when connections
5
+ // load or save. Managers look up by id via `getAdapter`.
4
6
  //
5
7
  // Spec: openspec/specs/multi-provider-architecture/spec.md
6
8
  // - "Provider registry exposes lookup by id"
7
9
  Object.defineProperty(exports, "__esModule", { value: true });
8
10
  exports.register = register;
11
+ exports.unregisterAdapter = unregisterAdapter;
9
12
  exports.getAdapter = getAdapter;
10
13
  exports.hasAdapter = hasAdapter;
14
+ exports.adapterKind = adapterKind;
15
+ exports.isLocalAdapterId = isLocalAdapterId;
11
16
  exports.listAdapters = listAdapters;
12
17
  exports._clearForTests = _clearForTests;
13
18
  const types_1 = require("./types");
14
19
  const _registry = new Map();
15
- function register(adapter) {
20
+ const _kinds = new Map();
21
+ /** Register an adapter. `kind` defaults to `'cli'` (module-load registrations);
22
+ * local adapters pass `'local'` so they can later be unregistered. */
23
+ function register(adapter, kind = 'cli') {
16
24
  _registry.set(adapter.id, adapter);
25
+ _kinds.set(adapter.id, kind);
26
+ }
27
+ /**
28
+ * Remove a LOCAL adapter. CLI adapters (claude/codex/gemini/kimi — anything
29
+ * registered without `kind: 'local'`) are never removed: returns false and
30
+ * leaves them registered.
31
+ */
32
+ function unregisterAdapter(id) {
33
+ if (_kinds.get(id) !== 'local')
34
+ return false;
35
+ _registry.delete(id);
36
+ _kinds.delete(id);
37
+ return true;
17
38
  }
18
39
  function getAdapter(id) {
19
40
  const adapter = _registry.get(id);
@@ -25,8 +46,21 @@ function getAdapter(id) {
25
46
  function hasAdapter(id) {
26
47
  return _registry.has(id);
27
48
  }
49
+ /** Kind of a registered adapter, or null when unknown. */
50
+ function adapterKind(id) {
51
+ return _kinds.get(id) ?? null;
52
+ }
53
+ /** True when `id` names a registered LOCAL (OpenAI-compatible) adapter. */
54
+ function isLocalAdapterId(id) {
55
+ return !!id && _kinds.get(id) === 'local';
56
+ }
57
+ /** CLI adapters first (registration order), then local adapters. */
28
58
  function listAdapters() {
29
- return Array.from(_registry.values());
59
+ const all = Array.from(_registry.values());
60
+ return [
61
+ ...all.filter((a) => _kinds.get(a.id) !== 'local'),
62
+ ...all.filter((a) => _kinds.get(a.id) === 'local'),
63
+ ];
30
64
  }
31
65
  /**
32
66
  * Reset the registry. Test-only — production code MUST NOT call this.
@@ -34,4 +68,5 @@ function listAdapters() {
34
68
  */
35
69
  function _clearForTests() {
36
70
  _registry.clear();
71
+ _kinds.clear();
37
72
  }
@@ -379,7 +379,7 @@ function branchRecords(results) {
379
379
  * fallback for these errors; execution stays bound to a verified worktree.
380
380
  */
381
381
  async function launchIsolatedRail(input, io = {}) {
382
- const { ctx, railIndex, ticketIds, loopId, loopName, loopGraph, provider, model, effort } = input;
382
+ const { ctx, railIndex, ticketIds, loopId, loopName, loopGraph, provider, model, effort, deciderEngine } = input;
383
383
  let expectedRepositoryBaseSha = input.repositoryExecution?.expectedBaseSha;
384
384
  let singleRepositoryId = input.repositoryExecution?.repositoryId;
385
385
  if (!input.repositoryExecution) {
@@ -1187,6 +1187,7 @@ async function launchIsolatedRail(input, io = {}) {
1187
1187
  });
1188
1188
  const spec = ctx.getTicketSpec(a.ticketId);
1189
1189
  const enginePromise = ctx.loopRunManager.run({
1190
+ runtimeProviderOverride: input.runtimeProviderOverride,
1190
1191
  runId: a.runId, loopId, loopName, graph: loopGraph, projectId: ctx.project.id,
1191
1192
  cwd: a.handle.worktreePath, repoDir: a.handle.worktreePath,
1192
1193
  repositoryId: singleRepositoryId,
@@ -1204,7 +1205,7 @@ async function launchIsolatedRail(input, io = {}) {
1204
1205
  },
1205
1206
  ticketCompletionStatus: runFinishedOpts.ticketCompletionStatus,
1206
1207
  deferTerminalOutcome: true,
1207
- constants, provider, model, effort,
1208
+ constants, provider, model, effort, ...(deciderEngine ? { deciderEngine } : {}),
1208
1209
  profileName: input.profileName,
1209
1210
  });
1210
1211
  runPromises.push(settleAllocatedRun(a, enginePromise));
@@ -1610,7 +1611,7 @@ async function launchIsolatedRail(input, io = {}) {
1610
1611
  try {
1611
1612
  const outcomes = await (0, rail_merge_orchestrator_1.runMergeBack)({
1612
1613
  git, executor: (0, loop_executors_1.createLoopExecutors)(), baseDir: baseRepo,
1613
- provider, model, effort, constants, branches,
1614
+ provider, model, effort, ...(deciderEngine ? { deciderEngine } : {}), constants, branches,
1614
1615
  // CRIT-2: record every merge-back AI step (verify/resolve-merge/fix) as a
1615
1616
  // `surface='job'` invocation tied to the rail (surface_ref_id
1616
1617
  // `${jobId}:merge:${step}`), primary ticket = ticketIds[0].