rivet-design 0.19.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -1
- package/README.npm.md +13 -1
- package/dist/agent-variants/PreviewRuntime.d.ts +100 -0
- package/dist/agent-variants/PreviewRuntime.d.ts.map +1 -0
- package/dist/agent-variants/PreviewRuntime.js +577 -0
- package/dist/agent-variants/PreviewRuntime.js.map +1 -0
- package/dist/agent-variants/WorkspaceInventory.d.ts +81 -0
- package/dist/agent-variants/WorkspaceInventory.d.ts.map +1 -0
- package/dist/agent-variants/WorkspaceInventory.js +172 -0
- package/dist/agent-variants/WorkspaceInventory.js.map +1 -0
- package/dist/agent-variants/WorkspaceProvisioning.d.ts +40 -0
- package/dist/agent-variants/WorkspaceProvisioning.d.ts.map +1 -0
- package/dist/agent-variants/WorkspaceProvisioning.js +419 -0
- package/dist/agent-variants/WorkspaceProvisioning.js.map +1 -0
- package/dist/agent-variants/WorktreeOrchestrator.d.ts +9 -178
- package/dist/agent-variants/WorktreeOrchestrator.d.ts.map +1 -1
- package/dist/agent-variants/WorktreeOrchestrator.js +190 -2213
- package/dist/agent-variants/WorktreeOrchestrator.js.map +1 -1
- package/dist/agent-variants/designContextArtifacts.d.ts +25 -0
- package/dist/agent-variants/designContextArtifacts.d.ts.map +1 -0
- package/dist/agent-variants/designContextArtifacts.js +471 -0
- package/dist/agent-variants/designContextArtifacts.js.map +1 -0
- package/dist/agent-variants/variant-project-runtime.d.ts +40 -0
- package/dist/agent-variants/variant-project-runtime.d.ts.map +1 -0
- package/dist/agent-variants/variant-project-runtime.js +3 -0
- package/dist/agent-variants/variant-project-runtime.js.map +1 -0
- package/dist/agent-variants/variantWorkspaceFiles.d.ts +44 -0
- package/dist/agent-variants/variantWorkspaceFiles.d.ts.map +1 -0
- package/dist/agent-variants/variantWorkspaceFiles.js +426 -0
- package/dist/agent-variants/variantWorkspaceFiles.js.map +1 -0
- package/dist/agent-variants/variantsTelemetry.d.ts +189 -0
- package/dist/agent-variants/variantsTelemetry.d.ts.map +1 -0
- package/dist/agent-variants/variantsTelemetry.js +160 -0
- package/dist/agent-variants/variantsTelemetry.js.map +1 -0
- package/dist/cli/commandNames.d.ts.map +1 -1
- package/dist/cli/commandNames.js +1 -0
- package/dist/cli/commandNames.js.map +1 -1
- package/dist/cli/commands/libraryOpen.d.ts.map +1 -1
- package/dist/cli/commands/libraryOpen.js +7 -4
- package/dist/cli/commands/libraryOpen.js.map +1 -1
- package/dist/cli/commands/profile.d.ts.map +1 -1
- package/dist/cli/commands/profile.js +19 -2
- package/dist/cli/commands/profile.js.map +1 -1
- package/dist/cli/commands/serve.d.ts.map +1 -1
- package/dist/cli/commands/serve.js +14 -0
- package/dist/cli/commands/serve.js.map +1 -1
- package/dist/cli/commands/variants.d.ts.map +1 -1
- package/dist/cli/commands/variants.js +86 -9
- package/dist/cli/commands/variants.js.map +1 -1
- package/dist/cli/commands/version.d.ts +3 -0
- package/dist/cli/commands/version.d.ts.map +1 -0
- package/dist/cli/commands/version.js +29 -0
- package/dist/cli/commands/version.js.map +1 -0
- package/dist/cli/router.d.ts.map +1 -1
- package/dist/cli/router.js +15 -3
- package/dist/cli/router.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/install/automaticCliUpdate.d.ts +8 -0
- package/dist/install/automaticCliUpdate.d.ts.map +1 -1
- package/dist/install/automaticCliUpdate.js +7 -3
- package/dist/install/automaticCliUpdate.js.map +1 -1
- package/dist/library/BrowserSessions.d.ts +13 -0
- package/dist/library/BrowserSessions.d.ts.map +1 -0
- package/dist/library/BrowserSessions.js +62 -0
- package/dist/library/BrowserSessions.js.map +1 -0
- package/dist/library/HubClient.d.ts.map +1 -1
- package/dist/library/HubClient.js +30 -5
- package/dist/library/HubClient.js.map +1 -1
- package/dist/library/HubServer.d.ts +2 -1
- package/dist/library/HubServer.d.ts.map +1 -1
- package/dist/library/HubServer.js +6 -2
- package/dist/library/HubServer.js.map +1 -1
- package/dist/library/browserLaunch.d.ts +6 -0
- package/dist/library/browserLaunch.d.ts.map +1 -0
- package/dist/library/browserLaunch.js +17 -0
- package/dist/library/browserLaunch.js.map +1 -0
- package/dist/library/http.d.ts +1 -1
- package/dist/library/http.d.ts.map +1 -1
- package/dist/library/http.js +2 -2
- package/dist/library/http.js.map +1 -1
- package/dist/library/hubPort.d.ts +2 -0
- package/dist/library/hubPort.d.ts.map +1 -0
- package/dist/library/hubPort.js +23 -0
- package/dist/library/hubPort.js.map +1 -0
- package/dist/routes/library/app.d.ts.map +1 -1
- package/dist/routes/library/app.js +13 -1
- package/dist/routes/library/app.js.map +1 -1
- package/dist/routes/library/browserAccess.d.ts +8 -0
- package/dist/routes/library/browserAccess.d.ts.map +1 -0
- package/dist/routes/library/browserAccess.js +71 -0
- package/dist/routes/library/browserAccess.js.map +1 -0
- package/dist/routes/library/index.d.ts.map +1 -1
- package/dist/routes/library/index.js +10 -4
- package/dist/routes/library/index.js.map +1 -1
- package/dist/routes/library/middleware.d.ts +4 -2
- package/dist/routes/library/middleware.d.ts.map +1 -1
- package/dist/routes/library/middleware.js +26 -3
- package/dist/routes/library/middleware.js.map +1 -1
- package/dist/routes/library/profiles.d.ts.map +1 -1
- package/dist/routes/library/profiles.js +6 -0
- package/dist/routes/library/profiles.js.map +1 -1
- package/dist/routes/library/types.d.ts +2 -0
- package/dist/routes/library/types.d.ts.map +1 -1
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +31 -5
- package/dist/server.js.map +1 -1
- package/dist/services/AuthService.d.ts +72 -13
- package/dist/services/AuthService.d.ts.map +1 -1
- package/dist/services/AuthService.js +57 -13
- package/dist/services/AuthService.js.map +1 -1
- package/dist/services/BrowserAgentClient.d.ts.map +1 -1
- package/dist/services/BrowserAgentClient.js +18 -3
- package/dist/services/BrowserAgentClient.js.map +1 -1
- package/dist/services/DependencyPreparationService.d.ts +1 -0
- package/dist/services/DependencyPreparationService.d.ts.map +1 -1
- package/dist/services/DependencyPreparationService.js.map +1 -1
- package/dist/services/HistoryReplayService.d.ts.map +1 -1
- package/dist/services/HistoryReplayService.js +1 -0
- package/dist/services/HistoryReplayService.js.map +1 -1
- package/dist/services/IntegrationsClient.d.ts.map +1 -1
- package/dist/services/IntegrationsClient.js +121 -5
- package/dist/services/IntegrationsClient.js.map +1 -1
- package/dist/services/ProjectLibrary.d.ts +6 -1
- package/dist/services/ProjectLibrary.d.ts.map +1 -1
- package/dist/services/ProjectLibrary.js +20 -4
- package/dist/services/ProjectLibrary.js.map +1 -1
- package/dist/services/ProjectPackageManager.d.ts +1 -0
- package/dist/services/ProjectPackageManager.d.ts.map +1 -1
- package/dist/services/ProjectPackageManager.js +24 -8
- package/dist/services/ProjectPackageManager.js.map +1 -1
- package/dist/services/PrototypeDeployService.d.ts.map +1 -1
- package/dist/services/PrototypeDeployService.js +8 -3
- package/dist/services/PrototypeDeployService.js.map +1 -1
- package/dist/services/TelemetryService.d.ts +2 -2
- package/dist/services/TelemetryService.d.ts.map +1 -1
- package/dist/services/TelemetryService.js +2 -2
- package/dist/services/TelemetryService.js.map +1 -1
- package/dist/services/VariantHistoryService.d.ts +9 -0
- package/dist/services/VariantHistoryService.d.ts.map +1 -1
- package/dist/services/VariantHistoryService.js +11 -2
- package/dist/services/VariantHistoryService.js.map +1 -1
- package/dist/services/accessTokenRefresh.d.ts.map +1 -1
- package/dist/services/accessTokenRefresh.js +12 -4
- package/dist/services/accessTokenRefresh.js.map +1 -1
- package/dist/services/profiles/DesignProfiles.d.ts +2 -1
- package/dist/services/profiles/DesignProfiles.d.ts.map +1 -1
- package/dist/services/profiles/DesignProfiles.js +45 -6
- package/dist/services/profiles/DesignProfiles.js.map +1 -1
- package/dist/services/profiles/ProfileIndex.d.ts +8 -0
- package/dist/services/profiles/ProfileIndex.d.ts.map +1 -1
- package/dist/services/profiles/ProfileIndex.js +46 -8
- package/dist/services/profiles/ProfileIndex.js.map +1 -1
- package/dist/services/profiles/profileSearch.d.ts +15 -0
- package/dist/services/profiles/profileSearch.d.ts.map +1 -0
- package/dist/services/profiles/profileSearch.js +41 -0
- package/dist/services/profiles/profileSearch.js.map +1 -0
- package/dist/types/design-library.d.ts +38 -0
- package/dist/types/design-library.d.ts.map +1 -1
- package/dist/types/design-library.js +8 -1
- package/dist/types/design-library.js.map +1 -1
- package/dist/utils/skills/claude-skill.d.ts +1 -1
- package/dist/utils/skills/claude-skill.d.ts.map +1 -1
- package/dist/utils/skills/claude-skill.js +1 -1
- package/dist/utils/skills/claude-skill.js.map +1 -1
- package/dist/utils/skills/cli-guidance.d.ts +2 -2
- package/dist/utils/skills/cli-guidance.d.ts.map +1 -1
- package/dist/utils/skills/cli-guidance.js +13 -8
- package/dist/utils/skills/cli-guidance.js.map +1 -1
- package/dist/utils/skills/codex-skill.d.ts +1 -1
- package/dist/utils/skills/codex-skill.d.ts.map +1 -1
- package/dist/utils/skills/codex-skill.js +6 -4
- package/dist/utils/skills/codex-skill.js.map +1 -1
- package/dist/utils/skills/cursor-rules.d.ts +1 -1
- package/dist/utils/skills/cursor-rules.js +1 -1
- package/package.json +1 -1
- package/src/ui/dist/assets/LibraryEntry-Db0yQPRU.js +36 -0
- package/src/ui/dist/assets/main-BAQFukSb.js +435 -0
- package/src/ui/dist/assets/main-Dcp0uKa5.css +1 -0
- package/src/ui/dist/index.html +2 -2
- package/src/ui/dist/assets/LibraryApp-B4ztVk52.js +0 -36
- package/src/ui/dist/assets/main-CAPEQf84.js +0 -438
- package/src/ui/dist/assets/main-Ky91HHF_.css +0 -1
|
@@ -3,19 +3,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.AgentVariantsOrchestrator = void 0;
|
|
7
|
-
exports.buildStaticPreviewDocument = buildStaticPreviewDocument;
|
|
6
|
+
exports.AgentVariantsOrchestrator = exports.buildStaticPreviewDocument = void 0;
|
|
8
7
|
const crypto_1 = require("crypto");
|
|
9
8
|
const events_1 = require("events");
|
|
10
9
|
const fs_1 = __importDefault(require("fs"));
|
|
11
10
|
const os_1 = __importDefault(require("os"));
|
|
12
11
|
const path_1 = __importDefault(require("path"));
|
|
13
12
|
const DependencyPreparationService_1 = require("../services/DependencyPreparationService");
|
|
14
|
-
const ProjectPackageManager_1 = require("../services/ProjectPackageManager");
|
|
15
|
-
const devServerCommand_1 = require("../utils/devServerCommand");
|
|
16
13
|
const simple_git_1 = require("simple-git");
|
|
17
14
|
const logger_1 = require("../utils/logger");
|
|
18
|
-
const devServerError_1 = require("../utils/devServerError");
|
|
19
15
|
const errors_1 = require("./errors");
|
|
20
16
|
const createProjectArtifacts_1 = require("./createProjectArtifacts");
|
|
21
17
|
const contracts_1 = require("./contracts");
|
|
@@ -23,15 +19,18 @@ const workItemBriefing_1 = require("./workItemBriefing");
|
|
|
23
19
|
const briefContent_1 = require("./briefContent");
|
|
24
20
|
const runLabel_1 = require("./runLabel");
|
|
25
21
|
const runPlan_1 = require("./runPlan");
|
|
26
|
-
const viteReactTs_1 = require("../services/templates/viteReactTs");
|
|
27
|
-
const StaticPreviewServer_1 = require("../services/StaticPreviewServer");
|
|
28
|
-
const designCatalog_1 = require("../services/templates/designCatalog");
|
|
29
22
|
const previewQa_1 = require("./previewQa");
|
|
30
|
-
const
|
|
23
|
+
const WorkspaceInventory_1 = require("./WorkspaceInventory");
|
|
24
|
+
const WorkspaceProvisioning_1 = require("./WorkspaceProvisioning");
|
|
25
|
+
const PreviewRuntime_1 = require("./PreviewRuntime");
|
|
31
26
|
const diffQa_1 = require("./diffQa");
|
|
32
27
|
const VariantHistoryService_1 = require("../services/VariantHistoryService");
|
|
33
28
|
const variantContext_1 = require("./variantContext");
|
|
34
|
-
const
|
|
29
|
+
const designContextArtifacts_1 = require("./designContextArtifacts");
|
|
30
|
+
const variantsTelemetry_1 = require("./variantsTelemetry");
|
|
31
|
+
const variantWorkspaceFiles_1 = require("./variantWorkspaceFiles");
|
|
32
|
+
var variantWorkspaceFiles_2 = require("./variantWorkspaceFiles");
|
|
33
|
+
Object.defineProperty(exports, "buildStaticPreviewDocument", { enumerable: true, get: function () { return variantWorkspaceFiles_2.buildStaticPreviewDocument; } });
|
|
35
34
|
const log = (0, logger_1.createLogger)('AgentVariantsOrchestrator');
|
|
36
35
|
const FRESH_DEV_SERVER_HOST = '127.0.0.1';
|
|
37
36
|
const BRIEF_LABEL_MAX_LENGTH = 48;
|
|
@@ -65,24 +64,9 @@ const BRIEF_LABEL_ALTERNATIVES = [
|
|
|
65
64
|
'Density',
|
|
66
65
|
'Color',
|
|
67
66
|
];
|
|
68
|
-
const FALLBACK_DIRECTION_FOCI = [
|
|
69
|
-
'Emphasize hierarchy and one unmistakable primary action.',
|
|
70
|
-
'Use stronger contrast, scale shifts, and a more expressive visual system.',
|
|
71
|
-
'Favor a quieter, spacious composition with restrained supporting detail.',
|
|
72
|
-
'Make information density and fast scanning the organizing principle.',
|
|
73
|
-
'Lead with imagery and let content frame the visual story.',
|
|
74
|
-
'Use modular cards and clear grouping to expose the product structure.',
|
|
75
|
-
'Explore tactile depth, layering, and purposeful interaction cues.',
|
|
76
|
-
'Reduce the concept to its most essential typography and geometry.',
|
|
77
|
-
];
|
|
78
67
|
const SETTLE_PERSISTS_TEARDOWN_TIMEOUT_MS = 20_000;
|
|
79
68
|
const RECIPE_PORT_SENTINEL = 65535;
|
|
80
|
-
const FRESH_DEV_SERVER_DEFAULT_PORT = 5173;
|
|
81
|
-
const DEV_SERVER_START_MAX_ATTEMPTS = 3;
|
|
82
|
-
const DEV_SERVER_MAX_AUTO_RESTARTS = 2;
|
|
83
69
|
const DEV_SERVER_RESTART_DELAY_MS = 1_000;
|
|
84
|
-
const DESIGN_CONTEXT_ROUTE_SEGMENT = 'design-md';
|
|
85
|
-
const DESIGN_CONTEXT_VIEW_SEGMENT = 'view';
|
|
86
70
|
const DEFAULT_PROVISION_TIMEOUT_MS = 540_000;
|
|
87
71
|
const PROVISION_TIMEOUT_MS = (() => {
|
|
88
72
|
const raw = process.env.RIVET_VARIANTS_PROVISION_TIMEOUT_MS;
|
|
@@ -99,142 +83,6 @@ const ACTIVE_VARIANT_SERVER_IDLE_TTL_MS = (() => {
|
|
|
99
83
|
? parsed
|
|
100
84
|
: DEFAULT_ACTIVE_VARIANT_SERVER_IDLE_TTL_MS;
|
|
101
85
|
})();
|
|
102
|
-
class ProvisionTimeoutError extends Error {
|
|
103
|
-
timeoutMs;
|
|
104
|
-
constructor(timeoutMs) {
|
|
105
|
-
super(`Worktree provisioning timed out after ${timeoutMs}ms`);
|
|
106
|
-
this.timeoutMs = timeoutMs;
|
|
107
|
-
this.name = 'ProvisionTimeoutError';
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
function withProvisionTimeout(work, timeoutMs) {
|
|
111
|
-
let timer;
|
|
112
|
-
const timeout = new Promise((_, reject) => {
|
|
113
|
-
timer = setTimeout(() => reject(new ProvisionTimeoutError(timeoutMs)), timeoutMs);
|
|
114
|
-
if (typeof timer.unref === 'function')
|
|
115
|
-
timer.unref();
|
|
116
|
-
});
|
|
117
|
-
return Promise.race([work, timeout]).finally(() => clearTimeout(timer));
|
|
118
|
-
}
|
|
119
|
-
const ALLOWED_INERT_ASSET_EXTENSIONS = new Set([
|
|
120
|
-
'.glb',
|
|
121
|
-
'.gltf',
|
|
122
|
-
'.obj',
|
|
123
|
-
'.fbx',
|
|
124
|
-
'.usdz',
|
|
125
|
-
'.png',
|
|
126
|
-
'.jpg',
|
|
127
|
-
'.jpeg',
|
|
128
|
-
'.gif',
|
|
129
|
-
'.webp',
|
|
130
|
-
'.svg',
|
|
131
|
-
'.avif',
|
|
132
|
-
'.bmp',
|
|
133
|
-
'.ico',
|
|
134
|
-
'.mp4',
|
|
135
|
-
'.webm',
|
|
136
|
-
'.mov',
|
|
137
|
-
'.mp3',
|
|
138
|
-
'.wav',
|
|
139
|
-
'.ogg',
|
|
140
|
-
'.m4a',
|
|
141
|
-
'.woff',
|
|
142
|
-
'.woff2',
|
|
143
|
-
'.ttf',
|
|
144
|
-
'.otf',
|
|
145
|
-
'.eot',
|
|
146
|
-
'.pdf',
|
|
147
|
-
]);
|
|
148
|
-
const SENSITIVE_PATH_SEGMENTS = new Set([
|
|
149
|
-
'.ssh',
|
|
150
|
-
'.aws',
|
|
151
|
-
'.gnupg',
|
|
152
|
-
'.kube',
|
|
153
|
-
'.docker',
|
|
154
|
-
'.config',
|
|
155
|
-
'.npm',
|
|
156
|
-
'.cargo',
|
|
157
|
-
'.gem',
|
|
158
|
-
'.netrc',
|
|
159
|
-
'private',
|
|
160
|
-
'secrets',
|
|
161
|
-
'credentials',
|
|
162
|
-
]);
|
|
163
|
-
function hasSensitivePathSegment(absolutePath) {
|
|
164
|
-
const segments = absolutePath.split(/[/\\]/).filter(Boolean);
|
|
165
|
-
return segments.some((segment) => SENSITIVE_PATH_SEGMENTS.has(segment.toLowerCase()));
|
|
166
|
-
}
|
|
167
|
-
function isPathWithinRoot(absolutePath, root) {
|
|
168
|
-
const relative = path_1.default.relative(root, absolutePath);
|
|
169
|
-
return (relative === '' ||
|
|
170
|
-
(relative.length > 0 &&
|
|
171
|
-
!relative.startsWith('..') &&
|
|
172
|
-
!path_1.default.isAbsolute(relative)));
|
|
173
|
-
}
|
|
174
|
-
function copyAssetIntoWorktree(worktreePath, entry, assetSourceRoot) {
|
|
175
|
-
if (!assetSourceRoot) {
|
|
176
|
-
throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `assetPlan.source requires an approved asset root before copying '${entry.source}'`);
|
|
177
|
-
}
|
|
178
|
-
if (!path_1.default.isAbsolute(assetSourceRoot)) {
|
|
179
|
-
throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `assetSourceRoot must be an absolute path, got '${assetSourceRoot}'`);
|
|
180
|
-
}
|
|
181
|
-
const absoluteSource = path_1.default.isAbsolute(entry.source)
|
|
182
|
-
? entry.source
|
|
183
|
-
: path_1.default.resolve(assetSourceRoot, entry.source);
|
|
184
|
-
const ext = path_1.default.extname(absoluteSource).toLowerCase();
|
|
185
|
-
if (!ALLOWED_INERT_ASSET_EXTENSIONS.has(ext)) {
|
|
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.`);
|
|
187
|
-
}
|
|
188
|
-
let lstat;
|
|
189
|
-
try {
|
|
190
|
-
lstat = fs_1.default.lstatSync(absoluteSource);
|
|
191
|
-
}
|
|
192
|
-
catch {
|
|
193
|
-
throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `assetPlan.source not found on disk: ${entry.source}`);
|
|
194
|
-
}
|
|
195
|
-
if (lstat.isSymbolicLink()) {
|
|
196
|
-
throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `assetPlan.source must not be a symlink: ${entry.source}`);
|
|
197
|
-
}
|
|
198
|
-
if (!lstat.isFile()) {
|
|
199
|
-
throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `assetPlan.source must be a regular file: ${entry.source}`);
|
|
200
|
-
}
|
|
201
|
-
let resolvedSource;
|
|
202
|
-
try {
|
|
203
|
-
resolvedSource = fs_1.default.realpathSync(absoluteSource);
|
|
204
|
-
}
|
|
205
|
-
catch {
|
|
206
|
-
throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `assetPlan.source could not be resolved: ${entry.source}`);
|
|
207
|
-
}
|
|
208
|
-
let resolvedAssetSourceRoot;
|
|
209
|
-
try {
|
|
210
|
-
resolvedAssetSourceRoot = fs_1.default.realpathSync(assetSourceRoot);
|
|
211
|
-
}
|
|
212
|
-
catch {
|
|
213
|
-
throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `assetSourceRoot could not be resolved: ${assetSourceRoot}`);
|
|
214
|
-
}
|
|
215
|
-
const resolvedStat = fs_1.default.statSync(resolvedSource);
|
|
216
|
-
if (!resolvedStat.isFile()) {
|
|
217
|
-
throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `assetPlan.source resolved target must be a regular file: ${entry.source}`);
|
|
218
|
-
}
|
|
219
|
-
if (!isPathWithinRoot(resolvedSource, resolvedAssetSourceRoot)) {
|
|
220
|
-
throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `assetPlan.source must stay inside the approved asset root: ${entry.source}`);
|
|
221
|
-
}
|
|
222
|
-
const relativeResolvedSource = path_1.default.relative(resolvedAssetSourceRoot, resolvedSource);
|
|
223
|
-
if (hasSensitivePathSegment(relativeResolvedSource)) {
|
|
224
|
-
throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `assetPlan.source traverses a sensitive directory and is refused: ${entry.source}`);
|
|
225
|
-
}
|
|
226
|
-
const resolvedExt = path_1.default.extname(resolvedSource).toLowerCase();
|
|
227
|
-
if (!ALLOWED_INERT_ASSET_EXTENSIONS.has(resolvedExt)) {
|
|
228
|
-
throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `assetPlan.source resolved extension '${resolvedExt || '(none)'}' is not on the allowlist (resolved from '${entry.source}').`);
|
|
229
|
-
}
|
|
230
|
-
const normalizedDest = path_1.default.normalize(entry.destination);
|
|
231
|
-
if (normalizedDest.startsWith('..') || path_1.default.isAbsolute(normalizedDest)) {
|
|
232
|
-
throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `assetPlan.destination must stay inside the worktree, got '${entry.destination}'`);
|
|
233
|
-
}
|
|
234
|
-
const absDest = path_1.default.join(worktreePath, normalizedDest);
|
|
235
|
-
fs_1.default.mkdirSync(path_1.default.dirname(absDest), { recursive: true });
|
|
236
|
-
fs_1.default.copyFileSync(resolvedSource, absDest);
|
|
237
|
-
}
|
|
238
86
|
const defaultPreviewQaRunner = ({ html }) => (0, previewQa_1.runPreviewQa)({ html });
|
|
239
87
|
const buildViewDisabledReason = (args) => {
|
|
240
88
|
if (args.status === 'failed') {
|
|
@@ -248,7 +96,6 @@ const buildViewDisabledReason = (args) => {
|
|
|
248
96
|
}
|
|
249
97
|
return 'Variant is still running';
|
|
250
98
|
};
|
|
251
|
-
const NOOP_TELEMETRY = { track: () => undefined };
|
|
252
99
|
const isImplementationWorkItemKind = (kind) => kind === 'static_preview' || kind === 'refine_variant' || kind === 'code_gen';
|
|
253
100
|
const DEFAULT_EXECUTION_MODE_BY_KIND = {
|
|
254
101
|
static_preview: 'create',
|
|
@@ -350,41 +197,68 @@ class AgentVariantsOrchestrator {
|
|
|
350
197
|
installDependencies;
|
|
351
198
|
materializeProject;
|
|
352
199
|
previewQaRunner;
|
|
353
|
-
switchPreviewPort;
|
|
354
|
-
setCommittedDevServerHealth;
|
|
355
200
|
variantHistory;
|
|
356
|
-
startStaticPreviewServerImpl;
|
|
357
201
|
provisionTimeoutMs;
|
|
358
202
|
activeVariantServerIdleTtlMs;
|
|
359
203
|
devServerRestartDelayMs;
|
|
360
204
|
resources = new Map();
|
|
361
|
-
|
|
205
|
+
workspaceInventory;
|
|
206
|
+
workspaceProvisioning;
|
|
207
|
+
previewRuntime;
|
|
362
208
|
inflightCommits = new Map();
|
|
363
209
|
activeSessionId = null;
|
|
364
210
|
events = new events_1.EventEmitter();
|
|
365
211
|
constructor(deps) {
|
|
366
212
|
this.store = deps.store;
|
|
367
213
|
this.worktrees = deps.worktreeManager;
|
|
214
|
+
this.workspaceInventory = new WorkspaceInventory_1.WorkspaceInventory({
|
|
215
|
+
worktreeManager: deps.worktreeManager,
|
|
216
|
+
});
|
|
368
217
|
this.resolveEnv = deps.resolveProjectEnvironment;
|
|
369
|
-
this.telemetry = deps.telemetry ?? NOOP_TELEMETRY;
|
|
218
|
+
this.telemetry = deps.telemetry ?? variantsTelemetry_1.NOOP_TELEMETRY;
|
|
370
219
|
this.installDependencies =
|
|
371
|
-
deps.installDependencies ?? defaultInstallDependencies;
|
|
220
|
+
deps.installDependencies ?? variantWorkspaceFiles_1.defaultInstallDependencies;
|
|
372
221
|
this.materializeProject =
|
|
373
|
-
deps.materializeProject ?? defaultMaterializeProject;
|
|
222
|
+
deps.materializeProject ?? variantWorkspaceFiles_1.defaultMaterializeProject;
|
|
374
223
|
this.previewQaRunner = deps.previewQaRunner ?? defaultPreviewQaRunner;
|
|
375
|
-
this.switchPreviewPort = deps.switchPreviewPort;
|
|
376
|
-
this.setCommittedDevServerHealth = deps.setCommittedDevServerHealth;
|
|
377
224
|
this.variantHistory = deps.variantHistory ?? new VariantHistoryService_1.VariantHistoryService();
|
|
378
|
-
this.startStaticPreviewServerImpl =
|
|
379
|
-
deps.startStaticPreviewServer ?? StaticPreviewServer_1.startStaticPreviewServer;
|
|
380
225
|
this.provisionTimeoutMs = deps.provisionTimeoutMs ?? PROVISION_TIMEOUT_MS;
|
|
226
|
+
this.workspaceProvisioning = new WorkspaceProvisioning_1.WorkspaceProvisioning({
|
|
227
|
+
store: deps.store,
|
|
228
|
+
inventory: this.workspaceInventory,
|
|
229
|
+
worktreeManager: deps.worktreeManager,
|
|
230
|
+
telemetry: deps.telemetry ?? variantsTelemetry_1.NOOP_TELEMETRY,
|
|
231
|
+
resolveProjectEnvironment: deps.resolveProjectEnvironment,
|
|
232
|
+
installDependencies: deps.installDependencies ?? variantWorkspaceFiles_1.defaultInstallDependencies,
|
|
233
|
+
provisionTimeoutMs: this.provisionTimeoutMs,
|
|
234
|
+
cancelSession: (sessionId) => this.cancel({ sessionId, reason: 'provisioning_failed' }),
|
|
235
|
+
changed: () => this.emitChange(),
|
|
236
|
+
isSessionLive: (sessionId) => this.resources.has(sessionId),
|
|
237
|
+
});
|
|
381
238
|
this.activeVariantServerIdleTtlMs =
|
|
382
239
|
deps.activeVariantServerIdleTtlMs ?? ACTIVE_VARIANT_SERVER_IDLE_TTL_MS;
|
|
383
240
|
this.devServerRestartDelayMs =
|
|
384
241
|
deps.devServerRestartDelayMs ?? DEV_SERVER_RESTART_DELAY_MS;
|
|
242
|
+
this.previewRuntime = new PreviewRuntime_1.PreviewRuntime({
|
|
243
|
+
worktreeManager: deps.worktreeManager,
|
|
244
|
+
resolveProjectEnvironment: deps.resolveProjectEnvironment,
|
|
245
|
+
getProjectContext: (sessionId) => this.store.getProjectContext(sessionId),
|
|
246
|
+
getDependencySignal: (sessionId) => this.workspaceInventory.getDependencySignal(sessionId),
|
|
247
|
+
installDependencies: this.installDependencies,
|
|
248
|
+
telemetry: this.telemetry,
|
|
249
|
+
isSessionLive: (sessionId) => this.resources.has(sessionId),
|
|
250
|
+
isCleanupStarted: (sessionId) => this.workspaceInventory.isCleanupStarted(sessionId),
|
|
251
|
+
hasUiClients: () => this.uiClientCount > 0,
|
|
252
|
+
changed: () => this.emitChange(),
|
|
253
|
+
switchPreviewPort: deps.switchPreviewPort,
|
|
254
|
+
setCommittedDevServerHealth: deps.setCommittedDevServerHealth,
|
|
255
|
+
startStaticPreviewServer: deps.startStaticPreviewServer,
|
|
256
|
+
activeVariantServerIdleTtlMs: this.activeVariantServerIdleTtlMs,
|
|
257
|
+
devServerRestartDelayMs: this.devServerRestartDelayMs,
|
|
258
|
+
});
|
|
385
259
|
}
|
|
386
260
|
propose(args) {
|
|
387
|
-
void this.
|
|
261
|
+
void this.previewRuntime.stopLingeringCommitted().catch((err) => {
|
|
388
262
|
log.warn('Stopping lingering committed dev servers failed', err);
|
|
389
263
|
});
|
|
390
264
|
const result = this.store.propose(args);
|
|
@@ -470,9 +344,9 @@ class AgentVariantsOrchestrator {
|
|
|
470
344
|
}
|
|
471
345
|
touchSession(sessionId) {
|
|
472
346
|
const resources = this.resources.get(sessionId);
|
|
473
|
-
if (!resources ||
|
|
347
|
+
if (!resources || this.workspaceInventory.isCleanupStarted(sessionId))
|
|
474
348
|
return;
|
|
475
|
-
this.
|
|
349
|
+
this.previewRuntime.touch(sessionId);
|
|
476
350
|
}
|
|
477
351
|
buildActiveSnapshot() {
|
|
478
352
|
const sessionId = this.activeSessionId;
|
|
@@ -490,11 +364,11 @@ class AgentVariantsOrchestrator {
|
|
|
490
364
|
: null;
|
|
491
365
|
const variants = this.getVariants(sessionId);
|
|
492
366
|
const sessionProjectContext = this.store.getProjectContext(sessionId);
|
|
493
|
-
const projectContext = toActiveProjectContext(sessionProjectContext);
|
|
367
|
+
const projectContext = (0, designContextArtifacts_1.toActiveProjectContext)(sessionProjectContext);
|
|
494
368
|
const destinationPath = projectContext.kind === 'fresh'
|
|
495
369
|
? projectContext.workspacePath
|
|
496
370
|
: undefined;
|
|
497
|
-
const artifacts = buildSessionArtifacts(sessionId, sessionProjectContext);
|
|
371
|
+
const artifacts = (0, designContextArtifacts_1.buildSessionArtifacts)(sessionId, sessionProjectContext);
|
|
498
372
|
const hostWork = this.getHostWork(sessionId);
|
|
499
373
|
const workerRuntime = this.store.getWorkerRuntime(sessionId);
|
|
500
374
|
return {
|
|
@@ -547,14 +421,8 @@ class AgentVariantsOrchestrator {
|
|
|
547
421
|
return planningItem;
|
|
548
422
|
}
|
|
549
423
|
const input = (item.input ?? {});
|
|
550
|
-
const workspace = this.
|
|
551
|
-
|
|
552
|
-
workItemId: item.workId,
|
|
553
|
-
kind,
|
|
554
|
-
});
|
|
555
|
-
const contextAssets = this.resources
|
|
556
|
-
.get(sessionId)
|
|
557
|
-
?.contextAssets.get(item.workId);
|
|
424
|
+
const workspace = this.workspaceInventory.requireAssignedWorkspace(sessionId, item.workId, kind);
|
|
425
|
+
const contextAssets = this.workspaceInventory.getContextAssets(sessionId, item.workId);
|
|
558
426
|
const mode = input.sourceVariantId
|
|
559
427
|
? 'vary'
|
|
560
428
|
: DEFAULT_EXECUTION_MODE_BY_KIND[kind];
|
|
@@ -601,16 +469,6 @@ class AgentVariantsOrchestrator {
|
|
|
601
469
|
return implementationItem;
|
|
602
470
|
});
|
|
603
471
|
}
|
|
604
|
-
getAssignedWorkspace(args) {
|
|
605
|
-
const workspace = this.resources
|
|
606
|
-
.get(args.sessionId)
|
|
607
|
-
?.workspaceManager.getAssignedWorkspace(args.workItemId);
|
|
608
|
-
const expectedKind = args.kind === 'code_gen' ? 'git_worktree' : 'artifact_directory';
|
|
609
|
-
if (!workspace || workspace.kind !== expectedKind) {
|
|
610
|
-
throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `Variant ${args.workItemId} has no assigned ${expectedKind} workspace`);
|
|
611
|
-
}
|
|
612
|
-
return workspace;
|
|
613
|
-
}
|
|
614
472
|
getVariantsTelemetryMode(projectContext, _target) {
|
|
615
473
|
if (projectContext.kind === 'fresh' && projectContext.variantContext) {
|
|
616
474
|
return 'source_grounded';
|
|
@@ -908,9 +766,9 @@ class AgentVariantsOrchestrator {
|
|
|
908
766
|
}
|
|
909
767
|
requestWork(args) {
|
|
910
768
|
const result = this.store.requestWork(args);
|
|
769
|
+
const resources = this.ensureResources(args.sessionId);
|
|
911
770
|
try {
|
|
912
|
-
this.
|
|
913
|
-
this.materializeContextAssets(args.sessionId, result.leasedWorkItems);
|
|
771
|
+
this.workspaceProvisioning.assignImplementationWorkspaces(args.sessionId, result.leasedWorkItems);
|
|
914
772
|
}
|
|
915
773
|
catch (error) {
|
|
916
774
|
for (const item of result.leasedWorkItems) {
|
|
@@ -937,12 +795,9 @@ class AgentVariantsOrchestrator {
|
|
|
937
795
|
}
|
|
938
796
|
throw error;
|
|
939
797
|
}
|
|
940
|
-
const
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
for (const item of result.leasedWorkItems) {
|
|
944
|
-
resources.leasedAt.set(item.id, now);
|
|
945
|
-
}
|
|
798
|
+
const now = Date.now();
|
|
799
|
+
for (const item of result.leasedWorkItems) {
|
|
800
|
+
resources.leasedAt.set(item.id, now);
|
|
946
801
|
}
|
|
947
802
|
this.writeLeasedWorkItemBriefings(args.sessionId, result.leasedWorkItems.map((item) => item.id));
|
|
948
803
|
this.trackRefinementLeased({ sessionId: args.sessionId, lease: result });
|
|
@@ -962,38 +817,6 @@ class AgentVariantsOrchestrator {
|
|
|
962
817
|
this.emitChange();
|
|
963
818
|
return decorated;
|
|
964
819
|
}
|
|
965
|
-
materializeContextAssets(sessionId, items) {
|
|
966
|
-
const resources = this.ensureResources(sessionId);
|
|
967
|
-
for (const item of items) {
|
|
968
|
-
if (!isImplementationWorkItemKind(item.kind))
|
|
969
|
-
continue;
|
|
970
|
-
const input = (item.input ?? {});
|
|
971
|
-
const assets = input.context?.contextAssets ?? [];
|
|
972
|
-
if (assets.length === 0)
|
|
973
|
-
continue;
|
|
974
|
-
const workspace = this.getAssignedWorkspace({
|
|
975
|
-
sessionId,
|
|
976
|
-
workItemId: item.id,
|
|
977
|
-
kind: item.kind,
|
|
978
|
-
});
|
|
979
|
-
const previewKind = workspace.kind === 'artifact_directory'
|
|
980
|
-
? 'static_artifact'
|
|
981
|
-
: (resources.contextAssetPreviewKind ?? 'dev_server');
|
|
982
|
-
resources.contextAssets.set(item.id, (0, contextAssets_1.materializeContextAssets)({ assets, workspace, previewKind }));
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
async resolveContextAssetPreviewKind(sessionId) {
|
|
986
|
-
const resources = this.ensureResources(sessionId);
|
|
987
|
-
try {
|
|
988
|
-
const environment = await this.resolveEnv(sessionId);
|
|
989
|
-
resources.contextAssetPreviewKind =
|
|
990
|
-
environment.framework === 'static' ? 'static_artifact' : 'dev_server';
|
|
991
|
-
}
|
|
992
|
-
catch (error) {
|
|
993
|
-
resources.contextAssetPreviewKind = 'dev_server';
|
|
994
|
-
log.warn(`Failed to resolve context asset preview kind for ${sessionId}; using dev-server references`, error);
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
820
|
writeLeasedWorkItemBriefings(sessionId, leasedWorkItemIds) {
|
|
998
821
|
if (leasedWorkItemIds.length === 0)
|
|
999
822
|
return;
|
|
@@ -1023,62 +846,6 @@ class AgentVariantsOrchestrator {
|
|
|
1023
846
|
}
|
|
1024
847
|
}
|
|
1025
848
|
}
|
|
1026
|
-
ensureStaticWorkspaces(sessionId, items) {
|
|
1027
|
-
const resources = this.ensureResources(sessionId);
|
|
1028
|
-
const projectContext = this.store.getProjectContext(sessionId);
|
|
1029
|
-
const provisionStartedAt = Date.now();
|
|
1030
|
-
let assignedCount = 0;
|
|
1031
|
-
for (const item of items) {
|
|
1032
|
-
if (item.kind !== 'static_preview' && item.kind !== 'refine_variant') {
|
|
1033
|
-
continue;
|
|
1034
|
-
}
|
|
1035
|
-
if (resources.workspaceManager.getAssignedWorkspace(item.id))
|
|
1036
|
-
continue;
|
|
1037
|
-
assignedCount += 1;
|
|
1038
|
-
const input = (item.input ?? {});
|
|
1039
|
-
const existingProjectPath = projectContext.kind === 'existing'
|
|
1040
|
-
? projectContext.projectPath
|
|
1041
|
-
: undefined;
|
|
1042
|
-
const existingParent = existingProjectPath && fs_1.default.existsSync(existingProjectPath)
|
|
1043
|
-
? path_1.default.join(existingProjectPath, '.rivet', 'variant-work', sessionId)
|
|
1044
|
-
: path_1.default.join(os_1.default.tmpdir(), 'rivet-variant-work', sessionId);
|
|
1045
|
-
const parent = projectContext.kind === 'fresh'
|
|
1046
|
-
? projectContext.workspacePath
|
|
1047
|
-
: existingParent;
|
|
1048
|
-
fs_1.default.mkdirSync(parent, { recursive: true });
|
|
1049
|
-
const rootPath = projectContext.kind === 'fresh'
|
|
1050
|
-
? path_1.default.join(parent, `.rivet-preview-${item.id}`)
|
|
1051
|
-
: path_1.default.join(parent, item.id);
|
|
1052
|
-
const sourceVariantId = input.sourceVariantId ?? input.variantId;
|
|
1053
|
-
const sourceDirectory = sourceVariantId
|
|
1054
|
-
? resources.staticPreviews.get(sourceVariantId)?.assetBase
|
|
1055
|
-
: undefined;
|
|
1056
|
-
resources.workspaceManager.assignArtifactWorkspace({
|
|
1057
|
-
workItemId: item.id,
|
|
1058
|
-
rootPath,
|
|
1059
|
-
allowedRoot: parent,
|
|
1060
|
-
entryPath: 'index.html',
|
|
1061
|
-
sourceDirectory,
|
|
1062
|
-
seedHtml: input.currentHtml,
|
|
1063
|
-
});
|
|
1064
|
-
}
|
|
1065
|
-
if (assignedCount > 0) {
|
|
1066
|
-
this.telemetry.track('agent_variants.provisioning_completed', {
|
|
1067
|
-
source: 'mcp',
|
|
1068
|
-
sessionId,
|
|
1069
|
-
kind: 'artifact_directory',
|
|
1070
|
-
variantCount: assignedCount,
|
|
1071
|
-
durationMs: Date.now() - provisionStartedAt,
|
|
1072
|
-
});
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
readAssignedStaticArtifact(sessionId, workItemId) {
|
|
1076
|
-
const workspaceManager = this.resources.get(sessionId)?.workspaceManager;
|
|
1077
|
-
if (!workspaceManager) {
|
|
1078
|
-
throw new errors_1.AgentVariantsError('SESSION_NOT_FOUND', `No live resources for session ${sessionId}`);
|
|
1079
|
-
}
|
|
1080
|
-
return workspaceManager.readArtifact(workItemId);
|
|
1081
|
-
}
|
|
1082
849
|
beginStaticPreviewRefinements(args) {
|
|
1083
850
|
const seenVariantIds = new Set();
|
|
1084
851
|
const droppedRefinements = [];
|
|
@@ -1215,8 +982,8 @@ class AgentVariantsOrchestrator {
|
|
|
1215
982
|
requestedCount: 1,
|
|
1216
983
|
sourceVariantId: args.source.variantId,
|
|
1217
984
|
});
|
|
1218
|
-
|
|
1219
|
-
|
|
985
|
+
this.ensureResources(args.sessionId);
|
|
986
|
+
this.workspaceInventory.setStaticPreview(args.sessionId, {
|
|
1220
987
|
workItemId: append.workItemId,
|
|
1221
988
|
briefId: append.briefId,
|
|
1222
989
|
entryPath: args.entryPath,
|
|
@@ -1263,7 +1030,7 @@ class AgentVariantsOrchestrator {
|
|
|
1263
1030
|
};
|
|
1264
1031
|
}
|
|
1265
1032
|
const { assetBase, hasAssets } = await this.resolveAdoptedStaticPreviewFiles(args.filesDir, args.entryPath);
|
|
1266
|
-
void this.
|
|
1033
|
+
void this.previewRuntime.stopLingeringCommitted().catch((err) => {
|
|
1267
1034
|
log.warn('Stopping lingering committed dev servers failed', err);
|
|
1268
1035
|
});
|
|
1269
1036
|
const created = this.store.createSessionFromStaticHistoryVariant({
|
|
@@ -1276,8 +1043,8 @@ class AgentVariantsOrchestrator {
|
|
|
1276
1043
|
projectContext: args.projectContext,
|
|
1277
1044
|
});
|
|
1278
1045
|
this.activeSessionId = created.sessionId;
|
|
1279
|
-
|
|
1280
|
-
|
|
1046
|
+
this.ensureResources(created.sessionId);
|
|
1047
|
+
this.workspaceInventory.setStaticPreview(created.sessionId, {
|
|
1281
1048
|
workItemId: created.workItemId,
|
|
1282
1049
|
briefId: created.briefId,
|
|
1283
1050
|
entryPath: args.entryPath,
|
|
@@ -1429,12 +1196,11 @@ class AgentVariantsOrchestrator {
|
|
|
1429
1196
|
const resources = this.resources.get(sessionId);
|
|
1430
1197
|
return this.store.getVariants(sessionId).map((variant) => {
|
|
1431
1198
|
const port = this.getDevServerPort(sessionId, variant.workItemId);
|
|
1432
|
-
const capturedDiff =
|
|
1199
|
+
const capturedDiff = this.workspaceInventory.getWorktree(sessionId, variant.workItemId)?.diff;
|
|
1433
1200
|
const changedFilesCount = typeof capturedDiff === 'string' && capturedDiff.length > 0
|
|
1434
|
-
? countDiffFiles(capturedDiff)
|
|
1201
|
+
? (0, variantWorkspaceFiles_1.countDiffFiles)(capturedDiff)
|
|
1435
1202
|
: undefined;
|
|
1436
|
-
const staticPreview =
|
|
1437
|
-
this.getStaticPreviewByBriefId(sessionId, variant.briefId);
|
|
1203
|
+
const staticPreview = this.workspaceInventory.getStaticPreview(sessionId, variant.workItemId) ?? this.getStaticPreviewByBriefId(sessionId, variant.briefId);
|
|
1438
1204
|
let preview;
|
|
1439
1205
|
if (staticPreview) {
|
|
1440
1206
|
preview = {
|
|
@@ -1461,7 +1227,7 @@ class AgentVariantsOrchestrator {
|
|
|
1461
1227
|
const isSucceeded = variant.status === 'succeeded';
|
|
1462
1228
|
const qaFailed = qa?.status === 'failed';
|
|
1463
1229
|
const canView = Boolean(preview) || (isSucceeded && Boolean(port));
|
|
1464
|
-
const previewFailure =
|
|
1230
|
+
const previewFailure = this.previewRuntime.getFailure(sessionId, variant.workItemId);
|
|
1465
1231
|
const previewUnavailable = !canView && isSucceeded && previewFailure
|
|
1466
1232
|
? {
|
|
1467
1233
|
reason: previewFailure.reason,
|
|
@@ -1474,7 +1240,7 @@ class AgentVariantsOrchestrator {
|
|
|
1474
1240
|
: 'Wait for a successful variant';
|
|
1475
1241
|
return {
|
|
1476
1242
|
...variant,
|
|
1477
|
-
design: enrichDesignSource(variant.design),
|
|
1243
|
+
design: (0, designContextArtifacts_1.enrichDesignSource)(variant.design),
|
|
1478
1244
|
...(preview ? { preview } : {}),
|
|
1479
1245
|
port,
|
|
1480
1246
|
...(changedFilesCount !== undefined ? { changedFilesCount } : {}),
|
|
@@ -1508,55 +1274,35 @@ class AgentVariantsOrchestrator {
|
|
|
1508
1274
|
return this.store.getProjectContext(sessionId);
|
|
1509
1275
|
}
|
|
1510
1276
|
getWorktreePath(sessionId, workItemId) {
|
|
1511
|
-
return this.
|
|
1277
|
+
return this.workspaceInventory.getWorktree(sessionId, workItemId)
|
|
1512
1278
|
?.worktreePath;
|
|
1513
1279
|
}
|
|
1514
1280
|
getProjectCwdInWorktree(worktreePath) {
|
|
1515
1281
|
return this.worktrees.getProjectCwdInWorktree(worktreePath);
|
|
1516
1282
|
}
|
|
1517
1283
|
getDevServerPort(sessionId, workItemId) {
|
|
1518
|
-
return this.
|
|
1284
|
+
return this.previewRuntime.getPort(sessionId, workItemId);
|
|
1519
1285
|
}
|
|
1520
1286
|
async restoreVariantPreview(sessionId, workItemId, options = {}) {
|
|
1521
1287
|
const resources = this.resources.get(sessionId);
|
|
1522
|
-
const record =
|
|
1523
|
-
if (!resources ||
|
|
1288
|
+
const record = this.workspaceInventory.getWorktree(sessionId, workItemId);
|
|
1289
|
+
if (!resources ||
|
|
1290
|
+
!record ||
|
|
1291
|
+
this.workspaceInventory.isCleanupStarted(sessionId)) {
|
|
1524
1292
|
return undefined;
|
|
1525
|
-
if (record.previewRestoration)
|
|
1526
|
-
return record.previewRestoration;
|
|
1527
|
-
if (record.port !== undefined && record.devServerProcess) {
|
|
1528
|
-
this.touchSession(sessionId);
|
|
1529
|
-
return record.port;
|
|
1530
1293
|
}
|
|
1531
|
-
const failure = resources.previewFailures.get(workItemId);
|
|
1532
1294
|
const isSucceeded = this.store
|
|
1533
1295
|
.getVariants(sessionId)
|
|
1534
1296
|
.some((variant) => variant.workItemId === workItemId && variant.status === 'succeeded');
|
|
1535
|
-
const isExplicitRetry = options.retryFailed === true &&
|
|
1536
|
-
failure?.reason !== 'missing_static_entry';
|
|
1537
|
-
if (!failure ||
|
|
1538
|
-
!isSucceeded ||
|
|
1539
|
-
(failure.reason !== 'idle_timeout' && !isExplicitRetry)) {
|
|
1540
|
-
return undefined;
|
|
1541
|
-
}
|
|
1542
|
-
resources.previewFailures.delete(workItemId);
|
|
1543
|
-
record.devServerAutoRestarts = 0;
|
|
1544
|
-
this.emitChange();
|
|
1545
1297
|
const projectContext = this.store.getProjectContext(sessionId);
|
|
1546
|
-
|
|
1298
|
+
return this.previewRuntime.restoreVariant({
|
|
1547
1299
|
sessionId,
|
|
1548
1300
|
workItemId,
|
|
1549
|
-
record,
|
|
1301
|
+
worktreePath: record.worktreePath,
|
|
1550
1302
|
isFresh: projectContext.kind === 'fresh',
|
|
1303
|
+
isSucceeded,
|
|
1304
|
+
retryFailed: options.retryFailed,
|
|
1551
1305
|
});
|
|
1552
|
-
record.previewRestoration = restoration;
|
|
1553
|
-
try {
|
|
1554
|
-
return await restoration;
|
|
1555
|
-
}
|
|
1556
|
-
finally {
|
|
1557
|
-
if (record.previewRestoration === restoration)
|
|
1558
|
-
record.previewRestoration = undefined;
|
|
1559
|
-
}
|
|
1560
1306
|
}
|
|
1561
1307
|
resolveDevServerPortForPreview(sessionId, workItemId) {
|
|
1562
1308
|
const port = this.getDevServerPort(sessionId, workItemId);
|
|
@@ -1574,9 +1320,7 @@ class AgentVariantsOrchestrator {
|
|
|
1574
1320
|
}
|
|
1575
1321
|
}
|
|
1576
1322
|
getStaticPreviewHtml(sessionId, workItemId) {
|
|
1577
|
-
const preview = this.
|
|
1578
|
-
.get(sessionId)
|
|
1579
|
-
?.staticPreviews.get(workItemId);
|
|
1323
|
+
const preview = this.workspaceInventory.getStaticPreview(sessionId, workItemId);
|
|
1580
1324
|
if (preview) {
|
|
1581
1325
|
try {
|
|
1582
1326
|
const fromDisk = fs_1.default.readFileSync(path_1.default.join(preview.assetBase, preview.entryPath), 'utf8');
|
|
@@ -1588,7 +1332,7 @@ class AgentVariantsOrchestrator {
|
|
|
1588
1332
|
try {
|
|
1589
1333
|
const output = this.store.getWorkItemOutput(sessionId, workItemId);
|
|
1590
1334
|
return typeof output?.html === 'string' && output.html.length > 0
|
|
1591
|
-
? buildStaticPreviewDocument({ html: output.html })
|
|
1335
|
+
? (0, variantWorkspaceFiles_1.buildStaticPreviewDocument)({ html: output.html })
|
|
1592
1336
|
: undefined;
|
|
1593
1337
|
}
|
|
1594
1338
|
catch {
|
|
@@ -1596,37 +1340,34 @@ class AgentVariantsOrchestrator {
|
|
|
1596
1340
|
}
|
|
1597
1341
|
}
|
|
1598
1342
|
getDesignContextMarkdown(sessionId, artifactId) {
|
|
1599
|
-
const artifact = findDesignContextArtifact(this.store.getProjectContext(sessionId), artifactId);
|
|
1343
|
+
const artifact = (0, designContextArtifacts_1.findDesignContextArtifact)(this.store.getProjectContext(sessionId), artifactId);
|
|
1600
1344
|
return artifact?.content;
|
|
1601
1345
|
}
|
|
1602
1346
|
getDesignContextViewerHtml(sessionId, artifactId) {
|
|
1603
|
-
const artifact = findDesignContextArtifact(this.store.getProjectContext(sessionId), artifactId);
|
|
1604
|
-
return artifact ? buildDesignContextViewerDocument(artifact) : undefined;
|
|
1347
|
+
const artifact = (0, designContextArtifacts_1.findDesignContextArtifact)(this.store.getProjectContext(sessionId), artifactId);
|
|
1348
|
+
return artifact ? (0, designContextArtifacts_1.buildDesignContextViewerDocument)(artifact) : undefined;
|
|
1605
1349
|
}
|
|
1606
1350
|
hasStaticPreviewAssets(sessionId, workItemId) {
|
|
1607
|
-
const record = this.
|
|
1608
|
-
.get(sessionId)
|
|
1609
|
-
?.staticPreviews.get(workItemId);
|
|
1351
|
+
const record = this.workspaceInventory.getStaticPreview(sessionId, workItemId);
|
|
1610
1352
|
return Boolean(record?.hasAssets);
|
|
1611
1353
|
}
|
|
1612
1354
|
resolveStaticPreviewAssetPath(sessionId, workItemId, requestedPath) {
|
|
1613
|
-
const record = this.
|
|
1614
|
-
.get(sessionId)
|
|
1615
|
-
?.staticPreviews.get(workItemId);
|
|
1355
|
+
const record = this.workspaceInventory.getStaticPreview(sessionId, workItemId);
|
|
1616
1356
|
if (!record?.assetBase)
|
|
1617
1357
|
return undefined;
|
|
1618
1358
|
if (!requestedPath || requestedPath.length === 0)
|
|
1619
1359
|
return undefined;
|
|
1620
|
-
return (resolveAssetWithinBase(record.assetBase, requestedPath) ??
|
|
1360
|
+
return ((0, variantWorkspaceFiles_1.resolveAssetWithinBase)(record.assetBase, requestedPath) ??
|
|
1621
1361
|
(record.assetFallbackBase
|
|
1622
|
-
? resolveAssetWithinBase(record.assetFallbackBase, requestedPath)
|
|
1362
|
+
? (0, variantWorkspaceFiles_1.resolveAssetWithinBase)(record.assetFallbackBase, requestedPath)
|
|
1623
1363
|
: undefined));
|
|
1624
1364
|
}
|
|
1625
1365
|
getStaticPreviewByBriefId(sessionId, briefId) {
|
|
1626
|
-
|
|
1627
|
-
if (!resources)
|
|
1366
|
+
if (!this.workspaceInventory.hasSession(sessionId))
|
|
1628
1367
|
return undefined;
|
|
1629
|
-
return
|
|
1368
|
+
return this.workspaceInventory
|
|
1369
|
+
.getStaticPreviews(sessionId)
|
|
1370
|
+
.find((preview) => preview.briefId === briefId);
|
|
1630
1371
|
}
|
|
1631
1372
|
buildStaticPreviewUrl(sessionId, workItemId) {
|
|
1632
1373
|
const safeSessionId = encodeURIComponent(sessionId);
|
|
@@ -1655,9 +1396,9 @@ class AgentVariantsOrchestrator {
|
|
|
1655
1396
|
this.emitChange();
|
|
1656
1397
|
try {
|
|
1657
1398
|
if (projectContext.kind === 'existing') {
|
|
1658
|
-
await this.resolveContextAssetPreviewKind(args.sessionId);
|
|
1399
|
+
await this.workspaceProvisioning.resolveContextAssetPreviewKind(args.sessionId);
|
|
1659
1400
|
}
|
|
1660
|
-
await this.
|
|
1401
|
+
await this.workspaceProvisioning.provision(args.sessionId, result);
|
|
1661
1402
|
this.emitChange();
|
|
1662
1403
|
}
|
|
1663
1404
|
catch (err) {
|
|
@@ -1730,10 +1471,10 @@ class AgentVariantsOrchestrator {
|
|
|
1730
1471
|
workItemIds: createdWorkItemIds,
|
|
1731
1472
|
});
|
|
1732
1473
|
if (args.sourceVariantId) {
|
|
1733
|
-
await this.
|
|
1474
|
+
await this.workspaceProvisioning.provisionForked(sessionId, args.sourceVariantId, createdWorkItemIds);
|
|
1734
1475
|
}
|
|
1735
1476
|
else {
|
|
1736
|
-
await this.
|
|
1477
|
+
await this.workspaceProvisioning.provisionAppended(sessionId, createdWorkItemIds);
|
|
1737
1478
|
}
|
|
1738
1479
|
this.telemetry.track('agent_variants.variants_appended', {
|
|
1739
1480
|
source: 'mcp',
|
|
@@ -1870,7 +1611,7 @@ class AgentVariantsOrchestrator {
|
|
|
1870
1611
|
return Array.from({ length: args.count }, (_, i) => withUniqueLabel({
|
|
1871
1612
|
briefId: briefId(i),
|
|
1872
1613
|
label: args.count > 1 ? `${runLabel} ${i + 1}` : runLabel,
|
|
1873
|
-
body: synthesizeUnifiedBriefBody(args.prompt, i + 1, args.count),
|
|
1614
|
+
body: (0, variantWorkspaceFiles_1.synthesizeUnifiedBriefBody)(args.prompt, i + 1, args.count),
|
|
1874
1615
|
}, i));
|
|
1875
1616
|
}
|
|
1876
1617
|
getAppendableExistingSessionId(projectPath) {
|
|
@@ -1887,96 +1628,6 @@ class AgentVariantsOrchestrator {
|
|
|
1887
1628
|
return null;
|
|
1888
1629
|
return sessionId;
|
|
1889
1630
|
}
|
|
1890
|
-
async registerWorktreeWorkspace(resources, record) {
|
|
1891
|
-
let projectPath = record.worktreePath;
|
|
1892
|
-
try {
|
|
1893
|
-
projectPath = await this.worktrees.getProjectCwdInWorktree(record.worktreePath);
|
|
1894
|
-
}
|
|
1895
|
-
catch (err) {
|
|
1896
|
-
log.warn(`Failed to resolve project path in worktree ${record.worktreePath}; using its root`, err);
|
|
1897
|
-
}
|
|
1898
|
-
resources.worktrees.set(record.workItemId, record);
|
|
1899
|
-
resources.workspaceManager.registerGitWorkspace({
|
|
1900
|
-
workItemId: record.workItemId,
|
|
1901
|
-
rootPath: record.worktreePath,
|
|
1902
|
-
projectPath,
|
|
1903
|
-
});
|
|
1904
|
-
}
|
|
1905
|
-
async withProvisioningTelemetry(args, run) {
|
|
1906
|
-
const startedAt = Date.now();
|
|
1907
|
-
this.telemetry.track('agent_variants.provisioning_started', {
|
|
1908
|
-
source: 'mcp',
|
|
1909
|
-
sessionId: args.sessionId,
|
|
1910
|
-
kind: args.kind,
|
|
1911
|
-
variantCount: args.variantCount,
|
|
1912
|
-
});
|
|
1913
|
-
try {
|
|
1914
|
-
const result = await run();
|
|
1915
|
-
this.telemetry.track('agent_variants.provisioning_completed', {
|
|
1916
|
-
source: 'mcp',
|
|
1917
|
-
sessionId: args.sessionId,
|
|
1918
|
-
kind: args.kind,
|
|
1919
|
-
variantCount: args.variantCount,
|
|
1920
|
-
durationMs: Date.now() - startedAt,
|
|
1921
|
-
});
|
|
1922
|
-
return result;
|
|
1923
|
-
}
|
|
1924
|
-
catch (err) {
|
|
1925
|
-
const timedOut = err instanceof ProvisionTimeoutError;
|
|
1926
|
-
this.telemetry.track('agent_variants.provisioning_failed', {
|
|
1927
|
-
source: 'mcp',
|
|
1928
|
-
sessionId: args.sessionId,
|
|
1929
|
-
kind: args.kind,
|
|
1930
|
-
variantCount: args.variantCount,
|
|
1931
|
-
durationMs: Date.now() - startedAt,
|
|
1932
|
-
timedOut,
|
|
1933
|
-
errorCode: timedOut
|
|
1934
|
-
? 'PROVISION_TIMEOUT'
|
|
1935
|
-
: err instanceof errors_1.AgentVariantsError
|
|
1936
|
-
? err.code
|
|
1937
|
-
: 'PROVISION_FAILED',
|
|
1938
|
-
});
|
|
1939
|
-
throw err;
|
|
1940
|
-
}
|
|
1941
|
-
}
|
|
1942
|
-
async provisionAppendedExistingWorktrees(sessionId, workItemIds) {
|
|
1943
|
-
if (workItemIds.length === 0)
|
|
1944
|
-
return;
|
|
1945
|
-
await this.withProvisioningTelemetry({
|
|
1946
|
-
sessionId,
|
|
1947
|
-
kind: 'appended_existing',
|
|
1948
|
-
variantCount: workItemIds.length,
|
|
1949
|
-
}, async () => {
|
|
1950
|
-
const resources = this.ensureResources(sessionId);
|
|
1951
|
-
const instanceKeyPrefix = `a${(0, crypto_1.randomUUID)().slice(0, 8)}-`;
|
|
1952
|
-
const paths = await withProvisionTimeout(this.worktrees.createWorktrees(sessionId, workItemIds.length, instanceKeyPrefix), this.provisionTimeoutMs);
|
|
1953
|
-
await Promise.all(workItemIds.map((id, index) => this.registerWorktreeWorkspace(resources, {
|
|
1954
|
-
workItemId: id,
|
|
1955
|
-
worktreePath: paths[index],
|
|
1956
|
-
})));
|
|
1957
|
-
});
|
|
1958
|
-
}
|
|
1959
|
-
async provisionForkedFromVariantWorktrees(sessionId, sourceVariantId, workItemIds) {
|
|
1960
|
-
if (workItemIds.length === 0)
|
|
1961
|
-
return;
|
|
1962
|
-
const sourcePath = this.getWorktreePath(sessionId, sourceVariantId);
|
|
1963
|
-
if (!sourcePath) {
|
|
1964
|
-
throw new errors_1.AgentVariantsError('VARIANT_NOT_EDITABLE', `Cannot fork from variant ${sourceVariantId}: no worktree path`);
|
|
1965
|
-
}
|
|
1966
|
-
const forkWorktreesFrom = this.worktrees.forkWorktreesFrom?.bind(this.worktrees);
|
|
1967
|
-
if (!forkWorktreesFrom) {
|
|
1968
|
-
throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', 'worktreeManager does not implement forkWorktreesFrom');
|
|
1969
|
-
}
|
|
1970
|
-
await this.withProvisioningTelemetry({ sessionId, kind: 'forked_variant', variantCount: workItemIds.length }, async () => {
|
|
1971
|
-
const resources = this.ensureResources(sessionId);
|
|
1972
|
-
const instanceKeyPrefix = `f${(0, crypto_1.randomUUID)().slice(0, 8)}-`;
|
|
1973
|
-
const paths = await withProvisionTimeout(forkWorktreesFrom(sourcePath, sessionId, workItemIds.length, instanceKeyPrefix), this.provisionTimeoutMs);
|
|
1974
|
-
await Promise.all(workItemIds.map((id, index) => this.registerWorktreeWorkspace(resources, {
|
|
1975
|
-
workItemId: id,
|
|
1976
|
-
worktreePath: paths[index],
|
|
1977
|
-
})));
|
|
1978
|
-
});
|
|
1979
|
-
}
|
|
1980
1631
|
async reportComplete(args) {
|
|
1981
1632
|
const reportReceivedAt = Date.now();
|
|
1982
1633
|
if (this.store.hasSession(args.sessionId) &&
|
|
@@ -1999,14 +1650,14 @@ class AgentVariantsOrchestrator {
|
|
|
1999
1650
|
args.output === undefined
|
|
2000
1651
|
? {
|
|
2001
1652
|
...args,
|
|
2002
|
-
output: this.
|
|
1653
|
+
output: this.workspaceProvisioning.readArtifact(args.sessionId, args.workItemId),
|
|
2003
1654
|
}
|
|
2004
1655
|
: args;
|
|
2005
1656
|
if (resolvedArgs.status === 'succeeded' &&
|
|
2006
1657
|
this.store.hasSession(args.sessionId) &&
|
|
2007
1658
|
(workItemKind === 'static_preview' ||
|
|
2008
1659
|
workItemKind === 'refine_variant') &&
|
|
2009
|
-
!parseStaticPreviewOutput(normalizeOutput(resolvedArgs.output))) {
|
|
1660
|
+
!(0, variantWorkspaceFiles_1.parseStaticPreviewOutput)((0, variantWorkspaceFiles_1.normalizeOutput)(resolvedArgs.output))) {
|
|
2010
1661
|
const telemetry = this.refinementTelemetryForWorkItem({
|
|
2011
1662
|
sessionId: args.sessionId,
|
|
2012
1663
|
workItemId: args.workItemId,
|
|
@@ -2082,7 +1733,7 @@ class AgentVariantsOrchestrator {
|
|
|
2082
1733
|
const durationMs = leasedAt !== undefined ? Date.now() - leasedAt : null;
|
|
2083
1734
|
const approveStartedAt = resources?.approveStartedAt;
|
|
2084
1735
|
const approveToCompleteMs = approveStartedAt !== undefined ? Date.now() - approveStartedAt : null;
|
|
2085
|
-
const outputBytes = estimateOutputBytes(effectiveArgs.output);
|
|
1736
|
+
const outputBytes = (0, variantWorkspaceFiles_1.estimateOutputBytes)(effectiveArgs.output);
|
|
2086
1737
|
const projectContextKind = this.store.getProjectContext(args.sessionId).kind;
|
|
2087
1738
|
const mode = workItemKind !== undefined && isImplementationWorkItemKind(workItemKind)
|
|
2088
1739
|
? this.store.isVaryForkWorkItem(args.sessionId, args.workItemId)
|
|
@@ -2106,7 +1757,7 @@ class AgentVariantsOrchestrator {
|
|
|
2106
1757
|
errorCode: effectiveArgs.error?.code ?? null,
|
|
2107
1758
|
errorMessage: effectiveArgs.error?.message ?? null,
|
|
2108
1759
|
errorApiStatus: effectiveArgs.error?.apiErrorStatus ?? null,
|
|
2109
|
-
isScaffold:
|
|
1760
|
+
isScaffold: this.workspaceInventory.isScaffoldBaseWorkItem(args.sessionId, args.workItemId),
|
|
2110
1761
|
projectContextKind,
|
|
2111
1762
|
tokensIn: args.tokensIn ?? null,
|
|
2112
1763
|
tokensOut: args.tokensOut ?? null,
|
|
@@ -2383,9 +2034,7 @@ class AgentVariantsOrchestrator {
|
|
|
2383
2034
|
log.warn(`persistVariantHistoryAtCommit (duplicate retry) failed for session ${args.sessionId}`, err);
|
|
2384
2035
|
});
|
|
2385
2036
|
}
|
|
2386
|
-
const
|
|
2387
|
-
const live = this.resources.get(args.sessionId)?.committedDevServer;
|
|
2388
|
-
const replayPort = lingering?.port ?? live?.port;
|
|
2037
|
+
const replayPort = this.previewRuntime.getCommittedPort(args.sessionId);
|
|
2389
2038
|
return {
|
|
2390
2039
|
enqueued: false,
|
|
2391
2040
|
duplicate: true,
|
|
@@ -2436,12 +2085,12 @@ class AgentVariantsOrchestrator {
|
|
|
2436
2085
|
else {
|
|
2437
2086
|
const compareBase = realDestination ?? destinationPath;
|
|
2438
2087
|
const anyAssetBaseIsDestination = [
|
|
2439
|
-
...
|
|
2088
|
+
...this.workspaceInventory.getStaticPreviews(args.sessionId),
|
|
2440
2089
|
].some((sp) => sp.assetBase === compareBase);
|
|
2441
2090
|
if (!anyAssetBaseIsDestination) {
|
|
2442
2091
|
const allowed = new Set();
|
|
2443
|
-
for (const sp of
|
|
2444
|
-
if (sp.assetBase && isStrictlyInside(sp.assetBase, compareBase)) {
|
|
2092
|
+
for (const sp of this.workspaceInventory.getStaticPreviews(args.sessionId)) {
|
|
2093
|
+
if (sp.assetBase && (0, variantWorkspaceFiles_1.isStrictlyInside)(sp.assetBase, compareBase)) {
|
|
2445
2094
|
allowed.add(path_1.default.basename(sp.assetBase));
|
|
2446
2095
|
}
|
|
2447
2096
|
}
|
|
@@ -2449,16 +2098,16 @@ class AgentVariantsOrchestrator {
|
|
|
2449
2098
|
}
|
|
2450
2099
|
}
|
|
2451
2100
|
if (isViteApp) {
|
|
2452
|
-
const record =
|
|
2101
|
+
const record = this.workspaceInventory.getWorktree(args.sessionId, args.variantId);
|
|
2453
2102
|
if (!record) {
|
|
2454
2103
|
throw new errors_1.AgentVariantsError('WORK_ITEM_NOT_FOUND', `Unknown variantId ${args.variantId} for session ${args.sessionId}`);
|
|
2455
2104
|
}
|
|
2456
2105
|
let usedRename = false;
|
|
2457
2106
|
let isDependencyInstallRequired = false;
|
|
2458
2107
|
try {
|
|
2459
|
-
if (areSameVolume(record.worktreePath, destinationPath)) {
|
|
2460
|
-
await this.
|
|
2461
|
-
isDependencyInstallRequired = await resolveNodeModulesSymlink(record.worktreePath);
|
|
2108
|
+
if ((0, variantWorkspaceFiles_1.areSameVolume)(record.worktreePath, destinationPath)) {
|
|
2109
|
+
await this.previewRuntime.stopVariantServers(args.sessionId);
|
|
2110
|
+
isDependencyInstallRequired = await (0, variantWorkspaceFiles_1.resolveNodeModulesSymlink)(record.worktreePath);
|
|
2462
2111
|
await this.renameWorktreeIntoDestination(record.worktreePath, destinationPath);
|
|
2463
2112
|
usedRename = true;
|
|
2464
2113
|
}
|
|
@@ -2470,11 +2119,11 @@ class AgentVariantsOrchestrator {
|
|
|
2470
2119
|
const message = err instanceof Error ? err.message : String(err);
|
|
2471
2120
|
throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `Failed to materialize vite_app variant to ${destinationPath}: ${message}`);
|
|
2472
2121
|
}
|
|
2473
|
-
changedFilesCount = countWorktreeFiles(destinationPath);
|
|
2122
|
+
changedFilesCount = (0, variantWorkspaceFiles_1.countWorktreeFiles)(destinationPath);
|
|
2474
2123
|
if (!usedRename) {
|
|
2475
2124
|
void this.installDependencies(destinationPath)
|
|
2476
|
-
.then(() => this.
|
|
2477
|
-
|
|
2125
|
+
.then(() => this.previewRuntime.startCommitted({
|
|
2126
|
+
sessionId: args.sessionId,
|
|
2478
2127
|
destinationPath,
|
|
2479
2128
|
}))
|
|
2480
2129
|
.catch((err) => {
|
|
@@ -2495,8 +2144,8 @@ class AgentVariantsOrchestrator {
|
|
|
2495
2144
|
catch (err) {
|
|
2496
2145
|
log.warn(`preserveUnchosenVariants failed for session ${args.sessionId}; continuing without history`, err);
|
|
2497
2146
|
}
|
|
2498
|
-
committedPreviewPort = await this.
|
|
2499
|
-
|
|
2147
|
+
committedPreviewPort = await this.previewRuntime.startCommitted({
|
|
2148
|
+
sessionId: args.sessionId,
|
|
2500
2149
|
destinationPath,
|
|
2501
2150
|
mode: 'vite_app',
|
|
2502
2151
|
});
|
|
@@ -2512,7 +2161,7 @@ class AgentVariantsOrchestrator {
|
|
|
2512
2161
|
envelopeDestination = destinationPath;
|
|
2513
2162
|
}
|
|
2514
2163
|
else {
|
|
2515
|
-
const staticPreview =
|
|
2164
|
+
const staticPreview = this.workspaceInventory.getStaticPreview(args.sessionId, args.variantId);
|
|
2516
2165
|
const htmlFromDir = staticPreview
|
|
2517
2166
|
? this.getStaticPreviewHtml(args.sessionId, args.variantId)
|
|
2518
2167
|
: undefined;
|
|
@@ -2537,7 +2186,7 @@ class AgentVariantsOrchestrator {
|
|
|
2537
2186
|
}
|
|
2538
2187
|
const chosenAssetBase = staticPreview?.assetBase;
|
|
2539
2188
|
if (chosenAssetBase &&
|
|
2540
|
-
isStrictlyInside(chosenAssetBase, flattenBase)) {
|
|
2189
|
+
(0, variantWorkspaceFiles_1.isStrictlyInside)(chosenAssetBase, flattenBase)) {
|
|
2541
2190
|
for (const entry of fs_1.default.readdirSync(chosenAssetBase)) {
|
|
2542
2191
|
const src = path_1.default.join(chosenAssetBase, entry);
|
|
2543
2192
|
const dst = path_1.default.join(flattenBase, entry);
|
|
@@ -2560,11 +2209,11 @@ class AgentVariantsOrchestrator {
|
|
|
2560
2209
|
}
|
|
2561
2210
|
fs_1.default.rmSync(chosenAssetBase, { recursive: true, force: true });
|
|
2562
2211
|
}
|
|
2563
|
-
for (const other of
|
|
2212
|
+
for (const other of this.workspaceInventory.getStaticPreviews(args.sessionId)) {
|
|
2564
2213
|
if (other.workItemId === args.variantId)
|
|
2565
2214
|
continue;
|
|
2566
2215
|
if (other.assetBase &&
|
|
2567
|
-
isStrictlyInside(other.assetBase, flattenBase)) {
|
|
2216
|
+
(0, variantWorkspaceFiles_1.isStrictlyInside)(other.assetBase, flattenBase)) {
|
|
2568
2217
|
fs_1.default.rmSync(other.assetBase, { recursive: true, force: true });
|
|
2569
2218
|
}
|
|
2570
2219
|
}
|
|
@@ -2574,9 +2223,9 @@ class AgentVariantsOrchestrator {
|
|
|
2574
2223
|
const message = err instanceof Error ? err.message : String(err);
|
|
2575
2224
|
throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `Failed to write static preview to ${destinationPath}: ${message}`);
|
|
2576
2225
|
}
|
|
2577
|
-
changedFilesCount = countWorktreeFiles(destinationPath);
|
|
2578
|
-
committedPreviewPort = await this.
|
|
2579
|
-
|
|
2226
|
+
changedFilesCount = (0, variantWorkspaceFiles_1.countWorktreeFiles)(destinationPath);
|
|
2227
|
+
committedPreviewPort = await this.previewRuntime.startCommitted({
|
|
2228
|
+
sessionId: args.sessionId,
|
|
2580
2229
|
destinationPath,
|
|
2581
2230
|
mode: 'static_preview',
|
|
2582
2231
|
});
|
|
@@ -2591,7 +2240,7 @@ class AgentVariantsOrchestrator {
|
|
|
2591
2240
|
}
|
|
2592
2241
|
else {
|
|
2593
2242
|
const env = await this.resolveEnv(args.sessionId);
|
|
2594
|
-
const record =
|
|
2243
|
+
const record = this.workspaceInventory.getWorktree(args.sessionId, args.variantId);
|
|
2595
2244
|
const saved = record
|
|
2596
2245
|
? null
|
|
2597
2246
|
: await this.getSavedVariant({
|
|
@@ -2606,7 +2255,7 @@ class AgentVariantsOrchestrator {
|
|
|
2606
2255
|
if (diff === undefined) {
|
|
2607
2256
|
throw new errors_1.AgentVariantsError('INVALID_STAGE_ACTION', `Variant ${args.variantId} has no captured diff yet — wait for report_variant_complete(succeeded) first`);
|
|
2608
2257
|
}
|
|
2609
|
-
changedFilesCount = countDiffFiles(diff);
|
|
2258
|
+
changedFilesCount = (0, variantWorkspaceFiles_1.countDiffFiles)(diff);
|
|
2610
2259
|
if (!this.worktrees.applyDiffToProject) {
|
|
2611
2260
|
throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', 'WorktreeManager does not support applying diffs to the project tree');
|
|
2612
2261
|
}
|
|
@@ -2706,212 +2355,7 @@ class AgentVariantsOrchestrator {
|
|
|
2706
2355
|
throw new errors_1.AgentVariantsError('DESTINATION_NOT_EMPTY', `Destination ${destinationPath} is not empty (${userVisibleEntries.length} entries) — refuse to materialize.`);
|
|
2707
2356
|
}
|
|
2708
2357
|
getVariantDiff(sessionId, variantId) {
|
|
2709
|
-
return this.
|
|
2710
|
-
}
|
|
2711
|
-
async provisionWorktrees(sessionId, approveResult) {
|
|
2712
|
-
const projectContext = this.store.getProjectContext(sessionId);
|
|
2713
|
-
const variantCount = approveResult.codeGenWorkItemIds.length +
|
|
2714
|
-
(approveResult.scaffoldBaseWorkItemId ? 1 : 0);
|
|
2715
|
-
if ((0, runPlan_1.resolveRunPlanPolicy)(projectContext).provision === 'none') {
|
|
2716
|
-
return;
|
|
2717
|
-
}
|
|
2718
|
-
const startedAt = Date.now();
|
|
2719
|
-
this.telemetry.track('agent_variants.provisioning_started', {
|
|
2720
|
-
source: 'mcp',
|
|
2721
|
-
sessionId,
|
|
2722
|
-
kind: projectContext.kind,
|
|
2723
|
-
variantCount,
|
|
2724
|
-
});
|
|
2725
|
-
const run = async () => {
|
|
2726
|
-
if (projectContext.kind === 'fresh') {
|
|
2727
|
-
await this.provisionFreshWorktrees(sessionId, approveResult, projectContext);
|
|
2728
|
-
return;
|
|
2729
|
-
}
|
|
2730
|
-
await this.provisionExistingWorktrees(sessionId, approveResult);
|
|
2731
|
-
};
|
|
2732
|
-
try {
|
|
2733
|
-
await withProvisionTimeout(run(), this.provisionTimeoutMs);
|
|
2734
|
-
this.telemetry.track('agent_variants.provisioning_completed', {
|
|
2735
|
-
source: 'mcp',
|
|
2736
|
-
sessionId,
|
|
2737
|
-
kind: projectContext.kind,
|
|
2738
|
-
variantCount,
|
|
2739
|
-
durationMs: Date.now() - startedAt,
|
|
2740
|
-
});
|
|
2741
|
-
}
|
|
2742
|
-
catch (err) {
|
|
2743
|
-
const timedOut = err instanceof ProvisionTimeoutError;
|
|
2744
|
-
this.telemetry.track('agent_variants.provisioning_failed', {
|
|
2745
|
-
source: 'mcp',
|
|
2746
|
-
sessionId,
|
|
2747
|
-
kind: projectContext.kind,
|
|
2748
|
-
variantCount,
|
|
2749
|
-
durationMs: Date.now() - startedAt,
|
|
2750
|
-
timedOut,
|
|
2751
|
-
errorCode: timedOut
|
|
2752
|
-
? 'PROVISION_TIMEOUT'
|
|
2753
|
-
: err instanceof errors_1.AgentVariantsError
|
|
2754
|
-
? err.code
|
|
2755
|
-
: 'PROVISION_FAILED',
|
|
2756
|
-
});
|
|
2757
|
-
void this.cancel({ sessionId, reason: 'provisioning_failed' }).catch((teardownErr) => {
|
|
2758
|
-
log.warn(`cleanup after provisioning failure for ${sessionId} failed`, teardownErr);
|
|
2759
|
-
});
|
|
2760
|
-
if (timedOut) {
|
|
2761
|
-
throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `Provisioning ${variantCount} variant worktree(s) timed out after ` +
|
|
2762
|
-
`${Math.round(this.provisionTimeoutMs / 1000)}s. This usually means the ` +
|
|
2763
|
-
`project is very large or slow to clone (e.g. a big monorepo, or a ` +
|
|
2764
|
-
`React Native tree with native build dirs that aren't git-ignored). ` +
|
|
2765
|
-
`Try git-ignoring build artifacts, or generate standalone variants ` +
|
|
2766
|
-
`with start_variants(mode='zero_to_one') instead of cloning this project.`);
|
|
2767
|
-
}
|
|
2768
|
-
throw err;
|
|
2769
|
-
}
|
|
2770
|
-
}
|
|
2771
|
-
async provisionExistingWorktrees(sessionId, approveResult) {
|
|
2772
|
-
const resources = this.ensureResources(sessionId);
|
|
2773
|
-
const totalCount = approveResult.codeGenWorkItemIds.length +
|
|
2774
|
-
(approveResult.scaffoldBaseWorkItemId ? 1 : 0);
|
|
2775
|
-
if (totalCount === 0)
|
|
2776
|
-
return;
|
|
2777
|
-
log.info(`Provisioning ${totalCount} worktree(s) for session ${sessionId}`);
|
|
2778
|
-
const paths = await this.worktrees.createWorktrees(sessionId, totalCount);
|
|
2779
|
-
let cursor = 0;
|
|
2780
|
-
if (approveResult.scaffoldBaseWorkItemId) {
|
|
2781
|
-
resources.scaffoldBaseWorkItemId = approveResult.scaffoldBaseWorkItemId;
|
|
2782
|
-
await this.registerWorktreeWorkspace(resources, {
|
|
2783
|
-
workItemId: approveResult.scaffoldBaseWorkItemId,
|
|
2784
|
-
worktreePath: paths[cursor],
|
|
2785
|
-
});
|
|
2786
|
-
cursor += 1;
|
|
2787
|
-
}
|
|
2788
|
-
for (const id of approveResult.codeGenWorkItemIds) {
|
|
2789
|
-
const p = paths[cursor];
|
|
2790
|
-
cursor += 1;
|
|
2791
|
-
await this.registerWorktreeWorkspace(resources, {
|
|
2792
|
-
workItemId: id,
|
|
2793
|
-
worktreePath: p,
|
|
2794
|
-
});
|
|
2795
|
-
}
|
|
2796
|
-
}
|
|
2797
|
-
async provisionFreshWorktrees(sessionId, approveResult, projectContext) {
|
|
2798
|
-
const resources = this.ensureResources(sessionId);
|
|
2799
|
-
const scaffoldId = approveResult.scaffoldBaseWorkItemId;
|
|
2800
|
-
if (!scaffoldId) {
|
|
2801
|
-
throw new errors_1.AgentVariantsError('INVALID_STAGE_ACTION', `Fresh session ${sessionId} approved without a scaffold_base item`);
|
|
2802
|
-
}
|
|
2803
|
-
const codeGenIds = approveResult.codeGenWorkItemIds;
|
|
2804
|
-
if (codeGenIds.length === 0)
|
|
2805
|
-
return;
|
|
2806
|
-
const designContext = projectContext.designContext
|
|
2807
|
-
? codeGenIds.map((codeGenId) => {
|
|
2808
|
-
const input = this.store.getWorkItemInput(sessionId, codeGenId);
|
|
2809
|
-
return input.designContextEntry;
|
|
2810
|
-
})
|
|
2811
|
-
: undefined;
|
|
2812
|
-
const createFresh = this.worktrees.createFreshWorktrees;
|
|
2813
|
-
if (!createFresh) {
|
|
2814
|
-
throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', 'worktreeManager does not implement createFreshWorktrees');
|
|
2815
|
-
}
|
|
2816
|
-
const scaffoldStartedAt = Date.now();
|
|
2817
|
-
trackScaffoldStarted(this.telemetry, {
|
|
2818
|
-
sessionId,
|
|
2819
|
-
variantCount: codeGenIds.length,
|
|
2820
|
-
designContext: summarizeDesignContext(designContext),
|
|
2821
|
-
});
|
|
2822
|
-
log.info(`Provisioning ${codeGenIds.length} fresh worktree(s) for session ${sessionId}`);
|
|
2823
|
-
const destinationParent = projectContext.workspaceRoot;
|
|
2824
|
-
const paths = await createFresh.call(this.worktrees, sessionId, codeGenIds.length, viteReactTs_1.VITE_REACT_TS_TEMPLATE, designContext, destinationParent);
|
|
2825
|
-
resources.scaffoldBaseWorkItemId = scaffoldId;
|
|
2826
|
-
resources.freshDestinationParent = destinationParent;
|
|
2827
|
-
await Promise.all(codeGenIds.map((workItemId, index) => this.registerWorktreeWorkspace(resources, {
|
|
2828
|
-
workItemId,
|
|
2829
|
-
worktreePath: paths[index],
|
|
2830
|
-
})));
|
|
2831
|
-
const assetPlan = projectContext.executionPlan?.assetPlan ?? [];
|
|
2832
|
-
if (assetPlan.length > 0) {
|
|
2833
|
-
try {
|
|
2834
|
-
for (const worktreePath of paths) {
|
|
2835
|
-
for (const entry of assetPlan) {
|
|
2836
|
-
copyAssetIntoWorktree(worktreePath, entry, projectContext.assetSourceRoot);
|
|
2837
|
-
}
|
|
2838
|
-
}
|
|
2839
|
-
}
|
|
2840
|
-
catch (err) {
|
|
2841
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
2842
|
-
log.warn(`asset copy failed for session ${sessionId}; marking scaffold_base failed`, err);
|
|
2843
|
-
trackScaffoldFailed(this.telemetry, {
|
|
2844
|
-
sessionId,
|
|
2845
|
-
errorCode: 'SCAFFOLD_FAILED',
|
|
2846
|
-
durationMs: Date.now() - scaffoldStartedAt,
|
|
2847
|
-
});
|
|
2848
|
-
try {
|
|
2849
|
-
this.store.failInternal({
|
|
2850
|
-
sessionId,
|
|
2851
|
-
workItemId: scaffoldId,
|
|
2852
|
-
error: { code: 'SCAFFOLD_FAILED', message },
|
|
2853
|
-
});
|
|
2854
|
-
this.emitChange();
|
|
2855
|
-
}
|
|
2856
|
-
catch (storeErr) {
|
|
2857
|
-
log.error(`failInternal failed for ${sessionId}/${scaffoldId}`, storeErr);
|
|
2858
|
-
}
|
|
2859
|
-
return;
|
|
2860
|
-
}
|
|
2861
|
-
}
|
|
2862
|
-
void this.runBackgroundInstall(sessionId, scaffoldId, paths, scaffoldStartedAt).catch((err) => {
|
|
2863
|
-
log.error(`runBackgroundInstall failed for session ${sessionId}`, err);
|
|
2864
|
-
});
|
|
2865
|
-
}
|
|
2866
|
-
async runBackgroundInstall(sessionId, scaffoldWorkItemId, worktreePaths, scaffoldStartedAt) {
|
|
2867
|
-
const installStartedAt = Date.now();
|
|
2868
|
-
try {
|
|
2869
|
-
await Promise.all(worktreePaths.map((worktreePath) => this.installDependencies(worktreePath, {
|
|
2870
|
-
workspaceRoot: worktreePath,
|
|
2871
|
-
signal: this.resources.get(sessionId)?.dependencyAbortController.signal,
|
|
2872
|
-
})));
|
|
2873
|
-
}
|
|
2874
|
-
catch (err) {
|
|
2875
|
-
const resources = this.resources.get(sessionId);
|
|
2876
|
-
if (!resources || resources.cleanupStarted)
|
|
2877
|
-
return;
|
|
2878
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
2879
|
-
log.warn(`npm install failed for session ${sessionId}; marking scaffold_base failed`, err);
|
|
2880
|
-
trackScaffoldFailed(this.telemetry, {
|
|
2881
|
-
sessionId,
|
|
2882
|
-
errorCode: 'SCAFFOLD_FAILED',
|
|
2883
|
-
durationMs: Date.now() - scaffoldStartedAt,
|
|
2884
|
-
});
|
|
2885
|
-
try {
|
|
2886
|
-
this.store.failInternal({
|
|
2887
|
-
sessionId,
|
|
2888
|
-
workItemId: scaffoldWorkItemId,
|
|
2889
|
-
error: { code: 'SCAFFOLD_FAILED', message },
|
|
2890
|
-
});
|
|
2891
|
-
this.emitChange();
|
|
2892
|
-
}
|
|
2893
|
-
catch (storeErr) {
|
|
2894
|
-
log.error(`failInternal failed for ${sessionId}/${scaffoldWorkItemId}`, storeErr);
|
|
2895
|
-
}
|
|
2896
|
-
return;
|
|
2897
|
-
}
|
|
2898
|
-
try {
|
|
2899
|
-
this.store.completeInternal({
|
|
2900
|
-
sessionId,
|
|
2901
|
-
workItemId: scaffoldWorkItemId,
|
|
2902
|
-
output: { worktreePaths },
|
|
2903
|
-
});
|
|
2904
|
-
this.emitChange();
|
|
2905
|
-
}
|
|
2906
|
-
catch (err) {
|
|
2907
|
-
log.warn(`completeInternal failed for ${sessionId}/${scaffoldWorkItemId}`, err);
|
|
2908
|
-
}
|
|
2909
|
-
trackScaffoldCompleted(this.telemetry, {
|
|
2910
|
-
sessionId,
|
|
2911
|
-
durationMs: Date.now() - scaffoldStartedAt,
|
|
2912
|
-
installDurationMs: Date.now() - installStartedAt,
|
|
2913
|
-
variantCount: worktreePaths.length,
|
|
2914
|
-
});
|
|
2358
|
+
return this.workspaceInventory.getWorktree(sessionId, variantId)?.diff;
|
|
2915
2359
|
}
|
|
2916
2360
|
async evaluateQaForReport(args) {
|
|
2917
2361
|
if (args.status !== 'succeeded')
|
|
@@ -2929,7 +2373,7 @@ class AgentVariantsOrchestrator {
|
|
|
2929
2373
|
let baseQa = null;
|
|
2930
2374
|
const checkedSource = undefined;
|
|
2931
2375
|
if (kind === 'static_preview') {
|
|
2932
|
-
const parsed = parseStaticPreviewOutput(normalizeOutput(args.output));
|
|
2376
|
+
const parsed = (0, variantWorkspaceFiles_1.parseStaticPreviewOutput)((0, variantWorkspaceFiles_1.normalizeOutput)(args.output));
|
|
2933
2377
|
if (!parsed)
|
|
2934
2378
|
return null;
|
|
2935
2379
|
baseQa = await this.runPreviewQaSafely(args, parsed.html);
|
|
@@ -2966,8 +2410,7 @@ class AgentVariantsOrchestrator {
|
|
|
2966
2410
|
if (!isDesignCreationCodeGenInput(input, this.store.getPrompt(args.sessionId))) {
|
|
2967
2411
|
return null;
|
|
2968
2412
|
}
|
|
2969
|
-
const
|
|
2970
|
-
const record = resources?.worktrees.get(args.workItemId);
|
|
2413
|
+
const record = this.workspaceInventory.getWorktree(args.sessionId, args.workItemId);
|
|
2971
2414
|
if (!record) {
|
|
2972
2415
|
const qa = buildDiffCaptureFailureQa('diff:worktree-record-missing', 'The succeeded report could not be checked because its worktree record is missing.');
|
|
2973
2416
|
this.memoizeQa(args.sessionId, args.workItemId, qa);
|
|
@@ -3086,248 +2529,31 @@ class AgentVariantsOrchestrator {
|
|
|
3086
2529
|
resources.repairCompletionResults.set(repairCompletionKey(args), completionResult);
|
|
3087
2530
|
return completionResult;
|
|
3088
2531
|
}
|
|
3089
|
-
async startVariantDevServer(args) {
|
|
3090
|
-
if (args.record.port !== undefined && args.record.devServerProcess) {
|
|
3091
|
-
return args.record.port;
|
|
3092
|
-
}
|
|
3093
|
-
if (!args.autoRestartTimer && args.record.pendingAutoRestart) {
|
|
3094
|
-
clearTimeout(args.record.pendingAutoRestart);
|
|
3095
|
-
args.record.pendingAutoRestart = undefined;
|
|
3096
|
-
}
|
|
3097
|
-
const canContinueStart = () => {
|
|
3098
|
-
const resources = this.resources.get(args.sessionId);
|
|
3099
|
-
const isAutoRestartCancelled = args.autoRestartTimer !== undefined &&
|
|
3100
|
-
args.record.pendingAutoRestart !== args.autoRestartTimer;
|
|
3101
|
-
return Boolean(resources && !resources.cleanupStarted && !isAutoRestartCancelled);
|
|
3102
|
-
};
|
|
3103
|
-
const previewStartedAt = Date.now();
|
|
3104
|
-
const projectContextKind = args.isFresh ? 'fresh' : 'existing';
|
|
3105
|
-
trackPreviewStarted(this.telemetry, {
|
|
3106
|
-
sessionId: args.sessionId,
|
|
3107
|
-
variantId: args.workItemId,
|
|
3108
|
-
projectContextKind,
|
|
3109
|
-
previewKind: 'dev_server',
|
|
3110
|
-
});
|
|
3111
|
-
const startPort = await this.resolveDevServerStartPort(args.sessionId, args.isFresh);
|
|
3112
|
-
let scanFrom = startPort;
|
|
3113
|
-
const maxAttempts = DEV_SERVER_START_MAX_ATTEMPTS;
|
|
3114
|
-
let installRetried = false;
|
|
3115
|
-
let lastDev;
|
|
3116
|
-
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
3117
|
-
if (!canContinueStart())
|
|
3118
|
-
return undefined;
|
|
3119
|
-
let port;
|
|
3120
|
-
try {
|
|
3121
|
-
port = await this.worktrees.findFreePortFrom(scanFrom);
|
|
3122
|
-
const dev = await this.resolveDevServer(args.sessionId, args.record.worktreePath, port, args.isFresh);
|
|
3123
|
-
lastDev = dev;
|
|
3124
|
-
if (!canContinueStart())
|
|
3125
|
-
return undefined;
|
|
3126
|
-
const proc = await this.worktrees.startDevServer(dev.cwd, port, dev.cmd, dev.args, dev.env);
|
|
3127
|
-
if (!canContinueStart()) {
|
|
3128
|
-
log.info(`Variant ${args.workItemId} dev server booted after preview cleanup; stopping it`);
|
|
3129
|
-
void this.worktrees.stopDevServer(proc).catch(() => undefined);
|
|
3130
|
-
return undefined;
|
|
3131
|
-
}
|
|
3132
|
-
if (args.autoRestartTimer) {
|
|
3133
|
-
args.record.pendingAutoRestart = undefined;
|
|
3134
|
-
}
|
|
3135
|
-
args.record.port = port;
|
|
3136
|
-
args.record.devServerProcess = proc;
|
|
3137
|
-
args.record.devCommand = dev;
|
|
3138
|
-
proc.once('exit', (code, signal) => {
|
|
3139
|
-
if (args.record.devServerProcess !== proc)
|
|
3140
|
-
return;
|
|
3141
|
-
args.record.port = undefined;
|
|
3142
|
-
args.record.devServerProcess = undefined;
|
|
3143
|
-
const resources = this.resources.get(args.sessionId);
|
|
3144
|
-
const restartsUsed = args.record.devServerAutoRestarts ?? 0;
|
|
3145
|
-
const canRestart = restartsUsed < DEV_SERVER_MAX_AUTO_RESTARTS &&
|
|
3146
|
-
resources !== undefined &&
|
|
3147
|
-
!resources.cleanupStarted;
|
|
3148
|
-
log.warn(`Variant ${args.workItemId} dev server exited unexpectedly (code=${code ?? 'null'}, signal=${signal ?? 'null'}); ` +
|
|
3149
|
-
(canRestart
|
|
3150
|
-
? `auto-restarting (${restartsUsed + 1}/${DEV_SERVER_MAX_AUTO_RESTARTS})`
|
|
3151
|
-
: 'preview disabled'));
|
|
3152
|
-
if (canRestart) {
|
|
3153
|
-
args.record.devServerAutoRestarts = restartsUsed + 1;
|
|
3154
|
-
this.emitChange();
|
|
3155
|
-
const timer = setTimeout(() => {
|
|
3156
|
-
const current = this.resources.get(args.sessionId);
|
|
3157
|
-
if (!current ||
|
|
3158
|
-
current.cleanupStarted ||
|
|
3159
|
-
args.record.pendingAutoRestart !== timer) {
|
|
3160
|
-
return;
|
|
3161
|
-
}
|
|
3162
|
-
this.startVariantDevServer({
|
|
3163
|
-
...args,
|
|
3164
|
-
autoRestartTimer: timer,
|
|
3165
|
-
})
|
|
3166
|
-
.catch((err) => log.warn(`Auto-restart of variant ${args.workItemId} dev server failed`, err))
|
|
3167
|
-
.finally(() => {
|
|
3168
|
-
if (args.record.pendingAutoRestart === timer) {
|
|
3169
|
-
args.record.pendingAutoRestart = undefined;
|
|
3170
|
-
}
|
|
3171
|
-
});
|
|
3172
|
-
}, this.devServerRestartDelayMs);
|
|
3173
|
-
args.record.pendingAutoRestart = timer;
|
|
3174
|
-
timer.unref?.();
|
|
3175
|
-
return;
|
|
3176
|
-
}
|
|
3177
|
-
resources?.previewFailures.set(args.workItemId, {
|
|
3178
|
-
reason: 'exited',
|
|
3179
|
-
portInUse: false,
|
|
3180
|
-
});
|
|
3181
|
-
this.emitChange();
|
|
3182
|
-
});
|
|
3183
|
-
this.resources
|
|
3184
|
-
.get(args.sessionId)
|
|
3185
|
-
?.previewFailures.delete(args.workItemId);
|
|
3186
|
-
this.emitChange();
|
|
3187
|
-
trackFreshDevServerStarted(this.telemetry, {
|
|
3188
|
-
sessionId: args.sessionId,
|
|
3189
|
-
variantId: args.workItemId,
|
|
3190
|
-
port,
|
|
3191
|
-
});
|
|
3192
|
-
trackPreviewReady(this.telemetry, {
|
|
3193
|
-
sessionId: args.sessionId,
|
|
3194
|
-
variantId: args.workItemId,
|
|
3195
|
-
projectContextKind,
|
|
3196
|
-
previewKind: 'dev_server',
|
|
3197
|
-
hasPort: true,
|
|
3198
|
-
port,
|
|
3199
|
-
durationMs: Date.now() - previewStartedAt,
|
|
3200
|
-
});
|
|
3201
|
-
log.info(`Variant ${args.workItemId} dev server up on port ${port} (worktree ${args.record.worktreePath}; cmd: ${dev.cmd} ${dev.args.join(' ')})`);
|
|
3202
|
-
this.touchSession(args.sessionId);
|
|
3203
|
-
return port;
|
|
3204
|
-
}
|
|
3205
|
-
catch (err) {
|
|
3206
|
-
if (!canContinueStart())
|
|
3207
|
-
return undefined;
|
|
3208
|
-
const devErr = (0, devServerError_1.isDevServerStartError)(err) ? err : undefined;
|
|
3209
|
-
const portInUse = devErr?.portInUse ?? false;
|
|
3210
|
-
const reason = devErr?.reason ?? 'unknown';
|
|
3211
|
-
const missingDepsOutput = (0, devServerError_1.isMissingDependencyOutput)(devErr?.detail ?? '');
|
|
3212
|
-
const missingDeps = missingDepsOutput &&
|
|
3213
|
-
reason === 'exited' &&
|
|
3214
|
-
!installRetried &&
|
|
3215
|
-
lastDev !== undefined &&
|
|
3216
|
-
canInstallWithoutTouchingHost(lastDev.cwd);
|
|
3217
|
-
const willRetry = attempt < maxAttempts &&
|
|
3218
|
-
(portInUse || missingDeps || (reason === 'exited' && attempt === 1));
|
|
3219
|
-
scanFrom = (port ?? scanFrom) + 1;
|
|
3220
|
-
log.warn(`Failed to start dev server for variant ${args.workItemId} on port ${port ?? 'unallocated'} ` +
|
|
3221
|
-
`(attempt ${attempt}/${maxAttempts}, reason=${reason}, portInUse=${portInUse}, missingDeps=${missingDeps})` +
|
|
3222
|
-
(willRetry
|
|
3223
|
-
? missingDeps
|
|
3224
|
-
? '; installing dependencies and retrying'
|
|
3225
|
-
: '; retrying on a fresh port'
|
|
3226
|
-
: '; live preview disabled for this variant'), err);
|
|
3227
|
-
if (willRetry) {
|
|
3228
|
-
if (missingDeps && lastDev) {
|
|
3229
|
-
installRetried = true;
|
|
3230
|
-
try {
|
|
3231
|
-
await this.installDependencies(lastDev.cwd, {
|
|
3232
|
-
workspaceRoot: args.record.worktreePath,
|
|
3233
|
-
force: true,
|
|
3234
|
-
signal: this.resources.get(args.sessionId)
|
|
3235
|
-
?.dependencyAbortController.signal,
|
|
3236
|
-
});
|
|
3237
|
-
}
|
|
3238
|
-
catch (installErr) {
|
|
3239
|
-
log.warn(`Dependency preparation failed for variant ${args.workItemId}`, installErr);
|
|
3240
|
-
this.resources
|
|
3241
|
-
.get(args.sessionId)
|
|
3242
|
-
?.previewFailures.set(args.workItemId, {
|
|
3243
|
-
reason: 'dependency_install_failed',
|
|
3244
|
-
portInUse: false,
|
|
3245
|
-
});
|
|
3246
|
-
trackPreviewFailed(this.telemetry, {
|
|
3247
|
-
sessionId: args.sessionId,
|
|
3248
|
-
variantId: args.workItemId,
|
|
3249
|
-
projectContextKind,
|
|
3250
|
-
previewKind: 'dev_server',
|
|
3251
|
-
errorCode: 'DEPENDENCY_INSTALL_FAILED',
|
|
3252
|
-
reason: 'dependency_install_failed',
|
|
3253
|
-
portInUse: false,
|
|
3254
|
-
durationMs: Date.now() - previewStartedAt,
|
|
3255
|
-
});
|
|
3256
|
-
this.emitChange();
|
|
3257
|
-
return undefined;
|
|
3258
|
-
}
|
|
3259
|
-
}
|
|
3260
|
-
continue;
|
|
3261
|
-
}
|
|
3262
|
-
this.resources
|
|
3263
|
-
.get(args.sessionId)
|
|
3264
|
-
?.previewFailures.set(args.workItemId, {
|
|
3265
|
-
reason,
|
|
3266
|
-
portInUse,
|
|
3267
|
-
});
|
|
3268
|
-
let errorCode = 'DEV_SERVER_START_FAILED';
|
|
3269
|
-
if (reason === 'dependency_install_failed')
|
|
3270
|
-
errorCode = 'DEPENDENCY_INSTALL_FAILED';
|
|
3271
|
-
if (reason === 'missing_dev_script')
|
|
3272
|
-
errorCode = 'DEV_SCRIPT_MISSING';
|
|
3273
|
-
trackFreshDevServerFailed(this.telemetry, {
|
|
3274
|
-
sessionId: args.sessionId,
|
|
3275
|
-
variantId: args.workItemId,
|
|
3276
|
-
errorCode,
|
|
3277
|
-
reason,
|
|
3278
|
-
portInUse,
|
|
3279
|
-
exitCode: devErr?.exitCode,
|
|
3280
|
-
signal: devErr?.signal,
|
|
3281
|
-
missingDeps: missingDepsOutput,
|
|
3282
|
-
});
|
|
3283
|
-
trackPreviewFailed(this.telemetry, {
|
|
3284
|
-
sessionId: args.sessionId,
|
|
3285
|
-
variantId: args.workItemId,
|
|
3286
|
-
projectContextKind,
|
|
3287
|
-
previewKind: 'dev_server',
|
|
3288
|
-
errorCode,
|
|
3289
|
-
reason,
|
|
3290
|
-
portInUse,
|
|
3291
|
-
durationMs: Date.now() - previewStartedAt,
|
|
3292
|
-
exitCode: devErr?.exitCode,
|
|
3293
|
-
signal: devErr?.signal,
|
|
3294
|
-
missingDeps: missingDepsOutput,
|
|
3295
|
-
});
|
|
3296
|
-
this.emitChange();
|
|
3297
|
-
return undefined;
|
|
3298
|
-
}
|
|
3299
|
-
finally {
|
|
3300
|
-
if (port !== undefined)
|
|
3301
|
-
this.worktrees.releasePort?.(port);
|
|
3302
|
-
}
|
|
3303
|
-
}
|
|
3304
|
-
return undefined;
|
|
3305
|
-
}
|
|
3306
2532
|
async handleSucceededReport(args) {
|
|
3307
2533
|
const { sessionId, workItemId } = args;
|
|
3308
2534
|
const resources = this.resources.get(sessionId);
|
|
3309
|
-
const record =
|
|
2535
|
+
const record = this.workspaceInventory.getWorktree(sessionId, workItemId);
|
|
3310
2536
|
if (resources && !record) {
|
|
3311
|
-
const staticPreview = parseStaticPreviewOutput(normalizeOutput(args.output));
|
|
2537
|
+
const staticPreview = (0, variantWorkspaceFiles_1.parseStaticPreviewOutput)((0, variantWorkspaceFiles_1.normalizeOutput)(args.output));
|
|
3312
2538
|
if (staticPreview) {
|
|
3313
2539
|
const input = this.store.getWorkItemInput(sessionId, workItemId);
|
|
3314
2540
|
if (input.briefId) {
|
|
3315
2541
|
const projectContext = this.store.getProjectContext(sessionId);
|
|
3316
2542
|
const previewStartedAt = Date.now();
|
|
3317
|
-
trackPreviewStarted(this.telemetry, {
|
|
2543
|
+
(0, variantsTelemetry_1.trackPreviewStarted)(this.telemetry, {
|
|
3318
2544
|
sessionId,
|
|
3319
2545
|
variantId: workItemId,
|
|
3320
2546
|
projectContextKind: projectContext.kind,
|
|
3321
2547
|
previewKind: 'static_artifact',
|
|
3322
2548
|
});
|
|
3323
|
-
const resolvedAssetBase = resolveStaticPreviewAssetBase({
|
|
2549
|
+
const resolvedAssetBase = (0, variantWorkspaceFiles_1.resolveStaticPreviewAssetBase)({
|
|
3324
2550
|
assetBase: staticPreview.assetBase,
|
|
3325
2551
|
projectContext,
|
|
3326
2552
|
});
|
|
3327
2553
|
const workspacePath = projectContext.kind === 'fresh'
|
|
3328
2554
|
? projectContext.workspacePath
|
|
3329
2555
|
: undefined;
|
|
3330
|
-
const assignedWorkspace =
|
|
2556
|
+
const assignedWorkspace = this.workspaceInventory.getAssignedWorkspace(sessionId, workItemId);
|
|
3331
2557
|
const assignedArtifactBase = assignedWorkspace?.kind === 'artifact_directory'
|
|
3332
2558
|
? assignedWorkspace.rootPath
|
|
3333
2559
|
: undefined;
|
|
@@ -3338,7 +2564,7 @@ class AgentVariantsOrchestrator {
|
|
|
3338
2564
|
: undefined);
|
|
3339
2565
|
if (!dir) {
|
|
3340
2566
|
log.warn(`static preview for ${workItemId} has no workspace dir; skipping`);
|
|
3341
|
-
trackPreviewFailed(this.telemetry, {
|
|
2567
|
+
(0, variantsTelemetry_1.trackPreviewFailed)(this.telemetry, {
|
|
3342
2568
|
sessionId,
|
|
3343
2569
|
variantId: workItemId,
|
|
3344
2570
|
projectContextKind: projectContext.kind,
|
|
@@ -3367,10 +2593,10 @@ class AgentVariantsOrchestrator {
|
|
|
3367
2593
|
assetBase: dir,
|
|
3368
2594
|
hasAssets: Boolean(resolvedAssetBase || assignedArtifactBase),
|
|
3369
2595
|
};
|
|
3370
|
-
|
|
2596
|
+
this.workspaceInventory.setStaticPreview(sessionId, record);
|
|
3371
2597
|
if (assignedWorkspace?.kind === 'artifact_directory' &&
|
|
3372
2598
|
assignedWorkspace.rootPath !== record.assetBase) {
|
|
3373
|
-
|
|
2599
|
+
this.workspaceInventory.removeArtifactWorkspace(sessionId, workItemId);
|
|
3374
2600
|
}
|
|
3375
2601
|
this.trackHistoryPersist(this.persistCompletedArtifactVariant({
|
|
3376
2602
|
sessionId,
|
|
@@ -3379,12 +2605,12 @@ class AgentVariantsOrchestrator {
|
|
|
3379
2605
|
log.warn(`persistCompletedArtifactVariant failed for ${sessionId}/${workItemId}`, err);
|
|
3380
2606
|
}));
|
|
3381
2607
|
const leasedAt = resources.leasedAt.get(workItemId);
|
|
3382
|
-
trackStaticPreviewCompleted(this.telemetry, {
|
|
2608
|
+
(0, variantsTelemetry_1.trackStaticPreviewCompleted)(this.telemetry, {
|
|
3383
2609
|
sessionId,
|
|
3384
2610
|
variantId: workItemId,
|
|
3385
2611
|
durationMs: leasedAt !== undefined ? Date.now() - leasedAt : null,
|
|
3386
2612
|
});
|
|
3387
|
-
trackPreviewReady(this.telemetry, {
|
|
2613
|
+
(0, variantsTelemetry_1.trackPreviewReady)(this.telemetry, {
|
|
3388
2614
|
sessionId,
|
|
3389
2615
|
variantId: workItemId,
|
|
3390
2616
|
projectContextKind: projectContext.kind,
|
|
@@ -3401,7 +2627,7 @@ class AgentVariantsOrchestrator {
|
|
|
3401
2627
|
log.warn(`No worktree record for ${sessionId}/${workItemId}; skipping diff capture`);
|
|
3402
2628
|
return;
|
|
3403
2629
|
}
|
|
3404
|
-
if (
|
|
2630
|
+
if (this.workspaceInventory.isScaffoldBaseWorkItem(sessionId, workItemId)) {
|
|
3405
2631
|
log.info(`scaffold_base ${workItemId} succeeded; awaiting dependent code-gen`);
|
|
3406
2632
|
return;
|
|
3407
2633
|
}
|
|
@@ -3410,7 +2636,7 @@ class AgentVariantsOrchestrator {
|
|
|
3410
2636
|
try {
|
|
3411
2637
|
record.diff =
|
|
3412
2638
|
record.diff ?? (await this.worktrees.getDiff(record.worktreePath));
|
|
3413
|
-
log.info(`Variant ${workItemId} diff captured (${countDiffFiles(record.diff)} files)`);
|
|
2639
|
+
log.info(`Variant ${workItemId} diff captured (${(0, variantWorkspaceFiles_1.countDiffFiles)(record.diff)} files)`);
|
|
3414
2640
|
this.emitChange();
|
|
3415
2641
|
}
|
|
3416
2642
|
catch (err) {
|
|
@@ -3440,10 +2666,10 @@ class AgentVariantsOrchestrator {
|
|
|
3440
2666
|
return;
|
|
3441
2667
|
}
|
|
3442
2668
|
}
|
|
3443
|
-
await this.
|
|
2669
|
+
await this.previewRuntime.startVariant({
|
|
3444
2670
|
sessionId,
|
|
3445
2671
|
workItemId,
|
|
3446
|
-
record,
|
|
2672
|
+
worktreePath: record.worktreePath,
|
|
3447
2673
|
isFresh,
|
|
3448
2674
|
});
|
|
3449
2675
|
}
|
|
@@ -3456,7 +2682,7 @@ class AgentVariantsOrchestrator {
|
|
|
3456
2682
|
if (!input.variantId) {
|
|
3457
2683
|
throw new errors_1.AgentVariantsError('MISSING_REQUIRED_INPUT', 'refine_variant work item is missing variantId');
|
|
3458
2684
|
}
|
|
3459
|
-
const parsed = parseStaticPreviewOutput(normalizeOutput(args.output));
|
|
2685
|
+
const parsed = (0, variantWorkspaceFiles_1.parseStaticPreviewOutput)((0, variantWorkspaceFiles_1.normalizeOutput)(args.output));
|
|
3460
2686
|
if (!parsed) {
|
|
3461
2687
|
throw new errors_1.AgentVariantsError('SCHEMA_VALIDATION_FAILED', 'refine_variant report_variant_complete requires output.html');
|
|
3462
2688
|
}
|
|
@@ -3464,12 +2690,12 @@ class AgentVariantsOrchestrator {
|
|
|
3464
2690
|
if (!targetInput.briefId) {
|
|
3465
2691
|
throw new errors_1.AgentVariantsError('VARIANT_NOT_EDITABLE', 'Target static_preview variant is missing briefId');
|
|
3466
2692
|
}
|
|
3467
|
-
const resolvedAssetBase = resolveStaticPreviewAssetBase({
|
|
2693
|
+
const resolvedAssetBase = (0, variantWorkspaceFiles_1.resolveStaticPreviewAssetBase)({
|
|
3468
2694
|
assetBase: parsed.assetBase,
|
|
3469
2695
|
projectContext: this.store.getProjectContext(args.sessionId),
|
|
3470
2696
|
});
|
|
3471
|
-
const previousRecord =
|
|
3472
|
-
const refinementWorkspace =
|
|
2697
|
+
const previousRecord = this.workspaceInventory.getStaticPreview(args.sessionId, input.variantId);
|
|
2698
|
+
const refinementWorkspace = this.workspaceInventory.getAssignedWorkspace(args.sessionId, args.workItemId);
|
|
3473
2699
|
const assignedArtifactBase = refinementWorkspace?.kind === 'artifact_directory'
|
|
3474
2700
|
? refinementWorkspace.rootPath
|
|
3475
2701
|
: undefined;
|
|
@@ -3488,7 +2714,7 @@ class AgentVariantsOrchestrator {
|
|
|
3488
2714
|
...(dir ? { assetBase: dir } : {}),
|
|
3489
2715
|
});
|
|
3490
2716
|
if (dir) {
|
|
3491
|
-
|
|
2717
|
+
this.workspaceInventory.setStaticPreview(args.sessionId, {
|
|
3492
2718
|
workItemId: input.variantId,
|
|
3493
2719
|
briefId: targetInput.briefId,
|
|
3494
2720
|
entryPath: 'index.html',
|
|
@@ -3500,7 +2726,8 @@ class AgentVariantsOrchestrator {
|
|
|
3500
2726
|
}
|
|
3501
2727
|
if (refinementWorkspace?.kind === 'artifact_directory' &&
|
|
3502
2728
|
dir === refinementWorkspace.rootPath) {
|
|
3503
|
-
|
|
2729
|
+
this.workspaceInventory.promoteArtifactWorkspace({
|
|
2730
|
+
sessionId: args.sessionId,
|
|
3504
2731
|
sourceWorkItemId: args.workItemId,
|
|
3505
2732
|
targetWorkItemId: input.variantId,
|
|
3506
2733
|
});
|
|
@@ -3575,9 +2802,7 @@ class AgentVariantsOrchestrator {
|
|
|
3575
2802
|
!saved.diff?.trim() &&
|
|
3576
2803
|
!saved.files?.length))
|
|
3577
2804
|
return false;
|
|
3578
|
-
const
|
|
3579
|
-
const preview = resources?.staticPreviews.get(variant.workItemId) ??
|
|
3580
|
-
this.getStaticPreviewByBriefId(sessionId, variant.briefId);
|
|
2805
|
+
const preview = this.workspaceInventory.getStaticPreview(sessionId, variant.workItemId) ?? this.getStaticPreviewByBriefId(sessionId, variant.briefId);
|
|
3581
2806
|
if (preview) {
|
|
3582
2807
|
const currentHtml = await fs_1.default.promises.readFile(path_1.default.join(preview.assetBase, preview.entryPath), 'utf8');
|
|
3583
2808
|
const savedHtml = await this.variantHistory.readStaticPreview({
|
|
@@ -3588,7 +2813,7 @@ class AgentVariantsOrchestrator {
|
|
|
3588
2813
|
if (currentHtml !== savedHtml)
|
|
3589
2814
|
return false;
|
|
3590
2815
|
}
|
|
3591
|
-
const diff =
|
|
2816
|
+
const diff = this.workspaceInventory.getWorktree(sessionId, variant.workItemId)?.diff;
|
|
3592
2817
|
if (diff && saved.diff !== diff)
|
|
3593
2818
|
return false;
|
|
3594
2819
|
}
|
|
@@ -3607,9 +2832,7 @@ class AgentVariantsOrchestrator {
|
|
|
3607
2832
|
log.warn(`persistCompletedExistingVariant: resolveEnv failed for ${args.sessionId}`, err);
|
|
3608
2833
|
return;
|
|
3609
2834
|
}
|
|
3610
|
-
const record = this.
|
|
3611
|
-
.get(args.sessionId)
|
|
3612
|
-
?.worktrees.get(args.workItemId);
|
|
2835
|
+
const record = this.workspaceInventory.getWorktree(args.sessionId, args.workItemId);
|
|
3613
2836
|
let sourceDir;
|
|
3614
2837
|
const staticEntryPath = env.framework === 'static' ? env.entryPath : undefined;
|
|
3615
2838
|
if (env.framework === 'static' && record) {
|
|
@@ -3630,23 +2853,24 @@ class AgentVariantsOrchestrator {
|
|
|
3630
2853
|
catch (err) {
|
|
3631
2854
|
log.warn(`persistCompletedExistingVariant: git root resolution failed for ${args.sessionId}`, err);
|
|
3632
2855
|
}
|
|
2856
|
+
const runningPreview = this.previewRuntime.getRunningVariant(args.sessionId, args.workItemId);
|
|
3633
2857
|
let serve;
|
|
3634
2858
|
if (sourceDir && record && staticEntryPath) {
|
|
3635
2859
|
const entryPath = path_1.default.relative(record.worktreePath, path_1.default.join(sourceDir, staticEntryPath));
|
|
3636
2860
|
serve = { kind: 'static', entryPath };
|
|
3637
2861
|
}
|
|
3638
|
-
else if (record
|
|
2862
|
+
else if (record && runningPreview) {
|
|
3639
2863
|
serve = {
|
|
3640
2864
|
kind: 'dev-server',
|
|
3641
|
-
recipe: buildServeRecipeFromResolved(record.worktreePath,
|
|
2865
|
+
recipe: (0, variantWorkspaceFiles_1.buildServeRecipeFromResolved)(record.worktreePath, runningPreview.command, runningPreview.port),
|
|
3642
2866
|
};
|
|
3643
2867
|
}
|
|
3644
2868
|
else if (record) {
|
|
3645
2869
|
try {
|
|
3646
|
-
const dev = await this.
|
|
2870
|
+
const dev = await this.previewRuntime.resolveCommand(args.sessionId, record.worktreePath, RECIPE_PORT_SENTINEL, false);
|
|
3647
2871
|
serve = {
|
|
3648
2872
|
kind: 'dev-server',
|
|
3649
|
-
recipe: buildServeRecipeFromResolved(record.worktreePath, dev, RECIPE_PORT_SENTINEL),
|
|
2873
|
+
recipe: (0, variantWorkspaceFiles_1.buildServeRecipeFromResolved)(record.worktreePath, dev, RECIPE_PORT_SENTINEL),
|
|
3650
2874
|
};
|
|
3651
2875
|
}
|
|
3652
2876
|
catch (err) {
|
|
@@ -3674,7 +2898,7 @@ class AgentVariantsOrchestrator {
|
|
|
3674
2898
|
preview: sourceDir && staticEntryPath
|
|
3675
2899
|
? { kind: 'static', entryPath: staticEntryPath }
|
|
3676
2900
|
: { kind: 'none', reason: 'diff_only' },
|
|
3677
|
-
changedFilesCount: countDiffFiles(args.diff),
|
|
2901
|
+
changedFilesCount: (0, variantWorkspaceFiles_1.countDiffFiles)(args.diff),
|
|
3678
2902
|
projectKind: 'existing',
|
|
3679
2903
|
snapshotDir,
|
|
3680
2904
|
baseDir,
|
|
@@ -3699,7 +2923,7 @@ class AgentVariantsOrchestrator {
|
|
|
3699
2923
|
if (env.framework !== 'static')
|
|
3700
2924
|
return 'not_static';
|
|
3701
2925
|
const previewStartedAt = Date.now();
|
|
3702
|
-
trackPreviewStarted(this.telemetry, {
|
|
2926
|
+
(0, variantsTelemetry_1.trackPreviewStarted)(this.telemetry, {
|
|
3703
2927
|
sessionId,
|
|
3704
2928
|
variantId: workItemId,
|
|
3705
2929
|
projectContextKind: 'existing',
|
|
@@ -3725,7 +2949,7 @@ class AgentVariantsOrchestrator {
|
|
|
3725
2949
|
}
|
|
3726
2950
|
catch (err) {
|
|
3727
2951
|
log.warn(`registerExistingStaticPreview: no entry HTML at ${entryFile} for ${sessionId}/${workItemId}; preview unavailable`, err);
|
|
3728
|
-
trackPreviewFailed(this.telemetry, {
|
|
2952
|
+
(0, variantsTelemetry_1.trackPreviewFailed)(this.telemetry, {
|
|
3729
2953
|
sessionId,
|
|
3730
2954
|
variantId: workItemId,
|
|
3731
2955
|
projectContextKind: 'existing',
|
|
@@ -3734,7 +2958,7 @@ class AgentVariantsOrchestrator {
|
|
|
3734
2958
|
reason: 'missing_static_entry',
|
|
3735
2959
|
durationMs: Date.now() - previewStartedAt,
|
|
3736
2960
|
});
|
|
3737
|
-
|
|
2961
|
+
this.previewRuntime.setFailure(sessionId, workItemId, {
|
|
3738
2962
|
reason: 'missing_static_entry',
|
|
3739
2963
|
portInUse: false,
|
|
3740
2964
|
});
|
|
@@ -3754,7 +2978,7 @@ class AgentVariantsOrchestrator {
|
|
|
3754
2978
|
assetFallbackBase = hostRealRoot;
|
|
3755
2979
|
}
|
|
3756
2980
|
catch { }
|
|
3757
|
-
|
|
2981
|
+
this.workspaceInventory.setStaticPreview(sessionId, {
|
|
3758
2982
|
workItemId,
|
|
3759
2983
|
briefId: input.briefId,
|
|
3760
2984
|
entryPath: env.entryPath,
|
|
@@ -3762,9 +2986,9 @@ class AgentVariantsOrchestrator {
|
|
|
3762
2986
|
hasAssets: true,
|
|
3763
2987
|
...(assetFallbackBase ? { assetFallbackBase } : {}),
|
|
3764
2988
|
});
|
|
3765
|
-
|
|
2989
|
+
this.previewRuntime.clearFailure(sessionId, workItemId);
|
|
3766
2990
|
log.info(`Variant ${workItemId} served as a static preview (existing/static, no dev server; root ${projectCwd})`);
|
|
3767
|
-
trackPreviewReady(this.telemetry, {
|
|
2991
|
+
(0, variantsTelemetry_1.trackPreviewReady)(this.telemetry, {
|
|
3768
2992
|
sessionId,
|
|
3769
2993
|
variantId: workItemId,
|
|
3770
2994
|
projectContextKind: 'existing',
|
|
@@ -3787,7 +3011,7 @@ class AgentVariantsOrchestrator {
|
|
|
3787
3011
|
const env = await this.resolveEnv(args.sessionId);
|
|
3788
3012
|
const entryPath = env.framework === 'static' ? env.entryPath : 'index.html';
|
|
3789
3013
|
const entryFile = path_1.default.resolve(args.sourceDir, entryPath);
|
|
3790
|
-
if (!isStrictlyInside(entryFile, args.sourceDir)) {
|
|
3014
|
+
if (!(0, variantWorkspaceFiles_1.isStrictlyInside)(entryFile, args.sourceDir)) {
|
|
3791
3015
|
throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', 'Artifact entry must be inside its project directory.');
|
|
3792
3016
|
}
|
|
3793
3017
|
const isEntryMapped = entryFile !== path_1.default.join(args.sourceDir, 'index.html');
|
|
@@ -3835,10 +3059,9 @@ class AgentVariantsOrchestrator {
|
|
|
3835
3059
|
: [historyProjectPath];
|
|
3836
3060
|
const input = this.store.getWorkItemInput(args.sessionId, args.workItemId);
|
|
3837
3061
|
const sessionPrompt = this.store.getPrompt(args.sessionId);
|
|
3838
|
-
const designArtifact = resolveDesignArtifact(input.designContextEntry);
|
|
3839
|
-
const
|
|
3840
|
-
const
|
|
3841
|
-
const staticPreview = resources?.staticPreviews.get(args.workItemId);
|
|
3062
|
+
const designArtifact = (0, designContextArtifacts_1.resolveDesignArtifact)(input.designContextEntry);
|
|
3063
|
+
const worktreeRecord = this.workspaceInventory.getWorktree(args.sessionId, args.workItemId);
|
|
3064
|
+
const staticPreview = this.workspaceInventory.getStaticPreview(args.sessionId, args.workItemId);
|
|
3842
3065
|
let sourceDir = null;
|
|
3843
3066
|
let tmpStagingDir = null;
|
|
3844
3067
|
if (worktreeRecord && fs_1.default.existsSync(worktreeRecord.worktreePath)) {
|
|
@@ -3846,7 +3069,7 @@ class AgentVariantsOrchestrator {
|
|
|
3846
3069
|
}
|
|
3847
3070
|
else if (staticPreview) {
|
|
3848
3071
|
tmpStagingDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), `rivet-variant-${args.workItemId}-`));
|
|
3849
|
-
copyStaticPreviewDirectory(staticPreview.assetBase, tmpStagingDir);
|
|
3072
|
+
(0, variantWorkspaceFiles_1.copyStaticPreviewDirectory)(staticPreview.assetBase, tmpStagingDir);
|
|
3850
3073
|
let stagedHtml = '';
|
|
3851
3074
|
try {
|
|
3852
3075
|
stagedHtml = fs_1.default.readFileSync(path_1.default.join(staticPreview.assetBase, 'index.html'), 'utf8');
|
|
@@ -3864,7 +3087,8 @@ class AgentVariantsOrchestrator {
|
|
|
3864
3087
|
log.warn(`persistCompletedArtifactVariant: no source for ${args.workItemId} (session ${args.sessionId}); skipping`);
|
|
3865
3088
|
return;
|
|
3866
3089
|
}
|
|
3867
|
-
const changedFilesCount = staticPreview ? 1 : countWorktreeFiles(sourceDir);
|
|
3090
|
+
const changedFilesCount = staticPreview ? 1 : (0, variantWorkspaceFiles_1.countWorktreeFiles)(sourceDir);
|
|
3091
|
+
const runningPreview = this.previewRuntime.getRunningVariant(args.sessionId, args.workItemId);
|
|
3868
3092
|
const preview = staticPreview
|
|
3869
3093
|
? { kind: 'static', entryPath: 'index.html' }
|
|
3870
3094
|
: {
|
|
@@ -3877,10 +3101,10 @@ class AgentVariantsOrchestrator {
|
|
|
3877
3101
|
};
|
|
3878
3102
|
const serve = staticPreview
|
|
3879
3103
|
? { kind: 'static', entryPath: 'index.html' }
|
|
3880
|
-
: worktreeRecord
|
|
3104
|
+
: worktreeRecord && runningPreview
|
|
3881
3105
|
? {
|
|
3882
3106
|
kind: 'dev-server',
|
|
3883
|
-
recipe: buildServeRecipeFromResolved(worktreeRecord.worktreePath,
|
|
3107
|
+
recipe: (0, variantWorkspaceFiles_1.buildServeRecipeFromResolved)(worktreeRecord.worktreePath, runningPreview.command, runningPreview.port),
|
|
3884
3108
|
}
|
|
3885
3109
|
: {
|
|
3886
3110
|
kind: 'dev-server',
|
|
@@ -3927,7 +3151,7 @@ class AgentVariantsOrchestrator {
|
|
|
3927
3151
|
...(diff !== undefined ? { diff } : {}),
|
|
3928
3152
|
sourceDir,
|
|
3929
3153
|
preview,
|
|
3930
|
-
changedFilesCount: diff === undefined ? changedFilesCount : countDiffFiles(diff),
|
|
3154
|
+
changedFilesCount: diff === undefined ? changedFilesCount : (0, variantWorkspaceFiles_1.countDiffFiles)(diff),
|
|
3931
3155
|
projectKind: projectContext.kind,
|
|
3932
3156
|
designMarkdown: designArtifact?.markdown,
|
|
3933
3157
|
designSource: designArtifact?.source,
|
|
@@ -4080,109 +3304,6 @@ class AgentVariantsOrchestrator {
|
|
|
4080
3304
|
return undefined;
|
|
4081
3305
|
}
|
|
4082
3306
|
}
|
|
4083
|
-
async resolveDevServerStartPort(sessionId, isFresh) {
|
|
4084
|
-
if (isFresh)
|
|
4085
|
-
return FRESH_DEV_SERVER_DEFAULT_PORT;
|
|
4086
|
-
try {
|
|
4087
|
-
const env = await this.resolveEnv(sessionId);
|
|
4088
|
-
return env.framework === 'static' ? 3000 : (env.defaultPort ?? 3000);
|
|
4089
|
-
}
|
|
4090
|
-
catch {
|
|
4091
|
-
return 3000;
|
|
4092
|
-
}
|
|
4093
|
-
}
|
|
4094
|
-
async resolveDevServer(sessionId, worktreePath, port, isFresh) {
|
|
4095
|
-
const resolved = await this.resolveDevServerCommand(sessionId, worktreePath, port, isFresh);
|
|
4096
|
-
await this.ensureProjectDependencies({
|
|
4097
|
-
signal: this.resources.get(sessionId)?.dependencyAbortController.signal,
|
|
4098
|
-
cwd: resolved.cwd,
|
|
4099
|
-
workspaceRoot: worktreePath,
|
|
4100
|
-
});
|
|
4101
|
-
return resolved;
|
|
4102
|
-
}
|
|
4103
|
-
async resolveDevServerCommand(sessionId, worktreePath, port, isFresh) {
|
|
4104
|
-
if (isFresh) {
|
|
4105
|
-
return {
|
|
4106
|
-
cwd: worktreePath,
|
|
4107
|
-
cmd: 'npm',
|
|
4108
|
-
args: [
|
|
4109
|
-
'run',
|
|
4110
|
-
'dev',
|
|
4111
|
-
'--',
|
|
4112
|
-
'--port',
|
|
4113
|
-
String(port),
|
|
4114
|
-
'--host',
|
|
4115
|
-
FRESH_DEV_SERVER_HOST,
|
|
4116
|
-
],
|
|
4117
|
-
env: { PORT: String(port) },
|
|
4118
|
-
packageManager: 'npm',
|
|
4119
|
-
};
|
|
4120
|
-
}
|
|
4121
|
-
const env = await this.resolveEnv(sessionId);
|
|
4122
|
-
if (env.framework === 'static') {
|
|
4123
|
-
throw new Error('Static projects do not have a dev server command');
|
|
4124
|
-
}
|
|
4125
|
-
const cwd = await this.resolveExistingProjectCwd(sessionId, worktreePath, env);
|
|
4126
|
-
if (env.devCommand) {
|
|
4127
|
-
assertDevScriptExists(cwd, env.devCommand);
|
|
4128
|
-
}
|
|
4129
|
-
const detected = fs_1.default.existsSync(path_1.default.join(cwd, 'package.json'))
|
|
4130
|
-
? await (0, ProjectPackageManager_1.resolveProjectPackageManager)({
|
|
4131
|
-
cwd,
|
|
4132
|
-
workspaceRoot: worktreePath,
|
|
4133
|
-
}).catch((error) => {
|
|
4134
|
-
throw new devServerError_1.DevServerStartError({
|
|
4135
|
-
message: error instanceof Error ? error.message : String(error),
|
|
4136
|
-
detail: '',
|
|
4137
|
-
reason: 'dependency_install_failed',
|
|
4138
|
-
portInUse: false,
|
|
4139
|
-
});
|
|
4140
|
-
})
|
|
4141
|
-
: null;
|
|
4142
|
-
const packageManager = detected?.name ?? env.packageManager;
|
|
4143
|
-
if (packageManager !== env.packageManager) {
|
|
4144
|
-
return {
|
|
4145
|
-
cwd,
|
|
4146
|
-
packageManager,
|
|
4147
|
-
...(0, devServerCommand_1.buildDevServerCommand)(env.framework, packageManager, env.devCommand, port),
|
|
4148
|
-
};
|
|
4149
|
-
}
|
|
4150
|
-
if (env.buildDevCommand) {
|
|
4151
|
-
return { cwd, packageManager, ...env.buildDevCommand(port) };
|
|
4152
|
-
}
|
|
4153
|
-
return {
|
|
4154
|
-
cwd,
|
|
4155
|
-
cmd: env.packageManager,
|
|
4156
|
-
args: [env.devCommand, '--port', String(port)],
|
|
4157
|
-
env: { PORT: String(port) },
|
|
4158
|
-
packageManager,
|
|
4159
|
-
};
|
|
4160
|
-
}
|
|
4161
|
-
async ensureProjectDependencies(args) {
|
|
4162
|
-
if (!fs_1.default.existsSync(path_1.default.join(args.cwd, 'package.json')))
|
|
4163
|
-
return;
|
|
4164
|
-
await this.installDependencies(args.cwd, {
|
|
4165
|
-
workspaceRoot: args.workspaceRoot,
|
|
4166
|
-
signal: args.signal,
|
|
4167
|
-
});
|
|
4168
|
-
}
|
|
4169
|
-
async resolveExistingProjectCwd(sessionId, worktreePath, env) {
|
|
4170
|
-
const fallback = await this.worktrees.getProjectCwdInWorktree(worktreePath);
|
|
4171
|
-
try {
|
|
4172
|
-
const projectContext = this.store.getProjectContext(sessionId);
|
|
4173
|
-
if (projectContext.kind !== 'existing' || !projectContext.projectPath) {
|
|
4174
|
-
return fallback;
|
|
4175
|
-
}
|
|
4176
|
-
const rel = path_1.default.relative(path_1.default.resolve(env.projectPath), path_1.default.resolve(projectContext.projectPath));
|
|
4177
|
-
if (!rel || rel.startsWith('..') || path_1.default.isAbsolute(rel)) {
|
|
4178
|
-
return fallback;
|
|
4179
|
-
}
|
|
4180
|
-
return path_1.default.join(worktreePath, rel);
|
|
4181
|
-
}
|
|
4182
|
-
catch {
|
|
4183
|
-
return fallback;
|
|
4184
|
-
}
|
|
4185
|
-
}
|
|
4186
3307
|
preserveUnchosenVariants(args) {
|
|
4187
3308
|
const resources = this.resources.get(args.sessionId);
|
|
4188
3309
|
const runnablePaths = new Map();
|
|
@@ -4216,7 +3337,7 @@ class AgentVariantsOrchestrator {
|
|
|
4216
3337
|
});
|
|
4217
3338
|
continue;
|
|
4218
3339
|
}
|
|
4219
|
-
const record =
|
|
3340
|
+
const record = this.workspaceInventory.getWorktree(args.sessionId, variant.workItemId);
|
|
4220
3341
|
if (!record) {
|
|
4221
3342
|
manifestEntries.push({
|
|
4222
3343
|
variantId: variant.workItemId,
|
|
@@ -4281,7 +3402,7 @@ class AgentVariantsOrchestrator {
|
|
|
4281
3402
|
catch (err) {
|
|
4282
3403
|
log.warn(`Writing variants history manifest failed for ${historyDir}`, err);
|
|
4283
3404
|
}
|
|
4284
|
-
|
|
3405
|
+
this.workspaceInventory.preserveViteSibling(args.sessionId);
|
|
4285
3406
|
return runnablePaths;
|
|
4286
3407
|
}
|
|
4287
3408
|
async renameWorktreeIntoDestination(sourceWorktreePath, destinationPath) {
|
|
@@ -4357,218 +3478,31 @@ class AgentVariantsOrchestrator {
|
|
|
4357
3478
|
log.warn(`Initial git history setup at ${destinationPath} failed`, err);
|
|
4358
3479
|
}
|
|
4359
3480
|
}
|
|
4360
|
-
async stopAllDevServersForSession(resources) {
|
|
4361
|
-
this.clearActivePreviewIdleTimer(resources);
|
|
4362
|
-
const stops = [];
|
|
4363
|
-
for (const record of resources.worktrees.values()) {
|
|
4364
|
-
if (record.pendingAutoRestart) {
|
|
4365
|
-
clearTimeout(record.pendingAutoRestart);
|
|
4366
|
-
record.pendingAutoRestart = undefined;
|
|
4367
|
-
}
|
|
4368
|
-
if (!record.devServerProcess)
|
|
4369
|
-
continue;
|
|
4370
|
-
const proc = record.devServerProcess;
|
|
4371
|
-
stops.push(this.worktrees
|
|
4372
|
-
.stopDevServer(proc)
|
|
4373
|
-
.catch((err) => log.warn(`stopDevServer pre-commit failed for ${resources.sessionId}/${record.workItemId}`, err)));
|
|
4374
|
-
record.devServerProcess = undefined;
|
|
4375
|
-
record.port = undefined;
|
|
4376
|
-
}
|
|
4377
|
-
await Promise.all(stops);
|
|
4378
|
-
return stops.length;
|
|
4379
|
-
}
|
|
4380
|
-
scheduleActivePreviewIdleTimer(resources) {
|
|
4381
|
-
if (!Number.isFinite(this.activeVariantServerIdleTtlMs) ||
|
|
4382
|
-
this.activeVariantServerIdleTtlMs <= 0) {
|
|
4383
|
-
return;
|
|
4384
|
-
}
|
|
4385
|
-
this.clearActivePreviewIdleTimer(resources);
|
|
4386
|
-
resources.activePreviewIdleTimer = setTimeout(() => {
|
|
4387
|
-
resources.activePreviewIdleTimer = undefined;
|
|
4388
|
-
void this.stopIdleVariantDevServers(resources.sessionId);
|
|
4389
|
-
}, this.activeVariantServerIdleTtlMs);
|
|
4390
|
-
resources.activePreviewIdleTimer.unref?.();
|
|
4391
|
-
}
|
|
4392
|
-
clearActivePreviewIdleTimer(resources) {
|
|
4393
|
-
if (!resources.activePreviewIdleTimer)
|
|
4394
|
-
return;
|
|
4395
|
-
clearTimeout(resources.activePreviewIdleTimer);
|
|
4396
|
-
resources.activePreviewIdleTimer = undefined;
|
|
4397
|
-
}
|
|
4398
|
-
async stopIdleVariantDevServers(sessionId) {
|
|
4399
|
-
const resources = this.resources.get(sessionId);
|
|
4400
|
-
if (!resources || resources.cleanupStarted)
|
|
4401
|
-
return;
|
|
4402
|
-
if (this.uiClientCount > 0) {
|
|
4403
|
-
this.scheduleActivePreviewIdleTimer(resources);
|
|
4404
|
-
return;
|
|
4405
|
-
}
|
|
4406
|
-
if (resources.activePreviewIdleStop) {
|
|
4407
|
-
await resources.activePreviewIdleStop;
|
|
4408
|
-
return;
|
|
4409
|
-
}
|
|
4410
|
-
const stop = (async () => {
|
|
4411
|
-
const expiringRecords = Array.from(resources.worktrees.values()).filter((record) => Boolean(record.devServerProcess) ||
|
|
4412
|
-
Boolean(record.pendingAutoRestart));
|
|
4413
|
-
const expiringWorkItemIds = expiringRecords.map((record) => record.workItemId);
|
|
4414
|
-
const pendingRestartCount = expiringRecords.filter((record) => Boolean(record.pendingAutoRestart)).length;
|
|
4415
|
-
const stoppedCount = await this.stopAllDevServersForSession(resources);
|
|
4416
|
-
if (expiringWorkItemIds.length === 0)
|
|
4417
|
-
return;
|
|
4418
|
-
for (const workItemId of expiringWorkItemIds) {
|
|
4419
|
-
resources.previewFailures.set(workItemId, {
|
|
4420
|
-
reason: 'idle_timeout',
|
|
4421
|
-
portInUse: false,
|
|
4422
|
-
});
|
|
4423
|
-
}
|
|
4424
|
-
log.info(`Reclaimed ${expiringWorkItemIds.length} idle variant preview(s) for session ${sessionId} ` +
|
|
4425
|
-
`(${stoppedCount} running, ${pendingRestartCount} restarting)`);
|
|
4426
|
-
try {
|
|
4427
|
-
this.switchPreviewPort?.(null);
|
|
4428
|
-
}
|
|
4429
|
-
catch (err) {
|
|
4430
|
-
log.warn(`switchPreviewPort(null) after idle variant server cleanup failed for ${sessionId}`, err);
|
|
4431
|
-
}
|
|
4432
|
-
this.emitChange();
|
|
4433
|
-
})().finally(() => {
|
|
4434
|
-
if (resources.activePreviewIdleStop === stop) {
|
|
4435
|
-
resources.activePreviewIdleStop = undefined;
|
|
4436
|
-
}
|
|
4437
|
-
});
|
|
4438
|
-
resources.activePreviewIdleStop = stop;
|
|
4439
|
-
await stop;
|
|
4440
|
-
}
|
|
4441
|
-
async startCommittedDevServer(args) {
|
|
4442
|
-
const mode = args.mode ?? 'vite_app';
|
|
4443
|
-
try {
|
|
4444
|
-
const port = await this.worktrees.getFreePort();
|
|
4445
|
-
const proc = mode === 'static_preview'
|
|
4446
|
-
? await this.startStaticPreviewServerImpl({
|
|
4447
|
-
rootPath: args.destinationPath,
|
|
4448
|
-
port,
|
|
4449
|
-
host: FRESH_DEV_SERVER_HOST,
|
|
4450
|
-
})
|
|
4451
|
-
: await this.worktrees.startDevServer(args.destinationPath, port, 'npm', [
|
|
4452
|
-
'run',
|
|
4453
|
-
'dev',
|
|
4454
|
-
'--',
|
|
4455
|
-
'--port',
|
|
4456
|
-
String(port),
|
|
4457
|
-
'--host',
|
|
4458
|
-
FRESH_DEV_SERVER_HOST,
|
|
4459
|
-
], { PORT: String(port) });
|
|
4460
|
-
args.resources.committedDevServer = {
|
|
4461
|
-
proc,
|
|
4462
|
-
port,
|
|
4463
|
-
path: args.destinationPath,
|
|
4464
|
-
};
|
|
4465
|
-
try {
|
|
4466
|
-
this.setCommittedDevServerHealth?.(port);
|
|
4467
|
-
}
|
|
4468
|
-
catch (err) {
|
|
4469
|
-
log.warn(`setCommittedDevServerHealth(${port}) after committed dev server start failed`, err);
|
|
4470
|
-
}
|
|
4471
|
-
try {
|
|
4472
|
-
this.switchPreviewPort?.(port);
|
|
4473
|
-
}
|
|
4474
|
-
catch (err) {
|
|
4475
|
-
log.warn(`switchPreviewPort(${port}) after committed dev server start failed`, err);
|
|
4476
|
-
}
|
|
4477
|
-
log.info(`Committed ${mode} dev server up at ${args.destinationPath} on port ${port}`);
|
|
4478
|
-
return port;
|
|
4479
|
-
}
|
|
4480
|
-
catch (err) {
|
|
4481
|
-
log.warn(`Failed to start committed ${mode} dev server at ${args.destinationPath} — iframe may show "preview disconnected" until user runs the dev server manually`, err);
|
|
4482
|
-
return undefined;
|
|
4483
|
-
}
|
|
4484
|
-
}
|
|
4485
|
-
async stopLingeringCommittedDevServers() {
|
|
4486
|
-
const entries = [...this.lingeringCommittedDevServers.entries()];
|
|
4487
|
-
this.lingeringCommittedDevServers.clear();
|
|
4488
|
-
if (entries.length > 0) {
|
|
4489
|
-
try {
|
|
4490
|
-
this.setCommittedDevServerHealth?.(null);
|
|
4491
|
-
}
|
|
4492
|
-
catch (err) {
|
|
4493
|
-
log.warn('setCommittedDevServerHealth(null) on lingering teardown failed', err);
|
|
4494
|
-
}
|
|
4495
|
-
}
|
|
4496
|
-
await Promise.all(entries.map(async ([sessionId, entry]) => {
|
|
4497
|
-
try {
|
|
4498
|
-
await this.worktrees.stopDevServer(entry.proc);
|
|
4499
|
-
}
|
|
4500
|
-
catch (err) {
|
|
4501
|
-
log.warn(`stopDevServer for lingering committed server (session ${sessionId}, port ${entry.port}) failed`, err);
|
|
4502
|
-
}
|
|
4503
|
-
}));
|
|
4504
|
-
}
|
|
4505
3481
|
async teardownSession(sessionId, reason) {
|
|
4506
3482
|
const resources = this.resources.get(sessionId);
|
|
4507
3483
|
if (!resources)
|
|
4508
3484
|
return;
|
|
4509
|
-
if (
|
|
3485
|
+
if (!this.workspaceInventory.beginCleanup(sessionId))
|
|
4510
3486
|
return;
|
|
4511
|
-
resources.cleanupStarted = true;
|
|
4512
|
-
this.clearActivePreviewIdleTimer(resources);
|
|
4513
3487
|
log.info(`Tearing down session ${sessionId} (reason: ${reason})`);
|
|
4514
|
-
|
|
4515
|
-
|
|
3488
|
+
await Promise.all(this.workspaceInventory
|
|
3489
|
+
.getWorktrees(sessionId)
|
|
3490
|
+
.map((record) => DependencyPreparationService_1.previewDependencies.settle(record.worktreePath)));
|
|
4516
3491
|
await Promise.race([
|
|
4517
3492
|
this.settleHistoryPersists(),
|
|
4518
3493
|
new Promise((resolve) => setTimeout(resolve, SETTLE_PERSISTS_TEARDOWN_TIMEOUT_MS)),
|
|
4519
3494
|
]);
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
.
|
|
4524
|
-
.catch((err) => log.warn(`stopDevServer failed for ${sessionId}/${r.workItemId}`, err)));
|
|
4525
|
-
await Promise.all(stops);
|
|
4526
|
-
if (resources.committedDevServer && reason === 'committed') {
|
|
4527
|
-
this.lingeringCommittedDevServers.set(sessionId, resources.committedDevServer);
|
|
4528
|
-
}
|
|
4529
|
-
else if (resources.committedDevServer) {
|
|
4530
|
-
try {
|
|
4531
|
-
await this.worktrees.stopDevServer(resources.committedDevServer.proc);
|
|
4532
|
-
}
|
|
4533
|
-
catch (err) {
|
|
4534
|
-
log.warn(`stopDevServer for committed dev server (session ${sessionId}) failed`, err);
|
|
4535
|
-
}
|
|
4536
|
-
}
|
|
4537
|
-
if (!resources.vitePreservedSiblings) {
|
|
4538
|
-
try {
|
|
4539
|
-
await this.worktrees.cleanupSession(sessionId, resources.freshDestinationParent);
|
|
4540
|
-
}
|
|
4541
|
-
catch (err) {
|
|
4542
|
-
log.warn(`cleanupSession (worktree removal) failed for ${sessionId}`, err);
|
|
4543
|
-
}
|
|
4544
|
-
}
|
|
4545
|
-
const ownedArtifactRoots = new Set(resources.workspaceManager.getOwnedArtifactRoots());
|
|
4546
|
-
resources.workspaceManager.removeOwnedArtifactWorkspaces();
|
|
4547
|
-
const staticDirectories = new Set();
|
|
4548
|
-
if (reason !== 'committed') {
|
|
4549
|
-
let isFresh = false;
|
|
4550
|
-
try {
|
|
4551
|
-
isFresh = this.store.getProjectContext(sessionId).kind === 'fresh';
|
|
4552
|
-
}
|
|
4553
|
-
catch {
|
|
4554
|
-
isFresh = false;
|
|
4555
|
-
}
|
|
4556
|
-
if (isFresh) {
|
|
4557
|
-
for (const preview of resources.staticPreviews.values()) {
|
|
4558
|
-
if (!ownedArtifactRoots.has(preview.assetBase)) {
|
|
4559
|
-
staticDirectories.add(preview.assetBase);
|
|
4560
|
-
}
|
|
4561
|
-
}
|
|
4562
|
-
}
|
|
4563
|
-
}
|
|
4564
|
-
for (const staticDirectory of staticDirectories) {
|
|
4565
|
-
try {
|
|
4566
|
-
fs_1.default.rmSync(staticDirectory, { recursive: true, force: true });
|
|
4567
|
-
}
|
|
4568
|
-
catch (err) {
|
|
4569
|
-
log.warn(`failed to remove static preview dir ${staticDirectory} for ${sessionId}`, err);
|
|
4570
|
-
}
|
|
3495
|
+
await this.previewRuntime.closeSession(sessionId, reason);
|
|
3496
|
+
let isFresh = false;
|
|
3497
|
+
try {
|
|
3498
|
+
isFresh = this.store.getProjectContext(sessionId).kind === 'fresh';
|
|
4571
3499
|
}
|
|
3500
|
+
catch { }
|
|
3501
|
+
await this.workspaceInventory.disposeAssignments({
|
|
3502
|
+
sessionId,
|
|
3503
|
+
isFresh,
|
|
3504
|
+
isCommitted: reason === 'committed',
|
|
3505
|
+
});
|
|
4572
3506
|
this.resources.delete(sessionId);
|
|
4573
3507
|
}
|
|
4574
3508
|
ensureResources(sessionId) {
|
|
@@ -4576,12 +3510,6 @@ class AgentVariantsOrchestrator {
|
|
|
4576
3510
|
if (!r) {
|
|
4577
3511
|
r = {
|
|
4578
3512
|
sessionId,
|
|
4579
|
-
worktrees: new Map(),
|
|
4580
|
-
staticPreviews: new Map(),
|
|
4581
|
-
workspaceManager: new VariantWorkspaceManager_1.VariantWorkspaceManager(),
|
|
4582
|
-
contextAssets: new Map(),
|
|
4583
|
-
cleanupStarted: false,
|
|
4584
|
-
dependencyAbortController: new AbortController(),
|
|
4585
3513
|
committedVariantIds: new Set(),
|
|
4586
3514
|
startedAt: Date.now(),
|
|
4587
3515
|
nextRoundIndex: 0,
|
|
@@ -4590,964 +3518,13 @@ class AgentVariantsOrchestrator {
|
|
|
4590
3518
|
qaResults: new Map(),
|
|
4591
3519
|
qaRegenRetried: new Set(),
|
|
4592
3520
|
repairCompletionResults: new Map(),
|
|
4593
|
-
previewFailures: new Map(),
|
|
4594
|
-
vitePreservedSiblings: false,
|
|
4595
3521
|
};
|
|
4596
3522
|
this.resources.set(sessionId, r);
|
|
3523
|
+
this.workspaceInventory.openSession(sessionId);
|
|
3524
|
+
this.previewRuntime.openSession(sessionId);
|
|
4597
3525
|
}
|
|
4598
3526
|
return r;
|
|
4599
3527
|
}
|
|
4600
3528
|
}
|
|
4601
3529
|
exports.AgentVariantsOrchestrator = AgentVariantsOrchestrator;
|
|
4602
|
-
function estimateOutputBytes(output) {
|
|
4603
|
-
if (output === undefined || output === null)
|
|
4604
|
-
return 0;
|
|
4605
|
-
try {
|
|
4606
|
-
return typeof output === 'string'
|
|
4607
|
-
? output.length
|
|
4608
|
-
: JSON.stringify(output).length;
|
|
4609
|
-
}
|
|
4610
|
-
catch {
|
|
4611
|
-
return 0;
|
|
4612
|
-
}
|
|
4613
|
-
}
|
|
4614
|
-
function normalizeOutput(output) {
|
|
4615
|
-
if (typeof output === 'string') {
|
|
4616
|
-
try {
|
|
4617
|
-
return JSON.parse(output);
|
|
4618
|
-
}
|
|
4619
|
-
catch {
|
|
4620
|
-
return output;
|
|
4621
|
-
}
|
|
4622
|
-
}
|
|
4623
|
-
return output;
|
|
4624
|
-
}
|
|
4625
|
-
function isStrictlyInside(child, parent) {
|
|
4626
|
-
const c = path_1.default.resolve(child);
|
|
4627
|
-
const p = path_1.default.resolve(parent);
|
|
4628
|
-
return c !== p && c.startsWith(p + path_1.default.sep);
|
|
4629
|
-
}
|
|
4630
|
-
function resolveAssetWithinBase(base, requestedPath) {
|
|
4631
|
-
const target = path_1.default.resolve(base, '.' + path_1.default.sep + requestedPath);
|
|
4632
|
-
if (target !== base && !target.startsWith(base + path_1.default.sep)) {
|
|
4633
|
-
return undefined;
|
|
4634
|
-
}
|
|
4635
|
-
let realTarget;
|
|
4636
|
-
try {
|
|
4637
|
-
realTarget = fs_1.default.realpathSync(target);
|
|
4638
|
-
}
|
|
4639
|
-
catch {
|
|
4640
|
-
return undefined;
|
|
4641
|
-
}
|
|
4642
|
-
if (realTarget !== base && !realTarget.startsWith(base + path_1.default.sep)) {
|
|
4643
|
-
return undefined;
|
|
4644
|
-
}
|
|
4645
|
-
try {
|
|
4646
|
-
const stat = fs_1.default.statSync(realTarget);
|
|
4647
|
-
if (!stat.isFile())
|
|
4648
|
-
return undefined;
|
|
4649
|
-
if (stat.nlink !== 1)
|
|
4650
|
-
return undefined;
|
|
4651
|
-
return realTarget;
|
|
4652
|
-
}
|
|
4653
|
-
catch {
|
|
4654
|
-
return undefined;
|
|
4655
|
-
}
|
|
4656
|
-
}
|
|
4657
|
-
function resolveStaticPreviewAssetBase(args) {
|
|
4658
|
-
if (!args.assetBase)
|
|
4659
|
-
return undefined;
|
|
4660
|
-
if (args.projectContext.kind !== 'fresh')
|
|
4661
|
-
return undefined;
|
|
4662
|
-
let workspace;
|
|
4663
|
-
let resolved;
|
|
4664
|
-
try {
|
|
4665
|
-
const wsStat = fs_1.default.lstatSync(args.projectContext.workspacePath);
|
|
4666
|
-
if (wsStat.isSymbolicLink()) {
|
|
4667
|
-
log.warn(`Static preview workspacePath ${args.projectContext.workspacePath} is a symlink — refusing assetBase`);
|
|
4668
|
-
return undefined;
|
|
4669
|
-
}
|
|
4670
|
-
const realWorkspaceRoot = fs_1.default.realpathSync(args.projectContext.workspaceRoot);
|
|
4671
|
-
workspace = fs_1.default.realpathSync(args.projectContext.workspacePath);
|
|
4672
|
-
if (!workspace.startsWith(realWorkspaceRoot + path_1.default.sep)) {
|
|
4673
|
-
log.warn(`Static preview workspacePath ${workspace} is not a strict descendant of workspaceRoot ${realWorkspaceRoot} — refusing assetBase`);
|
|
4674
|
-
return undefined;
|
|
4675
|
-
}
|
|
4676
|
-
const candidate = path_1.default.isAbsolute(args.assetBase)
|
|
4677
|
-
? args.assetBase
|
|
4678
|
-
: path_1.default.join(workspace, args.assetBase);
|
|
4679
|
-
resolved = fs_1.default.realpathSync(candidate);
|
|
4680
|
-
}
|
|
4681
|
-
catch {
|
|
4682
|
-
log.warn(`Static preview assetBase ${args.assetBase} could not be resolved — ignoring`);
|
|
4683
|
-
return undefined;
|
|
4684
|
-
}
|
|
4685
|
-
if (resolved !== workspace && !resolved.startsWith(workspace + path_1.default.sep)) {
|
|
4686
|
-
log.warn(`Static preview assetBase ${args.assetBase} escapes workspace ${workspace} — ignoring`);
|
|
4687
|
-
return undefined;
|
|
4688
|
-
}
|
|
4689
|
-
try {
|
|
4690
|
-
const stat = fs_1.default.statSync(resolved);
|
|
4691
|
-
if (!stat.isDirectory()) {
|
|
4692
|
-
log.warn(`Static preview assetBase ${resolved} is not a directory — ignoring`);
|
|
4693
|
-
return undefined;
|
|
4694
|
-
}
|
|
4695
|
-
return resolved;
|
|
4696
|
-
}
|
|
4697
|
-
catch {
|
|
4698
|
-
log.warn(`Static preview assetBase ${resolved} does not exist — ignoring`);
|
|
4699
|
-
return undefined;
|
|
4700
|
-
}
|
|
4701
|
-
}
|
|
4702
|
-
function parseStaticPreviewOutput(output) {
|
|
4703
|
-
if (!output || typeof output !== 'object')
|
|
4704
|
-
return null;
|
|
4705
|
-
const html = output.html;
|
|
4706
|
-
if (typeof html !== 'string' || html.trim().length === 0)
|
|
4707
|
-
return null;
|
|
4708
|
-
const css = output.css;
|
|
4709
|
-
const js = output.js;
|
|
4710
|
-
const assetBase = output.assetBase;
|
|
4711
|
-
return {
|
|
4712
|
-
html: buildStaticPreviewDocument({
|
|
4713
|
-
html,
|
|
4714
|
-
css: typeof css === 'string' ? css : undefined,
|
|
4715
|
-
js: typeof js === 'string' ? js : undefined,
|
|
4716
|
-
}),
|
|
4717
|
-
...(typeof assetBase === 'string' && assetBase.length > 0
|
|
4718
|
-
? { assetBase }
|
|
4719
|
-
: {}),
|
|
4720
|
-
};
|
|
4721
|
-
}
|
|
4722
|
-
function buildStaticPreviewDocument(input) {
|
|
4723
|
-
const style = input.css ? `<style>\n${input.css}\n</style>` : '';
|
|
4724
|
-
const script = input.js ? `<script>\n${input.js}\n</script>` : '';
|
|
4725
|
-
if (/<!doctype html>|<html[\s>]/i.test(input.html)) {
|
|
4726
|
-
let doc = input.html;
|
|
4727
|
-
if (style) {
|
|
4728
|
-
if (/<\/head>/i.test(doc)) {
|
|
4729
|
-
doc = doc.replace(/<\/head>/i, () => `${style}\n</head>`);
|
|
4730
|
-
}
|
|
4731
|
-
else {
|
|
4732
|
-
doc += `\n${style}`;
|
|
4733
|
-
}
|
|
4734
|
-
}
|
|
4735
|
-
if (script) {
|
|
4736
|
-
if (/<\/body>/i.test(doc)) {
|
|
4737
|
-
doc = doc.replace(/<\/body>/i, () => `${script}\n</body>`);
|
|
4738
|
-
}
|
|
4739
|
-
else {
|
|
4740
|
-
doc += `\n${script}`;
|
|
4741
|
-
}
|
|
4742
|
-
}
|
|
4743
|
-
return doc;
|
|
4744
|
-
}
|
|
4745
|
-
return `<!doctype html>
|
|
4746
|
-
<html lang="en">
|
|
4747
|
-
<head>
|
|
4748
|
-
<meta charset="UTF-8" />
|
|
4749
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
4750
|
-
${style}
|
|
4751
|
-
</head>
|
|
4752
|
-
<body>
|
|
4753
|
-
${input.html}
|
|
4754
|
-
${script}
|
|
4755
|
-
</body>
|
|
4756
|
-
</html>`;
|
|
4757
|
-
}
|
|
4758
|
-
function countDiffFiles(diff) {
|
|
4759
|
-
return (diff.match(/^diff --git /gm) ?? []).length;
|
|
4760
|
-
}
|
|
4761
|
-
function countWorktreeFiles(destinationPath) {
|
|
4762
|
-
let count = 0;
|
|
4763
|
-
const walk = (dir) => {
|
|
4764
|
-
let entries;
|
|
4765
|
-
try {
|
|
4766
|
-
entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
4767
|
-
}
|
|
4768
|
-
catch {
|
|
4769
|
-
return;
|
|
4770
|
-
}
|
|
4771
|
-
for (const entry of entries) {
|
|
4772
|
-
if (MATERIALIZE_EXCLUDE.has(entry.name))
|
|
4773
|
-
continue;
|
|
4774
|
-
const full = path_1.default.join(dir, entry.name);
|
|
4775
|
-
if (entry.isDirectory())
|
|
4776
|
-
walk(full);
|
|
4777
|
-
else if (entry.isFile())
|
|
4778
|
-
count += 1;
|
|
4779
|
-
}
|
|
4780
|
-
};
|
|
4781
|
-
walk(destinationPath);
|
|
4782
|
-
return count;
|
|
4783
|
-
}
|
|
4784
|
-
const enrichDesignSource = (design) => {
|
|
4785
|
-
if (!design || design.kind !== 'slug')
|
|
4786
|
-
return design;
|
|
4787
|
-
return {
|
|
4788
|
-
...design,
|
|
4789
|
-
displayName: (0, designCatalog_1.getDesignSystemBySlug)(design.slug)?.name ?? design.slug,
|
|
4790
|
-
};
|
|
4791
|
-
};
|
|
4792
|
-
const toActiveProjectContext = (projectContext) => {
|
|
4793
|
-
const sourceCount = projectContext.variantContext?.userContext.filter((source) => source.transport !== 'prompt').length ?? 0;
|
|
4794
|
-
const hasSourceContextSummary = Boolean(sourceCount > 0 ||
|
|
4795
|
-
(projectContext.variantContext?.rivetMetadata.length ?? 0) > 0);
|
|
4796
|
-
const activeSourceContext = hasSourceContextSummary
|
|
4797
|
-
? {
|
|
4798
|
-
sourceCount,
|
|
4799
|
-
isSourceGrounded: Boolean(sourceCount > 0 ||
|
|
4800
|
-
projectContext.designContext?.length ||
|
|
4801
|
-
projectContext.variantContext?.rivetMetadata.length),
|
|
4802
|
-
}
|
|
4803
|
-
: undefined;
|
|
4804
|
-
if (projectContext.kind === 'existing') {
|
|
4805
|
-
return {
|
|
4806
|
-
kind: 'existing',
|
|
4807
|
-
designContext: projectContext.designContext?.map((entry) => entry.kind === 'slug'
|
|
4808
|
-
? { kind: 'slug', slug: entry.slug }
|
|
4809
|
-
: { kind: 'markdown', label: entry.label }),
|
|
4810
|
-
...(activeSourceContext ? { sourceContext: activeSourceContext } : {}),
|
|
4811
|
-
};
|
|
4812
|
-
}
|
|
4813
|
-
return {
|
|
4814
|
-
kind: 'fresh',
|
|
4815
|
-
workspacePath: projectContext.workspacePath,
|
|
4816
|
-
workspaceRoot: projectContext.workspaceRoot,
|
|
4817
|
-
framework: projectContext.framework,
|
|
4818
|
-
designContext: projectContext.designContext?.map((entry) => entry.kind === 'slug'
|
|
4819
|
-
? { kind: 'slug', slug: entry.slug }
|
|
4820
|
-
: { kind: 'markdown', label: entry.label }),
|
|
4821
|
-
...(activeSourceContext ? { sourceContext: activeSourceContext } : {}),
|
|
4822
|
-
...(projectContext.executionPlan
|
|
4823
|
-
? {
|
|
4824
|
-
executionPlan: {
|
|
4825
|
-
mode: projectContext.executionPlan.mode,
|
|
4826
|
-
confidence: projectContext.executionPlan.confidence,
|
|
4827
|
-
reason: projectContext.executionPlan.reason,
|
|
4828
|
-
assetCount: projectContext.executionPlan.assetPlan?.length ?? 0,
|
|
4829
|
-
runtimeRequirementCount: projectContext.executionPlan.runtimeRequirements?.length ?? 0,
|
|
4830
|
-
},
|
|
4831
|
-
}
|
|
4832
|
-
: {}),
|
|
4833
|
-
};
|
|
4834
|
-
};
|
|
4835
|
-
const buildSessionArtifacts = (sessionId, projectContext) => {
|
|
4836
|
-
const artifacts = buildResolvedDesignContextArtifacts(projectContext);
|
|
4837
|
-
return artifacts.map(({ content: _content, ...artifact }) => ({
|
|
4838
|
-
...artifact,
|
|
4839
|
-
rawUrl: buildDesignContextRawUrl(sessionId, artifact.id),
|
|
4840
|
-
viewUrl: buildDesignContextViewUrl(sessionId, artifact.id),
|
|
4841
|
-
}));
|
|
4842
|
-
};
|
|
4843
|
-
const findDesignContextArtifact = (projectContext, artifactId) => {
|
|
4844
|
-
return buildResolvedDesignContextArtifacts(projectContext).find((artifact) => artifact.id === artifactId);
|
|
4845
|
-
};
|
|
4846
|
-
const buildResolvedDesignContextArtifacts = (projectContext) => {
|
|
4847
|
-
const designContext = projectContext.designContext;
|
|
4848
|
-
if (!designContext || designContext.length === 0)
|
|
4849
|
-
return [];
|
|
4850
|
-
const artifactsByContent = new Map();
|
|
4851
|
-
designContext.forEach((entry, slot) => {
|
|
4852
|
-
if (!entry)
|
|
4853
|
-
return;
|
|
4854
|
-
if (entry.kind === 'slug') {
|
|
4855
|
-
const markdown = (0, designCatalog_1.loadDesignSystemMarkdown)(entry.slug);
|
|
4856
|
-
if (!markdown)
|
|
4857
|
-
return;
|
|
4858
|
-
const catalogEntry = (0, designCatalog_1.getDesignSystemBySlug)(entry.slug);
|
|
4859
|
-
addDesignContextArtifact(artifactsByContent, {
|
|
4860
|
-
id: `design_context:${slot}:${entry.slug}`,
|
|
4861
|
-
kind: 'design_context',
|
|
4862
|
-
label: catalogEntry?.name ?? entry.slug,
|
|
4863
|
-
...(catalogEntry?.description
|
|
4864
|
-
? { summary: catalogEntry.description }
|
|
4865
|
-
: {}),
|
|
4866
|
-
status: 'ready',
|
|
4867
|
-
source: 'static',
|
|
4868
|
-
contentType: 'text/markdown',
|
|
4869
|
-
content: markdown,
|
|
4870
|
-
});
|
|
4871
|
-
return;
|
|
4872
|
-
}
|
|
4873
|
-
if (!entry.content)
|
|
4874
|
-
return;
|
|
4875
|
-
addDesignContextArtifact(artifactsByContent, {
|
|
4876
|
-
id: `design_context:${slot}:${entry.label}`,
|
|
4877
|
-
kind: 'design_context',
|
|
4878
|
-
label: entry.label,
|
|
4879
|
-
status: 'ready',
|
|
4880
|
-
source: 'agent_browser',
|
|
4881
|
-
contentType: 'text/markdown',
|
|
4882
|
-
content: entry.content,
|
|
4883
|
-
});
|
|
4884
|
-
});
|
|
4885
|
-
return [...artifactsByContent.values()].map((artifact) => {
|
|
4886
|
-
if (artifact.usedByVariantCount <= 1)
|
|
4887
|
-
return artifact;
|
|
4888
|
-
return {
|
|
4889
|
-
...artifact,
|
|
4890
|
-
summary: artifact.summary
|
|
4891
|
-
? `${artifact.summary} Used by ${artifact.usedByVariantCount} directions.`
|
|
4892
|
-
: `Used by ${artifact.usedByVariantCount} directions.`,
|
|
4893
|
-
};
|
|
4894
|
-
});
|
|
4895
|
-
};
|
|
4896
|
-
const addDesignContextArtifact = (artifactsByContent, artifact) => {
|
|
4897
|
-
const existing = artifactsByContent.get(artifact.content);
|
|
4898
|
-
if (existing) {
|
|
4899
|
-
existing.usedByVariantCount += 1;
|
|
4900
|
-
return;
|
|
4901
|
-
}
|
|
4902
|
-
artifactsByContent.set(artifact.content, {
|
|
4903
|
-
...artifact,
|
|
4904
|
-
usedByVariantCount: 1,
|
|
4905
|
-
});
|
|
4906
|
-
};
|
|
4907
|
-
const buildDesignContextRawUrl = (sessionId, artifactId) => {
|
|
4908
|
-
return `/api/variants/${encodeURIComponent(sessionId)}/${DESIGN_CONTEXT_ROUTE_SEGMENT}/${encodeURIComponent(artifactId)}`;
|
|
4909
|
-
};
|
|
4910
|
-
const buildDesignContextViewUrl = (sessionId, artifactId) => {
|
|
4911
|
-
return `${buildDesignContextRawUrl(sessionId, artifactId)}/${DESIGN_CONTEXT_VIEW_SEGMENT}`;
|
|
4912
|
-
};
|
|
4913
|
-
const buildDesignContextViewerDocument = (artifact) => {
|
|
4914
|
-
const title = `${artifact.label} DESIGN.md`;
|
|
4915
|
-
const visualHtml = renderDesignMarkdown(artifact.content, artifact.label);
|
|
4916
|
-
return `<!doctype html>
|
|
4917
|
-
<html lang="en">
|
|
4918
|
-
<head>
|
|
4919
|
-
<meta charset="utf-8" />
|
|
4920
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
4921
|
-
<title>${escapeHtml(title)}</title>
|
|
4922
|
-
<style>
|
|
4923
|
-
:root {
|
|
4924
|
-
color-scheme: dark;
|
|
4925
|
-
--font-main: 'Satoshi', 'Inter', system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
4926
|
-
--font-mono: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
|
|
4927
|
-
--main: #1C1C20;
|
|
4928
|
-
--main-light: #1E1E22;
|
|
4929
|
-
--main-input: #2E2E2E;
|
|
4930
|
-
--main-border: #232328;
|
|
4931
|
-
--main-hover: #404040;
|
|
4932
|
-
--content: #ffffff;
|
|
4933
|
-
--content-muted: #d1d5db;
|
|
4934
|
-
--content-subtle: #9ca3af;
|
|
4935
|
-
--divider: #4b5563;
|
|
4936
|
-
--primary: #FF3300;
|
|
4937
|
-
--primary-border: #ff6b35;
|
|
4938
|
-
}
|
|
4939
|
-
* { box-sizing: border-box; }
|
|
4940
|
-
body {
|
|
4941
|
-
margin: 0;
|
|
4942
|
-
min-height: 100vh;
|
|
4943
|
-
background: var(--main);
|
|
4944
|
-
color: var(--content);
|
|
4945
|
-
font-family: var(--font-main);
|
|
4946
|
-
}
|
|
4947
|
-
.page-shell {
|
|
4948
|
-
min-height: 100vh;
|
|
4949
|
-
padding: 22px 28px 56px;
|
|
4950
|
-
}
|
|
4951
|
-
.topbar {
|
|
4952
|
-
display: flex;
|
|
4953
|
-
align-items: center;
|
|
4954
|
-
justify-content: space-between;
|
|
4955
|
-
margin-bottom: 28px;
|
|
4956
|
-
}
|
|
4957
|
-
.brand {
|
|
4958
|
-
display: inline-flex;
|
|
4959
|
-
align-items: center;
|
|
4960
|
-
min-height: 28px;
|
|
4961
|
-
}
|
|
4962
|
-
.brand-logo {
|
|
4963
|
-
display: block;
|
|
4964
|
-
width: 64px;
|
|
4965
|
-
height: auto;
|
|
4966
|
-
}
|
|
4967
|
-
.avatar {
|
|
4968
|
-
width: 28px;
|
|
4969
|
-
height: 28px;
|
|
4970
|
-
border: 1px solid var(--main-border);
|
|
4971
|
-
border-radius: 8px;
|
|
4972
|
-
background: var(--main-light);
|
|
4973
|
-
}
|
|
4974
|
-
main {
|
|
4975
|
-
width: min(1120px, calc(100vw - 56px));
|
|
4976
|
-
margin: 0 auto;
|
|
4977
|
-
}
|
|
4978
|
-
.document {
|
|
4979
|
-
overflow: hidden;
|
|
4980
|
-
border: 1px solid var(--main-border);
|
|
4981
|
-
border-radius: 22px;
|
|
4982
|
-
background: var(--main-light);
|
|
4983
|
-
box-shadow: 0 18px 60px rgb(0 0 0 / 18%);
|
|
4984
|
-
}
|
|
4985
|
-
.document-title {
|
|
4986
|
-
padding: 42px 46px 32px;
|
|
4987
|
-
}
|
|
4988
|
-
.eyebrow {
|
|
4989
|
-
margin: 0 0 14px;
|
|
4990
|
-
color: var(--content-subtle);
|
|
4991
|
-
font-size: 0.68rem;
|
|
4992
|
-
font-weight: 700;
|
|
4993
|
-
letter-spacing: 0.16em;
|
|
4994
|
-
text-transform: uppercase;
|
|
4995
|
-
}
|
|
4996
|
-
.file-title {
|
|
4997
|
-
margin: 0;
|
|
4998
|
-
color: var(--content);
|
|
4999
|
-
font-size: clamp(2.35rem, 6vw, 4.9rem);
|
|
5000
|
-
font-weight: 600;
|
|
5001
|
-
letter-spacing: -0.07em;
|
|
5002
|
-
line-height: 0.94;
|
|
5003
|
-
}
|
|
5004
|
-
.visual {
|
|
5005
|
-
border-top: 1px solid var(--main-border);
|
|
5006
|
-
max-width: none;
|
|
5007
|
-
margin: 0 auto;
|
|
5008
|
-
padding: 36px 46px 48px;
|
|
5009
|
-
}
|
|
5010
|
-
.visual-inner {
|
|
5011
|
-
max-width: 720px;
|
|
5012
|
-
margin: 0 auto;
|
|
5013
|
-
}
|
|
5014
|
-
.visual h1,
|
|
5015
|
-
.visual h2,
|
|
5016
|
-
.visual h3 {
|
|
5017
|
-
margin: 1.2em 0 0.4em;
|
|
5018
|
-
color: var(--content);
|
|
5019
|
-
font-weight: 600;
|
|
5020
|
-
letter-spacing: -0.02em;
|
|
5021
|
-
line-height: 1.15;
|
|
5022
|
-
}
|
|
5023
|
-
.visual h1:first-child,
|
|
5024
|
-
.visual h2:first-child,
|
|
5025
|
-
.visual h3:first-child {
|
|
5026
|
-
margin-top: 0;
|
|
5027
|
-
}
|
|
5028
|
-
.visual h1 { font-size: 1.25rem; }
|
|
5029
|
-
.visual h2 { font-size: 1.1rem; }
|
|
5030
|
-
.visual h3 { font-size: 1rem; }
|
|
5031
|
-
.visual h2 + p,
|
|
5032
|
-
.visual h2 + ul {
|
|
5033
|
-
border-left: 1px solid var(--main-border);
|
|
5034
|
-
padding-left: 14px;
|
|
5035
|
-
}
|
|
5036
|
-
.visual p,
|
|
5037
|
-
.visual li,
|
|
5038
|
-
.visual blockquote {
|
|
5039
|
-
color: var(--content-muted);
|
|
5040
|
-
font-size: 0.88rem;
|
|
5041
|
-
line-height: 1.6;
|
|
5042
|
-
}
|
|
5043
|
-
.visual strong {
|
|
5044
|
-
color: var(--content);
|
|
5045
|
-
font-weight: 650;
|
|
5046
|
-
}
|
|
5047
|
-
.visual a {
|
|
5048
|
-
color: var(--primary-border);
|
|
5049
|
-
}
|
|
5050
|
-
.visual ul {
|
|
5051
|
-
display: grid;
|
|
5052
|
-
gap: 8px;
|
|
5053
|
-
padding-left: 1.1rem;
|
|
5054
|
-
}
|
|
5055
|
-
.visual blockquote {
|
|
5056
|
-
margin: 18px 0;
|
|
5057
|
-
border-left: 3px solid var(--primary);
|
|
5058
|
-
padding-left: 14px;
|
|
5059
|
-
color: var(--content-subtle);
|
|
5060
|
-
}
|
|
5061
|
-
.visual code {
|
|
5062
|
-
border-radius: 6px;
|
|
5063
|
-
border: 1px solid var(--main-border);
|
|
5064
|
-
background: var(--main-input);
|
|
5065
|
-
color: var(--content);
|
|
5066
|
-
padding: 0.12rem 0.34rem;
|
|
5067
|
-
font-family: var(--font-mono);
|
|
5068
|
-
font-size: 0.92em;
|
|
5069
|
-
}
|
|
5070
|
-
pre {
|
|
5071
|
-
margin: 0;
|
|
5072
|
-
overflow: auto;
|
|
5073
|
-
background: var(--main);
|
|
5074
|
-
color: var(--content-muted);
|
|
5075
|
-
padding: 16px;
|
|
5076
|
-
font: 0.78rem/1.55 var(--font-mono);
|
|
5077
|
-
white-space: pre-wrap;
|
|
5078
|
-
word-break: break-word;
|
|
5079
|
-
}
|
|
5080
|
-
@media (max-width: 900px) {
|
|
5081
|
-
.page-shell { padding: 18px 12px 34px; }
|
|
5082
|
-
.topbar { margin-bottom: 18px; padding: 0 4px; }
|
|
5083
|
-
main { width: min(100vw - 24px, 760px); }
|
|
5084
|
-
.document { border-radius: 18px; }
|
|
5085
|
-
.document-title { padding: 30px 24px 24px; }
|
|
5086
|
-
.file-title { font-size: clamp(2rem, 12vw, 3.6rem); }
|
|
5087
|
-
.visual { padding: 26px 24px 34px; }
|
|
5088
|
-
}
|
|
5089
|
-
</style>
|
|
5090
|
-
</head>
|
|
5091
|
-
<body>
|
|
5092
|
-
<div class="page-shell">
|
|
5093
|
-
<header class="topbar" aria-label="Rivet">
|
|
5094
|
-
<div class="brand"><img class="brand-logo" src="/assets/logo.png" alt="Rivet" /></div>
|
|
5095
|
-
<div class="avatar" aria-hidden="true"></div>
|
|
5096
|
-
</header>
|
|
5097
|
-
<main>
|
|
5098
|
-
<article class="document" id="designmd-visual" aria-label="DesignMD artifact">
|
|
5099
|
-
<div class="document-title">
|
|
5100
|
-
<p class="eyebrow">Design Context</p>
|
|
5101
|
-
<h1 class="file-title">${escapeHtml(title)}</h1>
|
|
5102
|
-
</div>
|
|
5103
|
-
<div class="visual"><div class="visual-inner">${visualHtml}</div></div>
|
|
5104
|
-
</article>
|
|
5105
|
-
</main>
|
|
5106
|
-
</div>
|
|
5107
|
-
</body>
|
|
5108
|
-
</html>`;
|
|
5109
|
-
};
|
|
5110
|
-
const renderDesignMarkdown = (markdown, artifactLabel) => {
|
|
5111
|
-
const lines = markdown.replace(/\r\n/g, '\n').split('\n');
|
|
5112
|
-
const output = [];
|
|
5113
|
-
const paragraph = [];
|
|
5114
|
-
const listItems = [];
|
|
5115
|
-
const codeLines = [];
|
|
5116
|
-
let isCodeBlock = false;
|
|
5117
|
-
let skippedFirstH1 = false;
|
|
5118
|
-
const flushParagraph = () => {
|
|
5119
|
-
if (paragraph.length === 0)
|
|
5120
|
-
return;
|
|
5121
|
-
output.push(`<p>${renderInlineMarkdown(paragraph.join(' '))}</p>`);
|
|
5122
|
-
paragraph.length = 0;
|
|
5123
|
-
};
|
|
5124
|
-
const flushList = () => {
|
|
5125
|
-
if (listItems.length === 0)
|
|
5126
|
-
return;
|
|
5127
|
-
output.push(`<ul>${listItems.map((item) => `<li>${renderInlineMarkdown(item)}</li>`).join('')}</ul>`);
|
|
5128
|
-
listItems.length = 0;
|
|
5129
|
-
};
|
|
5130
|
-
const flushCode = () => {
|
|
5131
|
-
if (codeLines.length === 0)
|
|
5132
|
-
return;
|
|
5133
|
-
output.push(`<pre>${escapeHtml(codeLines.join('\n'))}</pre>`);
|
|
5134
|
-
codeLines.length = 0;
|
|
5135
|
-
};
|
|
5136
|
-
lines.forEach((line) => {
|
|
5137
|
-
if (line.trim().startsWith('```')) {
|
|
5138
|
-
if (isCodeBlock) {
|
|
5139
|
-
flushCode();
|
|
5140
|
-
isCodeBlock = false;
|
|
5141
|
-
return;
|
|
5142
|
-
}
|
|
5143
|
-
flushParagraph();
|
|
5144
|
-
flushList();
|
|
5145
|
-
isCodeBlock = true;
|
|
5146
|
-
return;
|
|
5147
|
-
}
|
|
5148
|
-
if (isCodeBlock) {
|
|
5149
|
-
codeLines.push(line);
|
|
5150
|
-
return;
|
|
5151
|
-
}
|
|
5152
|
-
const trimmed = line.trim();
|
|
5153
|
-
if (!trimmed) {
|
|
5154
|
-
flushParagraph();
|
|
5155
|
-
flushList();
|
|
5156
|
-
return;
|
|
5157
|
-
}
|
|
5158
|
-
if (/^---+$/.test(trimmed)) {
|
|
5159
|
-
flushParagraph();
|
|
5160
|
-
flushList();
|
|
5161
|
-
output.push('<hr />');
|
|
5162
|
-
return;
|
|
5163
|
-
}
|
|
5164
|
-
const headingMatch = trimmed.match(/^(#{1,6})\s+(.+)$/);
|
|
5165
|
-
if (headingMatch) {
|
|
5166
|
-
flushParagraph();
|
|
5167
|
-
flushList();
|
|
5168
|
-
const level = Math.min(headingMatch[1].length, 3);
|
|
5169
|
-
const headingText = headingMatch[2].trim();
|
|
5170
|
-
const isDuplicateTitle = headingText === artifactLabel ||
|
|
5171
|
-
headingText === `${artifactLabel} DESIGN.md`;
|
|
5172
|
-
if (level === 1 && !skippedFirstH1 && isDuplicateTitle) {
|
|
5173
|
-
skippedFirstH1 = true;
|
|
5174
|
-
return;
|
|
5175
|
-
}
|
|
5176
|
-
output.push(`<h${level}>${renderInlineMarkdown(headingText)}</h${level}>`);
|
|
5177
|
-
return;
|
|
5178
|
-
}
|
|
5179
|
-
const listMatch = trimmed.match(/^[-*]\s+(.+)$/);
|
|
5180
|
-
if (listMatch) {
|
|
5181
|
-
flushParagraph();
|
|
5182
|
-
listItems.push(listMatch[1]);
|
|
5183
|
-
return;
|
|
5184
|
-
}
|
|
5185
|
-
const quoteMatch = trimmed.match(/^>\s?(.+)$/);
|
|
5186
|
-
if (quoteMatch) {
|
|
5187
|
-
flushParagraph();
|
|
5188
|
-
flushList();
|
|
5189
|
-
output.push(`<blockquote>${renderInlineMarkdown(quoteMatch[1])}</blockquote>`);
|
|
5190
|
-
return;
|
|
5191
|
-
}
|
|
5192
|
-
paragraph.push(trimmed);
|
|
5193
|
-
});
|
|
5194
|
-
flushCode();
|
|
5195
|
-
flushParagraph();
|
|
5196
|
-
flushList();
|
|
5197
|
-
return output.join('\n');
|
|
5198
|
-
};
|
|
5199
|
-
const renderInlineMarkdown = (value) => {
|
|
5200
|
-
return escapeHtml(value)
|
|
5201
|
-
.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
|
|
5202
|
-
.replace(/`([^`]+)`/g, '<code>$1</code>')
|
|
5203
|
-
.replace(/\*([^*]+)\*/g, '<em>$1</em>');
|
|
5204
|
-
};
|
|
5205
|
-
const escapeHtml = (value) => {
|
|
5206
|
-
return value
|
|
5207
|
-
.replace(/&/g, '&')
|
|
5208
|
-
.replace(/</g, '<')
|
|
5209
|
-
.replace(/>/g, '>')
|
|
5210
|
-
.replace(/"/g, '"')
|
|
5211
|
-
.replace(/'/g, ''');
|
|
5212
|
-
};
|
|
5213
|
-
const resolveDesignArtifact = (entry) => {
|
|
5214
|
-
if (!entry)
|
|
5215
|
-
return null;
|
|
5216
|
-
if (entry.kind === 'markdown') {
|
|
5217
|
-
return {
|
|
5218
|
-
markdown: entry.content,
|
|
5219
|
-
source: { kind: 'markdown', label: entry.label },
|
|
5220
|
-
};
|
|
5221
|
-
}
|
|
5222
|
-
const markdown = (0, designCatalog_1.loadDesignSystemMarkdown)(entry.slug);
|
|
5223
|
-
if (!markdown)
|
|
5224
|
-
return null;
|
|
5225
|
-
const catalogEntry = (0, designCatalog_1.getDesignSystemBySlug)(entry.slug);
|
|
5226
|
-
return {
|
|
5227
|
-
markdown,
|
|
5228
|
-
source: {
|
|
5229
|
-
kind: 'slug',
|
|
5230
|
-
slug: entry.slug,
|
|
5231
|
-
label: catalogEntry?.name ?? entry.slug,
|
|
5232
|
-
},
|
|
5233
|
-
};
|
|
5234
|
-
};
|
|
5235
|
-
const summarizeDesignContext = (designContext) => {
|
|
5236
|
-
if (!designContext)
|
|
5237
|
-
return null;
|
|
5238
|
-
return designContext.reduce((entries, entry, slot) => {
|
|
5239
|
-
if (!entry)
|
|
5240
|
-
return entries;
|
|
5241
|
-
if (entry.kind === 'slug') {
|
|
5242
|
-
entries.push({ slot, kind: 'slug', slug: entry.slug });
|
|
5243
|
-
return entries;
|
|
5244
|
-
}
|
|
5245
|
-
entries.push({
|
|
5246
|
-
slot,
|
|
5247
|
-
kind: 'markdown',
|
|
5248
|
-
markdownLabel: entry.label,
|
|
5249
|
-
});
|
|
5250
|
-
return entries;
|
|
5251
|
-
}, []);
|
|
5252
|
-
};
|
|
5253
|
-
const trackScaffoldStarted = (telemetry, data) => {
|
|
5254
|
-
if (telemetry.trackAgentVariantsScaffoldStarted) {
|
|
5255
|
-
telemetry.trackAgentVariantsScaffoldStarted(data);
|
|
5256
|
-
return;
|
|
5257
|
-
}
|
|
5258
|
-
telemetry.track('agent_variants.scaffold_started', {
|
|
5259
|
-
source: 'mcp',
|
|
5260
|
-
session_id: data.sessionId,
|
|
5261
|
-
variant_count: data.variantCount,
|
|
5262
|
-
design_context: data.designContext ?? null,
|
|
5263
|
-
});
|
|
5264
|
-
};
|
|
5265
|
-
const trackScaffoldCompleted = (telemetry, data) => {
|
|
5266
|
-
if (telemetry.trackAgentVariantsScaffoldCompleted) {
|
|
5267
|
-
telemetry.trackAgentVariantsScaffoldCompleted(data);
|
|
5268
|
-
return;
|
|
5269
|
-
}
|
|
5270
|
-
telemetry.track('agent_variants.scaffold_completed', {
|
|
5271
|
-
source: 'mcp',
|
|
5272
|
-
session_id: data.sessionId,
|
|
5273
|
-
duration_ms: data.durationMs,
|
|
5274
|
-
install_duration_ms: data.installDurationMs,
|
|
5275
|
-
variant_count: data.variantCount,
|
|
5276
|
-
});
|
|
5277
|
-
};
|
|
5278
|
-
const trackScaffoldFailed = (telemetry, data) => {
|
|
5279
|
-
if (telemetry.trackAgentVariantsScaffoldFailed) {
|
|
5280
|
-
telemetry.trackAgentVariantsScaffoldFailed(data);
|
|
5281
|
-
return;
|
|
5282
|
-
}
|
|
5283
|
-
telemetry.track('agent_variants.scaffold_failed', {
|
|
5284
|
-
source: 'mcp',
|
|
5285
|
-
session_id: data.sessionId,
|
|
5286
|
-
error_code: data.errorCode,
|
|
5287
|
-
duration_ms: data.durationMs,
|
|
5288
|
-
});
|
|
5289
|
-
};
|
|
5290
|
-
const trackFreshDevServerStarted = (telemetry, data) => {
|
|
5291
|
-
if (telemetry.trackAgentVariantsFreshDevServerStarted) {
|
|
5292
|
-
telemetry.trackAgentVariantsFreshDevServerStarted(data);
|
|
5293
|
-
return;
|
|
5294
|
-
}
|
|
5295
|
-
telemetry.track('agent_variants.fresh_dev_server_started', {
|
|
5296
|
-
source: 'mcp',
|
|
5297
|
-
session_id: data.sessionId,
|
|
5298
|
-
variant_id: data.variantId,
|
|
5299
|
-
port: data.port,
|
|
5300
|
-
});
|
|
5301
|
-
};
|
|
5302
|
-
const trackFreshDevServerFailed = (telemetry, data) => {
|
|
5303
|
-
if (telemetry.trackAgentVariantsFreshDevServerFailed) {
|
|
5304
|
-
telemetry.trackAgentVariantsFreshDevServerFailed(data);
|
|
5305
|
-
return;
|
|
5306
|
-
}
|
|
5307
|
-
telemetry.track('agent_variants.fresh_dev_server_failed', {
|
|
5308
|
-
source: 'mcp',
|
|
5309
|
-
session_id: data.sessionId,
|
|
5310
|
-
variant_id: data.variantId,
|
|
5311
|
-
error_code: data.errorCode,
|
|
5312
|
-
failure_reason: data.reason,
|
|
5313
|
-
port_in_use: data.portInUse,
|
|
5314
|
-
exit_code: data.exitCode,
|
|
5315
|
-
term_signal: data.signal,
|
|
5316
|
-
missing_deps: data.missingDeps,
|
|
5317
|
-
});
|
|
5318
|
-
};
|
|
5319
|
-
const trackPreviewStarted = (telemetry, data) => {
|
|
5320
|
-
if (telemetry.trackAgentVariantsPreviewStarted) {
|
|
5321
|
-
telemetry.trackAgentVariantsPreviewStarted(data);
|
|
5322
|
-
return;
|
|
5323
|
-
}
|
|
5324
|
-
telemetry.track('agent_variants.preview_started', {
|
|
5325
|
-
source: 'mcp',
|
|
5326
|
-
session_id: data.sessionId,
|
|
5327
|
-
variant_id: data.variantId,
|
|
5328
|
-
project_context_kind: data.projectContextKind,
|
|
5329
|
-
preview_kind: data.previewKind,
|
|
5330
|
-
});
|
|
5331
|
-
};
|
|
5332
|
-
const trackPreviewReady = (telemetry, data) => {
|
|
5333
|
-
if (telemetry.trackAgentVariantsPreviewReady) {
|
|
5334
|
-
telemetry.trackAgentVariantsPreviewReady(data);
|
|
5335
|
-
return;
|
|
5336
|
-
}
|
|
5337
|
-
telemetry.track('agent_variants.preview_ready', {
|
|
5338
|
-
source: 'mcp',
|
|
5339
|
-
session_id: data.sessionId,
|
|
5340
|
-
variant_id: data.variantId,
|
|
5341
|
-
project_context_kind: data.projectContextKind,
|
|
5342
|
-
preview_kind: data.previewKind,
|
|
5343
|
-
has_port: data.hasPort,
|
|
5344
|
-
port: data.port,
|
|
5345
|
-
duration_ms: data.durationMs,
|
|
5346
|
-
});
|
|
5347
|
-
};
|
|
5348
|
-
const trackPreviewFailed = (telemetry, data) => {
|
|
5349
|
-
if (telemetry.trackAgentVariantsPreviewFailed) {
|
|
5350
|
-
telemetry.trackAgentVariantsPreviewFailed(data);
|
|
5351
|
-
return;
|
|
5352
|
-
}
|
|
5353
|
-
telemetry.track('agent_variants.preview_failed', {
|
|
5354
|
-
source: 'mcp',
|
|
5355
|
-
session_id: data.sessionId,
|
|
5356
|
-
variant_id: data.variantId,
|
|
5357
|
-
project_context_kind: data.projectContextKind,
|
|
5358
|
-
preview_kind: data.previewKind,
|
|
5359
|
-
error_code: data.errorCode,
|
|
5360
|
-
failure_reason: data.reason,
|
|
5361
|
-
port_in_use: data.portInUse,
|
|
5362
|
-
duration_ms: data.durationMs,
|
|
5363
|
-
exit_code: data.exitCode,
|
|
5364
|
-
term_signal: data.signal,
|
|
5365
|
-
missing_deps: data.missingDeps,
|
|
5366
|
-
});
|
|
5367
|
-
};
|
|
5368
|
-
const trackStaticPreviewCompleted = (telemetry, data) => {
|
|
5369
|
-
if (telemetry.trackAgentVariantsStaticPreviewCompleted) {
|
|
5370
|
-
telemetry.trackAgentVariantsStaticPreviewCompleted(data);
|
|
5371
|
-
return;
|
|
5372
|
-
}
|
|
5373
|
-
telemetry.track('agent_variants.static_preview_completed', {
|
|
5374
|
-
source: 'mcp',
|
|
5375
|
-
session_id: data.sessionId,
|
|
5376
|
-
variant_id: data.variantId,
|
|
5377
|
-
duration_ms: data.durationMs,
|
|
5378
|
-
});
|
|
5379
|
-
};
|
|
5380
|
-
const MATERIALIZE_EXCLUDE = new Set([
|
|
5381
|
-
'node_modules',
|
|
5382
|
-
'.git',
|
|
5383
|
-
'.rivet',
|
|
5384
|
-
'dist',
|
|
5385
|
-
'.next',
|
|
5386
|
-
'.cache',
|
|
5387
|
-
'.vite',
|
|
5388
|
-
]);
|
|
5389
|
-
const STATIC_PREVIEW_HISTORY_EXCLUDE = new Set([
|
|
5390
|
-
...MATERIALIZE_EXCLUDE,
|
|
5391
|
-
'.rivet',
|
|
5392
|
-
]);
|
|
5393
|
-
const STATIC_PREVIEW_HISTORY_FILE_EXTENSIONS = new Set([
|
|
5394
|
-
...ALLOWED_INERT_ASSET_EXTENSIONS,
|
|
5395
|
-
'.css',
|
|
5396
|
-
'.htm',
|
|
5397
|
-
'.html',
|
|
5398
|
-
'.js',
|
|
5399
|
-
]);
|
|
5400
|
-
function copyStaticPreviewDirectory(src, dest) {
|
|
5401
|
-
fs_1.default.mkdirSync(dest, { recursive: true });
|
|
5402
|
-
let entries;
|
|
5403
|
-
try {
|
|
5404
|
-
entries = fs_1.default.readdirSync(src, { withFileTypes: true });
|
|
5405
|
-
}
|
|
5406
|
-
catch {
|
|
5407
|
-
return;
|
|
5408
|
-
}
|
|
5409
|
-
for (const entry of entries) {
|
|
5410
|
-
if (entry.name.startsWith('.') ||
|
|
5411
|
-
STATIC_PREVIEW_HISTORY_EXCLUDE.has(entry.name)) {
|
|
5412
|
-
continue;
|
|
5413
|
-
}
|
|
5414
|
-
const from = path_1.default.join(src, entry.name);
|
|
5415
|
-
const to = path_1.default.join(dest, entry.name);
|
|
5416
|
-
if (entry.isDirectory()) {
|
|
5417
|
-
copyStaticPreviewDirectory(from, to);
|
|
5418
|
-
}
|
|
5419
|
-
else if (entry.isFile()) {
|
|
5420
|
-
const extension = path_1.default.extname(entry.name).toLowerCase();
|
|
5421
|
-
if (!STATIC_PREVIEW_HISTORY_FILE_EXTENSIONS.has(extension))
|
|
5422
|
-
continue;
|
|
5423
|
-
let stat;
|
|
5424
|
-
try {
|
|
5425
|
-
stat = fs_1.default.statSync(from);
|
|
5426
|
-
}
|
|
5427
|
-
catch {
|
|
5428
|
-
continue;
|
|
5429
|
-
}
|
|
5430
|
-
if (!stat.isFile() || stat.nlink !== 1)
|
|
5431
|
-
continue;
|
|
5432
|
-
fs_1.default.mkdirSync(path_1.default.dirname(to), { recursive: true });
|
|
5433
|
-
fs_1.default.copyFileSync(from, to);
|
|
5434
|
-
}
|
|
5435
|
-
}
|
|
5436
|
-
}
|
|
5437
|
-
const defaultInstallDependencies = async (worktreePath, options) => {
|
|
5438
|
-
await DependencyPreparationService_1.previewDependencies.prepare({
|
|
5439
|
-
cwd: worktreePath,
|
|
5440
|
-
workspaceRoot: options?.workspaceRoot ?? worktreePath,
|
|
5441
|
-
force: options?.force,
|
|
5442
|
-
signal: options?.signal,
|
|
5443
|
-
});
|
|
5444
|
-
};
|
|
5445
|
-
const buildServeRecipeFromResolved = (snapshotRoot, dev, port) => {
|
|
5446
|
-
const portPattern = new RegExp(`(?<![0-9])${port}(?![0-9])`, 'g');
|
|
5447
|
-
const sub = (value) => value.replace(portPattern, '${PORT}');
|
|
5448
|
-
const relativeCwd = path_1.default.relative(snapshotRoot, dev.cwd);
|
|
5449
|
-
return {
|
|
5450
|
-
cwd: relativeCwd === '' ? '.' : relativeCwd,
|
|
5451
|
-
cmd: dev.cmd,
|
|
5452
|
-
argsTemplate: dev.args.map(sub),
|
|
5453
|
-
envTemplate: Object.fromEntries(Object.entries(dev.env).map(([key, value]) => [key, sub(value)])),
|
|
5454
|
-
};
|
|
5455
|
-
};
|
|
5456
|
-
const canInstallWithoutTouchingHost = (cwd) => {
|
|
5457
|
-
try {
|
|
5458
|
-
return !fs_1.default.lstatSync(path_1.default.join(cwd, 'node_modules')).isSymbolicLink();
|
|
5459
|
-
}
|
|
5460
|
-
catch {
|
|
5461
|
-
return true;
|
|
5462
|
-
}
|
|
5463
|
-
};
|
|
5464
|
-
const assertDevScriptExists = (cwd, scriptName) => {
|
|
5465
|
-
let scripts;
|
|
5466
|
-
try {
|
|
5467
|
-
const raw = fs_1.default.readFileSync(path_1.default.join(cwd, 'package.json'), 'utf8');
|
|
5468
|
-
const parsed = JSON.parse(raw);
|
|
5469
|
-
if (parsed && typeof parsed === 'object' && 'scripts' in parsed) {
|
|
5470
|
-
const candidate = parsed.scripts;
|
|
5471
|
-
if (candidate && typeof candidate === 'object') {
|
|
5472
|
-
scripts = candidate;
|
|
5473
|
-
}
|
|
5474
|
-
}
|
|
5475
|
-
}
|
|
5476
|
-
catch {
|
|
5477
|
-
return;
|
|
5478
|
-
}
|
|
5479
|
-
if (scripts && typeof scripts[scriptName] === 'string')
|
|
5480
|
-
return;
|
|
5481
|
-
const available = Object.keys(scripts ?? {});
|
|
5482
|
-
throw new devServerError_1.DevServerStartError({
|
|
5483
|
-
message: `package.json at ${cwd} has no "${scriptName}" script` +
|
|
5484
|
-
(available.length ? ` (available: ${available.join(', ')})` : ''),
|
|
5485
|
-
reason: 'missing_dev_script',
|
|
5486
|
-
portInUse: false,
|
|
5487
|
-
detail: available.length
|
|
5488
|
-
? `available scripts: ${available.join(', ')}`
|
|
5489
|
-
: '',
|
|
5490
|
-
});
|
|
5491
|
-
};
|
|
5492
|
-
function areSameVolume(a, b) {
|
|
5493
|
-
try {
|
|
5494
|
-
const statA = fs_1.default.statSync(a);
|
|
5495
|
-
const parentB = fs_1.default.existsSync(b) ? b : path_1.default.dirname(b);
|
|
5496
|
-
const statB = fs_1.default.statSync(parentB);
|
|
5497
|
-
return statA.dev === statB.dev;
|
|
5498
|
-
}
|
|
5499
|
-
catch {
|
|
5500
|
-
return false;
|
|
5501
|
-
}
|
|
5502
|
-
}
|
|
5503
|
-
async function resolveNodeModulesSymlink(worktreePath) {
|
|
5504
|
-
const nodeModulesPath = path_1.default.join(worktreePath, 'node_modules');
|
|
5505
|
-
try {
|
|
5506
|
-
if (!(await fs_1.default.promises.lstat(nodeModulesPath)).isSymbolicLink())
|
|
5507
|
-
return false;
|
|
5508
|
-
await fs_1.default.promises.rm(nodeModulesPath, { force: true });
|
|
5509
|
-
return true;
|
|
5510
|
-
}
|
|
5511
|
-
catch (error) {
|
|
5512
|
-
if (error.code === 'ENOENT')
|
|
5513
|
-
return true;
|
|
5514
|
-
throw error;
|
|
5515
|
-
}
|
|
5516
|
-
}
|
|
5517
|
-
const defaultMaterializeProject = async (sourceWorktreePath, destinationPath) => {
|
|
5518
|
-
await fs_1.default.promises.mkdir(destinationPath, { recursive: true });
|
|
5519
|
-
await fs_1.default.promises.cp(sourceWorktreePath, destinationPath, {
|
|
5520
|
-
recursive: true,
|
|
5521
|
-
filter: (sourcePath) => {
|
|
5522
|
-
const rel = path_1.default.relative(sourceWorktreePath, sourcePath);
|
|
5523
|
-
if (!rel)
|
|
5524
|
-
return true;
|
|
5525
|
-
const top = rel.split(path_1.default.sep)[0];
|
|
5526
|
-
return !MATERIALIZE_EXCLUDE.has(top);
|
|
5527
|
-
},
|
|
5528
|
-
});
|
|
5529
|
-
const git = (0, simple_git_1.simpleGit)(destinationPath);
|
|
5530
|
-
await git.raw(['init']);
|
|
5531
|
-
await git.raw(['add', '-A']);
|
|
5532
|
-
await git
|
|
5533
|
-
.raw([
|
|
5534
|
-
'-c',
|
|
5535
|
-
'user.name=Rivet',
|
|
5536
|
-
'-c',
|
|
5537
|
-
'user.email=hello@tryrivet.design',
|
|
5538
|
-
'commit',
|
|
5539
|
-
'--no-gpg-sign',
|
|
5540
|
-
'-m',
|
|
5541
|
-
'Initial commit (created with Rivet)',
|
|
5542
|
-
])
|
|
5543
|
-
.catch(() => { });
|
|
5544
|
-
};
|
|
5545
|
-
function synthesizeUnifiedBriefBody(prompt, index, total) {
|
|
5546
|
-
const trimmed = prompt.trim().replace(/\s+/g, ' ').slice(0, 200);
|
|
5547
|
-
const intent = trimmed.length > 0 ? trimmed : 'Create this variant';
|
|
5548
|
-
if (total <= 1)
|
|
5549
|
-
return intent;
|
|
5550
|
-
const focus = FALLBACK_DIRECTION_FOCI[(Math.max(index, 1) - 1) % FALLBACK_DIRECTION_FOCI.length];
|
|
5551
|
-
return `${intent} Direction ${index} of ${total}: ${focus}`;
|
|
5552
|
-
}
|
|
5553
3530
|
//# sourceMappingURL=WorktreeOrchestrator.js.map
|