@agent-native/core 0.84.8 → 0.84.10

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 CHANGED
@@ -28,4 +28,4 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core/corpus
28
28
  ## Generated Counts
29
29
 
30
30
  - core files: 2043
31
- - template files: 4908
31
+ - template files: 4909
@@ -1,5 +1,17 @@
1
1
  # @agent-native/core
2
2
 
3
+ ## 0.84.10
4
+
5
+ ### Patch Changes
6
+
7
+ - 720b8b0: Make agent chat recovery surface completed-tool timeouts more reliably and keep Design variant follow-ups bounded to the selected screen.
8
+
9
+ ## 0.84.9
10
+
11
+ ### Patch Changes
12
+
13
+ - 8cfc0ee: Show a visible warning when an agent stops after its final completed tool action, even if it sent text before that tool finished.
14
+
3
15
  ## 0.84.8
4
16
 
5
17
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.84.8",
3
+ "version": "0.84.10",
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": {
@@ -328,7 +328,8 @@ iteration, or a human-in-the-loop choice among design directions.
328
328
  full app per variant. Design saves every option as a normal screen on the
329
329
  overview board and renders an inline chat choice with one button per screen
330
330
  name. After the user picks, delete the unchosen variant screens and continue
331
- from the kept screen.
331
+ from the kept screen by first calling \`get-design-snapshot\` with that
332
+ screen's \`fileId\`, then making a bounded single-file pass.
332
333
  - If the chat choice buttons are not available in the host, ask the user to
333
334
  tell you the screen name they prefer. The variants are already real screens
334
335
  on the board, so do not ask them to paste HTML or copy a generated handoff
@@ -351,8 +352,10 @@ iteration, or a human-in-the-loop choice among design directions.
351
352
  and realistic controls over decorative mockups.
352
353
  5. After \`present-design-variants\`, wait for the user's pick before
353
354
  generating the next version. Keep the chosen screen, delete the other
354
- variant screens, then refine that direction with \`generate-design\` or
355
- \`edit-design\`.
355
+ variant screens, call \`get-design-snapshot\` with \`fileId\` for the kept
356
+ screen, then continue from that single file in a bounded pass. Prefer
357
+ \`edit-design\`; if \`generate-design\` is needed, update only the kept file
358
+ compactly and stop after the first successful save.
356
359
 
357
360
  ## Design Quality Bar
358
361
 
@@ -703,7 +703,26 @@ function hasContinuationProgress(content: ContentPart[]): boolean {
703
703
  );
704
704
  }
705
705
 
706
- function lastCompletedSideEffectTool(
706
+ const COMPLETED_TOOL_TIMEOUT_NAME_RE =
707
+ /^(add|apply|archive|capture|create|delete|deploy|duplicate|edit|generate|grant|insert|migrate|move|present|publish|remove|rename|reorder|revoke|save|send|set|sync|trash|update|write)(-|$)/;
708
+ const COMPLETED_TOOL_TIMEOUT_NAME_ALLOWLIST = new Set([
709
+ "connect-assets-mcp",
710
+ "import-design-tokens",
711
+ ]);
712
+
713
+ function isCompletedToolTimeoutCandidate(
714
+ part: Extract<ContentPart, { type: "tool-call" }>,
715
+ ): boolean {
716
+ if (part.completedSideEffect === false) return false;
717
+ if (part.completedSideEffect === true) return true;
718
+ const toolName = part.toolName.toLowerCase();
719
+ return (
720
+ COMPLETED_TOOL_TIMEOUT_NAME_ALLOWLIST.has(toolName) ||
721
+ COMPLETED_TOOL_TIMEOUT_NAME_RE.test(toolName)
722
+ );
723
+ }
724
+
725
+ function lastCompletedTimeoutCandidateTool(
707
726
  content: ContentPart[],
708
727
  ): Extract<ContentPart, { type: "tool-call" }> | undefined {
709
728
  for (let i = content.length - 1; i >= 0; i--) {
@@ -713,7 +732,7 @@ function lastCompletedSideEffectTool(
713
732
  part.activity !== true &&
714
733
  part.result !== undefined &&
715
734
  part.isError !== true &&
716
- part.completedSideEffect === true
735
+ isCompletedToolTimeoutCandidate(part)
717
736
  ) {
718
737
  return part;
719
738
  }
@@ -1866,7 +1885,7 @@ export function createAgentChatAdapter(
1866
1885
  // before tool_start; treating it as progress caused silent retry
1867
1886
  // loops when the LLM timed out while assembling a large tool input.
1868
1887
  const hasInFlightTool = hasInFlightToolCall(visibleContent);
1869
- const completedSideEffectTool = lastCompletedSideEffectTool(content);
1888
+ const completedTool = lastCompletedTimeoutCandidateTool(content);
1870
1889
  // Either real output or an actively-running tool counts as progress
1871
1890
  // for the stalled/empty caps.
1872
1891
  const madeProgress = madeContentProgress || hasInFlightTool;
@@ -1963,22 +1982,23 @@ export function createAgentChatAdapter(
1963
1982
  emptyTransientContinuationAttempts = 0;
1964
1983
  } else {
1965
1984
  totalTransientContinuationAttempts += 1;
1966
- // If a mutating action already completed, do not turn a missing
1967
- // closing sentence into a scary connection failure. Give the model
1968
- // one continuation opportunity (the completed tool itself counts
1969
- // as progress on the first timeout); if the follow-up produces no
1970
- // new content, stop locally with a clear "saved, final note timed
1971
- // out" message.
1985
+ // If a tool already completed, do not turn a missing closing
1986
+ // sentence into a scary connection failure. Give the model one
1987
+ // continuation opportunity (the completed tool itself counts as
1988
+ // progress on the first timeout); if the follow-up produces no new
1989
+ // content, stop locally with a clear completed-tool warning.
1972
1990
  if (
1973
- completedSideEffectTool &&
1991
+ signal.reason === "run_timeout" &&
1992
+ completedTool &&
1974
1993
  !hasInFlightToolCall(content) &&
1994
+ !currentPreparingToolName &&
1975
1995
  !madeContentProgress &&
1976
1996
  !hasInFlightTool
1977
1997
  ) {
1978
1998
  return {
1979
1999
  ok: false,
1980
2000
  resetVisibleContent: false,
1981
- completedToolName: completedSideEffectTool.toolName,
2001
+ completedToolName: completedTool.toolName,
1982
2002
  };
1983
2003
  }
1984
2004
  // Bail when the same write tool is stuck in-flight across too many
@@ -381,15 +381,21 @@ function interruptedToolMessage(pending: {
381
381
  return `The agent stopped before starting ${actionLabel}. No tool result was returned, so the requested changes were not made.`;
382
382
  }
383
383
 
384
- function hasAssistantText(content: ContentPart[]): boolean {
385
- return content.some(
386
- (part) => part.type === "text" && part.text.trim().length > 0,
387
- );
384
+ function lastAssistantTextIndex(content: ContentPart[]): number {
385
+ for (let i = content.length - 1; i >= 0; i--) {
386
+ const part = content[i];
387
+ if (part.type === "text" && part.text.trim().length > 0) return i;
388
+ }
389
+ return -1;
388
390
  }
389
391
 
390
- function completedToolNames(content: ContentPart[]): string[] {
392
+ function completedToolNamesAfterLastAssistantText(
393
+ content: ContentPart[],
394
+ ): string[] {
395
+ const lastTextIndex = lastAssistantTextIndex(content);
391
396
  const names = new Set<string>();
392
- for (const part of content) {
397
+ for (let i = lastTextIndex + 1; i < content.length; i++) {
398
+ const part = content[i];
393
399
  if (
394
400
  part.type === "tool-call" &&
395
401
  part.activity !== true &&
@@ -408,6 +414,25 @@ function completedToolOnlyMessage(toolNames: string[]): string | null {
408
414
  return `The agent completed ${label}, but stopped before sending a final message. Review the completed tool card above or ask the agent to continue.`;
409
415
  }
410
416
 
417
+ interface ProcessEventState {
418
+ completedToolsAfterLastAssistantText: Set<string>;
419
+ }
420
+
421
+ function markAssistantText(state: ProcessEventState | undefined) {
422
+ state?.completedToolsAfterLastAssistantText.clear();
423
+ }
424
+
425
+ function markCompletedToolAfterAssistantText(
426
+ state: ProcessEventState | undefined,
427
+ toolName: string,
428
+ ) {
429
+ state?.completedToolsAfterLastAssistantText.add(toolName);
430
+ }
431
+
432
+ function resetProcessEventState(state: ProcessEventState | undefined) {
433
+ state?.completedToolsAfterLastAssistantText.clear();
434
+ }
435
+
411
436
  /**
412
437
  * Process a single SSE event and update the content accumulator.
413
438
  * Returns: "continue" to keep going, "done" to stop, or a yield-ready result.
@@ -417,6 +442,7 @@ export function processEvent(
417
442
  content: ContentPart[],
418
443
  toolCallCounter: { value: number },
419
444
  tabId: string | undefined,
445
+ state?: ProcessEventState,
420
446
  ): {
421
447
  action:
422
448
  | "continue"
@@ -435,6 +461,7 @@ export function processEvent(
435
461
  if (ev.type === "clear") {
436
462
  // Server is retrying — discard partial text/tool output from the failed attempt
437
463
  content.length = 0;
464
+ resetProcessEventState(state);
438
465
  dispatchActivityClear(tabId);
439
466
  return { action: "continue" };
440
467
  }
@@ -445,6 +472,7 @@ export function processEvent(
445
472
  // image" doesn't linger beside streamed text. Idempotent (clears once, then
446
473
  // no-ops) so per-token text deltas stay cheap.
447
474
  if (ev.text) dispatchActivityClear(tabId);
475
+ if (ev.text?.trim()) markAssistantText(state);
448
476
  const lastPart = content[content.length - 1];
449
477
  if (lastPart && lastPart.type === "text") {
450
478
  lastPart.text += ev.text ?? "";
@@ -602,6 +630,9 @@ export function processEvent(
602
630
  }
603
631
  if (ev.mcpApp) part.mcpApp = ev.mcpApp;
604
632
  if (ev.chatUI) part.chatUI = ev.chatUI;
633
+ if (part.activity !== true && part.isError !== true) {
634
+ markCompletedToolAfterAssistantText(state, part.toolName);
635
+ }
605
636
  }
606
637
  }
607
638
  return {
@@ -852,9 +883,11 @@ export function processEvent(
852
883
  } as ChatModelRunResult,
853
884
  };
854
885
  }
855
- const toolOnlyMessage = hasAssistantText(content)
856
- ? null
857
- : completedToolOnlyMessage(completedToolNames(content));
886
+ const toolOnlyMessage = completedToolOnlyMessage(
887
+ state
888
+ ? [...state.completedToolsAfterLastAssistantText]
889
+ : completedToolNamesAfterLastAssistantText(content),
890
+ );
858
891
  if (toolOnlyMessage) {
859
892
  content.push({
860
893
  type: "text",
@@ -908,6 +941,9 @@ export async function* readSSEStream(
908
941
  let buf = "";
909
942
  let lastMeaningfulEventAt = Date.now();
910
943
  const activityTrail: ActivityTrailEntry[] = [];
944
+ const processEventState: ProcessEventState = {
945
+ completedToolsAfterLastAssistantText: new Set(),
946
+ };
911
947
 
912
948
  const withStreamMetadata = (r: ChatModelRunResult): ChatModelRunResult => {
913
949
  if (!runId && activityTrail.length === 0) return r;
@@ -999,6 +1035,7 @@ export async function* readSSEStream(
999
1035
  content,
1000
1036
  toolCallCounter,
1001
1037
  tabId,
1038
+ processEventState,
1002
1039
  );
1003
1040
 
1004
1041
  if (result) yield withStreamMetadata(result);
@@ -1062,6 +1099,9 @@ export async function readSSEStreamRaw(
1062
1099
  const decoder = new TextDecoder();
1063
1100
  let buf = "";
1064
1101
  let lastMeaningfulEventAt = Date.now();
1102
+ const processEventState: ProcessEventState = {
1103
+ completedToolsAfterLastAssistantText: new Set(),
1104
+ };
1065
1105
  // Tracks whether the most recent content state was already pushed via
1066
1106
  // onUpdate inside the loop, so the post-loop flush below doesn't emit the
1067
1107
  // identical content a second time when the stream closes without a terminal
@@ -1105,6 +1145,7 @@ export async function readSSEStreamRaw(
1105
1145
  content,
1106
1146
  toolCallCounter,
1107
1147
  tabId,
1148
+ processEventState,
1108
1149
  );
1109
1150
 
1110
1151
  if (
@@ -23,9 +23,22 @@ export default defineAction({
23
23
  "edited live, otherwise the stored content), the design's tweak " +
24
24
  "definitions, the user's applied tweak selections, and the resolved CSS " +
25
25
  "custom-property values so the agent sees the *tuned* design, not the " +
26
- "original generated tokens. Read-only.",
26
+ "original generated tokens. Pass fileId or filename when continuing from " +
27
+ "one selected screen so large multi-file designs stay bounded. Read-only.",
27
28
  schema: z.object({
28
29
  designId: z.string().describe("Design project ID to snapshot"),
30
+ fileId: z
31
+ .string()
32
+ .optional()
33
+ .describe(
34
+ "Optional design file ID to return. Use this after a variant pick to snapshot only the kept screen.",
35
+ ),
36
+ filename: z
37
+ .string()
38
+ .optional()
39
+ .describe(
40
+ "Optional design filename to return when fileId is unavailable.",
41
+ ),
29
42
  }),
30
43
  readOnly: true,
31
44
  http: { method: "GET" },
@@ -40,7 +53,7 @@ export default defineAction({
40
53
  height: 680,
41
54
  }),
42
55
  },
43
- run: async ({ designId }) => {
56
+ run: async ({ designId, fileId, filename }) => {
44
57
  const access = await resolveAccess("design", designId);
45
58
  if (!access) {
46
59
  const err = new Error("Design not found") as Error & {
@@ -52,6 +65,28 @@ export default defineAction({
52
65
  const design = access.resource as typeof schema.designs.$inferSelect;
53
66
 
54
67
  const snapshot = await buildDesignSnapshot(designId, design.data);
68
+ const requestedFileId = fileId?.trim();
69
+ const requestedFilename = filename?.trim();
70
+ const files = requestedFileId
71
+ ? snapshot.files.filter((file) => file.id === requestedFileId)
72
+ : requestedFilename
73
+ ? snapshot.files.filter((file) => file.filename === requestedFilename)
74
+ : snapshot.files;
75
+
76
+ if ((requestedFileId || requestedFilename) && files.length === 0) {
77
+ const err = new Error("Design file not found") as Error & {
78
+ statusCode: number;
79
+ };
80
+ err.statusCode = 404;
81
+ throw err;
82
+ }
83
+ if (!requestedFileId && requestedFilename && files.length > 1) {
84
+ const err = new Error(
85
+ "Multiple design files match filename; pass fileId instead",
86
+ ) as Error & { statusCode: number };
87
+ err.statusCode = 409;
88
+ throw err;
89
+ }
55
90
 
56
91
  return {
57
92
  designId,
@@ -60,13 +95,15 @@ export default defineAction({
60
95
  projectType: design.projectType,
61
96
  designSystemId: design.designSystemId ?? null,
62
97
  updatedAt: design.updatedAt,
63
- files: snapshot.files.map((f) => ({
98
+ files: files.map((f) => ({
99
+ id: f.id,
64
100
  filename: f.filename,
65
101
  fileType: f.fileType,
66
102
  content: f.content,
67
103
  source: f.source,
68
104
  })),
69
- fileCount: snapshot.files.length,
105
+ fileCount: files.length,
106
+ totalFileCount: snapshot.files.length,
70
107
  tweaks: snapshot.tweaks,
71
108
  appliedTweaks: snapshot.appliedTweaks,
72
109
  resolvedCssVars: snapshot.resolvedCssVars,
@@ -40,7 +40,17 @@ const FALLBACK_INSTRUCTIONS =
40
40
  "The generated directions have been saved as normal screens on the Design " +
41
41
  "overview board. The chat shows one button per screen. Ask the user to pick " +
42
42
  "a screen by name if the inline buttons are not available; after they pick, " +
43
- "delete the other variant screens and continue from the kept screen.";
43
+ "delete the other variant screens, call get-design-snapshot with fileId for " +
44
+ "the kept screen, and continue from that one file in a bounded pass.";
45
+
46
+ const VARIANT_PICK_SUBMIT_MESSAGE =
47
+ "Use this design direction. Delete the other variant screens, call " +
48
+ "get-design-snapshot with the selected option's fileId (or filename if " +
49
+ "needed) for the kept file, then continue from that current HTML in a " +
50
+ "bounded single-file pass. Prefer edit-design for refinements; if " +
51
+ "generate-design is needed, update only the kept file compactly. Do not " +
52
+ "generate a full multi-screen rewrite or resend a huge payload. Stop after " +
53
+ "the first successful save.";
44
54
 
45
55
  const variantSchema = z.object({
46
56
  id: z.string().min(1).describe("Stable variant id, e.g. 'minimal-focus'"),
@@ -410,7 +420,11 @@ export default defineAction({
410
420
  "Provide 2-5 variants (3 is the sweet spot). Use this for design " +
411
421
  "exploration before follow-up refinement. After the user's choice, keep " +
412
422
  "the chosen screen, delete the other generated variant screens, and " +
413
- "continue from the kept screen. For complex apps, make each variant a " +
423
+ "call get-design-snapshot with fileId for the kept screen before " +
424
+ "continuing from that single file in a bounded pass. Prefer edit-design " +
425
+ "for refinements; if generate-design is needed, update only the kept file " +
426
+ "compactly and stop after the first successful save. For complex apps, " +
427
+ "make each variant a " +
414
428
  "compact representative screen; pass concise labels/descriptions/features " +
415
429
  "and omit content when full HTML would be too large. Design will render " +
416
430
  "compact screens from the direction data. Expand the chosen direction " +
@@ -563,9 +577,9 @@ export default defineAction({
563
577
  await writeAppStateForCurrentTab("guided-questions", {
564
578
  title: prompt ?? "Pick a direction",
565
579
  description:
566
- "All options are on the board. Choose the one to keep and I will continue from it.",
580
+ "All options are on the board. Choose one to keep; I will delete the others, read only the kept screen, and continue from that single file in a bounded pass.",
567
581
  submitLabel: "Use selected direction",
568
- submitMessage: "Use this design direction.",
582
+ submitMessage: VARIANT_PICK_SUBMIT_MESSAGE,
569
583
  skipLabel: "Show another set",
570
584
  skipMessage: "None of these directions are right.",
571
585
  questions: [
@@ -590,7 +604,8 @@ export default defineAction({
590
604
  label: screen.label || optionName(index),
591
605
  value:
592
606
  `Keep "${screen.label}" (${screen.filename}, file id ${screen.id}) ` +
593
- `from variant set ${variantSetId}. Delete the other variant screens: ${otherScreens}.`,
607
+ `from variant set ${variantSetId}. Delete the other variant screens: ${otherScreens}. ` +
608
+ `Then call get-design-snapshot with designId ${designId} and fileId ${screen.id} (filename ${screen.filename}) and continue from that current HTML in a bounded single-file pass. Prefer edit-design for refinements; if generate-design is needed, update only this kept file compactly. Do not generate a full multi-screen rewrite or resend a huge payload. Stop after the first successful save.`,
594
609
  };
595
610
  }),
596
611
  },
@@ -608,7 +623,7 @@ export default defineAction({
608
623
  embed: true,
609
624
  fallbackInstructions: FALLBACK_INSTRUCTIONS,
610
625
  nextRequiredAction:
611
- "Wait for the user to pick a screen in chat. Then delete the unchosen variant screens with delete-file and continue from the chosen screen.",
626
+ "Wait for the user to pick a screen in chat. Then delete the unchosen variant screens with delete-file, call get-design-snapshot with fileId for the chosen screen, and continue from that single file in a bounded pass. Prefer edit-design; if generate-design is needed, update only the kept file compactly and stop after the first successful save.",
612
627
  };
613
628
  },
614
629
  link: ({ result }) => {
@@ -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 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."
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, call get-design-snapshot with fileId for the kept screen, then continue from that single file in a bounded pass. Prefer edit-design; if generate-design is needed, update only the kept file compactly and stop after the first successful save. 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 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."
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, call get-design-snapshot with fileId for the kept screen, then continue from that single file in a bounded pass. Prefer edit-design; if generate-design is needed, update only the kept file compactly and stop after the first successful save. 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)
@@ -1325,7 +1325,7 @@ function designVariantGenerationDirectives(
1325
1325
  ...designSystemGenerationDirectives(designSystemId),
1326
1326
  "The user's prompt already asks to explore multiple directions, so DO NOT call `show-design-questions` first and DO NOT call `generate-design` first.",
1327
1327
  "Call `present-design-variants` with 2-5 concise directions (3 when unspecified). Prefer label, description, accentColor, and feature bullets; omit large content HTML when needed because the action can render compact representative screens.",
1328
- "Wait for the user's chat pick, delete the unchosen variant screens, then continue from the kept screen.",
1328
+ "Wait for the user's chat pick, delete the unchosen variant screens, call `get-design-snapshot` with `fileId` for the kept screen, then continue from that single file in a bounded pass. Prefer `edit-design`; if `generate-design` is needed, update only the kept file compactly and stop after the first successful save.",
1329
1329
  ];
1330
1330
  }
1331
1331
 
@@ -1336,7 +1336,7 @@ function designGenerationDirectives(
1336
1336
  return [
1337
1337
  `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.`,
1338
1338
  ...designSystemGenerationDirectives(designSystemId),
1339
- "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.",
1339
+ "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, call `get-design-snapshot` with `fileId` for the kept screen, then continue from that single file in a bounded pass. Prefer `edit-design`; if `generate-design` is needed, update only the kept file compactly and stop after the first successful save. Otherwise generate one polished first direction.",
1340
1340
  "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.",
1341
1341
  "After generate-design succeeds, stop and summarize what was created.",
1342
1342
  ];
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-01
4
+ ---
5
+
6
+ Improved variant-pick follow-ups so selected directions continue from the kept screen in a bounded pass.
@@ -1 +1 @@
1
- {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/cli/skills.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/cli/skills.ts"],"names":[],"mappings":"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"}
@@ -308,7 +308,8 @@ iteration, or a human-in-the-loop choice among design directions.
308
308
  full app per variant. Design saves every option as a normal screen on the
309
309
  overview board and renders an inline chat choice with one button per screen
310
310
  name. After the user picks, delete the unchosen variant screens and continue
311
- from the kept screen.
311
+ from the kept screen by first calling \`get-design-snapshot\` with that
312
+ screen's \`fileId\`, then making a bounded single-file pass.
312
313
  - If the chat choice buttons are not available in the host, ask the user to
313
314
  tell you the screen name they prefer. The variants are already real screens
314
315
  on the board, so do not ask them to paste HTML or copy a generated handoff
@@ -331,8 +332,10 @@ iteration, or a human-in-the-loop choice among design directions.
331
332
  and realistic controls over decorative mockups.
332
333
  5. After \`present-design-variants\`, wait for the user's pick before
333
334
  generating the next version. Keep the chosen screen, delete the other
334
- variant screens, then refine that direction with \`generate-design\` or
335
- \`edit-design\`.
335
+ variant screens, call \`get-design-snapshot\` with \`fileId\` for the kept
336
+ screen, then continue from that single file in a bounded pass. Prefer
337
+ \`edit-design\`; if \`generate-design\` is needed, update only the kept file
338
+ compactly and stop after the first successful save.
336
339
 
337
340
  ## Design Quality Bar
338
341