rivet-design 0.13.3 → 0.13.5
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/agent-variants/SessionStore.d.ts +25 -4
- package/dist/agent-variants/SessionStore.d.ts.map +1 -1
- package/dist/agent-variants/SessionStore.js +32 -0
- package/dist/agent-variants/SessionStore.js.map +1 -1
- package/dist/agent-variants/VariantWorkspaceManager.d.ts +43 -0
- package/dist/agent-variants/VariantWorkspaceManager.d.ts.map +1 -0
- package/dist/agent-variants/VariantWorkspaceManager.js +216 -0
- package/dist/agent-variants/VariantWorkspaceManager.js.map +1 -0
- package/dist/agent-variants/WorktreeOrchestrator.d.ts +40 -3
- package/dist/agent-variants/WorktreeOrchestrator.d.ts.map +1 -1
- package/dist/agent-variants/WorktreeOrchestrator.js +424 -62
- package/dist/agent-variants/WorktreeOrchestrator.js.map +1 -1
- package/dist/agent-variants/contracts.d.ts +72 -14
- package/dist/agent-variants/contracts.d.ts.map +1 -1
- package/dist/agent-variants/contracts.js +17 -3
- package/dist/agent-variants/contracts.js.map +1 -1
- package/dist/agent-variants/runLabel.d.ts +5 -4
- package/dist/agent-variants/runLabel.d.ts.map +1 -1
- package/dist/agent-variants/runLabel.js +16 -8
- package/dist/agent-variants/runLabel.js.map +1 -1
- package/dist/agent-variants/workItemBriefing.d.ts +26 -0
- package/dist/agent-variants/workItemBriefing.d.ts.map +1 -0
- package/dist/agent-variants/workItemBriefing.js +110 -0
- package/dist/agent-variants/workItemBriefing.js.map +1 -0
- package/dist/cli/client.d.ts +2 -0
- package/dist/cli/client.d.ts.map +1 -1
- package/dist/cli/client.js +1 -1
- package/dist/cli/client.js.map +1 -1
- package/dist/cli/commands/mcpServe.d.ts.map +1 -1
- package/dist/cli/commands/mcpServe.js +47 -20
- package/dist/cli/commands/mcpServe.js.map +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +3 -1
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/variants.d.ts.map +1 -1
- package/dist/cli/commands/variants.js +118 -9
- package/dist/cli/commands/variants.js.map +1 -1
- package/dist/cli/hostWorkNextAction.d.ts +33 -26
- package/dist/cli/hostWorkNextAction.d.ts.map +1 -1
- package/dist/cli/hostWorkNextAction.js +47 -24
- package/dist/cli/hostWorkNextAction.js.map +1 -1
- package/dist/cli/router.js +2 -2
- package/dist/cli/router.js.map +1 -1
- package/dist/hosts/workerDefaults.d.ts +62 -0
- package/dist/hosts/workerDefaults.d.ts.map +1 -0
- package/dist/hosts/workerDefaults.js +77 -0
- package/dist/hosts/workerDefaults.js.map +1 -0
- package/dist/install/harnesses.d.ts.map +1 -1
- package/dist/install/harnesses.js +21 -2
- package/dist/install/harnesses.js.map +1 -1
- package/dist/install/mcpRegistration.d.ts +1 -1
- package/dist/install/mcpRegistration.d.ts.map +1 -1
- package/dist/install/mcpRegistration.js +23 -10
- package/dist/install/mcpRegistration.js.map +1 -1
- package/dist/mcp/changeBatchClassification.d.ts +2 -0
- package/dist/mcp/changeBatchClassification.d.ts.map +1 -1
- package/dist/mcp/changeBatchClassification.js +5 -0
- package/dist/mcp/changeBatchClassification.js.map +1 -1
- package/dist/routes/agentVariants.d.ts.map +1 -1
- package/dist/routes/agentVariants.js +11 -4
- package/dist/routes/agentVariants.js.map +1 -1
- package/dist/routes/emptyStatePage.d.ts +14 -0
- package/dist/routes/emptyStatePage.d.ts.map +1 -0
- package/dist/routes/emptyStatePage.js +601 -0
- package/dist/routes/emptyStatePage.js.map +1 -0
- package/dist/routes/static.d.ts.map +1 -1
- package/dist/routes/static.js +2 -73
- package/dist/routes/static.js.map +1 -1
- package/dist/services/TelemetryService.d.ts +8 -0
- package/dist/services/TelemetryService.d.ts.map +1 -1
- package/dist/services/TelemetryService.js +24 -0
- package/dist/services/TelemetryService.js.map +1 -1
- package/dist/services/VariantGenerationService.d.ts.map +1 -1
- package/dist/services/VariantGenerationService.js +14 -0
- package/dist/services/VariantGenerationService.js.map +1 -1
- package/dist/services/VariantRunService.d.ts +5 -2
- package/dist/services/VariantRunService.d.ts.map +1 -1
- package/dist/services/VariantRunService.js +40 -14
- package/dist/services/VariantRunService.js.map +1 -1
- package/dist/services/WorktreeManager.d.ts.map +1 -1
- package/dist/services/WorktreeManager.js +7 -2
- package/dist/services/WorktreeManager.js.map +1 -1
- package/dist/types/change-request-types.d.ts +5 -0
- package/dist/types/change-request-types.d.ts.map +1 -1
- package/dist/utils/cloneOrCopyDirSync.d.ts +14 -0
- package/dist/utils/cloneOrCopyDirSync.d.ts.map +1 -0
- package/dist/utils/cloneOrCopyDirSync.js +72 -0
- package/dist/utils/cloneOrCopyDirSync.js.map +1 -0
- package/dist/utils/skillWriter.d.ts +8 -0
- package/dist/utils/skillWriter.d.ts.map +1 -1
- package/dist/utils/skillWriter.js +35 -0
- package/dist/utils/skillWriter.js.map +1 -1
- package/dist/utils/skills/claude-skill.d.ts +2 -2
- package/dist/utils/skills/claude-skill.d.ts.map +1 -1
- package/dist/utils/skills/claude-skill.js +1 -1
- package/dist/utils/skills/cli-guidance.d.ts +2 -2
- package/dist/utils/skills/cli-guidance.d.ts.map +1 -1
- package/dist/utils/skills/cli-guidance.js +11 -10
- package/dist/utils/skills/cli-guidance.js.map +1 -1
- package/dist/utils/skills/cursor-rules.d.ts +2 -2
- package/dist/utils/skills/cursor-rules.d.ts.map +1 -1
- package/dist/utils/skills/cursor-rules.js +1 -1
- package/package.json +3 -1
- package/src/ui/dist/assets/main-Ct4AXMqD.js +422 -0
- package/src/ui/dist/index.html +1 -1
- package/src/ui/dist/assets/main-Bd9GWv8x.js +0 -419
|
@@ -17,6 +17,7 @@ const devServerError_1 = require("../utils/devServerError");
|
|
|
17
17
|
const errors_1 = require("./errors");
|
|
18
18
|
const createProjectArtifacts_1 = require("./createProjectArtifacts");
|
|
19
19
|
const contracts_1 = require("./contracts");
|
|
20
|
+
const workItemBriefing_1 = require("./workItemBriefing");
|
|
20
21
|
const briefContent_1 = require("./briefContent");
|
|
21
22
|
const runLabel_1 = require("./runLabel");
|
|
22
23
|
const runPlan_1 = require("./runPlan");
|
|
@@ -24,6 +25,7 @@ const viteReactTs_1 = require("../services/templates/viteReactTs");
|
|
|
24
25
|
const StaticPreviewServer_1 = require("../services/StaticPreviewServer");
|
|
25
26
|
const designCatalog_1 = require("../services/templates/designCatalog");
|
|
26
27
|
const previewQa_1 = require("./previewQa");
|
|
28
|
+
const VariantWorkspaceManager_1 = require("./VariantWorkspaceManager");
|
|
27
29
|
const diffQa_1 = require("./diffQa");
|
|
28
30
|
const VariantHistoryService_1 = require("../services/VariantHistoryService");
|
|
29
31
|
const variantContext_1 = require("./variantContext");
|
|
@@ -60,6 +62,16 @@ const BRIEF_LABEL_ALTERNATIVES = [
|
|
|
60
62
|
'Density',
|
|
61
63
|
'Color',
|
|
62
64
|
];
|
|
65
|
+
const FALLBACK_DIRECTION_FOCI = [
|
|
66
|
+
'Emphasize hierarchy and one unmistakable primary action.',
|
|
67
|
+
'Use stronger contrast, scale shifts, and a more expressive visual system.',
|
|
68
|
+
'Favor a quieter, spacious composition with restrained supporting detail.',
|
|
69
|
+
'Make information density and fast scanning the organizing principle.',
|
|
70
|
+
'Lead with imagery and let content frame the visual story.',
|
|
71
|
+
'Use modular cards and clear grouping to expose the product structure.',
|
|
72
|
+
'Explore tactile depth, layering, and purposeful interaction cues.',
|
|
73
|
+
'Reduce the concept to its most essential typography and geometry.',
|
|
74
|
+
];
|
|
63
75
|
// Upper bound on waiting for in-flight history persists during session
|
|
64
76
|
// teardown. Generous enough for a full-repo snapshot on a large project;
|
|
65
77
|
// small enough that a wedged git subprocess (which has its own 120s block
|
|
@@ -350,6 +362,12 @@ const buildViewDisabledReason = (args) => {
|
|
|
350
362
|
return 'Variant is still running';
|
|
351
363
|
};
|
|
352
364
|
const NOOP_TELEMETRY = { track: () => undefined };
|
|
365
|
+
const isImplementationWorkItemKind = (kind) => kind === 'static_preview' || kind === 'refine_variant' || kind === 'code_gen';
|
|
366
|
+
const DEFAULT_EXECUTION_MODE_BY_KIND = {
|
|
367
|
+
static_preview: 'create',
|
|
368
|
+
refine_variant: 'refine',
|
|
369
|
+
code_gen: 'create',
|
|
370
|
+
};
|
|
353
371
|
const DESIGN_CREATION_ACTION_RE = /\b(create|build|design|make|redesign|refresh|update)\b/i;
|
|
354
372
|
const DESIGN_CREATION_SURFACE_RE = /\b(home\s?page|landing page|dashboard|screen|site|website|app|page|ui|interface|component|hero)\b/i;
|
|
355
373
|
const NON_VISUAL_CHANGE_RE = /\b(api|backend|database|event|lint|query|server|test|tracking|typecheck)\b/i;
|
|
@@ -658,16 +676,71 @@ class AgentVariantsOrchestrator {
|
|
|
658
676
|
}
|
|
659
677
|
getHostWork(sessionId) {
|
|
660
678
|
return this.store.getHostWork(sessionId).map((item) => {
|
|
661
|
-
const
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
679
|
+
const kind = item.kind;
|
|
680
|
+
if (!isImplementationWorkItemKind(kind)) {
|
|
681
|
+
const planningItem = {
|
|
682
|
+
...item,
|
|
683
|
+
kind,
|
|
684
|
+
};
|
|
685
|
+
return planningItem;
|
|
686
|
+
}
|
|
687
|
+
const input = (item.input ?? {});
|
|
688
|
+
const workspace = this.getAssignedWorkspace({
|
|
689
|
+
sessionId,
|
|
690
|
+
workItemId: item.workId,
|
|
691
|
+
kind,
|
|
692
|
+
});
|
|
693
|
+
const mode = input.sourceVariantId
|
|
694
|
+
? 'vary'
|
|
695
|
+
: DEFAULT_EXECUTION_MODE_BY_KIND[kind];
|
|
696
|
+
const sourceLabel = input.sourceLabel ?? (mode === 'refine' ? input.briefLabel : undefined);
|
|
697
|
+
const sourceBody = input.sourceBody ??
|
|
698
|
+
(mode === 'refine' ? input.briefBody : undefined) ??
|
|
699
|
+
sourceLabel;
|
|
700
|
+
const implementationItem = {
|
|
701
|
+
workId: item.workId,
|
|
702
|
+
variantId: item.variantId,
|
|
703
|
+
kind,
|
|
704
|
+
status: item.status,
|
|
705
|
+
leaseId: item.leaseId,
|
|
706
|
+
attempt: item.attempt,
|
|
707
|
+
leaseExpiresAt: item.leaseExpiresAt,
|
|
708
|
+
workspace,
|
|
709
|
+
mode,
|
|
710
|
+
direction: {
|
|
711
|
+
label: input.briefLabel ?? 'Variant',
|
|
712
|
+
brief: input.briefBody ?? input.instruction ?? 'Create this variant',
|
|
713
|
+
...(input.briefNotes ? { notes: input.briefNotes } : {}),
|
|
714
|
+
},
|
|
715
|
+
...(sourceLabel && sourceBody
|
|
716
|
+
? { sourceDirection: { label: sourceLabel, brief: sourceBody } }
|
|
717
|
+
: {}),
|
|
718
|
+
...(input.relevantFiles?.length
|
|
719
|
+
? { relevantFiles: input.relevantFiles }
|
|
720
|
+
: {}),
|
|
721
|
+
...(input.instruction ? { instruction: input.instruction } : {}),
|
|
722
|
+
...(input.target ? { target: input.target } : {}),
|
|
723
|
+
...(input.targetElement ? { targetElement: input.targetElement } : {}),
|
|
724
|
+
...(input.context?.contextBundlePath
|
|
725
|
+
? { contextBundlePath: input.context.contextBundlePath }
|
|
726
|
+
: {}),
|
|
727
|
+
completion: kind === 'code_gen'
|
|
728
|
+
? { kind: 'worktree_diff' }
|
|
729
|
+
: { kind: 'static_artifact' },
|
|
668
730
|
};
|
|
731
|
+
return implementationItem;
|
|
669
732
|
});
|
|
670
733
|
}
|
|
734
|
+
getAssignedWorkspace(args) {
|
|
735
|
+
const workspace = this.resources
|
|
736
|
+
.get(args.sessionId)
|
|
737
|
+
?.workspaceManager.getAssignedWorkspace(args.workItemId);
|
|
738
|
+
const expectedKind = args.kind === 'code_gen' ? 'git_worktree' : 'artifact_directory';
|
|
739
|
+
if (!workspace || workspace.kind !== expectedKind) {
|
|
740
|
+
throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', `Variant ${args.workItemId} has no assigned ${expectedKind} workspace`);
|
|
741
|
+
}
|
|
742
|
+
return workspace;
|
|
743
|
+
}
|
|
671
744
|
getVariantsTelemetryMode(projectContext, _target) {
|
|
672
745
|
if (projectContext.kind === 'fresh' && projectContext.variantContext) {
|
|
673
746
|
return 'source_grounded';
|
|
@@ -967,6 +1040,35 @@ class AgentVariantsOrchestrator {
|
|
|
967
1040
|
}
|
|
968
1041
|
requestWork(args) {
|
|
969
1042
|
const result = this.store.requestWork(args);
|
|
1043
|
+
try {
|
|
1044
|
+
this.ensureStaticWorkspaces(args.sessionId, result.leasedWorkItems);
|
|
1045
|
+
}
|
|
1046
|
+
catch (error) {
|
|
1047
|
+
for (const item of result.leasedWorkItems) {
|
|
1048
|
+
if (item.kind !== 'static_preview' && item.kind !== 'refine_variant') {
|
|
1049
|
+
continue;
|
|
1050
|
+
}
|
|
1051
|
+
try {
|
|
1052
|
+
this.store.reportComplete({
|
|
1053
|
+
sessionId: args.sessionId,
|
|
1054
|
+
workItemId: item.id,
|
|
1055
|
+
leaseId: result.leaseId,
|
|
1056
|
+
attempt: item.attempt,
|
|
1057
|
+
status: 'failed',
|
|
1058
|
+
error: {
|
|
1059
|
+
code: 'RUNTIME_VALIDATION_FAILED',
|
|
1060
|
+
message: error instanceof Error
|
|
1061
|
+
? error.message
|
|
1062
|
+
: 'Failed to assign static variant workspace',
|
|
1063
|
+
},
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
catch (reportError) {
|
|
1067
|
+
log.warn(`Failed to record static workspace provisioning failure for ${item.id}`, reportError);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
throw error;
|
|
1071
|
+
}
|
|
970
1072
|
// Record lease times so variant_completed can report lease→report
|
|
971
1073
|
// duration. (We don't fire a separate code_gen_leased event — the
|
|
972
1074
|
// funnel signal we care about is per-variant completion duration.)
|
|
@@ -977,6 +1079,7 @@ class AgentVariantsOrchestrator {
|
|
|
977
1079
|
resources.leasedAt.set(item.id, now);
|
|
978
1080
|
}
|
|
979
1081
|
}
|
|
1082
|
+
this.writeLeasedWorkItemBriefings(args.sessionId, result.leasedWorkItems.map((item) => item.id));
|
|
980
1083
|
this.trackRefinementLeased({ sessionId: args.sessionId, lease: result });
|
|
981
1084
|
// Decorate each leased item's input with the worktreePath provisioned
|
|
982
1085
|
// by approve(). The skill instructs agents to write files to
|
|
@@ -998,6 +1101,94 @@ class AgentVariantsOrchestrator {
|
|
|
998
1101
|
this.emitChange();
|
|
999
1102
|
return decorated;
|
|
1000
1103
|
}
|
|
1104
|
+
/**
|
|
1105
|
+
* @effect Writes each leased implementation item's server-authored briefing
|
|
1106
|
+
* BESIDE its workspace so the coordinating host dispatches workers with a
|
|
1107
|
+
* short pointer prompt instead of composing prose per worker. Best-effort:
|
|
1108
|
+
* a briefing write failure never blocks the lease (the host guidance's
|
|
1109
|
+
* packet fields still carry the same data).
|
|
1110
|
+
*/
|
|
1111
|
+
writeLeasedWorkItemBriefings(sessionId, leasedWorkItemIds) {
|
|
1112
|
+
if (leasedWorkItemIds.length === 0)
|
|
1113
|
+
return;
|
|
1114
|
+
const leased = new Set(leasedWorkItemIds);
|
|
1115
|
+
for (const item of this.getHostWork(sessionId)) {
|
|
1116
|
+
if (!leased.has(item.workId))
|
|
1117
|
+
continue;
|
|
1118
|
+
if (!isImplementationWorkItemKind(item.kind))
|
|
1119
|
+
continue;
|
|
1120
|
+
const implementation = item;
|
|
1121
|
+
try {
|
|
1122
|
+
const briefingPath = (0, workItemBriefing_1.workItemBriefingPath)(sessionId, item.workId);
|
|
1123
|
+
fs_1.default.mkdirSync(path_1.default.dirname(briefingPath), { recursive: true });
|
|
1124
|
+
fs_1.default.writeFileSync(briefingPath, (0, workItemBriefing_1.renderWorkItemBriefing)(implementation), 'utf8');
|
|
1125
|
+
}
|
|
1126
|
+
catch (error) {
|
|
1127
|
+
log.warn(`Failed to write work-item briefing for ${item.workId}`, error);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
/** @effect Assigns isolated, server-owned artifact workspaces to leased static work. */
|
|
1132
|
+
ensureStaticWorkspaces(sessionId, items) {
|
|
1133
|
+
const resources = this.ensureResources(sessionId);
|
|
1134
|
+
const projectContext = this.store.getProjectContext(sessionId);
|
|
1135
|
+
// This lane provisions by copying the source artifact tree (vary forks) or
|
|
1136
|
+
// seeding an entry file — it bypasses provisionWorktrees, so it gets its
|
|
1137
|
+
// own provisioning telemetry here.
|
|
1138
|
+
const provisionStartedAt = Date.now();
|
|
1139
|
+
let assignedCount = 0;
|
|
1140
|
+
for (const item of items) {
|
|
1141
|
+
if (item.kind !== 'static_preview' && item.kind !== 'refine_variant') {
|
|
1142
|
+
continue;
|
|
1143
|
+
}
|
|
1144
|
+
if (resources.workspaceManager.getAssignedWorkspace(item.id))
|
|
1145
|
+
continue;
|
|
1146
|
+
assignedCount += 1;
|
|
1147
|
+
const input = (item.input ?? {});
|
|
1148
|
+
const existingProjectPath = projectContext.kind === 'existing'
|
|
1149
|
+
? projectContext.projectPath
|
|
1150
|
+
: undefined;
|
|
1151
|
+
const existingParent = existingProjectPath && fs_1.default.existsSync(existingProjectPath)
|
|
1152
|
+
? path_1.default.join(existingProjectPath, '.rivet', 'variant-work', sessionId)
|
|
1153
|
+
: path_1.default.join(os_1.default.tmpdir(), 'rivet-variant-work', sessionId);
|
|
1154
|
+
const parent = projectContext.kind === 'fresh'
|
|
1155
|
+
? projectContext.workspacePath
|
|
1156
|
+
: existingParent;
|
|
1157
|
+
fs_1.default.mkdirSync(parent, { recursive: true });
|
|
1158
|
+
const rootPath = projectContext.kind === 'fresh'
|
|
1159
|
+
? path_1.default.join(parent, `.rivet-preview-${item.id}`)
|
|
1160
|
+
: path_1.default.join(parent, item.id);
|
|
1161
|
+
const sourceVariantId = input.sourceVariantId ?? input.variantId;
|
|
1162
|
+
const sourceDirectory = sourceVariantId
|
|
1163
|
+
? resources.staticPreviews.get(sourceVariantId)?.assetBase
|
|
1164
|
+
: undefined;
|
|
1165
|
+
resources.workspaceManager.assignArtifactWorkspace({
|
|
1166
|
+
workItemId: item.id,
|
|
1167
|
+
rootPath,
|
|
1168
|
+
allowedRoot: parent,
|
|
1169
|
+
entryPath: 'index.html',
|
|
1170
|
+
sourceDirectory,
|
|
1171
|
+
seedHtml: input.currentHtml,
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
if (assignedCount > 0) {
|
|
1175
|
+
this.telemetry.track('agent_variants.provisioning_completed', {
|
|
1176
|
+
source: 'mcp',
|
|
1177
|
+
sessionId,
|
|
1178
|
+
kind: 'artifact_directory',
|
|
1179
|
+
variantCount: assignedCount,
|
|
1180
|
+
durationMs: Date.now() - provisionStartedAt,
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
/** Reads a completed static artifact while enforcing its assigned directory boundary. */
|
|
1185
|
+
readAssignedStaticArtifact(sessionId, workItemId) {
|
|
1186
|
+
const workspaceManager = this.resources.get(sessionId)?.workspaceManager;
|
|
1187
|
+
if (!workspaceManager) {
|
|
1188
|
+
throw new errors_1.AgentVariantsError('SESSION_NOT_FOUND', `No live resources for session ${sessionId}`);
|
|
1189
|
+
}
|
|
1190
|
+
return workspaceManager.readArtifact(workItemId);
|
|
1191
|
+
}
|
|
1001
1192
|
beginStaticPreviewRefinements(args) {
|
|
1002
1193
|
const seenVariantIds = new Set();
|
|
1003
1194
|
// Targets whose in-place refinement could neither be created nor folded
|
|
@@ -1039,6 +1230,7 @@ class AgentVariantsOrchestrator {
|
|
|
1039
1230
|
instruction: target.instruction,
|
|
1040
1231
|
currentHtml,
|
|
1041
1232
|
targetElement: target.element,
|
|
1233
|
+
relevantFiles: target.relevantFiles,
|
|
1042
1234
|
requestId: target.requestId,
|
|
1043
1235
|
});
|
|
1044
1236
|
createdWorkItemIds.push(refineItem.id);
|
|
@@ -1085,8 +1277,10 @@ class AgentVariantsOrchestrator {
|
|
|
1085
1277
|
instruction: target.instruction,
|
|
1086
1278
|
count: target.count,
|
|
1087
1279
|
...(target.briefs ? { briefs: target.briefs } : {}),
|
|
1280
|
+
...(target.runLabel ? { runLabel: target.runLabel } : {}),
|
|
1088
1281
|
currentHtml,
|
|
1089
1282
|
targetElement: target.element,
|
|
1283
|
+
relevantFiles: target.relevantFiles,
|
|
1090
1284
|
requestId: target.requestId,
|
|
1091
1285
|
});
|
|
1092
1286
|
createdWorkItemIds.push(...forks.map((item) => item.id));
|
|
@@ -1369,6 +1563,10 @@ class AgentVariantsOrchestrator {
|
|
|
1369
1563
|
getWorkItemInput(sessionId, workItemId) {
|
|
1370
1564
|
return this.store.getWorkItemInput(sessionId, workItemId);
|
|
1371
1565
|
}
|
|
1566
|
+
/** Returns status after orchestration QA and refinement post-processing. */
|
|
1567
|
+
getWorkItemStatus(sessionId, workItemId) {
|
|
1568
|
+
return this.store.getWorkItemStatus(sessionId, workItemId);
|
|
1569
|
+
}
|
|
1372
1570
|
getBriefs(sessionId) {
|
|
1373
1571
|
return this.store.getBriefs(sessionId);
|
|
1374
1572
|
}
|
|
@@ -1494,6 +1692,38 @@ class AgentVariantsOrchestrator {
|
|
|
1494
1692
|
getDevServerPort(sessionId, workItemId) {
|
|
1495
1693
|
return this.resources.get(sessionId)?.worktrees.get(workItemId)?.port;
|
|
1496
1694
|
}
|
|
1695
|
+
/**
|
|
1696
|
+
* Restores a Rivet-owned variant preview that the active-session idle timer
|
|
1697
|
+
* reclaimed. Other missing-preview causes remain terminal so this path never
|
|
1698
|
+
* attempts to restart user-owned or repeatedly failing servers.
|
|
1699
|
+
*
|
|
1700
|
+
* @effect Clears the idle marker, starts the worktree dev server, and emits
|
|
1701
|
+
* preview-state snapshots.
|
|
1702
|
+
* @deps Uses the live session resources, project context, and dev-server
|
|
1703
|
+
* manager owned by this orchestrator.
|
|
1704
|
+
*/
|
|
1705
|
+
async restoreVariantPreview(sessionId, workItemId) {
|
|
1706
|
+
const resources = this.resources.get(sessionId);
|
|
1707
|
+
const record = resources?.worktrees.get(workItemId);
|
|
1708
|
+
if (!resources || !record)
|
|
1709
|
+
return undefined;
|
|
1710
|
+
if (record.port !== undefined && record.devServerProcess) {
|
|
1711
|
+
this.touchSession(sessionId);
|
|
1712
|
+
return record.port;
|
|
1713
|
+
}
|
|
1714
|
+
if (resources.previewFailures.get(workItemId)?.reason !== 'idle_timeout') {
|
|
1715
|
+
return undefined;
|
|
1716
|
+
}
|
|
1717
|
+
resources.previewFailures.delete(workItemId);
|
|
1718
|
+
this.emitChange();
|
|
1719
|
+
const projectContext = this.store.getProjectContext(sessionId);
|
|
1720
|
+
return this.startVariantDevServer({
|
|
1721
|
+
sessionId,
|
|
1722
|
+
workItemId,
|
|
1723
|
+
record,
|
|
1724
|
+
isFresh: projectContext.kind === 'fresh',
|
|
1725
|
+
});
|
|
1726
|
+
}
|
|
1497
1727
|
/**
|
|
1498
1728
|
* @effect Resolves the dev server port for a preview request and refreshes
|
|
1499
1729
|
* active-preview retention for that session while the port is still live.
|
|
@@ -1713,6 +1943,9 @@ class AgentVariantsOrchestrator {
|
|
|
1713
1943
|
sessionId,
|
|
1714
1944
|
briefs,
|
|
1715
1945
|
runLabel: args.runLabel?.trim() || (0, runLabel_1.deriveRunLabel)(args.prompt),
|
|
1946
|
+
...(args.relevantFiles?.length
|
|
1947
|
+
? { relevantFiles: args.relevantFiles }
|
|
1948
|
+
: {}),
|
|
1716
1949
|
...(projectContext.kind === 'existing' && projectContext.variantContext
|
|
1717
1950
|
? { variantContext: projectContext.variantContext }
|
|
1718
1951
|
: {}),
|
|
@@ -1755,6 +1988,7 @@ class AgentVariantsOrchestrator {
|
|
|
1755
1988
|
prompt: args.prompt,
|
|
1756
1989
|
count,
|
|
1757
1990
|
target: args.target,
|
|
1991
|
+
relevantFiles: args.relevantFiles,
|
|
1758
1992
|
projectContext,
|
|
1759
1993
|
runLabel: args.runLabel,
|
|
1760
1994
|
...(args.sessionId ? { sessionId: args.sessionId } : {}),
|
|
@@ -1905,20 +2139,79 @@ class AgentVariantsOrchestrator {
|
|
|
1905
2139
|
return null;
|
|
1906
2140
|
return sessionId;
|
|
1907
2141
|
}
|
|
2142
|
+
/** @effect Registers a provisioned git worktree with runtime and workspace state. */
|
|
2143
|
+
async registerWorktreeWorkspace(resources, record) {
|
|
2144
|
+
let projectPath = record.worktreePath;
|
|
2145
|
+
try {
|
|
2146
|
+
projectPath = await this.worktrees.getProjectCwdInWorktree(record.worktreePath);
|
|
2147
|
+
}
|
|
2148
|
+
catch (err) {
|
|
2149
|
+
log.warn(`Failed to resolve project path in worktree ${record.worktreePath}; using its root`, err);
|
|
2150
|
+
}
|
|
2151
|
+
resources.worktrees.set(record.workItemId, record);
|
|
2152
|
+
resources.workspaceManager.registerGitWorkspace({
|
|
2153
|
+
workItemId: record.workItemId,
|
|
2154
|
+
rootPath: record.worktreePath,
|
|
2155
|
+
projectPath,
|
|
2156
|
+
});
|
|
2157
|
+
}
|
|
1908
2158
|
/**
|
|
1909
2159
|
* Provision a worktree for each appended existing-project variant. Mirrors
|
|
1910
2160
|
* {@link provisionExistingWorktrees} but only for the newly appended ids, and
|
|
1911
2161
|
* passes a per-batch `instanceKeyPrefix` so the new worktree dirs do not
|
|
1912
2162
|
* collide with the first batch's `${sessionId}-${i}` paths.
|
|
1913
2163
|
*/
|
|
2164
|
+
/** Emits provisioning started/completed/failed telemetry around `run` for
|
|
2165
|
+
* the vary-fork/append lanes that bypass provisionWorktrees (and its
|
|
2166
|
+
* telemetry). Rethrows failures untouched. */
|
|
2167
|
+
async withProvisioningTelemetry(args, run) {
|
|
2168
|
+
const startedAt = Date.now();
|
|
2169
|
+
this.telemetry.track('agent_variants.provisioning_started', {
|
|
2170
|
+
source: 'mcp',
|
|
2171
|
+
sessionId: args.sessionId,
|
|
2172
|
+
kind: args.kind,
|
|
2173
|
+
variantCount: args.variantCount,
|
|
2174
|
+
});
|
|
2175
|
+
try {
|
|
2176
|
+
const result = await run();
|
|
2177
|
+
this.telemetry.track('agent_variants.provisioning_completed', {
|
|
2178
|
+
source: 'mcp',
|
|
2179
|
+
sessionId: args.sessionId,
|
|
2180
|
+
kind: args.kind,
|
|
2181
|
+
variantCount: args.variantCount,
|
|
2182
|
+
durationMs: Date.now() - startedAt,
|
|
2183
|
+
});
|
|
2184
|
+
return result;
|
|
2185
|
+
}
|
|
2186
|
+
catch (err) {
|
|
2187
|
+
const timedOut = err instanceof ProvisionTimeoutError;
|
|
2188
|
+
this.telemetry.track('agent_variants.provisioning_failed', {
|
|
2189
|
+
source: 'mcp',
|
|
2190
|
+
sessionId: args.sessionId,
|
|
2191
|
+
kind: args.kind,
|
|
2192
|
+
variantCount: args.variantCount,
|
|
2193
|
+
durationMs: Date.now() - startedAt,
|
|
2194
|
+
timedOut,
|
|
2195
|
+
errorCode: timedOut
|
|
2196
|
+
? 'PROVISION_TIMEOUT'
|
|
2197
|
+
: err instanceof errors_1.AgentVariantsError
|
|
2198
|
+
? err.code
|
|
2199
|
+
: 'PROVISION_FAILED',
|
|
2200
|
+
});
|
|
2201
|
+
throw err;
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
1914
2204
|
async provisionAppendedExistingWorktrees(sessionId, workItemIds) {
|
|
1915
2205
|
if (workItemIds.length === 0)
|
|
1916
2206
|
return;
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
2207
|
+
await this.withProvisioningTelemetry({ sessionId, kind: 'appended_existing', variantCount: workItemIds.length }, async () => {
|
|
2208
|
+
const resources = this.ensureResources(sessionId);
|
|
2209
|
+
const instanceKeyPrefix = `a${(0, crypto_1.randomUUID)().slice(0, 8)}-`;
|
|
2210
|
+
const paths = await withProvisionTimeout(this.worktrees.createWorktrees(sessionId, workItemIds.length, instanceKeyPrefix), this.provisionTimeoutMs);
|
|
2211
|
+
await Promise.all(workItemIds.map((id, index) => this.registerWorktreeWorkspace(resources, {
|
|
2212
|
+
workItemId: id,
|
|
2213
|
+
worktreePath: paths[index],
|
|
2214
|
+
})));
|
|
1922
2215
|
});
|
|
1923
2216
|
}
|
|
1924
2217
|
/**
|
|
@@ -1936,14 +2229,21 @@ class AgentVariantsOrchestrator {
|
|
|
1936
2229
|
if (!this.worktrees.forkWorktreesFrom) {
|
|
1937
2230
|
throw new errors_1.AgentVariantsError('RUNTIME_VALIDATION_FAILED', 'worktreeManager does not implement forkWorktreesFrom');
|
|
1938
2231
|
}
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
2232
|
+
await this.withProvisioningTelemetry({ sessionId, kind: 'forked_variant', variantCount: workItemIds.length }, async () => {
|
|
2233
|
+
const resources = this.ensureResources(sessionId);
|
|
2234
|
+
const instanceKeyPrefix = `f${(0, crypto_1.randomUUID)().slice(0, 8)}-`;
|
|
2235
|
+
const paths = await withProvisionTimeout(this.worktrees.forkWorktreesFrom(sourcePath, sessionId, workItemIds.length, instanceKeyPrefix), this.provisionTimeoutMs);
|
|
2236
|
+
await Promise.all(workItemIds.map((id, index) => this.registerWorktreeWorkspace(resources, {
|
|
2237
|
+
workItemId: id,
|
|
2238
|
+
worktreePath: paths[index],
|
|
2239
|
+
})));
|
|
1944
2240
|
});
|
|
1945
2241
|
}
|
|
1946
2242
|
async reportComplete(args) {
|
|
2243
|
+
// Phase-timing anchor: lease→report (agent work) vs report→recorded
|
|
2244
|
+
// (QA gate + post-processing) are attributed separately on
|
|
2245
|
+
// `variant_completed` so latency work can tell them apart.
|
|
2246
|
+
const reportReceivedAt = Date.now();
|
|
1947
2247
|
// Cooperative abort short-circuit: if the work item was removed/cancelled
|
|
1948
2248
|
// while in flight (delete-while-loading, cancel_variant, session cancel),
|
|
1949
2249
|
// skip the schema and QA gates entirely — there is no point validating or
|
|
@@ -1956,18 +2256,23 @@ class AgentVariantsOrchestrator {
|
|
|
1956
2256
|
const workItemKind = this.store.hasSession(args.sessionId)
|
|
1957
2257
|
? this.store.getWorkItemKind(args.sessionId, args.workItemId)
|
|
1958
2258
|
: undefined;
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
2259
|
+
const resolvedArgs = args.status === 'succeeded' &&
|
|
2260
|
+
(workItemKind === 'static_preview' ||
|
|
2261
|
+
workItemKind === 'refine_variant') &&
|
|
2262
|
+
args.output === undefined
|
|
2263
|
+
? {
|
|
2264
|
+
...args,
|
|
2265
|
+
output: this.readAssignedStaticArtifact(args.sessionId, args.workItemId),
|
|
2266
|
+
}
|
|
2267
|
+
: args;
|
|
2268
|
+
// Contract validation keeps internal callers that provide an explicit
|
|
2269
|
+
// static output honest. Host workers complete assigned artifacts without
|
|
2270
|
+
// transporting the HTML through this report.
|
|
2271
|
+
if (resolvedArgs.status === 'succeeded' &&
|
|
1967
2272
|
this.store.hasSession(args.sessionId) &&
|
|
1968
2273
|
(workItemKind === 'static_preview' ||
|
|
1969
2274
|
workItemKind === 'refine_variant') &&
|
|
1970
|
-
!parseStaticPreviewOutput(normalizeOutput(
|
|
2275
|
+
!parseStaticPreviewOutput(normalizeOutput(resolvedArgs.output))) {
|
|
1971
2276
|
const telemetry = this.refinementTelemetryForWorkItem({
|
|
1972
2277
|
sessionId: args.sessionId,
|
|
1973
2278
|
workItemId: args.workItemId,
|
|
@@ -1978,25 +2283,25 @@ class AgentVariantsOrchestrator {
|
|
|
1978
2283
|
if (telemetry) {
|
|
1979
2284
|
this.telemetry.trackAgentVariantsRefinementFailed?.(telemetry);
|
|
1980
2285
|
}
|
|
1981
|
-
throw new errors_1.AgentVariantsError('SCHEMA_VALIDATION_FAILED', `${workItemKind}
|
|
1982
|
-
'Pass the full HTML inline as output: { html: "<!doctype html>...", css?: "...", js?: "..." }. ' +
|
|
1983
|
-
'Do NOT write the HTML to a file and report a file list — Rivet renders the HTML inline; on-disk files are not read.');
|
|
2286
|
+
throw new errors_1.AgentVariantsError('SCHEMA_VALIDATION_FAILED', `${workItemKind} output.html must be a string and non-empty when explicit output is provided. Host workers should write the assigned index.html instead.`);
|
|
1984
2287
|
}
|
|
1985
2288
|
// QA gate: for `succeeded` static_preview reports, run preview QA
|
|
1986
2289
|
// synchronously before recording success in the store. A failed QA
|
|
1987
2290
|
// verdict converts the report to `failed` with code `VARIANT_QA_FAILED`
|
|
1988
2291
|
// so the variant never reaches `ready` and `getVariants` can disable
|
|
1989
2292
|
// its commit action.
|
|
2293
|
+
const qaStartedAt = Date.now();
|
|
1990
2294
|
const qaEval = workItemKind === 'refine_variant'
|
|
1991
2295
|
? null
|
|
1992
|
-
: await this.evaluateQaForReport(
|
|
2296
|
+
: await this.evaluateQaForReport(resolvedArgs);
|
|
2297
|
+
const qaMs = qaEval === null ? null : Date.now() - qaStartedAt;
|
|
1993
2298
|
// A `retry` verdict re-leases the variant for one regeneration (the
|
|
1994
2299
|
// existing-project diff task-fit gate) instead of recording success —
|
|
1995
2300
|
// short-circuit the normal record path.
|
|
1996
2301
|
if (qaEval?.decision === 'retry') {
|
|
1997
|
-
return this.handleQaRegenRetry(
|
|
2302
|
+
return this.handleQaRegenRetry(resolvedArgs, qaEval.qa);
|
|
1998
2303
|
}
|
|
1999
|
-
let effectiveArgs = qaEval?.decision === 'fail_terminal' ? qaEval.overrideArgs :
|
|
2304
|
+
let effectiveArgs = qaEval?.decision === 'fail_terminal' ? qaEval.overrideArgs : resolvedArgs;
|
|
2000
2305
|
// For a successful refine_variant, swap the refined static preview into
|
|
2001
2306
|
// place and persist history BEFORE recording success in the store. This
|
|
2002
2307
|
// keeps the user-visible `refinement.status: succeeded` (and the snapshot
|
|
@@ -2016,7 +2321,7 @@ class AgentVariantsOrchestrator {
|
|
|
2016
2321
|
await this.handleStaticPreviewRefinement({
|
|
2017
2322
|
sessionId: args.sessionId,
|
|
2018
2323
|
workItemId: args.workItemId,
|
|
2019
|
-
output:
|
|
2324
|
+
output: effectiveArgs.output,
|
|
2020
2325
|
});
|
|
2021
2326
|
}
|
|
2022
2327
|
catch (err) {
|
|
@@ -2062,8 +2367,15 @@ class AgentVariantsOrchestrator {
|
|
|
2062
2367
|
const approveStartedAt = resources?.approveStartedAt;
|
|
2063
2368
|
const approveToCompleteMs = approveStartedAt !== undefined ? Date.now() - approveStartedAt : null;
|
|
2064
2369
|
// Byte size of generated output for HTML vs full-stack comparison.
|
|
2065
|
-
const outputBytes = estimateOutputBytes(
|
|
2370
|
+
const outputBytes = estimateOutputBytes(effectiveArgs.output);
|
|
2066
2371
|
const projectContextKind = this.store.getProjectContext(args.sessionId).kind;
|
|
2372
|
+
// Execution mode mirrors the host work-item snapshot derivation so
|
|
2373
|
+
// prod latency can finally segment vary vs create vs refine.
|
|
2374
|
+
const mode = workItemKind !== undefined && isImplementationWorkItemKind(workItemKind)
|
|
2375
|
+
? this.store.isVaryForkWorkItem(args.sessionId, args.workItemId)
|
|
2376
|
+
? 'vary'
|
|
2377
|
+
: DEFAULT_EXECUTION_MODE_BY_KIND[workItemKind]
|
|
2378
|
+
: null;
|
|
2067
2379
|
this.telemetry.track('agent_variants.variant_completed', {
|
|
2068
2380
|
source: 'mcp',
|
|
2069
2381
|
sessionId: args.sessionId,
|
|
@@ -2072,6 +2384,12 @@ class AgentVariantsOrchestrator {
|
|
|
2072
2384
|
attempt: args.attempt,
|
|
2073
2385
|
durationMs,
|
|
2074
2386
|
approveToCompleteMs,
|
|
2387
|
+
kind: workItemKind ?? null,
|
|
2388
|
+
mode,
|
|
2389
|
+
// Phase split of `durationMs`: agent work vs server-side gating.
|
|
2390
|
+
leaseToReportMs: leasedAt !== undefined ? reportReceivedAt - leasedAt : null,
|
|
2391
|
+
reportProcessingMs: Date.now() - reportReceivedAt,
|
|
2392
|
+
qaMs,
|
|
2075
2393
|
hasError: Boolean(effectiveArgs.error),
|
|
2076
2394
|
errorCode: effectiveArgs.error?.code ?? null,
|
|
2077
2395
|
isScaffold: resources?.scaffoldBaseWorkItemId === args.workItemId,
|
|
@@ -2151,7 +2469,7 @@ class AgentVariantsOrchestrator {
|
|
|
2151
2469
|
void this.handleSucceededReport({
|
|
2152
2470
|
sessionId: args.sessionId,
|
|
2153
2471
|
workItemId: args.workItemId,
|
|
2154
|
-
output:
|
|
2472
|
+
output: effectiveArgs.output,
|
|
2155
2473
|
}).catch((err) => {
|
|
2156
2474
|
log.error(`handleSucceededReport failed for ${args.sessionId}/${args.workItemId}`, err);
|
|
2157
2475
|
});
|
|
@@ -2947,7 +3265,7 @@ class AgentVariantsOrchestrator {
|
|
|
2947
3265
|
let cursor = 0;
|
|
2948
3266
|
if (approveResult.scaffoldBaseWorkItemId) {
|
|
2949
3267
|
resources.scaffoldBaseWorkItemId = approveResult.scaffoldBaseWorkItemId;
|
|
2950
|
-
|
|
3268
|
+
await this.registerWorktreeWorkspace(resources, {
|
|
2951
3269
|
workItemId: approveResult.scaffoldBaseWorkItemId,
|
|
2952
3270
|
worktreePath: paths[cursor],
|
|
2953
3271
|
});
|
|
@@ -2956,7 +3274,10 @@ class AgentVariantsOrchestrator {
|
|
|
2956
3274
|
for (const id of approveResult.codeGenWorkItemIds) {
|
|
2957
3275
|
const p = paths[cursor];
|
|
2958
3276
|
cursor += 1;
|
|
2959
|
-
|
|
3277
|
+
await this.registerWorktreeWorkspace(resources, {
|
|
3278
|
+
workItemId: id,
|
|
3279
|
+
worktreePath: p,
|
|
3280
|
+
});
|
|
2960
3281
|
}
|
|
2961
3282
|
}
|
|
2962
3283
|
/**
|
|
@@ -3013,12 +3334,10 @@ class AgentVariantsOrchestrator {
|
|
|
3013
3334
|
resources.freshDestinationParent = destinationParent;
|
|
3014
3335
|
// Each code_gen item maps 1:1 to a fresh worktree. The scaffold_base
|
|
3015
3336
|
// work item is internal — no dedicated worktree.
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
});
|
|
3021
|
-
}
|
|
3337
|
+
await Promise.all(codeGenIds.map((workItemId, index) => this.registerWorktreeWorkspace(resources, {
|
|
3338
|
+
workItemId,
|
|
3339
|
+
worktreePath: paths[index],
|
|
3340
|
+
})));
|
|
3022
3341
|
// Copy agent-planned assets (e.g. avatar.glb) into each worktree so
|
|
3023
3342
|
// generated code can reference them via plan.referenceAs. Done before
|
|
3024
3343
|
// the background install so the assets are on disk by the time the
|
|
@@ -3418,6 +3737,12 @@ class AgentVariantsOrchestrator {
|
|
|
3418
3737
|
return;
|
|
3419
3738
|
args.record.port = undefined;
|
|
3420
3739
|
args.record.devServerProcess = undefined;
|
|
3740
|
+
this.resources
|
|
3741
|
+
.get(args.sessionId)
|
|
3742
|
+
?.previewFailures.set(args.workItemId, {
|
|
3743
|
+
reason: 'exited',
|
|
3744
|
+
portInUse: false,
|
|
3745
|
+
});
|
|
3421
3746
|
log.warn(`Variant ${args.workItemId} dev server exited unexpectedly (code=${code ?? 'null'}, signal=${signal ?? 'null'}); preview disabled`);
|
|
3422
3747
|
this.emitChange();
|
|
3423
3748
|
});
|
|
@@ -3565,6 +3890,11 @@ class AgentVariantsOrchestrator {
|
|
|
3565
3890
|
hasAssets: Boolean(resolvedAssetBase),
|
|
3566
3891
|
};
|
|
3567
3892
|
resources.staticPreviews.set(workItemId, record);
|
|
3893
|
+
const assignedWorkspace = resources.workspaceManager.getAssignedWorkspace(workItemId);
|
|
3894
|
+
if (assignedWorkspace?.kind === 'artifact_directory' &&
|
|
3895
|
+
assignedWorkspace.rootPath !== record.assetBase) {
|
|
3896
|
+
resources.workspaceManager.removeArtifactWorkspace(workItemId);
|
|
3897
|
+
}
|
|
3568
3898
|
if (this.store.getProjectContext(sessionId).kind === 'fresh') {
|
|
3569
3899
|
// History at `<workspacePath>/.rivet/variants/<sessionId>/<variantId>/`
|
|
3570
3900
|
// is the on-disk record for this generated project.
|
|
@@ -3721,6 +4051,14 @@ class AgentVariantsOrchestrator {
|
|
|
3721
4051
|
hasAssets: Boolean(resolvedAssetBase) || Boolean(previousRecord?.hasAssets),
|
|
3722
4052
|
});
|
|
3723
4053
|
}
|
|
4054
|
+
const refinementWorkspace = resources.workspaceManager.getAssignedWorkspace(args.workItemId);
|
|
4055
|
+
if (refinementWorkspace?.kind === 'artifact_directory' &&
|
|
4056
|
+
dir === refinementWorkspace.rootPath) {
|
|
4057
|
+
resources.workspaceManager.promoteArtifactWorkspace({
|
|
4058
|
+
sourceWorkItemId: args.workItemId,
|
|
4059
|
+
targetWorkItemId: input.variantId,
|
|
4060
|
+
});
|
|
4061
|
+
}
|
|
3724
4062
|
// History persistence is best-effort, mirroring handleSucceededReport's
|
|
3725
4063
|
// fire-and-forget persistence for freshly completed variants. A failure
|
|
3726
4064
|
// here (e.g. a transient filesystem race) must not fail the refinement or
|
|
@@ -4666,17 +5004,30 @@ class AgentVariantsOrchestrator {
|
|
|
4666
5004
|
const resources = this.resources.get(sessionId);
|
|
4667
5005
|
if (!resources || resources.cleanupStarted)
|
|
4668
5006
|
return;
|
|
5007
|
+
if (this.uiClientCount > 0) {
|
|
5008
|
+
this.scheduleActivePreviewIdleTimer(resources);
|
|
5009
|
+
return;
|
|
5010
|
+
}
|
|
4669
5011
|
if (resources.activePreviewIdleStop) {
|
|
4670
5012
|
await resources.activePreviewIdleStop;
|
|
4671
5013
|
return;
|
|
4672
5014
|
}
|
|
4673
5015
|
const stop = (async () => {
|
|
5016
|
+
const expiringWorkItemIds = Array.from(resources.worktrees.values())
|
|
5017
|
+
.filter((record) => Boolean(record.devServerProcess))
|
|
5018
|
+
.map((record) => record.workItemId);
|
|
4674
5019
|
const stoppedCount = await this.stopAllDevServersForSession(resources);
|
|
4675
5020
|
if (stoppedCount === 0)
|
|
4676
5021
|
return;
|
|
5022
|
+
for (const workItemId of expiringWorkItemIds) {
|
|
5023
|
+
resources.previewFailures.set(workItemId, {
|
|
5024
|
+
reason: 'idle_timeout',
|
|
5025
|
+
portInUse: false,
|
|
5026
|
+
});
|
|
5027
|
+
}
|
|
4677
5028
|
log.info(`Stopped ${stoppedCount} idle variant dev server(s) for session ${sessionId}`);
|
|
4678
|
-
//
|
|
4679
|
-
//
|
|
5029
|
+
// The next selection restores only the requested Rivet-owned preview;
|
|
5030
|
+
// siblings remain stopped until the user asks to view them.
|
|
4680
5031
|
try {
|
|
4681
5032
|
this.switchPreviewPort?.(null);
|
|
4682
5033
|
}
|
|
@@ -4848,12 +5199,13 @@ class AgentVariantsOrchestrator {
|
|
|
4848
5199
|
log.warn(`cleanupSession (worktree removal) failed for ${sessionId}`, err);
|
|
4849
5200
|
}
|
|
4850
5201
|
}
|
|
4851
|
-
//
|
|
4852
|
-
//
|
|
4853
|
-
//
|
|
4854
|
-
//
|
|
4855
|
-
|
|
4856
|
-
|
|
5202
|
+
// Assigned static workspaces are always Rivet-owned, including existing-
|
|
5203
|
+
// project refinement work staged under `.rivet/variant-work` or /tmp.
|
|
5204
|
+
// Fresh staticPreviews are also Rivet staging unless commit already moved
|
|
5205
|
+
// the selected directory into the destination.
|
|
5206
|
+
const ownedArtifactRoots = new Set(resources.workspaceManager.getOwnedArtifactRoots());
|
|
5207
|
+
resources.workspaceManager.removeOwnedArtifactWorkspaces();
|
|
5208
|
+
const staticDirectories = new Set();
|
|
4857
5209
|
if (reason !== 'committed') {
|
|
4858
5210
|
let isFresh = false;
|
|
4859
5211
|
try {
|
|
@@ -4863,16 +5215,21 @@ class AgentVariantsOrchestrator {
|
|
|
4863
5215
|
isFresh = false;
|
|
4864
5216
|
}
|
|
4865
5217
|
if (isFresh) {
|
|
4866
|
-
for (const
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
}
|
|
4870
|
-
catch (err) {
|
|
4871
|
-
log.warn(`failed to remove static preview dir ${sp.assetBase} for ${sessionId}`, err);
|
|
5218
|
+
for (const preview of resources.staticPreviews.values()) {
|
|
5219
|
+
if (!ownedArtifactRoots.has(preview.assetBase)) {
|
|
5220
|
+
staticDirectories.add(preview.assetBase);
|
|
4872
5221
|
}
|
|
4873
5222
|
}
|
|
4874
5223
|
}
|
|
4875
5224
|
}
|
|
5225
|
+
for (const staticDirectory of staticDirectories) {
|
|
5226
|
+
try {
|
|
5227
|
+
fs_1.default.rmSync(staticDirectory, { recursive: true, force: true });
|
|
5228
|
+
}
|
|
5229
|
+
catch (err) {
|
|
5230
|
+
log.warn(`failed to remove static preview dir ${staticDirectory} for ${sessionId}`, err);
|
|
5231
|
+
}
|
|
5232
|
+
}
|
|
4876
5233
|
this.resources.delete(sessionId);
|
|
4877
5234
|
}
|
|
4878
5235
|
ensureResources(sessionId) {
|
|
@@ -4882,6 +5239,7 @@ class AgentVariantsOrchestrator {
|
|
|
4882
5239
|
sessionId,
|
|
4883
5240
|
worktrees: new Map(),
|
|
4884
5241
|
staticPreviews: new Map(),
|
|
5242
|
+
workspaceManager: new VariantWorkspaceManager_1.VariantWorkspaceManager(),
|
|
4885
5243
|
cleanupStarted: false,
|
|
4886
5244
|
committedVariantIds: new Set(),
|
|
4887
5245
|
startedAt: Date.now(),
|
|
@@ -6023,12 +6381,16 @@ const defaultMaterializeProject = async (sourceWorktreePath, destinationPath) =>
|
|
|
6023
6381
|
};
|
|
6024
6382
|
/**
|
|
6025
6383
|
* Synthesize a fallback brief body for a human-direct unified start slot.
|
|
6026
|
-
* Agent callers provide structured briefs before starting.
|
|
6027
|
-
*
|
|
6028
|
-
*
|
|
6384
|
+
* Agent callers provide structured briefs before starting. Human-direct
|
|
6385
|
+
* batches get stable, slot-specific focus statements without another model
|
|
6386
|
+
* call so workers do not independently invent overlapping directions.
|
|
6029
6387
|
*/
|
|
6030
|
-
function synthesizeUnifiedBriefBody(prompt,
|
|
6388
|
+
function synthesizeUnifiedBriefBody(prompt, index, total) {
|
|
6031
6389
|
const trimmed = prompt.trim().replace(/\s+/g, ' ').slice(0, 200);
|
|
6032
|
-
|
|
6390
|
+
const intent = trimmed.length > 0 ? trimmed : 'Create this variant';
|
|
6391
|
+
if (total <= 1)
|
|
6392
|
+
return intent;
|
|
6393
|
+
const focus = FALLBACK_DIRECTION_FOCI[(Math.max(index, 1) - 1) % FALLBACK_DIRECTION_FOCI.length];
|
|
6394
|
+
return `${intent} Direction ${index} of ${total}: ${focus}`;
|
|
6033
6395
|
}
|
|
6034
6396
|
//# sourceMappingURL=WorktreeOrchestrator.js.map
|