rivet-design 0.27.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/agent-variants/DirectionRefReservations.d.ts +11 -0
  2. package/dist/agent-variants/DirectionRefReservations.js +47 -0
  3. package/dist/agent-variants/PreviewRuntime.d.ts +4 -0
  4. package/dist/agent-variants/PreviewRuntime.js +5 -3
  5. package/dist/agent-variants/SessionStore.d.ts +10 -1
  6. package/dist/agent-variants/SessionStore.js +25 -2
  7. package/dist/agent-variants/WorkItemRequestTimes.d.ts +6 -0
  8. package/dist/agent-variants/WorkItemRequestTimes.js +21 -0
  9. package/dist/agent-variants/WorktreeOrchestrator.d.ts +2 -1
  10. package/dist/agent-variants/WorktreeOrchestrator.js +92 -78
  11. package/dist/agent-variants/contracts.d.ts +8 -0
  12. package/dist/agent-variants/existingStaticEntry.d.ts +34 -0
  13. package/dist/agent-variants/existingStaticEntry.js +136 -0
  14. package/dist/agent-variants/staticPreviewFiles.js +1 -1
  15. package/dist/agent-variants/variant-project-runtime.d.ts +2 -1
  16. package/dist/agent-variants/variantsTelemetry.d.ts +4 -0
  17. package/dist/agent-variants/variantsTelemetry.js +2 -0
  18. package/dist/agent-variants/workItemInput.d.ts +22 -0
  19. package/dist/agent-variants/workItemInput.js +12 -1
  20. package/dist/cli/commands/directionTarget.d.ts +23 -0
  21. package/dist/cli/commands/directionTarget.js +87 -0
  22. package/dist/cli/commands/open.js +28 -4
  23. package/dist/cli/commands/variants.js +36 -43
  24. package/dist/cli/envelope.d.ts +1 -1
  25. package/dist/cli/envelope.js +1 -0
  26. package/dist/cli/projectRuntimeUpgrade.d.ts +11 -0
  27. package/dist/cli/projectRuntimeUpgrade.js +43 -11
  28. package/dist/config/pluginMetrics.js +2 -0
  29. package/dist/config/posthog.d.ts +2 -0
  30. package/dist/config/posthog.js +5 -0
  31. package/dist/mcp/explorationSchema.js +1 -1
  32. package/dist/mcp/getExploration.js +11 -5
  33. package/dist/proxy-middleware/proxy-config.js +7 -0
  34. package/dist/routes/agentVariants.js +4 -0
  35. package/dist/routes/cli.js +10 -1
  36. package/dist/server.js +12 -6
  37. package/dist/services/DependencyPreparationService.js +44 -6
  38. package/dist/services/FeatureFlagService.js +2 -2
  39. package/dist/services/ProjectLibrary.d.ts +4 -1
  40. package/dist/services/ProjectLibrary.js +14 -9
  41. package/dist/services/ProjectRuntimeService.d.ts +8 -0
  42. package/dist/services/ProjectRuntimeService.js +15 -2
  43. package/dist/services/TelemetryService.d.ts +5 -0
  44. package/dist/services/TelemetryService.js +21 -2
  45. package/dist/services/VariantHistoryService.d.ts +8 -0
  46. package/dist/services/VariantHistoryService.js +40 -0
  47. package/dist/services/WorktreeManager.d.ts +1 -0
  48. package/dist/services/WorktreeManager.js +12 -0
  49. package/dist/services/createAgentVariantsOrchestrator.js +9 -2
  50. package/dist/services/directionRefs.d.ts +21 -0
  51. package/dist/services/directionRefs.js +148 -0
  52. package/dist/services/projectDirections.d.ts +22 -0
  53. package/dist/services/projectDirections.js +38 -0
  54. package/dist/types/direction-ref.d.ts +36 -0
  55. package/dist/types/direction-ref.js +3 -0
  56. package/dist/types/variant-history.d.ts +3 -1
  57. package/dist/utils/failureReason.d.ts +1 -1
  58. package/dist/utils/failureReason.js +3 -0
  59. package/dist/utils/skills/cli-guidance.js +4 -3
  60. package/dist/utils/subprocessEnvironment.d.ts +1 -1
  61. package/dist/utils/subprocessEnvironment.js +0 -1
  62. package/package.json +4 -4
  63. package/src/ui/dist/assets/LibraryEntry-BE8nZneH.js +37 -0
  64. package/src/ui/dist/assets/logo.png +0 -0
  65. package/src/ui/dist/assets/main-1EDrDqgn.js +498 -0
  66. package/src/ui/dist/assets/main-qgqnlUkI.css +1 -0
  67. package/src/ui/dist/assets/rivet-wordmark-orange-CYKVQDmG.svg +7 -0
  68. package/src/ui/dist/index.html +2 -2
  69. package/dist/services/StaticPreviewServer.d.ts +0 -8
  70. package/dist/services/StaticPreviewServer.js +0 -207
  71. package/src/ui/dist/assets/LibraryEntry-RI9Lc7P3.js +0 -37
  72. package/src/ui/dist/assets/main-D0UmAaUe.css +0 -1
  73. package/src/ui/dist/assets/main-DgrLzS5j.js +0 -497
  74. package/src/ui/dist/assets/rivet.svg +0 -3
  75. package/src/ui/dist/fonts/Goldman-Bold.woff2 +0 -0
  76. package/src/ui/dist/fonts/Goldman-Regular.woff2 +0 -0
@@ -543,6 +543,7 @@ const createAgentVariantsRouter = (orchestrator, bridge, telemetry, variantHisto
543
543
  filesDir,
544
544
  entryPath: preview.entryPath,
545
545
  label: detail.manifest.label,
546
+ directionRef: detail.manifest.directionRef,
546
547
  description: detail.manifest.brief,
547
548
  references: detail.manifest.references,
548
549
  previewRoutes: detail.manifest.previewRoutes,
@@ -1058,6 +1059,7 @@ const createAgentVariantsRouter = (orchestrator, bridge, telemetry, variantHisto
1058
1059
  sessionId: entry.manifest.sessionId,
1059
1060
  variantId: entry.manifest.variantId,
1060
1061
  requestId: entry.manifest.requestId,
1062
+ directionRef: entry.manifest.directionRef,
1061
1063
  label: entry.manifest.label,
1062
1064
  brief: entry.manifest.brief,
1063
1065
  references: entry.manifest.references,
@@ -1507,6 +1509,7 @@ const createAgentVariantsRouter = (orchestrator, bridge, telemetry, variantHisto
1507
1509
  filesDir,
1508
1510
  entryPath: preview.entryPath,
1509
1511
  label: detail.manifest.label,
1512
+ directionRef: detail.manifest.directionRef,
1510
1513
  description: detail.manifest.brief,
1511
1514
  references: detail.manifest.references,
1512
1515
  previewRoutes: detail.manifest.previewRoutes,
@@ -1580,6 +1583,7 @@ const createAgentVariantsRouter = (orchestrator, bridge, telemetry, variantHisto
1580
1583
  filesDir,
1581
1584
  entryPath: preview.entryPath,
1582
1585
  label: detail.manifest.label,
1586
+ directionRef: detail.manifest.directionRef,
1583
1587
  description: detail.manifest.brief,
1584
1588
  references: detail.manifest.references,
1585
1589
  previewRoutes: detail.manifest.previewRoutes,
@@ -102,8 +102,17 @@ const createCliRouter = (options) => {
102
102
  setImmediate(() => options.onShutdown?.());
103
103
  return;
104
104
  }
105
+ const isIdleRequired = req.body?.requireIdle === true;
105
106
  const activity = (0, runtimeActivity_1.classifyRuntimeActivity)(readStatus());
106
- if (!admission.beginRetirement(activity, true)) {
107
+ if (!admission.beginRetirement(activity, !isIdleRequired)) {
108
+ if (isIdleRequired) {
109
+ res.status(409).json({
110
+ reason: 'active_work',
111
+ error: 'Rivet left the server running because it has active work or is already restarting.',
112
+ upgrade: admission.status(),
113
+ });
114
+ return;
115
+ }
107
116
  res.status(409).json({
108
117
  reason: 'server_retiring',
109
118
  error: 'Rivet is already restarting. Retry after the runtime handoff completes.',
package/dist/server.js CHANGED
@@ -212,6 +212,7 @@ const startServer = async (options) => {
212
212
  ? (0, crypto_1.randomBytes)(QUEUE_ACCESS_TOKEN_BYTES).toString('base64url')
213
213
  : null;
214
214
  const variantsOrchestrator = options.variantsRuntime?.orchestrator ?? options.agentVariantsOrchestrator;
215
+ await variantsOrchestrator?.loadDirectionRefs();
215
216
  const inlineStartTracker = (0, inlineStartTracker_1.createInlineStartTracker)();
216
217
  const runtimeAdmission = new RuntimeAdmission_1.RuntimeAdmission(telemetry_2.RIVET_VERSION);
217
218
  const variantsBridge = options.variantsRuntime?.bridge ?? sessionBridge;
@@ -251,11 +252,16 @@ const startServer = async (options) => {
251
252
  const editorHost = () => `${EMBEDDED_AGENT_LISTEN_HOST}:${listenPort}`;
252
253
  const isPreviewRequest = (req) => req.headers.host === new URL(previewOrigin).host &&
253
254
  (previewPort === undefined || req.socket.localPort === previewPort);
255
+ const rejectRequest = ({ req, res, reason, }) => {
256
+ log.warn(`Rejected ${req.method} ${req.path} with 403: ${reason}`);
257
+ telemetry?.trackRequestRejected(reason);
258
+ res.sendStatus(403);
259
+ };
254
260
  app.use((req, res, next) => {
255
261
  const requestHost = req.headers.host;
256
262
  const isEditorRequest = requestHost === editorHost() && req.socket.localPort === listenPort;
257
263
  if (!isEditorRequest && !isPreviewRequest(req)) {
258
- res.sendStatus(403);
264
+ rejectRequest({ req, res, reason: 'host_not_allowed' });
259
265
  return;
260
266
  }
261
267
  next();
@@ -293,7 +299,7 @@ const startServer = async (options) => {
293
299
  return;
294
300
  }
295
301
  if (req.headers[editorAccess_2.EDITOR_ACCESS_TOKEN_HEADER]) {
296
- res.sendStatus(403);
302
+ rejectRequest({ req, res, reason: 'editor_token_on_preview' });
297
303
  return;
298
304
  }
299
305
  const isAppApi = req.path.toLowerCase().startsWith('/api/') &&
@@ -438,7 +444,7 @@ const startServer = async (options) => {
438
444
  PREVIEW_PORT_ALLOWED_API_RE.test(req.path.toLowerCase());
439
445
  if (!isPreviewRequest(req) && isPreviewPath) {
440
446
  if (req.headers[editorAccess_2.EDITOR_ACCESS_TOKEN_HEADER]) {
441
- res.sendStatus(403);
447
+ rejectRequest({ req, res, reason: 'editor_token_on_preview' });
442
448
  return;
443
449
  }
444
450
  res.redirect(302, `${previewOrigin}${req.originalUrl}`);
@@ -511,7 +517,7 @@ const startServer = async (options) => {
511
517
  });
512
518
  app.post('/api/library/browser-launches', express_1.default.urlencoded({ extended: false }), async (req, res) => {
513
519
  if (!(0, middleware_1.isLocalBrowserRequest)(req))
514
- return res.sendStatus(403);
520
+ return rejectRequest({ req, res, reason: 'origin_not_allowed' });
515
521
  const view = req.body?.view;
516
522
  if (view !== 'projects' && view !== 'references')
517
523
  return res.sendStatus(400);
@@ -591,14 +597,14 @@ const startServer = async (options) => {
591
597
  return;
592
598
  }
593
599
  if (req.headers[editorAccess_2.EDITOR_ACCESS_TOKEN_HEADER]) {
594
- res.sendStatus(403);
600
+ rejectRequest({ req, res, reason: 'editor_token_on_preview' });
595
601
  return;
596
602
  }
597
603
  res.redirect(302, `${previewOrigin}${req.originalUrl}`);
598
604
  return;
599
605
  }
600
606
  if (req.headers[editorAccess_2.EDITOR_ACCESS_TOKEN_HEADER]) {
601
- res.sendStatus(403);
607
+ rejectRequest({ req, res, reason: 'editor_token_on_preview' });
602
608
  return;
603
609
  }
604
610
  next();
@@ -38,17 +38,40 @@ const INSTALL_ENVIRONMENT_PREFIXES = [
38
38
  'corepack_',
39
39
  ];
40
40
  const INSTALL_ENVIRONMENT_KEYS = new Set(['node_options', 'path']);
41
+ const INSTALL_FAILURE_CAUSES = [
42
+ {
43
+ reasonCode: 'lockfile_outdated',
44
+ pattern: /^error Your lockfile needs to be updated, but yarn was run with `--frozen-lockfile`/m,
45
+ guidance: 'The lockfile does not match package.json. Update the lockfile in the project, commit it, then retry the preview.',
46
+ },
47
+ {
48
+ reasonCode: 'engine_incompatible',
49
+ pattern: /^error .+: The engine "node" is incompatible with this module/m,
50
+ guidance: 'The project or one of its dependencies does not support the active Node.js version. Switch to a Node.js version the project supports, then retry the preview.',
51
+ },
52
+ {
53
+ reasonCode: 'install_script_failed',
54
+ pattern: /^error .+: Command failed\.$/m,
55
+ guidance: 'A dependency install script failed. If it builds a native addon, switch to a Node.js version the project supports, then retry the preview.',
56
+ },
57
+ ];
58
+ const UNRECOGNIZED_INSTALL_FAILURE = {
59
+ reasonCode: 'install_failed',
60
+ guidance: 'Fix the installation error, then retry the preview.',
61
+ };
62
+ const installFailureCause = (output) => INSTALL_FAILURE_CAUSES.find(({ pattern }) => pattern.test(output));
41
63
  const isInstallEnvironmentKey = (key) => {
42
64
  const lower = key.toLowerCase();
43
65
  return (INSTALL_ENVIRONMENT_PREFIXES.some((prefix) => lower.startsWith(prefix)) ||
44
66
  INSTALL_ENVIRONMENT_KEYS.has(lower));
45
67
  };
46
68
  const yarnAgentForVersion = (version) => Number.parseInt(version, 10) > 1 ? 'yarn@berry' : 'yarn';
47
- const preparationError = ({ reasonCode, message, detail = '', }) => new devServerError_1.DevServerStartError({
69
+ const preparationError = ({ reasonCode, message, detail = '', exitCode, }) => new devServerError_1.DevServerStartError({
48
70
  message: detail ? `${message}\n${detail}` : message,
49
71
  reason: 'dependency_install_failed',
50
72
  portInUse: false,
51
73
  detail,
74
+ exitCode,
52
75
  reasonCode,
53
76
  summary: message,
54
77
  });
@@ -309,10 +332,13 @@ class DependencyPreparationService {
309
332
  stdio: 'pipe',
310
333
  });
311
334
  let output = '';
335
+ let failureCause;
312
336
  let interruption;
313
337
  let settleTimer;
314
338
  const drain = (chunk) => {
315
- output = (output + chunk.toString()).slice(-OUTPUT_TAIL_LIMIT);
339
+ const received = output + chunk.toString();
340
+ failureCause ??= installFailureCause(received);
341
+ output = received.slice(-OUTPUT_TAIL_LIMIT);
316
342
  };
317
343
  proc.stdout?.on('data', drain);
318
344
  proc.stderr?.on('data', drain);
@@ -351,14 +377,26 @@ class DependencyPreparationService {
351
377
  });
352
378
  proc.once('close', (code) => {
353
379
  cleanup();
354
- if (code === 0 && !interruption)
380
+ if (code === 0 && !interruption) {
355
381
  resolve(output);
356
- else
382
+ return;
383
+ }
384
+ const command = `${project.name} ${args.join(' ')}`;
385
+ if (interruption) {
357
386
  reject(preparationError({
358
- reasonCode: interruption ?? 'install_failed',
359
- message: `Dependency preparation failed: ${project.name} ${args.join(' ')} (${interruption ? 'cancelled or timed out' : `exit ${code}`}). Fix the installation error, then retry the preview.`,
387
+ reasonCode: interruption,
388
+ message: `Dependency preparation failed: ${command} (cancelled or timed out). ${UNRECOGNIZED_INSTALL_FAILURE.guidance}`,
360
389
  detail: output.trim(),
361
390
  }));
391
+ return;
392
+ }
393
+ const { reasonCode, guidance } = failureCause ?? UNRECOGNIZED_INSTALL_FAILURE;
394
+ reject(preparationError({
395
+ reasonCode,
396
+ message: `Dependency preparation failed: ${command} (exit ${code}). ${guidance}`,
397
+ detail: output.trim(),
398
+ exitCode: code,
399
+ }));
362
400
  });
363
401
  });
364
402
  }
@@ -5,9 +5,9 @@ const telemetryPolicy_1 = require("../config/telemetryPolicy");
5
5
  const posthog_node_1 = require("posthog-node");
6
6
  const logger_1 = require("../utils/logger");
7
7
  const ConfigManager_1 = require("./ConfigManager");
8
+ const posthog_1 = require("../config/posthog");
8
9
  const proxy_1 = require("../config/proxy");
9
10
  const log = (0, logger_1.createLogger)('FeatureFlagService');
10
- const POSTHOG_PUBLIC_API_KEY = 'phc_Ntj9tXHbS64XgYxlTfhglRmFivFsfm0AERph4ZlnNH';
11
11
  const remoteFlagsDisabled = () => {
12
12
  const v = process.env.RIVET_DISABLE_REMOTE_FLAGS;
13
13
  return v !== undefined && v !== 'false' && v !== '0';
@@ -20,7 +20,7 @@ class FeatureFlagService {
20
20
  return;
21
21
  }
22
22
  try {
23
- this.posthog = new posthog_node_1.PostHog(POSTHOG_PUBLIC_API_KEY, {
23
+ this.posthog = new posthog_node_1.PostHog(posthog_1.POSTHOG_PUBLIC_API_KEY, {
24
24
  host: proxy_1.POSTHOG_HOST,
25
25
  flushAt: 1,
26
26
  flushInterval: 0,
@@ -12,6 +12,9 @@ interface VariantReference {
12
12
  sessionId: string;
13
13
  variantId: string;
14
14
  }
15
+ interface LibraryListOptions {
16
+ isRemovedIncluded?: boolean;
17
+ }
15
18
  export declare const currentLibraryRoot: (env?: NodeJS.ProcessEnv) => string;
16
19
  export declare class ProjectLibrary {
17
20
  readonly registry: ProjectRegistry;
@@ -45,7 +48,7 @@ export declare class ProjectLibrary {
45
48
  projectPath: string;
46
49
  }): Promise<void>;
47
50
  private listProject;
48
- listVariants(projectId?: string): Promise<LibraryVariant[]>;
51
+ listVariants(projectId?: string, options?: LibraryListOptions): Promise<LibraryVariant[]>;
49
52
  private findVariant;
50
53
  variantSummary(reference: VariantReference): Promise<{
51
54
  summary: VariantHistorySummary;
@@ -23,6 +23,11 @@ const currentLibraryRoot = (env = process.env) => {
23
23
  : (0, ProjectRegistry_1.libraryRoot)({ channel });
24
24
  };
25
25
  exports.currentLibraryRoot = currentLibraryRoot;
26
+ const isSameDirection = (left, right) => (!left.directionRef ||
27
+ !right.directionRef ||
28
+ left.directionRef === right.directionRef) &&
29
+ JSON.stringify({ ...left, directionRef: undefined }) ===
30
+ JSON.stringify({ ...right, directionRef: undefined });
26
31
  class ProjectLibrary {
27
32
  registry;
28
33
  isReadOnly;
@@ -158,7 +163,7 @@ class ProjectLibrary {
158
163
  });
159
164
  }
160
165
  }
161
- async listProject(projectId) {
166
+ async listProject(projectId, options) {
162
167
  const project = this.registry.get(projectId);
163
168
  if (!project.isAvailable && project.storageKind === 'project')
164
169
  throw new Error('Working folder and project-local history are unavailable');
@@ -171,23 +176,23 @@ class ProjectLibrary {
171
176
  : history.listVariantsFromIndex({ projectPath }))).map((entry) => ({ ...entry, projectId, projectPath }))))).flat();
172
177
  const unique = new Map();
173
178
  for (const entry of entries) {
174
- if (entry.manifest.status === 'removed' ||
175
- entry.manifest.status === 'cancelled')
179
+ if (!options.isRemovedIncluded &&
180
+ (entry.manifest.status === 'removed' ||
181
+ entry.manifest.status === 'cancelled'))
176
182
  continue;
177
183
  const key = `${entry.manifest.sessionId}:${entry.manifest.variantId}`;
178
184
  const existing = unique.get(key);
179
- if (existing &&
180
- JSON.stringify(existing.manifest) !== JSON.stringify(entry.manifest))
185
+ if (existing && !isSameDirection(existing.manifest, entry.manifest))
181
186
  throw new Error('Conflicting history identities across attached roots');
182
- unique.set(key, entry);
187
+ unique.set(key, existing?.manifest.directionRef ? existing : entry);
183
188
  }
184
189
  return [...unique.values()];
185
190
  }
186
- async listVariants(projectId) {
191
+ async listVariants(projectId, options = {}) {
187
192
  if (projectId)
188
- return this.listProject(projectId);
193
+ return this.listProject(projectId, options);
189
194
  const projects = this.registry.list();
190
- const results = await Promise.allSettled(projects.map((project) => this.listProject(project.id)));
195
+ const results = await Promise.allSettled(projects.map((project) => this.listProject(project.id, options)));
191
196
  return results
192
197
  .flatMap((result, index) => {
193
198
  if (result.status === 'fulfilled') {
@@ -1,7 +1,15 @@
1
1
  import type { ProjectRegistry } from './ProjectRegistry';
2
+ import { type ServerUnavailableReason } from '../cli/telemetry';
2
3
  interface ProjectRuntime {
3
4
  editorUrl: string;
4
5
  }
6
+ export declare class ProjectRuntimeUnavailable extends Error {
7
+ readonly serverUnavailableReason?: ServerUnavailableReason;
8
+ constructor({ message, serverUnavailableReason, }: {
9
+ message: string;
10
+ serverUnavailableReason?: ServerUnavailableReason;
11
+ });
12
+ }
5
13
  export declare class ProjectRuntimeService {
6
14
  private readonly registry;
7
15
  private readonly activations;
@@ -36,14 +36,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.ProjectRuntimeService = void 0;
39
+ exports.ProjectRuntimeService = exports.ProjectRuntimeUnavailable = void 0;
40
40
  const fs_1 = __importDefault(require("fs"));
41
41
  const zod_1 = require("zod");
42
42
  const installationChannel_1 = require("../install/installationChannel");
43
43
  const libraryErrors_1 = require("./libraryErrors");
44
44
  const serverHandshake_1 = require("../cli/serverHandshake");
45
45
  const shared_1 = require("../cli/commands/shared");
46
+ const telemetry_1 = require("../cli/telemetry");
46
47
  const HEALTH_TIMEOUT_MS = 2_000;
48
+ class ProjectRuntimeUnavailable extends Error {
49
+ serverUnavailableReason;
50
+ constructor({ message, serverUnavailableReason, }) {
51
+ super(message);
52
+ this.name = 'ProjectRuntimeUnavailable';
53
+ this.serverUnavailableReason = serverUnavailableReason;
54
+ }
55
+ }
56
+ exports.ProjectRuntimeUnavailable = ProjectRuntimeUnavailable;
47
57
  class ProjectRuntimeService {
48
58
  registry;
49
59
  activations = new Map();
@@ -118,7 +128,10 @@ class ProjectRuntimeService {
118
128
  writeEnvelope: () => { },
119
129
  });
120
130
  if (!result.envelope.ok)
121
- throw new Error(result.envelope.error.message);
131
+ throw new ProjectRuntimeUnavailable({
132
+ message: result.envelope.error.message,
133
+ serverUnavailableReason: (0, telemetry_1.serverUnavailableReasonForEnvelope)(result.envelope),
134
+ });
122
135
  const data = zod_1.z
123
136
  .object({ editorUrl: zod_1.z.string().url() })
124
137
  .parse(result.envelope.data);
@@ -3,6 +3,7 @@ import type { BrowserMetadata, DesignContextKind, ServerUnavailableReason, Varia
3
3
  import type { GuidanceErrorCode, GuidanceSelection } from '../types/guidance';
4
4
  import type { MCPAuthMethod, MCPAuthState, MCPOnboardingCorrelation, MCPOAuthCallbackResult } from './MCPOnboardingTelemetry';
5
5
  import { type MCPVariantsTelemetryMetadata } from './MCPVariantsTelemetry';
6
+ export type RequestRejectionReason = 'host_not_allowed' | 'origin_not_allowed' | 'editor_token_on_preview';
6
7
  export type TelemetryOptions = {
7
8
  enabled?: boolean;
8
9
  source?: 'cli' | 'mcp' | 'core';
@@ -41,12 +42,14 @@ export declare class TelemetryService {
41
42
  trackGuidanceOperation(data: GuidanceOperation): void;
42
43
  trackProfileOperation(data: ProfileOperation): void;
43
44
  trackLibraryOperation(data: LibraryOperation): void;
45
+ trackRequestRejected(reason: RequestRejectionReason): void;
44
46
  private hashTelemetryValue;
45
47
  private posthog;
46
48
  private isEnabled;
47
49
  private readonly defaultSource;
48
50
  private readonly editor?;
49
51
  private readonly identifiedAccounts;
52
+ private readonly reportedRequestRejections;
50
53
  constructor(options?: boolean | TelemetryOptions);
51
54
  private get distinctId();
52
55
  identifyUser(identity?: AccountIdentity): void;
@@ -285,6 +288,7 @@ export declare class TelemetryService {
285
288
  hasPort: boolean;
286
289
  port?: number;
287
290
  durationMs?: number | null;
291
+ staticEntrySource?: string;
288
292
  }): void;
289
293
  trackAgentVariantsPreviewFailed(data: {
290
294
  sessionId: string;
@@ -298,6 +302,7 @@ export declare class TelemetryService {
298
302
  exitCode?: number | null;
299
303
  signal?: string | null;
300
304
  missingDeps?: boolean;
305
+ staticEntrySource?: string;
301
306
  error?: unknown;
302
307
  }): void;
303
308
  trackAgentVariantsPreviewSwitchStarted(data: {
@@ -11,10 +11,20 @@ const telemetryPolicy_1 = require("../config/telemetryPolicy");
11
11
  const pluginMetrics_1 = require("../config/pluginMetrics");
12
12
  const telemetryContract_1 = require("../config/telemetryContract");
13
13
  const failureReason_1 = require("../utils/failureReason");
14
+ const posthog_1 = require("../config/posthog");
14
15
  const proxy_1 = require("../config/proxy");
15
16
  const MCPVariantsTelemetry_1 = require("./MCPVariantsTelemetry");
16
17
  const errorMessage_1 = require("../utils/errorMessage");
18
+ const serverUnavailable_1 = require("../cli/serverUnavailable");
17
19
  const log = (0, logger_1.createLogger)('TelemetryService');
20
+ const serverUnavailableReasonProperties = (error) => {
21
+ const reason = typeof error === 'object' && error !== null
22
+ ? Reflect.get(error, 'serverUnavailableReason')
23
+ : undefined;
24
+ return (0, serverUnavailable_1.isServerUnavailableReason)(reason)
25
+ ? { server_unavailable_reason: reason }
26
+ : {};
27
+ };
18
28
  const KNOWN_MCP_EDITORS = new Set([
19
29
  'cursor',
20
30
  'codex',
@@ -32,7 +42,6 @@ const mcpEditorEventValue = (editor) => {
32
42
  }
33
43
  return 'other';
34
44
  };
35
- const POSTHOG_PUBLIC_API_KEY = 'phc_Ntj9tXHbS64XgYxlTfhglRmFivFsfm0AERph4ZlnNH';
36
45
  const HASH_PREFIX_LENGTH = 12;
37
46
  class TelemetryService {
38
47
  trackGuidanceOperation(data) {
@@ -61,8 +70,15 @@ class TelemetryService {
61
70
  this.track(`library_${data.operation}_${data.outcome}`, {
62
71
  duration_ms: data.durationMs,
63
72
  ...(0, failureReason_1.failureReasonCodeProperties)(data.error),
73
+ ...serverUnavailableReasonProperties(data.error),
64
74
  });
65
75
  }
76
+ trackRequestRejected(reason) {
77
+ if (this.reportedRequestRejections.has(reason))
78
+ return;
79
+ this.reportedRequestRejections.add(reason);
80
+ this.track('server_request_rejected', { reason });
81
+ }
66
82
  hashTelemetryValue(value) {
67
83
  return (0, crypto_1.createHash)('sha256')
68
84
  .update(value)
@@ -74,6 +90,7 @@ class TelemetryService {
74
90
  defaultSource;
75
91
  editor;
76
92
  identifiedAccounts = new Set();
93
+ reportedRequestRejections = new Set();
77
94
  constructor(options = true) {
78
95
  const opts = typeof options === 'boolean' ? { enabled: options } : options;
79
96
  this.isEnabled = (opts.enabled ?? true) && (0, telemetryPolicy_1.getTelemetryMode)() !== 'off';
@@ -81,7 +98,7 @@ class TelemetryService {
81
98
  this.editor = opts.editor;
82
99
  if (this.isEnabled) {
83
100
  try {
84
- this.posthog = new posthog_node_1.PostHog(POSTHOG_PUBLIC_API_KEY, {
101
+ this.posthog = new posthog_node_1.PostHog(posthog_1.POSTHOG_PUBLIC_API_KEY, {
85
102
  host: proxy_1.POSTHOG_HOST,
86
103
  flushAt: 1,
87
104
  flushInterval: 0,
@@ -682,6 +699,7 @@ class TelemetryService {
682
699
  has_port: data.hasPort,
683
700
  port: data.port,
684
701
  duration_ms: data.durationMs,
702
+ static_entry_source: data.staticEntrySource,
685
703
  source: 'mcp',
686
704
  });
687
705
  }
@@ -698,6 +716,7 @@ class TelemetryService {
698
716
  exit_code: data.exitCode,
699
717
  term_signal: data.signal,
700
718
  missing_deps: data.missingDeps,
719
+ static_entry_source: data.staticEntrySource,
701
720
  ...(0, failureReason_1.failureReasonProperties)(data.error),
702
721
  source: 'mcp',
703
722
  });
@@ -3,6 +3,7 @@ import type { GuidanceDocument } from '../types/guidance';
3
3
  import { type ReferenceImage } from './referenceImages';
4
4
  import { type VariantReference } from '../types/variant-reference';
5
5
  import type { VariantHistoryStatus, VariantHistoryKind, PersistVariantPreview, VariantServeSpec, VariantDesignSource, VariantDeployment, VariantCanvasDeployment, VariantHistoryManifest } from '../types/variant-history';
6
+ import type { DirectionRef } from '../types/direction-ref';
6
7
  export type { VariantHistoryStatus, VariantHistoryKind, VariantHistoryPreview, PersistVariantPreview, VariantTreePointer, VariantBasePointer, DevServerRecipe, VariantServeSpec, VariantHistoryArtifact, VariantDesignSource, VariantDeployment, VariantCanvasDeployment, VariantHistoryManifest, VariantHistorySessionFile, } from '../types/variant-history';
7
8
  import type { BundleMetadataStore } from '../storage/library/BundleMetadataStore';
8
9
  import { TreeStore } from './TreeStore';
@@ -13,6 +14,7 @@ export interface PersistVariantArgs {
13
14
  sessionId: string;
14
15
  variantId: string;
15
16
  requestId?: string;
17
+ directionRef?: DirectionRef;
16
18
  label: string;
17
19
  brief: string;
18
20
  references?: VariantReference[];
@@ -116,6 +118,12 @@ export declare class VariantHistoryService {
116
118
  label?: string;
117
119
  runLabel?: string;
118
120
  }): Promise<void>;
121
+ assignDirectionRef(args: {
122
+ projectPath: string;
123
+ sessionId: string;
124
+ variantId: string;
125
+ directionRef: DirectionRef;
126
+ }): Promise<void>;
119
127
  renameRun(args: {
120
128
  projectPath: string;
121
129
  sessionId: string;
@@ -203,6 +203,7 @@ class VariantHistoryService {
203
203
  let preservedDestinationPath;
204
204
  let preservedCreatedAt;
205
205
  let preservedRequestId;
206
+ let preservedDirectionRef;
206
207
  let preservedReferences;
207
208
  let preservedPreviewRoutes;
208
209
  let preservedTree;
@@ -219,6 +220,7 @@ class VariantHistoryService {
219
220
  });
220
221
  preservedCreatedAt = prior.createdAt;
221
222
  preservedRequestId = prior.requestId;
223
+ preservedDirectionRef = prior.directionRef;
222
224
  preservedReferences = prior.references;
223
225
  preservedPreviewRoutes = prior.previewRoutes;
224
226
  preservedTree = prior.tree;
@@ -363,6 +365,9 @@ class VariantHistoryService {
363
365
  ...((args.requestId ?? preservedRequestId)
364
366
  ? { requestId: args.requestId ?? preservedRequestId }
365
367
  : {}),
368
+ ...((preservedDirectionRef ?? args.directionRef)
369
+ ? { directionRef: preservedDirectionRef ?? args.directionRef }
370
+ : {}),
366
371
  label: args.label,
367
372
  brief: args.brief,
368
373
  references: (0, variant_reference_1.mergeVariantReferences)(references),
@@ -527,6 +532,41 @@ class VariantHistoryService {
527
532
  manifest,
528
533
  });
529
534
  }
535
+ async assignDirectionRef(args) {
536
+ assertSafeId(args.sessionId, 'sessionId');
537
+ assertSafeId(args.variantId, 'variantId');
538
+ const bundle = this.bundleFor(args.projectPath);
539
+ if (bundle) {
540
+ bundle.updateVariant({
541
+ ...args,
542
+ update: (manifest) => {
543
+ manifest.directionRef ??= args.directionRef;
544
+ },
545
+ });
546
+ return;
547
+ }
548
+ const manifestPath = path_1.default.join(this.variantDir(args.projectPath, args.sessionId, args.variantId), MANIFEST_FILENAME);
549
+ let manifest;
550
+ try {
551
+ const raw = await promises_1.default.readFile(manifestPath, 'utf8');
552
+ manifest = JSON.parse(raw);
553
+ }
554
+ catch (err) {
555
+ const code = (0, errnoCode_1.errnoCode)(err);
556
+ if (code !== 'ENOENT') {
557
+ log.warn(`assignDirectionRef: manifest read failed at ${manifestPath} (session ${args.sessionId} variant ${args.variantId})`, err);
558
+ }
559
+ return;
560
+ }
561
+ if (manifest.directionRef)
562
+ return;
563
+ manifest.directionRef = args.directionRef;
564
+ await writeManifestFile(manifestPath, manifest);
565
+ await this.projectManifestToIndex({
566
+ projectPath: args.projectPath,
567
+ manifest,
568
+ });
569
+ }
530
570
  async renameRun(args) {
531
571
  const entries = await this.listVariants({ projectPath: args.projectPath });
532
572
  const targets = entries.filter((entry) => {
@@ -23,6 +23,7 @@ export declare class WorktreeManager {
23
23
  createWorktrees(sessionId: string, count: number, instanceKeyPrefix?: string): Promise<string[]>;
24
24
  forkWorktreesFrom(sourceWorktreePath: string, sessionId: string, count: number, instanceKeyPrefix?: string): Promise<string[]>;
25
25
  getDiff(worktreePath: string): Promise<string>;
26
+ getChangedFiles(worktreePath: string): Promise<string[]>;
26
27
  getFreePort(): Promise<number>;
27
28
  findFreePortFrom(startPort: number, scanWindow?: number): Promise<number>;
28
29
  releasePort(port: number): void;
@@ -276,6 +276,18 @@ class WorktreeManager {
276
276
  await git.raw(['add', '--intent-to-add', '.']).catch(() => { });
277
277
  return git.diff(['--binary', '--full-index', 'HEAD']);
278
278
  }
279
+ async getChangedFiles(worktreePath) {
280
+ const git = (0, simple_git_1.simpleGit)(worktreePath, GIT_OPTS);
281
+ await git.raw(['add', '--intent-to-add', '.']).catch(() => { });
282
+ const names = await git.raw([
283
+ 'diff',
284
+ '--name-only',
285
+ '--diff-filter=d',
286
+ '-z',
287
+ 'HEAD',
288
+ ]);
289
+ return names.split('\0').filter(Boolean);
290
+ }
279
291
  getFreePort() {
280
292
  return new Promise((resolve, reject) => {
281
293
  const server = net_1.default.createServer();
@@ -1,6 +1,11 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.createAgentVariantsOrchestrator = exports.withVariantsSessionReplayLinking = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
4
9
  const index_core_1 = require("../index-core");
5
10
  const agent_variants_1 = require("../agent-variants");
6
11
  const devServerCommand_1 = require("../utils/devServerCommand");
@@ -69,11 +74,13 @@ const createAgentVariantsOrchestrator = (options) => {
69
74
  targetPath: projectPath,
70
75
  entryPath: options.staticEntry,
71
76
  }).entryPath;
72
- log.info(`Resolved env for variants session ${sessionId}: static/${entryPath}`);
77
+ const hasEntry = options.staticEntry !== undefined ||
78
+ fs_1.default.existsSync(path_1.default.join(projectPath, entryPath));
79
+ log.info(`Resolved env for variants session ${sessionId}: static/${hasEntry ? entryPath : '(no entry page)'}`);
73
80
  return {
74
81
  projectPath,
75
82
  framework,
76
- entryPath,
83
+ ...(hasEntry ? { entryPath } : {}),
77
84
  };
78
85
  }
79
86
  const packageManager = await detection.detectPackageManager(projectPath);
@@ -0,0 +1,21 @@
1
+ import type { DirectionAddress, DirectionCandidate, DirectionRef, DirectionResolution, DirectionSearchAddress } from '../types/direction-ref';
2
+ import type { VariantHistoryManifest, VariantHistoryStatus } from '../types/variant-history';
3
+ export declare const isDirectionActive: (status: VariantHistoryStatus) => boolean;
4
+ type DirectionRefSource = {
5
+ manifest: Pick<VariantHistoryManifest, 'sessionId' | 'variantId' | 'label' | 'createdAt' | 'directionRef'>;
6
+ };
7
+ export declare const slugifyDirectionLabel: (label: string) => string;
8
+ export declare const nextDirectionRef: (args: {
9
+ label: string;
10
+ taken: ReadonlySet<string>;
11
+ }) => DirectionRef;
12
+ export declare const withDirectionRefs: <Entry extends DirectionRefSource>(entries: readonly Entry[]) => Array<Entry & {
13
+ directionRef: DirectionRef;
14
+ }>;
15
+ export declare const parseDirectionAddress: (raw: string) => DirectionAddress | null;
16
+ export declare const resolveDirectionAddress: (args: {
17
+ address: DirectionSearchAddress;
18
+ candidates: readonly DirectionCandidate[];
19
+ }) => DirectionResolution;
20
+ export {};
21
+ //# sourceMappingURL=directionRefs.d.ts.map