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,20 +3,25 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.specsTools = specsTools;
4
4
  const zod_1 = require("zod");
5
5
  const types_1 = require("./types");
6
+ const agent_spec_framing_1 = require("../../agent-spec-framing");
6
7
  // Per-action danger classification for the specs (tickets) facade.
7
8
  const DESTRUCTIVE_ACTIONS = new Set(['delete', 'smash_undo', 'delete_epic_children']);
8
9
  // `create` routes through Quick Add Spec (generate-spec) so MCP-created specs
9
10
  // match the app's "Add Spec → Quick" flow — hence ai-spawn, not write.
10
11
  const AI_SPAWN_ACTIONS = new Set(['create', 'generate', 'ai_edit', 'contract_refine', 'smash']);
11
- // `commit_draft` (POST /tickets/from-draft) may fire-and-forget a Contract
12
- // Refine spawn as a side-effect (Explore-origin flips per the conversation's
13
- // scope; agent-authored inserts by the facade's default-ON `contractRefine` —
14
- // opt out per call with `contractRefine: false`), but the call itself is a
15
- // synchronous store mutation (a write), so it is classified as `write`.
12
+ // A post-commit background AI pass still incurs cost. Classify commit_draft
13
+ // using its actual side effects, not the synchronous HTTP response alone.
16
14
  const WRITE_ACTIONS = new Set([
17
15
  'update', 'from_prompt', 'save_draft', 'commit_draft', 'cancel_ai_edit',
18
16
  ]);
19
- function resolveSpecsTier(action) {
17
+ function resolveSpecsTier(action, args) {
18
+ if (action === 'commit_draft') {
19
+ // Explore's stored scope governs refinement; its route does not accept an
20
+ // opt-out override here. A standalone draft flip does not launch AI.
21
+ if (args.conversationId)
22
+ return 'ai-spawn';
23
+ return args.draftTicketId != null || args.contractRefine === false ? 'write' : 'ai-spawn';
24
+ }
20
25
  if (DESTRUCTIVE_ACTIONS.has(action))
21
26
  return 'destructive';
22
27
  if (AI_SPAWN_ACTIONS.has(action))
@@ -45,7 +50,7 @@ function specsTools() {
45
50
  'from_prompt (verbatim insert, no AI — use ONLY when you already have a complete finished spec to store as-is; ' +
46
51
  'cannot set acceptanceCriteria or shortSummary — prefer commit_draft for structured specs), ' +
47
52
  'save_draft (persist an Explore conversation as a draft), ' +
48
- 'commit_draft (write — TWO uses: (a) flip an Explore draft to a real spec, passing conversationId from ' +
53
+ 'commit_draft (ai-spawn when refinement may run; write for standalone draft flips or new inserts with contractRefine:false — TWO uses: (a) flip an Explore draft to a real spec, passing conversationId from ' +
49
54
  'specrails_chat and/or draftTicketId; (b) with NO conversationId/draftTicketId, insert a COMPLETE spec you ' +
50
55
  'authored in ONE call: title (required), description, acceptanceCriteria, priority, labels, shortSummary, ' +
51
56
  'assignee, prerequisites, metadata. THE canonical way to persist a spec refined in conversation — do NOT route ' +
@@ -62,7 +67,7 @@ function specsTools() {
62
67
  'list_attachments, get_attachment. The generate/ai_edit/contract_refine/smash actions are async (HTTP 202) — ' +
63
68
  'results arrive over the WebSocket, not the HTTP response.',
64
69
  hintTier: 'read',
65
- tier: (args) => resolveSpecsTier(args.action),
70
+ tier: (args) => resolveSpecsTier(args.action, args),
66
71
  inputSchema: {
67
72
  action: zod_1.z
68
73
  .enum([
@@ -88,6 +93,8 @@ function specsTools() {
88
93
  ])
89
94
  .describe('Operation to perform'),
90
95
  projectId: zod_1.z.string().optional().describe('Project id (defaults to the active project)'),
96
+ repositoryIds: zod_1.z.array(zod_1.z.string().min(1)).min(1).max(50).optional().describe('Affected repositories for this shared spec. Nonempty unique membership IDs from this project. Omission preserves existing scope; historical specs default to primary only.'),
97
+ repositoryId: zod_1.z.string().min(1).optional().describe('files_touched only: repository to inspect; required for multi-repository projects.'),
91
98
  // ── Ticket identity ──────────────────────────────────────────────
92
99
  id: zod_1.z
93
100
  .number()
@@ -101,6 +108,8 @@ function specsTools() {
101
108
  .describe('list: CSV status filter (draft|todo|in_progress|on_review|done|cancelled). update: set ticket status — note job outcomes set in_progress/on_review/done automatically (on_review = implemented, awaiting PR review); do not fight the pipeline.'),
102
109
  label: zod_1.z.string().optional().describe('list: filter by label (CSV)'),
103
110
  q: zod_1.z.string().optional().describe('list: free-text search over title + description'),
111
+ limit: zod_1.z.number().int().positive().max(100).optional().describe('list: compact results per page (default 50, maximum 100)'),
112
+ offset: zod_1.z.number().int().nonnegative().optional().describe('list: zero-based offset for the next compact page'),
104
113
  // ── create / update / from_prompt / commit_draft fields ──────────
105
114
  title: zod_1.z.string().optional().describe('Ticket title (required for create / commit_draft)'),
106
115
  description: zod_1.z
@@ -113,7 +122,7 @@ function specsTools() {
113
122
  .optional()
114
123
  .describe('update/from_prompt/commit_draft: ticket priority. update: null allowed only for drafts'),
115
124
  labels: zod_1.z.array(zod_1.z.string()).optional().describe('update/from_prompt/save_draft/commit_draft: labels array'),
116
- assignee: zod_1.z.string().optional().describe('update/commit_draft: assignee'),
125
+ assignee: zod_1.z.string().nullable().optional().describe('update/commit_draft: assignee; null clears'),
117
126
  prerequisites: zod_1.z.array(zod_1.z.number().int()).optional().describe('update/commit_draft: prerequisite ticket ids'),
118
127
  metadata: zod_1.z.record(zod_1.z.unknown()).optional().describe('update/commit_draft: free-form metadata object (update merges keys)'),
119
128
  acceptanceCriteria: zod_1.z
@@ -121,7 +130,7 @@ function specsTools() {
121
130
  .optional()
122
131
  .describe('update/commit_draft: acceptance-criteria bullets folded into the description'),
123
132
  shortSummary: zod_1.z.string().optional().describe('commit_draft: explicit short summary (camelCase key)'),
124
- short_summary: zod_1.z.string().optional().describe('update: explicit short summary (snake_case key; null clears)'),
133
+ short_summary: zod_1.z.string().nullable().optional().describe('update: explicit short summary (snake_case key; null clears)'),
125
134
  structured: zod_1.z.boolean().optional().describe('from_prompt: lightly structure the verbatim prompt'),
126
135
  createLocal: zod_1.z.boolean().optional().describe('create/generate/commit_draft: keep the spec local (skip Jira promote)'),
127
136
  // ── draft linkage ────────────────────────────────────────────────
@@ -192,6 +201,7 @@ function specsTools() {
192
201
  attachmentIds: args.attachmentIds,
193
202
  pendingSpecId: args.pendingSpecId,
194
203
  createLocal: args.createLocal,
204
+ ...(args.repositoryIds === undefined ? {} : { repositoryIds: args.repositoryIds }),
195
205
  });
196
206
  switch (action) {
197
207
  // ── reads ──────────────────────────────────────────────────────
@@ -204,14 +214,40 @@ function specsTools() {
204
214
  if (typeof args.q === 'string')
205
215
  qs.set('q', args.q);
206
216
  const suffix = qs.toString() ? `?${qs.toString()}` : '';
207
- return (0, types_1.apiCall)(ctx, 'GET', `${base}/tickets${suffix}`);
217
+ const result = await (0, types_1.apiCall)(ctx, 'GET', `${base}/tickets${suffix}`);
218
+ const tickets = Array.isArray(result.tickets) ? result.tickets : [];
219
+ const offset = typeof args.offset === 'number' ? args.offset : 0;
220
+ const limit = typeof args.limit === 'number' ? args.limit : 50;
221
+ const page = tickets.slice(offset, offset + limit).map((ticket) => ({
222
+ id: ticket.id,
223
+ title: ticket.title,
224
+ status: ticket.status,
225
+ priority: ticket.priority,
226
+ labels: ticket.labels,
227
+ ...(ticket.repositoryIds === undefined ? {} : { repositoryIds: ticket.repositoryIds }),
228
+ shortSummary: ticket.short_summary ?? ticket.shortSummary ?? null,
229
+ updatedAt: ticket.updated_at ?? ticket.updatedAt,
230
+ }));
231
+ const nextOffset = offset + page.length < tickets.length ? offset + page.length : null;
232
+ return {
233
+ tickets: page,
234
+ revision: result.revision,
235
+ total: result.total ?? tickets.length,
236
+ matched: tickets.length,
237
+ offset,
238
+ limit,
239
+ nextOffset,
240
+ hint: nextOffset == null
241
+ ? 'List complete. Use get(id) for the full description and acceptance criteria.'
242
+ : `More specs are available. Call list with offset=${nextOffset}; use get(id) for full details.`,
243
+ };
208
244
  }
209
245
  case 'get':
210
246
  return (0, types_1.apiCall)(ctx, 'GET', `${base}/tickets/${requireId()}`);
211
247
  case 'spending_summary':
212
248
  return (0, types_1.apiCall)(ctx, 'GET', `${base}/tickets/${requireId()}/spending-summary`);
213
249
  case 'files_touched':
214
- return (0, types_1.apiCall)(ctx, 'GET', `${base}/code/provenance?ticketId=${requireId()}`);
250
+ return (0, types_1.apiCall)(ctx, 'GET', `${(0, types_1.repositoryPath)(ctx, args.projectId, args.repositoryId)}/code/provenance?ticketId=${requireId()}`);
215
251
  case 'list_attachments': {
216
252
  const ticketId = args.ticketId;
217
253
  if (!ticketId)
@@ -225,7 +261,17 @@ function specsTools() {
225
261
  throw new Error('get_attachment requires a "ticketId".');
226
262
  if (!attachmentId)
227
263
  throw new Error('get_attachment requires an "attachmentId".');
228
- return (0, types_1.apiCall)(ctx, 'GET', `${base}/tickets/${encodeURIComponent(ticketId)}/attachments/${encodeURIComponent(attachmentId)}`);
264
+ // The download endpoint streams binary files; decoding those as
265
+ // text corrupts their content and can exhaust the model context.
266
+ const list = await (0, types_1.apiCall)(ctx, 'GET', `${base}/tickets/${encodeURIComponent(ticketId)}/attachments`);
267
+ const attachment = list.attachments?.find((entry) => entry.id === attachmentId);
268
+ if (!attachment)
269
+ throw new Error('Attachment not found. Use list_attachments to read current attachment ids.');
270
+ return {
271
+ attachment,
272
+ path: `${base}/tickets/${encodeURIComponent(ticketId)}/attachments/${encodeURIComponent(attachmentId)}`,
273
+ hint: 'Attachment metadata and download path; binary content is not inlined. Fetch the authenticated REST download or open the attachment in the app.',
274
+ };
229
275
  }
230
276
  // ── writes (synchronous store mutations) ────────────────────────
231
277
  case 'create': {
@@ -264,6 +310,7 @@ function specsTools() {
264
310
  metadata: args.metadata,
265
311
  acceptanceCriteria: args.acceptanceCriteria,
266
312
  short_summary: args.short_summary,
313
+ ...(args.repositoryIds === undefined ? {} : { repositoryIds: args.repositoryIds }),
267
314
  });
268
315
  case 'from_prompt':
269
316
  return (0, types_1.apiCall)(ctx, 'POST', `${base}/tickets/from-prompt`, {
@@ -272,6 +319,7 @@ function specsTools() {
272
319
  labels: args.labels,
273
320
  priority: args.priority,
274
321
  structured: args.structured,
322
+ ...(args.repositoryIds === undefined ? {} : { repositoryIds: args.repositoryIds }),
275
323
  pendingSpecId: args.pendingSpecId,
276
324
  });
277
325
  case 'save_draft': {
@@ -283,6 +331,7 @@ function specsTools() {
283
331
  description: args.description,
284
332
  labels: args.labels,
285
333
  editTicketId: args.editTicketId,
334
+ ...(args.repositoryIds === undefined ? {} : { repositoryIds: args.repositoryIds }),
286
335
  });
287
336
  }
288
337
  case 'commit_draft': {
@@ -299,7 +348,22 @@ function specsTools() {
299
348
  : agentAuthored
300
349
  ? true
301
350
  : undefined;
302
- return (0, types_1.apiCall)(ctx, 'POST', `${base}/tickets/from-draft`, {
351
+ // The framing gate (critical-spec-framing): a spec the in-app agent
352
+ // authored may not be persisted until the conversation holds a
353
+ // problem frame the user has answered, and one frame authorises ONE
354
+ // spec. Applied ONLY to first-party calls carrying a conversation —
355
+ // an external MCP client cannot render the framing card and holds no
356
+ // conversation here, so its behaviour is byte-identical to before.
357
+ // Throwing lands in registerTieredTool's catch, producing the same
358
+ // errorResult shape a tier refusal does.
359
+ const framedConversationId = ctx.firstPartyAgent ? ctx.originConversationId : null;
360
+ const agentProvider = framedConversationId ? (0, types_1.originConversationDefaults)(ctx).provider : undefined;
361
+ if (framedConversationId) {
362
+ const refusal = (0, agent_spec_framing_1.checkSpecFraming)(ctx.desktopDb, framedConversationId);
363
+ if (refusal)
364
+ throw new Error(refusal);
365
+ }
366
+ const committed = await (0, types_1.apiCall)(ctx, 'POST', `${base}/tickets/from-draft`, {
303
367
  title: args.title,
304
368
  description: args.description,
305
369
  draftTicketId: args.draftTicketId,
@@ -313,8 +377,16 @@ function specsTools() {
313
377
  prerequisites: args.prerequisites,
314
378
  metadata: args.metadata,
315
379
  createLocal: args.createLocal,
380
+ ...(args.repositoryIds === undefined ? {} : { repositoryIds: args.repositoryIds }),
316
381
  contractRefine,
382
+ agentProvider,
317
383
  });
384
+ // Spend the frame only on a spec that actually landed: a refusal or
385
+ // a failed write above throws before reaching here, so nothing is
386
+ // consumed by an attempt.
387
+ if (framedConversationId)
388
+ (0, agent_spec_framing_1.recordSpecCommitted)(ctx.desktopDb, framedConversationId);
389
+ return committed;
318
390
  }
319
391
  case 'cancel_ai_edit': {
320
392
  if (!args.requestId)
@@ -150,7 +150,7 @@ function inferTopic(question) {
150
150
  return 'general_usage';
151
151
  }
152
152
  async function collectDiagnostics(ctx, projectId, topic) {
153
- const projects = ctx.registry.listContexts().map((c) => (0, projects_1.serializeProject)(c.project));
153
+ const projects = ctx.registry.listProjects().map((p) => ({ ...(0, projects_1.serializeProject)(p), available: !!ctx.registry.getContext(p.id) }));
154
154
  const selectedProjectId = projectId ?? (0, types_1.getActiveProject)(ctx) ?? undefined;
155
155
  const out = {
156
156
  mcp: {
@@ -1,17 +1,38 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.McpApiError = void 0;
3
4
  exports.apiCall = apiCall;
4
5
  exports.projectPath = projectPath;
6
+ exports.requireRepository = requireRepository;
7
+ exports.repositoryPath = repositoryPath;
5
8
  exports.originConversationDefaults = originConversationDefaults;
6
9
  exports.requireProject = requireProject;
7
10
  exports.setActiveProject = setActiveProject;
8
11
  exports.getActiveProject = getActiveProject;
12
+ exports.toolTierAllowed = toolTierAllowed;
9
13
  exports.registerTieredTool = registerTieredTool;
10
14
  const mcp_tiers_1 = require("../mcp-tiers");
11
- const auth_1 = require("../../auth");
15
+ const internal_api_1 = require("../../internal-api");
12
16
  const agent_tier_1 = require("../../agent-tier");
13
17
  const agent_store_1 = require("../../agent-store");
14
18
  const agent_capability_1 = require("../agent-capability");
19
+ const project_repositories_1 = require("../../project-repositories");
20
+ const agent_steering_1 = require("../../agent-steering");
21
+ class McpApiError extends Error {
22
+ status;
23
+ data;
24
+ code;
25
+ constructor(message, status, data) {
26
+ super(message);
27
+ this.status = status;
28
+ this.data = data;
29
+ this.name = 'McpApiError';
30
+ const error = data?.error;
31
+ if (typeof error === 'string')
32
+ this.code = error;
33
+ }
34
+ }
35
+ exports.McpApiError = McpApiError;
15
36
  /**
16
37
  * Calls the app's own REST API over loopback using the MASTER token (held
17
38
  * server-side, never exposed to the MCP client). This reuses every router's
@@ -20,33 +41,45 @@ const agent_capability_1 = require("../agent-capability");
20
41
  * after `/api` (e.g. `/projects/<id>/tickets`).
21
42
  */
22
43
  async function apiCall(ctx, method, path, body) {
23
- const url = `http://127.0.0.1:${ctx.desktopPort}/api${path}`;
24
- const res = await fetch(url, {
25
- method,
26
- headers: {
27
- Authorization: `Bearer ${(0, auth_1.loadOrGenerateToken)()}`,
28
- ...(body !== undefined ? { 'content-type': 'application/json' } : {}),
29
- },
30
- body: body !== undefined ? JSON.stringify(body) : undefined,
31
- });
32
- const text = await res.text();
33
- let data;
34
- try {
35
- data = text ? JSON.parse(text) : null;
36
- }
37
- catch {
38
- data = text;
39
- }
44
+ // Shared loopback client (server/internal-api.ts) — also drives the milestone
45
+ // launch chain's chunk launches, so both stay byte-identical to a dashboard call.
46
+ // MCP calls additionally retain their request cancellation and bounded wait;
47
+ // those lifetimes belong to this call, not to the durable milestone chain.
48
+ const signal = ctx.signal
49
+ ? AbortSignal.any([ctx.signal, AbortSignal.timeout(120_000)])
50
+ : AbortSignal.timeout(120_000);
51
+ const res = await (0, internal_api_1.createInternalApi)({
52
+ port: ctx.desktopPort,
53
+ fetchImpl: (input, init) => fetch(input, { ...init, signal }),
54
+ }).call(method, path, body);
40
55
  if (!res.ok) {
56
+ const data = res.body;
41
57
  const detail = typeof data === 'object' && data !== null ? JSON.stringify(data) : String(data);
42
- throw new Error(`API ${method} ${path} → ${res.status}: ${detail}`);
58
+ throw new McpApiError(`API ${method} ${path} → ${res.status}: ${detail}`, res.status, data);
43
59
  }
44
- return data;
60
+ return res.body;
45
61
  }
46
62
  /** Convenience: build the `/projects/<id>` path prefix for a resolved project. */
47
63
  function projectPath(ctx, projectId) {
48
64
  return `/projects/${requireProject(ctx, projectId).project.id}`;
49
65
  }
66
+ /** Repository identity is relative to the resolved project, never a free path
67
+ * or another project's membership. MCP requires a choice where a file/Git
68
+ * operation could otherwise silently target the primary. */
69
+ function requireRepository(ctx, projectId, repositoryId) {
70
+ const { project } = requireProject(ctx, projectId);
71
+ const repositories = (0, project_repositories_1.getProjectRepositories)(project);
72
+ if (repositoryId === undefined && repositories.length > 1) {
73
+ const data = { error: 'repository_required', projectId: project.id, repositories };
74
+ throw new McpApiError(`Provide repositoryId for this multi-repository project: ${JSON.stringify(data)}`, 400, data);
75
+ }
76
+ return (0, project_repositories_1.resolveProjectRepository)(project, repositoryId);
77
+ }
78
+ function repositoryPath(ctx, projectId, repositoryId) {
79
+ const repository = requireRepository(ctx, projectId, repositoryId);
80
+ const base = `/projects/${encodeURIComponent(repository.projectId)}`;
81
+ return repositoryId === undefined ? base : `${base}/repositories/${encodeURIComponent(repository.id)}`;
82
+ }
50
83
  /**
51
84
  * Launch defaults derived from the LAUNCHING agent-chat conversation (the
52
85
  * authenticated origin carried on `ctx.originConversationId`). When the in-app agent
@@ -69,6 +102,7 @@ function originConversationDefaults(ctx) {
69
102
  return {};
70
103
  return {
71
104
  ...(conv.provider ? { provider: conv.provider } : {}),
105
+ ...(conv.model ? { model: conv.model } : {}),
72
106
  ...(conv.reasoning_effort ? { reasoningEffort: conv.reasoning_effort } : {}),
73
107
  };
74
108
  }
@@ -83,8 +117,12 @@ function requireProject(ctx, projectId) {
83
117
  throw new Error('No project specified and no active project selected. Call specrails_select_project or pass projectId.');
84
118
  }
85
119
  const pc = ctx.registry.getContext(id);
86
- if (!pc)
120
+ if (!pc) {
121
+ if (ctx.registry.getProjectRow?.(id)) {
122
+ throw new Error(`Project "${id}" is registered but its database is temporarily unavailable. Retry after recovery; do not register a duplicate project.`);
123
+ }
87
124
  throw new Error(`Unknown projectId "${id}". Use specrails_projects(list) to see valid ids.`);
125
+ }
88
126
  return pc;
89
127
  }
90
128
  // ── Active-project stickiness (per server process) ────────────────────────────
@@ -93,11 +131,25 @@ function requireProject(ctx, projectId) {
93
131
  // wins over the process-wide selection. `setActiveProject` remains for
94
132
  // `specrails_select_project`; request context must never mutate this global.
95
133
  let _activeProjectId = null;
96
- function setActiveProject(id) {
97
- _activeProjectId = id;
134
+ function setActiveProject(ctxOrId, id) {
135
+ if (typeof ctxOrId === 'object' && ctxOrId !== null) {
136
+ ctxOrId.sessionState ??= { activeProjectId: null };
137
+ ctxOrId.sessionState.activeProjectId = id ?? null;
138
+ }
139
+ else {
140
+ // Compatibility for direct legacy unit callers. Live transports always
141
+ // provide sessionState and never consult or change this fallback.
142
+ _activeProjectId = ctxOrId;
143
+ }
98
144
  }
99
145
  function getActiveProject(ctx) {
100
- return ctx.requestProjectId !== undefined ? ctx.requestProjectId : _activeProjectId;
146
+ return ctx.requestProjectId !== undefined ? ctx.requestProjectId
147
+ : ctx.sessionState ? ctx.sessionState.activeProjectId : _activeProjectId;
148
+ }
149
+ function toolTierAllowed(ctx, tier) {
150
+ return ctx.firstPartyAgent && ctx.agentTierLevel !== undefined
151
+ ? (0, agent_tier_1.levelAllowsTier)(ctx.agentTierLevel, tier)
152
+ : (0, mcp_tiers_1.isTierEnabled)(ctx.desktopDb, tier);
101
153
  }
102
154
  function tierAnnotations(tier) {
103
155
  switch (tier) {
@@ -112,7 +164,7 @@ function tierAnnotations(tier) {
112
164
  }
113
165
  }
114
166
  function toResult(data) {
115
- const text = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
167
+ const text = typeof data === 'string' ? data : JSON.stringify(data ?? null, null, 2);
116
168
  return { content: [{ type: 'text', text }] };
117
169
  }
118
170
  function errorResult(message) {
@@ -151,42 +203,64 @@ function emitMcpActivity(ctx, spec, args, tier) {
151
203
  * the tier check because it lives here, not in the handler.
152
204
  */
153
205
  function registerTieredTool(server, ctx, spec) {
154
- const hintTier = spec.hintTier ?? (typeof spec.tier === 'string' ? spec.tier : 'write');
206
+ // An action facade may read, write, launch AI and delete data through ONE
207
+ // protocol tool. Advertising the common read action as readOnlyHint:true
208
+ // misleads clients that use annotations when approving mutations.
209
+ const annotations = typeof spec.tier === 'function'
210
+ ? { readOnlyHint: false, destructiveHint: true, openWorldHint: true }
211
+ : tierAnnotations(spec.tier);
155
212
  server.registerTool(spec.name, {
156
213
  title: spec.title,
157
214
  description: spec.description,
158
215
  inputSchema: spec.inputSchema,
159
- annotations: tierAnnotations(hintTier),
216
+ annotations,
160
217
  }, async (args, extra) => {
218
+ if (extra?.signal?.aborted)
219
+ return errorResult('MCP request was cancelled before execution.');
161
220
  // Loopback identifies a machine, not a trusted caller. All first-party
162
221
  // authority and context come from this unguessable, server-minted bearer;
163
222
  // legacy tier/project/conversation headers are deliberately ignored.
164
- const capability = (0, agent_capability_1.verifyAgentCapability)(extra?.requestInfo?.headers?.[agent_tier_1.AGENT_CAPABILITY_HEADER]);
223
+ const suppliedCapability = extra?.requestInfo?.headers?.[agent_tier_1.AGENT_CAPABILITY_HEADER];
224
+ const capability = (0, agent_capability_1.verifyAgentCapability)(suppliedCapability);
225
+ if (suppliedCapability !== undefined && !capability) {
226
+ return errorResult('Agent capability expired or was revoked. Start a new mission turn to reconnect.');
227
+ }
165
228
  const callCtx = capability
166
229
  ? {
167
230
  ...ctx,
168
231
  requestProjectId: capability.projectId,
169
232
  originConversationId: capability.conversationId,
170
233
  firstPartyAgent: true,
234
+ agentTierLevel: capability.tierLevel,
171
235
  }
172
- : ctx;
236
+ : { ...ctx };
237
+ callCtx.signal = extra?.signal;
173
238
  const tier = typeof spec.tier === 'function' ? spec.tier(args ?? {}) : spec.tier;
174
- if (capability) {
175
- if (!(0, agent_tier_1.levelAllowsTier)(capability.tierLevel, tier)) {
176
- return errorResult((0, mcp_tiers_1.tierRefusalMessage)(tier));
177
- }
178
- }
179
- else if (!(0, mcp_tiers_1.isTierEnabled)(ctx.desktopDb, tier)) {
239
+ if (!toolTierAllowed(callCtx, tier)) {
180
240
  return errorResult((0, mcp_tiers_1.tierRefusalMessage)(tier));
181
241
  }
182
- try {
183
- const data = await spec.handler(callCtx, args ?? {});
184
- if (tier !== 'read')
185
- emitMcpActivity(callCtx, spec, args ?? {}, tier);
186
- return toResult(data);
187
- }
188
- catch (err) {
189
- return errorResult(err instanceof Error ? err.message : String(err));
242
+ const operation = async () => {
243
+ try {
244
+ const data = await spec.handler(callCtx, args ?? {});
245
+ if (tier !== 'read')
246
+ emitMcpActivity(callCtx, spec, args ?? {}, tier);
247
+ return toResult(data);
248
+ }
249
+ catch (err) {
250
+ return errorResult(err instanceof Error ? err.message : String(err));
251
+ }
252
+ };
253
+ if (capability && typeof suppliedCapability === 'string') {
254
+ callCtx.acknowledgeAgentUpdates = revision => (0, agent_steering_1.acknowledgeAgentSteering)(ctx.desktopDb, suppliedCapability, revision);
255
+ callCtx.acknowledgeAgentInputsRead = inputIds => (0, agent_steering_1.acknowledgeAgentInputsRead)(ctx.desktopDb, suppliedCapability, inputIds);
256
+ callCtx.onMissionInput = listener => (0, agent_steering_1.onAgentSteering)(ctx.desktopDb, suppliedCapability, listener);
257
+ return (0, agent_steering_1.runWithAgentSteering)(ctx.desktopDb, suppliedCapability, extra?.signal, operation, {
258
+ acknowledgement: spec.name === 'specrails_mission' && args?.action === 'acknowledge_updates',
259
+ });
190
260
  }
261
+ callCtx.acknowledgeAgentUpdates = undefined;
262
+ callCtx.acknowledgeAgentInputsRead = undefined;
263
+ callCtx.onMissionInput = undefined;
264
+ return operation();
191
265
  });
192
266
  }