rivet-design 0.17.3 → 0.17.5

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 (798) hide show
  1. package/bin/rivet.js +0 -16
  2. package/dist/agent-variants/SessionStore.d.ts +0 -239
  3. package/dist/agent-variants/SessionStore.d.ts.map +1 -1
  4. package/dist/agent-variants/SessionStore.js +10 -368
  5. package/dist/agent-variants/SessionStore.js.map +1 -1
  6. package/dist/agent-variants/VariantWorkspaceManager.d.ts +0 -10
  7. package/dist/agent-variants/VariantWorkspaceManager.d.ts.map +1 -1
  8. package/dist/agent-variants/VariantWorkspaceManager.js +0 -17
  9. package/dist/agent-variants/VariantWorkspaceManager.js.map +1 -1
  10. package/dist/agent-variants/WorkerExecutorService.d.ts +1 -105
  11. package/dist/agent-variants/WorkerExecutorService.d.ts.map +1 -1
  12. package/dist/agent-variants/WorkerExecutorService.js +11 -182
  13. package/dist/agent-variants/WorkerExecutorService.js.map +1 -1
  14. package/dist/agent-variants/WorktreeOrchestrator.d.ts +9 -611
  15. package/dist/agent-variants/WorktreeOrchestrator.d.ts.map +1 -1
  16. package/dist/agent-variants/WorktreeOrchestrator.js +111 -1468
  17. package/dist/agent-variants/WorktreeOrchestrator.js.map +1 -1
  18. package/dist/agent-variants/WorktreeOrchestrator.testHelpers.d.ts +0 -7
  19. package/dist/agent-variants/WorktreeOrchestrator.testHelpers.d.ts.map +1 -1
  20. package/dist/agent-variants/WorktreeOrchestrator.testHelpers.js +2 -25
  21. package/dist/agent-variants/WorktreeOrchestrator.testHelpers.js.map +1 -1
  22. package/dist/agent-variants/briefContent.d.ts +0 -11
  23. package/dist/agent-variants/briefContent.d.ts.map +1 -1
  24. package/dist/agent-variants/briefContent.js +0 -12
  25. package/dist/agent-variants/briefContent.js.map +1 -1
  26. package/dist/agent-variants/contextAssets.d.ts +0 -3
  27. package/dist/agent-variants/contextAssets.d.ts.map +1 -1
  28. package/dist/agent-variants/contextAssets.js +0 -3
  29. package/dist/agent-variants/contextAssets.js.map +1 -1
  30. package/dist/agent-variants/contracts.d.ts +5 -493
  31. package/dist/agent-variants/contracts.d.ts.map +1 -1
  32. package/dist/agent-variants/contracts.js +2 -167
  33. package/dist/agent-variants/contracts.js.map +1 -1
  34. package/dist/agent-variants/createProjectArtifacts.d.ts +0 -17
  35. package/dist/agent-variants/createProjectArtifacts.d.ts.map +1 -1
  36. package/dist/agent-variants/createProjectArtifacts.js +0 -10
  37. package/dist/agent-variants/createProjectArtifacts.js.map +1 -1
  38. package/dist/agent-variants/diffQa.d.ts +0 -1
  39. package/dist/agent-variants/diffQa.d.ts.map +1 -1
  40. package/dist/agent-variants/diffQa.js +0 -1
  41. package/dist/agent-variants/diffQa.js.map +1 -1
  42. package/dist/agent-variants/directionAuthoring.d.ts +0 -13
  43. package/dist/agent-variants/directionAuthoring.d.ts.map +1 -1
  44. package/dist/agent-variants/directionAuthoring.js +12 -46
  45. package/dist/agent-variants/directionAuthoring.js.map +1 -1
  46. package/dist/agent-variants/elementRefToTarget.d.ts +0 -17
  47. package/dist/agent-variants/elementRefToTarget.d.ts.map +1 -1
  48. package/dist/agent-variants/elementRefToTarget.js +0 -17
  49. package/dist/agent-variants/elementRefToTarget.js.map +1 -1
  50. package/dist/agent-variants/generatedDestination.d.ts +0 -3
  51. package/dist/agent-variants/generatedDestination.d.ts.map +1 -1
  52. package/dist/agent-variants/generatedDestination.js +0 -3
  53. package/dist/agent-variants/generatedDestination.js.map +1 -1
  54. package/dist/agent-variants/previewQa.d.ts +0 -6
  55. package/dist/agent-variants/previewQa.d.ts.map +1 -1
  56. package/dist/agent-variants/previewQa.js +0 -5
  57. package/dist/agent-variants/previewQa.js.map +1 -1
  58. package/dist/agent-variants/runLabel.d.ts +0 -15
  59. package/dist/agent-variants/runLabel.d.ts.map +1 -1
  60. package/dist/agent-variants/runLabel.js +0 -19
  61. package/dist/agent-variants/runLabel.js.map +1 -1
  62. package/dist/agent-variants/runPlan.d.ts +3 -84
  63. package/dist/agent-variants/runPlan.d.ts.map +1 -1
  64. package/dist/agent-variants/runPlan.js +0 -51
  65. package/dist/agent-variants/runPlan.js.map +1 -1
  66. package/dist/agent-variants/sourceContext.d.ts +0 -10
  67. package/dist/agent-variants/sourceContext.d.ts.map +1 -1
  68. package/dist/agent-variants/sourceContext.js +0 -36
  69. package/dist/agent-variants/sourceContext.js.map +1 -1
  70. package/dist/agent-variants/variantContext.d.ts +0 -6
  71. package/dist/agent-variants/variantContext.d.ts.map +1 -1
  72. package/dist/agent-variants/variantContext.js +0 -9
  73. package/dist/agent-variants/variantContext.js.map +1 -1
  74. package/dist/agent-variants/videoKeyframes.d.ts +0 -7
  75. package/dist/agent-variants/videoKeyframes.d.ts.map +1 -1
  76. package/dist/agent-variants/videoKeyframes.js +0 -7
  77. package/dist/agent-variants/videoKeyframes.js.map +1 -1
  78. package/dist/agent-variants/workItemBriefing.d.ts +0 -21
  79. package/dist/agent-variants/workItemBriefing.d.ts.map +1 -1
  80. package/dist/agent-variants/workItemBriefing.js +0 -49
  81. package/dist/agent-variants/workItemBriefing.js.map +1 -1
  82. package/dist/agent-variants/workerRuntime.d.ts +1 -137
  83. package/dist/agent-variants/workerRuntime.d.ts.map +1 -1
  84. package/dist/agent-variants/workerRuntime.js +8 -147
  85. package/dist/agent-variants/workerRuntime.js.map +1 -1
  86. package/dist/cli/browser.d.ts +0 -1
  87. package/dist/cli/browser.d.ts.map +1 -1
  88. package/dist/cli/browser.js +0 -1
  89. package/dist/cli/browser.js.map +1 -1
  90. package/dist/cli/callerRunnerPaths.d.ts +0 -50
  91. package/dist/cli/callerRunnerPaths.d.ts.map +1 -1
  92. package/dist/cli/callerRunnerPaths.js +0 -59
  93. package/dist/cli/callerRunnerPaths.js.map +1 -1
  94. package/dist/cli/client.d.ts +0 -18
  95. package/dist/cli/client.d.ts.map +1 -1
  96. package/dist/cli/client.js +0 -28
  97. package/dist/cli/client.js.map +1 -1
  98. package/dist/cli/commandNames.d.ts +0 -7
  99. package/dist/cli/commandNames.d.ts.map +1 -1
  100. package/dist/cli/commandNames.js +0 -7
  101. package/dist/cli/commandNames.js.map +1 -1
  102. package/dist/cli/commands/auth.d.ts +0 -6
  103. package/dist/cli/commands/auth.d.ts.map +1 -1
  104. package/dist/cli/commands/auth.js +0 -6
  105. package/dist/cli/commands/auth.js.map +1 -1
  106. package/dist/cli/commands/elementSelector.d.ts +0 -6
  107. package/dist/cli/commands/elementSelector.d.ts.map +1 -1
  108. package/dist/cli/commands/elementSelector.js +0 -7
  109. package/dist/cli/commands/elementSelector.js.map +1 -1
  110. package/dist/cli/commands/evidence.d.ts +0 -6
  111. package/dist/cli/commands/evidence.d.ts.map +1 -1
  112. package/dist/cli/commands/evidence.js +0 -6
  113. package/dist/cli/commands/evidence.js.map +1 -1
  114. package/dist/cli/commands/libraryOpen.d.ts +0 -1
  115. package/dist/cli/commands/libraryOpen.d.ts.map +1 -1
  116. package/dist/cli/commands/libraryOpen.js +3 -3
  117. package/dist/cli/commands/libraryOpen.js.map +1 -1
  118. package/dist/cli/commands/mcpServe.d.ts +0 -24
  119. package/dist/cli/commands/mcpServe.d.ts.map +1 -1
  120. package/dist/cli/commands/mcpServe.js +31 -102
  121. package/dist/cli/commands/mcpServe.js.map +1 -1
  122. package/dist/cli/commands/open.d.ts +0 -8
  123. package/dist/cli/commands/open.d.ts.map +1 -1
  124. package/dist/cli/commands/open.js +0 -57
  125. package/dist/cli/commands/open.js.map +1 -1
  126. package/dist/cli/commands/reference.d.ts +0 -5
  127. package/dist/cli/commands/reference.d.ts.map +1 -1
  128. package/dist/cli/commands/reference.js +0 -27
  129. package/dist/cli/commands/reference.js.map +1 -1
  130. package/dist/cli/commands/serve.d.ts +0 -5
  131. package/dist/cli/commands/serve.d.ts.map +1 -1
  132. package/dist/cli/commands/serve.js +0 -34
  133. package/dist/cli/commands/serve.js.map +1 -1
  134. package/dist/cli/commands/shared.d.ts +0 -16
  135. package/dist/cli/commands/shared.d.ts.map +1 -1
  136. package/dist/cli/commands/shared.js +0 -19
  137. package/dist/cli/commands/shared.js.map +1 -1
  138. package/dist/cli/commands/status.d.ts +0 -6
  139. package/dist/cli/commands/status.d.ts.map +1 -1
  140. package/dist/cli/commands/status.js +0 -11
  141. package/dist/cli/commands/status.js.map +1 -1
  142. package/dist/cli/commands/stop.d.ts +0 -5
  143. package/dist/cli/commands/stop.d.ts.map +1 -1
  144. package/dist/cli/commands/stop.js +0 -6
  145. package/dist/cli/commands/stop.js.map +1 -1
  146. package/dist/cli/commands/update.d.ts +0 -1
  147. package/dist/cli/commands/update.d.ts.map +1 -1
  148. package/dist/cli/commands/update.js +0 -1
  149. package/dist/cli/commands/update.js.map +1 -1
  150. package/dist/cli/commands/variants.d.ts +0 -1
  151. package/dist/cli/commands/variants.d.ts.map +1 -1
  152. package/dist/cli/commands/variants.js +0 -31
  153. package/dist/cli/commands/variants.js.map +1 -1
  154. package/dist/cli/commands/wait.d.ts +0 -21
  155. package/dist/cli/commands/wait.d.ts.map +1 -1
  156. package/dist/cli/commands/wait.js +0 -23
  157. package/dist/cli/commands/wait.js.map +1 -1
  158. package/dist/cli/entry.d.ts +0 -10
  159. package/dist/cli/entry.d.ts.map +1 -1
  160. package/dist/cli/entry.js +0 -13
  161. package/dist/cli/entry.js.map +1 -1
  162. package/dist/cli/envelope.d.ts +1 -25
  163. package/dist/cli/envelope.d.ts.map +1 -1
  164. package/dist/cli/envelope.js +0 -19
  165. package/dist/cli/envelope.js.map +1 -1
  166. package/dist/cli/flags.d.ts +0 -7
  167. package/dist/cli/flags.d.ts.map +1 -1
  168. package/dist/cli/flags.js +0 -7
  169. package/dist/cli/flags.js.map +1 -1
  170. package/dist/cli/handoff.d.ts +0 -33
  171. package/dist/cli/handoff.d.ts.map +1 -1
  172. package/dist/cli/handoff.js +0 -42
  173. package/dist/cli/handoff.js.map +1 -1
  174. package/dist/cli/handoffStore.d.ts +0 -11
  175. package/dist/cli/handoffStore.d.ts.map +1 -1
  176. package/dist/cli/handoffStore.js +0 -10
  177. package/dist/cli/handoffStore.js.map +1 -1
  178. package/dist/cli/hostWorkNextAction.d.ts +0 -11
  179. package/dist/cli/hostWorkNextAction.d.ts.map +1 -1
  180. package/dist/cli/hostWorkNextAction.js +0 -4
  181. package/dist/cli/hostWorkNextAction.js.map +1 -1
  182. package/dist/cli/io.d.ts +0 -9
  183. package/dist/cli/io.d.ts.map +1 -1
  184. package/dist/cli/io.js +0 -1
  185. package/dist/cli/io.js.map +1 -1
  186. package/dist/cli/resolveDefaultNodeEnv.d.ts +0 -5
  187. package/dist/cli/resolveDefaultNodeEnv.d.ts.map +1 -1
  188. package/dist/cli/resolveDefaultNodeEnv.js +0 -5
  189. package/dist/cli/resolveDefaultNodeEnv.js.map +1 -1
  190. package/dist/cli/router.d.ts +0 -21
  191. package/dist/cli/router.d.ts.map +1 -1
  192. package/dist/cli/router.js +0 -46
  193. package/dist/cli/router.js.map +1 -1
  194. package/dist/cli/runnerSelection.d.ts +0 -58
  195. package/dist/cli/runnerSelection.d.ts.map +1 -1
  196. package/dist/cli/runnerSelection.js +0 -16
  197. package/dist/cli/runnerSelection.js.map +1 -1
  198. package/dist/cli/serverHandshake.d.ts +0 -36
  199. package/dist/cli/serverHandshake.d.ts.map +1 -1
  200. package/dist/cli/serverHandshake.js +0 -29
  201. package/dist/cli/serverHandshake.js.map +1 -1
  202. package/dist/cli/serverUnavailable.d.ts +0 -1
  203. package/dist/cli/serverUnavailable.d.ts.map +1 -1
  204. package/dist/cli/serverUnavailable.js +0 -1
  205. package/dist/cli/serverUnavailable.js.map +1 -1
  206. package/dist/cli/serverVersion.d.ts +0 -22
  207. package/dist/cli/serverVersion.d.ts.map +1 -1
  208. package/dist/cli/serverVersion.js +0 -12
  209. package/dist/cli/serverVersion.js.map +1 -1
  210. package/dist/cli/telemetry.d.ts +0 -4
  211. package/dist/cli/telemetry.d.ts.map +1 -1
  212. package/dist/cli/telemetry.js +0 -4
  213. package/dist/cli/telemetry.js.map +1 -1
  214. package/dist/config/evaluateFlags.d.ts +0 -4
  215. package/dist/config/evaluateFlags.d.ts.map +1 -1
  216. package/dist/config/evaluateFlags.js +0 -11
  217. package/dist/config/evaluateFlags.js.map +1 -1
  218. package/dist/config/featureFlagUserKey.d.ts +0 -3
  219. package/dist/config/featureFlagUserKey.d.ts.map +1 -1
  220. package/dist/config/featureFlagUserKey.js +0 -3
  221. package/dist/config/featureFlagUserKey.js.map +1 -1
  222. package/dist/config/flags.d.ts +0 -14
  223. package/dist/config/flags.d.ts.map +1 -1
  224. package/dist/config/flags.js +0 -27
  225. package/dist/config/flags.js.map +1 -1
  226. package/dist/config/proxy.d.ts +0 -3
  227. package/dist/config/proxy.d.ts.map +1 -1
  228. package/dist/config/proxy.js +0 -11
  229. package/dist/config/proxy.js.map +1 -1
  230. package/dist/config/telemetry.d.ts +0 -13
  231. package/dist/config/telemetry.d.ts.map +1 -1
  232. package/dist/config/telemetry.js +0 -13
  233. package/dist/config/telemetry.js.map +1 -1
  234. package/dist/config/telemetryContract.d.ts +0 -1
  235. package/dist/config/telemetryContract.d.ts.map +1 -1
  236. package/dist/config/telemetryContract.js +0 -1
  237. package/dist/config/telemetryContract.js.map +1 -1
  238. package/dist/hosts/workerDefaults.d.ts +0 -69
  239. package/dist/hosts/workerDefaults.d.ts.map +1 -1
  240. package/dist/hosts/workerDefaults.js +0 -68
  241. package/dist/hosts/workerDefaults.js.map +1 -1
  242. package/dist/index-core.d.ts.map +1 -1
  243. package/dist/index-core.js +0 -2
  244. package/dist/index-core.js.map +1 -1
  245. package/dist/index.d.ts +0 -20
  246. package/dist/index.d.ts.map +1 -1
  247. package/dist/index.js +2 -105
  248. package/dist/index.js.map +1 -1
  249. package/dist/install/allowlists.d.ts +0 -32
  250. package/dist/install/allowlists.d.ts.map +1 -1
  251. package/dist/install/allowlists.js +0 -44
  252. package/dist/install/allowlists.js.map +1 -1
  253. package/dist/install/automaticCliUpdate.d.ts +0 -6
  254. package/dist/install/automaticCliUpdate.d.ts.map +1 -1
  255. package/dist/install/automaticCliUpdate.js +0 -13
  256. package/dist/install/automaticCliUpdate.js.map +1 -1
  257. package/dist/install/channelCli.d.ts +0 -1
  258. package/dist/install/channelCli.d.ts.map +1 -1
  259. package/dist/install/channelCli.js +0 -3
  260. package/dist/install/channelCli.js.map +1 -1
  261. package/dist/install/channelManager.d.ts +0 -14
  262. package/dist/install/channelManager.d.ts.map +1 -1
  263. package/dist/install/channelManager.js +0 -22
  264. package/dist/install/channelManager.js.map +1 -1
  265. package/dist/install/codexGuidance.d.ts +0 -1
  266. package/dist/install/codexGuidance.d.ts.map +1 -1
  267. package/dist/install/codexGuidance.js +0 -7
  268. package/dist/install/codexGuidance.js.map +1 -1
  269. package/dist/install/globalBootstrap.d.ts +0 -25
  270. package/dist/install/globalBootstrap.d.ts.map +1 -1
  271. package/dist/install/globalBootstrap.js +0 -10
  272. package/dist/install/globalBootstrap.js.map +1 -1
  273. package/dist/install/harnesses.d.ts +0 -66
  274. package/dist/install/harnesses.d.ts.map +1 -1
  275. package/dist/install/harnesses.js +0 -60
  276. package/dist/install/harnesses.js.map +1 -1
  277. package/dist/install/installationChannel.d.ts +0 -1
  278. package/dist/install/installationChannel.d.ts.map +1 -1
  279. package/dist/install/installationChannel.js +0 -1
  280. package/dist/install/installationChannel.js.map +1 -1
  281. package/dist/install/legacyMcp.d.ts +0 -23
  282. package/dist/install/legacyMcp.d.ts.map +1 -1
  283. package/dist/install/legacyMcp.js +3 -46
  284. package/dist/install/legacyMcp.js.map +1 -1
  285. package/dist/install/mcpRegistration.d.ts +0 -41
  286. package/dist/install/mcpRegistration.d.ts.map +1 -1
  287. package/dist/install/mcpRegistration.js +2 -70
  288. package/dist/install/mcpRegistration.js.map +1 -1
  289. package/dist/library/HubClient.d.ts +0 -2
  290. package/dist/library/HubClient.d.ts.map +1 -1
  291. package/dist/library/HubClient.js +0 -4
  292. package/dist/library/HubClient.js.map +1 -1
  293. package/dist/library/HubServer.d.ts +0 -1
  294. package/dist/library/HubServer.d.ts.map +1 -1
  295. package/dist/library/HubServer.js +0 -1
  296. package/dist/library/HubServer.js.map +1 -1
  297. package/dist/library/StaticPreviewServer.d.ts +0 -1
  298. package/dist/library/StaticPreviewServer.d.ts.map +1 -1
  299. package/dist/library/StaticPreviewServer.js +0 -1
  300. package/dist/library/StaticPreviewServer.js.map +1 -1
  301. package/dist/library/http.d.ts +0 -2
  302. package/dist/library/http.d.ts.map +1 -1
  303. package/dist/library/http.js +0 -2
  304. package/dist/library/http.js.map +1 -1
  305. package/dist/library/importFiles.d.ts +0 -6
  306. package/dist/library/importFiles.d.ts.map +1 -1
  307. package/dist/library/importFiles.js +45 -11
  308. package/dist/library/importFiles.js.map +1 -1
  309. package/dist/mcp/changeBatchClassification.d.ts +0 -40
  310. package/dist/mcp/changeBatchClassification.d.ts.map +1 -1
  311. package/dist/mcp/changeBatchClassification.js +0 -34
  312. package/dist/mcp/changeBatchClassification.js.map +1 -1
  313. package/dist/prompts/commentVariationPrompts.d.ts +0 -5
  314. package/dist/prompts/commentVariationPrompts.d.ts.map +1 -1
  315. package/dist/prompts/commentVariationPrompts.js +0 -5
  316. package/dist/prompts/commentVariationPrompts.js.map +1 -1
  317. package/dist/prototype-host.js +0 -15
  318. package/dist/prototype-host.js.map +1 -1
  319. package/dist/proxy-middleware/preview-bridge.d.ts +0 -2
  320. package/dist/proxy-middleware/preview-bridge.d.ts.map +1 -1
  321. package/dist/proxy-middleware/preview-bridge.js +7 -7
  322. package/dist/proxy-middleware/preview-bridge.js.map +1 -1
  323. package/dist/proxy-middleware/preview-connections.d.ts +8 -0
  324. package/dist/proxy-middleware/preview-connections.d.ts.map +1 -0
  325. package/dist/proxy-middleware/preview-connections.js +29 -0
  326. package/dist/proxy-middleware/preview-connections.js.map +1 -0
  327. package/dist/proxy-middleware/proxy-config.d.ts +0 -12
  328. package/dist/proxy-middleware/proxy-config.d.ts.map +1 -1
  329. package/dist/proxy-middleware/proxy-config.js +41 -90
  330. package/dist/proxy-middleware/proxy-config.js.map +1 -1
  331. package/dist/routes/account.d.ts +0 -1
  332. package/dist/routes/account.d.ts.map +1 -1
  333. package/dist/routes/account.js +0 -2
  334. package/dist/routes/account.js.map +1 -1
  335. package/dist/routes/agentVariants.d.ts +0 -15
  336. package/dist/routes/agentVariants.d.ts.map +1 -1
  337. package/dist/routes/agentVariants.js +14 -325
  338. package/dist/routes/agentVariants.js.map +1 -1
  339. package/dist/routes/cli.d.ts +0 -17
  340. package/dist/routes/cli.d.ts.map +1 -1
  341. package/dist/routes/cli.js +0 -13
  342. package/dist/routes/cli.js.map +1 -1
  343. package/dist/routes/emptyStatePage.d.ts +0 -12
  344. package/dist/routes/emptyStatePage.d.ts.map +1 -1
  345. package/dist/routes/emptyStatePage.js +0 -12
  346. package/dist/routes/emptyStatePage.js.map +1 -1
  347. package/dist/routes/git.d.ts.map +1 -1
  348. package/dist/routes/git.js +0 -13
  349. package/dist/routes/git.js.map +1 -1
  350. package/dist/routes/inlineStartTracker.d.ts +0 -16
  351. package/dist/routes/inlineStartTracker.d.ts.map +1 -1
  352. package/dist/routes/inlineStartTracker.js.map +1 -1
  353. package/dist/routes/integrations.d.ts +0 -6
  354. package/dist/routes/integrations.d.ts.map +1 -1
  355. package/dist/routes/integrations.js +0 -6
  356. package/dist/routes/integrations.js.map +1 -1
  357. package/dist/routes/library/app.d.ts +0 -1
  358. package/dist/routes/library/app.d.ts.map +1 -1
  359. package/dist/routes/library/app.js +0 -1
  360. package/dist/routes/library/app.js.map +1 -1
  361. package/dist/routes/library/index.d.ts +0 -1
  362. package/dist/routes/library/index.d.ts.map +1 -1
  363. package/dist/routes/library/index.js +0 -1
  364. package/dist/routes/library/index.js.map +1 -1
  365. package/dist/routes/library/middleware.d.ts +0 -3
  366. package/dist/routes/library/middleware.d.ts.map +1 -1
  367. package/dist/routes/library/middleware.js +0 -4
  368. package/dist/routes/library/middleware.js.map +1 -1
  369. package/dist/routes/library/navigation.d.ts +0 -1
  370. package/dist/routes/library/navigation.d.ts.map +1 -1
  371. package/dist/routes/library/navigation.js +0 -1
  372. package/dist/routes/library/navigation.js.map +1 -1
  373. package/dist/routes/library/projects.d.ts +0 -1
  374. package/dist/routes/library/projects.d.ts.map +1 -1
  375. package/dist/routes/library/projects.js +0 -1
  376. package/dist/routes/library/projects.js.map +1 -1
  377. package/dist/routes/library/staticPreview.d.ts +0 -1
  378. package/dist/routes/library/staticPreview.d.ts.map +1 -1
  379. package/dist/routes/library/staticPreview.js +0 -1
  380. package/dist/routes/library/staticPreview.js.map +1 -1
  381. package/dist/routes/library/variants.d.ts +0 -1
  382. package/dist/routes/library/variants.d.ts.map +1 -1
  383. package/dist/routes/library/variants.js +0 -1
  384. package/dist/routes/library/variants.js.map +1 -1
  385. package/dist/routes/mcp.d.ts +0 -3
  386. package/dist/routes/mcp.d.ts.map +1 -1
  387. package/dist/routes/mcp.js +3 -4
  388. package/dist/routes/mcp.js.map +1 -1
  389. package/dist/routes/prototypeHost.d.ts +0 -2
  390. package/dist/routes/prototypeHost.d.ts.map +1 -1
  391. package/dist/routes/prototypeHost.js +6 -32
  392. package/dist/routes/prototypeHost.js.map +1 -1
  393. package/dist/routes/static.d.ts +0 -3
  394. package/dist/routes/static.d.ts.map +1 -1
  395. package/dist/routes/static.js +0 -6
  396. package/dist/routes/static.js.map +1 -1
  397. package/dist/routes/support.d.ts +0 -3
  398. package/dist/routes/support.d.ts.map +1 -1
  399. package/dist/routes/support.js +0 -3
  400. package/dist/routes/support.js.map +1 -1
  401. package/dist/server.d.ts +0 -42
  402. package/dist/server.d.ts.map +1 -1
  403. package/dist/server.js +1 -129
  404. package/dist/server.js.map +1 -1
  405. package/dist/services/AuthService.d.ts +0 -76
  406. package/dist/services/AuthService.d.ts.map +1 -1
  407. package/dist/services/AuthService.js +0 -112
  408. package/dist/services/AuthService.js.map +1 -1
  409. package/dist/services/BrowserAgentClient.d.ts +1 -21
  410. package/dist/services/BrowserAgentClient.d.ts.map +1 -1
  411. package/dist/services/BrowserAgentClient.js +0 -10
  412. package/dist/services/BrowserAgentClient.js.map +1 -1
  413. package/dist/services/BundleStore.d.ts +0 -18
  414. package/dist/services/BundleStore.d.ts.map +1 -1
  415. package/dist/services/BundleStore.js +0 -10
  416. package/dist/services/BundleStore.js.map +1 -1
  417. package/dist/services/CliLogForwarder.d.ts +0 -26
  418. package/dist/services/CliLogForwarder.d.ts.map +1 -1
  419. package/dist/services/CliLogForwarder.js +1 -33
  420. package/dist/services/CliLogForwarder.js.map +1 -1
  421. package/dist/services/CommitMessageGenerator.d.ts +0 -6
  422. package/dist/services/CommitMessageGenerator.d.ts.map +1 -1
  423. package/dist/services/CommitMessageGenerator.js +0 -3
  424. package/dist/services/CommitMessageGenerator.js.map +1 -1
  425. package/dist/services/ComponentSearchService.d.ts +2 -16
  426. package/dist/services/ComponentSearchService.d.ts.map +1 -1
  427. package/dist/services/ComponentSearchService.js +2 -56
  428. package/dist/services/ComponentSearchService.js.map +1 -1
  429. package/dist/services/ConfigManager.d.ts +0 -113
  430. package/dist/services/ConfigManager.d.ts.map +1 -1
  431. package/dist/services/ConfigManager.js +0 -101
  432. package/dist/services/ConfigManager.js.map +1 -1
  433. package/dist/services/DependencyPreparationService.d.ts +28 -0
  434. package/dist/services/DependencyPreparationService.d.ts.map +1 -0
  435. package/dist/services/DependencyPreparationService.js +322 -0
  436. package/dist/services/DependencyPreparationService.js.map +1 -0
  437. package/dist/services/DevServerRuntimeService.d.ts +0 -20
  438. package/dist/services/DevServerRuntimeService.d.ts.map +1 -1
  439. package/dist/services/DevServerRuntimeService.js +0 -46
  440. package/dist/services/DevServerRuntimeService.js.map +1 -1
  441. package/dist/services/FeatureFlagService.d.ts +0 -15
  442. package/dist/services/FeatureFlagService.d.ts.map +1 -1
  443. package/dist/services/FeatureFlagService.js +0 -23
  444. package/dist/services/FeatureFlagService.js.map +1 -1
  445. package/dist/services/GatewayClient.d.ts +0 -27
  446. package/dist/services/GatewayClient.d.ts.map +1 -1
  447. package/dist/services/GatewayClient.js +0 -25
  448. package/dist/services/GatewayClient.js.map +1 -1
  449. package/dist/services/HistoryReplayService.d.ts +2 -32
  450. package/dist/services/HistoryReplayService.d.ts.map +1 -1
  451. package/dist/services/HistoryReplayService.js +55 -65
  452. package/dist/services/HistoryReplayService.js.map +1 -1
  453. package/dist/services/HubDiscovery.d.ts +0 -8
  454. package/dist/services/HubDiscovery.d.ts.map +1 -1
  455. package/dist/services/HubDiscovery.js +0 -8
  456. package/dist/services/HubDiscovery.js.map +1 -1
  457. package/dist/services/HubNavigation.d.ts +0 -6
  458. package/dist/services/HubNavigation.d.ts.map +1 -1
  459. package/dist/services/HubNavigation.js +5 -8
  460. package/dist/services/HubNavigation.js.map +1 -1
  461. package/dist/services/IntegrationsClient.d.ts +0 -34
  462. package/dist/services/IntegrationsClient.d.ts.map +1 -1
  463. package/dist/services/IntegrationsClient.js +0 -27
  464. package/dist/services/IntegrationsClient.js.map +1 -1
  465. package/dist/services/LocalGitService.d.ts +0 -57
  466. package/dist/services/LocalGitService.d.ts.map +1 -1
  467. package/dist/services/LocalGitService.js +4 -85
  468. package/dist/services/LocalGitService.js.map +1 -1
  469. package/dist/services/MCPOnboardingTelemetry.d.ts +0 -9
  470. package/dist/services/MCPOnboardingTelemetry.d.ts.map +1 -1
  471. package/dist/services/MCPOnboardingTelemetry.js +0 -9
  472. package/dist/services/MCPOnboardingTelemetry.js.map +1 -1
  473. package/dist/services/MCPVariantsTelemetry.d.ts +0 -2
  474. package/dist/services/MCPVariantsTelemetry.d.ts.map +1 -1
  475. package/dist/services/MCPVariantsTelemetry.js +0 -2
  476. package/dist/services/MCPVariantsTelemetry.js.map +1 -1
  477. package/dist/services/OperatingSystemServices.d.ts +0 -22
  478. package/dist/services/OperatingSystemServices.d.ts.map +1 -1
  479. package/dist/services/OperatingSystemServices.js +22 -80
  480. package/dist/services/OperatingSystemServices.js.map +1 -1
  481. package/dist/services/PRTitleGenerator.d.ts +0 -7
  482. package/dist/services/PRTitleGenerator.d.ts.map +1 -1
  483. package/dist/services/PRTitleGenerator.js +0 -3
  484. package/dist/services/PRTitleGenerator.js.map +1 -1
  485. package/dist/services/ProjectCardPreviews.d.ts +0 -5
  486. package/dist/services/ProjectCardPreviews.d.ts.map +1 -1
  487. package/dist/services/ProjectCardPreviews.js +0 -5
  488. package/dist/services/ProjectCardPreviews.js.map +1 -1
  489. package/dist/services/ProjectDetectionService.d.ts +0 -4
  490. package/dist/services/ProjectDetectionService.d.ts.map +1 -1
  491. package/dist/services/ProjectDetectionService.js +2 -44
  492. package/dist/services/ProjectDetectionService.js.map +1 -1
  493. package/dist/services/ProjectFolderPicker.d.ts +0 -3
  494. package/dist/services/ProjectFolderPicker.d.ts.map +1 -1
  495. package/dist/services/ProjectFolderPicker.js +0 -4
  496. package/dist/services/ProjectFolderPicker.js.map +1 -1
  497. package/dist/services/ProjectImport.d.ts +0 -1
  498. package/dist/services/ProjectImport.d.ts.map +1 -1
  499. package/dist/services/ProjectImport.js +0 -3
  500. package/dist/services/ProjectImport.js.map +1 -1
  501. package/dist/services/ProjectLibrary.d.ts +0 -18
  502. package/dist/services/ProjectLibrary.d.ts.map +1 -1
  503. package/dist/services/ProjectLibrary.js +0 -20
  504. package/dist/services/ProjectLibrary.js.map +1 -1
  505. package/dist/services/ProjectMetadataStore.d.ts +0 -73
  506. package/dist/services/ProjectMetadataStore.d.ts.map +1 -1
  507. package/dist/services/ProjectMetadataStore.js +0 -90
  508. package/dist/services/ProjectMetadataStore.js.map +1 -1
  509. package/dist/services/ProjectPackageManager.d.ts +23 -0
  510. package/dist/services/ProjectPackageManager.d.ts.map +1 -0
  511. package/dist/services/ProjectPackageManager.js +185 -0
  512. package/dist/services/ProjectPackageManager.js.map +1 -0
  513. package/dist/services/ProjectRegistry.d.ts +0 -16
  514. package/dist/services/ProjectRegistry.d.ts.map +1 -1
  515. package/dist/services/ProjectRegistry.js +0 -18
  516. package/dist/services/ProjectRegistry.js.map +1 -1
  517. package/dist/services/ProjectRuntimeService.d.ts +0 -4
  518. package/dist/services/ProjectRuntimeService.d.ts.map +1 -1
  519. package/dist/services/ProjectRuntimeService.js +0 -5
  520. package/dist/services/ProjectRuntimeService.js.map +1 -1
  521. package/dist/services/ProjectScreenshotService.d.ts +0 -5
  522. package/dist/services/ProjectScreenshotService.d.ts.map +1 -1
  523. package/dist/services/ProjectScreenshotService.js +0 -6
  524. package/dist/services/ProjectScreenshotService.js.map +1 -1
  525. package/dist/services/ProjectStorage.d.ts +0 -4
  526. package/dist/services/ProjectStorage.d.ts.map +1 -1
  527. package/dist/services/ProjectStorage.js +0 -4
  528. package/dist/services/ProjectStorage.js.map +1 -1
  529. package/dist/services/PrototypeBuildService.d.ts +1 -23
  530. package/dist/services/PrototypeBuildService.d.ts.map +1 -1
  531. package/dist/services/PrototypeBuildService.js +6 -31
  532. package/dist/services/PrototypeBuildService.js.map +1 -1
  533. package/dist/services/PrototypeDeployService.d.ts +0 -20
  534. package/dist/services/PrototypeDeployService.d.ts.map +1 -1
  535. package/dist/services/PrototypeDeployService.js +1 -24
  536. package/dist/services/PrototypeDeployService.js.map +1 -1
  537. package/dist/services/RequestAuthContext.d.ts +0 -5
  538. package/dist/services/RequestAuthContext.d.ts.map +1 -1
  539. package/dist/services/RequestAuthContext.js +0 -5
  540. package/dist/services/RequestAuthContext.js.map +1 -1
  541. package/dist/services/SessionBridgeService.d.ts.map +1 -1
  542. package/dist/services/SessionBridgeService.js +0 -2
  543. package/dist/services/SessionBridgeService.js.map +1 -1
  544. package/dist/services/SessionService.d.ts +0 -4
  545. package/dist/services/SessionService.d.ts.map +1 -1
  546. package/dist/services/SessionService.js +1 -22
  547. package/dist/services/SessionService.js.map +1 -1
  548. package/dist/services/StaticFileService.d.ts +0 -9
  549. package/dist/services/StaticFileService.d.ts.map +1 -1
  550. package/dist/services/StaticFileService.js +0 -11
  551. package/dist/services/StaticFileService.js.map +1 -1
  552. package/dist/services/StaticLibraryPreviews.d.ts +0 -3
  553. package/dist/services/StaticLibraryPreviews.d.ts.map +1 -1
  554. package/dist/services/StaticLibraryPreviews.js +0 -3
  555. package/dist/services/StaticLibraryPreviews.js.map +1 -1
  556. package/dist/services/StaticPreviewServer.d.ts +0 -16
  557. package/dist/services/StaticPreviewServer.d.ts.map +1 -1
  558. package/dist/services/StaticPreviewServer.js +2 -28
  559. package/dist/services/StaticPreviewServer.js.map +1 -1
  560. package/dist/services/SupabaseAuthService.d.ts +0 -11
  561. package/dist/services/SupabaseAuthService.d.ts.map +1 -1
  562. package/dist/services/SupabaseAuthService.js +0 -11
  563. package/dist/services/SupabaseAuthService.js.map +1 -1
  564. package/dist/services/SupportTicketService.d.ts +0 -4
  565. package/dist/services/SupportTicketService.d.ts.map +1 -1
  566. package/dist/services/SupportTicketService.js +0 -4
  567. package/dist/services/SupportTicketService.js.map +1 -1
  568. package/dist/services/TelemetryService.d.ts +0 -182
  569. package/dist/services/TelemetryService.d.ts.map +1 -1
  570. package/dist/services/TelemetryService.js +0 -193
  571. package/dist/services/TelemetryService.js.map +1 -1
  572. package/dist/services/TreeStore.d.ts +0 -72
  573. package/dist/services/TreeStore.d.ts.map +1 -1
  574. package/dist/services/TreeStore.js +2 -101
  575. package/dist/services/TreeStore.js.map +1 -1
  576. package/dist/services/VariantGenerationService.d.ts +0 -32
  577. package/dist/services/VariantGenerationService.d.ts.map +1 -1
  578. package/dist/services/VariantGenerationService.js +5 -62
  579. package/dist/services/VariantGenerationService.js.map +1 -1
  580. package/dist/services/VariantHistoryService.d.ts +0 -114
  581. package/dist/services/VariantHistoryService.d.ts.map +1 -1
  582. package/dist/services/VariantHistoryService.js +2 -144
  583. package/dist/services/VariantHistoryService.js.map +1 -1
  584. package/dist/services/VariantRunService.d.ts +0 -31
  585. package/dist/services/VariantRunService.d.ts.map +1 -1
  586. package/dist/services/VariantRunService.js +0 -35
  587. package/dist/services/VariantRunService.js.map +1 -1
  588. package/dist/services/VariantsRuntime.d.ts +0 -3
  589. package/dist/services/VariantsRuntime.d.ts.map +1 -1
  590. package/dist/services/VariantsRuntime.js +0 -5
  591. package/dist/services/VariantsRuntime.js.map +1 -1
  592. package/dist/services/WorktreeManager.d.ts +1 -220
  593. package/dist/services/WorktreeManager.d.ts.map +1 -1
  594. package/dist/services/WorktreeManager.js +75 -372
  595. package/dist/services/WorktreeManager.js.map +1 -1
  596. package/dist/services/accessTokenRefresh.d.ts +0 -25
  597. package/dist/services/accessTokenRefresh.d.ts.map +1 -1
  598. package/dist/services/accessTokenRefresh.js +0 -25
  599. package/dist/services/accessTokenRefresh.js.map +1 -1
  600. package/dist/services/adapters/TailwindTokenAdapter.d.ts +0 -29
  601. package/dist/services/adapters/TailwindTokenAdapter.d.ts.map +1 -1
  602. package/dist/services/adapters/TailwindTokenAdapter.js +1 -46
  603. package/dist/services/adapters/TailwindTokenAdapter.js.map +1 -1
  604. package/dist/services/createAgentVariantsOrchestrator.d.ts +0 -18
  605. package/dist/services/createAgentVariantsOrchestrator.d.ts.map +1 -1
  606. package/dist/services/createAgentVariantsOrchestrator.js +0 -22
  607. package/dist/services/createAgentVariantsOrchestrator.js.map +1 -1
  608. package/dist/services/extractors/CSSVariableExtractor.d.ts +0 -25
  609. package/dist/services/extractors/CSSVariableExtractor.d.ts.map +1 -1
  610. package/dist/services/extractors/CSSVariableExtractor.js +1 -42
  611. package/dist/services/extractors/CSSVariableExtractor.js.map +1 -1
  612. package/dist/services/historyRoots.d.ts +0 -12
  613. package/dist/services/historyRoots.d.ts.map +1 -1
  614. package/dist/services/historyRoots.js +0 -13
  615. package/dist/services/historyRoots.js.map +1 -1
  616. package/dist/services/libraryErrors.d.ts +0 -1
  617. package/dist/services/libraryErrors.d.ts.map +1 -1
  618. package/dist/services/libraryErrors.js +0 -1
  619. package/dist/services/libraryErrors.js.map +1 -1
  620. package/dist/services/pendingLogin.d.ts +0 -13
  621. package/dist/services/pendingLogin.d.ts.map +1 -1
  622. package/dist/services/pendingLogin.js +0 -3
  623. package/dist/services/pendingLogin.js.map +1 -1
  624. package/dist/services/staticProjectTarget.d.ts +0 -4
  625. package/dist/services/staticProjectTarget.d.ts.map +1 -1
  626. package/dist/services/staticProjectTarget.js +0 -4
  627. package/dist/services/staticProjectTarget.js.map +1 -1
  628. package/dist/services/staticStarter.d.ts +0 -20
  629. package/dist/services/staticStarter.d.ts.map +1 -1
  630. package/dist/services/staticStarter.js +0 -20
  631. package/dist/services/staticStarter.js.map +1 -1
  632. package/dist/services/templates/designCatalog.d.ts +0 -11
  633. package/dist/services/templates/designCatalog.d.ts.map +1 -1
  634. package/dist/services/templates/designCatalog.js +0 -26
  635. package/dist/services/templates/designCatalog.js.map +1 -1
  636. package/dist/services/templates/viteReactTs.d.ts +0 -26
  637. package/dist/services/templates/viteReactTs.d.ts.map +1 -1
  638. package/dist/services/templates/viteReactTs.js +0 -20
  639. package/dist/services/templates/viteReactTs.js.map +1 -1
  640. package/dist/storage/library/BundleMetadataStore.d.ts +0 -16
  641. package/dist/storage/library/BundleMetadataStore.d.ts.map +1 -1
  642. package/dist/storage/library/BundleMetadataStore.js +0 -17
  643. package/dist/storage/library/BundleMetadataStore.js.map +1 -1
  644. package/dist/storage/library/CoordinatorRecords.d.ts +0 -9
  645. package/dist/storage/library/CoordinatorRecords.d.ts.map +1 -1
  646. package/dist/storage/library/CoordinatorRecords.js +0 -9
  647. package/dist/storage/library/CoordinatorRecords.js.map +1 -1
  648. package/dist/storage/library/ProjectPreviewCache.d.ts +0 -4
  649. package/dist/storage/library/ProjectPreviewCache.d.ts.map +1 -1
  650. package/dist/storage/library/ProjectPreviewCache.js +0 -4
  651. package/dist/storage/library/ProjectPreviewCache.js.map +1 -1
  652. package/dist/storage/library/ProjectRecords.d.ts +0 -17
  653. package/dist/storage/library/ProjectRecords.d.ts.map +1 -1
  654. package/dist/storage/library/ProjectRecords.js +0 -17
  655. package/dist/storage/library/ProjectRecords.js.map +1 -1
  656. package/dist/storage/library/database.d.ts +0 -3
  657. package/dist/storage/library/database.d.ts.map +1 -1
  658. package/dist/storage/library/database.js +0 -3
  659. package/dist/storage/library/database.js.map +1 -1
  660. package/dist/storage/library/migrations.d.ts +0 -2
  661. package/dist/storage/library/migrations.d.ts.map +1 -1
  662. package/dist/storage/library/migrations.js +0 -2
  663. package/dist/storage/library/migrations.js.map +1 -1
  664. package/dist/storage/library/sourcePresentation.d.ts +0 -1
  665. package/dist/storage/library/sourcePresentation.d.ts.map +1 -1
  666. package/dist/storage/library/sourcePresentation.js +0 -1
  667. package/dist/storage/library/sourcePresentation.js.map +1 -1
  668. package/dist/storage/project-metadata/ProjectMetadataRecords.d.ts +0 -11
  669. package/dist/storage/project-metadata/ProjectMetadataRecords.d.ts.map +1 -1
  670. package/dist/storage/project-metadata/ProjectMetadataRecords.js +0 -13
  671. package/dist/storage/project-metadata/ProjectMetadataRecords.js.map +1 -1
  672. package/dist/storage/project-metadata/database.d.ts +0 -1
  673. package/dist/storage/project-metadata/database.d.ts.map +1 -1
  674. package/dist/storage/project-metadata/database.js +0 -7
  675. package/dist/storage/project-metadata/database.js.map +1 -1
  676. package/dist/storage/project-metadata/migrations.d.ts +0 -42
  677. package/dist/storage/project-metadata/migrations.d.ts.map +1 -1
  678. package/dist/storage/project-metadata/migrations.js +0 -41
  679. package/dist/storage/project-metadata/migrations.js.map +1 -1
  680. package/dist/types/account.d.ts +0 -1
  681. package/dist/types/account.d.ts.map +1 -1
  682. package/dist/types/change-request-types.d.ts +0 -167
  683. package/dist/types/change-request-types.d.ts.map +1 -1
  684. package/dist/types/change-types.d.ts +0 -1
  685. package/dist/types/change-types.d.ts.map +1 -1
  686. package/dist/types/change-types.js +0 -1
  687. package/dist/types/change-types.js.map +1 -1
  688. package/dist/types/project-library.d.ts +0 -1
  689. package/dist/types/project-library.d.ts.map +1 -1
  690. package/dist/types/style-manipulation-types.d.ts +0 -69
  691. package/dist/types/style-manipulation-types.d.ts.map +1 -1
  692. package/dist/types/style-manipulation-types.js +0 -26
  693. package/dist/types/style-manipulation-types.js.map +1 -1
  694. package/dist/types/token-types.d.ts +0 -30
  695. package/dist/types/token-types.d.ts.map +1 -1
  696. package/dist/types/token-types.js +0 -1
  697. package/dist/types/token-types.js.map +1 -1
  698. package/dist/types/variant-history.d.ts +0 -39
  699. package/dist/types/variant-history.d.ts.map +1 -1
  700. package/dist/utils/branchNames.d.ts +0 -1
  701. package/dist/utils/branchNames.d.ts.map +1 -1
  702. package/dist/utils/branchNames.js +0 -2
  703. package/dist/utils/branchNames.js.map +1 -1
  704. package/dist/utils/cloneOrCopyDirSync.d.ts +0 -12
  705. package/dist/utils/cloneOrCopyDirSync.d.ts.map +1 -1
  706. package/dist/utils/cloneOrCopyDirSync.js +1 -15
  707. package/dist/utils/cloneOrCopyDirSync.js.map +1 -1
  708. package/dist/utils/devServerCommand.d.ts +0 -23
  709. package/dist/utils/devServerCommand.d.ts.map +1 -1
  710. package/dist/utils/devServerCommand.js +0 -24
  711. package/dist/utils/devServerCommand.js.map +1 -1
  712. package/dist/utils/devServerError.d.ts +2 -16
  713. package/dist/utils/devServerError.d.ts.map +1 -1
  714. package/dist/utils/devServerError.js +4 -17
  715. package/dist/utils/devServerError.js.map +1 -1
  716. package/dist/utils/displayLocale.d.ts +0 -2
  717. package/dist/utils/displayLocale.d.ts.map +1 -1
  718. package/dist/utils/displayLocale.js +0 -2
  719. package/dist/utils/displayLocale.js.map +1 -1
  720. package/dist/utils/elementFileHintSignals.d.ts +0 -5
  721. package/dist/utils/elementFileHintSignals.d.ts.map +1 -1
  722. package/dist/utils/elementFileHintSignals.js +0 -5
  723. package/dist/utils/elementFileHintSignals.js.map +1 -1
  724. package/dist/utils/formatElementTargetingContext.d.ts +0 -6
  725. package/dist/utils/formatElementTargetingContext.d.ts.map +1 -1
  726. package/dist/utils/formatElementTargetingContext.js +3 -9
  727. package/dist/utils/formatElementTargetingContext.js.map +1 -1
  728. package/dist/utils/hostAgentEnv.d.ts +0 -17
  729. package/dist/utils/hostAgentEnv.d.ts.map +1 -1
  730. package/dist/utils/hostAgentEnv.js +0 -17
  731. package/dist/utils/hostAgentEnv.js.map +1 -1
  732. package/dist/utils/htmlDocument.d.ts +2 -8
  733. package/dist/utils/htmlDocument.d.ts.map +1 -1
  734. package/dist/utils/htmlDocument.js +20 -16
  735. package/dist/utils/htmlDocument.js.map +1 -1
  736. package/dist/utils/internalGitIdentity.d.ts +2 -0
  737. package/dist/utils/internalGitIdentity.d.ts.map +1 -0
  738. package/dist/utils/internalGitIdentity.js +12 -0
  739. package/dist/utils/internalGitIdentity.js.map +1 -0
  740. package/dist/utils/logger.d.ts +0 -5
  741. package/dist/utils/logger.d.ts.map +1 -1
  742. package/dist/utils/logger.js +2 -19
  743. package/dist/utils/logger.js.map +1 -1
  744. package/dist/utils/loginShellPath.d.ts +0 -9
  745. package/dist/utils/loginShellPath.d.ts.map +1 -1
  746. package/dist/utils/loginShellPath.js +0 -69
  747. package/dist/utils/loginShellPath.js.map +1 -1
  748. package/dist/utils/loopback.d.ts +0 -1
  749. package/dist/utils/loopback.d.ts.map +1 -1
  750. package/dist/utils/loopback.js +0 -1
  751. package/dist/utils/loopback.js.map +1 -1
  752. package/dist/utils/pid.d.ts +0 -1
  753. package/dist/utils/pid.d.ts.map +1 -1
  754. package/dist/utils/pid.js +0 -1
  755. package/dist/utils/pid.js.map +1 -1
  756. package/dist/utils/portUtils.d.ts +1 -14
  757. package/dist/utils/portUtils.d.ts.map +1 -1
  758. package/dist/utils/portUtils.js +1 -21
  759. package/dist/utils/portUtils.js.map +1 -1
  760. package/dist/utils/queueAccess.d.ts +0 -9
  761. package/dist/utils/queueAccess.d.ts.map +1 -1
  762. package/dist/utils/queueAccess.js +0 -9
  763. package/dist/utils/queueAccess.js.map +1 -1
  764. package/dist/utils/skillWriter.d.ts +0 -17
  765. package/dist/utils/skillWriter.d.ts.map +1 -1
  766. package/dist/utils/skillWriter.js +0 -20
  767. package/dist/utils/skillWriter.js.map +1 -1
  768. package/dist/utils/skills/claude-skill.d.ts +0 -5
  769. package/dist/utils/skills/claude-skill.d.ts.map +1 -1
  770. package/dist/utils/skills/claude-skill.js +0 -5
  771. package/dist/utils/skills/claude-skill.js.map +1 -1
  772. package/dist/utils/skills/cli-guidance.d.ts +0 -8
  773. package/dist/utils/skills/cli-guidance.d.ts.map +1 -1
  774. package/dist/utils/skills/cli-guidance.js +0 -8
  775. package/dist/utils/skills/cli-guidance.js.map +1 -1
  776. package/dist/utils/skills/cursor-rules.d.ts +0 -6
  777. package/dist/utils/skills/cursor-rules.d.ts.map +1 -1
  778. package/dist/utils/skills/cursor-rules.js +0 -6
  779. package/dist/utils/skills/cursor-rules.js.map +1 -1
  780. package/dist/utils/variantPreviewPort.d.ts +0 -3
  781. package/dist/utils/variantPreviewPort.d.ts.map +1 -1
  782. package/dist/utils/variantPreviewPort.js +0 -3
  783. package/dist/utils/variantPreviewPort.js.map +1 -1
  784. package/dist/utils/windowPlacement.d.ts +0 -4
  785. package/dist/utils/windowPlacement.d.ts.map +1 -1
  786. package/dist/utils/windowPlacement.js +0 -4
  787. package/dist/utils/windowPlacement.js.map +1 -1
  788. package/package.json +15 -14
  789. package/scripts/verify-node-version.cjs +0 -3
  790. package/src/ui/dist/assets/{LibraryApp-PQNnarmo.js → LibraryApp-x6PbzlGr.js} +1 -1
  791. package/src/ui/dist/assets/{main-C7hdsU-C.js → main-BW1Odsqp.js} +76 -76
  792. package/src/ui/dist/assets/main-DDQRZm89.css +1 -0
  793. package/src/ui/dist/index.html +2 -2
  794. package/dist/utils/skills/describe-motion-protocol.d.ts +0 -10
  795. package/dist/utils/skills/describe-motion-protocol.d.ts.map +0 -1
  796. package/dist/utils/skills/describe-motion-protocol.js +0 -231
  797. package/dist/utils/skills/describe-motion-protocol.js.map +0 -1
  798. package/src/ui/dist/assets/main-BUZjlAHd.css +0 -1
@@ -10,7 +10,9 @@ const events_1 = require("events");
10
10
  const fs_1 = __importDefault(require("fs"));
11
11
  const os_1 = __importDefault(require("os"));
12
12
  const path_1 = __importDefault(require("path"));
13
- const child_process_1 = require("child_process");
13
+ const DependencyPreparationService_1 = require("../services/DependencyPreparationService");
14
+ const ProjectPackageManager_1 = require("../services/ProjectPackageManager");
15
+ const devServerCommand_1 = require("../utils/devServerCommand");
14
16
  const simple_git_1 = require("simple-git");
15
17
  const logger_1 = require("../utils/logger");
16
18
  const devServerError_1 = require("../utils/devServerError");
@@ -73,44 +75,14 @@ const FALLBACK_DIRECTION_FOCI = [
73
75
  'Explore tactile depth, layering, and purposeful interaction cues.',
74
76
  'Reduce the concept to its most essential typography and geometry.',
75
77
  ];
76
- // Upper bound on waiting for in-flight history persists during session
77
- // teardown. Generous enough for a full-repo snapshot on a large project;
78
- // small enough that a wedged git subprocess (which has its own 120s block
79
- // timeout) can't make commit/cancel appear hung.
80
78
  const SETTLE_PERSISTS_TEARDOWN_TIMEOUT_MS = 20_000;
81
- // Sentinel port for deriving a persist-time serve recipe when the dev server
82
- // hasn't booted yet: resolved into the command, then reverse-substituted to
83
- // the ${PORT} placeholder. Digit-boundary-guarded substitution means only an
84
- // exact standalone occurrence is replaced, and 65535 never appears in any
85
- // framework's default invocation.
86
79
  const RECIPE_PORT_SENTINEL = 65535;
87
- // Fresh worktrees run the Vite React template, whose dev server defaults to
88
- // 5173. Used as the scan start when allocating a fresh variant's preview port.
89
80
  const FRESH_DEV_SERVER_DEFAULT_PORT = 5173;
90
- // Startup attempts for a variant's dev server. Attempt 2 covers a transient
91
- // early exit (with a dependency install first when the output shows
92
- // module-resolution errors); attempt 3 leaves room for one more port step
93
- // when a collision persists. Early exits fail fast, so extra attempts cost
94
- // little wall clock.
95
81
  const DEV_SERVER_START_MAX_ATTEMPTS = 3;
96
- // Auto-restarts allowed after a READY dev server crashes (see the exit
97
- // handler in startVariantDevServer). Two rescues cover transient crashes;
98
- // a third crash means the build is genuinely broken, so stop looping.
99
82
  const DEV_SERVER_MAX_AUTO_RESTARTS = 2;
100
- // Grace before an auto-restart so a crashing process's port and file locks
101
- // are actually released first.
102
83
  const DEV_SERVER_RESTART_DELAY_MS = 1_000;
103
84
  const DESIGN_CONTEXT_ROUTE_SEGMENT = 'design-md';
104
85
  const DESIGN_CONTEXT_VIEW_SEGMENT = 'view';
105
- // Hard ceiling on worktree provisioning so a slow/large host project can never
106
- // stall the `start_variants` / `approve_variant_briefs` tool call indefinitely.
107
- // Existing-project provisioning clones the host working tree once per variant
108
- // (`git worktree add` + untracked-file copy), so its cost scales with repo
109
- // size — a big monorepo (or a React Native tree with native build dirs) used to
110
- // hang the call for 15+ minutes with no signal. With this bound the call fails
111
- // fast and legibly instead. Per-git-op hangs are already caught by simple-git's
112
- // block timeout in WorktreeManager; this is the cross-op backstop. Override via
113
- // env for unusually large repos.
114
86
  const DEFAULT_PROVISION_TIMEOUT_MS = 540_000;
115
87
  const PROVISION_TIMEOUT_MS = (() => {
116
88
  const raw = process.env.RIVET_VARIANTS_PROVISION_TIMEOUT_MS;
@@ -119,10 +91,6 @@ const PROVISION_TIMEOUT_MS = (() => {
119
91
  ? parsed
120
92
  : DEFAULT_PROVISION_TIMEOUT_MS;
121
93
  })();
122
- // Active variant dev servers are useful only while the Rivet app is being used.
123
- // Reclaim them after app/API silence so abandoned sessions do not pin one child
124
- // process per succeeded variant forever. The session state and worktrees stay
125
- // intact; only live preview ports are cleared.
126
94
  const DEFAULT_ACTIVE_VARIANT_SERVER_IDLE_TTL_MS = 30 * 60 * 1000;
127
95
  const ACTIVE_VARIANT_SERVER_IDLE_TTL_MS = (() => {
128
96
  const raw = process.env.RIVET_ACTIVE_VARIANT_SERVER_IDLE_TTL_MS;
@@ -131,7 +99,6 @@ const ACTIVE_VARIANT_SERVER_IDLE_TTL_MS = (() => {
131
99
  ? parsed
132
100
  : DEFAULT_ACTIVE_VARIANT_SERVER_IDLE_TTL_MS;
133
101
  })();
134
- /** Sentinel so callers can distinguish a provisioning timeout from other errors. */
135
102
  class ProvisionTimeoutError extends Error {
136
103
  timeoutMs;
137
104
  constructor(timeoutMs) {
@@ -140,12 +107,6 @@ class ProvisionTimeoutError extends Error {
140
107
  this.name = 'ProvisionTimeoutError';
141
108
  }
142
109
  }
143
- /**
144
- * Reject with `ProvisionTimeoutError` if `work` hasn't settled within
145
- * `timeoutMs`. The underlying work is NOT cancellable (git/fs I/O), so it may
146
- * keep running after we reject — callers should best-effort clean up partial
147
- * state. The timer is unref'd so it never keeps the process alive on its own.
148
- */
149
110
  function withProvisionTimeout(work, timeoutMs) {
150
111
  let timer;
151
112
  const timeout = new Promise((_, reject) => {
@@ -155,11 +116,6 @@ function withProvisionTimeout(work, timeoutMs) {
155
116
  });
156
117
  return Promise.race([work, timeout]).finally(() => clearTimeout(timer));
157
118
  }
158
- /**
159
- * File types an agent-authored asset plan may copy into a generated preview.
160
- * This allowlist protects an agent-controlled path; caller-selected context
161
- * assets use the separate, type-neutral delivery path.
162
- */
163
119
  const ALLOWED_INERT_ASSET_EXTENSIONS = new Set([
164
120
  '.glb',
165
121
  '.gltf',
@@ -189,13 +145,6 @@ const ALLOWED_INERT_ASSET_EXTENSIONS = new Set([
189
145
  '.eot',
190
146
  '.pdf',
191
147
  ]);
192
- /**
193
- * Reject paths that traverse through a "sensitive" directory anywhere
194
- * in their ancestry — `.ssh`, `.aws`, `.config`, `.gnupg`, etc. Even if
195
- * a copy passes the extension allowlist, an agent-controlled source
196
- * resolving inside `~/.ssh/` is almost certainly an exfiltration
197
- * attempt rather than a legitimate asset reference.
198
- */
199
148
  const SENSITIVE_PATH_SEGMENTS = new Set([
200
149
  '.ssh',
201
150
  '.aws',
@@ -212,14 +161,9 @@ const SENSITIVE_PATH_SEGMENTS = new Set([
212
161
  'credentials',
213
162
  ]);
214
163
  function hasSensitivePathSegment(absolutePath) {
215
- // Split on any path separator; check each segment case-insensitively.
216
164
  const segments = absolutePath.split(/[/\\]/).filter(Boolean);
217
165
  return segments.some((segment) => SENSITIVE_PATH_SEGMENTS.has(segment.toLowerCase()));
218
166
  }
219
- /**
220
- * Returns true when `absolutePath` is the root itself or one of its
221
- * descendants after both paths have been resolved.
222
- */
223
167
  function isPathWithinRoot(absolutePath, root) {
224
168
  const relative = path_1.default.relative(root, absolutePath);
225
169
  return (relative === '' ||
@@ -227,25 +171,6 @@ function isPathWithinRoot(absolutePath, root) {
227
171
  !relative.startsWith('..') &&
228
172
  !path_1.default.isAbsolute(relative)));
229
173
  }
230
- /**
231
- * Copy one agent-planned asset file into a variant worktree at the
232
- * project-relative `destination`. The agent decides routing
233
- * (`destination` like `public/assets/avatar.glb`) so all variants share
234
- * a consistent layout. Fails loudly when the source is missing or the
235
- * destination tries to escape the worktree — silent fallbacks here
236
- * would re-introduce the broken-asset failure mode this routing is
237
- * supposed to prevent.
238
- *
239
- * Security: `entry.source` comes from `report_source_plan` and is
240
- * agent-controlled. To prevent a prompt-injected source plan from
241
- * exfiltrating arbitrary readable host files (SSH keys, dotfiles,
242
- * cloud credentials, project secrets) into the generated app, the
243
- * source must (1) be an absolute path that resolves via realpath
244
- * (no traversal, no symlinks pointing outside the resolved location),
245
- * (2) be a regular file (not a device, socket, fifo, or directory),
246
- * (3) live under the approved asset root, (4) have an extension on the
247
- * asset allowlist.
248
- */
249
174
  function copyAssetIntoWorktree(worktreePath, entry, assetSourceRoot) {
250
175
  if (!assetSourceRoot) {
251
176
  throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `assetPlan.source requires an approved asset root before copying '${entry.source}'`);
@@ -253,14 +178,6 @@ function copyAssetIntoWorktree(worktreePath, entry, assetSourceRoot) {
253
178
  if (!path_1.default.isAbsolute(assetSourceRoot)) {
254
179
  throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `assetSourceRoot must be an absolute path, got '${assetSourceRoot}'`);
255
180
  }
256
- // A relative source is resolved against the approved asset root. The
257
- // agent-facing protocol requires the source to live under that root but not
258
- // to be absolute, and the root path is internal to Rivet (a staged context
259
- // bundle) — so a relative name such as 'avatar.glb' is the natural, safe
260
- // form. Every downstream guard (symlink, extension, within-root, sensitive
261
- // segments) runs against this resolved absolute path, so the resolution
262
- // cannot widen what is copyable: a relative `..` escape still fails the
263
- // within-root check below.
264
181
  const absoluteSource = path_1.default.isAbsolute(entry.source)
265
182
  ? entry.source
266
183
  : path_1.default.resolve(assetSourceRoot, entry.source);
@@ -268,10 +185,6 @@ function copyAssetIntoWorktree(worktreePath, entry, assetSourceRoot) {
268
185
  if (!ALLOWED_INERT_ASSET_EXTENSIONS.has(ext)) {
269
186
  throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `assetPlan.source extension '${ext || '(none)'}' is not on the allowlist (got '${entry.source}'). Only inert media/font/document assets may be copied.`);
270
187
  }
271
- // lstat first: a symlink whose target lies outside the source path
272
- // could otherwise smuggle in arbitrary files. Reject symlinks outright;
273
- // realpath would resolve them but the user-visible intent of an
274
- // assetPlan entry is to name a concrete file.
275
188
  let lstat;
276
189
  try {
277
190
  lstat = fs_1.default.lstatSync(absoluteSource);
@@ -285,9 +198,6 @@ function copyAssetIntoWorktree(worktreePath, entry, assetSourceRoot) {
285
198
  if (!lstat.isFile()) {
286
199
  throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `assetPlan.source must be a regular file: ${entry.source}`);
287
200
  }
288
- // Resolve realpath as additional defense: even with no symlinks at the
289
- // leaf, an intermediate directory could be a symlink. Cross-check the
290
- // file kind on the resolved path.
291
201
  let resolvedSource;
292
202
  try {
293
203
  resolvedSource = fs_1.default.realpathSync(absoluteSource);
@@ -306,24 +216,9 @@ function copyAssetIntoWorktree(worktreePath, entry, assetSourceRoot) {
306
216
  if (!resolvedStat.isFile()) {
307
217
  throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `assetPlan.source resolved target must be a regular file: ${entry.source}`);
308
218
  }
309
- // Containment is the security boundary and must be checked before any
310
- // sensitive-segment scan. The approved asset root is system-chosen and
311
- // trusted (a staged context bundle), and its realpath can legitimately
312
- // pass through a "sensitive" segment that is NOT agent-controlled — on
313
- // macOS `fs.realpathSync(os.tmpdir())` resolves to `/private/var/folders/...`,
314
- // so the temp-staged root contains `private`. A symlinked intermediate dir
315
- // that forwards into a sensitive ancestor (e.g. `<asset-root>/avatar -> ~/.ssh`)
316
- // pushes the realpath OUTSIDE the root and is rejected here.
317
219
  if (!isPathWithinRoot(resolvedSource, resolvedAssetSourceRoot)) {
318
220
  throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `assetPlan.source must stay inside the approved asset root: ${entry.source}`);
319
221
  }
320
- // Sensitive-segment defense, scoped to the agent-controlled portion BELOW
321
- // the trusted root. Only this relative remainder is chosen by the agent's
322
- // source plan, so a sensitive segment it introduces under the root (a real
323
- // `<asset-root>/.ssh/key.glb`, or a within-root symlink that realpaths into
324
- // one) is still refused, while the trusted root prefix is never scanned.
325
- // `resolvedSource` is the fully symlink-resolved real path, so the scan
326
- // covers what the file actually resolves to, not just its literal name.
327
222
  const relativeResolvedSource = path_1.default.relative(resolvedAssetSourceRoot, resolvedSource);
328
223
  if (hasSensitivePathSegment(relativeResolvedSource)) {
329
224
  throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `assetPlan.source traverses a sensitive directory and is refused: ${entry.source}`);
@@ -341,7 +236,6 @@ function copyAssetIntoWorktree(worktreePath, entry, assetSourceRoot) {
341
236
  fs_1.default.copyFileSync(resolvedSource, absDest);
342
237
  }
343
238
  const defaultPreviewQaRunner = ({ html }) => (0, previewQa_1.runPreviewQa)({ html });
344
- /** Disabled-reason copy for a variant whose preview cannot be opened. */
345
239
  const buildViewDisabledReason = (args) => {
346
240
  if (args.status === 'failed') {
347
241
  return args.qa?.summary ?? 'Generation failed';
@@ -365,11 +259,9 @@ const DESIGN_CREATION_ACTION_RE = /\b(create|build|design|make|redesign|refresh|
365
259
  const DESIGN_CREATION_SURFACE_RE = /\b(home\s?page|landing page|dashboard|screen|site|website|app|page|ui|interface|component|hero)\b/i;
366
260
  const NON_VISUAL_CHANGE_RE = /\b(api|backend|database|event|lint|query|server|test|tracking|typecheck)\b/i;
367
261
  const DIFF_CAPTURE_FAILURE_SUMMARY = 'Unable to verify the generated design diff; regenerate this variant so Rivet can confirm source changes.';
368
- /** Separates invalid worker output from a Rivet check that could not run. */
369
262
  const qaFailureErrorCode = (qa) => qa.issues.some((issue) => issue.kind === 'verification_unavailable')
370
263
  ? 'VARIANT_VERIFICATION_UNAVAILABLE'
371
264
  : 'VARIANT_QA_FAILED';
372
- /** Identifies a repair-triggering completion across transport retries. */
373
265
  const repairCompletionKey = (args) => JSON.stringify([args.workItemId, args.leaseId, args.attempt, 'succeeded']);
374
266
  const briefLabelKey = (value) => value.trim().toLowerCase().replace(/\s+/g, ' ');
375
267
  const truncateBriefLabel = (value) => {
@@ -450,17 +342,6 @@ const isDesignCreationCodeGenInput = (input, prompt) => {
450
342
  return (DESIGN_CREATION_ACTION_RE.test(text) &&
451
343
  DESIGN_CREATION_SURFACE_RE.test(text));
452
344
  };
453
- /**
454
- * Wraps SessionStore for the operations that have side effects: approve
455
- * (provision worktrees), reportComplete (capture diff), commit (apply picked
456
- * output), cancel (teardown). Pure read paths bypass and call SessionStore
457
- * directly.
458
- *
459
- * This is the terminal-native variant of the orchestrator: the user picks
460
- * a single brief upfront, so there's only ever one code-gen item per
461
- * session and no live preview / proxy retarget. Once the agent reports
462
- * the variant succeeded, the CLI can commit the chosen output directly.
463
- */
464
345
  class AgentVariantsOrchestrator {
465
346
  store;
466
347
  worktrees;
@@ -477,31 +358,9 @@ class AgentVariantsOrchestrator {
477
358
  activeVariantServerIdleTtlMs;
478
359
  devServerRestartDelayMs;
479
360
  resources = new Map();
480
- /**
481
- * Committed dev servers from prior sessions that survived teardown. The
482
- * orchestrator outlives any single session — once a new session starts, the
483
- * prior committed dev server is no longer needed (the next session opens
484
- * its own preview chip), so stop it before the new session's first preview
485
- * goes live. Keyed by sessionId for diagnostics; values are kept alive
486
- * intentionally between commit and next `propose` / `shutdown`.
487
- */
488
361
  lingeringCommittedDevServers = new Map();
489
- /**
490
- * Per-session in-flight commit chain. Two overlapping commitVariant calls
491
- * would otherwise both pass the `getVariantPick` idempotency check (the
492
- * pick isn't recorded until after `applyDiffToProject`) and double-invoke
493
- * `git apply` — which corrupts the working tree on the second run. The
494
- * chain serializes commits per session so the second caller waits for the
495
- * first to finish and then hits the idempotency check (returning
496
- * `duplicate: true` for the same variantId, or `PENDING_CHANGE_CONFLICT`
497
- * for a different one).
498
- */
499
362
  inflightCommits = new Map();
500
- /** Most recent agent-variants sessionId — read by the iframe chip via
501
- * GET /api/variants/active. Cleared on cancel or commit. */
502
363
  activeSessionId = null;
503
- /** Push channel for the iframe chip's SSE subscription. Emits an
504
- * ActiveSnapshot on every state change so the chip never has to poll. */
505
364
  events = new events_1.EventEmitter();
506
365
  constructor(deps) {
507
366
  this.store = deps.store;
@@ -524,11 +383,7 @@ class AgentVariantsOrchestrator {
524
383
  this.devServerRestartDelayMs =
525
384
  deps.devServerRestartDelayMs ?? DEV_SERVER_RESTART_DELAY_MS;
526
385
  }
527
- // --- Pure delegations (no side effects) ---------------------------------
528
386
  propose(args) {
529
- // A new session means any lingering committed dev server from a prior
530
- // session is no longer needed — the next session opens its own preview.
531
- // Best-effort stop; failures here don't block propose.
532
387
  void this.stopLingeringCommittedDevServers().catch((err) => {
533
388
  log.warn('Stopping lingering committed dev servers failed', err);
534
389
  });
@@ -575,36 +430,18 @@ class AgentVariantsOrchestrator {
575
430
  this.emitChange();
576
431
  return result;
577
432
  }
578
- /** Returns the most recent agent-variants sessionId (or null). */
579
433
  getActiveSessionId() {
580
434
  return this.activeSessionId;
581
435
  }
582
- /** Subscribe to active-snapshot pushes. Returns an unsubscribe fn. */
583
436
  subscribeToEvents(listener) {
584
437
  this.events.on('change', listener);
585
438
  return () => {
586
439
  this.events.off('change', listener);
587
440
  };
588
441
  }
589
- /** Number of live Rivet UI clients subscribed to the variants SSE stream.
590
- * A subscribed client means a Rivet page is open in the browser. */
591
442
  uiClientCount = 0;
592
- /** Wall-clock of the last UI-client connect/disconnect. `hasUiClients`
593
- * treats a very recent disconnect as still-connected to ride out the brief
594
- * gap when an EventSource drops and auto-reconnects (default retry ~3s) —
595
- * otherwise a `propose`/`start` landing in that window reads zero clients
596
- * and spawns a duplicate browser tab. */
597
443
  lastUiClientActivityAt = 0;
598
- /** Grace window covering an EventSource reconnect (its default retry is ~3s;
599
- * the SSE heartbeat is 30s). Long enough to span a reconnect, short enough
600
- * that a genuine page close frees the "reopen on next propose" path quickly. */
601
444
  static UI_CLIENT_GRACE_MS = 6000;
602
- /**
603
- * Register a live Rivet UI client (called when the variants SSE stream opens).
604
- * Returns a release fn to call on disconnect. Used as the "a Rivet page is
605
- * already open" signal so the MCP server can avoid spawning a duplicate tab
606
- * every time the variants flow re-enters an already-active editor session.
607
- */
608
445
  registerUiClient() {
609
446
  this.uiClientCount += 1;
610
447
  this.lastUiClientActivityAt = Date.now();
@@ -618,8 +455,6 @@ class AgentVariantsOrchestrator {
618
455
  this.lastUiClientActivityAt = Date.now();
619
456
  };
620
457
  }
621
- /** True when at least one Rivet UI page is currently connected — or one
622
- * disconnected within the grace window (an EventSource mid-reconnect). */
623
458
  hasUiClients() {
624
459
  if (this.uiClientCount > 0)
625
460
  return true;
@@ -628,21 +463,17 @@ class AgentVariantsOrchestrator {
628
463
  return (Date.now() - this.lastUiClientActivityAt <
629
464
  AgentVariantsOrchestrator.UI_CLIENT_GRACE_MS);
630
465
  }
631
- /** @effect Records activity for the current variants session, if one exists. */
632
466
  touchActiveSession() {
633
467
  if (!this.activeSessionId)
634
468
  return;
635
469
  this.touchSession(this.activeSessionId);
636
470
  }
637
- /** @effect Resets idle cleanup for active variant preview servers. */
638
471
  touchSession(sessionId) {
639
472
  const resources = this.resources.get(sessionId);
640
473
  if (!resources || resources.cleanupStarted)
641
474
  return;
642
475
  this.scheduleActivePreviewIdleTimer(resources);
643
476
  }
644
- /** Build the snapshot the chip cares about. Reads from SessionStore +
645
- * per-resource state; safe to call at any time. */
646
477
  buildActiveSnapshot() {
647
478
  const sessionId = this.activeSessionId;
648
479
  if (!sessionId || !this.store.hasSession(sessionId)) {
@@ -681,22 +512,12 @@ class AgentVariantsOrchestrator {
681
512
  ...(workerRuntime ? { workerRuntime } : {}),
682
513
  };
683
514
  }
684
- /** Record which worker runtime this session's current batch leases to —
685
- * called by the generation services right after the lease-owner decision
686
- * so `variants start`/status can answer "what is running these?". */
687
515
  setWorkerRuntime(sessionId, summary) {
688
516
  if (!this.store.hasSession(sessionId))
689
517
  return;
690
518
  this.store.setWorkerRuntime(sessionId, summary);
691
- // Push a fresh snapshot to SSE subscribers: startUnified/requestWork
692
- // already emitted before this setter ran, and without an emit here a
693
- // subscribed client would not see workerRuntime until the next state
694
- // transition (possibly the first worker completion).
695
519
  this.emitChange();
696
520
  }
697
- /** User reference files stored in the work item's context bundle (uploaded
698
- * or copied local images). Surfaced on the snapshot so briefings can point
699
- * workers at the files directly — one Read instead of a bundle walk. */
700
521
  static contextFilesOf(context) {
701
522
  const files = (context?.userContext ?? [])
702
523
  .filter((source) => source.transport === 'file')
@@ -1048,8 +869,6 @@ class AgentVariantsOrchestrator {
1048
869
  if (projectContext.variantContext) {
1049
870
  (0, variantContext_1.writeVariantContextBundle)(projectContext.variantContext);
1050
871
  }
1051
- // Source-context quality is now measured off the raw context bundle plus
1052
- // the action metadata the agent reported, not the legacy summary fields.
1053
872
  const bundle = projectContext.kind === 'fresh'
1054
873
  ? projectContext.variantContext
1055
874
  : undefined;
@@ -1111,9 +930,6 @@ class AgentVariantsOrchestrator {
1111
930
  }
1112
931
  throw error;
1113
932
  }
1114
- // Record lease times so variant_completed can report lease→report
1115
- // duration. (We don't fire a separate code_gen_leased event — the
1116
- // funnel signal we care about is per-variant completion duration.)
1117
933
  const resources = this.resources.get(args.sessionId);
1118
934
  if (resources) {
1119
935
  const now = Date.now();
@@ -1123,10 +939,6 @@ class AgentVariantsOrchestrator {
1123
939
  }
1124
940
  this.writeLeasedWorkItemBriefings(args.sessionId, result.leasedWorkItems.map((item) => item.id));
1125
941
  this.trackRefinementLeased({ sessionId: args.sessionId, lease: result });
1126
- // Decorate each leased item's input with the worktreePath provisioned
1127
- // by approve(). The skill instructs agents to write files to
1128
- // `input.worktreePath` — without this decoration that field would be
1129
- // missing and the agent would have no idea where to edit.
1130
942
  const decorated = {
1131
943
  ...result,
1132
944
  leasedWorkItems: result.leasedWorkItems.map((item) => {
@@ -1143,7 +955,6 @@ class AgentVariantsOrchestrator {
1143
955
  this.emitChange();
1144
956
  return decorated;
1145
957
  }
1146
- /** @effect Copies caller-supplied context assets into each leased workspace. */
1147
958
  materializeContextAssets(sessionId, items) {
1148
959
  const resources = this.ensureResources(sessionId);
1149
960
  for (const item of items) {
@@ -1164,10 +975,6 @@ class AgentVariantsOrchestrator {
1164
975
  resources.contextAssets.set(item.id, (0, contextAssets_1.materializeContextAssets)({ assets, workspace, previewKind }));
1165
976
  }
1166
977
  }
1167
- /**
1168
- * @effect Records how an existing project's worktrees will be previewed so
1169
- * context assets receive references that resolve on that serving surface.
1170
- */
1171
978
  async resolveContextAssetPreviewKind(sessionId) {
1172
979
  const resources = this.ensureResources(sessionId);
1173
980
  try {
@@ -1180,13 +987,6 @@ class AgentVariantsOrchestrator {
1180
987
  log.warn(`Failed to resolve context asset preview kind for ${sessionId}; using dev-server references`, error);
1181
988
  }
1182
989
  }
1183
- /**
1184
- * @effect Writes each leased implementation item's server-authored briefing
1185
- * BESIDE its workspace so the coordinating host dispatches workers with a
1186
- * short pointer prompt instead of composing prose per worker. Best-effort:
1187
- * a briefing write failure never blocks the lease (the host guidance's
1188
- * packet fields still carry the same data).
1189
- */
1190
990
  writeLeasedWorkItemBriefings(sessionId, leasedWorkItemIds) {
1191
991
  if (leasedWorkItemIds.length === 0)
1192
992
  return;
@@ -1216,13 +1016,9 @@ class AgentVariantsOrchestrator {
1216
1016
  }
1217
1017
  }
1218
1018
  }
1219
- /** @effect Assigns isolated, server-owned artifact workspaces to leased static work. */
1220
1019
  ensureStaticWorkspaces(sessionId, items) {
1221
1020
  const resources = this.ensureResources(sessionId);
1222
1021
  const projectContext = this.store.getProjectContext(sessionId);
1223
- // This lane provisions by copying the source artifact tree (vary forks) or
1224
- // seeding an entry file — it bypasses provisionWorktrees, so it gets its
1225
- // own provisioning telemetry here.
1226
1022
  const provisionStartedAt = Date.now();
1227
1023
  let assignedCount = 0;
1228
1024
  for (const item of items) {
@@ -1269,7 +1065,6 @@ class AgentVariantsOrchestrator {
1269
1065
  });
1270
1066
  }
1271
1067
  }
1272
- /** Reads a completed static artifact while enforcing its assigned directory boundary. */
1273
1068
  readAssignedStaticArtifact(sessionId, workItemId) {
1274
1069
  const workspaceManager = this.resources.get(sessionId)?.workspaceManager;
1275
1070
  if (!workspaceManager) {
@@ -1279,15 +1074,7 @@ class AgentVariantsOrchestrator {
1279
1074
  }
1280
1075
  beginStaticPreviewRefinements(args) {
1281
1076
  const seenVariantIds = new Set();
1282
- // Targets whose in-place refinement could neither be created nor folded
1283
- // into a pending one (e.g. the variant was already sent). We finish the
1284
- // batch's other targets, then RETURN these so the caller can surface them
1285
- // in its response — rather than throwing (which would break the agent poll
1286
- // and lose the already-consumed batch) or silently dropping them.
1287
1077
  const droppedRefinements = [];
1288
- // Work items THIS batch created, so the caller can tell the agent to lease
1289
- // exactly these — letting concurrent runs (several watchers) each pick up
1290
- // their own batch instead of one greedily leasing everything.
1291
1078
  const createdWorkItemIds = [];
1292
1079
  for (const target of args.targets) {
1293
1080
  if (seenVariantIds.has(target.variantId))
@@ -1308,9 +1095,6 @@ class AgentVariantsOrchestrator {
1308
1095
  });
1309
1096
  const currentHtml = this.getStaticPreviewHtml(args.sessionId, target.variantId);
1310
1097
  if (target.count == null) {
1311
- // Plain Apply (no variantRequest) → refine the variant IN PLACE: one
1312
- // edit that overwrites the source, no new sibling cards. Only an
1313
- // explicit "Vary" (which carries a count) fans out new variants.
1314
1098
  try {
1315
1099
  const refineItem = this.store.createRefineVariantWorkItem({
1316
1100
  sessionId: args.sessionId,
@@ -1325,8 +1109,6 @@ class AgentVariantsOrchestrator {
1325
1109
  createdWorkItemIds.push(refineItem.id);
1326
1110
  }
1327
1111
  catch (error) {
1328
- // A refinement is already queued for this variant — fold the newer
1329
- // instruction in rather than dropping it (mirrors the apply queue).
1330
1112
  if (error instanceof errors_1.AgentVariantsError &&
1331
1113
  error.code === 'PENDING_CHANGE_CONFLICT') {
1332
1114
  const folded = this.store.appendPendingRefineVariantInstruction({
@@ -1336,8 +1118,6 @@ class AgentVariantsOrchestrator {
1336
1118
  fidelity: target.fidelity,
1337
1119
  });
1338
1120
  if (!folded) {
1339
- // Nothing to fold into — the conflict is terminal (the variant
1340
- // was already sent / is no longer editable). Don't swallow it.
1341
1121
  droppedRefinements.push({
1342
1122
  variantId: target.variantId,
1343
1123
  reason: error.message,
@@ -1357,9 +1137,6 @@ class AgentVariantsOrchestrator {
1357
1137
  }
1358
1138
  continue;
1359
1139
  }
1360
- // "Vary" fans out: create `count` NEW sibling variants seeded from the
1361
- // source HTML + instruction, rather than overwriting the source in place.
1362
- // The agent leases them through the standard request-work path.
1363
1140
  try {
1364
1141
  const forks = this.store.createForkedStaticPreviewWorkItems({
1365
1142
  sessionId: args.sessionId,
@@ -1377,11 +1154,6 @@ class AgentVariantsOrchestrator {
1377
1154
  createdWorkItemIds.push(...forks.map((item) => item.id));
1378
1155
  }
1379
1156
  catch (error) {
1380
- // Terminal conflict — the source is no longer editable, or a variant
1381
- // was already sent for this session (the fork path has nothing to fold
1382
- // into). Surface it structurally like the in-place branch instead of
1383
- // throwing, which would break the agent poll AND lose this batch (the
1384
- // server already consumed it from the FIFO before calling us).
1385
1157
  if (error instanceof errors_1.AgentVariantsError &&
1386
1158
  (error.code === 'PENDING_CHANGE_CONFLICT' ||
1387
1159
  error.code === 'VARIANT_NOT_EDITABLE')) {
@@ -1408,24 +1180,6 @@ class AgentVariantsOrchestrator {
1408
1180
  this.emitChange();
1409
1181
  return { droppedRefinements, createdWorkItemIds };
1410
1182
  }
1411
- /**
1412
- * Adopt a PAST variant (from a prior session's persisted history) into the
1413
- * active session, rehydrating it from its on-disk static snapshot so the
1414
- * comment / refine / Vary machinery treats it as a live, first-class variant.
1415
- *
1416
- * Triggered when the user makes their first edit on a selected past direction.
1417
- * The store appends an already-`succeeded` `static_preview` work item keyed by
1418
- * the past identity (idempotent); this method registers the matching static-
1419
- * preview record so {@link getStaticPreviewHtml}, {@link getVariants}
1420
- * (preview.kind = 'static_artifact', view enabled), and the
1421
- * `/api/variants/:sid/static/:vid` routes all light up exactly as they do for a
1422
- * freshly-generated static_preview variant.
1423
- *
1424
- * `filesDir` is the absolute path to the persisted `files/` snapshot and
1425
- * `entryPath` is its selected HTML document. Returns the stable active-session
1426
- * `workItemId` so the caller can route the triggering edit to it, plus whether
1427
- * it was already adopted.
1428
- */
1429
1183
  async adoptPastVariant(args) {
1430
1184
  const { assetBase, hasAssets } = await this.resolveAdoptedStaticPreviewFiles(args.filesDir, args.entryPath);
1431
1185
  const append = this.store.appendAdoptedStaticVariant({
@@ -1443,8 +1197,6 @@ class AgentVariantsOrchestrator {
1443
1197
  requestedCount: 1,
1444
1198
  sourceVariantId: args.source.variantId,
1445
1199
  });
1446
- // Register (or re-register, harmlessly) the static-preview record so the
1447
- // snapshot surfaces the adopted variant as a static_artifact.
1448
1200
  const resources = this.ensureResources(args.sessionId);
1449
1201
  resources.staticPreviews.set(append.workItemId, {
1450
1202
  workItemId: append.workItemId,
@@ -1469,11 +1221,6 @@ class AgentVariantsOrchestrator {
1469
1221
  alreadyAdopted: !append.created,
1470
1222
  };
1471
1223
  }
1472
- /**
1473
- * Rehydrate a static history variant into the current active session when it
1474
- * can still accept edits; otherwise create a replacement active session whose
1475
- * only visible direction is the selected history artifact.
1476
- */
1477
1224
  async bootstrapPastVariantSession(args) {
1478
1225
  const usableSessionId = this.getUsableActiveSessionId();
1479
1226
  if (usableSessionId) {
@@ -1554,12 +1301,6 @@ class AgentVariantsOrchestrator {
1554
1301
  }
1555
1302
  return { assetBase, hasAssets };
1556
1303
  }
1557
- /**
1558
- * True when the variant is an HTML-only static_preview work item. Adopted past
1559
- * variants and fresh `static_preview` variants both qualify. Existing-project
1560
- * `code_gen` variants can still expose a static artifact for preview, but Vary
1561
- * must keep those on the worktree-backed code_gen append path.
1562
- */
1563
1304
  isStaticPreviewVariant(sessionId, workItemId) {
1564
1305
  try {
1565
1306
  return (this.store.getWorkItemKind(sessionId, workItemId) === 'static_preview');
@@ -1568,11 +1309,6 @@ class AgentVariantsOrchestrator {
1568
1309
  return false;
1569
1310
  }
1570
1311
  }
1571
- /**
1572
- * Resolve the history source for a forked Vary row. If the immediate parent
1573
- * is an adopted past direction, collapse against that original history row;
1574
- * otherwise collapse against the same-session parent variant.
1575
- */
1576
1312
  resolveHistoryParentVariant(sessionId, sourceVariantId) {
1577
1313
  if (!sourceVariantId)
1578
1314
  return undefined;
@@ -1639,10 +1375,6 @@ class AgentVariantsOrchestrator {
1639
1375
  workItemCount: 1,
1640
1376
  instructionLength: args.instruction.length,
1641
1377
  });
1642
- // Lease the just-created refine item by id. A generic
1643
- // requestWork({ requestedLeaseCount: 1 }) takes the first leasable item in
1644
- // work-item order, which could be an unrelated still-pending static_preview
1645
- // rather than this refinement.
1646
1378
  return this.requestWork({
1647
1379
  sessionId: args.sessionId,
1648
1380
  leaseOwner: args.leaseOwner,
@@ -1655,7 +1387,6 @@ class AgentVariantsOrchestrator {
1655
1387
  getWorkItemInput(sessionId, workItemId) {
1656
1388
  return this.store.getWorkItemInput(sessionId, workItemId);
1657
1389
  }
1658
- /** Returns status after orchestration QA and refinement post-processing. */
1659
1390
  getWorkItemStatus(sessionId, workItemId) {
1660
1391
  return this.store.getWorkItemStatus(sessionId, workItemId);
1661
1392
  }
@@ -1672,16 +1403,10 @@ class AgentVariantsOrchestrator {
1672
1403
  const resources = this.resources.get(sessionId);
1673
1404
  return this.store.getVariants(sessionId).map((variant) => {
1674
1405
  const port = this.getDevServerPort(sessionId, variant.workItemId);
1675
- // Captured worktree diff (existing-project code_gen). Surfaced as a
1676
- // changed-files count so a diff-only variant (no dev server / static
1677
- // artifact) still reads as a real result in the panel.
1678
1406
  const capturedDiff = resources?.worktrees.get(variant.workItemId)?.diff;
1679
1407
  const changedFilesCount = typeof capturedDiff === 'string' && capturedDiff.length > 0
1680
1408
  ? countDiffFiles(capturedDiff)
1681
1409
  : undefined;
1682
- // For fresh sessions, static_preview items ARE the variants — look up
1683
- // directly by workItemId. Fall back to briefId search for existing-project
1684
- // sessions where code_gen items were paired with companion static_preview items.
1685
1410
  const staticPreview = resources?.staticPreviews.get(variant.workItemId) ??
1686
1411
  this.getStaticPreviewByBriefId(sessionId, variant.briefId);
1687
1412
  let preview;
@@ -1692,11 +1417,6 @@ class AgentVariantsOrchestrator {
1692
1417
  };
1693
1418
  }
1694
1419
  else if (!port) {
1695
- // handleSucceededReport is fire-and-forget so staticPreviews may not be
1696
- // populated yet when getVariants is called (e.g. in the terminal response).
1697
- // item.output is set synchronously by store.reportComplete, so it's always
1698
- // available. Read it directly as a fallback so previewUrls in terminal
1699
- // responses include the variant that just triggered terminal state.
1700
1420
  try {
1701
1421
  const output = this.store.getWorkItemOutput(sessionId, variant.workItemId);
1702
1422
  if (typeof output?.html === 'string' && output.html.length > 0) {
@@ -1707,7 +1427,6 @@ class AgentVariantsOrchestrator {
1707
1427
  }
1708
1428
  }
1709
1429
  catch {
1710
- /* work item may not exist in edge cases */
1711
1430
  }
1712
1431
  }
1713
1432
  if (!preview && port) {
@@ -1717,9 +1436,6 @@ class AgentVariantsOrchestrator {
1717
1436
  const isSucceeded = variant.status === 'succeeded';
1718
1437
  const qaFailed = qa?.status === 'failed';
1719
1438
  const canView = Boolean(preview) || (isSucceeded && Boolean(port));
1720
- // A succeeded variant with no live preview whose dev server we tried and
1721
- // failed to start: surface the cause so the UI can show an accurate
1722
- // per-direction message instead of the generic disconnected overlay.
1723
1439
  const previewFailure = resources?.previewFailures.get(variant.workItemId);
1724
1440
  const previewUnavailable = !canView && isSucceeded && previewFailure
1725
1441
  ? {
@@ -1763,64 +1479,60 @@ class AgentVariantsOrchestrator {
1763
1479
  hasSession(sessionId) {
1764
1480
  return this.store.hasSession(sessionId);
1765
1481
  }
1766
- /** Read the projectContext for a session — used by Express routes that
1767
- * build the chip snapshot via `stateForSession`. */
1768
1482
  getProjectContext(sessionId) {
1769
1483
  return this.store.getProjectContext(sessionId);
1770
1484
  }
1771
- /** Resolve a worktree path for a code-gen work item, if provisioned. */
1772
1485
  getWorktreePath(sessionId, workItemId) {
1773
1486
  return this.resources.get(sessionId)?.worktrees.get(workItemId)
1774
1487
  ?.worktreePath;
1775
1488
  }
1776
- /**
1777
- * Resolve the project cwd inside a worktree so subdirectory projects run
1778
- * from the same repo-relative location.
1779
- */
1780
1489
  getProjectCwdInWorktree(worktreePath) {
1781
1490
  return this.worktrees.getProjectCwdInWorktree(worktreePath);
1782
1491
  }
1783
- /** Resolve the dev server port for a code-gen work item, if running. */
1784
1492
  getDevServerPort(sessionId, workItemId) {
1785
1493
  return this.resources.get(sessionId)?.worktrees.get(workItemId)?.port;
1786
1494
  }
1787
- /**
1788
- * Restores a Rivet-owned variant preview that the active-session idle timer
1789
- * reclaimed. Other missing-preview causes remain terminal so this path never
1790
- * attempts to restart user-owned or repeatedly failing servers.
1791
- *
1792
- * @effect Clears the idle marker and prior crash budget, starts the worktree
1793
- * dev server, and emits preview-state snapshots.
1794
- * @deps Uses the live session resources, project context, and dev-server
1795
- * manager owned by this orchestrator.
1796
- */
1797
- async restoreVariantPreview(sessionId, workItemId) {
1495
+ async restoreVariantPreview(sessionId, workItemId, options = {}) {
1798
1496
  const resources = this.resources.get(sessionId);
1799
1497
  const record = resources?.worktrees.get(workItemId);
1800
- if (!resources || !record)
1498
+ if (!resources || !record || resources.cleanupStarted)
1801
1499
  return undefined;
1500
+ if (record.previewRestoration)
1501
+ return record.previewRestoration;
1802
1502
  if (record.port !== undefined && record.devServerProcess) {
1803
1503
  this.touchSession(sessionId);
1804
1504
  return record.port;
1805
1505
  }
1806
- if (resources.previewFailures.get(workItemId)?.reason !== 'idle_timeout') {
1506
+ const failure = resources.previewFailures.get(workItemId);
1507
+ const isSucceeded = this.store
1508
+ .getVariants(sessionId)
1509
+ .some((variant) => variant.workItemId === workItemId && variant.status === 'succeeded');
1510
+ const isExplicitRetry = options.retryFailed === true &&
1511
+ failure?.reason !== 'missing_static_entry';
1512
+ if (!failure ||
1513
+ !isSucceeded ||
1514
+ (failure.reason !== 'idle_timeout' && !isExplicitRetry)) {
1807
1515
  return undefined;
1808
1516
  }
1809
1517
  resources.previewFailures.delete(workItemId);
1810
1518
  record.devServerAutoRestarts = 0;
1811
1519
  this.emitChange();
1812
1520
  const projectContext = this.store.getProjectContext(sessionId);
1813
- return this.startVariantDevServer({
1521
+ const restoration = this.startVariantDevServer({
1814
1522
  sessionId,
1815
1523
  workItemId,
1816
1524
  record,
1817
1525
  isFresh: projectContext.kind === 'fresh',
1818
1526
  });
1527
+ record.previewRestoration = restoration;
1528
+ try {
1529
+ return await restoration;
1530
+ }
1531
+ finally {
1532
+ if (record.previewRestoration === restoration)
1533
+ record.previewRestoration = undefined;
1534
+ }
1819
1535
  }
1820
- /**
1821
- * @effect Resolves the dev server port for a preview request and refreshes
1822
- * active-preview retention for that session while the port is still live.
1823
- */
1824
1536
  resolveDevServerPortForPreview(sessionId, workItemId) {
1825
1537
  const port = this.getDevServerPort(sessionId, workItemId);
1826
1538
  if (typeof port === 'number') {
@@ -1828,14 +1540,6 @@ class AgentVariantsOrchestrator {
1828
1540
  }
1829
1541
  return port;
1830
1542
  }
1831
- /**
1832
- * True work-item kind for a variant, or undefined when the session/item is
1833
- * unknown. This is the authoritative routing signal for Vary/refine intakes:
1834
- * an existing-project `code_gen` variant materialized as a static preview
1835
- * (registerExistingStaticPreview) DISPLAYS as `static_artifact` and serves
1836
- * HTML, but must fork via the code_gen/worktree path — HTML presence is not
1837
- * a valid discriminator.
1838
- */
1839
1543
  getVariantWorkItemKind(sessionId, workItemId) {
1840
1544
  try {
1841
1545
  return this.store.getWorkItemKind(sessionId, workItemId);
@@ -1845,9 +1549,6 @@ class AgentVariantsOrchestrator {
1845
1549
  }
1846
1550
  }
1847
1551
  getStaticPreviewHtml(sessionId, workItemId) {
1848
- // Primary: read the materialized entry from the variant's per-variant
1849
- // directory (written by handleSucceededReport). The directory is the source
1850
- // of truth for the static deliverable.
1851
1552
  const preview = this.resources
1852
1553
  .get(sessionId)
1853
1554
  ?.staticPreviews.get(workItemId);
@@ -1858,12 +1559,8 @@ class AgentVariantsOrchestrator {
1858
1559
  return fromDisk;
1859
1560
  }
1860
1561
  catch {
1861
- // fall through to the work-item output fallback below
1862
1562
  }
1863
1563
  }
1864
- // Fallback: read directly from the work item's stored output — available
1865
- // as soon as reportComplete runs, before handleSucceededReport fires (so
1866
- // the per-variant directory may not exist yet).
1867
1564
  try {
1868
1565
  const output = this.store.getWorkItemOutput(sessionId, workItemId);
1869
1566
  return typeof output?.html === 'string' && output.html.length > 0
@@ -1874,38 +1571,20 @@ class AgentVariantsOrchestrator {
1874
1571
  return undefined;
1875
1572
  }
1876
1573
  }
1877
- /** Resolve the DESIGN.md markdown behind an artifact link. */
1878
1574
  getDesignContextMarkdown(sessionId, artifactId) {
1879
1575
  const artifact = findDesignContextArtifact(this.store.getProjectContext(sessionId), artifactId);
1880
1576
  return artifact?.content;
1881
1577
  }
1882
- /** Build the rendered DesignMD document for an artifact link. */
1883
1578
  getDesignContextViewerHtml(sessionId, artifactId) {
1884
1579
  const artifact = findDesignContextArtifact(this.store.getProjectContext(sessionId), artifactId);
1885
1580
  return artifact ? buildDesignContextViewerDocument(artifact) : undefined;
1886
1581
  }
1887
- /**
1888
- * `true` when the variant has a non-empty `assetBase` — i.e. its inlined
1889
- * HTML references sibling files served by the asset route. The route
1890
- * uses this to decide whether to inject a `<base href>` (only needed
1891
- * when relative URLs in the HTML must resolve against the asset
1892
- * sub-path). Self-contained HTML keeps its natural URL resolution.
1893
- */
1894
1582
  hasStaticPreviewAssets(sessionId, workItemId) {
1895
1583
  const record = this.resources
1896
1584
  .get(sessionId)
1897
1585
  ?.staticPreviews.get(workItemId);
1898
1586
  return Boolean(record?.hasAssets);
1899
1587
  }
1900
- /**
1901
- * Resolve an absolute, sandboxed path for a sibling asset that the
1902
- * variant's inlined HTML references (e.g. `./avatar.glb`, `./hero.png`).
1903
- * Returns `undefined` when the variant has no `assetBase`, the requested
1904
- * path escapes the base (path traversal), or the file does not exist.
1905
- *
1906
- * Used by the `/api/variants/:sessionId/static/:variantId/<asset>` route
1907
- * to serve assets from the agent's on-disk variant workspace pre-commit.
1908
- */
1909
1588
  resolveStaticPreviewAssetPath(sessionId, workItemId, requestedPath) {
1910
1589
  const record = this.resources
1911
1590
  .get(sessionId)
@@ -1914,12 +1593,6 @@ class AgentVariantsOrchestrator {
1914
1593
  return undefined;
1915
1594
  if (!requestedPath || requestedPath.length === 0)
1916
1595
  return undefined;
1917
- // Resolve against the variant's own directory first so any asset the agent
1918
- // actually wrote/modified wins. Only when the worktree clone lacks the file
1919
- // do we fall back to the host project root — that recovers git-ignored or
1920
- // binary assets the worktree provisioning couldn't carry over, which the
1921
- // original preview already serves (see `assetFallbackBase`). Both roots are
1922
- // held to the identical traversal/realpath/hardlink sandbox.
1923
1596
  return (resolveAssetWithinBase(record.assetBase, requestedPath) ??
1924
1597
  (record.assetFallbackBase
1925
1598
  ? resolveAssetWithinBase(record.assetFallbackBase, requestedPath)
@@ -1936,7 +1609,6 @@ class AgentVariantsOrchestrator {
1936
1609
  const safeWorkItemId = encodeURIComponent(workItemId);
1937
1610
  return `/api/variants/${safeSessionId}/static/${safeWorkItemId}`;
1938
1611
  }
1939
- // --- Mutation wrappers (state + side effects) ---------------------------
1940
1612
  async approve(args) {
1941
1613
  const result = this.store.approve(args);
1942
1614
  const resources = this.ensureResources(args.sessionId);
@@ -1957,11 +1629,6 @@ class AgentVariantsOrchestrator {
1957
1629
  workItemIds,
1958
1630
  });
1959
1631
  this.emitChange();
1960
- // Await provisioning before returning so request_work always sees a
1961
- // populated worktreePath on each leased item. Provisioning is fast
1962
- // (`git worktree add` per variant + node_modules symlink), and the
1963
- // agent has nothing useful to do between approve and request_work
1964
- // anyway — running them serially is the contract the skill assumes.
1965
1632
  try {
1966
1633
  if (projectContext.kind === 'existing') {
1967
1634
  await this.resolveContextAssetPreviewKind(args.sessionId);
@@ -1975,50 +1642,18 @@ class AgentVariantsOrchestrator {
1975
1642
  }
1976
1643
  return result;
1977
1644
  }
1978
- /**
1979
- * Unified one-call kickoff that collapses propose → report_briefs →
1980
- * approve. Uses AI-authored briefs supplied by agent callers, with a
1981
- * deterministic prompt-derived fallback for human-direct calls, and
1982
- * immediately approves so the session lands in `work_items_ready`.
1983
- *
1984
- * Supports both existing-project sessions (spawns code_gen work items
1985
- * for the agent to lease) and zero-to-one sessions (spawns
1986
- * static_preview work items; the server runs scaffold_base in the
1987
- * background). This is the single-call path; source-grounded zero-to-one
1988
- * sessions (source URLs / inspiration extraction) run through `propose`
1989
- * instead so the source-research flow can gate generation — start_variants
1990
- * routes them there. This method does not run source research.
1991
- *
1992
- * Host-agnostic: no LLM calls happen here. The agent (Claude Code,
1993
- * Cursor, Codex) generates the label and code when it leases the
1994
- * work items via continue_variants(action="request_work").
1995
- */
1996
1645
  async startUnified(args) {
1997
1646
  const count = args.briefs?.length ?? args.count ?? contracts_1.DEFAULT_VARIANT_COUNT;
1998
1647
  const projectContext = args.projectContext ?? {
1999
1648
  kind: 'existing',
2000
1649
  };
2001
1650
  const designContextCount = projectContext.designContext?.length ?? 0;
2002
- // Fresh source-grounded sessions (a captured context bundle on a fresh
2003
- // project) must run through `propose` so the source-research/source_plan
2004
- // flow gates generation; start_variants routes them there before reaching
2005
- // here, so this guard is an internal invariant. Existing-project sessions
2006
- // keep the single-call path even with a bundle — they have no source_plan
2007
- // stage, and the bundle's raw context is threaded straight into code_gen.
2008
- // Brief-driven fresh starts are exempt for the same reason: the caller
2009
- // already did its source research when it authored the briefs, so the
2010
- // bundle is raw worker context (reference images), not research input.
2011
1651
  const isSourceGrounded = projectContext.kind === 'fresh' &&
2012
1652
  Boolean(projectContext.variantContext) &&
2013
1653
  !args.briefs?.length;
2014
1654
  if (isSourceGrounded) {
2015
1655
  throw new errors_1.AgentVariantsError('INVALID_STAGE_ACTION', 'Source-grounded sessions must run through the source-research flow (propose), not the single-call startUnified path.');
2016
1656
  }
2017
- // A second "Vary" on an existing project that still has a live session
2018
- // ACCUMULATES into that session instead of starting a fresh one that
2019
- // would orphan the prior variants (and overwrite `activeSessionId`, so
2020
- // the RHS would only ever show the latest batch). Gated to existing
2021
- // projects so fresh 0→1 generation is unaffected.
2022
1657
  const appendSessionId = args.allowAccumulate &&
2023
1658
  projectContext.kind === 'existing' &&
2024
1659
  projectContext.projectPath
@@ -2056,8 +1691,6 @@ class AgentVariantsOrchestrator {
2056
1691
  };
2057
1692
  const { createdWorkItemIds } = this.store.appendCodeGenVariants({
2058
1693
  ...forkArgs,
2059
- // Fork parent (Vary on a variant): seeds each new worktree from the
2060
- // parent's tree instead of the base tree. Absent for a plain append.
2061
1694
  ...(args.sourceVariantId
2062
1695
  ? { sourceVariantId: args.sourceVariantId }
2063
1696
  : {}),
@@ -2096,8 +1729,6 @@ class AgentVariantsOrchestrator {
2096
1729
  projectContext,
2097
1730
  runLabel: args.runLabel,
2098
1731
  ...(args.fidelity ? { fidelity: args.fidelity } : {}),
2099
- // Briefs land via reportBriefs immediately below — never enter the
2100
- // source-plan stage, even for a fresh session carrying a bundle.
2101
1732
  hasPreparedBriefs: true,
2102
1733
  ...(args.sessionId ? { sessionId: args.sessionId } : {}),
2103
1734
  });
@@ -2128,18 +1759,11 @@ class AgentVariantsOrchestrator {
2128
1759
  });
2129
1760
  sessionId = proposeResult.sessionId;
2130
1761
  scaffoldBaseWorkItemId = approveResult.scaffoldBaseWorkItemId ?? null;
2131
- // For existing-project sessions, approve() returns the new code_gen
2132
- // ids in codeGenWorkItemIds. For zero-to-one, approve() spawns
2133
- // static_preview items instead — those ids aren't in codeGenWorkItemIds,
2134
- // so read them out of the active-variants snapshot which covers both
2135
- // kinds. Snapshot order mirrors brief order (insertion order).
2136
1762
  variantIds =
2137
1763
  projectContext.kind === 'fresh'
2138
1764
  ? this.store.getVariants(sessionId).map((v) => v.workItemId)
2139
1765
  : approveResult.codeGenWorkItemIds;
2140
1766
  }
2141
- // Stamp the UI correlation id so the panel maps its optimistic placeholders
2142
- // to these real cards (the same mechanism the static-preview fork uses).
2143
1767
  if (args.requestId) {
2144
1768
  this.store.assignVariantRequestId(sessionId, variantIds, args.requestId);
2145
1769
  }
@@ -2173,8 +1797,6 @@ class AgentVariantsOrchestrator {
2173
1797
  const lease = this.requestWork({
2174
1798
  sessionId,
2175
1799
  leaseOwner: args.leaseOwner,
2176
- // Append: lease ONLY the new batch so a still-leasable prior item
2177
- // (e.g. a re-leased one) isn't swept into this Vary's lease.
2178
1800
  ...(appendSessionId ? { workItemIds: variantIds } : {}),
2179
1801
  });
2180
1802
  leaseId = lease.leaseId;
@@ -2190,15 +1812,6 @@ class AgentVariantsOrchestrator {
2190
1812
  leasedWorkItems,
2191
1813
  };
2192
1814
  }
2193
- /**
2194
- * Build the per-variant briefs for a unified start. Supplied briefs keep
2195
- * meaningful direction labels, while duplicate or empty labels are made
2196
- * unique against the visible session rows. The fallback derives a short
2197
- * run-intent label from the prompt (e.g. "make it brighter" →
2198
- * "Brighter 1/2/…") rather than an indistinct "Variant N". `uniqueBriefIds`
2199
- * is set for the append path so the new batch's briefIds can't collide with
2200
- * the live session's existing ones.
2201
- */
2202
1815
  buildUnifiedBriefs(args) {
2203
1816
  const briefId = (i) => args.uniqueBriefIds ? `v-${(0, crypto_1.randomUUID)().slice(0, 8)}` : `v${i + 1}`;
2204
1817
  const runLabel = args.runLabel?.trim() || (0, runLabel_1.deriveRunLabel)(args.prompt);
@@ -2228,13 +1841,6 @@ class AgentVariantsOrchestrator {
2228
1841
  body: synthesizeUnifiedBriefBody(args.prompt, i + 1, args.count),
2229
1842
  }, i));
2230
1843
  }
2231
- /**
2232
- * The active sessionId IF it is an existing-project session for `projectPath`
2233
- * that can still accept appended variants — i.e. not committed, not
2234
- * cancelled/failed. Returns null otherwise, so start_variants falls back to
2235
- * creating a fresh session (first Vary, a different project, or after the
2236
- * prior session was committed/cancelled).
2237
- */
2238
1844
  getAppendableExistingSessionId(projectPath) {
2239
1845
  const sessionId = this.activeSessionId;
2240
1846
  if (!sessionId || !this.store.hasSession(sessionId))
@@ -2249,7 +1855,6 @@ class AgentVariantsOrchestrator {
2249
1855
  return null;
2250
1856
  return sessionId;
2251
1857
  }
2252
- /** @effect Registers a provisioned git worktree with runtime and workspace state. */
2253
1858
  async registerWorktreeWorkspace(resources, record) {
2254
1859
  let projectPath = record.worktreePath;
2255
1860
  try {
@@ -2265,15 +1870,6 @@ class AgentVariantsOrchestrator {
2265
1870
  projectPath,
2266
1871
  });
2267
1872
  }
2268
- /**
2269
- * Provision a worktree for each appended existing-project variant. Mirrors
2270
- * {@link provisionExistingWorktrees} but only for the newly appended ids, and
2271
- * passes a per-batch `instanceKeyPrefix` so the new worktree dirs do not
2272
- * collide with the first batch's `${sessionId}-${i}` paths.
2273
- */
2274
- /** Emits provisioning started/completed/failed telemetry around `run` for
2275
- * the vary-fork/append lanes that bypass provisionWorktrees (and its
2276
- * telemetry). Rethrows failures untouched. */
2277
1873
  async withProvisioningTelemetry(args, run) {
2278
1874
  const startedAt = Date.now();
2279
1875
  this.telemetry.track('agent_variants.provisioning_started', {
@@ -2328,11 +1924,6 @@ class AgentVariantsOrchestrator {
2328
1924
  })));
2329
1925
  });
2330
1926
  }
2331
- /**
2332
- * Provision worktrees for a code_gen Vary forked from a parent variant.
2333
- * Copies the parent's worktree tree so each sibling starts from the parent's
2334
- * diff rather than git HEAD / Original.
2335
- */
2336
1927
  async provisionForkedFromVariantWorktrees(sessionId, sourceVariantId, workItemIds) {
2337
1928
  if (workItemIds.length === 0)
2338
1929
  return;
@@ -2340,13 +1931,14 @@ class AgentVariantsOrchestrator {
2340
1931
  if (!sourcePath) {
2341
1932
  throw new errors_1.AgentVariantsError('VARIANT_NOT_EDITABLE', `Cannot fork from variant ${sourceVariantId}: no worktree path`);
2342
1933
  }
2343
- if (!this.worktrees.forkWorktreesFrom) {
1934
+ const forkWorktreesFrom = this.worktrees.forkWorktreesFrom?.bind(this.worktrees);
1935
+ if (!forkWorktreesFrom) {
2344
1936
  throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', 'worktreeManager does not implement forkWorktreesFrom');
2345
1937
  }
2346
1938
  await this.withProvisioningTelemetry({ sessionId, kind: 'forked_variant', variantCount: workItemIds.length }, async () => {
2347
1939
  const resources = this.ensureResources(sessionId);
2348
1940
  const instanceKeyPrefix = `f${(0, crypto_1.randomUUID)().slice(0, 8)}-`;
2349
- const paths = await withProvisionTimeout(this.worktrees.forkWorktreesFrom(sourcePath, sessionId, workItemIds.length, instanceKeyPrefix), this.provisionTimeoutMs);
1941
+ const paths = await withProvisionTimeout(forkWorktreesFrom(sourcePath, sessionId, workItemIds.length, instanceKeyPrefix), this.provisionTimeoutMs);
2350
1942
  await Promise.all(workItemIds.map((id, index) => this.registerWorktreeWorkspace(resources, {
2351
1943
  workItemId: id,
2352
1944
  worktreePath: paths[index],
@@ -2354,15 +1946,7 @@ class AgentVariantsOrchestrator {
2354
1946
  });
2355
1947
  }
2356
1948
  async reportComplete(args) {
2357
- // Phase-timing anchor: lease→report (agent work) vs report→recorded
2358
- // (QA gate + post-processing) are attributed separately on
2359
- // `variant_completed` so latency work can tell them apart.
2360
1949
  const reportReceivedAt = Date.now();
2361
- // Cooperative abort short-circuit: if the work item was removed/cancelled
2362
- // while in flight (delete-while-loading, cancel_variant, session cancel),
2363
- // skip the schema and QA gates entirely — there is no point validating or
2364
- // QA-ing output we are about to discard — and hand back the store's
2365
- // `aborted` signal so the agent stops this work item now.
2366
1950
  if (this.store.hasSession(args.sessionId) &&
2367
1951
  this.store.isWorkItemAborted(args.sessionId, args.workItemId)) {
2368
1952
  return this.store.reportComplete(args);
@@ -2386,9 +1970,6 @@ class AgentVariantsOrchestrator {
2386
1970
  output: this.readAssignedStaticArtifact(args.sessionId, args.workItemId),
2387
1971
  }
2388
1972
  : args;
2389
- // Contract validation keeps internal callers that provide an explicit
2390
- // static output honest. Host workers complete assigned artifacts without
2391
- // transporting the HTML through this report.
2392
1973
  if (resolvedArgs.status === 'succeeded' &&
2393
1974
  this.store.hasSession(args.sessionId) &&
2394
1975
  (workItemKind === 'static_preview' ||
@@ -2406,27 +1987,15 @@ class AgentVariantsOrchestrator {
2406
1987
  }
2407
1988
  throw new errors_1.AgentVariantsError('SCHEMA_VALIDATION_FAILED', `${workItemKind} output.html must be a string and non-empty when explicit output is provided. Host workers should write the assigned index.html instead.`);
2408
1989
  }
2409
- // Check Rivet-owned output integrity synchronously before recording
2410
- // success. A terminal failure uses `VARIANT_QA_FAILED` so the variant never
2411
- // reaches `ready` and `getVariants` can disable its commit action.
2412
1990
  const qaStartedAt = Date.now();
2413
1991
  const qaEval = workItemKind === 'refine_variant'
2414
1992
  ? null
2415
1993
  : await this.evaluateQaForReport(resolvedArgs);
2416
1994
  const qaMs = qaEval === null ? null : Date.now() - qaStartedAt;
2417
- // A `retry` verdict re-leases the variant for one structural repair instead
2418
- // of recording success, so short-circuit the normal record path.
2419
1995
  if (qaEval?.decision === 'retry') {
2420
1996
  return this.handleQaRegenRetry(resolvedArgs, qaEval.qa);
2421
1997
  }
2422
1998
  let effectiveArgs = qaEval?.decision === 'fail_terminal' ? qaEval.overrideArgs : resolvedArgs;
2423
- // For a successful refine_variant, swap the refined static preview into
2424
- // place and persist history BEFORE recording success in the store. This
2425
- // keeps the user-visible `refinement.status: succeeded` (and the snapshot
2426
- // emitted below) from ever being observable while the variant still serves
2427
- // stale HTML. If post-processing throws, convert the report to `failed` so
2428
- // the agent/UI surface the failure instead of a success pointing at
2429
- // unrefreshed output.
2430
1999
  if (workItemKind === 'refine_variant' &&
2431
2000
  effectiveArgs.status === 'succeeded') {
2432
2001
  try {
@@ -2473,9 +2042,6 @@ class AgentVariantsOrchestrator {
2473
2042
  throw error;
2474
2043
  }
2475
2044
  this.emitChange();
2476
- // Per-variant telemetry on terminal item statuses (skip 'running'
2477
- // heartbeats and the brief work item — only code_gen / scaffold_base
2478
- // matter for the funnel).
2479
2045
  if (effectiveArgs.status === 'succeeded' ||
2480
2046
  effectiveArgs.status === 'failed' ||
2481
2047
  effectiveArgs.status === 'cancelled') {
@@ -2484,11 +2050,8 @@ class AgentVariantsOrchestrator {
2484
2050
  const durationMs = leasedAt !== undefined ? Date.now() - leasedAt : null;
2485
2051
  const approveStartedAt = resources?.approveStartedAt;
2486
2052
  const approveToCompleteMs = approveStartedAt !== undefined ? Date.now() - approveStartedAt : null;
2487
- // Byte size of generated output for HTML vs full-stack comparison.
2488
2053
  const outputBytes = estimateOutputBytes(effectiveArgs.output);
2489
2054
  const projectContextKind = this.store.getProjectContext(args.sessionId).kind;
2490
- // Execution mode mirrors the host work-item snapshot derivation so
2491
- // prod latency can finally segment vary vs create vs refine.
2492
2055
  const mode = workItemKind !== undefined && isImplementationWorkItemKind(workItemKind)
2493
2056
  ? this.store.isVaryForkWorkItem(args.sessionId, args.workItemId)
2494
2057
  ? 'vary'
@@ -2504,15 +2067,11 @@ class AgentVariantsOrchestrator {
2504
2067
  approveToCompleteMs,
2505
2068
  kind: workItemKind ?? null,
2506
2069
  mode,
2507
- // Phase split of `durationMs`: agent work vs server-side gating.
2508
2070
  leaseToReportMs: leasedAt !== undefined ? reportReceivedAt - leasedAt : null,
2509
2071
  reportProcessingMs: Date.now() - reportReceivedAt,
2510
2072
  qaMs,
2511
2073
  hasError: Boolean(effectiveArgs.error),
2512
2074
  errorCode: effectiveArgs.error?.code ?? null,
2513
- // Failure detail + API status ride the event so field failures are
2514
- // diagnosable from telemetry alone (the July 2026 EXECUTOR_WORKER_FAILED
2515
- // incident took an hour of forensics for want of these).
2516
2075
  errorMessage: effectiveArgs.error?.message ?? null,
2517
2076
  errorApiStatus: effectiveArgs.error?.apiErrorStatus ?? null,
2518
2077
  isScaffold: resources?.scaffoldBaseWorkItemId === args.workItemId,
@@ -2598,10 +2157,6 @@ class AgentVariantsOrchestrator {
2598
2157
  log.error(`handleSucceededReport failed for ${args.sessionId}/${args.workItemId}`, err);
2599
2158
  });
2600
2159
  }
2601
- // Teardown is deferred to commitVariant / cancel. Terminal stage just
2602
- // means all variants are done; the user still needs to review and pick
2603
- // one. Resources (worktrees + captured diffs) live until that pick lands
2604
- // on the bridge.
2605
2160
  return result;
2606
2161
  }
2607
2162
  async cancel(args) {
@@ -2632,13 +2187,6 @@ class AgentVariantsOrchestrator {
2632
2187
  });
2633
2188
  return result;
2634
2189
  }
2635
- /**
2636
- * Cancel a single variant within a session. Siblings continue. Idempotent
2637
- * on terminal states. Per-variant worktree teardown is deferred to a
2638
- * follow-up; the lease is released, so requestWork won't re-surface this
2639
- * variant and the agent's report_variant_complete for it (if it lands
2640
- * after cancellation) is rejected by the stale-lease check.
2641
- */
2642
2190
  async cancelVariant(args) {
2643
2191
  const result = this.store.cancelWorkItem({
2644
2192
  sessionId: args.sessionId,
@@ -2668,12 +2216,6 @@ class AgentVariantsOrchestrator {
2668
2216
  alreadyTerminal: result.alreadyTerminal,
2669
2217
  };
2670
2218
  }
2671
- /**
2672
- * Remove a variant from the UI. Hides it from the active snapshot for good
2673
- * (it's filtered out of progress/summary/terminal accounting too) while
2674
- * retaining it in `.rivet/variants/` with status `removed` so the work is
2675
- * kept, never rendered again. Mirrors `cancelVariant`'s shape; idempotent.
2676
- */
2677
2219
  async removeVariant(args) {
2678
2220
  const result = this.store.removeWorkItem({
2679
2221
  sessionId: args.sessionId,
@@ -2701,13 +2243,6 @@ class AgentVariantsOrchestrator {
2701
2243
  alreadyRemoved: result.alreadyRemoved,
2702
2244
  };
2703
2245
  }
2704
- /**
2705
- * Rename a variant's user-visible title. Patches the in-memory work item so
2706
- * the next snapshot carries the new label, persists it to the variant's
2707
- * `.rivet/variants/` manifest (best-effort — a never-persisted variant simply
2708
- * skips the disk write), and pushes the updated snapshot over SSE. Mirrors
2709
- * `removeVariant`'s shape.
2710
- */
2711
2246
  async renameVariant(args) {
2712
2247
  const result = this.store.renameWorkItem({
2713
2248
  sessionId: args.sessionId,
@@ -2718,7 +2253,6 @@ class AgentVariantsOrchestrator {
2718
2253
  source: 'mcp',
2719
2254
  sessionId: args.sessionId,
2720
2255
  variantId: args.variantId,
2721
- // Telemetry policy: never the raw agent/user label text, only its length.
2722
2256
  labelLength: args.label.length,
2723
2257
  sessionStage: result.sessionStage,
2724
2258
  });
@@ -2733,10 +2267,6 @@ class AgentVariantsOrchestrator {
2733
2267
  sessionStage: result.sessionStage,
2734
2268
  };
2735
2269
  }
2736
- /**
2737
- * Rename a run folder: re-home one request batch in a single store pass,
2738
- * persist each affected manifest best-effort, and push one updated snapshot.
2739
- */
2740
2270
  async renameRun(args) {
2741
2271
  const result = this.store.renameRun({
2742
2272
  sessionId: args.sessionId,
@@ -2747,7 +2277,6 @@ class AgentVariantsOrchestrator {
2747
2277
  this.telemetry.track('agent_variants.run_renamed', {
2748
2278
  source: 'mcp',
2749
2279
  sessionId: args.sessionId,
2750
- // Telemetry policy: never the raw label text, only its length.
2751
2280
  runLabelLength: args.label.length,
2752
2281
  renamedCount: result.workItemIds.length,
2753
2282
  sessionStage: result.sessionStage,
@@ -2766,18 +2295,7 @@ class AgentVariantsOrchestrator {
2766
2295
  sessionStage: result.sessionStage,
2767
2296
  };
2768
2297
  }
2769
- /**
2770
- * User has reviewed the rendered variants and picked one. Look up the
2771
- * captured diff or generated project, apply it to the destination, and record
2772
- * the pick. Idempotent on (sessionId, variantId): repeating the call returns
2773
- * duplicate=true without applying again.
2774
- */
2775
2298
  async commitVariant(args) {
2776
- // Serialize per-session so two overlapping commits can't both run
2777
- // `applyDiffToProject` (or the fresh rename) before the first records
2778
- // its pick. The second caller waits for the first to settle, then runs
2779
- // commitVariantImpl which hits the idempotency check in
2780
- // `getVariantPick` / `recordVariantPick`.
2781
2299
  const prior = this.inflightCommits.get(args.sessionId);
2782
2300
  const run = (async () => {
2783
2301
  if (prior) {
@@ -2806,17 +2324,8 @@ class AgentVariantsOrchestrator {
2806
2324
  }
2807
2325
  }
2808
2326
  async commitVariantImpl(args) {
2809
- // Idempotent path — SessionStore.recordVariantPick is the source of truth
2810
- // for which variant the user picked. If the same variant is being
2811
- // committed again, replay the previously stored envelope's metadata
2812
- // without going back through resources (which may have been torn down).
2813
2327
  const existingPick = this.store.getVariantPick(args.sessionId);
2814
2328
  if (existingPick && existingPick.variantId === args.variantId) {
2815
- // Retry-safe history flip. If the first commit attempt applied
2816
- // successfully but the history-persist task crashed, the chosen variant
2817
- // stays at `completed` instead of `committed` on disk. Re-run the flip on
2818
- // every duplicate attempt; the terminal-status guard in `markStatus`
2819
- // makes this safely idempotent.
2820
2329
  const projectContext = this.store.getProjectContext(args.sessionId);
2821
2330
  let historyProjectPath;
2822
2331
  if (projectContext.kind === 'fresh') {
@@ -2842,9 +2351,6 @@ class AgentVariantsOrchestrator {
2842
2351
  log.warn(`persistVariantHistoryAtCommit (duplicate retry) failed for session ${args.sessionId}`, err);
2843
2352
  });
2844
2353
  }
2845
- // If the committed dev/static server is still running (either tracked
2846
- // on resources, or handed off to the lingering registry), surface its
2847
- // URL so a retry restores the iframe to the live project.
2848
2354
  const lingering = this.lingeringCommittedDevServers.get(args.sessionId);
2849
2355
  const live = this.resources.get(args.sessionId)?.committedDevServer;
2850
2356
  const replayPort = lingering?.port ?? live?.port;
@@ -2879,19 +2385,9 @@ class AgentVariantsOrchestrator {
2879
2385
  let envelopeDestination;
2880
2386
  let changedFilesCount;
2881
2387
  let runnablePaths;
2882
- // Port of the committed dev/static server, when one was spawned during
2883
- // this commit. Surfaced on the response as `previewUrl` so the UI can
2884
- // restore the iframe to the committed project without falling through to
2885
- // the auto-static fallback.
2886
2388
  let committedPreviewPort;
2887
2389
  if (projectContext.kind === 'fresh') {
2888
2390
  const destinationPath = projectContext.workspacePath;
2889
- // Realpath the destination ONCE for both the empty-check (below) and
2890
- // the static_preview flatten step (further down). Computing it twice
2891
- // could diverge if the path's realpath status changes between calls
2892
- // (e.g. the directory was a symlink during the empty check but got
2893
- // resolved between then and `mkdirSync`). `undefined` means realpath
2894
- // failed — caller branches treat that as "no canonical form known".
2895
2391
  let realDestination;
2896
2392
  try {
2897
2393
  realDestination = fs_1.default.realpathSync(destinationPath);
@@ -2899,34 +2395,13 @@ class AgentVariantsOrchestrator {
2899
2395
  catch {
2900
2396
  realDestination = undefined;
2901
2397
  }
2902
- // Read the commit policy off the canonical run plan rather than
2903
- // re-deriving from executionPlan.mode. `materialize_project` is the
2904
- // vite_app lane (rename/copy the whole worktree); `write_static_site`
2905
- // is the static_preview lane (inline HTML + sibling assets).
2906
2398
  const commitPolicy = (0, runPlan_1.resolveRunPlanPolicy)(projectContext).commit;
2907
2399
  const isViteApp = commitPolicy === 'materialize_project';
2908
- // vite_app: strict emptiness check — that lane renames a worktree into
2909
- // destinationPath and would clobber user files.
2910
- // static_preview: the agent intentionally wrote sibling assets under
2911
- // workspacePath (via `output.assetBase`) before reporting succeeded —
2912
- // those per-variant subdirs ARE the deliverable. Allow exactly the
2913
- // assetBase entries known to this session; anything else still
2914
- // triggers DESTINATION_NOT_EMPTY.
2915
2400
  if (isViteApp) {
2916
2401
  this.assertDestinationAvailable(destinationPath);
2917
2402
  }
2918
2403
  else {
2919
- // Compare assetBase entries (which are realpath'd by
2920
- // resolveStaticPreviewAssetBase) against the realpath'd destination
2921
- // — on macOS `/tmp` is a symlink to `/private/tmp`, and a raw
2922
- // string comparison would falsely reject every entry.
2923
2404
  const compareBase = realDestination ?? destinationPath;
2924
- // When ANY variant's assetBase is the destination itself (the agent
2925
- // wrote files directly at the workspace root rather than under a
2926
- // per-variant subdir), the empty-check would treat the agent's own
2927
- // deliverable as foreign content. Skip the check in that case — the
2928
- // workspace IS the deliverable; commit will overwrite index.html
2929
- // below.
2930
2405
  const anyAssetBaseIsDestination = [
2931
2406
  ...resources.staticPreviews.values(),
2932
2407
  ].some((sp) => sp.assetBase === compareBase);
@@ -2941,16 +2416,6 @@ class AgentVariantsOrchestrator {
2941
2416
  }
2942
2417
  }
2943
2418
  if (isViteApp) {
2944
- // Vite_app: the deliverable is the entire variant worktree, not a
2945
- // single HTML file. When the worktree lives on the same volume as
2946
- // the destination (the common case — provisionFreshWorktrees scaffolds
2947
- // under destinationParent/.rivet-variants/) we can rename it into
2948
- // place. That moves the entire tree — including the materialized
2949
- // node_modules — in O(1) with no copy, and the rename is atomic.
2950
- // For cross-volume cases (user passed a destinationParent on a
2951
- // different mount than the .rivet-variants dir, e.g. tests stubbing
2952
- // workspacePath to a tmp dir), fall back to the recursive-copy +
2953
- // background-install path so the feature still works.
2954
2419
  const record = resources.worktrees.get(args.variantId);
2955
2420
  if (!record) {
2956
2421
  throw new errors_1.AgentVariantsError('WORK_ITEM_NOT_FOUND', `Unknown variantId ${args.variantId} for session ${args.sessionId}`);
@@ -2959,25 +2424,7 @@ class AgentVariantsOrchestrator {
2959
2424
  let isDependencyInstallRequired = false;
2960
2425
  try {
2961
2426
  if (areSameVolume(record.worktreePath, destinationPath)) {
2962
- // Vite's dev server writes its module cache to `<worktreePath>/.vite/`
2963
- // and recreates that directory on the fly whenever it's missing. If
2964
- // we rename a worktree out from under a running dev server, Vite
2965
- // will materialize an empty `.vite/` shell back at the OLD path —
2966
- // leaving behind orphan stub dirs in `.rivet-variants/` and
2967
- // confusing later cleanup. Stop every variant's dev server first
2968
- // (including the chosen variant, which is about to move) so no
2969
- // process is holding the old paths open during the rename.
2970
- //
2971
- // We stop them eagerly here rather than relying on teardownSession
2972
- // because teardown runs AFTER the rename — too late to prevent the
2973
- // stub-dir race.
2974
2427
  await this.stopAllDevServersForSession(resources);
2975
- // node_modules in fresh variants 1..N is a symlink into variant 0
2976
- // (see runBackgroundInstall). After rename, variant 0's
2977
- // node_modules vanishes during cleanup of the sibling worktrees,
2978
- // leaving the chosen variant with a dangling link. Resolve the
2979
- // symlink to a real directory first so the destination has real
2980
- // deps and the rename is self-contained.
2981
2428
  isDependencyInstallRequired = await resolveNodeModulesSymlink(record.worktreePath);
2982
2429
  await this.renameWorktreeIntoDestination(record.worktreePath, destinationPath);
2983
2430
  usedRename = true;
@@ -2992,21 +2439,6 @@ class AgentVariantsOrchestrator {
2992
2439
  }
2993
2440
  changedFilesCount = countWorktreeFiles(destinationPath);
2994
2441
  if (!usedRename) {
2995
- // Cross-volume fallback: materializeProject excludes node_modules
2996
- // so we have to re-install at the destination. Don't await the
2997
- // install — the agent's commit_variant call should return
2998
- // immediately — but chain the dev-server start to it so the iframe
2999
- // recovers without manual intervention once deps are ready.
3000
- //
3001
- // NOTE: `committedPreviewPort` stays undefined on this branch by
3002
- // design — the install + start sequence takes minutes, far longer
3003
- // than the response can wait. The iframe's `switchPreviewPort`
3004
- // callback (invoked from `startCommittedDevServer` when the
3005
- // server finally comes up) retargets the proxy then, so the
3006
- // iframe reaches the new server without the URL being on the
3007
- // commit response. The response surface stays `previewUrl?:` (the
3008
- // field is optional) and consumers fall back to the proxy
3009
- // upstream when it's absent.
3010
2442
  void this.installDependencies(destinationPath)
3011
2443
  .then(() => this.startCommittedDevServer({
3012
2444
  resources,
@@ -3020,9 +2452,6 @@ class AgentVariantsOrchestrator {
3020
2452
  await this.installDependencies(destinationPath);
3021
2453
  }
3022
2454
  if (usedRename) {
3023
- // Preserve the unchosen variants as runnable siblings of the
3024
- // chosen project. Best-effort: failures here log and continue so
3025
- // a partial history never blocks the commit handoff.
3026
2455
  try {
3027
2456
  runnablePaths = this.preserveUnchosenVariants({
3028
2457
  sessionId: args.sessionId,
@@ -3033,12 +2462,6 @@ class AgentVariantsOrchestrator {
3033
2462
  catch (err) {
3034
2463
  log.warn(`preserveUnchosenVariants failed for session ${args.sessionId}; continuing without history`, err);
3035
2464
  }
3036
- // Stopped every dev server pre-rename to dodge the .vite/ stub-dir
3037
- // race. The iframe is still pointed at the (now-dead) chosen
3038
- // variant's port via the proxy, so without a fresh server at the
3039
- // destination the user would see "preview disconnected" the moment
3040
- // they commit. Spawn one and retarget the proxy. Best-effort: if
3041
- // it fails, the user can `npm run dev` themselves at destination.
3042
2465
  committedPreviewPort = await this.startCommittedDevServer({
3043
2466
  resources,
3044
2467
  destinationPath,
@@ -3056,18 +2479,7 @@ class AgentVariantsOrchestrator {
3056
2479
  envelopeDestination = destinationPath;
3057
2480
  }
3058
2481
  else {
3059
- // Static_preview: HTML is the entire deliverable. Write index.html.
3060
- // Prefer the in-memory record; fall back to the persisted history at
3061
- // `<workspacePath>/.rivet/variants/<sessionId>/<variantId>/files/index.html`
3062
- // so a process restart between report_variant_complete and commit
3063
- // doesn't strand the variant.
3064
2482
  const staticPreview = resources.staticPreviews.get(args.variantId);
3065
- // Resolve the committed index.html before the flatten step below
3066
- // moves/removes the per-variant dir. `getStaticPreviewHtml` reads the
3067
- // materialized `<dir>/index.html` and falls back to the work-item's
3068
- // stored output (the reported HTML) when the on-disk file is missing
3069
- // or empty — so a failed materialization still commits real content
3070
- // rather than an empty file.
3071
2483
  const htmlFromDir = staticPreview
3072
2484
  ? this.getStaticPreviewHtml(args.sessionId, args.variantId)
3073
2485
  : undefined;
@@ -3081,21 +2493,6 @@ class AgentVariantsOrchestrator {
3081
2493
  }
3082
2494
  try {
3083
2495
  fs_1.default.mkdirSync(destinationPath, { recursive: true });
3084
- // If the chosen variant has an `assetBase` subdir under
3085
- // destinationPath (e.g. `<workspace>/var-1` containing
3086
- // `avatar.glb`), move its contents up to the destination root so
3087
- // the committed HTML can reference them as `./avatar.glb`. Then
3088
- // delete every OTHER static_preview record's assetBase — the
3089
- // unchosen variants' sibling assets are no longer relevant once
3090
- // the user picked. Both steps run before the canonical index.html
3091
- // is written so the chosen variant's on-disk index.html (if any)
3092
- // gets overwritten by the inlined version below.
3093
- // assetBase entries are realpath'd; compare against the realpath
3094
- // of destinationPath so symlink-stripped paths line up (macOS:
3095
- // `/tmp` → `/private/tmp`). `realDestination` was computed once
3096
- // at the top of the fresh branch — re-realpath in case `mkdirSync`
3097
- // above just created the directory (the initial computation may
3098
- // have returned undefined when the dir didn't yet exist).
3099
2496
  let flattenBase = realDestination;
3100
2497
  if (!flattenBase) {
3101
2498
  try {
@@ -3106,32 +2503,17 @@ class AgentVariantsOrchestrator {
3106
2503
  }
3107
2504
  }
3108
2505
  const chosenAssetBase = staticPreview?.assetBase;
3109
- // Skip flattening when assetBase IS the destination — the agent
3110
- // wrote files directly at the workspace root, so there's no subdir
3111
- // to flatten. Only move when assetBase is a strict descendant.
3112
2506
  if (chosenAssetBase &&
3113
2507
  isStrictlyInside(chosenAssetBase, flattenBase)) {
3114
2508
  for (const entry of fs_1.default.readdirSync(chosenAssetBase)) {
3115
2509
  const src = path_1.default.join(chosenAssetBase, entry);
3116
2510
  const dst = path_1.default.join(flattenBase, entry);
3117
- // Self-collision guard: when an agent names a child the same
3118
- // as the parent dir (e.g. `var-1/var-1/`), `dst` resolves to
3119
- // `chosenAssetBase` itself. The rmSync below would then delete
3120
- // the parent — including every remaining entry we're about to
3121
- // move — and the next iteration's `renameSync` would fail
3122
- // with ENOENT. The agent's last attempted variant would also
3123
- // be silently dropped. Skip this entry; we still rm the
3124
- // parent at the end of the loop.
3125
2511
  if (path_1.default.resolve(dst) === path_1.default.resolve(chosenAssetBase)) {
3126
2512
  continue;
3127
2513
  }
3128
2514
  if (fs_1.default.existsSync(dst)) {
3129
2515
  fs_1.default.rmSync(dst, { recursive: true, force: true });
3130
2516
  }
3131
- // Cross-volume safety: rename fails with EXDEV when src and
3132
- // dst are on different mounts (possible when the user's
3133
- // workspacePath crosses a symlink to another volume). Fall
3134
- // back to recursive copy + remove so the commit doesn't die.
3135
2517
  try {
3136
2518
  fs_1.default.renameSync(src, dst);
3137
2519
  }
@@ -3160,10 +2542,6 @@ class AgentVariantsOrchestrator {
3160
2542
  throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `Failed to write static preview to ${destinationPath}: ${message}`);
3161
2543
  }
3162
2544
  changedFilesCount = countWorktreeFiles(destinationPath);
3163
- // Spawn a static file server at destinationPath and retarget the
3164
- // proxy so the iframe stays live across commit (parity with the
3165
- // vite_app rename path). Best-effort: a failure leaves the iframe
3166
- // showing "preview disconnected" but doesn't fail the commit.
3167
2545
  committedPreviewPort = await this.startCommittedDevServer({
3168
2546
  resources,
3169
2547
  destinationPath,
@@ -3179,10 +2557,6 @@ class AgentVariantsOrchestrator {
3179
2557
  }
3180
2558
  }
3181
2559
  else {
3182
- // Existing project: diff against the user's repo. resolveEnv lives in
3183
- // this branch only — fresh sessions don't have a user-supplied project
3184
- // and the bridge.getProjectPath() it reads from would either be stale
3185
- // or empty for them.
3186
2560
  const env = await this.resolveEnv(args.sessionId);
3187
2561
  const record = resources.worktrees.get(args.variantId);
3188
2562
  if (!record) {
@@ -3192,8 +2566,6 @@ class AgentVariantsOrchestrator {
3192
2566
  throw new errors_1.AgentVariantsError('INVALID_STAGE_ACTION', `Variant ${args.variantId} has no captured diff yet — wait for report_variant_complete(succeeded) first`);
3193
2567
  }
3194
2568
  changedFilesCount = countDiffFiles(record.diff);
3195
- // Apply the diff to the user's working tree so the variant lands on the
3196
- // current branch uncommitted.
3197
2569
  if (!this.worktrees.applyDiffToProject) {
3198
2570
  throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', 'WorktreeManager does not support applying diffs to the project tree');
3199
2571
  }
@@ -3221,17 +2593,10 @@ class AgentVariantsOrchestrator {
3221
2593
  changedFilesCount,
3222
2594
  payload,
3223
2595
  };
3224
- // Record the pick in the SessionStore so future commitVariant calls with
3225
- // the same variantId are idempotent and a different variantId returns
3226
- // PENDING_CHANGE_CONFLICT (already picked).
3227
2596
  this.store.recordVariantPick({
3228
2597
  sessionId: args.sessionId,
3229
2598
  envelope,
3230
2599
  });
3231
- // History: flip every persisted variant in this session to its terminal
3232
- // status. Variants are persisted at code_gen success time; this patches
3233
- // the status and destinationPath for the chosen one. Best-effort, never
3234
- // blocks the commit.
3235
2600
  const historyProjectPath = projectContext.kind === 'fresh'
3236
2601
  ? projectContext.workspacePath
3237
2602
  : envelopeDestination;
@@ -3249,13 +2614,6 @@ class AgentVariantsOrchestrator {
3249
2614
  log.warn(`persistVariantHistoryAtCommit failed for session ${args.sessionId}`, err);
3250
2615
  }
3251
2616
  resources.committedVariantIds.add(args.variantId);
3252
- // For fresh `new-project` commits the committed variant IS the user's
3253
- // final result — there's no dev server to fall back to. Keep the session
3254
- // active so SSE keeps pushing snapshots with the chosen variant; the UI's
3255
- // iframe stays on it instead of unmounting to about:blank (which would
3256
- // surface a misleading "Preview isn't connected" overlay). For diff /
3257
- // diff-applied commits the user moves on to their own dev server, so
3258
- // clearing is correct — that's the original behavior preserved below.
3259
2617
  if (this.activeSessionId === args.sessionId &&
3260
2618
  payload.kind !== 'project-created') {
3261
2619
  this.activeSessionId = null;
@@ -3275,9 +2633,6 @@ class AgentVariantsOrchestrator {
3275
2633
  this.emitChange();
3276
2634
  log.info(`Variant ${args.variantId} (${input.briefLabel}) committed by user`);
3277
2635
  if (!args.skipTeardown) {
3278
- // Once a variant is committed, the unpicked worktrees are no longer
3279
- // useful. Schedule teardown in the background so the agent's
3280
- // commit_variant call returns immediately.
3281
2636
  void this.teardownSession(args.sessionId, 'committed').catch((err) => {
3282
2637
  log.warn(`teardownSession after commit failed for ${args.sessionId}`, err);
3283
2638
  });
@@ -3298,25 +2653,7 @@ class AgentVariantsOrchestrator {
3298
2653
  async cleanupCommittedSession(sessionId) {
3299
2654
  await this.teardownSession(sessionId, 'committed');
3300
2655
  }
3301
- /**
3302
- * Ensure the user-facing destination path can receive the new project.
3303
- * Rejects when the path exists and is non-empty.
3304
- *
3305
- * `.rivet/` (pre-commit snapshots + history manifests) and `.gitignore`
3306
- * (written by VariantHistoryService.ensureGitignore when variants persist
3307
- * at success time) are both tolerated — neither is user-authored content
3308
- * that would be clobbered by the materialize step.
3309
- */
3310
- assertDestinationAvailable(destinationPath,
3311
- /**
3312
- * Top-level entries the caller knows it owns and can safely overwrite.
3313
- * Used by the fresh static_preview commit path so a directory holding
3314
- * only this session's per-variant `assetBase` subdirs still counts as
3315
- * "available" — those came from the agent during generation. Any entry
3316
- * NOT in this set (and not the standard `.rivet`/`.gitignore` exemptions)
3317
- * still triggers DESTINATION_NOT_EMPTY.
3318
- */
3319
- additionalAllowedEntries = new Set()) {
2656
+ assertDestinationAvailable(destinationPath, additionalAllowedEntries = new Set()) {
3320
2657
  if (!fs_1.default.existsSync(destinationPath))
3321
2658
  return;
3322
2659
  const entries = fs_1.default.readdirSync(destinationPath);
@@ -3327,18 +2664,13 @@ class AgentVariantsOrchestrator {
3327
2664
  return;
3328
2665
  throw new errors_1.AgentVariantsError('DESTINATION_NOT_EMPTY', `Destination ${destinationPath} is not empty (${userVisibleEntries.length} entries) — refuse to materialize.`);
3329
2666
  }
3330
- /** Read the captured diff for a code-gen variant, if available. */
3331
2667
  getVariantDiff(sessionId, variantId) {
3332
2668
  return this.resources.get(sessionId)?.worktrees.get(variantId)?.diff;
3333
2669
  }
3334
- // --- Side-effect implementations ----------------------------------------
3335
2670
  async provisionWorktrees(sessionId, approveResult) {
3336
2671
  const projectContext = this.store.getProjectContext(sessionId);
3337
2672
  const variantCount = approveResult.codeGenWorkItemIds.length +
3338
2673
  (approveResult.scaffoldBaseWorkItemId ? 1 : 0);
3339
- // Generated + static_preview provisions nothing (HTML is the deliverable)
3340
- // — skip the timing/telemetry wrapper so the fast path stays untouched.
3341
- // `provision: 'none'` is exactly the no-scaffold generated-static lane.
3342
2674
  if ((0, runPlan_1.resolveRunPlanPolicy)(projectContext).provision === 'none') {
3343
2675
  return;
3344
2676
  }
@@ -3351,10 +2683,6 @@ class AgentVariantsOrchestrator {
3351
2683
  });
3352
2684
  const run = async () => {
3353
2685
  if (projectContext.kind === 'fresh') {
3354
- // Fresh + vite_app: provision a Vite skeleton per variant and copy the
3355
- // agent-planned assetPlan files before the agent leases its code_gen
3356
- // items. (Background `npm install` is dispatched inside and is NOT
3357
- // awaited here — the timeout bounds worktree creation, not install.)
3358
2686
  await this.provisionFreshWorktrees(sessionId, approveResult, projectContext);
3359
2687
  return;
3360
2688
  }
@@ -3385,9 +2713,6 @@ class AgentVariantsOrchestrator {
3385
2713
  ? err.code
3386
2714
  : 'PROVISION_FAILED',
3387
2715
  });
3388
- // Cancel synchronously before cleanup so a retry does not see this
3389
- // failed session as active. cancel() schedules teardown without blocking
3390
- // the provisioning error on git cleanup.
3391
2716
  void this.cancel({ sessionId, reason: 'provisioning_failed' }).catch((teardownErr) => {
3392
2717
  log.warn(`cleanup after provisioning failure for ${sessionId} failed`, teardownErr);
3393
2718
  });
@@ -3402,11 +2727,6 @@ class AgentVariantsOrchestrator {
3402
2727
  throw err;
3403
2728
  }
3404
2729
  }
3405
- /**
3406
- * Existing-project flow: clone the user's repo N times via git worktree.
3407
- * Runs synchronously because the operation is fast (no install needed —
3408
- * node_modules is symlinked from the source project).
3409
- */
3410
2730
  async provisionExistingWorktrees(sessionId, approveResult) {
3411
2731
  const resources = this.ensureResources(sessionId);
3412
2732
  const totalCount = approveResult.codeGenWorkItemIds.length +
@@ -3433,19 +2753,6 @@ class AgentVariantsOrchestrator {
3433
2753
  });
3434
2754
  }
3435
2755
  }
3436
- /**
3437
- * Fresh-project flow: scaffold one template per variant + DESIGN.md per
3438
- * slot synchronously (fast — just file writes + git init), then dispatch a
3439
- * background `npm install` task per worktree. Returns immediately so the
3440
- * `approve_variant_briefs` MCP tool doesn't time out on cold installs.
3441
- *
3442
- * On install success: `sessionStore.completeInternal(scaffold_base)` runs,
3443
- * which unblocks the dependent code_gen items.
3444
- *
3445
- * On install failure: the scaffold_base work item is marked failed via
3446
- * `failInternal`, which cascades DEPENDENCY_FAILED to all dependent
3447
- * code_gen items so the session degrades cleanly.
3448
- */
3449
2756
  async provisionFreshWorktrees(sessionId, approveResult, projectContext) {
3450
2757
  const resources = this.ensureResources(sessionId);
3451
2758
  const scaffoldId = approveResult.scaffoldBaseWorkItemId;
@@ -3472,33 +2779,14 @@ class AgentVariantsOrchestrator {
3472
2779
  designContext: summarizeDesignContext(designContext),
3473
2780
  });
3474
2781
  log.info(`Provisioning ${codeGenIds.length} fresh worktree(s) for session ${sessionId}`);
3475
- // destinationParent for fresh worktrees: the user's workspace root
3476
- // (sibling to `.rivet/`). Keeping the worktree on the same volume as
3477
- // the materialize destination turns commit into a directory rename
3478
- // instead of a recursive copy. `path.dirname(workspacePath)` *used*
3479
- // to equal `workspaceRoot`, but after nesting subprojects under
3480
- // `<workspaceRoot>/.rivet/<slug>/` the dirname is now `.rivet/`,
3481
- // which would stage worktrees inside `.rivet/.rivet-variants/`.
3482
- // Use workspaceRoot directly so staging lives at
3483
- // `<workspaceRoot>/.rivet-variants/` as originally intended.
3484
2782
  const destinationParent = projectContext.workspaceRoot;
3485
2783
  const paths = await createFresh.call(this.worktrees, sessionId, codeGenIds.length, viteReactTs_1.VITE_REACT_TS_TEMPLATE, designContext, destinationParent);
3486
2784
  resources.scaffoldBaseWorkItemId = scaffoldId;
3487
2785
  resources.freshDestinationParent = destinationParent;
3488
- // Each code_gen item maps 1:1 to a fresh worktree. The scaffold_base
3489
- // work item is internal — no dedicated worktree.
3490
2786
  await Promise.all(codeGenIds.map((workItemId, index) => this.registerWorktreeWorkspace(resources, {
3491
2787
  workItemId,
3492
2788
  worktreePath: paths[index],
3493
2789
  })));
3494
- // Copy agent-planned assets (e.g. avatar.glb) into each worktree so
3495
- // generated code can reference them via plan.referenceAs. Done before
3496
- // the background install so the assets are on disk by the time the
3497
- // agent leases its code_gen item.
3498
- // If copy throws (e.g. source asset deleted between planning and
3499
- // provisioning), fail scaffold_base immediately so dependent code_gen
3500
- // items cascade DEPENDENCY_FAILED instead of leaving the session
3501
- // permanently stuck in `work_items_ready`.
3502
2790
  const assetPlan = projectContext.executionPlan?.assetPlan ?? [];
3503
2791
  if (assetPlan.length > 0) {
3504
2792
  try {
@@ -3530,39 +2818,22 @@ class AgentVariantsOrchestrator {
3530
2818
  return;
3531
2819
  }
3532
2820
  }
3533
- // Background install: don't await. The orchestrator finishes the
3534
- // approve→provision call immediately; the agent polls for scaffold
3535
- // completion via continue_variants(action='check').
3536
2821
  void this.runBackgroundInstall(sessionId, scaffoldId, paths, scaffoldStartedAt).catch((err) => {
3537
2822
  log.error(`runBackgroundInstall failed for session ${sessionId}`, err);
3538
2823
  });
3539
2824
  }
3540
- /**
3541
- * Run `npm install` in parallel across all fresh worktrees. On all-success,
3542
- * mark scaffold_base succeeded via `completeInternal`. On any failure,
3543
- * mark scaffold_base failed via `failInternal` and emit telemetry.
3544
- */
3545
2825
  async runBackgroundInstall(sessionId, scaffoldWorkItemId, worktreePaths, scaffoldStartedAt) {
3546
2826
  const installStartedAt = Date.now();
3547
2827
  try {
3548
- const [firstWorktree, ...rest] = worktreePaths;
3549
- await this.installDependencies(firstWorktree);
3550
- await Promise.all(rest.map(async (p) => {
3551
- const target = path_1.default.join(firstWorktree, 'node_modules');
3552
- const link = path_1.default.join(p, 'node_modules');
3553
- // The fresh template has no node_modules until install runs, so
3554
- // the link target is guaranteed absent in `p` — no need to remove
3555
- // first.
3556
- try {
3557
- fs_1.default.symlinkSync(target, link, 'dir');
3558
- }
3559
- catch (symlinkErr) {
3560
- log.warn(`Symlink node_modules failed for ${p}, falling back to install`, symlinkErr);
3561
- await this.installDependencies(p);
3562
- }
3563
- }));
2828
+ await Promise.all(worktreePaths.map((worktreePath) => this.installDependencies(worktreePath, {
2829
+ workspaceRoot: worktreePath,
2830
+ signal: this.resources.get(sessionId)?.dependencyAbortController.signal,
2831
+ })));
3564
2832
  }
3565
2833
  catch (err) {
2834
+ const resources = this.resources.get(sessionId);
2835
+ if (!resources || resources.cleanupStarted)
2836
+ return;
3566
2837
  const message = err instanceof Error ? err.message : String(err);
3567
2838
  log.warn(`npm install failed for session ${sessionId}; marking scaffold_base failed`, err);
3568
2839
  trackScaffoldFailed(this.telemetry, {
@@ -3601,12 +2872,6 @@ class AgentVariantsOrchestrator {
3601
2872
  variantCount: worktreePaths.length,
3602
2873
  });
3603
2874
  }
3604
- /**
3605
- * Checks succeeded implementation reports before they reach the store.
3606
- * Static previews must contain a structurally complete local artifact;
3607
- * applicable existing-project variants must contain an added diff. Returns
3608
- * a pass, a single repair retry, or a terminal integrity failure.
3609
- */
3610
2875
  async evaluateQaForReport(args) {
3611
2876
  if (args.status !== 'succeeded')
3612
2877
  return null;
@@ -3614,15 +2879,12 @@ class AgentVariantsOrchestrator {
3614
2879
  return null;
3615
2880
  const projectContext = this.store.getProjectContext(args.sessionId);
3616
2881
  const kind = this.store.getWorkItemKind(args.sessionId, args.workItemId);
3617
- // Existing-project code_gen has no owned preview document to inspect. Its
3618
- // structural contract is a captured diff containing added source content.
3619
2882
  if (projectContext.kind !== 'fresh') {
3620
2883
  if (kind === 'code_gen') {
3621
2884
  return this.evaluateExistingCodeGenQa(args);
3622
2885
  }
3623
2886
  return null;
3624
2887
  }
3625
- // Resolve the objective base QA for this deliverable kind.
3626
2888
  let baseQa = null;
3627
2889
  const checkedSource = undefined;
3628
2890
  if (kind === 'static_preview') {
@@ -3632,8 +2894,6 @@ class AgentVariantsOrchestrator {
3632
2894
  baseQa = await this.runPreviewQaSafely(args, parsed.html);
3633
2895
  }
3634
2896
  else if (kind === 'code_gen') {
3635
- // Fresh vite_app variants run agent-authored dev-server code. No
3636
- // preview document to inspect — behave exactly as before.
3637
2897
  return null;
3638
2898
  }
3639
2899
  else {
@@ -3651,7 +2911,6 @@ class AgentVariantsOrchestrator {
3651
2911
  },
3652
2912
  };
3653
2913
  }
3654
- // Synthesize a passing base verdict for vite_app (no objective layer).
3655
2914
  const passedBase = baseQa ?? {
3656
2915
  status: 'passed',
3657
2916
  issues: [],
@@ -3735,8 +2994,6 @@ class AgentVariantsOrchestrator {
3735
2994
  },
3736
2995
  };
3737
2996
  }
3738
- /** Run the injected preview QA runner, converting a thrown runner into a
3739
- * failed `VariantQaResult` (mirrors the prior inline behavior). */
3740
2997
  async runPreviewQaSafely(args, html) {
3741
2998
  try {
3742
2999
  return await this.previewQaRunner({
@@ -3763,11 +3020,6 @@ class AgentVariantsOrchestrator {
3763
3020
  memoizeQa(sessionId, workItemId, qa) {
3764
3021
  this.ensureResources(sessionId).qaResults.set(workItemId, qa);
3765
3022
  }
3766
- /**
3767
- * Handle a `retry` QA verdict: consume the agent's report, re-open the
3768
- * variant work item so it can be re-leased and regenerated against the QA
3769
- * notes, and record that it has now used its single retry.
3770
- */
3771
3023
  async handleQaRegenRetry(args, qa) {
3772
3024
  const resources = this.ensureResources(args.sessionId);
3773
3025
  const result = this.store.requeueForRegeneration({
@@ -3789,11 +3041,6 @@ class AgentVariantsOrchestrator {
3789
3041
  overall: null,
3790
3042
  });
3791
3043
  this.emitChange();
3792
- // Carry the requeue signal + critique so the tool layer can tell the agent
3793
- // to re-lease and regenerate. The store result is an opaque
3794
- // `waiting_for_results` (the variant is pending again); on its own that is
3795
- // indistinguishable from a sibling still in flight, so the agent would
3796
- // otherwise abandon the variant and the session would stall at ready:0.
3797
3044
  const completionResult = { ...result, requeuedForRegeneration: true, qa };
3798
3045
  resources.repairCompletionResults.set(repairCompletionKey(args), completionResult);
3799
3046
  return completionResult;
@@ -3812,16 +3059,6 @@ class AgentVariantsOrchestrator {
3812
3059
  args.record.pendingAutoRestart !== args.autoRestartTimer;
3813
3060
  return Boolean(resources && !resources.cleanupStarted && !isAutoRestartCancelled);
3814
3061
  };
3815
- // Bring up a dev server in the variant's worktree so the user can cycle
3816
- // through live variants in the iframe via the chip. Failures here are
3817
- // logged but non-fatal — the user can still pick by reading the diff.
3818
- //
3819
- // Pick a sensible port near the framework's default (3000 for Next, 5173
3820
- // for Vite) and inject it via PORT + the CLI flag (see buildDevServerCommand)
3821
- // so the server actually binds it — that's what moves a preview off a port
3822
- // the user's own dev server already holds instead of colliding on it. On a
3823
- // retry we scan past the port we just tried, so a transient race or a
3824
- // briefly-held port resolves onto the next free one.
3825
3062
  const previewStartedAt = Date.now();
3826
3063
  const projectContextKind = args.isFresh ? 'fresh' : 'existing';
3827
3064
  trackPreviewStarted(this.telemetry, {
@@ -3833,25 +3070,19 @@ class AgentVariantsOrchestrator {
3833
3070
  const startPort = await this.resolveDevServerStartPort(args.sessionId, args.isFresh);
3834
3071
  let scanFrom = startPort;
3835
3072
  const maxAttempts = DEV_SERVER_START_MAX_ATTEMPTS;
3836
- // At most one install-then-retry per start: a second missing-module exit
3837
- // after a fresh install means the install can't fix it.
3838
3073
  let installRetried = false;
3839
3074
  let lastDev;
3840
3075
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
3841
- // Teardown can begin while a prior attempt waits for dependency
3842
- // installation. Do not start more work against a removed worktree.
3843
3076
  if (!canContinueStart())
3844
3077
  return undefined;
3845
3078
  let port;
3846
3079
  try {
3847
3080
  port = await this.worktrees.findFreePortFrom(scanFrom);
3848
- const dev = await this.resolveDevServer(args.sessionId, args.workItemId, args.record.worktreePath, port, args.isFresh);
3081
+ const dev = await this.resolveDevServer(args.sessionId, args.record.worktreePath, port, args.isFresh);
3849
3082
  lastDev = dev;
3083
+ if (!canContinueStart())
3084
+ return undefined;
3850
3085
  const proc = await this.worktrees.startDevServer(dev.cwd, port, dev.cmd, dev.args, dev.env);
3851
- // The boot can outlive the session (a cancel during the readiness
3852
- // wait, or during an auto-restart's delay). At this point the child
3853
- // isn't on the record yet, so teardown's stop loop can't see it —
3854
- // kill it here instead of leaking an orphaned dev server.
3855
3086
  if (!canContinueStart()) {
3856
3087
  log.info(`Variant ${args.workItemId} dev server booted after preview cleanup; stopping it`);
3857
3088
  void this.worktrees.stopDevServer(proc).catch(() => undefined);
@@ -3863,16 +3094,6 @@ class AgentVariantsOrchestrator {
3863
3094
  args.record.port = port;
3864
3095
  args.record.devServerProcess = proc;
3865
3096
  args.record.devCommand = dev;
3866
- // If the dev server dies on its own (crash, OOM, Vite hard-fail),
3867
- // clear the port so the iframe stops routing the chip onto a dead
3868
- // socket, then bring a replacement up automatically. A ready server
3869
- // that later crashes is almost always recoverable by a clean restart
3870
- // (transient OOM, watcher crash, HMR wedge) — without one, every such
3871
- // crash surfaced as a permanent "preview unavailable" plus a burst of
3872
- // upstream_lost noise until the user regenerated the variant. The
3873
- // restart budget keeps a genuinely broken build (crash loop) from
3874
- // restarting forever. Intentional stops clear `devServerProcess`
3875
- // before killing, so the guard below filters them out.
3876
3097
  proc.once('exit', (code, signal) => {
3877
3098
  if (args.record.devServerProcess !== proc)
3878
3099
  return;
@@ -3891,8 +3112,6 @@ class AgentVariantsOrchestrator {
3891
3112
  args.record.devServerAutoRestarts = restartsUsed + 1;
3892
3113
  this.emitChange();
3893
3114
  const timer = setTimeout(() => {
3894
- // Re-check: the session may have been torn down while the
3895
- // restart was pending — don't spawn into a cleaned-up worktree.
3896
3115
  const current = this.resources.get(args.sessionId);
3897
3116
  if (!current ||
3898
3117
  current.cleanupStarted ||
@@ -3920,8 +3139,6 @@ class AgentVariantsOrchestrator {
3920
3139
  });
3921
3140
  this.emitChange();
3922
3141
  });
3923
- // A prior failure record (startup failure before a successful retry,
3924
- // idle reclaim, crash before this restart) is now stale.
3925
3142
  this.resources
3926
3143
  .get(args.sessionId)
3927
3144
  ?.previewFailures.delete(args.workItemId);
@@ -3950,16 +3167,6 @@ class AgentVariantsOrchestrator {
3950
3167
  const devErr = (0, devServerError_1.isDevServerStartError)(err) ? err : undefined;
3951
3168
  const portInUse = devErr?.portInUse ?? false;
3952
3169
  const reason = devErr?.reason ?? 'unknown';
3953
- // Classify what a retry can actually fix:
3954
- // - port_in_use: a fresh port fixes it (also step past unmatched
3955
- // collision messages via the blanket exited retry below).
3956
- // - missing/stale dependencies (module-resolution errors in the
3957
- // output): an install fixes it — install, then retry once.
3958
- // - any other early exit: retry once; exits fail fast, so one clean
3959
- // attempt is cheap insurance against transient races (fs churn from
3960
- // sibling provisioning, npm cache contention).
3961
- // - timeout / spawn_error / missing_dev_script: deterministic; a
3962
- // retry just doubles the wait, so fail with the actionable cause.
3963
3170
  const missingDepsOutput = (0, devServerError_1.isMissingDependencyOutput)(devErr?.detail ?? '');
3964
3171
  const missingDeps = missingDepsOutput &&
3965
3172
  reason === 'exited' &&
@@ -3968,8 +3175,6 @@ class AgentVariantsOrchestrator {
3968
3175
  canInstallWithoutTouchingHost(lastDev.cwd);
3969
3176
  const willRetry = attempt < maxAttempts &&
3970
3177
  (portInUse || missingDeps || (reason === 'exited' && attempt === 1));
3971
- // Next attempt scans past the port we just tried so we don't re-pick a
3972
- // port that's persistently held (vs. a transient race that freed it).
3973
3178
  scanFrom = (port ?? scanFrom) + 1;
3974
3179
  log.warn(`Failed to start dev server for variant ${args.workItemId} on port ${port ?? 'unallocated'} ` +
3975
3180
  `(attempt ${attempt}/${maxAttempts}, reason=${reason}, portInUse=${portInUse}, missingDeps=${missingDeps})` +
@@ -3982,33 +3187,48 @@ class AgentVariantsOrchestrator {
3982
3187
  if (missingDeps && lastDev) {
3983
3188
  installRetried = true;
3984
3189
  try {
3985
- await this.installDependencies(lastDev.cwd, lastDev.packageManager);
3190
+ await this.installDependencies(lastDev.cwd, {
3191
+ workspaceRoot: args.record.worktreePath,
3192
+ force: true,
3193
+ signal: this.resources.get(args.sessionId)
3194
+ ?.dependencyAbortController.signal,
3195
+ });
3986
3196
  }
3987
3197
  catch (installErr) {
3988
- // Still retry: the boot error may have been transient, and the
3989
- // retry's own failure telemetry is more specific than aborting
3990
- // here on the install's stderr.
3991
- log.warn(`Dependency install before dev-server retry failed for variant ${args.workItemId}`, installErr);
3198
+ log.warn(`Dependency preparation failed for variant ${args.workItemId}`, installErr);
3199
+ this.resources
3200
+ .get(args.sessionId)
3201
+ ?.previewFailures.set(args.workItemId, {
3202
+ reason: 'dependency_install_failed',
3203
+ portInUse: false,
3204
+ });
3205
+ trackPreviewFailed(this.telemetry, {
3206
+ sessionId: args.sessionId,
3207
+ variantId: args.workItemId,
3208
+ projectContextKind,
3209
+ previewKind: 'dev_server',
3210
+ errorCode: 'DEPENDENCY_INSTALL_FAILED',
3211
+ reason: 'dependency_install_failed',
3212
+ portInUse: false,
3213
+ durationMs: Date.now() - previewStartedAt,
3214
+ });
3215
+ this.emitChange();
3216
+ return undefined;
3992
3217
  }
3993
3218
  }
3994
3219
  continue;
3995
3220
  }
3996
- // Record the failure so getVariants can tell the UI this direction's
3997
- // preview couldn't start (and why), then push a snapshot so the chip
3998
- // updates from "loading" to the accurate per-direction message.
3999
3221
  this.resources
4000
3222
  .get(args.sessionId)
4001
3223
  ?.previewFailures.set(args.workItemId, {
4002
3224
  reason,
4003
3225
  portInUse,
4004
3226
  });
4005
- const errorCode = reason === 'missing_dev_script'
4006
- ? 'DEV_SCRIPT_MISSING'
4007
- : 'DEV_SERVER_START_FAILED';
4008
- // Telemetry gets structured classification only — never the raw
4009
- // output tail, which routinely carries absolute project paths and
4010
- // source frames (see AGENTS.md "What NEVER to capture"). The full
4011
- // tail still reaches the local logs via `err` above.
3227
+ let errorCode = 'DEV_SERVER_START_FAILED';
3228
+ if (reason === 'dependency_install_failed')
3229
+ errorCode = 'DEPENDENCY_INSTALL_FAILED';
3230
+ if (reason === 'missing_dev_script')
3231
+ errorCode = 'DEV_SCRIPT_MISSING';
4012
3232
  trackFreshDevServerFailed(this.telemetry, {
4013
3233
  sessionId: args.sessionId,
4014
3234
  variantId: args.workItemId,
@@ -4036,9 +3256,6 @@ class AgentVariantsOrchestrator {
4036
3256
  return undefined;
4037
3257
  }
4038
3258
  finally {
4039
- // Hand the reservation back whether the dev server bound the port
4040
- // (now visible to the listener check, so the reservation is redundant),
4041
- // it failed (port free again), or we're about to retry on the next port.
4042
3259
  if (port !== undefined)
4043
3260
  this.worktrees.releasePort?.(port);
4044
3261
  }
@@ -4066,12 +3283,6 @@ class AgentVariantsOrchestrator {
4066
3283
  assetBase: staticPreview.assetBase,
4067
3284
  projectContext,
4068
3285
  });
4069
- // Materialize the variant into a per-variant directory so every
4070
- // static variant is directory-backed (the same shape as the
4071
- // worktree-based variants). Reuse the agent's assetBase dir when it
4072
- // wrote sibling assets; otherwise create a Rivet-managed per-variant
4073
- // dir under workspacePath. `staticPreview.html` is the complete built
4074
- // document (html + css + js), written as index.html.
4075
3286
  const workspacePath = projectContext.kind === 'fresh'
4076
3287
  ? projectContext.workspacePath
4077
3288
  : undefined;
@@ -4094,16 +3305,10 @@ class AgentVariantsOrchestrator {
4094
3305
  }
4095
3306
  try {
4096
3307
  fs_1.default.mkdirSync(dir, { recursive: true });
4097
- // Realpath the dir so it lines up with the realpath'd destination
4098
- // the commit emptiness-check compares against (macOS symlinks
4099
- // `/var` → `/private/var`). `resolveStaticPreviewAssetBase`
4100
- // already realpaths the agent-provided case; do the same for the
4101
- // Rivet-created dir.
4102
3308
  try {
4103
3309
  dir = fs_1.default.realpathSync(dir);
4104
3310
  }
4105
3311
  catch {
4106
- // keep the un-realpath'd path if realpath fails
4107
3312
  }
4108
3313
  fs_1.default.writeFileSync(path_1.default.join(dir, 'index.html'), staticPreview.html, 'utf8');
4109
3314
  }
@@ -4124,8 +3329,6 @@ class AgentVariantsOrchestrator {
4124
3329
  resources.workspaceManager.removeArtifactWorkspace(workItemId);
4125
3330
  }
4126
3331
  if (this.store.getProjectContext(sessionId).kind === 'fresh') {
4127
- // History at `<workspacePath>/.rivet/variants/<sessionId>/<variantId>/`
4128
- // is the on-disk record for this generated project.
4129
3332
  this.trackHistoryPersist(this.persistCompletedFreshVariant({
4130
3333
  sessionId,
4131
3334
  workItemId,
@@ -4156,8 +3359,6 @@ class AgentVariantsOrchestrator {
4156
3359
  log.warn(`No worktree record for ${sessionId}/${workItemId}; skipping diff capture`);
4157
3360
  return;
4158
3361
  }
4159
- // scaffold_base sets up the base for dependent code_gen items. No diff
4160
- // to capture from it — its result is the disk state of the worktree.
4161
3362
  if (resources.scaffoldBaseWorkItemId === workItemId) {
4162
3363
  log.info(`scaffold_base ${workItemId} succeeded; awaiting dependent code-gen`);
4163
3364
  return;
@@ -4173,14 +3374,6 @@ class AgentVariantsOrchestrator {
4173
3374
  catch (err) {
4174
3375
  log.warn(`getDiff failed for ${record.worktreePath}`, err);
4175
3376
  }
4176
- // History: persist every completed variant immediately to
4177
- // `<projectPath>/.rivet/variants/`. Existing projects pass the captured
4178
- // diff (or an empty string when capture itself failed — the variant
4179
- // still succeeded code-gen-wise, and the history row is the only
4180
- // record the UI has). Fresh-project variants copy their worktree
4181
- // (vite_app) or the staged HTML (static_preview). Best-effort — a
4182
- // failure here must never block dev-server startup or the user's pick
4183
- // flow.
4184
3377
  if (!isFresh) {
4185
3378
  this.trackHistoryPersist(this.persistCompletedExistingVariant({
4186
3379
  sessionId,
@@ -4198,19 +3391,8 @@ class AgentVariantsOrchestrator {
4198
3391
  log.warn(`persistCompletedFreshVariant failed for ${sessionId}/${workItemId}`, err);
4199
3392
  }));
4200
3393
  }
4201
- // Static existing-mode projects (framework='static', no dev command) have
4202
- // no per-variant dev server to proxy the iframe at. Serve the variant's
4203
- // materialized static files directly — the same renderer the chip/iframe
4204
- // already uses for fresh static_preview variants — so the variant is
4205
- // viewable/cyclable instead of being silently stuck on "Preview is
4206
- // unavailable for this variant". When this succeeds there is no dev server
4207
- // to bring up, so skip the proxy path below.
4208
3394
  if (!isFresh) {
4209
3395
  const staticPreview = await this.registerExistingStaticPreview(sessionId, workItemId, record);
4210
- // Existing projects resolve their preview surface at success time (the
4211
- // one genuinely-late axis of the run plan): a framework='static' project
4212
- // materializes static files → static_artifact and needs no dev server;
4213
- // anything else falls through to the dev-server proxy below.
4214
3396
  if (staticPreview !== 'not_static') {
4215
3397
  this.emitChange();
4216
3398
  return;
@@ -4244,17 +3426,8 @@ class AgentVariantsOrchestrator {
4244
3426
  assetBase: parsed.assetBase,
4245
3427
  projectContext: this.store.getProjectContext(args.sessionId),
4246
3428
  });
4247
- // A refinement that only rewrites HTML won't re-declare an assetBase, but
4248
- // the refined markup can still reference the original sibling assets
4249
- // (images, GLB, fonts). Fall back to the variant's existing asset base so
4250
- // those relative URLs keep resolving instead of 404ing after the swap.
4251
3429
  const previousRecord = resources.staticPreviews.get(input.variantId);
4252
3430
  const dir = resolvedAssetBase ?? previousRecord?.assetBase;
4253
- // Re-materialize index.html in the per-variant dir FIRST. The directory is
4254
- // the source of truth (`getStaticPreviewHtml` reads it disk-first), so the
4255
- // write must succeed before we swap any in-memory / stored-output state to
4256
- // the refined HTML. If it fails we throw — converting to a `failed` report
4257
- // rather than a success that still serves the pre-refine document on disk.
4258
3431
  if (dir) {
4259
3432
  try {
4260
3433
  fs_1.default.mkdirSync(dir, { recursive: true });
@@ -4264,9 +3437,6 @@ class AgentVariantsOrchestrator {
4264
3437
  throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `Failed to materialize refined static preview for ${input.variantId}: ${err instanceof Error ? err.message : String(err)}`);
4265
3438
  }
4266
3439
  }
4267
- // Swap the served preview to the refined HTML. Runs before
4268
- // store.reportComplete so any failure above converts to a `failed` report
4269
- // rather than a success pointing at unrefreshed output.
4270
3440
  this.store.replaceWorkItemOutput(args.sessionId, input.variantId, {
4271
3441
  ...parsed,
4272
3442
  ...(dir ? { assetBase: dir } : {}),
@@ -4288,10 +3458,6 @@ class AgentVariantsOrchestrator {
4288
3458
  targetWorkItemId: input.variantId,
4289
3459
  });
4290
3460
  }
4291
- // History persistence is best-effort, mirroring handleSucceededReport's
4292
- // fire-and-forget persistence for freshly completed variants. A failure
4293
- // here (e.g. a transient filesystem race) must not fail the refinement or
4294
- // discard the already-serving refined HTML.
4295
3461
  try {
4296
3462
  await this.persistCompletedFreshVariant({
4297
3463
  sessionId: args.sessionId,
@@ -4301,22 +3467,10 @@ class AgentVariantsOrchestrator {
4301
3467
  catch (err) {
4302
3468
  log.warn(`persistCompletedFreshVariant failed for refined variant ${input.variantId} (session ${args.sessionId}); preview already swapped`, err);
4303
3469
  }
4304
- // The caller (reportComplete) emits the change after recording success so
4305
- // the snapshot reflects both the swapped HTML and refinement.status.
4306
3470
  }
4307
- /** The worktree/dev-server manager this orchestrator boots previews with.
4308
- * Exposed so HistoryReplayService shares the SAME instance: the
4309
- * reserved-but-not-yet-bound port set is per-instance, and a second
4310
- * manager scanning the same range can hand a live variant's pending port
4311
- * to a replay (EADDRINUSE for the loser). */
4312
3471
  get devServerManager() {
4313
3472
  return this.worktrees;
4314
3473
  }
4315
- /** In-flight fire-and-forget history persists. Tree-store snapshots spawn
4316
- * git subprocesses, so these can outlive the microtask flushes that used
4317
- * to be enough to drain them — {@link teardownSession} (and the test
4318
- * helpers' settleBackgroundWork) await {@link settleHistoryPersists}
4319
- * instead of racing the writes. */
4320
3474
  pendingHistoryPersists = new Set();
4321
3475
  trackHistoryPersist(persist) {
4322
3476
  this.pendingHistoryPersists.add(persist);
@@ -4326,20 +3480,11 @@ class AgentVariantsOrchestrator {
4326
3480
  this.pendingHistoryPersists.delete(persist);
4327
3481
  });
4328
3482
  }
4329
- /** Await every in-flight history persist, including ones that start while
4330
- * draining (a refine can persist mid-settle). */
4331
3483
  async settleHistoryPersists() {
4332
3484
  while (this.pendingHistoryPersists.size > 0) {
4333
3485
  await Promise.allSettled([...this.pendingHistoryPersists]);
4334
3486
  }
4335
3487
  }
4336
- /**
4337
- * Persist a completed existing-project code_gen variant into
4338
- * `<env.projectPath>/.rivet/variants/<sessionId>/<variantId>/`. Called from
4339
- * `handleSucceededReport` once the worktree diff has been captured but
4340
- * before the user picks. Status is `completed` until `commitVariant` or a
4341
- * cancellation transitions it.
4342
- */
4343
3488
  async persistCompletedExistingVariant(args) {
4344
3489
  const projectContext = this.store.getProjectContext(args.sessionId);
4345
3490
  if (projectContext.kind !== 'existing')
@@ -4367,13 +3512,7 @@ class AgentVariantsOrchestrator {
4367
3512
  }
4368
3513
  const input = this.store.getWorkItemInput(args.sessionId, args.workItemId);
4369
3514
  const sessionPrompt = this.store.getPrompt(args.sessionId);
4370
- // Tree-store snapshot inputs (dual-write). The snapshot root is the
4371
- // worktree root — the git root, per the tree-root contract — for EVERY
4372
- // framework, not just static: this is what makes dev-server (Vite /
4373
- // fullstack) variants replayable from history instead of diff-only.
4374
3515
  const snapshotDir = record?.worktreePath;
4375
- // Lazy per-session base snapshot from the host repo root. Best-effort:
4376
- // resolution failure just means no base ref (variant commits still land).
4377
3516
  let baseDir;
4378
3517
  try {
4379
3518
  baseDir = await this.worktrees.getGitRoot?.();
@@ -4383,9 +3522,6 @@ class AgentVariantsOrchestrator {
4383
3522
  }
4384
3523
  let serve;
4385
3524
  if (sourceDir && record && staticEntryPath) {
4386
- // Static framework: entryPath is relative to the snapshot root (the
4387
- // worktree root), pointing into the nested project dir when the
4388
- // project lives below the git root.
4389
3525
  const entryPath = path_1.default.relative(record.worktreePath, path_1.default.join(sourceDir, staticEntryPath));
4390
3526
  serve = { kind: 'static', entryPath };
4391
3527
  }
@@ -4396,12 +3532,6 @@ class AgentVariantsOrchestrator {
4396
3532
  };
4397
3533
  }
4398
3534
  else if (record) {
4399
- // Persist fires at code-gen success, BEFORE the dev server boots (the
4400
- // boot is what stashes record.devCommand/port) — so for the common
4401
- // path the live invocation isn't available yet. Run the same
4402
- // deterministic resolution the boot will use, against a sentinel port
4403
- // that becomes the ${PORT} placeholder. No dependency install here —
4404
- // resolveDevServerCommand is the side-effect-free half of the split.
4405
3535
  try {
4406
3536
  const dev = await this.resolveDevServerCommand(args.sessionId, record.worktreePath, RECIPE_PORT_SENTINEL, false);
4407
3537
  serve = {
@@ -4441,22 +3571,6 @@ class AgentVariantsOrchestrator {
4441
3571
  : {}),
4442
3572
  });
4443
3573
  }
4444
- /**
4445
- * Existing-mode `framework: 'static'` projects have no dev command, so there
4446
- * is no per-variant dev server for the iframe to proxy. Register the variant's
4447
- * materialized static site as a {@link StaticPreviewRecord} so the existing
4448
- * static-file plumbing — the `/api/variants/:sid/static/:vid[/asset]` routes,
4449
- * {@link getStaticPreviewHtml}, and {@link getVariants} (preview.kind=
4450
- * 'static_artifact', view enabled) — all light up exactly as they do for
4451
- * fresh `static_preview` variants. The entry HTML and its sibling assets are
4452
- * served straight from the variant's worktree project root (session-scoped,
4453
- * pre-commit; the persisted `.rivet/variants/.../files/` copy backs the
4454
- * post-session history route separately).
4455
- *
4456
- * Returns `registered` when the static preview is ready, `unavailable` when
4457
- * the project is static but its selected entry cannot be served, and
4458
- * `not_static` when the caller should start a dev server instead.
4459
- */
4460
3574
  async registerExistingStaticPreview(sessionId, workItemId, record) {
4461
3575
  let env;
4462
3576
  try {
@@ -4489,9 +3603,6 @@ class AgentVariantsOrchestrator {
4489
3603
  log.warn(`registerExistingStaticPreview: project cwd failed for ${sessionId}/${workItemId}`, err);
4490
3604
  return 'unavailable';
4491
3605
  }
4492
- // The materialized worktree IS the per-variant directory. Require its
4493
- // selected entry to exist; the directory is then served directly (no
4494
- // in-memory HTML copy — getStaticPreviewHtml reads it from here).
4495
3606
  const entryFile = path_1.default.join(projectCwd, env.entryPath);
4496
3607
  try {
4497
3608
  await fs_1.default.promises.access(entryFile, fs_1.default.constants.R_OK);
@@ -4513,41 +3624,26 @@ class AgentVariantsOrchestrator {
4513
3624
  });
4514
3625
  return 'unavailable';
4515
3626
  }
4516
- // The dir must be realpath'd so resolveStaticPreviewAssetPath's sandbox
4517
- // comparison lines up; sibling assets (css/js/images) then resolve from the
4518
- // same static root the entry HTML lives in.
4519
3627
  let dir;
4520
3628
  try {
4521
3629
  dir = fs_1.default.realpathSync(projectCwd);
4522
3630
  }
4523
3631
  catch {
4524
- // Fall back to the un-realpath'd cwd; serving still works, only the
4525
- // sandbox symlink-normalization is skipped.
4526
3632
  dir = projectCwd;
4527
3633
  }
4528
- // Host project root, used as a read-only fallback for assets the worktree
4529
- // clone is missing (git-ignored or binary files that `git worktree add` +
4530
- // text-patch + untracked-copy can't carry). Without this the original
4531
- // preview shows such an asset while the variant 404s. Realpath'd so the
4532
- // sandbox containment check in `resolveAssetWithinBase` lines up.
4533
3634
  let assetFallbackBase;
4534
3635
  try {
4535
3636
  const hostRealRoot = fs_1.default.realpathSync(env.projectPath);
4536
- // Skip the fallback if it would collapse onto the worktree root (can't
4537
- // happen in practice, but keeps the two roots genuinely distinct).
4538
3637
  if (hostRealRoot !== dir)
4539
3638
  assetFallbackBase = hostRealRoot;
4540
3639
  }
4541
3640
  catch {
4542
- // No host root resolvable — serve worktree assets only.
4543
3641
  }
4544
3642
  resources.staticPreviews.set(workItemId, {
4545
3643
  workItemId,
4546
3644
  briefId: input.briefId,
4547
3645
  entryPath: env.entryPath,
4548
3646
  assetBase: dir,
4549
- // Existing-mode static projects are real multi-file sites — assume
4550
- // sibling assets so the preview route resolves relative URLs.
4551
3647
  hasAssets: true,
4552
3648
  ...(assetFallbackBase ? { assetFallbackBase } : {}),
4553
3649
  });
@@ -4563,25 +3659,10 @@ class AgentVariantsOrchestrator {
4563
3659
  });
4564
3660
  return 'registered';
4565
3661
  }
4566
- /**
4567
- * Persist a completed fresh-project variant into
4568
- * `<projectContext.workspacePath>/.rivet/variants/<sessionId>/<variantId>/`.
4569
- * Called from `handleSucceededReport` right after the per-variant snapshot
4570
- * lands on disk — for static_preview that's the `.rivet/<slug>/` snapshot
4571
- * dir; for vite_app fresh variants it's the worktree itself. Status starts
4572
- * as 'completed' and transitions to 'committed' / 'rejected' / 'cancelled'
4573
- * via `markStatus` at commit or teardown time.
4574
- *
4575
- * Running at success time (rather than commit time) means the history panel
4576
- * populates live as variants generate — including when the user never picks
4577
- * one. Mirrors the existing-project persistCompletedExistingVariant flow.
4578
- */
4579
3662
  async persistCompletedFreshVariant(args) {
4580
3663
  const projectContext = this.store.getProjectContext(args.sessionId);
4581
3664
  if (projectContext.kind !== 'fresh')
4582
3665
  return;
4583
- // Variant history lives in the generated project folder so reopening that
4584
- // folder gives the history panel the same owner path that wrote it.
4585
3666
  const historyProjectPath = projectContext.workspacePath;
4586
3667
  const input = this.store.getWorkItemInput(args.sessionId, args.workItemId);
4587
3668
  const sessionPrompt = this.store.getPrompt(args.sessionId);
@@ -4589,11 +3670,6 @@ class AgentVariantsOrchestrator {
4589
3670
  const resources = this.resources.get(args.sessionId);
4590
3671
  const worktreeRecord = resources?.worktrees.get(args.workItemId);
4591
3672
  const staticPreview = resources?.staticPreviews.get(args.workItemId);
4592
- // Vite_app deliverables are full scaffolded worktrees — pass the worktree
4593
- // directory as sourceDir and let copyDirFiltered handle it (excludes
4594
- // node_modules, .rivet, etc). Static_preview deliverables are inline HTML
4595
- // captured in `resources.staticPreviews`; stage them in a tmp dir so the
4596
- // existing copy path works without poking the user's workspace.
4597
3673
  let sourceDir = null;
4598
3674
  let tmpStagingDir = null;
4599
3675
  if (worktreeRecord && fs_1.default.existsSync(worktreeRecord.worktreePath)) {
@@ -4602,7 +3678,6 @@ class AgentVariantsOrchestrator {
4602
3678
  else if (staticPreview) {
4603
3679
  tmpStagingDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), `rivet-variant-${args.workItemId}-`));
4604
3680
  copyStaticPreviewDirectory(staticPreview.assetBase, tmpStagingDir);
4605
- // Read the materialized index.html from the variant's per-variant dir.
4606
3681
  let stagedHtml = '';
4607
3682
  try {
4608
3683
  stagedHtml = fs_1.default.readFileSync(path_1.default.join(staticPreview.assetBase, 'index.html'), 'utf8');
@@ -4618,10 +3693,6 @@ class AgentVariantsOrchestrator {
4618
3693
  log.warn(`persistCompletedFreshVariant: no source for ${args.workItemId} (session ${args.sessionId}); skipping`);
4619
3694
  return;
4620
3695
  }
4621
- // For static_preview, only `index.html` is a real deliverable — `brief.md`
4622
- // is implementation-detail staging that we copy alongside it. Hardcode
4623
- // the count so the history row matches what `commit_variant` reports
4624
- // (always 1) instead of double-counting the brief.
4625
3696
  const changedFilesCount = staticPreview ? 1 : countWorktreeFiles(sourceDir);
4626
3697
  const preview = staticPreview
4627
3698
  ? { kind: 'static', entryPath: 'index.html' }
@@ -4633,12 +3704,6 @@ class AgentVariantsOrchestrator {
4633
3704
  script: 'dev',
4634
3705
  },
4635
3706
  };
4636
- // Tree-store serve contract. Fresh worktrees prefer the live record's
4637
- // resolved invocation (stashed at dev-server start); the fallback recipe
4638
- // mirrors resolveDevServer's fresh branch, which is fully deterministic
4639
- // for the Vite template. Static previews serve their staged index.html.
4640
- // Fresh sessions have no host project, so there is no base snapshot —
4641
- // variant commits are root commits.
4642
3707
  const serve = staticPreview
4643
3708
  ? { kind: 'static', entryPath: 'index.html' }
4644
3709
  : worktreeRecord?.devCommand && worktreeRecord.port !== undefined
@@ -4701,10 +3766,6 @@ class AgentVariantsOrchestrator {
4701
3766
  }
4702
3767
  }
4703
3768
  }
4704
- /**
4705
- * @effect Drains fresh-project history writes before commit moves or rewrites
4706
- * the generated project folder.
4707
- */
4708
3769
  async persistCompletedFreshVariantsForCommit(sessionId) {
4709
3770
  const projectContext = this.store.getProjectContext(sessionId);
4710
3771
  if (projectContext.kind !== 'fresh')
@@ -4724,14 +3785,6 @@ class AgentVariantsOrchestrator {
4724
3785
  }
4725
3786
  }
4726
3787
  }
4727
- /**
4728
- * At commit time, flip persisted variant manifests to terminal statuses
4729
- * ('committed' for the chosen, 'rejected' for the rest). Both existing- and
4730
- * fresh-project variants have already been persisted at code_gen success
4731
- * time, so this is a pure status update — no source-dir copy required.
4732
- * Also records the destination path on the chosen variant so history can
4733
- * surface "this variant became <path>".
4734
- */
4735
3788
  async persistVariantHistoryAtCommit(args) {
4736
3789
  const variants = this.store.getVariants(args.sessionId);
4737
3790
  for (const variant of variants) {
@@ -4752,13 +3805,6 @@ class AgentVariantsOrchestrator {
4752
3805
  });
4753
3806
  }
4754
3807
  }
4755
- /**
4756
- * Flip persisted variant manifests to status='cancelled' for every variant
4757
- * in the session that was already snapshotted to disk. Fresh- and existing-
4758
- * project variants both persist at success time now, so both need their
4759
- * manifests flipped here. Best-effort — missing manifests are silently
4760
- * skipped by VariantHistoryService.markStatus.
4761
- */
4762
3808
  async markPersistedVariantsCancelled(sessionId) {
4763
3809
  if (!this.store.hasSession(sessionId))
4764
3810
  return;
@@ -4794,9 +3840,6 @@ class AgentVariantsOrchestrator {
4794
3840
  const projectPath = await this.resolveHistoryProjectPath(sessionId);
4795
3841
  if (!projectPath)
4796
3842
  return;
4797
- // No-ops (ENOENT) when the variant was never persisted — e.g. removing a
4798
- // still-pending variant that produced no artifact. Completed variants have
4799
- // a manifest, which flips to `removed` and is retained on disk.
4800
3843
  await this.variantHistory.markStatus({
4801
3844
  projectPath,
4802
3845
  sessionId,
@@ -4810,9 +3853,6 @@ class AgentVariantsOrchestrator {
4810
3853
  const projectPath = await this.resolveHistoryProjectPath(sessionId);
4811
3854
  if (!projectPath)
4812
3855
  return;
4813
- // No-ops (ENOENT) when the variant has no manifest yet — e.g. renaming a
4814
- // still-generating direction before its artifact lands. The in-memory
4815
- // label still updates; the disk copy catches up when the variant persists.
4816
3856
  await this.variantHistory.renameVariant({
4817
3857
  projectPath,
4818
3858
  sessionId,
@@ -4821,11 +3861,6 @@ class AgentVariantsOrchestrator {
4821
3861
  runLabel,
4822
3862
  });
4823
3863
  }
4824
- /**
4825
- * Resolve the project path that owns `.rivet/variants/` for a session.
4826
- * Existing sessions use the user's project. Fresh sessions use the generated
4827
- * project folder. Must match what `persistCompletedFreshVariant` writes to.
4828
- */
4829
3864
  async resolveHistoryProjectPath(sessionId) {
4830
3865
  const projectContext = this.store.getProjectContext(sessionId);
4831
3866
  if (projectContext.kind === 'fresh') {
@@ -4852,13 +3887,6 @@ class AgentVariantsOrchestrator {
4852
3887
  return undefined;
4853
3888
  }
4854
3889
  }
4855
- /**
4856
- * The port to start scanning from when allocating a variant's preview dev
4857
- * server. Fresh worktrees run the Vite template (5173); existing projects use
4858
- * their detected framework's default (3000 for Next, etc.), falling back to
4859
- * 3000 if detection fails. Scanning upward from here lands the preview on a
4860
- * sensible, predictable port near the project's normal one.
4861
- */
4862
3890
  async resolveDevServerStartPort(sessionId, isFresh) {
4863
3891
  if (isFresh)
4864
3892
  return FRESH_DEV_SERVER_DEFAULT_PORT;
@@ -4870,30 +3898,15 @@ class AgentVariantsOrchestrator {
4870
3898
  return 3000;
4871
3899
  }
4872
3900
  }
4873
- /**
4874
- * Resolve dev server invocation for a worktree. Fresh-project worktrees
4875
- * always use the Vite template's npm command at the worktree root; existing
4876
- * projects defer to the user's framework/packageManager config.
4877
- */
4878
- async resolveDevServer(sessionId, workItemId, worktreePath, port, isFresh) {
3901
+ async resolveDevServer(sessionId, worktreePath, port, isFresh) {
4879
3902
  const resolved = await this.resolveDevServerCommand(sessionId, worktreePath, port, isFresh);
4880
- if (!isFresh) {
4881
- await this.ensureExistingProjectDependencies({
4882
- sessionId,
4883
- workItemId,
4884
- cwd: resolved.cwd,
4885
- packageManager: resolved.packageManager,
4886
- });
4887
- }
3903
+ await this.ensureProjectDependencies({
3904
+ signal: this.resources.get(sessionId)?.dependencyAbortController.signal,
3905
+ cwd: resolved.cwd,
3906
+ workspaceRoot: worktreePath,
3907
+ });
4888
3908
  return resolved;
4889
3909
  }
4890
- /**
4891
- * Pure invocation resolution — no dependency installs, no side effects.
4892
- * Split from {@link resolveDevServer} so history persistence can capture
4893
- * the SAME deterministic resolution the boot path uses without booting:
4894
- * persist fires at code-gen success, before the dev server starts, so the
4895
- * live record's stashed command usually isn't available yet.
4896
- */
4897
3910
  async resolveDevServerCommand(sessionId, worktreePath, port, isFresh) {
4898
3911
  if (isFresh) {
4899
3912
  return {
@@ -4917,18 +3930,28 @@ class AgentVariantsOrchestrator {
4917
3930
  throw new Error('Static projects do not have a dev server command');
4918
3931
  }
4919
3932
  const cwd = await this.resolveExistingProjectCwd(sessionId, worktreePath, env);
4920
- const packageManager = detectPackageManagerForCwd(cwd, env.packageManager);
4921
- // Every branch below — including the production buildDevCommand recipes
4922
- // from utils/devServerCommand.ts — invokes the package.json script named
4923
- // by env.devCommand, so the preflight applies to all of them.
4924
3933
  if (env.devCommand) {
4925
3934
  assertDevScriptExists(cwd, env.devCommand);
4926
3935
  }
3936
+ const detected = fs_1.default.existsSync(path_1.default.join(cwd, 'package.json'))
3937
+ ? await (0, ProjectPackageManager_1.resolveProjectPackageManager)({
3938
+ cwd,
3939
+ workspaceRoot: worktreePath,
3940
+ }).catch((error) => {
3941
+ throw new devServerError_1.DevServerStartError({
3942
+ message: error instanceof Error ? error.message : String(error),
3943
+ detail: '',
3944
+ reason: 'dependency_install_failed',
3945
+ portInUse: false,
3946
+ });
3947
+ })
3948
+ : null;
3949
+ const packageManager = detected?.name ?? env.packageManager;
4927
3950
  if (packageManager !== env.packageManager) {
4928
3951
  return {
4929
3952
  cwd,
4930
3953
  packageManager,
4931
- ...buildExistingProjectDevCommand(packageManager, env.devCommand, port),
3954
+ ...(0, devServerCommand_1.buildDevServerCommand)(env.framework, packageManager, env.devCommand, port),
4932
3955
  };
4933
3956
  }
4934
3957
  if (env.buildDevCommand) {
@@ -4942,27 +3965,14 @@ class AgentVariantsOrchestrator {
4942
3965
  packageManager,
4943
3966
  };
4944
3967
  }
4945
- /**
4946
- * Existing-project variants usually reuse the user's installed
4947
- * node_modules. Nested example projects may be standalone packages with no
4948
- * install in the cloned worktree; install only when the package has no
4949
- * node_modules so normal user projects are not penalized.
4950
- */
4951
- async ensureExistingProjectDependencies(args) {
4952
- const packageJsonPath = path_1.default.join(args.cwd, 'package.json');
4953
- const nodeModulesPath = path_1.default.join(args.cwd, 'node_modules');
4954
- if (!fs_1.default.existsSync(packageJsonPath) || fs_1.default.existsSync(nodeModulesPath)) {
3968
+ async ensureProjectDependencies(args) {
3969
+ if (!fs_1.default.existsSync(path_1.default.join(args.cwd, 'package.json')))
4955
3970
  return;
4956
- }
4957
- await this.installDependencies(args.cwd, args.packageManager);
3971
+ await this.installDependencies(args.cwd, {
3972
+ workspaceRoot: args.workspaceRoot,
3973
+ signal: args.signal,
3974
+ });
4958
3975
  }
4959
- /**
4960
- * Resolve the dev-server working directory for an existing-project variant.
4961
- * WorktreeManager is constructed around the editor/root project, but
4962
- * `start_variants` can target a nested app (for example `examples/blog`).
4963
- * In that case the worktree root is still the git root clone, while the dev
4964
- * server must run from the nested project path.
4965
- */
4966
3976
  async resolveExistingProjectCwd(sessionId, worktreePath, env) {
4967
3977
  const fallback = await this.worktrees.getProjectCwdInWorktree(worktreePath);
4968
3978
  try {
@@ -4980,17 +3990,6 @@ class AgentVariantsOrchestrator {
4980
3990
  return fallback;
4981
3991
  }
4982
3992
  }
4983
- /**
4984
- * After the chosen variant has been renamed into `destinationPath`, move
4985
- * each unchosen sibling worktree into a `<destinationParent>/<slug>-variants/NN-<label>/`
4986
- * folder and repoint its `node_modules` symlink at the chosen project's
4987
- * real install. Writes a manifest documenting all 5 variants (the chosen
4988
- * one with `folder: null` and `chosenPath: '../<slug>'`).
4989
- *
4990
- * Sets `vitePreservedSiblings = true` on session resources so the
4991
- * subsequent teardown skips `cleanupFreshWorktrees` (which would otherwise
4992
- * delete the just-preserved history). Dev servers still get stopped.
4993
- */
4994
3993
  preserveUnchosenVariants(args) {
4995
3994
  const resources = this.resources.get(args.sessionId);
4996
3995
  const runnablePaths = new Map();
@@ -4999,7 +3998,6 @@ class AgentVariantsOrchestrator {
4999
3998
  const destinationParent = path_1.default.dirname(args.destinationPath);
5000
3999
  const projectSlug = path_1.default.basename(args.destinationPath);
5001
4000
  const historyDir = (0, createProjectArtifacts_1.createVariantsHistoryPath)(destinationParent, projectSlug);
5002
- const sharedNodeModulesAbs = path_1.default.join(args.destinationPath, 'node_modules');
5003
4001
  const briefs = this.store.getBriefs(args.sessionId);
5004
4002
  const variants = this.store.getVariants(args.sessionId);
5005
4003
  const briefIndexById = new Map(briefs.map((b, i) => [b.briefId, i]));
@@ -5027,8 +4025,6 @@ class AgentVariantsOrchestrator {
5027
4025
  }
5028
4026
  const record = resources.worktrees.get(variant.workItemId);
5029
4027
  if (!record) {
5030
- // No worktree (e.g. failed variant) — still list it in manifest with
5031
- // null folder so the user can see why it's absent.
5032
4028
  manifestEntries.push({
5033
4029
  variantId: variant.workItemId,
5034
4030
  label,
@@ -5057,28 +4053,6 @@ class AgentVariantsOrchestrator {
5057
4053
  fs_1.default.renameSync(record.worktreePath, dest);
5058
4054
  return dest;
5059
4055
  })();
5060
- // Repoint node_modules at the chosen project's real install.
5061
- try {
5062
- if (this.worktrees.repointNodeModulesSymlink) {
5063
- this.worktrees.repointNodeModulesSymlink(newPath, sharedNodeModulesAbs);
5064
- }
5065
- else {
5066
- const link = path_1.default.join(newPath, 'node_modules');
5067
- try {
5068
- fs_1.default.rmSync(link, { force: true, recursive: true });
5069
- }
5070
- catch {
5071
- /* may not exist */
5072
- }
5073
- const target = path_1.default.relative(path_1.default.dirname(link), sharedNodeModulesAbs);
5074
- fs_1.default.symlinkSync(target, link, 'dir');
5075
- }
5076
- }
5077
- catch (err) {
5078
- log.warn(`repointNodeModulesSymlink failed for ${newPath}; variant still runnable after npm install`, err);
5079
- }
5080
- // Update the in-memory record so teardown doesn't try to operate on
5081
- // the stale path.
5082
4056
  record.worktreePath = newPath;
5083
4057
  runnablePaths.set(variant.workItemId, `${path_1.default.basename(historyDir)}/${folderName}`);
5084
4058
  manifestEntries.push({
@@ -5117,22 +4091,7 @@ class AgentVariantsOrchestrator {
5117
4091
  resources.vitePreservedSiblings = true;
5118
4092
  return runnablePaths;
5119
4093
  }
5120
- /**
5121
- * Rename `sourceWorktreePath` into `destinationPath`, then replace the
5122
- * worktree's `.git` with a fresh-history init + initial commit. Matches
5123
- * the historical materializeProject behavior of "user sees a fresh repo
5124
- * at destinationPath" so we don't surprise them with a `rivet: variant
5125
- * baseline` commit they didn't intend.
5126
- *
5127
- * The rename is the load-bearing operation: it moves the entire variant
5128
- * tree (template files + materialized node_modules) in O(1) when the
5129
- * source and destination share a volume. The git reset that follows is
5130
- * best-effort housekeeping; a failure there leaves files in place.
5131
- */
5132
4094
  async renameWorktreeIntoDestination(sourceWorktreePath, destinationPath) {
5133
- // destinationPath must not exist for fs.rename to behave as a move.
5134
- // assertDestinationAvailable already enforced that any existing directory
5135
- // contains only `.rivet`, which we preserve across the move.
5136
4095
  const destinationParent = path_1.default.dirname(destinationPath);
5137
4096
  await fs_1.default.promises.mkdir(destinationParent, { recursive: true });
5138
4097
  let preservedRivetParent;
@@ -5179,15 +4138,11 @@ class AgentVariantsOrchestrator {
5179
4138
  force: true,
5180
4139
  });
5181
4140
  }
5182
- // Replace the variant-baseline git history with a fresh init so the
5183
- // user doesn't inherit a `rivet: variant baseline` commit.
5184
4141
  const dotGit = path_1.default.join(destinationPath, '.git');
5185
4142
  try {
5186
4143
  fs_1.default.rmSync(dotGit, { recursive: true, force: true });
5187
4144
  }
5188
4145
  catch {
5189
- // Best-effort — leaving the baseline .git is preferable to failing
5190
- // the commit after we've moved the tree.
5191
4146
  }
5192
4147
  try {
5193
4148
  const git = (0, simple_git_1.simpleGit)(destinationPath);
@@ -5205,22 +4160,12 @@ class AgentVariantsOrchestrator {
5205
4160
  'Initial commit (created with Rivet)',
5206
4161
  ])
5207
4162
  .catch(() => {
5208
- // Best-effort: missing user identity or empty tree leaves the
5209
- // commit step un-applied. Files are still in place.
5210
4163
  });
5211
4164
  }
5212
4165
  catch (err) {
5213
4166
  log.warn(`Initial git history setup at ${destinationPath} failed`, err);
5214
4167
  }
5215
4168
  }
5216
- /**
5217
- * Stop every variant dev server currently running for `resources`. Clears
5218
- * the `devServerProcess` / `port` fields on each record so subsequent
5219
- * teardown logic doesn't try to stop them a second time. Called from
5220
- * `commitVariant` (vite_app rename path) BEFORE the worktree directory is
5221
- * renamed — leaving Vite alive across the rename leaks empty `.vite/`
5222
- * stub dirs at the old paths.
5223
- */
5224
4169
  async stopAllDevServersForSession(resources) {
5225
4170
  this.clearActivePreviewIdleTimer(resources);
5226
4171
  const stops = [];
@@ -5259,7 +4204,6 @@ class AgentVariantsOrchestrator {
5259
4204
  clearTimeout(resources.activePreviewIdleTimer);
5260
4205
  resources.activePreviewIdleTimer = undefined;
5261
4206
  }
5262
- /** @effect Stops idle variant child processes and clears dead preview ports. */
5263
4207
  async stopIdleVariantDevServers(sessionId) {
5264
4208
  const resources = this.resources.get(sessionId);
5265
4209
  if (!resources || resources.cleanupStarted)
@@ -5288,8 +4232,6 @@ class AgentVariantsOrchestrator {
5288
4232
  }
5289
4233
  log.info(`Reclaimed ${expiringWorkItemIds.length} idle variant preview(s) for session ${sessionId} ` +
5290
4234
  `(${stoppedCount} running, ${pendingRestartCount} restarting)`);
5291
- // The next selection restores only the requested Rivet-owned preview;
5292
- // siblings remain stopped until the user asks to view them.
5293
4235
  try {
5294
4236
  this.switchPreviewPort?.(null);
5295
4237
  }
@@ -5305,18 +4247,6 @@ class AgentVariantsOrchestrator {
5305
4247
  resources.activePreviewIdleStop = stop;
5306
4248
  await stop;
5307
4249
  }
5308
- /**
5309
- * Spawn a fresh dev server at the user-facing destinationPath and retarget
5310
- * the proxy so the iframe stays live across commit. Stores the process on
5311
- * `resources.committedDevServer` so teardown can hand it off to the
5312
- * lingering registry instead of killing it. Best-effort: a failure here is
5313
- * non-fatal — the commit still succeeds; the user just has to run
5314
- * `npm run dev` themselves to bring the preview back.
5315
- *
5316
- * `mode` selects the server flavor: `vite_app` spawns Vite via `npm run
5317
- * dev`; `static_preview` spawns the in-tree static file server bound to
5318
- * the destination directory (no install, no build, ~ms startup).
5319
- */
5320
4250
  async startCommittedDevServer(args) {
5321
4251
  const mode = args.mode ?? 'vite_app';
5322
4252
  try {
@@ -5341,14 +4271,6 @@ class AgentVariantsOrchestrator {
5341
4271
  port,
5342
4272
  path: args.destinationPath,
5343
4273
  };
5344
- // Advertise the Rivet-owned server on devServerHealth so the iframe's
5345
- // upstream probe stops reporting `ownership: 'none'`. Distinct from
5346
- // switchPreviewPort: this updates health metadata, switchPreviewPort
5347
- // retargets the proxy. Both fire here because the commit-handoff is
5348
- // the only place Rivet legitimately takes ownership of the dev
5349
- // server — variant cycling and cancel only retarget, they don't
5350
- // touch health (which the user's external dev server owns during a
5351
- // regular session).
5352
4274
  try {
5353
4275
  this.setCommittedDevServerHealth?.(port);
5354
4276
  }
@@ -5369,19 +4291,9 @@ class AgentVariantsOrchestrator {
5369
4291
  return undefined;
5370
4292
  }
5371
4293
  }
5372
- /**
5373
- * Stop any committed dev servers that were left alive past their session's
5374
- * teardown. Called at the start of a new `propose` (so the next session's
5375
- * preview doesn't compete with stale processes) and exposed for explicit
5376
- * shutdown hooks. Best-effort: per-server failures are logged but don't
5377
- * propagate.
5378
- */
5379
4294
  async stopLingeringCommittedDevServers() {
5380
4295
  const entries = [...this.lingeringCommittedDevServers.entries()];
5381
4296
  this.lingeringCommittedDevServers.clear();
5382
- // Clear the Rivet-owned-server advertisement on `devServerHealth` so the
5383
- // probe doesn't keep reporting a port that's about to stop listening.
5384
- // Safe to call even when no committed server existed for this process.
5385
4297
  if (entries.length > 0) {
5386
4298
  try {
5387
4299
  this.setCommittedDevServerHealth?.(null);
@@ -5408,40 +4320,22 @@ class AgentVariantsOrchestrator {
5408
4320
  resources.cleanupStarted = true;
5409
4321
  this.clearActivePreviewIdleTimer(resources);
5410
4322
  log.info(`Tearing down session ${sessionId} (reason: ${reason})`);
5411
- // Drain in-flight history persists BEFORE deleting worktrees: a tree
5412
- // snapshot's `git add -A --work-tree <worktree>` racing the deletion
5413
- // below doesn't fail — it stages whatever files remain, publishing a
5414
- // silently truncated snapshot. The design doc's ordering constraint
5415
- // ("tree committed before worktree teardown") is enforced here, not
5416
- // just in tests. Best-effort with a bound: never let a wedged git call
5417
- // stall teardown forever.
4323
+ resources.dependencyAbortController.abort();
4324
+ await Promise.all([...resources.worktrees.values()].map((record) => DependencyPreparationService_1.previewDependencies.settle(record.worktreePath)));
5418
4325
  await Promise.race([
5419
4326
  this.settleHistoryPersists(),
5420
4327
  new Promise((resolve) => setTimeout(resolve, SETTLE_PERSISTS_TEARDOWN_TIMEOUT_MS)),
5421
4328
  ]);
5422
- // Stop dev servers in parallel; ignore individual failures.
5423
- // NOTE: For the fresh vite_app rename path, commitVariant already stopped
5424
- // these dev servers BEFORE the rename (so Vite couldn't recreate `.vite/`
5425
- // stub dirs at the old worktree paths). In that case devServerProcess is
5426
- // already cleared and this loop is a no-op — exactly the desired behavior.
5427
4329
  const stops = [...resources.worktrees.values()]
5428
4330
  .filter((r) => r.devServerProcess)
5429
4331
  .map((r) => this.worktrees
5430
4332
  .stopDevServer(r.devServerProcess)
5431
4333
  .catch((err) => log.warn(`stopDevServer failed for ${sessionId}/${r.workItemId}`, err)));
5432
4334
  await Promise.all(stops);
5433
- // The committed dev server (spawned at destinationPath after a fresh
5434
- // vite_app commit) intentionally outlives this teardown — the iframe is
5435
- // pointed at it via the proxy and the user just chose this project. Hand
5436
- // it off to the lingering registry; it'll be stopped on the next
5437
- // `propose` or explicit shutdown.
5438
4335
  if (resources.committedDevServer && reason === 'committed') {
5439
4336
  this.lingeringCommittedDevServers.set(sessionId, resources.committedDevServer);
5440
4337
  }
5441
4338
  else if (resources.committedDevServer) {
5442
- // Non-committed teardown (cancel/shutdown) with a committed dev server
5443
- // shouldn't happen in current code paths, but stop it defensively so we
5444
- // don't leak procs.
5445
4339
  try {
5446
4340
  await this.worktrees.stopDevServer(resources.committedDevServer.proc);
5447
4341
  }
@@ -5449,10 +4343,6 @@ class AgentVariantsOrchestrator {
5449
4343
  log.warn(`stopDevServer for committed dev server (session ${sessionId}) failed`, err);
5450
4344
  }
5451
4345
  }
5452
- // When a vite_app commit already moved the unchosen worktrees into a
5453
- // sibling history dir, calling cleanupSession would re-delete them. Skip
5454
- // the worktree removal step in that case — the move already cleared the
5455
- // .rivet-variants/ entries for the chosen + preserved variants.
5456
4346
  if (!resources.vitePreservedSiblings) {
5457
4347
  try {
5458
4348
  await this.worktrees.cleanupSession(sessionId, resources.freshDestinationParent);
@@ -5461,10 +4351,6 @@ class AgentVariantsOrchestrator {
5461
4351
  log.warn(`cleanupSession (worktree removal) failed for ${sessionId}`, err);
5462
4352
  }
5463
4353
  }
5464
- // Assigned static workspaces are always Rivet-owned, including existing-
5465
- // project refinement work staged under `.rivet/variant-work` or /tmp.
5466
- // Fresh staticPreviews are also Rivet staging unless commit already moved
5467
- // the selected directory into the destination.
5468
4354
  const ownedArtifactRoots = new Set(resources.workspaceManager.getOwnedArtifactRoots());
5469
4355
  resources.workspaceManager.removeOwnedArtifactWorkspaces();
5470
4356
  const staticDirectories = new Set();
@@ -5504,6 +4390,7 @@ class AgentVariantsOrchestrator {
5504
4390
  workspaceManager: new VariantWorkspaceManager_1.VariantWorkspaceManager(),
5505
4391
  contextAssets: new Map(),
5506
4392
  cleanupStarted: false,
4393
+ dependencyAbortController: new AbortController(),
5507
4394
  committedVariantIds: new Set(),
5508
4395
  startedAt: Date.now(),
5509
4396
  nextRoundIndex: 0,
@@ -5521,10 +4408,6 @@ class AgentVariantsOrchestrator {
5521
4408
  }
5522
4409
  }
5523
4410
  exports.AgentVariantsOrchestrator = AgentVariantsOrchestrator;
5524
- /** MCP bridges sometimes serialize the output field as a JSON string instead
5525
- * of a parsed object. Normalize it before field-level inspection. */
5526
- /** Rough byte-size of the agent's output. Used as a proxy for "how much
5527
- * code did the agent write" when comparing HTML vs full-stack flows. */
5528
4411
  function estimateOutputBytes(output) {
5529
4412
  if (output === undefined || output === null)
5530
4413
  return 0;
@@ -5548,28 +4431,11 @@ function normalizeOutput(output) {
5548
4431
  }
5549
4432
  return output;
5550
4433
  }
5551
- /**
5552
- * `true` when `child` is a strict descendant of `parent` (resolved). Used by
5553
- * the static_preview commit branch before rm/rename to make sure we never
5554
- * touch anything outside the variant's workspace.
5555
- */
5556
4434
  function isStrictlyInside(child, parent) {
5557
4435
  const c = path_1.default.resolve(child);
5558
4436
  const p = path_1.default.resolve(parent);
5559
4437
  return c !== p && c.startsWith(p + path_1.default.sep);
5560
4438
  }
5561
- /**
5562
- * Resolve `requestedPath` to an absolute, sandboxed file under `base`, or
5563
- * `undefined` if it escapes the base, doesn't exist, isn't a regular file, or
5564
- * is a hardlink. Shared by `resolveStaticPreviewAssetPath` across the variant's
5565
- * own directory and the host-root fallback so both get identical containment.
5566
- *
5567
- * `base` is expected to be realpath'd by the caller. The requested target is
5568
- * realpath'd here too — without it, a symlink planted under the base (e.g. via
5569
- * a follow-up Write tool call) could pivot outside the sandbox. `requestedPath`
5570
- * is used as Express delivered it (already URL-decoded); decoding again would
5571
- * mangle filenames that legitimately contain `%` (e.g. `100%25.png` on disk).
5572
- */
5573
4439
  function resolveAssetWithinBase(base, requestedPath) {
5574
4440
  const target = path_1.default.resolve(base, '.' + path_1.default.sep + requestedPath);
5575
4441
  if (target !== base && !target.startsWith(base + path_1.default.sep)) {
@@ -5589,11 +4455,6 @@ function resolveAssetWithinBase(base, requestedPath) {
5589
4455
  const stat = fs_1.default.statSync(realTarget);
5590
4456
  if (!stat.isFile())
5591
4457
  return undefined;
5592
- // Hardlinks aren't dereferenced by `realpath` — they ARE the file from a
5593
- // path-resolution perspective, so the realpath check above can't detect
5594
- // them. An agent could `ln /path/to/.env base/x.glb` (hardlink, not
5595
- // symlink) and the resolver would happily serve the secret. Reject
5596
- // anything with multiple links — legitimate assets always have nlink === 1.
5597
4458
  if (stat.nlink !== 1)
5598
4459
  return undefined;
5599
4460
  return realTarget;
@@ -5602,19 +4463,6 @@ function resolveAssetWithinBase(base, requestedPath) {
5602
4463
  return undefined;
5603
4464
  }
5604
4465
  }
5605
- /**
5606
- * Sandbox the agent's `output.assetBase` to a path inside the session
5607
- * workspace. Accepts either an absolute path (used as-is after the
5608
- * within-workspace check) or a path relative to the fresh-session
5609
- * `workspacePath`. Returns `undefined` for non-fresh sessions, missing
5610
- * directories, or paths that escape the workspace via `..` or symlinks.
5611
- *
5612
- * `assetBase` is agent-controlled, so a prompt-injected variant could point
5613
- * it at a symlink whose real target is outside `workspacePath` — and the
5614
- * asset endpoint would then happily serve files like `~/.ssh/id_rsa` or
5615
- * `.env`. Resolving with `fs.realpathSync` collapses symlinks before the
5616
- * containment check, so the check applies to the real target, not the link.
5617
- */
5618
4466
  function resolveStaticPreviewAssetBase(args) {
5619
4467
  if (!args.assetBase)
5620
4468
  return undefined;
@@ -5623,23 +4471,6 @@ function resolveStaticPreviewAssetBase(args) {
5623
4471
  let workspace;
5624
4472
  let resolved;
5625
4473
  try {
5626
- // Defense against an agent that subverts the sandbox by replacing the
5627
- // Rivet-created `workspacePath` directory with a symlink. Plain
5628
- // `realpath` would silently follow that symlink and the containment
5629
- // check below would then trust the wrong root.
5630
- //
5631
- // Two layers:
5632
- // 1. `lstat` the workspacePath itself — if it's a symlink (rather
5633
- // than a real directory Rivet created), reject outright. This
5634
- // catches the obvious swap `ln -sf $HOME <workspacePath>` and the
5635
- // subtler swap to a sibling like `<workspaceRoot>` itself
5636
- // (which would pass any subsequent containment check based on
5637
- // ancestor matching).
5638
- // 2. realpath both and require the resolved workspacePath to be a
5639
- // STRICT descendant of the resolved workspaceRoot — equal is not
5640
- // enough (a symlink to workspaceRoot would otherwise be accepted
5641
- // and the asset route would then serve files at the project
5642
- // root, e.g. `.env`).
5643
4474
  const wsStat = fs_1.default.lstatSync(args.projectContext.workspacePath);
5644
4475
  if (wsStat.isSymbolicLink()) {
5645
4476
  log.warn(`Static preview workspacePath ${args.projectContext.workspacePath} is a symlink — refusing assetBase`);
@@ -5701,11 +4532,6 @@ function buildStaticPreviewDocument(input) {
5701
4532
  const style = input.css ? `<style>\n${input.css}\n</style>` : '';
5702
4533
  const script = input.js ? `<script>\n${input.js}\n</script>` : '';
5703
4534
  if (/<!doctype html>|<html[\s>]/i.test(input.html)) {
5704
- // Full document: inject css before </head> and js before </body>. The
5705
- // agent often passes a complete `<!doctype html>...` blob with css/js
5706
- // alongside; without this they're silently dropped and the variant ships
5707
- // unstyled and non-interactive. Falls back to appending to the end if
5708
- // the closing tag isn't found.
5709
4535
  let doc = input.html;
5710
4536
  if (style) {
5711
4537
  if (/<\/head>/i.test(doc)) {
@@ -5739,15 +4565,8 @@ ${input.html}
5739
4565
  </html>`;
5740
4566
  }
5741
4567
  function countDiffFiles(diff) {
5742
- // Each file in a unified diff starts with "diff --git ".
5743
4568
  return (diff.match(/^diff --git /gm) ?? []).length;
5744
4569
  }
5745
- /**
5746
- * Count source files under a materialized fresh-project destination,
5747
- * skipping install artifacts (`node_modules`, `.git`, build outputs) so the
5748
- * commit telemetry / chip count reflects the agent-visible surface area
5749
- * rather than the install footprint.
5750
- */
5751
4570
  function countWorktreeFiles(destinationPath) {
5752
4571
  let count = 0;
5753
4572
  const walk = (dir) => {
@@ -5780,8 +4599,6 @@ const enrichDesignSource = (design) => {
5780
4599
  };
5781
4600
  };
5782
4601
  const toActiveProjectContext = (projectContext) => {
5783
- // Source count + grounding are derived purely from the read-only context
5784
- // bundle now that `sourceContext` intake is gone.
5785
4602
  const sourceCount = projectContext.variantContext?.userContext.filter((source) => source.transport !== 'prompt').length ?? 0;
5786
4603
  const hasSourceContextSummary = Boolean(sourceCount > 0 ||
5787
4604
  (projectContext.variantContext?.rivetMetadata.length ?? 0) > 0);
@@ -5824,18 +4641,6 @@ const toActiveProjectContext = (projectContext) => {
5824
4641
  : {}),
5825
4642
  };
5826
4643
  };
5827
- /**
5828
- * Resolve the user-facing supporting artifacts for a session.
5829
- *
5830
- * For 0→1 (`fresh`) sessions with a populated `designContext`, each slot is
5831
- * turned into a `design_context` artifact that links to Rivet-hosted
5832
- * DESIGN.md views:
5833
- * - `slug` entries resolve bundled catalog markdown via the design catalog.
5834
- * - `markdown` entries (Agent Browser / inspiration extractor output) carry
5835
- * their stored markdown when the linked route is requested.
5836
- * Slots whose markdown can't be resolved are skipped so the UI never renders
5837
- * a metadata-only DESIGN.md row.
5838
- */
5839
4644
  const buildSessionArtifacts = (sessionId, projectContext) => {
5840
4645
  const artifacts = buildResolvedDesignContextArtifacts(projectContext);
5841
4646
  return artifacts.map(({ content: _content, ...artifact }) => ({
@@ -6173,7 +4978,6 @@ const renderDesignMarkdown = (markdown, artifactLabel) => {
6173
4978
  const headingText = headingMatch[2].trim();
6174
4979
  const isDuplicateTitle = headingText === artifactLabel ||
6175
4980
  headingText === `${artifactLabel} DESIGN.md`;
6176
- // The viewer header already renders the artifact title.
6177
4981
  if (level === 1 && !skippedFirstH1 && isDuplicateTitle) {
6178
4982
  skippedFirstH1 = true;
6179
4983
  return;
@@ -6215,14 +5019,6 @@ const escapeHtml = (value) => {
6215
5019
  .replace(/"/g, '&quot;')
6216
5020
  .replace(/'/g, '&#39;');
6217
5021
  };
6218
- /**
6219
- * Resolve a per-variant design context entry into the raw DESIGN.md markdown
6220
- * the worktree scaffolder writes, plus a small `designSource` descriptor for
6221
- * the variant manifest. Slug entries resolve through the bundled catalog;
6222
- * markdown entries (Agent Browser / inspiration extractor output) carry their
6223
- * stored markdown verbatim. Returns null when the entry is missing or the
6224
- * slug doesn't resolve to bundled markdown.
6225
- */
6226
5022
  const resolveDesignArtifact = (entry) => {
6227
5023
  if (!entry)
6228
5024
  return null;
@@ -6447,62 +5243,14 @@ function copyStaticPreviewDirectory(src, dest) {
6447
5243
  }
6448
5244
  }
6449
5245
  }
6450
- const buildInstallCommand = (packageManager) => {
6451
- if (packageManager === 'npm') {
6452
- return {
6453
- cmd: 'npm',
6454
- args: ['install', '--no-audit', '--no-fund', '--ignore-scripts'],
6455
- };
6456
- }
6457
- return {
6458
- cmd: packageManager,
6459
- args: ['install', '--ignore-scripts'],
6460
- };
6461
- };
6462
- const defaultInstallDependencies = (worktreePath, packageManager = 'npm') => {
6463
- const install = buildInstallCommand(packageManager);
6464
- return new Promise((resolve, reject) => {
6465
- const proc = (0, child_process_1.spawn)(install.cmd, install.args, {
6466
- cwd: worktreePath,
6467
- stdio: ['ignore', 'ignore', 'pipe'],
6468
- });
6469
- let stderr = '';
6470
- proc.stderr.on('data', (chunk) => {
6471
- stderr += chunk.toString();
6472
- });
6473
- proc.on('error', reject);
6474
- proc.on('exit', (code) => {
6475
- if (code === 0) {
6476
- resolve();
6477
- return;
6478
- }
6479
- reject(new Error(`${install.cmd} install in ${worktreePath} failed (code ${code}): ${stderr.slice(-512)}`));
6480
- });
5246
+ const defaultInstallDependencies = async (worktreePath, options) => {
5247
+ await DependencyPreparationService_1.previewDependencies.prepare({
5248
+ cwd: worktreePath,
5249
+ workspaceRoot: options?.workspaceRoot ?? worktreePath,
5250
+ force: options?.force,
5251
+ signal: options?.signal,
6481
5252
  });
6482
5253
  };
6483
- const detectPackageManagerForCwd = (cwd, fallback) => {
6484
- if (fs_1.default.existsSync(path_1.default.join(cwd, 'yarn.lock')))
6485
- return 'yarn';
6486
- if (fs_1.default.existsSync(path_1.default.join(cwd, 'pnpm-lock.yaml')))
6487
- return 'pnpm';
6488
- if (fs_1.default.existsSync(path_1.default.join(cwd, 'package-lock.json')))
6489
- return 'npm';
6490
- if (fallback === 'pnpm' || fallback === 'yarn' || fallback === 'npm') {
6491
- // A nested package without a lockfile should not inherit a root Yarn Berry
6492
- // workspace when it is not declared as a workspace member; npm treats it as
6493
- // an independent package and is the safest default for old examples.
6494
- return fs_1.default.existsSync(path_1.default.join(cwd, 'package.json')) ? 'npm' : fallback;
6495
- }
6496
- return 'npm';
6497
- };
6498
- /**
6499
- * Turn the invocation a live dev server actually booted with into a
6500
- * port-parameterized replay recipe. The resolution bakes the allocated port
6501
- * into args/env (`--port <n>`, `PORT=<n>`) — storing that verbatim would
6502
- * bake in a dead port, so occurrences of the port (digit-boundary-guarded so
6503
- * incidental digit runs survive) become `${PORT}` for replay to substitute.
6504
- * `cwd` is stored relative to the snapshot root per the tree-root contract.
6505
- */
6506
5254
  const buildServeRecipeFromResolved = (snapshotRoot, dev, port) => {
6507
5255
  const portPattern = new RegExp(`(?<![0-9])${port}(?![0-9])`, 'g');
6508
5256
  const sub = (value) => value.replace(portPattern, '${PORT}');
@@ -6514,15 +5262,6 @@ const buildServeRecipeFromResolved = (snapshotRoot, dev, port) => {
6514
5262
  envTemplate: Object.fromEntries(Object.entries(dev.env).map(([key, value]) => [key, sub(value)])),
6515
5263
  };
6516
5264
  };
6517
- /**
6518
- * Whether an install at `cwd` stays inside the Rivet-owned worktree.
6519
- * Existing-project worktrees SYMLINK `node_modules` to the user's real
6520
- * install (WorktreeManager.symlinkNodeModulesIfAvailable) — running a
6521
- * package manager through that link would mutate the user's project
6522
- * dependencies, which the variants flow must never do. Install is safe only
6523
- * when `node_modules` is absent (a nested standalone package — the install
6524
- * creates a worktree-local dir) or a real directory owned by the worktree.
6525
- */
6526
5265
  const canInstallWithoutTouchingHost = (cwd) => {
6527
5266
  try {
6528
5267
  return !fs_1.default.lstatSync(path_1.default.join(cwd, 'node_modules')).isSymbolicLink();
@@ -6531,15 +5270,6 @@ const canInstallWithoutTouchingHost = (cwd) => {
6531
5270
  return true;
6532
5271
  }
6533
5272
  };
6534
- /**
6535
- * Fail fast — before spawning anything — when the package.json at `cwd`
6536
- * positively lacks the dev script we're about to invoke. Without this the
6537
- * spawn exits with the package manager's "missing script" error after a full
6538
- * boot round-trip and surfaces as an opaque `exited` failure; with it the
6539
- * user gets an actionable message naming the scripts that DO exist. Missing
6540
- * or unparseable package.json is NOT treated as a failure here — other paths
6541
- * own those cases, and the spawn's own error is more specific.
6542
- */
6543
5273
  const assertDevScriptExists = (cwd, scriptName) => {
6544
5274
  let scripts;
6545
5275
  try {
@@ -6568,39 +5298,6 @@ const assertDevScriptExists = (cwd, scriptName) => {
6568
5298
  : '',
6569
5299
  });
6570
5300
  };
6571
- const buildExistingProjectDevCommand = (packageManager, devCommand, port) => {
6572
- const env = { PORT: String(port) };
6573
- if (packageManager === 'npm') {
6574
- return {
6575
- cmd: 'npm',
6576
- args: [
6577
- 'run',
6578
- devCommand,
6579
- '--',
6580
- '--port',
6581
- String(port),
6582
- '--host',
6583
- FRESH_DEV_SERVER_HOST,
6584
- ],
6585
- env,
6586
- };
6587
- }
6588
- return {
6589
- cmd: packageManager,
6590
- args: [devCommand, '--port', String(port), '--host', FRESH_DEV_SERVER_HOST],
6591
- env,
6592
- };
6593
- };
6594
- /**
6595
- * True when `a` and `b` (or the closest existing ancestor of each) live on
6596
- * the same filesystem volume. Used by commitVariant to decide between an
6597
- * O(1) rename and a recursive copy-with-reinstall fallback.
6598
- *
6599
- * `destinationPath` for fresh sessions does not exist yet (we'd fail the
6600
- * "destination must be empty" guard otherwise), so we compare against its
6601
- * parent directory which is guaranteed to exist (createZeroToOneTool either
6602
- * created it or validated it).
6603
- */
6604
5301
  function areSameVolume(a, b) {
6605
5302
  try {
6606
5303
  const statA = fs_1.default.statSync(a);
@@ -6612,65 +5309,19 @@ function areSameVolume(a, b) {
6612
5309
  return false;
6613
5310
  }
6614
5311
  }
6615
- /**
6616
- * Materialize node_modules from symlink → real directory at the given path,
6617
- * so a subsequent rename of that path leaves a self-contained tree.
6618
- *
6619
- * Fresh worktrees 1..N share variant 0's node_modules via a symlink (see
6620
- * runBackgroundInstall). When the user picks variant K and we rename it
6621
- * into the destination, the sibling worktrees get torn down — which would
6622
- * otherwise leave a dangling symlink in the destination if variant K was
6623
- * the one with the symlink (K > 0).
6624
- *
6625
- * Returns true when the destination must run a dependency install after the
6626
- * rename because node_modules is missing or points nowhere.
6627
- */
6628
5312
  async function resolveNodeModulesSymlink(worktreePath) {
6629
5313
  const nodeModulesPath = path_1.default.join(worktreePath, 'node_modules');
6630
- let lstat;
6631
- try {
6632
- lstat = await fs_1.default.promises.lstat(nodeModulesPath);
6633
- }
6634
- catch {
6635
- return true;
6636
- }
6637
- if (!lstat.isSymbolicLink())
6638
- return false;
6639
- let target;
6640
5314
  try {
6641
- target = await fs_1.default.promises.realpath(nodeModulesPath);
6642
- }
6643
- catch {
5315
+ if (!(await fs_1.default.promises.lstat(nodeModulesPath)).isSymbolicLink())
5316
+ return false;
6644
5317
  await fs_1.default.promises.rm(nodeModulesPath, { force: true });
6645
5318
  return true;
6646
5319
  }
6647
- // Stage the copy under a sibling temp name so a mid-copy failure (disk
6648
- // full, EACCES, etc.) leaves the original symlink intact rather than
6649
- // wiping it and leaving the worktree with no node_modules at all.
6650
- // The final swap is then atomic on the same filesystem (rename + unlink).
6651
- const stagingPath = `${nodeModulesPath}.materializing-${(0, crypto_1.randomBytes)(4).toString('hex')}`;
6652
- try {
6653
- await fs_1.default.promises.cp(target, stagingPath, {
6654
- recursive: true,
6655
- dereference: false,
6656
- });
5320
+ catch (error) {
5321
+ if (error.code === 'ENOENT')
5322
+ return true;
5323
+ throw error;
6657
5324
  }
6658
- catch (err) {
6659
- // Best-effort cleanup of the partial staging dir; leave the symlink
6660
- // intact so the caller can re-run materialization.
6661
- await fs_1.default.promises
6662
- .rm(stagingPath, { recursive: true, force: true })
6663
- .catch(() => { });
6664
- throw err;
6665
- }
6666
- // Swap: remove the symlink, then rename the staging dir into place.
6667
- // Both operations are local fs ops on the same parent — fast and
6668
- // unlikely to fail independently, but if the rename fails the symlink
6669
- // is already gone, so prefer to leave the staging dir on disk over a
6670
- // half-broken worktree (the caller will surface the throw).
6671
- await fs_1.default.promises.rm(nodeModulesPath, { force: true });
6672
- await fs_1.default.promises.rename(stagingPath, nodeModulesPath);
6673
- return false;
6674
5325
  }
6675
5326
  const defaultMaterializeProject = async (sourceWorktreePath, destinationPath) => {
6676
5327
  await fs_1.default.promises.mkdir(destinationPath, { recursive: true });
@@ -6699,16 +5350,8 @@ const defaultMaterializeProject = async (sourceWorktreePath, destinationPath) =>
6699
5350
  'Initial commit (created with Rivet)',
6700
5351
  ])
6701
5352
  .catch(() => {
6702
- // Best-effort: a missing user identity or empty tree leaves the
6703
- // commit step un-applied. The destination still has the files.
6704
5353
  });
6705
5354
  };
6706
- /**
6707
- * Synthesize a fallback brief body for a human-direct unified start slot.
6708
- * Agent callers provide structured briefs before starting. Human-direct
6709
- * batches get stable, slot-specific focus statements without another model
6710
- * call so workers do not independently invent overlapping directions.
6711
- */
6712
5355
  function synthesizeUnifiedBriefBody(prompt, index, total) {
6713
5356
  const trimmed = prompt.trim().replace(/\s+/g, ' ').slice(0, 200);
6714
5357
  const intent = trimmed.length > 0 ? trimmed : 'Create this variant';