@agent-native/core 0.80.0 → 0.80.2

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.
Files changed (228) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +28 -0
  3. package/corpus/core/docs/content/locales/ar-SA/skills-guide.mdx +5 -0
  4. package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +14 -1
  5. package/corpus/core/docs/content/locales/de-DE/skills-guide.mdx +5 -0
  6. package/corpus/core/docs/content/locales/de-DE/template-design.mdx +15 -1
  7. package/corpus/core/docs/content/locales/es-ES/skills-guide.mdx +5 -0
  8. package/corpus/core/docs/content/locales/es-ES/template-design.mdx +15 -1
  9. package/corpus/core/docs/content/locales/fr-FR/skills-guide.mdx +5 -0
  10. package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +15 -1
  11. package/corpus/core/docs/content/locales/hi-IN/skills-guide.mdx +5 -0
  12. package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +15 -1
  13. package/corpus/core/docs/content/locales/ja-JP/skills-guide.mdx +5 -0
  14. package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +16 -1
  15. package/corpus/core/docs/content/locales/ko-KR/skills-guide.mdx +5 -0
  16. package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +14 -1
  17. package/corpus/core/docs/content/locales/pt-BR/skills-guide.mdx +5 -0
  18. package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +14 -1
  19. package/corpus/core/docs/content/locales/zh-CN/skills-guide.mdx +5 -0
  20. package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +14 -1
  21. package/corpus/core/docs/content/locales/zh-TW/skills-guide.mdx +5 -0
  22. package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +14 -1
  23. package/corpus/core/docs/content/skills-guide.mdx +5 -0
  24. package/corpus/core/docs/content/template-design.mdx +14 -1
  25. package/corpus/core/package.json +1 -1
  26. package/corpus/core/src/application-state/handlers.ts +1 -1
  27. package/corpus/core/src/brand-kit/index.ts +1 -1
  28. package/corpus/core/src/cli/skills.ts +18 -0
  29. package/corpus/core/src/client/AgentPanel.tsx +1 -1
  30. package/corpus/core/src/client/MultiTabAssistantChat.tsx +1 -1
  31. package/corpus/core/src/client/extensions/portable-extension.ts +1 -1
  32. package/corpus/core/src/code-agents/transcript-normalizer.ts +1 -1
  33. package/corpus/core/src/extensions/path.ts +1 -4
  34. package/corpus/core/src/integrations/a2a-continuation-processor.ts +1 -1
  35. package/corpus/core/src/mcp/build-server.ts +2 -2
  36. package/corpus/core/src/mcp/embed-app.ts +35 -1
  37. package/corpus/core/src/org/handlers.ts +5 -5
  38. package/corpus/core/src/org/plugin.ts +2 -2
  39. package/corpus/core/src/scripts/call-agent.ts +1 -1
  40. package/corpus/core/src/scripts/db/safety.ts +1 -1
  41. package/corpus/core/src/server/auth.ts +3 -0
  42. package/corpus/core/src/server/builder-design-systems.ts +308 -0
  43. package/corpus/core/src/server/core-routes-plugin.ts +29 -3
  44. package/corpus/core/src/server/index.ts +8 -0
  45. package/corpus/core/src/styles/agent-native.css +14 -7
  46. package/corpus/core/src/usage/store.ts +2 -2
  47. package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +298 -15
  48. package/corpus/templates/analytics/actions/get-sql-dashboard.ts +9 -4
  49. package/corpus/templates/analytics/actions/mutate-dashboard.ts +18 -2
  50. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +12 -2
  51. package/corpus/templates/analytics/app/hooks/use-replay-storage-status.ts +66 -0
  52. package/corpus/templates/analytics/app/i18n/zh-TW.ts +37 -0
  53. package/corpus/templates/analytics/app/i18n-data.ts +404 -0
  54. package/corpus/templates/analytics/app/pages/Settings.tsx +333 -0
  55. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +2 -1
  56. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-layout.ts +121 -3
  57. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +49 -26
  58. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +137 -36
  59. package/corpus/templates/analytics/changelog/2026-06-29-analytics-agents-can-now-place-new-charts-into-requested-das.md +6 -0
  60. package/corpus/templates/analytics/changelog/2026-06-29-analytics-queries-now-report-failed-source-query-errors-inst.md +6 -0
  61. package/corpus/templates/analytics/changelog/2026-06-29-charts-can-now-be-dropped-between-full-dashboard-rows-to-cre.md +6 -0
  62. package/corpus/templates/analytics/changelog/2026-06-29-dashboard-email-reports-include-the-full-dashboard-image-wit.md +6 -0
  63. package/corpus/templates/analytics/changelog/2026-06-29-main-surface-border-reaches-the-top.md +6 -0
  64. package/corpus/templates/analytics/changelog/2026-06-29-session-replay-now-records-playable-sessions-in-production-a.md +6 -0
  65. package/corpus/templates/analytics/changelog/2026-06-29-set-up-session-replay-storage-from-settings-connect-builder-.md +6 -0
  66. package/corpus/templates/analytics/scripts/cleanup-empty-session-recordings.ts +102 -0
  67. package/corpus/templates/analytics/server/lib/dashboard-report.ts +49 -26
  68. package/corpus/templates/analytics/server/lib/real-data-actions.ts +81 -20
  69. package/corpus/templates/analytics/server/lib/replay-storage.ts +79 -0
  70. package/corpus/templates/analytics/server/lib/s3-upload-provider.ts +255 -0
  71. package/corpus/templates/analytics/server/lib/session-replay.ts +29 -8
  72. package/corpus/templates/analytics/server/plugins/agent-chat.ts +11 -2
  73. package/corpus/templates/analytics/server/plugins/onboarding.ts +97 -0
  74. package/corpus/templates/clips/desktop/src/lib/recorder.ts +24 -0
  75. package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +105 -5
  76. package/corpus/templates/content/actions/set-image-alt-text.ts +1 -1
  77. package/corpus/templates/content/shared/document-export.ts +1 -1
  78. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +8 -18
  79. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +17 -0
  80. package/corpus/templates/design/actions/add-localhost-screens.ts +32 -5
  81. package/corpus/templates/design/actions/index-design-system-with-builder.ts +259 -0
  82. package/corpus/templates/design/actions/update-file.ts +9 -2
  83. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +33 -75
  84. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +72 -9
  85. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +41 -39
  86. package/corpus/templates/design/app/components/design/EditPanel.tsx +1029 -249
  87. package/corpus/templates/design/app/components/design/LayersPanel.tsx +450 -101
  88. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +978 -154
  89. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +7 -9
  90. package/corpus/templates/design/app/components/design/inspector/AlignmentMatrix.tsx +133 -66
  91. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +1234 -344
  92. package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +144 -94
  93. package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +301 -72
  94. package/corpus/templates/design/app/components/design/inspector/FigmaColorPicker.tsx +876 -286
  95. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +601 -0
  96. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +242 -0
  97. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +30 -7
  98. package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +212 -155
  99. package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +423 -0
  100. package/corpus/templates/design/app/components/design/inspector/figma-icons.tsx +487 -107
  101. package/corpus/templates/design/app/components/design/inspector/index.ts +27 -0
  102. package/corpus/templates/design/app/global.css +29 -3
  103. package/corpus/templates/design/app/i18n/zh-TW.ts +13 -0
  104. package/corpus/templates/design/app/i18n-data.ts +239 -1
  105. package/corpus/templates/design/app/pages/DesignEditor.tsx +1023 -353
  106. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +57 -221
  107. package/corpus/templates/design/changelog/2026-06-29-canvas-selection-no-longer-flashes-broken-prototype-html-whe.md +6 -0
  108. package/corpus/templates/design/changelog/2026-06-29-command-menu-selections-are-clearer-in-dark-mode.md +6 -0
  109. package/corpus/templates/design/changelog/2026-06-29-design-mode-top-chrome-now-groups-collaborators-preview-shar.md +6 -0
  110. package/corpus/templates/design/changelog/2026-06-29-element-selection-now-stays-on-the-intended-layer-after-sele.md +6 -0
  111. package/corpus/templates/design/changelog/2026-06-29-screen-overview-full-view-controls-now-sit-above-frames-inst.md +6 -0
  112. package/corpus/templates/design/changelog/2026-06-29-screen-overview-tools-can-draw-shapes-create-screens-and-kee.md +6 -0
  113. package/corpus/templates/design/changelog/2026-06-29-the-pen-tool-now-creates-anchor-based-bezier-vectors-with-ha.md +6 -0
  114. package/corpus/templates/design/server/handlers/import-figma-system.ts +38 -85
  115. package/corpus/templates/design/server/lib/design-snapshot.ts +9 -1
  116. package/corpus/templates/design/shared/code-layer.ts +209 -9
  117. package/corpus/templates/design/shared/html-content.ts +21 -0
  118. package/corpus/templates/design/shared/pen-path.ts +207 -0
  119. package/corpus/templates/mail/app/lib/utils.ts +2 -2
  120. package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +9 -10
  121. package/corpus/templates/slides/AGENTS.md +3 -2
  122. package/corpus/templates/slides/actions/import-file.ts +26 -17
  123. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +259 -0
  124. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +35 -167
  125. package/corpus/templates/slides/app/components/design-system/fig-import-response.ts +9 -13
  126. package/corpus/templates/slides/app/context/DeckContext.tsx +1 -1
  127. package/corpus/templates/slides/app/i18n/ar-SA.ts +9 -37
  128. package/corpus/templates/slides/app/i18n/de-DE.ts +10 -25
  129. package/corpus/templates/slides/app/i18n/en-US.ts +9 -26
  130. package/corpus/templates/slides/app/i18n/es-ES.ts +10 -28
  131. package/corpus/templates/slides/app/i18n/fr-FR.ts +10 -28
  132. package/corpus/templates/slides/app/i18n/hi-IN.ts +9 -26
  133. package/corpus/templates/slides/app/i18n/ja-JP.ts +10 -22
  134. package/corpus/templates/slides/app/i18n/ko-KR.ts +9 -23
  135. package/corpus/templates/slides/app/i18n/pt-BR.ts +10 -28
  136. package/corpus/templates/slides/app/i18n/zh-CN.ts +9 -22
  137. package/corpus/templates/slides/app/i18n/zh-TW.ts +9 -22
  138. package/corpus/templates/slides/server/handlers/import-figma-system.ts +44 -12
  139. package/corpus/templates/slides/server/lib/chat-attachments.ts +1 -1
  140. package/corpus/templates/videos/app/components/NewCompositionPopover.tsx +1 -1
  141. package/dist/application-state/handlers.js +1 -1
  142. package/dist/application-state/handlers.js.map +1 -1
  143. package/dist/brand-kit/index.d.ts +1 -1
  144. package/dist/brand-kit/index.js +1 -1
  145. package/dist/brand-kit/index.js.map +1 -1
  146. package/dist/cli/skills.d.ts.map +1 -1
  147. package/dist/cli/skills.js +18 -0
  148. package/dist/cli/skills.js.map +1 -1
  149. package/dist/client/AgentPanel.js +1 -1
  150. package/dist/client/AgentPanel.js.map +1 -1
  151. package/dist/client/MultiTabAssistantChat.js +1 -1
  152. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  153. package/dist/client/extensions/portable-extension.d.ts +1 -1
  154. package/dist/client/extensions/portable-extension.d.ts.map +1 -1
  155. package/dist/client/extensions/portable-extension.js.map +1 -1
  156. package/dist/code-agents/transcript-normalizer.js +1 -1
  157. package/dist/code-agents/transcript-normalizer.js.map +1 -1
  158. package/dist/collab/awareness.d.ts +2 -2
  159. package/dist/collab/awareness.d.ts.map +1 -1
  160. package/dist/collab/routes.d.ts +1 -1
  161. package/dist/extensions/path.d.ts +1 -1
  162. package/dist/extensions/path.d.ts.map +1 -1
  163. package/dist/extensions/path.js.map +1 -1
  164. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  165. package/dist/integrations/a2a-continuation-processor.js +1 -1
  166. package/dist/integrations/a2a-continuation-processor.js.map +1 -1
  167. package/dist/mcp/build-server.d.ts +1 -1
  168. package/dist/mcp/build-server.d.ts.map +1 -1
  169. package/dist/mcp/build-server.js +1 -1
  170. package/dist/mcp/build-server.js.map +1 -1
  171. package/dist/mcp/embed-app.d.ts.map +1 -1
  172. package/dist/mcp/embed-app.js +35 -1
  173. package/dist/mcp/embed-app.js.map +1 -1
  174. package/dist/notifications/routes.d.ts +3 -3
  175. package/dist/observability/routes.d.ts +8 -8
  176. package/dist/org/handlers.js +5 -5
  177. package/dist/org/handlers.js.map +1 -1
  178. package/dist/org/plugin.js +2 -2
  179. package/dist/org/plugin.js.map +1 -1
  180. package/dist/resources/handlers.d.ts +1 -1
  181. package/dist/scripts/call-agent.js +1 -1
  182. package/dist/scripts/call-agent.js.map +1 -1
  183. package/dist/scripts/db/safety.js +1 -1
  184. package/dist/scripts/db/safety.js.map +1 -1
  185. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  186. package/dist/server/auth.d.ts.map +1 -1
  187. package/dist/server/auth.js +3 -0
  188. package/dist/server/auth.js.map +1 -1
  189. package/dist/server/builder-design-systems.d.ts +28 -0
  190. package/dist/server/builder-design-systems.d.ts.map +1 -0
  191. package/dist/server/builder-design-systems.js +217 -0
  192. package/dist/server/builder-design-systems.js.map +1 -0
  193. package/dist/server/core-routes-plugin.d.ts +1 -0
  194. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  195. package/dist/server/core-routes-plugin.js +20 -3
  196. package/dist/server/core-routes-plugin.js.map +1 -1
  197. package/dist/server/index.d.ts +1 -0
  198. package/dist/server/index.d.ts.map +1 -1
  199. package/dist/server/index.js +1 -0
  200. package/dist/server/index.js.map +1 -1
  201. package/dist/styles/agent-native.css +14 -7
  202. package/dist/usage/store.js +2 -2
  203. package/dist/usage/store.js.map +1 -1
  204. package/docs/content/locales/ar-SA/skills-guide.mdx +5 -0
  205. package/docs/content/locales/ar-SA/template-design.mdx +14 -1
  206. package/docs/content/locales/de-DE/skills-guide.mdx +5 -0
  207. package/docs/content/locales/de-DE/template-design.mdx +15 -1
  208. package/docs/content/locales/es-ES/skills-guide.mdx +5 -0
  209. package/docs/content/locales/es-ES/template-design.mdx +15 -1
  210. package/docs/content/locales/fr-FR/skills-guide.mdx +5 -0
  211. package/docs/content/locales/fr-FR/template-design.mdx +15 -1
  212. package/docs/content/locales/hi-IN/skills-guide.mdx +5 -0
  213. package/docs/content/locales/hi-IN/template-design.mdx +15 -1
  214. package/docs/content/locales/ja-JP/skills-guide.mdx +5 -0
  215. package/docs/content/locales/ja-JP/template-design.mdx +16 -1
  216. package/docs/content/locales/ko-KR/skills-guide.mdx +5 -0
  217. package/docs/content/locales/ko-KR/template-design.mdx +14 -1
  218. package/docs/content/locales/pt-BR/skills-guide.mdx +5 -0
  219. package/docs/content/locales/pt-BR/template-design.mdx +14 -1
  220. package/docs/content/locales/zh-CN/skills-guide.mdx +5 -0
  221. package/docs/content/locales/zh-CN/template-design.mdx +14 -1
  222. package/docs/content/locales/zh-TW/skills-guide.mdx +5 -0
  223. package/docs/content/locales/zh-TW/template-design.mdx +14 -1
  224. package/docs/content/skills-guide.mdx +5 -0
  225. package/docs/content/template-design.mdx +14 -1
  226. package/package.json +1 -1
  227. package/corpus/templates/design/actions/import-figma-file.ts +0 -166
  228. package/corpus/templates/slides/server/lib/fig-design-system.ts +0 -110
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.80.0",
3
+ "version": "0.80.2",
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": {
@@ -33,7 +33,7 @@ async function getSessionId(event: H3Event): Promise<string> {
33
33
  }
34
34
 
35
35
  function safeKey(key: string): string {
36
- return key.replace(/[^a-zA-Z0-9_:\-]/g, "");
36
+ return key.replace(/[^a-zA-Z0-9_:-]/g, "");
37
37
  }
38
38
 
39
39
  // --- Generic state handlers ---
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * The shared, template-agnostic Brand Kit surface. A Brand Kit unifies what
5
5
  * `design` and `slides` previously copy-pasted as a "design system": design
6
- * tokens + brand assets + custom instructions, extractable from Figma, code,
6
+ * tokens + brand assets + custom instructions, extractable from code,
7
7
  * GitHub, a URL, or documents, and used to generate on-brand content.
8
8
  *
9
9
  * This module is pure (no `defineAction`, no DB, no template imports). Templates
@@ -404,6 +404,10 @@ iframe-backed screens on the infinite canvas.
404
404
  iframe frame and URL metadata; change the copy's path/query for a new state.
405
405
  - Flow visualization is multiple URL states: \`/checkout?step=shipping\`,
406
406
  \`/checkout?step=payment\`, \`/checkout?step=done\`, etc.
407
+ - When the user gives a named flow or numbered screen list, preserve that order
408
+ and create one screen per URL/path. Shorthand like
409
+ \`localhost:1234/onboarding/1\` means
410
+ \`http://localhost:1234/onboarding/1\`.
407
411
 
408
412
  ## Required Local Bridge
409
413
 
@@ -439,6 +443,20 @@ pnpm action add-localhost-screens '{
439
443
  }'
440
444
  \`\`\`
441
445
 
446
+ For a numbered flow the user describes in chat, keep the labels and order:
447
+
448
+ \`\`\`bash
449
+ pnpm action add-localhost-screens '{
450
+ "designId": "<design-id>",
451
+ "connectionId": "<connection-id>",
452
+ "routes": [
453
+ { "url": "localhost:1234/onboarding/1", "title": "Screen 1" },
454
+ { "url": "localhost:1234/onboarding/2", "title": "Screen 2" },
455
+ { "url": "localhost:1234/onboarding/3", "title": "Screen 3" }
456
+ ]
457
+ }'
458
+ \`\`\`
459
+
442
460
  If no \`routes\` or \`paths\` are supplied, \`add-localhost-screens\` uses every
443
461
  route from the latest localhost manifest.
444
462
 
@@ -1318,7 +1318,7 @@ function AgentPanelInner({
1318
1318
  data-agent-page-new-chat=""
1319
1319
  aria-label={t("agentPanel.newChat")}
1320
1320
  onClick={() => {
1321
- void addTab();
1321
+ addTab();
1322
1322
  }}
1323
1323
  className="pointer-events-auto inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background/95 px-2.5 text-xs font-medium text-foreground shadow-sm backdrop-blur transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
1324
1324
  >
@@ -1625,7 +1625,7 @@ export function MultiTabAssistantChat({
1625
1625
  return next;
1626
1626
  });
1627
1627
  if (shouldRefreshThreads) {
1628
- void refreshThreads();
1628
+ refreshThreads();
1629
1629
  }
1630
1630
  }
1631
1631
  }
@@ -47,7 +47,7 @@ export interface AgentNativeExtensionStorageOptions {
47
47
  export interface AgentNativeExtensionStorageContext {
48
48
  extensionId: string;
49
49
  slotId?: string;
50
- scope?: Exclude<AgentNativeExtensionStorageScope, "all"> | string;
50
+ scope?: Exclude<AgentNativeExtensionStorageScope, "all">;
51
51
  userId?: string;
52
52
  organizationId?: string;
53
53
  [key: string]: unknown;
@@ -576,7 +576,7 @@ function joinAssistantChunks(previous: string, next: string): string {
576
576
  if (!next) return previous;
577
577
  if (/\s$/.test(previous) || /^\s/.test(next)) return `${previous}${next}`;
578
578
  if (/^[.,!?;:)\]}'"`]/.test(next)) return `${previous}${next}`;
579
- if (/[(\[{'"`]$/.test(previous)) return `${previous}${next}`;
579
+ if (/[([{'"`]$/.test(previous)) return `${previous}${next}`;
580
580
  return `${previous} ${next}`;
581
581
  }
582
582
 
@@ -19,10 +19,7 @@ export function extensionNameToSlug(
19
19
  return sliced || "extension";
20
20
  }
21
21
 
22
- export function extensionPath(
23
- id: string,
24
- name?: string | null | undefined,
25
- ): string {
22
+ export function extensionPath(id: string, name?: string | null): string {
26
23
  const encodedId = encodeURIComponent(id);
27
24
  if (name === undefined) return `/extensions/${encodedId}`;
28
25
  return `/extensions/${encodedId}/${extensionNameToSlug(name)}`;
@@ -528,7 +528,7 @@ function expandRelativeUrls(text: string, agentUrl: string): string {
528
528
  if (!text || !agentUrl) return text;
529
529
  const base = publicAgentBaseUrl(agentUrl);
530
530
  return text.replace(
531
- /(^|[\s(\[<"'`])(\/[a-z0-9_-][a-z0-9_/?&=%#.,:-]*)/gi,
531
+ /(^|[\s([<"'`])(\/[a-z0-9_-][a-z0-9_/?&=%#.,:-]*)/gi,
532
532
  (_match, lead, path) => `${lead}${base}${path}`,
533
533
  );
534
534
  }
@@ -750,7 +750,7 @@ function safeUiSegment(value: string | undefined, fallback: string): string {
750
750
 
751
751
  // ChatGPT and Claude cache MCP App resource HTML by `ui://` URI. Bump this
752
752
  // when the shared shell changes in a way that must invalidate host caches.
753
- const MCP_APP_RESOURCE_SHELL_VERSION = "shell-v53";
753
+ const MCP_APP_RESOURCE_SHELL_VERSION = "shell-v54";
754
754
 
755
755
  function legacyDefaultMcpAppUri(config: MCPConfig, actionName: string): string {
756
756
  const app = safeUiSegment(config.appId ?? config.name, "agent-native");
@@ -1889,7 +1889,7 @@ async function isConnectTokenAllowed(
1889
1889
  */
1890
1890
  export async function verifyAuth(
1891
1891
  authHeader: string | undefined,
1892
- ownerEmailHeader?: string | undefined,
1892
+ ownerEmailHeader?: string,
1893
1893
  options: { allowDevOpen?: boolean; resourceUrl?: string | string[] } = {},
1894
1894
  ): Promise<{
1895
1895
  authed: boolean;
@@ -124,6 +124,7 @@ export function embedApp(
124
124
  let app = null;
125
125
  let appConnectPromise = null;
126
126
  let openAiBridge = null;
127
+ let lastOpenAiSyncSignature = null;
127
128
  let wrapperRequestId = 0;
128
129
  const wrapperRequests = new Map();
129
130
  let toolInput = {};
@@ -1533,11 +1534,19 @@ export function embedApp(
1533
1534
  setMessage("Ready to open.");
1534
1535
  return;
1535
1536
  }
1536
- setMessage("Loading app");
1537
1537
  try {
1538
1538
  const selfNavigate = shouldSelfNavigateToApp();
1539
1539
  if (startedFor === launchUrl) return;
1540
1540
  startedFor = launchUrl;
1541
+ // Only show the loading message when no frame is mounted yet. ChatGPT
1542
+ // fires openai:set_globals (and thus re-launch) constantly; calling
1543
+ // setMessage() unconditionally here wiped the just-mounted iframe out of
1544
+ // the stage on every redundant launch, so the app rendered and was
1545
+ // instantly blanked back to "Loading app" forever. renderFrame()/
1546
+ // transplant replace the stage themselves when they actually (re)mount.
1547
+ if (!appFrame) {
1548
+ setMessage("Loading app");
1549
+ }
1541
1550
  const embedUrl = withChatBridgeParam(launchUrl);
1542
1551
  if (selfNavigate && isEmbedStartUrl(embedUrl)) {
1543
1552
  if (shouldTransplantAppDocument()) {
@@ -1662,6 +1671,31 @@ export function embedApp(
1662
1671
  toolResultData = objectValue(data);
1663
1672
  openUrl = openLinkFrom(params, data);
1664
1673
  openStartUrl = embedStartUrlFrom(params, data);
1674
+ // ChatGPT/Codex fire openai:set_globals extremely often, and the listener
1675
+ // that calls this re-syncs on each one. Because this function calls
1676
+ // notifyHostHeight()/sendHostContext() (which the host reflects back as a
1677
+ // fresh set_globals), an unguarded sync becomes an infinite feedback storm
1678
+ // that starves the host until it shows its sad-face placeholder. Only do
1679
+ // the host round-trips + (re)launch when something we care about changed.
1680
+ // Deliberately EXCLUDE bridge.maxHeight: the host mutates it on every
1681
+ // set_globals as it echoes our height back, which would defeat the guard.
1682
+ let signature;
1683
+ try {
1684
+ signature = JSON.stringify([
1685
+ toolInput,
1686
+ openUrl,
1687
+ openStartUrl,
1688
+ bridge.displayMode,
1689
+ bridge.theme,
1690
+ bridge.locale
1691
+ ]);
1692
+ } catch {
1693
+ signature = null;
1694
+ }
1695
+ if (signature !== null && signature === lastOpenAiSyncSignature) {
1696
+ return true;
1697
+ }
1698
+ lastOpenAiSyncSignature = signature;
1665
1699
  updateTitle(data);
1666
1700
  updateOpenButton();
1667
1701
  updateDisplayButton();
@@ -17,8 +17,8 @@ function extractInvitationId(event: H3Event): string | undefined {
17
17
  if (fromRouter) return fromRouter;
18
18
  const path = getRequestURL(event).pathname;
19
19
  const match =
20
- path.match(/^\/([^\/]+)\/accept\/?$/) ??
21
- path.match(/\/org\/invitations\/([^\/]+)\/accept\/?$/);
20
+ path.match(/^\/([^/]+)\/accept\/?$/) ??
21
+ path.match(/\/org\/invitations\/([^/]+)\/accept\/?$/);
22
22
  return match?.[1] ? decodeURIComponent(match[1]) : undefined;
23
23
  }
24
24
 
@@ -28,9 +28,9 @@ function extractMemberEmail(event: H3Event): string | undefined {
28
28
  if (fromRouter) return fromRouter;
29
29
  const path = getRequestURL(event).pathname;
30
30
  const match =
31
- path.match(/^\/([^\/]+)\/role\/?$/) ??
32
- path.match(/^\/([^\/]+)\/?$/) ??
33
- path.match(/\/org\/members\/([^\/]+)(?:\/role)?\/?$/);
31
+ path.match(/^\/([^/]+)\/role\/?$/) ??
32
+ path.match(/^\/([^/]+)\/?$/) ??
33
+ path.match(/\/org\/members\/([^/]+)(?:\/role)?\/?$/);
34
34
  return match?.[1] ? decodeURIComponent(match[1]) : undefined;
35
35
  }
36
36
  const nanoid = (): string =>
@@ -100,7 +100,7 @@ export function createOrgPlugin(): NitroPluginDef {
100
100
  return listMembersHandler(event);
101
101
  }
102
102
  // Tail is /:email/role
103
- if (/^\/[^\/]+\/role\/?$/.test(tail)) {
103
+ if (/^\/[^/]+\/role\/?$/.test(tail)) {
104
104
  if (method !== "PUT") {
105
105
  setResponseStatus(event, 405);
106
106
  return { error: "Method not allowed" };
@@ -129,7 +129,7 @@ export function createOrgPlugin(): NitroPluginDef {
129
129
  return { error: "Method not allowed" };
130
130
  }
131
131
  // Tail is /:id/accept
132
- if (/^\/[^\/]+\/accept\/?$/.test(tail)) {
132
+ if (/^\/[^/]+\/accept\/?$/.test(tail)) {
133
133
  if (method !== "POST") {
134
134
  setResponseStatus(event, 405);
135
135
  return { error: "Method not allowed" };
@@ -434,7 +434,7 @@ export function expandRelativeUrls(text: string, agentUrl: string): string {
434
434
  // Path must start at boundary (start, whitespace, or punctuation that isn't
435
435
  // ':' — to avoid mangling `https://example.com/foo` or markdown link bodies).
436
436
  return text.replace(
437
- /(^|[\s(\[<"'`])(\/[a-z0-9_-][a-z0-9_/?&=%#.,:-]*)/gi,
437
+ /(^|[\s([<"'`])(\/[a-z0-9_-][a-z0-9_/?&=%#.,:-]*)/gi,
438
438
  (_match, lead, path) => `${lead}${base}${path}`,
439
439
  );
440
440
  }
@@ -171,7 +171,7 @@ const ACCESS_CONTROL_COLUMN_TOKENS = new Set([
171
171
  function normalizeIdentifier(value: string): string {
172
172
  return value
173
173
  .trim()
174
- .replace(/^["'`\[]/, "")
174
+ .replace(/^["'`[]/, "")
175
175
  .replace(/["'`\]]$/, "")
176
176
  .toLowerCase();
177
177
  }
@@ -1855,6 +1855,9 @@ function createAuthGuardFn(): (
1855
1855
  // the gate below 401s anonymous /_agent-native/* requests before any DB
1856
1856
  // query, so the database would never get warmed.
1857
1857
  if (p === "/_agent-native/health") return;
1858
+ if (getMethod(event) === "GET" && p.startsWith("/_agent-native/avatar/")) {
1859
+ return;
1860
+ }
1858
1861
  if (isPublicPath(normalizedUrl, publicPaths)) return;
1859
1862
  if (shouldBypassAuthForBuilderConnect(event, p)) return;
1860
1863
  if (isPublicWorkspacePageRequest(event, p, config)) {
@@ -0,0 +1,308 @@
1
+ import { FeatureNotConfiguredError } from "./credential-provider.js";
2
+ import {
3
+ getBuilderProxyOrigin,
4
+ resolveBuilderCredentials,
5
+ } from "./credential-provider.js";
6
+
7
+ const DEFAULT_TIMEOUT_MS = 120_000;
8
+
9
+ export interface BuilderDesignSystemIndexFile {
10
+ name: string;
11
+ data: Uint8Array;
12
+ mimeType?: string;
13
+ }
14
+
15
+ export interface BuilderDesignSystemIndexOptions {
16
+ projectName?: string;
17
+ description?: string;
18
+ githubRepoUrl?: string;
19
+ connectedProjectId?: string;
20
+ files?: BuilderDesignSystemIndexFile[];
21
+ selection?: Record<string, string[]>;
22
+ devToolsVersion?: string;
23
+ }
24
+
25
+ export interface BuilderDesignSystemIndexResult {
26
+ ok: true;
27
+ source: "builder";
28
+ projectId: string;
29
+ jobId: string;
30
+ designSystemId: string;
31
+ suggestedTitle: string | null;
32
+ builderUrl: string;
33
+ status: "in-progress";
34
+ }
35
+
36
+ interface BuilderDesignSystemCredentials {
37
+ privateKey: string;
38
+ publicKey: string;
39
+ userId: string | null;
40
+ }
41
+
42
+ interface UploadStartResponse {
43
+ uploads?: Array<{ idx: number; uploadUrl: string; uploadToken: string }>;
44
+ }
45
+
46
+ interface GenerateResponse {
47
+ projectId?: string;
48
+ jobId?: string;
49
+ designSystemId?: string;
50
+ }
51
+
52
+ function trimTrailingSlash(value: string): string {
53
+ return value.replace(/\/+$/, "");
54
+ }
55
+
56
+ export function getBuilderDesignSystemsBaseUrl(): string {
57
+ return (
58
+ process.env.BUILDER_DESIGN_SYSTEMS_BASE_URL ||
59
+ `${trimTrailingSlash(getBuilderProxyOrigin())}/design-systems/v1`
60
+ );
61
+ }
62
+
63
+ function getBuilderAppHost(): string {
64
+ return (
65
+ process.env.BUILDER_APP_HOST ||
66
+ process.env.BUILDER_PUBLIC_APP_HOST ||
67
+ "https://builder.io"
68
+ );
69
+ }
70
+
71
+ function makeBuilderDesignSystemUrl(
72
+ path: string,
73
+ credentials: BuilderDesignSystemCredentials,
74
+ ): URL {
75
+ const base = `${trimTrailingSlash(getBuilderDesignSystemsBaseUrl())}/`;
76
+ const url = new URL(path.replace(/^\/+/, ""), base);
77
+ url.searchParams.set("apiKey", credentials.publicKey);
78
+ if (credentials.userId) url.searchParams.set("userId", credentials.userId);
79
+ return url;
80
+ }
81
+
82
+ function makeBuilderHeaders(
83
+ credentials: BuilderDesignSystemCredentials,
84
+ ): Record<string, string> {
85
+ return {
86
+ Authorization: `Bearer ${credentials.privateKey}`,
87
+ "x-builder-api-key": credentials.publicKey,
88
+ ...(credentials.userId ? { "x-builder-user-id": credentials.userId } : {}),
89
+ };
90
+ }
91
+
92
+ async function resolveBuilderDesignSystemCredentials(): Promise<BuilderDesignSystemCredentials> {
93
+ const credentials = await resolveBuilderCredentials();
94
+ if (!credentials.privateKey || !credentials.publicKey) {
95
+ throw new FeatureNotConfiguredError({
96
+ requiredCredential: "BUILDER_PRIVATE_KEY",
97
+ message:
98
+ "Connect Builder.io before indexing a design system from Figma or code.",
99
+ builderConnectUrl: "/_agent-native/builder/connect",
100
+ });
101
+ }
102
+ return {
103
+ privateKey: credentials.privateKey,
104
+ publicKey: credentials.publicKey,
105
+ userId: credentials.userId ?? null,
106
+ };
107
+ }
108
+
109
+ function mimeTypeForFile(file: BuilderDesignSystemIndexFile): string {
110
+ if (file.mimeType?.trim()) return file.mimeType.trim();
111
+ const lower = file.name.toLowerCase();
112
+ if (lower.endsWith(".fig")) return "application/octet-stream";
113
+ if (lower.endsWith(".pdf")) return "application/pdf";
114
+ if (lower.endsWith(".md") || lower.endsWith(".markdown"))
115
+ return "text/markdown";
116
+ if (lower.endsWith(".json")) return "application/json";
117
+ if (lower.endsWith(".css")) return "text/css";
118
+ return "text/plain";
119
+ }
120
+
121
+ function makeBody(bytes: Uint8Array, mimeType: string): BodyInit {
122
+ return typeof Blob !== "undefined"
123
+ ? new Blob([bytes as unknown as BlobPart], { type: mimeType })
124
+ : (bytes as unknown as BodyInit);
125
+ }
126
+
127
+ async function fetchWithTimeout(
128
+ url: string | URL,
129
+ init: RequestInit,
130
+ timeoutMs = DEFAULT_TIMEOUT_MS,
131
+ ): Promise<Response> {
132
+ const controller = new AbortController();
133
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
134
+ try {
135
+ return await fetch(url, { ...init, signal: controller.signal });
136
+ } finally {
137
+ clearTimeout(timer);
138
+ }
139
+ }
140
+
141
+ async function parseErrorBody(response: Response): Promise<string> {
142
+ const text = await response.text().catch(() => "");
143
+ if (!text) return response.statusText || `HTTP ${response.status}`;
144
+ try {
145
+ const json = JSON.parse(text) as { error?: unknown };
146
+ if (typeof json.error === "string") return json.error;
147
+ if (json.error && typeof json.error === "object") {
148
+ return JSON.stringify(json.error).slice(0, 500);
149
+ }
150
+ } catch {}
151
+ return text.replace(/\s+/g, " ").trim().slice(0, 500);
152
+ }
153
+
154
+ async function assertOk(response: Response, label: string): Promise<void> {
155
+ if (response.ok) return;
156
+ throw new Error(
157
+ `${label} (${response.status}): ${await parseErrorBody(response)}`,
158
+ );
159
+ }
160
+
161
+ async function uploadToResumableUrl(
162
+ slot: { uploadUrl: string },
163
+ file: BuilderDesignSystemIndexFile,
164
+ ): Promise<void> {
165
+ const mimeType = mimeTypeForFile(file);
166
+ const bytes = file.data;
167
+ const start = await fetchWithTimeout(slot.uploadUrl, {
168
+ method: "POST",
169
+ headers: {
170
+ "x-goog-resumable": "start",
171
+ "x-goog-content-length-range": `0,${bytes.byteLength}`,
172
+ "Content-Type": mimeType,
173
+ },
174
+ });
175
+ await assertOk(start, "Builder design-system upload session failed");
176
+ const sessionUrl = start.headers.get("Location");
177
+ if (!sessionUrl) {
178
+ throw new Error("Builder design-system upload session returned no URL.");
179
+ }
180
+
181
+ const response = await fetchWithTimeout(sessionUrl, {
182
+ method: "PUT",
183
+ headers: {
184
+ "Content-Range": `bytes 0-${bytes.byteLength - 1}/${bytes.byteLength}`,
185
+ "Content-Type": mimeType,
186
+ },
187
+ body: makeBody(bytes, mimeType),
188
+ });
189
+ await assertOk(response, "Builder design-system file upload failed");
190
+ }
191
+
192
+ function nonEmptyFiles(
193
+ files: BuilderDesignSystemIndexFile[] | undefined,
194
+ ): BuilderDesignSystemIndexFile[] {
195
+ return (files ?? []).filter((file) => file.data.byteLength > 0);
196
+ }
197
+
198
+ export function builderDesignSystemUrl(designSystemId?: string | null): string {
199
+ const host = trimTrailingSlash(getBuilderAppHost());
200
+ return designSystemId
201
+ ? `${host}/app/design-system-intelligence/${encodeURIComponent(
202
+ designSystemId,
203
+ )}`
204
+ : `${host}/app/design-system-intelligence`;
205
+ }
206
+
207
+ export async function startBuilderDesignSystemIndex(
208
+ options: BuilderDesignSystemIndexOptions,
209
+ ): Promise<BuilderDesignSystemIndexResult> {
210
+ const files = nonEmptyFiles(options.files);
211
+ const description = options.description?.trim();
212
+ if (description) {
213
+ files.unshift({
214
+ name: "additional-context.txt",
215
+ data: new TextEncoder().encode(description),
216
+ mimeType: "text/plain",
217
+ });
218
+ }
219
+ if (
220
+ files.length === 0 &&
221
+ !options.githubRepoUrl &&
222
+ !options.connectedProjectId
223
+ ) {
224
+ throw new Error(
225
+ "Provide at least one .fig/code/text file or a GitHub repository URL to index with Builder.",
226
+ );
227
+ }
228
+
229
+ const credentials = await resolveBuilderDesignSystemCredentials();
230
+ let uploadTokens: string[] = [];
231
+ if (files.length > 0) {
232
+ const uploadStart = await fetchWithTimeout(
233
+ makeBuilderDesignSystemUrl("upload/start", credentials),
234
+ {
235
+ method: "POST",
236
+ headers: {
237
+ ...makeBuilderHeaders(credentials),
238
+ "Content-Type": "application/json",
239
+ },
240
+ body: JSON.stringify({
241
+ attachments: files.map((file) => ({
242
+ name: file.name,
243
+ mimetype: mimeTypeForFile(file),
244
+ declaredSize: file.data.byteLength,
245
+ })),
246
+ }),
247
+ },
248
+ );
249
+ await assertOk(uploadStart, "Builder design-system upload start failed");
250
+ const uploadJson = (await uploadStart.json()) as UploadStartResponse;
251
+ const slots = [...(uploadJson.uploads ?? [])].sort((a, b) => a.idx - b.idx);
252
+ if (slots.length !== files.length) {
253
+ throw new Error("Builder did not return upload slots for all files.");
254
+ }
255
+ for (let i = 0; i < slots.length; i++) {
256
+ if (slots[i].idx !== i) {
257
+ throw new Error(`Builder upload slot mismatch: expected ${i}.`);
258
+ }
259
+ await uploadToResumableUrl(slots[i], files[i]);
260
+ }
261
+ uploadTokens = slots.map((slot) => slot.uploadToken);
262
+ }
263
+
264
+ const generate = await fetchWithTimeout(
265
+ makeBuilderDesignSystemUrl("generate", credentials),
266
+ {
267
+ method: "POST",
268
+ headers: {
269
+ ...makeBuilderHeaders(credentials),
270
+ "Content-Type": "application/json",
271
+ },
272
+ body: JSON.stringify({
273
+ uploads: uploadTokens,
274
+ ...(options.projectName?.trim()
275
+ ? { projectName: options.projectName.trim() }
276
+ : {}),
277
+ ...(options.githubRepoUrl?.trim()
278
+ ? { githubRepoUrl: options.githubRepoUrl.trim() }
279
+ : {}),
280
+ ...(options.connectedProjectId?.trim()
281
+ ? { connectedProjectId: options.connectedProjectId.trim() }
282
+ : {}),
283
+ ...(options.selection ? { selection: options.selection } : {}),
284
+ ...(options.devToolsVersion?.trim()
285
+ ? { devToolsVersion: options.devToolsVersion.trim() }
286
+ : {}),
287
+ }),
288
+ },
289
+ );
290
+ await assertOk(generate, "Builder design-system indexing failed");
291
+ const generated = (await generate.json()) as GenerateResponse;
292
+ if (!generated.projectId || !generated.jobId || !generated.designSystemId) {
293
+ throw new Error(
294
+ "Builder design-system indexing returned an incomplete response.",
295
+ );
296
+ }
297
+
298
+ return {
299
+ ok: true,
300
+ source: "builder",
301
+ projectId: generated.projectId,
302
+ jobId: generated.jobId,
303
+ designSystemId: generated.designSystemId,
304
+ suggestedTitle: options.projectName?.trim() || null,
305
+ builderUrl: builderDesignSystemUrl(generated.designSystemId),
306
+ status: "in-progress",
307
+ };
308
+ }
@@ -383,6 +383,31 @@ function parseBuilderCallbackBoolean(
383
383
  // inlines them in the DOM. Mirrors SAFE_DATA_IMAGE in sanitize-html.ts.
384
384
  export const AVATAR_RASTER_MIME = /^data:image\/(png|jpe?g|gif|webp);/i;
385
385
 
386
+ export function resolveAvatarEmailParam(
387
+ pathname: string,
388
+ appBasePath = "",
389
+ ): string {
390
+ const base = appBasePath.replace(/\/+$/, "");
391
+ const avatarPaths = Array.from(
392
+ new Set([`${base}/_agent-native/avatar/`, "/_agent-native/avatar/"]),
393
+ );
394
+
395
+ for (const avatarPath of avatarPaths) {
396
+ const avatarIndex = pathname.indexOf(avatarPath);
397
+ if (avatarIndex >= 0) {
398
+ return pathname
399
+ .slice(avatarIndex + avatarPath.length)
400
+ .replace(/^\/+/, "")
401
+ .split("/")[0];
402
+ }
403
+ }
404
+
405
+ const firstSegment = pathname.replace(/^\/+/, "").split("/")[0] ?? "";
406
+ if (!firstSegment || firstSegment === "_agent-native") return "";
407
+ if (base && firstSegment === base.replace(/^\/+/, "")) return "";
408
+ return firstSegment;
409
+ }
410
+
386
411
  async function detectUsageEngineName(
387
412
  event: H3Event,
388
413
  userEmail: string | undefined,
@@ -3010,9 +3035,10 @@ export function createCoreRoutesPlugin(
3010
3035
  `${P}/avatar`,
3011
3036
  defineEventHandler(async (event: H3Event) => {
3012
3037
  const method = getMethod(event);
3013
- const emailParam = (event.url?.pathname || "")
3014
- .replace(/^\/+/, "")
3015
- .split("/")[0];
3038
+ const emailParam = resolveAvatarEmailParam(
3039
+ event.url?.pathname || "",
3040
+ getConfiguredAppBasePath(),
3041
+ );
3016
3042
 
3017
3043
  if (method === "GET") {
3018
3044
  if (!emailParam) {
@@ -386,6 +386,14 @@ export {
386
386
  deleteBuilderCredentials,
387
387
  resolveSecret,
388
388
  } from "./credential-provider.js";
389
+ export {
390
+ builderDesignSystemUrl,
391
+ getBuilderDesignSystemsBaseUrl,
392
+ startBuilderDesignSystemIndex,
393
+ type BuilderDesignSystemIndexFile,
394
+ type BuilderDesignSystemIndexOptions,
395
+ type BuilderDesignSystemIndexResult,
396
+ } from "./builder-design-systems.js";
389
397
  export {
390
398
  getBuilderBranchProjectId,
391
399
  isBuilderBranchingEnabled,