rivet-design 0.11.17 → 0.12.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 (84) hide show
  1. package/dist/mcp/agent-variants/SessionStore.d.ts.map +1 -1
  2. package/dist/mcp/agent-variants/SessionStore.js +5 -3
  3. package/dist/mcp/agent-variants/SessionStore.js.map +1 -1
  4. package/dist/mcp/agent-variants/WorktreeOrchestrator.d.ts +29 -0
  5. package/dist/mcp/agent-variants/WorktreeOrchestrator.d.ts.map +1 -1
  6. package/dist/mcp/agent-variants/WorktreeOrchestrator.js +185 -14
  7. package/dist/mcp/agent-variants/WorktreeOrchestrator.js.map +1 -1
  8. package/dist/mcp/agent-variants/WorktreeOrchestrator.testHelpers.d.ts.map +1 -1
  9. package/dist/mcp/agent-variants/WorktreeOrchestrator.testHelpers.js +13 -0
  10. package/dist/mcp/agent-variants/WorktreeOrchestrator.testHelpers.js.map +1 -1
  11. package/dist/mcp/agent-variants/briefContent.d.ts +16 -0
  12. package/dist/mcp/agent-variants/briefContent.d.ts.map +1 -0
  13. package/dist/mcp/agent-variants/briefContent.js +42 -0
  14. package/dist/mcp/agent-variants/briefContent.js.map +1 -0
  15. package/dist/mcp/agent-variants/contracts.d.ts +1 -0
  16. package/dist/mcp/agent-variants/contracts.d.ts.map +1 -1
  17. package/dist/mcp/agent-variants/contracts.js +1 -1
  18. package/dist/mcp/agent-variants/contracts.js.map +1 -1
  19. package/dist/mcp/agent-variants/tools.d.ts +10 -0
  20. package/dist/mcp/agent-variants/tools.d.ts.map +1 -1
  21. package/dist/mcp/agent-variants/tools.js +11 -4
  22. package/dist/mcp/agent-variants/tools.js.map +1 -1
  23. package/dist/mcp/server.d.ts +57 -3
  24. package/dist/mcp/server.d.ts.map +1 -1
  25. package/dist/mcp/server.js +457 -48
  26. package/dist/mcp/server.js.map +1 -1
  27. package/dist/prototype-host.js +1 -1
  28. package/dist/routes/agentVariants.d.ts +15 -1
  29. package/dist/routes/agentVariants.d.ts.map +1 -1
  30. package/dist/routes/agentVariants.js +173 -7
  31. package/dist/routes/agentVariants.js.map +1 -1
  32. package/dist/routes/comments.d.ts +3 -0
  33. package/dist/routes/comments.d.ts.map +1 -0
  34. package/dist/routes/comments.js +113 -0
  35. package/dist/routes/comments.js.map +1 -0
  36. package/dist/routes/prototypeHost.d.ts +1 -1
  37. package/dist/routes/prototypeHost.d.ts.map +1 -1
  38. package/dist/routes/prototypeHost.js +164 -12
  39. package/dist/routes/prototypeHost.js.map +1 -1
  40. package/dist/server.d.ts.map +1 -1
  41. package/dist/server.js +2 -0
  42. package/dist/server.js.map +1 -1
  43. package/dist/services/BundleStore.d.ts +14 -0
  44. package/dist/services/BundleStore.d.ts.map +1 -1
  45. package/dist/services/BundleStore.js +77 -13
  46. package/dist/services/BundleStore.js.map +1 -1
  47. package/dist/services/HistoryReplayService.d.ts +78 -0
  48. package/dist/services/HistoryReplayService.d.ts.map +1 -0
  49. package/dist/services/HistoryReplayService.js +265 -0
  50. package/dist/services/HistoryReplayService.js.map +1 -0
  51. package/dist/services/ProjectDetectionService.d.ts.map +1 -1
  52. package/dist/services/ProjectDetectionService.js +14 -5
  53. package/dist/services/ProjectDetectionService.js.map +1 -1
  54. package/dist/services/PrototypeBuildService.d.ts +2 -2
  55. package/dist/services/PrototypeBuildService.js +2 -2
  56. package/dist/services/PrototypeDeployService.d.ts +11 -3
  57. package/dist/services/PrototypeDeployService.d.ts.map +1 -1
  58. package/dist/services/PrototypeDeployService.js +22 -9
  59. package/dist/services/PrototypeDeployService.js.map +1 -1
  60. package/dist/services/TreeStore.d.ts +118 -0
  61. package/dist/services/TreeStore.d.ts.map +1 -0
  62. package/dist/services/TreeStore.js +386 -0
  63. package/dist/services/TreeStore.js.map +1 -0
  64. package/dist/services/VariantHistoryService.d.ts +83 -0
  65. package/dist/services/VariantHistoryService.d.ts.map +1 -1
  66. package/dist/services/VariantHistoryService.js +169 -30
  67. package/dist/services/VariantHistoryService.js.map +1 -1
  68. package/dist/utils/skills/claude-skill.d.ts +1 -1
  69. package/dist/utils/skills/claude-skill.js +1 -1
  70. package/dist/utils/skills/cursor-rules.d.ts +1 -1
  71. package/dist/utils/skills/cursor-rules.js +1 -1
  72. package/dist/utils/skills/shared-variants-protocol.d.ts +2 -2
  73. package/dist/utils/skills/shared-variants-protocol.d.ts.map +1 -1
  74. package/dist/utils/skills/shared-variants-protocol.js +8 -6
  75. package/dist/utils/skills/shared-variants-protocol.js.map +1 -1
  76. package/package.json +1 -1
  77. package/src/ui/dist/assets/main-D5T0vjqq.js +655 -0
  78. package/src/ui/dist/assets/main-D_9NxkWD.css +1 -0
  79. package/src/ui/dist/favicon-16.png +0 -0
  80. package/src/ui/dist/favicon-32.png +0 -0
  81. package/src/ui/dist/favicon.ico +0 -0
  82. package/src/ui/dist/index.html +4 -2
  83. package/src/ui/dist/assets/main-C8R68t8Q.css +0 -1
  84. package/src/ui/dist/assets/main-CwQdqqea.js +0 -655
@@ -17,6 +17,7 @@ const devServerError_1 = require("../../utils/devServerError");
17
17
  const errors_1 = require("./errors");
18
18
  const createProjectArtifacts_1 = require("./createProjectArtifacts");
19
19
  const contracts_1 = require("./contracts");
20
+ const briefContent_1 = require("./briefContent");
20
21
  const runLabel_1 = require("./runLabel");
21
22
  const runPlan_1 = require("./runPlan");
22
23
  const viteReactTs_1 = require("../../services/templates/viteReactTs");
@@ -28,6 +29,17 @@ const VariantHistoryService_1 = require("../../services/VariantHistoryService");
28
29
  const variantContext_1 = require("./variantContext");
29
30
  const log = (0, logger_1.createLogger)('AgentVariantsOrchestrator');
30
31
  const FRESH_DEV_SERVER_HOST = '127.0.0.1';
32
+ // Upper bound on waiting for in-flight history persists during session
33
+ // teardown. Generous enough for a full-repo snapshot on a large project;
34
+ // small enough that a wedged git subprocess (which has its own 120s block
35
+ // timeout) can't make commit/cancel appear hung.
36
+ const SETTLE_PERSISTS_TEARDOWN_TIMEOUT_MS = 20_000;
37
+ // Sentinel port for deriving a persist-time serve recipe when the dev server
38
+ // hasn't booted yet: resolved into the command, then reverse-substituted to
39
+ // the ${PORT} placeholder. Digit-boundary-guarded substitution means only an
40
+ // exact standalone occurrence is replaced, and 65535 never appears in any
41
+ // framework's default invocation.
42
+ const RECIPE_PORT_SENTINEL = 65535;
31
43
  // Fresh worktrees run the Vite React template, whose dev server defaults to
32
44
  // 5173. Used as the scan start when allocating a fresh variant's preview port.
33
45
  const FRESH_DEV_SERVER_DEFAULT_PORT = 5173;
@@ -1663,7 +1675,7 @@ class AgentVariantsOrchestrator {
1663
1675
  buildUnifiedBriefs(args) {
1664
1676
  const briefId = (i) => args.uniqueBriefIds ? `v-${(0, crypto_1.randomUUID)().slice(0, 8)}` : `v${i + 1}`;
1665
1677
  if (args.briefs) {
1666
- return args.briefs.map((b, i) => ({
1678
+ return args.briefs.map((b, i) => (0, briefContent_1.normalizeBriefContent)({
1667
1679
  briefId: briefId(i),
1668
1680
  label: b.label,
1669
1681
  body: b.body,
@@ -3207,6 +3219,7 @@ class AgentVariantsOrchestrator {
3207
3219
  const proc = await this.worktrees.startDevServer(dev.cwd, port, dev.cmd, dev.args, dev.env);
3208
3220
  args.record.port = port;
3209
3221
  args.record.devServerProcess = proc;
3222
+ args.record.devCommand = dev;
3210
3223
  // If the dev server dies on its own (crash, OOM, Vite hard-fail),
3211
3224
  // clear the port so the iframe stops routing the chip onto a dead
3212
3225
  // socket. Without this the proxy retargets onto an unreachable port
@@ -3366,12 +3379,12 @@ class AgentVariantsOrchestrator {
3366
3379
  if (this.store.getProjectContext(sessionId).kind === 'fresh') {
3367
3380
  // History at `<workspacePath>/.rivet/variants/<sessionId>/<variantId>/`
3368
3381
  // is the on-disk record for this generated project.
3369
- this.persistCompletedFreshVariant({
3382
+ this.trackHistoryPersist(this.persistCompletedFreshVariant({
3370
3383
  sessionId,
3371
3384
  workItemId,
3372
3385
  }).catch((err) => {
3373
3386
  log.warn(`persistCompletedFreshVariant failed for ${sessionId}/${workItemId}`, err);
3374
- });
3387
+ }));
3375
3388
  }
3376
3389
  const leasedAt = resources.leasedAt.get(workItemId);
3377
3390
  trackStaticPreviewCompleted(this.telemetry, {
@@ -3422,21 +3435,21 @@ class AgentVariantsOrchestrator {
3422
3435
  // failure here must never block dev-server startup or the user's pick
3423
3436
  // flow.
3424
3437
  if (!isFresh) {
3425
- this.persistCompletedExistingVariant({
3438
+ this.trackHistoryPersist(this.persistCompletedExistingVariant({
3426
3439
  sessionId,
3427
3440
  workItemId,
3428
3441
  diff: record.diff ?? '',
3429
3442
  }).catch((err) => {
3430
3443
  log.warn(`persistCompletedExistingVariant failed for ${sessionId}/${workItemId}`, err);
3431
- });
3444
+ }));
3432
3445
  }
3433
3446
  else {
3434
- this.persistCompletedFreshVariant({
3447
+ this.trackHistoryPersist(this.persistCompletedFreshVariant({
3435
3448
  sessionId,
3436
3449
  workItemId,
3437
3450
  }).catch((err) => {
3438
3451
  log.warn(`persistCompletedFreshVariant failed for ${sessionId}/${workItemId}`, err);
3439
- });
3452
+ }));
3440
3453
  }
3441
3454
  // Static existing-mode projects (framework='static', no dev command) have
3442
3455
  // no per-variant dev server to proxy the iframe at. Serve the variant's
@@ -3535,6 +3548,35 @@ class AgentVariantsOrchestrator {
3535
3548
  // The caller (reportComplete) emits the change after recording success so
3536
3549
  // the snapshot reflects both the swapped HTML and refinement.status.
3537
3550
  }
3551
+ /** The worktree/dev-server manager this orchestrator boots previews with.
3552
+ * Exposed so HistoryReplayService shares the SAME instance: the
3553
+ * reserved-but-not-yet-bound port set is per-instance, and a second
3554
+ * manager scanning the same range can hand a live variant's pending port
3555
+ * to a replay (EADDRINUSE for the loser). */
3556
+ get devServerManager() {
3557
+ return this.worktrees;
3558
+ }
3559
+ /** In-flight fire-and-forget history persists. Tree-store snapshots spawn
3560
+ * git subprocesses, so these can outlive the microtask flushes that used
3561
+ * to be enough to drain them — {@link teardownSession} (and the test
3562
+ * helpers' settleBackgroundWork) await {@link settleHistoryPersists}
3563
+ * instead of racing the writes. */
3564
+ pendingHistoryPersists = new Set();
3565
+ trackHistoryPersist(persist) {
3566
+ this.pendingHistoryPersists.add(persist);
3567
+ void persist
3568
+ .catch(() => undefined)
3569
+ .finally(() => {
3570
+ this.pendingHistoryPersists.delete(persist);
3571
+ });
3572
+ }
3573
+ /** Await every in-flight history persist, including ones that start while
3574
+ * draining (a refine can persist mid-settle). */
3575
+ async settleHistoryPersists() {
3576
+ while (this.pendingHistoryPersists.size > 0) {
3577
+ await Promise.allSettled([...this.pendingHistoryPersists]);
3578
+ }
3579
+ }
3538
3580
  /**
3539
3581
  * Persist a completed existing-project code_gen variant into
3540
3582
  * `<env.projectPath>/.rivet/variants/<sessionId>/<variantId>/`. Called from
@@ -3568,10 +3610,57 @@ class AgentVariantsOrchestrator {
3568
3610
  }
3569
3611
  const input = this.store.getWorkItemInput(args.sessionId, args.workItemId);
3570
3612
  const sessionPrompt = this.store.getPrompt(args.sessionId);
3613
+ // Tree-store snapshot inputs (dual-write). The snapshot root is the
3614
+ // worktree root — the git root, per the tree-root contract — for EVERY
3615
+ // framework, not just static: this is what makes dev-server (Vite /
3616
+ // fullstack) variants replayable from history instead of diff-only.
3617
+ const snapshotDir = record?.worktreePath;
3618
+ // Lazy per-session base snapshot from the host repo root. Best-effort:
3619
+ // resolution failure just means no base ref (variant commits still land).
3620
+ let baseDir;
3621
+ try {
3622
+ baseDir = await this.worktrees.getGitRoot?.();
3623
+ }
3624
+ catch (err) {
3625
+ log.warn(`persistCompletedExistingVariant: git root resolution failed for ${args.sessionId}`, err);
3626
+ }
3627
+ let serve;
3628
+ if (sourceDir && record) {
3629
+ // Static framework: entryPath is relative to the snapshot root (the
3630
+ // worktree root), pointing into the nested project dir when the
3631
+ // project lives below the git root.
3632
+ const entryPath = path_1.default.relative(record.worktreePath, path_1.default.join(sourceDir, 'index.html'));
3633
+ serve = { kind: 'static', entryPath };
3634
+ }
3635
+ else if (record?.devCommand && record.port !== undefined) {
3636
+ serve = {
3637
+ kind: 'dev-server',
3638
+ recipe: buildServeRecipeFromResolved(record.worktreePath, record.devCommand, record.port),
3639
+ };
3640
+ }
3641
+ else if (record) {
3642
+ // Persist fires at code-gen success, BEFORE the dev server boots (the
3643
+ // boot is what stashes record.devCommand/port) — so for the common
3644
+ // path the live invocation isn't available yet. Run the same
3645
+ // deterministic resolution the boot will use, against a sentinel port
3646
+ // that becomes the ${PORT} placeholder. No dependency install here —
3647
+ // resolveDevServerCommand is the side-effect-free half of the split.
3648
+ try {
3649
+ const dev = await this.resolveDevServerCommand(args.sessionId, record.worktreePath, RECIPE_PORT_SENTINEL, false);
3650
+ serve = {
3651
+ kind: 'dev-server',
3652
+ recipe: buildServeRecipeFromResolved(record.worktreePath, dev, RECIPE_PORT_SENTINEL),
3653
+ };
3654
+ }
3655
+ catch (err) {
3656
+ log.warn(`persistCompletedExistingVariant: dev command resolution failed for ${args.sessionId}/${args.workItemId} — persisting without a serve recipe`, err);
3657
+ }
3658
+ }
3571
3659
  await this.variantHistory.persistVariant({
3572
3660
  projectPath: env.projectPath,
3573
3661
  sessionId: args.sessionId,
3574
3662
  variantId: args.workItemId,
3663
+ ...(input.requestId ? { requestId: input.requestId } : {}),
3575
3664
  label: input.briefLabel,
3576
3665
  brief: input.briefBody,
3577
3666
  sessionPrompt,
@@ -3584,6 +3673,9 @@ class AgentVariantsOrchestrator {
3584
3673
  : { kind: 'none', reason: 'diff_only' },
3585
3674
  changedFilesCount: countDiffFiles(args.diff),
3586
3675
  projectKind: 'existing',
3676
+ snapshotDir,
3677
+ baseDir,
3678
+ serve,
3587
3679
  });
3588
3680
  }
3589
3681
  /**
@@ -3772,11 +3864,42 @@ class AgentVariantsOrchestrator {
3772
3864
  script: 'dev',
3773
3865
  },
3774
3866
  };
3867
+ // Tree-store serve contract. Fresh worktrees prefer the live record's
3868
+ // resolved invocation (stashed at dev-server start); the fallback recipe
3869
+ // mirrors resolveDevServer's fresh branch, which is fully deterministic
3870
+ // for the Vite template. Static previews serve their staged index.html.
3871
+ // Fresh sessions have no host project, so there is no base snapshot —
3872
+ // variant commits are root commits.
3873
+ const serve = staticPreview
3874
+ ? { kind: 'static', entryPath: 'index.html' }
3875
+ : worktreeRecord?.devCommand && worktreeRecord.port !== undefined
3876
+ ? {
3877
+ kind: 'dev-server',
3878
+ recipe: buildServeRecipeFromResolved(worktreeRecord.worktreePath, worktreeRecord.devCommand, worktreeRecord.port),
3879
+ }
3880
+ : {
3881
+ kind: 'dev-server',
3882
+ recipe: {
3883
+ cwd: '.',
3884
+ cmd: 'npm',
3885
+ argsTemplate: [
3886
+ 'run',
3887
+ 'dev',
3888
+ '--',
3889
+ '--port',
3890
+ '${PORT}',
3891
+ '--host',
3892
+ FRESH_DEV_SERVER_HOST,
3893
+ ],
3894
+ envTemplate: { PORT: '${PORT}' },
3895
+ },
3896
+ };
3775
3897
  try {
3776
3898
  await this.variantHistory.persistVariant({
3777
3899
  projectPath: historyProjectPath,
3778
3900
  sessionId: args.sessionId,
3779
3901
  variantId: args.workItemId,
3902
+ ...(input.requestId ? { requestId: input.requestId } : {}),
3780
3903
  label: input.briefLabel,
3781
3904
  brief: input.briefBody,
3782
3905
  sessionPrompt,
@@ -3788,6 +3911,8 @@ class AgentVariantsOrchestrator {
3788
3911
  projectKind: 'fresh',
3789
3912
  designMarkdown: designArtifact?.markdown,
3790
3913
  designSource: designArtifact?.source,
3914
+ snapshotDir: sourceDir,
3915
+ serve,
3791
3916
  });
3792
3917
  }
3793
3918
  finally {
@@ -3975,6 +4100,25 @@ class AgentVariantsOrchestrator {
3975
4100
  * projects defer to the user's framework/packageManager config.
3976
4101
  */
3977
4102
  async resolveDevServer(sessionId, workItemId, worktreePath, port, isFresh) {
4103
+ const resolved = await this.resolveDevServerCommand(sessionId, worktreePath, port, isFresh);
4104
+ if (!isFresh) {
4105
+ await this.ensureExistingProjectDependencies({
4106
+ sessionId,
4107
+ workItemId,
4108
+ cwd: resolved.cwd,
4109
+ packageManager: resolved.packageManager,
4110
+ });
4111
+ }
4112
+ return resolved;
4113
+ }
4114
+ /**
4115
+ * Pure invocation resolution — no dependency installs, no side effects.
4116
+ * Split from {@link resolveDevServer} so history persistence can capture
4117
+ * the SAME deterministic resolution the boot path uses without booting:
4118
+ * persist fires at code-gen success, before the dev server starts, so the
4119
+ * live record's stashed command usually isn't available yet.
4120
+ */
4121
+ async resolveDevServerCommand(sessionId, worktreePath, port, isFresh) {
3978
4122
  if (isFresh) {
3979
4123
  return {
3980
4124
  cwd: worktreePath,
@@ -3989,31 +4133,28 @@ class AgentVariantsOrchestrator {
3989
4133
  FRESH_DEV_SERVER_HOST,
3990
4134
  ],
3991
4135
  env: { PORT: String(port) },
4136
+ packageManager: 'npm',
3992
4137
  };
3993
4138
  }
3994
4139
  const env = await this.resolveEnv(sessionId);
3995
4140
  const cwd = await this.resolveExistingProjectCwd(sessionId, worktreePath, env);
3996
4141
  const packageManager = detectPackageManagerForCwd(cwd, env.packageManager);
3997
- await this.ensureExistingProjectDependencies({
3998
- sessionId,
3999
- workItemId,
4000
- cwd,
4001
- packageManager,
4002
- });
4003
4142
  if (packageManager !== env.packageManager) {
4004
4143
  return {
4005
4144
  cwd,
4145
+ packageManager,
4006
4146
  ...buildExistingProjectDevCommand(packageManager, env.devCommand, port),
4007
4147
  };
4008
4148
  }
4009
4149
  if (env.buildDevCommand) {
4010
- return { cwd, ...env.buildDevCommand(port) };
4150
+ return { cwd, packageManager, ...env.buildDevCommand(port) };
4011
4151
  }
4012
4152
  return {
4013
4153
  cwd,
4014
4154
  cmd: env.packageManager,
4015
4155
  args: [env.devCommand, '--port', String(port)],
4016
4156
  env: { PORT: String(port) },
4157
+ packageManager,
4017
4158
  };
4018
4159
  }
4019
4160
  /**
@@ -4411,6 +4552,17 @@ class AgentVariantsOrchestrator {
4411
4552
  return;
4412
4553
  resources.cleanupStarted = true;
4413
4554
  log.info(`Tearing down session ${sessionId} (reason: ${reason})`);
4555
+ // Drain in-flight history persists BEFORE deleting worktrees: a tree
4556
+ // snapshot's `git add -A --work-tree <worktree>` racing the deletion
4557
+ // below doesn't fail — it stages whatever files remain, publishing a
4558
+ // silently truncated snapshot. The design doc's ordering constraint
4559
+ // ("tree committed before worktree teardown") is enforced here, not
4560
+ // just in tests. Best-effort with a bound: never let a wedged git call
4561
+ // stall teardown forever.
4562
+ await Promise.race([
4563
+ this.settleHistoryPersists(),
4564
+ new Promise((resolve) => setTimeout(resolve, SETTLE_PERSISTS_TEARDOWN_TIMEOUT_MS)),
4565
+ ]);
4414
4566
  // Stop dev servers in parallel; ignore individual failures.
4415
4567
  // NOTE: For the fresh vite_app rename path, commitVariant already stopped
4416
4568
  // these dev servers BEFORE the rename (so Vite couldn't recreate `.vite/`
@@ -5472,6 +5624,25 @@ const detectPackageManagerForCwd = (cwd, fallback) => {
5472
5624
  }
5473
5625
  return 'npm';
5474
5626
  };
5627
+ /**
5628
+ * Turn the invocation a live dev server actually booted with into a
5629
+ * port-parameterized replay recipe. The resolution bakes the allocated port
5630
+ * into args/env (`--port <n>`, `PORT=<n>`) — storing that verbatim would
5631
+ * bake in a dead port, so occurrences of the port (digit-boundary-guarded so
5632
+ * incidental digit runs survive) become `${PORT}` for replay to substitute.
5633
+ * `cwd` is stored relative to the snapshot root per the tree-root contract.
5634
+ */
5635
+ const buildServeRecipeFromResolved = (snapshotRoot, dev, port) => {
5636
+ const portPattern = new RegExp(`(?<![0-9])${port}(?![0-9])`, 'g');
5637
+ const sub = (value) => value.replace(portPattern, '${PORT}');
5638
+ const relativeCwd = path_1.default.relative(snapshotRoot, dev.cwd);
5639
+ return {
5640
+ cwd: relativeCwd === '' ? '.' : relativeCwd,
5641
+ cmd: dev.cmd,
5642
+ argsTemplate: dev.args.map(sub),
5643
+ envTemplate: Object.fromEntries(Object.entries(dev.env).map(([key, value]) => [key, sub(value)])),
5644
+ };
5645
+ };
5475
5646
  const buildExistingProjectDevCommand = (packageManager, devCommand, port) => {
5476
5647
  const env = { PORT: String(port) };
5477
5648
  if (packageManager === 'npm') {