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.
- package/bin/rivet.js +0 -16
- package/dist/agent-variants/SessionStore.d.ts +0 -239
- package/dist/agent-variants/SessionStore.d.ts.map +1 -1
- package/dist/agent-variants/SessionStore.js +10 -368
- package/dist/agent-variants/SessionStore.js.map +1 -1
- package/dist/agent-variants/VariantWorkspaceManager.d.ts +0 -10
- package/dist/agent-variants/VariantWorkspaceManager.d.ts.map +1 -1
- package/dist/agent-variants/VariantWorkspaceManager.js +0 -17
- package/dist/agent-variants/VariantWorkspaceManager.js.map +1 -1
- package/dist/agent-variants/WorkerExecutorService.d.ts +1 -105
- package/dist/agent-variants/WorkerExecutorService.d.ts.map +1 -1
- package/dist/agent-variants/WorkerExecutorService.js +11 -182
- package/dist/agent-variants/WorkerExecutorService.js.map +1 -1
- package/dist/agent-variants/WorktreeOrchestrator.d.ts +9 -611
- package/dist/agent-variants/WorktreeOrchestrator.d.ts.map +1 -1
- package/dist/agent-variants/WorktreeOrchestrator.js +111 -1468
- package/dist/agent-variants/WorktreeOrchestrator.js.map +1 -1
- package/dist/agent-variants/WorktreeOrchestrator.testHelpers.d.ts +0 -7
- package/dist/agent-variants/WorktreeOrchestrator.testHelpers.d.ts.map +1 -1
- package/dist/agent-variants/WorktreeOrchestrator.testHelpers.js +2 -25
- package/dist/agent-variants/WorktreeOrchestrator.testHelpers.js.map +1 -1
- package/dist/agent-variants/briefContent.d.ts +0 -11
- package/dist/agent-variants/briefContent.d.ts.map +1 -1
- package/dist/agent-variants/briefContent.js +0 -12
- package/dist/agent-variants/briefContent.js.map +1 -1
- package/dist/agent-variants/contextAssets.d.ts +0 -3
- package/dist/agent-variants/contextAssets.d.ts.map +1 -1
- package/dist/agent-variants/contextAssets.js +0 -3
- package/dist/agent-variants/contextAssets.js.map +1 -1
- package/dist/agent-variants/contracts.d.ts +5 -493
- package/dist/agent-variants/contracts.d.ts.map +1 -1
- package/dist/agent-variants/contracts.js +2 -167
- package/dist/agent-variants/contracts.js.map +1 -1
- package/dist/agent-variants/createProjectArtifacts.d.ts +0 -17
- package/dist/agent-variants/createProjectArtifacts.d.ts.map +1 -1
- package/dist/agent-variants/createProjectArtifacts.js +0 -10
- package/dist/agent-variants/createProjectArtifacts.js.map +1 -1
- package/dist/agent-variants/diffQa.d.ts +0 -1
- package/dist/agent-variants/diffQa.d.ts.map +1 -1
- package/dist/agent-variants/diffQa.js +0 -1
- package/dist/agent-variants/diffQa.js.map +1 -1
- package/dist/agent-variants/directionAuthoring.d.ts +0 -13
- package/dist/agent-variants/directionAuthoring.d.ts.map +1 -1
- package/dist/agent-variants/directionAuthoring.js +12 -46
- package/dist/agent-variants/directionAuthoring.js.map +1 -1
- package/dist/agent-variants/elementRefToTarget.d.ts +0 -17
- package/dist/agent-variants/elementRefToTarget.d.ts.map +1 -1
- package/dist/agent-variants/elementRefToTarget.js +0 -17
- package/dist/agent-variants/elementRefToTarget.js.map +1 -1
- package/dist/agent-variants/generatedDestination.d.ts +0 -3
- package/dist/agent-variants/generatedDestination.d.ts.map +1 -1
- package/dist/agent-variants/generatedDestination.js +0 -3
- package/dist/agent-variants/generatedDestination.js.map +1 -1
- package/dist/agent-variants/previewQa.d.ts +0 -6
- package/dist/agent-variants/previewQa.d.ts.map +1 -1
- package/dist/agent-variants/previewQa.js +0 -5
- package/dist/agent-variants/previewQa.js.map +1 -1
- package/dist/agent-variants/runLabel.d.ts +0 -15
- package/dist/agent-variants/runLabel.d.ts.map +1 -1
- package/dist/agent-variants/runLabel.js +0 -19
- package/dist/agent-variants/runLabel.js.map +1 -1
- package/dist/agent-variants/runPlan.d.ts +3 -84
- package/dist/agent-variants/runPlan.d.ts.map +1 -1
- package/dist/agent-variants/runPlan.js +0 -51
- package/dist/agent-variants/runPlan.js.map +1 -1
- package/dist/agent-variants/sourceContext.d.ts +0 -10
- package/dist/agent-variants/sourceContext.d.ts.map +1 -1
- package/dist/agent-variants/sourceContext.js +0 -36
- package/dist/agent-variants/sourceContext.js.map +1 -1
- package/dist/agent-variants/variantContext.d.ts +0 -6
- package/dist/agent-variants/variantContext.d.ts.map +1 -1
- package/dist/agent-variants/variantContext.js +0 -9
- package/dist/agent-variants/variantContext.js.map +1 -1
- package/dist/agent-variants/videoKeyframes.d.ts +0 -7
- package/dist/agent-variants/videoKeyframes.d.ts.map +1 -1
- package/dist/agent-variants/videoKeyframes.js +0 -7
- package/dist/agent-variants/videoKeyframes.js.map +1 -1
- package/dist/agent-variants/workItemBriefing.d.ts +0 -21
- package/dist/agent-variants/workItemBriefing.d.ts.map +1 -1
- package/dist/agent-variants/workItemBriefing.js +0 -49
- package/dist/agent-variants/workItemBriefing.js.map +1 -1
- package/dist/agent-variants/workerRuntime.d.ts +1 -137
- package/dist/agent-variants/workerRuntime.d.ts.map +1 -1
- package/dist/agent-variants/workerRuntime.js +8 -147
- package/dist/agent-variants/workerRuntime.js.map +1 -1
- package/dist/cli/browser.d.ts +0 -1
- package/dist/cli/browser.d.ts.map +1 -1
- package/dist/cli/browser.js +0 -1
- package/dist/cli/browser.js.map +1 -1
- package/dist/cli/callerRunnerPaths.d.ts +0 -50
- package/dist/cli/callerRunnerPaths.d.ts.map +1 -1
- package/dist/cli/callerRunnerPaths.js +0 -59
- package/dist/cli/callerRunnerPaths.js.map +1 -1
- package/dist/cli/client.d.ts +0 -18
- package/dist/cli/client.d.ts.map +1 -1
- package/dist/cli/client.js +0 -28
- package/dist/cli/client.js.map +1 -1
- package/dist/cli/commandNames.d.ts +0 -7
- package/dist/cli/commandNames.d.ts.map +1 -1
- package/dist/cli/commandNames.js +0 -7
- package/dist/cli/commandNames.js.map +1 -1
- package/dist/cli/commands/auth.d.ts +0 -6
- package/dist/cli/commands/auth.d.ts.map +1 -1
- package/dist/cli/commands/auth.js +0 -6
- package/dist/cli/commands/auth.js.map +1 -1
- package/dist/cli/commands/elementSelector.d.ts +0 -6
- package/dist/cli/commands/elementSelector.d.ts.map +1 -1
- package/dist/cli/commands/elementSelector.js +0 -7
- package/dist/cli/commands/elementSelector.js.map +1 -1
- package/dist/cli/commands/evidence.d.ts +0 -6
- package/dist/cli/commands/evidence.d.ts.map +1 -1
- package/dist/cli/commands/evidence.js +0 -6
- package/dist/cli/commands/evidence.js.map +1 -1
- package/dist/cli/commands/libraryOpen.d.ts +0 -1
- package/dist/cli/commands/libraryOpen.d.ts.map +1 -1
- package/dist/cli/commands/libraryOpen.js +3 -3
- package/dist/cli/commands/libraryOpen.js.map +1 -1
- package/dist/cli/commands/mcpServe.d.ts +0 -24
- package/dist/cli/commands/mcpServe.d.ts.map +1 -1
- package/dist/cli/commands/mcpServe.js +31 -102
- package/dist/cli/commands/mcpServe.js.map +1 -1
- package/dist/cli/commands/open.d.ts +0 -8
- package/dist/cli/commands/open.d.ts.map +1 -1
- package/dist/cli/commands/open.js +0 -57
- package/dist/cli/commands/open.js.map +1 -1
- package/dist/cli/commands/reference.d.ts +0 -5
- package/dist/cli/commands/reference.d.ts.map +1 -1
- package/dist/cli/commands/reference.js +0 -27
- package/dist/cli/commands/reference.js.map +1 -1
- package/dist/cli/commands/serve.d.ts +0 -5
- package/dist/cli/commands/serve.d.ts.map +1 -1
- package/dist/cli/commands/serve.js +0 -34
- package/dist/cli/commands/serve.js.map +1 -1
- package/dist/cli/commands/shared.d.ts +0 -16
- package/dist/cli/commands/shared.d.ts.map +1 -1
- package/dist/cli/commands/shared.js +0 -19
- package/dist/cli/commands/shared.js.map +1 -1
- package/dist/cli/commands/status.d.ts +0 -6
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +0 -11
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/stop.d.ts +0 -5
- package/dist/cli/commands/stop.d.ts.map +1 -1
- package/dist/cli/commands/stop.js +0 -6
- package/dist/cli/commands/stop.js.map +1 -1
- package/dist/cli/commands/update.d.ts +0 -1
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +0 -1
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/commands/variants.d.ts +0 -1
- package/dist/cli/commands/variants.d.ts.map +1 -1
- package/dist/cli/commands/variants.js +0 -31
- package/dist/cli/commands/variants.js.map +1 -1
- package/dist/cli/commands/wait.d.ts +0 -21
- package/dist/cli/commands/wait.d.ts.map +1 -1
- package/dist/cli/commands/wait.js +0 -23
- package/dist/cli/commands/wait.js.map +1 -1
- package/dist/cli/entry.d.ts +0 -10
- package/dist/cli/entry.d.ts.map +1 -1
- package/dist/cli/entry.js +0 -13
- package/dist/cli/entry.js.map +1 -1
- package/dist/cli/envelope.d.ts +1 -25
- package/dist/cli/envelope.d.ts.map +1 -1
- package/dist/cli/envelope.js +0 -19
- package/dist/cli/envelope.js.map +1 -1
- package/dist/cli/flags.d.ts +0 -7
- package/dist/cli/flags.d.ts.map +1 -1
- package/dist/cli/flags.js +0 -7
- package/dist/cli/flags.js.map +1 -1
- package/dist/cli/handoff.d.ts +0 -33
- package/dist/cli/handoff.d.ts.map +1 -1
- package/dist/cli/handoff.js +0 -42
- package/dist/cli/handoff.js.map +1 -1
- package/dist/cli/handoffStore.d.ts +0 -11
- package/dist/cli/handoffStore.d.ts.map +1 -1
- package/dist/cli/handoffStore.js +0 -10
- package/dist/cli/handoffStore.js.map +1 -1
- package/dist/cli/hostWorkNextAction.d.ts +0 -11
- package/dist/cli/hostWorkNextAction.d.ts.map +1 -1
- package/dist/cli/hostWorkNextAction.js +0 -4
- package/dist/cli/hostWorkNextAction.js.map +1 -1
- package/dist/cli/io.d.ts +0 -9
- package/dist/cli/io.d.ts.map +1 -1
- package/dist/cli/io.js +0 -1
- package/dist/cli/io.js.map +1 -1
- package/dist/cli/resolveDefaultNodeEnv.d.ts +0 -5
- package/dist/cli/resolveDefaultNodeEnv.d.ts.map +1 -1
- package/dist/cli/resolveDefaultNodeEnv.js +0 -5
- package/dist/cli/resolveDefaultNodeEnv.js.map +1 -1
- package/dist/cli/router.d.ts +0 -21
- package/dist/cli/router.d.ts.map +1 -1
- package/dist/cli/router.js +0 -46
- package/dist/cli/router.js.map +1 -1
- package/dist/cli/runnerSelection.d.ts +0 -58
- package/dist/cli/runnerSelection.d.ts.map +1 -1
- package/dist/cli/runnerSelection.js +0 -16
- package/dist/cli/runnerSelection.js.map +1 -1
- package/dist/cli/serverHandshake.d.ts +0 -36
- package/dist/cli/serverHandshake.d.ts.map +1 -1
- package/dist/cli/serverHandshake.js +0 -29
- package/dist/cli/serverHandshake.js.map +1 -1
- package/dist/cli/serverUnavailable.d.ts +0 -1
- package/dist/cli/serverUnavailable.d.ts.map +1 -1
- package/dist/cli/serverUnavailable.js +0 -1
- package/dist/cli/serverUnavailable.js.map +1 -1
- package/dist/cli/serverVersion.d.ts +0 -22
- package/dist/cli/serverVersion.d.ts.map +1 -1
- package/dist/cli/serverVersion.js +0 -12
- package/dist/cli/serverVersion.js.map +1 -1
- package/dist/cli/telemetry.d.ts +0 -4
- package/dist/cli/telemetry.d.ts.map +1 -1
- package/dist/cli/telemetry.js +0 -4
- package/dist/cli/telemetry.js.map +1 -1
- package/dist/config/evaluateFlags.d.ts +0 -4
- package/dist/config/evaluateFlags.d.ts.map +1 -1
- package/dist/config/evaluateFlags.js +0 -11
- package/dist/config/evaluateFlags.js.map +1 -1
- package/dist/config/featureFlagUserKey.d.ts +0 -3
- package/dist/config/featureFlagUserKey.d.ts.map +1 -1
- package/dist/config/featureFlagUserKey.js +0 -3
- package/dist/config/featureFlagUserKey.js.map +1 -1
- package/dist/config/flags.d.ts +0 -14
- package/dist/config/flags.d.ts.map +1 -1
- package/dist/config/flags.js +0 -27
- package/dist/config/flags.js.map +1 -1
- package/dist/config/proxy.d.ts +0 -3
- package/dist/config/proxy.d.ts.map +1 -1
- package/dist/config/proxy.js +0 -11
- package/dist/config/proxy.js.map +1 -1
- package/dist/config/telemetry.d.ts +0 -13
- package/dist/config/telemetry.d.ts.map +1 -1
- package/dist/config/telemetry.js +0 -13
- package/dist/config/telemetry.js.map +1 -1
- package/dist/config/telemetryContract.d.ts +0 -1
- package/dist/config/telemetryContract.d.ts.map +1 -1
- package/dist/config/telemetryContract.js +0 -1
- package/dist/config/telemetryContract.js.map +1 -1
- package/dist/hosts/workerDefaults.d.ts +0 -69
- package/dist/hosts/workerDefaults.d.ts.map +1 -1
- package/dist/hosts/workerDefaults.js +0 -68
- package/dist/hosts/workerDefaults.js.map +1 -1
- package/dist/index-core.d.ts.map +1 -1
- package/dist/index-core.js +0 -2
- package/dist/index-core.js.map +1 -1
- package/dist/index.d.ts +0 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -105
- package/dist/index.js.map +1 -1
- package/dist/install/allowlists.d.ts +0 -32
- package/dist/install/allowlists.d.ts.map +1 -1
- package/dist/install/allowlists.js +0 -44
- package/dist/install/allowlists.js.map +1 -1
- package/dist/install/automaticCliUpdate.d.ts +0 -6
- package/dist/install/automaticCliUpdate.d.ts.map +1 -1
- package/dist/install/automaticCliUpdate.js +0 -13
- package/dist/install/automaticCliUpdate.js.map +1 -1
- package/dist/install/channelCli.d.ts +0 -1
- package/dist/install/channelCli.d.ts.map +1 -1
- package/dist/install/channelCli.js +0 -3
- package/dist/install/channelCli.js.map +1 -1
- package/dist/install/channelManager.d.ts +0 -14
- package/dist/install/channelManager.d.ts.map +1 -1
- package/dist/install/channelManager.js +0 -22
- package/dist/install/channelManager.js.map +1 -1
- package/dist/install/codexGuidance.d.ts +0 -1
- package/dist/install/codexGuidance.d.ts.map +1 -1
- package/dist/install/codexGuidance.js +0 -7
- package/dist/install/codexGuidance.js.map +1 -1
- package/dist/install/globalBootstrap.d.ts +0 -25
- package/dist/install/globalBootstrap.d.ts.map +1 -1
- package/dist/install/globalBootstrap.js +0 -10
- package/dist/install/globalBootstrap.js.map +1 -1
- package/dist/install/harnesses.d.ts +0 -66
- package/dist/install/harnesses.d.ts.map +1 -1
- package/dist/install/harnesses.js +0 -60
- package/dist/install/harnesses.js.map +1 -1
- package/dist/install/installationChannel.d.ts +0 -1
- package/dist/install/installationChannel.d.ts.map +1 -1
- package/dist/install/installationChannel.js +0 -1
- package/dist/install/installationChannel.js.map +1 -1
- package/dist/install/legacyMcp.d.ts +0 -23
- package/dist/install/legacyMcp.d.ts.map +1 -1
- package/dist/install/legacyMcp.js +3 -46
- package/dist/install/legacyMcp.js.map +1 -1
- package/dist/install/mcpRegistration.d.ts +0 -41
- package/dist/install/mcpRegistration.d.ts.map +1 -1
- package/dist/install/mcpRegistration.js +2 -70
- package/dist/install/mcpRegistration.js.map +1 -1
- package/dist/library/HubClient.d.ts +0 -2
- package/dist/library/HubClient.d.ts.map +1 -1
- package/dist/library/HubClient.js +0 -4
- package/dist/library/HubClient.js.map +1 -1
- package/dist/library/HubServer.d.ts +0 -1
- package/dist/library/HubServer.d.ts.map +1 -1
- package/dist/library/HubServer.js +0 -1
- package/dist/library/HubServer.js.map +1 -1
- package/dist/library/StaticPreviewServer.d.ts +0 -1
- package/dist/library/StaticPreviewServer.d.ts.map +1 -1
- package/dist/library/StaticPreviewServer.js +0 -1
- package/dist/library/StaticPreviewServer.js.map +1 -1
- package/dist/library/http.d.ts +0 -2
- package/dist/library/http.d.ts.map +1 -1
- package/dist/library/http.js +0 -2
- package/dist/library/http.js.map +1 -1
- package/dist/library/importFiles.d.ts +0 -6
- package/dist/library/importFiles.d.ts.map +1 -1
- package/dist/library/importFiles.js +45 -11
- package/dist/library/importFiles.js.map +1 -1
- package/dist/mcp/changeBatchClassification.d.ts +0 -40
- package/dist/mcp/changeBatchClassification.d.ts.map +1 -1
- package/dist/mcp/changeBatchClassification.js +0 -34
- package/dist/mcp/changeBatchClassification.js.map +1 -1
- package/dist/prompts/commentVariationPrompts.d.ts +0 -5
- package/dist/prompts/commentVariationPrompts.d.ts.map +1 -1
- package/dist/prompts/commentVariationPrompts.js +0 -5
- package/dist/prompts/commentVariationPrompts.js.map +1 -1
- package/dist/prototype-host.js +0 -15
- package/dist/prototype-host.js.map +1 -1
- package/dist/proxy-middleware/preview-bridge.d.ts +0 -2
- package/dist/proxy-middleware/preview-bridge.d.ts.map +1 -1
- package/dist/proxy-middleware/preview-bridge.js +7 -7
- package/dist/proxy-middleware/preview-bridge.js.map +1 -1
- package/dist/proxy-middleware/preview-connections.d.ts +8 -0
- package/dist/proxy-middleware/preview-connections.d.ts.map +1 -0
- package/dist/proxy-middleware/preview-connections.js +29 -0
- package/dist/proxy-middleware/preview-connections.js.map +1 -0
- package/dist/proxy-middleware/proxy-config.d.ts +0 -12
- package/dist/proxy-middleware/proxy-config.d.ts.map +1 -1
- package/dist/proxy-middleware/proxy-config.js +41 -90
- package/dist/proxy-middleware/proxy-config.js.map +1 -1
- package/dist/routes/account.d.ts +0 -1
- package/dist/routes/account.d.ts.map +1 -1
- package/dist/routes/account.js +0 -2
- package/dist/routes/account.js.map +1 -1
- package/dist/routes/agentVariants.d.ts +0 -15
- package/dist/routes/agentVariants.d.ts.map +1 -1
- package/dist/routes/agentVariants.js +14 -325
- package/dist/routes/agentVariants.js.map +1 -1
- package/dist/routes/cli.d.ts +0 -17
- package/dist/routes/cli.d.ts.map +1 -1
- package/dist/routes/cli.js +0 -13
- package/dist/routes/cli.js.map +1 -1
- package/dist/routes/emptyStatePage.d.ts +0 -12
- package/dist/routes/emptyStatePage.d.ts.map +1 -1
- package/dist/routes/emptyStatePage.js +0 -12
- package/dist/routes/emptyStatePage.js.map +1 -1
- package/dist/routes/git.d.ts.map +1 -1
- package/dist/routes/git.js +0 -13
- package/dist/routes/git.js.map +1 -1
- package/dist/routes/inlineStartTracker.d.ts +0 -16
- package/dist/routes/inlineStartTracker.d.ts.map +1 -1
- package/dist/routes/inlineStartTracker.js.map +1 -1
- package/dist/routes/integrations.d.ts +0 -6
- package/dist/routes/integrations.d.ts.map +1 -1
- package/dist/routes/integrations.js +0 -6
- package/dist/routes/integrations.js.map +1 -1
- package/dist/routes/library/app.d.ts +0 -1
- package/dist/routes/library/app.d.ts.map +1 -1
- package/dist/routes/library/app.js +0 -1
- package/dist/routes/library/app.js.map +1 -1
- package/dist/routes/library/index.d.ts +0 -1
- package/dist/routes/library/index.d.ts.map +1 -1
- package/dist/routes/library/index.js +0 -1
- package/dist/routes/library/index.js.map +1 -1
- package/dist/routes/library/middleware.d.ts +0 -3
- package/dist/routes/library/middleware.d.ts.map +1 -1
- package/dist/routes/library/middleware.js +0 -4
- package/dist/routes/library/middleware.js.map +1 -1
- package/dist/routes/library/navigation.d.ts +0 -1
- package/dist/routes/library/navigation.d.ts.map +1 -1
- package/dist/routes/library/navigation.js +0 -1
- package/dist/routes/library/navigation.js.map +1 -1
- package/dist/routes/library/projects.d.ts +0 -1
- package/dist/routes/library/projects.d.ts.map +1 -1
- package/dist/routes/library/projects.js +0 -1
- package/dist/routes/library/projects.js.map +1 -1
- package/dist/routes/library/staticPreview.d.ts +0 -1
- package/dist/routes/library/staticPreview.d.ts.map +1 -1
- package/dist/routes/library/staticPreview.js +0 -1
- package/dist/routes/library/staticPreview.js.map +1 -1
- package/dist/routes/library/variants.d.ts +0 -1
- package/dist/routes/library/variants.d.ts.map +1 -1
- package/dist/routes/library/variants.js +0 -1
- package/dist/routes/library/variants.js.map +1 -1
- package/dist/routes/mcp.d.ts +0 -3
- package/dist/routes/mcp.d.ts.map +1 -1
- package/dist/routes/mcp.js +3 -4
- package/dist/routes/mcp.js.map +1 -1
- package/dist/routes/prototypeHost.d.ts +0 -2
- package/dist/routes/prototypeHost.d.ts.map +1 -1
- package/dist/routes/prototypeHost.js +6 -32
- package/dist/routes/prototypeHost.js.map +1 -1
- package/dist/routes/static.d.ts +0 -3
- package/dist/routes/static.d.ts.map +1 -1
- package/dist/routes/static.js +0 -6
- package/dist/routes/static.js.map +1 -1
- package/dist/routes/support.d.ts +0 -3
- package/dist/routes/support.d.ts.map +1 -1
- package/dist/routes/support.js +0 -3
- package/dist/routes/support.js.map +1 -1
- package/dist/server.d.ts +0 -42
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1 -129
- package/dist/server.js.map +1 -1
- package/dist/services/AuthService.d.ts +0 -76
- package/dist/services/AuthService.d.ts.map +1 -1
- package/dist/services/AuthService.js +0 -112
- package/dist/services/AuthService.js.map +1 -1
- package/dist/services/BrowserAgentClient.d.ts +1 -21
- package/dist/services/BrowserAgentClient.d.ts.map +1 -1
- package/dist/services/BrowserAgentClient.js +0 -10
- package/dist/services/BrowserAgentClient.js.map +1 -1
- package/dist/services/BundleStore.d.ts +0 -18
- package/dist/services/BundleStore.d.ts.map +1 -1
- package/dist/services/BundleStore.js +0 -10
- package/dist/services/BundleStore.js.map +1 -1
- package/dist/services/CliLogForwarder.d.ts +0 -26
- package/dist/services/CliLogForwarder.d.ts.map +1 -1
- package/dist/services/CliLogForwarder.js +1 -33
- package/dist/services/CliLogForwarder.js.map +1 -1
- package/dist/services/CommitMessageGenerator.d.ts +0 -6
- package/dist/services/CommitMessageGenerator.d.ts.map +1 -1
- package/dist/services/CommitMessageGenerator.js +0 -3
- package/dist/services/CommitMessageGenerator.js.map +1 -1
- package/dist/services/ComponentSearchService.d.ts +2 -16
- package/dist/services/ComponentSearchService.d.ts.map +1 -1
- package/dist/services/ComponentSearchService.js +2 -56
- package/dist/services/ComponentSearchService.js.map +1 -1
- package/dist/services/ConfigManager.d.ts +0 -113
- package/dist/services/ConfigManager.d.ts.map +1 -1
- package/dist/services/ConfigManager.js +0 -101
- package/dist/services/ConfigManager.js.map +1 -1
- package/dist/services/DependencyPreparationService.d.ts +28 -0
- package/dist/services/DependencyPreparationService.d.ts.map +1 -0
- package/dist/services/DependencyPreparationService.js +322 -0
- package/dist/services/DependencyPreparationService.js.map +1 -0
- package/dist/services/DevServerRuntimeService.d.ts +0 -20
- package/dist/services/DevServerRuntimeService.d.ts.map +1 -1
- package/dist/services/DevServerRuntimeService.js +0 -46
- package/dist/services/DevServerRuntimeService.js.map +1 -1
- package/dist/services/FeatureFlagService.d.ts +0 -15
- package/dist/services/FeatureFlagService.d.ts.map +1 -1
- package/dist/services/FeatureFlagService.js +0 -23
- package/dist/services/FeatureFlagService.js.map +1 -1
- package/dist/services/GatewayClient.d.ts +0 -27
- package/dist/services/GatewayClient.d.ts.map +1 -1
- package/dist/services/GatewayClient.js +0 -25
- package/dist/services/GatewayClient.js.map +1 -1
- package/dist/services/HistoryReplayService.d.ts +2 -32
- package/dist/services/HistoryReplayService.d.ts.map +1 -1
- package/dist/services/HistoryReplayService.js +55 -65
- package/dist/services/HistoryReplayService.js.map +1 -1
- package/dist/services/HubDiscovery.d.ts +0 -8
- package/dist/services/HubDiscovery.d.ts.map +1 -1
- package/dist/services/HubDiscovery.js +0 -8
- package/dist/services/HubDiscovery.js.map +1 -1
- package/dist/services/HubNavigation.d.ts +0 -6
- package/dist/services/HubNavigation.d.ts.map +1 -1
- package/dist/services/HubNavigation.js +5 -8
- package/dist/services/HubNavigation.js.map +1 -1
- package/dist/services/IntegrationsClient.d.ts +0 -34
- package/dist/services/IntegrationsClient.d.ts.map +1 -1
- package/dist/services/IntegrationsClient.js +0 -27
- package/dist/services/IntegrationsClient.js.map +1 -1
- package/dist/services/LocalGitService.d.ts +0 -57
- package/dist/services/LocalGitService.d.ts.map +1 -1
- package/dist/services/LocalGitService.js +4 -85
- package/dist/services/LocalGitService.js.map +1 -1
- package/dist/services/MCPOnboardingTelemetry.d.ts +0 -9
- package/dist/services/MCPOnboardingTelemetry.d.ts.map +1 -1
- package/dist/services/MCPOnboardingTelemetry.js +0 -9
- package/dist/services/MCPOnboardingTelemetry.js.map +1 -1
- package/dist/services/MCPVariantsTelemetry.d.ts +0 -2
- package/dist/services/MCPVariantsTelemetry.d.ts.map +1 -1
- package/dist/services/MCPVariantsTelemetry.js +0 -2
- package/dist/services/MCPVariantsTelemetry.js.map +1 -1
- package/dist/services/OperatingSystemServices.d.ts +0 -22
- package/dist/services/OperatingSystemServices.d.ts.map +1 -1
- package/dist/services/OperatingSystemServices.js +22 -80
- package/dist/services/OperatingSystemServices.js.map +1 -1
- package/dist/services/PRTitleGenerator.d.ts +0 -7
- package/dist/services/PRTitleGenerator.d.ts.map +1 -1
- package/dist/services/PRTitleGenerator.js +0 -3
- package/dist/services/PRTitleGenerator.js.map +1 -1
- package/dist/services/ProjectCardPreviews.d.ts +0 -5
- package/dist/services/ProjectCardPreviews.d.ts.map +1 -1
- package/dist/services/ProjectCardPreviews.js +0 -5
- package/dist/services/ProjectCardPreviews.js.map +1 -1
- package/dist/services/ProjectDetectionService.d.ts +0 -4
- package/dist/services/ProjectDetectionService.d.ts.map +1 -1
- package/dist/services/ProjectDetectionService.js +2 -44
- package/dist/services/ProjectDetectionService.js.map +1 -1
- package/dist/services/ProjectFolderPicker.d.ts +0 -3
- package/dist/services/ProjectFolderPicker.d.ts.map +1 -1
- package/dist/services/ProjectFolderPicker.js +0 -4
- package/dist/services/ProjectFolderPicker.js.map +1 -1
- package/dist/services/ProjectImport.d.ts +0 -1
- package/dist/services/ProjectImport.d.ts.map +1 -1
- package/dist/services/ProjectImport.js +0 -3
- package/dist/services/ProjectImport.js.map +1 -1
- package/dist/services/ProjectLibrary.d.ts +0 -18
- package/dist/services/ProjectLibrary.d.ts.map +1 -1
- package/dist/services/ProjectLibrary.js +0 -20
- package/dist/services/ProjectLibrary.js.map +1 -1
- package/dist/services/ProjectMetadataStore.d.ts +0 -73
- package/dist/services/ProjectMetadataStore.d.ts.map +1 -1
- package/dist/services/ProjectMetadataStore.js +0 -90
- package/dist/services/ProjectMetadataStore.js.map +1 -1
- package/dist/services/ProjectPackageManager.d.ts +23 -0
- package/dist/services/ProjectPackageManager.d.ts.map +1 -0
- package/dist/services/ProjectPackageManager.js +185 -0
- package/dist/services/ProjectPackageManager.js.map +1 -0
- package/dist/services/ProjectRegistry.d.ts +0 -16
- package/dist/services/ProjectRegistry.d.ts.map +1 -1
- package/dist/services/ProjectRegistry.js +0 -18
- package/dist/services/ProjectRegistry.js.map +1 -1
- package/dist/services/ProjectRuntimeService.d.ts +0 -4
- package/dist/services/ProjectRuntimeService.d.ts.map +1 -1
- package/dist/services/ProjectRuntimeService.js +0 -5
- package/dist/services/ProjectRuntimeService.js.map +1 -1
- package/dist/services/ProjectScreenshotService.d.ts +0 -5
- package/dist/services/ProjectScreenshotService.d.ts.map +1 -1
- package/dist/services/ProjectScreenshotService.js +0 -6
- package/dist/services/ProjectScreenshotService.js.map +1 -1
- package/dist/services/ProjectStorage.d.ts +0 -4
- package/dist/services/ProjectStorage.d.ts.map +1 -1
- package/dist/services/ProjectStorage.js +0 -4
- package/dist/services/ProjectStorage.js.map +1 -1
- package/dist/services/PrototypeBuildService.d.ts +1 -23
- package/dist/services/PrototypeBuildService.d.ts.map +1 -1
- package/dist/services/PrototypeBuildService.js +6 -31
- package/dist/services/PrototypeBuildService.js.map +1 -1
- package/dist/services/PrototypeDeployService.d.ts +0 -20
- package/dist/services/PrototypeDeployService.d.ts.map +1 -1
- package/dist/services/PrototypeDeployService.js +1 -24
- package/dist/services/PrototypeDeployService.js.map +1 -1
- package/dist/services/RequestAuthContext.d.ts +0 -5
- package/dist/services/RequestAuthContext.d.ts.map +1 -1
- package/dist/services/RequestAuthContext.js +0 -5
- package/dist/services/RequestAuthContext.js.map +1 -1
- package/dist/services/SessionBridgeService.d.ts.map +1 -1
- package/dist/services/SessionBridgeService.js +0 -2
- package/dist/services/SessionBridgeService.js.map +1 -1
- package/dist/services/SessionService.d.ts +0 -4
- package/dist/services/SessionService.d.ts.map +1 -1
- package/dist/services/SessionService.js +1 -22
- package/dist/services/SessionService.js.map +1 -1
- package/dist/services/StaticFileService.d.ts +0 -9
- package/dist/services/StaticFileService.d.ts.map +1 -1
- package/dist/services/StaticFileService.js +0 -11
- package/dist/services/StaticFileService.js.map +1 -1
- package/dist/services/StaticLibraryPreviews.d.ts +0 -3
- package/dist/services/StaticLibraryPreviews.d.ts.map +1 -1
- package/dist/services/StaticLibraryPreviews.js +0 -3
- package/dist/services/StaticLibraryPreviews.js.map +1 -1
- package/dist/services/StaticPreviewServer.d.ts +0 -16
- package/dist/services/StaticPreviewServer.d.ts.map +1 -1
- package/dist/services/StaticPreviewServer.js +2 -28
- package/dist/services/StaticPreviewServer.js.map +1 -1
- package/dist/services/SupabaseAuthService.d.ts +0 -11
- package/dist/services/SupabaseAuthService.d.ts.map +1 -1
- package/dist/services/SupabaseAuthService.js +0 -11
- package/dist/services/SupabaseAuthService.js.map +1 -1
- package/dist/services/SupportTicketService.d.ts +0 -4
- package/dist/services/SupportTicketService.d.ts.map +1 -1
- package/dist/services/SupportTicketService.js +0 -4
- package/dist/services/SupportTicketService.js.map +1 -1
- package/dist/services/TelemetryService.d.ts +0 -182
- package/dist/services/TelemetryService.d.ts.map +1 -1
- package/dist/services/TelemetryService.js +0 -193
- package/dist/services/TelemetryService.js.map +1 -1
- package/dist/services/TreeStore.d.ts +0 -72
- package/dist/services/TreeStore.d.ts.map +1 -1
- package/dist/services/TreeStore.js +2 -101
- package/dist/services/TreeStore.js.map +1 -1
- package/dist/services/VariantGenerationService.d.ts +0 -32
- package/dist/services/VariantGenerationService.d.ts.map +1 -1
- package/dist/services/VariantGenerationService.js +5 -62
- package/dist/services/VariantGenerationService.js.map +1 -1
- package/dist/services/VariantHistoryService.d.ts +0 -114
- package/dist/services/VariantHistoryService.d.ts.map +1 -1
- package/dist/services/VariantHistoryService.js +2 -144
- package/dist/services/VariantHistoryService.js.map +1 -1
- package/dist/services/VariantRunService.d.ts +0 -31
- package/dist/services/VariantRunService.d.ts.map +1 -1
- package/dist/services/VariantRunService.js +0 -35
- package/dist/services/VariantRunService.js.map +1 -1
- package/dist/services/VariantsRuntime.d.ts +0 -3
- package/dist/services/VariantsRuntime.d.ts.map +1 -1
- package/dist/services/VariantsRuntime.js +0 -5
- package/dist/services/VariantsRuntime.js.map +1 -1
- package/dist/services/WorktreeManager.d.ts +1 -220
- package/dist/services/WorktreeManager.d.ts.map +1 -1
- package/dist/services/WorktreeManager.js +75 -372
- package/dist/services/WorktreeManager.js.map +1 -1
- package/dist/services/accessTokenRefresh.d.ts +0 -25
- package/dist/services/accessTokenRefresh.d.ts.map +1 -1
- package/dist/services/accessTokenRefresh.js +0 -25
- package/dist/services/accessTokenRefresh.js.map +1 -1
- package/dist/services/adapters/TailwindTokenAdapter.d.ts +0 -29
- package/dist/services/adapters/TailwindTokenAdapter.d.ts.map +1 -1
- package/dist/services/adapters/TailwindTokenAdapter.js +1 -46
- package/dist/services/adapters/TailwindTokenAdapter.js.map +1 -1
- package/dist/services/createAgentVariantsOrchestrator.d.ts +0 -18
- package/dist/services/createAgentVariantsOrchestrator.d.ts.map +1 -1
- package/dist/services/createAgentVariantsOrchestrator.js +0 -22
- package/dist/services/createAgentVariantsOrchestrator.js.map +1 -1
- package/dist/services/extractors/CSSVariableExtractor.d.ts +0 -25
- package/dist/services/extractors/CSSVariableExtractor.d.ts.map +1 -1
- package/dist/services/extractors/CSSVariableExtractor.js +1 -42
- package/dist/services/extractors/CSSVariableExtractor.js.map +1 -1
- package/dist/services/historyRoots.d.ts +0 -12
- package/dist/services/historyRoots.d.ts.map +1 -1
- package/dist/services/historyRoots.js +0 -13
- package/dist/services/historyRoots.js.map +1 -1
- package/dist/services/libraryErrors.d.ts +0 -1
- package/dist/services/libraryErrors.d.ts.map +1 -1
- package/dist/services/libraryErrors.js +0 -1
- package/dist/services/libraryErrors.js.map +1 -1
- package/dist/services/pendingLogin.d.ts +0 -13
- package/dist/services/pendingLogin.d.ts.map +1 -1
- package/dist/services/pendingLogin.js +0 -3
- package/dist/services/pendingLogin.js.map +1 -1
- package/dist/services/staticProjectTarget.d.ts +0 -4
- package/dist/services/staticProjectTarget.d.ts.map +1 -1
- package/dist/services/staticProjectTarget.js +0 -4
- package/dist/services/staticProjectTarget.js.map +1 -1
- package/dist/services/staticStarter.d.ts +0 -20
- package/dist/services/staticStarter.d.ts.map +1 -1
- package/dist/services/staticStarter.js +0 -20
- package/dist/services/staticStarter.js.map +1 -1
- package/dist/services/templates/designCatalog.d.ts +0 -11
- package/dist/services/templates/designCatalog.d.ts.map +1 -1
- package/dist/services/templates/designCatalog.js +0 -26
- package/dist/services/templates/designCatalog.js.map +1 -1
- package/dist/services/templates/viteReactTs.d.ts +0 -26
- package/dist/services/templates/viteReactTs.d.ts.map +1 -1
- package/dist/services/templates/viteReactTs.js +0 -20
- package/dist/services/templates/viteReactTs.js.map +1 -1
- package/dist/storage/library/BundleMetadataStore.d.ts +0 -16
- package/dist/storage/library/BundleMetadataStore.d.ts.map +1 -1
- package/dist/storage/library/BundleMetadataStore.js +0 -17
- package/dist/storage/library/BundleMetadataStore.js.map +1 -1
- package/dist/storage/library/CoordinatorRecords.d.ts +0 -9
- package/dist/storage/library/CoordinatorRecords.d.ts.map +1 -1
- package/dist/storage/library/CoordinatorRecords.js +0 -9
- package/dist/storage/library/CoordinatorRecords.js.map +1 -1
- package/dist/storage/library/ProjectPreviewCache.d.ts +0 -4
- package/dist/storage/library/ProjectPreviewCache.d.ts.map +1 -1
- package/dist/storage/library/ProjectPreviewCache.js +0 -4
- package/dist/storage/library/ProjectPreviewCache.js.map +1 -1
- package/dist/storage/library/ProjectRecords.d.ts +0 -17
- package/dist/storage/library/ProjectRecords.d.ts.map +1 -1
- package/dist/storage/library/ProjectRecords.js +0 -17
- package/dist/storage/library/ProjectRecords.js.map +1 -1
- package/dist/storage/library/database.d.ts +0 -3
- package/dist/storage/library/database.d.ts.map +1 -1
- package/dist/storage/library/database.js +0 -3
- package/dist/storage/library/database.js.map +1 -1
- package/dist/storage/library/migrations.d.ts +0 -2
- package/dist/storage/library/migrations.d.ts.map +1 -1
- package/dist/storage/library/migrations.js +0 -2
- package/dist/storage/library/migrations.js.map +1 -1
- package/dist/storage/library/sourcePresentation.d.ts +0 -1
- package/dist/storage/library/sourcePresentation.d.ts.map +1 -1
- package/dist/storage/library/sourcePresentation.js +0 -1
- package/dist/storage/library/sourcePresentation.js.map +1 -1
- package/dist/storage/project-metadata/ProjectMetadataRecords.d.ts +0 -11
- package/dist/storage/project-metadata/ProjectMetadataRecords.d.ts.map +1 -1
- package/dist/storage/project-metadata/ProjectMetadataRecords.js +0 -13
- package/dist/storage/project-metadata/ProjectMetadataRecords.js.map +1 -1
- package/dist/storage/project-metadata/database.d.ts +0 -1
- package/dist/storage/project-metadata/database.d.ts.map +1 -1
- package/dist/storage/project-metadata/database.js +0 -7
- package/dist/storage/project-metadata/database.js.map +1 -1
- package/dist/storage/project-metadata/migrations.d.ts +0 -42
- package/dist/storage/project-metadata/migrations.d.ts.map +1 -1
- package/dist/storage/project-metadata/migrations.js +0 -41
- package/dist/storage/project-metadata/migrations.js.map +1 -1
- package/dist/types/account.d.ts +0 -1
- package/dist/types/account.d.ts.map +1 -1
- package/dist/types/change-request-types.d.ts +0 -167
- package/dist/types/change-request-types.d.ts.map +1 -1
- package/dist/types/change-types.d.ts +0 -1
- package/dist/types/change-types.d.ts.map +1 -1
- package/dist/types/change-types.js +0 -1
- package/dist/types/change-types.js.map +1 -1
- package/dist/types/project-library.d.ts +0 -1
- package/dist/types/project-library.d.ts.map +1 -1
- package/dist/types/style-manipulation-types.d.ts +0 -69
- package/dist/types/style-manipulation-types.d.ts.map +1 -1
- package/dist/types/style-manipulation-types.js +0 -26
- package/dist/types/style-manipulation-types.js.map +1 -1
- package/dist/types/token-types.d.ts +0 -30
- package/dist/types/token-types.d.ts.map +1 -1
- package/dist/types/token-types.js +0 -1
- package/dist/types/token-types.js.map +1 -1
- package/dist/types/variant-history.d.ts +0 -39
- package/dist/types/variant-history.d.ts.map +1 -1
- package/dist/utils/branchNames.d.ts +0 -1
- package/dist/utils/branchNames.d.ts.map +1 -1
- package/dist/utils/branchNames.js +0 -2
- package/dist/utils/branchNames.js.map +1 -1
- package/dist/utils/cloneOrCopyDirSync.d.ts +0 -12
- package/dist/utils/cloneOrCopyDirSync.d.ts.map +1 -1
- package/dist/utils/cloneOrCopyDirSync.js +1 -15
- package/dist/utils/cloneOrCopyDirSync.js.map +1 -1
- package/dist/utils/devServerCommand.d.ts +0 -23
- package/dist/utils/devServerCommand.d.ts.map +1 -1
- package/dist/utils/devServerCommand.js +0 -24
- package/dist/utils/devServerCommand.js.map +1 -1
- package/dist/utils/devServerError.d.ts +2 -16
- package/dist/utils/devServerError.d.ts.map +1 -1
- package/dist/utils/devServerError.js +4 -17
- package/dist/utils/devServerError.js.map +1 -1
- package/dist/utils/displayLocale.d.ts +0 -2
- package/dist/utils/displayLocale.d.ts.map +1 -1
- package/dist/utils/displayLocale.js +0 -2
- package/dist/utils/displayLocale.js.map +1 -1
- package/dist/utils/elementFileHintSignals.d.ts +0 -5
- package/dist/utils/elementFileHintSignals.d.ts.map +1 -1
- package/dist/utils/elementFileHintSignals.js +0 -5
- package/dist/utils/elementFileHintSignals.js.map +1 -1
- package/dist/utils/formatElementTargetingContext.d.ts +0 -6
- package/dist/utils/formatElementTargetingContext.d.ts.map +1 -1
- package/dist/utils/formatElementTargetingContext.js +3 -9
- package/dist/utils/formatElementTargetingContext.js.map +1 -1
- package/dist/utils/hostAgentEnv.d.ts +0 -17
- package/dist/utils/hostAgentEnv.d.ts.map +1 -1
- package/dist/utils/hostAgentEnv.js +0 -17
- package/dist/utils/hostAgentEnv.js.map +1 -1
- package/dist/utils/htmlDocument.d.ts +2 -8
- package/dist/utils/htmlDocument.d.ts.map +1 -1
- package/dist/utils/htmlDocument.js +20 -16
- package/dist/utils/htmlDocument.js.map +1 -1
- package/dist/utils/internalGitIdentity.d.ts +2 -0
- package/dist/utils/internalGitIdentity.d.ts.map +1 -0
- package/dist/utils/internalGitIdentity.js +12 -0
- package/dist/utils/internalGitIdentity.js.map +1 -0
- package/dist/utils/logger.d.ts +0 -5
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +2 -19
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/loginShellPath.d.ts +0 -9
- package/dist/utils/loginShellPath.d.ts.map +1 -1
- package/dist/utils/loginShellPath.js +0 -69
- package/dist/utils/loginShellPath.js.map +1 -1
- package/dist/utils/loopback.d.ts +0 -1
- package/dist/utils/loopback.d.ts.map +1 -1
- package/dist/utils/loopback.js +0 -1
- package/dist/utils/loopback.js.map +1 -1
- package/dist/utils/pid.d.ts +0 -1
- package/dist/utils/pid.d.ts.map +1 -1
- package/dist/utils/pid.js +0 -1
- package/dist/utils/pid.js.map +1 -1
- package/dist/utils/portUtils.d.ts +1 -14
- package/dist/utils/portUtils.d.ts.map +1 -1
- package/dist/utils/portUtils.js +1 -21
- package/dist/utils/portUtils.js.map +1 -1
- package/dist/utils/queueAccess.d.ts +0 -9
- package/dist/utils/queueAccess.d.ts.map +1 -1
- package/dist/utils/queueAccess.js +0 -9
- package/dist/utils/queueAccess.js.map +1 -1
- package/dist/utils/skillWriter.d.ts +0 -17
- package/dist/utils/skillWriter.d.ts.map +1 -1
- package/dist/utils/skillWriter.js +0 -20
- package/dist/utils/skillWriter.js.map +1 -1
- package/dist/utils/skills/claude-skill.d.ts +0 -5
- package/dist/utils/skills/claude-skill.d.ts.map +1 -1
- package/dist/utils/skills/claude-skill.js +0 -5
- package/dist/utils/skills/claude-skill.js.map +1 -1
- package/dist/utils/skills/cli-guidance.d.ts +0 -8
- package/dist/utils/skills/cli-guidance.d.ts.map +1 -1
- package/dist/utils/skills/cli-guidance.js +0 -8
- package/dist/utils/skills/cli-guidance.js.map +1 -1
- package/dist/utils/skills/cursor-rules.d.ts +0 -6
- package/dist/utils/skills/cursor-rules.d.ts.map +1 -1
- package/dist/utils/skills/cursor-rules.js +0 -6
- package/dist/utils/skills/cursor-rules.js.map +1 -1
- package/dist/utils/variantPreviewPort.d.ts +0 -3
- package/dist/utils/variantPreviewPort.d.ts.map +1 -1
- package/dist/utils/variantPreviewPort.js +0 -3
- package/dist/utils/variantPreviewPort.js.map +1 -1
- package/dist/utils/windowPlacement.d.ts +0 -4
- package/dist/utils/windowPlacement.d.ts.map +1 -1
- package/dist/utils/windowPlacement.js +0 -4
- package/dist/utils/windowPlacement.js.map +1 -1
- package/package.json +15 -14
- package/scripts/verify-node-version.cjs +0 -3
- package/src/ui/dist/assets/{LibraryApp-PQNnarmo.js → LibraryApp-x6PbzlGr.js} +1 -1
- package/src/ui/dist/assets/{main-C7hdsU-C.js → main-BW1Odsqp.js} +76 -76
- package/src/ui/dist/assets/main-DDQRZm89.css +1 -0
- package/src/ui/dist/index.html +2 -2
- package/dist/utils/skills/describe-motion-protocol.d.ts +0 -10
- package/dist/utils/skills/describe-motion-protocol.d.ts.map +0 -1
- package/dist/utils/skills/describe-motion-protocol.js +0 -231
- package/dist/utils/skills/describe-motion-protocol.js.map +0 -1
- 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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
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.
|
|
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,
|
|
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
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
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
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
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
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
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
|
-
...
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5412
|
-
|
|
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, '"')
|
|
6216
5020
|
.replace(/'/g, ''');
|
|
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
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
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';
|