specrails-desktop 2.58.1 → 2.59.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 (425) hide show
  1. package/README.md +2 -5
  2. package/client/dist/assets/{ActivityFeedPage-BQGppIR0.js → ActivityFeedPage-DBFt8ZHP.js} +1 -1
  3. package/client/dist/assets/AgentBrowserCapture-DiQx0W67.js +1 -0
  4. package/client/dist/assets/AgentModeAnalyticsPane-BmDSXp_J.js +2 -0
  5. package/client/dist/assets/AgentModeCodePane-BLQLPSwr.js +2 -0
  6. package/client/dist/assets/{AgentModeJobsPane-DR_06oqH.js → AgentModeJobsPane-BbTXdKUa.js} +1 -1
  7. package/client/dist/assets/{AgentsPage-BKIKjm_1.js → AgentsPage-Dei1jGR8.js} +3 -3
  8. package/client/dist/assets/{AnalyticsPage-069npRVo.js → AnalyticsPage-BhkeGIGn.js} +1 -1
  9. package/client/dist/assets/CodePage-B_9yYWUx.js +3 -0
  10. package/client/dist/assets/{DesktopAnalyticsPage-qtWAPM47.js → DesktopAnalyticsPage-DAcfEJac.js} +1 -1
  11. package/client/dist/assets/DocsDialog-WCuiYgsc.js +11 -0
  12. package/client/dist/assets/DocsPage-bGVYTMTU.js +11 -0
  13. package/client/dist/assets/{ExportDropdown-BcMpvV89.js → ExportDropdown-CsodZ-yb.js} +1 -1
  14. package/client/dist/assets/InteractiveJobComposer-D5fh_eVL.js +19 -0
  15. package/client/dist/assets/JobDetailModal-DlpXF61O.js +1 -0
  16. package/client/dist/assets/JobDetailPage-2yR19_Lb.js +1 -0
  17. package/client/dist/assets/JobsPage-CS9IR6Dq.js +1 -0
  18. package/client/dist/assets/LoopBuilderPage-BwiAtmOr.js +1 -0
  19. package/client/dist/assets/{LoopPreviewModal-je5pSCf-.js → LoopPreviewModal--upwvgTv.js} +1 -1
  20. package/client/dist/assets/LoopWindowSurface-DRnUixtl.js +1 -0
  21. package/client/dist/assets/LoopsPage-Xm3nRToQ.js +2 -0
  22. package/client/dist/assets/MinimizedChatsContext-D_8L4EWZ.js +1 -0
  23. package/client/dist/assets/PluginsPage-CAFQ1B4Q.js +2 -0
  24. package/client/dist/assets/ProjectSettingsDialog-DYSO4v90.js +1 -0
  25. package/client/dist/assets/{RepositoryDeliveries-BiwJuS8r.js → RepositoryDeliveries-CbQmW7Qx.js} +1 -1
  26. package/client/dist/assets/RepositoryScopeSelector-DKzvIGSZ.js +2 -0
  27. package/client/dist/assets/ReviewPacketPage-SDBhYdQB.js +4 -0
  28. package/client/dist/assets/TemplatePreviewModal-DHBUds51.js +1 -0
  29. package/client/dist/assets/TicketDetailModalContext-UAeNm7BB.js +16 -0
  30. package/client/dist/assets/{Trans-D3AfDylf.js → Trans-DExd3Z00.js} +1 -1
  31. package/client/dist/assets/agent-BElLtdKP.js +1 -0
  32. package/client/dist/assets/agent-C-m9VugX.js +1 -0
  33. package/client/dist/assets/agent-CGSKxfZk.js +1 -0
  34. package/client/dist/assets/agent-CW74ODDP.js +1 -0
  35. package/client/dist/assets/agent-DeCAZDhL.js +1 -0
  36. package/client/dist/assets/agent-DrM4qned.js +1 -0
  37. package/client/dist/assets/agent-KdIXq3w5.js +1 -0
  38. package/client/dist/assets/agent-jK8mCisM.js +1 -0
  39. package/client/dist/assets/{agentRuntime-DGz_IqKW.js → agentRuntime-15Pkk6we.js} +1 -1
  40. package/client/dist/assets/agentRuntime-Bpx3yWHK.js +1 -0
  41. package/client/dist/assets/{agentRuntime-Bg9tbmqm.js → agentRuntime-C78n13To.js} +1 -1
  42. package/client/dist/assets/{agentRuntime-D-GJVGkj.js → agentRuntime-CCI7XF4c.js} +1 -1
  43. package/client/dist/assets/{agentRuntime-D2__CVI_.js → agentRuntime-CKDHe2-a.js} +1 -1
  44. package/client/dist/assets/{agentRuntime-D35dGVOP.js → agentRuntime-Ddqvp2bP.js} +1 -1
  45. package/client/dist/assets/{agentRuntime-hInH9Fzj.js → agentRuntime-TXPQowKJ.js} +1 -1
  46. package/client/dist/assets/{agentRuntime-O1liDt6G.js → agentRuntime-hqZRy08E.js} +1 -1
  47. package/client/dist/assets/{builder-DGETbtZj.js → builder-B9gorTnT.js} +1 -1
  48. package/client/dist/assets/{builder-DyQperXU.js → builder-Bvw5s3Mi.js} +1 -1
  49. package/client/dist/assets/{builder-C1neIYGR.js → builder-CL5l4dU3.js} +1 -1
  50. package/client/dist/assets/{builder-DXEJUlC0.js → builder-CVDTVrQX.js} +1 -1
  51. package/client/dist/assets/{builder-CzW5ovyI.js → builder-CpS9liE9.js} +1 -1
  52. package/client/dist/assets/{builder-CvnFm0im.js → builder-DNctIL9Q.js} +1 -1
  53. package/client/dist/assets/{builder-D06L7iPy.js → builder-De2NxX6o.js} +1 -1
  54. package/client/dist/assets/{builder-C0FyvM83.js → builder-DeYT7Y-m.js} +1 -1
  55. package/client/dist/assets/{button-Ccc9Dc-D.js → button-D1xExSTF.js} +1 -1
  56. package/client/dist/assets/{common-DmsclwjP.js → common-Bdi0qN4S.js} +1 -1
  57. package/client/dist/assets/{common-C4Yvvhp6.js → common-Bhb9zN1P.js} +1 -1
  58. package/client/dist/assets/{common-od8WThlf.js → common-BykKlZg1.js} +1 -1
  59. package/client/dist/assets/{common-C0BOdeNi.js → common-CvnNNSJA.js} +1 -1
  60. package/client/dist/assets/{common-BCK7c9Pv.js → common-D5zk_mqq.js} +1 -1
  61. package/client/dist/assets/{common-DpLWwgI1.js → common-DQNO_al5.js} +1 -1
  62. package/client/dist/assets/{common-OCvl3c8t.js → common-Dax30Vmp.js} +1 -1
  63. package/client/dist/assets/{common-c0aujEZC.js → common-DkllpVSb.js} +1 -1
  64. package/client/dist/assets/{dialog-xXB2q5HJ.js → dialog-CMgps2MY.js} +1 -1
  65. package/client/dist/assets/external-link-D6XxC_Cu.js +1 -0
  66. package/client/dist/assets/file-text-BcLCfcWZ.js +1 -0
  67. package/client/dist/assets/{formatDistanceToNow-aBss1Nai.js → formatDistanceToNow-aw7H5ORC.js} +1 -1
  68. package/client/dist/assets/{getTimezoneOffsetInMilliseconds-DOGj1Mfz.js → getTimezoneOffsetInMilliseconds-NyC7gZJX.js} +1 -1
  69. package/client/dist/assets/index-CahchzS5.css +2 -0
  70. package/client/dist/assets/index-JSTZVJfe.js +83 -0
  71. package/client/dist/assets/integrations-BkpRUbvp.js +1 -0
  72. package/client/dist/assets/integrations-Bo8qbJf4.js +1 -0
  73. package/client/dist/assets/integrations-CmSrKzZb.js +1 -0
  74. package/client/dist/assets/integrations-DGtq26Rr.js +1 -0
  75. package/client/dist/assets/integrations-De38GFnQ.js +1 -0
  76. package/client/dist/assets/integrations-Dw6zdtb-.js +1 -0
  77. package/client/dist/assets/integrations-Dwl9KQp0.js +1 -0
  78. package/client/dist/assets/integrations-Refg_d2I.js +1 -0
  79. package/client/dist/assets/jira-api-DlXaRwA3.js +1 -0
  80. package/client/dist/assets/{jobs-KG4Sn12X.js → jobs-1G3wodhl.js} +1 -1
  81. package/client/dist/assets/{jobs-6_-r75yC.js → jobs-B4pu8TdF.js} +1 -1
  82. package/client/dist/assets/{jobs-pPv4KWjb.js → jobs-BD47Ov11.js} +1 -1
  83. package/client/dist/assets/{jobs-B1yqyId5.js → jobs-BLOoVRRl.js} +1 -1
  84. package/client/dist/assets/{jobs-BKiK-ZWh.js → jobs-CSrRx5lD.js} +1 -1
  85. package/client/dist/assets/{jobs-BFuV7ttH.js → jobs-DZAbh1m2.js} +1 -1
  86. package/client/dist/assets/{jobs-DmKV7b7d.js → jobs-epP7gt8W.js} +1 -1
  87. package/client/dist/assets/{jobs-BKa8LuwA.js → jobs-tWjcDF_6.js} +1 -1
  88. package/client/dist/assets/loops--KWr0X-w.js +1 -0
  89. package/client/dist/assets/{loops-BO3V8244.js → loops-3IutojWt.js} +1 -1
  90. package/client/dist/assets/{loops-C-hZK8pf.js → loops-BAosWtXE.js} +1 -1
  91. package/client/dist/assets/{loops-eUnCfLjC.js → loops-BISDssL5.js} +1 -1
  92. package/client/dist/assets/{loops-Dv849ckF.js → loops-Cm55CXJ4.js} +1 -1
  93. package/client/dist/assets/{loops-CDOWZ0ci.js → loops-D7tL_Saw.js} +1 -1
  94. package/client/dist/assets/{loops-BJDY9FTh.js → loops-DHQtnfze.js} +1 -1
  95. package/client/dist/assets/{loops-Q9LIgGkq.js → loops-kIJJkaxf.js} +1 -1
  96. package/client/dist/assets/nav--u7iPYlS.js +1 -0
  97. package/client/dist/assets/nav-C5Jgfaby.js +1 -0
  98. package/client/dist/assets/nav-CAna_Wpt.js +1 -0
  99. package/client/dist/assets/nav-CCLqeyHo.js +1 -0
  100. package/client/dist/assets/nav-Cf3c90Jk.js +1 -0
  101. package/client/dist/assets/nav-DMYQaQjR.js +1 -0
  102. package/client/dist/assets/nav-Df_PkZ9F.js +1 -0
  103. package/client/dist/assets/nav-VyNGBJwj.js +1 -0
  104. package/client/dist/assets/{project-repositories-CSnfzUpg.js → project-repositories-DXhb9FP4.js} +1 -1
  105. package/client/dist/assets/provider-capabilities-lePfeT_Y.js +1 -0
  106. package/client/dist/assets/save-OsUqVn99.js +1 -0
  107. package/client/dist/assets/settings-B5Hy_Kol.js +1 -0
  108. package/client/dist/assets/settings-BBj_kZaD.js +1 -0
  109. package/client/dist/assets/settings-ByVE7jdZ.js +1 -0
  110. package/client/dist/assets/settings-COlH_P33.js +1 -0
  111. package/client/dist/assets/settings-D77mC_Lb.js +1 -0
  112. package/client/dist/assets/settings-DZKy7MqN.js +1 -0
  113. package/client/dist/assets/settings-Dbyd6gmj.js +1 -0
  114. package/client/dist/assets/settings-Dn-pXukr.js +1 -0
  115. package/client/dist/assets/setup-Be485n2u.js +1 -0
  116. package/client/dist/assets/setup-CKGUxKr-.js +1 -0
  117. package/client/dist/assets/setup-Ck1Uwd7_.js +1 -0
  118. package/client/dist/assets/setup-Cm774a6v.js +1 -0
  119. package/client/dist/assets/setup-DIqMQpxt.js +1 -0
  120. package/client/dist/assets/setup-DfCsoNFL.js +1 -0
  121. package/client/dist/assets/setup-DkeE8mP0.js +1 -0
  122. package/client/dist/assets/setup-xVntVaz1.js +1 -0
  123. package/client/dist/assets/{spending-HBSk-6jv.js → spending-BdcyVkcd.js} +1 -1
  124. package/client/dist/assets/subscriptionUsage-Cf87DeS_.js +1 -0
  125. package/client/dist/assets/subscriptionUsage-D6u7VBdz.js +1 -0
  126. package/client/dist/assets/subscriptionUsage-DQ02J25O.js +1 -0
  127. package/client/dist/assets/subscriptionUsage-DWF3zKp4.js +1 -0
  128. package/client/dist/assets/subscriptionUsage-DkIf6Zuh.js +1 -0
  129. package/client/dist/assets/subscriptionUsage-DoWQv7l1.js +1 -0
  130. package/client/dist/assets/subscriptionUsage-N9hB-ARf.js +1 -0
  131. package/client/dist/assets/subscriptionUsage-zRcRPHkP.js +1 -0
  132. package/client/dist/assets/tauri-shell-DE6LfGPw.js +2 -0
  133. package/client/dist/assets/{useDesktop-0unHDBJJ.js → useDesktop-D07TvesS.js} +1 -1
  134. package/client/dist/assets/{useRuntimeRuns-BN1rhyEm.js → useRuntimeRuns-pGLmtAC6.js} +1 -1
  135. package/client/dist/assets/useSharedWebSocket-TzKtPRp9.js +2 -0
  136. package/client/dist/index.html +33 -32
  137. package/docs/README.md +1 -9
  138. package/docs/agent-live-steering.md +0 -6
  139. package/docs/agents/README.md +0 -2
  140. package/docs/agents/mcp.md +1 -14
  141. package/docs/cli.md +0 -13
  142. package/docs/codex.md +2 -75
  143. package/docs/creating-specs.md +0 -39
  144. package/docs/customizing.md +1 -15
  145. package/docs/getting-started.md +0 -23
  146. package/docs/guide/de/agents/2-profiles-and-the-balanced-default.md +0 -8
  147. package/docs/guide/de/agents/4-custom-agents-catalog.md +0 -15
  148. package/docs/guide/de/getting-started/1-what-is-specrails.md +0 -2
  149. package/docs/guide/de/getting-started/2-installing-and-first-run.md +0 -2
  150. package/docs/guide/de/getting-started/3-adding-your-first-project.md +0 -4
  151. package/docs/guide/de/getting-started/4-the-dashboard-tour.md +0 -5
  152. package/docs/guide/de/integrations/1-ai-providers.md +2 -50
  153. package/docs/guide/de/integrations/2-plugins.md +0 -8
  154. package/docs/guide/de/integrations/5-mcp-server.md +1 -14
  155. package/docs/guide/de/integrations/6-agent-chat.md +0 -11
  156. package/docs/guide/de/integrations/7-local-engines.md +0 -4
  157. package/docs/guide/de/pipeline/1-rails-and-jobs.md +0 -13
  158. package/docs/guide/de/pipeline/2-the-job-detail-view.md +0 -3
  159. package/docs/guide/de/pipeline/4-picking-an-engine-per-rail.md +0 -20
  160. package/docs/guide/de/pipeline/5-the-loop-builder.md +0 -6
  161. package/docs/guide/de/specs/2-add-spec-quick-mode.md +0 -7
  162. package/docs/guide/en/agents/2-profiles-and-the-balanced-default.md +0 -10
  163. package/docs/guide/en/agents/4-custom-agents-catalog.md +0 -25
  164. package/docs/guide/en/getting-started/1-what-is-specrails.md +0 -2
  165. package/docs/guide/en/getting-started/2-installing-and-first-run.md +0 -2
  166. package/docs/guide/en/getting-started/3-adding-your-first-project.md +0 -4
  167. package/docs/guide/en/getting-started/4-the-dashboard-tour.md +0 -5
  168. package/docs/guide/en/insights/3-code-explorer.md +0 -5
  169. package/docs/guide/en/integrations/1-ai-providers.md +10 -99
  170. package/docs/guide/en/integrations/2-plugins.md +0 -8
  171. package/docs/guide/en/integrations/5-mcp-server.md +1 -1
  172. package/docs/guide/en/integrations/6-agent-chat.md +0 -17
  173. package/docs/guide/en/integrations/7-local-engines.md +0 -4
  174. package/docs/guide/en/pipeline/1-rails-and-jobs.md +2 -24
  175. package/docs/guide/en/pipeline/2-the-job-detail-view.md +0 -3
  176. package/docs/guide/en/pipeline/4-picking-an-engine-per-rail.md +10 -65
  177. package/docs/guide/en/pipeline/5-the-loop-builder.md +0 -7
  178. package/docs/guide/en/specs/2-add-spec-quick-mode.md +0 -11
  179. package/docs/guide/es/agents/2-profiles-and-the-balanced-default.md +0 -8
  180. package/docs/guide/es/agents/4-custom-agents-catalog.md +0 -15
  181. package/docs/guide/es/getting-started/1-what-is-specrails.md +0 -2
  182. package/docs/guide/es/getting-started/2-installing-and-first-run.md +0 -2
  183. package/docs/guide/es/getting-started/3-adding-your-first-project.md +0 -4
  184. package/docs/guide/es/getting-started/4-the-dashboard-tour.md +0 -5
  185. package/docs/guide/es/integrations/1-ai-providers.md +10 -75
  186. package/docs/guide/es/integrations/2-plugins.md +0 -8
  187. package/docs/guide/es/integrations/5-mcp-server.md +1 -1
  188. package/docs/guide/es/integrations/6-agent-chat.md +0 -11
  189. package/docs/guide/es/integrations/7-local-engines.md +0 -4
  190. package/docs/guide/es/pipeline/1-rails-and-jobs.md +3 -13
  191. package/docs/guide/es/pipeline/2-the-job-detail-view.md +0 -3
  192. package/docs/guide/es/pipeline/4-picking-an-engine-per-rail.md +10 -53
  193. package/docs/guide/es/pipeline/5-the-loop-builder.md +1 -6
  194. package/docs/guide/es/specs/2-add-spec-quick-mode.md +0 -7
  195. package/docs/guide/fr/agents/2-profiles-and-the-balanced-default.md +0 -8
  196. package/docs/guide/fr/agents/4-custom-agents-catalog.md +0 -15
  197. package/docs/guide/fr/getting-started/1-what-is-specrails.md +0 -2
  198. package/docs/guide/fr/getting-started/2-installing-and-first-run.md +0 -2
  199. package/docs/guide/fr/getting-started/3-adding-your-first-project.md +0 -4
  200. package/docs/guide/fr/getting-started/4-the-dashboard-tour.md +0 -5
  201. package/docs/guide/fr/integrations/1-ai-providers.md +2 -50
  202. package/docs/guide/fr/integrations/2-plugins.md +0 -8
  203. package/docs/guide/fr/integrations/5-mcp-server.md +1 -14
  204. package/docs/guide/fr/integrations/6-agent-chat.md +0 -11
  205. package/docs/guide/fr/integrations/7-local-engines.md +0 -4
  206. package/docs/guide/fr/pipeline/1-rails-and-jobs.md +0 -13
  207. package/docs/guide/fr/pipeline/2-the-job-detail-view.md +0 -3
  208. package/docs/guide/fr/pipeline/4-picking-an-engine-per-rail.md +0 -22
  209. package/docs/guide/fr/pipeline/5-the-loop-builder.md +0 -6
  210. package/docs/guide/fr/specs/2-add-spec-quick-mode.md +0 -7
  211. package/docs/guide/it/agents/2-profiles-and-the-balanced-default.md +0 -8
  212. package/docs/guide/it/agents/4-custom-agents-catalog.md +0 -15
  213. package/docs/guide/it/getting-started/1-what-is-specrails.md +0 -2
  214. package/docs/guide/it/getting-started/2-installing-and-first-run.md +0 -2
  215. package/docs/guide/it/getting-started/3-adding-your-first-project.md +0 -4
  216. package/docs/guide/it/getting-started/4-the-dashboard-tour.md +0 -5
  217. package/docs/guide/it/integrations/1-ai-providers.md +2 -48
  218. package/docs/guide/it/integrations/2-plugins.md +0 -8
  219. package/docs/guide/it/integrations/5-mcp-server.md +1 -14
  220. package/docs/guide/it/integrations/6-agent-chat.md +0 -11
  221. package/docs/guide/it/integrations/7-local-engines.md +0 -4
  222. package/docs/guide/it/pipeline/1-rails-and-jobs.md +2 -13
  223. package/docs/guide/it/pipeline/2-the-job-detail-view.md +0 -3
  224. package/docs/guide/it/pipeline/4-picking-an-engine-per-rail.md +0 -20
  225. package/docs/guide/it/pipeline/5-the-loop-builder.md +1 -6
  226. package/docs/guide/it/specs/2-add-spec-quick-mode.md +0 -7
  227. package/docs/guide/ja/agents/2-profiles-and-the-balanced-default.md +0 -9
  228. package/docs/guide/ja/agents/4-custom-agents-catalog.md +0 -15
  229. package/docs/guide/ja/getting-started/1-what-is-specrails.md +0 -2
  230. package/docs/guide/ja/getting-started/2-installing-and-first-run.md +0 -2
  231. package/docs/guide/ja/getting-started/3-adding-your-first-project.md +0 -4
  232. package/docs/guide/ja/getting-started/4-the-dashboard-tour.md +0 -5
  233. package/docs/guide/ja/integrations/1-ai-providers.md +1 -69
  234. package/docs/guide/ja/integrations/2-plugins.md +0 -8
  235. package/docs/guide/ja/integrations/5-mcp-server.md +1 -14
  236. package/docs/guide/ja/integrations/6-agent-chat.md +0 -10
  237. package/docs/guide/ja/integrations/7-local-engines.md +0 -4
  238. package/docs/guide/ja/pipeline/1-rails-and-jobs.md +0 -13
  239. package/docs/guide/ja/pipeline/2-the-job-detail-view.md +0 -3
  240. package/docs/guide/ja/pipeline/4-picking-an-engine-per-rail.md +0 -21
  241. package/docs/guide/ja/pipeline/5-the-loop-builder.md +0 -6
  242. package/docs/guide/ja/specs/2-add-spec-quick-mode.md +0 -7
  243. package/docs/guide/pt/agents/2-profiles-and-the-balanced-default.md +0 -8
  244. package/docs/guide/pt/agents/4-custom-agents-catalog.md +0 -15
  245. package/docs/guide/pt/getting-started/1-what-is-specrails.md +0 -2
  246. package/docs/guide/pt/getting-started/2-installing-and-first-run.md +0 -2
  247. package/docs/guide/pt/getting-started/3-adding-your-first-project.md +0 -4
  248. package/docs/guide/pt/getting-started/4-the-dashboard-tour.md +0 -5
  249. package/docs/guide/pt/integrations/1-ai-providers.md +2 -48
  250. package/docs/guide/pt/integrations/2-plugins.md +0 -8
  251. package/docs/guide/pt/integrations/5-mcp-server.md +1 -14
  252. package/docs/guide/pt/integrations/6-agent-chat.md +0 -10
  253. package/docs/guide/pt/integrations/7-local-engines.md +0 -4
  254. package/docs/guide/pt/pipeline/1-rails-and-jobs.md +0 -13
  255. package/docs/guide/pt/pipeline/2-the-job-detail-view.md +0 -2
  256. package/docs/guide/pt/pipeline/4-picking-an-engine-per-rail.md +0 -21
  257. package/docs/guide/pt/pipeline/5-the-loop-builder.md +0 -6
  258. package/docs/guide/pt/specs/2-add-spec-quick-mode.md +0 -7
  259. package/docs/guide/zh/agents/2-profiles-and-the-balanced-default.md +0 -7
  260. package/docs/guide/zh/agents/4-custom-agents-catalog.md +0 -15
  261. package/docs/guide/zh/getting-started/1-what-is-specrails.md +0 -2
  262. package/docs/guide/zh/getting-started/2-installing-and-first-run.md +0 -2
  263. package/docs/guide/zh/getting-started/3-adding-your-first-project.md +0 -4
  264. package/docs/guide/zh/getting-started/4-the-dashboard-tour.md +0 -5
  265. package/docs/guide/zh/integrations/1-ai-providers.md +1 -67
  266. package/docs/guide/zh/integrations/2-plugins.md +0 -8
  267. package/docs/guide/zh/integrations/5-mcp-server.md +1 -14
  268. package/docs/guide/zh/integrations/6-agent-chat.md +0 -9
  269. package/docs/guide/zh/integrations/7-local-engines.md +0 -4
  270. package/docs/guide/zh/pipeline/1-rails-and-jobs.md +0 -13
  271. package/docs/guide/zh/pipeline/2-the-job-detail-view.md +0 -2
  272. package/docs/guide/zh/pipeline/4-picking-an-engine-per-rail.md +0 -20
  273. package/docs/guide/zh/pipeline/5-the-loop-builder.md +0 -6
  274. package/docs/guide/zh/specs/2-add-spec-quick-mode.md +0 -7
  275. package/docs/internals/adding-a-provider.md +7 -0
  276. package/docs/internals/desktop-owned-workflows.md +121 -0
  277. package/docs/internals/operations-runbook.md +20 -0
  278. package/docs/internals/programmatic-agent-runtime.md +34 -28
  279. package/docs/internals/project-builder.md +34 -11
  280. package/docs/internals/source-map.md +51 -3
  281. package/docs/internals/spec-addenda.md +11 -5
  282. package/docs/local-providers.md +8 -13
  283. package/docs/mcp.md +1 -17
  284. package/docs/platforms/macos.md +0 -40
  285. package/docs/platforms/windows-parity.md +1 -1
  286. package/docs/platforms/windows.md +0 -8
  287. package/docs/running-pipelines.md +1 -46
  288. package/docs/subscription-usage.md +19 -0
  289. package/docs/terminal.md +0 -2
  290. package/docs/tracking-cost.md +2 -33
  291. package/package.json +1 -1
  292. package/server/dist/core-execution.js +24 -15
  293. package/server/dist/desktop-db.js +23 -10
  294. package/server/dist/desktop-router.js +8 -4
  295. package/server/dist/index.js +10 -5
  296. package/server/dist/mcp/guide.js +1 -0
  297. package/server/dist/mcp/tools/jobs.js +1 -1
  298. package/server/dist/mcp/tools/loops.js +2 -0
  299. package/server/dist/mcp/tools/meta.js +6 -7
  300. package/server/dist/mcp/tools/projects.js +31 -6
  301. package/server/dist/mcp/tools/rails.js +6 -3
  302. package/server/dist/mcp/tools/setup.js +10 -0
  303. package/server/dist/mcp/tools/specs.js +1 -1
  304. package/server/dist/modules/accounting/runtime/pricing.js +6 -1
  305. package/server/dist/modules/agent-runtime/runtime/agent-runtime-bridge.js +55 -12
  306. package/server/dist/modules/agent-runtime/runtime/agent-runtime-metrics.js +1 -1
  307. package/server/dist/modules/agent-runtime/runtime/agent-runtime-settings-router.js +1 -1
  308. package/server/dist/modules/agent-runtime/runtime/agent-runtime-settings.js +42 -0
  309. package/server/dist/modules/agent-runtime/runtime/agent-runtime-verification-suggestions.js +10 -4
  310. package/server/dist/modules/builder/runtime/milestone-chain.js +76 -10
  311. package/server/dist/modules/delivery/runtime/delivery-evidence.js +3 -2
  312. package/server/dist/modules/delivery/runtime/multi-repo-execution.js +3 -0
  313. package/server/dist/modules/delivery/runtime/rail-isolated-launch.js +1 -0
  314. package/server/dist/modules/delivery/runtime/rail-launch-parser.js +8 -0
  315. package/server/dist/modules/delivery/runtime/rails-router.js +48 -26
  316. package/server/dist/modules/delivery/runtime/review-packet.js +1 -1
  317. package/server/dist/modules/loops/runtime/loop-agent-defaults.json +6 -0
  318. package/server/dist/modules/loops/runtime/loop-agents.js +21 -0
  319. package/server/dist/modules/loops/runtime/loop-command-catalog.js +1 -1
  320. package/server/dist/modules/loops/runtime/loop-core-factory.js +19 -3
  321. package/server/dist/modules/loops/runtime/loop-definition.js +79 -12
  322. package/server/dist/modules/loops/runtime/loop-effect.js +1 -1
  323. package/server/dist/modules/loops/runtime/loop-executors.js +24 -9
  324. package/server/dist/modules/loops/runtime/loop-factory.js +1 -1
  325. package/server/dist/modules/loops/runtime/loop-graph.js +5 -14
  326. package/server/dist/modules/loops/runtime/loop-implement-recipe.js +49 -0
  327. package/server/dist/modules/loops/runtime/loop-run-manager.js +3 -3
  328. package/server/dist/modules/loops/runtime/loop-templates.js +29 -5
  329. package/server/dist/modules/loops/runtime/loops-router.js +17 -0
  330. package/server/dist/modules/missions/runtime/agent-context-resolver.js +1 -1
  331. package/server/dist/modules/missions/runtime/agent-operator-prompt.js +17 -8
  332. package/server/dist/modules/specs/runtime/spec-models.js +4 -7
  333. package/server/dist/modules/subscription-usage/adapters/claude.js +113 -0
  334. package/server/dist/modules/subscription-usage/adapters/codex.js +164 -0
  335. package/server/dist/modules/subscription-usage/adapters/errors.js +15 -0
  336. package/server/dist/modules/subscription-usage/adapters/http.js +20 -0
  337. package/server/dist/modules/subscription-usage/adapters/local-auth.js +61 -0
  338. package/server/dist/modules/subscription-usage/application.js +11 -0
  339. package/server/dist/modules/subscription-usage/domain.js +70 -0
  340. package/server/dist/modules/subscription-usage/index.js +2 -0
  341. package/server/dist/modules/subscription-usage/ports.js +2 -0
  342. package/server/dist/modules/subscription-usage/runtime/composition.js +20 -0
  343. package/server/dist/modules/subscription-usage/runtime/usage-service.js +111 -0
  344. package/server/dist/project-registry.js +16 -1
  345. package/server/dist/project-repositories.js +51 -2
  346. package/server/dist/project-router-helpers.js +1 -1
  347. package/server/dist/project-router-loop-runs.js +4 -1
  348. package/server/dist/providers/claude-adapter.js +6 -4
  349. package/server/dist/providers/codex-adapter.js +4 -5
  350. package/server/dist/schemas/workflow-definition.schema.json +2 -0
  351. package/client/dist/assets/AgentBrowserCapture-PI-cxyxi.js +0 -1
  352. package/client/dist/assets/AgentModeAnalyticsPane-mQsOqdXx.js +0 -2
  353. package/client/dist/assets/AgentModeCodePane-BL-vpmlY.js +0 -2
  354. package/client/dist/assets/CodePage-Alitorrv.js +0 -3
  355. package/client/dist/assets/DocsDialog-DwL6rgcT.js +0 -11
  356. package/client/dist/assets/DocsPage-BA1hiJZ-.js +0 -11
  357. package/client/dist/assets/InteractiveJobComposer-B3H1YHr6.js +0 -19
  358. package/client/dist/assets/JobDetailModal-XZQ9eAAy.js +0 -1
  359. package/client/dist/assets/JobDetailPage-B-SIaq_q.js +0 -1
  360. package/client/dist/assets/JobsPage-iQXAv2mx.js +0 -1
  361. package/client/dist/assets/LoopBuilderPage-B_gbFAHn.js +0 -1
  362. package/client/dist/assets/LoopsPage-BZwcY5cu.js +0 -2
  363. package/client/dist/assets/MinimizedChatsContext-CNx_0bZu.js +0 -1
  364. package/client/dist/assets/PluginsPage-DFocnoHX.js +0 -2
  365. package/client/dist/assets/ProjectSettingsDialog-Bh3S6EBB.js +0 -1
  366. package/client/dist/assets/RepositoryScopeSelector-C2mvWBQ6.js +0 -1
  367. package/client/dist/assets/ReviewPacketPage-CrAf-WDy.js +0 -4
  368. package/client/dist/assets/TemplatePreviewModal-C7DordpN.js +0 -1
  369. package/client/dist/assets/TicketDetailModalContext-B-DySpKL.js +0 -16
  370. package/client/dist/assets/agent-BaG10AN6.js +0 -1
  371. package/client/dist/assets/agent-CH4aJ884.js +0 -1
  372. package/client/dist/assets/agent-CLc9fA7v.js +0 -1
  373. package/client/dist/assets/agent-DHh9-AdL.js +0 -1
  374. package/client/dist/assets/agent-DkKnd6Wv.js +0 -1
  375. package/client/dist/assets/agent-aiVhqFW1.js +0 -1
  376. package/client/dist/assets/agent-dbznD3eO.js +0 -1
  377. package/client/dist/assets/agent-sMVFcPwL.js +0 -1
  378. package/client/dist/assets/agentRuntime-BeUb3g9g.js +0 -1
  379. package/client/dist/assets/file-text-CFT-xXKN.js +0 -1
  380. package/client/dist/assets/index-BS7KCWkV.css +0 -2
  381. package/client/dist/assets/index-C4Mv6tV6.js +0 -80
  382. package/client/dist/assets/integrations-CH0zy1Mw.js +0 -1
  383. package/client/dist/assets/integrations-CIAjhd7L.js +0 -1
  384. package/client/dist/assets/integrations-CZdwOe8A.js +0 -1
  385. package/client/dist/assets/integrations-Ct7omMld.js +0 -1
  386. package/client/dist/assets/integrations-D6MSxy3-.js +0 -1
  387. package/client/dist/assets/integrations-DbZa-Zyc.js +0 -1
  388. package/client/dist/assets/integrations-b8pH-vfl.js +0 -1
  389. package/client/dist/assets/integrations-mB-7zoyT.js +0 -1
  390. package/client/dist/assets/jira-api-BYbEPlkr.js +0 -1
  391. package/client/dist/assets/layers-eDMjG41I.js +0 -1
  392. package/client/dist/assets/loops-B4_D-nbg.js +0 -1
  393. package/client/dist/assets/nav-B-tpjyc2.js +0 -1
  394. package/client/dist/assets/nav-BJNXGg29.js +0 -1
  395. package/client/dist/assets/nav-BdhtRLP7.js +0 -1
  396. package/client/dist/assets/nav-ByYgyN5T.js +0 -1
  397. package/client/dist/assets/nav-DA0Mva2c.js +0 -1
  398. package/client/dist/assets/nav-DcJm_Jg_.js +0 -1
  399. package/client/dist/assets/nav-DhMPtAXc.js +0 -1
  400. package/client/dist/assets/nav-DhPb-Isc.js +0 -1
  401. package/client/dist/assets/provider-capabilities-Vi1ZGvAK.js +0 -1
  402. package/client/dist/assets/save-B6wGWwBR.js +0 -1
  403. package/client/dist/assets/settings-3o4Ntzi5.js +0 -1
  404. package/client/dist/assets/settings-B97jlkwD.js +0 -1
  405. package/client/dist/assets/settings-BTaT_bsS.js +0 -1
  406. package/client/dist/assets/settings-CT8w7WiC.js +0 -1
  407. package/client/dist/assets/settings-Cvr8_tvh.js +0 -1
  408. package/client/dist/assets/settings-Dn1qgAC8.js +0 -1
  409. package/client/dist/assets/settings-EShDdg4J.js +0 -1
  410. package/client/dist/assets/settings-tfLhQZRN.js +0 -1
  411. package/client/dist/assets/setup-CBMu3tqH.js +0 -1
  412. package/client/dist/assets/setup-CPh-e6J2.js +0 -1
  413. package/client/dist/assets/setup-CRKLp_ke.js +0 -1
  414. package/client/dist/assets/setup-Cy302v4J.js +0 -1
  415. package/client/dist/assets/setup-D681vTPf.js +0 -1
  416. package/client/dist/assets/setup-DYK0VBkE.js +0 -1
  417. package/client/dist/assets/setup-KAuiIiVs.js +0 -1
  418. package/client/dist/assets/setup-OGX-oCfI.js +0 -1
  419. package/client/dist/assets/useSharedWebSocket-BZMrX4jr.js +0 -2
  420. package/client/dist/assets/wrench-BfmNO9Vw.js +0 -1
  421. package/docs/agy-cli-provider-study.md +0 -257
  422. package/docs/gemini-cli-provider-study.md +0 -301
  423. package/docs/gemini-core-support-evaluation.md +0 -160
  424. package/docs/gemini.md +0 -257
  425. package/docs/kimi.md +0 -291
@@ -25,17 +25,42 @@ function projectsTools() {
25
25
  name: 'specrails_projects',
26
26
  title: 'Projects',
27
27
  description: 'List and inspect registered Specrails projects, resolve a project by filesystem path, or unregister one. ' +
28
- 'Actions: list, get, resolve (by path), unregister (destructive — removes the project and its workspace).',
28
+ 'Actions: list, get, resolve (by project/repository/workspace path), repositories (inventory), repository_add / repository_update (write, configure registered code workspacePaths), repository_remove (destructive), unregister (destructive — removes the project and its workspace).',
29
29
  hintTier: 'read',
30
- tier: (args) => (args.action === 'unregister' ? 'destructive' : 'read'),
30
+ tier: (args) => ['unregister', 'repository_remove'].includes(String(args.action)) ? 'destructive' : ['repository_add', 'repository_update'].includes(String(args.action)) ? 'write' : 'read',
31
31
  inputSchema: {
32
- action: zod_1.z.enum(['list', 'get', 'resolve', 'unregister']).describe('Operation to perform'),
33
- projectId: zod_1.z.string().optional().describe('Project id (for get / unregister)'),
34
- path: zod_1.z.string().optional().describe('Filesystem path (for resolve)'),
32
+ action: zod_1.z.enum(['list', 'get', 'resolve', 'repositories', 'repository_add', 'repository_update', 'repository_remove', 'unregister']).describe('Operation to perform'),
33
+ projectId: zod_1.z.string().optional().describe('Project id; defaults to the active project for get and repository operations; required for unregister'),
34
+ path: zod_1.z.string().optional().describe('Filesystem path for resolve or repository_add/update; repository root retains its Git/delivery identity'),
35
+ repositoryId: zod_1.z.string().min(1).optional().describe('Required for repository_update/remove; discover from repositories/get'),
36
+ name: zod_1.z.string().min(1).optional().describe('Repository display name'),
37
+ integrationBranch: zod_1.z.string().nullable().optional().describe('Repository integration branch; null clears it'),
38
+ workspacePaths: zod_1.z.array(zod_1.z.string().min(1)).min(1).max(50).nullable().optional().describe('repository_add/update: code directories inside the repository, absolute or root-relative. null resets to repository root. Independent Git roots need separate memberships. Changes are rejected while active runs depend on this scope.'),
35
39
  },
36
40
  handler: (ctx, args) => {
37
41
  const action = args.action;
38
42
  switch (action) {
43
+ case 'repositories':
44
+ return (0, types_1.apiCall)(ctx, 'GET', `${(0, types_1.projectPath)(ctx, args.projectId)}/repositories`);
45
+ case 'repository_add':
46
+ case 'repository_update':
47
+ case 'repository_remove': {
48
+ const base = `${(0, types_1.projectPath)(ctx, args.projectId)}/repositories`;
49
+ const id = args.repositoryId;
50
+ if (action !== 'repository_add' && !id)
51
+ throw new Error(`${action} requires repositoryId.`);
52
+ if (action === 'repository_remove')
53
+ return (0, types_1.apiCall)(ctx, 'DELETE', `${base}/${encodeURIComponent(id)}`);
54
+ if (action === 'repository_add' && !args.path)
55
+ throw new Error('repository_add requires path.');
56
+ const body = {};
57
+ for (const key of ['path', 'name', 'integrationBranch', 'workspacePaths'])
58
+ if (args[key] !== undefined)
59
+ body[key] = args[key];
60
+ if (!Object.keys(body).length)
61
+ throw new Error('repository_update requires at least one field to update.');
62
+ return (0, types_1.apiCall)(ctx, action === 'repository_add' ? 'POST' : 'PATCH', action === 'repository_add' ? base : `${base}/${encodeURIComponent(id)}`, body);
63
+ }
39
64
  case 'list':
40
65
  return ctx.registry.listProjects().map((p) => ({ ...serializeProject(p), available: !!ctx.registry.getContext(p.id) }));
41
66
  case 'get': {
@@ -53,7 +78,7 @@ function projectsTools() {
53
78
  throw new Error('resolve requires a "path".');
54
79
  const selected = args.projectId;
55
80
  const key = (0, project_repositories_1.repositoryPathKey)((0, project_repositories_1.canonicalRepositoryPath)(p));
56
- const matches = ctx.registry.listProjects().filter(row => !selected || row.id === selected).flatMap(project => (0, project_repositories_1.getProjectRepositories)(project).filter(repository => (0, project_repositories_1.repositoryPathKey)((0, project_repositories_1.canonicalRepositoryPath)(repository.path)) === key)
81
+ const matches = ctx.registry.listProjects().filter(row => !selected || row.id === selected).flatMap(project => (0, project_repositories_1.getProjectRepositories)(project).filter(repository => [repository.path, ...(repository.workspacePaths ?? (repository.workspacePath ? [repository.workspacePath] : []))].some(root => (0, project_repositories_1.repositoryPathKey)((0, project_repositories_1.canonicalRepositoryPath)(root)) === key))
57
82
  .map(repository => ({ project, repository })));
58
83
  if (matches.length > 1)
59
84
  return { resolved: false, ambiguous: true, matches: matches.map(({ project, repository }) => ({ projectId: project.id, projectName: project.name, repositoryId: repository.id, repositoryName: repository.name })) };
@@ -49,7 +49,7 @@ function railsTools() {
49
49
  'When launched from the in-app agent chat without an explicit aiEngine, the engine defaults to your conversation\'s provider. On that same provider, an omitted model inherits the conversation model; an explicit model wins (pass aiEngine to override; launch_all always uses each rail\'s stored engine). ' +
50
50
  'For Implement, the selected launch provider applies to ALL runtime roles (architect, developer, reviewer); an explicit launch model/effort also applies to each role. Resuming an existing execution keeps its original frozen role configuration; start a new launch to change providers. ' +
51
51
  'User-facing naming: call the free-form autonomous mode "Freestyle"; use "freestyle" as the canonical API enum value for that same capability. ' +
52
- 'For small OpenSpec-governed work, recommend "SDD Quick (OpenSpec)" and launch with mode "loop" plus loopId "factory:sdd-quick-openspec"; keep Freestyle for ticket-local implementation-only work. ' +
52
+ 'Always honor an explicit workflow selection, including Implement for addenda or delivery changes. Without a preference, for small OpenSpec-governed work, recommend "SDD Quick (OpenSpec)" and launch with mode "loop" plus loopId "factory:sdd-quick-openspec"; keep Freestyle for ticket-local implementation-only work. ' +
53
53
  'NAMING: railIndex is the 0-BASED internal identity; the dashboard shows rails 1-based ("Rail N" = railIndex N-1). When talking to the user, ALWAYS say "Rail <railIndex + 1>" (or the rail\'s custom name) — results include railLabel with the correct user-facing label.',
54
54
  hintTier: 'read',
55
55
  tier: (a) => {
@@ -68,6 +68,7 @@ function railsTools() {
68
68
  .describe('Operation to perform'),
69
69
  projectId: zod_1.z.string().optional().describe('Project id (defaults to the active project)'),
70
70
  repositoryIds: zod_1.z.array(zod_1.z.string().min(1)).min(1).max(50).optional().describe('launch only: explicit affected repository memberships for the coordinated run; must include every repository required by the assigned specs. Omission uses the assigned specs\' repository selections.'),
71
+ workspaceSelection: zod_1.z.record(zod_1.z.string(), zod_1.z.array(zod_1.z.string().min(1)).min(1)).optional().describe('launch only: map selected repository IDs to registered code workspace paths; discover workspacePaths through project context. Omission verifies all configured code workspaces. Never use the parent project path unless it is itself a registered code workspace.'),
71
72
  repositoryId: zod_1.z.string().min(1).optional().describe('review_packet only: member of a grouped delivery to inspect; omission returns the public parent with grouped outcomes.'),
72
73
  railIndex: zod_1.z
73
74
  .number()
@@ -129,11 +130,11 @@ function railsTools() {
129
130
  revisionOfDeliveryId: zod_1.z
130
131
  .string()
131
132
  .optional()
132
- .describe('Revise a delivery that is ALREADY awaiting the user\'s decision (launch). Continues with Quick SDD (the Revision loop is retired): pass its prDeliveryId (from rails.prDeliveries) together with revisionNote. Use it whenever the user asks for a change to work you already delivered — never publish/discard/merge first. The rail must still carry exactly that delivery\'s specs.'),
133
+ .describe('Revise a delivery that is ALREADY awaiting the user\'s decision (launch). Continues with the explicitly selected loop, defaulting to Quick SDD (the Revision loop is retired): pass its prDeliveryId (from rails.prDeliveries) together with revisionNote. Use it whenever the user asks for a change to work you already delivered — never publish/discard/merge first. The rail must still carry exactly that delivery\'s specs.'),
133
134
  revisionNote: zod_1.z
134
135
  .string()
135
136
  .optional()
136
- .describe('What to change, in the user\'s own words (required with revisionOfDeliveryId). It is injected into every Quick SDD phase and shown on the updated review packet as "what you asked to change".'),
137
+ .describe('What to change, in the user\'s own words (required with revisionOfDeliveryId). It is injected into every selected workflow phase and shown on the updated review packet as "what you asked to change".'),
137
138
  followUp: zod_1.z
138
139
  .object({
139
140
  comments: zod_1.z.array(zod_1.z.object({
@@ -257,6 +258,8 @@ function railsTools() {
257
258
  body.targetPrNumber = args.targetPrNumber;
258
259
  if (args.repositoryIds !== undefined)
259
260
  body.repositoryIds = args.repositoryIds;
261
+ if (args.workspaceSelection !== undefined)
262
+ body.workspaceSelection = args.workspaceSelection;
260
263
  if (args.baseBranch !== undefined)
261
264
  body.baseBranch = args.baseBranch;
262
265
  // Revision of a delivery already awaiting the user's decision: the
@@ -88,6 +88,8 @@ function setupTools() {
88
88
  .array(zod_1.z.string())
89
89
  .optional()
90
90
  .describe('add_project: providers to install (first = primary/default); de-duped server-side'),
91
+ workspacePaths: zod_1.z.array(zod_1.z.string().min(1)).min(1).max(50).optional().describe('add_project: code workspace directories inside the primary project root'),
92
+ repositories: zod_1.z.array(zod_1.z.object({ path: zod_1.z.string().min(1), name: zod_1.z.string().optional(), workspacePaths: zod_1.z.array(zod_1.z.string().min(1)).min(1).max(50).optional(), integrationBranch: zod_1.z.string().nullable().optional() })).max(50).optional().describe('add_project: additional repository memberships with their code workspaces'),
91
93
  // install_config
92
94
  config: zod_1.z
93
95
  .record(zod_1.z.unknown())
@@ -115,6 +117,10 @@ function setupTools() {
115
117
  body.provider = args.provider;
116
118
  if (Array.isArray(args.providers))
117
119
  body.providers = args.providers;
120
+ if (args.workspacePaths !== undefined)
121
+ body.workspacePaths = args.workspacePaths;
122
+ if (args.repositories !== undefined)
123
+ body.repositories = args.repositories;
118
124
  return (0, types_1.apiCall)(ctx, 'POST', '/projects', body);
119
125
  }
120
126
  // ─── Per-project ────────────────────────────────────────────────────
@@ -137,6 +143,10 @@ function setupTools() {
137
143
  const body = {};
138
144
  if (Array.isArray(args.providers))
139
145
  body.providers = args.providers;
146
+ if (args.workspacePaths !== undefined)
147
+ body.workspacePaths = args.workspacePaths;
148
+ if (args.repositories !== undefined)
149
+ body.repositories = args.repositories;
140
150
  else if (typeof args.provider === 'string')
141
151
  body.provider = args.provider;
142
152
  else
@@ -313,7 +313,7 @@ function specsTools() {
313
313
  });
314
314
  return {
315
315
  ...r,
316
- hint: 'The addendum is open: launch with factory:sdd-quick-openspec. Rail launches route open addenda to Quick SDD, continue a same-spec pending delivery, and brief every AI step with the full delta and a fresh OpenSpec target. Do not use Revision or edit openspecChangeName. Do not edit the spec description to repeat it.',
316
+ hint: 'The addendum is open: respect the user\'s explicit loop selection, including factory:implement; otherwise recommend factory:sdd-quick-openspec. Rail launches continue a same-spec pending delivery, and brief every AI step with the full delta and a fresh OpenSpec target. Do not use Revision or edit openspecChangeName. Do not edit the spec description to repeat it.',
317
317
  };
318
318
  }
319
319
  case 'update_addendum': {
@@ -76,7 +76,10 @@ exports.PRICING = {
76
76
  // Opus 5.5: https://platform.claude.com/docs/en/models/opus-5-5/overview
77
77
  'claude:opus': { inputPer1M: 4.00, outputPer1M: 20.00, cacheReadPer1M: 0.20, cacheWritePer1M: 5.00, inputIncludesCacheReads: false, lastReviewedAt: '2026-09-24' },
78
78
  'claude:claude-opus-5-5': { inputPer1M: 4.00, outputPer1M: 20.00, cacheReadPer1M: 0.20, cacheWritePer1M: 5.00, inputIncludesCacheReads: false, lastReviewedAt: '2026-09-24' },
79
- 'claude:sonnet': { inputPer1M: 3.00, outputPer1M: 15.00, cacheReadPer1M: 0.30, cacheWritePer1M: 3.75, inputIncludesCacheReads: false, lastReviewedAt: '2026-07-02' },
79
+ 'claude:claude-sonnet-5': { inputPer1M: 3.00, outputPer1M: 15.00, cacheReadPer1M: 0.30, cacheWritePer1M: 3.75, inputIncludesCacheReads: false, lastReviewedAt: '2026-07-02' },
80
+ // Sonnet 5.5: https://platform.claude.com/docs/en/models/sonnet-5-5/overview
81
+ 'claude:sonnet': { inputPer1M: 2.00, outputPer1M: 10.00, cacheReadPer1M: 0.20, cacheWritePer1M: 2.50, inputIncludesCacheReads: false, lastReviewedAt: '2026-09-30' },
82
+ 'claude:claude-sonnet-5-5': { inputPer1M: 2.00, outputPer1M: 10.00, cacheReadPer1M: 0.20, cacheWritePer1M: 2.50, inputIncludesCacheReads: false, lastReviewedAt: '2026-09-30' },
80
83
  'claude:haiku': { inputPer1M: 1.00, outputPer1M: 5.00, cacheReadPer1M: 0.10, cacheWritePer1M: 1.25, inputIncludesCacheReads: false, lastReviewedAt: '2026-07-02' },
81
84
  'claude:fable': { inputPer1M: 10.00, outputPer1M: 50.00, cacheReadPer1M: 1.00, cacheWritePer1M: 12.50, inputIncludesCacheReads: false, lastReviewedAt: '2026-07-02' },
82
85
  };
@@ -96,6 +99,8 @@ function resolvePriceEntry(providerId, model) {
96
99
  // Keep the existing estimates for older concrete Opus generations.
97
100
  if (/^claude-opus-(?:4(?:-|$)|5$)/.test(model))
98
101
  return exports.PRICING['claude:claude-opus-5'];
102
+ if (/^claude-sonnet-(?:4(?:-|$)|5$)/.test(model))
103
+ return exports.PRICING['claude:claude-sonnet-5'];
99
104
  const family = CLAUDE_MODEL_FAMILY.exec(model);
100
105
  if (family)
101
106
  return exports.PRICING[`claude:${family[1]}`];
@@ -32,13 +32,39 @@ function runtimeChangeName(runId) {
32
32
  * cwd is relative to the registered directory; one already inside the scope is kept.
33
33
  */
34
34
  function scopedHostChecks(checks, repositories = []) {
35
- return checks.map(check => {
36
- const scope = repositories.find(repository => repository.id === check.repositoryId)?.scope?.[0];
37
- if (!scope || (check.cwd !== undefined && (0, node_path_1.isAbsolute)(check.cwd)))
38
- return check;
39
- const cwd = (check.cwd ?? '.').replace(/\\/g, '/');
40
- const inside = cwd === scope || cwd.startsWith(scope + '/');
41
- return { ...check, cwd: inside ? cwd : node_path_1.posix.normalize(node_path_1.posix.join(scope, cwd)) };
35
+ return checks.flatMap(check => {
36
+ const repository = repositories.find(repository => repository.id === check.repositoryId);
37
+ const scopes = repository?.scope;
38
+ if (!repository || !scopes?.length)
39
+ return [check];
40
+ if (check.cwd === undefined)
41
+ return scopes.map((scope, index) => ({ ...check, cwd: scope,
42
+ ...(check.key && scopes.length > 1 ? { key: check.key.slice(0, 90) + '-workspace-' + index } : {}),
43
+ }));
44
+ const declared = repository.registeredScope;
45
+ const inputCwd = check.cwd.replace(/\\/g, '/');
46
+ const rawCwd = (0, node_path_1.isAbsolute)(inputCwd) && repository.path ? (0, node_path_1.relative)(repository.path, inputCwd).split(node_path_1.sep).join('/') : inputCwd;
47
+ if (!(0, node_path_1.isAbsolute)(rawCwd) && declared?.some(root => rawCwd === root || rawCwd.startsWith(root + '/')) && !scopes.some(root => rawCwd === root || rawCwd.startsWith(root + '/')))
48
+ return [];
49
+ let cwd = check.cwd.replace(/\\/g, '/');
50
+ if ((0, node_path_1.isAbsolute)(cwd)) {
51
+ if (!repository.path)
52
+ throw new Error('Verification workspace root is unavailable');
53
+ cwd = (0, node_path_1.relative)(repository.path, cwd).split(node_path_1.sep).join('/');
54
+ }
55
+ else {
56
+ const normalized = node_path_1.posix.normalize(cwd);
57
+ if (scopes.some(scope => cwd === scope || cwd.startsWith(scope + '/')))
58
+ cwd = normalized;
59
+ else if (scopes.length === 1)
60
+ cwd = node_path_1.posix.join(scopes[0], normalized);
61
+ else
62
+ throw new Error('Verification must select an explicit workspace cwd when several scopes are configured');
63
+ }
64
+ cwd = node_path_1.posix.normalize(cwd);
65
+ if (!scopes.some(root => cwd === root || cwd.startsWith(root + '/')))
66
+ throw new Error(`Verification cwd ${JSON.stringify(check.cwd)} escapes the selected code workspace ${scopes.join(', ')}`);
67
+ return [{ ...check, cwd }];
42
68
  });
43
69
  }
44
70
  exports.RUNTIME_HOST_ENV_KEYS = [
@@ -80,7 +106,7 @@ function readFrozenRuntimeHost(contextPath, baseEnv, runId) {
80
106
  * rail's cancellation and worktree ownership remain in Desktop. */
81
107
  async function runAgentRuntimeInvocation(options) {
82
108
  const definitionEngine = options.engineVersion === 2 || options.definitionPath !== undefined || options.prepareDefinition !== undefined;
83
- if (options.resume && (options.definitionPath || options.prepareDefinition || options.workflowRoleBindings))
109
+ if (options.resume && (options.definitionPath || options.prepareDefinition || options.workflowRoleBindings || options.loopConfig))
84
110
  throw new Error('Resume must use the frozen workflow definition and role bindings');
85
111
  const selectedCli = options.resume ? (0, agent_runtime_package_1.resolveRetainedAgentRuntime)(options.contextPath) : (0, agent_runtime_loader_1.findCoreAgentRuntimeCli)();
86
112
  let cli = selectedCli;
@@ -96,11 +122,12 @@ async function runAgentRuntimeInvocation(options) {
96
122
  if (!Array.isArray(admittedContext.repositories) || !admittedContext.repositories.length || admittedContext.repositories.some(repo => !repo || typeof repo.id !== 'string' || !repo.id))
97
123
  throw new Error('Core context is missing its repository scope');
98
124
  const source = (0, node_fs_1.existsSync)(options.configPath) ? 'project-role' : 'default';
99
- const { config, origins } = (0, agent_runtime_effective_config_1.resolveEffectiveRuntimeConfig)((0, agent_runtime_settings_1.loadRuntimeConfigFile)(options.configPath, options.defaultProvider), {
100
- repositoryIds: admittedContext.repositories.map(repo => repo.id), source, providerOverride: options.providerOverride,
125
+ const { config, origins } = (0, agent_runtime_effective_config_1.resolveEffectiveRuntimeConfig)(options.loopConfig ? (0, agent_runtime_settings_1.loadLoopRuntimeConfig)(options.configPath, options.loopConfig, options.providerOverride ?? { provider: options.defaultProvider ?? 'claude' }) : (0, agent_runtime_settings_1.loadRuntimeConfigFile)(options.configPath, options.defaultProvider), {
126
+ repositoryIds: admittedContext.repositories.map(repo => repo.id), source: options.loopConfig ? 'loop-role' : source, providerOverride: options.loopConfig ? undefined : options.providerOverride,
101
127
  });
102
128
  config.verification = scopedHostChecks(config.verification, admittedContext.repositories);
103
- config.rolePrompts = { ...(await (0, agent_runtime_loader_1.loadCoreAgentRuntime)()).rolePromptDefaults(), ...(0, agent_runtime_settings_1.loadRuntimeRolePrompts)(), ...config.rolePrompts };
129
+ if (!options.loopConfig)
130
+ config.rolePrompts = { ...(await (0, agent_runtime_loader_1.loadCoreAgentRuntime)()).rolePromptDefaults(), ...(0, agent_runtime_settings_1.loadRuntimeRolePrompts)(), ...config.rolePrompts };
104
131
  // Core rejects unknown connection keys: drop the desktop-only local-engine
105
132
  // fields (label/defaultModel/rates/supportsReasoningEffort) before Core sees it.
106
133
  const runtime = await (0, agent_runtime_loader_1.loadCoreAgentRuntime)();
@@ -120,8 +147,13 @@ async function runAgentRuntimeInvocation(options) {
120
147
  if (definitionEngine && !draft)
121
148
  throw new Error('A new Core workflow requires a definition');
122
149
  const selectedOrigins = (0, agent_runtime_effective_config_1.bindWorkflowRoleSelections)(config, draft, options.workflowRoleBindings);
150
+ if (options.loopConfig && draft?.roles?.includes('loop-decider') && !config.roles?.['loop-decider'])
151
+ throw new Error('The loop must define its loop-decider agent');
123
152
  const workflowOrigins = { ...(0, agent_runtime_effective_config_1.bindWorkflowRoleDefaults)(config, draft), ...selectedOrigins };
124
- const override = options.providerOverride;
153
+ if (options.loopConfig)
154
+ for (const role of Object.keys(config.roles ?? {}))
155
+ workflowOrigins[role] = 'loop-role';
156
+ const override = options.loopConfig ? undefined : options.providerOverride;
125
157
  runtime.validateRuntimeConfig(JSON.parse(JSON.stringify(config)));
126
158
  (0, agent_runtime_loader_1.validateRequestedRoleEfforts)(runtime, config);
127
159
  cli = (0, agent_runtime_package_1.retainAgentRuntime)(cli, options.contextPath);
@@ -169,6 +201,17 @@ async function runAgentRuntimeInvocation(options) {
169
201
  const definitionFile = (0, node_path_1.join)((0, node_path_1.dirname)(options.contextPath), 'desktop-workflow-definition.json');
170
202
  const requiresVerified = definitionEngine && JSON.parse((0, node_fs_1.readFileSync)(definitionFile, 'utf8')).delivery?.requiresVerified === true;
171
203
  const frozenPath = (0, node_path_1.join)((0, node_path_1.dirname)(options.contextPath), 'desktop-runtime-config.json');
204
+ if ((0, node_fs_1.existsSync)(frozenPath)) {
205
+ const checks = JSON.parse((0, node_fs_1.readFileSync)(frozenPath, 'utf8')).verification;
206
+ for (const check of checks) {
207
+ const repository = admittedContext.repositories?.find(repo => repo.id === check.repositoryId);
208
+ const cwd = repository?.path ? (0, node_path_1.resolve)(repository.path, check.cwd ?? repository.scope?.[0] ?? '.') : check.cwd;
209
+ try {
210
+ options.onLine?.(`[verification scope] ${JSON.stringify({ repositoryId: check.repositoryId, workspace: repository?.name, cwd, command: check.command, args: check.args })}\n`);
211
+ }
212
+ catch { /* Advisory log observer. */ }
213
+ }
214
+ }
172
215
  if ((0, node_fs_1.existsSync)(frozenPath)) {
173
216
  const frozen = JSON.parse((0, node_fs_1.readFileSync)(frozenPath, 'utf8'));
174
217
  for (const [role, assignment] of [...Object.entries(frozen.agents), ...(frozen.fixer ? [['fixer', frozen.fixer]] : [])]) {
@@ -121,7 +121,7 @@ function applyRuntimeSelectionOrigins(summary, selection, runId) {
121
121
  const record = object(selection), origins = object(record?.origins);
122
122
  if (!summary || record?.schemaVersion !== 1 || record.runId !== runId || !origins)
123
123
  return summary;
124
- if (!['architect', 'developer', 'reviewer'].every(role => ['project-role', 'default', ...(role === 'developer' ? ['explicit-launch-override'] : [])].includes(String(origins[role]))))
124
+ if (!['architect', 'developer', 'reviewer'].every(role => ['loop-role', 'project-role', 'default', ...(role === 'developer' ? ['explicit-launch-override'] : [])].includes(String(origins[role]))))
125
125
  return summary;
126
126
  return { ...summary, roles: summary.roles.map(role => {
127
127
  const origin = origins[role.role];
@@ -84,7 +84,7 @@ function registerAgentRuntimeSettingsRoutes({ router, ctx }) {
84
84
  router.get('/:projectId/agent-runtime/verification-suggestions', (req, res) => {
85
85
  try {
86
86
  const project = ctx(req).project;
87
- const repositories = (0, project_repositories_1.getProjectRepositories)(project).map((repository) => ({ id: repository.id, name: repository.name, path: repository.path }));
87
+ const repositories = (0, project_repositories_1.getProjectRepositories)(project).map((repository) => ({ id: repository.id, name: repository.name, path: repository.path, workspacePath: repository.workspacePath, workspacePaths: repository.workspacePaths }));
88
88
  res.json({ repositories: repositories.map(({ id, name }) => ({ id, name })), suggestions: (0, agent_runtime_verification_suggestions_1.suggestVerificationCommands)(repositories) });
89
89
  }
90
90
  catch {
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.AgentRuntimeConfigError = exports.REVIEW_THRESHOLD_FLOORS = exports.MAX_OUTPUT_TOKENS_MAX = exports.MAX_OUTPUT_TOKENS_MIN = exports.CONTEXT_WINDOW_TOKENS_MAX = exports.CONTEXT_WINDOW_TOKENS_MIN = exports.RUNTIME_ROLE_ID = void 0;
7
7
  exports.stripDesktopConnectionFields = stripDesktopConnectionFields;
8
8
  exports.coreConnectionFieldGates = coreConnectionFieldGates;
9
+ exports.validateLoopRuntimeSettings = validateLoopRuntimeSettings;
10
+ exports.loadLoopRuntimeConfig = loadLoopRuntimeConfig;
9
11
  exports.validateAgentRuntimeConfig = validateAgentRuntimeConfig;
10
12
  exports.defaultAgentRuntimeConfig = defaultAgentRuntimeConfig;
11
13
  exports.agentRuntimeConfigPath = agentRuntimeConfigPath;
@@ -121,6 +123,46 @@ exports.REVIEW_THRESHOLD_FLOORS = {
121
123
  minScore: 70,
122
124
  aspects: { type_correctness: 60, pattern_adherence: 60, test_coverage: 60, security: 75, architectural_alignment: 60 },
123
125
  };
126
+ /** Validate a portable recipe without accepting host-owned fields or project fallbacks. */
127
+ function validateLoopRuntimeSettings(input, providers = loadRuntimeProviders()) {
128
+ if (!input || typeof input !== 'object' || Array.isArray(input) || Object.keys(input).some(key => ![
129
+ 'schemaVersion', 'agents', 'fixer', 'roles', 'rolePrompts', 'limits', 'review', 'architect', 'approvalBeforeArchive', 'efficiency', 'guardrails',
130
+ ].includes(key)))
131
+ throw new AgentRuntimeConfigError('Invalid loop agent configuration');
132
+ // "inherit" is a recipe-only assignment, resolved before Core sees it.
133
+ const portableProviders = [...providers, { id: 'inherit', kind: 'cli', cli: 'claude' }];
134
+ const config = validateAgentRuntimeConfig({ ...input, enabled: true, providers: portableProviders, verification: [] });
135
+ if (['architect', 'developer', 'reviewer', 'fixer'].some(role => !config.rolePrompts?.[role]?.trim()))
136
+ throw new AgentRuntimeConfigError('Loop agents require an explicit definition for architect, developer, reviewer and fixer');
137
+ const { enabled: _enabled, providers: _providers, verification: _checks, ...settings } = config;
138
+ return settings;
139
+ }
140
+ /** Read only repository checks from the old project file. Role migration is explicit. */
141
+ function loadLoopRuntimeConfig(file, input, selection = { provider: 'claude' }) {
142
+ let verification = [];
143
+ try {
144
+ const saved = JSON.parse(node_fs_1.default.readFileSync(file, 'utf8'));
145
+ if (!saved || typeof saved !== 'object' || Array.isArray(saved))
146
+ throw new AgentRuntimeConfigError('Saved runtime configuration must be an object');
147
+ verification = saved.verification ?? [];
148
+ }
149
+ catch (error) {
150
+ if (error.code !== 'ENOENT')
151
+ throw error;
152
+ }
153
+ const providers = loadRuntimeProviders();
154
+ const settings = validateLoopRuntimeSettings(input, providers);
155
+ const inherit = (agent) => agent.provider !== 'inherit' ? agent : {
156
+ ...agent, provider: selection.provider, model: agent.model ?? selection.model, effort: agent.effort ?? selection.effort,
157
+ };
158
+ for (const role of ROLES)
159
+ settings.agents[role] = inherit(settings.agents[role]);
160
+ if (settings.fixer)
161
+ settings.fixer = inherit(settings.fixer);
162
+ for (const [role, agent] of Object.entries(settings.roles ?? {}))
163
+ settings.roles[role] = inherit(agent);
164
+ return validateAgentRuntimeConfig({ ...settings, providers, enabled: true, verification });
165
+ }
124
166
  const CLI_PROVIDERS = ['claude', 'codex', 'gemini', 'kimi'];
125
167
  const ROLES = ['architect', 'developer', 'reviewer'];
126
168
  const schemaValidator = new ajv_1.default({ allErrors: true }).compile(agent_runtime_schema_json_1.default);
@@ -110,10 +110,16 @@ function otherSuggestions(repository) {
110
110
  function suggestVerificationCommands(repositories) {
111
111
  const out = [];
112
112
  for (const repository of repositories) {
113
- if (!repository.path || !node_fs_1.default.existsSync(repository.path))
114
- continue;
115
- const node = nodeSuggestions(repository);
116
- out.push(...(node.length ? node : otherSuggestions(repository)));
113
+ const workspaces = repository.workspacePaths ?? (repository.workspacePath ? [repository.workspacePath] : [repository.path]);
114
+ for (const workspace of workspaces) {
115
+ if (!workspace || !node_fs_1.default.existsSync(workspace))
116
+ continue;
117
+ const member = { ...repository, path: workspace };
118
+ const node = nodeSuggestions(member);
119
+ const checks = node.length ? node : otherSuggestions(member);
120
+ const cwd = node_path_1.default.relative(repository.path, workspace).split(node_path_1.default.sep).join('/');
121
+ out.push(...checks.map(check => cwd ? { ...check, cwd, reason: `${cwd}: ${check.reason}` } : check));
122
+ }
117
123
  }
118
124
  return out;
119
125
  }
@@ -4,14 +4,11 @@
4
4
  // "Launch Milestone N": order the milestone's todo specs by their declared
5
5
  // dependencies (prerequisites, then execution_order), put ONE spec on each
6
6
  // rail (mode `implement`), launch the first through the ORDINARY rails launch
7
- // route, and — when that spec's delivery settles — launch the next one STACKED
8
- // on the delivered branch, so a greenfield walking skeleton accumulates without
9
- // waiting for a merge. Launches are always sequential (the Parallel option was
10
- // removed); auto-continue is on by default and a failure always pauses. The plan lives in SQLite (survives window close, app
11
- // restart, machine sleep), advances from the delivery-settle chokepoint (the
12
- // `rail.pr_state` broadcast tapped in the project's bound broadcast — the
13
- // engine's `onLoopRunFinished` fires BEFORE the delivery row leaves
14
- // `building`, so it is only a fallback for delivery-less shared-cwd runs),
7
+ // route, and integrate each completed delivery through the guarded local decision
8
+ // path BEFORE allocating the next worktree from the integration branch. Older
9
+ // adapters without integration retain their historical stacking behavior.
10
+ // Launches are sequential; auto-continue is on by default. The plan lives in
11
+ // SQLite and retains the current delivery through integration and recovery.
15
12
  // pauses with a typed reason on any failure (never skips ahead), and is
16
13
  // visible/controllable (resume / cancel) from every surface.
17
14
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -77,6 +74,7 @@ class MilestoneChainManager {
77
74
  db;
78
75
  projectId;
79
76
  io;
77
+ integrating = new Set();
80
78
  constructor(db, projectId, io) {
81
79
  this.db = db;
82
80
  this.projectId = projectId;
@@ -218,7 +216,7 @@ class MilestoneChainManager {
218
216
  }
219
217
  /**
220
218
  * Launch a milestone. Always sequential: one spec per rail, each stacked on
221
- * the previous delivered branch. `autoAdvance` defaults to ON; turning it off
219
+ * the accumulated integration branch when integration is bound. `autoAdvance` defaults to ON; turning it off
222
220
  * parks the chain at a checkpoint after every delivered rail.
223
221
  */
224
222
  async start(n, opts = {}) {
@@ -308,7 +306,7 @@ class MilestoneChainManager {
308
306
  return;
309
307
  }
310
308
  const earlierDeliveries = (0, milestone_chain_store_1.parseLaunched)(row).filter((entry) => entry.chunk <= chunkIndex && entry.deliveryId).map((entry) => entry.deliveryId);
311
- const multiRepositoryHistory = earlierDeliveries.some((id) => this.io.getDelivery(id)?.executionManifest);
309
+ const multiRepositoryHistory = !this.io.integrateDelivery && earlierDeliveries.some((id) => this.io.getDelivery(id)?.executionManifest);
312
310
  if (!multiRepositoryHistory && head && !(await this.branchExistsSafe(head))) {
313
311
  this.pause(row, 'head_missing', { headBranch: head });
314
312
  return;
@@ -371,6 +369,56 @@ class MilestoneChainManager {
371
369
  return 'chunk_stopped';
372
370
  return 'chunk_failed';
373
371
  }
372
+ completeIntegrated(row) {
373
+ if (!row.current_delivery_id || !(0, milestone_chain_store_1.isActiveChainStatus)(row.status))
374
+ return;
375
+ (0, milestone_chain_store_1.updateChain)(this.db, row.id, row.status, { status: 'waiting', currentDeliveryId: null, currentRunIds: [], headBranch: row.integration_branch, pauseReason: 'integration_complete' }, this.now());
376
+ this.afterChunkSuccess(row.id, row.integration_branch);
377
+ }
378
+ /** Keep the delivery attached until its guarded integration is durably confirmed. */
379
+ async integrateChunk(chainId) {
380
+ if (!this.io.integrateDelivery || this.integrating.has(chainId))
381
+ return;
382
+ const row = (0, milestone_chain_store_1.getChain)(this.db, chainId);
383
+ if (!row?.current_delivery_id || !(0, milestone_chain_store_1.isActiveChainStatus)(row.status))
384
+ return;
385
+ const snap = this.io.getDelivery(row.current_delivery_id);
386
+ if (!snap) {
387
+ this.pause(row, 'integration_failed:delivery_missing');
388
+ return;
389
+ }
390
+ if (snap.implementationOutcome === 'partially_succeeded' || snap.implementationOutcome === 'failed') {
391
+ this.pauseFailedChunk(row, 'chunk_failed');
392
+ return;
393
+ }
394
+ if (snap.decision === 'merged' || snap.decision === 'completed') {
395
+ this.completeIntegrated(row);
396
+ return;
397
+ }
398
+ this.integrating.add(chainId);
399
+ (0, milestone_chain_store_1.updateChain)(this.db, row.id, row.status, { status: 'waiting', pauseReason: 'integration_pending' }, this.now());
400
+ const pending = (0, milestone_chain_store_1.getChain)(this.db, chainId);
401
+ this.emit(pending);
402
+ try {
403
+ const result = await this.io.integrateDelivery(snap);
404
+ const current = (0, milestone_chain_store_1.getChain)(this.db, chainId);
405
+ if (!current || !(0, milestone_chain_store_1.isActiveChainStatus)(current.status) || current.current_delivery_id !== snap.id)
406
+ return;
407
+ const settled = this.io.getDelivery(snap.id);
408
+ if (settled?.decision === 'merged' || settled?.decision === 'completed')
409
+ this.completeIntegrated(current);
410
+ else
411
+ this.pause(current, 'integration_failed:' + (result.ok ? 'not_confirmed' : result.error));
412
+ }
413
+ catch {
414
+ const current = (0, milestone_chain_store_1.getChain)(this.db, chainId);
415
+ if (current && (0, milestone_chain_store_1.isActiveChainStatus)(current.status) && current.current_delivery_id === snap.id)
416
+ this.pause(current, 'integration_failed:unavailable');
417
+ }
418
+ finally {
419
+ this.integrating.delete(chainId);
420
+ }
421
+ }
374
422
  /** The delivery-settle chokepoint: a `rail.pr_state` for the in-flight chunk. */
375
423
  handleDeliveryState(snap) {
376
424
  for (const row of (0, milestone_chain_store_1.listActiveChains)(this.db)) {
@@ -378,6 +426,13 @@ class MilestoneChainManager {
378
426
  continue;
379
427
  if (snap.decision === 'building')
380
428
  continue;
429
+ if (this.io.integrateDelivery && SUCCESS_DECISIONS.has(snap.decision)) {
430
+ if (row.status === 'running' || row.status === 'waiting' && row.pause_reason === 'integration_pending')
431
+ void this.integrateChunk(row.id);
432
+ else if (row.pause_reason?.startsWith('integration_') && ['merged', 'completed'].includes(snap.decision))
433
+ this.completeIntegrated(row);
434
+ continue;
435
+ }
381
436
  if (SUCCESS_DECISIONS.has(snap.decision)) {
382
437
  const delivered = snap.executionManifest ? null : snap.decision === 'no_changes'
383
438
  ? row.head_branch
@@ -457,6 +512,10 @@ class MilestoneChainManager {
457
512
  return { ok: false, status: 404, error: 'chain_not_found' };
458
513
  if (row.status !== 'paused' && row.status !== 'awaiting_approval')
459
514
  return { ok: false, status: 409, error: 'chain_not_paused', detail: `chain is ${row.status}` };
515
+ if (this.io.integrateDelivery && row.current_delivery_id && row.pause_reason?.startsWith('integration_')) {
516
+ await this.integrateChunk(id);
517
+ return { ok: true, status: 202, chain: (0, milestone_chain_store_1.toChainSnapshot)((0, milestone_chain_store_1.getChain)(this.db, id)) };
518
+ }
460
519
  if (row.head_branch && !(await this.branchExistsSafe(row.head_branch))) {
461
520
  (0, milestone_chain_store_1.updateChain)(this.db, row.id, row.status, { status: 'paused', pauseReason: 'head_missing' }, this.now());
462
521
  const fresh = (0, milestone_chain_store_1.getChain)(this.db, row.id);
@@ -503,6 +562,13 @@ class MilestoneChainManager {
503
562
  */
504
563
  async recoverOnStartup() {
505
564
  for (const row of (0, milestone_chain_store_1.listActiveChains)(this.db)) {
565
+ if (this.io.integrateDelivery && row.status === 'waiting' && row.pause_reason?.startsWith('integration_')) {
566
+ if (row.current_delivery_id)
567
+ await this.integrateChunk(row.id);
568
+ else if (row.pause_reason === 'integration_complete')
569
+ this.afterChunkSuccess(row.id, row.integration_branch);
570
+ continue;
571
+ }
506
572
  if (row.status !== 'running')
507
573
  continue;
508
574
  if (row.current_delivery_id) {
@@ -475,7 +475,7 @@ function projectDefinitionRuntimeEvidence(status, reviewerStepId) {
475
475
  if (!['succeeded', 'failed'].includes(scope.status) || !scope.attemptId || !isRecord(scope.output))
476
476
  continue;
477
477
  const output = scope.output;
478
- if (scope.kind === 'verify' && typeof output.receiptId === 'string' && Array.isArray(output.commands)) {
478
+ if ((scope.kind === 'verify' || scope.kind === 'implementation-step' && output.phase === 'verify') && typeof output.receiptId === 'string' && Array.isArray(output.commands)) {
479
479
  for (const raw of output.commands) {
480
480
  if (commands.length >= RUNTIME_LIST_CAP)
481
481
  break;
@@ -487,7 +487,8 @@ function projectDefinitionRuntimeEvidence(status, reviewerStepId) {
487
487
  }
488
488
  }
489
489
  const review = scope.status !== 'succeeded' ? null : scope.kind === 'implementation' ? parseRuntimeReview(output.review)
490
- : reviewerStepId === scope.nodePath && scope.kind === 'role-turn' ? parseRuntimeReview(output.structured ?? output) : null;
490
+ : scope.kind === 'implementation-step' && output.phase === 'reviewer' ? parseRuntimeReview(output)
491
+ : reviewerStepId === scope.nodePath && scope.kind === 'role-turn' ? parseRuntimeReview(output.structured ?? output) : null;
491
492
  if (review)
492
493
  scopedReviews.push({ nodePath: scope.nodePath, scopeId: scope.scopeId, attemptId: scope.attemptId, review });
493
494
  }
@@ -389,6 +389,9 @@ async function launchMultiRepositoryRail(input, io = {}) {
389
389
  if (head.code !== 0 || !/^[a-f0-9]{40,64}$/i.test(head.stdout.trim()))
390
390
  throw new Error(`Cannot freeze HEAD for ${repository.name}`);
391
391
  manifest.repositories.push({ repositoryId: repository.id, name: repository.name, sourcePath: repository.path,
392
+ ...(repository.workspacePath ? { workspacePath: repository.workspacePath } : {}),
393
+ ...(repository.workspacePaths ? { workspacePaths: [...repository.workspacePaths] } : {}),
394
+ ...((input.workspaceSelection?.[repository.id] ?? previousRepository?.selectedWorkspacePaths) ? { selectedWorkspacePaths: [...(input.workspaceSelection?.[repository.id] ?? previousRepository.selectedWorkspacePaths)] } : {}),
392
395
  gitCommonDir: (0, repo_lock_1.repositoryLockKey)(repository.path), baseBranch: child.base_branch, integrationBranch: integrationBranches.get(repository.id) ?? previousRepository?.integrationBranch ?? child.base_branch, baseSha: head.stdout.trim(),
393
396
  worktreePath: worktree.worktree_path, branch: worktree.branch, worktreeId: worktree.id });
394
397
  }
@@ -1275,6 +1275,7 @@ async function launchIsolatedRail(input, io = {}) {
1275
1275
  runId: a.runId, loopId, loopName, graph: loopGraph, projectId: ctx.project.id,
1276
1276
  cwd: a.handle.worktreePath, repoDir: a.handle.worktreePath,
1277
1277
  repositoryId: singleRepositoryId,
1278
+ workspacePaths: singleRepositoryId ? input.workspaceSelection?.[singleRepositoryId] : undefined,
1278
1279
  isolation: { branch: a.handle.branch, worktreePath: a.handle.worktreePath },
1279
1280
  railIndex, ticketId: a.ticketIds.length ? a.ticketId : null,
1280
1281
  spec: {
@@ -75,6 +75,12 @@ function coerceRailLaunchProposal(value) {
75
75
  const mode = modeRaw === 'batch' || modeRaw === 'batch-implement' ? 'implement' : modeRaw;
76
76
  if (!exports.RAIL_LAUNCH_MODES.includes(mode))
77
77
  return { ok: false, reason: 'invalid_mode' };
78
+ const repositoryIds = value.repositoryIds;
79
+ const workspaceSelection = value.workspaceSelection;
80
+ if (repositoryIds !== undefined && (!Array.isArray(repositoryIds) || !repositoryIds.length || repositoryIds.length > 50 || repositoryIds.some(id => typeof id !== 'string' || !id.trim()) || new Set(repositoryIds).size !== repositoryIds.length))
81
+ return { ok: false, reason: 'invalid_workspace_selection' };
82
+ if (workspaceSelection !== undefined && (!isRecord(workspaceSelection) || !Object.keys(workspaceSelection).length || Object.keys(workspaceSelection).length > 50 || Object.entries(workspaceSelection).some(([id, paths]) => !id.trim() || !Array.isArray(paths) || !paths.length || paths.length > 50 || paths.some(item => typeof item !== 'string' || !item.trim() || item.length > 4096) || new Set(paths).size !== paths.length)))
83
+ return { ok: false, reason: 'invalid_workspace_selection' };
78
84
  const newRailRaw = value.newRail;
79
85
  const newRail = newRailRaw === true
80
86
  ? { name: null }
@@ -89,6 +95,8 @@ function coerceRailLaunchProposal(value) {
89
95
  railIndex,
90
96
  newRail,
91
97
  ticketIds,
98
+ ...(repositoryIds !== undefined ? { repositoryIds: repositoryIds } : {}),
99
+ ...(workspaceSelection !== undefined ? { workspaceSelection: workspaceSelection } : {}),
92
100
  mode: mode,
93
101
  loopId: optString(value.loopId, 120),
94
102
  aiEngine: optString(value.aiEngine ?? value.provider ?? value.engine, 80),