@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
@@ -15,6 +15,8 @@ import { useCallback, useEffect, useRef, useState } from "react";
15
15
 
16
16
  const NETWORK_RESTART_BASE_MS = 1_000;
17
17
  const NETWORK_RESTART_MAX_MS = 30_000;
18
+ const RESTART_RETRY_BASE_MS = 400;
19
+ const MAX_RESTART_ATTEMPTS = 8;
18
20
 
19
21
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
22
  function getSpeechRecognitionCtor(): any {
@@ -42,6 +44,14 @@ export interface LiveTranscriptionApi {
42
44
  stopAndWait: (timeoutMs?: number) => Promise<string>;
43
45
  pause: () => void;
44
46
  resume: () => void;
47
+ /**
48
+ * Non-null when recognition died before the caller asked it to stop, so the
49
+ * returned transcript covers only part of the session. Callers must pass it
50
+ * along rather than storing a partial transcript as a finished one. Read
51
+ * through a getter (not state) so a stop handler can never observe a stale
52
+ * "everything was fine" value from an earlier render.
53
+ */
54
+ getIncompleteReason: () => string | null;
45
55
  }
46
56
 
47
57
  export function useLiveTranscription(
@@ -64,6 +74,20 @@ export function useLiveTranscription(
64
74
  const restartTimerRef = useRef<number | null>(null);
65
75
  const networkErrorCountRef = useRef(0);
66
76
  const restartDelayRef = useRef(0);
77
+ const restartFailureCountRef = useRef(0);
78
+ const incompleteReasonRef = useRef<string | null>(null);
79
+
80
+ // First reason wins: it names what actually broke the capture, and later
81
+ // teardown noise must not overwrite it.
82
+ const markIncomplete = useCallback((reason: string) => {
83
+ if (incompleteReasonRef.current) return;
84
+ incompleteReasonRef.current = reason;
85
+ }, []);
86
+
87
+ const getIncompleteReason = useCallback(
88
+ () => incompleteReasonRef.current,
89
+ [],
90
+ );
67
91
 
68
92
  const currentTranscript = useCallback(
69
93
  () =>
@@ -102,6 +126,8 @@ export function useLiveTranscription(
102
126
  stoppedManuallyRef.current = false;
103
127
  networkErrorCountRef.current = 0;
104
128
  restartDelayRef.current = 0;
129
+ restartFailureCountRef.current = 0;
130
+ incompleteReasonRef.current = null;
105
131
  setTranscript("");
106
132
  setInterimText("");
107
133
 
@@ -122,6 +148,7 @@ export function useLiveTranscription(
122
148
  if (event.results.length > 0) {
123
149
  networkErrorCountRef.current = 0;
124
150
  restartDelayRef.current = 0;
151
+ restartFailureCountRef.current = 0;
125
152
  }
126
153
  };
127
154
 
@@ -133,6 +160,9 @@ export function useLiveTranscription(
133
160
  event.error === "audio-capture"
134
161
  ) {
135
162
  stoppedManuallyRef.current = true;
163
+ markIncomplete(
164
+ `Browser speech recognition stopped mid-recording (${event.error}).`,
165
+ );
136
166
  setIsActive(false);
137
167
  return;
138
168
  }
@@ -163,17 +193,34 @@ export function useLiveTranscription(
163
193
  }
164
194
  try {
165
195
  recognition.start();
196
+ restartFailureCountRef.current = 0;
166
197
  } catch {
167
- setIsActive(false);
198
+ // Chrome throws InvalidStateError while the previous session is
199
+ // still releasing. `onend` has already fired and will not fire
200
+ // again, so this timer is the only thing left that can revive
201
+ // recognition — giving up here freezes the transcript mid-recording
202
+ // and the partial text then looks like the whole recording.
203
+ const attempt = ++restartFailureCountRef.current;
204
+ if (attempt >= MAX_RESTART_ATTEMPTS) {
205
+ markIncomplete(
206
+ "Browser speech recognition stopped mid-recording and could not be restarted.",
207
+ );
208
+ setIsActive(false);
209
+ return;
210
+ }
211
+ restartTimerRef.current = window.setTimeout(
212
+ restart,
213
+ RESTART_RETRY_BASE_MS * attempt,
214
+ );
168
215
  }
169
216
  };
170
- const delayMs = restartDelayRef.current;
217
+ // Never call start() synchronously from inside onend — Chrome still
218
+ // has the session marked as running during dispatch and throws.
219
+ restartTimerRef.current = window.setTimeout(
220
+ restart,
221
+ restartDelayRef.current,
222
+ );
171
223
  restartDelayRef.current = 0;
172
- if (delayMs > 0) {
173
- restartTimerRef.current = window.setTimeout(restart, delayMs);
174
- } else {
175
- restart();
176
- }
177
224
  return;
178
225
  }
179
226
  setIsActive(false);
@@ -185,10 +232,14 @@ export function useLiveTranscription(
185
232
  try {
186
233
  recognition.start();
187
234
  setIsActive(true);
188
- } catch {
189
- /* browser may block without user gesture */
235
+ } catch (err) {
236
+ // Browser may block without user gesture. Nothing was captured, and the
237
+ // caller must not treat the resulting empty text as "no speech".
238
+ markIncomplete(
239
+ `Browser speech recognition could not start (${(err as Error)?.message || "unknown error"}).`,
240
+ );
190
241
  }
191
- }, [currentTranscript, lang]);
242
+ }, [currentTranscript, lang, markIncomplete]);
192
243
 
193
244
  const stop = useCallback((): string => {
194
245
  const text = currentTranscript();
@@ -307,5 +358,6 @@ export function useLiveTranscription(
307
358
  stopAndWait,
308
359
  pause,
309
360
  resume,
361
+ getIncompleteReason,
310
362
  };
311
363
  }
package/src/db/client.ts CHANGED
@@ -1595,6 +1595,35 @@ async function initClient(): Promise<void> {
1595
1595
  * Get the singleton database client. Returns a `DbExec` whose first
1596
1596
  * `execute()` call lazily initializes the underlying driver.
1597
1597
  */
1598
+ /**
1599
+ * Point a missing-table failure at the cause instead of the symptom.
1600
+ *
1601
+ * The driver reports `no such table: x` / `relation "x" does not exist` from
1602
+ * whichever query happened to touch it first, so the stack lands in an action
1603
+ * and reads as a bug in that action. The actual cause is almost always that no
1604
+ * migration ever created the table — a template with no `server/plugins/db.ts`
1605
+ * creates none, and core's own tables self-heal, so app tables are the only
1606
+ * ones that fail this way. Appends rather than replaces: `isDuplicateColumnError`
1607
+ * and friends match substrings of the driver's original text.
1608
+ */
1609
+ export function annotateMissingTable(err: unknown, sql: unknown): unknown {
1610
+ if (!(err instanceof Error)) return err;
1611
+ const match =
1612
+ /no such table:?\s*["'`]?([\w.]+)/i.exec(err.message) ??
1613
+ /relation\s+["'`]?([\w.]+)["'`]?\s+does not exist/i.exec(err.message);
1614
+ if (!match) return err;
1615
+ if (err.message.includes("server/plugins/db.ts")) return err;
1616
+ const statement =
1617
+ typeof sql === "string" ? sql : (sql as { sql?: string })?.sql;
1618
+ err.message =
1619
+ `${err.message}\n` +
1620
+ ` [agent-native] Table "${match[1]}" does not exist. App tables are created by migrations in ` +
1621
+ `server/plugins/db.ts — check that the plugin exists and declares a migration for this table, ` +
1622
+ `then restart the dev server so it runs.` +
1623
+ (statement ? `\n Statement: ${statement.slice(0, 200)}` : "");
1624
+ return err;
1625
+ }
1626
+
1598
1627
  export function getDbExec(): DbExec {
1599
1628
  if (_exec) return _exec;
1600
1629
 
@@ -1608,6 +1637,16 @@ export function getDbExec(): DbExec {
1608
1637
  return sql;
1609
1638
  }
1610
1639
 
1640
+ async function execAnnotated(
1641
+ s: string | { sql: string; args?: unknown[] },
1642
+ ): ReturnType<DbExec["execute"]> {
1643
+ try {
1644
+ return await _exec!.execute(sanitize(s));
1645
+ } catch (err) {
1646
+ throw annotateMissingTable(err, s);
1647
+ }
1648
+ }
1649
+
1611
1650
  // Return a proxy that lazy-inits on first call
1612
1651
  const proxy: DbExec = {
1613
1652
  async execute(sql) {
@@ -1624,7 +1663,7 @@ export function getDbExec(): DbExec {
1624
1663
  }
1625
1664
  // After init, swap to a sanitizing wrapper around the real client
1626
1665
  const wrapper: DbExec = {
1627
- execute: (s) => _exec!.execute(sanitize(s)),
1666
+ execute: (s) => execAnnotated(s),
1628
1667
  atomicBatch: _exec!.atomicBatch
1629
1668
  ? (statements) =>
1630
1669
  _exec!.atomicBatch!(statements.map((s) => sanitize(s)))
@@ -1640,7 +1679,7 @@ export function getDbExec(): DbExec {
1640
1679
  : undefined,
1641
1680
  };
1642
1681
  Object.assign(proxy, wrapper);
1643
- return _exec!.execute(sanitize(sql));
1682
+ return execAnnotated(sql);
1644
1683
  },
1645
1684
  async transaction(fn) {
1646
1685
  if (!_initPromise) _initPromise = initClient();
@@ -1652,7 +1691,7 @@ export function getDbExec(): DbExec {
1652
1691
  throw err;
1653
1692
  }
1654
1693
  const wrapper: DbExec = {
1655
- execute: (s) => _exec!.execute(sanitize(s)),
1694
+ execute: (s) => execAnnotated(s),
1656
1695
  atomicBatch: _exec!.atomicBatch
1657
1696
  ? (statements) =>
1658
1697
  _exec!.atomicBatch!(statements.map((s) => sanitize(s)))
@@ -132,38 +132,56 @@ async function loadGetTableConfig(): Promise<
132
132
  }
133
133
 
134
134
  /**
135
- * Live columns already present on `table`, keyed by column name.
136
- * Postgres: `information_schema.columns` scoped to the table's schema
137
- * (default `public`). SQLite: `PRAGMA table_info(table)`.
138
- *
139
- * Returns `null` when the table itself does not exist (caller no-ops —
140
- * table creation owns that path) or when introspection fails outright.
135
+ * Load every requested Postgres table's live columns in one round trip.
136
+ * Calling information_schema separately for each table adds dozens of
137
+ * serialized network requests to every serverless cold start.
141
138
  */
142
- async function introspectLiveColumns(
139
+ async function introspectPostgresColumns(
143
140
  db: DbExec,
144
- table: string,
145
- schema: string | undefined,
146
- ): Promise<Set<string> | null> {
147
- if (isPostgres()) {
148
- try {
149
- const tableSchema = schema || "public";
150
- const { rows: existsRows } = await db.execute({
151
- sql: `SELECT 1 FROM information_schema.tables
152
- WHERE table_schema = ? AND table_name = ? LIMIT 1`,
153
- args: [tableSchema, table],
154
- });
155
- if (existsRows.length === 0) return null;
156
- const { rows } = await db.execute({
157
- sql: `SELECT column_name FROM information_schema.columns
158
- WHERE table_schema = ? AND table_name = ?`,
159
- args: [tableSchema, table],
160
- });
161
- return new Set(rows.map((r) => String(r.column_name)));
162
- } catch {
163
- return null;
141
+ tables: DeclaredTableLike[],
142
+ ): Promise<Map<string, Set<string>> | null> {
143
+ if (tables.length === 0) return new Map();
144
+ const pairs = [
145
+ ...new Map(
146
+ tables.map((table) => {
147
+ const schema = table.schema || "public";
148
+ return [`${schema}.${table.name}`, { schema, table: table.name }];
149
+ }),
150
+ ).values(),
151
+ ];
152
+ const args: unknown[] = [];
153
+ const predicates = pairs.map(({ schema, table }) => {
154
+ args.push(schema, table);
155
+ return `(table_schema = ? AND table_name = ?)`;
156
+ });
157
+ try {
158
+ const { rows } = await db.execute({
159
+ sql: `SELECT table_schema, table_name, column_name
160
+ FROM information_schema.columns
161
+ WHERE ${predicates.join(" OR ")}`,
162
+ args,
163
+ });
164
+ const columns = new Map<string, Set<string>>();
165
+ for (const row of rows) {
166
+ const key = `${String(row.table_schema)}.${String(row.table_name)}`;
167
+ const tableColumns = columns.get(key) ?? new Set<string>();
168
+ tableColumns.add(String(row.column_name));
169
+ columns.set(key, tableColumns);
164
170
  }
171
+ return columns;
172
+ } catch {
173
+ return null;
165
174
  }
175
+ }
166
176
 
177
+ /**
178
+ * Live SQLite columns already present on `table`, keyed by column name.
179
+ * Returns `null` when the table does not exist or introspection fails.
180
+ */
181
+ async function introspectSqliteLiveColumns(
182
+ db: DbExec,
183
+ table: string,
184
+ ): Promise<Set<string> | null> {
167
185
  // SQLite: PRAGMA table_info returns zero rows for a non-existent table
168
186
  // (no error), so we can't distinguish "missing table" from "no columns"
169
187
  // that way — probe sqlite_master first.
@@ -308,10 +326,13 @@ export async function ensureAdditiveColumns(
308
326
  return result;
309
327
  }
310
328
 
329
+ const declaredTables: DeclaredTableLike[] = [];
311
330
  for (const tableObj of tables) {
312
- let config: DeclaredTableLike;
313
331
  try {
314
- config = getTableConfig(tableObj);
332
+ const config = getTableConfig(tableObj);
333
+ if (config.name && PLAIN_IDENTIFIER.test(config.name)) {
334
+ declaredTables.push(config);
335
+ }
315
336
  } catch (err) {
316
337
  // Not a table this dialect's getTableConfig understands (e.g. a stray
317
338
  // export that isn't a Drizzle table) — skip quietly rather than
@@ -319,15 +340,21 @@ export async function ensureAdditiveColumns(
319
340
  logger.warn(
320
341
  `[ensure-additive-columns] skipping non-table export: ${err instanceof Error ? err.message : String(err)}`,
321
342
  );
322
- continue;
323
343
  }
344
+ }
324
345
 
325
- const tableName = config.name;
326
- if (!tableName || !PLAIN_IDENTIFIER.test(tableName)) continue;
346
+ const postgresColumns = isPostgres()
347
+ ? await introspectPostgresColumns(db, declaredTables)
348
+ : null;
327
349
 
350
+ for (const config of declaredTables) {
351
+ const tableName = config.name;
328
352
  let liveColumns: Set<string> | null;
329
353
  try {
330
- liveColumns = await introspectLiveColumns(db, tableName, config.schema);
354
+ liveColumns = isPostgres()
355
+ ? (postgresColumns?.get(`${config.schema || "public"}.${tableName}`) ??
356
+ null)
357
+ : await introspectSqliteLiveColumns(db, tableName);
331
358
  } catch (err) {
332
359
  result.errors.push({
333
360
  column: `${tableName}.*`,
@@ -1688,7 +1688,7 @@ export function generateCloudflarePagesStaticShellFromManifest(
1688
1688
  ? DEFAULT_ROOT_LOADER_REACT_ROUTER_TURBO_STREAM
1689
1689
  : EMPTY_REACT_ROUTER_TURBO_STREAM;
1690
1690
 
1691
- return `<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/><link rel="manifest" href="/manifest.json"/><link rel="icon" type="image/svg+xml" href="/favicon.svg"/>${modulePreloads}${stylesheets}</head><body><div style="display:flex;align-items:center;justify-content:center;height:100vh;width:100%"><svg role="status" aria-label="Loading" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="animation:an-spin 1s linear infinite;opacity:0.7"><path d="M21 12a9 9 0 1 1-6.219-8.56"></path></svg><style>@keyframes an-spin { to { transform: rotate(360deg) } } @media (prefers-color-scheme: dark) { html { background: #09090b; color: #fafafa } }</style></div><script>window.__reactRouterContext = ${JSON.stringify(context)};window.__reactRouterContext.stream = new ReadableStream({start(controller){window.__reactRouterContext.streamController = controller;}}).pipeThrough(new TextEncoderStream());</script><script type="module" async="">${routeModuleScript}</script><!--$--><script>window.__reactRouterContext.streamController.enqueue(${JSON.stringify(encodedInitialState)});</script><!--$--><script>window.__reactRouterContext.streamController.close();</script><!--/$--><!--/$--></body></html>`;
1691
+ return `<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/><link rel="manifest" href="/manifest.json"/><link rel="icon" type="image/svg+xml" href="/favicon.svg"/>${modulePreloads}${stylesheets}</head><body><div style="display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;height:100vh;width:100%"><svg role="status" aria-label="Loading" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="animation:an-spin 1s linear infinite;opacity:0.7"><path d="M21 12a9 9 0 1 1-6.219-8.56"></path></svg><p class="an-stall-hint">Still loading. If this does not finish, reload the page — the browser console may show what failed.</p><style>@keyframes an-spin { to { transform: rotate(360deg) } } @keyframes an-stall-in { to { opacity: 0.6 } } .an-stall-hint { opacity: 0; margin: 0; max-width: 32rem; padding: 0 1.5rem; text-align: center; font-size: 0.875rem; line-height: 1.5; font-family: ui-sans-serif, system-ui, sans-serif; animation: an-stall-in 0.4s ease-out 10s forwards } @media (prefers-reduced-motion: reduce) { .an-stall-hint { animation-duration: 0s } } @media (prefers-color-scheme: dark) { html { background: #09090b; color: #fafafa } }</style></div><script>window.__reactRouterContext = ${JSON.stringify(context)};window.__reactRouterContext.stream = new ReadableStream({start(controller){window.__reactRouterContext.streamController = controller;}}).pipeThrough(new TextEncoderStream());</script><script type="module" async="">${routeModuleScript}</script><!--$--><script>window.__reactRouterContext.streamController.enqueue(${JSON.stringify(encodedInitialState)});</script><!--$--><script>window.__reactRouterContext.streamController.close();</script><!--/$--><!--/$--></body></html>`;
1692
1692
  }
1693
1693
 
1694
1694
  function writeCloudflarePagesStaticShell({
@@ -2343,6 +2343,28 @@ const LIBSQL_NATIVE_PACKAGE_NAMES = [
2343
2343
  const FFMPEG_STATIC_PACKAGE_NAME = "ffmpeg-static";
2344
2344
  const RESVG_SCOPE = "@resvg";
2345
2345
  const RESVG_PACKAGE_PREFIX = "resvg-js";
2346
+
2347
+ // Serverless functions only ever run on 64-bit Linux. The darwin/win32/android
2348
+ // and 32-bit-arm prebuilds of these native packages are ~100MB that can never
2349
+ // execute there, and Netlify copies the whole server dir again for every extra
2350
+ // emitted function — so the dead weight is paid once per function. Cold start
2351
+ // scales with bundle size, and a page that opens several requests at once
2352
+ // scales out to that many cold containers, which is how this surfaces: 502/504
2353
+ // on the first burst rather than as an obviously slow deploy.
2354
+ const SERVERLESS_NATIVE_PACKAGE_SUFFIXES = [
2355
+ "linux-x64-gnu",
2356
+ "linux-x64-musl",
2357
+ "linux-arm64-gnu",
2358
+ "linux-arm64-musl",
2359
+ ];
2360
+
2361
+ export function isServerlessNativePlatformPackage(
2362
+ packageName: string,
2363
+ ): boolean {
2364
+ return SERVERLESS_NATIVE_PACKAGE_SUFFIXES.some((suffix) =>
2365
+ packageName.endsWith(suffix),
2366
+ );
2367
+ }
2346
2368
  const FFMPEG_STATIC_BINARY_NAMES =
2347
2369
  process.platform === "win32" ? ["ffmpeg.exe", "ffmpeg"] : ["ffmpeg"];
2348
2370
  const SERVERLESS_FFMPEG_STATIC_PLATFORM = "linux";
@@ -3441,6 +3463,7 @@ function copyInstalledLibsqlNativePackages(serverDir: string | undefined) {
3441
3463
  let copied = 0;
3442
3464
 
3443
3465
  for (const packageName of LIBSQL_NATIVE_PACKAGE_NAMES) {
3466
+ if (!isServerlessNativePlatformPackage(packageName)) continue;
3444
3467
  const src = findInstalledLibsqlNativePackage(nodeModulesRoots, packageName);
3445
3468
  if (!src) continue;
3446
3469
 
@@ -3457,7 +3480,13 @@ function copyInstalledLibsqlNativePackages(serverDir: string | undefined) {
3457
3480
 
3458
3481
  function copyInstalledResvgPackages(serverDir: string | undefined) {
3459
3482
  if (!serverDir || !fs.existsSync(serverDir)) return;
3460
- const packages = findInstalledResvgPackages(nodeModulesAncestors(cwd));
3483
+ // `resvg-js` itself is the JS wrapper that gets imported; everything else in
3484
+ // the scope is a per-platform prebuild.
3485
+ const packages = findInstalledResvgPackages(nodeModulesAncestors(cwd)).filter(
3486
+ ({ packageName }) =>
3487
+ packageName === RESVG_PACKAGE_PREFIX ||
3488
+ isServerlessNativePlatformPackage(packageName),
3489
+ );
3461
3490
  if (packages.length === 0) return;
3462
3491
 
3463
3492
  const destScopeDir = path.join(serverDir, "node_modules", RESVG_SCOPE);
@@ -367,6 +367,9 @@ async function reconfigureManager(manager: McpClientManager): Promise<void> {
367
367
  export function mountMcpServersRoutes(
368
368
  nitroApp: any,
369
369
  manager: McpClientManager,
370
+ options: {
371
+ waitUntilReady?: () => Promise<void>;
372
+ } = {},
370
373
  ): void {
371
374
  const mountedApps: WeakSet<object> = ((
372
375
  globalThis as any
@@ -375,13 +378,17 @@ export function mountMcpServersRoutes(
375
378
  mountedApps.add(nitroApp);
376
379
 
377
380
  mountMcpOAuthRoutes(nitroApp, {
378
- reconfigure: () => reconfigureManager(manager),
381
+ reconfigure: async () => {
382
+ await options.waitUntilReady?.();
383
+ await reconfigureManager(manager);
384
+ },
379
385
  });
380
386
 
381
387
  try {
382
388
  getH3App(nitroApp).use(
383
389
  "/_agent-native/mcp/servers",
384
390
  defineEventHandler(async (event: H3Event) => {
391
+ await options.waitUntilReady?.();
385
392
  const method = getMethod(event);
386
393
  const pathname = (event.url?.pathname || "")
387
394
  .replace(/^\/+/, "")
@@ -421,6 +428,7 @@ export function mountMcpServersRoutes(
421
428
  getH3App(nitroApp).use(
422
429
  "/_agent-native/mcp/builtin",
423
430
  defineEventHandler(async (event: H3Event) => {
431
+ await options.waitUntilReady?.();
424
432
  const method = getMethod(event);
425
433
  const pathname = (event.url?.pathname || "")
426
434
  .replace(/^\/+/, "")
@@ -442,6 +450,7 @@ export function mountMcpServersRoutes(
442
450
  getH3App(nitroApp).use(
443
451
  "/_agent-native/mcp/apps",
444
452
  defineEventHandler(async (event: H3Event) => {
453
+ await options.waitUntilReady?.();
445
454
  const method = getMethod(event);
446
455
  const pathname = (event.url?.pathname || "")
447
456
  .replace(/^\/+/, "")
@@ -0,0 +1,152 @@
1
+ import {
2
+ defineEventHandler,
3
+ getRequestURL,
4
+ createError,
5
+ type H3Event,
6
+ } from "h3";
7
+
8
+ import { getDbExec } from "../db/client.js";
9
+ import { readBody } from "../server/h3-helpers.js";
10
+ import {
11
+ getRegisteredAppRoles,
12
+ listAppMemberRoles,
13
+ setAppMemberRole,
14
+ type AppRolesDescriptor,
15
+ } from "./app-roles.js";
16
+ import { getOrgContext } from "./context.js";
17
+ import { canManageOrg } from "./permissions.js";
18
+
19
+ /**
20
+ * Resolve the descriptor a request is talking about.
21
+ *
22
+ * The `appId` arrives from the client but can only ever select among
23
+ * descriptors the server already declared through `defineAppRoles`; an
24
+ * unregistered id is a 404, never an implicit new app. That keeps the trusted
25
+ * vocabulary in app source while still letting one workspace host several apps.
26
+ */
27
+ function requireDescriptor(appId: string | null): AppRolesDescriptor<string> {
28
+ if (!appId?.trim()) {
29
+ throw createError({ statusCode: 400, message: "appId is required" });
30
+ }
31
+ const descriptor = getRegisteredAppRoles(appId.trim());
32
+ if (!descriptor) {
33
+ throw createError({
34
+ statusCode: 404,
35
+ message: `No app roles registered for "${appId}"`,
36
+ });
37
+ }
38
+ return descriptor;
39
+ }
40
+
41
+ /** Extract the :email tail. The mount prefix is stripped before we see it. */
42
+ function extractMemberEmail(event: H3Event): string | undefined {
43
+ const path = getRequestURL(event).pathname;
44
+ const match =
45
+ path.match(/^\/([^/]+)\/?$/) ?? path.match(/\/app-roles\/([^/]+)\/?$/);
46
+ return match?.[1] ? decodeURIComponent(match[1]) : undefined;
47
+ }
48
+
49
+ async function isOrgMember(orgId: string, email: string): Promise<boolean> {
50
+ const { rows } = await getDbExec().execute({
51
+ sql: `SELECT 1 FROM org_members WHERE org_id = ? AND LOWER(email) = ? LIMIT 1`,
52
+ args: [orgId, email.trim().toLowerCase()],
53
+ });
54
+ return rows.length > 0;
55
+ }
56
+
57
+ /**
58
+ * GET /_agent-native/org/app-roles?appId=X — the app's vocabulary plus every
59
+ * assignment in the active org, and the caller's own resolved role.
60
+ */
61
+ export const listAppRolesHandler = defineEventHandler(
62
+ async (event: H3Event) => {
63
+ const url = getRequestURL(event);
64
+ const descriptor = requireDescriptor(url.searchParams.get("appId"));
65
+ const ctx = await getOrgContext(event);
66
+
67
+ const base = {
68
+ appId: descriptor.appId,
69
+ roles: descriptor.roles,
70
+ defaultRole: descriptor.defaultRole ?? null,
71
+ roleLabels: descriptor.roleLabels ?? {},
72
+ canManage: canManageOrg(ctx.role),
73
+ };
74
+ if (!ctx.orgId) return { ...base, assignments: [], myRole: null };
75
+
76
+ const assignments = await listAppMemberRoles(descriptor.appId, ctx.orgId);
77
+ const mine = assignments.find(
78
+ (a) => a.email.toLowerCase() === ctx.email.toLowerCase(),
79
+ );
80
+ return {
81
+ ...base,
82
+ assignments,
83
+ // The caller's own role, for progressive disclosure only. Every guarded
84
+ // operation re-resolves this server-side; a client that lies about it gains
85
+ // nothing but a differently-shaped UI.
86
+ myRole: mine && descriptor.roles.includes(mine.role) ? mine.role : null,
87
+ };
88
+ },
89
+ );
90
+
91
+ /**
92
+ * PUT /_agent-native/org/app-roles/:email — assign or clear an app role.
93
+ * Body: `{ appId, role }`, where `role: null` clears the assignment.
94
+ *
95
+ * Org owner/admin only. App roles never confer the right to manage app roles:
96
+ * that would let an app admin escalate inside a team they cannot otherwise
97
+ * administer, and it is the org roster this overlay hangs off.
98
+ */
99
+ export const setAppRoleHandler = defineEventHandler(async (event: H3Event) => {
100
+ const ctx = await getOrgContext(event);
101
+ if (!ctx.email) {
102
+ throw createError({ statusCode: 401, message: "Authentication required" });
103
+ }
104
+ if (!ctx.orgId) {
105
+ throw createError({ statusCode: 400, message: "No active organization" });
106
+ }
107
+ if (!canManageOrg(ctx.role)) {
108
+ throw createError({
109
+ statusCode: 403,
110
+ message: "Organization admin role required",
111
+ });
112
+ }
113
+
114
+ const body = await readBody(event);
115
+ const descriptor = requireDescriptor(body?.appId ?? null);
116
+
117
+ const email = extractMemberEmail(event);
118
+ if (!email) {
119
+ throw createError({ statusCode: 400, message: "Member email is required" });
120
+ }
121
+
122
+ const role = body?.role ?? null;
123
+ if (role !== null && !descriptor.roles.includes(String(role))) {
124
+ throw createError({
125
+ statusCode: 400,
126
+ message: `Unknown ${descriptor.appId} role "${role}"`,
127
+ });
128
+ }
129
+
130
+ // Assignments hang off membership. Writing one for a non-member would leave a
131
+ // row that resolves to nothing and reappears if that person is ever invited.
132
+ if (!(await isOrgMember(ctx.orgId, email))) {
133
+ throw createError({
134
+ statusCode: 404,
135
+ message: "Not a member of this organization",
136
+ });
137
+ }
138
+
139
+ await setAppMemberRole({
140
+ appId: descriptor.appId,
141
+ orgId: ctx.orgId,
142
+ email,
143
+ role: role === null ? null : String(role),
144
+ updatedBy: ctx.email,
145
+ });
146
+
147
+ return {
148
+ appId: descriptor.appId,
149
+ email,
150
+ role: role === null ? null : String(role),
151
+ };
152
+ });