specrails-desktop 2.58.1 → 2.59.1

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 (438) hide show
  1. package/README.md +2 -5
  2. package/client/dist/assets/{ActivityFeedPage-BQGppIR0.js → ActivityFeedPage-vAY9C71W.js} +1 -1
  3. package/client/dist/assets/AgentBrowserCapture-CpwjGXQm.js +1 -0
  4. package/client/dist/assets/AgentModeAnalyticsPane-BHeoJVxQ.js +2 -0
  5. package/client/dist/assets/AgentModeCodePane-B0Nf3YkN.js +2 -0
  6. package/client/dist/assets/{AgentModeJobsPane-DR_06oqH.js → AgentModeJobsPane-CpbIItY_.js} +1 -1
  7. package/client/dist/assets/{AgentsPage-BKIKjm_1.js → AgentsPage-BVJAFXn3.js} +3 -3
  8. package/client/dist/assets/{AnalyticsPage-069npRVo.js → AnalyticsPage-DDWees-K.js} +1 -1
  9. package/client/dist/assets/CodePage-CzV5tkvS.js +3 -0
  10. package/client/dist/assets/{DesktopAnalyticsPage-qtWAPM47.js → DesktopAnalyticsPage-DfRHNK7i.js} +1 -1
  11. package/client/dist/assets/DocsDialog-BxduBYbh.js +11 -0
  12. package/client/dist/assets/DocsPage-DOVeRJn_.js +11 -0
  13. package/client/dist/assets/{ExportDropdown-BcMpvV89.js → ExportDropdown-BnKVV8UK.js} +1 -1
  14. package/client/dist/assets/InteractiveJobComposer-Da9dvM-0.js +19 -0
  15. package/client/dist/assets/JobDetailModal-DhwKbZGe.js +1 -0
  16. package/client/dist/assets/JobDetailPage-MtPE-E_1.js +1 -0
  17. package/client/dist/assets/JobsPage-0vdlbNik.js +1 -0
  18. package/client/dist/assets/LoopBuilderPage-DCUHozXh.js +1 -0
  19. package/client/dist/assets/{LoopPreviewModal-je5pSCf-.js → LoopPreviewModal-Df-S8NGf.js} +1 -1
  20. package/client/dist/assets/LoopWindowSurface-sdb7QbSo.js +1 -0
  21. package/client/dist/assets/LoopsPage-CDfCC9Px.js +2 -0
  22. package/client/dist/assets/MinimizedChatsContext-DKlPf54Y.js +1 -0
  23. package/client/dist/assets/PluginsPage-iL0G3rQQ.js +2 -0
  24. package/client/dist/assets/ProjectSettingsDialog-DOkjT7xv.js +1 -0
  25. package/client/dist/assets/{RepositoryDeliveries-BiwJuS8r.js → RepositoryDeliveries-CJVpOUpZ.js} +1 -1
  26. package/client/dist/assets/RepositoryScopeSelector-BeM-2gIZ.js +2 -0
  27. package/client/dist/assets/ReviewPacketPage-EBCim2w_.js +4 -0
  28. package/client/dist/assets/TemplatePreviewModal-1-92UGwN.js +1 -0
  29. package/client/dist/assets/TicketDetailModalContext-CkVXuHxU.js +16 -0
  30. package/client/dist/assets/{Trans-D3AfDylf.js → Trans-CzhPjnjB.js} +1 -1
  31. package/client/dist/assets/agent--8QAfoXL.js +1 -0
  32. package/client/dist/assets/agent-3HxXkIWX.js +1 -0
  33. package/client/dist/assets/agent-BjttZLIl.js +1 -0
  34. package/client/dist/assets/agent-CpFCz32T.js +1 -0
  35. package/client/dist/assets/agent-DVnw6ybO.js +1 -0
  36. package/client/dist/assets/agent-Z5s_GTP0.js +1 -0
  37. package/client/dist/assets/agent-hv57-zyG.js +1 -0
  38. package/client/dist/assets/agent-xGrZ_aN1.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-DlQaXc7c.js} +1 -1
  68. package/client/dist/assets/{getTimezoneOffsetInMilliseconds-DOGj1Mfz.js → getTimezoneOffsetInMilliseconds-DxUFGcVq.js} +1 -1
  69. package/client/dist/assets/index-Bfunvi6w.css +2 -0
  70. package/client/dist/assets/index-Czov72g7.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-CbJvbRYx.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-BkBD3rxx.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-CYotH-9N.js} +1 -1
  124. package/client/dist/assets/subscriptionUsage-BH73RO86.js +1 -0
  125. package/client/dist/assets/subscriptionUsage-BWTeWlHx.js +1 -0
  126. package/client/dist/assets/subscriptionUsage-CBh82zVa.js +1 -0
  127. package/client/dist/assets/subscriptionUsage-CHcQ7lLe.js +1 -0
  128. package/client/dist/assets/subscriptionUsage-CdlE5Mg1.js +1 -0
  129. package/client/dist/assets/subscriptionUsage-DUPBmSQi.js +1 -0
  130. package/client/dist/assets/subscriptionUsage-DrlwrJ17.js +1 -0
  131. package/client/dist/assets/subscriptionUsage-EFu0mIK3.js +1 -0
  132. package/client/dist/assets/tauri-shell-DE6LfGPw.js +2 -0
  133. package/client/dist/assets/{useDesktop-0unHDBJJ.js → useDesktop-DpxVGfUC.js} +1 -1
  134. package/client/dist/assets/{useRuntimeRuns-BN1rhyEm.js → useRuntimeRuns-CfGRbhr9.js} +1 -1
  135. package/client/dist/assets/useSharedWebSocket-C17TGqT8.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/api-reference.md +1 -0
  277. package/docs/internals/core-runtime-updates.md +9 -2
  278. package/docs/internals/desktop-owned-workflows.md +144 -0
  279. package/docs/internals/mission-rail-cards.md +15 -0
  280. package/docs/internals/operations-runbook.md +26 -0
  281. package/docs/internals/programmatic-agent-runtime.md +34 -28
  282. package/docs/internals/project-builder.md +34 -11
  283. package/docs/internals/safe-pr-review-flow.md +25 -7
  284. package/docs/internals/source-map.md +53 -3
  285. package/docs/internals/spec-addenda.md +11 -5
  286. package/docs/internals/visual-design-audit.md +5 -0
  287. package/docs/local-providers.md +8 -13
  288. package/docs/mcp.md +1 -17
  289. package/docs/platforms/macos.md +0 -40
  290. package/docs/platforms/windows-parity.md +1 -1
  291. package/docs/platforms/windows.md +0 -8
  292. package/docs/running-pipelines.md +1 -46
  293. package/docs/subscription-usage.md +19 -0
  294. package/docs/terminal.md +0 -2
  295. package/docs/tracking-cost.md +2 -33
  296. package/package.json +1 -1
  297. package/server/dist/core-execution.js +24 -15
  298. package/server/dist/core-update-manager.js +1 -1
  299. package/server/dist/db/migrations.js +9 -0
  300. package/server/dist/desktop-db.js +23 -10
  301. package/server/dist/desktop-router.js +8 -4
  302. package/server/dist/index.js +10 -5
  303. package/server/dist/mcp/guide.js +1 -0
  304. package/server/dist/mcp/tools/jobs.js +1 -1
  305. package/server/dist/mcp/tools/loops.js +2 -0
  306. package/server/dist/mcp/tools/meta.js +6 -7
  307. package/server/dist/mcp/tools/projects.js +31 -6
  308. package/server/dist/mcp/tools/rails.js +6 -3
  309. package/server/dist/mcp/tools/setup.js +10 -0
  310. package/server/dist/mcp/tools/specs.js +1 -1
  311. package/server/dist/modules/accounting/runtime/pricing.js +6 -1
  312. package/server/dist/modules/agent-runtime/runtime/agent-runtime-bridge.js +169 -17
  313. package/server/dist/modules/agent-runtime/runtime/agent-runtime-metrics.js +1 -1
  314. package/server/dist/modules/agent-runtime/runtime/agent-runtime-settings-router.js +1 -1
  315. package/server/dist/modules/agent-runtime/runtime/agent-runtime-settings.js +42 -0
  316. package/server/dist/modules/agent-runtime/runtime/agent-runtime-verification-suggestions.js +10 -4
  317. package/server/dist/modules/builder/runtime/milestone-chain.js +76 -10
  318. package/server/dist/modules/delivery/runtime/delivery-evidence.js +3 -2
  319. package/server/dist/modules/delivery/runtime/multi-repo-execution.js +10 -5
  320. package/server/dist/modules/delivery/runtime/rail-isolated-launch.js +66 -30
  321. package/server/dist/modules/delivery/runtime/rail-launch-parser.js +8 -0
  322. package/server/dist/modules/delivery/runtime/rail-pr-store.js +3 -3
  323. package/server/dist/modules/delivery/runtime/rail-relaunch.js +154 -0
  324. package/server/dist/modules/delivery/runtime/rails-router.js +157 -36
  325. package/server/dist/modules/delivery/runtime/review-packet.js +1 -1
  326. package/server/dist/modules/execution/runtime/revision-seed.js +6 -2
  327. package/server/dist/modules/loops/runtime/loop-agent-defaults.json +6 -0
  328. package/server/dist/modules/loops/runtime/loop-agents.js +21 -0
  329. package/server/dist/modules/loops/runtime/loop-command-catalog.js +1 -1
  330. package/server/dist/modules/loops/runtime/loop-core-factory.js +19 -3
  331. package/server/dist/modules/loops/runtime/loop-definition-events.js +1 -1
  332. package/server/dist/modules/loops/runtime/loop-definition.js +79 -12
  333. package/server/dist/modules/loops/runtime/loop-effect.js +1 -1
  334. package/server/dist/modules/loops/runtime/loop-executors.js +24 -9
  335. package/server/dist/modules/loops/runtime/loop-factory.js +1 -1
  336. package/server/dist/modules/loops/runtime/loop-graph.js +5 -14
  337. package/server/dist/modules/loops/runtime/loop-implement-recipe.js +53 -0
  338. package/server/dist/modules/loops/runtime/loop-run-manager.js +30 -20
  339. package/server/dist/modules/loops/runtime/loop-templates.js +29 -5
  340. package/server/dist/modules/loops/runtime/loops-router.js +17 -0
  341. package/server/dist/modules/missions/runtime/agent-context-resolver.js +1 -1
  342. package/server/dist/modules/missions/runtime/agent-operator-prompt.js +17 -8
  343. package/server/dist/modules/specs/runtime/spec-models.js +4 -7
  344. package/server/dist/modules/subscription-usage/adapters/claude.js +123 -0
  345. package/server/dist/modules/subscription-usage/adapters/codex.js +164 -0
  346. package/server/dist/modules/subscription-usage/adapters/errors.js +15 -0
  347. package/server/dist/modules/subscription-usage/adapters/http.js +20 -0
  348. package/server/dist/modules/subscription-usage/adapters/local-auth.js +61 -0
  349. package/server/dist/modules/subscription-usage/application.js +12 -0
  350. package/server/dist/modules/subscription-usage/domain.js +104 -0
  351. package/server/dist/modules/subscription-usage/index.js +2 -0
  352. package/server/dist/modules/subscription-usage/ports.js +2 -0
  353. package/server/dist/modules/subscription-usage/runtime/composition.js +20 -0
  354. package/server/dist/modules/subscription-usage/runtime/usage-service.js +111 -0
  355. package/server/dist/project-registry.js +16 -1
  356. package/server/dist/project-repositories.js +51 -2
  357. package/server/dist/project-router-helpers.js +1 -1
  358. package/server/dist/project-router-loop-runs.js +4 -1
  359. package/server/dist/providers/claude-adapter.js +6 -4
  360. package/server/dist/providers/codex-adapter.js +4 -5
  361. package/server/dist/schemas/workflow-definition.schema.json +2 -0
  362. package/server/dist/worktree-manager.js +114 -6
  363. package/server/dist/worktree-overlay.js +4 -4
  364. package/client/dist/assets/AgentBrowserCapture-PI-cxyxi.js +0 -1
  365. package/client/dist/assets/AgentModeAnalyticsPane-mQsOqdXx.js +0 -2
  366. package/client/dist/assets/AgentModeCodePane-BL-vpmlY.js +0 -2
  367. package/client/dist/assets/CodePage-Alitorrv.js +0 -3
  368. package/client/dist/assets/DocsDialog-DwL6rgcT.js +0 -11
  369. package/client/dist/assets/DocsPage-BA1hiJZ-.js +0 -11
  370. package/client/dist/assets/InteractiveJobComposer-B3H1YHr6.js +0 -19
  371. package/client/dist/assets/JobDetailModal-XZQ9eAAy.js +0 -1
  372. package/client/dist/assets/JobDetailPage-B-SIaq_q.js +0 -1
  373. package/client/dist/assets/JobsPage-iQXAv2mx.js +0 -1
  374. package/client/dist/assets/LoopBuilderPage-B_gbFAHn.js +0 -1
  375. package/client/dist/assets/LoopsPage-BZwcY5cu.js +0 -2
  376. package/client/dist/assets/MinimizedChatsContext-CNx_0bZu.js +0 -1
  377. package/client/dist/assets/PluginsPage-DFocnoHX.js +0 -2
  378. package/client/dist/assets/ProjectSettingsDialog-Bh3S6EBB.js +0 -1
  379. package/client/dist/assets/RepositoryScopeSelector-C2mvWBQ6.js +0 -1
  380. package/client/dist/assets/ReviewPacketPage-CrAf-WDy.js +0 -4
  381. package/client/dist/assets/TemplatePreviewModal-C7DordpN.js +0 -1
  382. package/client/dist/assets/TicketDetailModalContext-B-DySpKL.js +0 -16
  383. package/client/dist/assets/agent-BaG10AN6.js +0 -1
  384. package/client/dist/assets/agent-CH4aJ884.js +0 -1
  385. package/client/dist/assets/agent-CLc9fA7v.js +0 -1
  386. package/client/dist/assets/agent-DHh9-AdL.js +0 -1
  387. package/client/dist/assets/agent-DkKnd6Wv.js +0 -1
  388. package/client/dist/assets/agent-aiVhqFW1.js +0 -1
  389. package/client/dist/assets/agent-dbznD3eO.js +0 -1
  390. package/client/dist/assets/agent-sMVFcPwL.js +0 -1
  391. package/client/dist/assets/agentRuntime-BeUb3g9g.js +0 -1
  392. package/client/dist/assets/file-text-CFT-xXKN.js +0 -1
  393. package/client/dist/assets/index-BS7KCWkV.css +0 -2
  394. package/client/dist/assets/index-C4Mv6tV6.js +0 -80
  395. package/client/dist/assets/integrations-CH0zy1Mw.js +0 -1
  396. package/client/dist/assets/integrations-CIAjhd7L.js +0 -1
  397. package/client/dist/assets/integrations-CZdwOe8A.js +0 -1
  398. package/client/dist/assets/integrations-Ct7omMld.js +0 -1
  399. package/client/dist/assets/integrations-D6MSxy3-.js +0 -1
  400. package/client/dist/assets/integrations-DbZa-Zyc.js +0 -1
  401. package/client/dist/assets/integrations-b8pH-vfl.js +0 -1
  402. package/client/dist/assets/integrations-mB-7zoyT.js +0 -1
  403. package/client/dist/assets/jira-api-BYbEPlkr.js +0 -1
  404. package/client/dist/assets/layers-eDMjG41I.js +0 -1
  405. package/client/dist/assets/loops-B4_D-nbg.js +0 -1
  406. package/client/dist/assets/nav-B-tpjyc2.js +0 -1
  407. package/client/dist/assets/nav-BJNXGg29.js +0 -1
  408. package/client/dist/assets/nav-BdhtRLP7.js +0 -1
  409. package/client/dist/assets/nav-ByYgyN5T.js +0 -1
  410. package/client/dist/assets/nav-DA0Mva2c.js +0 -1
  411. package/client/dist/assets/nav-DcJm_Jg_.js +0 -1
  412. package/client/dist/assets/nav-DhMPtAXc.js +0 -1
  413. package/client/dist/assets/nav-DhPb-Isc.js +0 -1
  414. package/client/dist/assets/provider-capabilities-Vi1ZGvAK.js +0 -1
  415. package/client/dist/assets/save-B6wGWwBR.js +0 -1
  416. package/client/dist/assets/settings-3o4Ntzi5.js +0 -1
  417. package/client/dist/assets/settings-B97jlkwD.js +0 -1
  418. package/client/dist/assets/settings-BTaT_bsS.js +0 -1
  419. package/client/dist/assets/settings-CT8w7WiC.js +0 -1
  420. package/client/dist/assets/settings-Cvr8_tvh.js +0 -1
  421. package/client/dist/assets/settings-Dn1qgAC8.js +0 -1
  422. package/client/dist/assets/settings-EShDdg4J.js +0 -1
  423. package/client/dist/assets/settings-tfLhQZRN.js +0 -1
  424. package/client/dist/assets/setup-CBMu3tqH.js +0 -1
  425. package/client/dist/assets/setup-CPh-e6J2.js +0 -1
  426. package/client/dist/assets/setup-CRKLp_ke.js +0 -1
  427. package/client/dist/assets/setup-Cy302v4J.js +0 -1
  428. package/client/dist/assets/setup-D681vTPf.js +0 -1
  429. package/client/dist/assets/setup-DYK0VBkE.js +0 -1
  430. package/client/dist/assets/setup-KAuiIiVs.js +0 -1
  431. package/client/dist/assets/setup-OGX-oCfI.js +0 -1
  432. package/client/dist/assets/useSharedWebSocket-BZMrX4jr.js +0 -2
  433. package/client/dist/assets/wrench-BfmNO9Vw.js +0 -1
  434. package/docs/agy-cli-provider-study.md +0 -257
  435. package/docs/gemini-cli-provider-study.md +0 -301
  436. package/docs/gemini-core-support-evaluation.md +0 -160
  437. package/docs/gemini.md +0 -257
  438. package/docs/kimi.md +0 -291
@@ -129,7 +129,7 @@ function createDefinitionEventProjection(input) {
129
129
  persist('loop_step', { index: nextCount, kind: 'core', title: workflow.nodePath, iteration: workflow.visit, startedAtMs: Date.parse(at), ...correlation });
130
130
  }
131
131
  else if (opening && ['step_succeeded', 'step_failed', 'step_blocked', 'step_interrupted', 'step_retrying', 'step_paused'].includes(String(workflow.type))) {
132
- persist('loop_step_end', { index: opening.index, status: workflow.type === 'step_succeeded' ? 'ok' : workflow.type === 'step_paused' ? 'paused' : workflow.type === 'step_interrupted' ? 'interrupted' : 'failed',
132
+ persist('loop_step_end', { index: opening.index, status: workflow.type === 'step_succeeded' ? workflow.outcome === 'fail' ? 'failed' : 'ok' : workflow.type === 'step_paused' ? 'paused' : workflow.type === 'step_interrupted' ? 'interrupted' : 'failed',
133
133
  runtimeStatus: String(workflow.type).slice(5), outcome: workflow.outcome, exitCode: null,
134
134
  durationMs: Math.max(0, Date.parse(at) - Number(opening.startedAtMs)), ...correlation });
135
135
  }
@@ -4,12 +4,14 @@ exports.compileLoopToDefinition = compileLoopToDefinition;
4
4
  const loop_command_catalog_1 = require("./loop-command-catalog");
5
5
  const loop_constants_1 = require("./loop-constants");
6
6
  const loop_graph_1 = require("./loop-graph");
7
- const AI_KINDS = new Set(['prompt', 'role-turn', 'decider', 'implementation']);
7
+ const AI_KINDS = new Set(['prompt', 'role-turn', 'decider', 'implementation', 'implementation-step']);
8
8
  const object = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
9
9
  /** No I/O, model call, hash implementation or scheduling belongs in this adapter. */
10
10
  function compileLoopToDefinition(graph, launch) {
11
11
  (0, loop_graph_1.assertDefinitionGraph)(graph);
12
12
  const allGraphs = [graph, ...Object.values(graph.components ?? {})];
13
+ if (!graph.config.agents && allGraphs.some(body => body.nodes.some(node => node.data?.kind === 'implementation-step')))
14
+ throw new Error('Independent implementation steps require loop-owned agents');
13
15
  for (const body of allGraphs) {
14
16
  (0, loop_graph_1.assertDefinitionGraph)(body);
15
17
  const validation = (0, loop_graph_1.validateLoopGraph)(body);
@@ -41,14 +43,25 @@ function compileLoopToDefinition(graph, launch) {
41
43
  ...(typeof node.data?.reason === 'string' ? { reason: node.data.reason } : {}),
42
44
  }
43
45
  : node.data.params;
44
- const engineInput = object(rawParams.engine) ? rawParams.engine : {};
45
- const provider = typeof engineInput.provider === 'string' ? engineInput.provider : launch.provider;
46
+ const promptRole = node.id === 'fix' ? 'fixer' : node.id === 'prepare' ? 'architect' : rawParams.access === 'read' ? 'reviewer' : 'developer';
47
+ const agents = graph.config.agents ? launch.loopAgents ?? graph.config.agents : undefined;
48
+ const loopEngine = agents ? (promptRole === 'fixer' ? agents.fixer ?? agents.agents.developer : agents.agents[promptRole]) : undefined;
49
+ const engineInput = { ...(loopEngine ? { provider: loopEngine.provider, ...(loopEngine.model ? { model: loopEngine.model } : {}), ...(loopEngine.effort ? { effort: loopEngine.effort } : {}), ...(loopEngine.thinking ? { thinking: loopEngine.thinking } : {}), ...(loopEngine.maxTurns ? { maxTurns: loopEngine.maxTurns } : {}) } : {}), ...(object(rawParams.engine) ? rawParams.engine : {}) };
50
+ const inherited = engineInput.provider === 'inherit';
51
+ const provider = inherited ? launch.provider : typeof engineInput.provider === 'string' ? engineInput.provider : launch.provider;
52
+ if (inherited) {
53
+ engineInput.provider = provider;
54
+ if (!engineInput.model && launch.model)
55
+ engineInput.model = launch.model;
56
+ if (!engineInput.effort && launch.effort)
57
+ engineInput.effort = launch.effort;
58
+ }
46
59
  const params = resolveValue(rawParams, launch, provider);
47
60
  if (kind === 'prompt') {
48
61
  params.engine = {
49
62
  provider,
50
- ...(provider === launch.provider && launch.model ? { model: launch.model } : {}),
51
- ...(provider === launch.provider && launch.effort ? { effort: launch.effort } : {}),
63
+ ...(!loopEngine && provider === launch.provider && launch.model ? { model: launch.model } : {}),
64
+ ...(!loopEngine && provider === launch.provider && launch.effort ? { effort: launch.effort } : {}),
52
65
  ...engineInput,
53
66
  };
54
67
  if (typeof rawParams.text === 'string' &&
@@ -61,6 +74,15 @@ function compileLoopToDefinition(graph, launch) {
61
74
  }
62
75
  }
63
76
  }
77
+ if (kind === 'prompt' && graph.config.agents) {
78
+ const definition = graph.config.agents.rolePrompts?.[promptRole]?.replaceAll('{{', '{{{{');
79
+ if (definition) {
80
+ if (object(params.nativeCommand))
81
+ params.nativeCommand = { ...params.nativeCommand, args: String(params.nativeCommand.args ?? '') + '\n\nLoop agent definition:\n' + definition };
82
+ else if (typeof params.text === 'string')
83
+ params.text = definition + '\n\n' + params.text;
84
+ }
85
+ }
64
86
  if (launch.briefing && ['prompt', 'role-turn', 'decider'].includes(kind)) {
65
87
  const suffix = '\n\nFrozen launch context:\n' + launch.briefing;
66
88
  if (object(params.nativeCommand))
@@ -70,7 +92,7 @@ function compileLoopToDefinition(graph, launch) {
70
92
  if (typeof params[field] === 'string')
71
93
  params[field] += suffix;
72
94
  }
73
- if (kind === 'implementation')
95
+ if ((kind === 'implementation' || kind === 'implementation-step'))
74
96
  for (const role of ['architect', 'developer', 'reviewer'])
75
97
  roleIds.add(role);
76
98
  if ((kind === 'role-turn' || kind === 'decider') && typeof params.roleId === 'string')
@@ -102,10 +124,51 @@ function compileLoopToDefinition(graph, launch) {
102
124
  };
103
125
  };
104
126
  const body = compileBody(graph);
127
+ // Specialize the Desktop Implement recipe with the frozen launch scope.
128
+ // Its ordinary review/candidate gates remain in place; an addendum cannot
129
+ // disappear behind a generic approved=true response.
130
+ if (launch.id === 'factory:implement' && launch.addendaIds?.length) {
131
+ const reviewer = body.nodes.reviewer;
132
+ const policy = body.nodes['review-policy'];
133
+ if (reviewer?.kind === 'role-turn' && policy?.kind === 'condition' && object(reviewer.params.structuredOutput)) {
134
+ const schema = reviewer.params.structuredOutput;
135
+ const entries = Object.fromEntries(launch.addendaIds.map((id, index) => [`a${index}`, {
136
+ type: 'object', additionalProperties: false, required: ['id', 'verdict', 'files', 'tests'],
137
+ properties: {
138
+ id: { type: 'string', const: id },
139
+ verdict: { enum: ['applied', 'partial', 'blocked'] },
140
+ files: { type: 'array', items: { type: 'string', minLength: 1 }, maxItems: 100 },
141
+ tests: { type: 'array', items: { type: 'string', minLength: 1 }, maxItems: 100 },
142
+ },
143
+ }]));
144
+ reviewer.params.structuredOutput = {
145
+ ...schema, properties: { ...(object(schema.properties) ? schema.properties : {}), addenda: { type: 'object', additionalProperties: false, properties: entries, required: Object.keys(entries) } },
146
+ required: [...(Array.isArray(schema.required) ? schema.required : []), 'addenda'],
147
+ };
148
+ reviewer.params.prompt += '\nReport every frozen addendum in addenda (slots in launch order): ' + JSON.stringify(launch.addendaIds) + '. Inspect real code and verification evidence. Use applied only with concrete files and executed tests; otherwise partial or blocked. Also include one line per addendum in summary: - [id] applied — files: paths — tests: commands and results.';
149
+ const checks = launch.addendaIds.map((_, index) => {
150
+ const ref = `$outputs.reviewer.structured.addenda.a${index}`;
151
+ return `${ref}.verdict == "applied" && ${ref}.files.length > 0 && ${ref}.tests.length > 0`;
152
+ });
153
+ // Core bounds expression length/token count. Keep large addendum scopes
154
+ // in small successive gates instead of generating one unbounded expression.
155
+ let next = reviewer.ends.next;
156
+ for (let offset = checks.length - 1; offset >= 0; offset -= 10) {
157
+ const first = Math.max(0, offset - 9);
158
+ let id = `addenda-review-${first}`;
159
+ while (body.nodes[id])
160
+ id += '-gate';
161
+ body.nodes[id] = { kind: 'condition', params: { expr: checks.slice(first, offset + 1).map(expr => `(${expr})`).join(' && ') }, ends: { true: next, false: policy.ends.false } };
162
+ next = id;
163
+ }
164
+ reviewer.ends.next = next;
165
+ body.maxTransitions = Math.min(10000, body.maxTransitions + Math.ceil(checks.length / 10) * graph.config.maxIterations);
166
+ }
167
+ }
105
168
  const components = Object.fromEntries(Object.entries(graph.components ?? {})
106
169
  .sort(([a], [b]) => a.localeCompare(b, 'en'))
107
170
  .map(([id, component]) => [id, compileBody(component)]));
108
- const implementation = [body, ...Object.values(components)].some((component) => Object.values(component.nodes).some((node) => node.kind === 'implementation'));
171
+ const implementation = [body, ...Object.values(components)].some((component) => Object.values(component.nodes).some((node) => (node.kind === 'implementation' || node.kind === 'implementation-step')));
109
172
  const budget = {
110
173
  ...(graph.config.maxCostUsd && graph.config.maxCostUsd > 0
111
174
  ? { maxCostUsd: graph.config.maxCostUsd }
@@ -143,8 +206,8 @@ function pieceWrites(node, roles) {
143
206
  if (kind === 'prompt')
144
207
  return params.access !== 'read';
145
208
  if (kind === 'role-turn')
146
- return roles?.[String(params.roleId)]?.access !== 'read';
147
- return ['verify', 'shell', 'openspec-archive', 'implementation'].includes(kind ?? '');
209
+ return (roles ?? {})[String(params.roleId)]?.access !== 'read';
210
+ return ['verify', 'shell', 'openspec-archive', 'implementation', 'implementation-step'].includes(kind ?? '');
148
211
  }
149
212
  /** Expand templates once. Text inserted by spec/constants cannot introduce another token. */
150
213
  function resolveValue(value, launch, provider) {
@@ -153,10 +216,14 @@ function resolveValue(value, launch, provider) {
153
216
  if (token === '{{{{')
154
217
  return token; // Core resolves the literal escape at execution.
155
218
  if (/^\{\{\s*spec\./.test(token))
156
- return (0, loop_graph_1.interpolateSpec)(token, launch.spec);
219
+ return (0, loop_graph_1.interpolateSpec)(token, launch.spec).replaceAll('{{', '{{{{');
157
220
  if (/^\{\{\s*const:/.test(token))
158
- return (0, loop_constants_1.resolveConstants)(token.replace(/\s+/g, ''), launch.constants);
159
- return (0, loop_command_catalog_1.expandCommands)(token, { provider, ticketIds: launch.spec?.ticketIds, specId: launch.spec?.id });
221
+ return (0, loop_constants_1.resolveConstants)(token.replace(/\s+/g, ''), launch.constants).replaceAll('{{', '{{{{');
222
+ // Registry templates contain launch tokens themselves. Resolve only
223
+ // those trusted tokens, never commands introduced by task data.
224
+ return (0, loop_command_catalog_1.expandCommands)(token, { provider, ticketIds: launch.spec?.ticketIds, specId: launch.spec?.id }).replace(/\{\{\s*(?:spec\.\w+|const:[A-Za-z0-9_.-]+)\s*\}\}/g, nested => (/^\{\{\s*spec\./.test(nested)
225
+ ? (0, loop_graph_1.interpolateSpec)(nested, launch.spec)
226
+ : (0, loop_constants_1.resolveConstants)(nested.replace(/\s+/g, ''), launch.constants)).replaceAll('{{', '{{{{'));
160
227
  });
161
228
  }
162
229
  if (Array.isArray(value))
@@ -11,7 +11,7 @@ function classifyLoopEffect(graph) {
11
11
  if (n.data?.kind === 'prompt')
12
12
  return n.data.params?.access !== 'read';
13
13
  // Role access binds at launch; isolation is conservative before that binding.
14
- return ['role-turn', 'verify', 'shell', 'openspec-archive', 'implementation', 'component', 'map'].includes(String(n.data?.kind));
14
+ return ['role-turn', 'verify', 'shell', 'openspec-archive', 'implementation', 'implementation-step', 'component', 'map'].includes(String(n.data?.kind));
15
15
  }) || Object.values(graph.components ?? {}).some(component => classifyLoopEffect(component) === 'mutating');
16
16
  return writes ? 'mutating' : 'read-only';
17
17
  }
@@ -185,10 +185,20 @@ function createLoopExecutors(opts = {}) {
185
185
  const host = (0, agent_runtime_bridge_1.readFrozenRuntimeHost)(contextPath, resolveEnv(), runId);
186
186
  await (0, agent_runtime_bridge_1.runAgentRuntimeControl)({ kind: 'cancel', runId, contextPath, requestId, ...host });
187
187
  },
188
- async assertDefinitionSupport() {
188
+ async assertDefinitionSupport(graph) {
189
189
  const runtime = await (0, agent_runtime_loader_1.loadCoreAgentRuntime)();
190
190
  if (runtime.api?.capabilities?.engineV2 !== 1 || runtime.api?.capabilities?.workflowDefinitions !== 1)
191
191
  throw new Error('engine_unsupported: Update Core to run workflow definitions');
192
+ if (graph && [graph, ...Object.values(graph.components ?? {})].some(body => body.nodes.some(node => node.data?.kind === 'artifact-contract'))) {
193
+ if (runtime.api?.capabilities?.workflowAgentSteps !== 1)
194
+ throw new Error('engine_unsupported: Update Core to run configurable workflow agent steps');
195
+ }
196
+ if (graph && [graph, ...Object.values(graph.components ?? {})].some(body => body.nodes.some(node => node.data?.kind === 'implementation-step'))) {
197
+ if (runtime.api?.capabilities?.implementationSteps !== 1)
198
+ throw new Error('engine_unsupported: Update Core to run independent implementation steps');
199
+ if (!graph.config.agents)
200
+ throw new Error('Independent implementation steps require loop-owned agents');
201
+ }
192
202
  },
193
203
  async assertLegacyEngineSupport() {
194
204
  // Legacy traversal predates Core: only a Core that positively lacks engine 1 blocks it.
@@ -211,17 +221,21 @@ function createLoopExecutors(opts = {}) {
211
221
  return { ...result, runtimeStatus: result.runtimeStatus ?? 'failed' };
212
222
  }
213
223
  const seeded = (0, loop_run_manager_1.seedChangeId)({ ...request, runId });
214
- const briefing = [request.followUp?.briefing, request.addenda?.briefing].filter(Boolean).join('\n\n');
224
+ const delta = seeded && (seeded.source === 'addenda' || seeded.source === 'revision')
225
+ ? `DELTA OPENSPEC TARGET: ${seeded.id}. Use ONLY openspec/changes/${seeded.id}/; create it if missing. Inspect the current branch before planning. For delivered work, plan and implement ONLY the attached addenda or delivery change request, preserving existing behavior outside the delta. For work not yet implemented, implement the full spec together with its addenda. Map each requested change to acceptance checks. Do not redo or archive the original proposal. Report each addendum id with concrete files and test evidence.`
226
+ : undefined;
227
+ const briefing = [delta, request.constants?.REVISION_REQUEST, request.followUp?.briefing, request.addenda?.briefing].filter(Boolean).join('\n\n');
215
228
  const spec = request.spec && briefing ? { ...request.spec, description: [request.spec.description, briefing].filter(Boolean).join('\n\n'), ...(request.spec.tickets ? { tickets: request.spec.tickets.map(ticket => ({ ...ticket, description: [ticket.description, briefing].filter(Boolean).join('\n\n') })) } : {}) } : request.spec;
216
229
  const env = { ...programmaticStepEnv(resolveEnv(), request.repoDir, request.executionManifest), SPECRAILS_GIT_AUTO: 'false' };
217
- const core = (0, core_execution_1.prepareCoreExecution)({ run: { runId, projectId: request.projectId, repositoryId: request.repositoryId, spec, goal: request.spec?.title ?? request.loopName }, cwd: request.cwd, repoDir: request.repoDir, manifest: request.executionManifest, env, sourcePath: request.executionManifest ? undefined : opts.sourcePath?.() });
230
+ const core = (0, core_execution_1.prepareCoreExecution)({ run: { runId, projectId: request.projectId, repositoryId: request.repositoryId, spec, goal: request.spec?.title ?? request.loopName }, cwd: request.cwd, repoDir: request.repoDir, manifest: request.executionManifest, env, sourcePath: request.executionManifest ? undefined : opts.sourcePath?.(), workspacePaths: request.executionManifest ? undefined : opts.workspacePaths?.(), selectedWorkspacePaths: request.workspacePaths });
218
231
  const result = await (0, agent_runtime_bridge_1.runAgentRuntimeInvocation)({
219
232
  contextPath: core.contextPath, cwd: request.cwd, env: core.env,
220
233
  configPath: runtimeConfigPath(request.cwd), engineVersion: 2,
234
+ loopConfig: request.graph.config.agents,
221
235
  change: seeded?.id ?? (0, agent_runtime_bridge_1.runtimeChangeName)(runId),
222
236
  resume: input.resume, answer: input.answer, approve: input.approve, interruptId: input.interruptId,
223
- defaultProvider: request.provider, providerOverride: request.runtimeProviderOverride,
224
- ...(request.graph.config.legacyDeciderRole ? { workflowRoleBindings: {
237
+ defaultProvider: request.provider, providerOverride: request.runtimeProviderOverride ?? (request.graph.config.agents ? { provider: request.provider, model: request.model, effort: request.effort } : undefined),
238
+ ...(!request.graph.config.agents && request.graph.config.legacyDeciderRole ? { workflowRoleBindings: {
225
239
  [request.graph.config.legacyDeciderRole]: {
226
240
  ...(request.deciderEngine ?? { provider: request.provider, model: request.model, effort: request.effort }),
227
241
  access: 'read', artifacts: 'none',
@@ -229,9 +243,10 @@ function createLoopExecutors(opts = {}) {
229
243
  } } : {}),
230
244
  ...(!input.resume ? { prepareDefinition: (config) => (0, loop_definition_1.compileLoopToDefinition)(request.graph, {
231
245
  id: request.loopId, title: request.loopName, spec: request.spec,
246
+ ...(request.graph.config.agents ? { loopAgents: config } : {}),
232
247
  constants: request.constants ?? {}, provider: request.provider, model: request.model, effort: request.effort,
233
248
  roles: { architect: { access: 'read' }, developer: { access: 'write' }, reviewer: { access: 'read' }, ...config.roles }, repositoryCount: request.executionManifest?.repositories.length ?? 1,
234
- changeId: seeded?.id, briefing,
249
+ changeId: seeded?.id, briefing: [briefing, core.workspaceBriefing].filter(Boolean).join('\n\n'), addendaIds: request.addenda?.ids,
235
250
  }) } : {}),
236
251
  onPrepared: input.onPrepared, onLine: input.onLine, onRuntimeEvent: input.onRuntimeEvent, onSpawn: input.onSpawn, timeoutMs: input.timeoutMs,
237
252
  });
@@ -297,7 +312,7 @@ function createLoopExecutors(opts = {}) {
297
312
  const existingContext = coreRun ? runtimeContextPath(cwd, coreRun.runId, baseEnv) : undefined;
298
313
  const programmatic = coreRun && existingContext && (coreRun.implementation || (coreRun.verificationStep && existsRuntimeRequest(existingContext)));
299
314
  const baseStepEnv = programmatic ? programmaticStepEnv(baseEnv, repoDir, executionManifest) : withProfileEnv(aiStepEnv(baseEnv, repoDir, executionManifest), provider, profileName);
300
- const core = coreRun ? (0, core_execution_1.prepareCoreExecution)({ run: coreRun, cwd, repoDir, manifest: executionManifest, env: baseStepEnv, sourcePath: executionManifest ? undefined : opts.sourcePath?.() }) : undefined;
315
+ const core = coreRun ? (0, core_execution_1.prepareCoreExecution)({ run: coreRun, cwd, repoDir, manifest: executionManifest, env: baseStepEnv, sourcePath: executionManifest ? undefined : opts.sourcePath?.(), workspacePaths: executionManifest ? undefined : opts.workspacePaths?.(), selectedWorkspacePaths: coreRun?.workspacePaths }) : undefined;
301
316
  const stepEnv = core?.env ?? baseStepEnv;
302
317
  if (coreRun?.implementation && core) {
303
318
  completionContexts.set(coreRun.runId, { cwd, contextPath: core.contextPath, env: stepEnv, runId: coreRun.runId });
@@ -536,7 +551,7 @@ function createLoopExecutors(opts = {}) {
536
551
  if (!adapter.capabilities.persistentStdin)
537
552
  return null;
538
553
  const baseStepEnv = withProfileEnv(aiStepEnv(resolveEnv(), repoDir, executionManifest), provider, profileName);
539
- const core = coreRun ? (0, core_execution_1.prepareCoreExecution)({ run: coreRun, cwd, repoDir, manifest: executionManifest, env: baseStepEnv, sourcePath: executionManifest ? undefined : opts.sourcePath?.() }) : undefined;
554
+ const core = coreRun ? (0, core_execution_1.prepareCoreExecution)({ run: coreRun, cwd, repoDir, manifest: executionManifest, env: baseStepEnv, sourcePath: executionManifest ? undefined : opts.sourcePath?.(), workspacePaths: executionManifest ? undefined : opts.workspacePaths?.(), selectedWorkspacePaths: coreRun?.workspacePaths }) : undefined;
540
555
  const stepEnv = core?.env ?? baseStepEnv;
541
556
  const extraArgs = aiStepExtraArgs(adapter, cwd, repoDir, executionManifest);
542
557
  if (repoDir) {
@@ -592,7 +607,7 @@ function createLoopExecutors(opts = {}) {
592
607
  const baseEnv = resolveEnv();
593
608
  const programmatic = existsRuntimeRequest(runtimeContextPath(cwd, coreRun.runId, baseEnv));
594
609
  const baseStepEnv = programmatic ? programmaticStepEnv(baseEnv, repoDir, executionManifest) : withProfileEnv(aiStepEnv(baseEnv, repoDir, executionManifest), provider, profileName);
595
- const core = (0, core_execution_1.prepareCoreExecution)({ run: coreRun, cwd, repoDir, manifest: executionManifest, env: baseStepEnv, sourcePath: executionManifest ? undefined : opts.sourcePath?.() });
610
+ const core = (0, core_execution_1.prepareCoreExecution)({ run: coreRun, cwd, repoDir, manifest: executionManifest, env: baseStepEnv, sourcePath: executionManifest ? undefined : opts.sourcePath?.(), workspacePaths: executionManifest ? undefined : opts.workspacePaths?.(), selectedWorkspacePaths: coreRun?.workspacePaths });
596
611
  if (existsRuntimeRequest(core.contextPath))
597
612
  return (0, core_execution_1.checkCoreCompletion)(core.contextPath, cwd, { ...programmaticStepEnv(resolveEnv(), repoDir, executionManifest), SPECRAILS_EXECUTION_CONTEXT: core.contextPath }, coreRun.runId);
598
613
  const env = (0, runtime_1.buildProviderEnv)((0, providers_1.getAdapter)(provider), { prompt: '', model, reasoning_effort: effort }, core.env);
@@ -94,7 +94,7 @@ const FACTORY_BY_ID = new Map([
94
94
  function factoryLoopsForCapabilities(capabilities) {
95
95
  if (capabilities?.engineV2 !== 1 || capabilities.workflowDefinitions !== 1)
96
96
  return exports.FACTORY_LOOPS;
97
- return exports.FACTORY_LOOPS.map(factory => ({ ...factory, graph: (0, loop_core_factory_1.coreFactoryGraph)(factory.id === 'factory:implement' ? 'implement' : factory.id === 'factory:freestyle' ? 'freestyle' : 'quick-sdd') }));
97
+ return exports.FACTORY_LOOPS.map(factory => ({ ...factory, graph: (0, loop_core_factory_1.coreFactoryGraph)(factory.id === 'factory:implement' ? 'implement' : factory.id === 'factory:freestyle' ? 'freestyle' : 'quick-sdd', capabilities.implementationSteps === 1, capabilities.workflowAgentSteps === 1) }));
98
98
  }
99
99
  function getFactoryLoop(id, capabilities) {
100
100
  const legacy = FACTORY_BY_ID.get(id);
@@ -1,14 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Loop graph model + validation — the pure core shared by the canvas builder
4
- * (`loop-builder-canvas`) and the publish gate (`loops-store.publishLoop`).
5
- *
6
- * A loop is a directed graph of typed nodes connected by edges. Cycles are
7
- * allowed (a loop routes back to an earlier node); the run is bounded by the
8
- * graph's `config.maxIterations` + `timeoutMinutes`. This module has NO I/O and
9
- * NO provider knowledge — it only describes and validates the shape, so it is
10
- * trivially unit-testable and reused verbatim by both the store and the engine.
11
- */
12
2
  Object.defineProperty(exports, "__esModule", { value: true });
13
3
  exports.CORE_NODE_KINDS = void 0;
14
4
  exports.isDefinitionReviewerPath = isDefinitionReviewerPath;
@@ -24,8 +14,8 @@ exports.successors = successors;
24
14
  exports.loopNeedsTicket = loopNeedsTicket;
25
15
  exports.interpolateSpec = interpolateSpec;
26
16
  exports.CORE_NODE_KINDS = ['prompt', 'role-turn', 'decider', 'condition', 'assign', 'verify', 'shell',
27
- 'openspec-validate', 'openspec-archive', 'approval', 'question', 'gate', 'map', 'join',
28
- 'component', 'implementation', 'end'];
17
+ 'artifact-contract', 'openspec-validate', 'openspec-archive', 'approval', 'question', 'gate', 'map', 'join',
18
+ 'component', 'implementation', 'implementation-step', 'end'];
29
19
  function isDefinitionReviewerPath(graph, value) {
30
20
  if (typeof value !== 'string' || !value || value.length > 2048)
31
21
  return false;
@@ -38,7 +28,7 @@ function isDefinitionReviewerPath(graph, value) {
38
28
  if (!node || node.type !== 'core')
39
29
  return false;
40
30
  if (index === segments.length - 1)
41
- return node.data?.kind === 'role-turn';
31
+ return node.data?.kind === 'role-turn' || node.data?.kind === 'implementation-step' && node.data.params?.phase === 'reviewer';
42
32
  const ref = node.data?.kind === 'component' ? node.data.params?.ref : node.data?.kind === 'map' ? node.data.params?.body : undefined;
43
33
  body = typeof ref === 'string' ? graph.components?.[ref] : undefined;
44
34
  }
@@ -176,6 +166,7 @@ function validateLoopGraph(graph, catalog) {
176
166
  (cfg.maxCostUsd !== undefined && !Number.isFinite(cfg.maxCostUsd)) ||
177
167
  (cfg.maxTransitions !== undefined && (!Number.isInteger(cfg.maxTransitions) || cfg.maxTransitions < 1 || cfg.maxTransitions > 10000)) ||
178
168
  (cfg.maxTokens !== undefined && (!Number.isInteger(cfg.maxTokens) || cfg.maxTokens < 1)) ||
169
+ (cfg.ticketScope !== undefined && !['all', 'per-ticket'].includes(cfg.ticketScope)) ||
179
170
  (cfg.reviewerStepId !== undefined && !isDefinitionReviewerPath(graph, cfg.reviewerStepId))) {
180
171
  errors.push({
181
172
  code: 'INVALID_CONFIG',
@@ -300,7 +291,7 @@ function loopNeedsTicket(graph) {
300
291
  if (!graph)
301
292
  return false;
302
293
  for (const node of graph.nodes) {
303
- if (node.type === 'core' && node.data?.kind === 'implementation')
294
+ if (node.type === 'core' && ['implementation', 'implementation-step'].includes(node.data?.kind ?? ''))
304
295
  return true;
305
296
  const text = [node.data?.prompt, node.data?.command, node.data?.goal,
306
297
  node.type === 'core' ? JSON.stringify(node.data?.params) : undefined]
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.configurableImplementGraph = configurableImplementGraph;
4
+ const loop_agents_1 = require("./loop-agents");
5
+ const objectSchema = (properties, required = Object.keys(properties)) => ({ type: 'object', properties, required, additionalProperties: false });
6
+ const string = { type: 'string' };
7
+ const strings = { type: 'array', items: string, maxItems: 100 };
8
+ const score = { type: 'number', minimum: 0, maximum: 100 };
9
+ const aspects = { type_correctness: 60, pattern_adherence: 60, test_coverage: 60, security: 75, architectural_alignment: 60 };
10
+ /** The recipe, roles, schemas and decisions are Desktop data, never Core phases. */
11
+ function configurableImplementGraph() {
12
+ const agents = (0, loop_agents_1.defaultLoopAgents)();
13
+ agents.roles = {
14
+ plan: { ...agents.agents.architect, access: 'read', artifacts: 'all', openspecSkill: 'openspec-ff-change', prompt: agents.rolePrompts?.architect },
15
+ build: { ...agents.agents.developer, access: 'write', artifacts: 'tasks-checkboxes', openspecSkill: 'openspec-apply-change', prompt: agents.rolePrompts?.developer },
16
+ assess: { ...agents.agents.reviewer, access: 'read', artifacts: 'none', openspecSkill: 'openspec-verify-change', prompt: agents.rolePrompts?.reviewer },
17
+ correct: { ...agents.fixer, access: 'write', artifacts: 'tasks-checkboxes', openspecSkill: 'openspec-apply-change', prompt: agents.rolePrompts?.fixer },
18
+ };
19
+ const nodes = [{ id: 'start', type: 'start', position: { x: 0, y: 0 } }], edges = [];
20
+ const node = (id, label, kind, params, outcomes) => {
21
+ nodes.push({ id, type: 'core', position: { x: 0, y: nodes.length * 140 }, data: { kind, label, params } });
22
+ for (const [outcome, target] of Object.entries(outcomes))
23
+ edges.push({ id: `e-${id}-${outcome}`, source: id, target, label: outcome });
24
+ };
25
+ const turn = (id, label, roleId, prompt, schema, next) => node(id, label, 'role-turn', { roleId, prompt, structuredOutput: schema, sessionContinuity: 'none' }, { next, invalid: 'failed', failed: 'failed' });
26
+ const change = '{{run.changeId}}';
27
+ turn('architect', 'Architect', 'plan', `Plan the frozen ticket requirements for OpenSpec change ${change}. Use the bound official fast-forward skill. Author complete proposal, design, delta specs and actionable tasks. Propose existing repository verification commands for repositories with no configured checks. Return confidence, question (empty when none), and verification [{repositoryId,command,args,cwd?}]. When revisiting low confidence, investigate the missing repository evidence before asking again. Context and human answers: {{history.text}}`, objectSchema({ confidence: { enum: ['high', 'medium', 'low'] }, question: string, verification: { type: 'array', maxItems: 20, items: objectSchema({ repositoryId: string, command: string, args: strings, cwd: string }, ['repositoryId', 'command', 'args']) } }), 'confidence');
28
+ node('confidence', 'Planning confidence', 'condition', { expr: '$outputs.architect.structured.confidence != "low"' }, { true: 'validate', false: 'investigate' });
29
+ node('investigate', 'Investigate low confidence once', 'condition', { expr: '!exists($vars.planInvestigated) || $vars.planInvestigated == false' }, { true: 'mark-investigated', false: 'clarify' });
30
+ node('mark-investigated', 'Mark confidence investigation', 'assign', { set: { planInvestigated: true } }, { next: 'architect', failed: 'failed' });
31
+ node('clarify', 'Clarify plan', 'question', { text: '{{outputs.architect.structured.question}}' }, { next: 'reset-confidence' });
32
+ node('reset-confidence', 'Reset confidence after answer', 'assign', { set: { planInvestigated: false } }, { next: 'architect', failed: 'failed' });
33
+ node('validate', 'Validate OpenSpec', 'openspec-validate', { change }, { pass: 'freeze', fail: 'architect', failed: 'failed' });
34
+ node('freeze', 'Freeze approved artifacts', 'artifact-contract', { change, contractId: 'plan', action: 'freeze' }, { pass: 'developer', fail: 'architect', failed: 'failed' });
35
+ const implementation = objectSchema({ summary: string, incomplete: strings });
36
+ turn('developer', 'Developer', 'build', `Implement every approved task for change ${change} using the bound official apply skill. Preserve proposal, design and specs; only tick tasks you completed. Report summary and incomplete task descriptions. Prior execution evidence and human answers: {{history.text}}`, implementation, 'tasks');
37
+ node('tasks', 'Check artifacts and completed tasks', 'artifact-contract', { change, contractId: 'plan', action: 'check', requireCompletedTasks: true }, { pass: 'verify', fail: 'developer', failed: 'failed' });
38
+ node('verify', 'Verify candidate', 'verify', { commands: 'configured', additionalCommandsFrom: 'architect' }, { pass: 'reviewer', fail: 'correction-context', failed: 'failed' });
39
+ turn('reviewer', 'Reviewer', 'assess', `Review change ${change} read-only against all approved artifacts and frozen ticket acceptance criteria. Inspect real code and the host verification evidence: {{outputs.verify}}. Return approved, summary, issues, score (0-100), and aspects. Prior execution: {{history.text}}`, objectSchema({ approved: { type: 'boolean' }, summary: string, issues: strings, score, aspects: objectSchema(Object.fromEntries(Object.keys(aspects).map(aspect => [aspect, score]))) }), 'review-policy');
40
+ node('review-policy', 'Review acceptance policy', 'condition', { expr: ['$outputs.reviewer.structured.approved == true', '$outputs.reviewer.structured.issues.length == 0', '$outputs.reviewer.structured.score >= 70', ...Object.entries(aspects).map(([aspect, threshold]) => `$outputs.reviewer.structured.aspects.${aspect} >= ${threshold}`)].join(' && ') }, { true: 'approve', false: 'correction-context' });
41
+ node('correction-context', 'Select current review findings', 'condition', { expr: '$outputs.verify.valid == true && exists($outputs.reviewer.candidateHash) && $outputs.reviewer.candidateHash == $outputs.verify.candidateHash' }, { true: 'capture-review', false: 'clear-review' });
42
+ node('capture-review', 'Capture current review findings', 'assign', { set: { latestReview: '{{outputs.reviewer}}' } }, { next: 'fixer', failed: 'failed' });
43
+ node('clear-review', 'Clear outdated review findings', 'assign', { set: { latestReview: 'No reviewer findings for the current verified candidate. Diagnose the current host verification failure first.' } }, { next: 'fixer', failed: 'failed' });
44
+ turn('fixer', 'Fixer', 'correct', `Correct the reported implementation, test or review failure for change ${change} using the bound apply skill and preserving approved artifacts. Read failureSummary, command, cwd, exitCode and evidenceId first; use read_verification_evidence for truncated evidence. Reproduce the exact focused failure, inspect the assertion and tested code, then apply the smallest repair that preserves the required behavior. An unchanged filename does not prove an unrelated failure. A mandatory test inside the admitted workspace may receive a proven formatting/compatibility repair while retaining every required guard. Report diagnosis, focused commands with original exit codes, repair and unresolved failures in summary and incomplete. If the cause cannot be repaired within scope, explain the evidence and required action; an unchanged candidate with failed verification stops this run. Host verification: {{outputs.verify}}. Latest review findings for this candidate: {{run.latestReview}}. Prior execution: {{history.text}}`, implementation, 'correction-progress');
45
+ node('correction-progress', 'Check correction progress', 'condition', { expr: '$outputs.verify.valid == true || (exists($outputs.fixer.candidateHash) && $outputs.fixer.candidateHash != null && $outputs.fixer.candidateHash != $outputs.verify.candidateHash)' }, { true: 'tasks', false: 'correction-stalled' });
46
+ node('approve', 'Approve archive', 'approval', { reason: `Approve archiving the reviewed and verified candidate for change ${change}.`, bindCandidate: true, enabled: agents.approvalBeforeArchive === true }, { next: 'approval-policy' });
47
+ node('approval-policy', 'Approval decision', 'condition', { expr: '$outputs.approve.response.approved == true' }, { true: 'archive', false: 'failed' });
48
+ node('archive', 'Archive OpenSpec', 'openspec-archive', { change, allowArchived: true, requiresVerified: true, reviewedCandidate: '{{outputs.reviewer.candidateHash}}', approvedCandidate: '{{outputs.approve.candidateHash}}' }, { next: 'verify-archive', failed: 'verify' });
49
+ node('verify-archive', 'Verify archived candidate', 'verify', { commands: 'configured', additionalCommandsFrom: 'architect' }, { pass: 'done', fail: 'failed', failed: 'failed' });
50
+ nodes.push({ id: 'done', type: 'end', position: { x: 0, y: nodes.length * 140 }, data: { outcome: 'success', requiresVerified: true } }, { id: 'failed', type: 'end', position: { x: 360, y: nodes.length * 140 }, data: { outcome: 'failure' } }, { id: 'correction-stalled', type: 'end', position: { x: 720, y: nodes.length * 140 }, data: { outcome: 'failure', reason: 'Verification remains failed and the correction made no candidate changes. {{outputs.fixer.structured.summary}}' } });
51
+ edges.unshift({ id: 'e-start', source: 'start', target: 'architect' });
52
+ return { nodes, edges, config: { maxIterations: 12, maxTransitions: 200, timeoutMinutes: 0, aiStepTimeoutMinutes: 0, change: 'new', journal: 'ledger-only', ticketScope: 'all', agents, reviewerStepId: 'reviewer' } };
53
+ }
@@ -145,29 +145,37 @@ const CHANGE_ID_RE = /openspec[\\/]+changes[\\/]+(?!archive(?:[\\/]|$))([A-Za-z0
145
145
  function resolveRunVars(text, vars) {
146
146
  return text.replace(RUN_TOKEN_RE, (_m, key) => vars[key] ?? '');
147
147
  }
148
- const CHANGE_NAME_RE = /^[a-z0-9][a-z0-9._-]{0,99}$/i;
148
+ const CHANGE_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
149
149
  /** The authoritative OpenSpec target for a launch.
150
- * Addenda/delivery deltas get a run-specific name; ordinary launches use the
150
+ * Addenda/delivery deltas use durable content/lineage identity; ordinary launches use the
151
151
  * follow-up's declared name, else the spec's `openspecChangeName`. */
152
152
  function seedChangeId(req) {
153
- if (req.addenda?.ids.length) {
154
- // A new generation never picks an old/archived proposal, even when the
155
- // ticket metadata still names it. Resuming the same run keeps its target.
156
- const hash = (0, crypto_1.createHash)('sha256').update(JSON.stringify([req.runId, req.addenda.ids, req.addenda.briefing])).digest('hex').slice(0, 20);
157
- return { id: `spec-addenda-${hash}`, source: 'addenda' };
158
- }
159
- if (req.constants?.REVISION_REQUEST) {
160
- const hash = (0, crypto_1.createHash)('sha256').update(JSON.stringify([req.runId, req.constants.REVISION_REQUEST])).digest('hex').slice(0, 20);
161
- return { id: `delivery-change-${hash}`, source: 'revision' };
162
- }
153
+ const valid = (value) => value.length <= 64 && CHANGE_NAME_RE.test(value);
163
154
  const fromFollowUp = typeof req.followUp?.openspecChangeName === 'string' ? req.followUp.openspecChangeName.trim() : '';
164
- if (fromFollowUp && CHANGE_NAME_RE.test(fromFollowUp))
165
- return { id: fromFollowUp, source: 'followUp' };
166
155
  const direct = typeof req.spec?.openspecChangeName === 'string' ? req.spec.openspecChangeName.trim() : '';
167
156
  const fromMetadata = typeof req.spec?.metadata?.openspecChangeName === 'string' ? req.spec.metadata.openspecChangeName.trim() : '';
168
- const fromSpec = direct || fromMetadata;
169
- if (fromSpec && CHANGE_NAME_RE.test(fromSpec))
170
- return { id: fromSpec, source: 'spec' };
157
+ const declared = valid(fromFollowUp) ? fromFollowUp : valid(direct || fromMetadata) ? direct || fromMetadata : '';
158
+ const source = declared === fromFollowUp ? 'followUp' : 'spec';
159
+ if (req.addenda?.ids.length || req.constants?.REVISION_REQUEST) {
160
+ const state = declared ? openspecChangeState(req.cwd, declared) : 'missing';
161
+ // A pending implementation's addenda refine the same change. Only work
162
+ // already delivered/archived needs a separate delta proposal.
163
+ if (declared && (state === 'active' || (state === 'missing' && !req.constants?.REVISION_REQUEST && req.spec?.status !== 'on_review')))
164
+ return { id: declared, source };
165
+ const hash = (0, crypto_1.createHash)('sha256').update(JSON.stringify([
166
+ req.constants?.REVISION_OF_DELIVERY_ID ?? null,
167
+ req.constants?.REVISION_NUMBER ?? null,
168
+ req.constants?.REVISION_REQUEST ?? null,
169
+ req.addenda ? [...req.addenda.ids].sort() : [], req.addenda?.briefing ?? null,
170
+ ])).digest('hex').slice(0, 12);
171
+ const revisionNumber = req.constants?.REVISION_NUMBER;
172
+ const kind = req.addenda?.ids.length ? 'addenda' : 'revision';
173
+ const suffix = req.constants?.REVISION_REQUEST ? `rev${revisionNumber && /^[1-9]\d{0,3}$/.test(revisionNumber) ? revisionNumber : '1'}-${hash}` : `addenda-${hash}`;
174
+ const stem = declared || (req.ticketId && req.ticketId > 0 ? `ticket-${req.ticketId}` : 'spec');
175
+ return { id: `${stem.slice(0, 63 - suffix.length).replace(/-+$/, '')}-${suffix}`, source: kind };
176
+ }
177
+ if (declared)
178
+ return { id: declared, source };
171
179
  return undefined;
172
180
  }
173
181
  /** Where an OpenSpec change currently lives under `cwd`: an ACTIVE
@@ -498,7 +506,7 @@ class LoopRunManager {
498
506
  if ((0, loop_graph_1.isDefinitionGraph)(graph)) {
499
507
  if (!this.executors.runDefinition)
500
508
  throw new Error('engine_unsupported: Core workflow execution is unavailable');
501
- await this.executors.assertDefinitionSupport?.();
509
+ await this.executors.assertDefinitionSupport?.(graph);
502
510
  }
503
511
  else
504
512
  await this.executors.assertLegacyEngineSupport?.();
@@ -854,6 +862,8 @@ class LoopRunManager {
854
862
  });
855
863
  if (definitionEngine)
856
864
  (0, loop_runs_store_1.saveDefinitionRun)(this.db, runId, { request: req, source: 'definition' });
865
+ if (req.launchConfig)
866
+ this.db.prepare('UPDATE loop_runs SET launch_config_json = ? WHERE id = ?').run(JSON.stringify(req.launchConfig), runId);
857
867
  if (req.executionManifest)
858
868
  this.db.prepare('UPDATE loop_runs SET execution_manifest = ? WHERE id = ?').run(JSON.stringify(req.executionManifest), runId);
859
869
  (0, db_1.createJob)(this.db, {
@@ -1422,7 +1432,7 @@ class LoopRunManager {
1422
1432
  const effectiveTimeoutMs = Number.isFinite(remainingMs)
1423
1433
  ? Math.max(1, Math.min(aiStepTimeoutMs === 0 ? Infinity : (aiStepTimeoutMs ?? 15 * 60_000), remainingMs))
1424
1434
  : aiStepTimeoutMs;
1425
- const coreRun = { runId, implementation: requiresCoreCompletion, spec: req.spec, goal: req.spec ? undefined : JSON.stringify({ loop: req.loopName, graph: req.graph }), repositoryId: req.repositoryId, verificationStep: node.data?.requireVerificationPass === true || /\{\{\s*cmd:(?:verify|revision-verify|opsx:verify)\s*\}\}/.test(rawTemplate) };
1435
+ const coreRun = { runId, implementation: requiresCoreCompletion, spec: req.spec, goal: req.spec ? undefined : JSON.stringify({ loop: req.loopName, graph: req.graph }), repositoryId: req.repositoryId, workspacePaths: req.workspacePaths, verificationStep: node.data?.requireVerificationPass === true || /\{\{\s*cmd:(?:verify|revision-verify|opsx:verify)\s*\}\}/.test(rawTemplate) };
1426
1436
  const interactivePlan = this.executors.planInteractiveAiStep?.({
1427
1437
  coreRun,
1428
1438
  provider: nodeProvider,
@@ -1552,7 +1562,7 @@ class LoopRunManager {
1552
1562
  && requiresCoreCompletion
1553
1563
  && this.executors.validateCoreCompletion) {
1554
1564
  const completion = this.executors.validateCoreCompletion({
1555
- coreRun: { runId, spec: req.spec, goal: req.spec ? undefined : JSON.stringify({ loop: req.loopName, graph: req.graph }), repositoryId: req.repositoryId },
1565
+ coreRun: { runId, spec: req.spec, goal: req.spec ? undefined : JSON.stringify({ loop: req.loopName, graph: req.graph }), repositoryId: req.repositoryId, workspacePaths: req.workspacePaths },
1556
1566
  provider: nodeProvider, model: nodeModel, effort: nodeEffort, profileName: req.profileName,
1557
1567
  cwd: req.cwd, repoDir: req.repoDir, executionManifest: req.executionManifest,
1558
1568
  });
@@ -8,6 +8,8 @@ exports.coreStarterGraph = coreStarterGraph;
8
8
  exports.supportsCoreTemplates = supportsCoreTemplates;
9
9
  exports.loopTemplatesForCapabilities = loopTemplatesForCapabilities;
10
10
  exports.getLoopTemplate = getLoopTemplate;
11
+ const loop_core_factory_1 = require("./loop-core-factory");
12
+ const loop_agents_1 = require("./loop-agents");
11
13
  /** Closed taxonomy a template's `category` must belong to. Single source of truth
12
14
  * for the gallery's category chips (the client derives its chip list from the
13
15
  * categories actually present in the served catalog). */
@@ -303,7 +305,8 @@ exports.LEGACY_LOOP_TEMPLATES = exports.LOOP_TEMPLATES;
303
305
  // - every mutating starter runs the real `verify` piece over the project's
304
306
  // configured checks and ends with `requiresVerified: true` — a passing sentinel
305
307
  // alone never reaches a success end;
306
- // - ship-and-green = native `implementation` → verify → decider → prompt(fix) loop;
308
+ // - ship-and-green expands implementation operations when supported, then
309
+ // verify → decider → prompt(fix); old Core retains the compatibility wrapper;
307
310
  // - prompts, goals and iteration caps come from STARTER_TEXT (legacy parity).
308
311
  /** Fix step shared by every mutating Core starter. Reuses the tuned `{{cmd:fix}}`
309
312
  * contract and covers the verify→fix edge, where no Decider verdict precedes it. */
@@ -324,7 +327,7 @@ function watcherVerdict(goal) {
324
327
  const CORE_GOAL_EVIDENCE = 'The configured host verification already passed on the current candidate before this decision. Judge only from concrete evidence in the history (commands run, their output, files changed); green baseline checks, setup or planning alone do not prove the goal.';
325
328
  const SHIP_AND_GREEN_GOAL = 'Stop only when the history proves the spec is implemented: every acceptance criterion maps to real code and behavioral evidence across every selected ticket and repository, and the configured host verification passed on the current candidate. Setup, planning, a launched subagent or green baseline checks alone are insufficient; continue when work is missing or incomplete.';
326
329
  /** Wall-clock cap for starters (legacy default). ship-and-green is untimed like the
327
- * Implement factory: the native implementation piece runs the whole pipeline. */
330
+ * Implement factory: implementation operations retain their own execution limits. */
328
331
  const STARTER_TIMEOUT_MIN = 30;
329
332
  const CORE_STARTER_SHAPES = {
330
333
  'ship-and-green': 'ship',
@@ -338,8 +341,10 @@ const CORE_STARTER_SHAPES = {
338
341
  };
339
342
  /** Core definition graph for one of the eight starters. Deterministic: same id ⇒
340
343
  * structurally identical graph (positions included). */
341
- function coreStarterGraph(id) {
344
+ function coreStarterGraph(id, independent = false, configurable = false) {
342
345
  const shape = CORE_STARTER_SHAPES[id];
346
+ if (shape === 'ship' && configurable)
347
+ return (0, loop_core_factory_1.coreFactoryGraph)('implement', true, true);
343
348
  const { prompt, goal, maxIterations } = STARTER_TEXT[id];
344
349
  const nodes = [{ id: 'start', type: 'start', position: { x: COL_X, y: 0 } }];
345
350
  const edges = [];
@@ -381,10 +386,29 @@ function coreStarterGraph(id) {
381
386
  }
382
387
  edges.unshift({ id: 'e-start', source: 'start', target: entry });
383
388
  nodes.push({ id: 'done', type: 'end', position: { x: COL_X, y: ROW_GAP * nodes.length }, data: successEnd }, { id: 'failed', type: 'end', position: { x: COL_RIGHT_X, y: ROW_GAP * nodes.length }, data: { outcome: 'failure' } });
389
+ if (independent) {
390
+ config.agents = (0, loop_agents_1.defaultLoopAgents)(shape === 'ship' ? 'implementation' : 'free');
391
+ if (shape === 'ship') {
392
+ const operations = (0, loop_core_factory_1.coreFactoryGraph)('implement', true);
393
+ const path = (id) => id === 'done' ? 'verify' : id === 'failed' ? 'failed' : `implementation-${id}`;
394
+ nodes.splice(nodes.findIndex(node => node.id === 'implement'), 1);
395
+ const remove = new Set(edges.filter(edge => edge.source === 'implement').map(edge => edge.id));
396
+ for (let i = edges.length - 1; i >= 0; i--)
397
+ if (remove.has(edges[i].id))
398
+ edges.splice(i, 1);
399
+ for (const edge of edges)
400
+ if (edge.target === 'implement')
401
+ edge.target = path('architect');
402
+ for (const node of operations.nodes.filter(node => node.type === 'core'))
403
+ nodes.push({ ...node, id: path(node.id) });
404
+ for (const edge of operations.edges.filter(edge => edge.source !== 'start'))
405
+ edges.push({ ...edge, id: 'implementation-' + edge.id, source: path(edge.source), target: path(edge.target) });
406
+ }
407
+ }
384
408
  return { nodes, edges, config };
385
409
  }
386
410
  const CORE_STARTER_DESCRIPTIONS = {
387
- 'ship-and-green': 'Fully autonomous: run Core\'s native implementation pipeline for the spec, then verify with the project\'s configured checks and refine (fix) on failure — verify → decide → fix until green. Success requires a verified candidate; no human intervention.',
411
+ 'ship-and-green': 'Fully autonomous: run the loop\'s implementation steps for the spec, then verify with the project\'s configured checks and refine (fix) on failure — verify → decide → fix until green. Success requires a verified candidate; no human intervention.',
388
412
  'verify-pass': 'Autonomous verify-and-fix: run the project\'s configured build/lint/tests through Core\'s verify piece, then refine on failure — verify → fix → verify until green.',
389
413
  'ci-watch': 'Poll CI checks on the open PR (the agent uses the repo\'s CI tooling) until every check is green. Read-only: the watcher never edits the repository.',
390
414
  'lint-and-fix': 'The agent detects and runs the project\'s linter and fixes every issue, iterating until the codebase is clean. Every pass is confirmed by the project\'s configured verification before the loop can succeed.',
@@ -414,7 +438,7 @@ function loopTemplatesForCapabilities(capabilities) {
414
438
  ...template,
415
439
  description: CORE_STARTER_DESCRIPTIONS[template.id],
416
440
  ...(readOnly ? { readOnly: true } : {}),
417
- graph: coreStarterGraph(template.id),
441
+ graph: coreStarterGraph(template.id, capabilities?.implementationSteps === 1, capabilities?.workflowAgentSteps === 1),
418
442
  };
419
443
  });
420
444
  }
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.registerLoopsRoutes = registerLoopsRoutes;
4
+ const loop_agents_1 = require("./loop-agents");
5
+ const agent_runtime_settings_1 = require("../../agent-runtime/runtime/agent-runtime-settings");
4
6
  const ids_1 = require("../../../ids");
5
7
  const feature_flags_1 = require("../../../feature-flags");
6
8
  const loops_store_1 = require("./loops-store");
@@ -57,6 +59,12 @@ function registerLoopsRoutes(router, deps) {
57
59
  }
58
60
  return true;
59
61
  }
62
+ router.get('/loop-agent-defaults', async (_req, res) => {
63
+ if (!guard(res))
64
+ return;
65
+ const runtime = await (0, agent_runtime_loader_1.loadCoreAgentRuntime)().catch(() => null);
66
+ res.json({ agents: (0, loop_agents_1.defaultLoopAgents)(), guardrails: { supported: runtime?.api?.capabilities?.configurableGuardrails === 1, catalog: runtime?.api?.guardrails ?? [] } });
67
+ });
60
68
  // ── Templates ──────────────────────────────────────────────────────────────
61
69
  router.get('/loop-templates', async (_req, res) => {
62
70
  if (!guard(res))
@@ -410,6 +418,15 @@ function registerLoopsRoutes(router, deps) {
410
418
  res.status(409).json({ error: 'engine_unsupported', message: 'Update Core to publish executable workflows.' });
411
419
  return;
412
420
  }
421
+ if (current.graph.config.agents) {
422
+ try {
423
+ (0, agent_runtime_settings_1.validateLoopRuntimeSettings)(current.graph.config.agents);
424
+ }
425
+ catch (error) {
426
+ res.status(422).json({ error: 'invalid_loop_agents', message: error instanceof Error ? error.message : 'Invalid loop agent settings' });
427
+ return;
428
+ }
429
+ }
413
430
  let draft;
414
431
  try {
415
432
  draft = (0, loop_definition_1.compileLoopToDefinition)(current.graph, { id: current.id, title: current.name, constants: (0, loop_constants_1.loadConstantMap)(db), provider: 'claude', spec: { id: 1, title: 'Sample spec', description: 'Publication preview' } });