specrails-desktop 2.36.0 → 2.42.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 (510) hide show
  1. package/README.md +94 -247
  2. package/client/dist/assets/ActivityFeedPage-DoEMumQS.js +1 -0
  3. package/client/dist/assets/AgentBrowserCapture-DIh4K0gh.js +1 -0
  4. package/client/dist/assets/AgentModeAnalyticsPane-mbq4WEoO.js +2 -0
  5. package/client/dist/assets/AgentModeCodePane-CKh7zuA4.js +2 -0
  6. package/client/dist/assets/AgentModeJobsPane-BDokPiLJ.js +1 -0
  7. package/client/dist/assets/AgentsPage-BFVAZbBj.js +87 -0
  8. package/client/dist/assets/AnalyticsPage-3Y9LuWwz.js +1 -0
  9. package/client/dist/assets/CodePage-C9KyuV_C.js +3 -0
  10. package/client/dist/assets/DesktopAnalyticsPage-Bhi54bXQ.js +1 -0
  11. package/client/dist/assets/{DocsDialog-C76VdBJC.js → DocsDialog-Dj6mt4ga.js} +2 -2
  12. package/client/dist/assets/{DocsPage-ziMlj6B6.js → DocsPage-24yWglNI.js} +1 -1
  13. package/client/dist/assets/ExportDropdown-ophapHrT.js +1 -0
  14. package/client/dist/assets/InteractiveJobComposer-C1ucmxQh.js +19 -0
  15. package/client/dist/assets/JobDetailModal-DNWmgVHD.js +1 -0
  16. package/client/dist/assets/JobDetailPage-BcajZyuu.js +1 -0
  17. package/client/dist/assets/JobsPage-B9nTZw6e.js +1 -0
  18. package/client/dist/assets/LoopBuilderPage-Oc48iOFW.js +7 -0
  19. package/client/dist/assets/LoopPreviewModal-ae0Fr6SU.js +1 -0
  20. package/client/dist/assets/LoopsPage-CUEqzUBI.js +1 -0
  21. package/client/dist/assets/MinimizedChatsContext-Deg39peL.js +1 -0
  22. package/client/dist/assets/PluginsPage-C1xtARIx.js +2 -0
  23. package/client/dist/assets/ProjectSettingsDialog-JZzrXFBN.js +1 -0
  24. package/client/dist/assets/RepositoryDeliveries-CSDsiVUz.js +5 -0
  25. package/client/dist/assets/RepositoryScopeSelector-D-RCaknl.js +1 -0
  26. package/client/dist/assets/ReviewPacketPage-x5EfGTiL.js +1 -0
  27. package/client/dist/assets/TemplatePreviewModal-BSyuZxGq.js +1 -0
  28. package/client/dist/assets/TicketDetailModalContext-DqyF0so-.js +14 -0
  29. package/client/dist/assets/Trans-SnMX4MhW.js +1 -0
  30. package/client/dist/assets/agent-BdT0SB1D.js +1 -0
  31. package/client/dist/assets/agent-Bh9K7iEt.js +1 -0
  32. package/client/dist/assets/agent-C4ibdFk9.js +1 -0
  33. package/client/dist/assets/agent-CSFY22Zo.js +1 -0
  34. package/client/dist/assets/agent-DAnqxUn8.js +1 -0
  35. package/client/dist/assets/agent-DTQiu0Mi.js +1 -0
  36. package/client/dist/assets/agent-T54WuR_Y.js +1 -0
  37. package/client/dist/assets/agent-i75WDZOA.js +1 -0
  38. package/client/dist/assets/browser-05vRDg_y.js +1 -0
  39. package/client/dist/assets/browser-Bulb-cB1.js +1 -0
  40. package/client/dist/assets/browser-C7f-Jsg1.js +1 -0
  41. package/client/dist/assets/browser-CG0PE1j_.js +1 -0
  42. package/client/dist/assets/browser-CMzaqAjo.js +1 -0
  43. package/client/dist/assets/browser-DMajYdNX.js +1 -0
  44. package/client/dist/assets/browser-DxcOHrdP.js +1 -0
  45. package/client/dist/assets/browser-ObE7xVo3.js +1 -0
  46. package/client/dist/assets/builder-765NeYTp.js +1 -0
  47. package/client/dist/assets/builder-8MG3V6ea.js +1 -0
  48. package/client/dist/assets/builder-B-1UBs5K.js +1 -0
  49. package/client/dist/assets/builder-BTiGnPT0.js +1 -0
  50. package/client/dist/assets/builder-Cx9Rz-7u.js +1 -0
  51. package/client/dist/assets/builder-DKFMELBl.js +1 -0
  52. package/client/dist/assets/builder-OcmKrC0h.js +1 -0
  53. package/client/dist/assets/builder-P9qVBXFZ.js +1 -0
  54. package/client/dist/assets/{button-CJqFnUOL.js → button-DCBqEaZl.js} +1 -1
  55. package/client/dist/assets/{code-CkUuW4m9.js → code-B37GRg9C.js} +1 -1
  56. package/client/dist/assets/{code-B0O8-p8k.js → code-BBOEZbf9.js} +1 -1
  57. package/client/dist/assets/{code-B1VLZDhS.js → code-BRm-jcyx.js} +1 -1
  58. package/client/dist/assets/{code-Bpj4Ddml.js → code-CF7w62CR.js} +1 -1
  59. package/client/dist/assets/{code-BykBZ5-e.js → code-CLuvJt2r.js} +1 -1
  60. package/client/dist/assets/{code-B86IMAfz.js → code-D0RCm59f.js} +1 -1
  61. package/client/dist/assets/code-DaZ-ZwCT.js +1 -0
  62. package/client/dist/assets/{code-cAqaqMTx.js → code-iIHkGaCL.js} +1 -1
  63. package/client/dist/assets/commands-0XjAftla.js +1 -0
  64. package/client/dist/assets/commands-BAWSYHjQ.js +1 -0
  65. package/client/dist/assets/commands-BBwowgiE.js +1 -0
  66. package/client/dist/assets/commands-BacjIZLN.js +1 -0
  67. package/client/dist/assets/commands-Bs62IAZ3.js +1 -0
  68. package/client/dist/assets/commands-CLPdh-Hh.js +1 -0
  69. package/client/dist/assets/commands-DlbFzgrp.js +1 -0
  70. package/client/dist/assets/commands-LjPSsi5F.js +1 -0
  71. package/client/dist/assets/common-B5AZNYMH.js +1 -0
  72. package/client/dist/assets/common-BFZ0c_vN.js +1 -0
  73. package/client/dist/assets/common-BrlwEnL7.js +1 -0
  74. package/client/dist/assets/common-BttQx3z4.js +1 -0
  75. package/client/dist/assets/common-CM65k_-e.js +1 -0
  76. package/client/dist/assets/common-D4mvFFlu.js +1 -0
  77. package/client/dist/assets/common-TZ9Ny96S.js +1 -0
  78. package/client/dist/assets/common-nFpSF4GC.js +1 -0
  79. package/client/dist/assets/dashboard-B14uYXkG.js +1 -0
  80. package/client/dist/assets/dashboard-BK_mez-z.js +1 -0
  81. package/client/dist/assets/dashboard-BfBir_6o.js +1 -0
  82. package/client/dist/assets/dashboard-Bt0cCzeG.js +1 -0
  83. package/client/dist/assets/dashboard-BwkjxI6H.js +1 -0
  84. package/client/dist/assets/{dashboard-C8WOEiPu.js → dashboard-C3bDKf7s.js} +1 -1
  85. package/client/dist/assets/dashboard-CdvjGfog.js +1 -0
  86. package/client/dist/assets/dashboard-fRrP0Kv0.js +1 -0
  87. package/client/dist/assets/dialog-SRw01_QU.js +45 -0
  88. package/client/dist/assets/dist-js-CJ_2XX1Y.js +1 -0
  89. package/client/dist/assets/formatDistanceToNow-DSj5DprF.js +1 -0
  90. package/client/dist/assets/getTimezoneOffsetInMilliseconds-YhD8rIEs.js +1 -0
  91. package/client/dist/assets/index-DwAmL7m5.css +2 -0
  92. package/client/dist/assets/index-EzrkUb8B.js +76 -0
  93. package/client/dist/assets/{jira-api-B1-MUehg.js → jira-api-CSCqGr-Q.js} +1 -1
  94. package/client/dist/assets/{jobs-CN_zv4Gr.js → jobs-B8rrzfyZ.js} +1 -1
  95. package/client/dist/assets/{jobs-DUlWSYq2.js → jobs-BS6RTurf.js} +1 -1
  96. package/client/dist/assets/{jobs-Bpks6doU.js → jobs-BTT0Bsks.js} +1 -1
  97. package/client/dist/assets/{jobs-Bgq-PZkC.js → jobs-CezU69AE.js} +1 -1
  98. package/client/dist/assets/{jobs-SwYS2M-B.js → jobs-ClFJae0s.js} +1 -1
  99. package/client/dist/assets/{jobs-BAls3bWq.js → jobs-DJSFIFVy.js} +1 -1
  100. package/client/dist/assets/{jobs-DhxKgWFq.js → jobs-DhgGr8vm.js} +1 -1
  101. package/client/dist/assets/{jobs-B_TeSQxb.js → jobs-DzMjUcaN.js} +1 -1
  102. package/client/dist/assets/mcp-6rMoZfEO.js +1 -0
  103. package/client/dist/assets/mcp-BD8sL5mP.js +1 -0
  104. package/client/dist/assets/mcp-BkcA3I5s.js +1 -0
  105. package/client/dist/assets/mcp-CCpSelOh.js +1 -0
  106. package/client/dist/assets/mcp-CUAN6ORK.js +1 -0
  107. package/client/dist/assets/mcp-CqbRxRTE.js +1 -0
  108. package/client/dist/assets/mcp-DP2YfVhO.js +1 -0
  109. package/client/dist/assets/mcp-DUVQamDz.js +1 -0
  110. package/client/dist/assets/narration-B-ynWTpn.js +1 -0
  111. package/client/dist/assets/narration-Bj_laZwg.js +1 -0
  112. package/client/dist/assets/narration-BxHpmMv_.js +1 -0
  113. package/client/dist/assets/narration-C1Px0-0j.js +1 -0
  114. package/client/dist/assets/narration-CAXhGbzM.js +1 -0
  115. package/client/dist/assets/narration-DQ1l_Kq6.js +1 -0
  116. package/client/dist/assets/narration-XISXwqZd.js +1 -0
  117. package/client/dist/assets/narration-kwzfglQo.js +1 -0
  118. package/client/dist/assets/nav-1-Hh2kOz.js +1 -0
  119. package/client/dist/assets/nav-B34QkESm.js +1 -0
  120. package/client/dist/assets/nav-Bap_UXAq.js +1 -0
  121. package/client/dist/assets/{nav-pYuXq7sf.js → nav-BwxXIB24.js} +1 -1
  122. package/client/dist/assets/nav-Ch-zHBG5.js +1 -0
  123. package/client/dist/assets/nav-CygC_WN-.js +1 -0
  124. package/client/dist/assets/{nav-iwjCg5FR.js → nav-KnnLQtOQ.js} +1 -1
  125. package/client/dist/assets/nav-m00uxYUG.js +1 -0
  126. package/client/dist/assets/packet-BQ2tXfJI.js +1 -0
  127. package/client/dist/assets/packet-BdpOJ1Cy.js +1 -0
  128. package/client/dist/assets/packet-C3Ievb9R.js +1 -0
  129. package/client/dist/assets/packet-C4zJIG0Y.js +1 -0
  130. package/client/dist/assets/packet-Cx-zRuWn.js +1 -0
  131. package/client/dist/assets/packet-DUTGX52M.js +1 -0
  132. package/client/dist/assets/packet-DhsKtUPB.js +1 -0
  133. package/client/dist/assets/packet-z3LJ8Zyy.js +1 -0
  134. package/client/dist/assets/project-repositories-DYhTvwQ1.js +1 -0
  135. package/client/dist/assets/provider-capabilities-B-wOHfLP.js +1 -0
  136. package/client/dist/assets/settings-BBt2xtDQ.js +1 -0
  137. package/client/dist/assets/settings-BJB9RKJj.js +1 -0
  138. package/client/dist/assets/settings-CHwQR2rB.js +1 -0
  139. package/client/dist/assets/settings-CrMi3kSi.js +1 -0
  140. package/client/dist/assets/settings-DKyhDGD1.js +1 -0
  141. package/client/dist/assets/settings-Deupji9c.js +1 -0
  142. package/client/dist/assets/settings-DqRMmwAM.js +1 -0
  143. package/client/dist/assets/settings-J4rj6vo-.js +1 -0
  144. package/client/dist/assets/setup-2xGgu4Sk.js +1 -0
  145. package/client/dist/assets/setup-Ba11l2JA.js +1 -0
  146. package/client/dist/assets/setup-BzW4kMok.js +1 -0
  147. package/client/dist/assets/setup-COzwYhkJ.js +1 -0
  148. package/client/dist/assets/setup-CRVULhtF.js +1 -0
  149. package/client/dist/assets/setup-DgvlxwpE.js +1 -0
  150. package/client/dist/assets/setup-Dw_wYeEY.js +1 -0
  151. package/client/dist/assets/setup-ZPtEeJPW.js +1 -0
  152. package/client/dist/assets/spending-BqbdPm_4.js +1 -0
  153. package/client/dist/assets/square-Ci-PFcFe.js +1 -0
  154. package/client/dist/assets/{terminal-BFRZnMPP.js → terminal-BAzpHtpY.js} +1 -1
  155. package/client/dist/assets/{terminal-IahE4LW9.js → terminal-C7dHeR3x.js} +1 -1
  156. package/client/dist/assets/{terminal-tITa81iz.js → terminal-CMla5Qsz.js} +1 -1
  157. package/client/dist/assets/{terminal-BZiIu3rf.js → terminal-CdNXH-n9.js} +1 -1
  158. package/client/dist/assets/{terminal-ScjyXNxQ.js → terminal-Cw5bZsmV.js} +1 -1
  159. package/client/dist/assets/{terminal-C2NAd7BW.js → terminal-DFUP4eNv.js} +1 -1
  160. package/client/dist/assets/{terminal-d7GadXbN.js → terminal-DGqljeK_.js} +1 -1
  161. package/client/dist/assets/{terminal-QpXZU2Bn.js → terminal-cU70NodO.js} +1 -1
  162. package/client/dist/assets/useDesktop-RSMTyy7V.js +1 -0
  163. package/client/dist/assets/useSharedWebSocket-BOdS5_Iz.js +2 -0
  164. package/client/dist/index.html +34 -35
  165. package/docs/README.md +3 -0
  166. package/docs/agent-live-steering.md +57 -0
  167. package/docs/ci-cd.md +57 -0
  168. package/docs/code-explorer.md +48 -0
  169. package/docs/codex.md +12 -1
  170. package/docs/features/detachable-mission-windows.md +59 -0
  171. package/docs/guide/de/integrations/5-mcp-server.md +13 -9
  172. package/docs/guide/de/integrations/6-agent-chat.md +1 -0
  173. package/docs/guide/en/integrations/5-mcp-server.md +13 -9
  174. package/docs/guide/en/integrations/6-agent-chat.md +1 -0
  175. package/docs/guide/es/integrations/5-mcp-server.md +13 -9
  176. package/docs/guide/es/integrations/6-agent-chat.md +1 -0
  177. package/docs/guide/fr/integrations/5-mcp-server.md +13 -9
  178. package/docs/guide/fr/integrations/6-agent-chat.md +1 -0
  179. package/docs/guide/it/integrations/5-mcp-server.md +13 -9
  180. package/docs/guide/it/integrations/6-agent-chat.md +1 -0
  181. package/docs/guide/ja/integrations/5-mcp-server.md +13 -9
  182. package/docs/guide/ja/integrations/6-agent-chat.md +1 -0
  183. package/docs/guide/pt/integrations/5-mcp-server.md +13 -9
  184. package/docs/guide/pt/integrations/6-agent-chat.md +1 -0
  185. package/docs/guide/zh/integrations/5-mcp-server.md +13 -9
  186. package/docs/guide/zh/integrations/6-agent-chat.md +1 -0
  187. package/docs/internals/README.md +1 -0
  188. package/docs/internals/browser-capture-performance.md +55 -3
  189. package/docs/internals/browser-login-popups.md +45 -0
  190. package/docs/internals/browser-native-retina-audit.md +39 -0
  191. package/docs/internals/ci-cd-audit.md +39 -0
  192. package/docs/internals/core-runtime-updates.md +68 -0
  193. package/docs/internals/embedded-browser-native-webview-evaluation.md +2 -0
  194. package/docs/internals/implementation-reliability-audit.md +93 -0
  195. package/docs/internals/interactive-jobs.md +110 -6
  196. package/docs/internals/mcp-mission-audit.md +76 -0
  197. package/docs/internals/project-builder.md +404 -42
  198. package/docs/internals/safe-pr-review-flow.md +52 -10
  199. package/docs/internals/startup-recovery-audit.md +58 -0
  200. package/docs/mcp.md +74 -41
  201. package/docs/mission-processes.md +59 -0
  202. package/docs/multi-repo-projects.md +51 -0
  203. package/docs/platforms/windows-parity.md +70 -0
  204. package/docs/platforms/windows.md +13 -11
  205. package/docs/running-pipelines.md +14 -0
  206. package/mcp-bridge/dist/specrails-mcp.js +7186 -0
  207. package/package.json +9 -4
  208. package/server/dist/agent-chat-manager.js +537 -68
  209. package/server/dist/agent-chat-router.js +87 -6
  210. package/server/dist/agent-context-resolver.js +129 -25
  211. package/server/dist/agent-defaults.js +19 -11
  212. package/server/dist/agent-input-store.js +206 -0
  213. package/server/dist/agent-mcp-config.js +54 -15
  214. package/server/dist/agent-operator-prompt.js +255 -65
  215. package/server/dist/agent-spec-framing.js +202 -0
  216. package/server/dist/agent-steering.js +218 -0
  217. package/server/dist/agent-store.js +153 -0
  218. package/server/dist/api-not-found.js +8 -0
  219. package/server/dist/background-process-control.js +164 -0
  220. package/server/dist/background-process-service.js +61 -0
  221. package/server/dist/background-process-store.js +242 -0
  222. package/server/dist/background-windows-bootstrap.js +238 -0
  223. package/server/dist/blueprint-chat-manager.js +476 -62
  224. package/server/dist/blueprint-commit.js +21 -1
  225. package/server/dist/blueprint-draft-parser.js +122 -17
  226. package/server/dist/blueprint-generation.js +238 -0
  227. package/server/dist/blueprint-operator-prompt.js +91 -63
  228. package/server/dist/blueprint-router.js +55 -3
  229. package/server/dist/blueprint-spec-fixtures.js +85 -0
  230. package/server/dist/blueprint-spec-quality.js +57 -16
  231. package/server/dist/blueprint-store.js +92 -1
  232. package/server/dist/browser-capture-manager.js +86 -30
  233. package/server/dist/browser-playwright.js +168 -45
  234. package/server/dist/browser-viewport.js +26 -0
  235. package/server/dist/chat-manager.js +23 -15
  236. package/server/dist/chromium-archive.cjs +83 -0
  237. package/server/dist/chromium-resolver.js +72 -64
  238. package/server/dist/code-activity.js +145 -0
  239. package/server/dist/code-explorer-router.js +714 -114
  240. package/server/dist/core-compat.js +28 -1
  241. package/server/dist/core-execution.js +111 -0
  242. package/server/dist/core-package.js +4 -16
  243. package/server/dist/core-runtime.js +202 -0
  244. package/server/dist/core-update-manager.js +161 -60
  245. package/server/dist/core-update-state.js +29 -0
  246. package/server/dist/db.js +66 -35
  247. package/server/dist/delivery-evidence.js +142 -39
  248. package/server/dist/desktop-db.js +239 -43
  249. package/server/dist/desktop-router.js +109 -9
  250. package/server/dist/dev-ports.js +8 -3
  251. package/server/dist/file-provenance.js +34 -22
  252. package/server/dist/file-story-manager.js +62 -64
  253. package/server/dist/file-story.js +118 -35
  254. package/server/dist/file-summary-generator.js +46 -5
  255. package/server/dist/file-summary-manager.js +355 -106
  256. package/server/dist/framework-manager.js +98 -110
  257. package/server/dist/framework-reseed.js +76 -10
  258. package/server/dist/git-diagnostics.js +2 -2
  259. package/server/dist/headroom-manager.js +38 -3
  260. package/server/dist/host-control.js +52 -0
  261. package/server/dist/index.js +43 -2
  262. package/server/dist/interactive-job-session.js +149 -16
  263. package/server/dist/internal-api.js +48 -0
  264. package/server/dist/jira/jira-materializer.js +1 -0
  265. package/server/dist/json-tolerant.js +174 -0
  266. package/server/dist/legacy-migration.js +4 -2
  267. package/server/dist/loop-command-catalog.js +83 -21
  268. package/server/dist/loop-constants.js +6 -1
  269. package/server/dist/loop-decider.js +11 -1
  270. package/server/dist/loop-executors.js +194 -45
  271. package/server/dist/loop-factory.js +11 -4
  272. package/server/dist/loop-graph.js +90 -4
  273. package/server/dist/loop-run-manager.js +301 -69
  274. package/server/dist/loop-shell-invocation.js +21 -0
  275. package/server/dist/loop-step-idle.js +55 -0
  276. package/server/dist/loop-templates.js +31 -7
  277. package/server/dist/mcp/agent-capability.js +37 -4
  278. package/server/dist/mcp/guide.js +149 -36
  279. package/server/dist/mcp/mcp-admin-router.js +20 -4
  280. package/server/dist/mcp/mcp-server.js +89 -43
  281. package/server/dist/mcp/mcp-tiers.js +6 -5
  282. package/server/dist/mcp/mcp-token.js +3 -1
  283. package/server/dist/mcp/resources.js +5 -4
  284. package/server/dist/mcp/tools/catalog.js +4 -0
  285. package/server/dist/mcp/tools/code.js +125 -14
  286. package/server/dist/mcp/tools/context.js +153 -0
  287. package/server/dist/mcp/tools/git.js +15 -5
  288. package/server/dist/mcp/tools/jobs.js +84 -71
  289. package/server/dist/mcp/tools/loops.js +17 -6
  290. package/server/dist/mcp/tools/meta.js +74 -27
  291. package/server/dist/mcp/tools/mission.js +29 -0
  292. package/server/dist/mcp/tools/projects.js +22 -6
  293. package/server/dist/mcp/tools/rails.js +48 -11
  294. package/server/dist/mcp/tools/specs.js +86 -14
  295. package/server/dist/mcp/tools/support.js +1 -1
  296. package/server/dist/mcp/tools/types.js +117 -43
  297. package/server/dist/mcp/tools/watch.js +144 -51
  298. package/server/dist/milestone-chain-store.js +164 -0
  299. package/server/dist/milestone-chain.js +487 -0
  300. package/server/dist/milestone-progress.js +348 -0
  301. package/server/dist/mobile/mobile-devices.js +10 -7
  302. package/server/dist/mobile/mobile-gateway.js +2 -0
  303. package/server/dist/mobile/mobile-missions.js +214 -0
  304. package/server/dist/mobile/mobile-redact.js +17 -3
  305. package/server/dist/mobile/mobile-router.js +46 -4
  306. package/server/dist/mobile/mobile-ws.js +49 -4
  307. package/server/dist/multi-repo-bases.js +56 -0
  308. package/server/dist/multi-repo-checkout.js +101 -0
  309. package/server/dist/multi-repo-delivery.js +100 -0
  310. package/server/dist/multi-repo-execution-store.js +65 -0
  311. package/server/dist/multi-repo-execution.js +453 -0
  312. package/server/dist/offline-assemble.js +10 -8
  313. package/server/dist/openspec-runtime-plugin-commands.json +18 -0
  314. package/server/dist/openspec-runtime-plugin.js +120 -0
  315. package/server/dist/plugins/codex-spawn.js +49 -0
  316. package/server/dist/plugins/serena/manifest.js +2 -2
  317. package/server/dist/plugins/serena/templates/instructions.md +16 -0
  318. package/server/dist/pr-publisher.js +1 -1
  319. package/server/dist/profiles-router.js +3 -18
  320. package/server/dist/project-code-discovery.js +142 -0
  321. package/server/dist/project-git.js +19 -21
  322. package/server/dist/project-registry.js +272 -20
  323. package/server/dist/project-repositories.js +157 -0
  324. package/server/dist/project-repository-provenance.js +31 -0
  325. package/server/dist/project-router-background-processes.js +105 -0
  326. package/server/dist/project-router-git.js +75 -16
  327. package/server/dist/project-router-jobs.js +2 -126
  328. package/server/dist/project-router-loop-runs.js +72 -2
  329. package/server/dist/project-router-tickets.js +53 -8
  330. package/server/dist/project-router.js +239 -38
  331. package/server/dist/provider-detection.js +18 -6
  332. package/server/dist/provider-limit.js +64 -0
  333. package/server/dist/providers/claude-adapter.js +26 -1
  334. package/server/dist/providers/claude-background-tasks.js +18 -0
  335. package/server/dist/providers/claude-live-session.js +383 -0
  336. package/server/dist/providers/codex-adapter.js +19 -0
  337. package/server/dist/providers/codex-live-session.js +545 -0
  338. package/server/dist/providers/gemini-adapter.js +1 -0
  339. package/server/dist/providers/live-session-types.js +12 -0
  340. package/server/dist/providers/live-session.js +13 -0
  341. package/server/dist/providers/terminal-result.js +24 -0
  342. package/server/dist/queue-manager.js +2 -0
  343. package/server/dist/rail-isolated-launch.js +141 -16
  344. package/server/dist/rail-pr-decision.js +364 -99
  345. package/server/dist/rail-pr-store.js +24 -10
  346. package/server/dist/rail-worktree-release.js +34 -2
  347. package/server/dist/rail-worktrees-store.js +4 -4
  348. package/server/dist/rails-router.js +249 -55
  349. package/server/dist/repo-lock.js +92 -17
  350. package/server/dist/review-packet.js +23 -13
  351. package/server/dist/schemas/file-summary.v1.json +59 -59
  352. package/server/dist/schemas/profile.v1.json +137 -137
  353. package/server/dist/setup-manager.js +52 -8
  354. package/server/dist/shell-integration/bash-shim.bash +45 -0
  355. package/server/dist/shell-integration/fish-shim.fish +36 -0
  356. package/server/dist/shell-integration/powershell-shim.ps1 +48 -0
  357. package/server/dist/shell-integration/zsh-shim.zsh +47 -0
  358. package/server/dist/smash-runner.js +1 -0
  359. package/server/dist/spawn-lifecycle.js +44 -27
  360. package/server/dist/spec-contract-prompt.js +143 -0
  361. package/server/dist/spec-draft-parser.js +6 -1
  362. package/server/dist/spec-models.js +1 -0
  363. package/server/dist/stuck-run-detector.js +1 -0
  364. package/server/dist/terminal-manager.js +21 -1
  365. package/server/dist/terminal-shell-integration.js +4 -1
  366. package/server/dist/transient-children.js +619 -230
  367. package/server/dist/util/cli-prompt.js +78 -5
  368. package/server/dist/util/sqlite-migrations.js +20 -0
  369. package/server/dist/util/win-spawn.js +18 -3
  370. package/server/dist/util/windows-provider-shell.js +29 -0
  371. package/server/dist/verification-sentinel.js +14 -0
  372. package/server/dist/vitest-setup.js +2 -0
  373. package/server/dist/windows-job-supervisor.js +133 -0
  374. package/server/dist/workspace-manager.js +9 -30
  375. package/server/dist/worktree-manager.js +17 -2
  376. package/server/dist/worktree-overlay.js +3 -1
  377. package/client/dist/assets/ActivityFeedPage-tAQcWs1R.js +0 -1
  378. package/client/dist/assets/AgentBrowserCapture-BX8BNae5.js +0 -1
  379. package/client/dist/assets/AgentModeAnalyticsPane-BVAqCL0K.js +0 -2
  380. package/client/dist/assets/AgentModeCodePane-9XfqQc9c.js +0 -2
  381. package/client/dist/assets/AgentModeJobsPane-BCjLsPhc.js +0 -1
  382. package/client/dist/assets/AgentsPage-C8J_42sm.js +0 -87
  383. package/client/dist/assets/AnalyticsPage-BtQuyfJm.js +0 -1
  384. package/client/dist/assets/CodePage-BZ2MwdCu.js +0 -2
  385. package/client/dist/assets/DesktopAnalyticsPage-Bh_29V4g.js +0 -1
  386. package/client/dist/assets/ExportDropdown-BrtfWzEG.js +0 -1
  387. package/client/dist/assets/InteractiveJobComposer-BCZiSfzc.js +0 -19
  388. package/client/dist/assets/JobDetailModal-NAJmuYIT.js +0 -1
  389. package/client/dist/assets/JobDetailPage-fE0ZycUX.js +0 -1
  390. package/client/dist/assets/JobsPage-z5M_HVJG.js +0 -1
  391. package/client/dist/assets/LoopBuilderPage-j1FQS47c.js +0 -7
  392. package/client/dist/assets/LoopPreviewModal-DhDJjdCe.js +0 -1
  393. package/client/dist/assets/LoopsPage-Ll7zORZb.js +0 -1
  394. package/client/dist/assets/MinimizedChatsContext-Bd3OqR_S.js +0 -1
  395. package/client/dist/assets/PluginsPage-DN4kGvJt.js +0 -2
  396. package/client/dist/assets/ProjectSettingsDialog-OgsgxjzF.js +0 -1
  397. package/client/dist/assets/ReviewPacketPage-DFog4sXn.js +0 -1
  398. package/client/dist/assets/TemplatePreviewModal-L2XxqiOD.js +0 -1
  399. package/client/dist/assets/TicketDetailModal-CsF-AWZX.js +0 -14
  400. package/client/dist/assets/Trans-Bsz9_gyq.js +0 -1
  401. package/client/dist/assets/agent-BQgKv_2F.js +0 -1
  402. package/client/dist/assets/agent-C1NRDJqL.js +0 -1
  403. package/client/dist/assets/agent-CLNleiy6.js +0 -1
  404. package/client/dist/assets/agent-CmceArkQ.js +0 -1
  405. package/client/dist/assets/agent-D3UxQ3SR.js +0 -1
  406. package/client/dist/assets/agent-DB1JeE64.js +0 -1
  407. package/client/dist/assets/agent-DXkHcQ-D.js +0 -1
  408. package/client/dist/assets/agent-DjJJJ2M8.js +0 -1
  409. package/client/dist/assets/auth-D_5bQv5W.js +0 -1
  410. package/client/dist/assets/ban-E4EaZe3a.js +0 -1
  411. package/client/dist/assets/bot-DzuuuC5R.js +0 -1
  412. package/client/dist/assets/browser-BgqHDMXP.js +0 -1
  413. package/client/dist/assets/browser-CsKJtrpv.js +0 -1
  414. package/client/dist/assets/browser-Cu58EpZU.js +0 -1
  415. package/client/dist/assets/browser-CvXOJm-j.js +0 -1
  416. package/client/dist/assets/browser-DOrFh1c7.js +0 -1
  417. package/client/dist/assets/browser-DQv_Jo7x.js +0 -1
  418. package/client/dist/assets/browser-LPgxvJu2.js +0 -1
  419. package/client/dist/assets/browser-eL-3MIEI.js +0 -1
  420. package/client/dist/assets/builder-0IWtosUm.js +0 -1
  421. package/client/dist/assets/builder-Bf2N6XWn.js +0 -1
  422. package/client/dist/assets/builder-BnqaaBkT.js +0 -1
  423. package/client/dist/assets/builder-BxxfGgAO.js +0 -1
  424. package/client/dist/assets/builder-C5IIgWsq.js +0 -1
  425. package/client/dist/assets/builder-CHuahItu.js +0 -1
  426. package/client/dist/assets/builder-CjWosXL_.js +0 -1
  427. package/client/dist/assets/builder-CpCIOoKj.js +0 -1
  428. package/client/dist/assets/code-CHPkOkDS.js +0 -1
  429. package/client/dist/assets/commands-6tYQih5m.js +0 -1
  430. package/client/dist/assets/commands-BK2QinXN.js +0 -1
  431. package/client/dist/assets/commands-C2Nca76h.js +0 -1
  432. package/client/dist/assets/commands-CKdLihNX.js +0 -1
  433. package/client/dist/assets/commands-CYIBTAlc.js +0 -1
  434. package/client/dist/assets/commands-CjUtYJIM.js +0 -1
  435. package/client/dist/assets/commands-Cq_u_8y9.js +0 -1
  436. package/client/dist/assets/commands-D_ZHYQug.js +0 -1
  437. package/client/dist/assets/common-6MLbD-jM.js +0 -1
  438. package/client/dist/assets/common-BKGEu4rm.js +0 -1
  439. package/client/dist/assets/common-BNbnv8cE.js +0 -1
  440. package/client/dist/assets/common-BqGWtDbt.js +0 -1
  441. package/client/dist/assets/common-DBtTzeYm.js +0 -1
  442. package/client/dist/assets/common-DEW3Nr8n.js +0 -1
  443. package/client/dist/assets/common-DHsv9N5d.js +0 -1
  444. package/client/dist/assets/common-mkrEcW-Y.js +0 -1
  445. package/client/dist/assets/dashboard-B9GhC5ku.js +0 -1
  446. package/client/dist/assets/dashboard-CBnv6zOI.js +0 -1
  447. package/client/dist/assets/dashboard-CaP0fU87.js +0 -1
  448. package/client/dist/assets/dashboard-D4ZZ4x84.js +0 -1
  449. package/client/dist/assets/dashboard-DmuTQBX8.js +0 -1
  450. package/client/dist/assets/dashboard-DynLYj3d.js +0 -1
  451. package/client/dist/assets/dashboard-zj4gGFJ1.js +0 -1
  452. package/client/dist/assets/dialog-CdWCb02j.js +0 -45
  453. package/client/dist/assets/dist-bVqfYmby.js +0 -1
  454. package/client/dist/assets/dist-js-CWeUdf5Z.js +0 -1
  455. package/client/dist/assets/formatDistanceToNow-DQCvMgUc.js +0 -1
  456. package/client/dist/assets/getTimezoneOffsetInMilliseconds-C16cxSe1.js +0 -1
  457. package/client/dist/assets/git-refresh-wTW0rO0Z.js +0 -1
  458. package/client/dist/assets/i18n-hnX2DPmo.js +0 -2
  459. package/client/dist/assets/index-DIhMEAVw.js +0 -66
  460. package/client/dist/assets/index-DwygcU7s.css +0 -2
  461. package/client/dist/assets/mcp-B8X8wBLu.js +0 -1
  462. package/client/dist/assets/mcp-C2AojZZN.js +0 -1
  463. package/client/dist/assets/mcp-D8BT78_L.js +0 -1
  464. package/client/dist/assets/mcp-Df5RWBNk.js +0 -1
  465. package/client/dist/assets/mcp-Dj7BgMQW.js +0 -1
  466. package/client/dist/assets/mcp-Q7iT7rcz.js +0 -1
  467. package/client/dist/assets/mcp-YlvNvJbW.js +0 -1
  468. package/client/dist/assets/mcp-mKFpTSRb.js +0 -1
  469. package/client/dist/assets/narration-4Z2VUhZw.js +0 -1
  470. package/client/dist/assets/narration-BGuiEDj3.js +0 -1
  471. package/client/dist/assets/narration-BuSoqRmv.js +0 -1
  472. package/client/dist/assets/narration-CB9t29fy.js +0 -1
  473. package/client/dist/assets/narration-CU3x8FHD.js +0 -1
  474. package/client/dist/assets/narration-CWeXhfP5.js +0 -1
  475. package/client/dist/assets/narration-DRaDnhT5.js +0 -1
  476. package/client/dist/assets/narration-DtEbQgCw.js +0 -1
  477. package/client/dist/assets/nav-ApJVg3xu.js +0 -1
  478. package/client/dist/assets/nav-BKNky1Bg.js +0 -1
  479. package/client/dist/assets/nav-C6vWWKYx.js +0 -1
  480. package/client/dist/assets/nav-CXJ6Aw89.js +0 -1
  481. package/client/dist/assets/nav-CsmFBIDf.js +0 -1
  482. package/client/dist/assets/nav-DjkmQJjd.js +0 -1
  483. package/client/dist/assets/packet-B1IxaL5f.js +0 -1
  484. package/client/dist/assets/packet-Bh5kkbiB.js +0 -1
  485. package/client/dist/assets/packet-By8fqYxW.js +0 -1
  486. package/client/dist/assets/packet-CZdvA44E.js +0 -1
  487. package/client/dist/assets/packet-CrjHRQCP.js +0 -1
  488. package/client/dist/assets/packet-Dwi2pTtx.js +0 -1
  489. package/client/dist/assets/packet-_-ZIa0IR.js +0 -1
  490. package/client/dist/assets/packet-n0W6E1Hn.js +0 -1
  491. package/client/dist/assets/play-DNlrawbS.js +0 -1
  492. package/client/dist/assets/provider-capabilities-Doej9lfa.js +0 -1
  493. package/client/dist/assets/settings-Bd0x4TBa.js +0 -1
  494. package/client/dist/assets/settings-C1-7cXzC.js +0 -1
  495. package/client/dist/assets/settings-CAFaT38b.js +0 -1
  496. package/client/dist/assets/settings-Ct3v1nW2.js +0 -1
  497. package/client/dist/assets/settings-DHh7_IF5.js +0 -1
  498. package/client/dist/assets/settings-DQXAmdcv.js +0 -1
  499. package/client/dist/assets/settings-guzlhpup.js +0 -1
  500. package/client/dist/assets/settings-sZfTPMEH.js +0 -1
  501. package/client/dist/assets/setup-3jWaoVUO.js +0 -1
  502. package/client/dist/assets/setup-BjgRneb2.js +0 -1
  503. package/client/dist/assets/setup-BzFIjv-f.js +0 -1
  504. package/client/dist/assets/setup-C4VfQsxE.js +0 -1
  505. package/client/dist/assets/setup-DfUkpDbN.js +0 -1
  506. package/client/dist/assets/setup-ECe90Pmu.js +0 -1
  507. package/client/dist/assets/setup-dlqC4Jmm.js +0 -1
  508. package/client/dist/assets/setup-jwfLMiM2.js +0 -1
  509. package/client/dist/assets/spending-EC9xqio-.js +0 -1
  510. package/client/dist/assets/useDesktop-BQVt8lFd.js +0 -1
@@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.watchTool = watchTool;
4
4
  const zod_1 = require("zod");
5
5
  const types_1 = require("./types");
6
- // Event `type` suffixes/strings that mean an async operation has settled.
7
- const TERMINAL_PATTERNS = [
6
+ // Exact event types that mean an async operation has settled.
7
+ const TERMINAL_PATTERNS = new Set([
8
8
  'job.finalized',
9
9
  'rail.job_completed',
10
10
  'rail.job_stopped',
@@ -16,6 +16,7 @@ const TERMINAL_PATTERNS = [
16
16
  'ticket_ai_edit_error',
17
17
  'agent_refine_ready',
18
18
  'agent_refine_error',
19
+ 'agent_refine_cancelled',
19
20
  'smash.completed',
20
21
  'smash.failed',
21
22
  'smash.undone',
@@ -24,31 +25,51 @@ const TERMINAL_PATTERNS = [
24
25
  'explore.contract_refine_failed',
25
26
  'file.summary_updated',
26
27
  'file.summary_failed',
28
+ 'file.summary_skipped',
27
29
  'setup_install_done',
28
30
  'setup_error',
29
31
  'plugin.installed',
30
32
  'plugin.degraded',
31
- ];
33
+ ]);
32
34
  // Job statuses that mean a polled job has settled (see project-router-jobs.ts).
33
- const TERMINAL_JOB_STATUSES = new Set(['completed', 'failed', 'canceled']);
35
+ const TERMINAL_JOB_STATUSES = new Set(['completed', 'failed', 'canceled', 'skipped', 'zombie_terminated']);
34
36
  // Jobs are identified by UUIDs (server/ids.ts newId → randomUUID). When the ref
35
37
  // looks like one, the wait loop also polls the job read as a fallback for jobs
36
38
  // that never emit a watch-terminal event (e.g. rail-bypass `spawn` jobs).
37
39
  const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
38
40
  const JOB_POLL_INTERVAL_MS = 5000;
39
- const TIMEOUT_SUGGESTION = 'timeout ≠ failure — the operation may still be running. Re-watch with the same ref, or poll specrails_jobs(get)/specrails_specs(get) for current state.';
41
+ const TIMEOUT_SUGGESTION = 'timeout ≠ failure — the operation may still be running. Re-watch with the same ref and kind, or poll specrails_jobs(get)/specrails_loops(run_get)/specrails_specs(get) for current state.';
40
42
  function isTerminal(type) {
41
- return TERMINAL_PATTERNS.some((p) => type === p || type.endsWith(p));
43
+ return TERMINAL_PATTERNS.has(type);
42
44
  }
43
45
  function mentionsRef(msg, ref) {
44
- // Heuristic: the reference (jobId / requestId / loopRunId) appears somewhere
45
- // in the message payload. Cheap and robust across the heterogeneous WS union.
46
- try {
47
- return JSON.stringify(msg).includes(ref);
48
- }
49
- catch {
50
- return false;
51
- }
46
+ // Identity must match exactly: arbitrary log/output text can mention another
47
+ // operation, and "job-1" must never settle when "job-10" finishes.
48
+ const record = msg;
49
+ if (['jobId', 'processId', 'requestId', 'conversationId', 'loopRunId', 'refineId', 'runId', 'sessionId']
50
+ .some((key) => record[key] === ref))
51
+ return true;
52
+ if (msg.type.startsWith('plugin.'))
53
+ return record.name === ref;
54
+ if (msg.type.startsWith('file.'))
55
+ return record.path === ref;
56
+ if (msg.type.startsWith('setup_'))
57
+ return record.projectId === ref;
58
+ if (msg.type === 'smash.undone')
59
+ return String(record.ticketId) === ref;
60
+ return false;
61
+ }
62
+ const MAX_EVENT_BYTES = 16_000;
63
+ const MAX_BUFFER_BYTES = 256_000;
64
+ function boundedEvent(msg) {
65
+ const json = JSON.stringify(msg);
66
+ if (json.length <= MAX_EVENT_BYTES)
67
+ return { event: msg, bytes: json.length };
68
+ const record = msg;
69
+ const identity = Object.fromEntries(Object.entries(record).filter(([key, value]) => ['type', 'projectId', 'jobId', 'processId', 'requestId', 'conversationId', 'loopRunId', 'refineId', 'runId', 'sessionId', 'status', 'name', 'path'].includes(key)
70
+ && typeof value === 'string').map(([key, value]) => [key, value.slice(0, 1024)]));
71
+ const event = { ...identity, truncated: true, originalLength: json.length, preview: json.slice(0, 6000) };
72
+ return { event, bytes: JSON.stringify(event).length };
52
73
  }
53
74
  function watchTool() {
54
75
  return {
@@ -58,95 +79,167 @@ function watchTool() {
58
79
  'and return its events. Pass the reference (jobId / requestId / conversationId / loopRunId) returned by the action; projectId is optional ' +
59
80
  '(defaults to the active project; app-level operations need none). ' +
60
81
  'Resolves when a terminal event for that reference is seen, or after untilMs. ' +
61
- 'When the ref is a jobId (or kind:"job" is passed), the job status is also polled every 5s so jobs settle even without a watch-terminal event. ' +
82
+ 'Use kind:"job" or kind:"loop_run" for durable recovery: reads current state immediately, then every 5s, including operations that finished before this call. UUID refs auto-detect jobs/loop runs. ' +
62
83
  'Default untilMs 120000 is often shorter than a rail run (max 600000). ' +
63
- 'settled:false means TIMEOUT, not failure — re-watch or poll the domain read (specrails_jobs(get) / specrails_specs(get) / specrails_plugins(health)).',
84
+ 'settled means terminal, NOT necessarily successful; inspect terminalEvent.status/outcome. Events are bounded/truncated to protect context; other operation kinds only observe future events. ' +
85
+ 'settled:false with reason:"timeout" means TIMEOUT, not failure — re-watch or poll the domain read. Cancellation stops waiting without stopping the operation. ' +
86
+ 'A new message in the owning mission ends only this wait immediately with reason:"user_update" so the agent can incorporate it; the underlying job/rail keeps running.',
64
87
  tier: 'read',
65
88
  inputSchema: {
66
89
  projectId: zod_1.z
67
90
  .string()
68
91
  .optional()
69
92
  .describe('Project id the operation runs in (defaults to the active project; omit for app-level operations)'),
70
- ref: zod_1.z.string().describe('jobId / requestId / conversationId / loopRunId returned by the async action'),
93
+ ref: zod_1.z.string().min(1).describe('Exact jobId / requestId / conversationId / loopRunId returned by the async action'),
71
94
  untilMs: zod_1.z.number().int().min(1000).max(600000).default(120000).describe('Max time to wait (ms)'),
72
95
  kind: zod_1.z
73
- .enum(['job'])
96
+ .enum(['job', 'loop_run'])
74
97
  .optional()
75
- .describe('Force the ref to be treated as a jobId (enables the 5s job-status poll fallback even for non-UUID ids)'),
98
+ .describe('Durable result type: job or loop_run. Reads immediately and every 5s, even for non-UUID ids. Omit to auto-detect UUID jobs/loops or listen for other event kinds.'),
76
99
  },
77
100
  handler: (ctx, args) => {
78
101
  // Optional + active-project resolution: an explicit projectId wins, else
79
- // the active project. When neither exists, project filtering is skipped —
80
- // the ref-substring match is discriminating enough, and app-level agent
81
- // operations carry no projectId at all.
102
+ // the active project. App-level operations can have neither; exact
103
+ // operation identity still filters their events.
82
104
  const projectId = args.projectId ?? (0, types_1.getActiveProject)(ctx) ?? undefined;
83
105
  const ref = args.ref;
84
106
  const untilMs = args.untilMs ?? 120000;
85
- const pollAsJob = args.kind === 'job' || UUID_RE.test(ref);
107
+ const kind = args.kind;
108
+ if (kind && !projectId)
109
+ throw new Error('Watching a job or loop_run requires projectId or an active project. Select the project or pass projectId.');
110
+ const shouldPoll = kind !== undefined || UUID_RE.test(ref);
86
111
  return new Promise((resolve) => {
112
+ const readController = new AbortController();
113
+ const readCtx = { ...ctx, signal: ctx.signal
114
+ ? AbortSignal.any([ctx.signal, readController.signal])
115
+ : readController.signal };
87
116
  const events = [];
117
+ let eventCount = 0;
118
+ let bufferBytes = 0;
88
119
  let done = false;
120
+ let poller;
121
+ let timer;
122
+ let unsub = () => { };
123
+ let unsubMission = () => { };
124
+ const addEvent = (msg) => {
125
+ if (done)
126
+ return;
127
+ const entry = boundedEvent(msg);
128
+ events.push(entry);
129
+ eventCount++;
130
+ bufferBytes += entry.bytes;
131
+ while (events.length > 50 || bufferBytes > MAX_BUFFER_BYTES) {
132
+ bufferBytes -= events.shift().bytes;
133
+ }
134
+ };
135
+ const aborted = () => finish(false, 'canceled');
89
136
  const finish = (settled, reason) => {
90
137
  if (done)
91
138
  return;
92
139
  done = true;
140
+ readController.abort();
93
141
  clearTimeout(timer);
94
142
  if (poller)
95
143
  clearInterval(poller);
96
144
  unsub();
145
+ unsubMission();
146
+ ctx.signal?.removeEventListener('abort', aborted);
97
147
  resolve({
98
148
  settled,
99
149
  reason,
100
150
  ref,
101
151
  projectId: projectId ?? null,
102
- eventCount: events.length,
103
- terminalEvent: settled ? events[events.length - 1] : null,
104
- events: events.slice(-50),
105
- ...(settled ? {} : { suggestion: TIMEOUT_SUGGESTION }),
152
+ eventCount,
153
+ terminalEvent: settled ? events[events.length - 1]?.event ?? null : null,
154
+ events: events.map((entry) => entry.event),
155
+ ...(reason === 'user_update' ? { operationStopped: false } : {}),
156
+ ...(settled ? {} : { suggestion: reason === 'user_update'
157
+ ? 'Waiting ended because the user updated this mission; the operation was NOT stopped and may still be running. Incorporate the user instructions received through the active mission input channel, then decide the next action from current state.'
158
+ : reason === 'canceled'
159
+ ? 'Waiting was canceled; the operation was not stopped. Read its current state before deciding the next action.'
160
+ : TIMEOUT_SUGGESTION }),
106
161
  });
107
162
  };
108
- const unsub = ctx.eventBus.onMessage((msg) => {
163
+ if (ctx.signal?.aborted) {
164
+ aborted();
165
+ return;
166
+ }
167
+ ctx.signal?.addEventListener('abort', aborted, { once: true });
168
+ unsub = ctx.eventBus.onMessage((msg) => {
169
+ if (done)
170
+ return;
109
171
  const msgProjectId = msg.projectId;
110
172
  if (projectId && msgProjectId && msgProjectId !== projectId)
111
173
  return;
112
174
  if (!mentionsRef(msg, ref))
113
175
  return;
114
- events.push(msg);
176
+ addEvent(msg);
115
177
  const type = msg.type ?? '';
178
+ // A loop's final job accounting may precede its durable loop outcome.
179
+ // Explicit kinds must wait for their own lifecycle, even if ids match.
180
+ if (kind === 'loop_run' && !type.startsWith('loop.run_'))
181
+ return;
182
+ if (kind === 'job' && type !== 'job.finalized' && !type.startsWith('rail.job_'))
183
+ return;
116
184
  if (isTerminal(type))
117
185
  finish(true, `terminal:${type}`);
118
186
  });
119
- // Job-status poll fallback: some jobs (rail-bypass spawn) never emit a
120
- // watch-terminal event; poll the job read every 5s and settle when its
121
- // status is terminal. Polling needs a resolvable project; errors (e.g.
122
- // the ref is not a job in this project) are ignored and the wait
123
- // continues on the event path.
124
- let poller;
125
- if (pollAsJob && projectId) {
187
+ if (ctx.onMissionInput) {
188
+ unsubMission = ctx.onMissionInput(() => finish(false, 'user_update'));
189
+ // Registration can synchronously report an already-pending input.
190
+ // finish then ran before its disposer was assigned; release it now.
191
+ if (done) {
192
+ unsubMission();
193
+ return;
194
+ }
195
+ }
196
+ // Subscribe BEFORE reading durable state, closing the fast-completion
197
+ // race. Never overlap reads or let a late response resurrect this wait.
198
+ if (shouldPoll && projectId) {
126
199
  let polling = false;
127
- poller = setInterval(() => {
200
+ const poll = async () => {
128
201
  if (done || polling)
129
202
  return;
130
203
  polling = true;
131
- (0, types_1.apiCall)(ctx, 'GET', `/projects/${encodeURIComponent(projectId)}/jobs/${encodeURIComponent(ref)}`)
132
- .then((r) => {
133
- const status = r?.job?.status;
134
- if (typeof status === 'string' && TERMINAL_JOB_STATUSES.has(status)) {
135
- events.push({ type: 'job.poll_settled', projectId, jobId: ref, status });
136
- finish(true, `poll:job:${status}`);
204
+ try {
205
+ const base = `/projects/${encodeURIComponent(projectId)}`;
206
+ if (kind !== 'loop_run') {
207
+ const result = await (0, types_1.apiCall)(readCtx, 'GET', `${base}/jobs/${encodeURIComponent(ref)}`).catch(() => null);
208
+ if (done)
209
+ return;
210
+ const job = result?.job;
211
+ if (job) {
212
+ if (typeof job.status === 'string' && TERMINAL_JOB_STATUSES.has(job.status)) {
213
+ addEvent({ type: 'job.poll_settled', projectId, jobId: ref, status: job.status });
214
+ finish(true, `poll:job:${job.status}`);
215
+ }
216
+ return;
217
+ }
218
+ }
219
+ if (kind === 'job')
220
+ return;
221
+ const result = await (0, types_1.apiCall)(readCtx, 'GET', `${base}/loop-runs/${encodeURIComponent(ref)}`);
222
+ if (done)
223
+ return;
224
+ const run = result?.loopRun;
225
+ if (run?.status === 'completed') {
226
+ addEvent({ type: 'loop.poll_settled', projectId, loopRunId: ref, status: run.status, outcome: run.final_outcome ?? null });
227
+ finish(true, `poll:loop_run:${run.final_outcome ?? run.status}`);
137
228
  }
138
- })
139
- .catch(() => {
140
- /* not a job / transient error — keep waiting on events */
141
- })
142
- .finally(() => {
229
+ }
230
+ catch {
231
+ // Unknown ref / transient read failure: stay subscribed, retry.
232
+ }
233
+ finally {
143
234
  polling = false;
144
- });
145
- }, JOB_POLL_INTERVAL_MS);
235
+ }
236
+ };
237
+ poller = setInterval(() => { void poll(); }, JOB_POLL_INTERVAL_MS);
146
238
  if (typeof poller.unref === 'function')
147
239
  poller.unref();
240
+ void poll();
148
241
  }
149
- const timer = setTimeout(() => finish(false, 'timeout'), untilMs);
242
+ timer = setTimeout(() => finish(false, 'timeout'), untilMs);
150
243
  // Do not keep the event loop alive solely for this watch.
151
244
  if (typeof timer.unref === 'function')
152
245
  timer.unref();
@@ -0,0 +1,164 @@
1
+ "use strict";
2
+ // Durable milestone launch chains (premium-milestone-progress, migration 58).
3
+ //
4
+ // One row per "Launch Milestone" in sequential mode: the chunk plan, the
5
+ // chunk currently in flight (rail / runs / delivery), the branch the next
6
+ // chunk stacks on, and a compare-and-set status so a settle is never advanced
7
+ // twice. Replaces the browser-local localStorage plan that died with the
8
+ // window and never stacked chunks.
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.ACTIVE_CHAIN_STATUSES = void 0;
11
+ exports.isActiveChainStatus = isActiveChainStatus;
12
+ exports.createChain = createChain;
13
+ exports.getChain = getChain;
14
+ exports.listActiveChains = listActiveChains;
15
+ exports.listChains = listChains;
16
+ exports.parseChunks = parseChunks;
17
+ exports.parseLaunched = parseLaunched;
18
+ exports.parseRunIds = parseRunIds;
19
+ exports.listChainsTouchingDelivery = listChainsTouchingDelivery;
20
+ exports.updateChain = updateChain;
21
+ exports.toChainSnapshot = toChainSnapshot;
22
+ exports.pauseChainsForDiscardedHead = pauseChainsForDiscardedHead;
23
+ exports.ACTIVE_CHAIN_STATUSES = new Set(['running', 'waiting', 'paused', 'awaiting_approval']);
24
+ function isActiveChainStatus(status) {
25
+ return exports.ACTIVE_CHAIN_STATUSES.has(status);
26
+ }
27
+ function iso(nowMs) {
28
+ return new Date(nowMs ?? Date.now()).toISOString();
29
+ }
30
+ function createChain(db, input) {
31
+ const ts = iso(input.nowMs);
32
+ db.prepare(`
33
+ INSERT INTO milestone_launch_chains
34
+ (id, milestone_n, milestone_id, mode, chunks, next_chunk, current_run_ids, integration_branch, status, launched, auto_advance, created_at, updated_at)
35
+ VALUES (?, ?, ?, ?, ?, 0, '[]', ?, ?, '[]', ?, ?, ?)
36
+ `).run(input.id, input.milestoneN, input.milestoneId, input.mode, JSON.stringify(input.chunks), input.integrationBranch, input.status ?? 'running', input.autoAdvance === false ? 0 : 1, ts, ts);
37
+ return getChain(db, input.id);
38
+ }
39
+ function getChain(db, id) {
40
+ return db.prepare('SELECT * FROM milestone_launch_chains WHERE id = ?').get(id);
41
+ }
42
+ /** Non-terminal chains (running / waiting / paused / awaiting_approval), oldest first. */
43
+ function listActiveChains(db) {
44
+ return db.prepare(`
45
+ SELECT * FROM milestone_launch_chains
46
+ WHERE status IN ('running','waiting','paused','awaiting_approval')
47
+ ORDER BY created_at ASC, rowid ASC
48
+ `).all();
49
+ }
50
+ /** Every chain, newest first (progress projection picks the newest per milestone). */
51
+ function listChains(db) {
52
+ return db.prepare('SELECT * FROM milestone_launch_chains ORDER BY created_at DESC, rowid DESC').all();
53
+ }
54
+ function parseChunks(row) {
55
+ try {
56
+ const parsed = JSON.parse(row.chunks);
57
+ return Array.isArray(parsed) ? parsed.filter((c) => Array.isArray(c)) : [];
58
+ }
59
+ catch {
60
+ return [];
61
+ }
62
+ }
63
+ function parseLaunched(row) {
64
+ try {
65
+ const parsed = JSON.parse(row.launched);
66
+ return Array.isArray(parsed) ? parsed : [];
67
+ }
68
+ catch {
69
+ return [];
70
+ }
71
+ }
72
+ function parseRunIds(row) {
73
+ try {
74
+ const parsed = JSON.parse(row.current_run_ids);
75
+ return Array.isArray(parsed) ? parsed.filter((v) => typeof v === 'string') : [];
76
+ }
77
+ catch {
78
+ return [];
79
+ }
80
+ }
81
+ /** Chains whose launched chunks include this delivery (any status). */
82
+ function listChainsTouchingDelivery(db, deliveryId) {
83
+ return listChains(db).filter((row) => parseLaunched(row).some((l) => l.deliveryId === deliveryId));
84
+ }
85
+ const PATCH_COLUMNS = {
86
+ nextChunk: 'next_chunk',
87
+ currentRailIndex: 'current_rail_index',
88
+ currentRunIds: 'current_run_ids',
89
+ currentDeliveryId: 'current_delivery_id',
90
+ headBranch: 'head_branch',
91
+ status: 'status',
92
+ pauseReason: 'pause_reason',
93
+ launched: 'launched',
94
+ lastRunOutcome: 'last_run_outcome',
95
+ autoAdvance: 'auto_advance',
96
+ retryChunk: 'retry_chunk',
97
+ };
98
+ function patchValue(key, patch) {
99
+ const raw = patch[key];
100
+ if (key === 'currentRunIds' || key === 'launched')
101
+ return JSON.stringify(raw ?? []);
102
+ if (key === 'autoAdvance')
103
+ return raw ? 1 : 0;
104
+ return raw ?? null;
105
+ }
106
+ /**
107
+ * Compare-and-set update: applies only while the row's status is one of
108
+ * `expected` (a string or a list). Returns true when a row changed.
109
+ */
110
+ function updateChain(db, id, expected, patch, nowMs) {
111
+ const keys = Object.keys(patch).filter((k) => patch[k] !== undefined);
112
+ const setClause = keys.map((k) => `${PATCH_COLUMNS[k]} = ?`).concat('updated_at = ?').join(', ');
113
+ const values = keys.map((k) => patchValue(k, patch));
114
+ const expectedList = Array.isArray(expected) ? expected : [expected];
115
+ const placeholders = expectedList.map(() => '?').join(', ');
116
+ return db.prepare(`UPDATE milestone_launch_chains SET ${setClause} WHERE id = ? AND status IN (${placeholders})`)
117
+ .run(...values, iso(nowMs), id, ...expectedList).changes > 0;
118
+ }
119
+ function toChainSnapshot(row) {
120
+ return {
121
+ id: row.id,
122
+ milestoneN: row.milestone_n,
123
+ mode: row.mode,
124
+ status: row.status,
125
+ pauseReason: row.pause_reason,
126
+ autoAdvance: row.auto_advance !== 0,
127
+ nextChunk: row.next_chunk,
128
+ totalChunks: parseChunks(row).length,
129
+ currentRailIndex: row.current_rail_index,
130
+ headBranch: row.head_branch,
131
+ launched: parseLaunched(row),
132
+ updatedAt: row.updated_at,
133
+ };
134
+ }
135
+ /**
136
+ * A delivery that a chain's later chunks build on was discarded by the user:
137
+ * pause every active chain whose launched chunks include it, and rewind the
138
+ * head to the previous launched chunk's delivery (`resolveBranch`), else null
139
+ * (= the integration branch). Returns the rows it paused.
140
+ */
141
+ function pauseChainsForDiscardedHead(db, deliveryId, resolveBranch, nowMs) {
142
+ const paused = [];
143
+ for (const row of listActiveChains(db)) {
144
+ const launched = parseLaunched(row);
145
+ const idx = launched.findIndex((l) => l.deliveryId === deliveryId);
146
+ if (idx < 0)
147
+ continue;
148
+ let previous = null;
149
+ for (let i = idx - 1; i >= 0; i--) {
150
+ const id = launched[i].deliveryId;
151
+ if (!id)
152
+ continue;
153
+ previous = resolveBranch(id);
154
+ if (previous)
155
+ break;
156
+ }
157
+ if (updateChain(db, row.id, row.status, { status: 'paused', pauseReason: 'head_discarded', headBranch: previous }, nowMs)) {
158
+ const fresh = getChain(db, row.id);
159
+ if (fresh)
160
+ paused.push(fresh);
161
+ }
162
+ }
163
+ return paused;
164
+ }