rivet-design 0.9.2 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mcp/agent-variants/SessionStore.d.ts +78 -2
- package/dist/mcp/agent-variants/SessionStore.d.ts.map +1 -1
- package/dist/mcp/agent-variants/SessionStore.js +464 -62
- package/dist/mcp/agent-variants/SessionStore.js.map +1 -1
- package/dist/mcp/agent-variants/WorktreeOrchestrator.d.ts +331 -9
- package/dist/mcp/agent-variants/WorktreeOrchestrator.d.ts.map +1 -1
- package/dist/mcp/agent-variants/WorktreeOrchestrator.js +1985 -61
- package/dist/mcp/agent-variants/WorktreeOrchestrator.js.map +1 -1
- package/dist/mcp/agent-variants/WorktreeOrchestrator.testHelpers.d.ts +65 -0
- package/dist/mcp/agent-variants/WorktreeOrchestrator.testHelpers.d.ts.map +1 -0
- package/dist/mcp/agent-variants/WorktreeOrchestrator.testHelpers.js +162 -0
- package/dist/mcp/agent-variants/WorktreeOrchestrator.testHelpers.js.map +1 -0
- package/dist/mcp/agent-variants/contracts.d.ts +2508 -10
- package/dist/mcp/agent-variants/contracts.d.ts.map +1 -1
- package/dist/mcp/agent-variants/contracts.js +295 -5
- package/dist/mcp/agent-variants/contracts.js.map +1 -1
- package/dist/mcp/agent-variants/createProjectArtifacts.d.ts +78 -0
- package/dist/mcp/agent-variants/createProjectArtifacts.d.ts.map +1 -0
- package/dist/mcp/agent-variants/createProjectArtifacts.js +123 -0
- package/dist/mcp/agent-variants/createProjectArtifacts.js.map +1 -0
- package/dist/mcp/agent-variants/createZeroToOneTool.d.ts +241 -0
- package/dist/mcp/agent-variants/createZeroToOneTool.d.ts.map +1 -0
- package/dist/mcp/agent-variants/createZeroToOneTool.js +213 -0
- package/dist/mcp/agent-variants/createZeroToOneTool.js.map +1 -0
- package/dist/mcp/agent-variants/designContextStore.d.ts +160 -0
- package/dist/mcp/agent-variants/designContextStore.d.ts.map +1 -0
- package/dist/mcp/agent-variants/designContextStore.js +295 -0
- package/dist/mcp/agent-variants/designContextStore.js.map +1 -0
- package/dist/mcp/agent-variants/elementRefToTarget.d.ts +21 -0
- package/dist/mcp/agent-variants/elementRefToTarget.d.ts.map +1 -0
- package/dist/mcp/agent-variants/elementRefToTarget.js +47 -0
- package/dist/mcp/agent-variants/elementRefToTarget.js.map +1 -0
- package/dist/mcp/agent-variants/errors.d.ts +1 -1
- package/dist/mcp/agent-variants/errors.d.ts.map +1 -1
- package/dist/mcp/agent-variants/errors.js +7 -0
- package/dist/mcp/agent-variants/errors.js.map +1 -1
- package/dist/mcp/agent-variants/index.d.ts +4 -2
- package/dist/mcp/agent-variants/index.d.ts.map +1 -1
- package/dist/mcp/agent-variants/index.js +7 -1
- package/dist/mcp/agent-variants/index.js.map +1 -1
- package/dist/mcp/agent-variants/inspirationDesignContext.d.ts +440 -0
- package/dist/mcp/agent-variants/inspirationDesignContext.d.ts.map +1 -0
- package/dist/mcp/agent-variants/inspirationDesignContext.js +2467 -0
- package/dist/mcp/agent-variants/inspirationDesignContext.js.map +1 -0
- package/dist/mcp/agent-variants/pendingChangesAdapter.d.ts.map +1 -1
- package/dist/mcp/agent-variants/pendingChangesAdapter.js +21 -7
- package/dist/mcp/agent-variants/pendingChangesAdapter.js.map +1 -1
- package/dist/mcp/agent-variants/previewQa.d.ts +61 -0
- package/dist/mcp/agent-variants/previewQa.d.ts.map +1 -0
- package/dist/mcp/agent-variants/previewQa.js +374 -0
- package/dist/mcp/agent-variants/previewQa.js.map +1 -0
- package/dist/mcp/agent-variants/sourceContext.d.ts +8 -0
- package/dist/mcp/agent-variants/sourceContext.d.ts.map +1 -0
- package/dist/mcp/agent-variants/sourceContext.js +183 -0
- package/dist/mcp/agent-variants/sourceContext.js.map +1 -0
- package/dist/mcp/agent-variants/tools.d.ts +36 -0
- package/dist/mcp/agent-variants/tools.d.ts.map +1 -1
- package/dist/mcp/agent-variants/tools.js +451 -19
- 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 -0
- package/dist/mcp/changeBatchClassification.js +65 -0
- package/dist/mcp/changeBatchClassification.js.map +1 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +258 -41
- package/dist/mcp/server.js.map +1 -1
- package/dist/prompts/agentModPrompts.js +4 -4
- package/dist/prompts/agentModPrompts.js.map +1 -1
- package/dist/proxy-middleware/proxy-config.d.ts.map +1 -1
- package/dist/proxy-middleware/proxy-config.js +1 -15
- package/dist/proxy-middleware/proxy-config.js.map +1 -1
- package/dist/routes/agentVariants.d.ts +3 -1
- package/dist/routes/agentVariants.d.ts.map +1 -1
- package/dist/routes/agentVariants.js +138 -13
- package/dist/routes/agentVariants.js.map +1 -1
- package/dist/routes/mcp.d.ts +7 -1
- package/dist/routes/mcp.d.ts.map +1 -1
- package/dist/routes/mcp.js +139 -16
- package/dist/routes/mcp.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +23 -5
- package/dist/server.js.map +1 -1
- package/dist/services/ProjectDetectionService.d.ts.map +1 -1
- package/dist/services/ProjectDetectionService.js +9 -0
- package/dist/services/ProjectDetectionService.js.map +1 -1
- package/dist/services/SessionBridgeService.d.ts +22 -0
- package/dist/services/SessionBridgeService.d.ts.map +1 -1
- package/dist/services/SessionBridgeService.js +61 -0
- package/dist/services/SessionBridgeService.js.map +1 -1
- package/dist/services/TelemetryService.d.ts +121 -0
- package/dist/services/TelemetryService.d.ts.map +1 -1
- package/dist/services/TelemetryService.js +155 -0
- package/dist/services/TelemetryService.js.map +1 -1
- package/dist/services/WorktreeManager.d.ts +116 -6
- package/dist/services/WorktreeManager.d.ts.map +1 -1
- package/dist/services/WorktreeManager.js +394 -19
- package/dist/services/WorktreeManager.js.map +1 -1
- package/dist/services/agent/AgentModService.js +6 -6
- package/dist/services/agent/AgentModService.js.map +1 -1
- package/dist/services/templates/designCatalog.d.ts +27 -0
- package/dist/services/templates/designCatalog.d.ts.map +1 -0
- package/dist/services/templates/designCatalog.js +141 -0
- package/dist/services/templates/designCatalog.js.map +1 -0
- package/dist/services/templates/designmd/airbnb.md +545 -0
- package/dist/services/templates/designmd/airtable.md +554 -0
- package/dist/services/templates/designmd/apple.md +562 -0
- package/dist/services/templates/designmd/binance.md +634 -0
- package/dist/services/templates/designmd/bmw-m.md +503 -0
- package/dist/services/templates/designmd/bmw.md +544 -0
- package/dist/services/templates/designmd/bugatti.md +454 -0
- package/dist/services/templates/designmd/cal.md +542 -0
- package/dist/services/templates/designmd/claude.md +589 -0
- package/dist/services/templates/designmd/clay.md +541 -0
- package/dist/services/templates/designmd/cohere.md +451 -0
- package/dist/services/templates/designmd/cursor.md +537 -0
- package/dist/services/templates/designmd/expo.md +526 -0
- package/dist/services/templates/designmd/figma.md +578 -0
- package/dist/services/templates/designmd/framer.md +544 -0
- package/dist/services/templates/designmd/hp.md +670 -0
- package/dist/services/templates/designmd/linear.app.md +548 -0
- package/dist/services/templates/designmd/mintlify.md +852 -0
- package/dist/services/templates/designmd/miro.md +825 -0
- package/dist/services/templates/designmd/notion.md +821 -0
- package/dist/services/templates/designmd/raycast.md +669 -0
- package/dist/services/templates/designmd/resend.md +585 -0
- package/dist/services/templates/designmd/sentry.md +262 -0
- package/dist/services/templates/designmd/shopify.md +350 -0
- package/dist/services/templates/designmd/spotify.md +246 -0
- package/dist/services/templates/designmd/stripe.md +322 -0
- package/dist/services/templates/designmd/supabase.md +255 -0
- package/dist/services/templates/designmd/superhuman.md +252 -0
- package/dist/services/templates/designmd/uber.md +295 -0
- package/dist/services/templates/designmd/vercel.md +310 -0
- package/dist/services/templates/viteReactTs.d.ts +48 -0
- package/dist/services/templates/viteReactTs.d.ts.map +1 -0
- package/dist/services/templates/viteReactTs.js +274 -0
- package/dist/services/templates/viteReactTs.js.map +1 -0
- package/dist/types/change-request-types.d.ts +29 -3
- package/dist/types/change-request-types.d.ts.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 +19 -98
- package/dist/utils/skills/claude-skill.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 +15 -80
- package/dist/utils/skills/cursor-rules.js.map +1 -1
- package/dist/utils/skills/shared-variants-protocol.d.ts +23 -0
- package/dist/utils/skills/shared-variants-protocol.d.ts.map +1 -0
- package/dist/utils/skills/shared-variants-protocol.js +130 -0
- package/dist/utils/skills/shared-variants-protocol.js.map +1 -0
- package/package.json +6 -6
- package/src/ui/dist/assets/main-CpX7fB64.js +382 -0
- package/src/ui/dist/assets/main-Qqe2_oMT.css +1 -0
- package/src/ui/dist/index.html +2 -2
- package/src/ui/dist/assets/main-AsPCtLsx.js +0 -382
- package/src/ui/dist/assets/main-BzmseUDd.css +0 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SessionStore = exports.SCAFFOLD_LEASE_TTL_MS = exports.DEFAULT_LEASE_TTL_MS = void 0;
|
|
3
|
+
exports.SessionStore = exports.SOURCE_PLAN_OUTPUT_SCHEMA = exports.SCAFFOLD_LEASE_TTL_MS = exports.DEFAULT_LEASE_TTL_MS = void 0;
|
|
4
4
|
const crypto_1 = require("crypto");
|
|
5
5
|
const errors_1 = require("./errors");
|
|
6
6
|
const contracts_1 = require("./contracts");
|
|
7
|
+
const sourceContext_1 = require("./sourceContext");
|
|
7
8
|
const DEFAULT_VARIANT_COUNT = 4;
|
|
8
9
|
const MAX_VARIANT_COUNT = 8;
|
|
9
10
|
/** TTL for a lease on a code_gen work item. Bumped to 10min after observing
|
|
@@ -21,6 +22,7 @@ function fingerprint(parts) {
|
|
|
21
22
|
function leaseTtlForKind(kind) {
|
|
22
23
|
return kind === 'scaffold_base' ? exports.SCAFFOLD_LEASE_TTL_MS : exports.DEFAULT_LEASE_TTL_MS;
|
|
23
24
|
}
|
|
25
|
+
/** Describes the work item contract returned to an agent lease holder. */
|
|
24
26
|
function descriptorFor(item) {
|
|
25
27
|
return {
|
|
26
28
|
id: item.id,
|
|
@@ -31,18 +33,77 @@ function descriptorFor(item) {
|
|
|
31
33
|
output_schema: outputSchemaFor(item.kind),
|
|
32
34
|
};
|
|
33
35
|
}
|
|
36
|
+
exports.SOURCE_PLAN_OUTPUT_SCHEMA = {
|
|
37
|
+
sourceIntent: 'Array<{url, role: design_source|interaction_reference|asset_source|content_reference|other, confidence: low|medium|high, reason}> — agent-decided URL classification; classify by prompt intent only, no deterministic URL heuristics.',
|
|
38
|
+
sourceContext: 'sourceFindings, sectionInventory, visualObservations, sourceRoles, qualityBar, screenshotReferences?, risks?',
|
|
39
|
+
designContexts: 'Array<{url, label, markdown}> REQUIRED for every design_source URL from sourceIntent (full browser-extracted DESIGN.md).',
|
|
40
|
+
executionPlan: "{ mode: 'static_preview' | 'vite_app', confidence: low|medium|high, reason, assetPlan?: Array<{source, destination, referenceAs}>, runtimeRequirements?, userQuestion? } — choose vite_app for large local assets, model-viewer, Three.js, route structure, package deps, or files under public/; assetPlan.source must live under the approved asset root; static_preview for self-contained HTML/CSS/JS prototypes.",
|
|
41
|
+
};
|
|
42
|
+
/** Returns the completion payload shape required for each leased work item. */
|
|
34
43
|
function outputSchemaFor(kind) {
|
|
35
44
|
switch (kind) {
|
|
45
|
+
case 'source_plan':
|
|
46
|
+
return exports.SOURCE_PLAN_OUTPUT_SCHEMA;
|
|
36
47
|
case 'brief':
|
|
37
|
-
return { briefs: 'Array<{briefId, label, body}>' };
|
|
48
|
+
return { briefs: 'Array<{briefId, label, body, visualReferenceUrl?}>' };
|
|
38
49
|
case 'scaffold_base':
|
|
39
50
|
return { workspacePath: 'string', framework: 'string' };
|
|
51
|
+
case 'static_preview':
|
|
52
|
+
return { html: 'string', css: 'string?', js: 'string?' };
|
|
40
53
|
case 'code_gen':
|
|
41
54
|
return { worktreePath: 'string', changedFiles: 'string[]' };
|
|
42
55
|
case 'cleanup_runtime':
|
|
43
56
|
return { released: 'boolean' };
|
|
44
57
|
}
|
|
45
58
|
}
|
|
59
|
+
const variantDesignFromInput = (entry) => {
|
|
60
|
+
if (!entry)
|
|
61
|
+
return undefined;
|
|
62
|
+
if (entry.kind === 'slug') {
|
|
63
|
+
return {
|
|
64
|
+
kind: 'slug',
|
|
65
|
+
slug: entry.slug,
|
|
66
|
+
displayName: entry.slug,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
kind: 'markdown',
|
|
71
|
+
label: entry.label,
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
const createBriefWorkItem = (args) => ({
|
|
75
|
+
id: (0, crypto_1.randomUUID)(),
|
|
76
|
+
kind: 'brief',
|
|
77
|
+
status: 'pending',
|
|
78
|
+
attempt: 0,
|
|
79
|
+
dependsOn: [],
|
|
80
|
+
input: {
|
|
81
|
+
prompt: args.prompt,
|
|
82
|
+
count: args.count,
|
|
83
|
+
target: args.target,
|
|
84
|
+
projectContext: args.projectContext,
|
|
85
|
+
},
|
|
86
|
+
acceptedReports: [],
|
|
87
|
+
});
|
|
88
|
+
/**
|
|
89
|
+
* Source-grounded fresh sessions require a single `source_plan` planning hop
|
|
90
|
+
* before briefs. Sessions without source URLs (or with the plan already
|
|
91
|
+
* materialized via `artifact`) skip straight to `awaiting_briefs`.
|
|
92
|
+
*/
|
|
93
|
+
const requiresSourcePlan = (projectContext) => {
|
|
94
|
+
if (projectContext.kind !== 'fresh')
|
|
95
|
+
return false;
|
|
96
|
+
const sourceContext = projectContext.sourceContext;
|
|
97
|
+
if (!sourceContext)
|
|
98
|
+
return false;
|
|
99
|
+
const hasSources = (sourceContext.sourceUrls?.length ?? 0) > 0;
|
|
100
|
+
if (!hasSources)
|
|
101
|
+
return false;
|
|
102
|
+
const hasArtifact = Boolean(sourceContext.artifact);
|
|
103
|
+
const hasIntent = Boolean(sourceContext.sourceIntent);
|
|
104
|
+
const hasExecutionPlan = Boolean(projectContext.executionPlan);
|
|
105
|
+
return !(hasArtifact && hasIntent && hasExecutionPlan);
|
|
106
|
+
};
|
|
46
107
|
class SessionStore {
|
|
47
108
|
sessions = new Map();
|
|
48
109
|
clock;
|
|
@@ -57,36 +118,58 @@ class SessionStore {
|
|
|
57
118
|
}
|
|
58
119
|
const projectContext = args.projectContext ?? { kind: 'existing' };
|
|
59
120
|
const count = clampCount(args.count);
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
121
|
+
const sourceContextForPlan = projectContext.kind === 'fresh' ? projectContext.sourceContext : undefined;
|
|
122
|
+
const needsSourcePlan = requiresSourcePlan(projectContext);
|
|
123
|
+
const initialItem = needsSourcePlan
|
|
124
|
+
? {
|
|
125
|
+
id: (0, crypto_1.randomUUID)(),
|
|
126
|
+
kind: 'source_plan',
|
|
127
|
+
status: 'pending',
|
|
128
|
+
attempt: 0,
|
|
129
|
+
dependsOn: [],
|
|
130
|
+
input: {
|
|
131
|
+
prompt: args.prompt,
|
|
132
|
+
count,
|
|
133
|
+
target: args.target,
|
|
134
|
+
sourceUrls: sourceContextForPlan?.sourceUrls ?? [],
|
|
135
|
+
sourceArtifacts: sourceContextForPlan?.sourceArtifacts ?? [],
|
|
136
|
+
preserveBrand: sourceContextForPlan?.preserveBrand ?? false,
|
|
137
|
+
projectContext,
|
|
138
|
+
},
|
|
139
|
+
acceptedReports: [],
|
|
140
|
+
}
|
|
141
|
+
: createBriefWorkItem({
|
|
67
142
|
prompt: args.prompt,
|
|
68
143
|
count,
|
|
69
144
|
target: args.target,
|
|
70
145
|
projectContext,
|
|
71
|
-
}
|
|
72
|
-
acceptedReports: [],
|
|
73
|
-
};
|
|
146
|
+
});
|
|
74
147
|
const record = {
|
|
75
148
|
sessionId,
|
|
76
|
-
stage: 'awaiting_briefs',
|
|
149
|
+
stage: needsSourcePlan ? 'awaiting_source_plan' : 'awaiting_briefs',
|
|
77
150
|
prompt: args.prompt,
|
|
78
151
|
count,
|
|
79
152
|
target: args.target,
|
|
80
153
|
projectContext,
|
|
81
154
|
briefs: [],
|
|
82
155
|
approvedBriefs: [],
|
|
83
|
-
workItems: new Map([[
|
|
84
|
-
workItemOrder: [
|
|
156
|
+
workItems: new Map([[initialItem.id, initialItem]]),
|
|
157
|
+
workItemOrder: [initialItem.id],
|
|
85
158
|
acceptedFingerprints: new Map(),
|
|
86
159
|
createdAt: this.clock.now(),
|
|
87
160
|
};
|
|
88
161
|
this.sessions.set(sessionId, record);
|
|
89
|
-
return
|
|
162
|
+
return needsSourcePlan
|
|
163
|
+
? {
|
|
164
|
+
sessionId,
|
|
165
|
+
stage: 'awaiting_source_plan',
|
|
166
|
+
sourcePlanWorkItem: initialItem,
|
|
167
|
+
}
|
|
168
|
+
: {
|
|
169
|
+
sessionId,
|
|
170
|
+
stage: 'awaiting_briefs',
|
|
171
|
+
briefWorkItem: initialItem,
|
|
172
|
+
};
|
|
90
173
|
}
|
|
91
174
|
reportBriefs(args) {
|
|
92
175
|
const session = this.requireSession(args.sessionId);
|
|
@@ -122,6 +205,107 @@ class SessionStore {
|
|
|
122
205
|
session.acceptedFingerprints.set(fp, result);
|
|
123
206
|
return result;
|
|
124
207
|
}
|
|
208
|
+
reportSourcePlan(args) {
|
|
209
|
+
const session = this.requireSession(args.sessionId);
|
|
210
|
+
const fp = fingerprint([
|
|
211
|
+
'reportSourcePlan',
|
|
212
|
+
args.sessionId,
|
|
213
|
+
args.workItemId,
|
|
214
|
+
args.leaseId,
|
|
215
|
+
args.attempt,
|
|
216
|
+
'succeeded',
|
|
217
|
+
]);
|
|
218
|
+
const cached = session.acceptedFingerprints.get(fp);
|
|
219
|
+
if (cached)
|
|
220
|
+
return cached;
|
|
221
|
+
this.assertStage(session, ['awaiting_source_plan']);
|
|
222
|
+
const item = this.requireWorkItem(session, args.workItemId);
|
|
223
|
+
if (item.kind !== 'source_plan') {
|
|
224
|
+
throw new errors_1.AgentVariantsError('INVALID_STAGE_ACTION', `Work item ${args.workItemId} is not a source_plan item`);
|
|
225
|
+
}
|
|
226
|
+
this.validateLease(item, args.leaseId, args.attempt);
|
|
227
|
+
const { sourceIntent, sourceContext, designContexts, executionPlan } = args.sourcePlan;
|
|
228
|
+
// Cross-check: every source URL on the session must be classified, and
|
|
229
|
+
// every design_source URL must come with a browser-extracted DESIGN.md.
|
|
230
|
+
const sessionSourceUrls = session.projectContext.kind === 'fresh'
|
|
231
|
+
? (session.projectContext.sourceContext?.sourceUrls ?? [])
|
|
232
|
+
: [];
|
|
233
|
+
const classifiedUrls = new Set(sourceIntent.sources.map((e) => e.url));
|
|
234
|
+
const missingClassification = sessionSourceUrls.filter((url) => !classifiedUrls.has(url));
|
|
235
|
+
if (missingClassification.length > 0) {
|
|
236
|
+
throw new errors_1.AgentVariantsError('SOURCE_CONTEXT_INVALID', `source_plan missing sourceIntent classification for: ${missingClassification.join(', ')}`);
|
|
237
|
+
}
|
|
238
|
+
const designSourceUrls = sourceIntent.sources
|
|
239
|
+
.filter((entry) => entry.role === 'design_source')
|
|
240
|
+
.map((entry) => entry.url);
|
|
241
|
+
const reportedDesignUrls = new Set((designContexts ?? []).map((entry) => entry.url));
|
|
242
|
+
const missingDesignContexts = designSourceUrls.filter((url) => !reportedDesignUrls.has(url));
|
|
243
|
+
if (missingDesignContexts.length > 0) {
|
|
244
|
+
throw new errors_1.AgentVariantsError('SOURCE_CONTEXT_INVALID', `Missing browser-extracted DESIGN.md for design source URL(s): ${missingDesignContexts.join(', ')}`);
|
|
245
|
+
}
|
|
246
|
+
const normalizedArtifact = (0, sourceContext_1.normalizeSourceContextArtifact)(sourceContext);
|
|
247
|
+
if (session.projectContext.kind === 'fresh') {
|
|
248
|
+
// Persist one designContext entry PER design_source URL, aligned
|
|
249
|
+
// to the SOURCE_INTENT design_source order (NOT the agent's
|
|
250
|
+
// designContexts submission order). Brief fan-out
|
|
251
|
+
// (provisionFreshWorktrees) reads slot [min(briefIndex, length-1)]
|
|
252
|
+
// and falls back to slot 0 — a single-source session still ships
|
|
253
|
+
// one shared markdown, but a multi-source session puts each
|
|
254
|
+
// design_source's DESIGN.md at a deterministic slot regardless of
|
|
255
|
+
// how many non-design_source entries the agent classified or the
|
|
256
|
+
// order it shipped designContexts in. Without this alignment, an
|
|
257
|
+
// extra interaction_reference URL ahead of a design_source in the
|
|
258
|
+
// agent's payload would push the wrong DESIGN.md onto variant 0.
|
|
259
|
+
const designContextByUrl = new Map();
|
|
260
|
+
for (const entry of designContexts ?? []) {
|
|
261
|
+
designContextByUrl.set(entry.url, {
|
|
262
|
+
label: entry.label,
|
|
263
|
+
markdown: entry.markdown,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
const alignedDesignContexts = designSourceUrls
|
|
267
|
+
.map((url) => designContextByUrl.get(url))
|
|
268
|
+
.filter((entry) => Boolean(entry));
|
|
269
|
+
session.projectContext = {
|
|
270
|
+
...session.projectContext,
|
|
271
|
+
...(alignedDesignContexts.length > 0
|
|
272
|
+
? {
|
|
273
|
+
designContext: alignedDesignContexts.map((entry) => ({
|
|
274
|
+
kind: 'markdown',
|
|
275
|
+
label: entry.label,
|
|
276
|
+
content: entry.markdown,
|
|
277
|
+
})),
|
|
278
|
+
}
|
|
279
|
+
: {}),
|
|
280
|
+
sourceContext: {
|
|
281
|
+
...(session.projectContext.sourceContext ?? {}),
|
|
282
|
+
sourceIntent,
|
|
283
|
+
artifact: normalizedArtifact,
|
|
284
|
+
},
|
|
285
|
+
executionPlan,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
item.status = 'succeeded';
|
|
289
|
+
item.output = {
|
|
290
|
+
sourceIntent,
|
|
291
|
+
sourceContext: normalizedArtifact,
|
|
292
|
+
...(designContexts ? { designContexts } : {}),
|
|
293
|
+
executionPlan,
|
|
294
|
+
};
|
|
295
|
+
item.lease = undefined;
|
|
296
|
+
const briefItem = createBriefWorkItem({
|
|
297
|
+
prompt: session.prompt,
|
|
298
|
+
count: session.count,
|
|
299
|
+
target: session.target,
|
|
300
|
+
projectContext: session.projectContext,
|
|
301
|
+
});
|
|
302
|
+
session.workItems.set(briefItem.id, briefItem);
|
|
303
|
+
session.workItemOrder.push(briefItem.id);
|
|
304
|
+
session.stage = 'awaiting_briefs';
|
|
305
|
+
const result = { briefWorkItem: briefItem };
|
|
306
|
+
session.acceptedFingerprints.set(fp, result);
|
|
307
|
+
return result;
|
|
308
|
+
}
|
|
125
309
|
approve(args) {
|
|
126
310
|
const session = this.requireSession(args.sessionId);
|
|
127
311
|
this.assertStage(session, ['awaiting_approval']);
|
|
@@ -135,48 +319,114 @@ class SessionStore {
|
|
|
135
319
|
}
|
|
136
320
|
}
|
|
137
321
|
session.approvedBriefs = args.selections;
|
|
322
|
+
const isFresh = session.projectContext.kind === 'fresh';
|
|
323
|
+
const executionPlan = session.projectContext.kind === 'fresh'
|
|
324
|
+
? session.projectContext.executionPlan
|
|
325
|
+
: undefined;
|
|
326
|
+
// Surface an agent-flagged clarification as a hard blocker rather than
|
|
327
|
+
// silently downgrading to a static preview. The agent sets
|
|
328
|
+
// `userQuestion` when it cannot decide between static_preview and
|
|
329
|
+
// vite_app without more info from the user.
|
|
330
|
+
if (executionPlan?.userQuestion) {
|
|
331
|
+
throw new errors_1.AgentVariantsError('MISSING_REQUIRED_INPUT', executionPlan.userQuestion);
|
|
332
|
+
}
|
|
333
|
+
// Fresh sessions default to static_preview when no plan was reported
|
|
334
|
+
// (source-less zero-to-one). Source-grounded sessions carry an
|
|
335
|
+
// executionPlan from `report_source_plan`.
|
|
336
|
+
const freshMode = isFresh
|
|
337
|
+
? (executionPlan?.mode ?? 'static_preview')
|
|
338
|
+
: 'static_preview';
|
|
339
|
+
const needsViteApp = isFresh && freshMode === 'vite_app';
|
|
340
|
+
const codeGenIds = [];
|
|
341
|
+
// For fresh+vite_app sessions, code_gen items depend on a single
|
|
342
|
+
// scaffold_base item so the orchestrator can do the Vite skeleton +
|
|
343
|
+
// npm install + asset copy before the agent leases the code_gen.
|
|
138
344
|
let scaffoldBaseId;
|
|
139
|
-
if (
|
|
345
|
+
if (needsViteApp) {
|
|
346
|
+
scaffoldBaseId = (0, crypto_1.randomUUID)();
|
|
140
347
|
const scaffoldItem = {
|
|
141
|
-
id:
|
|
348
|
+
id: scaffoldBaseId,
|
|
142
349
|
kind: 'scaffold_base',
|
|
143
350
|
status: 'pending',
|
|
144
351
|
attempt: 0,
|
|
145
352
|
dependsOn: [],
|
|
146
|
-
input: {
|
|
147
|
-
workspacePath: session.projectContext.workspacePath,
|
|
148
|
-
framework: session.projectContext.framework,
|
|
149
|
-
},
|
|
353
|
+
input: { projectContext: session.projectContext },
|
|
150
354
|
acceptedReports: [],
|
|
355
|
+
internal: true,
|
|
151
356
|
};
|
|
152
357
|
session.workItems.set(scaffoldItem.id, scaffoldItem);
|
|
153
358
|
session.workItemOrder.push(scaffoldItem.id);
|
|
154
|
-
scaffoldBaseId = scaffoldItem.id;
|
|
155
|
-
}
|
|
156
|
-
const codeGenIds = [];
|
|
157
|
-
for (const sel of args.selections) {
|
|
158
|
-
const brief = session.briefs.find((b) => b.briefId === sel.briefId);
|
|
159
|
-
if (!brief)
|
|
160
|
-
continue;
|
|
161
|
-
const codeGenItem = {
|
|
162
|
-
id: (0, crypto_1.randomUUID)(),
|
|
163
|
-
kind: 'code_gen',
|
|
164
|
-
status: 'pending',
|
|
165
|
-
attempt: 0,
|
|
166
|
-
dependsOn: scaffoldBaseId ? [scaffoldBaseId] : [],
|
|
167
|
-
input: {
|
|
168
|
-
briefId: brief.briefId,
|
|
169
|
-
briefLabel: brief.label,
|
|
170
|
-
briefBody: sel.bodyOverride ?? brief.body,
|
|
171
|
-
target: session.target,
|
|
172
|
-
projectContext: session.projectContext,
|
|
173
|
-
},
|
|
174
|
-
acceptedReports: [],
|
|
175
|
-
};
|
|
176
|
-
session.workItems.set(codeGenItem.id, codeGenItem);
|
|
177
|
-
session.workItemOrder.push(codeGenItem.id);
|
|
178
|
-
codeGenIds.push(codeGenItem.id);
|
|
179
359
|
}
|
|
360
|
+
args.selections.forEach((sel) => {
|
|
361
|
+
const briefIndex = session.briefs.findIndex((b) => b.briefId === sel.briefId);
|
|
362
|
+
if (briefIndex === -1)
|
|
363
|
+
return;
|
|
364
|
+
const brief = session.briefs[briefIndex];
|
|
365
|
+
// D-VR2: Plumb the optional per-brief visualReferenceUrl into the
|
|
366
|
+
// resulting work-item input. Omitted (not nulled) when absent so the
|
|
367
|
+
// shape stays backward-compatible for callers that destructure.
|
|
368
|
+
const visualRef = brief.visualReferenceUrl
|
|
369
|
+
? { visualReferenceUrl: brief.visualReferenceUrl }
|
|
370
|
+
: {};
|
|
371
|
+
if (isFresh && !needsViteApp) {
|
|
372
|
+
// Fresh + static_preview: HTML is the deliverable. No scaffold or
|
|
373
|
+
// code_gen — the static_preview item is leased directly.
|
|
374
|
+
const staticPreviewItem = {
|
|
375
|
+
id: (0, crypto_1.randomUUID)(),
|
|
376
|
+
kind: 'static_preview',
|
|
377
|
+
status: 'pending',
|
|
378
|
+
attempt: 0,
|
|
379
|
+
dependsOn: [],
|
|
380
|
+
input: {
|
|
381
|
+
briefId: brief.briefId,
|
|
382
|
+
briefLabel: brief.label,
|
|
383
|
+
briefBody: sel.bodyOverride ?? brief.body,
|
|
384
|
+
...visualRef,
|
|
385
|
+
projectContext: session.projectContext,
|
|
386
|
+
},
|
|
387
|
+
acceptedReports: [],
|
|
388
|
+
};
|
|
389
|
+
session.workItems.set(staticPreviewItem.id, staticPreviewItem);
|
|
390
|
+
session.workItemOrder.push(staticPreviewItem.id);
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
// Existing project OR fresh+vite_app: code_gen against a worktree.
|
|
394
|
+
// Fresh+vite_app additionally depends on scaffold_base so the agent
|
|
395
|
+
// doesn't lease code_gen before the Vite skeleton lands.
|
|
396
|
+
//
|
|
397
|
+
// Per-slot designContextEntry is read by provisionFreshWorktrees to
|
|
398
|
+
// decide what DESIGN.md to write per variant. Falls back to slot 0
|
|
399
|
+
// when the session has fewer designContext entries than variants
|
|
400
|
+
// (the common case after report_source_plan ships one shared
|
|
401
|
+
// markdown).
|
|
402
|
+
const freshDesignContext = session.projectContext.kind === 'fresh'
|
|
403
|
+
? session.projectContext.designContext
|
|
404
|
+
: undefined;
|
|
405
|
+
const designContextEntry = freshDesignContext
|
|
406
|
+
? freshDesignContext[Math.min(briefIndex, freshDesignContext.length - 1)]
|
|
407
|
+
: undefined;
|
|
408
|
+
const codeGenItem = {
|
|
409
|
+
id: (0, crypto_1.randomUUID)(),
|
|
410
|
+
kind: 'code_gen',
|
|
411
|
+
status: 'pending',
|
|
412
|
+
attempt: 0,
|
|
413
|
+
dependsOn: scaffoldBaseId ? [scaffoldBaseId] : [],
|
|
414
|
+
input: {
|
|
415
|
+
briefId: brief.briefId,
|
|
416
|
+
briefLabel: brief.label,
|
|
417
|
+
briefBody: sel.bodyOverride ?? brief.body,
|
|
418
|
+
...visualRef,
|
|
419
|
+
target: session.target,
|
|
420
|
+
projectContext: session.projectContext,
|
|
421
|
+
...(designContextEntry ? { designContextEntry } : {}),
|
|
422
|
+
},
|
|
423
|
+
acceptedReports: [],
|
|
424
|
+
};
|
|
425
|
+
session.workItems.set(codeGenItem.id, codeGenItem);
|
|
426
|
+
session.workItemOrder.push(codeGenItem.id);
|
|
427
|
+
codeGenIds.push(codeGenItem.id);
|
|
428
|
+
}
|
|
429
|
+
});
|
|
180
430
|
session.stage = 'work_items_ready';
|
|
181
431
|
return {
|
|
182
432
|
approvedCount: args.selections.length,
|
|
@@ -188,7 +438,11 @@ class SessionStore {
|
|
|
188
438
|
// --- Lease management ---------------------------------------------------
|
|
189
439
|
requestWork(args) {
|
|
190
440
|
const session = this.requireSession(args.sessionId);
|
|
191
|
-
this.assertStage(session, [
|
|
441
|
+
this.assertStage(session, [
|
|
442
|
+
'awaiting_source_plan',
|
|
443
|
+
'work_items_ready',
|
|
444
|
+
'waiting_for_results',
|
|
445
|
+
]);
|
|
192
446
|
this.expireStaleLeases(session);
|
|
193
447
|
const leasable = this.leasableItems(session);
|
|
194
448
|
const limit = args.requestedLeaseCount ?? leasable.length;
|
|
@@ -210,11 +464,14 @@ class SessionStore {
|
|
|
210
464
|
};
|
|
211
465
|
item.status = 'running';
|
|
212
466
|
}
|
|
213
|
-
session.stage =
|
|
467
|
+
session.stage =
|
|
468
|
+
session.stage === 'awaiting_source_plan'
|
|
469
|
+
? 'awaiting_source_plan'
|
|
470
|
+
: 'waiting_for_results';
|
|
214
471
|
return {
|
|
215
472
|
leaseId,
|
|
216
473
|
leaseTtlMs: Math.max(...issued.map((i) => leaseTtlForKind(i.kind))),
|
|
217
|
-
leasedWorkItems: issued.map(descriptorFor),
|
|
474
|
+
leasedWorkItems: issued.map((item) => descriptorFor(item)),
|
|
218
475
|
};
|
|
219
476
|
}
|
|
220
477
|
reportComplete(args) {
|
|
@@ -275,6 +532,49 @@ class SessionStore {
|
|
|
275
532
|
session.acceptedFingerprints.set(fp, result);
|
|
276
533
|
return result;
|
|
277
534
|
}
|
|
535
|
+
/**
|
|
536
|
+
* Mark a server-owned work item as succeeded without going through the
|
|
537
|
+
* lease + agent-report path. Used for `scaffold_base` items where the
|
|
538
|
+
* orchestrator runs the scaffold + npm install in the background and needs
|
|
539
|
+
* to unblock dependent code_gen items when it's done.
|
|
540
|
+
*
|
|
541
|
+
* Bypasses lease validation because no lease was ever issued for internal
|
|
542
|
+
* items. Idempotent: completing an already-succeeded item is a no-op.
|
|
543
|
+
*/
|
|
544
|
+
completeInternal(args) {
|
|
545
|
+
const session = this.requireSession(args.sessionId);
|
|
546
|
+
const item = this.requireWorkItem(session, args.workItemId);
|
|
547
|
+
if (!item.internal) {
|
|
548
|
+
throw new errors_1.AgentVariantsError('INVALID_STAGE_ACTION', `completeInternal called on non-internal work item ${args.workItemId}`);
|
|
549
|
+
}
|
|
550
|
+
if (item.status === 'succeeded') {
|
|
551
|
+
return this.evaluateTerminal(session);
|
|
552
|
+
}
|
|
553
|
+
item.status = 'succeeded';
|
|
554
|
+
item.output = args.output;
|
|
555
|
+
item.lease = undefined;
|
|
556
|
+
return this.evaluateTerminal(session);
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Mark a server-owned work item as failed, cascading DEPENDENCY_FAILED to
|
|
560
|
+
* its dependents. Used when the orchestrator's background scaffold work
|
|
561
|
+
* (npm install, file writes) fails and we need the session to degrade.
|
|
562
|
+
*/
|
|
563
|
+
failInternal(args) {
|
|
564
|
+
const session = this.requireSession(args.sessionId);
|
|
565
|
+
const item = this.requireWorkItem(session, args.workItemId);
|
|
566
|
+
if (!item.internal) {
|
|
567
|
+
throw new errors_1.AgentVariantsError('INVALID_STAGE_ACTION', `failInternal called on non-internal work item ${args.workItemId}`);
|
|
568
|
+
}
|
|
569
|
+
if (item.status === 'succeeded' || item.status === 'failed') {
|
|
570
|
+
return this.evaluateTerminal(session);
|
|
571
|
+
}
|
|
572
|
+
item.status = 'failed';
|
|
573
|
+
item.error = args.error;
|
|
574
|
+
item.lease = undefined;
|
|
575
|
+
this.cascadeDependencyFailure(session, item.id);
|
|
576
|
+
return this.evaluateTerminal(session);
|
|
577
|
+
}
|
|
278
578
|
// --- Cancel / cleanup ---------------------------------------------------
|
|
279
579
|
cancel(args) {
|
|
280
580
|
const session = this.requireSession(args.sessionId);
|
|
@@ -294,6 +594,37 @@ class SessionStore {
|
|
|
294
594
|
// Phase 2 wires the worktree teardown.
|
|
295
595
|
return { stage: 'cancelled', cleanupStatus: 'pending' };
|
|
296
596
|
}
|
|
597
|
+
/**
|
|
598
|
+
* Cancel a single work item without tearing down its sibling variants or
|
|
599
|
+
* the session itself. Idempotent on terminal states — already-cancelled,
|
|
600
|
+
* succeeded, or failed items return their current status with
|
|
601
|
+
* `alreadyTerminal: true` and no state change. Triggers
|
|
602
|
+
* `evaluateTerminal` so the session can reach a terminal stage if this
|
|
603
|
+
* was the last in-flight item.
|
|
604
|
+
*/
|
|
605
|
+
cancelWorkItem(args) {
|
|
606
|
+
const session = this.requireSession(args.sessionId);
|
|
607
|
+
const item = this.requireWorkItem(session, args.workItemId);
|
|
608
|
+
if (item.status === 'cancelled' ||
|
|
609
|
+
item.status === 'succeeded' ||
|
|
610
|
+
item.status === 'failed') {
|
|
611
|
+
return {
|
|
612
|
+
workItemId: item.id,
|
|
613
|
+
finalStatus: item.status,
|
|
614
|
+
sessionStage: session.stage,
|
|
615
|
+
alreadyTerminal: true,
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
item.status = 'cancelled';
|
|
619
|
+
item.lease = undefined;
|
|
620
|
+
this.evaluateTerminal(session);
|
|
621
|
+
return {
|
|
622
|
+
workItemId: item.id,
|
|
623
|
+
finalStatus: 'cancelled',
|
|
624
|
+
sessionStage: session.stage,
|
|
625
|
+
alreadyTerminal: false,
|
|
626
|
+
};
|
|
627
|
+
}
|
|
297
628
|
// --- Variant pick handoff (queues to pending-changes adapter) -----------
|
|
298
629
|
recordVariantPick(args) {
|
|
299
630
|
const session = this.requireSession(args.sessionId);
|
|
@@ -324,6 +655,10 @@ class SessionStore {
|
|
|
324
655
|
const session = this.requireSession(sessionId);
|
|
325
656
|
return this.summaryOf(session);
|
|
326
657
|
}
|
|
658
|
+
getVariants(sessionId) {
|
|
659
|
+
const session = this.requireSession(sessionId);
|
|
660
|
+
return this.variantsOf(session);
|
|
661
|
+
}
|
|
327
662
|
getVariantPick(sessionId) {
|
|
328
663
|
return this.requireSession(sessionId).variantPick;
|
|
329
664
|
}
|
|
@@ -337,6 +672,11 @@ class SessionStore {
|
|
|
337
672
|
const item = this.requireWorkItem(session, workItemId);
|
|
338
673
|
return item.input;
|
|
339
674
|
}
|
|
675
|
+
getWorkItemOutput(sessionId, workItemId) {
|
|
676
|
+
const session = this.requireSession(sessionId);
|
|
677
|
+
const item = this.requireWorkItem(session, workItemId);
|
|
678
|
+
return item.output;
|
|
679
|
+
}
|
|
340
680
|
/** Read the projectContext for a session (existing | fresh). */
|
|
341
681
|
getProjectContext(sessionId) {
|
|
342
682
|
return this.requireSession(sessionId).projectContext;
|
|
@@ -392,14 +732,38 @@ class SessionStore {
|
|
|
392
732
|
const item = session.workItems.get(id);
|
|
393
733
|
if (item.kind === 'brief')
|
|
394
734
|
continue;
|
|
735
|
+
// Server-owned items never surface to external agents.
|
|
736
|
+
if (item.internal)
|
|
737
|
+
continue;
|
|
395
738
|
if (item.status !== 'pending')
|
|
396
739
|
continue;
|
|
397
740
|
if (!this.dependenciesMet(session, item))
|
|
398
741
|
continue;
|
|
742
|
+
if (item.kind === 'static_preview' &&
|
|
743
|
+
this.hasLeasableCodeGenForStaticPreview(session, item)) {
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
399
746
|
out.push(item);
|
|
400
747
|
}
|
|
401
748
|
return out;
|
|
402
749
|
}
|
|
750
|
+
hasLeasableCodeGenForStaticPreview(session, staticPreview) {
|
|
751
|
+
const staticInput = staticPreview.input;
|
|
752
|
+
if (!staticInput.briefId)
|
|
753
|
+
return false;
|
|
754
|
+
for (const candidate of session.workItems.values()) {
|
|
755
|
+
if (candidate.kind !== 'code_gen')
|
|
756
|
+
continue;
|
|
757
|
+
if (candidate.status !== 'pending')
|
|
758
|
+
continue;
|
|
759
|
+
if (!this.dependenciesMet(session, candidate))
|
|
760
|
+
continue;
|
|
761
|
+
const codeInput = candidate.input;
|
|
762
|
+
if (codeInput.briefId === staticInput.briefId)
|
|
763
|
+
return true;
|
|
764
|
+
}
|
|
765
|
+
return false;
|
|
766
|
+
}
|
|
403
767
|
dependenciesMet(session, item) {
|
|
404
768
|
for (const depId of item.dependsOn) {
|
|
405
769
|
const dep = session.workItems.get(depId);
|
|
@@ -419,9 +783,26 @@ class SessionStore {
|
|
|
419
783
|
}
|
|
420
784
|
}
|
|
421
785
|
}
|
|
786
|
+
terminalItems(session) {
|
|
787
|
+
const kind = this.variantWorkItemKind(session);
|
|
788
|
+
return [...session.workItems.values()].filter((i) => i.kind === kind);
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* The work-item kind that represents a user-facing variant for this
|
|
792
|
+
* session. Drives both progress accounting and the active variants
|
|
793
|
+
* snapshot. Fresh+vite_app sessions use `code_gen` like existing
|
|
794
|
+
* projects; fresh+static_preview (the default for source-less 0→1)
|
|
795
|
+
* keeps `static_preview` as the deliverable.
|
|
796
|
+
*/
|
|
797
|
+
variantWorkItemKind(session) {
|
|
798
|
+
if (session.projectContext.kind !== 'fresh')
|
|
799
|
+
return 'code_gen';
|
|
800
|
+
const mode = session.projectContext.executionPlan?.mode ?? 'static_preview';
|
|
801
|
+
return mode === 'vite_app' ? 'code_gen' : 'static_preview';
|
|
802
|
+
}
|
|
422
803
|
evaluateTerminal(session) {
|
|
423
|
-
const
|
|
424
|
-
const allDone =
|
|
804
|
+
const items = this.terminalItems(session);
|
|
805
|
+
const allDone = items.every((i) => i.status === 'succeeded' ||
|
|
425
806
|
i.status === 'failed' ||
|
|
426
807
|
i.status === 'cancelled');
|
|
427
808
|
if (!allDone) {
|
|
@@ -430,9 +811,9 @@ class SessionStore {
|
|
|
430
811
|
progress: this.progressOf(session),
|
|
431
812
|
};
|
|
432
813
|
}
|
|
433
|
-
const succeeded =
|
|
814
|
+
const succeeded = items.filter((i) => i.status === 'succeeded').length;
|
|
434
815
|
let terminal;
|
|
435
|
-
if (succeeded ===
|
|
816
|
+
if (succeeded === items.length)
|
|
436
817
|
terminal = 'ready';
|
|
437
818
|
else if (succeeded > 0)
|
|
438
819
|
terminal = 'degraded';
|
|
@@ -446,17 +827,22 @@ class SessionStore {
|
|
|
446
827
|
};
|
|
447
828
|
}
|
|
448
829
|
progressOf(session) {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
830
|
+
if (session.stage === 'awaiting_source_plan') {
|
|
831
|
+
const planItems = [...session.workItems.values()].filter((item) => item.kind === 'source_plan');
|
|
832
|
+
const ready = planItems.filter((item) => item.status === 'succeeded').length;
|
|
833
|
+
return { ready, total: planItems.length };
|
|
834
|
+
}
|
|
835
|
+
const items = this.terminalItems(session);
|
|
836
|
+
const ready = items.filter((i) => i.status === 'succeeded').length;
|
|
837
|
+
return { ready, total: items.length };
|
|
452
838
|
}
|
|
453
839
|
summaryOf(session) {
|
|
454
|
-
const
|
|
840
|
+
const items = this.terminalItems(session);
|
|
455
841
|
return {
|
|
456
|
-
successCount:
|
|
457
|
-
failureCount:
|
|
458
|
-
cancelledCount:
|
|
459
|
-
variants:
|
|
842
|
+
successCount: items.filter((i) => i.status === 'succeeded').length,
|
|
843
|
+
failureCount: items.filter((i) => i.status === 'failed').length,
|
|
844
|
+
cancelledCount: items.filter((i) => i.status === 'cancelled').length,
|
|
845
|
+
variants: items.map((i) => {
|
|
460
846
|
const input = i.input;
|
|
461
847
|
return {
|
|
462
848
|
workItemId: i.id,
|
|
@@ -466,6 +852,22 @@ class SessionStore {
|
|
|
466
852
|
}),
|
|
467
853
|
};
|
|
468
854
|
}
|
|
855
|
+
variantsOf(session) {
|
|
856
|
+
const kind = this.variantWorkItemKind(session);
|
|
857
|
+
return [...session.workItems.values()]
|
|
858
|
+
.filter((i) => i.kind === kind)
|
|
859
|
+
.map((item) => {
|
|
860
|
+
const input = item.input;
|
|
861
|
+
return {
|
|
862
|
+
workItemId: item.id,
|
|
863
|
+
briefId: input.briefId ?? '',
|
|
864
|
+
label: input.briefLabel ?? 'Variant',
|
|
865
|
+
status: item.status,
|
|
866
|
+
design: variantDesignFromInput(input.designContextEntry),
|
|
867
|
+
...(item.error?.code ? { errorCode: String(item.error.code) } : {}),
|
|
868
|
+
};
|
|
869
|
+
});
|
|
870
|
+
}
|
|
469
871
|
}
|
|
470
872
|
exports.SessionStore = SessionStore;
|
|
471
873
|
function clampCount(count) {
|