rivet-design 0.11.10 → 0.11.12

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 (138) hide show
  1. package/dist/config/flags.d.ts +3 -0
  2. package/dist/config/flags.d.ts.map +1 -1
  3. package/dist/config/flags.js +3 -0
  4. package/dist/config/flags.js.map +1 -1
  5. package/dist/mcp/agent-variants/SessionStore.d.ts +150 -10
  6. package/dist/mcp/agent-variants/SessionStore.d.ts.map +1 -1
  7. package/dist/mcp/agent-variants/SessionStore.js +472 -28
  8. package/dist/mcp/agent-variants/SessionStore.js.map +1 -1
  9. package/dist/mcp/agent-variants/WorktreeOrchestrator.d.ts +143 -51
  10. package/dist/mcp/agent-variants/WorktreeOrchestrator.d.ts.map +1 -1
  11. package/dist/mcp/agent-variants/WorktreeOrchestrator.js +465 -299
  12. package/dist/mcp/agent-variants/WorktreeOrchestrator.js.map +1 -1
  13. package/dist/mcp/agent-variants/WorktreeOrchestrator.testHelpers.d.ts +5 -4
  14. package/dist/mcp/agent-variants/WorktreeOrchestrator.testHelpers.d.ts.map +1 -1
  15. package/dist/mcp/agent-variants/WorktreeOrchestrator.testHelpers.js +24 -5
  16. package/dist/mcp/agent-variants/WorktreeOrchestrator.testHelpers.js.map +1 -1
  17. package/dist/mcp/agent-variants/contracts.d.ts +103 -90
  18. package/dist/mcp/agent-variants/contracts.d.ts.map +1 -1
  19. package/dist/mcp/agent-variants/contracts.js +43 -16
  20. package/dist/mcp/agent-variants/contracts.js.map +1 -1
  21. package/dist/mcp/agent-variants/createZeroToOneTool.d.ts.map +1 -1
  22. package/dist/mcp/agent-variants/createZeroToOneTool.js +4 -6
  23. package/dist/mcp/agent-variants/createZeroToOneTool.js.map +1 -1
  24. package/dist/mcp/agent-variants/index.d.ts +3 -3
  25. package/dist/mcp/agent-variants/index.d.ts.map +1 -1
  26. package/dist/mcp/agent-variants/index.js +1 -2
  27. package/dist/mcp/agent-variants/index.js.map +1 -1
  28. package/dist/mcp/agent-variants/previewQa.d.ts.map +1 -1
  29. package/dist/mcp/agent-variants/previewQa.js +0 -6
  30. package/dist/mcp/agent-variants/previewQa.js.map +1 -1
  31. package/dist/mcp/agent-variants/tools.d.ts +12 -4
  32. package/dist/mcp/agent-variants/tools.d.ts.map +1 -1
  33. package/dist/mcp/agent-variants/tools.js +69 -13
  34. package/dist/mcp/agent-variants/tools.js.map +1 -1
  35. package/dist/mcp/changeBatchClassification.d.ts +30 -0
  36. package/dist/mcp/changeBatchClassification.d.ts.map +1 -1
  37. package/dist/mcp/changeBatchClassification.js +76 -0
  38. package/dist/mcp/changeBatchClassification.js.map +1 -1
  39. package/dist/mcp/instructions.d.ts.map +1 -1
  40. package/dist/mcp/instructions.js +7 -1
  41. package/dist/mcp/instructions.js.map +1 -1
  42. package/dist/mcp/server.d.ts +51 -3
  43. package/dist/mcp/server.d.ts.map +1 -1
  44. package/dist/mcp/server.js +404 -156
  45. package/dist/mcp/server.js.map +1 -1
  46. package/dist/mcp/watchLoopResumePrompt.d.ts +16 -0
  47. package/dist/mcp/watchLoopResumePrompt.d.ts.map +1 -0
  48. package/dist/mcp/watchLoopResumePrompt.js +21 -0
  49. package/dist/mcp/watchLoopResumePrompt.js.map +1 -0
  50. package/dist/proxy-middleware/proxy-config.d.ts +13 -0
  51. package/dist/proxy-middleware/proxy-config.d.ts.map +1 -1
  52. package/dist/proxy-middleware/proxy-config.js +61 -1
  53. package/dist/proxy-middleware/proxy-config.js.map +1 -1
  54. package/dist/routes/agentVariants.d.ts +10 -1
  55. package/dist/routes/agentVariants.d.ts.map +1 -1
  56. package/dist/routes/agentVariants.js +236 -5
  57. package/dist/routes/agentVariants.js.map +1 -1
  58. package/dist/routes/integrations.d.ts +8 -0
  59. package/dist/routes/integrations.d.ts.map +1 -0
  60. package/dist/routes/integrations.js +50 -0
  61. package/dist/routes/integrations.js.map +1 -0
  62. package/dist/routes/mcp.d.ts.map +1 -1
  63. package/dist/routes/mcp.js +106 -47
  64. package/dist/routes/mcp.js.map +1 -1
  65. package/dist/server.d.ts.map +1 -1
  66. package/dist/server.js +2 -0
  67. package/dist/server.js.map +1 -1
  68. package/dist/services/BridgeBatchPipeline.d.ts +99 -0
  69. package/dist/services/BridgeBatchPipeline.d.ts.map +1 -0
  70. package/dist/services/BridgeBatchPipeline.js +278 -0
  71. package/dist/services/BridgeBatchPipeline.js.map +1 -0
  72. package/dist/services/BridgeQueueStore.d.ts +28 -0
  73. package/dist/services/BridgeQueueStore.d.ts.map +1 -0
  74. package/dist/services/BridgeQueueStore.js +88 -0
  75. package/dist/services/BridgeQueueStore.js.map +1 -0
  76. package/dist/services/InlineVariantGenerationService.d.ts.map +1 -1
  77. package/dist/services/InlineVariantGenerationService.js +15 -3
  78. package/dist/services/InlineVariantGenerationService.js.map +1 -1
  79. package/dist/services/IntegrationsClient.d.ts +24 -0
  80. package/dist/services/IntegrationsClient.d.ts.map +1 -1
  81. package/dist/services/IntegrationsClient.js +71 -1
  82. package/dist/services/IntegrationsClient.js.map +1 -1
  83. package/dist/services/SessionBridgeService.d.ts +127 -31
  84. package/dist/services/SessionBridgeService.d.ts.map +1 -1
  85. package/dist/services/SessionBridgeService.js +428 -83
  86. package/dist/services/SessionBridgeService.js.map +1 -1
  87. package/dist/services/TelemetryService.d.ts +30 -0
  88. package/dist/services/TelemetryService.d.ts.map +1 -1
  89. package/dist/services/TelemetryService.js +42 -0
  90. package/dist/services/TelemetryService.js.map +1 -1
  91. package/dist/services/WorktreeManager.d.ts +29 -2
  92. package/dist/services/WorktreeManager.d.ts.map +1 -1
  93. package/dist/services/WorktreeManager.js +73 -7
  94. package/dist/services/WorktreeManager.js.map +1 -1
  95. package/dist/services/createAgentVariantsOrchestrator.d.ts +0 -8
  96. package/dist/services/createAgentVariantsOrchestrator.d.ts.map +1 -1
  97. package/dist/services/createAgentVariantsOrchestrator.js +1 -18
  98. package/dist/services/createAgentVariantsOrchestrator.js.map +1 -1
  99. package/dist/utils/portUtils.d.ts +9 -0
  100. package/dist/utils/portUtils.d.ts.map +1 -1
  101. package/dist/utils/portUtils.js +26 -0
  102. package/dist/utils/portUtils.js.map +1 -1
  103. package/dist/utils/skills/claude-skill.d.ts +1 -1
  104. package/dist/utils/skills/claude-skill.d.ts.map +1 -1
  105. package/dist/utils/skills/claude-skill.js +31 -12
  106. package/dist/utils/skills/claude-skill.js.map +1 -1
  107. package/dist/utils/skills/cursor-rules.d.ts +1 -1
  108. package/dist/utils/skills/cursor-rules.d.ts.map +1 -1
  109. package/dist/utils/skills/cursor-rules.js +26 -11
  110. package/dist/utils/skills/cursor-rules.js.map +1 -1
  111. package/dist/utils/skills/shared-variants-protocol.d.ts +28 -0
  112. package/dist/utils/skills/shared-variants-protocol.d.ts.map +1 -1
  113. package/dist/utils/skills/shared-variants-protocol.js +88 -14
  114. package/dist/utils/skills/shared-variants-protocol.js.map +1 -1
  115. package/package.json +2 -1
  116. package/src/ui/dist/assets/logo-animation/clasp.png +0 -0
  117. package/src/ui/dist/assets/logo-animation/eraser.png +0 -0
  118. package/src/ui/dist/assets/logo-animation/playdough.png +0 -0
  119. package/src/ui/dist/assets/logo-animation/sea/bubblephone.png +0 -0
  120. package/src/ui/dist/assets/logo-animation/sea/cart.png +0 -0
  121. package/src/ui/dist/assets/logo-animation/sea/chair.png +0 -0
  122. package/src/ui/dist/assets/logo-animation/sea/desk.png +0 -0
  123. package/src/ui/dist/assets/logo-animation/sea/mug.png +0 -0
  124. package/src/ui/dist/assets/logo-animation/sea/pitcher.png +0 -0
  125. package/src/ui/dist/assets/logo-animation/sea/pot.png +0 -0
  126. package/src/ui/dist/assets/logo-animation/sea/startac.png +0 -0
  127. package/src/ui/dist/assets/logo-animation/sea/terracotta-1.png +0 -0
  128. package/src/ui/dist/assets/logo-animation/sea/terracotta-2.png +0 -0
  129. package/src/ui/dist/assets/logo-animation/sea/vhsplant.png +0 -0
  130. package/src/ui/dist/assets/main-DREt3GGU.css +1 -0
  131. package/src/ui/dist/assets/main-DZ_9EXU9.js +655 -0
  132. package/src/ui/dist/index.html +2 -2
  133. package/dist/mcp/agent-variants/designCritique.d.ts +0 -167
  134. package/dist/mcp/agent-variants/designCritique.d.ts.map +0 -1
  135. package/dist/mcp/agent-variants/designCritique.js +0 -774
  136. package/dist/mcp/agent-variants/designCritique.js.map +0 -1
  137. package/src/ui/dist/assets/main-CYm60Rod.js +0 -641
  138. package/src/ui/dist/assets/main-IY99De7M.css +0 -1
@@ -7,7 +7,6 @@ const contracts_1 = require("./contracts");
7
7
  const runLabel_1 = require("./runLabel");
8
8
  const runPlan_1 = require("./runPlan");
9
9
  const DEFAULT_VARIANT_COUNT = 4;
10
- const MAX_VARIANT_COUNT = 8;
11
10
  /** TTL for a lease on a code_gen work item. Bumped to 20min after a timing
12
11
  * audit found first-variant single-round code-gen routinely runs 540–950s
13
12
  * (4+ Task sub-agents in parallel), so the previous 10min cap was the most
@@ -20,6 +19,17 @@ exports.DEFAULT_LEASE_TTL_MS = 20 * 60 * 1000;
20
19
  * code_gen bump; scaffold timing has never bound, but a longer lease is safe. */
21
20
  exports.SCAFFOLD_LEASE_TTL_MS = 30 * 60 * 1000;
22
21
  const SYSTEM_CLOCK = { now: () => Date.now() };
22
+ /** True when a work item is a past variant adopted into the active session
23
+ * (see {@link SessionStore.appendAdoptedStaticVariant}). Adopted variants are
24
+ * surfaced as session members but excluded from generation accounting. */
25
+ function isAdoptedVariant(item) {
26
+ return Boolean(item.input.adoptedFrom);
27
+ }
28
+ /** True when a static preview is a Vary sibling forked from another variant. */
29
+ function isForkedStaticPreviewVariant(item) {
30
+ return (item.kind === 'static_preview' &&
31
+ Boolean(item.input.sourceVariantId));
32
+ }
23
33
  function fingerprint(parts) {
24
34
  return parts.map((p) => (p === undefined ? '∅' : String(p))).join('|');
25
35
  }
@@ -719,7 +729,23 @@ class SessionStore {
719
729
  }
720
730
  leasable = [target];
721
731
  }
722
- const limit = args.requestedLeaseCount ?? leasable.length;
732
+ else if (args.workItemIds !== undefined) {
733
+ // Scoped lease: restrict to a specific batch's work items so concurrent
734
+ // owners (e.g. several watchers each handling their own Vary run) don't
735
+ // greedily lease each other's items. Leases whatever subset of the
736
+ // requested ids is still leasable; only errors if NONE are.
737
+ const wanted = new Set(args.workItemIds);
738
+ leasable = leasable.filter((item) => wanted.has(item.id));
739
+ if (leasable.length === 0) {
740
+ throw new errors_1.AgentVariantsError('INVALID_STAGE_ACTION', 'None of the requested work items are currently leasable (already leased, running, or done)');
741
+ }
742
+ }
743
+ // `workItemIds` takes precedence over `requestedLeaseCount`: a scoped batch
744
+ // leases ALL of its still-leasable items, not a count-capped subset (the
745
+ // scoping above already narrowed `leasable` to exactly that batch).
746
+ const limit = args.workItemIds !== undefined
747
+ ? leasable.length
748
+ : (args.requestedLeaseCount ?? leasable.length);
723
749
  const issued = leasable.slice(0, Math.max(0, limit));
724
750
  if (issued.length === 0) {
725
751
  throw new errors_1.AgentVariantsError('INVALID_STAGE_ACTION', 'No work items currently leasable (all running, blocked by deps, or done)');
@@ -812,6 +838,16 @@ class SessionStore {
812
838
  item.status = args.status;
813
839
  item.output = args.output;
814
840
  item.error = args.error;
841
+ // Apply an agent-synthesized title/description so the variant card shows a
842
+ // meaningful name (parent identity + the change) instead of the provisional
843
+ // instruction text. Only on success; capped so the label stays short.
844
+ if (args.status === 'succeeded' && (args.title || args.description)) {
845
+ const input = item.input;
846
+ if (args.title)
847
+ input.briefLabel = args.title.trim().slice(0, 80);
848
+ if (args.description)
849
+ input.briefBody = args.description.trim();
850
+ }
815
851
  item.acceptedReports.push(fp);
816
852
  item.lease = undefined;
817
853
  if (args.status === 'failed') {
@@ -825,21 +861,20 @@ class SessionStore {
825
861
  return result;
826
862
  }
827
863
  /**
828
- * Re-open a succeeded variant work item for one aesthetic regeneration.
864
+ * Re-open a succeeded variant work item for one QA-driven regeneration.
829
865
  * Consumes the agent's report (validating the lease), then resets the item
830
866
  * to `pending` so it becomes leasable again via `request_work`, stashing the
831
- * design critique onto its input so the re-leased descriptor carries the
832
- * notes the agent must address before re-reporting.
867
+ * QA notes onto its input so the re-leased descriptor carries the notes the
868
+ * agent must address before re-reporting.
833
869
  *
834
870
  * The work item never reaches a terminal status, so the session stays in
835
- * `waiting_for_results` and never goes terminal off the back of a
836
- * design-quality failure. Idempotent on replay via the accepted-fingerprint
837
- * cache.
871
+ * `waiting_for_results` and never goes terminal off the back of a QA
872
+ * failure. Idempotent on replay via the accepted-fingerprint cache.
838
873
  */
839
- requeueForDesignCritique(args) {
874
+ requeueForRegeneration(args) {
840
875
  const session = this.requireSession(args.sessionId);
841
876
  const fp = fingerprint([
842
- 'requeueForDesignCritique',
877
+ 'requeueForRegeneration',
843
878
  args.sessionId,
844
879
  args.workItemId,
845
880
  args.leaseId,
@@ -858,7 +893,7 @@ class SessionStore {
858
893
  item.output = undefined;
859
894
  item.error = undefined;
860
895
  const input = (item.input ?? {});
861
- item.input = { ...input, designCritique: args.critique };
896
+ item.input = { ...input, qaRetry: args.qaRetry };
862
897
  const result = {
863
898
  stage: session.stage,
864
899
  progress: this.progressOf(session),
@@ -903,6 +938,8 @@ class SessionStore {
903
938
  briefLabel: targetInput.briefLabel,
904
939
  briefBody: targetInput.briefBody,
905
940
  ...(args.currentHtml ? { currentHtml: args.currentHtml } : {}),
941
+ ...(args.targetElement ? { targetElement: args.targetElement } : {}),
942
+ ...(args.requestId ? { requestId: args.requestId } : {}),
906
943
  projectContext: targetInput.projectContext ?? session.projectContext,
907
944
  ...(targetInput.context ? { context: targetInput.context } : {}),
908
945
  },
@@ -913,6 +950,373 @@ class SessionStore {
913
950
  session.stage = 'work_items_ready';
914
951
  return item;
915
952
  }
953
+ /**
954
+ * Fork a completed static_preview variant into N NEW sibling variants. Unlike
955
+ * {@link createRefineVariantWorkItem} (which overwrites the source variant in
956
+ * place), each forked item is a fresh `static_preview` work item with its own
957
+ * id, seeded from the source variant's HTML (`currentHtml`) plus the user's
958
+ * instruction. This backs the "Vary" action, where `variantRequest.count`
959
+ * asks for N new explorations rather than an in-place edit. Returns the
960
+ * created items so the caller can lease them via the normal request-work path.
961
+ */
962
+ createForkedStaticPreviewWorkItems(args) {
963
+ const session = this.requireSession(args.sessionId);
964
+ const source = this.requireWorkItem(session, args.sourceVariantId);
965
+ if (source.kind !== 'static_preview' || source.status !== 'succeeded') {
966
+ throw new errors_1.AgentVariantsError('VARIANT_NOT_EDITABLE', 'Only completed static_preview variants can be varied');
967
+ }
968
+ if (session.variantPick) {
969
+ throw new errors_1.AgentVariantsError('PENDING_CHANGE_CONFLICT', 'Cannot vary a variant after one has already been sent');
970
+ }
971
+ const activeRefinement = [...session.workItems.values()].find((item) => {
972
+ if (item.kind !== 'refine_variant')
973
+ return false;
974
+ const input = item.input;
975
+ return (input.variantId === args.sourceVariantId &&
976
+ (item.status === 'pending' || item.status === 'running'));
977
+ });
978
+ if (activeRefinement) {
979
+ throw new errors_1.AgentVariantsError('PENDING_CHANGE_CONFLICT', 'Cannot vary a variant while an in-place refinement is pending or running');
980
+ }
981
+ const sourceInput = source.input;
982
+ const seedHtml = args.currentHtml ??
983
+ source.output?.html;
984
+ const total = Math.min(Math.max(Math.trunc(args.count) || 1, 1), contracts_1.VARIANT_COUNT_MAX);
985
+ // Label/describe forked variants by the CHANGE requested (the Vary
986
+ // instruction), not the source brief — otherwise every run on the same
987
+ // variant reads identically ("<source> · variation N") and concurrent runs
988
+ // are indistinguishable in the Directions list.
989
+ const instruction = args.instruction.trim();
990
+ const prettyInstruction = instruction
991
+ ? instruction.charAt(0).toUpperCase() + instruction.slice(1)
992
+ : (sourceInput.briefLabel ?? 'Variant');
993
+ const LABEL_MAX = 48;
994
+ const created = [];
995
+ for (let i = 0; i < total; i++) {
996
+ const suffix = total > 1 ? ` · ${i + 1}` : '';
997
+ const labelBase = prettyInstruction.length > LABEL_MAX - suffix.length
998
+ ? `${prettyInstruction.slice(0, LABEL_MAX - suffix.length - 1)}…`
999
+ : prettyInstruction;
1000
+ const item = {
1001
+ id: (0, crypto_1.randomUUID)(),
1002
+ kind: 'static_preview',
1003
+ status: 'pending',
1004
+ attempt: 0,
1005
+ dependsOn: [],
1006
+ input: {
1007
+ briefId: `${sourceInput.briefId ?? 'v'}-vary-${(0, crypto_1.randomUUID)().slice(0, 8)}`,
1008
+ // Provisional name/description (the change) shown while generating;
1009
+ // the agent reports a synthesized title/description on completion.
1010
+ briefLabel: `${labelBase}${suffix}`,
1011
+ briefBody: prettyInstruction,
1012
+ instruction: args.instruction,
1013
+ sourceVariantId: args.sourceVariantId,
1014
+ // Parent identity, so the agent can synthesize a name/description that
1015
+ // combines the source variant with the requested change.
1016
+ ...(sourceInput.briefLabel
1017
+ ? { sourceLabel: sourceInput.briefLabel }
1018
+ : {}),
1019
+ ...(sourceInput.briefBody
1020
+ ? { sourceBody: sourceInput.briefBody }
1021
+ : {}),
1022
+ ...(seedHtml ? { currentHtml: seedHtml } : {}),
1023
+ ...(args.targetElement ? { targetElement: args.targetElement } : {}),
1024
+ ...(args.requestId ? { requestId: args.requestId } : {}),
1025
+ projectContext: sourceInput.projectContext ?? session.projectContext,
1026
+ ...(sourceInput.context ? { context: sourceInput.context } : {}),
1027
+ },
1028
+ acceptedReports: [],
1029
+ };
1030
+ session.workItems.set(item.id, item);
1031
+ // Surface newest siblings at the top of the Directions list: unshift each
1032
+ // in creation order so the batch ends up [variation N … variation 1]
1033
+ // ahead of the pre-existing variants.
1034
+ session.workItemOrder.unshift(item.id);
1035
+ created.push(item);
1036
+ }
1037
+ session.stage = 'work_items_ready';
1038
+ return created;
1039
+ }
1040
+ /**
1041
+ * Append new code_gen variants to an EXISTING-project session that already
1042
+ * holds variants — the worktree-backed analog of
1043
+ * {@link createForkedStaticPreviewWorkItems}. A second "Vary" on an existing
1044
+ * project lands here so its variants ACCUMULATE in the live session instead
1045
+ * of orphaning the prior ones in a fresh, superseding session. The new items
1046
+ * are leasable via {@link requestWork} exactly like the initial code_gen
1047
+ * batch; the orchestrator provisions a worktree per item and runs each
1048
+ * through the normal completion/preview pipeline.
1049
+ *
1050
+ * Reopens the session to `work_items_ready` so the appended pending work is
1051
+ * visible and drives the stage back through generation to ready/degraded.
1052
+ */
1053
+ appendExistingCodeGenVariants(args) {
1054
+ const session = this.requireSession(args.sessionId);
1055
+ if (session.projectContext.kind !== 'existing') {
1056
+ throw new errors_1.AgentVariantsError('INVALID_STAGE_ACTION', 'Only existing-project sessions can append code_gen variants');
1057
+ }
1058
+ if (session.variantPick) {
1059
+ throw new errors_1.AgentVariantsError('PENDING_CHANGE_CONFLICT', 'Cannot append variants after one has already been committed');
1060
+ }
1061
+ if (session.stage === 'cancelled' || session.stage === 'failed') {
1062
+ throw new errors_1.AgentVariantsError('INVALID_STAGE_ACTION', `Cannot append variants to a ${session.stage} session`);
1063
+ }
1064
+ if (args.briefs.length === 0) {
1065
+ throw new errors_1.AgentVariantsError('MISSING_REQUIRED_INPUT', 'At least one brief is required to append variants');
1066
+ }
1067
+ const createdWorkItemIds = [];
1068
+ for (const brief of args.briefs) {
1069
+ const context = resolveVariantWorkItemContext({
1070
+ bundle: args.variantContext ?? session.projectContext.variantContext,
1071
+ projectContext: session.projectContext,
1072
+ brief,
1073
+ slotIndex: session.briefs.length,
1074
+ });
1075
+ const visualRef = brief.visualReferenceUrl
1076
+ ? { visualReferenceUrl: brief.visualReferenceUrl }
1077
+ : {};
1078
+ const item = {
1079
+ id: (0, crypto_1.randomUUID)(),
1080
+ kind: 'code_gen',
1081
+ status: 'pending',
1082
+ attempt: 0,
1083
+ dependsOn: [],
1084
+ input: {
1085
+ briefId: brief.briefId,
1086
+ briefLabel: brief.label,
1087
+ briefBody: brief.body,
1088
+ ...visualRef,
1089
+ ...(args.runLabel ? { runLabel: args.runLabel } : {}),
1090
+ target: session.target,
1091
+ projectContext: session.projectContext,
1092
+ ...(context ? { context } : {}),
1093
+ },
1094
+ acceptedReports: [],
1095
+ };
1096
+ session.workItems.set(item.id, item);
1097
+ // Surface the newest Vary batch at the top of the Directions list, matching
1098
+ // the static-preview fork ordering.
1099
+ session.workItemOrder.unshift(item.id);
1100
+ session.briefs.push(brief);
1101
+ createdWorkItemIds.push(item.id);
1102
+ }
1103
+ session.stage = 'work_items_ready';
1104
+ return { createdWorkItemIds, briefs: args.briefs };
1105
+ }
1106
+ /**
1107
+ * Fork a succeeded code_gen variant into N NEW sibling variants. Each forked
1108
+ * item is seeded from the source variant's worktree (not git HEAD / Original)
1109
+ * and carries `sourceVariantId` so the orchestrator can copy the parent's
1110
+ * tree when provisioning. Backs code_gen "Vary" when the UI targets a parent
1111
+ * variant with `variantRequest.count > 0`.
1112
+ */
1113
+ createForkedCodeGenWorkItems(args) {
1114
+ const session = this.requireSession(args.sessionId);
1115
+ const source = this.requireWorkItem(session, args.sourceVariantId);
1116
+ if (source.kind !== 'code_gen' || source.status !== 'succeeded') {
1117
+ throw new errors_1.AgentVariantsError('VARIANT_NOT_EDITABLE', 'Only completed code_gen variants can be varied');
1118
+ }
1119
+ if (session.projectContext.kind !== 'existing') {
1120
+ throw new errors_1.AgentVariantsError('INVALID_STAGE_ACTION', 'Only existing-project sessions can fork code_gen variants');
1121
+ }
1122
+ if (session.variantPick) {
1123
+ throw new errors_1.AgentVariantsError('PENDING_CHANGE_CONFLICT', 'Cannot vary a variant after one has already been committed');
1124
+ }
1125
+ if (session.stage === 'cancelled' || session.stage === 'failed') {
1126
+ throw new errors_1.AgentVariantsError('INVALID_STAGE_ACTION', `Cannot fork variants in a ${session.stage} session`);
1127
+ }
1128
+ if (args.briefs.length === 0) {
1129
+ throw new errors_1.AgentVariantsError('MISSING_REQUIRED_INPUT', 'At least one brief is required to fork variants');
1130
+ }
1131
+ const activeRefinement = [...session.workItems.values()].find((item) => {
1132
+ if (item.kind !== 'refine_variant')
1133
+ return false;
1134
+ const input = item.input;
1135
+ return (input.variantId === args.sourceVariantId &&
1136
+ (item.status === 'pending' || item.status === 'running'));
1137
+ });
1138
+ if (activeRefinement) {
1139
+ throw new errors_1.AgentVariantsError('PENDING_CHANGE_CONFLICT', 'Cannot vary a variant while an in-place refinement is pending or running');
1140
+ }
1141
+ const sourceInput = source.input;
1142
+ const createdWorkItemIds = [];
1143
+ for (const brief of args.briefs) {
1144
+ const context = resolveVariantWorkItemContext({
1145
+ bundle: args.variantContext ?? session.projectContext.variantContext,
1146
+ projectContext: session.projectContext,
1147
+ brief,
1148
+ slotIndex: session.briefs.length,
1149
+ });
1150
+ const visualRef = brief.visualReferenceUrl
1151
+ ? { visualReferenceUrl: brief.visualReferenceUrl }
1152
+ : {};
1153
+ const item = {
1154
+ id: (0, crypto_1.randomUUID)(),
1155
+ kind: 'code_gen',
1156
+ status: 'pending',
1157
+ attempt: 0,
1158
+ dependsOn: [],
1159
+ input: {
1160
+ briefId: brief.briefId,
1161
+ briefLabel: brief.label,
1162
+ briefBody: brief.body,
1163
+ ...visualRef,
1164
+ ...(args.runLabel ? { runLabel: args.runLabel } : {}),
1165
+ sourceVariantId: args.sourceVariantId,
1166
+ ...(sourceInput.briefLabel
1167
+ ? { sourceLabel: sourceInput.briefLabel }
1168
+ : {}),
1169
+ ...(sourceInput.briefBody ? { sourceBody: sourceInput.briefBody } : {}),
1170
+ target: session.target,
1171
+ projectContext: session.projectContext,
1172
+ ...(context ? { context } : {}),
1173
+ },
1174
+ acceptedReports: [],
1175
+ };
1176
+ session.workItems.set(item.id, item);
1177
+ session.workItemOrder.unshift(item.id);
1178
+ session.briefs.push(brief);
1179
+ createdWorkItemIds.push(item.id);
1180
+ }
1181
+ session.stage = 'work_items_ready';
1182
+ return { createdWorkItemIds, briefs: args.briefs };
1183
+ }
1184
+ /**
1185
+ * Adopt a PAST variant (from a prior session's persisted history) into THIS
1186
+ * active session as a first-class, already-`succeeded` `static_preview`
1187
+ * variant. Triggered when the user makes their first edit (a comment or a
1188
+ * Vary) on a selected past direction: the past variant is rehydrated as a
1189
+ * live session member so the comment/refine machinery scopes and routes to it
1190
+ * exactly like a freshly-generated variant. The caller (orchestrator) registers
1191
+ * the static-preview record that points at the rehydrated files so the snapshot
1192
+ * surfaces it as a `static_artifact`.
1193
+ *
1194
+ * Kept as `static_preview` regardless of the host session's primary variant
1195
+ * kind so {@link createRefineVariantWorkItem} / {@link createForkedStaticPreviewWorkItems}
1196
+ * accept it. Adopted variants are pre-completed artifacts, so they are excluded
1197
+ * from progress/summary/terminal accounting and never reopen the session.
1198
+ *
1199
+ * Idempotent: re-adopting the same `{sessionId, variantId}` returns the
1200
+ * existing work item id (`created: false`) rather than duplicating it.
1201
+ */
1202
+ appendAdoptedStaticVariant(args) {
1203
+ const session = this.requireSession(args.sessionId);
1204
+ if (session.stage === 'cancelled' || session.stage === 'failed') {
1205
+ throw new errors_1.AgentVariantsError('INVALID_STAGE_ACTION', `Cannot adopt a variant into a ${session.stage} session`);
1206
+ }
1207
+ const existing = this.findAdoptedVariant(session, args.source);
1208
+ if (existing) {
1209
+ const input = existing.input;
1210
+ return {
1211
+ workItemId: existing.id,
1212
+ briefId: input.briefId ?? '',
1213
+ created: false,
1214
+ };
1215
+ }
1216
+ if (session.variantPick) {
1217
+ throw new errors_1.AgentVariantsError('PENDING_CHANGE_CONFLICT', 'Cannot adopt a variant after one has already been committed');
1218
+ }
1219
+ const briefId = `adopted-${(0, crypto_1.randomUUID)().slice(0, 8)}`;
1220
+ const item = {
1221
+ id: (0, crypto_1.randomUUID)(),
1222
+ kind: 'static_preview',
1223
+ status: 'succeeded',
1224
+ attempt: 0,
1225
+ dependsOn: [],
1226
+ input: {
1227
+ briefId,
1228
+ briefLabel: args.label,
1229
+ ...(args.description ? { briefBody: args.description } : {}),
1230
+ ...(args.runLabel ? { runLabel: args.runLabel } : {}),
1231
+ adoptedFrom: { ...args.source },
1232
+ projectContext: session.projectContext,
1233
+ },
1234
+ acceptedReports: [],
1235
+ };
1236
+ session.workItems.set(item.id, item);
1237
+ // Adoption is triggered by the user's first in-place edit (a comment /
1238
+ // refine) on an ALREADY-DISPLAYED past direction — not a request for a new
1239
+ // exploration. Appending (rather than unshifting to the top) preserves the
1240
+ // positions of the existing active variants so a refined card does NOT
1241
+ // leapfrog a newer Vary/fork batch. Genuinely new variants (Vary / fork /
1242
+ // append) still surface at the top via their own `unshift` paths. Stage is
1243
+ // intentionally left untouched — the adopted variant is already complete,
1244
+ // so the session must not reopen to `work_items_ready`.
1245
+ session.workItemOrder.push(item.id);
1246
+ return { workItemId: item.id, briefId, created: true };
1247
+ }
1248
+ /**
1249
+ * Create an active session whose initial visible direction is a static
1250
+ * variant rehydrated from persisted history.
1251
+ *
1252
+ * This is the no-usable-session counterpart to
1253
+ * {@link appendAdoptedStaticVariant}: the session starts terminal/ready
1254
+ * because the adopted artifact is already generated, and the adopted work item
1255
+ * remains excluded from generation progress because this run did not generate
1256
+ * fresh variants.
1257
+ */
1258
+ createSessionFromStaticHistoryVariant(args) {
1259
+ const sessionId = (0, crypto_1.randomUUID)();
1260
+ const record = {
1261
+ sessionId,
1262
+ stage: 'ready',
1263
+ prompt: args.prompt,
1264
+ runLabel: args.runLabel?.trim() || undefined,
1265
+ count: 0,
1266
+ projectContext: args.projectContext,
1267
+ briefs: [],
1268
+ approvedBriefs: [],
1269
+ workItems: new Map(),
1270
+ workItemOrder: [],
1271
+ acceptedFingerprints: new Map(),
1272
+ createdAt: this.clock.now(),
1273
+ };
1274
+ this.sessions.set(sessionId, record);
1275
+ const adopted = this.appendAdoptedStaticVariant({
1276
+ sessionId,
1277
+ source: args.source,
1278
+ label: args.label,
1279
+ description: args.description,
1280
+ runLabel: args.runLabel,
1281
+ });
1282
+ return {
1283
+ sessionId,
1284
+ workItemId: adopted.workItemId,
1285
+ briefId: adopted.briefId,
1286
+ };
1287
+ }
1288
+ /** Find an already-adopted variant by its source identity, or undefined. */
1289
+ findAdoptedVariant(session, source) {
1290
+ for (const item of session.workItems.values()) {
1291
+ if (item.removed)
1292
+ continue;
1293
+ const adoptedFrom = item.input
1294
+ .adoptedFrom;
1295
+ if (adoptedFrom &&
1296
+ adoptedFrom.sessionId === source.sessionId &&
1297
+ adoptedFrom.variantId === source.variantId) {
1298
+ return item;
1299
+ }
1300
+ }
1301
+ return undefined;
1302
+ }
1303
+ /**
1304
+ * Stamp a UI correlation id onto already-created variant work items so the
1305
+ * snapshot surfaces it and the panel can map its optimistic placeholders to
1306
+ * the real cards (see {@link ActiveVariantSnapshot.requestId}). Skips ids
1307
+ * that are unknown to the session. Used by `start_variants` for both the
1308
+ * initial batch and the appended (accumulated) batch.
1309
+ */
1310
+ assignVariantRequestId(sessionId, workItemIds, requestId) {
1311
+ const session = this.requireSession(sessionId);
1312
+ for (const id of workItemIds) {
1313
+ const item = session.workItems.get(id);
1314
+ if (!item)
1315
+ continue;
1316
+ const input = (item.input ?? {});
1317
+ item.input = { ...input, requestId };
1318
+ }
1319
+ }
916
1320
  /**
917
1321
  * Fold a newer instruction into a variant's still-pending refinement. Used
918
1322
  * when a second refinement is requested for a variant that already has a
@@ -1349,8 +1753,13 @@ class SessionStore {
1349
1753
  const kind = this.variantWorkItemKind(session);
1350
1754
  // Removed variants are excluded everywhere progress/summary/terminal
1351
1755
  // accounting reads from, so a removed variant neither blocks the session
1352
- // from going terminal nor inflates the ready/total counts.
1353
- return [...session.workItems.values()].filter((i) => i.kind === kind && !i.removed);
1756
+ // from going terminal nor inflates the ready/total counts. Adopted past
1757
+ // variants are likewise excluded: they are pre-completed artifacts pulled in
1758
+ // for editing, not part of this run's generation, so they must not inflate
1759
+ // progress/total or flip a failed run to degraded.
1760
+ return [...session.workItems.values()].filter((i) => (i.kind === kind || isForkedStaticPreviewVariant(i)) &&
1761
+ !i.removed &&
1762
+ !isAdoptedVariant(i));
1354
1763
  }
1355
1764
  /**
1356
1765
  * The work-item kind that represents a user-facing variant for this
@@ -1434,6 +1843,13 @@ class SessionStore {
1434
1843
  }),
1435
1844
  };
1436
1845
  }
1846
+ refinementSnapshotPriority(item) {
1847
+ if (item.status === 'running')
1848
+ return 2;
1849
+ if (item.status === 'pending')
1850
+ return 1;
1851
+ return 0;
1852
+ }
1437
1853
  variantsOf(session) {
1438
1854
  const kind = this.variantWorkItemKind(session);
1439
1855
  const refinementsByVariantId = new Map();
@@ -1443,30 +1859,58 @@ class SessionStore {
1443
1859
  const input = item.input;
1444
1860
  if (!input.variantId)
1445
1861
  continue;
1446
- refinementsByVariantId.set(input.variantId, item);
1862
+ const existing = refinementsByVariantId.get(input.variantId);
1863
+ if (!existing ||
1864
+ this.refinementSnapshotPriority(item) >
1865
+ this.refinementSnapshotPriority(existing)) {
1866
+ refinementsByVariantId.set(input.variantId, item);
1867
+ }
1447
1868
  }
1448
- return [...session.workItems.values()]
1449
- .filter((i) => i.kind === kind && !i.removed)
1869
+ return session.workItemOrder
1870
+ .map((id) => session.workItems.get(id))
1871
+ .filter((i) => !!i &&
1872
+ !i.removed &&
1873
+ (i.kind === kind ||
1874
+ isAdoptedVariant(i) ||
1875
+ isForkedStaticPreviewVariant(i)))
1450
1876
  .map((item) => {
1451
1877
  const input = item.input;
1878
+ const refineItem = refinementsByVariantId.get(item.id);
1879
+ const refineRequestId = refineItem?.input
1880
+ ?.requestId;
1452
1881
  return {
1453
1882
  workItemId: item.id,
1454
1883
  briefId: input.briefId ?? '',
1455
1884
  label: input.briefLabel ?? 'Variant',
1456
1885
  status: item.status,
1457
1886
  createdAt: new Date(session.createdAt).toISOString(),
1458
- runLabel: session.runLabel ?? (0, runLabel_1.deriveRunLabel)(session.prompt),
1887
+ // Prefer the per-item label stamped by an appended Vary batch; the
1888
+ // session-level fallback is correct only for the first batch (its
1889
+ // prompt is the session prompt). Without this, accumulated batches
1890
+ // all show batch 1's tag.
1891
+ runLabel: input.runLabel ??
1892
+ session.runLabel ??
1893
+ (0, runLabel_1.deriveRunLabel)(session.prompt),
1894
+ // Correlation id of the UI request that produced this variant (a fork)
1895
+ // or, for an in-place refine, the request driving its refinement —
1896
+ // lets the UI map its optimistic placeholders to real cards.
1897
+ ...(input.requestId
1898
+ ? { requestId: input.requestId }
1899
+ : refineRequestId
1900
+ ? { requestId: refineRequestId }
1901
+ : {}),
1459
1902
  ...(input.briefBody ? { description: input.briefBody } : {}),
1903
+ ...(input.adoptedFrom
1904
+ ? { adoptedSource: { ...input.adoptedFrom } }
1905
+ : {}),
1460
1906
  design: variantDesignFromInput(input.designContextEntry),
1461
- ...(refinementsByVariantId.get(item.id)
1907
+ ...(refineItem
1462
1908
  ? {
1463
1909
  refinement: {
1464
- workItemId: refinementsByVariantId.get(item.id).id,
1465
- status: refinementsByVariantId.get(item.id).status,
1466
- ...(refinementsByVariantId.get(item.id).error?.code
1467
- ? {
1468
- errorCode: String(refinementsByVariantId.get(item.id).error?.code),
1469
- }
1910
+ workItemId: refineItem.id,
1911
+ status: refineItem.status,
1912
+ ...(refineItem.error?.code
1913
+ ? { errorCode: String(refineItem.error.code) }
1470
1914
  : {}),
1471
1915
  },
1472
1916
  }
@@ -1480,10 +1924,10 @@ exports.SessionStore = SessionStore;
1480
1924
  function clampCount(count) {
1481
1925
  if (count === undefined)
1482
1926
  return DEFAULT_VARIANT_COUNT;
1483
- if (count < 2)
1484
- return 2;
1485
- if (count > MAX_VARIANT_COUNT)
1486
- return MAX_VARIANT_COUNT;
1927
+ if (count < contracts_1.VARIANT_COUNT_MIN)
1928
+ return contracts_1.VARIANT_COUNT_MIN;
1929
+ if (count > contracts_1.VARIANT_COUNT_MAX)
1930
+ return contracts_1.VARIANT_COUNT_MAX;
1487
1931
  return count;
1488
1932
  }
1489
1933
  //# sourceMappingURL=SessionStore.js.map