specrails-desktop 2.29.1 → 2.29.2

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 (544) hide show
  1. package/client/dist/assets/ActivityFeedPage-ClpD5CFj.js +1 -0
  2. package/client/dist/assets/AgentBrowserCapture-CSPs9Ofc.js +1 -0
  3. package/client/dist/assets/AgentModeCodePane-DwcyklA5.js +2 -0
  4. package/client/dist/assets/AgentModeJobsPane-C69bqlMa.js +1 -0
  5. package/client/dist/assets/AgentsPage-pRiRr9-W.js +86 -0
  6. package/client/dist/assets/AnalyticsPage-C14exKsC.js +1 -0
  7. package/client/dist/assets/BarChart-nBOMNfiy.js +33 -0
  8. package/client/dist/assets/CodePage-CK76JKm9.js +2 -0
  9. package/client/dist/assets/DesktopAnalyticsPage-Cnv0f3XQ.js +1 -0
  10. package/client/dist/assets/DocsDialog-CHUm_Ym0.js +11 -0
  11. package/client/dist/assets/DocsPage-COI-oDok.js +11 -0
  12. package/client/dist/assets/ExportDropdown-BrtfWzEG.js +1 -0
  13. package/client/dist/assets/IntegrationsPage-Bs00_tl4.js +1 -0
  14. package/client/dist/assets/InteractiveJobComposer-CypM7R1F.js +19 -0
  15. package/client/dist/assets/JobDetailModal-B_W0H9dw.js +1 -0
  16. package/client/dist/assets/JobDetailPage-CeeXOoKS.js +1 -0
  17. package/client/dist/assets/JobsPage-CwhJG9jw.js +1 -0
  18. package/client/dist/assets/LoopBuilderPage-C51t1WOM.js +7 -0
  19. package/client/dist/assets/LoopPreviewModal-Y2HgtIJu.js +1 -0
  20. package/client/dist/assets/LoopsPage-Bvn1Kw5V.js +1 -0
  21. package/client/dist/assets/MinimizedChatsContext-DiFvZDPW.js +1 -0
  22. package/client/dist/assets/PluginsPage-BGPRJpdf.js +2 -0
  23. package/client/dist/assets/ProjectSettingsDialog-D_GTyGLc.js +1 -0
  24. package/client/dist/assets/TemplatePreviewModal-szwBhd7h.js +1 -0
  25. package/client/dist/assets/TicketDetailModal-9UEyK974.js +14 -0
  26. package/client/dist/assets/Trans-B_vKZ-Hb.js +1 -0
  27. package/client/dist/assets/activity-DiyjsgQV.js +1 -0
  28. package/client/dist/assets/addon-image-BRmmRHgQ.js +1 -0
  29. package/client/dist/assets/addon-ligatures-qIZUPvRV.js +2 -0
  30. package/client/dist/assets/{addon-webgl-DMbGeI_u.js → addon-webgl-C2d7lriQ.js} +3 -3
  31. package/client/dist/assets/addspec-Rw8uVBC1.js +1 -0
  32. package/client/dist/assets/agent-4rmXoo_p.js +1 -0
  33. package/client/dist/assets/agent-BxZuNGih.js +1 -0
  34. package/client/dist/assets/agent-C3uXOYv_.js +1 -0
  35. package/client/dist/assets/agent-CA8Y4wbw.js +1 -0
  36. package/client/dist/assets/agent-CASKrhG12.js +1 -0
  37. package/client/dist/assets/agent-Cv-jud5n2.js +1 -0
  38. package/client/dist/assets/agent-DitUxYh2.js +1 -0
  39. package/client/dist/assets/agent-Doow4X7o.js +1 -0
  40. package/client/dist/assets/agents-DhG7mg6-.js +1 -0
  41. package/client/dist/assets/agentstudio-BIUe1Im2.js +1 -0
  42. package/client/dist/assets/aiedit-iIjz2_Qk.js +1 -0
  43. package/client/dist/assets/analytics-DZ8Aoq3I.js +1 -0
  44. package/client/dist/assets/api-B2M7CtDw.js +1 -0
  45. package/client/dist/assets/arrow-left-CpIR4j7G.js +1 -0
  46. package/client/dist/assets/arrow-up-right-9DoMn7fD.js +1 -0
  47. package/client/dist/assets/attachments-B53PG733.js +1 -0
  48. package/client/dist/assets/brain-DI9lHkKY.js +1 -0
  49. package/client/dist/assets/browser-CXUcnTMI.js +1 -0
  50. package/client/dist/assets/button-BUIIBQPT.js +1 -0
  51. package/client/dist/assets/chat-BGD7MXAy.js +1 -0
  52. package/client/dist/assets/chevron-right-CViMxucY.js +1 -0
  53. package/client/dist/assets/chunk-KS7C4IRE-Cx5sqopX.js +3 -0
  54. package/client/dist/assets/circle-x-CMYsN4ZH.js +1 -0
  55. package/client/dist/assets/code-BDXEM4ml.js +1 -0
  56. package/client/dist/assets/commands-DyUZQ1FI.js +1 -0
  57. package/client/dist/assets/common-CD1QHriA2.js +1 -0
  58. package/client/dist/assets/common-CwwPaRxx.js +1 -0
  59. package/client/dist/assets/common-DVQuhs-m.js +1 -0
  60. package/client/dist/assets/common-Dg2jaX7d.js +1 -0
  61. package/client/dist/assets/common-DuBwj6tK.js +1 -0
  62. package/client/dist/assets/common-MRj9MHcc.js +1 -0
  63. package/client/dist/assets/common-O4ZpnMLp.js +1 -0
  64. package/client/dist/assets/common-fyeF0v7C2.js +1 -0
  65. package/client/dist/assets/createLucideIcon-Bjbbp_KS.js +1 -0
  66. package/client/dist/assets/{css.worker-Wv5dxAWO.js → css.worker-yKcq13f2.js} +13 -13
  67. package/client/dist/assets/{cssMode-DBHbkBvI.js → cssMode-BM4z9OX0.js} +1 -1
  68. package/client/dist/assets/dashboard-B_NkSHYm.js +1 -0
  69. package/client/dist/assets/dashboard-ClBb_3pq.js +1 -0
  70. package/client/dist/assets/dashboard-CpCmJvMB.js +1 -0
  71. package/client/dist/assets/dashboard-Czohu35O2.js +1 -0
  72. package/client/dist/assets/dashboard-DcjbD0UK.js +1 -0
  73. package/client/dist/assets/dashboard-Dvx5F3we.js +1 -0
  74. package/client/dist/assets/dashboard-R1bOPjBh2.js +1 -0
  75. package/client/dist/assets/dashboard-owcDucDi.js +1 -0
  76. package/client/dist/assets/dialog-CzIekCw0.js +45 -0
  77. package/client/dist/assets/dist-Cez5CRkX.js +1 -0
  78. package/client/dist/assets/dist-bVqfYmby.js +1 -0
  79. package/client/dist/assets/{dist-js-CgpP6tKI.js → dist-js-CcBCbb32.js} +1 -1
  80. package/client/dist/assets/{dist-js-DHiN-A_E.js → dist-js-VwztRkGj.js} +1 -1
  81. package/client/dist/assets/download-CFHyyHcw.js +1 -0
  82. package/client/dist/assets/editor.api2-4ndOupAA.js +872 -0
  83. package/client/dist/assets/{editor.main-C9J2ikJK.js → editor.main-DxrmGfoq.js} +2 -2
  84. package/client/dist/assets/editor.worker-tytyn86m.js +26 -0
  85. package/client/dist/assets/explore-DgBACn39.js +1 -0
  86. package/client/dist/assets/external-link-BcU5EWZp.js +1 -0
  87. package/client/dist/assets/file-text-CFT-xXKN.js +1 -0
  88. package/client/dist/assets/formatDistanceToNow-ByHIauTQ.js +1 -0
  89. package/client/dist/assets/{freemarker2-J5iIIIK-.js → freemarker2-CELlTDJ2.js} +1 -1
  90. package/client/dist/assets/getTimezoneOffsetInMilliseconds-BxHAksaU.js +1 -0
  91. package/client/dist/assets/git-branch-BRM0F5GN.js +1 -0
  92. package/client/dist/assets/{handlebars-DSw1l_tw.js → handlebars-NEjHJ7o_.js} +1 -1
  93. package/client/dist/assets/{html-h2-wE1Lj.js → html-CA10zN5A.js} +1 -1
  94. package/client/dist/assets/{html.worker-CQP8QQsS.js → html.worker-DSJ9zmd7.js} +12 -12
  95. package/client/dist/assets/{htmlMode-Cc_ij7lW.js → htmlMode-89rPYiyq.js} +1 -1
  96. package/client/dist/assets/i18n-CgaRxpII.js +2 -0
  97. package/client/dist/assets/index-BflYQpWm.css +2 -0
  98. package/client/dist/assets/index-C1_-BTXy.js +72 -0
  99. package/client/dist/assets/integrations-mB-7zoyT.js +1 -0
  100. package/client/dist/assets/{javascript-C1q3xv6N.js → javascript-B1gDmwzS.js} +1 -1
  101. package/client/dist/assets/jira-CmKWERE0.js +1 -0
  102. package/client/dist/assets/jira-api-BgFIr8eD.js +1 -0
  103. package/client/dist/assets/job-time-BCL7wee9.js +1 -0
  104. package/client/dist/assets/jobs-B-sDHEay2.js +1 -0
  105. package/client/dist/assets/jobs-BCtGfgUA.js +1 -0
  106. package/client/dist/assets/jobs-BHR_EnDI.js +1 -0
  107. package/client/dist/assets/jobs-BMbZJjJJ.js +1 -0
  108. package/client/dist/assets/jobs-CVFD8rar.js +1 -0
  109. package/client/dist/assets/jobs-DoSaFzAa.js +1 -0
  110. package/client/dist/assets/jobs-DzSIR8V62.js +1 -0
  111. package/client/dist/assets/jobs-lk1_Dq1U.js +1 -0
  112. package/client/dist/assets/json.worker-Oa9UrkKE.js +58 -0
  113. package/client/dist/assets/{jsonMode-CeaCdEhr.js → jsonMode-xHLMfzhF.js} +1 -1
  114. package/client/dist/assets/jsx-runtime-zu2_FqZY.js +1 -0
  115. package/client/dist/assets/lib-C7Mhm-gT.js +7 -0
  116. package/client/dist/assets/lib-CGdyxIWg.js +29 -0
  117. package/client/dist/assets/{liquid-eFsVK0V-.js → liquid-BY6hCUXG.js} +1 -1
  118. package/client/dist/assets/loader-circle-DcygOQpC.js +1 -0
  119. package/client/dist/assets/loops-BzfXWEwO.js +1 -0
  120. package/client/dist/assets/{lspLanguageFeatures-DT3-bbhn.js → lspLanguageFeatures-ira2aEJh.js} +1 -1
  121. package/client/dist/assets/mcp-uDkXYu4L.js +1 -0
  122. package/client/dist/assets/{mdx-Fuwnh1cX.js → mdx-BNx0xr3N.js} +1 -1
  123. package/client/dist/assets/monaco.contribution-Bt92R3Vw.js +2 -0
  124. package/client/dist/assets/nav-myFaXRHD.js +1 -0
  125. package/client/dist/assets/pencil-C9RkrIPZ.js +1 -0
  126. package/client/dist/assets/play-DNlrawbS.js +1 -0
  127. package/client/dist/assets/plus-CsnjfkmX.js +1 -0
  128. package/client/dist/assets/preload-helper-Czpn1I53.js +1 -0
  129. package/client/dist/assets/{python-BgoEs6Cj.js → python-DLdbtmNw.js} +1 -1
  130. package/client/dist/assets/{razor-CM1sPMMq.js → razor-B9gdhAbR.js} +1 -1
  131. package/client/dist/assets/react-dom-DiWzBDP9.js +1 -0
  132. package/client/dist/assets/rolldown-runtime-CNC7AqOf.js +1 -0
  133. package/client/dist/assets/save-28sIlCdr.js +1 -0
  134. package/client/dist/assets/search-Cq_gE3Ik.js +1 -0
  135. package/client/dist/assets/settings-B0ypw9uw.js +1 -0
  136. package/client/dist/assets/setup-CmK87LuS.js +1 -0
  137. package/client/dist/assets/sparkles-DrY8hb0X.js +1 -0
  138. package/client/dist/assets/specs-DII0C8gw.js +1 -0
  139. package/client/dist/assets/spending-C4p06nen.js +1 -0
  140. package/client/dist/assets/terminal-BFRZnMPP.js +1 -0
  141. package/client/dist/assets/tickets-B8BuAaj5.js +1 -0
  142. package/client/dist/assets/tickets-B_yh4qQk.js +1 -0
  143. package/client/dist/assets/tickets-Bs37scwY2.js +1 -0
  144. package/client/dist/assets/tickets-CFk2n2Or.js +1 -0
  145. package/client/dist/assets/tickets-DQp7JS2R2.js +1 -0
  146. package/client/dist/assets/tickets-D_yDquEw.js +1 -0
  147. package/client/dist/assets/tickets-DpmdE8tv2.js +1 -0
  148. package/client/dist/assets/tickets-pAUJkDTu.js +1 -0
  149. package/client/dist/assets/trash-2-BOU3WdZZ.js +1 -0
  150. package/client/dist/assets/triangle-alert-H9T4jAmh.js +1 -0
  151. package/client/dist/assets/{ts.worker-METxwbDZ.js → ts.worker-BtjBFNHX.js} +48 -33
  152. package/client/dist/assets/{tsMode-BmT8WogI.js → tsMode-DEjSThU9.js} +1 -1
  153. package/client/dist/assets/{typescript-CQrQPsoW.js → typescript-CRRnD2Ts.js} +1 -1
  154. package/client/dist/assets/upload-DYMjAQ6M.js +1 -0
  155. package/client/dist/assets/useBrowserCaptureSession-2OqX0vap.js +1 -0
  156. package/client/dist/assets/useDesktop-X8YB9xzv.js +1 -0
  157. package/client/dist/assets/useSharedWebSocket-DaPh7QBL.js +1 -0
  158. package/client/dist/assets/utils-BC4DHlz2.js +1 -0
  159. package/client/dist/assets/value-s-gzBeyF.js +1 -0
  160. package/client/dist/assets/{workers-U0UXyLUf.js → workers-uOo7WLaa.js} +1 -1
  161. package/client/dist/assets/x-CBMI9ejn.js +1 -0
  162. package/client/dist/assets/{xml-WRxJs8_I.js → xml-EDREH2X7.js} +1 -1
  163. package/client/dist/assets/{yaml-CjpSTjJU.js → yaml-BjaDVX1E.js} +1 -1
  164. package/client/dist/assets/zap-CHo3aCD1.js +1 -0
  165. package/client/dist/index.html +73 -32
  166. package/docs/guide/en/integrations/5-mcp-server.md +2 -2
  167. package/docs/internals/safe-pr-review-flow.md +190 -132
  168. package/docs/mcp.md +15 -5
  169. package/package.json +11 -7
  170. package/server/dist/active-pr-continuation.js +16 -1
  171. package/server/dist/agent-chat-manager.js +360 -181
  172. package/server/dist/agent-cwd-manager.js +52 -8
  173. package/server/dist/agent-generator.js +90 -48
  174. package/server/dist/agent-mcp-config.js +69 -36
  175. package/server/dist/agent-operator-prompt.js +3 -2
  176. package/server/dist/agent-refine-manager.js +47 -6
  177. package/server/dist/agent-tier.js +13 -11
  178. package/server/dist/artifact-registry.js +33 -2
  179. package/server/dist/auth.js +5 -4
  180. package/server/dist/browser-capture-manager.js +92 -29
  181. package/server/dist/chat-manager.js +302 -46
  182. package/server/dist/contract-refine-runner.js +19 -6
  183. package/server/dist/db.js +665 -8
  184. package/server/dist/desktop-router.js +1 -2
  185. package/server/dist/headroom-manager.js +1310 -121
  186. package/server/dist/headroom-routing.js +120 -2
  187. package/server/dist/index.js +48 -13
  188. package/server/dist/interactive-job-session.js +237 -82
  189. package/server/dist/jira/jira-adf.js +10 -0
  190. package/server/dist/jira/jira-db.js +15 -3
  191. package/server/dist/jira/jira-sync-manager.js +182 -13
  192. package/server/dist/job-listing.js +140 -0
  193. package/server/dist/job-spawn-idempotency.js +67 -0
  194. package/server/dist/loop-run-manager.js +508 -98
  195. package/server/dist/loop-runs-store.js +266 -13
  196. package/server/dist/mcp/agent-capability.js +71 -0
  197. package/server/dist/mcp/guide.js +5 -2
  198. package/server/dist/mcp/mcp-server.js +5 -10
  199. package/server/dist/mcp/mcp-token.js +5 -5
  200. package/server/dist/mcp/tools/jobs.js +20 -13
  201. package/server/dist/mcp/tools/types.js +20 -42
  202. package/server/dist/mobile/mobile-router.js +1 -1
  203. package/server/dist/pr-lifecycle.js +77 -0
  204. package/server/dist/pr-publisher.js +94 -27
  205. package/server/dist/process-admission.js +82 -0
  206. package/server/dist/project-registry.js +572 -174
  207. package/server/dist/project-router-git.js +13 -2
  208. package/server/dist/project-router-jobs.js +111 -55
  209. package/server/dist/project-router-tickets.js +32 -2
  210. package/server/dist/providers/claude-adapter.js +21 -13
  211. package/server/dist/queue-manager.js +2332 -623
  212. package/server/dist/rail-isolated-launch.js +1137 -290
  213. package/server/dist/rail-isolation.js +4 -2
  214. package/server/dist/rail-pr-decision.js +740 -266
  215. package/server/dist/rail-pr-delivery.js +2 -1
  216. package/server/dist/rail-pr-store.js +317 -18
  217. package/server/dist/rail-pr-ticket-effects.js +328 -0
  218. package/server/dist/rail-worktree-release.js +18 -2
  219. package/server/dist/rails-router.js +149 -119
  220. package/server/dist/rails-store.js +106 -3
  221. package/server/dist/ticket-store.js +23 -1
  222. package/server/dist/transient-children.js +13 -0
  223. package/server/dist/util/cli-prompt.js +42 -8
  224. package/server/dist/util/secure-token-file.js +84 -0
  225. package/server/dist/worktree-manager.js +6 -5
  226. package/client/dist/assets/ActivityFeedPage-D8RKRRoM.js +0 -1
  227. package/client/dist/assets/AgentBrowserCapture-BUHPHNSN.js +0 -1
  228. package/client/dist/assets/AgentModeCodePane-GZc8bNGT.js +0 -2
  229. package/client/dist/assets/AgentModeJobsPane-DWtQqj3k.js +0 -1
  230. package/client/dist/assets/AgentsPage-CKPtHkRP.js +0 -86
  231. package/client/dist/assets/AnalyticsPage-CZW8c77k.js +0 -1
  232. package/client/dist/assets/BarChart-DugEoe2q.js +0 -33
  233. package/client/dist/assets/CodePage-CQ-uKETG.js +0 -2
  234. package/client/dist/assets/DesktopAnalyticsPage-Bgo4SyCH.js +0 -1
  235. package/client/dist/assets/DocsDialog-DxpWw_La.js +0 -11
  236. package/client/dist/assets/DocsPage-CmcOkK19.js +0 -11
  237. package/client/dist/assets/ExportDropdown-DvTS2wc7.js +0 -1
  238. package/client/dist/assets/IntegrationsPage-B72WxJ3Z.js +0 -1
  239. package/client/dist/assets/InteractiveJobComposer-B5gh4Qoy.js +0 -19
  240. package/client/dist/assets/JobDetailModal-D-BeqsJ6.js +0 -1
  241. package/client/dist/assets/JobDetailPage-DwdozwLO.js +0 -1
  242. package/client/dist/assets/JobsPage-BDkinklt.js +0 -1
  243. package/client/dist/assets/LoopBuilderPage-CclJ3VZA.js +0 -7
  244. package/client/dist/assets/LoopPreviewModal-Dw4UgoLu.js +0 -1
  245. package/client/dist/assets/LoopsPage-D7ac4M24.js +0 -1
  246. package/client/dist/assets/PluginsPage-wdC-qa40.js +0 -2
  247. package/client/dist/assets/ProjectSettingsDialog-BlTlRtGI.js +0 -1
  248. package/client/dist/assets/TemplatePreviewModal-CeMx2Red.js +0 -1
  249. package/client/dist/assets/activity-Dbl71S-E.js +0 -1
  250. package/client/dist/assets/addon-image-CwoZj17F.js +0 -1
  251. package/client/dist/assets/addon-ligatures-CWDrED80.js +0 -2
  252. package/client/dist/assets/addspec-DrXqP8ip.js +0 -1
  253. package/client/dist/assets/agent-B8BXmlUQ2.js +0 -1
  254. package/client/dist/assets/agent-BEXYxDbs.js +0 -1
  255. package/client/dist/assets/agent-BYc3-6Ln2.js +0 -1
  256. package/client/dist/assets/agent-BbJorPdT.js +0 -1
  257. package/client/dist/assets/agent-CewFImgL.js +0 -1
  258. package/client/dist/assets/agent-DfhYoUmp.js +0 -1
  259. package/client/dist/assets/agent-TK_rrK4d.js +0 -1
  260. package/client/dist/assets/agent-bHjA9R_j2.js +0 -1
  261. package/client/dist/assets/agents-CGDn_3kB.js +0 -1
  262. package/client/dist/assets/agentstudio-Dxe_94PK.js +0 -1
  263. package/client/dist/assets/aiedit-DrfHQnlz.js +0 -1
  264. package/client/dist/assets/analytics-CxDYcBl9.js +0 -1
  265. package/client/dist/assets/attachments-DvxQbBTC.js +0 -1
  266. package/client/dist/assets/brain-C92rjjYP.js +0 -1
  267. package/client/dist/assets/browser-DM8G151q.js +0 -1
  268. package/client/dist/assets/chat-0dg5hOo_.js +0 -1
  269. package/client/dist/assets/chunk-BNv3lrIs.js +0 -1
  270. package/client/dist/assets/code-B62fClk-.js +0 -1
  271. package/client/dist/assets/commands-DCJ-R9E4.js +0 -1
  272. package/client/dist/assets/common-BM3LFLCE2.js +0 -1
  273. package/client/dist/assets/common-Bwafujnc2.js +0 -1
  274. package/client/dist/assets/common-C-vU-j-I.js +0 -1
  275. package/client/dist/assets/common-C5HKSbbz.js +0 -1
  276. package/client/dist/assets/common-DXrmj6WD.js +0 -1
  277. package/client/dist/assets/common-DhWTkocg.js +0 -1
  278. package/client/dist/assets/common-DiPTT7V_2.js +0 -1
  279. package/client/dist/assets/common-DmqBPwZG.js +0 -1
  280. package/client/dist/assets/dashboard-B5ang-ZS.js +0 -1
  281. package/client/dist/assets/dashboard-CHHcOWVL2.js +0 -1
  282. package/client/dist/assets/dashboard-DBD4wPGY2.js +0 -1
  283. package/client/dist/assets/dashboard-DBuLfWo9.js +0 -1
  284. package/client/dist/assets/dashboard-DSnztwWH.js +0 -1
  285. package/client/dist/assets/dashboard-D_aP_InU.js +0 -1
  286. package/client/dist/assets/dashboard-DmBcdl1B.js +0 -1
  287. package/client/dist/assets/dashboard-RIcUKY1Y2.js +0 -1
  288. package/client/dist/assets/editor.api2-C3B0tp2x.js +0 -872
  289. package/client/dist/assets/editor.worker-Bd9IXS8d.js +0 -26
  290. package/client/dist/assets/explore-_XyU5e7R.js +0 -1
  291. package/client/dist/assets/index-BVbgKXUu.css +0 -2
  292. package/client/dist/assets/index-DyY1QyIn.js +0 -158
  293. package/client/dist/assets/integrations-By80EBkf.js +0 -1
  294. package/client/dist/assets/jira-DeIjwCK3.js +0 -1
  295. package/client/dist/assets/jobs-1GV2-4zd2.js +0 -1
  296. package/client/dist/assets/jobs-26F9gGcE.js +0 -1
  297. package/client/dist/assets/jobs-BgQzB0Sx2.js +0 -1
  298. package/client/dist/assets/jobs-BqWui9_f.js +0 -1
  299. package/client/dist/assets/jobs-CE8qeIYj.js +0 -1
  300. package/client/dist/assets/jobs-DrYmsKy5.js +0 -1
  301. package/client/dist/assets/jobs-DzeuASRf.js +0 -1
  302. package/client/dist/assets/jobs-Wh7dn28q2.js +0 -1
  303. package/client/dist/assets/json.worker-DzV-CpCQ.js +0 -58
  304. package/client/dist/assets/lib-0PFDyMxJ.js +0 -7
  305. package/client/dist/assets/loops-CO6q75W8.js +0 -1
  306. package/client/dist/assets/mcp-HwBbQDk0.js +0 -1
  307. package/client/dist/assets/monaco.contribution-Desq7oq4.js +0 -2
  308. package/client/dist/assets/nav-BTg8s-cD.js +0 -1
  309. package/client/dist/assets/preload-helper-DSXbuxSR.js +0 -1
  310. package/client/dist/assets/settings-cJNynGIZ.js +0 -1
  311. package/client/dist/assets/setup-D2CmgAro.js +0 -1
  312. package/client/dist/assets/specs-PGIOzMtc.js +0 -1
  313. package/client/dist/assets/terminal-CFt56A5f.js +0 -1
  314. package/client/dist/assets/tickets-B7zT4IRf.js +0 -1
  315. package/client/dist/assets/tickets-BHv3ewvq.js +0 -1
  316. package/client/dist/assets/tickets-BoXom7l8.js +0 -1
  317. package/client/dist/assets/tickets-C3b8cHhd2.js +0 -1
  318. package/client/dist/assets/tickets-CFXnJsYF.js +0 -1
  319. package/client/dist/assets/tickets-CWYFvx1W2.js +0 -1
  320. package/client/dist/assets/tickets-DTHo9c3C.js +0 -1
  321. package/client/dist/assets/tickets-SuX_HkK4.js +0 -1
  322. package/client/dist/assets/upload-CmR4h9H1.js +0 -1
  323. package/client/dist/assets/value-JA3bf47v.js +0 -1
  324. /package/client/dist/assets/{abap-D0E4FfVW.js → abap-08VXUWAP.js} +0 -0
  325. /package/client/dist/assets/{activity--iN4Ad2R2.js → activity-B-rp4h_v.js} +0 -0
  326. /package/client/dist/assets/{activity-6hM_ZSg9.js → activity-C6kJpm9q2.js} +0 -0
  327. /package/client/dist/assets/{activity-CIOLxGvh.js → activity-CGx4kb_j.js} +0 -0
  328. /package/client/dist/assets/{activity-BZNfOdiS.js → activity-CnoX2bH7.js} +0 -0
  329. /package/client/dist/assets/{activity-BuVo5sPB.js → activity-DAyekdlt2.js} +0 -0
  330. /package/client/dist/assets/{activity-D2r2h3SE2.js → activity-DCxtpT7j.js} +0 -0
  331. /package/client/dist/assets/{activity-DtxrAUMo2.js → activity-JzwRaWPD.js} +0 -0
  332. /package/client/dist/assets/{addspec-BMtLZLW62.js → addspec-BXjvkh5q.js} +0 -0
  333. /package/client/dist/assets/{addspec-CRuvWn8d.js → addspec-C4bwrmpi.js} +0 -0
  334. /package/client/dist/assets/{addspec-D-JLDsUV.js → addspec-CArHXUDM.js} +0 -0
  335. /package/client/dist/assets/{addspec-mgPOt5q02.js → addspec-DTKJcXiR.js} +0 -0
  336. /package/client/dist/assets/{addspec-DfhDbg5c.js → addspec-DbNBbrha2.js} +0 -0
  337. /package/client/dist/assets/{addspec-CP0HRuxY.js → addspec-htHGZHOe2.js} +0 -0
  338. /package/client/dist/assets/{addspec-bsNDdqRm2.js → addspec-lr_LP6jk.js} +0 -0
  339. /package/client/dist/assets/{agents-78bTZGZh.js → agents-6ZOJXWVk2.js} +0 -0
  340. /package/client/dist/assets/{agents-DDtCJ5M02.js → agents-BZ0XQKjg.js} +0 -0
  341. /package/client/dist/assets/{agents-CWo-KyAB.js → agents-BqWklnHR.js} +0 -0
  342. /package/client/dist/assets/{agents-BUdEi3-N.js → agents-Bz-thHkB2.js} +0 -0
  343. /package/client/dist/assets/{agents-bvyRLczL.js → agents-CJclElx6.js} +0 -0
  344. /package/client/dist/assets/{agents-BpoUaoW92.js → agents-D_l9L0VA.js} +0 -0
  345. /package/client/dist/assets/{agents-BLhWCmDS2.js → agents-u_C0jPB5.js} +0 -0
  346. /package/client/dist/assets/{agentstudio-BMQxc_bz.js → agentstudio-BF19hbXj.js} +0 -0
  347. /package/client/dist/assets/{agentstudio-C_Jq8Tl4.js → agentstudio-BLGvxWBx2.js} +0 -0
  348. /package/client/dist/assets/{agentstudio-Dtq3dGYk.js → agentstudio-D9UKiWTj2.js} +0 -0
  349. /package/client/dist/assets/{agentstudio-CWgXG5W52.js → agentstudio-Drs37nIM.js} +0 -0
  350. /package/client/dist/assets/{agentstudio-sI27TpBu2.js → agentstudio-nEnKkcXM.js} +0 -0
  351. /package/client/dist/assets/{agentstudio-jKMTdT70.js → agentstudio-pyFcSBKy.js} +0 -0
  352. /package/client/dist/assets/{agentstudio-COZriimI2.js → agentstudio-zfTzQh3U.js} +0 -0
  353. /package/client/dist/assets/{aiedit-hknZlly82.js → aiedit-B0KUbpB6.js} +0 -0
  354. /package/client/dist/assets/{aiedit-eMN4eQPF.js → aiedit-BoG24nlg2.js} +0 -0
  355. /package/client/dist/assets/{aiedit-pc5JDUq1.js → aiedit-CjlSRQVP.js} +0 -0
  356. /package/client/dist/assets/{aiedit-B03pD_Na.js → aiedit-DCjA7jLJ.js} +0 -0
  357. /package/client/dist/assets/{aiedit-6CHdb_iv2.js → aiedit-DNXxq_Dr.js} +0 -0
  358. /package/client/dist/assets/{aiedit-igmw-ZoZ2.js → aiedit-DQ-0_pvE.js} +0 -0
  359. /package/client/dist/assets/{aiedit-BtRqRxML.js → aiedit-olnv3QSt2.js} +0 -0
  360. /package/client/dist/assets/{analytics-CCprAqk3.js → analytics-BC7mwR4s.js} +0 -0
  361. /package/client/dist/assets/{analytics-CR1TJqUi2.js → analytics-BiNg8edP.js} +0 -0
  362. /package/client/dist/assets/{analytics-Bj-SmKHa.js → analytics-C0s_T2GN.js} +0 -0
  363. /package/client/dist/assets/{analytics-C5dpiS0o.js → analytics-CdsAPAwZ2.js} +0 -0
  364. /package/client/dist/assets/{analytics-BWMUZ2Pq2.js → analytics-Cr1_q-lC.js} +0 -0
  365. /package/client/dist/assets/{analytics-DfNdXR0y2.js → analytics-IEsKZvpW2.js} +0 -0
  366. /package/client/dist/assets/{analytics-rmHuJv0V.js → analytics-SRPf84OT.js} +0 -0
  367. /package/client/dist/assets/{apex-D_X_8J4d.js → apex-BWPQTe0t.js} +0 -0
  368. /package/client/dist/assets/{atom-one-dark-BMDPFwRZ.js → atom-one-dark-qiGhVQxg.js} +0 -0
  369. /package/client/dist/assets/{attachments-BgBNSqQd.js → attachments-D0wXXt9T2.js} +0 -0
  370. /package/client/dist/assets/{attachments-C9GTTJsx2.js → attachments-DenIjuVI.js} +0 -0
  371. /package/client/dist/assets/{attachments-BjyMmrwE.js → attachments-DhO7LPfy.js} +0 -0
  372. /package/client/dist/assets/{attachments-DPCmkFZu2.js → attachments-DoH5w0x4.js} +0 -0
  373. /package/client/dist/assets/{attachments-DCZOL1RU.js → attachments-Q9yNtiEM.js} +0 -0
  374. /package/client/dist/assets/{attachments-rNn1ekRy2.js → attachments-V_WZAYXx2.js} +0 -0
  375. /package/client/dist/assets/{attachments-DL-bPy9E.js → attachments-eaeScpjM.js} +0 -0
  376. /package/client/dist/assets/{auth-DpZq5p1e.js → auth-D_5bQv5W.js} +0 -0
  377. /package/client/dist/assets/{azcli-PZLnZ3jH.js → azcli-Bc_sGQ0U.js} +0 -0
  378. /package/client/dist/assets/{bat-KlBOxpO4.js → bat-i0X4ZdIN.js} +0 -0
  379. /package/client/dist/assets/{bicep-CgG9oF3V.js → bicep-B5-_aFwp.js} +0 -0
  380. /package/client/dist/assets/{browser-BPkhILSJ.js → browser-C25DkQiO.js} +0 -0
  381. /package/client/dist/assets/{browser-BqLqJVVT.js → browser-CiJ7-L32.js} +0 -0
  382. /package/client/dist/assets/{browser-rWKj-RR_2.js → browser-CvgDySd9.js} +0 -0
  383. /package/client/dist/assets/{browser-Cvg-QHvd2.js → browser-CyR1Kp3m2.js} +0 -0
  384. /package/client/dist/assets/{browser-FRv8P02E.js → browser-DwZljcRh2.js} +0 -0
  385. /package/client/dist/assets/{browser-Cc0iDqUi.js → browser-DxP-alBK.js} +0 -0
  386. /package/client/dist/assets/{browser-BijVYG0Y2.js → browser-rRCwi7Y7.js} +0 -0
  387. /package/client/dist/assets/{cameligo-6R45j6Ph.js → cameligo-DMUM7wLl.js} +0 -0
  388. /package/client/dist/assets/{chat-Bom6bqK3.js → chat-2rIB8HhT.js} +0 -0
  389. /package/client/dist/assets/{chat-vylxqR0P2.js → chat-BR8oe53x2.js} +0 -0
  390. /package/client/dist/assets/{chat-Dnwg-NpQ.js → chat-Bua03tty.js} +0 -0
  391. /package/client/dist/assets/{chat-BgL3zv6x2.js → chat-CZpAQan1.js} +0 -0
  392. /package/client/dist/assets/{chat-DsbwJhus.js → chat-CdY5SGvS.js} +0 -0
  393. /package/client/dist/assets/{chat-Bh3NdgJs.js → chat-DZyu3ImY2.js} +0 -0
  394. /package/client/dist/assets/{chat-DdwZjT8K2.js → chat-D_I7nU5K.js} +0 -0
  395. /package/client/dist/assets/{clojure-CLAAMO7J.js → clojure-Cm7r79vr.js} +0 -0
  396. /package/client/dist/assets/{clsx-CNmW0Nip.js → clsx-CjueKrWZ.js} +0 -0
  397. /package/client/dist/assets/{code-UPVvmAKM2.js → code-76gS0IdL.js} +0 -0
  398. /package/client/dist/assets/{code-BRcDlze52.js → code-C1VwxlML.js} +0 -0
  399. /package/client/dist/assets/{code-CeQ0FZpC.js → code-CnMZyJpr.js} +0 -0
  400. /package/client/dist/assets/{code-DgeHz9CB.js → code-D20ef9Cf2.js} +0 -0
  401. /package/client/dist/assets/{code-DzC1rMk12.js → code-DdOXaWRY.js} +0 -0
  402. /package/client/dist/assets/{code-CwnBPPyA.js → code-Duohuf9l.js} +0 -0
  403. /package/client/dist/assets/{code-BdMy3Ffq.js → code-geHmknB92.js} +0 -0
  404. /package/client/dist/assets/{coffee-AruSygo-.js → coffee-Ba7i2nA0.js} +0 -0
  405. /package/client/dist/assets/{commands-DkU0qliq.js → commands-B9RZO4qS.js} +0 -0
  406. /package/client/dist/assets/{commands-u-4eEHCN.js → commands-BEwbAjtD2.js} +0 -0
  407. /package/client/dist/assets/{commands-JWMfGDmr.js → commands-Bbxu8kLU.js} +0 -0
  408. /package/client/dist/assets/{commands-C4Y3C-PF2.js → commands-CDU3qv68.js} +0 -0
  409. /package/client/dist/assets/{commands-xHDDGfcP2.js → commands-CzezgtQj.js} +0 -0
  410. /package/client/dist/assets/{commands-CSDs9q_2.js → commands-DTQBF_q52.js} +0 -0
  411. /package/client/dist/assets/{commands-RHOchN4n2.js → commands-hOtKuOXc.js} +0 -0
  412. /package/client/dist/assets/{cpp-DEE5r4Pw.js → cpp-C7h46wYY.js} +0 -0
  413. /package/client/dist/assets/{csharp-BVXJUvbK.js → csharp-BKxtCVv1.js} +0 -0
  414. /package/client/dist/assets/{csp-cXOF0JoV.js → csp-bTuwJoIa.js} +0 -0
  415. /package/client/dist/assets/{css-C_zQuAoy.js → css-DIMkf-bt.js} +0 -0
  416. /package/client/dist/assets/{cypher-t95m_Q-O.js → cypher-CVaqCwHa.js} +0 -0
  417. /package/client/dist/assets/{dart-B4r1Tg-e.js → dart-onAF5SnQ.js} +0 -0
  418. /package/client/dist/assets/{dockerfile-6mt7zHQw.js → dockerfile-DZFCIeNp.js} +0 -0
  419. /package/client/dist/assets/{ecl-DEY_Fz_Q.js → ecl-D05T4iGw.js} +0 -0
  420. /package/client/dist/assets/{elixir-R6-q1c5t.js → elixir-6RTg0lbw.js} +0 -0
  421. /package/client/dist/assets/{explore-B7fblXBb2.js → explore-BG99VGpL.js} +0 -0
  422. /package/client/dist/assets/{explore-CNBzrEPS.js → explore-C0TjprJW2.js} +0 -0
  423. /package/client/dist/assets/{explore-C0k8HrkN.js → explore-C2dgEo7q.js} +0 -0
  424. /package/client/dist/assets/{explore-BHtEpoNN2.js → explore-C8NDHbob.js} +0 -0
  425. /package/client/dist/assets/{explore-Dahc1lOx.js → explore-CWWgzQGR.js} +0 -0
  426. /package/client/dist/assets/{explore-D_mWBYCS2.js → explore-CcQXpdtS.js} +0 -0
  427. /package/client/dist/assets/{explore-UvW1jEMa.js → explore-DsXAgnmJ2.js} +0 -0
  428. /package/client/dist/assets/{flow9-CNQvHBOt.js → flow9-C5_-GSwl.js} +0 -0
  429. /package/client/dist/assets/{format-command-CuWuRvka.js → format-command-Dfa1o2d-.js} +0 -0
  430. /package/client/dist/assets/{fsharp-vPZkFXwv.js → fsharp-C8Ef5oNN.js} +0 -0
  431. /package/client/dist/assets/{go-KUzBMYJ_.js → go-C-y9NEjX.js} +0 -0
  432. /package/client/dist/assets/{graphql-C3W0rCsD.js → graphql-fmXr3nnJ.js} +0 -0
  433. /package/client/dist/assets/{hcl-BOOMFSNX.js → hcl-CpzslTdj.js} +0 -0
  434. /package/client/dist/assets/{ini-CFat1WJr.js → ini-sBoK_t0W.js} +0 -0
  435. /package/client/dist/assets/{integrations-TAcYNh0b.js → integrations-CH0zy1Mw2.js} +0 -0
  436. /package/client/dist/assets/{integrations-C1ThkTC52.js → integrations-CIAjhd7L.js} +0 -0
  437. /package/client/dist/assets/{integrations-2YcNyAl3.js → integrations-CZdwOe8A.js} +0 -0
  438. /package/client/dist/assets/{integrations-BXCvXUlG2.js → integrations-Ct7omMld.js} +0 -0
  439. /package/client/dist/assets/{integrations-Crk2rFhL2.js → integrations-D6MSxy3-.js} +0 -0
  440. /package/client/dist/assets/{integrations-Dn4JiGtW.js → integrations-DbZa-Zyc.js} +0 -0
  441. /package/client/dist/assets/{integrations-D9YrJ_Ep.js → integrations-b8pH-vfl2.js} +0 -0
  442. /package/client/dist/assets/{java-Dee-CNAM.js → java-BEtHBSE6.js} +0 -0
  443. /package/client/dist/assets/{jira-D5efgwp_.js → jira-BySfUuQZ.js} +0 -0
  444. /package/client/dist/assets/{jira-bDs7Im2C.js → jira-Byw2C0F8.js} +0 -0
  445. /package/client/dist/assets/{jira-BHAKtc4B.js → jira-CNeYi06w2.js} +0 -0
  446. /package/client/dist/assets/{jira-Be369RIR2.js → jira-D11gPhxz.js} +0 -0
  447. /package/client/dist/assets/{jira-CPpgA-5z2.js → jira-D38Gj7AT.js} +0 -0
  448. /package/client/dist/assets/{jira-uNH1gETL2.js → jira-gAvOyije.js} +0 -0
  449. /package/client/dist/assets/{jira-DS7ownDy.js → jira-pRaL7JQo2.js} +0 -0
  450. /package/client/dist/assets/{julia-CGNlLYuU.js → julia-Bri6UV-V.js} +0 -0
  451. /package/client/dist/assets/{kotlin-CY7lhknJ.js → kotlin-BOotOW0E.js} +0 -0
  452. /package/client/dist/assets/{less-D0KN7hfJ.js → less-B9JPFI3C.js} +0 -0
  453. /package/client/dist/assets/{lexon-CuRJufPs.js → lexon-CfSJPG6W.js} +0 -0
  454. /package/client/dist/assets/{loops-LIgO_-eM.js → loops-CPpgMapU.js} +0 -0
  455. /package/client/dist/assets/{loops-DJNhp17p2.js → loops-CWWbKuVL.js} +0 -0
  456. /package/client/dist/assets/{loops-D6D3XHjj.js → loops-Crc9It8o2.js} +0 -0
  457. /package/client/dist/assets/{loops-CvOWsi5r.js → loops-DH2hZcbK2.js} +0 -0
  458. /package/client/dist/assets/{loops-vhJdbcz92.js → loops-DS5TmvIU2.js} +0 -0
  459. /package/client/dist/assets/{loops-K5_HXfJa.js → loops-P2f0cN8-.js} +0 -0
  460. /package/client/dist/assets/{loops-BvqQ-bHR2.js → loops-fJV-9hH7.js} +0 -0
  461. /package/client/dist/assets/{lua-DHvH62Zf.js → lua-CsQS60Ue.js} +0 -0
  462. /package/client/dist/assets/{m3-tlthQ8Fo.js → m3-D-oSqn_W.js} +0 -0
  463. /package/client/dist/assets/{markdown-Dbx1LgUZ.js → markdown-Cimd5fb3.js} +0 -0
  464. /package/client/dist/assets/{mcp-Dcl-XxkR.js → mcp-Czd_iyp02.js} +0 -0
  465. /package/client/dist/assets/{mcp-XJNRfxVp2.js → mcp-D2pc5GGx.js} +0 -0
  466. /package/client/dist/assets/{mcp-sRmWE8WJ.js → mcp-D4ZjtIx32.js} +0 -0
  467. /package/client/dist/assets/{mcp-DtbxuH3V2.js → mcp-DIfBUv2D2.js} +0 -0
  468. /package/client/dist/assets/{mcp-H-gnQUdN.js → mcp-DsTwmjST.js} +0 -0
  469. /package/client/dist/assets/{mcp-N1nzp8oL.js → mcp-SMkn8boG.js} +0 -0
  470. /package/client/dist/assets/{mcp-CjVKXAhD2.js → mcp-fSfiVYAc.js} +0 -0
  471. /package/client/dist/assets/{mips-D_O3IUz_.js → mips-CIPQ_RoX.js} +0 -0
  472. /package/client/dist/assets/{msdax-C_ZmCnRt.js → msdax-DauUninz.js} +0 -0
  473. /package/client/dist/assets/{mysql-C-dCEYSX.js → mysql-SOo6toE5.js} +0 -0
  474. /package/client/dist/assets/{nav-BElxljIF2.js → nav-BRJBxaGt2.js} +0 -0
  475. /package/client/dist/assets/{nav-COu2E_-L.js → nav-CAGKv3_F.js} +0 -0
  476. /package/client/dist/assets/{nav-BXO7PJJG2.js → nav-CNskRJKQ2.js} +0 -0
  477. /package/client/dist/assets/{nav-CW9pxWfC2.js → nav-DY93irBL.js} +0 -0
  478. /package/client/dist/assets/{nav-CXHUA4uf.js → nav-DhjvYgqt.js} +0 -0
  479. /package/client/dist/assets/{nav-DdHF5VWJ.js → nav-Gb-r4wop.js} +0 -0
  480. /package/client/dist/assets/{nav-C8kOuGh8.js → nav-TvumAFD22.js} +0 -0
  481. /package/client/dist/assets/{objective-c-DZY9Aqxd.js → objective-c-FvmIjYaQ.js} +0 -0
  482. /package/client/dist/assets/{pascal-BVHLXoor.js → pascal-DrH0SRf2.js} +0 -0
  483. /package/client/dist/assets/{pascaligo-XdG_6u3k.js → pascaligo-D-ptJ9y-.js} +0 -0
  484. /package/client/dist/assets/{perl-b9QV0BcD.js → perl-oz_6vUea.js} +0 -0
  485. /package/client/dist/assets/{pgsql-Du5FDOgT.js → pgsql-DTj74zXo.js} +0 -0
  486. /package/client/dist/assets/{php-WJD0z2Z_.js → php-nr791fC2.js} +0 -0
  487. /package/client/dist/assets/{pla-CbciTf2w.js → pla-CopQ2nXW.js} +0 -0
  488. /package/client/dist/assets/{postiats-C1QxmvVq.js → postiats-43DmfD33.js} +0 -0
  489. /package/client/dist/assets/{powerquery-BEYDhPLZ.js → powerquery-D3hlyOfw.js} +0 -0
  490. /package/client/dist/assets/{powershell-CfaYw4XL.js → powershell-DmHpPYUd.js} +0 -0
  491. /package/client/dist/assets/{protobuf-pwZEDOYa.js → protobuf-C531GsRP.js} +0 -0
  492. /package/client/dist/assets/{pug-5ftqSPVe.js → pug-Z5eAx3Zn.js} +0 -0
  493. /package/client/dist/assets/{qsharp-uI6qZMq4.js → qsharp-DkqhCAOL.js} +0 -0
  494. /package/client/dist/assets/{r-BK54P1EG.js → r-BwWrilGY.js} +0 -0
  495. /package/client/dist/assets/{redis-DI0yd7kN.js → redis-ClamHrr6.js} +0 -0
  496. /package/client/dist/assets/{redshift-Dov26eyI.js → redshift-DT7zqm-g.js} +0 -0
  497. /package/client/dist/assets/{restructuredtext-QqJ_1dC-.js → restructuredtext-BYgofb2h.js} +0 -0
  498. /package/client/dist/assets/{ruby-5WjveeVb.js → ruby-DezsRK8O.js} +0 -0
  499. /package/client/dist/assets/{rust-BzVSfqFH.js → rust-DdL9SqIa.js} +0 -0
  500. /package/client/dist/assets/{sb-DMIUfT-3.js → sb-CcwsVR0C.js} +0 -0
  501. /package/client/dist/assets/{scala-CSKA7lqt.js → scala-DHpiXF5c.js} +0 -0
  502. /package/client/dist/assets/{scheme-CojOP2Qc.js → scheme-BeGwcela.js} +0 -0
  503. /package/client/dist/assets/{scss-DoBZGJG6.js → scss-gp-XZpBa.js} +0 -0
  504. /package/client/dist/assets/{settings-1w1O5Og82.js → settings-B9ilOSYO.js} +0 -0
  505. /package/client/dist/assets/{settings-Jc46vUhd.js → settings-BqlWHRHA.js} +0 -0
  506. /package/client/dist/assets/{settings-BSihkr0e.js → settings-D8dlqt4d.js} +0 -0
  507. /package/client/dist/assets/{settings-BiDOB3hV2.js → settings-DAZqjX2a2.js} +0 -0
  508. /package/client/dist/assets/{settings-DNL68KTE.js → settings-DRh39v1S2.js} +0 -0
  509. /package/client/dist/assets/{settings-BoubJhDP2.js → settings-Mj1OJbfd2.js} +0 -0
  510. /package/client/dist/assets/{settings-CB-EZ_W8.js → settings-lRReaSYm.js} +0 -0
  511. /package/client/dist/assets/{setup-D84y3wx-.js → setup-CGZjy2IF2.js} +0 -0
  512. /package/client/dist/assets/{setup-BuQNKztt2.js → setup-CXa0Sx16.js} +0 -0
  513. /package/client/dist/assets/{setup-BwMovM9z2.js → setup-D2ktKKMF2.js} +0 -0
  514. /package/client/dist/assets/{setup-DU0ULX8q2.js → setup-DG2H7ha5.js} +0 -0
  515. /package/client/dist/assets/{setup-BXpw07r-.js → setup-DK5bHDUq2.js} +0 -0
  516. /package/client/dist/assets/{setup-7J8TjG4O.js → setup-DrWlIQTZ.js} +0 -0
  517. /package/client/dist/assets/{setup-BiduSX9d.js → setup-rzHUVgAu.js} +0 -0
  518. /package/client/dist/assets/{shell-YOaE4udl.js → shell-CC2rA5mh.js} +0 -0
  519. /package/client/dist/assets/{solidity-l3jSyVLu.js → solidity-BEEn4gHE.js} +0 -0
  520. /package/client/dist/assets/{sophia-BhSGTaQJ.js → sophia-CRfGWb83.js} +0 -0
  521. /package/client/dist/assets/{sparql-qhk6QD9w.js → sparql-D_Lu-MrJ.js} +0 -0
  522. /package/client/dist/assets/{specs-CWtKcVDC.js → specs-BcyxaCwf2.js} +0 -0
  523. /package/client/dist/assets/{specs-DTplt2XM.js → specs-C1q9geAi.js} +0 -0
  524. /package/client/dist/assets/{specs-PozUNhqb.js → specs-CLZKysq-2.js} +0 -0
  525. /package/client/dist/assets/{specs-C9Sd7BhH2.js → specs-DZMS9o54.js} +0 -0
  526. /package/client/dist/assets/{specs-D2clrt2c2.js → specs-Dm6zUI-R.js} +0 -0
  527. /package/client/dist/assets/{specs-CBDvEYKv.js → specs-V3TvhKLX.js} +0 -0
  528. /package/client/dist/assets/{specs-CIPr9reA2.js → specs-dV3Mq5Mu2.js} +0 -0
  529. /package/client/dist/assets/{sql-DoO3y21-.js → sql-NEE52Syq.js} +0 -0
  530. /package/client/dist/assets/{st-ChZAY8oG.js → st-DbInun42.js} +0 -0
  531. /package/client/dist/assets/{swift-D290Tr7_.js → swift-Bxkupp3x.js} +0 -0
  532. /package/client/dist/assets/{systemverilog-DsxzgJ-o.js → systemverilog-Bz4Y3fRF.js} +0 -0
  533. /package/client/dist/assets/{tcl-BKDUHAAR.js → tcl-DISqw1ZD.js} +0 -0
  534. /package/client/dist/assets/{terminal-De2Brd23.js → terminal-BZiIu3rf.js} +0 -0
  535. /package/client/dist/assets/{terminal-cqfd3EDV.js → terminal-C2NAd7BW2.js} +0 -0
  536. /package/client/dist/assets/{terminal-WSAvHXoU.js → terminal-IahE4LW92.js} +0 -0
  537. /package/client/dist/assets/{terminal-Z7KsDM_n.js → terminal-QpXZU2Bn2.js} +0 -0
  538. /package/client/dist/assets/{terminal-xGbCZJHg.js → terminal-ScjyXNxQ.js} +0 -0
  539. /package/client/dist/assets/{terminal-BRm03iFA2.js → terminal-d7GadXbN.js} +0 -0
  540. /package/client/dist/assets/{terminal-DxQWCqSD2.js → terminal-tITa81iz.js} +0 -0
  541. /package/client/dist/assets/{twig-UpZQaM8G.js → twig-De2hgUGE.js} +0 -0
  542. /package/client/dist/assets/{typespec-DzaKvN8i.js → typespec-B8J7ngcE.js} +0 -0
  543. /package/client/dist/assets/{vb-CZpbb7tk.js → vb-DV3o63ZY.js} +0 -0
  544. /package/client/dist/assets/{wgsl-CNt3OIvl.js → wgsl-DpFanUEy.js} +0 -0
@@ -7,6 +7,7 @@ exports.LoopRunManager = void 0;
7
7
  exports.truncate = truncate;
8
8
  exports.resolveRunVars = resolveRunVars;
9
9
  exports.extractChangeId = extractChangeId;
10
+ exports.recoverOrphanLoopStepAccounting = recoverOrphanLoopStepAccounting;
10
11
  const tree_kill_1 = __importDefault(require("tree-kill"));
11
12
  const db_1 = require("./db");
12
13
  const interactive_job_session_1 = require("./interactive-job-session");
@@ -17,6 +18,9 @@ const loop_decider_1 = require("./loop-decider");
17
18
  const loop_runs_store_1 = require("./loop-runs-store");
18
19
  const ai_invocations_1 = require("./ai-invocations");
19
20
  const ids_1 = require("./ids");
21
+ const providers_1 = require("./providers");
22
+ const result_event_1 = require("./result-event");
23
+ const rails_store_1 = require("./rails-store");
20
24
  const IMPLEMENT_CMD_TOKEN_RE = /\{\{\s*cmd:(?:implement|batch)\s*\}\}/;
21
25
  const IMPLEMENT_CMD_TEXT_RE = /(?:^|\s)(?:\/(?:specrails:|sr:)?(?:implement|batch-implement)|\$(?:implement|batch-implement))\b/;
22
26
  const LOOP_BLOCKED_LINE_RE = /(?:^|\n)\s*LOOP_BLOCKED:\s*(.+?)(?:\n|$)/i;
@@ -93,11 +97,211 @@ function extractChangeId(text) {
93
97
  const m = CHANGE_ID_RE.exec(text);
94
98
  return m ? m[1] : undefined;
95
99
  }
100
+ function insertLoopInvocation(db, payload, result, finishedAt) {
101
+ const ticketIds = Array.isArray(payload.ticketIds)
102
+ ? [...new Set(payload.ticketIds)]
103
+ : (payload.ticketId == null ? [] : [payload.ticketId]);
104
+ const targets = ticketIds.length > 0 ? ticketIds : [null];
105
+ const splitInt = (value) => {
106
+ if (value === undefined)
107
+ return targets.map(() => undefined);
108
+ const base = Math.floor(value / targets.length);
109
+ const remainder = value - base * targets.length;
110
+ return targets.map((_target, index) => base + (index < remainder ? 1 : 0));
111
+ };
112
+ const tokensIn = splitInt(result.tokensIn);
113
+ const tokensOut = splitInt(result.tokensOut ?? (result.tokensIn === undefined && result.tokensOut === undefined
114
+ ? result.tokens
115
+ : undefined));
116
+ const cacheRead = splitInt(result.tokensCacheRead);
117
+ const cacheCreate = splitInt(result.tokensCacheCreate);
118
+ const turns = splitInt(result.numTurns);
119
+ targets.forEach((ticketId, index) => {
120
+ const suffix = ticketId == null || targets.length === 1 ? '' : `:t${ticketId}`;
121
+ (0, ai_invocations_1.recordInvocation)(db, {
122
+ id: `${payload.invocationId}${suffix}`,
123
+ project_id: payload.projectId,
124
+ provider: result.provider ?? payload.provider,
125
+ surface: 'loop',
126
+ surface_ref_id: `${payload.surfaceRefId}${suffix}`,
127
+ ticket_id: ticketId,
128
+ status: result.failed ? 'failed' : 'success',
129
+ started_at: payload.startedAt,
130
+ finished_at: finishedAt,
131
+ total_cost_usd: result.cost === undefined ? undefined : result.cost / targets.length,
132
+ total_cost_usd_estimated: result.estimated ?? false,
133
+ tokens_in: tokensIn[index],
134
+ tokens_out: tokensOut[index],
135
+ tokens_cache_read: cacheRead[index],
136
+ tokens_cache_create: cacheCreate[index],
137
+ num_turns: turns[index],
138
+ session_id: result.sessionId,
139
+ duration_ms: result.durationMs === undefined ? undefined : result.durationMs / targets.length,
140
+ duration_api_ms: result.durationApiMs === undefined ? undefined : result.durationApiMs / targets.length,
141
+ model: result.model ?? payload.model ?? undefined,
142
+ loop_run_id: payload.runId,
143
+ });
144
+ });
145
+ const aggregate = db.prepare(`
146
+ SELECT COALESCE(SUM(total_cost_usd), 0) AS cost,
147
+ COALESCE(SUM(tokens_in), 0) AS tokens_in,
148
+ COALESCE(SUM(tokens_out), 0) AS tokens_out,
149
+ COALESCE(SUM(tokens_cache_read), 0) AS cache_read,
150
+ COALESCE(SUM(tokens_cache_create), 0) AS cache_create,
151
+ COALESCE(SUM(duration_ms), 0) AS duration,
152
+ COALESCE(SUM(num_turns), 0) AS turns
153
+ FROM ai_invocations
154
+ WHERE surface = 'loop' AND loop_run_id = ?
155
+ `).get(payload.runId);
156
+ db.prepare(`
157
+ UPDATE jobs
158
+ SET total_cost_usd = ?, tokens_in = ?, tokens_out = ?,
159
+ tokens_cache_read = ?, tokens_cache_create = ?,
160
+ duration_ms = ?, num_turns = ?
161
+ WHERE id = ? AND owner = 'loop'
162
+ `).run(aggregate.cost, aggregate.tokens_in, aggregate.tokens_out, aggregate.cache_read, aggregate.cache_create, aggregate.duration, aggregate.turns, payload.runId);
163
+ }
164
+ /** Recover every AI/decider step that was staged before provider spawn but did
165
+ * not atomically land its invocation. Completed interactive turns are the jobs
166
+ * delta from the durable baseline; raw events after completedEventSeq are the
167
+ * unfinished/failed-to-checkpoint turn. The insert and checkpoint removal share
168
+ * one transaction, so a crash between them replays without duplication. */
169
+ function boundedInflightDurationMs(payload, finishedAt) {
170
+ const startedAt = payload.activeTurnStartedAtMs;
171
+ const lastActivityAt = payload.lastActivityAtMs;
172
+ if (!Number.isFinite(startedAt) || !Number.isFinite(lastActivityAt))
173
+ return 0;
174
+ const finishedAtMs = Date.parse(finishedAt);
175
+ const boundedEnd = Number.isFinite(finishedAtMs)
176
+ ? Math.min(lastActivityAt, finishedAtMs)
177
+ : lastActivityAt;
178
+ return Math.max(0, boundedEnd - startedAt);
179
+ }
180
+ function recoverOrphanLoopStepAccounting(db, finishedAt = new Date().toISOString(), onlyRunId) {
181
+ let recovered = 0;
182
+ for (const row of (0, loop_runs_store_1.listLoopStepRecoveries)(db).filter((candidate) => !onlyRunId || candidate.run_id === onlyRunId)) {
183
+ let payload;
184
+ try {
185
+ payload = JSON.parse(row.payload);
186
+ if (!payload || payload.runId !== row.run_id || payload.stepKey !== row.step_key)
187
+ continue;
188
+ }
189
+ catch {
190
+ continue;
191
+ }
192
+ if (payload.settledResult) {
193
+ const settled = payload.settledResult;
194
+ const didRecover = (0, loop_runs_store_1.completeLoopStepRecovery)(db, payload.runId, payload.stepKey, (stable) => {
195
+ insertLoopInvocation(db, stable, settled, finishedAt);
196
+ const aggregate = (0, loop_runs_store_1.readLoopJobUsage)(db, payload.runId);
197
+ const recoveredLoopDuration = (payload.loopDurationBaseline ?? 0) + (settled.durationMs ?? 0);
198
+ db.prepare(`
199
+ UPDATE loop_runs
200
+ SET total_cost_usd = ?, total_tokens = ?, total_duration_ms = ?,
201
+ iteration_count = MAX(iteration_count, COALESCE(?, iteration_count))
202
+ WHERE id = ?
203
+ `).run(aggregate.totalCostUsd, aggregate.tokensIn + aggregate.tokensOut, recoveredLoopDuration, stable.iterationCount ?? null, payload.runId);
204
+ // ai_invocations contains AI-active duration only. The backing job is
205
+ // the whole loop and must retain shell duration already represented by
206
+ // loopDurationBaseline across this recovery transaction.
207
+ db.prepare(`UPDATE jobs SET duration_ms = ? WHERE id = ? AND owner = 'loop'`)
208
+ .run(recoveredLoopDuration, payload.runId);
209
+ });
210
+ if (didRecover)
211
+ recovered += 1;
212
+ continue;
213
+ }
214
+ const current = (0, loop_runs_store_1.readLoopJobUsage)(db, payload.runId);
215
+ const completed = {
216
+ tokensIn: Math.max(0, current.tokensIn - (payload.baseline?.tokensIn ?? 0)),
217
+ tokensOut: Math.max(0, current.tokensOut - (payload.baseline?.tokensOut ?? 0)),
218
+ tokensCacheRead: Math.max(0, current.tokensCacheRead - (payload.baseline?.tokensCacheRead ?? 0)),
219
+ tokensCacheCreate: Math.max(0, current.tokensCacheCreate - (payload.baseline?.tokensCacheCreate ?? 0)),
220
+ totalCostUsd: Math.max(0, current.totalCostUsd - (payload.baseline?.totalCostUsd ?? 0)),
221
+ numTurns: Math.max(0, current.numTurns - (payload.baseline?.numTurns ?? 0)),
222
+ };
223
+ const rawRows = db.prepare(`
224
+ SELECT seq, event_type, payload FROM events
225
+ WHERE job_id = ? AND seq > ? AND source = 'stdout'
226
+ ORDER BY seq
227
+ `).all(payload.runId, payload.completedEventSeq ?? -1);
228
+ const adapter = (0, providers_1.getAdapter)(payload.provider);
229
+ const events = rawRows
230
+ .map((raw) => adapter.parseStreamLine(raw.payload))
231
+ .filter((event) => event != null);
232
+ const { result: partial, estimated } = (0, result_event_1.finaliseInvocationResult)(adapter, events, {
233
+ fallbackModel: payload.model ?? undefined,
234
+ });
235
+ const hasResult = rawRows.some((raw) => raw.event_type === 'result');
236
+ const partialCost = hasResult && partial.total_cost_usd != null
237
+ ? Math.max(0, partial.total_cost_usd - (payload.providerCostBaseline ?? 0))
238
+ : (partial.total_cost_usd ?? 0);
239
+ const partialTurns = hasResult && partial.num_turns != null
240
+ ? Math.max(0, partial.num_turns - (payload.providerTurnsBaseline ?? 0))
241
+ : (partial.num_turns ?? (events.length > 0 ? 1 : 0));
242
+ const partialUsage = {
243
+ tokens_in: partial.tokens_in ?? 0,
244
+ tokens_out: partial.tokens_out ?? 0,
245
+ tokens_cache_read: partial.tokens_cache_read ?? 0,
246
+ tokens_cache_create: partial.tokens_cache_create ?? 0,
247
+ total_cost_usd: partialCost,
248
+ num_turns: partialTurns,
249
+ model: partial.model,
250
+ session_id: partial.session_id,
251
+ estimated: partialCost > 0 && estimated,
252
+ };
253
+ const partialDurationMs = hasResult
254
+ ? (partial.duration_ms ?? 0)
255
+ : ((partial.duration_ms ?? 0) > 0
256
+ ? partial.duration_ms
257
+ : boundedInflightDurationMs(payload, finishedAt));
258
+ // completedDurationMs contains only fully-checkpointed turns. The current
259
+ // raw turn contributes either provider duration OR the bounded wall fallback,
260
+ // never both.
261
+ const recoveredDurationMs = (payload.completedDurationMs ?? 0) + partialDurationMs;
262
+ const didRecover = (0, loop_runs_store_1.completeLoopStepRecovery)(db, payload.runId, payload.stepKey, (stable) => {
263
+ if (partialUsage.tokens_in || partialUsage.tokens_out ||
264
+ partialUsage.tokens_cache_read || partialUsage.tokens_cache_create ||
265
+ partialUsage.total_cost_usd || partialUsage.num_turns) {
266
+ (0, db_1.accumulateInteractiveTurn)(db, payload.runId, partialUsage);
267
+ }
268
+ insertLoopInvocation(db, stable, {
269
+ cost: completed.totalCostUsd + partialUsage.total_cost_usd,
270
+ tokens: completed.tokensIn + completed.tokensOut + partialUsage.tokens_in + partialUsage.tokens_out,
271
+ tokensIn: completed.tokensIn + partialUsage.tokens_in,
272
+ tokensOut: completed.tokensOut + partialUsage.tokens_out,
273
+ tokensCacheRead: completed.tokensCacheRead + partialUsage.tokens_cache_read,
274
+ tokensCacheCreate: completed.tokensCacheCreate + partialUsage.tokens_cache_create,
275
+ numTurns: completed.numTurns + partialUsage.num_turns,
276
+ durationMs: recoveredDurationMs,
277
+ model: partial.model ?? payload.model ?? undefined,
278
+ sessionId: partial.session_id,
279
+ provider: payload.provider,
280
+ estimated: partialUsage.estimated,
281
+ failed: true,
282
+ }, finishedAt);
283
+ const aggregate = (0, loop_runs_store_1.readLoopJobUsage)(db, payload.runId);
284
+ const recoveredLoopDuration = (payload.loopDurationBaseline ?? 0) + recoveredDurationMs;
285
+ db.prepare(`
286
+ UPDATE loop_runs
287
+ SET total_cost_usd = ?, total_tokens = ?, total_duration_ms = ?,
288
+ iteration_count = MAX(iteration_count, COALESCE(?, iteration_count))
289
+ WHERE id = ?
290
+ `).run(aggregate.totalCostUsd, aggregate.tokensIn + aggregate.tokensOut, recoveredLoopDuration, stable.iterationCount ?? null, payload.runId);
291
+ db.prepare(`UPDATE jobs SET duration_ms = ? WHERE id = ? AND owner = 'loop'`)
292
+ .run(recoveredLoopDuration, payload.runId);
293
+ });
294
+ if (didRecover)
295
+ recovered += 1;
296
+ }
297
+ return recovered;
298
+ }
96
299
  class LoopRunManager {
97
300
  db;
98
301
  broadcast;
99
302
  executors;
100
303
  now;
304
+ _disposed = false;
101
305
  _cancelled = new Set();
102
306
  /** The currently-spawned child per run, so cancel/stop can actually KILL a
103
307
  * blocked spawn (the cooperative `_cancelled` flag can't interrupt an await). */
@@ -108,6 +312,7 @@ class LoopRunManager {
108
312
  * id, which IS the backing job row's id — the manager-agnostic turn routing
109
313
  * (project-router-jobs) addresses sessions by that job id. */
110
314
  _interactiveSteps = new Map();
315
+ _activeStepRecovery = new Map();
111
316
  _pausedHumanDecisions = new Map();
112
317
  constructor(db, broadcast, executors, now = () => Date.now()) {
113
318
  this.db = db;
@@ -115,6 +320,14 @@ class LoopRunManager {
115
320
  this.executors = executors;
116
321
  this.now = now;
117
322
  }
323
+ /** WebSocket delivery is advisory. A disconnected/misbehaving listener must
324
+ * never change launch, accounting, traversal, or terminal outcomes. */
325
+ _emit(msg) {
326
+ try {
327
+ this.broadcast(msg);
328
+ }
329
+ catch { /* persisted state remains authoritative */ }
330
+ }
118
331
  /** Cancel an in-flight run: flag it AND kill the active spawned child so a
119
332
  * blocked AI Step / Shell returns immediately (the engine then settles
120
333
  * 'stopped' at the next boundary). An interactive step is torn down through
@@ -183,7 +396,57 @@ class LoopRunManager {
183
396
  * runs — the startup orphan sweeps (jobs + loop_runs) reconcile the rows on
184
397
  * the next boot, exactly as they do for a crash today. */
185
398
  shutdown() {
186
- for (const session of this._interactiveSteps.values()) {
399
+ if (this._disposed)
400
+ return;
401
+ this._disposed = true;
402
+ for (const runId of this._activeChild.keys())
403
+ this._cancelled.add(runId);
404
+ for (const runId of this._interactiveSteps.keys())
405
+ this._cancelled.add(runId);
406
+ for (const [runId, session] of this._interactiveSteps) {
407
+ try {
408
+ const snapshot = session.snapshotForAbort();
409
+ const stepKey = this._activeStepRecovery.get(runId);
410
+ if (stepKey) {
411
+ let recoveredProjectId = null;
412
+ (0, loop_runs_store_1.completeLoopStepRecovery)(this.db, runId, stepKey, (payload) => {
413
+ recoveredProjectId = payload.projectId;
414
+ insertLoopInvocation(this.db, payload, {
415
+ cost: snapshot.totals.total_cost_usd,
416
+ tokens: snapshot.totals.tokens_in + snapshot.totals.tokens_out,
417
+ tokensIn: snapshot.totals.tokens_in,
418
+ tokensOut: snapshot.totals.tokens_out,
419
+ tokensCacheRead: snapshot.totals.tokens_cache_read,
420
+ tokensCacheCreate: snapshot.totals.tokens_cache_create,
421
+ numTurns: snapshot.totals.num_turns,
422
+ durationMs: snapshot.activeDurationMs,
423
+ sessionId: snapshot.sessionId ?? undefined,
424
+ model: snapshot.model ?? undefined,
425
+ provider: payload.provider,
426
+ estimated: snapshot.estimated,
427
+ failed: true,
428
+ }, new Date(this.now()).toISOString());
429
+ const aggregate = (0, loop_runs_store_1.readLoopJobUsage)(this.db, runId);
430
+ const recoveredLoopDuration = (payload.loopDurationBaseline ?? 0) + snapshot.activeDurationMs;
431
+ this.db.prepare(`
432
+ UPDATE loop_runs
433
+ SET total_cost_usd = ?, total_tokens = ?, total_duration_ms = ?,
434
+ iteration_count = MAX(iteration_count, COALESCE(?, iteration_count))
435
+ WHERE id = ?
436
+ `).run(aggregate.totalCostUsd, aggregate.tokensIn + aggregate.tokensOut, recoveredLoopDuration, payload.iterationCount ?? null, runId);
437
+ this.db.prepare(`UPDATE jobs SET duration_ms = ? WHERE id = ? AND owner = 'loop'`)
438
+ .run(recoveredLoopDuration, runId);
439
+ });
440
+ this._activeStepRecovery.delete(runId);
441
+ if (recoveredProjectId) {
442
+ this._emit({ type: 'spending.invalidated', projectId: recoveredProjectId });
443
+ }
444
+ }
445
+ }
446
+ catch (err) {
447
+ // Leave the durable step checkpoint for startup raw-event recovery.
448
+ console.error(`[loop] shutdown accounting checkpoint failed for ${runId}:`, err);
449
+ }
187
450
  try {
188
451
  session.dispose();
189
452
  }
@@ -209,6 +472,9 @@ class LoopRunManager {
209
472
  this._pausedHumanDecisions.clear();
210
473
  }
211
474
  async run(req) {
475
+ if (this._disposed)
476
+ throw new Error('LoopRunManager is shut down');
477
+ const neverAfterDispose = () => new Promise(() => { });
212
478
  const runId = req.runId ?? (0, ids_1.newId)();
213
479
  const maxIterations = req.graph.config.maxIterations;
214
480
  const timeoutMs = req.graph.config.timeoutMinutes * 60_000;
@@ -226,20 +492,41 @@ class LoopRunManager {
226
492
  const maxCostUsd = typeof req.graph.config.maxCostUsd === 'number' && req.graph.config.maxCostUsd > 0
227
493
  ? req.graph.config.maxCostUsd
228
494
  : undefined;
229
- (0, loop_runs_store_1.createLoopRun)(this.db, {
230
- id: runId,
231
- projectId: req.projectId,
232
- loopId: req.loopId,
233
- loopName: req.loopName ?? null,
234
- railIndex: req.railIndex ?? null,
235
- ticketId: req.ticketId ?? null,
236
- provider: req.provider,
237
- model: req.model,
238
- reasoningEffort: req.effort ?? null,
239
- iterationLimit: maxIterations,
240
- startedAt: new Date(this.now()).toISOString(),
495
+ const launchStartedAt = new Date(this.now()).toISOString();
496
+ const jobCommand = `loop: ${req.loopName ?? req.loopId}${req.ticketId != null ? ` #${req.ticketId}` : ''}`;
497
+ const exactTicketIds = req.spec?.ticketIds ?? (req.ticketId == null ? [] : [req.ticketId]);
498
+ const persistLaunch = this.db.transaction(() => {
499
+ (0, rails_store_1.claimTicketOutcomeOwners)(this.db, exactTicketIds, runId);
500
+ if (req.railIndex != null && exactTicketIds.length > 0) {
501
+ (0, rails_store_1.claimRailTickets)(this.db, req.railIndex, exactTicketIds, runId);
502
+ }
503
+ (0, loop_runs_store_1.createLoopRun)(this.db, {
504
+ id: runId,
505
+ projectId: req.projectId,
506
+ loopId: req.loopId,
507
+ loopName: req.loopName ?? null,
508
+ railIndex: req.railIndex ?? null,
509
+ ticketId: req.ticketId ?? null,
510
+ provider: req.provider,
511
+ model: req.model,
512
+ reasoningEffort: req.effort ?? null,
513
+ ticketIds: exactTicketIds,
514
+ ticketCompletionStatus: req.ticketCompletionStatus ?? 'done',
515
+ causalOwnership: true,
516
+ iterationLimit: maxIterations,
517
+ startedAt: launchStartedAt,
518
+ });
519
+ (0, db_1.createJob)(this.db, {
520
+ id: runId,
521
+ command: jobCommand,
522
+ started_at: launchStartedAt,
523
+ provider: req.provider,
524
+ owner: 'loop',
525
+ causal_ownership: true,
526
+ });
241
527
  });
242
- this.broadcast({
528
+ persistLaunch();
529
+ this._emit({
243
530
  type: 'loop.run_started',
244
531
  projectId: req.projectId,
245
532
  loopRunId: runId,
@@ -247,14 +534,7 @@ class LoopRunManager {
247
534
  railIndex: req.railIndex ?? null,
248
535
  });
249
536
  // Surface the run as a JOB so the full session streams live in the Jobs list
250
- // + JobDetail, exactly like implement/freestyle. The job id IS the run id;
251
- // createJob is idempotent. Synchronous (before the first await) so the row
252
- // exists by the time the launch responds → "View Log" never 404s.
253
- const jobCommand = `loop: ${req.loopName ?? req.loopId}${req.ticketId != null ? ` #${req.ticketId}` : ''}`;
254
- try {
255
- (0, db_1.createJob)(this.db, { id: runId, command: jobCommand, started_at: new Date(this.now()).toISOString() });
256
- }
257
- catch { /* best-effort; the run still proceeds */ }
537
+ // + JobDetail. Both identities committed together above, before any spawn.
258
538
  let seq = 0;
259
539
  // Monotonic event-seq allocator for THIS run's job row. Shared with an
260
540
  // interactive step session (which persists its own provider events/logs on
@@ -277,7 +557,7 @@ class LoopRunManager {
277
557
  (0, db_1.appendEvent)(this.db, runId, s, { event_type: eventType, source: 'stdout', payload: json });
278
558
  }
279
559
  catch { /* best-effort */ }
280
- this.broadcast({ type: 'event', jobId: runId, event_type: eventType, source: 'stdout', payload: json, seq: s, timestamp: new Date(this.now()).toISOString() });
560
+ this._emit({ type: 'event', jobId: runId, event_type: eventType, source: 'stdout', payload: json, seq: s, timestamp: new Date(this.now()).toISOString() });
281
561
  };
282
562
  // Emit a structured step-boundary event (for a segmented/collapsible client
283
563
  // view) AND a visual divider line in the flat log, so each loop step is a
@@ -316,7 +596,7 @@ class LoopRunManager {
316
596
  (0, db_1.appendEvent)(this.db, runId, s, { event_type: 'log', source, payload: JSON.stringify({ line }) });
317
597
  }
318
598
  catch { /* events table best-effort */ }
319
- this.broadcast({ type: 'log', source, line, timestamp: new Date(this.now()).toISOString(), processId: runId });
599
+ this._emit({ type: 'log', source, line, timestamp: new Date(this.now()).toISOString(), processId: runId });
320
600
  };
321
601
  // Forward a RAW provider stdout line (claude/codex JSONL) as an `event` —
322
602
  // identical to QueueManager — so JobStatusPanel parses real activity
@@ -341,7 +621,16 @@ class LoopRunManager {
341
621
  (0, db_1.appendEvent)(this.db, runId, s, { event_type: eventType, source: 'stdout', payload: line });
342
622
  }
343
623
  catch { /* best-effort */ }
344
- this.broadcast({ type: 'event', jobId: runId, event_type: eventType, source: 'stdout', payload: line, seq: s, timestamp: new Date(this.now()).toISOString() });
624
+ const recoveryStepKey = this._activeStepRecovery.get(runId);
625
+ if (recoveryStepKey) {
626
+ try {
627
+ (0, loop_runs_store_1.updateLoopStepActivityCheckpoint)(this.db, runId, recoveryStepKey, undefined, this.now());
628
+ }
629
+ catch (err) {
630
+ console.error(`[loop] raw activity checkpoint failed for ${runId}/${recoveryStepKey}:`, err);
631
+ }
632
+ }
633
+ this._emit({ type: 'event', jobId: runId, event_type: eventType, source: 'stdout', payload: line, seq: s, timestamp: new Date(this.now()).toISOString() });
345
634
  };
346
635
  logLine(`▶ Loop "${req.loopName ?? req.loopId}" started${req.spec?.title ? ` — spec: ${req.spec.title}` : ''}`);
347
636
  if (req.isolation)
@@ -365,6 +654,13 @@ class LoopRunManager {
365
654
  let totalCost = 0;
366
655
  let totalTokens = 0;
367
656
  let totalDuration = 0;
657
+ let finalJobUsage = {
658
+ tokensIn: 0,
659
+ tokensOut: 0,
660
+ tokensCacheRead: 0,
661
+ tokensCacheCreate: 0,
662
+ numTurns: 0,
663
+ };
368
664
  let aiSessionId;
369
665
  // Consecutive AI steps that hard-failed with no output (provider down /
370
666
  // out of quota / crashing). Reset on any step that runs or produces output.
@@ -409,7 +705,7 @@ class LoopRunManager {
409
705
  const record = (refSuffix, r,
410
706
  // The REAL turn-start instant, captured before the step/decider await. The
411
707
  // row is bucketed/ordered by started_at, so it must be the start, not finish.
412
- startedAt) => {
708
+ startedAt, stepKey) => {
413
709
  totalCost += r.cost ?? 0;
414
710
  // A cost-bearing step that produced work (tokens) or hard-failed but reports
415
711
  // no priced cost → its real spend is missing from the total. The common case
@@ -428,53 +724,80 @@ class LoopRunManager {
428
724
  costUnknownWarned = true;
429
725
  logLine('⚠️ A step reported no priced cost (non-Claude estimate / unknown model / failed step) — the cost cap may under-count.', 'stderr');
430
726
  }
431
- totalTokens += r.tokens ?? 0;
727
+ totalTokens += r.tokens ?? ((r.tokensIn ?? 0) + (r.tokensOut ?? 0));
432
728
  totalDuration += r.durationMs ?? 0;
433
- (0, ai_invocations_1.recordInvocation)(this.db, {
434
- id: (0, ids_1.newId)(),
435
- project_id: req.projectId,
436
- provider: r.provider ?? req.provider,
437
- surface: 'loop',
438
- surface_ref_id: refSuffix,
439
- ticket_id: req.ticketId ?? null,
440
- // A hard-failed step (spawn error / non-zero exit / timeout) must NOT be
441
- // recorded as success — that under-states failureRate and lets a crashed
442
- // step into the success-only avg-cost. The result object doesn't separate
443
- // timeout from other hard-failures, so all map to 'failed'.
444
- status: r.failed ? 'failed' : 'success',
445
- // BUG-32: started_at is the real turn-start (captured before the await),
446
- // not the finish instant — so daily-timeline bucketing + scatter/table
447
- // ORDER BY started_at reflect when the step actually began.
448
- started_at: startedAt,
449
- finished_at: new Date(this.now()).toISOString(),
450
- total_cost_usd: r.cost,
451
- total_cost_usd_estimated: r.estimated ?? false,
452
- // BUG-04: persist the per-direction breakdown to its matching column —
453
- // folding everything into tokens_out drops cache tokens (the largest
454
- // component on cache-hit claude runs) and corrupts the in/out split.
455
- tokens_in: r.tokensIn,
456
- tokens_out: r.tokensOut,
457
- tokens_cache_read: r.tokensCacheRead,
458
- tokens_cache_create: r.tokensCacheCreate,
459
- // LOW-8: thread the finalised result's turn/session/duration metadata so
460
- // surface='loop' rows aren't NULL on these columns — otherwise scatter
461
- // numTurns, totalTurns and activeDurationMs silently exclude every loop step.
462
- num_turns: r.numTurns,
463
- session_id: r.sessionId,
464
- duration_ms: r.durationMs,
465
- duration_api_ms: r.durationApiMs,
466
- model: r.model ?? req.model,
467
- loop_run_id: runId,
468
- });
729
+ const finishedAt = new Date(this.now()).toISOString();
730
+ if (stepKey) {
731
+ (0, loop_runs_store_1.setLoopStepSettledResult)(this.db, runId, stepKey, r);
732
+ const completed = (0, loop_runs_store_1.completeLoopStepRecovery)(this.db, runId, stepKey, (payload) => {
733
+ insertLoopInvocation(this.db, payload, r, finishedAt);
734
+ (0, loop_runs_store_1.updateLoopRunCounters)(this.db, runId, {
735
+ iterationCount: iteration,
736
+ totalCostUsd: totalCost,
737
+ totalTokens,
738
+ totalDurationMs: totalDuration,
739
+ });
740
+ });
741
+ if (!completed)
742
+ throw new Error(`Missing loop step checkpoint ${runId}/${stepKey}`);
743
+ }
744
+ else {
745
+ // Compatibility for synthetic/test call paths that predate staged steps.
746
+ (0, ai_invocations_1.recordInvocation)(this.db, {
747
+ id: (0, ids_1.newId)(), project_id: req.projectId,
748
+ provider: r.provider ?? req.provider, surface: 'loop',
749
+ surface_ref_id: refSuffix, ticket_id: req.ticketId ?? null,
750
+ status: r.failed ? 'failed' : 'success', started_at: startedAt,
751
+ finished_at: finishedAt, total_cost_usd: r.cost,
752
+ total_cost_usd_estimated: r.estimated ?? false,
753
+ tokens_in: r.tokensIn,
754
+ tokens_out: r.tokensOut ?? (r.tokensIn === undefined && r.tokensOut === undefined ? r.tokens : undefined),
755
+ tokens_cache_read: r.tokensCacheRead,
756
+ tokens_cache_create: r.tokensCacheCreate,
757
+ num_turns: r.numTurns, session_id: r.sessionId,
758
+ duration_ms: r.durationMs, duration_api_ms: r.durationApiMs,
759
+ model: r.model ?? req.model, loop_run_id: runId,
760
+ });
761
+ }
469
762
  // BUG-07: the loop path is the only recordInvocation callsite that never
470
763
  // invalidated open spending dashboards — they'd freeze for the whole (often
471
764
  // multi-hour) run. Broadcast after each row, wrapped so a broadcast failure
472
765
  // can't break traversal (mirrors the file-summary callsite).
473
766
  try {
474
- this.broadcast({ type: 'spending.invalidated', projectId: req.projectId });
767
+ this._emit({ type: 'spending.invalidated', projectId: req.projectId });
475
768
  }
476
769
  catch { /* best-effort — never break traversal on a broadcast failure */ }
477
770
  };
771
+ const stageAiAccounting = (kind, nodeId, surfaceRefId, startedAt) => {
772
+ const stepKey = `${kind}:${stepNum}:${nodeId}`;
773
+ const startedAtMs = Date.parse(startedAt);
774
+ const payload = {
775
+ version: 1,
776
+ runId,
777
+ stepKey,
778
+ invocationId: (0, ids_1.newId)(),
779
+ projectId: req.projectId,
780
+ provider: req.provider,
781
+ model: req.model ?? null,
782
+ surfaceRefId,
783
+ ticketIds: exactTicketIds,
784
+ startedAt,
785
+ baseline: (0, loop_runs_store_1.readLoopJobUsage)(this.db, runId),
786
+ completedEventSeq: seq - 1,
787
+ providerCostBaseline: 0,
788
+ providerTurnsBaseline: 0,
789
+ loopDurationBaseline: this.db.prepare(`SELECT total_duration_ms FROM loop_runs WHERE id = ?`)
790
+ .get(runId)?.total_duration_ms ?? totalDuration,
791
+ completedDurationMs: 0,
792
+ iterationCount: iteration,
793
+ ...(Number.isFinite(startedAtMs)
794
+ ? { activeTurnStartedAtMs: startedAtMs, lastActivityAtMs: startedAtMs }
795
+ : {}),
796
+ };
797
+ (0, loop_runs_store_1.stageLoopStepRecovery)(this.db, payload);
798
+ this._activeStepRecovery.set(runId, stepKey);
799
+ return stepKey;
800
+ };
478
801
  const composeHistory = () => {
479
802
  const joined = history.join('\n');
480
803
  return joined.length > HISTORY_MAX_CHARS ? '…' + joined.slice(-HISTORY_MAX_CHARS) : joined;
@@ -502,15 +825,15 @@ class LoopRunManager {
502
825
  totalDurationMs: totalDuration,
503
826
  });
504
827
  const pausedAt = new Date(this.now()).toISOString();
505
- this.broadcast({
828
+ this._emit({
506
829
  type: 'loop.run_paused',
507
830
  projectId: req.projectId,
508
831
  loopRunId: runId,
509
832
  railIndex: req.railIndex ?? null,
510
833
  reason,
511
- ticketIds: req.ticketId != null ? [req.ticketId] : [],
834
+ ticketIds: exactTicketIds,
512
835
  });
513
- this.broadcast({
836
+ this._emit({
514
837
  type: 'job.interactive',
515
838
  projectId: req.projectId,
516
839
  jobId: runId,
@@ -519,6 +842,8 @@ class LoopRunManager {
519
842
  timestamp: pausedAt,
520
843
  });
521
844
  const decision = await decisionPromise;
845
+ if (this._disposed)
846
+ return new Promise(() => { });
522
847
  if (decision.action === 'stop' || this._cancelled.has(runId)) {
523
848
  logLine(`\n■ Loop stop requested while paused.`, 'stderr');
524
849
  return { action: 'stop' };
@@ -528,14 +853,14 @@ class LoopRunManager {
528
853
  const resumedAt = new Date(this.now()).toISOString();
529
854
  logLine(`\n▶ Loop resumed — human decision received${answer ? `:\n${answer}` : '.'}`);
530
855
  history.push(`Human decision: ${truncate(answer)}`);
531
- this.broadcast({
856
+ this._emit({
532
857
  type: 'loop.run_resumed',
533
858
  projectId: req.projectId,
534
859
  loopRunId: runId,
535
860
  railIndex: req.railIndex ?? null,
536
- ticketIds: req.ticketId != null ? [req.ticketId] : [],
861
+ ticketIds: exactTicketIds,
537
862
  });
538
- this.broadcast({
863
+ this._emit({
539
864
  type: 'job.interactive',
540
865
  projectId: req.projectId,
541
866
  jobId: runId,
@@ -618,6 +943,7 @@ class LoopRunManager {
618
943
  // bucketed/ordered by started_at, which must be the turn-start, not the
619
944
  // finish time (this.now() evaluated after the await would be the finish).
620
945
  const aiStepStart = new Date(this.now()).toISOString();
946
+ const aiRecoveryKey = stageAiAccounting('ai', node.id, `loop:${runId}`, aiStepStart);
621
947
  // Interactive upgrade (default for persistent-stdin providers): the
622
948
  // executors return a resident-session plan when the kill-switch is on
623
949
  // and the provider is capable (claude); null falls through to the
@@ -640,8 +966,11 @@ class LoopRunManager {
640
966
  prompt,
641
967
  fallbackModel: nodeModel,
642
968
  nextEventSeq: takeSeq,
969
+ recoveryStepKey: aiRecoveryKey,
643
970
  })
644
971
  : await this.executors.runAiStep({ prompt, sessionId: aiSessionId, provider: nodeProvider, model: nodeModel, effort: nodeEffort, cwd: req.cwd, repoDir: req.repoDir, onLine: logLine, onRawLine, onSpawn: (c) => this._activeChild.set(runId, c), aiStepTimeoutMs });
972
+ if (this._disposed)
973
+ return neverAfterDispose();
645
974
  this._activeChild.delete(runId);
646
975
  // Zero-work strictness (run 01f41203): a settle that consumed NO
647
976
  // model work — the claude CLI's synthetic `Unknown command:` result
@@ -690,7 +1019,8 @@ class LoopRunManager {
690
1019
  if (res.sessionId && (!stepFailed || (blockedReason !== null && !res.failed && !zeroWork)))
691
1020
  aiSessionId = res.sessionId;
692
1021
  history.push(`AI Step: ${truncate(res.text)}`);
693
- record(`loop:${runId}`, { ...res, failed: stepFailed }, aiStepStart);
1022
+ record(`loop:${runId}`, { ...res, failed: stepFailed }, aiStepStart, aiRecoveryKey);
1023
+ this._activeStepRecovery.delete(runId);
694
1024
  if (blockedReason) {
695
1025
  const decision = await awaitHumanDecision(blockedReason);
696
1026
  if (decision.action === 'stop') {
@@ -760,10 +1090,18 @@ class LoopRunManager {
760
1090
  emitStep('shell', `⚡ ${nodeLabel || 'Shell'}`, node.id, iteration + 1);
761
1091
  logLine(`$ ${command}`);
762
1092
  const sh = await this.executors.runShell({ command, cwd: req.cwd, onLine: logLine, onSpawn: (c) => this._activeChild.set(runId, c) });
1093
+ if (this._disposed)
1094
+ return neverAfterDispose();
763
1095
  this._activeChild.delete(runId);
764
1096
  logLine(`(exit ${sh.exitCode})`);
765
1097
  emitStepEnd({ status: sh.exitCode === 0 ? 'ok' : 'failed', exitCode: sh.exitCode, durationMs: sh.durationMs });
766
1098
  totalDuration += sh.durationMs ?? 0;
1099
+ (0, loop_runs_store_1.updateLoopRunCounters)(this.db, runId, {
1100
+ iterationCount: iteration,
1101
+ totalCostUsd: totalCost,
1102
+ totalTokens,
1103
+ totalDurationMs: totalDuration,
1104
+ });
767
1105
  history.push(`Shell \`${command}\` exit=${sh.exitCode}: ${truncate(sh.stdout || sh.stderr)}`);
768
1106
  nodeId = succs[0]?.id;
769
1107
  break;
@@ -781,6 +1119,7 @@ class LoopRunManager {
781
1119
  logLine(`Goal: ${goal}`);
782
1120
  // BUG-32: capture the real Decider start BEFORE the await (see AI step).
783
1121
  const deciderStart = new Date(this.now()).toISOString();
1122
+ const deciderRecoveryKey = stageAiAccounting('decider', node.id, `loop:${runId}:decider`, deciderStart);
784
1123
  const dec = await this.executors.runDecider({
785
1124
  systemPrompt: (0, loop_decider_1.buildDeciderSystemPrompt)(),
786
1125
  // Give the Decider the spec so it can verify completeness against the
@@ -795,6 +1134,8 @@ class LoopRunManager {
795
1134
  onRawLine,
796
1135
  onSpawn: (c) => this._activeChild.set(runId, c),
797
1136
  });
1137
+ if (this._disposed)
1138
+ return neverAfterDispose();
798
1139
  this._activeChild.delete(runId);
799
1140
  // A parseable Decider verdict means this AI invocation (SAME
800
1141
  // provider/model) succeeded → the provider is alive, so clear any
@@ -806,7 +1147,8 @@ class LoopRunManager {
806
1147
  const verdictWord = dec.blocked ? 'blocked' : dec.continue ? 'continue' : 'stop';
807
1148
  // BUG-03: a Decider that couldn't parse a verdict (dec.parsed === false)
808
1149
  // is a failed AI invocation — record it as such, not as success.
809
- record(`loop:${runId}:decider`, { ...dec, failed: !dec.parsed }, deciderStart);
1150
+ record(`loop:${runId}:decider`, { ...dec, failed: !dec.parsed }, deciderStart, deciderRecoveryKey);
1151
+ this._activeStepRecovery.delete(runId);
810
1152
  logLine(`Decision: ${verdictWord} — ${dec.reasoning}`);
811
1153
  // The verdict line above is the decider step's last output. `decision`
812
1154
  // is the route actually taken (an unparseable verdict defaults to
@@ -823,7 +1165,7 @@ class LoopRunManager {
823
1165
  totalTokens,
824
1166
  totalDurationMs: totalDuration,
825
1167
  });
826
- this.broadcast({
1168
+ this._emit({
827
1169
  type: 'loop.run_progress',
828
1170
  projectId: req.projectId,
829
1171
  loopRunId: runId,
@@ -930,21 +1272,65 @@ class LoopRunManager {
930
1272
  }
931
1273
  }
932
1274
  catch (err) {
1275
+ if (this._disposed)
1276
+ return neverAfterDispose();
933
1277
  outcome = 'failed';
934
1278
  console.error(`[loop] run=${runId} traversal threw:`, err);
935
1279
  logLine(`error: ${err?.message ?? String(err)}`, 'stderr');
936
1280
  }
1281
+ if (this._disposed)
1282
+ return neverAfterDispose();
937
1283
  // Backstop: a traversal exception (or any future path that breaks out
938
1284
  // mid-step) must not leave the last step dangling — close it as failed.
939
1285
  // The ONLY way a persisted stream ends with an open step is a mid-flight
940
1286
  // dispose (shutdown/project removal), where this code never runs.
941
1287
  emitStepEnd({ status: 'failed' });
1288
+ // A traversal exception can bypass record(). Reconcile the staged step
1289
+ // BEFORE writing aggregate job totals, while its jobs baseline is still
1290
+ // valid. If reconciliation itself fails, finishLoopRunAndJob detects the
1291
+ // surviving checkpoint and preserves those baseline columns for startup.
1292
+ try {
1293
+ const recoveredSteps = recoverOrphanLoopStepAccounting(this.db, new Date(this.now()).toISOString(), runId);
1294
+ this._activeStepRecovery.delete(runId);
1295
+ const aggregate = this.db.prepare(`
1296
+ SELECT COALESCE(SUM(total_cost_usd), 0) AS cost,
1297
+ COALESCE(SUM(tokens_in), 0) AS tokens_in,
1298
+ COALESCE(SUM(tokens_out), 0) AS tokens_out,
1299
+ COALESCE(SUM(tokens_cache_read), 0) AS cache_read,
1300
+ COALESCE(SUM(tokens_cache_create), 0) AS cache_create,
1301
+ COALESCE(SUM(duration_ms), 0) AS duration,
1302
+ COALESCE(SUM(num_turns), 0) AS turns
1303
+ FROM ai_invocations
1304
+ WHERE surface = 'loop' AND loop_run_id = ?
1305
+ `).get(runId);
1306
+ totalCost = aggregate.cost;
1307
+ totalTokens = aggregate.tokens_in + aggregate.tokens_out;
1308
+ if (recoveredSteps > 0) {
1309
+ // Recovery already committed the authoritative D+X totals. Refresh the
1310
+ // traversal locals before finishLoopRunAndJob; otherwise a catch path
1311
+ // that never called record() writes its stale D baseline back over the
1312
+ // recovered row (duration was the visible casualty, but keep all
1313
+ // counters/iteration coherent).
1314
+ const durable = (0, loop_runs_store_1.getLoopRun)(this.db, runId);
1315
+ if (durable) {
1316
+ totalCost = durable.total_cost_usd;
1317
+ totalTokens = durable.total_tokens;
1318
+ totalDuration = durable.total_duration_ms;
1319
+ iteration = Math.max(iteration, durable.iteration_count);
1320
+ }
1321
+ }
1322
+ finalJobUsage = {
1323
+ tokensIn: aggregate.tokens_in,
1324
+ tokensOut: aggregate.tokens_out,
1325
+ tokensCacheRead: aggregate.cache_read,
1326
+ tokensCacheCreate: aggregate.cache_create,
1327
+ numTurns: aggregate.turns,
1328
+ };
1329
+ }
1330
+ catch (err) {
1331
+ console.error(`[loop] staged accounting reconciliation failed for ${runId}:`, err);
1332
+ }
942
1333
  console.log(`[loop] settle run=${runId} outcome=${outcome} iterations=${iteration} cost=$${totalCost.toFixed(4)}`);
943
- (0, loop_runs_store_1.finishLoopRun)(this.db, runId, {
944
- outcome,
945
- finishedAt: new Date(this.now()).toISOString(),
946
- counters: { iterationCount: iteration, totalCostUsd: totalCost, totalTokens, totalDurationMs: totalDuration },
947
- });
948
1334
  // Settle the backing job so the Jobs list + JobDetail reflect the final
949
1335
  // status, and emit job.finalized so an open JobDetail re-fetches + stops the
950
1336
  // live stream (mirrors QueueManager).
@@ -957,39 +1343,52 @@ class LoopRunManager {
957
1343
  // `≥` when any cost-bearing step ended unpriced (timeout/crash) — the figure
958
1344
  // is a lower bound, not exact.
959
1345
  logLine(`\n■ Loop finished: ${outcome} — ${iteration} iteration${iteration === 1 ? '' : 's'}, ${costUncertain ? '≥ ' : ''}$${totalCost.toFixed(4)}`);
960
- try {
961
- (0, db_1.finishJob)(this.db, runId, {
962
- exit_code: outcome === 'success' ? 0 : 1,
1346
+ const finishedAt = new Date(this.now()).toISOString();
1347
+ (0, loop_runs_store_1.finishLoopRunAndJob)(this.db, runId, {
1348
+ outcome,
1349
+ finishedAt,
1350
+ counters: {
1351
+ iterationCount: iteration,
1352
+ totalCostUsd: totalCost,
1353
+ totalTokens,
1354
+ totalDurationMs: totalDuration,
1355
+ },
1356
+ job: {
1357
+ exitCode: outcome === 'success' ? 0 : 1,
963
1358
  status: jobStatus,
964
- total_cost_usd: totalCost,
965
- tokens_out: totalTokens,
966
- duration_ms: totalDuration,
967
- num_turns: iteration,
968
- });
969
- }
970
- catch { /* best-effort */ }
971
- this.broadcast({
1359
+ totalCostUsd: totalCost,
1360
+ tokensIn: finalJobUsage.tokensIn,
1361
+ tokensOut: finalJobUsage.tokensOut,
1362
+ tokensCacheRead: finalJobUsage.tokensCacheRead,
1363
+ tokensCacheCreate: finalJobUsage.tokensCacheCreate,
1364
+ durationMs: totalDuration,
1365
+ numTurns: finalJobUsage.numTurns,
1366
+ },
1367
+ callbackOutcome: req.deferTerminalOutcome ? 'failed' : outcome,
1368
+ outcomeFinalized: !req.deferTerminalOutcome,
1369
+ });
1370
+ this._emit({
972
1371
  type: 'job.finalized',
973
1372
  projectId: req.projectId,
974
1373
  jobId: runId,
975
1374
  status: jobStatus,
976
1375
  totals: {
977
- tokens_in: 0,
978
- tokens_out: totalTokens,
979
- tokens_cache_read: 0,
980
- tokens_cache_create: 0,
1376
+ tokens_in: finalJobUsage.tokensIn,
1377
+ tokens_out: finalJobUsage.tokensOut,
1378
+ tokens_cache_read: finalJobUsage.tokensCacheRead,
1379
+ tokens_cache_create: finalJobUsage.tokensCacheCreate,
981
1380
  total_cost_usd: totalCost,
982
- num_turns: iteration,
1381
+ num_turns: finalJobUsage.numTurns,
983
1382
  },
984
1383
  timestamp: new Date(this.now()).toISOString(),
985
1384
  });
986
- this.broadcast({
1385
+ this._emit({
987
1386
  type: 'loop.run_completed',
988
1387
  projectId: req.projectId,
989
1388
  loopRunId: runId,
990
1389
  railIndex: req.railIndex ?? null,
991
1390
  status: outcome,
992
- ticketIds: req.ticketId != null ? [req.ticketId] : [],
1391
+ ticketIds: exactTicketIds,
993
1392
  });
994
1393
  return { runId, outcome, iterations: iteration, totalCostUsd: totalCost };
995
1394
  }
@@ -1032,7 +1431,18 @@ class LoopRunManager {
1032
1431
  // The step timeout below is the sole watchdog — the one-shot loop path
1033
1432
  // has no zombie detector either (byte-parity), so none is armed here.
1034
1433
  nextEventSeq: input.nextEventSeq,
1434
+ persistTurnUsage: (usage, completedEventSeq, checkpoint) => {
1435
+ const checkpointTurn = this.db.transaction(() => {
1436
+ (0, db_1.accumulateInteractiveTurn)(this.db, input.runId, usage);
1437
+ (0, loop_runs_store_1.updateLoopStepEventCheckpoint)(this.db, input.runId, input.recoveryStepKey, completedEventSeq, checkpoint?.cost, checkpoint?.turns, checkpoint?.activeDurationMs);
1438
+ });
1439
+ checkpointTurn();
1440
+ },
1441
+ persistTurnActivity: (turnStartedAtMs, activityAtMs) => {
1442
+ (0, loop_runs_store_1.updateLoopStepActivityCheckpoint)(this.db, input.runId, input.recoveryStepKey, turnStartedAtMs, activityAtMs);
1443
+ },
1035
1444
  spawn: input.plan.spawn,
1445
+ killTree: input.plan.killTree,
1036
1446
  onSettle: (info) => {
1037
1447
  if (timer) {
1038
1448
  clearTimeout(timer);
@@ -1041,7 +1451,7 @@ class LoopRunManager {
1041
1451
  this._interactiveSteps.delete(input.runId);
1042
1452
  // The step's session is gone — the composer flips to its gentle
1043
1453
  // "waiting for the next step" state instead of erroring on 409.
1044
- this.broadcast({
1454
+ this._emit({
1045
1455
  type: 'job.interactive',
1046
1456
  projectId: input.projectId,
1047
1457
  jobId: input.runId,
@@ -1095,7 +1505,7 @@ class LoopRunManager {
1095
1505
  catch { /* best-effort */ }
1096
1506
  // A resident step session is live — an open Job Detail composer re-enables
1097
1507
  // without polling (mirror flip of the onSettle broadcast above).
1098
- this.broadcast({
1508
+ this._emit({
1099
1509
  type: 'job.interactive',
1100
1510
  projectId: input.projectId,
1101
1511
  jobId: input.runId,