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
@@ -0,0 +1,164 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createBackgroundProcessControl = createBackgroundProcessControl;
7
+ const child_process_1 = require("child_process");
8
+ const path_1 = __importDefault(require("path"));
9
+ const win_spawn_1 = require("./util/win-spawn");
10
+ function gone(error) { return error?.code === 'ESRCH'; }
11
+ let windowsSnapshotPending;
12
+ async function windowsSnapshot(fresh = false) {
13
+ // Multiple apps share the expensive OS query. After sending a signal wait
14
+ // out any earlier query and require a new snapshot to confirm termination.
15
+ if (fresh && windowsSnapshotPending)
16
+ await windowsSnapshotPending.catch(() => { });
17
+ if (windowsSnapshotPending)
18
+ return windowsSnapshotPending;
19
+ const env = (0, win_spawn_1.windowsSpawnEnv)();
20
+ const systemRoot = env.SystemRoot || env.windir || 'C:\\Windows';
21
+ const powershell = path_1.default.win32.join(systemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe');
22
+ const script = "$ErrorActionPreference='Stop'; @(Get-CimInstance Win32_Process | Where-Object { $_.ProcessId -gt 0 -and $null -ne $_.CreationDate } | Select-Object @{n='pid';e={[int]$_.ProcessId}},@{n='parentPid';e={[int]$_.ParentProcessId}},@{n='createdAt';e={$_.CreationDate.ToUniversalTime().ToString('o')}}) | ConvertTo-Json -Compress";
23
+ const pending = new Promise((resolve, reject) => {
24
+ (0, child_process_1.execFile)(powershell, ['-NoLogo', '-NoProfile', '-NonInteractive', '-Command', script], {
25
+ env, windowsHide: true, timeout: 2500, maxBuffer: 4 * 1024 * 1024, encoding: 'utf8',
26
+ }, (error, stdout) => {
27
+ if (error) {
28
+ reject(error);
29
+ return;
30
+ }
31
+ try {
32
+ const decoded = JSON.parse(stdout || '[]');
33
+ const rows = Array.isArray(decoded) ? decoded : [decoded];
34
+ if (rows.some(row => !row || !Number.isInteger(row.pid) || row.pid <= 0 || !Number.isInteger(row.parentPid) || row.parentPid < 0 || typeof row.createdAt !== 'string' || !Number.isFinite(Date.parse(row.createdAt)))) {
35
+ throw new Error('Invalid Windows process identity snapshot.');
36
+ }
37
+ resolve(rows);
38
+ }
39
+ catch (error) {
40
+ reject(error);
41
+ }
42
+ });
43
+ });
44
+ windowsSnapshotPending = pending;
45
+ try {
46
+ return await pending;
47
+ }
48
+ finally {
49
+ if (windowsSnapshotPending === pending)
50
+ windowsSnapshotPending = undefined;
51
+ }
52
+ }
53
+ /** POSIX children are launched in a dedicated session/group. Query and signal
54
+ * that group, not a rediscovered tree whose parent may already have exited.
55
+ * Windows uses creation-time identities plus taskkill /T /F; deliberately
56
+ * detached/breakaway processes require OS job containment and are not claimed
57
+ * to be contained here. A failed identity query must never report stopped. */
58
+ function createBackgroundProcessControl(child, startedAt) {
59
+ const pid = child.pid;
60
+ if (process.platform !== 'win32') {
61
+ return {
62
+ async isAlive() {
63
+ try {
64
+ process.kill(-pid, 0);
65
+ return true;
66
+ }
67
+ catch (error) {
68
+ if (gone(error))
69
+ return false;
70
+ throw error;
71
+ }
72
+ },
73
+ async terminate(signal) {
74
+ try {
75
+ process.kill(-pid, signal);
76
+ }
77
+ catch (error) {
78
+ if (!gone(error))
79
+ throw error;
80
+ }
81
+ },
82
+ };
83
+ }
84
+ const owned = new Map();
85
+ let rootExited = false;
86
+ let identified = false;
87
+ let snapshotPending;
88
+ child.once('exit', () => { rootExited = true; });
89
+ child.once('close', () => { rootExited = true; });
90
+ const inspect = async (fresh = false) => {
91
+ if (fresh && snapshotPending)
92
+ await snapshotPending.catch(() => { });
93
+ if (snapshotPending)
94
+ return snapshotPending;
95
+ // Snapshot while the child handle still refers to our live root. A later
96
+ // process with the same pid must never become a new ownership anchor.
97
+ const canIdentifyRoot = !rootExited && !identified;
98
+ const pending = windowsSnapshot(fresh).then(rows => {
99
+ const current = new Map(rows.map(row => [row.pid, row]));
100
+ const root = current.get(pid);
101
+ if (canIdentifyRoot && !rootExited && root && Date.parse(root.createdAt) >= startedAt - 2000) {
102
+ owned.set(pid, root);
103
+ identified = true;
104
+ }
105
+ if (!identified)
106
+ throw new Error('Could not verify the Windows background process identity; stop cannot be confirmed.');
107
+ const live = new Map();
108
+ for (const [ownedPid, identity] of owned) {
109
+ const match = current.get(ownedPid);
110
+ if (match?.createdAt === identity.createdAt)
111
+ live.set(ownedPid, match);
112
+ }
113
+ let changed = true;
114
+ while (changed) {
115
+ changed = false;
116
+ for (const row of rows) {
117
+ const parent = live.get(row.parentPid);
118
+ if (!live.has(row.pid) && parent && Date.parse(row.createdAt) >= Date.parse(parent.createdAt)) {
119
+ live.set(row.pid, row);
120
+ owned.set(row.pid, row);
121
+ changed = true;
122
+ }
123
+ }
124
+ }
125
+ return [...live.values()];
126
+ });
127
+ snapshotPending = pending;
128
+ try {
129
+ return await pending;
130
+ }
131
+ finally {
132
+ if (snapshotPending === pending)
133
+ snapshotPending = undefined;
134
+ }
135
+ };
136
+ // Begin capturing descendants before a stop request or a fast parent exit.
137
+ // A shared query may have begun before this particular bootstrap spawned.
138
+ // Retry once with a fresh bounded query while its child handle is still live.
139
+ const ready = inspect().catch(error => { if (rootExited)
140
+ throw error; return inspect(true); }).then(() => undefined);
141
+ void ready.catch(() => { });
142
+ return {
143
+ ready,
144
+ async isAlive() { return (await inspect()).length > 0; },
145
+ async terminate(signal) {
146
+ const live = await inspect();
147
+ const ids = new Set(live.map(row => row.pid));
148
+ const roots = live.filter(row => !ids.has(row.parentPid));
149
+ const errors = [];
150
+ await Promise.all(roots.map(row => new Promise(resolve => {
151
+ try {
152
+ (0, win_spawn_1.treeKillSafe)(row.pid, signal, error => { if (error)
153
+ errors.push(error); resolve(); });
154
+ }
155
+ catch (error) {
156
+ errors.push(error);
157
+ resolve();
158
+ }
159
+ })));
160
+ if ((await inspect(true)).length > 0 && errors.length)
161
+ throw errors[0];
162
+ },
163
+ };
164
+ }
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BackgroundProcessOwnershipLostError = void 0;
7
+ exports.resolveBackgroundCwd = resolveBackgroundCwd;
8
+ exports.backgroundProcessHooks = backgroundProcessHooks;
9
+ exports.backgroundStartBusyReason = backgroundStartBusyReason;
10
+ exports.stopOwnedBackgroundProcess = stopOwnedBackgroundProcess;
11
+ const node_path_1 = __importDefault(require("node:path"));
12
+ const loop_runs_store_1 = require("./loop-runs-store");
13
+ const project_repositories_1 = require("./project-repositories");
14
+ const transient_children_1 = require("./transient-children");
15
+ /** The same scope and lifecycle policy applies to REST and the mission MCP. */
16
+ function resolveBackgroundCwd(repositoryRoot, cwd) {
17
+ if (cwd !== undefined && typeof cwd !== 'string')
18
+ throw new project_repositories_1.RepositoryValidationError('cwd must be a string');
19
+ const resolved = node_path_1.default.resolve(repositoryRoot, typeof cwd === 'string' && cwd.trim() ? cwd : '.');
20
+ if (!(0, project_repositories_1.isRepositoryPathWithin)((0, project_repositories_1.canonicalRepositoryPath)(repositoryRoot), (0, project_repositories_1.canonicalRepositoryPath)(resolved))) {
21
+ throw new project_repositories_1.RepositoryValidationError('background_start cwd must stay within the selected repository.');
22
+ }
23
+ return resolved;
24
+ }
25
+ function backgroundProcessHooks(broadcast) {
26
+ const publish = (type) => (process) => broadcast({ type, process,
27
+ projectId: process.projectId, timestamp: new Date().toISOString() });
28
+ return { onStarted: publish('background_process.started'), onUpdated: publish('background_process.updated'), onExited: publish('background_process.exited') };
29
+ }
30
+ function backgroundStartBusyReason(c) {
31
+ const activeJobId = typeof c.queueManager?.getActiveJobId === 'function' ? c.queueManager.getActiveJobId() : null;
32
+ if (activeJobId)
33
+ return `job ${activeJobId} is still running`;
34
+ // Context-only fixtures have no database; a real database error must not
35
+ // silently bypass the concurrent-run guard.
36
+ if (!c.db)
37
+ return null;
38
+ const activeLoops = (0, loop_runs_store_1.listActiveLoopRuns)(c.db, c.project.id);
39
+ return activeLoops.length ? `loop run ${activeLoops[0].id} is still running` : null;
40
+ }
41
+ class BackgroundProcessOwnershipLostError extends Error {
42
+ status = 409;
43
+ constructor() {
44
+ super('This execution was recovered from history after Specrails restarted. Its current OS state is unknown, so its old PID cannot be used to stop it. Inspect its retained logs and the current application state first.');
45
+ this.name = 'BackgroundProcessOwnershipLostError';
46
+ }
47
+ }
48
+ exports.BackgroundProcessOwnershipLostError = BackgroundProcessOwnershipLostError;
49
+ function stopOwnedBackgroundProcess(pid, owner) {
50
+ const process = (0, transient_children_1.getBackgroundProcess)(pid, owner.processId);
51
+ if (!process || process.projectId !== owner.projectId || process.chatId !== owner.chatId ||
52
+ (owner.processId !== undefined && process.processId !== owner.processId))
53
+ return null;
54
+ if (process.status === 'interrupted')
55
+ throw new BackgroundProcessOwnershipLostError();
56
+ if (['exited', 'killed', 'failed'].includes(process.status))
57
+ return process;
58
+ if (!(0, transient_children_1.killOwnedBackgroundProcess)(pid, owner))
59
+ return null;
60
+ return (0, transient_children_1.getBackgroundProcess)(pid, owner.processId);
61
+ }
@@ -0,0 +1,242 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BackgroundProcessStore = exports.BACKGROUND_HISTORY_MAX_LINE_CHARS = exports.BACKGROUND_HISTORY_MAX_LINES = exports.BACKGROUND_HISTORY_RETENTION_MS = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
10
+ const crypto_1 = require("crypto");
11
+ const secure_fs_1 = require("./util/secure-fs");
12
+ exports.BACKGROUND_HISTORY_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
13
+ exports.BACKGROUND_HISTORY_MAX_LINES = 10_000;
14
+ exports.BACKGROUND_HISTORY_MAX_LINE_CHARS = 4000;
15
+ const defaultPolicy = { retentionMs: exports.BACKGROUND_HISTORY_RETENTION_MS, maxFinishedRuns: 1000, maxLines: exports.BACKGROUND_HISTORY_MAX_LINES, maxTextBytes: 256 * 1024 * 1024 };
16
+ const activeStatuses = "('starting','running','stopping')";
17
+ /** Log volume stays in its own SQLite file so batched writes cannot hold the
18
+ * mission/conversation database lock. Only execution UUIDs identify history;
19
+ * opening this store never probes, adopts or signals an old operating-system PID. */
20
+ class BackgroundProcessStore {
21
+ file;
22
+ db;
23
+ policy;
24
+ lastPrune = 0;
25
+ ownerToken = (0, crypto_1.randomUUID)();
26
+ constructor(file, policy = {}) {
27
+ this.file = file;
28
+ this.policy = { ...defaultPolicy, ...policy };
29
+ for (const value of Object.values(this.policy))
30
+ if (!Number.isSafeInteger(value) || value <= 0)
31
+ throw new Error('Background history limits must be positive integers.');
32
+ if (file !== ':memory:') {
33
+ fs_1.default.mkdirSync(path_1.default.dirname(file), { recursive: true });
34
+ (0, secure_fs_1.secureDir)(path_1.default.dirname(file));
35
+ }
36
+ this.db = new better_sqlite3_1.default(file);
37
+ try {
38
+ this.db.pragma('journal_mode = WAL');
39
+ this.db.pragma('foreign_keys = ON');
40
+ this.db.pragma('busy_timeout = 1000');
41
+ this.db.pragma('journal_size_limit = 10000000');
42
+ const version = this.db.pragma('user_version', { simple: true });
43
+ if (version > 1)
44
+ throw new Error('Background history was written by a newer Specrails version.');
45
+ if (version < 1)
46
+ this.db.transaction(() => {
47
+ this.db.exec(`
48
+ CREATE TABLE background_process_runs (
49
+ process_id TEXT PRIMARY KEY,
50
+ pid INTEGER NOT NULL,
51
+ project_id TEXT NOT NULL,
52
+ chat_id TEXT NOT NULL,
53
+ started_at INTEGER NOT NULL,
54
+ updated_at INTEGER NOT NULL,
55
+ status TEXT NOT NULL CHECK(status IN ('starting','running','stopping','exited','killed','failed','interrupted')),
56
+ metadata TEXT NOT NULL,
57
+ next_sequence INTEGER NOT NULL DEFAULT 0,
58
+ clipped INTEGER NOT NULL DEFAULT 0,
59
+ line_count INTEGER NOT NULL DEFAULT 0,
60
+ text_bytes INTEGER NOT NULL DEFAULT 0
61
+ );
62
+ CREATE INDEX background_process_owner ON background_process_runs(project_id, chat_id, started_at DESC);
63
+ CREATE INDEX background_process_retention ON background_process_runs(status, updated_at);
64
+ CREATE TABLE background_process_lines (
65
+ process_id TEXT NOT NULL REFERENCES background_process_runs(process_id) ON DELETE CASCADE,
66
+ sequence INTEGER NOT NULL,
67
+ at INTEGER NOT NULL,
68
+ source TEXT NOT NULL CHECK(source IN ('stdout','stderr')),
69
+ line TEXT NOT NULL,
70
+ partial INTEGER NOT NULL,
71
+ text_bytes INTEGER NOT NULL,
72
+ PRIMARY KEY(process_id, sequence)
73
+ ) WITHOUT ROWID;
74
+ PRAGMA user_version = 1;
75
+ `);
76
+ })();
77
+ this.db.exec('CREATE TABLE IF NOT EXISTS background_process_session_owner (slot INTEGER PRIMARY KEY CHECK(slot=1), pid INTEGER NOT NULL, token TEXT NOT NULL)');
78
+ this.db.transaction(() => {
79
+ const owner = this.db.prepare('SELECT pid,token FROM background_process_session_owner WHERE slot=1').get();
80
+ if (owner) {
81
+ let dead = false;
82
+ if (Number.isSafeInteger(owner.pid) && owner.pid > 0) {
83
+ try {
84
+ process.kill(owner.pid, 0);
85
+ }
86
+ catch (error) {
87
+ dead = error.code === 'ESRCH';
88
+ }
89
+ }
90
+ if (!dead)
91
+ throw new Error('Background process history is already owned by another active Specrails server.');
92
+ }
93
+ this.db.prepare('INSERT OR REPLACE INTO background_process_session_owner(slot,pid,token) VALUES(1,?,?)').run(process.pid, this.ownerToken);
94
+ this.recover();
95
+ this.prune(true);
96
+ }).immediate();
97
+ (0, secure_fs_1.secureDbFile)(file);
98
+ }
99
+ catch (error) {
100
+ this.db.close();
101
+ throw error;
102
+ }
103
+ }
104
+ recover() {
105
+ const now = Date.now();
106
+ const rows = this.db.prepare(`SELECT process_id, metadata FROM background_process_runs WHERE status IN ${activeStatuses}`).all();
107
+ const update = this.db.prepare("UPDATE background_process_runs SET status='interrupted', metadata=?, updated_at=? WHERE process_id=?");
108
+ this.db.transaction(() => {
109
+ for (const row of rows) {
110
+ const process = JSON.parse(row.metadata);
111
+ process.status = 'interrupted';
112
+ process.recoveredAt = now;
113
+ process.error = 'Specrails restarted before process exit could be confirmed. This historical process is not attached to this session.';
114
+ update.run(JSON.stringify(process), now, row.process_id);
115
+ }
116
+ })();
117
+ }
118
+ write(input) {
119
+ const { process } = input;
120
+ const lines = input.lines.map(line => ({ ...line, partial: line.partial === true ? 1 : 0, text_bytes: Buffer.byteLength(line.line, 'utf8') }));
121
+ this.db.transaction(() => {
122
+ const result = this.db.prepare(`INSERT INTO background_process_runs(process_id,pid,project_id,chat_id,started_at,updated_at,status,metadata,next_sequence,clipped)
123
+ VALUES(?,?,?,?,?,?,?,?,?,?) ON CONFLICT(process_id) DO UPDATE SET updated_at=excluded.updated_at,status=excluded.status,metadata=excluded.metadata,
124
+ next_sequence=MAX(background_process_runs.next_sequence,excluded.next_sequence),clipped=MAX(background_process_runs.clipped,excluded.clipped)
125
+ WHERE background_process_runs.pid=excluded.pid AND background_process_runs.project_id=excluded.project_id AND background_process_runs.chat_id=excluded.chat_id`)
126
+ .run(process.processId, process.pid, process.projectId, process.chatId, process.startedAt, Date.now(), process.status, JSON.stringify(process), input.nextSequence, input.clipped ? 1 : 0);
127
+ if (!result.changes)
128
+ throw new Error('Background process history identity does not match its original owner.');
129
+ if (lines.length)
130
+ this.db.prepare(`INSERT INTO background_process_lines(process_id,sequence,at,source,line,partial,text_bytes)
131
+ SELECT ?,json_extract(value,'$.sequence'),json_extract(value,'$.at'),json_extract(value,'$.source'),json_extract(value,'$.line'),json_extract(value,'$.partial'),json_extract(value,'$.text_bytes') FROM json_each(?) WHERE 1
132
+ ON CONFLICT(process_id,sequence) DO UPDATE SET line=excluded.line,partial=excluded.partial,text_bytes=excluded.text_bytes`)
133
+ .run(process.processId, JSON.stringify(lines));
134
+ this.db.prepare(`DELETE FROM background_process_lines WHERE process_id=? AND sequence NOT IN
135
+ (SELECT sequence FROM background_process_lines WHERE process_id=? ORDER BY sequence DESC LIMIT ?)`)
136
+ .run(process.processId, process.processId, this.policy.maxLines);
137
+ this.recount(process.processId);
138
+ this.prune(false);
139
+ })();
140
+ }
141
+ recount(processId) {
142
+ this.db.prepare(`UPDATE background_process_runs SET line_count=(SELECT COUNT(*) FROM background_process_lines WHERE process_id=?),
143
+ text_bytes=(SELECT COALESCE(SUM(text_bytes),0) FROM background_process_lines WHERE process_id=?) WHERE process_id=?`).run(processId, processId, processId);
144
+ }
145
+ /** Retention prunes records/text, then SQLite reuses its freed pages. The
146
+ * finite text ceiling therefore also bounds repeated long-running sessions. */
147
+ prune(force = false) {
148
+ const now = Date.now();
149
+ if (!force && now - this.lastPrune < 1000) {
150
+ const totals = this.db.prepare(`SELECT COALESCE(SUM(text_bytes),0) AS bytes,SUM(CASE WHEN status NOT IN ${activeStatuses} THEN 1 ELSE 0 END) AS finished FROM background_process_runs`).get();
151
+ if (totals.bytes <= this.policy.maxTextBytes && totals.finished <= this.policy.maxFinishedRuns)
152
+ return;
153
+ }
154
+ this.db.transaction(() => {
155
+ this.db.prepare(`DELETE FROM background_process_runs WHERE status NOT IN ${activeStatuses} AND updated_at < ?`).run(now - this.policy.retentionMs);
156
+ this.db.prepare(`DELETE FROM background_process_runs WHERE process_id IN (SELECT process_id FROM background_process_runs WHERE status NOT IN ${activeStatuses}
157
+ ORDER BY updated_at DESC,started_at DESC,process_id DESC LIMIT -1 OFFSET ?)`).run(this.policy.maxFinishedRuns);
158
+ let total = this.db.prepare('SELECT COALESCE(SUM(text_bytes),0) AS bytes FROM background_process_runs').get().bytes;
159
+ if (total > this.policy.maxTextBytes) {
160
+ const rows = this.db.prepare(`SELECT process_id,text_bytes,status FROM background_process_runs ORDER BY CASE WHEN status IN ${activeStatuses} THEN 1 ELSE 0 END,updated_at,started_at`).all();
161
+ for (const row of rows) {
162
+ if (total <= this.policy.maxTextBytes)
163
+ break;
164
+ if (!['starting', 'running', 'stopping'].includes(row.status)) {
165
+ this.db.prepare('DELETE FROM background_process_runs WHERE process_id=?').run(row.process_id);
166
+ total -= row.text_bytes;
167
+ }
168
+ else {
169
+ this.db.prepare(`DELETE FROM background_process_lines WHERE process_id=? AND sequence IN (
170
+ SELECT sequence FROM (SELECT sequence,SUM(text_bytes) OVER (ORDER BY sequence)-text_bytes AS preceding_bytes FROM background_process_lines WHERE process_id=?) WHERE preceding_bytes < ?)`)
171
+ .run(row.process_id, row.process_id, total - this.policy.maxTextBytes);
172
+ this.recount(row.process_id);
173
+ const remaining = this.db.prepare('SELECT text_bytes FROM background_process_runs WHERE process_id=?').get(row.process_id).text_bytes;
174
+ total -= row.text_bytes - remaining;
175
+ }
176
+ }
177
+ }
178
+ })();
179
+ this.lastPrune = now;
180
+ }
181
+ get(pid, processId) {
182
+ this.prune();
183
+ const row = (processId
184
+ ? this.db.prepare('SELECT metadata FROM background_process_runs WHERE pid=? AND process_id=?').get(pid, processId)
185
+ : this.db.prepare('SELECT metadata FROM background_process_runs WHERE pid=? ORDER BY started_at DESC,rowid DESC LIMIT 1').get(pid));
186
+ return row ? JSON.parse(row.metadata) : null;
187
+ }
188
+ list(filter = {}) {
189
+ this.prune();
190
+ const clauses = [], params = [];
191
+ if (filter.projectId) {
192
+ clauses.push('project_id=?');
193
+ params.push(filter.projectId);
194
+ }
195
+ if (filter.chatId) {
196
+ clauses.push('chat_id=?');
197
+ params.push(filter.chatId);
198
+ }
199
+ if (!filter.includeFinished)
200
+ clauses.push(`status IN ${activeStatuses}`);
201
+ const rows = this.db.prepare(`SELECT metadata FROM background_process_runs ${clauses.length ? `WHERE ${clauses.join(' AND ')}` : ''} ORDER BY started_at,rowid`).all(...params);
202
+ return rows.map(row => JSON.parse(row.metadata));
203
+ }
204
+ logs(pid, filter = {}) {
205
+ const process = this.get(pid, filter.processId);
206
+ if (!process || (filter.projectId && process.projectId !== filter.projectId) || (filter.chatId && process.chatId !== filter.chatId))
207
+ return null;
208
+ const row = this.db.prepare('SELECT next_sequence,clipped,line_count FROM background_process_runs WHERE process_id=?').get(process.processId);
209
+ const limit = typeof filter.limit === 'number' && Number.isFinite(filter.limit) ? Math.max(1, Math.min(this.policy.maxLines, Math.floor(filter.limit))) : 2000;
210
+ const lines = this.db.prepare('SELECT sequence,at,source,line,partial FROM background_process_lines WHERE process_id=? ORDER BY sequence DESC LIMIT ?').all(process.processId, limit)
211
+ .reverse().map(line => ({ ...line, partial: line.partial === 1 }));
212
+ const droppedLines = Math.max(0, row.next_sequence - lines.length);
213
+ return { process, lines, nextSequence: row.next_sequence, truncated: droppedLines > 0 || !!row.clipped, droppedLines,
214
+ maxLines: this.policy.maxLines, maxLineChars: exports.BACKGROUND_HISTORY_MAX_LINE_CHARS, retentionMs: this.policy.retentionMs };
215
+ }
216
+ close() {
217
+ if (!this.db.open)
218
+ return;
219
+ try {
220
+ this.db.prepare('DELETE FROM background_process_session_owner WHERE slot=1 AND token=?').run(this.ownerToken);
221
+ this.db.pragma('wal_checkpoint(TRUNCATE)');
222
+ }
223
+ finally {
224
+ this.db.close();
225
+ }
226
+ }
227
+ purge(filter) {
228
+ if (!filter.projectId && !filter.chatId)
229
+ throw new Error('A conversation or project is required to purge background history.');
230
+ const clauses = [], params = [];
231
+ if (filter.projectId) {
232
+ clauses.push('project_id=?');
233
+ params.push(filter.projectId);
234
+ }
235
+ if (filter.chatId) {
236
+ clauses.push('chat_id=?');
237
+ params.push(filter.chatId);
238
+ }
239
+ this.db.prepare(`DELETE FROM background_process_runs WHERE ${clauses.join(' AND ')}`).run(...params);
240
+ }
241
+ }
242
+ exports.BackgroundProcessStore = BackgroundProcessStore;