@agent-native/core 0.127.3 → 0.128.1

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 (457) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +101 -0
  3. package/corpus/core/README.md +5 -26
  4. package/corpus/core/docs/content/actions.mdx +47 -4
  5. package/corpus/core/docs/content/native-chat-ui.mdx +7 -0
  6. package/corpus/core/docs/content/organizations-teams-permissions.mdx +109 -3
  7. package/corpus/core/docs/content/template-design-collaboration-and-full-apps.mdx +15 -0
  8. package/corpus/core/docs/content/toolkit-org-team.mdx +72 -12
  9. package/corpus/core/package.json +2 -1
  10. package/corpus/core/src/a2a/artifact-response.ts +36 -29
  11. package/corpus/core/src/action.ts +83 -2
  12. package/corpus/core/src/chat-threads/store.ts +48 -43
  13. package/corpus/core/src/cli/skills-content/design-visual-edit-skill.ts +46 -3
  14. package/corpus/core/src/client/AgentPanel.tsx +26 -6
  15. package/corpus/core/src/client/AssistantChat.tsx +63 -7
  16. package/corpus/core/src/client/DefaultSpinner.tsx +29 -0
  17. package/corpus/core/src/client/org/TeamPage.tsx +161 -6
  18. package/corpus/core/src/client/org/hooks.ts +67 -0
  19. package/corpus/core/src/client/org/index.ts +9 -0
  20. package/corpus/core/src/client/transcription/use-live-transcription.ts +62 -10
  21. package/corpus/core/src/db/client.ts +42 -3
  22. package/corpus/core/src/db/ensure-additive-columns.ts +60 -33
  23. package/corpus/core/src/deploy/build.ts +31 -2
  24. package/corpus/core/src/mcp-client/routes.ts +10 -1
  25. package/corpus/core/src/org/app-roles-handlers.ts +152 -0
  26. package/corpus/core/src/org/app-roles.ts +355 -0
  27. package/corpus/core/src/org/index.ts +27 -1
  28. package/corpus/core/src/org/migrations.ts +22 -0
  29. package/corpus/core/src/org/plugin.ts +27 -0
  30. package/corpus/core/src/org/schema.ts +15 -0
  31. package/corpus/core/src/scripts/docs/search.ts +17 -0
  32. package/corpus/core/src/scripts/docs/source-search.ts +10 -0
  33. package/corpus/core/src/server/agent-chat/script-entries.ts +3 -0
  34. package/corpus/core/src/server/agent-chat-plugin.ts +53 -52
  35. package/corpus/core/src/server/builder-design-systems.ts +299 -82
  36. package/corpus/core/src/server/core-routes-plugin.ts +6 -1
  37. package/corpus/core/src/server/framework-request-handler.ts +44 -6
  38. package/corpus/core/src/server/index.ts +8 -0
  39. package/corpus/core/src/templates/default/.agents/skills/actions/SKILL.md +32 -0
  40. package/corpus/core/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +7 -0
  41. package/corpus/core/src/templates/default/.agents/skills/security/SKILL.md +31 -0
  42. package/corpus/core/src/templates/headless/.agents/skills/actions/SKILL.md +32 -0
  43. package/corpus/core/src/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -0
  44. package/corpus/core/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +7 -0
  45. package/corpus/core/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +40 -0
  46. package/corpus/core/src/templates/workspace-core/.agents/skills/security/SKILL.md +31 -0
  47. package/corpus/core/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +4 -0
  48. package/corpus/templates/analytics/.agents/skills/actions/SKILL.md +32 -0
  49. package/corpus/templates/analytics/.agents/skills/security/SKILL.md +31 -0
  50. package/corpus/templates/analytics/.agents/skills/sharing/SKILL.md +4 -0
  51. package/corpus/templates/analytics/_gitignore +2 -0
  52. package/corpus/templates/analytics/actions/data-source-status.ts +21 -12
  53. package/corpus/templates/analytics/app/global.css +7 -7
  54. package/corpus/templates/analytics/app/i18n-data.ts +30 -0
  55. package/corpus/templates/analytics/app/lib/data-source-status.ts +2 -1
  56. package/corpus/templates/analytics/app/pages/DataSources.tsx +54 -10
  57. package/corpus/templates/analytics/changelog/2026-07-28-analytics-pages-load-reliably-with-large-dashboard-workspaces.md +6 -0
  58. package/corpus/templates/analytics/changelog/2026-07-28-dashboard-loading-shimmer-now-flows-smoothly-in-one-directi.md +6 -0
  59. package/corpus/templates/analytics/server/lib/first-party-dashboard-repair.ts +5 -4
  60. package/corpus/templates/analytics/server/plugins/agent-chat.ts +54 -42
  61. package/corpus/templates/analytics/server/plugins/db.ts +1 -18
  62. package/corpus/templates/assets/.agents/skills/actions/SKILL.md +32 -0
  63. package/corpus/templates/assets/.agents/skills/security/SKILL.md +31 -0
  64. package/corpus/templates/assets/.agents/skills/sharing/SKILL.md +4 -0
  65. package/corpus/templates/assets/_gitignore +2 -0
  66. package/corpus/templates/brain/.agents/skills/actions/SKILL.md +32 -0
  67. package/corpus/templates/brain/.agents/skills/adding-a-feature/SKILL.md +7 -0
  68. package/corpus/templates/brain/.agents/skills/security/SKILL.md +31 -0
  69. package/corpus/templates/brain/.agents/skills/sharing/SKILL.md +4 -0
  70. package/corpus/templates/calendar/.agents/skills/actions/SKILL.md +32 -0
  71. package/corpus/templates/calendar/.agents/skills/security/SKILL.md +31 -0
  72. package/corpus/templates/calendar/.agents/skills/sharing/SKILL.md +4 -0
  73. package/corpus/templates/calendar/_gitignore +2 -0
  74. package/corpus/templates/chat/.agents/skills/actions/SKILL.md +32 -0
  75. package/corpus/templates/chat/.agents/skills/adding-a-feature/SKILL.md +7 -0
  76. package/corpus/templates/chat/.agents/skills/security/SKILL.md +31 -0
  77. package/corpus/templates/chat/.agents/skills/sharing/SKILL.md +4 -0
  78. package/corpus/templates/clips/.agents/skills/actions/SKILL.md +32 -0
  79. package/corpus/templates/clips/.agents/skills/security/SKILL.md +31 -0
  80. package/corpus/templates/clips/_gitignore +2 -0
  81. package/corpus/templates/clips/actions/request-transcript.ts +77 -11
  82. package/corpus/templates/clips/actions/save-browser-transcript.ts +28 -7
  83. package/corpus/templates/clips/app/routes/record.tsx +11 -3
  84. package/corpus/templates/clips/changelog/2026-07-28-browser-recordings-no-longer-save-a-truncated-transcript-as-.md +6 -0
  85. package/corpus/templates/clips/changelog/2026-07-28-browser-recordings-no-longer-stop-transcribing-partway-throu.md +6 -0
  86. package/corpus/templates/clips/changelog/2026-07-28-desktop-full-screen-clips-now-get-an-instant-local-transcrip.md +6 -0
  87. package/corpus/templates/clips/changelog/2026-07-28-long-transcriptions-no-longer-show-a-false-transcription-sto.md +6 -0
  88. package/corpus/templates/clips/changelog/2026-07-28-restarting-for-an-update-now-installs-the-newest-release-ins.md +6 -0
  89. package/corpus/templates/clips/changelog/2026-07-28-the-meeting-recording-pill-now-anchors-to-the-right-edge-of-.md +6 -0
  90. package/corpus/templates/clips/desktop/src/lib/audio-meter.ts +31 -2
  91. package/corpus/templates/clips/desktop/src/lib/recorder.ts +114 -13
  92. package/corpus/templates/clips/desktop/src/lib/updater.ts +12 -4
  93. package/corpus/templates/clips/desktop/src/overlays/live-audio-bars.tsx +40 -15
  94. package/corpus/templates/clips/desktop/src/styles.css +10 -7
  95. package/corpus/templates/clips/desktop/src-tauri/src/recording_indicator.rs +21 -14
  96. package/corpus/templates/clips/shared/transcript-status.ts +10 -0
  97. package/corpus/templates/content/.agents/skills/actions/SKILL.md +32 -0
  98. package/corpus/templates/content/.agents/skills/security/SKILL.md +31 -0
  99. package/corpus/templates/content/.agents/skills/sharing/SKILL.md +4 -0
  100. package/corpus/templates/content/_gitignore +2 -0
  101. package/corpus/templates/crm/.agents/skills/sharing/SKILL.md +4 -0
  102. package/corpus/templates/design/.agents/skills/actions/SKILL.md +32 -0
  103. package/corpus/templates/design/.agents/skills/security/SKILL.md +31 -0
  104. package/corpus/templates/design/.agents/skills/sharing/SKILL.md +4 -0
  105. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +46 -3
  106. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +7 -5
  107. package/corpus/templates/design/_gitignore +2 -0
  108. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +2 -2
  109. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +7 -4
  110. package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +1 -1
  111. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +10 -4
  112. package/corpus/templates/design/app/i18n-data.ts +10 -0
  113. package/corpus/templates/design/app/lib/builder-design-system-upload.ts +280 -0
  114. package/corpus/templates/design/app/lib/design-ui-events.ts +5 -0
  115. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +90 -54
  116. package/corpus/templates/design/app/root.tsx +18 -1
  117. package/corpus/templates/design/changelog/2026-07-28-figma-fig-uploads-now-show-indexing-progress-and-an-open-in-.md +6 -0
  118. package/corpus/templates/design/changelog/2026-07-28-show-or-hide-design-editing-chrome-with-cmd-on-apple-devices.md +6 -0
  119. package/corpus/templates/design/server/handlers/design-system-decode-job-status.ts +49 -0
  120. package/corpus/templates/design/server/handlers/design-system-upload-start.ts +87 -0
  121. package/corpus/templates/design/server/handlers/index-design-system-sources.ts +82 -0
  122. package/corpus/templates/design/server/routes/api/design-system-decode-job-status.get.ts +1 -0
  123. package/corpus/templates/design/server/routes/api/design-system-upload-start.post.ts +1 -0
  124. package/corpus/templates/design/server/routes/api/index-design-system-sources.post.ts +1 -0
  125. package/corpus/templates/dispatch/.agents/skills/actions/SKILL.md +32 -0
  126. package/corpus/templates/dispatch/.agents/skills/security/SKILL.md +31 -0
  127. package/corpus/templates/dispatch/.agents/skills/sharing/SKILL.md +4 -0
  128. package/corpus/templates/dispatch/_gitignore +2 -0
  129. package/corpus/templates/forms/.agents/skills/actions/SKILL.md +32 -0
  130. package/corpus/templates/forms/.agents/skills/security/SKILL.md +31 -0
  131. package/corpus/templates/forms/.agents/skills/sharing/SKILL.md +4 -0
  132. package/corpus/templates/forms/_gitignore +2 -0
  133. package/corpus/templates/macros/.agents/skills/security/SKILL.md +31 -0
  134. package/corpus/templates/macros/.agents/skills/sharing/SKILL.md +4 -0
  135. package/corpus/templates/macros/_gitignore +2 -0
  136. package/corpus/templates/mail/.agents/skills/actions/SKILL.md +32 -0
  137. package/corpus/templates/mail/.agents/skills/security/SKILL.md +31 -0
  138. package/corpus/templates/mail/.agents/skills/sharing/SKILL.md +4 -0
  139. package/corpus/templates/mail/_gitignore +2 -0
  140. package/corpus/templates/plan/.agents/skills/actions/SKILL.md +32 -0
  141. package/corpus/templates/plan/.agents/skills/adding-a-feature/SKILL.md +7 -0
  142. package/corpus/templates/plan/.agents/skills/security/SKILL.md +31 -0
  143. package/corpus/templates/plan/.agents/skills/sharing/SKILL.md +4 -0
  144. package/corpus/templates/plan/_gitignore +2 -0
  145. package/corpus/templates/slides/.agents/skills/actions/SKILL.md +32 -0
  146. package/corpus/templates/slides/.agents/skills/security/SKILL.md +31 -0
  147. package/corpus/templates/slides/.agents/skills/sharing/SKILL.md +4 -0
  148. package/corpus/templates/slides/_gitignore +2 -0
  149. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +126 -53
  150. package/corpus/templates/slides/app/components/design-system/builder-design-system-upload.ts +271 -0
  151. package/corpus/templates/slides/app/components/design-system/builder-index-response.ts +1 -1
  152. package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
  153. package/corpus/templates/slides/server/handlers/design-system-decode-job-status.ts +49 -0
  154. package/corpus/templates/slides/server/handlers/design-system-upload-start.ts +87 -0
  155. package/corpus/templates/slides/server/handlers/index-design-system-sources.ts +79 -0
  156. package/corpus/templates/slides/server/routes/api/design-system-decode-job-status.get.ts +1 -0
  157. package/corpus/templates/slides/server/routes/api/design-system-upload-start.post.ts +1 -0
  158. package/corpus/templates/slides/server/routes/api/index-design-system-sources.post.ts +1 -0
  159. package/corpus/templates/tasks/.agents/skills/actions/SKILL.md +32 -0
  160. package/corpus/templates/tasks/.agents/skills/adding-a-feature/SKILL.md +7 -0
  161. package/corpus/templates/tasks/.agents/skills/security/SKILL.md +31 -0
  162. package/corpus/templates/tasks/.agents/skills/sharing/SKILL.md +4 -0
  163. package/corpus/templates/tasks/_gitignore +2 -0
  164. package/dist/a2a/artifact-response.d.ts.map +1 -1
  165. package/dist/a2a/artifact-response.js +30 -29
  166. package/dist/a2a/artifact-response.js.map +1 -1
  167. package/dist/action.d.ts +36 -0
  168. package/dist/action.d.ts.map +1 -1
  169. package/dist/action.js +37 -2
  170. package/dist/action.js.map +1 -1
  171. package/dist/chat-threads/store.d.ts +12 -0
  172. package/dist/chat-threads/store.d.ts.map +1 -1
  173. package/dist/chat-threads/store.js +48 -42
  174. package/dist/chat-threads/store.js.map +1 -1
  175. package/dist/cli/skills-content/design-visual-edit-skill.d.ts +1 -1
  176. package/dist/cli/skills-content/design-visual-edit-skill.d.ts.map +1 -1
  177. package/dist/cli/skills-content/design-visual-edit-skill.js +46 -3
  178. package/dist/cli/skills-content/design-visual-edit-skill.js.map +1 -1
  179. package/dist/client/AgentPanel.d.ts +3 -1
  180. package/dist/client/AgentPanel.d.ts.map +1 -1
  181. package/dist/client/AgentPanel.js +11 -9
  182. package/dist/client/AgentPanel.js.map +1 -1
  183. package/dist/client/AssistantChat.d.ts.map +1 -1
  184. package/dist/client/AssistantChat.js +57 -6
  185. package/dist/client/AssistantChat.js.map +1 -1
  186. package/dist/client/DefaultSpinner.d.ts +7 -0
  187. package/dist/client/DefaultSpinner.d.ts.map +1 -1
  188. package/dist/client/DefaultSpinner.js +25 -1
  189. package/dist/client/DefaultSpinner.js.map +1 -1
  190. package/dist/client/org/TeamPage.d.ts +13 -1
  191. package/dist/client/org/TeamPage.d.ts.map +1 -1
  192. package/dist/client/org/TeamPage.js +41 -11
  193. package/dist/client/org/TeamPage.js.map +1 -1
  194. package/dist/client/org/hooks.d.ts +37 -0
  195. package/dist/client/org/hooks.d.ts.map +1 -1
  196. package/dist/client/org/hooks.js +37 -0
  197. package/dist/client/org/hooks.js.map +1 -1
  198. package/dist/client/org/index.d.ts +3 -2
  199. package/dist/client/org/index.d.ts.map +1 -1
  200. package/dist/client/org/index.js +1 -1
  201. package/dist/client/org/index.js.map +1 -1
  202. package/dist/client/transcription/use-live-transcription.d.ts +8 -0
  203. package/dist/client/transcription/use-live-transcription.d.ts.map +1 -1
  204. package/dist/client/transcription/use-live-transcription.js +38 -11
  205. package/dist/client/transcription/use-live-transcription.js.map +1 -1
  206. package/dist/collab/routes.d.ts +1 -1
  207. package/dist/db/client.d.ts +12 -0
  208. package/dist/db/client.d.ts.map +1 -1
  209. package/dist/db/client.js +40 -3
  210. package/dist/db/client.js.map +1 -1
  211. package/dist/db/ensure-additive-columns.d.ts.map +1 -1
  212. package/dist/db/ensure-additive-columns.js +54 -32
  213. package/dist/db/ensure-additive-columns.js.map +1 -1
  214. package/dist/deploy/build.d.ts +1 -0
  215. package/dist/deploy/build.d.ts.map +1 -1
  216. package/dist/deploy/build.js +23 -2
  217. package/dist/deploy/build.js.map +1 -1
  218. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  219. package/dist/mcp-client/routes.d.ts +3 -1
  220. package/dist/mcp-client/routes.d.ts.map +1 -1
  221. package/dist/mcp-client/routes.js +8 -2
  222. package/dist/mcp-client/routes.js.map +1 -1
  223. package/dist/notifications/routes.d.ts +1 -1
  224. package/dist/org/app-roles-handlers.d.ts +27 -0
  225. package/dist/org/app-roles-handlers.d.ts.map +1 -0
  226. package/dist/org/app-roles-handlers.js +125 -0
  227. package/dist/org/app-roles-handlers.js.map +1 -0
  228. package/dist/org/app-roles.d.ts +136 -0
  229. package/dist/org/app-roles.d.ts.map +1 -0
  230. package/dist/org/app-roles.js +216 -0
  231. package/dist/org/app-roles.js.map +1 -0
  232. package/dist/org/index.d.ts +4 -1
  233. package/dist/org/index.d.ts.map +1 -1
  234. package/dist/org/index.js +3 -1
  235. package/dist/org/index.js.map +1 -1
  236. package/dist/org/migrations.d.ts.map +1 -1
  237. package/dist/org/migrations.js +22 -0
  238. package/dist/org/migrations.js.map +1 -1
  239. package/dist/org/plugin.d.ts +2 -0
  240. package/dist/org/plugin.d.ts.map +1 -1
  241. package/dist/org/plugin.js +20 -0
  242. package/dist/org/plugin.js.map +1 -1
  243. package/dist/org/schema.d.ts +143 -0
  244. package/dist/org/schema.d.ts.map +1 -1
  245. package/dist/org/schema.js +14 -0
  246. package/dist/org/schema.js.map +1 -1
  247. package/dist/provider-api/actions/custom-provider-registration.d.ts +8 -8
  248. package/dist/provider-api/actions/provider-api.d.ts +4 -4
  249. package/dist/scripts/docs/search.d.ts.map +1 -1
  250. package/dist/scripts/docs/search.js +15 -0
  251. package/dist/scripts/docs/search.js.map +1 -1
  252. package/dist/scripts/docs/source-search.d.ts +7 -0
  253. package/dist/scripts/docs/source-search.d.ts.map +1 -1
  254. package/dist/scripts/docs/source-search.js +9 -0
  255. package/dist/scripts/docs/source-search.js.map +1 -1
  256. package/dist/secrets/routes.d.ts +9 -9
  257. package/dist/server/agent-chat/script-entries.d.ts.map +1 -1
  258. package/dist/server/agent-chat/script-entries.js +3 -0
  259. package/dist/server/agent-chat/script-entries.js.map +1 -1
  260. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  261. package/dist/server/agent-chat-plugin.js +46 -48
  262. package/dist/server/agent-chat-plugin.js.map +1 -1
  263. package/dist/server/builder-design-systems.d.ts +49 -0
  264. package/dist/server/builder-design-systems.d.ts.map +1 -1
  265. package/dist/server/builder-design-systems.js +221 -69
  266. package/dist/server/builder-design-systems.js.map +1 -1
  267. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  268. package/dist/server/core-routes-plugin.js +6 -1
  269. package/dist/server/core-routes-plugin.js.map +1 -1
  270. package/dist/server/framework-request-handler.d.ts.map +1 -1
  271. package/dist/server/framework-request-handler.js +42 -6
  272. package/dist/server/framework-request-handler.js.map +1 -1
  273. package/dist/server/index.d.ts +1 -1
  274. package/dist/server/index.d.ts.map +1 -1
  275. package/dist/server/index.js +1 -1
  276. package/dist/server/index.js.map +1 -1
  277. package/dist/server/realtime-token.d.ts +1 -1
  278. package/dist/templates/chat/.agents/skills/actions/SKILL.md +32 -0
  279. package/dist/templates/chat/.agents/skills/adding-a-feature/SKILL.md +7 -0
  280. package/dist/templates/chat/.agents/skills/security/SKILL.md +31 -0
  281. package/dist/templates/chat/.agents/skills/sharing/SKILL.md +4 -0
  282. package/dist/templates/default/.agents/skills/actions/SKILL.md +32 -0
  283. package/dist/templates/default/.agents/skills/adding-a-feature/SKILL.md +7 -0
  284. package/dist/templates/default/.agents/skills/security/SKILL.md +31 -0
  285. package/dist/templates/headless/.agents/skills/actions/SKILL.md +32 -0
  286. package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -0
  287. package/dist/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +7 -0
  288. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +40 -0
  289. package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +31 -0
  290. package/dist/templates/workspace-core/.agents/skills/sharing/SKILL.md +4 -0
  291. package/docs/content/actions.mdx +47 -4
  292. package/docs/content/native-chat-ui.mdx +7 -0
  293. package/docs/content/organizations-teams-permissions.mdx +109 -3
  294. package/docs/content/template-design-collaboration-and-full-apps.mdx +15 -0
  295. package/docs/content/toolkit-org-team.mdx +72 -12
  296. package/package.json +2 -1
  297. package/src/a2a/artifact-response.ts +36 -29
  298. package/src/action.ts +83 -2
  299. package/src/chat-threads/store.ts +48 -43
  300. package/src/cli/skills-content/design-visual-edit-skill.ts +46 -3
  301. package/src/client/AgentPanel.tsx +26 -6
  302. package/src/client/AssistantChat.tsx +63 -7
  303. package/src/client/DefaultSpinner.tsx +29 -0
  304. package/src/client/org/TeamPage.tsx +161 -6
  305. package/src/client/org/hooks.ts +67 -0
  306. package/src/client/org/index.ts +9 -0
  307. package/src/client/transcription/use-live-transcription.ts +62 -10
  308. package/src/db/client.ts +42 -3
  309. package/src/db/ensure-additive-columns.ts +60 -33
  310. package/src/deploy/build.ts +31 -2
  311. package/src/mcp-client/routes.ts +10 -1
  312. package/src/org/app-roles-handlers.ts +152 -0
  313. package/src/org/app-roles.ts +355 -0
  314. package/src/org/index.ts +27 -1
  315. package/src/org/migrations.ts +22 -0
  316. package/src/org/plugin.ts +27 -0
  317. package/src/org/schema.ts +15 -0
  318. package/src/scripts/docs/search.ts +17 -0
  319. package/src/scripts/docs/source-search.ts +10 -0
  320. package/src/server/agent-chat/script-entries.ts +3 -0
  321. package/src/server/agent-chat-plugin.ts +53 -52
  322. package/src/server/builder-design-systems.ts +299 -82
  323. package/src/server/core-routes-plugin.ts +6 -1
  324. package/src/server/framework-request-handler.ts +44 -6
  325. package/src/server/index.ts +8 -0
  326. package/src/templates/chat/.agents/skills/actions/SKILL.md +32 -0
  327. package/src/templates/chat/.agents/skills/adding-a-feature/SKILL.md +7 -0
  328. package/src/templates/chat/.agents/skills/security/SKILL.md +31 -0
  329. package/src/templates/chat/.agents/skills/sharing/SKILL.md +4 -0
  330. package/src/templates/default/.agents/skills/actions/SKILL.md +32 -0
  331. package/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +7 -0
  332. package/src/templates/default/.agents/skills/security/SKILL.md +31 -0
  333. package/src/templates/headless/.agents/skills/actions/SKILL.md +32 -0
  334. package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -0
  335. package/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +7 -0
  336. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +40 -0
  337. package/src/templates/workspace-core/.agents/skills/security/SKILL.md +31 -0
  338. package/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +4 -0
  339. package/corpus/core/src/templates/chat/.agents/skills/actions/SKILL.md +0 -530
  340. package/corpus/core/src/templates/chat/.agents/skills/adding-a-feature/SKILL.md +0 -191
  341. package/corpus/core/src/templates/chat/.agents/skills/agent-native-docs/SKILL.md +0 -115
  342. package/corpus/core/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +0 -177
  343. package/corpus/core/src/templates/chat/.agents/skills/capture-learnings/SKILL.md +0 -89
  344. package/corpus/core/src/templates/chat/.agents/skills/create-skill/SKILL.md +0 -221
  345. package/corpus/core/src/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +0 -220
  346. package/corpus/core/src/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +0 -263
  347. package/corpus/core/src/templates/chat/.agents/skills/feature-flags/SKILL.md +0 -169
  348. package/corpus/core/src/templates/chat/.agents/skills/frontend-design/SKILL.md +0 -174
  349. package/corpus/core/src/templates/chat/.agents/skills/real-time-sync/SKILL.md +0 -232
  350. package/corpus/core/src/templates/chat/.agents/skills/security/SKILL.md +0 -282
  351. package/corpus/core/src/templates/chat/.agents/skills/self-modifying-code/SKILL.md +0 -119
  352. package/corpus/core/src/templates/chat/.agents/skills/shadcn-ui/SKILL.md +0 -123
  353. package/corpus/core/src/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +0 -122
  354. package/corpus/core/src/templates/chat/.env.example +0 -5
  355. package/corpus/core/src/templates/chat/.ignore +0 -0
  356. package/corpus/core/src/templates/chat/.oxfmtrc.json +0 -8
  357. package/corpus/core/src/templates/chat/AGENTS.md +0 -77
  358. package/corpus/core/src/templates/chat/CHANGELOG.md +0 -10
  359. package/corpus/core/src/templates/chat/DEVELOPING.md +0 -185
  360. package/corpus/core/src/templates/chat/README.md +0 -32
  361. package/corpus/core/src/templates/chat/_gitignore +0 -46
  362. package/corpus/core/src/templates/chat/actions/hello.ts +0 -13
  363. package/corpus/core/src/templates/chat/actions/navigate.ts +0 -41
  364. package/corpus/core/src/templates/chat/actions/run.ts +0 -2
  365. package/corpus/core/src/templates/chat/actions/view-screen.ts +0 -31
  366. package/corpus/core/src/templates/chat/app/components/layout/Header.tsx +0 -60
  367. package/corpus/core/src/templates/chat/app/components/layout/Layout.tsx +0 -188
  368. package/corpus/core/src/templates/chat/app/components/layout/Sidebar.tsx +0 -505
  369. package/corpus/core/src/templates/chat/app/components/ui/button.tsx +0 -1
  370. package/corpus/core/src/templates/chat/app/components/ui/card.tsx +0 -1
  371. package/corpus/core/src/templates/chat/app/components/ui/dropdown-menu.tsx +0 -1
  372. package/corpus/core/src/templates/chat/app/components/ui/input.tsx +0 -1
  373. package/corpus/core/src/templates/chat/app/components/ui/label.tsx +0 -1
  374. package/corpus/core/src/templates/chat/app/components/ui/sheet.tsx +0 -1
  375. package/corpus/core/src/templates/chat/app/components/ui/toolkit-provider.tsx +0 -17
  376. package/corpus/core/src/templates/chat/app/components/ui/tooltip.tsx +0 -1
  377. package/corpus/core/src/templates/chat/app/design-system.ts +0 -3
  378. package/corpus/core/src/templates/chat/app/entry.client.tsx +0 -22
  379. package/corpus/core/src/templates/chat/app/entry.server.tsx +0 -10
  380. package/corpus/core/src/templates/chat/app/global.css +0 -93
  381. package/corpus/core/src/templates/chat/app/hooks/use-navigation-state.ts +0 -93
  382. package/corpus/core/src/templates/chat/app/i18n/ar-SA.ts +0 -68
  383. package/corpus/core/src/templates/chat/app/i18n/de-DE.ts +0 -71
  384. package/corpus/core/src/templates/chat/app/i18n/en-US.ts +0 -70
  385. package/corpus/core/src/templates/chat/app/i18n/es-ES.ts +0 -71
  386. package/corpus/core/src/templates/chat/app/i18n/fr-FR.ts +0 -71
  387. package/corpus/core/src/templates/chat/app/i18n/hi-IN.ts +0 -69
  388. package/corpus/core/src/templates/chat/app/i18n/index.ts +0 -34
  389. package/corpus/core/src/templates/chat/app/i18n/ja-JP.ts +0 -70
  390. package/corpus/core/src/templates/chat/app/i18n/ko-KR.ts +0 -70
  391. package/corpus/core/src/templates/chat/app/i18n/pt-BR.ts +0 -71
  392. package/corpus/core/src/templates/chat/app/i18n/zh-CN.ts +0 -66
  393. package/corpus/core/src/templates/chat/app/i18n/zh-TW.ts +0 -66
  394. package/corpus/core/src/templates/chat/app/i18n-data.ts +0 -556
  395. package/corpus/core/src/templates/chat/app/lib/agent-page.tsx +0 -49
  396. package/corpus/core/src/templates/chat/app/lib/app-config.ts +0 -11
  397. package/corpus/core/src/templates/chat/app/lib/tab-id.ts +0 -1
  398. package/corpus/core/src/templates/chat/app/lib/utils.ts +0 -1
  399. package/corpus/core/src/templates/chat/app/root.tsx +0 -173
  400. package/corpus/core/src/templates/chat/app/routes/_index.tsx +0 -94
  401. package/corpus/core/src/templates/chat/app/routes/agent.tsx +0 -24
  402. package/corpus/core/src/templates/chat/app/routes/chat.$threadId.tsx +0 -1
  403. package/corpus/core/src/templates/chat/app/routes/database.tsx +0 -17
  404. package/corpus/core/src/templates/chat/app/routes/extensions.$id.$slug.tsx +0 -2
  405. package/corpus/core/src/templates/chat/app/routes/extensions.$id.tsx +0 -11
  406. package/corpus/core/src/templates/chat/app/routes/extensions._index.tsx +0 -11
  407. package/corpus/core/src/templates/chat/app/routes/extensions.tsx +0 -5
  408. package/corpus/core/src/templates/chat/app/routes/observability.tsx +0 -19
  409. package/corpus/core/src/templates/chat/app/routes/settings.tsx +0 -89
  410. package/corpus/core/src/templates/chat/app/routes/team.tsx +0 -11
  411. package/corpus/core/src/templates/chat/app/routes.ts +0 -4
  412. package/corpus/core/src/templates/chat/app/vite-env.d.ts +0 -6
  413. package/corpus/core/src/templates/chat/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +0 -6
  414. package/corpus/core/src/templates/chat/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +0 -6
  415. package/corpus/core/src/templates/chat/changelog/2026-06-27-traditional-chinese-copy-uses-taiwan-terminology.md +0 -6
  416. package/corpus/core/src/templates/chat/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +0 -6
  417. package/corpus/core/src/templates/chat/changelog/2026-06-29-chat-layouts-adapt-when-the-agent-sidebar-is-open.md +0 -6
  418. package/corpus/core/src/templates/chat/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +0 -5
  419. package/corpus/core/src/templates/chat/changelog/2026-07-10-chat-now-makes-ai-connection-setup-clear-without-shifting-th.md +0 -6
  420. package/corpus/core/src/templates/chat/changelog/2026-07-13-a-full-agent-page-now-brings-context-files-connections-jobs-.md +0 -6
  421. package/corpus/core/src/templates/chat/changelog/2026-07-14-chat-opens-reliably-on-hosted-deployments-instead-of-failing.md +0 -6
  422. package/corpus/core/src/templates/chat/changelog/2026-07-14-fixed-chat-template-startup-with-older-core-versions.md +0 -6
  423. package/corpus/core/src/templates/chat/changelog/2026-07-15-chat-navigation-focuses-on-chat-and-agent.md +0 -6
  424. package/corpus/core/src/templates/chat/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +0 -6
  425. package/corpus/core/src/templates/chat/changelog/2026-07-22-compact-sidebar-footer.md +0 -6
  426. package/corpus/core/src/templates/chat/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +0 -6
  427. package/corpus/core/src/templates/chat/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +0 -6
  428. package/corpus/core/src/templates/chat/changelog/2026-07-22-recent-chats-are-easier-to-scan-and-expand.md +0 -6
  429. package/corpus/core/src/templates/chat/changelog/2026-07-23-full-page-chat-is-better-centered-with-quieter-chat-history-.md +0 -6
  430. package/corpus/core/src/templates/chat/changelog/2026-07-24-borderless-secondary-surfaces.md +0 -6
  431. package/corpus/core/src/templates/chat/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +0 -6
  432. package/corpus/core/src/templates/chat/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +0 -6
  433. package/corpus/core/src/templates/chat/components.json +0 -20
  434. package/corpus/core/src/templates/chat/learnings.defaults.md +0 -5
  435. package/corpus/core/src/templates/chat/netlify.toml +0 -11
  436. package/corpus/core/src/templates/chat/package.json +0 -92
  437. package/corpus/core/src/templates/chat/public/agent-native-icon-dark.svg +0 -10
  438. package/corpus/core/src/templates/chat/public/agent-native-icon-light.svg +0 -10
  439. package/corpus/core/src/templates/chat/public/agent-native-logo-dark.svg +0 -21
  440. package/corpus/core/src/templates/chat/public/agent-native-logo-light.svg +0 -21
  441. package/corpus/core/src/templates/chat/public/favicon.svg +0 -1
  442. package/corpus/core/src/templates/chat/public/icon-180.svg +0 -1
  443. package/corpus/core/src/templates/chat/public/icon-192.svg +0 -1
  444. package/corpus/core/src/templates/chat/public/icon-512.svg +0 -1
  445. package/corpus/core/src/templates/chat/public/manifest.json +0 -21
  446. package/corpus/core/src/templates/chat/react-router.config.ts +0 -7
  447. package/corpus/core/src/templates/chat/server/middleware/auth.ts +0 -15
  448. package/corpus/core/src/templates/chat/server/plugins/agent-chat.ts +0 -21
  449. package/corpus/core/src/templates/chat/server/plugins/auth.ts +0 -17
  450. package/corpus/core/src/templates/chat/server/routes/[...page].get.ts +0 -5
  451. package/corpus/core/src/templates/chat/ssr-entry.ts +0 -15
  452. package/corpus/core/src/templates/chat/tsconfig.json +0 -21
  453. package/corpus/core/src/templates/chat/vite.config.ts +0 -19
  454. package/corpus/templates/design/server/handlers/index-design-system-with-builder.ts +0 -151
  455. package/corpus/templates/design/server/routes/api/index-design-system-with-builder.post.ts +0 -1
  456. package/corpus/templates/slides/server/handlers/index-design-system-with-builder.ts +0 -119
  457. package/corpus/templates/slides/server/routes/api/index-design-system-with-builder.post.ts +0 -1
@@ -1,5 +1,4 @@
1
1
  import { sendToAgentChat } from "@agent-native/core/client/agent-chat";
2
- import { appApiPath } from "@agent-native/core/client/api-path";
3
2
  import {
4
3
  useActionQuery,
5
4
  useActionMutation,
@@ -36,9 +35,13 @@ import { Label } from "@/components/ui/label";
36
35
  import { ScrollArea } from "@/components/ui/scroll-area";
37
36
  import { Textarea } from "@/components/ui/textarea";
38
37
 
38
+ import {
39
+ uploadAndIndexFigmaFiles,
40
+ pollDecodeJobStatus,
41
+ type DecodeJobStatus,
42
+ } from "./builder-design-system-upload";
39
43
  import {
40
44
  MAX_BUILDER_INDEX_UPLOAD_BYTES,
41
- readBuilderIndexResponse,
42
45
  formatFileSize,
43
46
  type BuilderIndexResult,
44
47
  } from "./builder-index-response";
@@ -117,6 +120,62 @@ export function DesignSystemSetup({
117
120
  const [builderIndexError, setBuilderIndexError] = useState<string | null>(
118
121
  null,
119
122
  );
123
+ const [decodeStatus, setDecodeStatus] = useState<DecodeJobStatus | null>(
124
+ null,
125
+ );
126
+ const decodePollRef = useRef<AbortController | null>(null);
127
+
128
+ const stopDecodePolling = useCallback(() => {
129
+ decodePollRef.current?.abort();
130
+ decodePollRef.current = null;
131
+ }, []);
132
+
133
+ const startDecodePolling = useCallback(
134
+ (jobId: string, indexResult: BuilderIndexResult) => {
135
+ decodePollRef.current?.abort();
136
+ const controller = new AbortController();
137
+ decodePollRef.current = controller;
138
+ setDecodeStatus({
139
+ status: "pending",
140
+ branchUrl: null,
141
+ error: null,
142
+ framesProcessed: 0,
143
+ totalFrames: 0,
144
+ });
145
+ pollDecodeJobStatus(jobId, {
146
+ signal: controller.signal,
147
+ onUpdate: (status) => {
148
+ if (!controller.signal.aborted) setDecodeStatus(status);
149
+ },
150
+ })
151
+ .then((status) => {
152
+ if (controller.signal.aborted) return;
153
+ setDecodeStatus(status);
154
+ setBuilderIndexResult(
155
+ status.branchUrl
156
+ ? { ...indexResult, builderUrl: status.branchUrl }
157
+ : indexResult,
158
+ );
159
+ setBuilderIndexing(false);
160
+ })
161
+ .catch((err: unknown) => {
162
+ if (controller.signal.aborted) return;
163
+ if (err instanceof DOMException && err.name === "AbortError") return;
164
+ setDecodeStatus((prev) => ({
165
+ status: "error",
166
+ branchUrl: prev?.branchUrl ?? null,
167
+ error: err instanceof Error ? err.message : String(err),
168
+ framesProcessed: prev?.framesProcessed ?? 0,
169
+ totalFrames: prev?.totalFrames ?? 0,
170
+ }));
171
+ setBuilderIndexResult(indexResult);
172
+ setBuilderIndexing(false);
173
+ });
174
+ },
175
+ [],
176
+ );
177
+
178
+ useEffect(() => stopDecodePolling, [stopDecodePolling]);
120
179
 
121
180
  const codeInputRef = useRef<HTMLInputElement>(null);
122
181
  const docInputRef = useRef<HTMLInputElement>(null);
@@ -170,8 +229,10 @@ export function DesignSystemSetup({
170
229
  setBuilderIndexing(false);
171
230
  setBuilderIndexResult(null);
172
231
  setBuilderIndexError(null);
232
+ stopDecodePolling();
233
+ setDecodeStatus(null);
173
234
  }
174
- }, [open]);
235
+ }, [open, stopDecodePolling]);
175
236
 
176
237
  const hasAnySources = useMemo(() => {
177
238
  return (
@@ -269,30 +330,34 @@ export function DesignSystemSetup({
269
330
 
270
331
  setBuilderIndexError(null);
271
332
  setBuilderIndexResult(null);
333
+ stopDecodePolling();
334
+ setDecodeStatus(null);
272
335
  setBuilderIndexing(true);
273
336
  try {
274
- const body = new FormData();
275
- body.append("file", file);
276
- const res = await fetch(
277
- appApiPath("/api/index-design-system-with-builder"),
278
- {
279
- method: "POST",
280
- body,
281
- },
282
- );
283
- const parsed = await readBuilderIndexResponse(res);
284
- setBuilderIndexResult(parsed);
337
+ const suggestedTitle =
338
+ file.name
339
+ .replace(/\.fig$/i, "")
340
+ .replace(/[-_]+/g, " ")
341
+ .trim() || "Imported brand";
342
+ const parsed = await uploadAndIndexFigmaFiles([file], {
343
+ projectName: suggestedTitle,
344
+ });
345
+ if (parsed.jobId) {
346
+ startDecodePolling(parsed.jobId, parsed);
347
+ } else {
348
+ setBuilderIndexResult(parsed);
349
+ setBuilderIndexing(false);
350
+ }
285
351
  } catch (err) {
286
352
  setBuilderIndexError(
287
353
  err instanceof Error
288
354
  ? err.message
289
355
  : t("designSystemSetup.figParseFailed"),
290
356
  );
291
- } finally {
292
357
  setBuilderIndexing(false);
293
358
  }
294
359
  },
295
- [t],
360
+ [t, startDecodePolling, stopDecodePolling],
296
361
  );
297
362
 
298
363
  const handleEditSave = async () => {
@@ -543,7 +608,10 @@ export function DesignSystemSetup({
543
608
  ) : (
544
609
  <BuilderIndexPreview
545
610
  result={builderIndexResult}
611
+ decodeStatus={decodeStatus}
546
612
  onReset={() => {
613
+ stopDecodePolling();
614
+ setDecodeStatus(null);
547
615
  setBuilderIndexResult(null);
548
616
  setBuilderIndexError(null);
549
617
  }}
@@ -880,12 +948,22 @@ function TagList({
880
948
 
881
949
  function BuilderIndexPreview({
882
950
  result,
951
+ decodeStatus,
883
952
  onReset,
884
953
  }: {
885
954
  result: BuilderIndexResult;
955
+ decodeStatus: DecodeJobStatus | null;
886
956
  onReset: () => void;
887
957
  }) {
888
958
  const t = useT();
959
+ const decodeDone =
960
+ decodeStatus == null ||
961
+ Boolean(decodeStatus.branchUrl) ||
962
+ decodeStatus.status === "complete";
963
+ const decodeFailed = decodeStatus?.status === "error";
964
+ const decodeText = decodeFailed
965
+ ? t("designSystemSetup.decodeFailed", { error: decodeStatus?.error ?? "" })
966
+ : null;
889
967
  return (
890
968
  <div className="space-y-4 rounded-lg border border-border bg-accent/40 p-4">
891
969
  <div className="flex items-start gap-3">
@@ -905,44 +983,39 @@ function BuilderIndexPreview({
905
983
  </div>
906
984
  </div>
907
985
 
908
- <dl className="grid grid-cols-[112px_minmax(0,1fr)] gap-x-3 gap-y-2 rounded-md border border-border bg-card/50 p-3 text-xs">
909
- <dt className="text-muted-foreground">
910
- {t("designSystemSetup.builderDesignSystemId")}
911
- </dt>
912
- <dd className="truncate font-mono text-foreground/80">
913
- {result.designSystemId}
914
- </dd>
915
- <dt className="text-muted-foreground">
916
- {t("designSystemSetup.builderJobId")}
917
- </dt>
918
- <dd className="truncate font-mono text-foreground/80">
919
- {result.jobId}
920
- </dd>
921
- </dl>
922
-
923
- <div className="flex flex-wrap items-center gap-2 border-t border-border pt-3">
924
- <Button size="sm" asChild className="cursor-pointer">
925
- <a
926
- href={withBuilderUtmTrackingParams(result.builderUrl, {
927
- campaign: "product",
928
- content: "design_system_intelligence",
929
- })}
930
- target="_blank"
931
- rel="noreferrer"
986
+ {decodeText && (
987
+ <div className="border-t border-border pt-3 text-xs text-destructive">
988
+ {decodeText}
989
+ </div>
990
+ )}
991
+
992
+ {(decodeDone || decodeFailed) && (
993
+ <div className="flex flex-wrap items-center gap-2 border-t border-border pt-3">
994
+ {decodeDone && !decodeFailed && (
995
+ <Button size="sm" asChild className="cursor-pointer">
996
+ <a
997
+ href={withBuilderUtmTrackingParams(result.builderUrl, {
998
+ campaign: "product",
999
+ content: "design_system_intelligence",
1000
+ })}
1001
+ target="_blank"
1002
+ rel="noreferrer"
1003
+ >
1004
+ <IconExternalLink className="w-3.5 h-3.5" />
1005
+ {t("designSystemSetup.openInBuilder")}
1006
+ </a>
1007
+ </Button>
1008
+ )}
1009
+ <Button
1010
+ size="sm"
1011
+ variant="ghost"
1012
+ onClick={onReset}
1013
+ className="cursor-pointer"
932
1014
  >
933
- <IconExternalLink className="w-3.5 h-3.5" />
934
- {t("designSystemSetup.openInBuilder")}
935
- </a>
936
- </Button>
937
- <Button
938
- size="sm"
939
- variant="ghost"
940
- onClick={onReset}
941
- className="cursor-pointer"
942
- >
943
- {t("designSystemSetup.chooseAnotherFile")}
944
- </Button>
945
- </div>
1015
+ {t("designSystemSetup.chooseAnotherFile")}
1016
+ </Button>
1017
+ </div>
1018
+ )}
946
1019
  </div>
947
1020
  );
948
1021
  }
@@ -0,0 +1,271 @@
1
+ import { appApiPath } from "@agent-native/core/client/api-path";
2
+
3
+ import {
4
+ type BuilderIndexResult,
5
+ readBuilderIndexResponse,
6
+ } from "./builder-index-response";
7
+
8
+ // GCS resumable uploads require every chunk except the last to be a multiple
9
+ // of 256 KiB. 16 MiB is the recommended default and keeps very large `.fig`
10
+ // files off a single request body (the serverless host caps bodies well below
11
+ // Figma export sizes).
12
+ const GCS_CHUNK_SIZE = 16 * 1024 * 1024;
13
+ const MAX_CHUNK_RETRIES = 5;
14
+
15
+ interface UploadSlot {
16
+ idx: number;
17
+ uploadUrl: string;
18
+ uploadToken: string;
19
+ }
20
+
21
+ async function readJson(res: Response): Promise<any> {
22
+ try {
23
+ return await res.json();
24
+ } catch {
25
+ return null;
26
+ }
27
+ }
28
+
29
+ async function requestUploadSlots(files: File[]): Promise<UploadSlot[]> {
30
+ const res = await fetch(appApiPath("/api/design-system-upload-start"), {
31
+ method: "POST",
32
+ headers: { "Content-Type": "application/json" },
33
+ body: JSON.stringify({
34
+ attachments: files.map((file) => ({
35
+ name: file.name,
36
+ mimetype: file.type || "application/octet-stream",
37
+ declaredSize: file.size,
38
+ })),
39
+ }),
40
+ });
41
+ const json = await readJson(res);
42
+ if (!res.ok || json?.error) {
43
+ throw new Error(json?.error || `Failed to start upload (${res.status})`);
44
+ }
45
+ const slots = Array.isArray(json?.uploads)
46
+ ? ([...json.uploads] as UploadSlot[]).sort((a, b) => a.idx - b.idx)
47
+ : [];
48
+ if (slots.length !== files.length) {
49
+ throw new Error("Upload could not be started for all files.");
50
+ }
51
+ return slots;
52
+ }
53
+
54
+ async function initiateResumableSession(
55
+ uploadUrl: string,
56
+ mimetype: string,
57
+ fileSize: number,
58
+ ): Promise<string> {
59
+ const response = await fetch(uploadUrl, {
60
+ method: "POST",
61
+ headers: {
62
+ "x-goog-resumable": "start",
63
+ // The signed URL commits to the exact declared size; echo it back
64
+ // byte-for-byte or GCS rejects the session.
65
+ "x-goog-content-length-range": `0,${fileSize}`,
66
+ "Content-Type": mimetype,
67
+ },
68
+ });
69
+ if (!response.ok) {
70
+ throw new Error(`Failed to initiate upload session (${response.status})`);
71
+ }
72
+ const sessionUri = response.headers.get("Location");
73
+ if (!sessionUri) {
74
+ throw new Error("Upload session URI missing from storage response");
75
+ }
76
+ return sessionUri;
77
+ }
78
+
79
+ // GCS reports the highest committed byte in a `Range: bytes=0-<end>` header.
80
+ function committedOffsetFromRange(response: Response): number | null {
81
+ const match = response.headers.get("Range")?.match(/bytes=0-(\d+)/);
82
+ return match ? parseInt(match[1], 10) + 1 : null;
83
+ }
84
+
85
+ async function queryCommittedOffset(
86
+ sessionUri: string,
87
+ total: number,
88
+ ): Promise<number> {
89
+ const response = await fetch(sessionUri, {
90
+ method: "PUT",
91
+ headers: { "Content-Range": `bytes */${total}` },
92
+ });
93
+ if (response.status === 200 || response.status === 201) return total;
94
+ if (response.status === 308) return committedOffsetFromRange(response) ?? 0;
95
+ throw new Error(`Failed to query upload status (${response.status})`);
96
+ }
97
+
98
+ function delay(ms: number, signal?: AbortSignal): Promise<void> {
99
+ return new Promise((resolve, reject) => {
100
+ if (signal?.aborted) {
101
+ return reject(new DOMException("Aborted", "AbortError"));
102
+ }
103
+ const timer = setTimeout(resolve, ms);
104
+ signal?.addEventListener(
105
+ "abort",
106
+ () => {
107
+ clearTimeout(timer);
108
+ reject(new DOMException("Aborted", "AbortError"));
109
+ },
110
+ { once: true },
111
+ );
112
+ });
113
+ }
114
+
115
+ async function streamFileToStorage(
116
+ slot: UploadSlot,
117
+ file: File,
118
+ onProgress: (uploadedBytes: number) => void,
119
+ ): Promise<void> {
120
+ const mimetype = file.type || "application/octet-stream";
121
+ const sessionUri = await initiateResumableSession(
122
+ slot.uploadUrl,
123
+ mimetype,
124
+ file.size,
125
+ );
126
+ const total = file.size;
127
+
128
+ if (total === 0) {
129
+ const response = await fetch(sessionUri, {
130
+ method: "PUT",
131
+ headers: { "Content-Range": "bytes */0" },
132
+ body: new Uint8Array(0),
133
+ });
134
+ if (response.status !== 200 && response.status !== 201) {
135
+ throw new Error(
136
+ `Failed to finalize zero-byte upload (${response.status})`,
137
+ );
138
+ }
139
+ onProgress(0);
140
+ return;
141
+ }
142
+
143
+ let offset = 0;
144
+ let retries = 0;
145
+ while (offset < total) {
146
+ const end = Math.min(offset + GCS_CHUNK_SIZE, total);
147
+ const isLast = end === total;
148
+ try {
149
+ const response = await fetch(sessionUri, {
150
+ method: "PUT",
151
+ headers: { "Content-Range": `bytes ${offset}-${end - 1}/${total}` },
152
+ body: file.slice(offset, end),
153
+ });
154
+ if (response.status === 200 || response.status === 201) {
155
+ offset = total;
156
+ } else if (!isLast && response.status === 308) {
157
+ const nextOffset = committedOffsetFromRange(response) ?? offset;
158
+ if (nextOffset <= offset) {
159
+ throw new Error(`Upload stalled at byte ${offset}`);
160
+ }
161
+ offset = nextOffset;
162
+ } else {
163
+ throw new Error(`Unexpected upload status ${response.status}`);
164
+ }
165
+ retries = 0;
166
+ onProgress(offset);
167
+ } catch (err) {
168
+ if (++retries > MAX_CHUNK_RETRIES) throw err;
169
+ await delay(500 * retries);
170
+ try {
171
+ offset = await queryCommittedOffset(sessionUri, total);
172
+ } catch {
173
+ // If the offset query also fails, retry from the last local offset —
174
+ // GCS's resumable PUT safely re-acknowledges bytes it already has.
175
+ }
176
+ onProgress(offset);
177
+ }
178
+ }
179
+ }
180
+
181
+ export interface UploadAndIndexOptions {
182
+ projectName?: string;
183
+ onProgress?: (fraction: number) => void;
184
+ }
185
+
186
+ /**
187
+ * Streams `.fig`/design files straight to storage in resumable chunks, then
188
+ * finalizes Builder DSI indexing with the resulting upload tokens. No file
189
+ * bytes pass through the app server, so arbitrarily large Figma files work.
190
+ */
191
+ export async function uploadAndIndexFigmaFiles(
192
+ files: File[],
193
+ options: UploadAndIndexOptions = {},
194
+ ): Promise<BuilderIndexResult> {
195
+ if (files.length === 0) throw new Error("No files to upload.");
196
+
197
+ const slots = await requestUploadSlots(files);
198
+
199
+ const totalBytes = files.reduce((sum, file) => sum + file.size, 0) || 1;
200
+ const uploadedPerFile = new Array<number>(files.length).fill(0);
201
+ for (let i = 0; i < files.length; i++) {
202
+ await streamFileToStorage(slots[i], files[i], (uploaded) => {
203
+ uploadedPerFile[i] = uploaded;
204
+ const done = uploadedPerFile.reduce((sum, n) => sum + n, 0);
205
+ options.onProgress?.(Math.min(done / totalBytes, 1));
206
+ });
207
+ }
208
+
209
+ const res = await fetch(appApiPath("/api/index-design-system-sources"), {
210
+ method: "POST",
211
+ headers: { "Content-Type": "application/json" },
212
+ body: JSON.stringify({
213
+ projectName: options.projectName,
214
+ uploadTokens: slots.map((slot) => slot.uploadToken),
215
+ }),
216
+ });
217
+ return readBuilderIndexResponse(res);
218
+ }
219
+
220
+ export interface DecodeJobStatus {
221
+ status: "pending" | "processing" | "complete" | "error";
222
+ branchUrl: string | null;
223
+ error: string | null;
224
+ framesProcessed: number;
225
+ totalFrames: number;
226
+ }
227
+
228
+ const DECODE_JOB_POLL_INTERVAL_MS = 5_000;
229
+ const DECODE_JOB_MAX_POLLS = 120; // ~10 min at 5s, so a stuck job can't loop forever
230
+
231
+ export interface PollDecodeJobOptions {
232
+ signal?: AbortSignal;
233
+ onUpdate?: (status: DecodeJobStatus) => void;
234
+ }
235
+
236
+ /**
237
+ * After indexing returns a jobId, the `.fig` decode job is still `pending` with
238
+ * no branchUrl. Poll until the branch appears or the job reaches a terminal
239
+ * state. A job that reports `status: "error"` resolves so the caller can read
240
+ * `status.error`; network failures, timeouts, and aborts reject.
241
+ */
242
+ export async function pollDecodeJobStatus(
243
+ jobId: string,
244
+ options: PollDecodeJobOptions = {},
245
+ ): Promise<DecodeJobStatus> {
246
+ const { signal, onUpdate } = options;
247
+ const path = appApiPath(
248
+ `/api/design-system-decode-job-status?jobId=${encodeURIComponent(jobId)}`,
249
+ );
250
+ for (let i = 0; i < DECODE_JOB_MAX_POLLS; i++) {
251
+ if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
252
+ const res = await fetch(path, { signal });
253
+ const json = await readJson(res);
254
+ if (!res.ok || json?.error) {
255
+ throw new Error(json?.error || `Status check failed (${res.status})`);
256
+ }
257
+ const status = json as DecodeJobStatus;
258
+ onUpdate?.(status);
259
+ if (
260
+ status.branchUrl ||
261
+ status.status === "complete" ||
262
+ status.status === "error"
263
+ ) {
264
+ return status;
265
+ }
266
+ await delay(DECODE_JOB_POLL_INTERVAL_MS, signal);
267
+ }
268
+ throw new Error(
269
+ "Timed out waiting for the design system to finish decoding.",
270
+ );
271
+ }
@@ -13,7 +13,7 @@ export interface BuilderIndexResult {
13
13
  builderConnectUrl?: string;
14
14
  }
15
15
 
16
- export const MAX_BUILDER_INDEX_UPLOAD_BYTES = 200 * 1024 * 1024;
16
+ export const MAX_BUILDER_INDEX_UPLOAD_BYTES = 512 * 1024 * 1024;
17
17
 
18
18
  export function formatFileSize(bytes: number): string {
19
19
  return `${Math.round(bytes / 1024 / 1024)} MB`;
@@ -537,6 +537,7 @@ const messages = {
537
537
  builderDesignSystemId: "Design system",
538
538
  builderJobId: "Job",
539
539
  openInBuilder: "Open in Builder",
540
+ decodeFailed: "Decoding failed: {{error}}",
540
541
  websiteUrl: "Website URL",
541
542
  websitePlaceholder: "example.com or Nike",
542
543
  add: "Add",
@@ -0,0 +1,49 @@
1
+ import {
2
+ FeatureNotConfiguredError,
3
+ fetchBuilderDesignSystemDecodeJobStatus,
4
+ getSession,
5
+ } from "@agent-native/core/server";
6
+ import { defineEventHandler, getQuery, setResponseStatus } from "h3";
7
+
8
+ import { withSlidesRequestContext } from "./request-auth-context.js";
9
+
10
+ /**
11
+ * Reads a Builder design-system decode job's status. The UI polls this after
12
+ * `/api/index-design-system-sources` returns a jobId, until the `.fig` decode
13
+ * job leaves `pending` and exposes a `branchUrl` (or reports an `error`).
14
+ */
15
+ export const designSystemDecodeJobStatus = defineEventHandler(async (event) => {
16
+ const session = await getSession(event).catch(() => null);
17
+ if (!session?.email) {
18
+ setResponseStatus(event, 401);
19
+ return { error: "Unauthorized" };
20
+ }
21
+
22
+ const jobId = getQuery(event).jobId;
23
+ if (typeof jobId !== "string" || !jobId.trim()) {
24
+ setResponseStatus(event, 400);
25
+ return { error: "jobId is required." };
26
+ }
27
+
28
+ try {
29
+ return await withSlidesRequestContext(event, () =>
30
+ fetchBuilderDesignSystemDecodeJobStatus(jobId.trim()),
31
+ );
32
+ } catch (err) {
33
+ if (err instanceof FeatureNotConfiguredError) {
34
+ setResponseStatus(event, 412);
35
+ return {
36
+ error: err.message,
37
+ builderConnectUrl:
38
+ err.builderConnectUrl ?? "/_agent-native/builder/connect",
39
+ };
40
+ }
41
+ setResponseStatus(event, 502);
42
+ return {
43
+ error:
44
+ err instanceof Error
45
+ ? err.message
46
+ : "Failed to read decode job status.",
47
+ };
48
+ }
49
+ });
@@ -0,0 +1,87 @@
1
+ import {
2
+ FeatureNotConfiguredError,
3
+ getSession,
4
+ startBuilderDesignSystemUpload,
5
+ } from "@agent-native/core/server";
6
+ import { defineEventHandler, readBody, setResponseStatus } from "h3";
7
+
8
+ import { withSlidesRequestContext } from "./request-auth-context.js";
9
+
10
+ const MAX_FIG_BYTES = 512 * 1024 * 1024;
11
+
12
+ interface AttachmentInput {
13
+ name?: unknown;
14
+ mimetype?: unknown;
15
+ declaredSize?: unknown;
16
+ }
17
+
18
+ /**
19
+ * Opens signed resumable-upload slots so the browser can stream large `.fig`
20
+ * bytes straight to storage. Only small JSON metadata rides through the app
21
+ * server; the file bytes never do.
22
+ */
23
+ export const designSystemUploadStart = defineEventHandler(async (event) => {
24
+ const session = await getSession(event).catch(() => null);
25
+ if (!session?.email) {
26
+ setResponseStatus(event, 401);
27
+ return { error: "Unauthorized" };
28
+ }
29
+
30
+ const body = (await readBody(event).catch(() => null)) as {
31
+ attachments?: AttachmentInput[];
32
+ } | null;
33
+ const rawList = Array.isArray(body?.attachments) ? body.attachments : [];
34
+ if (rawList.length === 0) {
35
+ setResponseStatus(event, 400);
36
+ return { error: "No attachments provided." };
37
+ }
38
+
39
+ const attachments: {
40
+ name: string;
41
+ mimetype: string;
42
+ declaredSize: number;
43
+ }[] = [];
44
+ for (const raw of rawList) {
45
+ const name = typeof raw?.name === "string" ? raw.name.trim() : "";
46
+ const mimetype =
47
+ typeof raw?.mimetype === "string" && raw.mimetype.trim()
48
+ ? raw.mimetype.trim()
49
+ : "application/octet-stream";
50
+ const declaredSize = Number(raw?.declaredSize);
51
+ if (!name) {
52
+ setResponseStatus(event, 400);
53
+ return { error: "Attachment name is required." };
54
+ }
55
+ if (!Number.isSafeInteger(declaredSize) || declaredSize <= 0) {
56
+ setResponseStatus(event, 400);
57
+ return { error: "Attachment declaredSize must be a positive integer." };
58
+ }
59
+ if (declaredSize > MAX_FIG_BYTES) {
60
+ setResponseStatus(event, 413);
61
+ return {
62
+ error: `File too large (max ${Math.round(MAX_FIG_BYTES / 1024 / 1024)} MB).`,
63
+ };
64
+ }
65
+ attachments.push({ name, mimetype, declaredSize });
66
+ }
67
+
68
+ try {
69
+ const uploads = await withSlidesRequestContext(event, () =>
70
+ startBuilderDesignSystemUpload(attachments),
71
+ );
72
+ return { uploads };
73
+ } catch (err) {
74
+ if (err instanceof FeatureNotConfiguredError) {
75
+ setResponseStatus(event, 412);
76
+ return {
77
+ error: err.message,
78
+ builderConnectUrl:
79
+ err.builderConnectUrl ?? "/_agent-native/builder/connect",
80
+ };
81
+ }
82
+ setResponseStatus(event, 502);
83
+ return {
84
+ error: err instanceof Error ? err.message : "Failed to start upload.",
85
+ };
86
+ }
87
+ });