@agent-native/core 0.84.6 → 0.84.7
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +6 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/agent-chat-adapter.ts +74 -2
- package/corpus/templates/design/AGENTS.md +9 -7
- package/corpus/templates/design/actions/present-design-variants.ts +202 -10
- package/corpus/templates/design/app/hooks/use-question-flow.ts +2 -2
- package/corpus/templates/design/app/pages/DesignEditor.tsx +1 -1
- package/corpus/templates/design/changelog/2026-07-01-design-variants-can-now-be-generated-from-compact-directions.md +6 -0
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +63 -2
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/package.json +1 -1
package/corpus/README.md
CHANGED
package/corpus/core/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @agent-native/core
|
|
2
2
|
|
|
3
|
+
## 0.84.7
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- ab1e410: Stop repeated agent-chat action-preparation loops with a clear terminal warning, and let Design agents present compact variant directions without streaming large HTML payloads.
|
|
8
|
+
|
|
3
9
|
## 0.84.6
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/corpus/core/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.84.
|
|
3
|
+
"version": "0.84.7",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -93,6 +93,12 @@ const MAX_EMPTY_TRANSIENT_CONTINUATIONS = 3;
|
|
|
93
93
|
// round re-sending any large pasted payload) before bailing. Catching the
|
|
94
94
|
// repeat ends it in a few rounds with a clear, actionable message instead.
|
|
95
95
|
const MAX_REPEATED_TRANSIENT_CONTINUATIONS = 3;
|
|
96
|
+
// How many consecutive continuations that only reach the SAME "preparing
|
|
97
|
+
// action" activity card we tolerate before giving up. This catches runs that
|
|
98
|
+
// keep timing out while assembling a large tool payload: they are not empty,
|
|
99
|
+
// and the narration may vary enough to bypass the text-repeat guard, but the
|
|
100
|
+
// real tool never starts.
|
|
101
|
+
const MAX_REPEATED_ACTION_PREPARATION_CONTINUATIONS = 3;
|
|
96
102
|
const RETRY_BASE_DELAY_MS = 500;
|
|
97
103
|
const RETRY_MAX_DELAY_MS = 8_000;
|
|
98
104
|
const MAX_HISTORY_ATTACHMENT_CHARS = 60_000;
|
|
@@ -782,6 +788,22 @@ function lastActivityTool(
|
|
|
782
788
|
return undefined;
|
|
783
789
|
}
|
|
784
790
|
|
|
791
|
+
function lastUnresolvedToolActivity(
|
|
792
|
+
content: ContentPart[],
|
|
793
|
+
): string | undefined {
|
|
794
|
+
for (let i = content.length - 1; i >= 0; i--) {
|
|
795
|
+
const part = content[i];
|
|
796
|
+
if (
|
|
797
|
+
part.type === "tool-call" &&
|
|
798
|
+
part.activity === true &&
|
|
799
|
+
part.result === undefined
|
|
800
|
+
) {
|
|
801
|
+
return part.toolName;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
return undefined;
|
|
805
|
+
}
|
|
806
|
+
|
|
785
807
|
function snapshotContent(content: ContentPart[]): ContentPart[] {
|
|
786
808
|
return content.map((part) =>
|
|
787
809
|
part.type === "text" ? { ...part } : { ...part, args: { ...part.args } },
|
|
@@ -889,7 +911,7 @@ function incrementalActionGuidance(tool: string): string | undefined {
|
|
|
889
911
|
case "update-design":
|
|
890
912
|
return "persist a minimal first version (fewer files) with `generate-design`, then refine individual files with `edit-design` search/replace instead of resending everything";
|
|
891
913
|
case "present-design-variants":
|
|
892
|
-
return "
|
|
914
|
+
return "call `present-design-variants` with concise labels, descriptions, accent colors, and feature bullets; omit large `content` HTML when needed so the action can render compact representative screens, then refine the chosen direction with `generate-design` or `edit-design`";
|
|
893
915
|
case "create-visual-plan":
|
|
894
916
|
case "create-ui-plan":
|
|
895
917
|
case "create-plan-design":
|
|
@@ -1390,6 +1412,9 @@ export function createAgentChatAdapter(
|
|
|
1390
1412
|
let repeatedInFlightToolCount = 0;
|
|
1391
1413
|
let recoveryGaveUpOnInFlightTool = false;
|
|
1392
1414
|
const MAX_REPEATED_INFLIGHT_TOOL_STALLS = 3;
|
|
1415
|
+
let lastPreparingToolName: string | undefined;
|
|
1416
|
+
let repeatedActionPreparationCount = 0;
|
|
1417
|
+
let recoveryGaveUpOnActionPreparation = false;
|
|
1393
1418
|
const continuationHistoryFragments: string[] = [];
|
|
1394
1419
|
const structuredContinuationFragments: AgentChatStructuredMessage[] = [];
|
|
1395
1420
|
let visibleContinuationPrefix: ContentPart[] = [];
|
|
@@ -1420,6 +1445,10 @@ export function createAgentChatAdapter(
|
|
|
1420
1445
|
lastInFlightToolName
|
|
1421
1446
|
? `last_inflight_tool: ${lastInFlightToolName}`
|
|
1422
1447
|
: "",
|
|
1448
|
+
`repeated_action_preparation_stalls: ${repeatedActionPreparationCount}`,
|
|
1449
|
+
lastPreparingToolName
|
|
1450
|
+
? `last_preparing_tool: ${lastPreparingToolName}`
|
|
1451
|
+
: "",
|
|
1423
1452
|
`total_transient_continuations: ${totalTransientContinuationAttempts}`,
|
|
1424
1453
|
attemptedRunIds.length > 0
|
|
1425
1454
|
? `attempted_runs: ${attemptedRunIds.join(", ")}`
|
|
@@ -1436,6 +1465,12 @@ export function createAgentChatAdapter(
|
|
|
1436
1465
|
if (recoveryGaveUpOnRepetition) {
|
|
1437
1466
|
return "The agent got stuck repeating the same response without finishing, so I stopped the automatic retries. This often happens when it tries to re-type a large pasted file into one action — starting a new chat, or asking for a smaller first step, usually gets it unstuck.";
|
|
1438
1467
|
}
|
|
1468
|
+
if (recoveryGaveUpOnActionPreparation) {
|
|
1469
|
+
const tool = lastPreparingToolName
|
|
1470
|
+
? ` the ${humanizeActionName(lastPreparingToolName)} action`
|
|
1471
|
+
: " the same action";
|
|
1472
|
+
return `The agent got stuck preparing${tool} input and never started the tool, so I stopped the automatic retries. Try a smaller first step or a more compact version of the request.`;
|
|
1473
|
+
}
|
|
1439
1474
|
if (
|
|
1440
1475
|
content.length === 0 &&
|
|
1441
1476
|
(reason === "run_timeout" ||
|
|
@@ -1512,6 +1547,8 @@ export function createAgentChatAdapter(
|
|
|
1512
1547
|
repeatedTransientContinuationAttempts,
|
|
1513
1548
|
repeatedInFlightToolCount,
|
|
1514
1549
|
lastInFlightToolName,
|
|
1550
|
+
repeatedActionPreparationCount,
|
|
1551
|
+
lastPreparingToolName,
|
|
1515
1552
|
totalTransientContinuationAttempts,
|
|
1516
1553
|
...extra,
|
|
1517
1554
|
},
|
|
@@ -1529,6 +1566,8 @@ export function createAgentChatAdapter(
|
|
|
1529
1566
|
repeatedTransientContinuationAttempts,
|
|
1530
1567
|
repeatedInFlightToolCount,
|
|
1531
1568
|
lastInFlightToolName,
|
|
1569
|
+
repeatedActionPreparationCount,
|
|
1570
|
+
lastPreparingToolName,
|
|
1532
1571
|
totalTransientContinuationAttempts,
|
|
1533
1572
|
},
|
|
1534
1573
|
},
|
|
@@ -1832,8 +1871,13 @@ export function createAgentChatAdapter(
|
|
|
1832
1871
|
// for the stalled/empty caps.
|
|
1833
1872
|
const madeProgress = madeContentProgress || hasInFlightTool;
|
|
1834
1873
|
const madeDurableToolProgress = visibleContent.some(
|
|
1835
|
-
(part) =>
|
|
1874
|
+
(part) =>
|
|
1875
|
+
part.type === "tool-call" &&
|
|
1876
|
+
part.activity !== true &&
|
|
1877
|
+
part.result !== undefined,
|
|
1836
1878
|
);
|
|
1879
|
+
const currentPreparingToolName =
|
|
1880
|
+
lastUnresolvedToolActivity(visibleContent);
|
|
1837
1881
|
// In-flight tool stall guard. When the same write tool is stuck
|
|
1838
1882
|
// in-flight because the connection keeps dropping (stream_ended),
|
|
1839
1883
|
// hasInFlightTool=true keeps madeProgress=true and completely
|
|
@@ -1877,6 +1921,27 @@ export function createAgentChatAdapter(
|
|
|
1877
1921
|
repeatedInFlightToolCount = 0;
|
|
1878
1922
|
}
|
|
1879
1923
|
|
|
1924
|
+
const isRepeatedActionPreparationCandidate =
|
|
1925
|
+
signal.reason !== "loop_limit" &&
|
|
1926
|
+
currentPreparingToolName !== undefined &&
|
|
1927
|
+
!hasInFlightTool &&
|
|
1928
|
+
!madeDurableToolProgress;
|
|
1929
|
+
if (isRepeatedActionPreparationCandidate) {
|
|
1930
|
+
if (currentPreparingToolName === lastPreparingToolName) {
|
|
1931
|
+
repeatedActionPreparationCount += 1;
|
|
1932
|
+
} else {
|
|
1933
|
+
repeatedActionPreparationCount = 0;
|
|
1934
|
+
lastPreparingToolName = currentPreparingToolName;
|
|
1935
|
+
}
|
|
1936
|
+
} else if (
|
|
1937
|
+
!currentPreparingToolName ||
|
|
1938
|
+
hasInFlightTool ||
|
|
1939
|
+
madeDurableToolProgress
|
|
1940
|
+
) {
|
|
1941
|
+
repeatedActionPreparationCount = 0;
|
|
1942
|
+
lastPreparingToolName = undefined;
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1880
1945
|
// Degenerate repetition guard. When the model gets stuck re-streaming
|
|
1881
1946
|
// the SAME narration every continuation without ever starting or
|
|
1882
1947
|
// finishing a tool, each round is "new" text — so madeProgress stays
|
|
@@ -1925,6 +1990,13 @@ export function createAgentChatAdapter(
|
|
|
1925
1990
|
recoveryGaveUpOnInFlightTool = true;
|
|
1926
1991
|
return { ok: false, resetVisibleContent: false };
|
|
1927
1992
|
}
|
|
1993
|
+
if (
|
|
1994
|
+
repeatedActionPreparationCount >
|
|
1995
|
+
MAX_REPEATED_ACTION_PREPARATION_CONTINUATIONS
|
|
1996
|
+
) {
|
|
1997
|
+
recoveryGaveUpOnActionPreparation = true;
|
|
1998
|
+
return { ok: false, resetVisibleContent: false };
|
|
1999
|
+
}
|
|
1928
2000
|
// Bail fast on a non-advancing repetition loop, well before the
|
|
1929
2001
|
// stalled/empty/total budgets would (each round otherwise re-sends
|
|
1930
2002
|
// the whole pasted payload). Tracked separately so it never trips
|
|
@@ -133,9 +133,11 @@ patterns live in `.agents/skills/`.
|
|
|
133
133
|
write-back remains a localhost/fusion follow-up capability.
|
|
134
134
|
- For multi-variant work, use `present-design-variants` so every candidate is
|
|
135
135
|
saved as a normal overview-board screen and the user gets one inline chat
|
|
136
|
-
button per screen name. Keep each variant compact:
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
button per screen name. Keep each variant compact: prefer concise labels,
|
|
137
|
+
descriptions, accent colors, and feature bullets, and omit full HTML when it
|
|
138
|
+
would make the tool input too large. The action can render representative
|
|
139
|
+
screens from direction data. After the user picks, delete the unchosen
|
|
140
|
+
variant screens before continuing from the kept screen.
|
|
139
141
|
- Use framework sharing actions for design and design-system visibility/grants.
|
|
140
142
|
- `/visual-edit` is a public entry route and public `/design/:id` links may
|
|
141
143
|
render read-only public designs without a session. Do not open anonymous write
|
|
@@ -218,10 +220,10 @@ patterns live in `.agents/skills/`.
|
|
|
218
220
|
register that manifest with `connect-localhost`, call `add-localhost-screens`,
|
|
219
221
|
and open the editor in overview mode.
|
|
220
222
|
- For human-in-the-loop UI exploration, create a design shell, call
|
|
221
|
-
`present-design-variants` with 2-5
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
223
|
+
`present-design-variants` with 2-5 concise directions (three by default),
|
|
224
|
+
wait for the user to pick one in chat, delete the other generated variant
|
|
225
|
+
screens with `delete-file`, then use `get-design-snapshot` and
|
|
226
|
+
`generate-design` or `edit-design` for follow-up refinements.
|
|
225
227
|
- If inline chat choice buttons are unavailable, the user can tell you the
|
|
226
228
|
preferred screen name. Do not show a separate variant picker or ask them to
|
|
227
229
|
paste a copyable handoff summary.
|
|
@@ -48,11 +48,26 @@ const variantSchema = z.object({
|
|
|
48
48
|
.string()
|
|
49
49
|
.min(1)
|
|
50
50
|
.describe("Short user-facing screen name, e.g. 'One-Line Focus'"),
|
|
51
|
+
description: z
|
|
52
|
+
.string()
|
|
53
|
+
.optional()
|
|
54
|
+
.describe(
|
|
55
|
+
"Short visual direction summary. Use this instead of a huge HTML payload when exploring variants quickly.",
|
|
56
|
+
),
|
|
57
|
+
accentColor: z
|
|
58
|
+
.string()
|
|
59
|
+
.optional()
|
|
60
|
+
.describe("Optional CSS color used as this variant's primary accent."),
|
|
61
|
+
features: z
|
|
62
|
+
.array(z.string())
|
|
63
|
+
.max(8)
|
|
64
|
+
.optional()
|
|
65
|
+
.describe("Optional short feature/polish bullets to show in the variant."),
|
|
51
66
|
content: z
|
|
52
67
|
.string()
|
|
53
|
-
.
|
|
68
|
+
.optional()
|
|
54
69
|
.describe(
|
|
55
|
-
"
|
|
70
|
+
"Optional complete self-contained HTML document for this variant. Keep it compact: one representative screen or directional snapshot, not a full multi-screen app. For faster exploration, omit this and provide label/description/features; Design will generate a compact representative screen.",
|
|
56
71
|
),
|
|
57
72
|
width: z
|
|
58
73
|
.number()
|
|
@@ -137,14 +152,17 @@ function boundedDimension(value: unknown, min: number, max: number) {
|
|
|
137
152
|
: undefined;
|
|
138
153
|
}
|
|
139
154
|
|
|
140
|
-
function inferVariantSize(
|
|
155
|
+
function inferVariantSize(
|
|
156
|
+
variant: z.infer<typeof variantSchema>,
|
|
157
|
+
prompt?: string,
|
|
158
|
+
) {
|
|
141
159
|
const explicitWidth = boundedDimension(variant.width, 240, 1920);
|
|
142
160
|
const explicitHeight = boundedDimension(variant.height, 240, 3000);
|
|
143
161
|
if (explicitWidth && explicitHeight) {
|
|
144
162
|
return { width: explicitWidth, height: explicitHeight };
|
|
145
163
|
}
|
|
146
164
|
|
|
147
|
-
const content = variant.content;
|
|
165
|
+
const content = variant.content ?? "";
|
|
148
166
|
const cssWidth =
|
|
149
167
|
firstCssPixelValue(content, "width") ??
|
|
150
168
|
firstCssPixelValue(content, "max-width") ??
|
|
@@ -167,7 +185,15 @@ function inferVariantSize(variant: z.infer<typeof variantSchema>) {
|
|
|
167
185
|
};
|
|
168
186
|
}
|
|
169
187
|
|
|
170
|
-
const lowercase =
|
|
188
|
+
const lowercase = [
|
|
189
|
+
content,
|
|
190
|
+
variant.label,
|
|
191
|
+
variant.description ?? "",
|
|
192
|
+
...(variant.features ?? []),
|
|
193
|
+
prompt ?? "",
|
|
194
|
+
]
|
|
195
|
+
.join(" ")
|
|
196
|
+
.toLowerCase();
|
|
171
197
|
if (
|
|
172
198
|
/\b(?:mobile|phone|iphone|android)\b/.test(lowercase) ||
|
|
173
199
|
/\b(?:max-w-sm|max-w-md|w-\[(?:360|375|390|393|414)px\])\b/.test(lowercase)
|
|
@@ -190,6 +216,163 @@ function inferVariantSize(variant: z.infer<typeof variantSchema>) {
|
|
|
190
216
|
};
|
|
191
217
|
}
|
|
192
218
|
|
|
219
|
+
function escapeHtml(value: string) {
|
|
220
|
+
return value
|
|
221
|
+
.replace(/&/g, "&")
|
|
222
|
+
.replace(/</g, "<")
|
|
223
|
+
.replace(/>/g, ">")
|
|
224
|
+
.replace(/"/g, """);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function colorForVariant(
|
|
228
|
+
variant: z.infer<typeof variantSchema>,
|
|
229
|
+
index: number,
|
|
230
|
+
) {
|
|
231
|
+
const provided = variant.accentColor?.trim();
|
|
232
|
+
if (provided) return provided;
|
|
233
|
+
return ["#8b5cf6", "#06b6d4", "#10b981", "#f43f5e", "#f59e0b"][index % 5]!;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function fallbackVariantContent(
|
|
237
|
+
variant: z.infer<typeof variantSchema>,
|
|
238
|
+
index: number,
|
|
239
|
+
prompt?: string,
|
|
240
|
+
size: { width: number; height: number } = {
|
|
241
|
+
width: DESKTOP_WIDTH,
|
|
242
|
+
height: DESKTOP_HEIGHT,
|
|
243
|
+
},
|
|
244
|
+
) {
|
|
245
|
+
const label = escapeHtml(variant.label.trim() || optionName(index));
|
|
246
|
+
const description = escapeHtml(
|
|
247
|
+
variant.description?.trim() ||
|
|
248
|
+
"A compact dark-mode product direction with a clear primary workflow, crisp hierarchy, and fast keyboard-first flow.",
|
|
249
|
+
);
|
|
250
|
+
const sourcePrompt = escapeHtml(
|
|
251
|
+
prompt?.trim() ||
|
|
252
|
+
"Generated app interface direction with a polished workflow and production-ready interaction model.",
|
|
253
|
+
);
|
|
254
|
+
const accent = escapeHtml(colorForVariant(variant, index));
|
|
255
|
+
const features =
|
|
256
|
+
variant.features && variant.features.length > 0
|
|
257
|
+
? variant.features.slice(0, 6)
|
|
258
|
+
: [
|
|
259
|
+
"Primary workflow",
|
|
260
|
+
"Fast capture",
|
|
261
|
+
"Structured details",
|
|
262
|
+
"Status tracking",
|
|
263
|
+
"Inline editing",
|
|
264
|
+
"Shortcut hints",
|
|
265
|
+
];
|
|
266
|
+
const safeFeatures = features.map((feature) => escapeHtml(feature));
|
|
267
|
+
const cardTitles = [
|
|
268
|
+
safeFeatures[0] ?? "Primary workflow",
|
|
269
|
+
safeFeatures[1] ?? "Structured details",
|
|
270
|
+
safeFeatures[2] ?? "Polished interactions",
|
|
271
|
+
safeFeatures[3] ?? "Status tracking",
|
|
272
|
+
safeFeatures[4] ?? "Review flow",
|
|
273
|
+
];
|
|
274
|
+
const density =
|
|
275
|
+
index % 3 === 0 ? "spacious" : index % 3 === 1 ? "glass" : "dense";
|
|
276
|
+
const screenWidth = Math.round(size.width);
|
|
277
|
+
const screenHeight = Math.round(size.height);
|
|
278
|
+
const compact = screenWidth <= 560;
|
|
279
|
+
const tablet = screenWidth > 560 && screenWidth <= 900;
|
|
280
|
+
|
|
281
|
+
return `<!doctype html>
|
|
282
|
+
<html lang="en">
|
|
283
|
+
<head>
|
|
284
|
+
<meta charset="utf-8" />
|
|
285
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
286
|
+
<title>${label}</title>
|
|
287
|
+
<style>
|
|
288
|
+
:root { color-scheme: dark; --accent: ${accent}; --bg: #080a0f; --panel: rgba(18, 22, 33, 0.82); --line: rgba(255,255,255,.11); --muted: #94a3b8; }
|
|
289
|
+
* { box-sizing: border-box; }
|
|
290
|
+
body { margin: 0; width: ${screenWidth}px; min-height: ${screenHeight}px; overflow: hidden; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; color: #f8fafc; background:
|
|
291
|
+
radial-gradient(circle at 18% 8%, color-mix(in srgb, var(--accent) 42%, transparent), transparent 30%),
|
|
292
|
+
linear-gradient(140deg, #05070b 0%, #111827 48%, #05070b 100%); }
|
|
293
|
+
.shell { width: ${screenWidth}px; min-height: ${screenHeight}px; padding: ${compact ? "18" : "34"}px; display: grid; grid-template-columns: ${compact ? "1fr" : tablet ? "220px 1fr" : "258px 1fr 304px"}; gap: ${compact ? "14" : "22"}px; }
|
|
294
|
+
.panel { border: 1px solid var(--line); background: var(--panel); border-radius: ${density === "dense" ? "14" : "22"}px; box-shadow: 0 24px 80px rgba(0,0,0,.35); backdrop-filter: blur(${density === "glass" ? "26" : "10"}px); }
|
|
295
|
+
.sidebar { padding: 22px; display: flex; flex-direction: column; gap: 18px; }
|
|
296
|
+
.brand { display:flex; align-items:center; justify-content:space-between; gap:12px; }
|
|
297
|
+
.mark { width: 40px; height: 40px; border-radius: 14px; background: var(--accent); box-shadow: 0 0 32px color-mix(in srgb, var(--accent) 58%, transparent); display:grid; place-items:center; font-weight:800; color:#05070b; }
|
|
298
|
+
h1 { margin: 0; font-size: 32px; line-height: 1.05; letter-spacing: 0; }
|
|
299
|
+
h2 { margin: 0; font-size: 16px; letter-spacing: 0; }
|
|
300
|
+
p { margin: 0; color: var(--muted); line-height: 1.5; }
|
|
301
|
+
.nav, .tasks, .right { display: grid; gap: 12px; }
|
|
302
|
+
.nav div, .task, .metric, .calendar { border: 1px solid var(--line); border-radius: 14px; background: rgba(255,255,255,.045); padding: 13px 14px; }
|
|
303
|
+
.nav div:first-child { color: #fff; background: color-mix(in srgb, var(--accent) 18%, rgba(255,255,255,.06)); border-color: color-mix(in srgb, var(--accent) 48%, var(--line)); }
|
|
304
|
+
.main { padding: 24px; display:flex; flex-direction:column; gap:18px; }
|
|
305
|
+
.top { display:flex; align-items:flex-start; justify-content:space-between; gap:18px; }
|
|
306
|
+
.badge { border:1px solid color-mix(in srgb, var(--accent) 45%, var(--line)); color:#fff; background: color-mix(in srgb, var(--accent) 20%, transparent); padding:8px 11px; border-radius:999px; font-size:12px; }
|
|
307
|
+
.board { display:grid; grid-template-columns: ${compact ? "1fr" : "repeat(3, 1fr)"}; gap:14px; flex:1; min-height:0; }
|
|
308
|
+
.column { border:1px solid var(--line); border-radius:18px; background:rgba(255,255,255,.035); padding:14px; display:flex; flex-direction:column; gap:12px; }
|
|
309
|
+
.column header { display:flex; justify-content:space-between; align-items:center; color:#cbd5e1; font-size:13px; }
|
|
310
|
+
.task { display:grid; gap:10px; padding:14px; }
|
|
311
|
+
.task strong { font-size:14px; line-height:1.25; }
|
|
312
|
+
.meta { display:flex; flex-wrap:wrap; gap:7px; }
|
|
313
|
+
.chip { font-size:11px; color:#dbeafe; border:1px solid rgba(255,255,255,.12); background:rgba(255,255,255,.055); border-radius:999px; padding:5px 8px; }
|
|
314
|
+
.priority { color:#fff; background: color-mix(in srgb, var(--accent) 22%, rgba(255,255,255,.06)); border-color: color-mix(in srgb, var(--accent) 46%, var(--line)); }
|
|
315
|
+
.right { padding:22px; align-content:start; }
|
|
316
|
+
.metric { display:grid; gap:8px; }
|
|
317
|
+
.metric b { font-size:28px; }
|
|
318
|
+
.calendar { display:grid; grid-template-columns: repeat(7, 1fr); gap:7px; }
|
|
319
|
+
.calendar span { display:grid; place-items:center; height:32px; border-radius:10px; color:#cbd5e1; background:rgba(255,255,255,.04); font-size:12px; }
|
|
320
|
+
.calendar .hot { color:#05070b; background:var(--accent); font-weight:800; }
|
|
321
|
+
.features { display:flex; flex-wrap:wrap; gap:8px; }
|
|
322
|
+
.shortcut { margin-top:auto; border-top:1px solid var(--line); padding-top:14px; display:flex; justify-content:space-between; gap:10px; color:#cbd5e1; font-size:12px; }
|
|
323
|
+
${tablet ? ".right { display: none; }" : ""}
|
|
324
|
+
${compact ? ".sidebar { padding: 16px; } .nav { grid-template-columns: repeat(2, minmax(0, 1fr)); } .nav div { padding: 10px; } .main { padding: 18px; } .top { display: grid; } .top .badge { width: fit-content; } h1 { font-size: 26px; } .right { display: none; } .column:nth-child(n+3) { display: none; }" : ""}
|
|
325
|
+
</style>
|
|
326
|
+
</head>
|
|
327
|
+
<body>
|
|
328
|
+
<main class="shell">
|
|
329
|
+
<aside class="panel sidebar">
|
|
330
|
+
<div class="brand"><div class="mark">${escapeHtml(String.fromCharCode(65 + index))}</div><span class="badge">${label}</span></div>
|
|
331
|
+
<div>
|
|
332
|
+
<h2>Direction</h2>
|
|
333
|
+
<p>${description}</p>
|
|
334
|
+
</div>
|
|
335
|
+
<div class="nav">
|
|
336
|
+
<div>Overview</div><div>Primary flow</div><div>Details</div><div>Timeline</div><div>Output</div>
|
|
337
|
+
</div>
|
|
338
|
+
<div class="features">${safeFeatures.map((feature) => `<span class="chip">${feature}</span>`).join("")}</div>
|
|
339
|
+
<div class="shortcut"><span>⌘K command</span><span>G then B</span></div>
|
|
340
|
+
</aside>
|
|
341
|
+
<section class="panel main">
|
|
342
|
+
<div class="top">
|
|
343
|
+
<div><h1>${label}</h1><p>${sourcePrompt}</p></div>
|
|
344
|
+
<span class="badge">${compact ? "Mobile" : tablet ? "Tablet" : "Desktop"} concept · live data</span>
|
|
345
|
+
</div>
|
|
346
|
+
<div class="board">
|
|
347
|
+
<section class="column"><header><span>Focus</span><b>4</b></header>
|
|
348
|
+
<article class="task"><strong>${cardTitles[0]}</strong><div class="meta"><span class="chip priority">Primary</span><span class="chip">Now</span><span class="chip">Fast path</span></div></article>
|
|
349
|
+
<article class="task"><strong>${cardTitles[1]}</strong><div class="meta"><span class="chip">Detail view</span><span class="chip">Shortcut E</span></div></article>
|
|
350
|
+
</section>
|
|
351
|
+
<section class="column"><header><span>Build</span><b>6</b></header>
|
|
352
|
+
<article class="task"><strong>${cardTitles[2]}</strong><div class="meta"><span class="chip priority">P2</span><span class="chip">Flow</span></div></article>
|
|
353
|
+
<article class="task"><strong>${cardTitles[3]}</strong><div class="meta"><span class="chip">Inline edit</span><span class="chip">Next</span></div></article>
|
|
354
|
+
</section>
|
|
355
|
+
<section class="column"><header><span>Ready</span><b>12</b></header>
|
|
356
|
+
<article class="task"><strong>${cardTitles[4]}</strong><div class="meta"><span class="chip">Complete</span><span class="chip">Motion ready</span></div></article>
|
|
357
|
+
</section>
|
|
358
|
+
</div>
|
|
359
|
+
</section>
|
|
360
|
+
<aside class="panel right">
|
|
361
|
+
<h2>Progress</h2>
|
|
362
|
+
<div class="metric"><p>Current flow</p><b>68%</b><p>Representative state for this direction</p></div>
|
|
363
|
+
<h2>Timeline</h2>
|
|
364
|
+
<div class="calendar">${Array.from({ length: 14 }, (_, day) => `<span class="${day === 4 || day === 9 ? "hot" : ""}">${day + 1}</span>`).join("")}</div>
|
|
365
|
+
<h2>Key moments</h2>
|
|
366
|
+
<div class="tasks">
|
|
367
|
+
<div class="task"><strong>${safeFeatures[0] ?? "Primary workflow"}</strong><div class="meta"><span class="chip priority">Hero</span><span class="chip">45m</span></div></div>
|
|
368
|
+
<div class="task"><strong>${safeFeatures[1] ?? "Polished interaction"}</strong><div class="meta"><span class="chip">Motion</span><span class="chip">⌘ Enter</span></div></div>
|
|
369
|
+
</div>
|
|
370
|
+
</aside>
|
|
371
|
+
</main>
|
|
372
|
+
</body>
|
|
373
|
+
</html>`;
|
|
374
|
+
}
|
|
375
|
+
|
|
193
376
|
function placeVariantScreens(screens: VariantScreen[]) {
|
|
194
377
|
const placements: CanvasFramePlacement[] = [];
|
|
195
378
|
const columns = Math.min(MAX_COLUMNS, Math.max(1, screens.length));
|
|
@@ -228,7 +411,9 @@ export default defineAction({
|
|
|
228
411
|
"exploration before follow-up refinement. After the user's choice, keep " +
|
|
229
412
|
"the chosen screen, delete the other generated variant screens, and " +
|
|
230
413
|
"continue from the kept screen. For complex apps, make each variant a " +
|
|
231
|
-
"compact
|
|
414
|
+
"compact representative screen; pass concise labels/descriptions/features " +
|
|
415
|
+
"and omit content when full HTML would be too large. Design will render " +
|
|
416
|
+
"compact screens from the direction data. Expand the chosen direction " +
|
|
232
417
|
"after the user picks.",
|
|
233
418
|
schema: z.object({
|
|
234
419
|
designId: z.string().describe("Design project ID to show variants for"),
|
|
@@ -241,7 +426,7 @@ export default defineAction({
|
|
|
241
426
|
.min(2)
|
|
242
427
|
.max(5)
|
|
243
428
|
.describe(
|
|
244
|
-
"2-5 concise, visually distinct generated design options to place as overview screens (3 is the sweet spot).
|
|
429
|
+
"2-5 concise, visually distinct generated design options to place as overview screens (3 is the sweet spot). Prefer short label/description/features for each direction; include inline HTML content only when it is compact enough to finish.",
|
|
245
430
|
),
|
|
246
431
|
}),
|
|
247
432
|
mcpApp: {
|
|
@@ -280,18 +465,25 @@ export default defineAction({
|
|
|
280
465
|
const preferredFilename = `variant-${slug}.html`;
|
|
281
466
|
const filename = uniqueFilename(preferredFilename, usedFilenames);
|
|
282
467
|
const fileId = nanoid();
|
|
283
|
-
const
|
|
468
|
+
const providedContent = variant.content?.trim();
|
|
469
|
+
const initialSize = inferVariantSize(variant, prompt);
|
|
470
|
+
const content =
|
|
471
|
+
providedContent ||
|
|
472
|
+
fallbackVariantContent(variant, index, prompt, initialSize);
|
|
473
|
+
const { width, height } = providedContent
|
|
474
|
+
? inferVariantSize({ ...variant, content })
|
|
475
|
+
: initialSize;
|
|
284
476
|
|
|
285
477
|
await db.insert(schema.designFiles).values({
|
|
286
478
|
id: fileId,
|
|
287
479
|
designId,
|
|
288
480
|
filename,
|
|
289
481
|
fileType: "html",
|
|
290
|
-
content
|
|
482
|
+
content,
|
|
291
483
|
createdAt: now,
|
|
292
484
|
updatedAt: now,
|
|
293
485
|
});
|
|
294
|
-
await seedFromText(fileId,
|
|
486
|
+
await seedFromText(fileId, content);
|
|
295
487
|
|
|
296
488
|
screens.push({
|
|
297
489
|
id: fileId,
|
|
@@ -41,7 +41,7 @@ export function useQuestionFlow(
|
|
|
41
41
|
formattedAnswers,
|
|
42
42
|
"",
|
|
43
43
|
designId
|
|
44
|
-
? "Now continue the design. Honor any answer about variations: if the user asked to explore options, call present-design-variants with 2-5
|
|
44
|
+
? "Now continue the design. Honor any answer about variations: if the user asked to explore options, call present-design-variants with 2-5 concise directions using label, description, accentColor, and feature bullets; omit large content HTML when needed because the action can render compact representative screens - wait for their chat pick, delete the unchosen variant screens, then continue from the kept screen; otherwise call generate-design with one complete, renderable index.html first. Do not ask another question unless a required decision is still genuinely missing."
|
|
45
45
|
: "Now continue the design. Honor any answer about variations: use variants only if requested; otherwise generate one polished direction.",
|
|
46
46
|
]
|
|
47
47
|
.filter(Boolean)
|
|
@@ -79,7 +79,7 @@ export function useQuestionFlow(
|
|
|
79
79
|
formattedAnswers,
|
|
80
80
|
"",
|
|
81
81
|
designId
|
|
82
|
-
? "Now continue the design. Honor any answer about variations: if the user asked to explore options, call present-design-variants with 2-5
|
|
82
|
+
? "Now continue the design. Honor any answer about variations: if the user asked to explore options, call present-design-variants with 2-5 concise directions using label, description, accentColor, and feature bullets; omit large content HTML when needed because the action can render compact representative screens - wait for their chat pick, delete the unchosen variant screens, then continue from the kept screen; otherwise call generate-design with one complete, renderable index.html first. Do not ask another question unless a required decision is still genuinely missing."
|
|
83
83
|
: "Now continue the design. Honor any answer about variations: use variants only if requested; otherwise generate one polished direction.",
|
|
84
84
|
]
|
|
85
85
|
.filter(Boolean)
|
|
@@ -1309,7 +1309,7 @@ function designGenerationDirectives(
|
|
|
1309
1309
|
return [
|
|
1310
1310
|
`Use the \`generate-design --designId="${designId}"\` action with exactly one complete, renderable \`index.html\` file first. The design already exists - DO NOT call create-design.`,
|
|
1311
1311
|
...designSystemGenerationDirectives(designSystemId),
|
|
1312
|
-
"If the user asked to explore variations, call `present-design-variants` with 2-5
|
|
1312
|
+
"If the user asked to explore variations, call `present-design-variants` with 2-5 concise directions. Prefer label, description, accentColor, and feature bullets; omit large content HTML when needed because the action can render compact representative screens. Wait for their chat pick, delete the unchosen variant screens, then continue from the kept screen. Otherwise generate one polished first direction.",
|
|
1313
1313
|
"Keep the first pass bounded enough to finish quickly: one self-contained Alpine.js + Tailwind CDN HTML document, polished but concise. Add 3-6 tweaks only when they naturally fit the design.",
|
|
1314
1314
|
"After generate-design succeeds, stop and summarize what was created.",
|
|
1315
1315
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-chat-adapter.d.ts","sourceRoot":"","sources":["../../src/client/agent-chat-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAsB,MAAM,qBAAqB,CAAC;AAMhF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAkBrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,MAAM,oBAAoB;AAC9B;;;;GAIG;AACD,KAAK;AACP,0EAA0E;GACxE,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-chat-adapter.d.ts","sourceRoot":"","sources":["../../src/client/agent-chat-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAsB,MAAM,qBAAqB,CAAC;AAMhF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAkBrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,MAAM,oBAAoB;AAC9B;;;;GAIG;AACD,KAAK;AACP,0EAA0E;GACxE,WAAW,CAAC;AAgqChB;;;;GAIG;AACH;;;GAGG;AACH,MAAM,WAAW,6BAA6B;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAC3C,SAAS,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAC5C,SAAS,CAAC,EAAE;QAAE,OAAO,EAAE,eAAe,GAAG,SAAS,CAAA;KAAE,CAAC;IACrD,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,CAAA;KAAE,CAAC;IAC3D,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC;AAED,wBAAgB,sBAAsB,CACpC,OAAO,CAAC,EAAE,6BAA6B,GACtC,gBAAgB,CAo+ClB"}
|