specrails-desktop 2.47.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 (248) hide show
  1. package/README.md +2 -0
  2. package/client/dist/assets/{ActivityFeedPage-DSCpSOGV.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-Dw3DNFkh.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-BwuvqS2Y.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-Byvq8Oqx.js → LoopBuilderPage-DQE-7Vby.js} +2 -2
  19. package/client/dist/assets/{LoopPreviewModal-BMC9Mgj1.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-nspqxH-M.js → ProjectSettingsDialog-D6vBCoay.js} +1 -1
  24. package/client/dist/assets/{RepositoryDeliveries-Dq9NJOWi.js → RepositoryDeliveries-C36ipKp_.js} +1 -1
  25. package/client/dist/assets/{RepositoryScopeSelector-C3jKG2z0.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-C0KyPo2O.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-DyI4rbHa.js → agents-CA5eT1d0.js} +1 -1
  47. package/client/dist/assets/{agents-_5mJMkAE.js → agents-CAgy-5uS.js} +1 -1
  48. package/client/dist/assets/{agents-BIT1UdTy.js → agents-CNm6Qzlv.js} +1 -1
  49. package/client/dist/assets/{agents-CST-lpYX.js → agents-CTwbx-fb.js} +1 -1
  50. package/client/dist/assets/{agents-C7fKfvm8.js → agents-DEGV1Vao.js} +1 -1
  51. package/client/dist/assets/{agents-DsgGMGwU.js → agents-HsDF1aQU.js} +1 -1
  52. package/client/dist/assets/{agents-Cm2JYrAa.js → agents-IQjdA-z9.js} +1 -1
  53. package/client/dist/assets/{agents-H6tVQUNP.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-D44phX1J.js → formatDistanceToNow-w_2U5pm0.js} +1 -1
  72. package/client/dist/assets/{getTimezoneOffsetInMilliseconds-EAh9LFSx.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-UWi_xIZ-.js → narration--hUhjM5v.js} +1 -1
  85. package/client/dist/assets/{narration-DiPyj06T.js → narration-CHI06czm.js} +1 -1
  86. package/client/dist/assets/{narration-DrBAf8uv.js → narration-DjKB3pQZ.js} +1 -1
  87. package/client/dist/assets/{narration-CqNvgRi9.js → narration-DtCYvRLM.js} +1 -1
  88. package/client/dist/assets/{narration-B1uGfofH.js → narration-DvLQBW9U.js} +1 -1
  89. package/client/dist/assets/{narration-BKcL4Srl.js → narration-eSx3gR0W.js} +1 -1
  90. package/client/dist/assets/{narration-OyujFouL.js → narration-n3hOA7LY.js} +1 -1
  91. package/client/dist/assets/{narration-BtjKyHVM.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-GRYdjlKB.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-CN9PHM65.js → spending-hcgxHP4O.js} +1 -1
  119. package/client/dist/assets/{useDesktop-iius_s5d.js → useDesktop-CAisjKWC.js} +1 -1
  120. package/client/dist/assets/{useSharedWebSocket-D7hkZ3Ka.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/local-runner/dist/specrails-local-runner.js +15815 -0
  136. package/package.json +7 -5
  137. package/server/dist/agent-chat-manager.js +8 -1
  138. package/server/dist/agent-chat-router.js +5 -2
  139. package/server/dist/agent-fence-promotion.js +53 -0
  140. package/server/dist/agent-mcp-config.js +4 -1
  141. package/server/dist/agent-runtime-bridge.js +7 -2
  142. package/server/dist/agent-runtime-controls-router.js +12 -1
  143. package/server/dist/agent-runtime-controls.js +37 -2
  144. package/server/dist/agent-runtime-effective-config.js +28 -7
  145. package/server/dist/agent-runtime-metrics.js +1 -1
  146. package/server/dist/agent-runtime-settings-router.js +76 -6
  147. package/server/dist/agent-runtime-settings.js +137 -7
  148. package/server/dist/agent-spec-framing.js +10 -4
  149. package/server/dist/binary-probe.js +12 -0
  150. package/server/dist/blueprint-router.js +3 -2
  151. package/server/dist/chat-manager.js +5 -1
  152. package/server/dist/desktop-router.js +87 -4
  153. package/server/dist/index.js +13 -0
  154. package/server/dist/interactive-job-session.js +3 -1
  155. package/server/dist/local-engine-detection.js +118 -0
  156. package/server/dist/loop-executors.js +2 -1
  157. package/server/dist/loop-role-engines.js +129 -0
  158. package/server/dist/loop-run-manager.js +4 -4
  159. package/server/dist/mcp/tools/app-settings.js +30 -4
  160. package/server/dist/mcp/tools/rails.js +1 -1
  161. package/server/dist/pr-body.js +11 -2
  162. package/server/dist/pricing.js +15 -0
  163. package/server/dist/provider-detection.js +63 -1
  164. package/server/dist/provider-selection.js +26 -1
  165. package/server/dist/providers/index.js +4 -1
  166. package/server/dist/providers/local-adapter-registry.js +56 -0
  167. package/server/dist/providers/local-adapter.js +426 -0
  168. package/server/dist/providers/registry.js +39 -4
  169. package/server/dist/rail-isolated-launch.js +3 -3
  170. package/server/dist/rails-router.js +66 -10
  171. package/server/dist/result-event.js +17 -0
  172. package/server/dist/review-packet.js +1 -0
  173. package/server/dist/schemas/agent-runtime.schema.json +10 -2
  174. package/server/dist/setup-manager.js +5 -1
  175. package/server/dist/spec-models.js +14 -2
  176. package/server/dist/worktree-node-modules.js +97 -65
  177. package/client/dist/assets/AgentBrowserCapture-mc1c5pgS.js +0 -1
  178. package/client/dist/assets/AgentModeAnalyticsPane--UkamBTm.js +0 -2
  179. package/client/dist/assets/AgentModeCodePane-DJCswsvU.js +0 -2
  180. package/client/dist/assets/AgentsPage-ChElPUL9.js +0 -87
  181. package/client/dist/assets/AnalyticsPage-BGtkFHBp.js +0 -1
  182. package/client/dist/assets/CodePage-BBnw3VGn.js +0 -3
  183. package/client/dist/assets/DesktopAnalyticsPage-B85tNScc.js +0 -1
  184. package/client/dist/assets/DocsDialog-D4cXrOMl.js +0 -11
  185. package/client/dist/assets/DocsPage-BCAbC6qy.js +0 -11
  186. package/client/dist/assets/InteractiveJobComposer-Coq-Zhk0.js +0 -19
  187. package/client/dist/assets/JobDetailModal--Mjam0Nc.js +0 -1
  188. package/client/dist/assets/JobDetailPage-B6ShWSBh.js +0 -1
  189. package/client/dist/assets/JobsPage-DHU_ep_B.js +0 -1
  190. package/client/dist/assets/LoopsPage-CFGl-Nw8.js +0 -1
  191. package/client/dist/assets/MinimizedChatsContext-DHjHBYqa.js +0 -1
  192. package/client/dist/assets/PluginsPage-BHygRnW6.js +0 -2
  193. package/client/dist/assets/ReviewPacketPage-C9mqhi65.js +0 -1
  194. package/client/dist/assets/TemplatePreviewModal-BmwoAjOr.js +0 -1
  195. package/client/dist/assets/TicketDetailModalContext-lYmoONrd.js +0 -14
  196. package/client/dist/assets/agentRuntime-62GWdc26.js +0 -1
  197. package/client/dist/assets/agentRuntime-BN1mKAuY.js +0 -1
  198. package/client/dist/assets/agentRuntime-BNBCzQLM.js +0 -1
  199. package/client/dist/assets/agentRuntime-BQE_K1GG.js +0 -1
  200. package/client/dist/assets/agentRuntime-CCU3HCtS.js +0 -1
  201. package/client/dist/assets/agentRuntime-DT72Bpdv.js +0 -1
  202. package/client/dist/assets/agentRuntime-ZfMPeeiP.js +0 -1
  203. package/client/dist/assets/agentRuntime-g3W8Ry4d.js +0 -1
  204. package/client/dist/assets/analytics-47pYygtV.js +0 -1
  205. package/client/dist/assets/analytics-B0vt1g5U.js +0 -1
  206. package/client/dist/assets/analytics-BMzCvrCU.js +0 -1
  207. package/client/dist/assets/analytics-BlFN3Zwx.js +0 -1
  208. package/client/dist/assets/analytics-CZYzL-nR.js +0 -1
  209. package/client/dist/assets/analytics-DAsb9MJ8.js +0 -1
  210. package/client/dist/assets/analytics-FXQlQ2Yt.js +0 -1
  211. package/client/dist/assets/analytics-RnFCNkah.js +0 -1
  212. package/client/dist/assets/dashboard-BfBir_6o.js +0 -1
  213. package/client/dist/assets/file-text-BcLCfcWZ.js +0 -1
  214. package/client/dist/assets/index-Cfh9aaep.css +0 -2
  215. package/client/dist/assets/index-DgMBighL.js +0 -76
  216. package/client/dist/assets/jira-api-BQR6G0YY.js +0 -1
  217. package/client/dist/assets/jobs-5PQJbdOf.js +0 -1
  218. package/client/dist/assets/jobs-B2B08Oy9.js +0 -1
  219. package/client/dist/assets/jobs-BJZtBbwb.js +0 -1
  220. package/client/dist/assets/jobs-BlWEsQb7.js +0 -1
  221. package/client/dist/assets/jobs-ConfD8xD.js +0 -1
  222. package/client/dist/assets/jobs-DMPU64ms.js +0 -1
  223. package/client/dist/assets/jobs-DojASiTp.js +0 -1
  224. package/client/dist/assets/jobs-kVHkNpB-.js +0 -1
  225. package/client/dist/assets/packet-BQ2tXfJI.js +0 -1
  226. package/client/dist/assets/packet-BdpOJ1Cy.js +0 -1
  227. package/client/dist/assets/packet-C3Ievb9R.js +0 -1
  228. package/client/dist/assets/packet-C4zJIG0Y.js +0 -1
  229. package/client/dist/assets/packet-Cx-zRuWn.js +0 -1
  230. package/client/dist/assets/packet-DUTGX52M.js +0 -1
  231. package/client/dist/assets/packet-DhsKtUPB.js +0 -1
  232. package/client/dist/assets/packet-z3LJ8Zyy.js +0 -1
  233. package/client/dist/assets/provider-capabilities-CXipwrXJ.js +0 -1
  234. package/client/dist/assets/settings-B3rWEwOx.js +0 -1
  235. package/client/dist/assets/settings-BcVoiMyw.js +0 -1
  236. package/client/dist/assets/settings-BlYvaQ7w.js +0 -1
  237. package/client/dist/assets/settings-CD5uEBiL.js +0 -1
  238. package/client/dist/assets/settings-CyhS0DLJ.js +0 -1
  239. package/client/dist/assets/settings-DCMFUGDs.js +0 -1
  240. package/client/dist/assets/settings-DmbYKyMJ.js +0 -1
  241. package/client/dist/assets/setup-2xGgu4Sk.js +0 -1
  242. package/client/dist/assets/setup-Ba11l2JA.js +0 -1
  243. package/client/dist/assets/setup-BzW4kMok.js +0 -1
  244. package/client/dist/assets/setup-COzwYhkJ.js +0 -1
  245. package/client/dist/assets/setup-CRVULhtF.js +0 -1
  246. package/client/dist/assets/setup-DgvlxwpE.js +0 -1
  247. package/client/dist/assets/setup-Dw_wYeEY.js +0 -1
  248. package/client/dist/assets/setup-ZPtEeJPW.js +0 -1
@@ -12,7 +12,9 @@ const file_provenance_1 = require("./file-provenance");
12
12
  const file_story_1 = require("./file-story");
13
13
  const loops_store_1 = require("./loops-store");
14
14
  const loop_runs_store_1 = require("./loop-runs-store");
15
+ const registry_1 = require("./providers/registry");
15
16
  const providers_1 = require("./providers");
17
+ const loop_role_engines_1 = require("./loop-role-engines");
16
18
  const runtime_1 = require("./providers/runtime");
17
19
  const agent_defaults_1 = require("./agent-defaults");
18
20
  const profile_manager_1 = require("./profile-manager");
@@ -374,8 +376,9 @@ function createRailsRouter() {
374
376
  }
375
377
  const value = body.aiEngine;
376
378
  const c = ctx(req);
377
- // null clears the override; a string must be one of the project's providers.
378
- if (value !== null) {
379
+ // null clears the override; a string must be one of the project's providers
380
+ // — or the `roles` sentinel (per-role engines from Settings ▸ Specrails Agents).
381
+ if (value !== null && value !== loop_role_engines_1.ROLES_ENGINE) {
379
382
  const check = (0, provider_selection_1.validateRequestedProvider)(c.project, value);
380
383
  if (!check.ok) {
381
384
  res.status(400).json({ error: check.error });
@@ -635,7 +638,21 @@ function createRailsRouter() {
635
638
  }
636
639
  // AI engine precedence: explicit body param > stored rail engine > primary.
637
640
  // `undefined`/empty in both means "run on the project's primary provider".
638
- const requestedEngine = aiEngine === undefined ? (rail.aiEngine ?? undefined) : aiEngine;
641
+ const requestedEngineRaw = aiEngine === undefined ? (rail.aiEngine ?? undefined) : aiEngine;
642
+ // `roles` (hybrid-role-engines): the pipeline roles keep the project's
643
+ // per-role runtime settings (no flattening override) and the loop's own
644
+ // steps come from the stored loop-role engines — verifier for every
645
+ // non-core ai-step, decider for the Loop Decider. The rail's "engine" for
646
+ // the non-core path is therefore the verifier's provider.
647
+ const rolesMode = requestedEngineRaw === loop_role_engines_1.ROLES_ENGINE;
648
+ if (rolesMode && mode === 'freestyle') {
649
+ res.status(400).json({ error: 'roles_engine_unsupported_mode', detail: 'Freestyle has no roles; pick a concrete engine' });
650
+ return;
651
+ }
652
+ const loopRoleEngines = rolesMode ? (0, loop_role_engines_1.loadLoopRoleEngines)(c.project) : {};
653
+ const requestedEngine = rolesMode
654
+ ? (loopRoleEngines.verifier?.provider && (0, provider_selection_1.validateRequestedProvider)(c.project, loopRoleEngines.verifier.provider).ok ? loopRoleEngines.verifier.provider : undefined)
655
+ : requestedEngineRaw;
639
656
  const engineCheck = (0, provider_selection_1.validateRequestedProvider)(c.project, requestedEngine);
640
657
  if (!engineCheck.ok) {
641
658
  res.status(400).json({ error: engineCheck.error });
@@ -646,9 +663,18 @@ function createRailsRouter() {
646
663
  const railProvider = requestedEngine ? engineCheck.provider : undefined;
647
664
  // Mission/MCP and Launch-all submit aiEngine or use the saved rail engine.
648
665
  // Resolve this at the shared boundary, not only in the Dashboard payload.
649
- if (!runtimeProviderOverride && requestedEngine) {
666
+ // Local (OpenAI-compatible) engines: the override carries provider + model
667
+ // only — OpenAI endpoints have no effort knob, so effort is DROPPED (never
668
+ // forwarded to Core's executor), and cost caps are refused (a local run has
669
+ // no billable cost to cap against).
670
+ const localEngine = (0, registry_1.isLocalAdapterId)(engineCheck.provider);
671
+ if (rolesMode && runtimeProviderOverride) {
672
+ res.status(400).json({ error: 'runtime_provider_mismatch', detail: 'A roles launch carries no provider override' });
673
+ return;
674
+ }
675
+ if (!runtimeProviderOverride && requestedEngine && !rolesMode) {
650
676
  try {
651
- runtimeProviderOverride = (0, agent_runtime_settings_1.validateRuntimeProviderOverride)({ provider: engineCheck.provider, ...(model ? { model } : {}), ...(reasoning_effort ? { effort: reasoning_effort } : {}) });
677
+ runtimeProviderOverride = (0, agent_runtime_settings_1.validateRuntimeProviderOverride)({ provider: engineCheck.provider, ...(model ? { model } : {}), ...(reasoning_effort && !localEngine ? { effort: reasoning_effort } : {}) });
652
678
  }
653
679
  catch {
654
680
  res.status(400).json({ error: 'invalid_runtime_provider_override' });
@@ -659,6 +685,13 @@ function createRailsRouter() {
659
685
  res.status(400).json({ error: 'runtime_provider_mismatch' });
660
686
  return;
661
687
  }
688
+ if (localEngine && runtimeProviderOverride?.effort !== undefined) {
689
+ runtimeProviderOverride = { provider: runtimeProviderOverride.provider, ...(runtimeProviderOverride.model !== undefined ? { model: runtimeProviderOverride.model } : {}) };
690
+ }
691
+ if (localEngine && req.body?.maxCostUsd !== undefined && req.body?.maxCostUsd !== null) {
692
+ res.status(400).json({ error: 'local_engine_no_cost_cap' });
693
+ return;
694
+ }
662
695
  // Freestyle bypasses the OpenSpec pipeline and hands the raw spec to a
663
696
  // provider that explicitly advertises autonomous freestyle support.
664
697
  if (mode === 'freestyle' && (0, providers_1.getAdapter)(engineCheck.provider).capabilities.freestyle !== true) {
@@ -779,26 +812,40 @@ function createRailsRouter() {
779
812
  return;
780
813
  }
781
814
  }
815
+ const verifierEngine = rolesMode ? (0, loop_role_engines_1.resolveLoopRoleEngine)(c.project, 'verifier', { provider: loopProvider, model: loopAdapter.defaultModel() }, loopRoleEngines) : undefined;
782
816
  const loopModel = typeof model === 'string' && model
783
817
  ? model
784
- : (profileModel ?? globalAgentDefaults?.pipelineModel ?? loopAdapter.defaultModel());
818
+ : (verifierEngine?.model ?? profileModel ?? globalAgentDefaults?.pipelineModel ?? loopAdapter.defaultModel());
785
819
  let effort;
786
- if (reasoning_effort !== undefined && reasoning_effort !== null) {
820
+ // Local (OpenAI-compatible) engines have no per-invocation effort knob
821
+ // unless the connection opts in; a requested effort is DROPPED, not a
822
+ // 400 — the engine selectors send the last-used effort blindly.
823
+ const effortRequested = localEngine && !loopAdapter.capabilities.supportsReasoningEffort ? undefined : (reasoning_effort ?? verifierEngine?.effort);
824
+ if (effortRequested !== undefined && effortRequested !== null) {
787
825
  const allowed = (0, providers_1.reasoningEffortsForModel)(loopAdapter, loopModel);
788
- if (typeof reasoning_effort !== 'string' ||
789
- !allowed.includes(reasoning_effort)) {
826
+ if (typeof effortRequested !== 'string' ||
827
+ !allowed.includes(effortRequested)) {
790
828
  res.status(400).json({
791
829
  error: `reasoning_effort is not valid for provider "${loopProvider}" and model "${loopModel}"`,
792
830
  allowed,
793
831
  });
794
832
  return;
795
833
  }
796
- effort = reasoning_effort;
834
+ effort = effortRequested;
797
835
  }
798
836
  else if (globalAgentDefaults?.pipelineEffort
799
837
  && (0, runtime_1.isReasoningEffortValidForModel)(loopAdapter, loopModel, globalAgentDefaults.pipelineEffort)) {
800
838
  effort = globalAgentDefaults.pipelineEffort;
801
839
  }
840
+ // Loop Decider engine under roles mode (else the rail's engine, as before).
841
+ // Its provider must enforce a read-only tool policy like the rail's.
842
+ const deciderEngine = rolesMode && loopGraph.nodes.some((node) => node.type === 'decider')
843
+ ? (0, loop_role_engines_1.resolveLoopRoleEngine)(c.project, 'decider', { provider: loopProvider, model: loopModel, ...(effort ? { effort } : {}) }, loopRoleEngines)
844
+ : undefined;
845
+ if (deciderEngine && !(0, providers_1.supportsToolPolicy)((0, providers_1.getAdapter)(deciderEngine.provider), 'read-only')) {
846
+ res.status(409).json({ code: 'provider_tool_policy_unsupported', provider: deciderEngine.provider, requiredPolicy: 'read-only', error: `Provider '${deciderEngine.provider}' cannot run Loop Deciders because its headless CLI does not enforce a read-only tool policy.` });
847
+ return;
848
+ }
802
849
  // The loop's command(s) declare ticket scope + required capabilities.
803
850
  const promptsText = loopGraph.nodes
804
851
  .filter((n) => n.type === 'ai-step')
@@ -934,6 +981,7 @@ function createRailsRouter() {
934
981
  runtimeProviderOverride,
935
982
  ctx: c, railIndex, ticketIds: [...rail.ticketIds], repositoryIds, ...repositoryBases, loopId, loopName, loopGraph,
936
983
  provider: loopProvider, model: loopModel, effort, scope,
984
+ ...(deciderEngine ? { deciderEngine } : {}),
937
985
  profileName: resolvedProfile,
938
986
  originSurface: originSurface ?? 'dashboard',
939
987
  originConversationId: originConversationId ?? null,
@@ -1066,6 +1114,7 @@ function createRailsRouter() {
1066
1114
  provider: loopProvider,
1067
1115
  model: loopModel,
1068
1116
  effort,
1117
+ ...(deciderEngine ? { deciderEngine } : {}),
1069
1118
  profileName: resolvedProfile,
1070
1119
  })
1071
1120
  .then((r) => {
@@ -1244,6 +1293,13 @@ function createRailsRouter() {
1244
1293
  catch (err) {
1245
1294
  console.warn(`[rails-router] stop: loop cancel(${runId}) failed: ${err.message}`);
1246
1295
  }
1296
+ // A run the engine no longer owns (reconciled as an orphan by a context
1297
+ // reload while still alive) never settles through onLoopRunFinished, so
1298
+ // its rail entry would pin the tickets "in flight" forever. When the
1299
+ // durable row is already terminal, release the entry here.
1300
+ const row = (0, loop_runs_store_1.getLoopRun)(c.db, runId);
1301
+ if (!row || row.status === 'completed')
1302
+ c.railLoopRuns.delete(runId);
1247
1303
  const stopMsg = {
1248
1304
  type: 'loop.run_stopped',
1249
1305
  projectId: c.project.id,
@@ -23,6 +23,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
23
23
  exports.finaliseInvocationResult = finaliseInvocationResult;
24
24
  exports.normaliseResultEvent = normaliseResultEvent;
25
25
  const pricing_1 = require("./pricing");
26
+ const local_adapter_1 = require("./providers/local-adapter");
26
27
  /**
27
28
  * Finalise an AI invocation by walking the adapter's parsed events, stamping
28
29
  * the model when the adapter does not report one, and applying the local
@@ -68,6 +69,22 @@ function finaliseInvocationResult(adapter, events, opts = {}) {
68
69
  // (COST-ACCOUNTING-AUDIT CRIT-1). A genuine native cost — including a
69
70
  // numeric 0 — still passes through untouched with estimated=false.
70
71
  const nativeCostAbsent = cloned.total_cost_usd === null || cloned.total_cost_usd === undefined;
72
+ // Local (OpenAI-compatible) engines are NEVER rate-card guessed: cost is the
73
+ // connection's optional user-supplied rates (flagged estimated) or NULL —
74
+ // honest "cost unknown", never $0 (design D9).
75
+ if ((0, local_adapter_1.isLocalAdapter)(adapter)) {
76
+ if (nativeCostAbsent && hasBillableUsage) {
77
+ const computed = (0, pricing_1.estimateLocalCostUsd)(adapter.localConnection.rates, {
78
+ tokens_in: cloned.tokens_in,
79
+ tokens_out: cloned.tokens_out,
80
+ });
81
+ if (computed !== null) {
82
+ cloned.total_cost_usd = computed;
83
+ estimated = true;
84
+ }
85
+ }
86
+ return { result: cloned, estimated };
87
+ }
71
88
  if (nativeCostAbsent && hasBillableUsage) {
72
89
  const estimator = opts.estimator ?? pricing_1.estimateCostUsd;
73
90
  const computed = estimator(adapter.id, cloned.model, {
@@ -240,6 +240,7 @@ function composeReviewPacket({ db, row, repositoryId = row.repository_id ?? unde
240
240
  title: snap?.title ?? null,
241
241
  problem: narrative.problem,
242
242
  solution: narrative.solution,
243
+ solutionOverflow: narrative.overflow,
243
244
  labels: snap?.labels ?? [],
244
245
  implementationOutcome: unitRows[0]?.implementationOutcome ?? null,
245
246
  deliveryOutcome: unitRows[0]?.deliveryOutcome ?? null,
@@ -12,10 +12,12 @@
12
12
  "planning": { "enum": ["full", "proportional"] }, "acceptDeveloperChecks": { "type": "boolean" },
13
13
  "verification": { "type": "object", "additionalProperties": false, "properties": { "maxConcurrency": { "type": "integer", "minimum": 1, "maximum": 4 } } }
14
14
  } },
15
+ "guardrails": { "type": "object", "additionalProperties": false, "properties": { "plan-validation": { "type": "boolean" }, "genuine-blocking-question": { "type": "boolean" }, "plan-language-hint": { "type": "boolean" }, "frozen-plan-writes": { "type": "boolean" }, "primary-language": { "type": "boolean" }, "duplicate-sibling": { "type": "boolean" }, "one-test-per-module": { "type": "boolean" }, "empty-write": { "type": "boolean" }, "evidence-gated-ticking": { "type": "boolean" }, "inventory-retry": { "type": "boolean" }, "synthetic-corrections": { "type": "boolean" }, "silent-group-closure": { "type": "boolean" }, "verify-per-group": { "type": "boolean" }, "test-reachability": { "type": "boolean" }, "exit-code-honesty": { "type": "boolean" }, "environment-repair": { "type": "boolean" }, "lockfile-repair": { "type": "boolean" }, "verify-idle-timeout": { "type": "boolean" } } },
15
16
  "rolePrompts": { "type": "object", "additionalProperties": false, "properties": {
16
17
  "architect": { "type": "string", "minLength": 1, "maxLength": 20000, "pattern": "\\S" },
17
18
  "developer": { "type": "string", "minLength": 1, "maxLength": 20000, "pattern": "\\S" },
18
- "reviewer": { "type": "string", "minLength": 1, "maxLength": 20000, "pattern": "\\S" }
19
+ "reviewer": { "type": "string", "minLength": 1, "maxLength": 20000, "pattern": "\\S" },
20
+ "fixer": { "type": "string", "minLength": 1, "maxLength": 20000, "pattern": "\\S" }
19
21
  } },
20
22
  "enabled": { "type": "boolean", "deprecated": true, "description": "Accepted for saved configuration compatibility; implementation always uses the agent runtime." },
21
23
  "providers": { "type": "array", "items": { "oneOf": [
@@ -25,9 +27,14 @@
25
27
  { "type": "object", "additionalProperties": false, "required": ["id", "kind", "baseUrl"], "properties": {
26
28
  "id": { "$ref": "#/definitions/id" }, "kind": { "const": "openai-compatible" },
27
29
  "baseUrl": { "type": "string", "pattern": "^https?://[^/?#@]+(?:/[^?#]*)?$" },
28
- "apiKeyEnv": { "type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" }
30
+ "apiKeyEnv": { "type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" },
31
+ "supportsReasoningEffort": { "type": "boolean", "description": "The endpoint honours the OpenAI reasoning_effort request field (low|medium|high)." },
32
+ "agentLoop": { "enum": ["compact", "free"], "description": "compact (default): host-driven pipelines of small structured calls for small local models; free: one agentic loop per role with guardrails." },
33
+ "contextWindowTokens": { "type": "integer", "minimum": 4096, "description": "Context window used for the compaction budget (chars/4 estimate). Default 32768." },
34
+ "maxOutputTokens": { "type": "integer", "minimum": 1024, "description": "Output budget (max_tokens) of one tool turn; retried once at twice this when cut off, always bounded by the remaining context window. Default 8192." }
29
35
  } }
30
36
  ] } },
37
+ "fixer": { "$ref": "#/definitions/agent" },
31
38
  "agents": { "type": "object", "additionalProperties": false, "required": ["architect", "developer", "reviewer"], "properties": {
32
39
  "architect": { "$ref": "#/definitions/agent" }, "developer": { "$ref": "#/definitions/agent" }, "reviewer": { "$ref": "#/definitions/agent" }
33
40
  } },
@@ -67,6 +74,7 @@
67
74
  "score": { "type": "number", "minimum": 0, "maximum": 100 },
68
75
  "agent": { "type": "object", "additionalProperties": false, "required": ["provider"], "dependencies": { "escalation": ["model"] }, "properties": {
69
76
  "effort": { "$ref": "#/definitions/effort" },
77
+ "thinking": { "type": "string", "enum": ["on", "off"], "description": "OpenAI-compatible engines only: private thinking (chat_template_kwargs.enable_thinking). Default off." },
70
78
  "escalation": { "type": "object", "additionalProperties": false, "required": ["model"], "properties": {
71
79
  "model": { "type": "string", "minLength": 1, "maxLength": 256, "pattern": "^[^-\\r\\n\\u0000][^\\r\\n\\u0000]*$" }, "effort": { "$ref": "#/definitions/effort" }
72
80
  } },
@@ -20,6 +20,7 @@ const win_spawn_1 = require("./util/win-spawn");
20
20
  const setup_prerequisites_1 = require("./setup-prerequisites");
21
21
  const core_package_1 = require("./core-package");
22
22
  const providers_1 = require("./providers");
23
+ const registry_1 = require("./providers/registry");
23
24
  const runtime_1 = require("./providers/runtime");
24
25
  const result_event_1 = require("./result-event");
25
26
  const ai_invocations_1 = require("./ai-invocations");
@@ -752,7 +753,10 @@ class SetupManager {
752
753
  const state = this.silentAssembleState(projectId);
753
754
  if (state.running)
754
755
  return;
755
- const list = (opts?.providersFilter ?? providers).filter((p) => (0, providers_1.hasAdapter)(p));
756
+ // Local (OpenAI-compatible) engines have no core framework target: core
757
+ // `init` rejects them (`unsupported provider 'local'`), so they never enter
758
+ // the assemble list whatever door (add, detection, retry) supplied them.
759
+ const list = (opts?.providersFilter ?? providers).filter((p) => (0, providers_1.hasAdapter)(p) && !(0, registry_1.isLocalAdapterId)(p));
756
760
  if (list.length === 0)
757
761
  return;
758
762
  this._silentAssembles.set(projectId, { running: true, failed: [] });
@@ -48,7 +48,14 @@ exports.PROVIDER_DEFAULT_MODEL = {
48
48
  kimi: 'k3',
49
49
  };
50
50
  function getModelsForProvider(provider) {
51
- return PROVIDER_MODELS[provider] ?? [];
51
+ const fixed = PROVIDER_MODELS[provider];
52
+ if (fixed)
53
+ return fixed;
54
+ // Dynamically registered adapters (local OpenAI-compatible engines) own a
55
+ // live catalog — the discovered `GET /v1/models` ids, or the stored default.
56
+ if ((0, providers_1.hasAdapter)(provider))
57
+ return (0, providers_1.getAdapter)(provider).modelCatalog().map(({ value, label }) => ({ value, label }));
58
+ return [];
52
59
  }
53
60
  function isValidModelForProvider(model, provider) {
54
61
  if (!(0, providers_1.hasAdapter)(provider))
@@ -56,5 +63,10 @@ function isValidModelForProvider(model, provider) {
56
63
  return (0, providers_1.isModelAvailableForAdapter)((0, providers_1.getAdapter)(provider), model);
57
64
  }
58
65
  function getProviderDefault(provider) {
59
- return exports.PROVIDER_DEFAULT_MODEL[provider] ?? '';
66
+ const fixed = exports.PROVIDER_DEFAULT_MODEL[provider];
67
+ if (fixed)
68
+ return fixed;
69
+ if ((0, providers_1.hasAdapter)(provider))
70
+ return (0, providers_1.getAdapter)(provider).defaultModel();
71
+ return '';
60
72
  }
@@ -33,59 +33,21 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.WORKTREE_DEPENDENCY_CACHES = void 0;
36
37
  exports.isWorktreeNodeModulesEnabled = isWorktreeNodeModulesEnabled;
38
+ exports.discoverPackageDirs = discoverPackageDirs;
37
39
  exports.authenticateWarmNodeModulesLinks = authenticateWarmNodeModulesLinks;
38
40
  exports.linkNodeModulesIntoWorktree = linkNodeModulesIntoWorktree;
39
41
  /**
40
- * Warm-dependency reuse for isolated rail worktrees.
42
+ * Reuse installed packages without sharing tool caches with the base checkout.
43
+ * Each worktree owns its node_modules directory; package entries remain links,
44
+ * while .vite, .vite-temp and .cache are local writable directories. No install,
45
+ * dependency copy or permission broadening is performed. Legacy whole-directory
46
+ * links are migrated only after their exact source has been authenticated.
41
47
  *
42
- * `git worktree add` materializes only TRACKED files, so a fresh worktree has
43
- * no `node_modules` — every isolated run used to pay a cold `npm install`
44
- * (root + nested packages) plus cold build caches before doing any real work.
45
- * This module links the base checkout's existing install into the worktree so
46
- * the run starts warm.
47
- *
48
- * Mechanics:
49
- * - DISCOVER: every directory that holds a `package.json` in the base repo at
50
- * depth ≤ `MAX_DEPTH` (root plus shallow sub-packages like `client/`),
51
- * skipping dot-dirs and anything inside a `node_modules` tree.
52
- * - LINK: for each discovered package dir whose base checkout actually has a
53
- * `node_modules`, create the same relative path in the worktree as a
54
- * SYMLINK (junction on Windows). No copy fallback on purpose — copying a
55
- * multi-GB dependency tree would be slower than the `npm install` it
56
- * replaces; a failed link simply degrades to the legacy cold start.
57
- * - NEVER overwrite: an existing entry at the destination (a real dir, a
58
- * prior link, anything) is left untouched, so resume passes are idempotent
59
- * and an agent-made install is never clobbered.
60
- *
61
- * The returned `authenticated` paths are appended to the launch's overlay-
62
- * exclusion list, so a linked `node_modules` inherits the same commit-time
63
- * guarantees as overlay scaffolding (excluded pathspecs + index audit) even if
64
- * the repo's `.gitignore` were ever missing the entry.
65
- *
66
- * Release safety (why `authenticated` exists next to `linked`): a repo whose
67
- * `.gitignore` carries the ordinary `node_modules/` DIRECTORY pattern does not
68
- * ignore a SYMLINK named `node_modules`, so the link surfaces as an untracked
69
- * entry in `git status`. Without evidence authorizing it, worktree release read
70
- * that as "the worktree contains changes made after settlement", parked the row
71
- * at `needs-review` forever and permanently blocked checkout of the branch. The
72
- * links therefore also carry `OverlayCleanupEvidence`, and a link created by an
73
- * EARLIER pass (resume) is re-authenticated instead of being ignored — the old
74
- * `linked`-only contract silently dropped both guarantees on resume.
75
- *
76
- * Authentication is deliberately narrow and source-anchored, never name-based:
77
- * the worktree entry must be a symlink whose resolved target is exactly the base
78
- * checkout's identically-named dependency directory. A real directory, a copy,
79
- * or a link pointing anywhere else stays unauthorized and preserves the
80
- * worktree, as `Recoverable work is never removed automatically` requires.
81
- *
82
- * Honest caveat (documented, accepted): the link SHARES the dependency tree
83
- * with the base checkout. Reads (tests, builds) are safe under concurrency; an
84
- * in-worktree `npm install` would write through to the shared tree — the same
85
- * mutation it would make in the user's checkout after merge.
86
- *
87
- * Kill switch: `SPECRAILS_WORKTREE_NODE_MODULES=false` restores the byte-
88
- * identical cold-worktree behaviour.
48
+ * Cleanup authority covers individual source-anchored links, never the entire
49
+ * writable directory. Replaced packages and newly created files therefore keep
50
+ * the existing ignored-artifact / recoverable-work settlement guarantees.
89
51
  */
90
52
  const fs = __importStar(require("fs"));
91
53
  const path = __importStar(require("path"));
@@ -94,6 +56,7 @@ const worktree_overlay_1 = require("./worktree-overlay");
94
56
  const MAX_DEPTH = 2;
95
57
  /** Name of the dependency directory this module links. */
96
58
  const DEPS_DIR = 'node_modules';
59
+ exports.WORKTREE_DEPENDENCY_CACHES = ['.vite', '.vite-temp', '.cache'];
97
60
  function isWorktreeNodeModulesEnabled() {
98
61
  return (process.env.SPECRAILS_WORKTREE_NODE_MODULES ?? '').toLowerCase() !== 'false';
99
62
  }
@@ -139,7 +102,8 @@ function discoverPackageDirs(baseRepo) {
139
102
  if (name.startsWith('.') || name === 'node_modules')
140
103
  continue;
141
104
  const childAbs = path.join(abs, name);
142
- if (!isDir(childAbs))
105
+ // A first-party symlink can escape the checkout or form a discovery cycle.
106
+ if (!fs.lstatSync(childAbs).isDirectory())
143
107
  continue;
144
108
  walk(rel === '' ? name : `${rel}/${name}`, depth + 1);
145
109
  }
@@ -176,7 +140,7 @@ function authenticateWarmLink(baseRepo, worktreePath, rel) {
176
140
  return null;
177
141
  // A link to something that is not a live directory proves nothing about the
178
142
  // base checkout's dependency tree.
179
- if (!isDir(target))
143
+ if (!exists(target))
180
144
  return null;
181
145
  }
182
146
  catch {
@@ -233,12 +197,60 @@ function discoverWorktreeDependencyPaths(worktreePath) {
233
197
  function authenticateWarmNodeModulesLinks(baseRepo, worktreePath) {
234
198
  const evidence = [];
235
199
  for (const rel of discoverWorktreeDependencyPaths(worktreePath)) {
236
- const entry = authenticateWarmLink(baseRepo, worktreePath, rel);
237
- if (entry)
238
- evidence.push(entry);
200
+ const legacy = authenticateWarmLink(baseRepo, worktreePath, rel);
201
+ if (legacy) {
202
+ evidence.push(legacy);
203
+ continue;
204
+ }
205
+ const dest = path.join(worktreePath, rel);
206
+ try {
207
+ if (!fs.lstatSync(dest).isDirectory())
208
+ continue;
209
+ for (const name of fs.readdirSync(dest)) {
210
+ if (exports.WORKTREE_DEPENDENCY_CACHES.includes(name))
211
+ continue;
212
+ const entryRel = `${rel}/${name}`;
213
+ const entry = authenticateWarmLink(baseRepo, worktreePath, entryRel);
214
+ if (entry) {
215
+ evidence.push(entry);
216
+ continue;
217
+ }
218
+ // Scoped package parents are local too; never follow foreign symlinks.
219
+ if (name.startsWith('@') && fs.lstatSync(path.join(dest, name)).isDirectory()) {
220
+ for (const scoped of fs.readdirSync(path.join(dest, name))) {
221
+ const child = authenticateWarmLink(baseRepo, worktreePath, `${entryRel}/${scoped}`);
222
+ if (child)
223
+ evidence.push(child);
224
+ }
225
+ }
226
+ }
227
+ }
228
+ catch { /* Unreadable or concurrently removed entries confer no authority. */ }
239
229
  }
240
230
  return evidence;
241
231
  }
232
+ function linkDependencyEntry(source, destination) {
233
+ fs.symlinkSync(source, destination, process.platform === 'win32'
234
+ ? (isDir(source) ? 'junction' : 'file') : undefined);
235
+ }
236
+ function createLocalDependencyTree(source, destination) {
237
+ fs.mkdirSync(destination);
238
+ for (const name of fs.readdirSync(source)) {
239
+ if (exports.WORKTREE_DEPENDENCY_CACHES.includes(name))
240
+ continue;
241
+ const src = path.join(source, name);
242
+ const dest = path.join(destination, name);
243
+ if (name.startsWith('@') && isDir(src)) {
244
+ fs.mkdirSync(dest);
245
+ for (const scoped of fs.readdirSync(src))
246
+ linkDependencyEntry(path.join(src, scoped), path.join(dest, scoped));
247
+ }
248
+ else
249
+ linkDependencyEntry(src, dest);
250
+ }
251
+ for (const cache of exports.WORKTREE_DEPENDENCY_CACHES)
252
+ fs.mkdirSync(path.join(destination, cache));
253
+ }
242
254
  /**
243
255
  * Link the base checkout's installed `node_modules` trees into a fresh
244
256
  * worktree. Best-effort and side-effect-transparent: never throws, never
@@ -256,25 +268,45 @@ function linkNodeModulesIntoWorktree(baseRepo, worktreePath) {
256
268
  if (!isDir(src))
257
269
  continue;
258
270
  const dest = path.join(worktreePath, ...rel.split('/'));
259
- if (!exists(dest)) {
271
+ const legacy = authenticateWarmLink(baseRepo, worktreePath, rel);
272
+ if (!exists(dest) || legacy) {
273
+ let staging;
274
+ let removedLegacy = false;
260
275
  try {
261
276
  fs.mkdirSync(path.dirname(dest), { recursive: true });
262
- fs.symlinkSync(src, dest, process.platform === 'win32' ? 'junction' : undefined);
277
+ staging = fs.mkdtempSync(path.join(path.dirname(dest), '.specrails-dependencies-'));
278
+ const prepared = path.join(staging, DEPS_DIR);
279
+ createLocalDependencyTree(src, prepared);
280
+ if (legacy) {
281
+ // Recheck immediately before removing the old link. Never unlink a
282
+ // directory or a replacement another actor installed in the meantime.
283
+ const current = authenticateWarmLink(baseRepo, worktreePath, rel);
284
+ if (!current || current.digest !== legacy.digest)
285
+ throw new Error('dependency link changed during preparation');
286
+ fs.unlinkSync(dest);
287
+ removedLegacy = true;
288
+ }
289
+ fs.renameSync(prepared, dest);
263
290
  result.linked.push(rel);
264
291
  }
265
292
  catch (err) {
266
- result.warnings.push(`failed to link ${rel}: ${errMsg(err)}`);
267
- continue;
293
+ if (removedLegacy && !exists(dest)) {
294
+ try {
295
+ linkDependencyEntry(src, dest);
296
+ }
297
+ catch (restoreError) {
298
+ result.warnings.push(`failed to restore ${rel}: ${errMsg(restoreError)}`);
299
+ }
300
+ }
301
+ result.warnings.push(`failed to prepare ${rel}: ${errMsg(err)}`);
302
+ }
303
+ finally {
304
+ if (staging)
305
+ fs.rmSync(staging, { recursive: true, force: true });
268
306
  }
269
- }
270
- // Authenticate what is actually on disk NOW — a link we just made, or one a
271
- // previous pass made. An entry that cannot prove itself (a real dir, an
272
- // agent-made install) is deliberately left unauthorized.
273
- const entry = authenticateWarmLink(baseRepo, worktreePath, rel);
274
- if (entry) {
275
- result.authenticated.push(rel);
276
- result.evidence.push(entry);
277
307
  }
278
308
  }
309
+ result.evidence = authenticateWarmNodeModulesLinks(baseRepo, worktreePath);
310
+ result.authenticated = result.evidence.map(entry => entry.path);
279
311
  return result;
280
312
  }
@@ -1 +0,0 @@
1
- import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{n as t,t as n}from"./jsx-runtime-zu2_FqZY.js";import{d as r,f as i,m as a,u as o}from"./TicketDetailModalContext-lYmoONrd.js";import{n as s}from"./createLucideIcon-Bjbbp_KS.js";import{s as c}from"./useDesktop-iius_s5d.js";import{Ht as l,I as u,K as d,Q as f,Wt as p,b as m,tt as h}from"./index-DgMBighL.js";var g=e(t(),1),_=n();function v({projectId:e,conversationId:t}){let{t:n}=s(`agent`),{error:v}=p(),y=h(t??`__new-mission__`),{closeBrowser:b,queueCapture:x,browserOwnerId:S,browserUrl:C,setBrowserUrl:w}=d(),{materializeDraftConversation:T}=u(),[E,D]=(0,g.useState)(!1),[O,k]=(0,g.useState)(!1),[A,j]=(0,g.useState)(`probing`);(0,g.useEffect)(()=>{if(t&&(E||O||A===`screencast`))return f(t)},[t,E,O,A]);let M=(0,g.useMemo)(()=>{if(C)return C;try{return i(localStorage.getItem(`specrails-desktop:agent-browser-url:${e}`)??``)??`about:blank`}catch{return`about:blank`}},[e]);(0,g.useEffect)(()=>{let e=!0;return r().then(t=>{e&&j(t?`native`:`screencast`)}),()=>{e=!1}},[]);let N=(0,g.useMemo)(()=>`agent-${t??`home`}-${Math.round(performance.now())}`,[t]),P=async e=>{k(!0);try{let r=await l(t??(await T()).id,a(e.screenshotDataUrl,`capture-${Date.now()}.png`));x(r),c.success(n(`workspace.browserCaptured`)),b()}catch(e){throw c.error(n(`workspace.uploadFailed`),{description:e instanceof Error?e.message:void 0}),e}finally{k(!1)}};return A===`probing`?null:A===`native`?(0,_.jsx)(o,{ownerId:S??void 0,leaseRevision:y,onBusyChange:D,transferError:v,url:M,onClose:b,onFallback:()=>j(`screencast`),onUrlChange:t=>{if(w(t),i(t)&&t!==`about:blank`)try{localStorage.setItem(`specrails-desktop:agent-browser-url:${e}`,t)}catch{}},confirmLabel:n(`workspace.browserConfirm`),selectLabel:n(`workspace.browserSelectStart`),onCaptured:P}):(0,_.jsx)(m,{open:!0,onClose:b,projectId:e,pendingSpecId:N,confirmLabel:n(`workspace.browserConfirm`),selectLabel:n(`workspace.browserSelectStart`),onCaptured:P})}export{v as AgentBrowserCapture};
@@ -1,2 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/AnalyticsPage-BGtkFHBp.js","assets/rolldown-runtime-CNC7AqOf.js","assets/jsx-runtime-zu2_FqZY.js","assets/chunk-KS7C4IRE-DEKu087k.js","assets/preload-helper-Czpn1I53.js","assets/useSharedWebSocket-D7hkZ3Ka.js","assets/activity-DiyjsgQV.js","assets/addspec-D2YIQ0GU.js","assets/agent-Bh9K7iEt.js","assets/agentRuntime-BNBCzQLM.js","assets/agents-CST-lpYX.js","assets/agentstudio-CN2pokQu.js","assets/aiedit-iIjz2_Qk.js","assets/analytics-BMzCvrCU.js","assets/attachments-B53PG733.js","assets/browser-CG0PE1j_.js","assets/builder-BTiGnPT0.js","assets/chat-BGD7MXAy.js","assets/code-DaZ-ZwCT.js","assets/commands-BAWSYHjQ.js","assets/common-BrlwEnL7.js","assets/dashboard-BfBir_6o.js","assets/explore-DgBACn39.js","assets/integrations-mB-7zoyT.js","assets/jira-CKJg3HZd.js","assets/jobs-DMPU64ms.js","assets/loops-B7YpGizO.js","assets/mcp-CUAN6ORK.js","assets/narration-OyujFouL.js","assets/nav-Ch-zHBG5.js","assets/packet-Cx-zRuWn.js","assets/settings-BlYvaQ7w.js","assets/setup-Ba11l2JA.js","assets/specs-DII0C8gw.js","assets/terminal-BAzpHtpY.js","assets/tickets-W6pBmbrX.js","assets/createLucideIcon-Bjbbp_KS.js","assets/Trans-C0KyPo2O.js","assets/useDesktop-iius_s5d.js","assets/react-dom-DiWzBDP9.js","assets/spending-CN9PHM65.js","assets/clsx-CjueKrWZ.js","assets/formatDistanceToNow-D44phX1J.js","assets/getTimezoneOffsetInMilliseconds-EAh9LFSx.js","assets/provider-capabilities-CXipwrXJ.js","assets/index-DgMBighL.js","assets/TicketDetailModalContext-lYmoONrd.js","assets/save-B6wGWwBR.js","assets/MinimizedChatsContext-DHjHBYqa.js","assets/sparkles-DrY8hb0X.js","assets/x-CBMI9ejn.js","assets/file-text-BcLCfcWZ.js","assets/circle-x-CMYsN4ZH.js","assets/download-CFHyyHcw.js","assets/git-branch-BRM0F5GN.js","assets/loader-circle-DcygOQpC.js","assets/pencil-C9RkrIPZ.js","assets/square-Ci-PFcFe.js","assets/plus-CsnjfkmX.js","assets/search-Cq_gE3Ik.js","assets/trash-2-BOU3WdZZ.js","assets/triangle-alert-H9T4jAmh.js","assets/jira-api-BQR6G0YY.js","assets/zap-CHo3aCD1.js","assets/dialog-xXB2q5HJ.js","assets/utils-BC4DHlz2.js","assets/lib-CGdyxIWg.js","assets/button-Ccc9Dc-D.js","assets/RepositoryScopeSelector-C3jKG2z0.js","assets/project-repositories-GRYdjlKB.js","assets/core-B3mKBIh5.js","assets/RepositoryDeliveries-Dq9NJOWi.js","assets/format-duration-NJVPOPBG.js","assets/event-Bt7f3wXe.js","assets/index-Cfh9aaep.css","assets/ExportDropdown-BwuvqS2Y.js","assets/BarChart-nBOMNfiy.js","assets/value-s-gzBeyF.js"])))=>i.map(i=>d[i]);
2
- import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{n as t,t as n}from"./jsx-runtime-zu2_FqZY.js";import{t as r}from"./preload-helper-Czpn1I53.js";import{n as i}from"./createLucideIcon-Bjbbp_KS.js";import{t as a}from"./loader-circle-DcygOQpC.js";import{t as o}from"./x-CBMI9ejn.js";import{t as s}from"./utils-BC4DHlz2.js";import{K as c,vt as l,yt as u}from"./index-DgMBighL.js";var d=e(t(),1),f=n(),p=(0,d.lazy)(()=>r(()=>import(`./AnalyticsPage-BGtkFHBp.js`),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77]))),m=420,h=560;function g(){let{t:e}=i(`agent`),{closeAnalyticsPane:t}=c(),[n,r]=(0,d.useState)(h),[g,_]=(0,d.useState)(!1),v=(0,d.useCallback)(e=>{let t=e.clientX,i=n,a=window.innerWidth-m;try{e.currentTarget.setPointerCapture(e.pointerId)}catch{}let o=e=>{let n=Math.min(Math.max(i+(t-e.clientX),m),a);r(n)},s=()=>{window.removeEventListener(`pointermove`,o),window.removeEventListener(`pointerup`,s),window.removeEventListener(`pointercancel`,s)};window.addEventListener(`pointermove`,o),window.addEventListener(`pointerup`,s),window.addEventListener(`pointercancel`,s)},[n]);return(0,f.jsxs)(`div`,{className:s(`flex h-full flex-col overflow-hidden border-l border-border bg-background`,g&&`absolute inset-0 z-30 border-l-0`),style:g?void 0:{width:n,flexShrink:0},"data-testid":`agent-analytics-pane`,children:[(0,f.jsx)(`div`,{className:`relative flex items-center`,children:!g&&(0,f.jsx)(`div`,{role:`separator`,"aria-orientation":`vertical`,onPointerDown:v,className:`absolute left-0 top-0 h-full w-1.5 -translate-x-full cursor-col-resize select-none touch-none hover:bg-accent-primary/20`,title:e(`workspace.resizeAnalytics`)})}),(0,f.jsxs)(`div`,{className:`flex items-center gap-2 border-b border-border/60 bg-surface/40 px-3 py-1.5`,children:[(0,f.jsx)(`span`,{className:`text-xs font-medium text-foreground/70`,children:e(`workspace.analytics`)}),(0,f.jsxs)(`div`,{className:`ml-auto flex items-center gap-1`,children:[(0,f.jsx)(`button`,{type:`button`,onClick:()=>_(e=>!e),"aria-label":e(g?`restore`:`maximize`),title:e(g?`restore`:`maximize`),className:`rounded-md p-1 text-foreground/60 hover:bg-surface hover:text-foreground`,children:g?(0,f.jsx)(l,{className:`h-4 w-4`}):(0,f.jsx)(u,{className:`h-4 w-4`})}),(0,f.jsx)(`button`,{type:`button`,onClick:t,"aria-label":e(`workspace.closeAnalytics`),title:e(`workspace.closeAnalytics`),className:`rounded-md p-1 text-foreground/60 hover:bg-surface hover:text-foreground`,children:(0,f.jsx)(o,{className:`h-4 w-4`})})]})]}),(0,f.jsx)(`div`,{className:`min-h-0 flex-1 overflow-y-auto`,children:(0,f.jsx)(d.Suspense,{fallback:(0,f.jsx)(`div`,{className:`flex h-24 items-center justify-center text-muted-foreground`,children:(0,f.jsx)(a,{className:`h-4 w-4 animate-spin`})}),children:(0,f.jsx)(p,{})})})]})}export{g as AgentModeAnalyticsPane};
@@ -1,2 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/CodePage-BBnw3VGn.js","assets/rolldown-runtime-CNC7AqOf.js","assets/jsx-runtime-zu2_FqZY.js","assets/react-dom-DiWzBDP9.js","assets/preload-helper-Czpn1I53.js","assets/chunk-KS7C4IRE-DEKu087k.js","assets/TicketDetailModalContext-lYmoONrd.js","assets/useSharedWebSocket-D7hkZ3Ka.js","assets/activity-DiyjsgQV.js","assets/addspec-D2YIQ0GU.js","assets/agent-Bh9K7iEt.js","assets/agentRuntime-BNBCzQLM.js","assets/agents-CST-lpYX.js","assets/agentstudio-CN2pokQu.js","assets/aiedit-iIjz2_Qk.js","assets/analytics-BMzCvrCU.js","assets/attachments-B53PG733.js","assets/browser-CG0PE1j_.js","assets/builder-BTiGnPT0.js","assets/chat-BGD7MXAy.js","assets/code-DaZ-ZwCT.js","assets/commands-BAWSYHjQ.js","assets/common-BrlwEnL7.js","assets/dashboard-BfBir_6o.js","assets/explore-DgBACn39.js","assets/integrations-mB-7zoyT.js","assets/jira-CKJg3HZd.js","assets/jobs-DMPU64ms.js","assets/loops-B7YpGizO.js","assets/mcp-CUAN6ORK.js","assets/narration-OyujFouL.js","assets/nav-Ch-zHBG5.js","assets/packet-Cx-zRuWn.js","assets/settings-BlYvaQ7w.js","assets/setup-Ba11l2JA.js","assets/specs-DII0C8gw.js","assets/terminal-BAzpHtpY.js","assets/tickets-W6pBmbrX.js","assets/createLucideIcon-Bjbbp_KS.js","assets/Trans-C0KyPo2O.js","assets/useDesktop-iius_s5d.js","assets/save-B6wGWwBR.js","assets/spending-CN9PHM65.js","assets/MinimizedChatsContext-DHjHBYqa.js","assets/sparkles-DrY8hb0X.js","assets/x-CBMI9ejn.js","assets/file-text-BcLCfcWZ.js","assets/circle-x-CMYsN4ZH.js","assets/download-CFHyyHcw.js","assets/git-branch-BRM0F5GN.js","assets/loader-circle-DcygOQpC.js","assets/pencil-C9RkrIPZ.js","assets/square-Ci-PFcFe.js","assets/plus-CsnjfkmX.js","assets/search-Cq_gE3Ik.js","assets/trash-2-BOU3WdZZ.js","assets/triangle-alert-H9T4jAmh.js","assets/jira-api-BQR6G0YY.js","assets/zap-CHo3aCD1.js","assets/dialog-xXB2q5HJ.js","assets/utils-BC4DHlz2.js","assets/clsx-CjueKrWZ.js","assets/lib-CGdyxIWg.js","assets/formatDistanceToNow-D44phX1J.js","assets/getTimezoneOffsetInMilliseconds-EAh9LFSx.js","assets/button-Ccc9Dc-D.js","assets/RepositoryScopeSelector-C3jKG2z0.js","assets/project-repositories-GRYdjlKB.js","assets/provider-capabilities-CXipwrXJ.js","assets/index-DgMBighL.js","assets/core-B3mKBIh5.js","assets/RepositoryDeliveries-Dq9NJOWi.js","assets/format-duration-NJVPOPBG.js","assets/event-Bt7f3wXe.js","assets/index-Cfh9aaep.css"])))=>i.map(i=>d[i]);
2
- import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{n as t,t as n}from"./jsx-runtime-zu2_FqZY.js";import{t as r}from"./preload-helper-Czpn1I53.js";import{n as i}from"./createLucideIcon-Bjbbp_KS.js";import{t as a}from"./x-CBMI9ejn.js";import{$ as o,K as s,et as c,vt as l,yt as u}from"./index-DgMBighL.js";var d=e(t(),1),f=n(),p=(0,d.lazy)(()=>r(()=>import(`./CodePage-BBnw3VGn.js`),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74]))),m=420,h=560;function g({projectId:e,conversationId:t}){let{t:n}=i(`agent`),{closeCodePane:r}=s(),[g,_]=(0,d.useState)(h),[v,y]=(0,d.useState)(()=>window.innerWidth),[b,x]=(0,d.useState)(!1),S=(0,d.useRef)(null),C=(0,d.useRef)(null),w=Math.max(0,v-Math.min(m,v/2)),T=Math.min(m,w),E=Math.min(w,Math.max(T,g)),D=(0,d.useCallback)(e=>Math.min(w,Math.max(T,e)),[w,T]),O=`${e}:${t}`,k=(0,d.useCallback)(n=>{c(e,t,{path:n})},[e,t]);(0,d.useLayoutEffect)(()=>{let e=S.current,t=e?.parentElement;if(!e||!t)return;let n=()=>{let n=t.getBoundingClientRect().width||t.clientWidth||window.innerWidth,r=0;for(let n of t.children){if(n===e||!(n instanceof HTMLElement))continue;let t=getComputedStyle(n);t.display===`none`||t.position===`absolute`||t.position===`fixed`||Number(t.flexGrow)>0||(r+=n.getBoundingClientRect().width)}y(Math.max(0,n-r))},r=new ResizeObserver(n),i=()=>{r.disconnect(),r.observe(t);for(let n of t.children)n!==e&&r.observe(n);n()},a=new MutationObserver(i);return a.observe(t,{childList:!0}),i(),window.addEventListener(`resize`,n),()=>{r.disconnect(),a.disconnect(),window.removeEventListener(`resize`,n)}},[O]),(0,d.useEffect)(()=>()=>{C.current?.()},[O,b,w]);let A=(0,d.useCallback)(e=>{if(e.button!==0)return;e.preventDefault(),C.current?.();let t=e.clientX,n=E,r=e.pointerId,i=e.currentTarget;try{i.setPointerCapture(r)}catch{}let a=e=>{e.pointerId===r&&_(D(n+t-e.clientX))},o=()=>{window.removeEventListener(`pointermove`,a),window.removeEventListener(`pointerup`,s),window.removeEventListener(`pointercancel`,s),window.removeEventListener(`blur`,o),i.removeEventListener(`lostpointercapture`,o);try{i.hasPointerCapture(r)&&i.releasePointerCapture(r)}catch{}C.current=null},s=e=>{e.pointerId===r&&o()};C.current=o,window.addEventListener(`pointermove`,a),window.addEventListener(`pointerup`,s),window.addEventListener(`pointercancel`,s),window.addEventListener(`blur`,o),i.addEventListener(`lostpointercapture`,o)},[E,D]);return(0,f.jsxs)(`div`,{ref:S,className:`flex h-full min-w-0 flex-col overflow-hidden border-l border-border bg-background${b?` absolute inset-0 z-30 border-l-0`:` relative`}`,style:b?void 0:{width:E,maxWidth:`100%`,flexShrink:0},"data-testid":`agent-code-pane`,children:[!b&&(0,f.jsx)(`div`,{role:`separator`,"aria-orientation":`vertical`,onPointerDown:A,tabIndex:0,"aria-label":n(`workspace.resizeCode`),"aria-valuemin":Math.round(T),"aria-valuemax":Math.round(w),"aria-valuenow":Math.round(E),onKeyDown:e=>{let t=e.key===`ArrowLeft`?E+32:e.key===`ArrowRight`?E-32:e.key===`Home`?T:e.key===`End`?w:null;t!==null&&(e.preventDefault(),_(D(t)))},className:`absolute inset-y-0 left-0 z-20 w-1.5 cursor-col-resize select-none touch-none hover:bg-accent-primary/20 focus-visible:bg-accent-primary/30 focus-visible:outline-none`,title:n(`workspace.resizeCode`)}),(0,f.jsxs)(`div`,{className:`flex items-center gap-2 border-b border-border/60 bg-surface/40 px-3 py-1.5`,children:[(0,f.jsx)(`span`,{className:`text-xs font-medium text-foreground/70`,children:n(`workspace.files`)}),(0,f.jsxs)(`div`,{className:`ml-auto flex items-center gap-1`,children:[(0,f.jsx)(`button`,{type:`button`,onClick:()=>{C.current?.(),x(e=>!e)},"aria-label":n(b?`restore`:`maximize`),title:n(b?`restore`:`maximize`),className:`rounded-md p-1 text-foreground/60 hover:bg-surface hover:text-foreground`,children:b?(0,f.jsx)(l,{className:`h-4 w-4`}):(0,f.jsx)(u,{className:`h-4 w-4`})}),(0,f.jsx)(`button`,{type:`button`,onClick:()=>{C.current?.(),r()},"aria-label":n(`workspace.closeFiles`),title:n(`workspace.closeFiles`),className:`rounded-md p-1 text-foreground/60 hover:bg-surface hover:text-foreground`,children:(0,f.jsx)(a,{className:`h-4 w-4`})})]})]}),(0,f.jsx)(`div`,{className:`min-h-0 flex-1 overflow-hidden`,children:(0,f.jsx)(d.Suspense,{fallback:(0,f.jsx)(`div`,{className:`flex h-full items-center justify-center text-sm text-muted-foreground`,children:`…`}),children:(0,f.jsx)(p,{embedded:!0,initialRepositoryId:o(e,t).repositoryId,onRepositoryChange:n=>c(e,t,{repositoryId:n}),initialPath:o(e,t).path,onSelectedPathChange:k},O)})})]},O)}export{g as AgentModeCodePane};