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,7 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.BlueprintChatManager = void 0;
6
+ exports.BlueprintChatManager = exports.BUILDER_TURN_INTENTS = void 0;
7
+ exports.planSnapshotRepair = planSnapshotRepair;
7
8
  const crypto_1 = require("crypto");
8
9
  const tree_kill_1 = __importDefault(require("tree-kill"));
9
10
  const providers_1 = require("./providers");
@@ -15,8 +16,30 @@ const desktop_db_1 = require("./desktop-db");
15
16
  const builder_cwd_manager_1 = require("./builder-cwd-manager");
16
17
  const blueprint_operator_prompt_1 = require("./blueprint-operator-prompt");
17
18
  const blueprint_draft_parser_1 = require("./blueprint-draft-parser");
19
+ const blueprint_draft_parser_2 = require("./blueprint-draft-parser");
20
+ const blueprint_generation_1 = require("./blueprint-generation");
21
+ const blueprint_spec_quality_1 = require("./blueprint-spec-quality");
18
22
  const explore_draft_title_1 = require("./explore-draft-title");
19
23
  const blueprint_store_1 = require("./blueprint-store");
24
+ exports.BUILDER_TURN_INTENTS = ['surprise', 'approve'];
25
+ function repairKindForRejection(reason) {
26
+ return reason === 'truncated' ? 'truncated' : 'invalid_json';
27
+ }
28
+ /** Decide whether a settled parse needs ONE repair turn and what to ask. */
29
+ function planSnapshotRepair(parse) {
30
+ if (!parse.blueprint) {
31
+ const last = parse.rejected[parse.rejected.length - 1];
32
+ if (!last)
33
+ return null;
34
+ const reasonNote = last.reason === 'missing_version' ? 'The payload must be the blueprint object itself with an integer blueprintVersion. ' : '';
35
+ return { kind: repairKindForRejection(last.reason), detail: `${reasonNote}${last.detail}` };
36
+ }
37
+ const audit = (0, blueprint_spec_quality_1.auditRawBlueprintForM1)(parse.rawBlueprint);
38
+ if (audit.claimsComplete && !audit.valid) {
39
+ return { kind: 'quality', detail: (0, blueprint_spec_quality_1.formatQualityIssuesForModel)(audit.issues) };
40
+ }
41
+ return null;
42
+ }
20
43
  class BlueprintChatManager {
21
44
  _broadcast;
22
45
  _db;
@@ -115,7 +138,7 @@ class BlueprintChatManager {
115
138
  return;
116
139
  }
117
140
  try {
118
- await this._runTurn(conversation, userText, options);
141
+ await this._runTurn(conversation, { userText, options });
119
142
  }
120
143
  catch (err) {
121
144
  if (this._disposed)
@@ -127,8 +150,64 @@ class BlueprintChatManager {
127
150
  this._abortedTurns.delete(conversationId);
128
151
  }
129
152
  }
130
- async _runTurn(conversation, userText, options) {
153
+ /**
154
+ * Manual repair (the panel's "retry" after a rejected snapshot, or "ask the
155
+ * Builder to fix" after a failed audit). Decides what to ask from the
156
+ * PERSISTED state, so it works after a restart, and runs ONE repair turn on
157
+ * the same session. Resolves as soon as the turn is scheduled.
158
+ */
159
+ async repairSnapshot(conversationId, options = {}) {
160
+ if (this._disposed)
161
+ return { ok: false, reason: 'unknown_conversation' };
162
+ const conversation = (0, blueprint_store_1.getBlueprintConversation)(this._db, conversationId);
163
+ if (!conversation)
164
+ return { ok: false, reason: 'unknown_conversation' };
165
+ if (this._active.has(conversationId))
166
+ return { ok: false, reason: 'streaming' };
167
+ const snapshot = (0, blueprint_store_1.getBlueprintSnapshot)(this._db, conversationId);
168
+ let repair = null;
169
+ if (snapshot.issue) {
170
+ repair = { kind: repairKindForRejection(snapshot.issue.reason), detail: snapshot.issue.detail };
171
+ }
172
+ else if (snapshot.rawBlueprint !== null) {
173
+ const audit = (0, blueprint_spec_quality_1.auditRawBlueprintForM1)(snapshot.rawBlueprint);
174
+ if (audit.claimsComplete && !audit.valid) {
175
+ repair = { kind: 'quality', detail: (0, blueprint_spec_quality_1.formatQualityIssuesForModel)(audit.issues) };
176
+ }
177
+ }
178
+ // A halted batched generation (outline or partly written specs, no
179
+ // rejection pending) resumes from the next unfilled range instead of
180
+ // re-asking for a whole snapshot.
181
+ const resume = !repair && !snapshot.issue && snapshot.rawBlueprint !== null
182
+ && (0, blueprint_generation_1.unfilledSpecIndices)(snapshot.rawBlueprint).length > 0;
183
+ if (!repair && !resume)
184
+ return { ok: false, reason: 'nothing_to_repair' };
185
+ const adapter = (0, providers_1.getAdapter)(conversation.provider);
186
+ if (!conversation.session_id || !adapter.capabilities.nativeResume)
187
+ return { ok: false, reason: 'no_session' };
188
+ const request = repair;
189
+ void (async () => {
190
+ try {
191
+ // Same model as the conversation; the composer's effort rides along so a
192
+ // manual retry / Continue generating runs exactly like a regular send.
193
+ const turnOptions = { reasoningEffort: options.reasoningEffort };
194
+ await this._runTurn(conversation, request ? { options: turnOptions, repair: request } : { options: turnOptions, resume: true });
195
+ }
196
+ catch (err) {
197
+ if (this._disposed)
198
+ return;
199
+ console.error(`[blueprint-chat] repair turn failed (${conversationId}):`, err);
200
+ this._emitError(conversationId, err instanceof Error ? err.message : 'The Builder repair turn failed.');
201
+ }
202
+ finally {
203
+ this._abortedTurns.delete(conversationId);
204
+ }
205
+ })();
206
+ return { ok: true, kind: repair ? repair.kind : 'resume' };
207
+ }
208
+ async _runTurn(conversation, turn) {
131
209
  const conversationId = conversation.id;
210
+ const { options } = turn;
132
211
  const adapter = (0, providers_1.getAdapter)(conversation.provider);
133
212
  const toolPolicy = (0, runtime_1.pureOutputToolPolicy)(adapter);
134
213
  if (!toolPolicy) {
@@ -148,33 +227,42 @@ class BlueprintChatManager {
148
227
  : undefined;
149
228
  const cwd = (0, builder_cwd_manager_1.ensureBuilderCwd)();
150
229
  const timestamp = () => new Date().toISOString();
151
- (0, blueprint_store_1.addBlueprintMessage)(this._db, { conversationId, role: 'user', content: userText });
152
- // Deterministic title from the first user prompt (no AI spend).
153
- if (!conversation.title) {
154
- const title = (0, explore_draft_title_1.generateAutoTitle)([{ role: 'user', content: userText }]);
155
- if (title) {
156
- (0, blueprint_store_1.updateBlueprintConversation)(this._db, conversationId, { title });
230
+ // The session id the NEXT invocation resumes: the row's id at entry, then
231
+ // whatever the previous invocation captured (a repair turn must continue
232
+ // the session the rejected block came from).
233
+ let currentSessionId = conversation.session_id ?? null;
234
+ if (turn.userText !== undefined) {
235
+ (0, blueprint_store_1.addBlueprintMessage)(this._db, { conversationId, role: 'user', content: turn.userText, intent: options.intent ?? null });
236
+ // Deterministic title from the first user prompt (no AI spend).
237
+ if (!conversation.title) {
238
+ const title = (0, explore_draft_title_1.generateAutoTitle)([{ role: 'user', content: turn.userText }]);
239
+ if (title) {
240
+ (0, blueprint_store_1.updateBlueprintConversation)(this._db, conversationId, { title });
241
+ }
157
242
  }
158
243
  }
159
- const invoke = async (useResume) => {
244
+ const invoke = async (useResume, userText) => {
160
245
  const action = useResume ? 'chat-resume' : 'chat-turn';
161
246
  const startedAt = new Date().toISOString();
162
247
  let streamed = '';
163
- let capturedSessionId = useResume ? conversation.session_id ?? null : null;
248
+ let capturedSessionId = useResume ? currentSessionId : null;
164
249
  let capturedError = null;
165
250
  // `chat-turn` / `chat-resume` intentionally ignore `systemPrompt` for
166
251
  // providers whose normal project-chat stance lives in their cwd
167
252
  // instructions file. The day-0 Builder has no project instructions, so
168
253
  // its schema/operator prompt must ride in the user prompt instead.
254
+ // Batched generation needs a resumable session; a provider without one
255
+ // is told to generate everything in a single reply (legacy protocol).
256
+ const effectiveUser = adapter.capabilities.nativeResume ? userText : `${userText}\n\n${blueprint_operator_prompt_1.SINGLE_RESPONSE_MODE_LINE}`;
169
257
  const prompt = adapter.capabilities.systemPromptArg
170
- ? userText
171
- : `${blueprint_operator_prompt_1.BUILDER_SYSTEM_PROMPT}\n\n---\n\n${userText}`;
258
+ ? effectiveUser
259
+ : `${blueprint_operator_prompt_1.BUILDER_SYSTEM_PROMPT}\n\n---\n\n${effectiveUser}`;
172
260
  console.log(`[blueprint-chat] turn start conv=${conversationId} provider=${adapter.id} action=${action} model=${model}`);
173
261
  const buildOpts = {
174
262
  prompt,
175
263
  systemPrompt: adapter.capabilities.systemPromptArg ? blueprint_operator_prompt_1.BUILDER_SYSTEM_PROMPT : undefined,
176
264
  model,
177
- sessionId: useResume ? conversation.session_id ?? undefined : undefined,
265
+ sessionId: useResume ? currentSessionId ?? undefined : undefined,
178
266
  reasoning_effort: reasoningEffort,
179
267
  toolPolicy,
180
268
  };
@@ -239,87 +327,413 @@ class BlueprintChatManager {
239
327
  const persistSession = (sessionId) => {
240
328
  if (this._disposed)
241
329
  return;
330
+ currentSessionId = sessionId;
242
331
  if ((0, blueprint_store_1.getBlueprintConversation)(this._db, conversationId)) {
243
332
  (0, blueprint_store_1.updateBlueprintConversation)(this._db, conversationId, { session_id: sessionId });
244
333
  }
245
334
  };
246
- // One agent_invocations row per settled turn, project_id NULL (day-0 spend).
335
+ // One agent_invocations row per settled invocation, project_id NULL
336
+ // (day-0 spend) — a repair turn is a second spawn and bills too.
247
337
  const record = (outcome, status) => {
248
338
  if (this._disposed || outcome.disposed)
249
339
  return;
250
340
  this._recordTurn(conversation, adapter, model, outcome, status);
251
341
  };
252
- const settleText = (r) => {
253
- const { stripped, blueprint, rawBlueprint } = (0, blueprint_draft_parser_1.parseBlueprintDraftBlocks)(r.text);
342
+ /** Persist one assistant reply: stripped transcript + unstripped raw when
343
+ * it carried a block. An empty stripped reply (block only) is still
344
+ * stored so the raw payload survives; the API hides empty rows. */
345
+ const persistReply = (r, stripped, hadBlocks) => {
254
346
  const content = stripped.trim();
255
347
  if ((0, blueprint_store_1.getBlueprintConversation)(this._db, conversationId)) {
256
- (0, blueprint_store_1.addBlueprintMessage)(this._db, { conversationId, role: 'assistant', content: content || r.text });
348
+ (0, blueprint_store_1.addBlueprintMessage)(this._db, {
349
+ conversationId,
350
+ role: 'assistant',
351
+ content: hadBlocks ? content : content || r.text,
352
+ rawContent: hadBlocks ? r.text : null,
353
+ });
257
354
  persistSession(r.sessionId);
258
355
  }
356
+ return hadBlocks ? content : content || r.text;
357
+ };
358
+ const persistAssistant = (r, parse) => {
359
+ // Generation fences (spec-detail / spec-audit) are stripped like
360
+ // blueprint-draft blocks — never raw JSON in the transcript.
361
+ const gen = (0, blueprint_generation_1.parseGenerationBlocks)(parse.stripped);
362
+ return persistReply(r, gen.stripped, parse.hadBlocks || gen.hadBlocks);
363
+ };
364
+ /**
365
+ * Apply a reply to a base snapshot (D7 patch protocol): a full
366
+ * blueprint-draft block wins; otherwise spec-detail / spec-audit blocks are
367
+ * merged into `baseRaw` by index. Returns the parse-shaped result the
368
+ * finish path understands, plus the transcript-safe text.
369
+ */
370
+ const applyReply = (text, baseRaw) => {
371
+ const parse = (0, blueprint_draft_parser_1.parseBlueprintDraftBlocks)(text);
372
+ const gen = (0, blueprint_generation_1.parseGenerationBlocks)(parse.stripped);
373
+ if (parse.blueprint)
374
+ return { parse: { ...parse, stripped: gen.stripped }, stripped: gen.stripped, hadBlocks: true, gen };
375
+ const patches = [...gen.details, ...(gen.audit?.fixes ?? [])];
376
+ // An audit verdict is a patch even with zero fixes (it flips specsComplete).
377
+ if ((patches.length > 0 || gen.audit) && baseRaw !== null && baseRaw !== undefined) {
378
+ let merged = (0, blueprint_generation_1.mergeSpecDetails)(baseRaw, patches);
379
+ if (gen.audit)
380
+ merged = (0, blueprint_generation_1.withSpecsComplete)(merged, gen.audit.specsComplete);
381
+ const blueprint = (0, blueprint_draft_parser_2.coerceBlueprint)(merged);
382
+ if (blueprint) {
383
+ return {
384
+ parse: { stripped: gen.stripped, blueprint, rawBlueprint: merged, hadBlocks: true, rejected: parse.rejected, repaired: false, truncated: gen.truncated },
385
+ stripped: gen.stripped, hadBlocks: true, gen,
386
+ };
387
+ }
388
+ }
389
+ const rejected = gen.rejected.length > 0 && parse.rejected.length === 0 && !parse.blueprint
390
+ ? [...parse.rejected, { index: 0, reason: gen.truncated ? 'truncated' : 'invalid_json', detail: gen.rejected.join('; ') }]
391
+ : parse.rejected;
392
+ return { parse: { ...parse, stripped: gen.stripped, rejected, hadBlocks: parse.hadBlocks || gen.hadBlocks }, stripped: gen.stripped, hadBlocks: parse.hadBlocks || gen.hadBlocks, gen };
393
+ };
394
+ const finishTurn = (fullText, parse, meta) => {
395
+ let snapshot;
396
+ if (parse.blueprint) {
397
+ const audit = (0, blueprint_spec_quality_1.auditRawBlueprintForM1)(parse.rawBlueprint);
398
+ snapshot = {
399
+ status: 'accepted',
400
+ repaired: parse.repaired || meta.fromRepairTurn,
401
+ repairAttempted: meta.repairAttempted,
402
+ claimsComplete: audit.claimsComplete,
403
+ ...(audit.claimsComplete && !audit.valid ? { qualityIssues: audit.issues } : {}),
404
+ ...(meta.generation ? { generation: meta.generation } : {}),
405
+ ...(meta.continuing ? { continuing: true } : {}),
406
+ ...(meta.generationHalted ? { generationHalted: true } : {}),
407
+ };
408
+ if ((0, blueprint_store_1.getBlueprintConversation)(this._db, conversationId)) {
409
+ (0, blueprint_store_1.saveBlueprintSnapshot)(this._db, conversationId, { blueprint: parse.blueprint, rawBlueprint: parse.rawBlueprint });
410
+ }
411
+ }
412
+ else if (parse.rejected.length > 0) {
413
+ const last = parse.rejected[parse.rejected.length - 1];
414
+ snapshot = { status: 'rejected', reason: last.reason, detail: last.detail, repairAttempted: meta.repairAttempted };
415
+ if ((0, blueprint_store_1.getBlueprintConversation)(this._db, conversationId)) {
416
+ (0, blueprint_store_1.saveBlueprintSnapshotIssue)(this._db, conversationId, { reason: last.reason, detail: last.detail, at: timestamp() });
417
+ }
418
+ console.warn(`[blueprint-chat] snapshot rejected conv=${conversationId} reason=${last.reason} repairAttempted=${meta.repairAttempted}: ${last.detail}`);
419
+ }
420
+ else {
421
+ snapshot = { status: 'none' };
422
+ }
259
423
  this._broadcast({
260
424
  type: 'blueprint.done',
261
425
  conversationId,
262
- fullText: content || r.text,
263
- blueprint,
264
- rawBlueprint,
426
+ fullText,
427
+ blueprint: parse.blueprint,
428
+ rawBlueprint: parse.rawBlueprint,
429
+ snapshot,
430
+ ...(meta.continuing ? { continuing: true } : {}),
265
431
  timestamp: timestamp(),
266
432
  });
267
433
  };
268
- const canResume = !!conversation.session_id && adapter.capabilities.nativeResume;
269
- let r = await invoke(canResume);
270
- if (this._disposed || r.disposed)
271
- return;
272
- if (!(0, blueprint_store_1.getBlueprintConversation)(this._db, conversationId)) {
273
- record(r, 'aborted'); // deleted mid-turn; the spawn still billed
274
- return;
275
- }
276
- if (this._abortedTurns.delete(conversationId)) {
277
- if (r.text)
278
- settleText(r);
279
- record(r, 'aborted');
280
- return;
281
- }
282
- // Auto-heal a stale session: a resume with no text retries once fresh.
283
- if (canResume && !r.spawnFailed && !r.error && !r.text) {
284
- (0, blueprint_store_1.updateBlueprintConversation)(this._db, conversationId, { session_id: null });
285
- r = await invoke(false);
434
+ /** Shared post-invocation triage. Returns the outcome to continue with, or
435
+ * null when the turn is over (disposed / deleted / aborted / failed). */
436
+ const settle = async (first, allowAutoHeal) => {
437
+ let r = first;
286
438
  if (this._disposed || r.disposed)
287
- return;
439
+ return null;
288
440
  if (!(0, blueprint_store_1.getBlueprintConversation)(this._db, conversationId)) {
441
+ record(r, 'aborted'); // deleted mid-turn; the spawn still billed
442
+ return null;
443
+ }
444
+ if (this._abortedTurns.delete(conversationId)) {
445
+ if (r.text) {
446
+ const parse = (0, blueprint_draft_parser_1.parseBlueprintDraftBlocks)(r.text);
447
+ finishTurn(persistAssistant(r, parse), parse, { repairAttempted: false, fromRepairTurn: false });
448
+ }
289
449
  record(r, 'aborted');
290
- return;
450
+ return null;
451
+ }
452
+ // Auto-heal a stale session: a resume with no text retries once fresh.
453
+ if (allowAutoHeal && !r.spawnFailed && !r.error && !r.text) {
454
+ (0, blueprint_store_1.updateBlueprintConversation)(this._db, conversationId, { session_id: null });
455
+ currentSessionId = null;
456
+ r = await invoke(false, turn.userText ?? '');
457
+ if (this._disposed || r.disposed)
458
+ return null;
459
+ if (!(0, blueprint_store_1.getBlueprintConversation)(this._db, conversationId)) {
460
+ record(r, 'aborted');
461
+ return null;
462
+ }
463
+ if (this._abortedTurns.delete(conversationId)) {
464
+ if (r.text) {
465
+ const parse = (0, blueprint_draft_parser_1.parseBlueprintDraftBlocks)(r.text);
466
+ finishTurn(persistAssistant(r, parse), parse, { repairAttempted: false, fromRepairTurn: false });
467
+ }
468
+ record(r, 'aborted');
469
+ return null;
470
+ }
471
+ }
472
+ if (r.spawnFailed) {
473
+ this._emitError(conversationId, `Failed to launch ${adapter.binary}. Is it installed and on PATH?`);
474
+ record(r, 'failed');
475
+ return null;
476
+ }
477
+ if (r.error || r.code !== 0) {
478
+ persistSession(null);
479
+ const reason = r.error ||
480
+ (r.stderrTail ? r.stderrTail.split('\n').filter(Boolean).slice(-3).join(' ').slice(0, 300) : '') ||
481
+ `${adapter.binary} exited with code ${r.code ?? 'unknown'}`;
482
+ this._emitError(conversationId, reason);
483
+ record(r, 'failed');
484
+ return null;
485
+ }
486
+ if (!r.text) {
487
+ persistSession(null);
488
+ const reason = (r.stderrTail ? r.stderrTail.split('\n').filter(Boolean).slice(-3).join(' ').slice(0, 300) : '') ||
489
+ 'The Builder returned no output.';
490
+ this._emitError(conversationId, reason);
491
+ record(r, 'failed');
492
+ return null;
493
+ }
494
+ return r;
495
+ };
496
+ /** Repair-turn triage (mirrors `settle` minus the auto-heal and the error
497
+ * broadcasts — a failed repair never turns a successful user turn into an
498
+ * error): 'stop' = the turn is over (disposed / deleted / aborted), null =
499
+ * the repair spawn produced nothing usable (the original outcome stands). */
500
+ const settleRepair = (r) => {
501
+ if (this._disposed || r.disposed)
502
+ return 'stop';
503
+ if (!(0, blueprint_store_1.getBlueprintConversation)(this._db, conversationId)) {
504
+ record(r, 'aborted');
505
+ return 'stop';
291
506
  }
292
507
  if (this._abortedTurns.delete(conversationId)) {
293
- if (r.text)
294
- settleText(r);
295
508
  record(r, 'aborted');
509
+ return 'stop';
510
+ }
511
+ if (r.spawnFailed || r.error || r.code !== 0 || !r.text) {
512
+ console.warn(`[blueprint-chat] repair turn produced no usable reply conv=${conversationId} code=${r.code} error=${r.error ?? ''}`);
513
+ record(r, 'failed');
514
+ return null;
515
+ }
516
+ return r;
517
+ };
518
+ const canResume = !!currentSessionId && adapter.capabilities.nativeResume;
519
+ /** The quality-repair tail (one automatic repair turn when the model's
520
+ * block was unusable, or it claimed completion and the gate disagrees).
521
+ * Patch-aware: a repair reply may be spec-detail blocks merged into the
522
+ * snapshot, or a full blueprint-draft block. */
523
+ const finishWithQualityRepair = async (text, current, meta) => {
524
+ const repair = planSnapshotRepair(current);
525
+ const canRepair = repair !== null && !!currentSessionId && adapter.capabilities.nativeResume;
526
+ if (!repair || !canRepair) {
527
+ finishTurn(text, current, { repairAttempted: false, fromRepairTurn: false, ...meta });
296
528
  return;
297
529
  }
298
- }
299
- if (r.spawnFailed) {
300
- this._emitError(conversationId, `Failed to launch ${adapter.binary}. Is it installed and on PATH?`);
301
- record(r, 'failed');
530
+ console.warn(`[blueprint-chat] snapshot needs repair conv=${conversationId} kind=${repair.kind}: ${repair.detail.slice(0, 200)}`);
531
+ this._broadcast({
532
+ type: 'blueprint.repairing',
533
+ conversationId,
534
+ kind: repair.kind,
535
+ attempt: 1,
536
+ manual: false,
537
+ timestamp: timestamp(),
538
+ });
539
+ const r2 = settleRepair(await invoke(true, (0, blueprint_operator_prompt_1.buildSnapshotRepairPrompt)(repair.kind, repair.detail)));
540
+ if (r2 === 'stop')
541
+ return;
542
+ if (r2 === null) {
543
+ // The repair spawn failed — the original turn still stands.
544
+ finishTurn(text, current, { repairAttempted: true, fromRepairTurn: false, ...meta });
545
+ return;
546
+ }
547
+ const applied = applyReply(r2.text, current.rawBlueprint);
548
+ const prose2 = persistReply(r2, applied.stripped, applied.hadBlocks);
549
+ record(r2, 'success');
550
+ const combinedText = prose2 ? (text ? `${text}\n\n${prose2}` : prose2) : text;
551
+ if (applied.parse.blueprint) {
552
+ finishTurn(combinedText, applied.parse, { repairAttempted: true, fromRepairTurn: true, ...meta });
553
+ }
554
+ else {
555
+ // Still unusable: report the ORIGINAL outcome (plus the newest
556
+ // rejection detail when the repair produced one) so the UI can offer a
557
+ // manual retry with the freshest diagnostic.
558
+ const merged = applied.parse.rejected.length > 0
559
+ ? { ...current, rejected: [...current.rejected, ...applied.parse.rejected] }
560
+ : current;
561
+ finishTurn(combinedText, merged, { repairAttempted: true, fromRepairTurn: false, ...meta });
562
+ }
563
+ };
564
+ // ── App-driven batched generation (D7). An OUTLINE snapshot (every spec
565
+ // named, no bodies) on a resumable session starts the detail → audit
566
+ // drive: the app asks for two specs per turn as spec-detail blocks, merges
567
+ // them by index, announces each phase, keeps the panel live after every
568
+ // turn (`continuing`), then runs one audit turn and the normal quality
569
+ // repair. Depth is no longer capped by a single reply's output budget.
570
+ const driveGeneration = async (initialRaw, fullText, base, mode) => {
571
+ let raw = initialRaw;
572
+ const total = (0, blueprint_generation_1.specTitles)(raw).length;
573
+ const totalTurns = (0, blueprint_generation_1.projectedGenerationTurns)(total);
574
+ // A resume re-derives the turn ordinal from what is already written so
575
+ // the "turn x/y" pill stays truthful and the budget covers the rest.
576
+ let turnNo = mode === 'resume'
577
+ ? 1 + Math.ceil((total - (0, blueprint_generation_1.unfilledSpecIndices)(raw).length) / blueprint_generation_1.SPECS_PER_DETAIL_TURN)
578
+ : 1;
579
+ const descriptor = (phase, from, to) => ({ phase, from, to, total, turn: turnNo, totalTurns });
580
+ const announce = (phase, from, to) => {
581
+ this._broadcast({ type: 'blueprint.generating', conversationId, ...descriptor(phase, from, to), timestamp: timestamp() });
582
+ };
583
+ const snapshotOf = (rawSnapshot) => ({
584
+ stripped: '', blueprint: (0, blueprint_draft_parser_2.coerceBlueprint)(rawSnapshot) ?? base.blueprint, rawBlueprint: rawSnapshot,
585
+ hadBlocks: true, rejected: [], repaired: base.repaired, truncated: false,
586
+ });
587
+ const emitContinuing = (rawSnapshot, generation) => {
588
+ finishTurn('', snapshotOf(rawSnapshot), { repairAttempted: false, fromRepairTurn: false, generation, continuing: true });
589
+ };
590
+ const halt = (rawSnapshot, generation) => {
591
+ console.warn(`[blueprint-chat] generation halted conv=${conversationId} phase=${generation.phase} turn=${turnNo}/${totalTurns}`);
592
+ finishTurn(fullText, snapshotOf((0, blueprint_generation_1.withSpecsComplete)(rawSnapshot, false)), { repairAttempted: true, fromRepairTurn: false, generation, generationHalted: true });
593
+ };
594
+ // The outline itself is the first accepted frame of the drive; a resume
595
+ // re-announces the next range so the panel leaves its halted state.
596
+ let range = (0, blueprint_generation_1.nextDetailRange)(raw);
597
+ emitContinuing(raw, mode === 'resume' && range
598
+ ? descriptor('details', range.from + 1, range.to + 1)
599
+ : descriptor('outline', 0, 0));
600
+ while (range) {
601
+ if (turnNo >= blueprint_generation_1.MAX_GENERATION_TURNS) {
602
+ halt(raw, descriptor('details', range.from + 1, range.to + 1));
603
+ return;
604
+ }
605
+ turnNo += 1;
606
+ const titles = (0, blueprint_generation_1.specTitles)(raw);
607
+ announce('details', range.from + 1, range.to + 1);
608
+ const r2 = settleRepair(await invoke(true, (0, blueprint_generation_1.buildDetailPrompt)(range, titles)));
609
+ if (r2 === 'stop')
610
+ return;
611
+ if (r2 === null) {
612
+ halt(raw, descriptor('details', range.from + 1, range.to + 1));
613
+ return;
614
+ }
615
+ const applied = applyReply(r2.text, raw);
616
+ persistReply(r2, applied.stripped, applied.hadBlocks);
617
+ record(r2, 'success');
618
+ let merged = applied.parse.blueprint ? applied.parse.rawBlueprint : raw;
619
+ if (!(0, blueprint_generation_1.rangeFilled)(merged, range) && turnNo < blueprint_generation_1.MAX_GENERATION_TURNS) {
620
+ // ONE repair for this range (a cut-off or malformed block).
621
+ turnNo += 1;
622
+ announce('repair', range.from + 1, range.to + 1);
623
+ const detail = applied.gen.rejected.join('; ') || 'no spec-detail block for the requested indexes';
624
+ const r3 = settleRepair(await invoke(true, (0, blueprint_generation_1.buildDetailRepairPrompt)(range, titles, detail)));
625
+ if (r3 === 'stop')
626
+ return;
627
+ if (r3 !== null) {
628
+ const applied3 = applyReply(r3.text, merged);
629
+ persistReply(r3, applied3.stripped, applied3.hadBlocks);
630
+ record(r3, 'success');
631
+ if (applied3.parse.blueprint)
632
+ merged = applied3.parse.rawBlueprint;
633
+ }
634
+ }
635
+ raw = (0, blueprint_generation_1.withSpecsComplete)(merged, false);
636
+ if (!(0, blueprint_generation_1.rangeFilled)(raw, range)) {
637
+ halt(raw, descriptor('details', range.from + 1, range.to + 1));
638
+ return;
639
+ }
640
+ emitContinuing(raw, descriptor('details', range.from + 1, range.to + 1));
641
+ range = (0, blueprint_generation_1.nextDetailRange)(raw);
642
+ }
643
+ // Audit turn: the model self-audits the whole batch and may patch specs.
644
+ if (turnNo < blueprint_generation_1.MAX_GENERATION_TURNS) {
645
+ turnNo += 1;
646
+ announce('audit', 1, total);
647
+ const ra = settleRepair(await invoke(true, (0, blueprint_generation_1.buildAuditPrompt)()));
648
+ if (ra === 'stop')
649
+ return;
650
+ if (ra !== null) {
651
+ const appliedAudit = applyReply(ra.text, raw);
652
+ persistReply(ra, appliedAudit.stripped, appliedAudit.hadBlocks);
653
+ record(ra, 'success');
654
+ if (appliedAudit.parse.blueprint)
655
+ raw = appliedAudit.parse.rawBlueprint;
656
+ const verdict = appliedAudit.gen.audit;
657
+ if (!verdict) {
658
+ // A reply without a spec-audit verdict: let the deterministic gate judge.
659
+ raw = (0, blueprint_generation_1.withSpecsComplete)(raw, true);
660
+ }
661
+ else if (!verdict.specsComplete && verdict.issues.length > 0 && turnNo < blueprint_generation_1.MAX_GENERATION_TURNS) {
662
+ // The model found blocking problems: ONE corrections turn (spec-detail
663
+ // patches for the affected specs), then the deterministic gate judges.
664
+ turnNo += 1;
665
+ announce('repair', 1, total);
666
+ const rc = settleRepair(await invoke(true, (0, blueprint_generation_1.buildAuditIssuesPrompt)(verdict.issues)));
667
+ if (rc === 'stop')
668
+ return;
669
+ if (rc !== null) {
670
+ const appliedFix = applyReply(rc.text, raw);
671
+ persistReply(rc, appliedFix.stripped, appliedFix.hadBlocks);
672
+ record(rc, 'success');
673
+ if (appliedFix.parse.blueprint)
674
+ raw = appliedFix.parse.rawBlueprint;
675
+ }
676
+ raw = (0, blueprint_generation_1.withSpecsComplete)(raw, true);
677
+ }
678
+ else if (!verdict.specsComplete) {
679
+ // No actionable issues (or no turn budget left): let the gate judge.
680
+ raw = (0, blueprint_generation_1.withSpecsComplete)(raw, true);
681
+ }
682
+ }
683
+ else {
684
+ raw = (0, blueprint_generation_1.withSpecsComplete)(raw, true);
685
+ }
686
+ }
687
+ else {
688
+ raw = (0, blueprint_generation_1.withSpecsComplete)(raw, true);
689
+ }
690
+ await finishWithQualityRepair(fullText, snapshotOf(raw), { generation: descriptor('audit', 1, total) });
691
+ };
692
+ // ── Manual repair turn: the request IS the repair; never nests another.
693
+ if (turn.repair) {
694
+ this._broadcast({
695
+ type: 'blueprint.repairing',
696
+ conversationId,
697
+ kind: turn.repair.kind,
698
+ attempt: 1,
699
+ manual: true,
700
+ timestamp: timestamp(),
701
+ });
702
+ const r = await settle(await invoke(canResume, (0, blueprint_operator_prompt_1.buildSnapshotRepairPrompt)(turn.repair.kind, turn.repair.detail)), false);
703
+ if (!r)
704
+ return;
705
+ const base = (0, blueprint_store_1.getBlueprintSnapshot)(this._db, conversationId).rawBlueprint;
706
+ const applied = applyReply(r.text, base);
707
+ const fullText = persistReply(r, applied.stripped, applied.hadBlocks);
708
+ record(r, 'success');
709
+ finishTurn(fullText, applied.parse, { repairAttempted: true, fromRepairTurn: true });
302
710
  return;
303
711
  }
304
- if (r.error || r.code !== 0) {
305
- persistSession(null);
306
- const reason = r.error ||
307
- (r.stderrTail ? r.stderrTail.split('\n').filter(Boolean).slice(-3).join(' ').slice(0, 300) : '') ||
308
- `${adapter.binary} exited with code ${r.code ?? 'unknown'}`;
309
- this._emitError(conversationId, reason);
310
- record(r, 'failed');
712
+ // ── Resume a halted batched generation (manual "Continue generating").
713
+ if (turn.resume) {
714
+ const persisted = (0, blueprint_store_1.getBlueprintSnapshot)(this._db, conversationId).rawBlueprint;
715
+ const blueprint = (0, blueprint_draft_parser_2.coerceBlueprint)(persisted);
716
+ if (!blueprint || (0, blueprint_generation_1.unfilledSpecIndices)(persisted).length === 0)
717
+ return;
718
+ await driveGeneration(persisted, '', { blueprint, repaired: false }, 'resume');
311
719
  return;
312
720
  }
313
- if (r.text) {
314
- settleText(r);
315
- record(r, 'success');
721
+ // ── Regular user turn.
722
+ const r = await settle(await invoke(canResume, turn.userText ?? ''), canResume);
723
+ if (!r)
724
+ return;
725
+ const parse = (0, blueprint_draft_parser_1.parseBlueprintDraftBlocks)(r.text);
726
+ const fullText = persistAssistant(r, parse);
727
+ record(r, 'success');
728
+ // ── App-driven batched generation (D7): an OUTLINE snapshot on a
729
+ // resumable session starts the detail → audit drive.
730
+ const canDrive = parse.blueprint !== null && (0, blueprint_generation_1.isOutlineSnapshot)(parse.rawBlueprint)
731
+ && !!currentSessionId && adapter.capabilities.nativeResume;
732
+ if (canDrive) {
733
+ await driveGeneration(parse.rawBlueprint, fullText, { blueprint: parse.blueprint, repaired: parse.repaired }, 'outline');
316
734
  return;
317
735
  }
318
- persistSession(null);
319
- const reason = (r.stderrTail ? r.stderrTail.split('\n').filter(Boolean).slice(-3).join(' ').slice(0, 300) : '') ||
320
- 'The Builder returned no output.';
321
- this._emitError(conversationId, reason);
322
- record(r, 'failed');
736
+ await finishWithQualityRepair(fullText, parse, {});
323
737
  }
324
738
  _recordTurn(conversation, adapter, model, outcome, status) {
325
739
  if (this._disposed)