rivet-design 0.12.3 → 0.13.0

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 (535) hide show
  1. package/README.md +50 -26
  2. package/README.npm.md +50 -26
  3. package/bin/rivet.js +39 -24
  4. package/dist/{mcp/agent-variants → agent-variants}/SessionStore.d.ts +11 -19
  5. package/dist/agent-variants/SessionStore.d.ts.map +1 -0
  6. package/dist/{mcp/agent-variants → agent-variants}/SessionStore.js +77 -80
  7. package/dist/agent-variants/SessionStore.js.map +1 -0
  8. package/dist/{mcp/agent-variants → agent-variants}/WorktreeOrchestrator.d.ts +53 -21
  9. package/dist/agent-variants/WorktreeOrchestrator.d.ts.map +1 -0
  10. package/dist/{mcp/agent-variants → agent-variants}/WorktreeOrchestrator.js +291 -49
  11. package/dist/agent-variants/WorktreeOrchestrator.js.map +1 -0
  12. package/dist/{mcp/agent-variants → agent-variants}/WorktreeOrchestrator.testHelpers.d.ts +1 -9
  13. package/dist/agent-variants/WorktreeOrchestrator.testHelpers.d.ts.map +1 -0
  14. package/dist/{mcp/agent-variants → agent-variants}/WorktreeOrchestrator.testHelpers.js +3 -18
  15. package/dist/agent-variants/WorktreeOrchestrator.testHelpers.js.map +1 -0
  16. package/dist/agent-variants/briefContent.d.ts.map +1 -0
  17. package/dist/agent-variants/briefContent.js.map +1 -0
  18. package/dist/{mcp/agent-variants → agent-variants}/contracts.d.ts +715 -721
  19. package/dist/agent-variants/contracts.d.ts.map +1 -0
  20. package/dist/{mcp/agent-variants → agent-variants}/contracts.js +2 -3
  21. package/dist/agent-variants/contracts.js.map +1 -0
  22. package/dist/agent-variants/createProjectArtifacts.d.ts.map +1 -0
  23. package/dist/agent-variants/createProjectArtifacts.js.map +1 -0
  24. package/dist/agent-variants/diffQa.d.ts.map +1 -0
  25. package/dist/agent-variants/diffQa.js.map +1 -0
  26. package/dist/{mcp/agent-variants → agent-variants}/elementRefToTarget.d.ts +1 -1
  27. package/dist/agent-variants/elementRefToTarget.d.ts.map +1 -0
  28. package/dist/agent-variants/elementRefToTarget.js.map +1 -0
  29. package/dist/agent-variants/errors.d.ts.map +1 -0
  30. package/dist/agent-variants/errors.js.map +1 -0
  31. package/dist/agent-variants/generatedDestination.d.ts +29 -0
  32. package/dist/agent-variants/generatedDestination.d.ts.map +1 -0
  33. package/dist/agent-variants/generatedDestination.js +59 -0
  34. package/dist/agent-variants/generatedDestination.js.map +1 -0
  35. package/dist/{mcp/agent-variants → agent-variants}/index.d.ts +3 -6
  36. package/dist/agent-variants/index.d.ts.map +1 -0
  37. package/dist/{mcp/agent-variants → agent-variants}/index.js +3 -6
  38. package/dist/agent-variants/index.js.map +1 -0
  39. package/dist/agent-variants/previewQa.d.ts.map +1 -0
  40. package/dist/agent-variants/previewQa.js.map +1 -0
  41. package/dist/agent-variants/runLabel.d.ts.map +1 -0
  42. package/dist/agent-variants/runLabel.js.map +1 -0
  43. package/dist/agent-variants/runPlan.d.ts.map +1 -0
  44. package/dist/agent-variants/runPlan.js.map +1 -0
  45. package/dist/agent-variants/sourceContext.d.ts.map +1 -0
  46. package/dist/agent-variants/sourceContext.js.map +1 -0
  47. package/dist/agent-variants/variantContext.d.ts.map +1 -0
  48. package/dist/agent-variants/variantContext.js.map +1 -0
  49. package/dist/cli/client.d.ts +43 -0
  50. package/dist/cli/client.d.ts.map +1 -0
  51. package/dist/cli/client.js +109 -0
  52. package/dist/cli/client.js.map +1 -0
  53. package/dist/cli/commandNames.d.ts +9 -0
  54. package/dist/cli/commandNames.d.ts.map +1 -0
  55. package/dist/cli/commandNames.js +24 -0
  56. package/dist/cli/commandNames.js.map +1 -0
  57. package/dist/cli/commands/auth.d.ts +10 -0
  58. package/dist/cli/commands/auth.d.ts.map +1 -0
  59. package/dist/cli/commands/auth.js +81 -0
  60. package/dist/cli/commands/auth.js.map +1 -0
  61. package/dist/cli/commands/elementSelector.d.ts +10 -0
  62. package/dist/cli/commands/elementSelector.d.ts.map +1 -0
  63. package/dist/cli/commands/elementSelector.js +41 -0
  64. package/dist/cli/commands/elementSelector.js.map +1 -0
  65. package/dist/cli/commands/evidence.d.ts +9 -0
  66. package/dist/cli/commands/evidence.d.ts.map +1 -0
  67. package/dist/cli/commands/evidence.js +85 -0
  68. package/dist/cli/commands/evidence.js.map +1 -0
  69. package/dist/cli/commands/mcpServe.d.ts +40 -0
  70. package/dist/cli/commands/mcpServe.d.ts.map +1 -0
  71. package/dist/cli/commands/mcpServe.js +440 -0
  72. package/dist/cli/commands/mcpServe.js.map +1 -0
  73. package/dist/cli/commands/open.d.ts +28 -0
  74. package/dist/cli/commands/open.d.ts.map +1 -0
  75. package/dist/cli/commands/open.js +280 -0
  76. package/dist/cli/commands/open.js.map +1 -0
  77. package/dist/cli/commands/reference.d.ts +8 -0
  78. package/dist/cli/commands/reference.d.ts.map +1 -0
  79. package/dist/cli/commands/reference.js +101 -0
  80. package/dist/cli/commands/reference.js.map +1 -0
  81. package/dist/cli/commands/serve.d.ts +9 -0
  82. package/dist/cli/commands/serve.d.ts.map +1 -0
  83. package/dist/cli/commands/serve.js +182 -0
  84. package/dist/cli/commands/serve.js.map +1 -0
  85. package/dist/cli/commands/shared.d.ts +31 -0
  86. package/dist/cli/commands/shared.d.ts.map +1 -0
  87. package/dist/cli/commands/shared.js +82 -0
  88. package/dist/cli/commands/shared.js.map +1 -0
  89. package/dist/cli/commands/status.d.ts +10 -0
  90. package/dist/cli/commands/status.d.ts.map +1 -0
  91. package/dist/cli/commands/status.js +61 -0
  92. package/dist/cli/commands/status.js.map +1 -0
  93. package/dist/cli/commands/stop.d.ts +8 -0
  94. package/dist/cli/commands/stop.d.ts.map +1 -0
  95. package/dist/cli/commands/stop.js +41 -0
  96. package/dist/cli/commands/stop.js.map +1 -0
  97. package/dist/cli/commands/variants.d.ts +4 -0
  98. package/dist/cli/commands/variants.d.ts.map +1 -0
  99. package/dist/cli/commands/variants.js +374 -0
  100. package/dist/cli/commands/variants.js.map +1 -0
  101. package/dist/cli/commands/wait.d.ts +10 -0
  102. package/dist/cli/commands/wait.d.ts.map +1 -0
  103. package/dist/cli/commands/wait.js +101 -0
  104. package/dist/cli/commands/wait.js.map +1 -0
  105. package/dist/cli/entry.d.ts +12 -0
  106. package/dist/cli/entry.d.ts.map +1 -0
  107. package/dist/cli/entry.js +61 -0
  108. package/dist/cli/entry.js.map +1 -0
  109. package/dist/cli/envelope.d.ts +60 -0
  110. package/dist/cli/envelope.d.ts.map +1 -0
  111. package/dist/cli/envelope.js +57 -0
  112. package/dist/cli/envelope.js.map +1 -0
  113. package/dist/cli/flags.d.ts +20 -0
  114. package/dist/cli/flags.d.ts.map +1 -0
  115. package/dist/cli/flags.js +72 -0
  116. package/dist/cli/flags.js.map +1 -0
  117. package/dist/cli/handoff.d.ts +54 -0
  118. package/dist/cli/handoff.d.ts.map +1 -0
  119. package/dist/cli/handoff.js +106 -0
  120. package/dist/cli/handoff.js.map +1 -0
  121. package/dist/cli/handoffStore.d.ts +24 -0
  122. package/dist/cli/handoffStore.d.ts.map +1 -0
  123. package/dist/cli/handoffStore.js +49 -0
  124. package/dist/cli/handoffStore.js.map +1 -0
  125. package/dist/cli/hostWorkNextAction.d.ts +45 -0
  126. package/dist/cli/hostWorkNextAction.d.ts.map +1 -0
  127. package/dist/cli/hostWorkNextAction.js +43 -0
  128. package/dist/cli/hostWorkNextAction.js.map +1 -0
  129. package/dist/cli/io.d.ts +17 -0
  130. package/dist/cli/io.d.ts.map +1 -0
  131. package/dist/cli/io.js +19 -0
  132. package/dist/cli/io.js.map +1 -0
  133. package/dist/cli/router.d.ts +23 -0
  134. package/dist/cli/router.d.ts.map +1 -0
  135. package/dist/cli/router.js +236 -0
  136. package/dist/cli/router.js.map +1 -0
  137. package/dist/cli/runnerSelection.d.ts +60 -0
  138. package/dist/cli/runnerSelection.d.ts.map +1 -0
  139. package/dist/cli/runnerSelection.js +93 -0
  140. package/dist/cli/runnerSelection.js.map +1 -0
  141. package/dist/cli/serverHandshake.d.ts +51 -0
  142. package/dist/cli/serverHandshake.d.ts.map +1 -0
  143. package/dist/cli/serverHandshake.js +144 -0
  144. package/dist/cli/serverHandshake.js.map +1 -0
  145. package/dist/index.d.ts +2 -14
  146. package/dist/index.d.ts.map +1 -1
  147. package/dist/index.js +84 -138
  148. package/dist/index.js.map +1 -1
  149. package/dist/install/allowlists.d.ts +65 -0
  150. package/dist/install/allowlists.d.ts.map +1 -0
  151. package/dist/install/allowlists.js +333 -0
  152. package/dist/install/allowlists.js.map +1 -0
  153. package/dist/install/codexGuidance.d.ts +11 -0
  154. package/dist/install/codexGuidance.d.ts.map +1 -0
  155. package/dist/install/codexGuidance.js +90 -0
  156. package/dist/install/codexGuidance.js.map +1 -0
  157. package/dist/install/harnesses.d.ts +37 -26
  158. package/dist/install/harnesses.d.ts.map +1 -1
  159. package/dist/install/harnesses.js +312 -502
  160. package/dist/install/harnesses.js.map +1 -1
  161. package/dist/install/legacyMcp.d.ts +26 -0
  162. package/dist/install/legacyMcp.d.ts.map +1 -0
  163. package/dist/install/legacyMcp.js +160 -0
  164. package/dist/install/legacyMcp.js.map +1 -0
  165. package/dist/install/mcpRegistration.d.ts +45 -0
  166. package/dist/install/mcpRegistration.d.ts.map +1 -0
  167. package/dist/install/mcpRegistration.js +215 -0
  168. package/dist/install/mcpRegistration.js.map +1 -0
  169. package/dist/mcp/changeBatchClassification.d.ts +18 -20
  170. package/dist/mcp/changeBatchClassification.d.ts.map +1 -1
  171. package/dist/mcp/changeBatchClassification.js +41 -46
  172. package/dist/mcp/changeBatchClassification.js.map +1 -1
  173. package/dist/proxy-middleware/proxy-config.d.ts +6 -0
  174. package/dist/proxy-middleware/proxy-config.d.ts.map +1 -1
  175. package/dist/proxy-middleware/proxy-config.js +162 -5
  176. package/dist/proxy-middleware/proxy-config.js.map +1 -1
  177. package/dist/routes/agentVariants.d.ts +14 -2
  178. package/dist/routes/agentVariants.d.ts.map +1 -1
  179. package/dist/routes/agentVariants.js +321 -65
  180. package/dist/routes/agentVariants.js.map +1 -1
  181. package/dist/routes/cli.d.ts +31 -0
  182. package/dist/routes/cli.d.ts.map +1 -0
  183. package/dist/routes/cli.js +95 -0
  184. package/dist/routes/cli.js.map +1 -0
  185. package/dist/routes/git.d.ts +1 -0
  186. package/dist/routes/git.d.ts.map +1 -1
  187. package/dist/routes/git.js +2 -1
  188. package/dist/routes/git.js.map +1 -1
  189. package/dist/routes/inlineStartTracker.d.ts +23 -0
  190. package/dist/routes/inlineStartTracker.d.ts.map +1 -0
  191. package/dist/routes/inlineStartTracker.js +17 -0
  192. package/dist/routes/inlineStartTracker.js.map +1 -0
  193. package/dist/routes/mcp.d.ts +2 -12
  194. package/dist/routes/mcp.d.ts.map +1 -1
  195. package/dist/routes/mcp.js +4 -785
  196. package/dist/routes/mcp.js.map +1 -1
  197. package/dist/server.d.ts +24 -9
  198. package/dist/server.d.ts.map +1 -1
  199. package/dist/server.js +169 -33
  200. package/dist/server.js.map +1 -1
  201. package/dist/services/CommitMessageGenerator.d.ts +9 -1
  202. package/dist/services/CommitMessageGenerator.d.ts.map +1 -1
  203. package/dist/services/CommitMessageGenerator.js +4 -1
  204. package/dist/services/CommitMessageGenerator.js.map +1 -1
  205. package/dist/services/ComponentSearchService.d.ts +4 -1
  206. package/dist/services/ComponentSearchService.d.ts.map +1 -1
  207. package/dist/services/ComponentSearchService.js.map +1 -1
  208. package/dist/services/ConfigManager.d.ts +8 -0
  209. package/dist/services/ConfigManager.d.ts.map +1 -1
  210. package/dist/services/ConfigManager.js +9 -0
  211. package/dist/services/ConfigManager.js.map +1 -1
  212. package/dist/services/HistoryReplayService.d.ts +20 -9
  213. package/dist/services/HistoryReplayService.d.ts.map +1 -1
  214. package/dist/services/HistoryReplayService.js +99 -43
  215. package/dist/services/HistoryReplayService.js.map +1 -1
  216. package/dist/services/MCPVariantsTelemetry.d.ts +1 -1
  217. package/dist/services/MCPVariantsTelemetry.d.ts.map +1 -1
  218. package/dist/services/MCPVariantsTelemetry.js +1 -0
  219. package/dist/services/MCPVariantsTelemetry.js.map +1 -1
  220. package/dist/services/PRTitleGenerator.d.ts +10 -2
  221. package/dist/services/PRTitleGenerator.d.ts.map +1 -1
  222. package/dist/services/PRTitleGenerator.js +8 -2
  223. package/dist/services/PRTitleGenerator.js.map +1 -1
  224. package/dist/services/ProjectDetectionService.d.ts.map +1 -1
  225. package/dist/services/ProjectDetectionService.js +14 -4
  226. package/dist/services/ProjectDetectionService.js.map +1 -1
  227. package/dist/services/ProjectMetadataStore.d.ts +115 -0
  228. package/dist/services/ProjectMetadataStore.d.ts.map +1 -0
  229. package/dist/services/ProjectMetadataStore.js +336 -0
  230. package/dist/services/ProjectMetadataStore.js.map +1 -0
  231. package/dist/services/SessionBridgeService.d.ts +52 -224
  232. package/dist/services/SessionBridgeService.d.ts.map +1 -1
  233. package/dist/services/SessionBridgeService.js +97 -649
  234. package/dist/services/SessionBridgeService.js.map +1 -1
  235. package/dist/services/SessionService.d.ts +4 -1
  236. package/dist/services/SessionService.d.ts.map +1 -1
  237. package/dist/services/SessionService.js +7 -4
  238. package/dist/services/SessionService.js.map +1 -1
  239. package/dist/services/StaticFileService.d.ts.map +1 -1
  240. package/dist/services/StaticFileService.js +6 -0
  241. package/dist/services/StaticFileService.js.map +1 -1
  242. package/dist/services/TelemetryService.d.ts +16 -80
  243. package/dist/services/TelemetryService.d.ts.map +1 -1
  244. package/dist/services/TelemetryService.js +18 -94
  245. package/dist/services/TelemetryService.js.map +1 -1
  246. package/dist/services/VariantGenerationService.d.ts +52 -0
  247. package/dist/services/VariantGenerationService.d.ts.map +1 -0
  248. package/dist/services/VariantGenerationService.js +272 -0
  249. package/dist/services/VariantGenerationService.js.map +1 -0
  250. package/dist/services/VariantHistoryService.d.ts +37 -1
  251. package/dist/services/VariantHistoryService.d.ts.map +1 -1
  252. package/dist/services/VariantHistoryService.js +122 -1
  253. package/dist/services/VariantHistoryService.js.map +1 -1
  254. package/dist/services/VariantRunService.d.ts +25 -6
  255. package/dist/services/VariantRunService.d.ts.map +1 -1
  256. package/dist/services/VariantRunService.js +98 -4
  257. package/dist/services/VariantRunService.js.map +1 -1
  258. package/dist/services/VariantsRuntime.d.ts +1 -3
  259. package/dist/services/VariantsRuntime.d.ts.map +1 -1
  260. package/dist/services/VariantsRuntime.js +0 -1
  261. package/dist/services/VariantsRuntime.js.map +1 -1
  262. package/dist/services/WorktreeManager.d.ts +1 -1
  263. package/dist/services/WorktreeManager.d.ts.map +1 -1
  264. package/dist/services/createAgentVariantsOrchestrator.d.ts +2 -2
  265. package/dist/services/createAgentVariantsOrchestrator.d.ts.map +1 -1
  266. package/dist/services/createAgentVariantsOrchestrator.js +2 -3
  267. package/dist/services/createAgentVariantsOrchestrator.js.map +1 -1
  268. package/dist/services/projectMetadataMigrations.d.ts +59 -0
  269. package/dist/services/projectMetadataMigrations.d.ts.map +1 -0
  270. package/dist/services/projectMetadataMigrations.js +109 -0
  271. package/dist/services/projectMetadataMigrations.js.map +1 -0
  272. package/dist/types/change-request-types.d.ts +22 -0
  273. package/dist/types/change-request-types.d.ts.map +1 -1
  274. package/dist/utils/logger.d.ts +2 -1
  275. package/dist/utils/logger.d.ts.map +1 -1
  276. package/dist/utils/logger.js +3 -7
  277. package/dist/utils/logger.js.map +1 -1
  278. package/dist/utils/loopback.d.ts +3 -0
  279. package/dist/utils/loopback.d.ts.map +1 -0
  280. package/dist/utils/loopback.js +13 -0
  281. package/dist/utils/loopback.js.map +1 -0
  282. package/dist/utils/pid.d.ts +3 -0
  283. package/dist/utils/pid.d.ts.map +1 -0
  284. package/dist/utils/pid.js +15 -0
  285. package/dist/utils/pid.js.map +1 -0
  286. package/dist/utils/queueAccess.d.ts +7 -0
  287. package/dist/utils/queueAccess.d.ts.map +1 -1
  288. package/dist/utils/queueAccess.js +8 -1
  289. package/dist/utils/queueAccess.js.map +1 -1
  290. package/dist/utils/shouldRecordHostedDemoSessionAction.d.ts +3 -3
  291. package/dist/utils/shouldRecordHostedDemoSessionAction.d.ts.map +1 -1
  292. package/dist/utils/shouldRecordHostedDemoSessionAction.js +3 -19
  293. package/dist/utils/shouldRecordHostedDemoSessionAction.js.map +1 -1
  294. package/dist/utils/skillWriter.d.ts.map +1 -1
  295. package/dist/utils/skillWriter.js +12 -11
  296. package/dist/utils/skillWriter.js.map +1 -1
  297. package/dist/utils/skills/claude-skill.d.ts +7 -2
  298. package/dist/utils/skills/claude-skill.d.ts.map +1 -1
  299. package/dist/utils/skills/claude-skill.js +10 -83
  300. package/dist/utils/skills/claude-skill.js.map +1 -1
  301. package/dist/utils/skills/cli-guidance.d.ts +12 -0
  302. package/dist/utils/skills/cli-guidance.d.ts.map +1 -0
  303. package/dist/utils/skills/cli-guidance.js +57 -0
  304. package/dist/utils/skills/cli-guidance.js.map +1 -0
  305. package/dist/utils/skills/cursor-rules.d.ts +8 -2
  306. package/dist/utils/skills/cursor-rules.d.ts.map +1 -1
  307. package/dist/utils/skills/cursor-rules.js +11 -80
  308. package/dist/utils/skills/cursor-rules.js.map +1 -1
  309. package/dist/utils/variantSessionStart.d.ts +1 -1
  310. package/dist/utils/variantSessionStart.d.ts.map +1 -1
  311. package/dist/utils/variantSessionStart.js +1 -1
  312. package/dist/utils/variantSessionStart.js.map +1 -1
  313. package/package.json +4 -4
  314. package/src/ui/dist/assets/main-BWOvFoal.js +419 -0
  315. package/src/ui/dist/assets/main-Dyos9I29.css +1 -0
  316. package/src/ui/dist/index.html +2 -2
  317. package/dist/mcp/agent-variants/SessionStore.d.ts.map +0 -1
  318. package/dist/mcp/agent-variants/SessionStore.js.map +0 -1
  319. package/dist/mcp/agent-variants/WorktreeOrchestrator.d.ts.map +0 -1
  320. package/dist/mcp/agent-variants/WorktreeOrchestrator.js.map +0 -1
  321. package/dist/mcp/agent-variants/WorktreeOrchestrator.testHelpers.d.ts.map +0 -1
  322. package/dist/mcp/agent-variants/WorktreeOrchestrator.testHelpers.js.map +0 -1
  323. package/dist/mcp/agent-variants/areNaSourceContext.d.ts +0 -20
  324. package/dist/mcp/agent-variants/areNaSourceContext.d.ts.map +0 -1
  325. package/dist/mcp/agent-variants/areNaSourceContext.js +0 -118
  326. package/dist/mcp/agent-variants/areNaSourceContext.js.map +0 -1
  327. package/dist/mcp/agent-variants/briefContent.d.ts.map +0 -1
  328. package/dist/mcp/agent-variants/briefContent.js.map +0 -1
  329. package/dist/mcp/agent-variants/contracts.d.ts.map +0 -1
  330. package/dist/mcp/agent-variants/contracts.js.map +0 -1
  331. package/dist/mcp/agent-variants/createProjectArtifacts.d.ts.map +0 -1
  332. package/dist/mcp/agent-variants/createProjectArtifacts.js.map +0 -1
  333. package/dist/mcp/agent-variants/createZeroToOneTool.d.ts +0 -63
  334. package/dist/mcp/agent-variants/createZeroToOneTool.d.ts.map +0 -1
  335. package/dist/mcp/agent-variants/createZeroToOneTool.js +0 -265
  336. package/dist/mcp/agent-variants/createZeroToOneTool.js.map +0 -1
  337. package/dist/mcp/agent-variants/diffQa.d.ts.map +0 -1
  338. package/dist/mcp/agent-variants/diffQa.js.map +0 -1
  339. package/dist/mcp/agent-variants/elementRefToTarget.d.ts.map +0 -1
  340. package/dist/mcp/agent-variants/elementRefToTarget.js.map +0 -1
  341. package/dist/mcp/agent-variants/errors.d.ts.map +0 -1
  342. package/dist/mcp/agent-variants/errors.js.map +0 -1
  343. package/dist/mcp/agent-variants/generatedDestination.d.ts +0 -75
  344. package/dist/mcp/agent-variants/generatedDestination.d.ts.map +0 -1
  345. package/dist/mcp/agent-variants/generatedDestination.js +0 -103
  346. package/dist/mcp/agent-variants/generatedDestination.js.map +0 -1
  347. package/dist/mcp/agent-variants/index.d.ts.map +0 -1
  348. package/dist/mcp/agent-variants/index.js.map +0 -1
  349. package/dist/mcp/agent-variants/pendingChangesAdapter.d.ts +0 -38
  350. package/dist/mcp/agent-variants/pendingChangesAdapter.d.ts.map +0 -1
  351. package/dist/mcp/agent-variants/pendingChangesAdapter.js +0 -93
  352. package/dist/mcp/agent-variants/pendingChangesAdapter.js.map +0 -1
  353. package/dist/mcp/agent-variants/pinterestSourceContext.d.ts +0 -23
  354. package/dist/mcp/agent-variants/pinterestSourceContext.d.ts.map +0 -1
  355. package/dist/mcp/agent-variants/pinterestSourceContext.js +0 -137
  356. package/dist/mcp/agent-variants/pinterestSourceContext.js.map +0 -1
  357. package/dist/mcp/agent-variants/previewQa.d.ts.map +0 -1
  358. package/dist/mcp/agent-variants/previewQa.js.map +0 -1
  359. package/dist/mcp/agent-variants/runLabel.d.ts.map +0 -1
  360. package/dist/mcp/agent-variants/runLabel.js.map +0 -1
  361. package/dist/mcp/agent-variants/runPlan.d.ts.map +0 -1
  362. package/dist/mcp/agent-variants/runPlan.js.map +0 -1
  363. package/dist/mcp/agent-variants/sourceContext.d.ts.map +0 -1
  364. package/dist/mcp/agent-variants/sourceContext.js.map +0 -1
  365. package/dist/mcp/agent-variants/tools.d.ts +0 -187
  366. package/dist/mcp/agent-variants/tools.d.ts.map +0 -1
  367. package/dist/mcp/agent-variants/tools.js +0 -1165
  368. package/dist/mcp/agent-variants/tools.js.map +0 -1
  369. package/dist/mcp/agent-variants/variantContext.d.ts.map +0 -1
  370. package/dist/mcp/agent-variants/variantContext.js.map +0 -1
  371. package/dist/mcp/auth/httpOAuthProvider.d.ts +0 -103
  372. package/dist/mcp/auth/httpOAuthProvider.d.ts.map +0 -1
  373. package/dist/mcp/auth/httpOAuthProvider.js +0 -735
  374. package/dist/mcp/auth/httpOAuthProvider.js.map +0 -1
  375. package/dist/mcp/auth/tools.d.ts +0 -39
  376. package/dist/mcp/auth/tools.d.ts.map +0 -1
  377. package/dist/mcp/auth/tools.js +0 -304
  378. package/dist/mcp/auth/tools.js.map +0 -1
  379. package/dist/mcp/branding.d.ts +0 -4
  380. package/dist/mcp/branding.d.ts.map +0 -1
  381. package/dist/mcp/branding.js +0 -8
  382. package/dist/mcp/branding.js.map +0 -1
  383. package/dist/mcp/hostedServer.d.ts +0 -27
  384. package/dist/mcp/hostedServer.d.ts.map +0 -1
  385. package/dist/mcp/hostedServer.js +0 -71
  386. package/dist/mcp/hostedServer.js.map +0 -1
  387. package/dist/mcp/httpServer.d.ts +0 -63
  388. package/dist/mcp/httpServer.d.ts.map +0 -1
  389. package/dist/mcp/httpServer.js +0 -602
  390. package/dist/mcp/httpServer.js.map +0 -1
  391. package/dist/mcp/instructions.d.ts +0 -25
  392. package/dist/mcp/instructions.d.ts.map +0 -1
  393. package/dist/mcp/instructions.js +0 -39
  394. package/dist/mcp/instructions.js.map +0 -1
  395. package/dist/mcp/integrations/tools.d.ts +0 -19
  396. package/dist/mcp/integrations/tools.d.ts.map +0 -1
  397. package/dist/mcp/integrations/tools.js +0 -101
  398. package/dist/mcp/integrations/tools.js.map +0 -1
  399. package/dist/mcp/server.d.ts +0 -214
  400. package/dist/mcp/server.d.ts.map +0 -1
  401. package/dist/mcp/server.js +0 -2536
  402. package/dist/mcp/server.js.map +0 -1
  403. package/dist/mcp/toolAnalytics.d.ts +0 -26
  404. package/dist/mcp/toolAnalytics.d.ts.map +0 -1
  405. package/dist/mcp/toolAnalytics.js +0 -95
  406. package/dist/mcp/toolAnalytics.js.map +0 -1
  407. package/dist/mcp/types.d.ts +0 -7
  408. package/dist/mcp/types.d.ts.map +0 -1
  409. package/dist/mcp/types.js +0 -3
  410. package/dist/mcp/types.js.map +0 -1
  411. package/dist/mcp/watchLoopResumePrompt.d.ts +0 -16
  412. package/dist/mcp/watchLoopResumePrompt.d.ts.map +0 -1
  413. package/dist/mcp/watchLoopResumePrompt.js +0 -21
  414. package/dist/mcp/watchLoopResumePrompt.js.map +0 -1
  415. package/dist/prompts/agentModPrompts.d.ts +0 -4
  416. package/dist/prompts/agentModPrompts.d.ts.map +0 -1
  417. package/dist/prompts/agentModPrompts.js +0 -34
  418. package/dist/prompts/agentModPrompts.js.map +0 -1
  419. package/dist/prompts/tokenWriterPrompts.d.ts +0 -4
  420. package/dist/prompts/tokenWriterPrompts.d.ts.map +0 -1
  421. package/dist/prompts/tokenWriterPrompts.js +0 -59
  422. package/dist/prompts/tokenWriterPrompts.js.map +0 -1
  423. package/dist/routes/comments.d.ts +0 -3
  424. package/dist/routes/comments.d.ts.map +0 -1
  425. package/dist/routes/comments.js +0 -113
  426. package/dist/routes/comments.js.map +0 -1
  427. package/dist/routes/components.d.ts +0 -7
  428. package/dist/routes/components.d.ts.map +0 -1
  429. package/dist/routes/components.js +0 -88
  430. package/dist/routes/components.js.map +0 -1
  431. package/dist/routes/controls.d.ts +0 -7
  432. package/dist/routes/controls.d.ts.map +0 -1
  433. package/dist/routes/controls.js +0 -50
  434. package/dist/routes/controls.js.map +0 -1
  435. package/dist/routes/design.d.ts +0 -2
  436. package/dist/routes/design.d.ts.map +0 -1
  437. package/dist/routes/design.js +0 -29
  438. package/dist/routes/design.js.map +0 -1
  439. package/dist/routes/modifications.d.ts +0 -10
  440. package/dist/routes/modifications.d.ts.map +0 -1
  441. package/dist/routes/modifications.js +0 -394
  442. package/dist/routes/modifications.js.map +0 -1
  443. package/dist/routes/tokens.d.ts +0 -2
  444. package/dist/routes/tokens.d.ts.map +0 -1
  445. package/dist/routes/tokens.js +0 -115
  446. package/dist/routes/tokens.js.map +0 -1
  447. package/dist/services/AgentSessionService.d.ts +0 -91
  448. package/dist/services/AgentSessionService.d.ts.map +0 -1
  449. package/dist/services/AgentSessionService.js +0 -282
  450. package/dist/services/AgentSessionService.js.map +0 -1
  451. package/dist/services/BridgeBatchPipeline.d.ts +0 -103
  452. package/dist/services/BridgeBatchPipeline.d.ts.map +0 -1
  453. package/dist/services/BridgeBatchPipeline.js +0 -295
  454. package/dist/services/BridgeBatchPipeline.js.map +0 -1
  455. package/dist/services/BridgeQueueStore.d.ts +0 -28
  456. package/dist/services/BridgeQueueStore.d.ts.map +0 -1
  457. package/dist/services/BridgeQueueStore.js +0 -88
  458. package/dist/services/BridgeQueueStore.js.map +0 -1
  459. package/dist/services/CSSTokenWriter.d.ts +0 -22
  460. package/dist/services/CSSTokenWriter.d.ts.map +0 -1
  461. package/dist/services/CSSTokenWriter.js +0 -116
  462. package/dist/services/CSSTokenWriter.js.map +0 -1
  463. package/dist/services/DesignTokenService.d.ts +0 -39
  464. package/dist/services/DesignTokenService.d.ts.map +0 -1
  465. package/dist/services/DesignTokenService.js +0 -100
  466. package/dist/services/DesignTokenService.js.map +0 -1
  467. package/dist/services/InlineVariantGenerationService.d.ts +0 -51
  468. package/dist/services/InlineVariantGenerationService.d.ts.map +0 -1
  469. package/dist/services/InlineVariantGenerationService.js +0 -475
  470. package/dist/services/InlineVariantGenerationService.js.map +0 -1
  471. package/dist/services/InterfaceGenerationService.d.ts +0 -12
  472. package/dist/services/InterfaceGenerationService.d.ts.map +0 -1
  473. package/dist/services/InterfaceGenerationService.js +0 -256
  474. package/dist/services/InterfaceGenerationService.js.map +0 -1
  475. package/dist/services/TailwindConfigWriter.d.ts +0 -24
  476. package/dist/services/TailwindConfigWriter.d.ts.map +0 -1
  477. package/dist/services/TailwindConfigWriter.js +0 -105
  478. package/dist/services/TailwindConfigWriter.js.map +0 -1
  479. package/dist/services/TerminalAgentRunner.d.ts +0 -65
  480. package/dist/services/TerminalAgentRunner.d.ts.map +0 -1
  481. package/dist/services/TerminalAgentRunner.js +0 -511
  482. package/dist/services/TerminalAgentRunner.js.map +0 -1
  483. package/dist/services/VisualVariantAgentRunner.d.ts +0 -20
  484. package/dist/services/VisualVariantAgentRunner.d.ts.map +0 -1
  485. package/dist/services/VisualVariantAgentRunner.js +0 -66
  486. package/dist/services/VisualVariantAgentRunner.js.map +0 -1
  487. package/dist/services/adapters/TailwindCSSTokenAdapter.d.ts +0 -35
  488. package/dist/services/adapters/TailwindCSSTokenAdapter.d.ts.map +0 -1
  489. package/dist/services/adapters/TailwindCSSTokenAdapter.js +0 -278
  490. package/dist/services/adapters/TailwindCSSTokenAdapter.js.map +0 -1
  491. package/dist/services/agent/AgentCore.d.ts +0 -126
  492. package/dist/services/agent/AgentCore.d.ts.map +0 -1
  493. package/dist/services/agent/AgentCore.js +0 -759
  494. package/dist/services/agent/AgentCore.js.map +0 -1
  495. package/dist/services/agent/AgentModService.d.ts +0 -98
  496. package/dist/services/agent/AgentModService.d.ts.map +0 -1
  497. package/dist/services/agent/AgentModService.js +0 -400
  498. package/dist/services/agent/AgentModService.js.map +0 -1
  499. package/dist/services/agent/index.d.ts +0 -2
  500. package/dist/services/agent/index.d.ts.map +0 -1
  501. package/dist/services/agent/index.js +0 -6
  502. package/dist/services/agent/index.js.map +0 -1
  503. package/dist/types/editor-interface-types.d.ts +0 -67
  504. package/dist/types/editor-interface-types.d.ts.map +0 -1
  505. package/dist/types/editor-interface-types.js +0 -12
  506. package/dist/types/editor-interface-types.js.map +0 -1
  507. package/dist/utils/elementRefToContext.d.ts +0 -4
  508. package/dist/utils/elementRefToContext.d.ts.map +0 -1
  509. package/dist/utils/elementRefToContext.js +0 -63
  510. package/dist/utils/elementRefToContext.js.map +0 -1
  511. package/dist/utils/skills/shared-variants-protocol.d.ts +0 -69
  512. package/dist/utils/skills/shared-variants-protocol.d.ts.map +0 -1
  513. package/dist/utils/skills/shared-variants-protocol.js +0 -292
  514. package/dist/utils/skills/shared-variants-protocol.js.map +0 -1
  515. package/src/ui/dist/assets/main-CLAqO1bm.js +0 -656
  516. package/src/ui/dist/assets/main-Ke-Q9lWa.css +0 -1
  517. /package/dist/{mcp/agent-variants → agent-variants}/briefContent.d.ts +0 -0
  518. /package/dist/{mcp/agent-variants → agent-variants}/briefContent.js +0 -0
  519. /package/dist/{mcp/agent-variants → agent-variants}/createProjectArtifacts.d.ts +0 -0
  520. /package/dist/{mcp/agent-variants → agent-variants}/createProjectArtifacts.js +0 -0
  521. /package/dist/{mcp/agent-variants → agent-variants}/diffQa.d.ts +0 -0
  522. /package/dist/{mcp/agent-variants → agent-variants}/diffQa.js +0 -0
  523. /package/dist/{mcp/agent-variants → agent-variants}/elementRefToTarget.js +0 -0
  524. /package/dist/{mcp/agent-variants → agent-variants}/errors.d.ts +0 -0
  525. /package/dist/{mcp/agent-variants → agent-variants}/errors.js +0 -0
  526. /package/dist/{mcp/agent-variants → agent-variants}/previewQa.d.ts +0 -0
  527. /package/dist/{mcp/agent-variants → agent-variants}/previewQa.js +0 -0
  528. /package/dist/{mcp/agent-variants → agent-variants}/runLabel.d.ts +0 -0
  529. /package/dist/{mcp/agent-variants → agent-variants}/runLabel.js +0 -0
  530. /package/dist/{mcp/agent-variants → agent-variants}/runPlan.d.ts +0 -0
  531. /package/dist/{mcp/agent-variants → agent-variants}/runPlan.js +0 -0
  532. /package/dist/{mcp/agent-variants → agent-variants}/sourceContext.d.ts +0 -0
  533. /package/dist/{mcp/agent-variants → agent-variants}/sourceContext.js +0 -0
  534. /package/dist/{mcp/agent-variants → agent-variants}/variantContext.d.ts +0 -0
  535. /package/dist/{mcp/agent-variants → agent-variants}/variantContext.js +0 -0
@@ -1,2536 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.startMCPServer = exports.createRivetMcp = exports.canReuseExistingProjectVisualEditor = exports.closeDetachedVisualEditorServer = exports.getActiveVisualEditorProjectAction = exports.buildVariantAckFailedResponse = exports.buildVariantManualAckBlockedResponse = exports.ackVariantBridgeLease = exports.ackApplyBridgeLease = exports.createVariantRequestLeaseTracker = exports.buildLeasedVariantResponse = exports.buildStaticPreviewRefineResponse = exports.collectStaticPreviewRefineTargetInstructions = exports.buildApplyChangesResponse = exports.buildNoPendingChangesResponse = exports.leaseAvailableBatchForPoll = exports.resolveLeasePickupRequestId = exports.buildVariantRequestBatchResponse = exports.buildMixedVariantBatchResponse = exports.startParentProcessMonitor = exports.buildVisualEditorWatchAction = exports.configureMcpStdioLogging = void 0;
7
- const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
8
- const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
9
- const v3_1 = require("zod/v3");
10
- const index_core_1 = require("../index-core");
11
- const SessionBridgeService_1 = require("../services/SessionBridgeService");
12
- const server_1 = require("../server");
13
- const staticStarter_1 = require("../services/staticStarter");
14
- const TelemetryService_1 = require("../services/TelemetryService");
15
- const AuthService_1 = require("../services/AuthService");
16
- const IntegrationsClient_1 = require("../services/IntegrationsClient");
17
- const tools_1 = require("./auth/tools");
18
- const toolAnalytics_1 = require("./toolAnalytics");
19
- const tools_2 = require("./integrations/tools");
20
- const instructions_1 = require("./instructions");
21
- const ConfigManager_1 = require("../services/ConfigManager");
22
- const MCPOnboardingTelemetry_1 = require("../services/MCPOnboardingTelemetry");
23
- const open_1 = __importDefault(require("open"));
24
- const index_1 = require("../index");
25
- const evaluateFlags_1 = require("../config/evaluateFlags");
26
- const skillWriter_1 = require("../utils/skillWriter");
27
- const portUtils_1 = require("../utils/portUtils");
28
- const harnesses_1 = require("../install/harnesses");
29
- const agent_variants_1 = require("./agent-variants");
30
- const child_process_1 = require("child_process");
31
- const fs_1 = __importDefault(require("fs"));
32
- const path_1 = __importDefault(require("path"));
33
- const util_1 = require("util");
34
- const devServerCommand_1 = require("../utils/devServerCommand");
35
- const StaticPreviewServer_1 = require("../services/StaticPreviewServer");
36
- const staticProjectTarget_1 = require("../services/staticProjectTarget");
37
- const createAgentVariantsOrchestrator_1 = require("../services/createAgentVariantsOrchestrator");
38
- const changeBatchClassification_1 = require("./changeBatchClassification");
39
- const log = (0, index_core_1.createLogger)('MCPServer');
40
- const execAsync = (0, util_1.promisify)(child_process_1.exec);
41
- /**
42
- * @effect Routes stdio MCP diagnostics to stderr before applying the requested log level.
43
- */
44
- const configureMcpStdioLogging = (deps = {
45
- env: process.env,
46
- redirectLogs: index_core_1.redirectLogsToStderr,
47
- setLevel: index_core_1.setLogLevel,
48
- }) => {
49
- deps.redirectLogs();
50
- deps.setLevel(deps.env.RIVET_LOG_LEVEL ?? 'SILENT');
51
- };
52
- exports.configureMcpStdioLogging = configureMcpStdioLogging;
53
- /** Poll until the port responds to HTTP or the timeout is reached. */
54
- async function waitForPort(port, host, timeoutMs = 60_000) {
55
- const deadline = Date.now() + timeoutMs;
56
- while (Date.now() < deadline) {
57
- try {
58
- await fetch(`http://${host}:${port}`, {
59
- signal: AbortSignal.timeout(1000),
60
- });
61
- return true;
62
- }
63
- catch {
64
- await new Promise((r) => setTimeout(r, 500));
65
- }
66
- }
67
- return false;
68
- }
69
- /** Stop a spawned process with graceful SIGTERM and SIGKILL fallback. */
70
- async function stopChildProcess(child, timeoutMs = DEV_SERVER_STOP_TIMEOUT_MS) {
71
- if (child.killed || child.exitCode !== null || child.pid == null) {
72
- return;
73
- }
74
- const waitForExit = new Promise((resolve) => {
75
- const onExit = () => {
76
- child.off('exit', onExit);
77
- child.off('error', onExit);
78
- resolve();
79
- };
80
- child.once('exit', onExit);
81
- child.once('error', onExit);
82
- });
83
- try {
84
- child.kill('SIGTERM');
85
- }
86
- catch {
87
- // no-op; fallback still runs below if process persists
88
- }
89
- const timedOut = await Promise.race([
90
- waitForExit.then(() => false),
91
- new Promise((resolve) => setTimeout(() => resolve(true), timeoutMs)),
92
- ]);
93
- if (!timedOut) {
94
- return;
95
- }
96
- try {
97
- child.kill('SIGKILL');
98
- }
99
- catch {
100
- // best effort
101
- }
102
- await waitForExit;
103
- }
104
- /** Builds the required host-agent action that keeps a visual editor session listening. */
105
- const buildVisualEditorWatchAction = (sessionId, timing = 'immediate') => ({
106
- tool: 'watch_for_changes',
107
- arguments: { sessionId },
108
- required: true,
109
- timing,
110
- loopUntil: 'close_visual_editor',
111
- onTimeout: 'call_again',
112
- afterHandlingChanges: 'call_again',
113
- });
114
- exports.buildVisualEditorWatchAction = buildVisualEditorWatchAction;
115
- const DEV_SERVER_STOP_TIMEOUT_MS = 5000;
116
- const PARENT_PROCESS_MONITOR_INTERVAL_MS = 2000;
117
- const SUPERSEDED_CHANGES_MESSAGE = 'Queued changes were superseded by newer user input. Call watch_for_changes again.';
118
- /**
119
- * Watches for MCP clients disappearing without closing stdio cleanly.
120
- * A parent PID transition to init means the original client process is gone
121
- * while the Express server can still keep the event loop alive.
122
- */
123
- const startParentProcessMonitor = (params) => {
124
- const getParentPid = params.getParentPid ?? (() => process.ppid);
125
- const exitProcess = params.exitProcess ?? ((code) => process.exit(code));
126
- let previousParentPid = getParentPid();
127
- const interval = setInterval(() => {
128
- const currentParentPid = getParentPid();
129
- const isReparentedToInit = previousParentPid !== 1 && currentParentPid === 1;
130
- previousParentPid = currentParentPid;
131
- if (!isReparentedToInit) {
132
- return;
133
- }
134
- clearInterval(interval);
135
- params.onOrphaned().then(() => exitProcess(0), () => exitProcess(1));
136
- }, params.intervalMs ?? PARENT_PROCESS_MONITOR_INTERVAL_MS);
137
- interval.unref();
138
- return () => clearInterval(interval);
139
- };
140
- exports.startParentProcessMonitor = startParentProcessMonitor;
141
- function normalizeFilesystemPath(inputPath) {
142
- if (!inputPath) {
143
- return '';
144
- }
145
- const resolvedPath = path_1.default.resolve(inputPath);
146
- try {
147
- return fs_1.default.realpathSync(resolvedPath);
148
- }
149
- catch {
150
- return resolvedPath;
151
- }
152
- }
153
- function pathsMatchExactly(pathA, pathB) {
154
- if (!pathA || !pathB) {
155
- return false;
156
- }
157
- return pathA === pathB;
158
- }
159
- async function getListeningPortPids(port) {
160
- try {
161
- const { stdout } = await execAsync(`lsof -nP -iTCP:${port} -sTCP:LISTEN -Fp`);
162
- const pids = stdout
163
- .split('\n')
164
- .map((line) => line.trim())
165
- .filter((line) => line.startsWith('p'))
166
- .map((line) => parseInt(line.slice(1), 10))
167
- .filter((pid) => Number.isInteger(pid) && pid > 0);
168
- return [...new Set(pids)];
169
- }
170
- catch {
171
- return [];
172
- }
173
- }
174
- async function getProcessCwd(pid) {
175
- if (!Number.isInteger(pid) || pid <= 0) {
176
- return null;
177
- }
178
- try {
179
- const { stdout } = await execAsync(`lsof -a -p ${pid} -d cwd -Fn`);
180
- const cwdLine = stdout
181
- .split('\n')
182
- .map((line) => line.trim())
183
- .find((line) => line.startsWith('n'));
184
- if (!cwdLine) {
185
- return null;
186
- }
187
- return cwdLine.slice(1) || null;
188
- }
189
- catch {
190
- return null;
191
- }
192
- }
193
- async function isPortOwnedByProject(port, projectPath) {
194
- if (!Number.isInteger(port) || port <= 0 || port >= 65536) {
195
- return {
196
- matches: false,
197
- reason: `Invalid port ${port} for project validation.`,
198
- };
199
- }
200
- const normalizedProjectPath = normalizeFilesystemPath(projectPath);
201
- if (!normalizedProjectPath) {
202
- return {
203
- matches: false,
204
- reason: 'Project path is invalid for strict server validation.',
205
- };
206
- }
207
- const candidatePids = await getListeningPortPids(port);
208
- if (candidatePids.length === 0) {
209
- return {
210
- matches: false,
211
- reason: `No listening process found on localhost:${port}.`,
212
- };
213
- }
214
- const observedProcessPaths = [];
215
- for (const pid of candidatePids) {
216
- const cwd = await getProcessCwd(pid);
217
- const normalizedCwd = normalizeFilesystemPath(cwd);
218
- if (!normalizedCwd) {
219
- observedProcessPaths.push(`${pid}:<unknown_cwd>`);
220
- continue;
221
- }
222
- observedProcessPaths.push(`${pid}:${normalizedCwd}`);
223
- if (pathsMatchExactly(normalizedProjectPath, normalizedCwd)) {
224
- return {
225
- matches: true,
226
- reason: `Process ${pid} is running from ${normalizedCwd}.`,
227
- };
228
- }
229
- }
230
- return {
231
- matches: false,
232
- reason: `A server is listening on localhost:${port}, but process paths do not match the selected project path. selected=${normalizedProjectPath}; observed=${observedProcessPaths.join(', ') || '<none>'}`,
233
- selectedProjectPath: normalizedProjectPath,
234
- observedProcessPaths,
235
- };
236
- }
237
- function detectFrameworkFromHtml(html) {
238
- const lower = html.toLowerCase();
239
- const hasViteClient = lower.includes('/@vite/client');
240
- const hasViteMarkers = lower.includes('vite') &&
241
- (lower.includes('hmr') || lower.includes('/src/main.'));
242
- if (hasViteClient || hasViteMarkers) {
243
- return 'vite';
244
- }
245
- const hasNextAssets = lower.includes('/_next/');
246
- const hasNextMarkers = lower.includes('__next_data__') || lower.includes('next-route-announcer');
247
- if (hasNextAssets || hasNextMarkers) {
248
- return 'nextjs';
249
- }
250
- return 'unknown';
251
- }
252
- function normalizeExpectedFramework(framework) {
253
- if (!framework)
254
- return 'unknown';
255
- const lower = framework.toLowerCase();
256
- if (lower.includes('vite'))
257
- return 'vite';
258
- if (lower.includes('next'))
259
- return 'nextjs';
260
- return 'unknown';
261
- }
262
- function isFrameworkCompatible(expected, detected) {
263
- if (expected === 'unknown')
264
- return true;
265
- return expected === detected;
266
- }
267
- async function validateProjectServer(options) {
268
- const { host, port, framework, projectPath } = options;
269
- const signals = [];
270
- const isReachable = await waitForPort(port, host, 3000);
271
- if (!isReachable) {
272
- return {
273
- valid: false,
274
- reason: `No server is responding on ${host}:${port}.`,
275
- detectedFramework: 'unknown',
276
- signals,
277
- };
278
- }
279
- signals.push('tcp_reachable');
280
- if (projectPath) {
281
- const projectMatch = await isPortOwnedByProject(port, projectPath);
282
- if (!projectMatch.matches) {
283
- return {
284
- valid: false,
285
- reason: projectMatch.reason,
286
- detectedFramework: 'unknown',
287
- signals,
288
- selectedProjectPath: projectMatch.selectedProjectPath,
289
- observedProcessPaths: projectMatch.observedProcessPaths,
290
- };
291
- }
292
- signals.push('path_match');
293
- }
294
- try {
295
- const response = await fetch(`http://${host}:${port}`, {
296
- signal: AbortSignal.timeout(3000),
297
- });
298
- const html = await response.text();
299
- const detectedFramework = detectFrameworkFromHtml(html);
300
- if (detectedFramework === 'vite') {
301
- signals.push('vite_marker');
302
- }
303
- if (detectedFramework === 'nextjs') {
304
- signals.push('next_marker');
305
- }
306
- const expected = normalizeExpectedFramework(framework);
307
- if (!isFrameworkCompatible(expected, detectedFramework)) {
308
- return {
309
- valid: false,
310
- reason: `Expected ${expected}, but detected ${detectedFramework} on ${host}:${port}.`,
311
- detectedFramework,
312
- signals,
313
- statusCode: response.status,
314
- };
315
- }
316
- if (detectedFramework === 'unknown' && expected !== 'unknown') {
317
- return {
318
- valid: false,
319
- reason: 'Server is reachable but did not expose framework markers needed for validation.',
320
- detectedFramework,
321
- signals,
322
- statusCode: response.status,
323
- };
324
- }
325
- return {
326
- valid: true,
327
- reason: `Validated ${host}:${port} as a project server.`,
328
- detectedFramework,
329
- signals,
330
- statusCode: response.status,
331
- };
332
- }
333
- catch (error) {
334
- const message = error instanceof Error ? error.message : 'Unknown error';
335
- return {
336
- valid: false,
337
- reason: `HTTP validation failed: ${message}`,
338
- detectedFramework: 'unknown',
339
- signals,
340
- };
341
- }
342
- }
343
- /**
344
- * Starts the Rivet MCP server on stdio transport.
345
- * Called when the user runs `rivet mcp`.
346
- */
347
- function getSendButtonLabel(mcpEditor) {
348
- return (0, harnesses_1.sendButtonLabelFor)(mcpEditor);
349
- }
350
- function trackChangesQueuedFromIntent(intent, bridge, telemetry, sessionId) {
351
- const telemetryContext = bridge.getTelemetryContext();
352
- telemetry.trackChangesQueued({
353
- sessionId,
354
- styleChangeCount: intent.changes.filter((c) => c.kind === 'style').length,
355
- textChangeCount: intent.changes.filter((c) => c.kind === 'text').length,
356
- commentCount: intent.changes.filter((c) => c.kind === 'comment').length,
357
- chatCount: intent.changes.filter((c) => c.kind === 'chat').length,
358
- round: bridge.getRoundsCompleted(),
359
- traceId: intent.traceId,
360
- agentRunId: intent.agentRunId,
361
- browserSessionId: telemetryContext?.browserSessionId,
362
- browserDistinctId: telemetryContext?.browserDistinctId,
363
- });
364
- }
365
- const trackOrphanedMCPRun = (telemetry, sessionId, orphanedRun) => {
366
- telemetry.trackAgentRunFailed({
367
- traceId: orphanedRun.traceId,
368
- agentRunId: orphanedRun.agentRunId,
369
- sessionId,
370
- posthogSessionId: orphanedRun.browserSessionId,
371
- durationMs: Date.now() - orphanedRun.startedAt,
372
- errorType: 'mcp_run_replaced',
373
- errorMessage: 'A new MCP apply run started before the previous run completed',
374
- source: 'mcp',
375
- });
376
- };
377
- const APPLY_GUIDE = 'Read each file in sourceFiles, then apply every change in the changes array. ' +
378
- 'style: find the element by className/xpath and update the CSS property from "from" to "to". ' +
379
- 'text: find the element and replace the text content. ' +
380
- 'comment: interpret the instruction using the element context and apply appropriate code changes. ' +
381
- 'chat: interpret the free-text instruction in the "instruction" field and apply appropriate code changes across any file in the project. ' +
382
- 'variant: a user-picked variant from the agent-variants flow. ' +
383
- 'Variant protocol reminder: source_research (if present) -> report_source_context -> brief approval -> generation -> user commit. ' +
384
- "variant.payload.kind=diff-applied: the server has already `git apply`d the unified diff to the user's working tree (uncommitted). DO NOT re-apply — acknowledge the variant and let the user review. " +
385
- 'variant.payload.kind=diff (legacy): apply the unified diff in payload.diff against the project root via `git apply` (no manual edits). ' +
386
- 'variant.payload.kind=project-created: the project has already been materialized at destinationPath and git-committed by the server. Optionally run `npm install` in destinationPath and continue from there — no further file operations are required for the pick itself. ' +
387
- 'For elements with empty filePaths, search the codebase by className or xpath. ' +
388
- 'If a change has imageCount > 0, the corresponding reference images appear as image content items immediately following this text in the tool result — view them before making changes. ' +
389
- "IMPORTANT — variantRequest branch: if a comment item carries `variantRequest: { count }`, do NOT edit any files. Instead, derive `target` from the comment's `element` using this priority: " +
390
- '(a) prefer `{ type: "file", ref: filePaths[0] }` when `filePaths.length === 1`; otherwise ' +
391
- '(b) `rivetId` → `{ type: "element", ref: \'[data-rivet-id="<rivetId>"]\' }`, then ' +
392
- '(c) `id` (no whitespace) → `{ type: "element", ref: "#<id>" }`, then ' +
393
- '(d) `xpath` → `{ type: "element", ref: "<xpath>" }`, then ' +
394
- '(e) single-token `className` → `{ type: "element", ref: ".<token>" }`. ' +
395
- 'If the comment has imageCount > 0, inspect the following image content items before calling `start_variants`; pass each image through `userContext` as `{ transport: "upload", dataUrl, label: "Reference image N", userIntent: "Inline variant visual reference" }` when your client exposes a data URL, or pass concise visual observations as `{ transport: "text", content, label: "Reference image observations", userIntent: "Inline variant visual reference" }` when it does not. ' +
396
- 'Then call `start_variants` with `{ mode: "existing", prompt: instruction, count: variantRequest.count, target, userContext }`. `start_variants` is the single kickoff call. `start_variants` returns leased work items; generate each variant in its worktree, call `report_work_item({ kind: "status", ... })` for each leased item, then wait for the user to pick and call `commit_variant`. ' +
397
- 'CRITICAL WORKFLOW: 1. Call get_pending_changes with a message sharing your brief plan. 2. Edit the files. 3. Call get_pending_changes with a message summarizing what you changed and refresh_git: true so the Rivet UI updates. 4. STOP and let the user know their changes have been applied.';
398
- /**
399
- * Prefix added to the apply guide when a batch targets a specific variant. The
400
- * variant's sources live in an isolated worktree, so all edits must happen
401
- * there (not the base project) for the change to land on the right variant.
402
- * The reference suffix is appended when any comment in the batch carries
403
- * `resolvedReferences` so the agent knows to consult those directories as
404
- * read-only context.
405
- */
406
- const buildApplyGuide = (worktreePath, hasReferences) => {
407
- const targetPrefix = worktreePath
408
- ? `These changes target a specific variant whose sources live in an isolated git worktree at \`${worktreePath}\`. Treat that worktree as the project root: read and edit files relative to it, and run git/refresh commands inside it. Do NOT edit the base project for this batch. `
409
- : '';
410
- const referenceSuffix = hasReferences
411
- ? ' Some comments include `resolvedReferences` — each entry is a label, optional brief, and a directory path pointing to another variant\'s source. The user attached these as @-mention context; treat them as read-only and inspect with your Read/Grep tools as needed (do NOT edit files inside those paths). Use them to ground "make this more like X" style requests.'
412
- : '';
413
- return `${targetPrefix}${APPLY_GUIDE}${referenceSuffix}`;
414
- };
415
- const requestHasResolvedReferences = (request) => request.changes.some((c) => c.kind === 'comment' &&
416
- Array.isArray(c.resolvedReferences) &&
417
- c.resolvedReferences.length > 0);
418
- const VARIANT_REQUEST_GUIDE = 'This batch is a Rivet variant request — do NOT edit files directly. ' +
419
- 'The pending change is a single CommentChangeItem; `variantRequest.count` is only the editor\'s default placeholder count — YOU own the real number. Decide how many directions to generate from the user\'s `instruction` text — honor an explicit number ("give me 5"), a relative ask ("more options"), or a qualitative cue ("lots of variety"); default to 3 only when the instruction gives no count signal. ' +
420
- "Derive the structured `start_variants.target` from the comment's `element` using this priority: " +
421
- '(a) `{ type: "file", ref: filePaths[0] }` when `filePaths.length === 1`; otherwise ' +
422
- '(b) `{ type: "element", ref: `[data-rivet-id="${rivetId}"]` }` when `rivetId` is present, then ' +
423
- '(c) `{ type: "element", ref: `#${id}` }` when `id` has no whitespace, then ' +
424
- '(d) `{ type: "element", ref: xpath }`, then ' +
425
- '(e) `{ type: "element", ref: `.${token}` }` for a single-token `className`. ' +
426
- "When you set a `target` from the comment's `element`, scope each variant's change to THAT element and its immediate section/card — do not restyle the rest of the page. " +
427
- 'If the comment has imageCount > 0, inspect the following image content items before calling `start_variants`; pass each image through `userContext` as `{ transport: "upload", dataUrl, label: "Reference image N", userIntent: "Inline variant visual reference" }` when your client exposes a data URL, or pass concise visual observations as `{ transport: "text", content, label: "Reference image observations", userIntent: "Inline variant visual reference" }` when it does not. ' +
428
- 'Then call `start_variants` with `{ mode: "existing", prompt: instruction, projectPath, target, userContext, requestId, briefs }`, passing this batch\'s `projectPath` and `requestId` through verbatim so the run stays attached to the visual editor project and the editor can map its loading placeholders to the new cards. Set the count by supplying `briefs` (one distinct `{ label, body }` per variant — `briefs.length` is the count, and each card is named by what it changes); the editor seeds a few placeholders and reconciles to however many you actually generate. `start_variants` is the single kickoff call. ' +
429
- 'ACCUMULATION: when a variants session is already live for this project, calling `start_variants` again does NOT discard it — Rivet APPENDS the new directions to that same active session (they show up alongside the existing ones), so just call `start_variants` per Vary; never try to reuse a prior sessionId or worry about orphaning earlier variants. ' +
430
- '`start_variants` returns leased work items; generate each variant in its worktree and call `report_work_item({ kind: "status", ... })` for each leased item. ' +
431
- 'After the user picks a variant via `commit_variant`, STOP and tell the user their variant has been committed.';
432
- const VARIANT_MIXED_BATCH_GUIDE = 'Mixed batch rejected: a `variantRequest` comment must be the only item in its batch. ' +
433
- 'Ask the user to send the variant request on its own (clear other pending changes first) and retry.';
434
- // Shared preamble for both static-preview refine guides (fork vs in-place).
435
- // Keeps the STATIC_PREVIEW_REFINE_GUIDE marker, the lease call, and the
436
- // targetElement scoping rule consistent across both flows.
437
- const STATIC_PREVIEW_REFINE_GUIDE_PREAMBLE = 'STATIC_PREVIEW_REFINE_GUIDE: These changes target static_preview variant(s). ' +
438
- 'Do NOT edit base project files. ' +
439
- "Lease ONLY this batch's work items: call `continue_variants({ action: 'request_work', workItemIds: <the response.workItemIds array> })`. " +
440
- "Passing workItemIds (rather than a bare requestedLeaseCount) keeps concurrent runs from leasing each other's work, so several batches can be handled in parallel. " +
441
- 'When input.targetElement is present it is the specific element the user selected (match it in the HTML by tagName/className/id/textContent): ' +
442
- 'scope the change to THAT element and its immediate section/card — do not restyle the rest of the page. ';
443
- // "Vary" flow: the server forked N NEW sibling variants; the original is left
444
- // untouched and each fork gets a synthesized parent+change name.
445
- const STATIC_PREVIEW_FORK_GUIDE = STATIC_PREVIEW_REFINE_GUIDE_PREAMBLE +
446
- 'Do NOT overwrite the original variant. ' +
447
- 'The server has already created the requested number of NEW sibling variants per target — each is a fresh ' +
448
- 'static_preview work item seeded from the source variant (input.currentHtml) plus your instruction (input.instruction). ' +
449
- 'For each leased static_preview work item, generate a NEW variation of input.currentHtml that applies input.instruction. ' +
450
- 'Then call `report_work_item` (kind: "status") with output.html containing the full updated static HTML, ' +
451
- 'AND a synthesized `title` (≤ 6 words) + one-sentence `description` that combine the parent variant ' +
452
- '(input.sourceLabel / input.sourceBody) with the change (input.instruction) — e.g. "Icon grid — red hover". ' +
453
- 'Each leased item becomes its own new variant card; the original variant is left unchanged.';
454
- // "Apply" flow: the server created a single in-place refinement work item that
455
- // UPDATES THE SAME variant — it is NOT a new sibling, so the variant keeps its
456
- // existing name and no synthesized title/description is reported.
457
- const STATIC_PREVIEW_INPLACE_REFINE_GUIDE = STATIC_PREVIEW_REFINE_GUIDE_PREAMBLE +
458
- 'The server has created an IN-PLACE refinement work item that UPDATES THE SAME variant per target — ' +
459
- 'it is NOT a new sibling. Lease it, then edit input.currentHtml to apply input.instruction, ' +
460
- 'preserving everything else. ' +
461
- 'Then call `report_work_item` (kind: "status") with output.html containing the full updated static HTML. ' +
462
- 'Do NOT pass a new `title`/`description` — the variant keeps its existing name. ' +
463
- 'The edited HTML replaces the existing variant in place.';
464
- /**
465
- * Pick the guide that matches what the server actually did with this batch:
466
- * a "Vary" target carries a `count` (the server forked N siblings), while a
467
- * plain "Apply" has no count (the server created one in-place refinement that
468
- * overwrites the source). A mixed batch falls back to the fork guide, whose
469
- * lease+report steps still cover the count-bearing targets.
470
- */
471
- const selectStaticPreviewRefineGuide = (targetInstructions) => targetInstructions.some(({ count }) => count != null)
472
- ? STATIC_PREVIEW_FORK_GUIDE
473
- : STATIC_PREVIEW_INPLACE_REFINE_GUIDE;
474
- const buildLeasePayloadFields = (lease) => ({
475
- leaseId: lease.leaseId,
476
- requestId: lease.requestId,
477
- kind: lease.kind,
478
- mergedRequestIds: lease.mergedRequestIds,
479
- mergedFrom: lease.mergedFrom,
480
- });
481
- const getActiveLeaseMetadata = (bridge) => bridge.getActiveLeaseMetadata();
482
- const buildSupersededChangesResponse = () => ({
483
- content: [
484
- {
485
- type: 'text',
486
- text: JSON.stringify({
487
- hasChanges: false,
488
- superseded: true,
489
- message: SUPERSEDED_CHANGES_MESSAGE,
490
- }),
491
- },
492
- ],
493
- });
494
- const buildMixedVariantBatchResponse = (rejectedRequest) => {
495
- if (rejectedRequest === undefined) {
496
- return buildSupersededChangesResponse();
497
- }
498
- return {
499
- content: [
500
- {
501
- type: 'text',
502
- text: JSON.stringify({
503
- hasChanges: false,
504
- rejected: true,
505
- reason: 'mixed_variant_batch',
506
- message: VARIANT_MIXED_BATCH_GUIDE,
507
- }),
508
- },
509
- ],
510
- isError: true,
511
- };
512
- };
513
- exports.buildMixedVariantBatchResponse = buildMixedVariantBatchResponse;
514
- const buildVariantRequestBatchResponse = (intent, lease, projectPath) => {
515
- if (!intent) {
516
- return buildSupersededChangesResponse();
517
- }
518
- const { changes, imageContent } = (0, changeBatchClassification_1.extractImages)(intent.changes);
519
- const requestId = intent.agentRunId ?? intent.traceId;
520
- return {
521
- content: [
522
- {
523
- type: 'text',
524
- text: JSON.stringify({
525
- hasChanges: true,
526
- kind: 'variant_request',
527
- changes,
528
- sourceFiles: intent.sourceFiles,
529
- ...(projectPath ? { projectPath } : {}),
530
- ...(requestId ? { requestId } : {}),
531
- ...(intent.target ? { target: intent.target } : {}),
532
- ...(lease ? buildLeasePayloadFields(lease) : {}),
533
- applyGuide: VARIANT_REQUEST_GUIDE,
534
- }),
535
- },
536
- ...imageContent,
537
- ],
538
- };
539
- };
540
- exports.buildVariantRequestBatchResponse = buildVariantRequestBatchResponse;
541
- /**
542
- * Resolves whether `watch_for_changes` / `get_pending_changes` should pass
543
- * `requestId` into lease pickup. requestId is only for idempotent re-poll of
544
- * the same in-flight lease — omit it after ack so FIFO can pick the next batch.
545
- */
546
- const resolveLeasePickupRequestId = (args) => args.refresh_git || args.ack_batch ? undefined : args.requestId;
547
- exports.resolveLeasePickupRequestId = resolveLeasePickupRequestId;
548
- /**
549
- * Lease work for a poll. A matching requestId keeps idempotent re-polls on the
550
- * same in-flight batch; a stale requestId must not strand unrelated queued work.
551
- */
552
- const leaseAvailableBatchForPoll = (args) => {
553
- if (args.requestId) {
554
- const requestedLease = args.bridge.leaseBatch(args.requestId);
555
- if (requestedLease) {
556
- return requestedLease;
557
- }
558
- }
559
- return args.bridge.leaseBatch();
560
- };
561
- exports.leaseAvailableBatchForPoll = leaseAvailableBatchForPoll;
562
- /**
563
- * Builds a poll response for an empty apply queue.
564
- */
565
- const buildNoPendingChangesResponse = (params = {}) => ({
566
- content: [
567
- {
568
- type: 'text',
569
- text: JSON.stringify({
570
- hasChanges: false,
571
- ...(params.message ? { message: params.message } : {}),
572
- ...(params.devServerHealth !== undefined
573
- ? { devServerHealth: params.devServerHealth }
574
- : {}),
575
- ...(params.pendingVariantRequests !== undefined
576
- ? { pendingVariantRequests: params.pendingVariantRequests }
577
- : {}),
578
- ...(params.activeLeases !== undefined
579
- ? { activeLeases: params.activeLeases }
580
- : {}),
581
- }),
582
- },
583
- ],
584
- });
585
- exports.buildNoPendingChangesResponse = buildNoPendingChangesResponse;
586
- /**
587
- * Builds a poll response for a normal apply batch.
588
- */
589
- const buildApplyChangesResponse = (params) => {
590
- const { changes, imageContent } = (0, changeBatchClassification_1.extractImages)(params.intent.changes);
591
- return {
592
- content: [
593
- {
594
- type: 'text',
595
- text: JSON.stringify({
596
- hasChanges: true,
597
- kind: 'apply',
598
- changes,
599
- sourceFiles: params.intent.sourceFiles,
600
- ...(params.intent.target ? { target: params.intent.target } : {}),
601
- ...(params.intent.worktreePath
602
- ? { worktreePath: params.intent.worktreePath }
603
- : {}),
604
- ...(params.lease ? buildLeasePayloadFields(params.lease) : {}),
605
- applyGuide: buildApplyGuide(params.intent.worktreePath, requestHasResolvedReferences(params.intent)),
606
- ...(params.pendingVariantRequests !== undefined
607
- ? { pendingVariantRequests: params.pendingVariantRequests }
608
- : {}),
609
- }),
610
- },
611
- ...imageContent,
612
- ],
613
- };
614
- };
615
- exports.buildApplyChangesResponse = buildApplyChangesResponse;
616
- const staticPreviewTargetKey = (target) => `${target.sessionId}:${target.variantId}`;
617
- const getCommentStaticPreviewTarget = (comment, fallbackTarget) => comment.staticPreviewTarget ?? fallbackTarget;
618
- const collectStaticPreviewRefineTargetInstructions = (request) => {
619
- const groups = new Map();
620
- // Per-request correlation id: each Vary/Apply is dispatched as its own batch,
621
- // so the batch's agentRunId (or traceId) uniquely identifies this UI request.
622
- const requestId = request.agentRunId ?? request.traceId;
623
- for (const change of request.changes) {
624
- if (change.kind !== 'comment')
625
- continue;
626
- const target = getCommentStaticPreviewTarget(change, request.staticPreviewTarget);
627
- if (!target)
628
- continue;
629
- const key = staticPreviewTargetKey(target);
630
- const count = change.variantRequest?.count;
631
- const existing = groups.get(key);
632
- if (existing) {
633
- existing.instruction = `${existing.instruction}\n\n${change.instruction}`;
634
- if (count != null) {
635
- existing.count = Math.max(existing.count ?? 0, count);
636
- }
637
- existing.element = existing.element ?? change.element;
638
- }
639
- else {
640
- groups.set(key, {
641
- target,
642
- instruction: change.instruction,
643
- ...(count != null ? { count } : {}),
644
- element: change.element,
645
- ...(requestId ? { requestId } : {}),
646
- });
647
- }
648
- }
649
- return [...groups.values()];
650
- };
651
- exports.collectStaticPreviewRefineTargetInstructions = collectStaticPreviewRefineTargetInstructions;
652
- const buildStaticPreviewRefineResponse = (consumedRequest, droppedRefinements = [], createdWorkItemIds = [], lease) => {
653
- if (!consumedRequest) {
654
- return buildSupersededChangesResponse();
655
- }
656
- const { changes, imageContent } = (0, changeBatchClassification_1.extractImages)(consumedRequest.changes);
657
- const targetInstructions = (0, exports.collectStaticPreviewRefineTargetInstructions)(consumedRequest);
658
- const staticPreviewTargets = targetInstructions.map(({ target }) => target);
659
- return {
660
- content: [
661
- {
662
- type: 'text',
663
- text: JSON.stringify({
664
- hasChanges: true,
665
- kind: 'static_preview_refine',
666
- changes,
667
- sourceFiles: consumedRequest.sourceFiles,
668
- staticPreviewTarget: consumedRequest.staticPreviewTarget,
669
- staticPreviewTargets,
670
- ...(lease ? buildLeasePayloadFields(lease) : {}),
671
- applyGuide: selectStaticPreviewRefineGuide(targetInstructions),
672
- // The exact work items THIS batch created. Lease ONLY these (pass them
673
- // as `workItemIds` to request_work) so concurrent runs each pick up
674
- // their own batch instead of one greedily leasing everything.
675
- ...(createdWorkItemIds.length > 0
676
- ? { workItemIds: createdWorkItemIds }
677
- : {}),
678
- // In-place refinements the server could not apply (the variant was
679
- // already sent / no longer editable). Surfaced structurally so the
680
- // agent knows the instruction was NOT applied, instead of the poll
681
- // throwing and losing the already-consumed batch.
682
- ...(droppedRefinements.length > 0
683
- ? { rejectedRefinements: droppedRefinements }
684
- : {}),
685
- }),
686
- },
687
- ...imageContent,
688
- ],
689
- };
690
- };
691
- exports.buildStaticPreviewRefineResponse = buildStaticPreviewRefineResponse;
692
- /**
693
- * Build the MCP response for a leased variant batch.
694
- */
695
- const buildLeasedVariantResponse = (params) => {
696
- const intent = params.lease.intent;
697
- if (params.isFreshStaticPreviewVariantRequestBatch(intent)) {
698
- return params.withPendingVariantRequests(params.handleStaticPreviewRefineBatch(intent, params.lease));
699
- }
700
- if ((0, changeBatchClassification_1.isSingleVariantRequestBatch)(intent)) {
701
- return params.withPendingVariantRequests((0, exports.buildVariantRequestBatchResponse)(intent, params.lease, params.projectPath));
702
- }
703
- if ((0, changeBatchClassification_1.isStaticPreviewRefineBatch)(intent)) {
704
- return params.withPendingVariantRequests(params.handleStaticPreviewRefineBatch(intent, params.lease));
705
- }
706
- return params.withPendingVariantRequests((0, exports.buildVariantRequestBatchResponse)(intent, params.lease, params.projectPath));
707
- };
708
- exports.buildLeasedVariantResponse = buildLeasedVariantResponse;
709
- const FINISHED_VARIANT_REPORT_STATUSES = new Set([
710
- 'succeeded',
711
- 'failed',
712
- 'cancelled',
713
- ]);
714
- const variantRequestWorkKey = (sessionId, workItemId) => `${sessionId}:${workItemId}`;
715
- /**
716
- * Tracks a UI-originated variant request lease across the variant work items
717
- * that satisfy it, releasing the bridge lease once the whole batch has
718
- * finished.
719
- */
720
- const createVariantRequestLeaseTracker = (params) => {
721
- const trackedByWorkItem = new Map();
722
- const trackedByRequest = new Map();
723
- const finishedVariantWorkByRequest = new Map();
724
- const markGeneratedWorkFinished = (lease) => {
725
- finishedVariantWorkByRequest.set(lease.requestId, lease.leaseId);
726
- };
727
- const hasFinishedGeneratedWork = (lease) => finishedVariantWorkByRequest.get(lease.requestId) === lease.leaseId;
728
- const findTrackedLease = (args) => {
729
- if (args.requestId) {
730
- const tracked = trackedByRequest.get(args.requestId);
731
- if (tracked) {
732
- return tracked;
733
- }
734
- }
735
- if (args.leaseId) {
736
- for (const tracked of trackedByRequest.values()) {
737
- if (tracked.leaseId === args.leaseId) {
738
- return tracked;
739
- }
740
- }
741
- }
742
- return null;
743
- };
744
- const findActiveVariantLease = (args) => {
745
- const activeLeases = params.activeVariantLeases?.() ?? [];
746
- return (activeLeases.find((lease) => {
747
- if (lease.kind !== 'variant') {
748
- return false;
749
- }
750
- if (args.leaseId && lease.leaseId === args.leaseId) {
751
- return true;
752
- }
753
- return Boolean(args.requestId && lease.requestId === args.requestId);
754
- }) ?? null);
755
- };
756
- return {
757
- claim(args) {
758
- if (!args.requestId) {
759
- return;
760
- }
761
- const lease = params.leaseRequest(args.requestId);
762
- if (!lease || lease.kind !== 'variant') {
763
- return;
764
- }
765
- if (args.workItemIds.length === 0) {
766
- if (trackedByRequest.has(lease.requestId)) {
767
- return;
768
- }
769
- markGeneratedWorkFinished(lease);
770
- params.ackRequest({
771
- leaseId: lease.leaseId,
772
- requestId: lease.requestId,
773
- });
774
- return;
775
- }
776
- finishedVariantWorkByRequest.delete(lease.requestId);
777
- const tracked = trackedByRequest.get(lease.requestId) ??
778
- {
779
- leaseId: lease.leaseId,
780
- requestId: lease.requestId,
781
- remainingWorkItemIds: new Set(),
782
- };
783
- trackedByRequest.set(lease.requestId, tracked);
784
- for (const workItemId of args.workItemIds) {
785
- tracked.remainingWorkItemIds.add(workItemId);
786
- trackedByWorkItem.set(variantRequestWorkKey(args.sessionId, workItemId), tracked);
787
- }
788
- },
789
- complete(args) {
790
- if (args.requeuedForRegeneration ||
791
- (!args.aborted && !FINISHED_VARIANT_REPORT_STATUSES.has(args.status))) {
792
- return;
793
- }
794
- const key = variantRequestWorkKey(args.sessionId, args.workItemId);
795
- const tracked = trackedByWorkItem.get(key);
796
- if (!tracked) {
797
- return;
798
- }
799
- tracked.remainingWorkItemIds.delete(args.workItemId);
800
- trackedByWorkItem.delete(key);
801
- if (tracked.remainingWorkItemIds.size > 0) {
802
- return;
803
- }
804
- trackedByRequest.delete(tracked.requestId);
805
- markGeneratedWorkFinished(tracked);
806
- params.ackRequest({
807
- leaseId: tracked.leaseId,
808
- requestId: tracked.requestId,
809
- });
810
- },
811
- getManualAckBlock(args) {
812
- const tracked = findTrackedLease(args);
813
- if (!tracked || tracked.remainingWorkItemIds.size === 0) {
814
- const activeLease = findActiveVariantLease(args);
815
- if (!activeLease) {
816
- return null;
817
- }
818
- if (hasFinishedGeneratedWork(activeLease)) {
819
- return null;
820
- }
821
- return {
822
- reason: 'variant_work_unclaimed',
823
- leaseId: activeLease.leaseId,
824
- requestId: activeLease.requestId,
825
- remainingWorkItemIds: [],
826
- };
827
- }
828
- return {
829
- reason: 'variant_work_unfinished',
830
- leaseId: tracked.leaseId,
831
- requestId: tracked.requestId,
832
- remainingWorkItemIds: [...tracked.remainingWorkItemIds],
833
- };
834
- },
835
- };
836
- };
837
- exports.createVariantRequestLeaseTracker = createVariantRequestLeaseTracker;
838
- const ackApplyBridgeLease = (args) => {
839
- const ackResult = args.bridge.ackBatch({
840
- leaseId: args.leaseId,
841
- requestId: args.requestId,
842
- });
843
- if (ackResult.kind === 'apply' && ackResult.mergedRequestIds.length > 0) {
844
- args.telemetry.trackMCPChangeAcked({
845
- channel: 'apply',
846
- leaseId: args.leaseId,
847
- requestId: args.requestId ?? ackResult.mergedRequestIds[0],
848
- changeCount: ackResult.mergedRequestIds.length,
849
- sessionId: args.sessionId,
850
- });
851
- return;
852
- }
853
- args.telemetry.trackMCPChangeAckFailed({
854
- channel: 'apply',
855
- leaseId: args.leaseId,
856
- requestId: args.requestId,
857
- changeCount: 0,
858
- sessionId: args.sessionId,
859
- failureReason: 'ack_failed',
860
- });
861
- };
862
- exports.ackApplyBridgeLease = ackApplyBridgeLease;
863
- const ackVariantBridgeLease = (args) => {
864
- if (!args.leaseId) {
865
- return {
866
- ok: false,
867
- reason: 'missing_lease_id',
868
- requestId: args.requestId,
869
- };
870
- }
871
- const ackResult = args.bridge.ackBatch({
872
- leaseId: args.leaseId,
873
- requestId: args.requestId,
874
- });
875
- const sessionId = args.bridge.getSessionId() ?? undefined;
876
- if (ackResult.kind === 'variant' && ackResult.mergedRequestIds.length > 0) {
877
- args.telemetry.trackMCPChangeAcked({
878
- channel: 'variant',
879
- leaseId: args.leaseId,
880
- requestId: args.requestId ?? ackResult.mergedRequestIds[0],
881
- changeCount: ackResult.mergedRequestIds.length,
882
- sessionId,
883
- });
884
- return {
885
- ok: true,
886
- leaseId: args.leaseId,
887
- requestId: args.requestId ?? ackResult.mergedRequestIds[0],
888
- changeCount: ackResult.mergedRequestIds.length,
889
- };
890
- }
891
- args.telemetry.trackMCPChangeAckFailed({
892
- channel: 'variant',
893
- leaseId: args.leaseId,
894
- requestId: args.requestId,
895
- changeCount: 0,
896
- sessionId,
897
- failureReason: 'ack_failed',
898
- });
899
- return {
900
- ok: false,
901
- reason: 'ack_failed',
902
- leaseId: args.leaseId,
903
- requestId: args.requestId,
904
- };
905
- };
906
- exports.ackVariantBridgeLease = ackVariantBridgeLease;
907
- /** Builds the MCP error returned when generated variant work still owns a lease. */
908
- const buildVariantManualAckBlockedResponse = (block) => ({
909
- content: [
910
- {
911
- type: 'text',
912
- text: JSON.stringify({
913
- error: 'variant_ack_blocked',
914
- reason: block.reason,
915
- message: block.reason === 'variant_work_unfinished'
916
- ? 'This variant request still has unfinished generated work. Finish it with report_work_item; do not use ack_batch to skip or finish generated variant work.'
917
- : 'This UI variant request has an active lease, but generated work has not claimed it yet. Process it through start_variants or scoped continue_variants({ action: "request_work", workItemIds }) and finish with report_work_item; do not use ack_batch to skip variant work.',
918
- leaseId: block.leaseId,
919
- requestId: block.requestId,
920
- remainingWorkItemIds: block.remainingWorkItemIds,
921
- }),
922
- },
923
- ],
924
- isError: true,
925
- });
926
- exports.buildVariantManualAckBlockedResponse = buildVariantManualAckBlockedResponse;
927
- /** Builds the MCP error returned when ack_batch does not match an active variant lease. */
928
- const buildVariantAckFailedResponse = (result) => ({
929
- content: [
930
- {
931
- type: 'text',
932
- text: JSON.stringify({
933
- error: 'variant_ack_failed',
934
- reason: result.reason,
935
- message: result.reason === 'missing_lease_id'
936
- ? 'ack_batch requires the leaseId from the variant batch.'
937
- : 'No active variant lease matched this ack_batch request.',
938
- ...(result.leaseId ? { leaseId: result.leaseId } : {}),
939
- ...(result.requestId ? { requestId: result.requestId } : {}),
940
- }),
941
- },
942
- ],
943
- isError: true,
944
- });
945
- exports.buildVariantAckFailedResponse = buildVariantAckFailedResponse;
946
- /**
947
- * Chooses how an active visual editor session should handle a project change.
948
- */
949
- const getActiveVisualEditorProjectAction = (args) => {
950
- if (args.activeProjectPath === args.requestedProjectPath) {
951
- return 'attach';
952
- }
953
- if (args.devServerOwnership === 'none') {
954
- return 'rebind';
955
- }
956
- return 'restart';
957
- };
958
- exports.getActiveVisualEditorProjectAction = getActiveVisualEditorProjectAction;
959
- /**
960
- * Closes a Rivet server handle that is no longer attached to a bridge session.
961
- */
962
- const closeDetachedVisualEditorServer = async (args) => {
963
- if (!args.serverHandle || args.isBridgeActive) {
964
- return false;
965
- }
966
- await args.cleanup();
967
- return true;
968
- };
969
- exports.closeDetachedVisualEditorServer = closeDetachedVisualEditorServer;
970
- /**
971
- * Existing-project variants need a mounted upstream for the Original preview.
972
- */
973
- const canReuseExistingProjectVisualEditor = (args) => Boolean(args.attachment && args.devServerOwnership !== 'none');
974
- exports.canReuseExistingProjectVisualEditor = canReuseExistingProjectVisualEditor;
975
- /**
976
- * Builds the fully configured Rivet MCP server (tools, auth gating, session
977
- * bridge, telemetry) without binding it to a transport. Shared by the stdio
978
- * and streamable-HTTP entry points. By default it registers process-level
979
- * signal handlers for stdio cleanup; HTTP hosts own shutdown externally.
980
- */
981
- const createRivetMcp = (options) => {
982
- const { mcpEditor, revokeBearerToken } = options ?? {};
983
- const shouldRegisterProcessHandlers = options?.registerProcessHandlers ?? true;
984
- const sendButtonLabel = getSendButtonLabel(mcpEditor);
985
- const bridge = new SessionBridgeService_1.SessionBridgeService();
986
- const telemetry = new TelemetryService_1.TelemetryService({
987
- enabled: true,
988
- source: 'mcp',
989
- editor: mcpEditor,
990
- });
991
- const authService = new AuthService_1.AuthService();
992
- const integrationsClient = new IntegrationsClient_1.IntegrationsClient({ authService });
993
- const requireAuth = (0, tools_1.createAuthGuard)(authService);
994
- // Agent-driven variants orchestrator. Long-lived singleton: state machine +
995
- // worktree lifecycle for any variants session, surfaced via the 5 MCP tools
996
- // (registered below near mcp.connect) and the /api/variants Express routes
997
- // (mounted by startServer when this orchestrator is passed in).
998
- const agentVariantsOrchestrator = (0, createAgentVariantsOrchestrator_1.createAgentVariantsOrchestrator)({
999
- bridge,
1000
- telemetry,
1001
- });
1002
- // `agent_variants.*` events also fire from the MCP tool handlers (not just the
1003
- // orchestrator), so share the same session-replay-linking wrapper with the
1004
- // tools registration below. The factory wraps the orchestrator's copy itself.
1005
- const variantsTelemetry = (0, createAgentVariantsOrchestrator_1.withVariantsSessionReplayLinking)(telemetry, bridge);
1006
- /**
1007
- * Resolve a variants sessionId to its projectContext kind, swallowing the
1008
- * `SESSION_NOT_FOUND` throw for unknown/expired sessions so classification can
1009
- * fall back to the default path.
1010
- */
1011
- const resolveSessionKind = (sessionId) => {
1012
- try {
1013
- return agentVariantsOrchestrator.getProjectContext(sessionId).kind;
1014
- }
1015
- catch {
1016
- return undefined;
1017
- }
1018
- };
1019
- /**
1020
- * A `variant_request` ("generate N variants") on a FRESH static-preview
1021
- * variant has no file-backed worktree to code_gen against: the inline preview
1022
- * HTML is gitignored and never lands in a `git worktree` clone, so the default
1023
- * `mode:"existing"` guide would send the agent to edit files that don't exist.
1024
- * Reroute it through the static-preview refine flow, which operates on the
1025
- * variant's inline HTML.
1026
- */
1027
- const isFreshStaticPreviewVariantRequestBatch = (intent) => (0, changeBatchClassification_1.isFreshStaticPreviewVariantRequest)(intent, resolveSessionKind, (sid, vid) => agentVariantsOrchestrator.isStaticPreviewVariant(sid, vid));
1028
- /**
1029
- * Kick off static-preview refinement work items for a leased batch.
1030
- */
1031
- const handleStaticPreviewRefineBatch = (intent, lease) => {
1032
- const consumedRequest = (0, changeBatchClassification_1.withDerivedStaticPreviewTargets)(intent);
1033
- let droppedRefinements = [];
1034
- let createdWorkItemIds = [];
1035
- if (consumedRequest) {
1036
- const targetInstructions = (0, exports.collectStaticPreviewRefineTargetInstructions)(consumedRequest);
1037
- const sessionId = targetInstructions[0]?.target.sessionId;
1038
- if (sessionId) {
1039
- ({ droppedRefinements, createdWorkItemIds } =
1040
- agentVariantsOrchestrator.beginStaticPreviewRefinements({
1041
- sessionId,
1042
- targets: targetInstructions.map(({ target, instruction, count, element, requestId }) => ({
1043
- variantId: target.variantId,
1044
- instruction,
1045
- count,
1046
- element,
1047
- requestId,
1048
- })),
1049
- }));
1050
- variantRequestLeaseTracker.claim({
1051
- requestId: lease.requestId,
1052
- sessionId,
1053
- workItemIds: createdWorkItemIds,
1054
- });
1055
- }
1056
- }
1057
- return (0, exports.buildStaticPreviewRefineResponse)(consumedRequest, droppedRefinements, createdWorkItemIds, lease);
1058
- };
1059
- /** Release a variant bridge lease after tracked generated work has finished. */
1060
- const ackGeneratedVariantBridgeLease = (args) => {
1061
- const result = (0, exports.ackVariantBridgeLease)({ bridge, telemetry, ...args });
1062
- if (!result.ok) {
1063
- log.warn(`Failed to release generated variant bridge lease ${args.leaseId}: ${result.reason}`);
1064
- }
1065
- };
1066
- /** Complete an apply lease and refresh git status in the Rivet UI. */
1067
- const handleApplyAck = (sessionId, args) => {
1068
- const leaseId = args.leaseId ?? bridge.findActiveApplyLease()?.leaseId ?? undefined;
1069
- if (leaseId) {
1070
- (0, exports.ackApplyBridgeLease)({
1071
- bridge,
1072
- telemetry,
1073
- sessionId,
1074
- leaseId,
1075
- requestId: args.requestId,
1076
- });
1077
- }
1078
- handleRefreshGit(sessionId);
1079
- bridge.setStatus('READY');
1080
- };
1081
- /** Release a variant lease after the agent finishes the batch. */
1082
- const handleVariantAck = (args) => {
1083
- const manualAckBlock = variantRequestLeaseTracker.getManualAckBlock(args);
1084
- if (manualAckBlock) {
1085
- return (0, exports.buildVariantManualAckBlockedResponse)(manualAckBlock);
1086
- }
1087
- const result = (0, exports.ackVariantBridgeLease)({ bridge, telemetry, ...args });
1088
- if (!result.ok) {
1089
- return (0, exports.buildVariantAckFailedResponse)(result);
1090
- }
1091
- return null;
1092
- };
1093
- const variantRequestLeaseTracker = (0, exports.createVariantRequestLeaseTracker)({
1094
- leaseRequest: (requestId) => bridge.leaseBatch(requestId),
1095
- ackRequest: ackGeneratedVariantBridgeLease,
1096
- activeVariantLeases: () => getActiveLeaseMetadata(bridge),
1097
- });
1098
- /** Complete the active run correlation and refresh git status in the Rivet UI. */
1099
- const handleRefreshGit = (sessionId) => {
1100
- const activeRunCorrelation = bridge.completeActiveRunCorrelation();
1101
- if (activeRunCorrelation) {
1102
- telemetry.trackAgentRunCompleted({
1103
- traceId: activeRunCorrelation.traceId,
1104
- agentRunId: activeRunCorrelation.agentRunId,
1105
- sessionId,
1106
- posthogSessionId: activeRunCorrelation.browserSessionId,
1107
- durationMs: Date.now() - activeRunCorrelation.startedAt,
1108
- source: 'mcp',
1109
- });
1110
- }
1111
- bridge.reportFeedback('__RIVET_REFRESH_GIT__');
1112
- };
1113
- /**
1114
- * Start tracking an MCP apply run after a batch is leased.
1115
- */
1116
- const beginApplyingRun = (intent, sessionId, _lease) => {
1117
- bridge.setStatus('APPLYING');
1118
- const { newRun, orphanedRun } = bridge.startApplyingRun(intent);
1119
- const turnMetadata = (0, MCPOnboardingTelemetry_1.summarizeMCPConversationTurn)(intent, bridge.getRoundsCompleted() + 1, sessionId);
1120
- trackChangesQueuedFromIntent(intent, bridge, telemetry, sessionId);
1121
- if (orphanedRun) {
1122
- trackOrphanedMCPRun(telemetry, sessionId, orphanedRun);
1123
- }
1124
- telemetry.trackAgentRunStarted({
1125
- traceId: newRun.traceId,
1126
- agentRunId: newRun.agentRunId,
1127
- instructionLength: turnMetadata.instructionLengthTotal,
1128
- elementCount: turnMetadata.elementCount,
1129
- posthogSessionId: newRun.browserSessionId,
1130
- resumeSessionId: sessionId,
1131
- source: 'mcp',
1132
- });
1133
- };
1134
- /**
1135
- * Lease and route the next batch from the unified bridge pipeline.
1136
- */
1137
- const leaseAndRouteBatch = (sessionId, requestId) => {
1138
- while (true) {
1139
- const applyHead = bridge.getPendingChanges();
1140
- if (!applyHead || !(0, changeBatchClassification_1.isMixedVariantBatch)(applyHead)) {
1141
- break;
1142
- }
1143
- const rejectedRequest = bridge.rejectMixedVariantRequestBatch(applyHead);
1144
- return withPendingVariantRequests((0, exports.buildMixedVariantBatchResponse)(rejectedRequest));
1145
- }
1146
- const lease = (0, exports.leaseAvailableBatchForPoll)({ bridge, requestId });
1147
- if (!lease) {
1148
- return null;
1149
- }
1150
- telemetry.trackMCPChangeLeased({
1151
- channel: lease.kind,
1152
- leaseId: lease.leaseId,
1153
- requestId: lease.requestId,
1154
- changeCount: lease.mergedFrom,
1155
- sessionId,
1156
- });
1157
- if (lease.kind === 'variant') {
1158
- return (0, exports.buildLeasedVariantResponse)({
1159
- lease,
1160
- isFreshStaticPreviewVariantRequestBatch,
1161
- handleStaticPreviewRefineBatch,
1162
- withPendingVariantRequests,
1163
- projectPath: bridge.getProjectPath(),
1164
- });
1165
- }
1166
- beginApplyingRun(lease.intent, sessionId, lease);
1167
- return (0, exports.buildApplyChangesResponse)({
1168
- intent: lease.intent,
1169
- lease,
1170
- pendingVariantRequests: bridge.pendingVariantRequestCount(),
1171
- });
1172
- };
1173
- /**
1174
- * Stamp the live variant-request FIFO depth onto a built change response's
1175
- * first text block as `pendingVariantRequests`. Read from the bridge AFTER
1176
- * any batch was consumed, so the count reflects how many MORE batches are
1177
- * still queued (`> 0` ⇒ the agent should keep draining via
1178
- * watch_for_changes / get_pending_changes). Non-JSON text blocks pass through
1179
- * unchanged. Mutates and returns the same response object for convenience.
1180
- */
1181
- const withPendingVariantRequests = (response) => {
1182
- const head = response.content[0];
1183
- if (head?.type !== 'text')
1184
- return response;
1185
- try {
1186
- const parsed = JSON.parse(head.text);
1187
- parsed.pendingVariantRequests = bridge.pendingVariantRequestCount();
1188
- head.text = JSON.stringify(parsed);
1189
- }
1190
- catch {
1191
- // Not a JSON payload — leave it untouched.
1192
- }
1193
- return response;
1194
- };
1195
- // Tailor the sign-in instructions to the user's stored credentials so the
1196
- // agent is told to sign in up front (rather than after an AUTH_REQUIRED
1197
- // failure). A synchronous disk read is enough: the presence of an access or
1198
- // refresh token means the user has signed in before, and the per-tool guard
1199
- // will silently refresh a stale token; only a total absence means we must
1200
- // ask the agent to authenticate first.
1201
- const configManager = (0, ConfigManager_1.getConfigManager)();
1202
- const hasStoredCredentials = configManager.isAuthenticated() || Boolean(configManager.getRefreshToken());
1203
- const mcp = new mcp_js_1.McpServer({ name: 'rivet', version: '0.1.0' }, {
1204
- capabilities: { tools: {} },
1205
- instructions: (0, instructions_1.buildRivetMcpInstructions)(hasStoredCredentials ? 'authenticated' : 'unauthenticated', { firstRun: !configManager.hasCompletedFirstVariantsRun() }),
1206
- });
1207
- const analyticsMcp = (0, toolAnalytics_1.withMCPToolAnalytics)(mcp, {
1208
- telemetry,
1209
- getSessionId: () => bridge.getSessionId() ?? undefined,
1210
- getAuthState: () => configManager.isAuthenticated() || Boolean(configManager.getRefreshToken())
1211
- ? 'authenticated'
1212
- : 'unauthenticated',
1213
- // Once the editor browser connects it reports its PostHog session id via
1214
- // change-request telemetryContext; carrying it here lets tool-execution
1215
- // events link to the session replay. Null until the browser first reports.
1216
- getBrowserSession: () => {
1217
- const context = bridge.getTelemetryContext();
1218
- if (!context) {
1219
- return undefined;
1220
- }
1221
- return {
1222
- browserSessionId: context.browserSessionId,
1223
- browserDistinctId: context.browserDistinctId,
1224
- };
1225
- },
1226
- mcpEditor,
1227
- });
1228
- // Every substantive tool is gated behind login: register them on `guardedMcp`
1229
- // so an unauthenticated call returns AUTH_REQUIRED instead of running. The
1230
- // login/logout/auth_status tools register on `analyticsMcp` so they stay
1231
- // reachable while logged out and still emit tool analytics.
1232
- const guardedMcp = (0, tools_1.withAuthGuard)(analyticsMcp, requireAuth);
1233
- // Track the Express server
1234
- let serverHandle = null;
1235
- let sessionStartedAt = null;
1236
- // Track a dev server we auto-started so we can kill it on close
1237
- let devServerProcess = null;
1238
- let devServerOwnership = 'none';
1239
- let devServerHost = null;
1240
- let devServerPort = null;
1241
- let rivetServerPort = null;
1242
- let rivetServerUrl = null;
1243
- let isCleanupRunning = false;
1244
- /**
1245
- * Surfaces a provider connect prompt inside the Rivet editor when a fetch
1246
- * returns `not_connected`. Deep-links the active editor with `?connect=` so
1247
- * the "Connect design references" dialog opens over it (see App.tsx). When no
1248
- * editor is running there is nothing to open; the agent still relays the
1249
- * not-connected nudge from the tool result. Failures are non-fatal.
1250
- */
1251
- const openIntegrationConnectUrl = async (input) => {
1252
- if (!rivetServerUrl) {
1253
- return;
1254
- }
1255
- const target = new URL(rivetServerUrl);
1256
- target.searchParams.set('connect', input.provider);
1257
- await (0, open_1.default)(target.toString());
1258
- };
1259
- const updateBridgeHealth = (reason, isReachable) => {
1260
- bridge.updateDevServerHealth({
1261
- reason,
1262
- isReachable,
1263
- lastCheckedAt: Date.now(),
1264
- });
1265
- };
1266
- const cleanupSessionResources = async (options) => {
1267
- if (isCleanupRunning) {
1268
- return { totalRoundsCompleted: 0, sessionId: null };
1269
- }
1270
- isCleanupRunning = true;
1271
- try {
1272
- const activeSessionId = bridge.getSessionId();
1273
- const sessionIdToClose = options?.closeSessionId ??
1274
- (bridge.isActive() && activeSessionId ? activeSessionId : null);
1275
- const telemetryContext = bridge.getTelemetryContext();
1276
- const activeRunCorrelation = bridge.completeActiveRunCorrelation();
1277
- let totalRoundsCompleted = 0;
1278
- if (sessionIdToClose && bridge.isActive()) {
1279
- totalRoundsCompleted = bridge.endSession();
1280
- }
1281
- if (serverHandle) {
1282
- try {
1283
- await serverHandle.close();
1284
- }
1285
- catch (error) {
1286
- log.warn('Error stopping Express server:', error);
1287
- }
1288
- serverHandle = null;
1289
- }
1290
- if (devServerOwnership === 'rivet_owned' && devServerProcess) {
1291
- try {
1292
- await stopChildProcess(devServerProcess);
1293
- }
1294
- catch (error) {
1295
- log.warn('Error stopping dev server process:', error);
1296
- }
1297
- }
1298
- devServerProcess = null;
1299
- devServerOwnership = 'none';
1300
- devServerHost = null;
1301
- devServerPort = null;
1302
- rivetServerPort = null;
1303
- rivetServerUrl = null;
1304
- updateBridgeHealth('rivet_unreachable', false);
1305
- const durationMs = sessionStartedAt ? Date.now() - sessionStartedAt : 0;
1306
- sessionStartedAt = null;
1307
- if (options?.trackSessionEnd !== false && sessionIdToClose) {
1308
- telemetry.trackMCPSessionEnd({
1309
- sessionId: sessionIdToClose,
1310
- rounds: totalRoundsCompleted,
1311
- durationMs,
1312
- browserSessionId: telemetryContext?.browserSessionId,
1313
- browserDistinctId: telemetryContext?.browserDistinctId,
1314
- });
1315
- if (activeRunCorrelation) {
1316
- telemetry.trackAgentRunFailed({
1317
- traceId: activeRunCorrelation.traceId,
1318
- agentRunId: activeRunCorrelation.agentRunId,
1319
- sessionId: sessionIdToClose,
1320
- posthogSessionId: activeRunCorrelation.browserSessionId,
1321
- durationMs: Date.now() - activeRunCorrelation.startedAt,
1322
- errorType: 'mcp_session_closed',
1323
- errorMessage: 'MCP session closed before apply completion confirmation',
1324
- source: 'mcp',
1325
- });
1326
- }
1327
- }
1328
- await telemetry.flush();
1329
- return {
1330
- totalRoundsCompleted,
1331
- sessionId: sessionIdToClose,
1332
- };
1333
- }
1334
- finally {
1335
- isCleanupRunning = false;
1336
- }
1337
- };
1338
- // Flush telemetry + resources before process exits
1339
- const shutdown = async () => {
1340
- await cleanupSessionResources({ trackSessionEnd: true });
1341
- await telemetry.shutdown();
1342
- };
1343
- if (shouldRegisterProcessHandlers) {
1344
- const stopParentProcessMonitor = (0, exports.startParentProcessMonitor)({
1345
- onOrphaned: async () => {
1346
- log.warn(`MCP parent process disappeared; shutting down orphaned process ${process.pid}`);
1347
- await shutdown();
1348
- },
1349
- });
1350
- process.on('SIGTERM', async () => {
1351
- stopParentProcessMonitor();
1352
- await shutdown();
1353
- process.exit(0);
1354
- });
1355
- process.on('SIGINT', async () => {
1356
- stopParentProcessMonitor();
1357
- await shutdown();
1358
- process.exit(0);
1359
- });
1360
- // Flush when the event loop empties (e.g., MCP transport disconnects)
1361
- process.on('beforeExit', async () => {
1362
- await shutdown();
1363
- });
1364
- }
1365
- /** Returns an attachment for the current visual editor session, if one exists. */
1366
- const attachActiveVisualEditor = async (projectPath) => {
1367
- if (!bridge.isActive()) {
1368
- return null;
1369
- }
1370
- if (!rivetServerUrl || rivetServerPort === null) {
1371
- throw new Error('Rivet visual editor session is active but URL metadata is unavailable');
1372
- }
1373
- const activeEditorSessionId = bridge.getSessionId();
1374
- if (!activeEditorSessionId) {
1375
- throw new Error('Rivet visual editor session is active but has no session ID');
1376
- }
1377
- const projectAction = (0, exports.getActiveVisualEditorProjectAction)({
1378
- activeProjectPath: bridge.getProjectPath(),
1379
- requestedProjectPath: projectPath,
1380
- devServerOwnership,
1381
- });
1382
- if (projectAction === 'restart') {
1383
- await cleanupSessionResources({ trackSessionEnd: false });
1384
- return null;
1385
- }
1386
- if (projectAction === 'rebind') {
1387
- bridge.rebindProjectPath(projectPath);
1388
- }
1389
- // Don't spawn a duplicate tab when a Rivet page is already open. The
1390
- // variants flow re-enters this path on every propose/start call; calling
1391
- // `open()` again opens a fresh tab in most browsers rather than focusing
1392
- // the existing one. Only (re)open when no UI client is connected — i.e.
1393
- // the user closed the page and needs it back.
1394
- if (!agentVariantsOrchestrator.hasUiClients()) {
1395
- try {
1396
- await (0, open_1.default)(rivetServerUrl);
1397
- }
1398
- catch {
1399
- log.warn('Failed to focus existing Rivet browser automatically');
1400
- }
1401
- }
1402
- return {
1403
- sessionId: activeEditorSessionId,
1404
- url: rivetServerUrl,
1405
- rivetPort: rivetServerPort,
1406
- opened: false,
1407
- attached: true,
1408
- };
1409
- };
1410
- /** Reads feature flags for a visual editor session without blocking startup on flag failures. */
1411
- const resolveSessionGitEnabled = async () => {
1412
- try {
1413
- const featureFlags = await (0, evaluateFlags_1.getFeatureFlags)();
1414
- return featureFlags.gitUi.isEnabled;
1415
- }
1416
- catch (error) {
1417
- log.warn('Failed to evaluate feature flags for MCP session startup; defaulting git to enabled:', error);
1418
- return true;
1419
- }
1420
- };
1421
- /** Writes editor-specific guidance once a visual editor session is ready. */
1422
- const writeEditorGuidance = (projectPath) => {
1423
- if (mcpEditor === 'cursor') {
1424
- (0, skillWriter_1.writeCursorRulesIfNeeded)(projectPath);
1425
- }
1426
- else if (mcpEditor === 'claude' || mcpEditor === undefined) {
1427
- (0, skillWriter_1.writeSkillFileIfNeeded)();
1428
- }
1429
- };
1430
- /**
1431
- * Ensure zero-to-one variant sessions have a Rivet UI shell subscribed to
1432
- * the shared variants orchestrator. Fresh projects do not have a base app
1433
- * server yet, so the shell starts with the existing proxy path and waits for
1434
- * generated variant dev servers to become selectable.
1435
- */
1436
- const ensureZeroToOneVisualEditorOpen = async (input) => {
1437
- // The bridge tracks the generated project folder. That keeps /api/config,
1438
- // history reads, and the committed preview focused on the same folder.
1439
- const { projectPath } = input;
1440
- const activeAttachment = await attachActiveVisualEditor(projectPath);
1441
- if (activeAttachment) {
1442
- return activeAttachment;
1443
- }
1444
- await (0, exports.closeDetachedVisualEditorServer)({
1445
- isBridgeActive: bridge.isActive(),
1446
- serverHandle,
1447
- cleanup: async () => {
1448
- await cleanupSessionResources({ trackSessionEnd: false });
1449
- },
1450
- });
1451
- let nextRivetPort;
1452
- try {
1453
- nextRivetPort = await (0, portUtils_1.findAvailablePort)(index_1.DEFAULT_PORT);
1454
- }
1455
- catch (error) {
1456
- log.warn(`Port discovery failed for zero-to-one editor, falling back to ${index_1.DEFAULT_PORT}:`, error);
1457
- nextRivetPort = index_1.DEFAULT_PORT;
1458
- }
1459
- let retries = 0;
1460
- const maxRetries = 5;
1461
- while (!serverHandle && retries < maxRetries) {
1462
- try {
1463
- serverHandle = await (0, server_1.startServer)({
1464
- framework: input.framework,
1465
- projectPath: input.projectPath,
1466
- userPort: 0,
1467
- userHost: 'localhost',
1468
- sessionBridge: bridge,
1469
- agentVariantsOrchestrator,
1470
- telemetry,
1471
- mcpEditor,
1472
- skipProcessHandlers: true,
1473
- rivetPort: nextRivetPort,
1474
- isGitEnabled: false,
1475
- });
1476
- }
1477
- catch (error) {
1478
- if (error?.code !== 'EADDRINUSE') {
1479
- throw error;
1480
- }
1481
- retries++;
1482
- nextRivetPort = await (0, portUtils_1.findAvailablePort)(nextRivetPort + 1);
1483
- log.debug(`Zero-to-one editor port in use, retrying with port ${nextRivetPort} (attempt ${retries}/${maxRetries})`);
1484
- }
1485
- }
1486
- if (!serverHandle) {
1487
- throw new Error(`Could not start Rivet server after ${maxRetries} attempts.`);
1488
- }
1489
- const editorSessionId = bridge.startSession(projectPath);
1490
- sessionStartedAt = Date.now();
1491
- devServerOwnership = 'none';
1492
- devServerHost = 'localhost';
1493
- devServerPort = null;
1494
- bridge.setDevServerContext({
1495
- ownership: devServerOwnership,
1496
- host: devServerHost,
1497
- port: devServerPort,
1498
- startedByRivet: false,
1499
- });
1500
- updateBridgeHealth('unknown', null);
1501
- telemetry.trackMCPSessionStart({
1502
- framework: input.framework,
1503
- devServerStarted: false,
1504
- sessionId: editorSessionId,
1505
- mcpEditor,
1506
- });
1507
- // Mirror the writeSkillFileIfNeeded / writeCursorRulesIfNeeded call
1508
- // that open_visual_editor performs after a successful open. The
1509
- // unified start_variants flow opens the editor via this hook instead
1510
- // of through the open_visual_editor tool, so without this the
1511
- // SKILL.md / rivet.mdc file would never be rewritten when only
1512
- // start_variants is used.
1513
- writeEditorGuidance(input.projectPath);
1514
- const url = `http://localhost:${nextRivetPort}/rivet`;
1515
- rivetServerPort = nextRivetPort;
1516
- rivetServerUrl = url;
1517
- try {
1518
- await (0, open_1.default)(url);
1519
- }
1520
- catch {
1521
- log.warn('Failed to open browser automatically');
1522
- }
1523
- return {
1524
- sessionId: editorSessionId,
1525
- url,
1526
- rivetPort: nextRivetPort,
1527
- opened: true,
1528
- attached: false,
1529
- };
1530
- };
1531
- /**
1532
- * Ensure existing-project variant sessions have the base app mounted as the
1533
- * default preview upstream before variant preview targets are selected.
1534
- */
1535
- const ensureExistingVisualEditorOpen = async (input) => {
1536
- const { projectPath } = input;
1537
- const activeAttachment = await attachActiveVisualEditor(projectPath);
1538
- if (activeAttachment &&
1539
- (0, exports.canReuseExistingProjectVisualEditor)({
1540
- attachment: activeAttachment,
1541
- devServerOwnership,
1542
- })) {
1543
- return activeAttachment;
1544
- }
1545
- if (activeAttachment) {
1546
- await cleanupSessionResources({ trackSessionEnd: false });
1547
- }
1548
- await (0, exports.closeDetachedVisualEditorServer)({
1549
- isBridgeActive: bridge.isActive(),
1550
- serverHandle,
1551
- cleanup: async () => {
1552
- await cleanupSessionResources({ trackSessionEnd: false });
1553
- },
1554
- });
1555
- const detection = new index_core_1.ProjectDetectionService();
1556
- let nextDevServerPort = null;
1557
- let nextDevServerHost = 'localhost';
1558
- let nextDevServerOwnership = 'none';
1559
- let staticTarget = null;
1560
- if (input.framework === 'static') {
1561
- staticTarget = (0, staticProjectTarget_1.resolveStaticProjectTarget)({ targetPath: projectPath });
1562
- if ((0, staticProjectTarget_1.needsStaticEntryPath)(staticTarget)) {
1563
- throw new Error('Static folder has multiple HTML files and no index.html. Open the visual editor with a specific entry path before starting variants.');
1564
- }
1565
- const entryFullPath = path_1.default.join(projectPath, staticTarget.entryPath);
1566
- const didWriteStarter = staticTarget.htmlEntryFiles.length === 0
1567
- ? (0, staticStarter_1.ensureStaticStarterCanvas)(projectPath)
1568
- : false;
1569
- const hasStaticEntry = fs_1.default.existsSync(entryFullPath) ||
1570
- (didWriteStarter && staticTarget.entryPath === 'index.html');
1571
- if (hasStaticEntry) {
1572
- nextDevServerHost = '127.0.0.1';
1573
- nextDevServerPort = await (0, portUtils_1.findAvailablePort)(index_1.DEFAULT_USER_PORT);
1574
- devServerProcess = await (0, StaticPreviewServer_1.startStaticPreviewServer)({
1575
- rootPath: projectPath,
1576
- port: nextDevServerPort,
1577
- host: nextDevServerHost,
1578
- });
1579
- nextDevServerOwnership = 'rivet_owned';
1580
- devServerProcess.on('exit', (code) => {
1581
- log.info(`Static preview server exited with code ${code}`);
1582
- devServerProcess = null;
1583
- if (bridge.isActive()) {
1584
- updateBridgeHealth('upstream_unreachable', false);
1585
- }
1586
- });
1587
- updateBridgeHealth('healthy', true);
1588
- log.info(`Static preview server ready on ${nextDevServerHost}:${nextDevServerPort}`);
1589
- }
1590
- }
1591
- else {
1592
- const frameworkConfig = devServerCommand_1.FRAMEWORK_DEV_CONFIG[input.framework];
1593
- const packageManager = await detection.detectPackageManager(projectPath);
1594
- const preferredPort = detection.readConfiguredPort(projectPath) ?? frameworkConfig.defaultPort;
1595
- nextDevServerPort = await (0, portUtils_1.findAvailablePort)(preferredPort);
1596
- const { cmd, args: cmdArgs, env, } = (0, devServerCommand_1.buildDevServerCommand)(input.framework, packageManager, frameworkConfig.devCommand, nextDevServerPort);
1597
- log.info(`Auto-starting dev server: ${cmd} ${cmdArgs.join(' ')} in ${projectPath}`);
1598
- devServerProcess = (0, child_process_1.spawn)(cmd, cmdArgs, {
1599
- cwd: projectPath,
1600
- env: { ...process.env, ...env },
1601
- stdio: 'pipe',
1602
- detached: false,
1603
- });
1604
- nextDevServerOwnership = 'rivet_owned';
1605
- devServerProcess.on('error', (err) => {
1606
- log.warn('Dev server process error:', err);
1607
- });
1608
- devServerProcess.on('exit', (code) => {
1609
- log.info(`Dev server exited with code ${code}`);
1610
- devServerProcess = null;
1611
- if (bridge.isActive()) {
1612
- updateBridgeHealth('upstream_unreachable', false);
1613
- }
1614
- });
1615
- devServerProcess.stdout?.on('data', (data) => {
1616
- log.debug(`[dev-server] ${data.toString().trim()}`);
1617
- });
1618
- devServerProcess.stderr?.on('data', (data) => {
1619
- log.debug(`[dev-server] ${data.toString().trim()}`);
1620
- });
1621
- const ready = await waitForPort(nextDevServerPort, nextDevServerHost, 30_000);
1622
- if (!ready) {
1623
- if (devServerProcess) {
1624
- await stopChildProcess(devServerProcess);
1625
- }
1626
- devServerProcess = null;
1627
- throw new Error(`Dev server did not become ready on port ${nextDevServerPort} within 30s.`);
1628
- }
1629
- const startupValidation = await validateProjectServer({
1630
- host: nextDevServerHost,
1631
- port: nextDevServerPort,
1632
- framework: input.framework,
1633
- projectPath,
1634
- });
1635
- if (!startupValidation.valid) {
1636
- if (devServerProcess) {
1637
- await stopChildProcess(devServerProcess);
1638
- }
1639
- devServerProcess = null;
1640
- throw new Error(startupValidation.reason);
1641
- }
1642
- updateBridgeHealth('healthy', true);
1643
- log.info(`Dev server ready on ${nextDevServerHost}:${nextDevServerPort}`);
1644
- }
1645
- let nextRivetPort;
1646
- try {
1647
- nextRivetPort = await (0, portUtils_1.findAvailablePort)(index_1.DEFAULT_PORT);
1648
- }
1649
- catch (error) {
1650
- log.warn(`Port discovery failed for existing-project editor, falling back to ${index_1.DEFAULT_PORT}:`, error);
1651
- nextRivetPort = index_1.DEFAULT_PORT;
1652
- }
1653
- let retries = 0;
1654
- const maxRetries = 5;
1655
- const isGitEnabledForSession = await resolveSessionGitEnabled();
1656
- while (!serverHandle && retries < maxRetries) {
1657
- try {
1658
- serverHandle = await (0, server_1.startServer)({
1659
- framework: input.framework,
1660
- projectPath,
1661
- staticEntry: staticTarget?.entryPath,
1662
- userPort: nextDevServerPort ?? 0,
1663
- userHost: nextDevServerHost,
1664
- sessionBridge: bridge,
1665
- agentVariantsOrchestrator,
1666
- telemetry,
1667
- mcpEditor,
1668
- skipProcessHandlers: true,
1669
- rivetPort: nextRivetPort,
1670
- isGitEnabled: isGitEnabledForSession,
1671
- });
1672
- }
1673
- catch (error) {
1674
- if (error?.code !== 'EADDRINUSE') {
1675
- if (devServerProcess) {
1676
- await stopChildProcess(devServerProcess);
1677
- }
1678
- devServerProcess = null;
1679
- throw error;
1680
- }
1681
- retries++;
1682
- nextRivetPort = await (0, portUtils_1.findAvailablePort)(nextRivetPort + 1);
1683
- log.debug(`Existing-project editor port in use, retrying with port ${nextRivetPort} (attempt ${retries}/${maxRetries})`);
1684
- }
1685
- }
1686
- if (!serverHandle) {
1687
- if (devServerProcess) {
1688
- await stopChildProcess(devServerProcess);
1689
- }
1690
- devServerProcess = null;
1691
- throw new Error(`Could not start Rivet server after ${maxRetries} attempts.`);
1692
- }
1693
- const editorSessionId = bridge.startSession(projectPath);
1694
- sessionStartedAt = Date.now();
1695
- devServerOwnership = nextDevServerOwnership;
1696
- devServerHost = nextDevServerHost;
1697
- devServerPort = nextDevServerPort;
1698
- bridge.setDevServerContext({
1699
- ownership: devServerOwnership,
1700
- host: devServerHost,
1701
- port: devServerPort,
1702
- startedByRivet: devServerOwnership === 'rivet_owned',
1703
- });
1704
- if (devServerOwnership === 'none') {
1705
- updateBridgeHealth('unknown', null);
1706
- }
1707
- else {
1708
- updateBridgeHealth('healthy', true);
1709
- }
1710
- telemetry.trackMCPSessionStart({
1711
- framework: input.framework,
1712
- devServerStarted: devServerOwnership === 'rivet_owned',
1713
- sessionId: editorSessionId,
1714
- mcpEditor,
1715
- });
1716
- writeEditorGuidance(projectPath);
1717
- const url = `http://localhost:${nextRivetPort}/rivet`;
1718
- rivetServerPort = nextRivetPort;
1719
- rivetServerUrl = url;
1720
- try {
1721
- await (0, open_1.default)(url);
1722
- }
1723
- catch {
1724
- log.warn('Failed to open browser automatically');
1725
- }
1726
- return {
1727
- sessionId: editorSessionId,
1728
- url,
1729
- rivetPort: nextRivetPort,
1730
- opened: true,
1731
- attached: false,
1732
- };
1733
- };
1734
- // Define schemas separately to avoid TS2589 "excessively deep" type inference
1735
- const openEditorArgs = {
1736
- projectPath: v3_1.z
1737
- .string()
1738
- .describe('Path to the project directory or a standalone HTML file. Defaults to current working directory.')
1739
- .optional(),
1740
- entryPath: v3_1.z
1741
- .string()
1742
- .describe('Static HTML entry path relative to projectPath. Use when opening a static folder with multiple HTML files and no index.html.')
1743
- .optional(),
1744
- framework: v3_1.z
1745
- .string()
1746
- .describe('Project framework. Optional; when omitted, Rivet detects it from projectPath.')
1747
- .optional(),
1748
- startPort: v3_1.z
1749
- .number()
1750
- .describe('Preferred starting port for the dev server. Rivet will find the next free port at or above this value. Falls back to the framework default if omitted.')
1751
- .optional(),
1752
- startupMode: v3_1.z
1753
- .enum(['manual_existing', 'ai_startup'])
1754
- .describe('How to connect the dev server: manual_existing uses an already-running server, ai_startup auto-starts one. Optional; if omitted, Rivet defaults to ai_startup.')
1755
- .optional(),
1756
- existingPort: v3_1.z
1757
- .number()
1758
- .describe('Port of an already-running dev server. Required for startupMode manual_existing, and reused for follow-up calls after nextStep responses.')
1759
- .optional(),
1760
- forceAttachExternal: v3_1.z
1761
- .boolean()
1762
- .describe('Confirmation flag for startupMode manual_existing when validation reports a project mismatch. Set true only after user confirms attaching anyway.')
1763
- .optional(),
1764
- };
1765
- const sessionIdArgs = {
1766
- sessionId: v3_1.z
1767
- .string()
1768
- .describe('The session ID returned by open_visual_editor'),
1769
- };
1770
- guardedMcp.tool('open_visual_editor', 'Start a Rivet visual editing session. Pass projectPath directly; framework is optional and will be detected when omitted. If the response has opened: false, follow the nextStep/nextAction instructions to recover. If opened: true, the response includes nextAction { tool: "watch_for_changes", ... }; host agents must call it immediately and keep that loop active until the user stops or closes the editor.', openEditorArgs, async (args) => {
1771
- if (bridge.isActive()) {
1772
- return {
1773
- content: [
1774
- {
1775
- type: 'text',
1776
- text: JSON.stringify({
1777
- error: 'Session already active',
1778
- sessionId: bridge.getSessionId(),
1779
- }),
1780
- },
1781
- ],
1782
- isError: true,
1783
- };
1784
- }
1785
- const requestedProjectPath = args.projectPath || process.cwd();
1786
- let projectPath = requestedProjectPath;
1787
- let staticTarget = null;
1788
- try {
1789
- staticTarget = (0, staticProjectTarget_1.resolveStaticProjectTarget)({
1790
- targetPath: requestedProjectPath,
1791
- entryPath: args.entryPath,
1792
- });
1793
- projectPath = staticTarget.projectPath;
1794
- }
1795
- catch (error) {
1796
- const message = error instanceof Error ? error.message : 'Unknown error';
1797
- return {
1798
- content: [
1799
- {
1800
- type: 'text',
1801
- text: JSON.stringify({
1802
- error: `Could not open project target: ${message}`,
1803
- }),
1804
- },
1805
- ],
1806
- isError: true,
1807
- };
1808
- }
1809
- // When the caller omits framework (e.g. "open rivet"), detect it from
1810
- // the workspace rather than assuming a
1811
- // dev-server framework. An empty or HTML-only folder resolves to
1812
- // 'static', so the editor opens in static mode instead of trying to
1813
- // boot a Vite/Next dev server that isn't there. Detection only throws
1814
- // for a nonexistent path; fall back to the prior 'nextjs' default.
1815
- let framework;
1816
- if (args.framework) {
1817
- framework = args.framework;
1818
- }
1819
- else {
1820
- try {
1821
- framework = new index_core_1.ProjectDetectionService().detectFramework(projectPath);
1822
- }
1823
- catch {
1824
- framework = 'nextjs';
1825
- }
1826
- }
1827
- const requestedStartupMode = args.startupMode;
1828
- const startupMode = requestedStartupMode ?? 'ai_startup';
1829
- let nextDevServerPort = null;
1830
- let nextDevServerHost = 'localhost';
1831
- let nextDevServerOwnership = 'none';
1832
- const startupModeSelection = requestedStartupMode ? 'explicit' : 'auto';
1833
- if (framework === 'static') {
1834
- staticTarget = (0, staticProjectTarget_1.resolveStaticProjectTarget)({
1835
- targetPath: requestedProjectPath,
1836
- entryPath: args.entryPath,
1837
- });
1838
- projectPath = staticTarget.projectPath;
1839
- if ((0, staticProjectTarget_1.needsStaticEntryPath)(staticTarget)) {
1840
- return {
1841
- content: [
1842
- {
1843
- type: 'text',
1844
- text: JSON.stringify({
1845
- opened: false,
1846
- nextStep: 'entry_path_required',
1847
- error: 'Static folder has multiple HTML files and no index.html. Pass projectPath as the target HTML file, or call open_visual_editor again with entryPath.',
1848
- projectPath,
1849
- staticHtmlFiles: staticTarget.htmlEntryFiles,
1850
- nextAction: 'Call open_visual_editor with projectPath set to the specific HTML file, or with projectPath set to the folder and entryPath set to one of staticHtmlFiles.',
1851
- }),
1852
- },
1853
- ],
1854
- isError: false,
1855
- };
1856
- }
1857
- const entryFullPath = path_1.default.join(projectPath, staticTarget.entryPath);
1858
- const didWriteStarter = staticTarget.htmlEntryFiles.length === 0
1859
- ? (0, staticStarter_1.ensureStaticStarterCanvas)(projectPath)
1860
- : false;
1861
- const hasStaticEntry = fs_1.default.existsSync(entryFullPath) ||
1862
- (didWriteStarter && staticTarget.entryPath === 'index.html');
1863
- const isHistoryWorkspace = staticTarget.htmlEntryFiles.length === 0 &&
1864
- fs_1.default.existsSync(path_1.default.join(projectPath, '.rivet'));
1865
- if (!hasStaticEntry && !isHistoryWorkspace) {
1866
- return {
1867
- content: [
1868
- {
1869
- type: 'text',
1870
- text: JSON.stringify({
1871
- opened: false,
1872
- nextStep: 'entry_path_required',
1873
- error: `Static entry not found: ${staticTarget.entryPath}`,
1874
- projectPath,
1875
- staticHtmlFiles: staticTarget.htmlEntryFiles,
1876
- nextAction: 'Call open_visual_editor again with entryPath set to an existing HTML file.',
1877
- }),
1878
- },
1879
- ],
1880
- isError: false,
1881
- };
1882
- }
1883
- if (startupMode === 'manual_existing') {
1884
- const existingPort = args.existingPort ?? args.startPort ?? null;
1885
- if (!existingPort) {
1886
- return {
1887
- content: [
1888
- {
1889
- type: 'text',
1890
- text: JSON.stringify({
1891
- error: 'startupMode manual_existing requires an existingPort value.',
1892
- suggestion: 'Pass existingPort for your running static file server.',
1893
- }),
1894
- },
1895
- ],
1896
- isError: true,
1897
- };
1898
- }
1899
- const isReachable = await waitForPort(existingPort, nextDevServerHost, 3000);
1900
- if (!isReachable) {
1901
- return {
1902
- content: [
1903
- {
1904
- type: 'text',
1905
- text: JSON.stringify({
1906
- error: `Cannot attach to ${nextDevServerHost}:${existingPort} because it is not reachable.`,
1907
- }),
1908
- },
1909
- ],
1910
- isError: true,
1911
- };
1912
- }
1913
- nextDevServerPort = existingPort;
1914
- nextDevServerOwnership = 'external';
1915
- updateBridgeHealth('healthy', true);
1916
- }
1917
- else if (hasStaticEntry) {
1918
- nextDevServerHost = '127.0.0.1';
1919
- nextDevServerPort = await (0, portUtils_1.findAvailablePort)(args.startPort ?? index_1.DEFAULT_USER_PORT);
1920
- devServerProcess = await (0, StaticPreviewServer_1.startStaticPreviewServer)({
1921
- rootPath: projectPath,
1922
- port: nextDevServerPort,
1923
- host: nextDevServerHost,
1924
- });
1925
- nextDevServerOwnership = 'rivet_owned';
1926
- devServerProcess.on('exit', (code) => {
1927
- log.info(`Static preview server exited with code ${code}`);
1928
- devServerProcess = null;
1929
- if (bridge.isActive()) {
1930
- updateBridgeHealth('upstream_unreachable', false);
1931
- }
1932
- });
1933
- updateBridgeHealth('healthy', true);
1934
- log.info(`Static preview server ready on ${nextDevServerHost}:${nextDevServerPort}`);
1935
- }
1936
- }
1937
- else {
1938
- const frameworkConfig = devServerCommand_1.FRAMEWORK_DEV_CONFIG[framework] ?? {
1939
- devCommand: 'dev',
1940
- defaultPort: 3000,
1941
- };
1942
- const detection = new index_core_1.ProjectDetectionService();
1943
- if (!frameworkConfig.devCommand) {
1944
- return {
1945
- content: [
1946
- {
1947
- type: 'text',
1948
- text: JSON.stringify({
1949
- error: 'Cannot auto-start: no dev command configured for this framework.',
1950
- suggestion: 'Start the dev server manually, then call open_visual_editor again.',
1951
- }),
1952
- },
1953
- ],
1954
- isError: true,
1955
- };
1956
- }
1957
- if (startupMode === 'manual_existing') {
1958
- const existingPort = args.existingPort ?? args.startPort ?? null;
1959
- if (!existingPort) {
1960
- return {
1961
- content: [
1962
- {
1963
- type: 'text',
1964
- text: JSON.stringify({
1965
- error: 'startupMode manual_existing requires an existingPort value.',
1966
- suggestion: 'Pass existingPort for your running dev server.',
1967
- }),
1968
- },
1969
- ],
1970
- isError: true,
1971
- };
1972
- }
1973
- const manualValidation = await validateProjectServer({
1974
- host: nextDevServerHost,
1975
- port: existingPort,
1976
- framework,
1977
- projectPath,
1978
- });
1979
- if (!manualValidation.valid) {
1980
- if (args.forceAttachExternal) {
1981
- const isReachable = await waitForPort(existingPort, nextDevServerHost, 3000);
1982
- if (!isReachable) {
1983
- return {
1984
- content: [
1985
- {
1986
- type: 'text',
1987
- text: JSON.stringify({
1988
- error: `Cannot attach to ${nextDevServerHost}:${existingPort} because it is not reachable.`,
1989
- validation: manualValidation,
1990
- }),
1991
- },
1992
- ],
1993
- isError: true,
1994
- };
1995
- }
1996
- nextDevServerPort = existingPort;
1997
- nextDevServerOwnership = 'external';
1998
- log.info(`Force-attached to external dev server on ${nextDevServerHost}:${nextDevServerPort}`);
1999
- updateBridgeHealth('healthy', true);
2000
- }
2001
- else if (manualValidation.selectedProjectPath ||
2002
- (manualValidation.observedProcessPaths?.length ?? 0) > 0) {
2003
- return {
2004
- content: [
2005
- {
2006
- type: 'text',
2007
- text: JSON.stringify({
2008
- opened: false,
2009
- nextStep: 'mismatch_confirmation_required',
2010
- error: manualValidation.reason,
2011
- existingServerPort: existingPort,
2012
- selectedProjectPath: manualValidation.selectedProjectPath,
2013
- observedProcessPaths: manualValidation.observedProcessPaths,
2014
- validation: manualValidation,
2015
- nextAction: 'If the user confirms, call open_visual_editor again with startupMode: "manual_existing", existingPort, and forceAttachExternal: true. Otherwise ask for another port.',
2016
- }),
2017
- },
2018
- ],
2019
- isError: false,
2020
- };
2021
- }
2022
- else {
2023
- return {
2024
- content: [
2025
- {
2026
- type: 'text',
2027
- text: JSON.stringify({
2028
- error: manualValidation.reason,
2029
- validation: manualValidation,
2030
- }),
2031
- },
2032
- ],
2033
- isError: true,
2034
- };
2035
- }
2036
- }
2037
- if (!nextDevServerPort) {
2038
- nextDevServerPort = existingPort;
2039
- nextDevServerOwnership = 'external';
2040
- log.info(`Using existing dev server on ${nextDevServerHost}:${nextDevServerPort}`);
2041
- updateBridgeHealth('healthy', true);
2042
- }
2043
- }
2044
- else {
2045
- const packageManager = await detection.detectPackageManager(projectPath);
2046
- const preferredPort = args.startPort ??
2047
- detection.readConfiguredPort(projectPath) ??
2048
- frameworkConfig.defaultPort;
2049
- nextDevServerPort = await (0, portUtils_1.findAvailablePort)(preferredPort);
2050
- const { cmd, args: cmdArgs, env, } = (0, devServerCommand_1.buildDevServerCommand)(framework, packageManager, frameworkConfig.devCommand, nextDevServerPort);
2051
- log.info(`Auto-starting dev server: ${cmd} ${cmdArgs.join(' ')} in ${projectPath}`);
2052
- devServerProcess = (0, child_process_1.spawn)(cmd, cmdArgs, {
2053
- cwd: projectPath,
2054
- env: { ...process.env, ...env },
2055
- stdio: 'pipe',
2056
- detached: false,
2057
- });
2058
- nextDevServerOwnership = 'rivet_owned';
2059
- devServerProcess.on('error', (err) => {
2060
- log.warn('Dev server process error:', err);
2061
- });
2062
- devServerProcess.on('exit', (code) => {
2063
- log.info(`Dev server exited with code ${code}`);
2064
- devServerProcess = null;
2065
- if (bridge.isActive()) {
2066
- updateBridgeHealth('upstream_unreachable', false);
2067
- }
2068
- });
2069
- devServerProcess.stdout?.on('data', (data) => {
2070
- log.debug(`[dev-server] ${data.toString().trim()}`);
2071
- });
2072
- devServerProcess.stderr?.on('data', (data) => {
2073
- log.debug(`[dev-server] ${data.toString().trim()}`);
2074
- });
2075
- const ready = await waitForPort(nextDevServerPort, nextDevServerHost, 30_000);
2076
- if (!ready) {
2077
- if (devServerProcess) {
2078
- await stopChildProcess(devServerProcess);
2079
- }
2080
- devServerProcess = null;
2081
- const existingServer = await detection.findRunningDevServer(projectPath, [index_1.DEFAULT_PORT]);
2082
- return {
2083
- content: [
2084
- {
2085
- type: 'text',
2086
- text: JSON.stringify({
2087
- opened: false,
2088
- nextStep: 'manual_port_required',
2089
- error: `Dev server did not become ready on port ${nextDevServerPort} within 30s.`,
2090
- existingServerPort: existingServer?.port ?? null,
2091
- validation: null,
2092
- nextAction: 'Call open_visual_editor again with startupMode: "manual_existing" and existingPort set to a running app server port.',
2093
- }),
2094
- },
2095
- ],
2096
- isError: false,
2097
- };
2098
- }
2099
- const startupValidation = await validateProjectServer({
2100
- host: nextDevServerHost,
2101
- port: nextDevServerPort,
2102
- framework,
2103
- projectPath,
2104
- });
2105
- if (!startupValidation.valid) {
2106
- if (devServerProcess) {
2107
- await stopChildProcess(devServerProcess);
2108
- }
2109
- devServerProcess = null;
2110
- const existingServer = await detection.findRunningDevServer(projectPath, [index_1.DEFAULT_PORT]);
2111
- return {
2112
- content: [
2113
- {
2114
- type: 'text',
2115
- text: JSON.stringify({
2116
- opened: false,
2117
- nextStep: 'manual_port_required',
2118
- error: startupValidation.reason,
2119
- existingServerPort: existingServer?.port ?? null,
2120
- validation: startupValidation,
2121
- nextAction: 'Call open_visual_editor again with startupMode: "manual_existing" and existingPort set to a running app server port.',
2122
- }),
2123
- },
2124
- ],
2125
- isError: false,
2126
- };
2127
- }
2128
- updateBridgeHealth('healthy', true);
2129
- log.info(`Dev server ready on ${nextDevServerHost}:${nextDevServerPort}`);
2130
- }
2131
- }
2132
- // Find a free port for Rivet's Express server (avoids conflict with desktop or other Rivet instances)
2133
- let isGitEnabledForSession = true;
2134
- try {
2135
- const featureFlags = await (0, evaluateFlags_1.getFeatureFlags)();
2136
- isGitEnabledForSession = featureFlags.gitUi.isEnabled;
2137
- }
2138
- catch (error) {
2139
- log.warn('Failed to evaluate feature flags for MCP session startup; defaulting git to enabled:', error);
2140
- }
2141
- let rivetPort;
2142
- try {
2143
- rivetPort = await (0, portUtils_1.findAvailablePort)(index_1.DEFAULT_PORT);
2144
- log.info(`Searching for available port starting from ${index_1.DEFAULT_PORT}... found ${rivetPort}`);
2145
- }
2146
- catch (error) {
2147
- log.warn(`Port discovery failed, falling back to ${index_1.DEFAULT_PORT}:`, error);
2148
- rivetPort = index_1.DEFAULT_PORT;
2149
- }
2150
- // Start the Rivet Express server (with retry on EADDRINUSE to avoid races)
2151
- let retries = 0;
2152
- const maxRetries = 5;
2153
- let serverStarted = false;
2154
- while (!serverStarted && retries < maxRetries) {
2155
- try {
2156
- serverHandle = await (0, server_1.startServer)({
2157
- framework,
2158
- projectPath,
2159
- staticEntry: staticTarget?.entryPath,
2160
- userPort: nextDevServerPort ?? 0,
2161
- userHost: nextDevServerHost,
2162
- sessionBridge: bridge,
2163
- agentVariantsOrchestrator,
2164
- telemetry,
2165
- mcpEditor,
2166
- skipProcessHandlers: true,
2167
- rivetPort,
2168
- isGitEnabled: isGitEnabledForSession,
2169
- });
2170
- serverStarted = true;
2171
- }
2172
- catch (error) {
2173
- if (error?.code === 'EADDRINUSE') {
2174
- retries++;
2175
- rivetPort = await (0, portUtils_1.findAvailablePort)(rivetPort + 1);
2176
- log.debug(`Port in use, retrying with port ${rivetPort} (attempt ${retries}/${maxRetries})`);
2177
- }
2178
- else {
2179
- const message = error instanceof Error ? error.message : 'Server start failed';
2180
- if (devServerProcess) {
2181
- await stopChildProcess(devServerProcess);
2182
- }
2183
- devServerProcess = null;
2184
- return {
2185
- content: [
2186
- {
2187
- type: 'text',
2188
- text: JSON.stringify({ error: message }),
2189
- },
2190
- ],
2191
- isError: true,
2192
- };
2193
- }
2194
- }
2195
- }
2196
- if (!serverStarted) {
2197
- if (devServerProcess) {
2198
- await stopChildProcess(devServerProcess);
2199
- }
2200
- devServerProcess = null;
2201
- return {
2202
- content: [
2203
- {
2204
- type: 'text',
2205
- text: JSON.stringify({
2206
- error: `Could not start Rivet server after ${maxRetries} attempts.`,
2207
- }),
2208
- },
2209
- ],
2210
- isError: true,
2211
- };
2212
- }
2213
- const sessionId = bridge.startSession(projectPath);
2214
- sessionStartedAt = Date.now();
2215
- devServerOwnership = nextDevServerOwnership;
2216
- devServerHost = nextDevServerHost;
2217
- devServerPort = nextDevServerPort;
2218
- bridge.setDevServerContext({
2219
- ownership: devServerOwnership,
2220
- host: devServerHost,
2221
- port: devServerPort,
2222
- startedByRivet: devServerOwnership === 'rivet_owned',
2223
- });
2224
- if (devServerOwnership === 'none') {
2225
- updateBridgeHealth('unknown', null);
2226
- }
2227
- else {
2228
- updateBridgeHealth('healthy', true);
2229
- }
2230
- // Write editor-specific guidance file on first successful open
2231
- if (mcpEditor === 'cursor') {
2232
- (0, skillWriter_1.writeCursorRulesIfNeeded)(projectPath);
2233
- }
2234
- else if (mcpEditor === 'claude' || mcpEditor === undefined) {
2235
- (0, skillWriter_1.writeSkillFileIfNeeded)();
2236
- }
2237
- // Track session start
2238
- telemetry.trackMCPSessionStart({
2239
- framework,
2240
- devServerStarted: devServerOwnership === 'rivet_owned',
2241
- sessionId,
2242
- mcpEditor,
2243
- });
2244
- // Open the editor shell at /rivet. Static projects may also expose the
2245
- // project root at / when a preview backend exists.
2246
- const url = `http://localhost:${rivetPort}/rivet`;
2247
- rivetServerPort = rivetPort;
2248
- rivetServerUrl = url;
2249
- try {
2250
- await (0, open_1.default)(url);
2251
- }
2252
- catch {
2253
- log.warn('Failed to open browser automatically');
2254
- }
2255
- return {
2256
- content: [
2257
- {
2258
- type: 'text',
2259
- text: JSON.stringify({
2260
- sessionId,
2261
- url,
2262
- projectPath,
2263
- framework,
2264
- rivetPort,
2265
- opened: true,
2266
- devServerPort: nextDevServerPort,
2267
- devServerOwnership: nextDevServerOwnership,
2268
- staticEntryPath: staticTarget?.entryPath ?? null,
2269
- previewUrl: framework === 'static'
2270
- ? `http://localhost:${rivetPort}/static/`
2271
- : `http://localhost:${rivetPort}/`,
2272
- startupMode,
2273
- startupModeSelection,
2274
- requestedStartupMode,
2275
- isGitEnabled: isGitEnabledForSession,
2276
- nextAction: (0, exports.buildVisualEditorWatchAction)(sessionId),
2277
- next_step: `Rivet is open. Tell the user: "Rivet is ready — make your visual changes and click '${sendButtonLabel}'." Then immediately call watch_for_changes({ sessionId: "${sessionId}" }) and keep the watch loop active until the user stops or closes the editor. If watch_for_changes times out, call it again. After handling a batch, call it again; for hosts with background workers, dispatch static-preview variant/refine batches and re-watch immediately, and for hosts without background workers, process the scoped batch inline before re-watching.`,
2278
- }),
2279
- },
2280
- ],
2281
- };
2282
- });
2283
- guardedMcp.tool('watch_for_changes', `Required visual-editor watch loop. Call immediately after a successful open_visual_editor response, or after a start_variants response that includes editorNextAction/visualEditor. It blocks until the user sends a change batch, then returns a leased payload with leaseId and kind. After editing files (apply), call watch_for_changes again with refresh_git: true and the leaseId to release the apply lease and refresh git. For UI-originated variant_request/static_preview_refine batches, lease the exact workItemIds and finish generated work with report_work_item; do not use ack_batch to skip or finish generated variant work. If it times out (hasChanges: false), call it again to keep waiting.`, {
2284
- sessionId: v3_1.z
2285
- .string()
2286
- .describe('The session ID returned by open_visual_editor'),
2287
- timeoutSeconds: v3_1.z
2288
- .number()
2289
- .min(1)
2290
- .max(600)
2291
- .optional()
2292
- .describe('How long to wait for the user to send changes (default: 600, max: 600)'),
2293
- message: v3_1.z
2294
- .string()
2295
- .optional()
2296
- .describe('A progress or summary message to show in the Rivet UI before waiting (e.g. your plan or what you just changed)'),
2297
- refresh_git: v3_1.z
2298
- .boolean()
2299
- .optional()
2300
- .describe('After completing an apply lease: set true with leaseId to ack the lease and trigger a git status refresh in the Rivet UI'),
2301
- ack_batch: v3_1.z
2302
- .boolean()
2303
- .optional()
2304
- .describe('Only for variant leases that are not tied to generated work items. UI-originated variant_request/static_preview_refine work is released by report_work_item; unclaimed or unfinished generated work rejects ack_batch.'),
2305
- leaseId: v3_1.z
2306
- .string()
2307
- .optional()
2308
- .describe('The leaseId from the batch you just completed'),
2309
- requestId: v3_1.z
2310
- .string()
2311
- .optional()
2312
- .describe('Optional requestId from the leased batch for idempotent ack'),
2313
- }, async (args) => {
2314
- if (!bridge.isActive() || bridge.getSessionId() !== args.sessionId) {
2315
- return {
2316
- content: [
2317
- {
2318
- type: 'text',
2319
- text: JSON.stringify({ error: 'No active session with that ID' }),
2320
- },
2321
- ],
2322
- isError: true,
2323
- };
2324
- }
2325
- const timeoutMs = Math.min((args.timeoutSeconds ?? 600) * 1000, 600_000);
2326
- if (args.message) {
2327
- bridge.reportFeedback(args.message);
2328
- }
2329
- if (args.refresh_git) {
2330
- handleApplyAck(args.sessionId, {
2331
- leaseId: args.leaseId,
2332
- requestId: args.requestId,
2333
- });
2334
- }
2335
- if (args.ack_batch) {
2336
- const ackError = handleVariantAck({
2337
- leaseId: args.leaseId,
2338
- requestId: args.requestId,
2339
- });
2340
- if (ackError) {
2341
- return ackError;
2342
- }
2343
- }
2344
- // `handleApplyAck` sets READY so the UI can clear comment markers. Do not
2345
- // overwrite it synchronously — the UI polls `/api/mcp/status` and needs
2346
- // one READY observation before the watch loop re-arms.
2347
- if (!args.refresh_git) {
2348
- bridge.setStatus('WATCHING');
2349
- }
2350
- try {
2351
- await bridge.waitForChanges(timeoutMs);
2352
- }
2353
- catch {
2354
- const health = bridge.getDevServerHealth();
2355
- return (0, exports.buildNoPendingChangesResponse)({
2356
- message: 'Timed out waiting for changes. Call watch_for_changes again to keep waiting.',
2357
- devServerHealth: health,
2358
- pendingVariantRequests: bridge.pendingVariantRequestCount(),
2359
- activeLeases: getActiveLeaseMetadata(bridge),
2360
- });
2361
- }
2362
- const routed = leaseAndRouteBatch(args.sessionId, (0, exports.resolveLeasePickupRequestId)(args));
2363
- if (routed) {
2364
- return routed;
2365
- }
2366
- const health = bridge.getDevServerHealth();
2367
- return (0, exports.buildNoPendingChangesResponse)({
2368
- message: 'No pending changes. Call watch_for_changes again to keep waiting.',
2369
- devServerHealth: health,
2370
- pendingVariantRequests: bridge.pendingVariantRequestCount(),
2371
- activeLeases: getActiveLeaseMetadata(bridge),
2372
- });
2373
- });
2374
- guardedMcp.tool('get_pending_changes', `Pick up pending design changes from the Rivet visual editor. Returns a leased batch with leaseId and kind when work is queued. After editing files (apply), call again with refresh_git: true and leaseId to ack and refresh git. For UI-originated variant_request/static_preview_refine batches, lease the exact workItemIds and finish generated work with report_work_item; do not use ack_batch to skip or finish generated variant work. Returns hasChanges: false if no changes are queued yet.`, {
2375
- sessionId: v3_1.z
2376
- .string()
2377
- .describe('The session ID returned by open_visual_editor'),
2378
- message: v3_1.z
2379
- .string()
2380
- .optional()
2381
- .describe('A progress or summary message to show in the Rivet UI (e.g. your plan or what you just changed)'),
2382
- refresh_git: v3_1.z
2383
- .boolean()
2384
- .optional()
2385
- .describe('After completing an apply lease: set true with leaseId to ack and trigger a git status refresh in the Rivet UI'),
2386
- ack_batch: v3_1.z
2387
- .boolean()
2388
- .optional()
2389
- .describe('Only for variant leases that are not tied to generated work items. UI-originated variant_request/static_preview_refine work is released by report_work_item; unclaimed or unfinished generated work rejects ack_batch.'),
2390
- leaseId: v3_1.z
2391
- .string()
2392
- .optional()
2393
- .describe('The leaseId from the batch you just completed'),
2394
- requestId: v3_1.z
2395
- .string()
2396
- .optional()
2397
- .describe('Optional requestId from the leased batch for idempotent ack'),
2398
- }, async (args) => {
2399
- if (!bridge.isActive() || bridge.getSessionId() !== args.sessionId) {
2400
- return {
2401
- content: [
2402
- {
2403
- type: 'text',
2404
- text: JSON.stringify({ error: 'No active session with that ID' }),
2405
- },
2406
- ],
2407
- isError: true,
2408
- };
2409
- }
2410
- if (args.message) {
2411
- bridge.reportFeedback(args.message);
2412
- }
2413
- if (args.refresh_git) {
2414
- handleApplyAck(args.sessionId, {
2415
- leaseId: args.leaseId,
2416
- requestId: args.requestId,
2417
- });
2418
- }
2419
- if (args.ack_batch) {
2420
- const ackError = handleVariantAck({
2421
- leaseId: args.leaseId,
2422
- requestId: args.requestId,
2423
- });
2424
- if (ackError) {
2425
- return ackError;
2426
- }
2427
- }
2428
- const routed = leaseAndRouteBatch(args.sessionId, (0, exports.resolveLeasePickupRequestId)(args));
2429
- if (routed) {
2430
- return routed;
2431
- }
2432
- return (0, exports.buildNoPendingChangesResponse)({
2433
- pendingVariantRequests: bridge.pendingVariantRequestCount(),
2434
- activeLeases: getActiveLeaseMetadata(bridge),
2435
- });
2436
- });
2437
- guardedMcp.tool('close_visual_editor', 'Stop the Rivet visual editing session and clean up resources. Call this when the user is done making visual changes.', sessionIdArgs, async (args) => {
2438
- if (!bridge.isActive() || bridge.getSessionId() !== args.sessionId) {
2439
- return {
2440
- content: [
2441
- {
2442
- type: 'text',
2443
- text: JSON.stringify({ error: 'No active session with that ID' }),
2444
- },
2445
- ],
2446
- isError: true,
2447
- };
2448
- }
2449
- const { totalRoundsCompleted } = await cleanupSessionResources({
2450
- closeSessionId: args.sessionId,
2451
- trackSessionEnd: true,
2452
- });
2453
- return {
2454
- content: [
2455
- {
2456
- type: 'text',
2457
- text: JSON.stringify({ closed: true, totalRoundsCompleted }),
2458
- },
2459
- ],
2460
- };
2461
- });
2462
- // Register the agent-variants MCP tools (orchestrator was constructed at
2463
- // the top of runMcpServer so it's shared with the Express routes).
2464
- // Default inspiration fetcher + design-context store are wired
2465
- // automatically when omitted, so production picks them up here.
2466
- (0, agent_variants_1.registerAgentVariantsTools)(guardedMcp, {
2467
- orchestrator: agentVariantsOrchestrator,
2468
- leaseOwner: () => 'mcp-server',
2469
- ensureVisualEditorOpen: ensureZeroToOneVisualEditorOpen,
2470
- ensureExistingVisualEditorOpen,
2471
- getProjectPath: () => bridge.getProjectPath(),
2472
- resolveVariantRequestProjectPath: (requestId) => bridge.resolveVariantRequestProjectPath(requestId),
2473
- pendingVariantRequestCount: () => bridge.pendingVariantRequestCount(),
2474
- claimVariantRequestLease: variantRequestLeaseTracker.claim,
2475
- completeVariantRequestWork: variantRequestLeaseTracker.complete,
2476
- isVariantSendEnabled: async () => {
2477
- const flags = await (0, evaluateFlags_1.getFeatureFlags)();
2478
- return flags.variantSend.isEnabled;
2479
- },
2480
- telemetry: variantsTelemetry,
2481
- integrationsClient,
2482
- openIntegrationConnectUrl,
2483
- markFirstVariantsRun: () => configManager.markFirstVariantsRun(),
2484
- });
2485
- (0, tools_2.registerIntegrationsTools)(guardedMcp, {
2486
- integrationsClient,
2487
- openIntegrationConnectUrl,
2488
- });
2489
- (0, tools_1.registerAuthTools)(analyticsMcp, {
2490
- authService,
2491
- telemetry,
2492
- mcpEditor,
2493
- revokeBearerToken,
2494
- });
2495
- log.info('agent-variants + integrations + auth MCP tools registered');
2496
- const closeActiveSession = async () => {
2497
- await cleanupSessionResources({ trackSessionEnd: true });
2498
- };
2499
- return { mcp, closeActiveSession, shutdown };
2500
- };
2501
- exports.createRivetMcp = createRivetMcp;
2502
- const startMCPServer = async (mcpEditor) => {
2503
- (0, exports.configureMcpStdioLogging)();
2504
- // Refresh the Claude skill at startup — NOT only after a successful
2505
- // open_visual_editor (the other call sites below). Otherwise stale guidance
2506
- // that refuses an action deadlocks: e.g. a v22 skill that won't open an empty
2507
- // folder means open_visual_editor never runs, so the skill never rewrites,
2508
- // so the agent keeps refusing forever. Writing on boot guarantees the agent
2509
- // loads current guidance on its next turn. Idempotent + version-gated.
2510
- // (Cursor rules stay post-open: they're project-scoped and the project path
2511
- // isn't known until the editor opens.)
2512
- if (mcpEditor === 'claude' || mcpEditor === undefined) {
2513
- (0, skillWriter_1.writeSkillFileIfNeeded)();
2514
- }
2515
- const localAuthBridge = await (0, AuthService_1.startLocalAuthBridgeServer)();
2516
- try {
2517
- const { mcp } = (0, exports.createRivetMcp)({ mcpEditor });
2518
- // Connect via stdio
2519
- const transport = new stdio_js_1.StdioServerTransport();
2520
- await mcp.connect(transport);
2521
- log.info('Rivet MCP server started on stdio');
2522
- // Keep the MCP process alive for as long as the client keeps stdio open.
2523
- // Without this guard, some Node/stdin combinations can allow the process
2524
- // to exit before the client finishes initialization.
2525
- await new Promise((resolve) => {
2526
- process.stdin.once('end', resolve);
2527
- process.stdin.once('close', resolve);
2528
- process.stdin.resume();
2529
- });
2530
- }
2531
- finally {
2532
- await localAuthBridge?.close();
2533
- }
2534
- };
2535
- exports.startMCPServer = startMCPServer;
2536
- //# sourceMappingURL=server.js.map