rivet-design 0.9.2 → 0.9.4

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 (157) hide show
  1. package/dist/mcp/agent-variants/SessionStore.d.ts +78 -2
  2. package/dist/mcp/agent-variants/SessionStore.d.ts.map +1 -1
  3. package/dist/mcp/agent-variants/SessionStore.js +464 -62
  4. package/dist/mcp/agent-variants/SessionStore.js.map +1 -1
  5. package/dist/mcp/agent-variants/WorktreeOrchestrator.d.ts +331 -9
  6. package/dist/mcp/agent-variants/WorktreeOrchestrator.d.ts.map +1 -1
  7. package/dist/mcp/agent-variants/WorktreeOrchestrator.js +1985 -61
  8. package/dist/mcp/agent-variants/WorktreeOrchestrator.js.map +1 -1
  9. package/dist/mcp/agent-variants/WorktreeOrchestrator.testHelpers.d.ts +65 -0
  10. package/dist/mcp/agent-variants/WorktreeOrchestrator.testHelpers.d.ts.map +1 -0
  11. package/dist/mcp/agent-variants/WorktreeOrchestrator.testHelpers.js +162 -0
  12. package/dist/mcp/agent-variants/WorktreeOrchestrator.testHelpers.js.map +1 -0
  13. package/dist/mcp/agent-variants/contracts.d.ts +2508 -10
  14. package/dist/mcp/agent-variants/contracts.d.ts.map +1 -1
  15. package/dist/mcp/agent-variants/contracts.js +295 -5
  16. package/dist/mcp/agent-variants/contracts.js.map +1 -1
  17. package/dist/mcp/agent-variants/createProjectArtifacts.d.ts +78 -0
  18. package/dist/mcp/agent-variants/createProjectArtifacts.d.ts.map +1 -0
  19. package/dist/mcp/agent-variants/createProjectArtifacts.js +123 -0
  20. package/dist/mcp/agent-variants/createProjectArtifacts.js.map +1 -0
  21. package/dist/mcp/agent-variants/createZeroToOneTool.d.ts +241 -0
  22. package/dist/mcp/agent-variants/createZeroToOneTool.d.ts.map +1 -0
  23. package/dist/mcp/agent-variants/createZeroToOneTool.js +213 -0
  24. package/dist/mcp/agent-variants/createZeroToOneTool.js.map +1 -0
  25. package/dist/mcp/agent-variants/designContextStore.d.ts +160 -0
  26. package/dist/mcp/agent-variants/designContextStore.d.ts.map +1 -0
  27. package/dist/mcp/agent-variants/designContextStore.js +295 -0
  28. package/dist/mcp/agent-variants/designContextStore.js.map +1 -0
  29. package/dist/mcp/agent-variants/elementRefToTarget.d.ts +21 -0
  30. package/dist/mcp/agent-variants/elementRefToTarget.d.ts.map +1 -0
  31. package/dist/mcp/agent-variants/elementRefToTarget.js +47 -0
  32. package/dist/mcp/agent-variants/elementRefToTarget.js.map +1 -0
  33. package/dist/mcp/agent-variants/errors.d.ts +1 -1
  34. package/dist/mcp/agent-variants/errors.d.ts.map +1 -1
  35. package/dist/mcp/agent-variants/errors.js +7 -0
  36. package/dist/mcp/agent-variants/errors.js.map +1 -1
  37. package/dist/mcp/agent-variants/index.d.ts +4 -2
  38. package/dist/mcp/agent-variants/index.d.ts.map +1 -1
  39. package/dist/mcp/agent-variants/index.js +7 -1
  40. package/dist/mcp/agent-variants/index.js.map +1 -1
  41. package/dist/mcp/agent-variants/inspirationDesignContext.d.ts +440 -0
  42. package/dist/mcp/agent-variants/inspirationDesignContext.d.ts.map +1 -0
  43. package/dist/mcp/agent-variants/inspirationDesignContext.js +2467 -0
  44. package/dist/mcp/agent-variants/inspirationDesignContext.js.map +1 -0
  45. package/dist/mcp/agent-variants/pendingChangesAdapter.d.ts.map +1 -1
  46. package/dist/mcp/agent-variants/pendingChangesAdapter.js +21 -7
  47. package/dist/mcp/agent-variants/pendingChangesAdapter.js.map +1 -1
  48. package/dist/mcp/agent-variants/previewQa.d.ts +61 -0
  49. package/dist/mcp/agent-variants/previewQa.d.ts.map +1 -0
  50. package/dist/mcp/agent-variants/previewQa.js +374 -0
  51. package/dist/mcp/agent-variants/previewQa.js.map +1 -0
  52. package/dist/mcp/agent-variants/sourceContext.d.ts +8 -0
  53. package/dist/mcp/agent-variants/sourceContext.d.ts.map +1 -0
  54. package/dist/mcp/agent-variants/sourceContext.js +183 -0
  55. package/dist/mcp/agent-variants/sourceContext.js.map +1 -0
  56. package/dist/mcp/agent-variants/tools.d.ts +36 -0
  57. package/dist/mcp/agent-variants/tools.d.ts.map +1 -1
  58. package/dist/mcp/agent-variants/tools.js +451 -19
  59. package/dist/mcp/agent-variants/tools.js.map +1 -1
  60. package/dist/mcp/changeBatchClassification.d.ts +30 -0
  61. package/dist/mcp/changeBatchClassification.d.ts.map +1 -0
  62. package/dist/mcp/changeBatchClassification.js +65 -0
  63. package/dist/mcp/changeBatchClassification.js.map +1 -0
  64. package/dist/mcp/server.d.ts.map +1 -1
  65. package/dist/mcp/server.js +258 -41
  66. package/dist/mcp/server.js.map +1 -1
  67. package/dist/prompts/agentModPrompts.js +4 -4
  68. package/dist/prompts/agentModPrompts.js.map +1 -1
  69. package/dist/proxy-middleware/proxy-config.d.ts.map +1 -1
  70. package/dist/proxy-middleware/proxy-config.js +1 -15
  71. package/dist/proxy-middleware/proxy-config.js.map +1 -1
  72. package/dist/routes/agentVariants.d.ts +3 -1
  73. package/dist/routes/agentVariants.d.ts.map +1 -1
  74. package/dist/routes/agentVariants.js +138 -13
  75. package/dist/routes/agentVariants.js.map +1 -1
  76. package/dist/routes/mcp.d.ts +7 -1
  77. package/dist/routes/mcp.d.ts.map +1 -1
  78. package/dist/routes/mcp.js +139 -16
  79. package/dist/routes/mcp.js.map +1 -1
  80. package/dist/server.d.ts.map +1 -1
  81. package/dist/server.js +23 -5
  82. package/dist/server.js.map +1 -1
  83. package/dist/services/ProjectDetectionService.d.ts.map +1 -1
  84. package/dist/services/ProjectDetectionService.js +9 -0
  85. package/dist/services/ProjectDetectionService.js.map +1 -1
  86. package/dist/services/SessionBridgeService.d.ts +22 -0
  87. package/dist/services/SessionBridgeService.d.ts.map +1 -1
  88. package/dist/services/SessionBridgeService.js +61 -0
  89. package/dist/services/SessionBridgeService.js.map +1 -1
  90. package/dist/services/TelemetryService.d.ts +121 -0
  91. package/dist/services/TelemetryService.d.ts.map +1 -1
  92. package/dist/services/TelemetryService.js +155 -0
  93. package/dist/services/TelemetryService.js.map +1 -1
  94. package/dist/services/WorktreeManager.d.ts +116 -6
  95. package/dist/services/WorktreeManager.d.ts.map +1 -1
  96. package/dist/services/WorktreeManager.js +394 -19
  97. package/dist/services/WorktreeManager.js.map +1 -1
  98. package/dist/services/agent/AgentModService.js +6 -6
  99. package/dist/services/agent/AgentModService.js.map +1 -1
  100. package/dist/services/templates/designCatalog.d.ts +27 -0
  101. package/dist/services/templates/designCatalog.d.ts.map +1 -0
  102. package/dist/services/templates/designCatalog.js +141 -0
  103. package/dist/services/templates/designCatalog.js.map +1 -0
  104. package/dist/services/templates/designmd/airbnb.md +545 -0
  105. package/dist/services/templates/designmd/airtable.md +554 -0
  106. package/dist/services/templates/designmd/apple.md +562 -0
  107. package/dist/services/templates/designmd/binance.md +634 -0
  108. package/dist/services/templates/designmd/bmw-m.md +503 -0
  109. package/dist/services/templates/designmd/bmw.md +544 -0
  110. package/dist/services/templates/designmd/bugatti.md +454 -0
  111. package/dist/services/templates/designmd/cal.md +542 -0
  112. package/dist/services/templates/designmd/claude.md +589 -0
  113. package/dist/services/templates/designmd/clay.md +541 -0
  114. package/dist/services/templates/designmd/cohere.md +451 -0
  115. package/dist/services/templates/designmd/cursor.md +537 -0
  116. package/dist/services/templates/designmd/expo.md +526 -0
  117. package/dist/services/templates/designmd/figma.md +578 -0
  118. package/dist/services/templates/designmd/framer.md +544 -0
  119. package/dist/services/templates/designmd/hp.md +670 -0
  120. package/dist/services/templates/designmd/linear.app.md +548 -0
  121. package/dist/services/templates/designmd/mintlify.md +852 -0
  122. package/dist/services/templates/designmd/miro.md +825 -0
  123. package/dist/services/templates/designmd/notion.md +821 -0
  124. package/dist/services/templates/designmd/raycast.md +669 -0
  125. package/dist/services/templates/designmd/resend.md +585 -0
  126. package/dist/services/templates/designmd/sentry.md +262 -0
  127. package/dist/services/templates/designmd/shopify.md +350 -0
  128. package/dist/services/templates/designmd/spotify.md +246 -0
  129. package/dist/services/templates/designmd/stripe.md +322 -0
  130. package/dist/services/templates/designmd/supabase.md +255 -0
  131. package/dist/services/templates/designmd/superhuman.md +252 -0
  132. package/dist/services/templates/designmd/uber.md +295 -0
  133. package/dist/services/templates/designmd/vercel.md +310 -0
  134. package/dist/services/templates/viteReactTs.d.ts +48 -0
  135. package/dist/services/templates/viteReactTs.d.ts.map +1 -0
  136. package/dist/services/templates/viteReactTs.js +274 -0
  137. package/dist/services/templates/viteReactTs.js.map +1 -0
  138. package/dist/types/change-request-types.d.ts +29 -3
  139. package/dist/types/change-request-types.d.ts.map +1 -1
  140. package/dist/utils/skills/claude-skill.d.ts +2 -2
  141. package/dist/utils/skills/claude-skill.d.ts.map +1 -1
  142. package/dist/utils/skills/claude-skill.js +19 -98
  143. package/dist/utils/skills/claude-skill.js.map +1 -1
  144. package/dist/utils/skills/cursor-rules.d.ts +2 -2
  145. package/dist/utils/skills/cursor-rules.d.ts.map +1 -1
  146. package/dist/utils/skills/cursor-rules.js +15 -80
  147. package/dist/utils/skills/cursor-rules.js.map +1 -1
  148. package/dist/utils/skills/shared-variants-protocol.d.ts +23 -0
  149. package/dist/utils/skills/shared-variants-protocol.d.ts.map +1 -0
  150. package/dist/utils/skills/shared-variants-protocol.js +130 -0
  151. package/dist/utils/skills/shared-variants-protocol.js.map +1 -0
  152. package/package.json +6 -6
  153. package/src/ui/dist/assets/main-CpX7fB64.js +382 -0
  154. package/src/ui/dist/assets/main-Qqe2_oMT.css +1 -0
  155. package/src/ui/dist/index.html +2 -2
  156. package/src/ui/dist/assets/main-AsPCtLsx.js +0 -382
  157. package/src/ui/dist/assets/main-BzmseUDd.css +0 -1
@@ -1,11 +1,59 @@
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 });
6
+ exports.normalizeAgentOutput = exports.slugifyPrompt = void 0;
3
7
  exports.registerAgentVariantsTools = registerAgentVariantsTools;
8
+ exports.buildQaFailurePrompt = buildQaFailurePrompt;
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const os_1 = __importDefault(require("os"));
11
+ const path_1 = __importDefault(require("path"));
4
12
  const contracts_1 = require("./contracts");
5
13
  const errors_1 = require("./errors");
14
+ const createZeroToOneTool_1 = require("./createZeroToOneTool");
15
+ Object.defineProperty(exports, "slugifyPrompt", { enumerable: true, get: function () { return createZeroToOneTool_1.slugifyPrompt; } });
16
+ const viteReactTs_1 = require("../../services/templates/viteReactTs");
17
+ const ProjectDetectionService_1 = require("../../services/ProjectDetectionService");
18
+ const SessionStore_1 = require("./SessionStore");
19
+ /**
20
+ * Coerce `output` to a structured value when an agent (incorrectly) sends a
21
+ * stringified JSON. The MCP schema accepts `z.unknown()` so the call doesn't
22
+ * fail validation, but downstream consumers (e.g. `output.html` in
23
+ * WorktreeOrchestrator) expect an object and silently no-op on strings —
24
+ * leaving the variant stuck at "not ready" until session expiry.
25
+ */
26
+ const normalizeAgentOutput = (output) => {
27
+ if (typeof output !== 'string')
28
+ return output;
29
+ try {
30
+ return JSON.parse(output);
31
+ }
32
+ catch {
33
+ return output;
34
+ }
35
+ };
36
+ exports.normalizeAgentOutput = normalizeAgentOutput;
37
+ /**
38
+ * Reject caller-supplied fresh destinations at the generic variants boundary.
39
+ * Fresh projects must go through `create_zero_to_one_project`, where the
40
+ * server derives the destination path before delegating to the orchestrator.
41
+ */
42
+ const validateFreshProjectInput = (args) => {
43
+ if (args.projectContext?.kind !== 'fresh')
44
+ return null;
45
+ return {
46
+ stage: 'failed',
47
+ errorCode: 'SCHEMA_VALIDATION_FAILED',
48
+ message: 'Fresh-project sessions must be started with create_zero_to_one_project so Rivet derives and validates the destination path.',
49
+ };
50
+ };
6
51
  function registerAgentVariantsTools(mcp, deps) {
7
- mcp.tool('propose_variants', 'START HERE for any "create variants", "show me N options for X", "explore approaches to X", "build me Y from scratch", or "create a new Z app" request. DO NOT generate variants natively (Task tool, parallel tool calls, or your own creative process). Returns { sessionId, briefWorkItem }. Protocol: 1) call this tool, 2) run the brief work item with your own LLM to generate N briefs, 3) call report_variant_briefs, 3.5) AUTO-OPEN the Rivet editor — call detect_project + open_visual_editor if not already open; the user needs the iframe up to see variants render, do NOT wait for them to ask, 4) SHOW the briefs to the user IN CHAT as a numbered list and ask "look good or want adjustments?" — DO NOT ask which one to pick, 5) wait for user feedback (affirmative → proceed; tweak → bodyOverride or re-brief), 6) call approve_variant_briefs({sessionId, briefIds: [all]}), 7) call continue_variants(action="request_work") to lease all items in parallel (scaffold_base first for fresh-project), 8) run all code-gens IN PARALLEL in their own worktreePath, calling report_variant_complete per item, 9) DO NOT print diffs — instead tell the user the variants are ready and to use the chip at the bottom-center of the Rivet iframe to cycle (prev/next pills) and commit (check button), 10) watch_for_changes (or poll get_pending_changes) — when the user clicks the check, you get a VariantChangeItem on the queue, 11) apply via git apply (diff payload) or copy the worktree (new-project payload). Pass projectContext.kind=fresh with workspacePath for brand-new projects.', contracts_1.proposeVariantsInput, async (args) => {
52
+ mcp.tool('propose_variants', 'Stage 1 of the legacy variants flow with an in-chat brief approval gate. Prefer `start_variants` for new code paths it collapses propose + report_briefs + approve + request_work into a single call. Use this only when the agent needs the user to review briefs before generation. Returns { sessionId, briefWorkItem }. Full protocol in the rivet skill.', contracts_1.proposeVariantsInput, async (args) => {
8
53
  try {
54
+ const failure = validateFreshProjectInput(args);
55
+ if (failure)
56
+ return jsonResponse(failure);
9
57
  const result = deps.orchestrator.propose({
10
58
  sessionId: args.sessionId,
11
59
  prompt: args.prompt,
@@ -13,6 +61,21 @@ function registerAgentVariantsTools(mcp, deps) {
13
61
  target: args.target,
14
62
  projectContext: args.projectContext,
15
63
  });
64
+ if (result.stage === 'awaiting_source_plan') {
65
+ const out = {
66
+ sessionId: result.sessionId,
67
+ stage: 'awaiting_source_plan',
68
+ sourcePlanWorkItem: {
69
+ id: result.sourcePlanWorkItem.id,
70
+ kind: 'source_plan',
71
+ attempt: result.sourcePlanWorkItem.attempt,
72
+ input: result.sourcePlanWorkItem.input,
73
+ output_schema: SessionStore_1.SOURCE_PLAN_OUTPUT_SCHEMA,
74
+ },
75
+ nextAction: 'continue_variants',
76
+ };
77
+ return jsonResponse(out);
78
+ }
16
79
  const out = {
17
80
  sessionId: result.sessionId,
18
81
  stage: 'awaiting_briefs',
@@ -21,7 +84,7 @@ function registerAgentVariantsTools(mcp, deps) {
21
84
  kind: 'brief',
22
85
  attempt: result.briefWorkItem.attempt,
23
86
  input: result.briefWorkItem.input,
24
- output_schema: { briefs: 'Array<{briefId, label, body}>' },
87
+ output_schema: { briefs: 'Array<{briefId, label: "≤4-word title", body: "ONE sentence ≤12 words"}>' },
25
88
  },
26
89
  nextAction: 'report_variant_briefs',
27
90
  };
@@ -31,7 +94,7 @@ function registerAgentVariantsTools(mcp, deps) {
31
94
  return errorResponse(err);
32
95
  }
33
96
  });
34
- mcp.tool('report_variant_briefs', 'Agent reports N drafted briefs back to Rivet after running its own LLM call on the brief work item. Transitions the session to awaiting_approval; user reviews briefs in the variants tab.', contracts_1.reportVariantBriefsInput, async (args) => {
97
+ mcp.tool('report_variant_briefs', 'Report drafted briefs from the leased brief work item. Each brief: label 4 words, body = exactly ONE sentence ≤ 12 words — a single evocative descriptor, no bullets, no lists, no multi-clause run-ons. Body is shown directly to the user in the variants panel so it must be concise and immediately scannable. On success, transitions to awaiting_approval.', contracts_1.reportVariantBriefsInput, async (args) => {
35
98
  try {
36
99
  const result = deps.orchestrator.reportBriefs({
37
100
  sessionId: args.sessionId,
@@ -43,6 +106,7 @@ function registerAgentVariantsTools(mcp, deps) {
43
106
  const out = {
44
107
  stage: 'awaiting_approval',
45
108
  briefs: result.briefs,
109
+ approvalPrompt: buildApprovalPrompt(result.briefs),
46
110
  nextAction: 'continue_variants',
47
111
  };
48
112
  return jsonResponse(out);
@@ -51,8 +115,45 @@ function registerAgentVariantsTools(mcp, deps) {
51
115
  return errorResponse(err);
52
116
  }
53
117
  });
54
- mcp.tool('approve_variant_briefs', 'Call AFTER showing the briefs to the user in chat and getting an affirmative ("looks good", "yep", "go") OR refined selections from them. Provide briefIds for ALL the briefs the user wants generated (typically all of them). Each approved brief gets a parallel code-gen work item Rivet provisions one isolated worktree per item. Advance via continue_variants(action="request_work") to lease and run them in parallel. For tweak-with-feedback, use the selections form with bodyOverride per brief.', contracts_1.approveVariantBriefsInput, async (args) => {
118
+ mcp.tool('report_source_plan', 'Report the consolidated source plan for a leased source_plan work item. Combines URL/role classification, source research findings, browser-extracted DESIGN.md per design_source URL, and the static_preview vs vite_app execution decision. Choose vite_app when the request needs large local assets (e.g. GLB models), model-viewer, Three.js, route structure, package dependencies, or files under public/; choose static_preview for self-contained HTML/CSS/JS prototypes. If the requirement is ambiguous, set executionPlan.userQuestion instead of guessing Rivet will surface it as a clarification blocker at approve time. Reports unlock the brief work item for source-grounded fresh-project sessions.', contracts_1.reportSourcePlanInput, async (args) => {
119
+ try {
120
+ const result = deps.orchestrator.reportSourcePlan({
121
+ sessionId: args.sessionId,
122
+ workItemId: args.workItemId,
123
+ leaseId: args.leaseId,
124
+ attempt: args.attempt,
125
+ sourcePlan: args.sourcePlan,
126
+ });
127
+ const plan = args.sourcePlan.executionPlan;
128
+ const out = {
129
+ stage: 'awaiting_briefs',
130
+ briefWorkItem: {
131
+ id: result.briefWorkItem.id,
132
+ kind: 'brief',
133
+ attempt: result.briefWorkItem.attempt,
134
+ input: result.briefWorkItem.input,
135
+ output_schema: {
136
+ briefs: 'Array<{briefId, label: "≤4-word title", body: "ONE sentence ≤12 words"}>',
137
+ },
138
+ },
139
+ executionPlan: {
140
+ mode: plan.mode,
141
+ confidence: plan.confidence,
142
+ reason: plan.reason,
143
+ assetCount: plan.assetPlan?.length ?? 0,
144
+ runtimeRequirementCount: plan.runtimeRequirements?.length ?? 0,
145
+ },
146
+ nextAction: 'report_variant_briefs',
147
+ };
148
+ return jsonResponse(out);
149
+ }
150
+ catch (err) {
151
+ return errorResponse(err);
152
+ }
153
+ });
154
+ mcp.tool('approve_variant_briefs', 'Call AFTER showing the briefs to the user in chat and getting an affirmative ("looks good", "yep", "go") OR refined selections from them. Set confirmedByUser=true when the user approves. Only use bypassRequestedByUser=true (with bypassReason) if the user explicitly asks to bypass confirmation. Provide briefIds for ALL the briefs the user wants generated (typically all of them). Each approved brief gets a parallel code-gen work item — Rivet provisions one isolated worktree per item. Advance via continue_variants(action="request_work") to lease and run them in parallel. For tweak-with-feedback, use the selections form with bodyOverride per brief.', contracts_1.approveVariantBriefsInput, async (args) => {
55
155
  try {
156
+ assertExplicitUserConfirmation('approve_variant_briefs', args, 'approve variant briefs');
56
157
  const selections = args.selections ??
57
158
  (args.briefIds ?? []).map((briefId) => ({ briefId }));
58
159
  if (selections.length === 0) {
@@ -75,8 +176,9 @@ function registerAgentVariantsTools(mcp, deps) {
75
176
  return errorResponse(err);
76
177
  }
77
178
  });
78
- mcp.tool('commit_variant', "Call AFTER all variants have been generated AND you've shown each variant's diff/changes to the user in chat AND the user has picked one. Pass the variantId (the workItemId of the chosen variant). Rivet looks up that variant's captured diff and enqueues it onto the existing pending-changes channel — call get_pending_changes next to retrieve and apply. Idempotent on (sessionId, variantId).", contracts_1.commitVariantInput, async (args) => {
179
+ mcp.tool('commit_variant', "Call AFTER all variants have been generated AND you've shown each variant's diff/changes to the user in chat AND the user has picked one. Set confirmedByUser=true when the user confirms the pick. Only use bypassRequestedByUser=true (with bypassReason) if the user explicitly asks to bypass confirmation. Pass the variantId (the workItemId of the chosen variant). Rivet looks up that variant's captured diff and enqueues it onto the existing pending-changes channel — call get_pending_changes next to retrieve and apply. Idempotent on (sessionId, variantId).", contracts_1.commitVariantInput, async (args) => {
79
180
  try {
181
+ assertExplicitUserConfirmation('commit_variant', args, 'commit a variant');
80
182
  const result = await deps.orchestrator.commitVariant({
81
183
  sessionId: args.sessionId,
82
184
  variantId: args.variantId,
@@ -86,6 +188,8 @@ function registerAgentVariantsTools(mcp, deps) {
86
188
  enqueued: result.enqueued,
87
189
  duplicate: result.duplicate,
88
190
  changedFilesCount: result.changedFilesCount,
191
+ payloadKind: result.payloadKind,
192
+ destinationPath: result.destinationPath,
89
193
  };
90
194
  return jsonResponse(out);
91
195
  }
@@ -93,7 +197,7 @@ function registerAgentVariantsTools(mcp, deps) {
93
197
  return errorResponse(err);
94
198
  }
95
199
  });
96
- mcp.tool('continue_variants', 'Long-poll/state-pump the variants flow. action=check returns immediately; action=wait blocks up to ~5min for a state change; action=request_work asks for a lease on ready work items.', contracts_1.continueVariantsInput, async (args) => {
200
+ mcp.tool('continue_variants', 'Long-poll/state-pump the variants flow. action=check returns immediately; action=wait blocks up to ~5min for state changes; action=request_work leases ready work items (source_research, static_preview, or code_gen depending on stage). static_preview is for fresh zero-to-one sessions and should return {html, css?, js?} so Rivet can show fast visual previews before Vite/code generation finishes.', contracts_1.continueVariantsInput, async (args) => {
97
201
  try {
98
202
  const stage = deps.orchestrator.getStage(args.sessionId);
99
203
  if (stage === 'awaiting_briefs') {
@@ -102,24 +206,35 @@ function registerAgentVariantsTools(mcp, deps) {
102
206
  throw new errors_1.AgentVariantsError('INVALID_STAGE_ACTION', 'Run the brief work item and call report_variant_briefs before continue_variants');
103
207
  }
104
208
  if (stage === 'awaiting_approval') {
209
+ const briefs = deps.orchestrator.getBriefs(args.sessionId);
105
210
  return jsonResponse({
106
211
  stage: 'awaiting_approval',
107
- briefs: deps.orchestrator.getBriefs(args.sessionId),
212
+ briefs,
213
+ approvalPrompt: buildApprovalPrompt(briefs),
108
214
  });
109
215
  }
110
- if (stage === 'work_items_ready' &&
111
- args.action === 'request_work') {
216
+ if (args.action === 'request_work' &&
217
+ (stage === 'awaiting_source_plan' ||
218
+ stage === 'work_items_ready' ||
219
+ stage === 'waiting_for_results')) {
220
+ // SessionStore.requestWork accepts both stages and expires stale
221
+ // leases before issuing new ones, so this also recycles wedged
222
+ // variants whose leases expired while the session was already in
223
+ // waiting_for_results.
112
224
  const lease = deps.orchestrator.requestWork({
113
225
  sessionId: args.sessionId,
114
226
  leaseOwner: deps.leaseOwner(),
115
227
  requestedLeaseCount: args.requestedLeaseCount,
116
228
  });
117
- return jsonResponse({
118
- stage: 'work_items_ready',
229
+ const postLeaseStage = deps.orchestrator.getStage(args.sessionId);
230
+ const leasePayload = {
119
231
  leaseId: lease.leaseId,
120
232
  leaseTtlMs: lease.leaseTtlMs,
121
233
  leasedWorkItems: lease.leasedWorkItems,
122
- });
234
+ };
235
+ return jsonResponse(postLeaseStage === 'awaiting_source_plan'
236
+ ? { stage: 'awaiting_source_plan', ...leasePayload }
237
+ : { stage: 'work_items_ready', ...leasePayload });
123
238
  }
124
239
  if (stage === 'work_items_ready' || stage === 'waiting_for_results') {
125
240
  return jsonResponse({
@@ -127,17 +242,36 @@ function registerAgentVariantsTools(mcp, deps) {
127
242
  progress: deps.orchestrator.getProgress(args.sessionId),
128
243
  });
129
244
  }
245
+ if (stage === 'awaiting_source_plan') {
246
+ return jsonResponse({
247
+ stage: 'awaiting_source_plan',
248
+ progress: deps.orchestrator.getProgress(args.sessionId),
249
+ });
250
+ }
130
251
  // Terminal stages
131
- return jsonResponse({
132
- stage,
133
- summary: deps.orchestrator.getSummary(args.sessionId),
134
- });
252
+ {
253
+ const summary = deps.orchestrator.getSummary(args.sessionId);
254
+ const variants = deps.orchestrator.getVariants(args.sessionId);
255
+ const previewUrls = variants
256
+ .filter((v) => v.preview?.kind === 'static_artifact')
257
+ .map((v) => ({
258
+ label: v.label,
259
+ url: v.preview.url,
260
+ }));
261
+ const qaSurfacing = buildQaSurfacing(variants);
262
+ return jsonResponse({
263
+ stage,
264
+ summary,
265
+ ...(previewUrls.length > 0 ? { previewUrls } : {}),
266
+ ...qaSurfacing,
267
+ });
268
+ }
135
269
  }
136
270
  catch (err) {
137
271
  return errorResponse(err);
138
272
  }
139
273
  });
140
- mcp.tool('report_variant_complete', "Agent reports per-variant code-gen status: running (heartbeat), succeeded, failed, or cancelled. Requires a valid leaseId and matching attempt obtained from continue_variants(action='request_work').", contracts_1.reportVariantCompleteInput, async (args) => {
274
+ mcp.tool('report_variant_complete', "Agent reports per-work-item status: running (heartbeat), succeeded, failed, or cancelled. Requires a valid leaseId and matching attempt obtained from continue_variants(action='request_work'). For benchmark instrumentation, agents can optionally include tokensIn, tokensOut, and model in the call so Rivet can record cost per variant.", contracts_1.reportVariantCompleteInput, async (args) => {
141
275
  try {
142
276
  const result = await deps.orchestrator.reportComplete({
143
277
  sessionId: args.sessionId,
@@ -145,17 +279,31 @@ function registerAgentVariantsTools(mcp, deps) {
145
279
  leaseId: args.leaseId,
146
280
  attempt: args.attempt,
147
281
  status: args.status,
148
- output: args.output,
282
+ output: (0, exports.normalizeAgentOutput)(args.output),
149
283
  error: args.error,
284
+ tokensIn: args.tokensIn,
285
+ tokensOut: args.tokensOut,
286
+ model: args.model,
150
287
  });
151
288
  let out;
152
289
  if (result.stage === 'ready' ||
153
290
  result.stage === 'degraded' ||
154
291
  result.stage === 'failed' ||
155
292
  result.stage === 'cancelled') {
293
+ const summary = result.summary ?? deps.orchestrator.getSummary(args.sessionId);
294
+ const variants = deps.orchestrator.getVariants(args.sessionId);
295
+ const previewUrls = variants
296
+ .filter((v) => v.preview?.kind === 'static_artifact')
297
+ .map((v) => ({
298
+ label: v.label,
299
+ url: v.preview.url,
300
+ }));
301
+ const qaSurfacing = buildQaSurfacing(variants);
156
302
  out = {
157
303
  stage: result.stage,
158
- summary: result.summary ?? deps.orchestrator.getSummary(args.sessionId),
304
+ summary,
305
+ ...(previewUrls.length > 0 ? { previewUrls } : {}),
306
+ ...qaSurfacing,
159
307
  };
160
308
  }
161
309
  else {
@@ -186,6 +334,39 @@ function registerAgentVariantsTools(mcp, deps) {
186
334
  return errorResponse(err);
187
335
  }
188
336
  });
337
+ mcp.tool('start_variants', 'Single-call kickoff for variants exploration. Spawns N variant worktrees, opens the visual editor, and returns a lease + worktreePaths — no follow-up detect_project / open_visual_editor / continue_variants needed. Pass `briefs` (one per variant) so each card has a distinct name and description; omitting them stubs every variant with the user prompt verbatim. `briefs.length` sets the count. mode="existing" for existing projects, "zero_to_one" for prompt-only fresh projects. For source-grounded fresh projects (inspiration URLs / custom design context) use create_zero_to_one_project instead. Full protocol in the rivet skill.', contracts_1.startVariantsInput, async (args) => {
338
+ try {
339
+ if (args.mode === 'zero_to_one') {
340
+ const out = await handleStartVariantsZeroToOne(args, deps);
341
+ return jsonResponse(out);
342
+ }
343
+ const out = await handleStartVariantsExisting(args, deps);
344
+ return jsonResponse(out);
345
+ }
346
+ catch (err) {
347
+ return errorResponse(err);
348
+ }
349
+ });
350
+ mcp.tool('cancel_variant', 'Cancel a single variant within an in-flight session without affecting its siblings. Called by the Rivet UI when the user clicks the cancel button on a variant card. Marks the variant cancelled and releases its lease; sibling variants continue. Idempotent on terminal states (already cancelled / succeeded / failed returns alreadyTerminal=true with the actual finalStatus). Per-variant worktree teardown happens at session commit/cancel time. Distinct from cancel_variants (plural), which kills the entire session.', contracts_1.cancelVariantInput, async (args) => {
351
+ try {
352
+ const result = await deps.orchestrator.cancelVariant({
353
+ sessionId: args.sessionId,
354
+ variantId: args.variantId,
355
+ reason: args.reason,
356
+ });
357
+ const out = {
358
+ sessionId: result.sessionId,
359
+ variantId: result.variantId,
360
+ finalStatus: result.finalStatus,
361
+ sessionStage: result.sessionStage,
362
+ alreadyTerminal: result.alreadyTerminal,
363
+ };
364
+ return jsonResponse(out);
365
+ }
366
+ catch (err) {
367
+ return errorResponse(err);
368
+ }
369
+ });
189
370
  }
190
371
  // --- Helpers --------------------------------------------------------------
191
372
  // Return type is left implicit so the MCP SDK's overload selection picks the
@@ -218,4 +399,255 @@ function errorResponse(err) {
218
399
  isError: true,
219
400
  };
220
401
  }
402
+ function assertExplicitUserConfirmation(toolName, confirmation, actionLabel) {
403
+ if (confirmation.confirmedByUser) {
404
+ return;
405
+ }
406
+ if (confirmation.bypassRequestedByUser &&
407
+ confirmation.bypassReason?.trim().length) {
408
+ return;
409
+ }
410
+ throw new errors_1.AgentVariantsError('MISSING_REQUIRED_INPUT', `${toolName} requires explicit user confirmation to ${actionLabel}. Set confirmedByUser=true after user confirmation, or set bypassRequestedByUser=true with bypassReason when the user explicitly asks to bypass.`);
411
+ }
412
+ function buildApprovalPrompt(briefs) {
413
+ const formattedBriefs = briefs
414
+ .map((brief, index) => `${index + 1}. name: ${brief.label}\n description: ${brief.body}`)
415
+ .join('\n');
416
+ return (`Review these variant briefs:\n${formattedBriefs}\n\n` +
417
+ 'Do these look good, or do you want any adjustments before I proceed?');
418
+ }
419
+ /**
420
+ * Compose the QA surfacing block (`qaPrompt`, `qa[]`) attached to terminal
421
+ * tool responses. Returns an empty object when no variant has a recorded
422
+ * QA verdict so existing-project flows (which never trigger preview QA)
423
+ * remain a no-op.
424
+ *
425
+ * Exported indirectly via the orchestrator's terminal tool responses so
426
+ * the calling agent can show the user a concrete retry/continue/cancel
427
+ * choice when one or more variants fail QA.
428
+ */
429
+ function buildQaSurfacing(variants) {
430
+ const withQa = variants.filter((v) => Boolean(v.qa) && v.qa.status !== 'not_run');
431
+ if (withQa.length === 0)
432
+ return {};
433
+ const qaEntries = withQa.map((v) => ({
434
+ workItemId: v.workItemId,
435
+ label: v.label,
436
+ qa: v.qa,
437
+ }));
438
+ const failures = withQa.filter((v) => v.qa.status === 'failed');
439
+ if (failures.length === 0) {
440
+ return { qa: qaEntries };
441
+ }
442
+ return {
443
+ qa: qaEntries,
444
+ qaPrompt: buildQaFailurePrompt(failures.map((v) => ({ label: v.label, qa: v.qa })), withQa.length - failures.length),
445
+ };
446
+ }
447
+ /**
448
+ * User-facing prompt template for QA failures. Lists each failed variant
449
+ * with its summary + first asset/console issue, then asks the user
450
+ * whether to retry, continue with healthy variants, or cancel. Kept as
451
+ * data (not a log line) so the calling agent can present the choice
452
+ * directly to the human.
453
+ */
454
+ function buildQaFailurePrompt(failures, healthyCount) {
455
+ if (failures.length === 0) {
456
+ return '';
457
+ }
458
+ const lines = failures.map((failure) => {
459
+ const firstIssue = failure.qa.issues[0];
460
+ const issueDetail = firstIssue
461
+ ? ` (${describeIssue(firstIssue.kind)}: ${firstIssue.detail}${firstIssue.message ? ` — ${firstIssue.message}` : ''})`
462
+ : '';
463
+ return `• ${failure.label}: ${failure.qa.summary}${issueDetail}`;
464
+ });
465
+ const healthyLine = healthyCount > 0
466
+ ? `\n\n${healthyCount} other variant${healthyCount === 1 ? '' : 's'} passed QA and can still be committed.`
467
+ : '';
468
+ const intro = failures.length === 1
469
+ ? '1 variant failed QA:'
470
+ : `${failures.length} variants failed QA:`;
471
+ return `${intro}\n${lines.join('\n')}${healthyLine}\n\nWould you like to retry the failed variant${failures.length === 1 ? '' : 's'}, continue with healthy variants only, or cancel?`;
472
+ }
473
+ const describeIssue = (kind) => {
474
+ switch (kind) {
475
+ case 'asset_load_failed':
476
+ return 'asset load failed';
477
+ case 'console_error':
478
+ return 'console/runtime error';
479
+ case 'preview_unavailable':
480
+ return 'preview unavailable';
481
+ }
482
+ };
483
+ /**
484
+ * Handle the zero-to-one branch of `start_variants`. Mirrors the
485
+ * destination-derivation, framework validation, and visual-editor
486
+ * auto-open behavior of `create_zero_to_one_project`, but skips the
487
+ * source-research / inspiration-extraction path. Source-grounded fresh
488
+ * projects must continue to use `create_zero_to_one_project`.
489
+ */
490
+ async function handleStartVariantsZeroToOne(args, deps) {
491
+ const framework = args.framework ?? 'vite';
492
+ if (framework !== 'vite') {
493
+ return {
494
+ stage: 'failed',
495
+ errorCode: 'UNSUPPORTED_FRAMEWORK',
496
+ message: `start_variants(mode='zero_to_one') only supports framework='vite'; got '${framework}'.`,
497
+ };
498
+ }
499
+ const homedir = deps.homedir ?? os_1.default.homedir;
500
+ const destinationParent = args.destinationParent ?? homedir();
501
+ if (!path_1.default.isAbsolute(destinationParent)) {
502
+ return {
503
+ stage: 'failed',
504
+ errorCode: 'SCHEMA_VALIDATION_FAILED',
505
+ message: `destinationParent must be absolute; got '${destinationParent}'.`,
506
+ };
507
+ }
508
+ const slug = (0, createZeroToOneTool_1.slugifyPrompt)(args.prompt);
509
+ const destinationPath = path_1.default.join(destinationParent, slug);
510
+ if (!(0, viteReactTs_1.isDestinationEmpty)(destinationPath)) {
511
+ return {
512
+ stage: 'failed',
513
+ errorCode: 'DESTINATION_NOT_EMPTY',
514
+ message: `Destination '${destinationPath}' is not empty.`,
515
+ };
516
+ }
517
+ const hasSourceContext = args.sourceContext &&
518
+ ((args.sourceContext.sourceUrls?.length ?? 0) > 0 ||
519
+ (args.sourceContext.sourceArtifacts?.length ?? 0) > 0 ||
520
+ Boolean(args.sourceContext.sourceIntent) ||
521
+ Boolean(args.sourceContext.artifact));
522
+ if (hasSourceContext) {
523
+ return {
524
+ stage: 'failed',
525
+ errorCode: 'INVALID_STAGE_ACTION',
526
+ message: "start_variants(mode='zero_to_one') cannot run source-grounded sessions. " +
527
+ 'Use create_zero_to_one_project for inspiration URLs / source artifacts so the source_plan flow can run.',
528
+ };
529
+ }
530
+ fs_1.default.mkdirSync(destinationParent, { recursive: true });
531
+ const projectContext = {
532
+ kind: 'fresh',
533
+ workspacePath: destinationPath,
534
+ framework: 'vite',
535
+ };
536
+ const visualEditor = deps.ensureVisualEditorOpen
537
+ ? await deps.ensureVisualEditorOpen({
538
+ projectPath: destinationPath,
539
+ framework: 'vite',
540
+ })
541
+ : undefined;
542
+ const result = await deps.orchestrator.startUnified({
543
+ prompt: args.prompt,
544
+ count: args.count,
545
+ target: args.target,
546
+ projectContext,
547
+ leaseOwner: deps.leaseOwner(),
548
+ briefs: args.briefs,
549
+ });
550
+ return assembleStartVariantsResponse({
551
+ mode: 'zero_to_one',
552
+ result,
553
+ destinationPath,
554
+ visualEditor,
555
+ });
556
+ }
557
+ /**
558
+ * Handle the existing-project branch of `start_variants`. Detects the
559
+ * project's framework server-side (cwd or the provided projectPath) so
560
+ * the agent doesn't need a separate detect_project call, auto-opens the
561
+ * visual editor on the resolved projectPath, and leases all variant
562
+ * work items so they're ready to code in the same response.
563
+ */
564
+ async function handleStartVariantsExisting(args, deps) {
565
+ const projectPath = args.projectPath ?? process.cwd();
566
+ if (!path_1.default.isAbsolute(projectPath)) {
567
+ return {
568
+ stage: 'failed',
569
+ errorCode: 'SCHEMA_VALIDATION_FAILED',
570
+ message: `projectPath must be absolute; got '${projectPath}'.`,
571
+ };
572
+ }
573
+ let framework;
574
+ try {
575
+ framework = new ProjectDetectionService_1.ProjectDetectionService().detectFramework(projectPath);
576
+ }
577
+ catch (err) {
578
+ const msg = err instanceof Error ? err.message : 'Unknown detection error';
579
+ return {
580
+ stage: 'failed',
581
+ errorCode: 'SCHEMA_VALIDATION_FAILED',
582
+ message: `Could not detect project at '${projectPath}': ${msg}. Ensure it has a package.json, or pass projectPath explicitly.`,
583
+ };
584
+ }
585
+ // ensureVisualEditorOpen is typed for framework='vite' (matches the
586
+ // zero-to-one entry point), but it works against any projectPath. For
587
+ // existing projects with non-vite frameworks, the agent can still open
588
+ // the editor itself via the standalone open_visual_editor tool.
589
+ const visualEditor = deps.ensureVisualEditorOpen && framework === 'vite'
590
+ ? await deps.ensureVisualEditorOpen({
591
+ projectPath,
592
+ framework: 'vite',
593
+ })
594
+ : undefined;
595
+ const result = await deps.orchestrator.startUnified({
596
+ prompt: args.prompt,
597
+ count: args.count,
598
+ target: args.target,
599
+ leaseOwner: deps.leaseOwner(),
600
+ briefs: args.briefs,
601
+ });
602
+ return assembleStartVariantsResponse({
603
+ mode: 'existing',
604
+ result,
605
+ project: { projectPath, framework },
606
+ visualEditor,
607
+ });
608
+ }
609
+ /**
610
+ * Build the StartVariantsOutput from a startUnified result + the lease
611
+ * info it surfaced. Pairs each variant in the original `variants` list
612
+ * with its `leasedWorkItems[]` entry from requestWork so the agent sees
613
+ * one consistent per-variant object with everything it needs.
614
+ */
615
+ function assembleStartVariantsResponse(args) {
616
+ const { result } = args;
617
+ if (result.leaseId === undefined ||
618
+ result.leaseTtlMs === undefined ||
619
+ result.leasedWorkItems === undefined) {
620
+ return {
621
+ stage: 'failed',
622
+ errorCode: 'INVALID_STAGE_ACTION',
623
+ message: 'start_variants expected a lease to be issued by startUnified but none was returned.',
624
+ };
625
+ }
626
+ const itemById = new Map(result.leasedWorkItems.map((item) => [item.id, item]));
627
+ const variants = [];
628
+ for (const v of result.variants) {
629
+ const workItem = itemById.get(v.variantId);
630
+ if (!workItem) {
631
+ return {
632
+ stage: 'failed',
633
+ errorCode: 'INVALID_STAGE_ACTION',
634
+ message: `start_variants leased fewer items than variants — missing workItem for ${v.variantId}.`,
635
+ };
636
+ }
637
+ variants.push({ ...v, workItem });
638
+ }
639
+ return {
640
+ sessionId: result.sessionId,
641
+ stage: 'work_items_ready',
642
+ mode: args.mode,
643
+ leaseId: result.leaseId,
644
+ leaseTtlMs: result.leaseTtlMs,
645
+ variants,
646
+ scaffoldBaseWorkItemId: result.scaffoldBaseWorkItemId,
647
+ ...(args.destinationPath ? { destinationPath: args.destinationPath } : {}),
648
+ ...(args.visualEditor ? { visualEditor: args.visualEditor } : {}),
649
+ ...(args.project ? { project: args.project } : {}),
650
+ nextAction: 'report_variant_complete',
651
+ };
652
+ }
221
653
  //# sourceMappingURL=tools.js.map