rivet-design 0.11.11 → 0.11.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/install/harnesses.d.ts.map +1 -1
- package/dist/install/harnesses.js +122 -4
- package/dist/install/harnesses.js.map +1 -1
- package/dist/mcp/agent-variants/SessionStore.d.ts +150 -10
- package/dist/mcp/agent-variants/SessionStore.d.ts.map +1 -1
- package/dist/mcp/agent-variants/SessionStore.js +468 -23
- package/dist/mcp/agent-variants/SessionStore.js.map +1 -1
- package/dist/mcp/agent-variants/WorktreeOrchestrator.d.ts +143 -51
- package/dist/mcp/agent-variants/WorktreeOrchestrator.d.ts.map +1 -1
- package/dist/mcp/agent-variants/WorktreeOrchestrator.js +465 -299
- package/dist/mcp/agent-variants/WorktreeOrchestrator.js.map +1 -1
- package/dist/mcp/agent-variants/WorktreeOrchestrator.testHelpers.d.ts +5 -4
- package/dist/mcp/agent-variants/WorktreeOrchestrator.testHelpers.d.ts.map +1 -1
- package/dist/mcp/agent-variants/WorktreeOrchestrator.testHelpers.js +24 -5
- package/dist/mcp/agent-variants/WorktreeOrchestrator.testHelpers.js.map +1 -1
- package/dist/mcp/agent-variants/contracts.d.ts +102 -89
- package/dist/mcp/agent-variants/contracts.d.ts.map +1 -1
- package/dist/mcp/agent-variants/contracts.js +29 -14
- package/dist/mcp/agent-variants/contracts.js.map +1 -1
- package/dist/mcp/agent-variants/index.d.ts +3 -3
- package/dist/mcp/agent-variants/index.d.ts.map +1 -1
- package/dist/mcp/agent-variants/index.js +1 -2
- package/dist/mcp/agent-variants/index.js.map +1 -1
- package/dist/mcp/agent-variants/previewQa.d.ts.map +1 -1
- package/dist/mcp/agent-variants/previewQa.js +0 -6
- package/dist/mcp/agent-variants/previewQa.js.map +1 -1
- package/dist/mcp/agent-variants/tools.d.ts +12 -4
- package/dist/mcp/agent-variants/tools.d.ts.map +1 -1
- package/dist/mcp/agent-variants/tools.js +69 -13
- package/dist/mcp/agent-variants/tools.js.map +1 -1
- package/dist/mcp/changeBatchClassification.d.ts +30 -0
- package/dist/mcp/changeBatchClassification.d.ts.map +1 -1
- package/dist/mcp/changeBatchClassification.js +76 -0
- package/dist/mcp/changeBatchClassification.js.map +1 -1
- package/dist/mcp/instructions.d.ts.map +1 -1
- package/dist/mcp/instructions.js +7 -1
- package/dist/mcp/instructions.js.map +1 -1
- package/dist/mcp/server.d.ts +51 -3
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +404 -156
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/watchLoopResumePrompt.d.ts +16 -0
- package/dist/mcp/watchLoopResumePrompt.d.ts.map +1 -0
- package/dist/mcp/watchLoopResumePrompt.js +21 -0
- package/dist/mcp/watchLoopResumePrompt.js.map +1 -0
- package/dist/routes/agentVariants.d.ts +10 -1
- package/dist/routes/agentVariants.d.ts.map +1 -1
- package/dist/routes/agentVariants.js +214 -3
- package/dist/routes/agentVariants.js.map +1 -1
- package/dist/routes/mcp.d.ts.map +1 -1
- package/dist/routes/mcp.js +106 -47
- package/dist/routes/mcp.js.map +1 -1
- package/dist/services/BridgeBatchPipeline.d.ts +99 -0
- package/dist/services/BridgeBatchPipeline.d.ts.map +1 -0
- package/dist/services/BridgeBatchPipeline.js +278 -0
- package/dist/services/BridgeBatchPipeline.js.map +1 -0
- package/dist/services/BridgeQueueStore.d.ts +28 -0
- package/dist/services/BridgeQueueStore.d.ts.map +1 -0
- package/dist/services/BridgeQueueStore.js +88 -0
- package/dist/services/BridgeQueueStore.js.map +1 -0
- package/dist/services/InlineVariantGenerationService.d.ts.map +1 -1
- package/dist/services/InlineVariantGenerationService.js +15 -3
- package/dist/services/InlineVariantGenerationService.js.map +1 -1
- package/dist/services/SessionBridgeService.d.ts +127 -31
- package/dist/services/SessionBridgeService.d.ts.map +1 -1
- package/dist/services/SessionBridgeService.js +428 -83
- package/dist/services/SessionBridgeService.js.map +1 -1
- package/dist/services/TelemetryService.d.ts +30 -0
- package/dist/services/TelemetryService.d.ts.map +1 -1
- package/dist/services/TelemetryService.js +42 -0
- package/dist/services/TelemetryService.js.map +1 -1
- package/dist/services/WorktreeManager.d.ts +29 -2
- package/dist/services/WorktreeManager.d.ts.map +1 -1
- package/dist/services/WorktreeManager.js +73 -7
- package/dist/services/WorktreeManager.js.map +1 -1
- package/dist/services/createAgentVariantsOrchestrator.d.ts +0 -8
- package/dist/services/createAgentVariantsOrchestrator.d.ts.map +1 -1
- package/dist/services/createAgentVariantsOrchestrator.js +1 -18
- package/dist/services/createAgentVariantsOrchestrator.js.map +1 -1
- package/dist/utils/portUtils.d.ts +9 -0
- package/dist/utils/portUtils.d.ts.map +1 -1
- package/dist/utils/portUtils.js +26 -0
- package/dist/utils/portUtils.js.map +1 -1
- package/dist/utils/skills/claude-skill.d.ts +1 -1
- package/dist/utils/skills/claude-skill.d.ts.map +1 -1
- package/dist/utils/skills/claude-skill.js +31 -12
- package/dist/utils/skills/claude-skill.js.map +1 -1
- package/dist/utils/skills/cursor-rules.d.ts +1 -1
- package/dist/utils/skills/cursor-rules.d.ts.map +1 -1
- package/dist/utils/skills/cursor-rules.js +26 -11
- package/dist/utils/skills/cursor-rules.js.map +1 -1
- package/dist/utils/skills/shared-variants-protocol.d.ts +28 -0
- package/dist/utils/skills/shared-variants-protocol.d.ts.map +1 -1
- package/dist/utils/skills/shared-variants-protocol.js +88 -14
- package/dist/utils/skills/shared-variants-protocol.js.map +1 -1
- package/package.json +3 -2
- package/src/ui/dist/assets/main-DREt3GGU.css +1 -0
- package/src/ui/dist/assets/main-Dt7ShmjM.js +655 -0
- package/src/ui/dist/index.html +2 -2
- package/dist/mcp/agent-variants/designCritique.d.ts +0 -167
- package/dist/mcp/agent-variants/designCritique.d.ts.map +0 -1
- package/dist/mcp/agent-variants/designCritique.js +0 -774
- package/dist/mcp/agent-variants/designCritique.js.map +0 -1
- package/src/ui/dist/assets/main-B1LAuLK_.js +0 -655
- package/src/ui/dist/assets/main-D-YwdYjm.css +0 -1
|
@@ -19,6 +19,17 @@ exports.DEFAULT_LEASE_TTL_MS = 20 * 60 * 1000;
|
|
|
19
19
|
* code_gen bump; scaffold timing has never bound, but a longer lease is safe. */
|
|
20
20
|
exports.SCAFFOLD_LEASE_TTL_MS = 30 * 60 * 1000;
|
|
21
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
|
+
}
|
|
22
33
|
function fingerprint(parts) {
|
|
23
34
|
return parts.map((p) => (p === undefined ? '∅' : String(p))).join('|');
|
|
24
35
|
}
|
|
@@ -718,7 +729,23 @@ class SessionStore {
|
|
|
718
729
|
}
|
|
719
730
|
leasable = [target];
|
|
720
731
|
}
|
|
721
|
-
|
|
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);
|
|
722
749
|
const issued = leasable.slice(0, Math.max(0, limit));
|
|
723
750
|
if (issued.length === 0) {
|
|
724
751
|
throw new errors_1.AgentVariantsError('INVALID_STAGE_ACTION', 'No work items currently leasable (all running, blocked by deps, or done)');
|
|
@@ -811,6 +838,16 @@ class SessionStore {
|
|
|
811
838
|
item.status = args.status;
|
|
812
839
|
item.output = args.output;
|
|
813
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
|
+
}
|
|
814
851
|
item.acceptedReports.push(fp);
|
|
815
852
|
item.lease = undefined;
|
|
816
853
|
if (args.status === 'failed') {
|
|
@@ -824,21 +861,20 @@ class SessionStore {
|
|
|
824
861
|
return result;
|
|
825
862
|
}
|
|
826
863
|
/**
|
|
827
|
-
* Re-open a succeeded variant work item for one
|
|
864
|
+
* Re-open a succeeded variant work item for one QA-driven regeneration.
|
|
828
865
|
* Consumes the agent's report (validating the lease), then resets the item
|
|
829
866
|
* to `pending` so it becomes leasable again via `request_work`, stashing the
|
|
830
|
-
*
|
|
831
|
-
*
|
|
867
|
+
* QA notes onto its input so the re-leased descriptor carries the notes the
|
|
868
|
+
* agent must address before re-reporting.
|
|
832
869
|
*
|
|
833
870
|
* The work item never reaches a terminal status, so the session stays in
|
|
834
|
-
* `waiting_for_results` and never goes terminal off the back of a
|
|
835
|
-
*
|
|
836
|
-
* 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.
|
|
837
873
|
*/
|
|
838
|
-
|
|
874
|
+
requeueForRegeneration(args) {
|
|
839
875
|
const session = this.requireSession(args.sessionId);
|
|
840
876
|
const fp = fingerprint([
|
|
841
|
-
'
|
|
877
|
+
'requeueForRegeneration',
|
|
842
878
|
args.sessionId,
|
|
843
879
|
args.workItemId,
|
|
844
880
|
args.leaseId,
|
|
@@ -857,7 +893,7 @@ class SessionStore {
|
|
|
857
893
|
item.output = undefined;
|
|
858
894
|
item.error = undefined;
|
|
859
895
|
const input = (item.input ?? {});
|
|
860
|
-
item.input = { ...input,
|
|
896
|
+
item.input = { ...input, qaRetry: args.qaRetry };
|
|
861
897
|
const result = {
|
|
862
898
|
stage: session.stage,
|
|
863
899
|
progress: this.progressOf(session),
|
|
@@ -902,6 +938,8 @@ class SessionStore {
|
|
|
902
938
|
briefLabel: targetInput.briefLabel,
|
|
903
939
|
briefBody: targetInput.briefBody,
|
|
904
940
|
...(args.currentHtml ? { currentHtml: args.currentHtml } : {}),
|
|
941
|
+
...(args.targetElement ? { targetElement: args.targetElement } : {}),
|
|
942
|
+
...(args.requestId ? { requestId: args.requestId } : {}),
|
|
905
943
|
projectContext: targetInput.projectContext ?? session.projectContext,
|
|
906
944
|
...(targetInput.context ? { context: targetInput.context } : {}),
|
|
907
945
|
},
|
|
@@ -912,6 +950,373 @@ class SessionStore {
|
|
|
912
950
|
session.stage = 'work_items_ready';
|
|
913
951
|
return item;
|
|
914
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
|
+
}
|
|
915
1320
|
/**
|
|
916
1321
|
* Fold a newer instruction into a variant's still-pending refinement. Used
|
|
917
1322
|
* when a second refinement is requested for a variant that already has a
|
|
@@ -1348,8 +1753,13 @@ class SessionStore {
|
|
|
1348
1753
|
const kind = this.variantWorkItemKind(session);
|
|
1349
1754
|
// Removed variants are excluded everywhere progress/summary/terminal
|
|
1350
1755
|
// accounting reads from, so a removed variant neither blocks the session
|
|
1351
|
-
// from going terminal nor inflates the ready/total counts.
|
|
1352
|
-
|
|
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));
|
|
1353
1763
|
}
|
|
1354
1764
|
/**
|
|
1355
1765
|
* The work-item kind that represents a user-facing variant for this
|
|
@@ -1433,6 +1843,13 @@ class SessionStore {
|
|
|
1433
1843
|
}),
|
|
1434
1844
|
};
|
|
1435
1845
|
}
|
|
1846
|
+
refinementSnapshotPriority(item) {
|
|
1847
|
+
if (item.status === 'running')
|
|
1848
|
+
return 2;
|
|
1849
|
+
if (item.status === 'pending')
|
|
1850
|
+
return 1;
|
|
1851
|
+
return 0;
|
|
1852
|
+
}
|
|
1436
1853
|
variantsOf(session) {
|
|
1437
1854
|
const kind = this.variantWorkItemKind(session);
|
|
1438
1855
|
const refinementsByVariantId = new Map();
|
|
@@ -1442,30 +1859,58 @@ class SessionStore {
|
|
|
1442
1859
|
const input = item.input;
|
|
1443
1860
|
if (!input.variantId)
|
|
1444
1861
|
continue;
|
|
1445
|
-
refinementsByVariantId.
|
|
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
|
+
}
|
|
1446
1868
|
}
|
|
1447
|
-
return
|
|
1448
|
-
.
|
|
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)))
|
|
1449
1876
|
.map((item) => {
|
|
1450
1877
|
const input = item.input;
|
|
1878
|
+
const refineItem = refinementsByVariantId.get(item.id);
|
|
1879
|
+
const refineRequestId = refineItem?.input
|
|
1880
|
+
?.requestId;
|
|
1451
1881
|
return {
|
|
1452
1882
|
workItemId: item.id,
|
|
1453
1883
|
briefId: input.briefId ?? '',
|
|
1454
1884
|
label: input.briefLabel ?? 'Variant',
|
|
1455
1885
|
status: item.status,
|
|
1456
1886
|
createdAt: new Date(session.createdAt).toISOString(),
|
|
1457
|
-
|
|
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
|
+
: {}),
|
|
1458
1902
|
...(input.briefBody ? { description: input.briefBody } : {}),
|
|
1903
|
+
...(input.adoptedFrom
|
|
1904
|
+
? { adoptedSource: { ...input.adoptedFrom } }
|
|
1905
|
+
: {}),
|
|
1459
1906
|
design: variantDesignFromInput(input.designContextEntry),
|
|
1460
|
-
...(
|
|
1907
|
+
...(refineItem
|
|
1461
1908
|
? {
|
|
1462
1909
|
refinement: {
|
|
1463
|
-
workItemId:
|
|
1464
|
-
status:
|
|
1465
|
-
...(
|
|
1466
|
-
? {
|
|
1467
|
-
errorCode: String(refinementsByVariantId.get(item.id).error?.code),
|
|
1468
|
-
}
|
|
1910
|
+
workItemId: refineItem.id,
|
|
1911
|
+
status: refineItem.status,
|
|
1912
|
+
...(refineItem.error?.code
|
|
1913
|
+
? { errorCode: String(refineItem.error.code) }
|
|
1469
1914
|
: {}),
|
|
1470
1915
|
},
|
|
1471
1916
|
}
|