@agent-native/core 0.79.17 → 0.79.19

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 (355) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +14 -0
  3. package/corpus/core/docs/content/file-uploads.md +11 -4
  4. package/corpus/core/docs/content/locales/ar-SA/multi-app-workspace.md +3 -3
  5. package/corpus/core/docs/content/locales/de-DE/multi-app-workspace.md +3 -3
  6. package/corpus/core/docs/content/locales/es-ES/multi-app-workspace.md +3 -3
  7. package/corpus/core/docs/content/locales/fr-FR/multi-app-workspace.md +3 -3
  8. package/corpus/core/docs/content/locales/hi-IN/multi-app-workspace.md +3 -3
  9. package/corpus/core/docs/content/locales/ja-JP/multi-app-workspace.md +3 -3
  10. package/corpus/core/docs/content/locales/ko-KR/multi-app-workspace.md +3 -3
  11. package/corpus/core/docs/content/locales/pt-BR/multi-app-workspace.md +3 -3
  12. package/corpus/core/docs/content/locales/zh-CN/multi-app-workspace.md +3 -3
  13. package/corpus/core/docs/content/locales/zh-TW/multi-app-workspace.md +3 -3
  14. package/corpus/core/docs/content/multi-app-workspace.md +3 -3
  15. package/corpus/core/docs/content/template-clips.md +14 -5
  16. package/corpus/core/package.json +1 -1
  17. package/corpus/core/src/agent/engine/builder-engine.ts +8 -3
  18. package/corpus/core/src/cli/mcp.ts +11 -6
  19. package/corpus/core/src/client/AssistantChat.tsx +6 -2
  20. package/corpus/core/src/client/chat/markdown-renderer.tsx +1 -0
  21. package/corpus/core/src/client/i18n.tsx +77 -65
  22. package/corpus/core/src/client/index.ts +7 -1
  23. package/corpus/core/src/client/org/OrgSwitcher.tsx +5 -5
  24. package/corpus/core/src/client/org/TeamPage.tsx +11 -3
  25. package/corpus/core/src/client/settings/SettingsTabsPage.tsx +216 -0
  26. package/corpus/core/src/client/settings/index.ts +5 -0
  27. package/corpus/core/src/file-upload/index.ts +1 -0
  28. package/corpus/core/src/file-upload/registry.ts +22 -6
  29. package/corpus/core/src/file-upload/types.ts +6 -1
  30. package/corpus/core/src/integrations/adapters/email.ts +26 -18
  31. package/corpus/core/src/integrations/adapters/slack.ts +8 -7
  32. package/corpus/core/src/integrations/adapters/telegram.ts +9 -11
  33. package/corpus/core/src/integrations/adapters/whatsapp.ts +12 -9
  34. package/corpus/core/src/integrations/plugin.ts +79 -13
  35. package/corpus/core/src/onboarding/default-steps.ts +6 -3
  36. package/corpus/core/src/onboarding/types.ts +1 -1
  37. package/corpus/core/src/org/handlers.ts +1 -1
  38. package/corpus/core/src/server/agent-chat-plugin.ts +4 -7
  39. package/corpus/core/src/server/better-auth-instance.ts +19 -62
  40. package/corpus/core/src/server/core-routes-plugin.ts +93 -180
  41. package/corpus/core/src/server/create-server.ts +55 -116
  42. package/corpus/core/src/server/email-actions.ts +3 -6
  43. package/corpus/core/src/server/email.ts +52 -17
  44. package/corpus/core/src/server/index.ts +2 -1
  45. package/corpus/core/src/server/scoped-key-storage.ts +223 -0
  46. package/corpus/core/src/shared/agent-env.ts +3 -3
  47. package/corpus/core/src/sharing/actions/share-resource.ts +1 -1
  48. package/corpus/core/src/styles/agent-native.css +47 -3
  49. package/corpus/core/src/templates/workspace-root/AGENTS.md +3 -2
  50. package/corpus/core/src/templates/workspace-root/README.md +1 -1
  51. package/corpus/core/src/templates/workspace-root/scripts/repair-workspace-org.ts +29 -90
  52. package/corpus/core/src/terminal/pty-server.ts +3 -38
  53. package/corpus/core/src/triggers/dispatcher.ts +1 -2
  54. package/corpus/templates/.retired/issues/package.json +7 -0
  55. package/corpus/templates/.retired/meeting-notes/package.json +7 -0
  56. package/corpus/templates/.retired/recruiting/package.json +7 -0
  57. package/corpus/templates/.retired/scheduling/package.json +7 -0
  58. package/corpus/templates/.retired/voice/package.json +7 -0
  59. package/corpus/templates/analytics/app/components/layout/Header.tsx +0 -1
  60. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +1 -18
  61. package/corpus/templates/analytics/app/global.css +15 -0
  62. package/corpus/templates/analytics/app/i18n/zh-TW.ts +7 -1
  63. package/corpus/templates/analytics/app/i18n-data.ts +71 -2
  64. package/corpus/templates/analytics/app/pages/Ask.tsx +1 -1
  65. package/corpus/templates/analytics/app/pages/Settings.tsx +120 -73
  66. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +0 -11
  67. package/corpus/templates/analytics/app/routes/catalog.tsx +33 -176
  68. package/corpus/templates/analytics/app/routes/team.tsx +3 -2
  69. package/corpus/templates/analytics/changelog/2026-06-26-dashboard-templates-are-tucked-into-settings-and-show-simple.md +6 -0
  70. package/corpus/templates/analytics/changelog/2026-06-26-the-ask-tab-uses-a-softer-dark-gray-canvas-while-sidebar-cha.md +6 -0
  71. package/corpus/templates/analytics/changelog/2026-06-26-the-sessions-page-shows-a-single-page-title-instead-of-repea.md +6 -0
  72. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +37 -28
  73. package/corpus/templates/assets/app/routes/settings.tsx +65 -43
  74. package/corpus/templates/assets/app/routes/team.tsx +2 -10
  75. package/corpus/templates/brain/app/routes/settings.tsx +390 -358
  76. package/corpus/templates/brain/app/routes/team.tsx +2 -8
  77. package/corpus/templates/calendar/actions/db-connect.ts +5 -29
  78. package/corpus/templates/calendar/app/components/CloudUpgrade.tsx +8 -51
  79. package/corpus/templates/calendar/app/components/calendar/GoogleConnectBanner.tsx +2 -1
  80. package/corpus/templates/calendar/app/components/calendar/GoogleSetupWizard.tsx +4 -2
  81. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +0 -2
  82. package/corpus/templates/calendar/app/pages/Settings.tsx +304 -271
  83. package/corpus/templates/calendar/app/routes/_app.team.tsx +2 -17
  84. package/corpus/templates/calendar/server/handlers/google-auth.ts +22 -9
  85. package/corpus/templates/calendar/server/lib/booking-emails.ts +1 -1
  86. package/corpus/templates/calendar/server/lib/event-guest-notifications.ts +1 -1
  87. package/corpus/templates/calendar/server/lib/google-calendar.ts +48 -28
  88. package/corpus/templates/chat/app/components/layout/Header.tsx +0 -1
  89. package/corpus/templates/chat/app/hooks/use-navigation-state.ts +4 -2
  90. package/corpus/templates/chat/app/routes/settings.tsx +54 -54
  91. package/corpus/templates/chat/app/routes/team.tsx +2 -10
  92. package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +7 -2
  93. package/corpus/templates/clips/.agents/skills/recording/SKILL.md +1 -1
  94. package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +7 -0
  95. package/corpus/templates/clips/AGENTS.md +8 -0
  96. package/corpus/templates/clips/actions/invite-member.ts +1 -1
  97. package/corpus/templates/clips/actions/request-transcript.ts +1 -1
  98. package/corpus/templates/clips/app/components/library/library-layout.tsx +325 -333
  99. package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +80 -40
  100. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +2 -0
  101. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +629 -643
  102. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +263 -158
  103. package/corpus/templates/clips/app/routes/share.$shareId.tsx +10 -2
  104. package/corpus/templates/clips/changelog/2026-06-26-long-desktop-recordings-are-checkpointed-so-macos-finalizati.md +6 -0
  105. package/corpus/templates/clips/changelog/2026-06-26-non-mac-desktop-recordings-wait-for-upload-transcription.md +6 -0
  106. package/corpus/templates/clips/changelog/2026-06-26-recording-details-button-stays-at-the-far-right-on-small-screen.md +6 -0
  107. package/corpus/templates/clips/changelog/2026-06-26-s3-compatible-storage-keys-now-save-securely-from-clips-sett.md +6 -0
  108. package/corpus/templates/clips/changelog/2026-06-26-shared-clip-links-guide-agents-through-processing-transcripts.md +6 -0
  109. package/corpus/templates/clips/changelog/2026-06-26-the-agent-sidebar-now-slides-in-smoothly-without-duplicate-e.md +6 -0
  110. package/corpus/templates/clips/changelog/2026-06-26-the-clip-details-sidebar-now-collapses-into-a-slide-out-pane.md +6 -0
  111. package/corpus/templates/clips/desktop/src/lib/recorder.ts +55 -3
  112. package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +217 -5
  113. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +1 -0
  114. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +176 -5
  115. package/corpus/templates/clips/server/lib/public-agent-context.ts +31 -0
  116. package/corpus/templates/clips/server/lib/s3-upload-provider.ts +60 -14
  117. package/corpus/templates/clips/server/lib/video-storage.ts +37 -9
  118. package/corpus/templates/clips/server/routes/api/agent-transcript.json.get.ts +5 -0
  119. package/corpus/templates/content/app/components/layout/Header.tsx +0 -1
  120. package/corpus/templates/content/app/components/sidebar/NotionButton.tsx +1 -0
  121. package/corpus/templates/content/app/routes/_app.settings.tsx +54 -55
  122. package/corpus/templates/content/app/routes/_app.team.tsx +2 -28
  123. package/corpus/templates/content/server/lib/notion.ts +43 -11
  124. package/corpus/templates/content/server/routes/api/notion/auth-url.get.ts +2 -2
  125. package/corpus/templates/content/server/routes/api/notion/status.get.ts +3 -3
  126. package/corpus/templates/design/app/routes/settings.tsx +60 -38
  127. package/corpus/templates/design/app/routes/team.tsx +2 -10
  128. package/corpus/templates/dispatch/app/hooks/use-navigation-state.ts +2 -2
  129. package/corpus/templates/dispatch/app/routes/settings.tsx +70 -55
  130. package/corpus/templates/dispatch/app/routes/team.tsx +5 -1
  131. package/corpus/templates/forms/actions/db-connect.ts +7 -36
  132. package/corpus/templates/forms/actions/navigate.ts +2 -2
  133. package/corpus/templates/forms/app/components/CloudUpgrade.tsx +8 -47
  134. package/corpus/templates/forms/app/components/layout/Header.tsx +0 -1
  135. package/corpus/templates/forms/app/components/layout/Layout.tsx +42 -25
  136. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +35 -76
  137. package/corpus/templates/forms/app/global.css +9 -25
  138. package/corpus/templates/forms/app/hooks/use-navigation-state.ts +4 -4
  139. package/corpus/templates/forms/app/i18n/ar-SA.ts +1 -0
  140. package/corpus/templates/forms/app/i18n/de-DE.ts +1 -0
  141. package/corpus/templates/forms/app/i18n/en-US.ts +1 -0
  142. package/corpus/templates/forms/app/i18n/es-ES.ts +1 -0
  143. package/corpus/templates/forms/app/i18n/fr-FR.ts +1 -0
  144. package/corpus/templates/forms/app/i18n/hi-IN.ts +1 -0
  145. package/corpus/templates/forms/app/i18n/ja-JP.ts +1 -0
  146. package/corpus/templates/forms/app/i18n/ko-KR.ts +1 -0
  147. package/corpus/templates/forms/app/i18n/pt-BR.ts +1 -0
  148. package/corpus/templates/forms/app/i18n/zh-CN.ts +1 -0
  149. package/corpus/templates/forms/app/i18n/zh-TW.ts +1 -0
  150. package/corpus/templates/forms/app/lib/route-prewarm.ts +9 -8
  151. package/corpus/templates/forms/app/pages/AskPage.tsx +70 -0
  152. package/corpus/templates/forms/app/pages/FormsListPage.tsx +74 -81
  153. package/corpus/templates/forms/app/root.tsx +1 -1
  154. package/corpus/templates/forms/app/routes/_app.ask.tsx +14 -0
  155. package/corpus/templates/forms/app/routes/_app.settings.tsx +54 -54
  156. package/corpus/templates/forms/app/routes/_app.team.tsx +2 -9
  157. package/corpus/templates/forms/app/routes/_index.tsx +21 -130
  158. package/corpus/templates/forms/changelog/2026-06-26-ask-forms-opens-as-a-standard-app-tab-with-a-simpler-sidebar.md +6 -0
  159. package/corpus/templates/forms/changelog/2026-06-26-forms-are-shown-in-a-tighter-row-list-with-a-denser-sidebar.md +6 -0
  160. package/corpus/templates/forms/shared/navigation.ts +4 -2
  161. package/corpus/templates/macros/app/routes/settings.tsx +54 -54
  162. package/corpus/templates/macros/app/routes/team.tsx +2 -10
  163. package/corpus/templates/mail/actions/helpers.ts +2 -2
  164. package/corpus/templates/mail/app/components/GoogleConnectBanner.tsx +2 -1
  165. package/corpus/templates/mail/app/pages/SettingsPage.tsx +5 -5
  166. package/corpus/templates/mail/app/routes/team.tsx +3 -2
  167. package/corpus/templates/mail/server/handlers/emails.ts +3 -2
  168. package/corpus/templates/mail/server/handlers/google-auth.ts +19 -12
  169. package/corpus/templates/mail/server/lib/automation-engine.ts +3 -2
  170. package/corpus/templates/mail/server/lib/email-state.ts +2 -3
  171. package/corpus/templates/mail/server/lib/google-auth.ts +28 -11
  172. package/corpus/templates/mail/server/lib/jobs.ts +3 -2
  173. package/corpus/templates/mail/server/onboarding.ts +9 -6
  174. package/corpus/templates/mail/server/routes/api/attachments.get.ts +3 -3
  175. package/corpus/templates/plan/actions/request-plan-access.ts +1 -1
  176. package/corpus/templates/plan/app/components/layout/Header.tsx +0 -1
  177. package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +56 -4
  178. package/corpus/templates/plan/app/global.css +8 -3
  179. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +4 -2
  180. package/corpus/templates/plan/app/lib/route-prewarm.ts +0 -4
  181. package/corpus/templates/plan/app/pages/PlansPage.tsx +1 -1
  182. package/corpus/templates/plan/app/routes/settings.tsx +75 -73
  183. package/corpus/templates/plan/app/routes/team.tsx +2 -8
  184. package/corpus/templates/plan/changelog/2026-06-26-canvas-grid-panning-now-stays-smooth-and-fills-the-workspace.md +6 -0
  185. package/corpus/templates/plan/server/lib/comment-notifications.ts +1 -1
  186. package/corpus/templates/slides/actions/check-image-gen.ts +2 -1
  187. package/corpus/templates/slides/actions/db-connect.ts +7 -45
  188. package/corpus/templates/slides/actions/edit-image.ts +6 -3
  189. package/corpus/templates/slides/actions/generate-image-api.ts +1 -1
  190. package/corpus/templates/slides/actions/generate-image.ts +2 -2
  191. package/corpus/templates/slides/actions/generate-slides-ai.ts +5 -3
  192. package/corpus/templates/slides/actions/image-gen-status.ts +4 -3
  193. package/corpus/templates/slides/actions/image-search.ts +6 -3
  194. package/corpus/templates/slides/actions/search-images.ts +6 -3
  195. package/corpus/templates/slides/app/components/CloudUpgrade.tsx +8 -51
  196. package/corpus/templates/slides/app/components/layout/Header.tsx +0 -1
  197. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +0 -2
  198. package/corpus/templates/slides/app/routes/settings.tsx +54 -54
  199. package/corpus/templates/slides/app/routes/team.tsx +2 -12
  200. package/corpus/templates/slides/server/handlers/google-docs-auth.ts +9 -9
  201. package/corpus/templates/slides/server/handlers/image-gen.ts +5 -1
  202. package/corpus/templates/slides/server/handlers/image-providers/gemini.ts +9 -1
  203. package/corpus/templates/slides/server/handlers/image-providers/index.ts +18 -8
  204. package/corpus/templates/slides/server/handlers/image-providers/openai.ts +10 -4
  205. package/corpus/templates/slides/server/handlers/image-providers/types.ts +1 -0
  206. package/corpus/templates/slides/server/lib/google-docs-oauth.ts +59 -34
  207. package/corpus/templates/slides/server/register-secrets.ts +60 -0
  208. package/corpus/templates/videos/actions/db-connect.ts +7 -45
  209. package/corpus/templates/videos/app/components/CloudUpgrade.tsx +8 -51
  210. package/corpus/templates/videos/app/components/Sidebar.tsx +1 -9
  211. package/corpus/templates/videos/app/components/layout/Header.tsx +0 -1
  212. package/corpus/templates/videos/app/components/layout/NavSidebar.tsx +0 -3
  213. package/corpus/templates/videos/app/routes/settings.tsx +54 -54
  214. package/corpus/templates/videos/app/routes/team.tsx +2 -8
  215. package/dist/agent/engine/builder-engine.d.ts.map +1 -1
  216. package/dist/agent/engine/builder-engine.js +8 -3
  217. package/dist/agent/engine/builder-engine.js.map +1 -1
  218. package/dist/cli/mcp.d.ts.map +1 -1
  219. package/dist/cli/mcp.js +3 -4
  220. package/dist/cli/mcp.js.map +1 -1
  221. package/dist/client/AssistantChat.d.ts.map +1 -1
  222. package/dist/client/AssistantChat.js +6 -2
  223. package/dist/client/AssistantChat.js.map +1 -1
  224. package/dist/client/chat/markdown-renderer.d.ts.map +1 -1
  225. package/dist/client/chat/markdown-renderer.js +1 -0
  226. package/dist/client/chat/markdown-renderer.js.map +1 -1
  227. package/dist/client/i18n.d.ts.map +1 -1
  228. package/dist/client/i18n.js +20 -9
  229. package/dist/client/i18n.js.map +1 -1
  230. package/dist/client/index.d.ts +1 -1
  231. package/dist/client/index.d.ts.map +1 -1
  232. package/dist/client/index.js +1 -1
  233. package/dist/client/index.js.map +1 -1
  234. package/dist/client/org/OrgSwitcher.d.ts +3 -3
  235. package/dist/client/org/OrgSwitcher.js +2 -2
  236. package/dist/client/org/OrgSwitcher.js.map +1 -1
  237. package/dist/client/org/TeamPage.d.ts +6 -1
  238. package/dist/client/org/TeamPage.d.ts.map +1 -1
  239. package/dist/client/org/TeamPage.js +2 -2
  240. package/dist/client/org/TeamPage.js.map +1 -1
  241. package/dist/client/settings/SettingsTabsPage.d.ts +27 -0
  242. package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -0
  243. package/dist/client/settings/SettingsTabsPage.js +108 -0
  244. package/dist/client/settings/SettingsTabsPage.js.map +1 -0
  245. package/dist/client/settings/index.d.ts +1 -0
  246. package/dist/client/settings/index.d.ts.map +1 -1
  247. package/dist/client/settings/index.js +1 -0
  248. package/dist/client/settings/index.js.map +1 -1
  249. package/dist/file-upload/index.d.ts +1 -1
  250. package/dist/file-upload/index.d.ts.map +1 -1
  251. package/dist/file-upload/index.js +1 -1
  252. package/dist/file-upload/index.js.map +1 -1
  253. package/dist/file-upload/registry.d.ts +1 -0
  254. package/dist/file-upload/registry.d.ts.map +1 -1
  255. package/dist/file-upload/registry.js +24 -6
  256. package/dist/file-upload/registry.js.map +1 -1
  257. package/dist/file-upload/types.d.ts +6 -1
  258. package/dist/file-upload/types.d.ts.map +1 -1
  259. package/dist/file-upload/types.js.map +1 -1
  260. package/dist/integrations/adapters/email.d.ts.map +1 -1
  261. package/dist/integrations/adapters/email.js +18 -18
  262. package/dist/integrations/adapters/email.js.map +1 -1
  263. package/dist/integrations/adapters/slack.d.ts.map +1 -1
  264. package/dist/integrations/adapters/slack.js +8 -7
  265. package/dist/integrations/adapters/slack.js.map +1 -1
  266. package/dist/integrations/adapters/telegram.d.ts.map +1 -1
  267. package/dist/integrations/adapters/telegram.js +9 -9
  268. package/dist/integrations/adapters/telegram.js.map +1 -1
  269. package/dist/integrations/adapters/whatsapp.d.ts.map +1 -1
  270. package/dist/integrations/adapters/whatsapp.js +10 -9
  271. package/dist/integrations/adapters/whatsapp.js.map +1 -1
  272. package/dist/integrations/plugin.d.ts.map +1 -1
  273. package/dist/integrations/plugin.js +47 -13
  274. package/dist/integrations/plugin.js.map +1 -1
  275. package/dist/notifications/routes.d.ts +3 -3
  276. package/dist/observability/routes.d.ts +6 -6
  277. package/dist/onboarding/default-steps.d.ts.map +1 -1
  278. package/dist/onboarding/default-steps.js +6 -5
  279. package/dist/onboarding/default-steps.js.map +1 -1
  280. package/dist/onboarding/types.d.ts +1 -1
  281. package/dist/onboarding/types.d.ts.map +1 -1
  282. package/dist/onboarding/types.js.map +1 -1
  283. package/dist/org/handlers.js +1 -1
  284. package/dist/org/handlers.js.map +1 -1
  285. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  286. package/dist/server/agent-chat-plugin.js +4 -6
  287. package/dist/server/agent-chat-plugin.js.map +1 -1
  288. package/dist/server/agent-engine-api-key-route.d.ts +2 -2
  289. package/dist/server/better-auth-instance.d.ts.map +1 -1
  290. package/dist/server/better-auth-instance.js +17 -56
  291. package/dist/server/better-auth-instance.js.map +1 -1
  292. package/dist/server/core-routes-plugin.d.ts +1 -1
  293. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  294. package/dist/server/core-routes-plugin.js +67 -153
  295. package/dist/server/core-routes-plugin.js.map +1 -1
  296. package/dist/server/create-server.d.ts +3 -9
  297. package/dist/server/create-server.d.ts.map +1 -1
  298. package/dist/server/create-server.js +37 -91
  299. package/dist/server/create-server.js.map +1 -1
  300. package/dist/server/email-actions.d.ts +2 -3
  301. package/dist/server/email-actions.d.ts.map +1 -1
  302. package/dist/server/email-actions.js +3 -6
  303. package/dist/server/email-actions.js.map +1 -1
  304. package/dist/server/email.d.ts +3 -3
  305. package/dist/server/email.d.ts.map +1 -1
  306. package/dist/server/email.js +40 -19
  307. package/dist/server/email.js.map +1 -1
  308. package/dist/server/index.d.ts +3 -3
  309. package/dist/server/index.d.ts.map +1 -1
  310. package/dist/server/index.js +3 -3
  311. package/dist/server/index.js.map +1 -1
  312. package/dist/server/scoped-key-storage.d.ts +22 -0
  313. package/dist/server/scoped-key-storage.d.ts.map +1 -0
  314. package/dist/server/scoped-key-storage.js +152 -0
  315. package/dist/server/scoped-key-storage.js.map +1 -0
  316. package/dist/shared/agent-env.d.ts +3 -3
  317. package/dist/shared/agent-env.d.ts.map +1 -1
  318. package/dist/shared/agent-env.js +3 -3
  319. package/dist/shared/agent-env.js.map +1 -1
  320. package/dist/sharing/actions/share-resource.js +1 -1
  321. package/dist/sharing/actions/share-resource.js.map +1 -1
  322. package/dist/styles/agent-native.css +47 -3
  323. package/dist/templates/workspace-root/AGENTS.md +3 -2
  324. package/dist/templates/workspace-root/README.md +1 -1
  325. package/dist/templates/workspace-root/scripts/repair-workspace-org.ts +29 -90
  326. package/dist/terminal/pty-server.d.ts.map +1 -1
  327. package/dist/terminal/pty-server.js +3 -36
  328. package/dist/terminal/pty-server.js.map +1 -1
  329. package/dist/triggers/dispatcher.js +1 -1
  330. package/dist/triggers/dispatcher.js.map +1 -1
  331. package/docs/content/file-uploads.md +11 -4
  332. package/docs/content/locales/ar-SA/multi-app-workspace.md +3 -3
  333. package/docs/content/locales/de-DE/multi-app-workspace.md +3 -3
  334. package/docs/content/locales/es-ES/multi-app-workspace.md +3 -3
  335. package/docs/content/locales/fr-FR/multi-app-workspace.md +3 -3
  336. package/docs/content/locales/hi-IN/multi-app-workspace.md +3 -3
  337. package/docs/content/locales/ja-JP/multi-app-workspace.md +3 -3
  338. package/docs/content/locales/ko-KR/multi-app-workspace.md +3 -3
  339. package/docs/content/locales/pt-BR/multi-app-workspace.md +3 -3
  340. package/docs/content/locales/zh-CN/multi-app-workspace.md +3 -3
  341. package/docs/content/locales/zh-TW/multi-app-workspace.md +3 -3
  342. package/docs/content/multi-app-workspace.md +3 -3
  343. package/docs/content/template-clips.md +14 -5
  344. package/package.json +1 -1
  345. package/src/templates/workspace-root/AGENTS.md +3 -2
  346. package/src/templates/workspace-root/README.md +1 -1
  347. package/src/templates/workspace-root/scripts/repair-workspace-org.ts +29 -90
  348. package/corpus/core/src/server/env-var-writes.ts +0 -16
  349. package/corpus/templates/analytics/app/pages/Team.tsx +0 -9
  350. package/corpus/templates/calendar/app/pages/Team.tsx +0 -12
  351. package/corpus/templates/mail/app/pages/Team.tsx +0 -17
  352. package/dist/server/env-var-writes.d.ts +0 -10
  353. package/dist/server/env-var-writes.d.ts.map +0 -1
  354. package/dist/server/env-var-writes.js +0 -18
  355. package/dist/server/env-var-writes.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import * as SelectPrimitive from "@radix-ui/react-select";
1
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
2
2
  import { IconCheck, IconChevronDown, IconLanguage } from "@tabler/icons-react";
3
3
  import i18next, { type i18n as I18nInstance } from "i18next";
4
4
  import React, {
@@ -34,6 +34,7 @@ import {
34
34
  } from "../localization/shared.js";
35
35
  import { setClientAppState } from "./application-state.js";
36
36
  import { callAction } from "./use-action.js";
37
+ import { cn } from "./utils.js";
37
38
 
38
39
  export {
39
40
  DEFAULT_LOCALE,
@@ -676,6 +677,7 @@ export function LanguagePicker({
676
677
  variant?: "select" | "icon";
677
678
  }) {
678
679
  const { locale, preference, setPreference } = useLocale();
680
+ const [open, setOpen] = useState(false);
679
681
  const copy =
680
682
  LANGUAGE_PICKER_COPY[locale] ?? LANGUAGE_PICKER_COPY[DEFAULT_LOCALE];
681
683
  const resolvedLabel = label ?? copy.label;
@@ -691,84 +693,94 @@ export function LanguagePicker({
691
693
  : []),
692
694
  ...SUPPORTED_LOCALES.map((code) => ({
693
695
  value: code,
694
- label:
695
- LOCALE_METADATA[code].nativeName === LOCALE_METADATA[code].englishName
696
- ? LOCALE_METADATA[code].nativeName
697
- : `${LOCALE_METADATA[code].nativeName} (${LOCALE_METADATA[code].englishName})`,
696
+ label: `${LOCALE_METADATA[code].nativeName} (${code})`,
698
697
  description: code,
699
698
  })),
700
699
  ];
701
700
  const selected = options.find((option) => option.value === preference);
701
+ const selectedLabel = selected?.label ?? preference;
702
+ const triggerLabel = `${resolvedLabel}: ${selectedLabel}`;
703
+
704
+ function handleOptionClick(value: LocalePreference) {
705
+ setOpen(false);
706
+ void setPreference(normalizeLocalizationPreference(value).locale);
707
+ }
702
708
 
703
709
  return (
704
710
  <div className={className}>
705
- <SelectPrimitive.Root
706
- value={preference}
707
- onValueChange={(value) =>
708
- void setPreference(normalizeLocalizationPreference(value).locale)
709
- }
710
- >
711
- <SelectPrimitive.Trigger
712
- className={
713
- variant === "icon"
714
- ? "flex h-8 w-8 items-center justify-center rounded-md border border-border bg-background text-foreground outline-none transition-colors hover:bg-accent/40 data-[placeholder]:text-muted-foreground"
715
- : "flex h-9 w-full items-center justify-between rounded-md border border-border bg-background px-3 text-start text-[12px] text-foreground outline-none transition-colors hover:bg-accent/40 data-[placeholder]:text-muted-foreground"
716
- }
717
- aria-label={resolvedLabel}
718
- title={selected?.label ?? resolvedLabel}
719
- >
720
- <span className="flex min-w-0 items-center gap-2">
721
- <IconLanguage className="h-4 w-4 shrink-0 text-muted-foreground" />
711
+ <PopoverPrimitive.Root open={open} onOpenChange={setOpen}>
712
+ <PopoverPrimitive.Trigger asChild>
713
+ <button
714
+ type="button"
715
+ aria-label={triggerLabel}
716
+ title={triggerLabel}
717
+ data-language-picker-trigger
718
+ className={cn(
719
+ "shrink-0 rounded-md border border-border bg-background text-foreground outline-none transition-colors hover:border-foreground/30 hover:bg-accent/40 hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 data-[state=open]:border-foreground/30 data-[state=open]:bg-accent/40",
720
+ variant === "icon"
721
+ ? "flex h-8 w-8 items-center justify-center"
722
+ : "flex h-9 w-full items-center justify-between gap-2 px-3 text-start text-sm",
723
+ )}
724
+ >
725
+ <span className="flex min-w-0 items-center gap-2">
726
+ <IconLanguage className="h-4 w-4 shrink-0 text-muted-foreground" />
727
+ {variant === "select" ? (
728
+ <span className="truncate">{selectedLabel}</span>
729
+ ) : (
730
+ <span className="sr-only">{triggerLabel}</span>
731
+ )}
732
+ </span>
722
733
  {variant === "select" ? (
723
- <SelectPrimitive.Value>
724
- <span className="truncate">
725
- {selected?.label ?? preference}
726
- </span>
727
- </SelectPrimitive.Value>
734
+ <IconChevronDown
735
+ className="h-3.5 w-3.5 shrink-0 text-muted-foreground"
736
+ aria-hidden="true"
737
+ />
728
738
  ) : null}
729
- </span>
730
- {variant === "select" ? (
731
- <SelectPrimitive.Icon asChild>
732
- <IconChevronDown className="h-3.5 w-3.5 text-muted-foreground" />
733
- </SelectPrimitive.Icon>
734
- ) : null}
735
- </SelectPrimitive.Trigger>
736
- <SelectPrimitive.Portal>
737
- <SelectPrimitive.Content
738
- position="popper"
739
+ </button>
740
+ </PopoverPrimitive.Trigger>
741
+ <PopoverPrimitive.Portal>
742
+ <PopoverPrimitive.Content
743
+ align={variant === "icon" ? "end" : "start"}
739
744
  sideOffset={6}
740
- className={
745
+ role="menu"
746
+ className={cn(
747
+ "z-[9999] max-h-[min(20rem,var(--radix-popover-content-available-height))] overflow-y-auto rounded-lg border border-border bg-popover p-1 text-popover-foreground shadow-lg outline-none will-change-[transform,opacity] data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:duration-100 data-[state=open]:duration-150 data-[state=closed]:ease-in data-[state=open]:ease-out data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1",
741
748
  variant === "icon"
742
- ? "z-[9999] min-w-56 overflow-hidden rounded-lg border border-border bg-popover shadow-lg"
743
- : "z-[9999] w-[var(--radix-select-trigger-width)] overflow-hidden rounded-lg border border-border bg-popover shadow-lg"
744
- }
749
+ ? "min-w-56"
750
+ : "w-[min(20rem,calc(100vw-2rem))] min-w-[var(--radix-popover-trigger-width)]",
751
+ )}
745
752
  >
746
- <SelectPrimitive.Viewport className="p-1">
747
- {options.map((option) => (
748
- <SelectPrimitive.Item
753
+ {options.map((option) => {
754
+ const optionSelected = option.value === preference;
755
+ return (
756
+ <button
749
757
  key={option.value}
750
- value={option.value}
751
- className="relative flex w-full cursor-pointer select-none items-start gap-2 rounded-md px-8 py-2.5 text-[12px] outline-none data-[highlighted]:bg-accent/60 data-[state=checked]:bg-accent/40"
758
+ type="button"
759
+ role="menuitemradio"
760
+ aria-checked={optionSelected}
761
+ title={option.description}
762
+ onClick={() => handleOptionClick(option.value)}
763
+ className={cn(
764
+ "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-start text-sm outline-none transition-colors hover:bg-accent/60 hover:text-foreground focus-visible:bg-accent/60 focus-visible:text-foreground",
765
+ optionSelected
766
+ ? "bg-accent/40 text-foreground"
767
+ : "text-muted-foreground",
768
+ )}
752
769
  >
753
- <span className="absolute start-2 top-2.5 flex h-4 w-4 items-center justify-center text-muted-foreground">
754
- <SelectPrimitive.ItemIndicator>
755
- <IconCheck className="h-3.5 w-3.5" />
756
- </SelectPrimitive.ItemIndicator>
757
- </span>
758
- <div className="flex min-w-0 flex-col">
759
- <SelectPrimitive.ItemText>
760
- <span className="text-foreground">{option.label}</span>
761
- </SelectPrimitive.ItemText>
762
- <span className="mt-0.5 text-[11px] leading-relaxed text-muted-foreground">
763
- {option.description}
764
- </span>
765
- </div>
766
- </SelectPrimitive.Item>
767
- ))}
768
- </SelectPrimitive.Viewport>
769
- </SelectPrimitive.Content>
770
- </SelectPrimitive.Portal>
771
- </SelectPrimitive.Root>
770
+ <IconCheck
771
+ className={cn(
772
+ "h-3.5 w-3.5 shrink-0",
773
+ optionSelected ? "opacity-100" : "opacity-0",
774
+ )}
775
+ aria-hidden="true"
776
+ />
777
+ <span className="truncate">{option.label}</span>
778
+ </button>
779
+ );
780
+ })}
781
+ </PopoverPrimitive.Content>
782
+ </PopoverPrimitive.Portal>
783
+ </PopoverPrimitive.Root>
772
784
  </div>
773
785
  );
774
786
  }
@@ -610,7 +610,13 @@ export {
610
610
  type DataWidgetResultMetadata,
611
611
  } from "./chat/widgets/data-widget-types.js";
612
612
  export { AgentNativeIcon } from "./components/icons/AgentNativeIcon.js";
613
- export { SettingsPanel, type SettingsPanelProps } from "./settings/index.js";
613
+ export {
614
+ SettingsPanel,
615
+ SettingsTabsPage,
616
+ type SettingsPanelProps,
617
+ type SettingsTabItem,
618
+ type SettingsTabsPageProps,
619
+ } from "./settings/index.js";
614
620
  export { useBuilderStatus } from "./settings/useBuilderStatus.js";
615
621
  export {
616
622
  openBuilderConnectPopup,
@@ -64,9 +64,9 @@ export interface OrgSwitcherProps {
64
64
  reserveSpace?: boolean;
65
65
  /**
66
66
  * Path to navigate to when the user clicks "Organization settings".
67
- * Defaults to `/team`, the standard organization-management route. Templates
68
- * with an established org surface can pass their own path; pass `null` to
69
- * only open the in-sidebar settings panel.
67
+ * Defaults to the standard Team tab inside Settings. Templates with an
68
+ * established org surface can pass their own path; pass `null` to only open
69
+ * the in-sidebar settings panel.
70
70
  */
71
71
  settingsPath?: string | null;
72
72
  }
@@ -97,7 +97,7 @@ const SECTION_LABEL_CLASS =
97
97
  const APP_SUBMENU_CONTENT_CLASS =
98
98
  "z-50 w-72 rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2";
99
99
 
100
- const DEFAULT_ORGANIZATION_SETTINGS_PATH = "/team";
100
+ const DEFAULT_ORGANIZATION_SETTINGS_PATH = "/settings#team";
101
101
 
102
102
  const APP_ICON_MAP: Record<string, typeof IconApps> = {
103
103
  Mail: IconMail,
@@ -131,7 +131,7 @@ function appMenuIcon(app: OrgSwitcherAppLink): typeof IconApps {
131
131
  }
132
132
 
133
133
  function organizationSettingsPath(path: string): string {
134
- return `${path.replace(/#.*$/, "")}#workspace-settings`;
134
+ return path.includes("#") ? path : `${path}#team`;
135
135
  }
136
136
 
137
137
  function AppMenuLink({
@@ -58,6 +58,11 @@ export interface TeamPageProps {
58
58
  * Title shown at the top of the page. Defaults to "Team".
59
59
  */
60
60
  title?: string;
61
+ /**
62
+ * Hide the page title when this is rendered inside another titled surface,
63
+ * such as the Settings > Team tab.
64
+ */
65
+ showTitle?: boolean;
61
66
  /**
62
67
  * Description shown on the "Create an Organization" card. Defaults to
63
68
  * "Set up a team to collaborate with your colleagues."
@@ -1260,6 +1265,7 @@ function A2ASecretSection({ secret }: { secret: string | null | undefined }) {
1260
1265
  export function TeamPage({
1261
1266
  layout,
1262
1267
  title,
1268
+ showTitle = true,
1263
1269
  createOrgDescription,
1264
1270
  className,
1265
1271
  }: TeamPageProps) {
@@ -1268,9 +1274,11 @@ export function TeamPage({
1268
1274
 
1269
1275
  const content = (
1270
1276
  <div className={`space-y-6 max-w-2xl ${className ?? ""}`}>
1271
- <h2 className="text-2xl font-bold tracking-tight">
1272
- {title ?? t("org.team")}
1273
- </h2>
1277
+ {showTitle ? (
1278
+ <h2 className="text-2xl font-bold tracking-tight">
1279
+ {title ?? t("org.team")}
1280
+ </h2>
1281
+ ) : null}
1274
1282
 
1275
1283
  {isLoading && (
1276
1284
  <section className="rounded-lg border border-border bg-card p-6">
@@ -0,0 +1,216 @@
1
+ import { IconHistory, IconSettings, IconUsers } from "@tabler/icons-react";
2
+ import {
3
+ useEffect,
4
+ useMemo,
5
+ useState,
6
+ type ComponentType,
7
+ type ReactNode,
8
+ } from "react";
9
+
10
+ import { cn } from "../utils.js";
11
+
12
+ type SettingsTabIcon = ComponentType<{ className?: string }>;
13
+
14
+ export interface SettingsTabItem {
15
+ id: string;
16
+ label: string;
17
+ icon?: SettingsTabIcon;
18
+ content: ReactNode;
19
+ }
20
+
21
+ export interface SettingsTabsPageProps {
22
+ general: ReactNode;
23
+ team?: ReactNode;
24
+ whatsNew?: ReactNode;
25
+ extraTabs?: SettingsTabItem[];
26
+ generalLabel?: string;
27
+ teamLabel?: string;
28
+ whatsNewLabel?: string;
29
+ ariaLabel?: string;
30
+ defaultTab?: string;
31
+ className?: string;
32
+ navClassName?: string;
33
+ contentClassName?: string;
34
+ }
35
+
36
+ function normalizeTabId(value?: string | null): string | null {
37
+ const normalized = value
38
+ ?.replace(/^#/, "")
39
+ .trim()
40
+ .toLowerCase()
41
+ .replace(/['"]/g, "")
42
+ .replace(/[\s_]+/g, "-");
43
+ if (!normalized) return null;
44
+ if (
45
+ normalized === "whats-new" ||
46
+ normalized === "what-s-new" ||
47
+ normalized === "changelog" ||
48
+ normalized === "updates"
49
+ ) {
50
+ return "whats-new";
51
+ }
52
+ if (
53
+ normalized === "workspace" ||
54
+ normalized === "workspace-settings" ||
55
+ normalized === "organization" ||
56
+ normalized === "org"
57
+ ) {
58
+ return "team";
59
+ }
60
+ return normalized;
61
+ }
62
+
63
+ function activeTabFromHash(
64
+ tabs: SettingsTabItem[],
65
+ defaultTab: string,
66
+ ): string {
67
+ if (typeof window === "undefined") return defaultTab;
68
+ const fromHash = normalizeTabId(window.location.hash);
69
+ if (fromHash && tabs.some((tab) => tab.id === fromHash)) return fromHash;
70
+ return defaultTab;
71
+ }
72
+
73
+ function updateHashForTab(tabId: string) {
74
+ if (typeof window === "undefined") return;
75
+ const { pathname, search } = window.location;
76
+ const hash = tabId === "general" ? "" : `#${encodeURIComponent(tabId)}`;
77
+ window.history.pushState(null, "", `${pathname}${search}${hash}`);
78
+ }
79
+
80
+ export function SettingsTabsPage({
81
+ general,
82
+ team,
83
+ whatsNew,
84
+ extraTabs = [],
85
+ generalLabel = "General",
86
+ teamLabel = "Team",
87
+ whatsNewLabel = "What's new",
88
+ ariaLabel = "Settings sections",
89
+ defaultTab = "general",
90
+ className,
91
+ navClassName,
92
+ contentClassName,
93
+ }: SettingsTabsPageProps) {
94
+ const tabs = useMemo<SettingsTabItem[]>(() => {
95
+ const next: SettingsTabItem[] = [
96
+ {
97
+ id: "general",
98
+ label: generalLabel,
99
+ icon: IconSettings,
100
+ content: general,
101
+ },
102
+ ];
103
+ if (team) {
104
+ next.push({
105
+ id: "team",
106
+ label: teamLabel,
107
+ icon: IconUsers,
108
+ content: team,
109
+ });
110
+ }
111
+ if (whatsNew) {
112
+ next.push({
113
+ id: "whats-new",
114
+ label: whatsNewLabel,
115
+ icon: IconHistory,
116
+ content: whatsNew,
117
+ });
118
+ }
119
+ next.push(...extraTabs);
120
+ return next;
121
+ }, [
122
+ extraTabs,
123
+ general,
124
+ generalLabel,
125
+ team,
126
+ teamLabel,
127
+ whatsNew,
128
+ whatsNewLabel,
129
+ ]);
130
+
131
+ const fallbackTab = tabs.some((tab) => tab.id === defaultTab)
132
+ ? defaultTab
133
+ : (tabs[0]?.id ?? "general");
134
+ const [activeTab, setActiveTab] = useState(() =>
135
+ activeTabFromHash(tabs, fallbackTab),
136
+ );
137
+
138
+ useEffect(() => {
139
+ if (tabs.some((tab) => tab.id === activeTab)) return;
140
+ setActiveTab(fallbackTab);
141
+ }, [activeTab, fallbackTab, tabs]);
142
+
143
+ useEffect(() => {
144
+ const handleHashChange = () => {
145
+ setActiveTab(activeTabFromHash(tabs, fallbackTab));
146
+ };
147
+ window.addEventListener("hashchange", handleHashChange);
148
+ return () => window.removeEventListener("hashchange", handleHashChange);
149
+ }, [fallbackTab, tabs]);
150
+
151
+ const selectedTab = tabs.find((tab) => tab.id === activeTab) ?? tabs[0];
152
+
153
+ return (
154
+ <div
155
+ className={cn(
156
+ "flex min-h-full w-full flex-col overflow-hidden bg-background sm:flex-row",
157
+ className,
158
+ )}
159
+ >
160
+ <nav
161
+ aria-label={ariaLabel}
162
+ role="tablist"
163
+ className={cn(
164
+ "flex shrink-0 gap-1 overflow-x-auto border-b border-border/50 bg-muted/30 p-2 sm:w-48 sm:flex-col sm:overflow-x-visible sm:border-b-0 sm:border-e",
165
+ navClassName,
166
+ )}
167
+ >
168
+ {tabs.map((tab) => {
169
+ const Icon = tab.icon;
170
+ const selected = tab.id === selectedTab?.id;
171
+ return (
172
+ <button
173
+ key={tab.id}
174
+ type="button"
175
+ role="tab"
176
+ aria-selected={selected}
177
+ aria-controls={`settings-tabpanel-${tab.id}`}
178
+ id={`settings-tab-${tab.id}`}
179
+ onClick={() => {
180
+ setActiveTab(tab.id);
181
+ updateHashForTab(tab.id);
182
+ }}
183
+ className={cn(
184
+ "flex min-h-9 shrink-0 items-center gap-2 rounded-md px-3 py-2 text-start text-sm font-medium transition-colors sm:w-full",
185
+ selected
186
+ ? "bg-background text-foreground shadow-sm ring-1 ring-border/60"
187
+ : "text-muted-foreground hover:bg-accent/60 hover:text-foreground",
188
+ )}
189
+ >
190
+ {Icon ? (
191
+ <Icon
192
+ className={cn(
193
+ "size-4 shrink-0",
194
+ selected ? "text-foreground" : "text-muted-foreground",
195
+ )}
196
+ />
197
+ ) : null}
198
+ <span className="truncate">{tab.label}</span>
199
+ </button>
200
+ );
201
+ })}
202
+ </nav>
203
+ <div
204
+ id={`settings-tabpanel-${selectedTab?.id ?? "general"}`}
205
+ role="tabpanel"
206
+ aria-labelledby={`settings-tab-${selectedTab?.id ?? "general"}`}
207
+ className={cn(
208
+ "min-w-0 flex-1 overflow-y-auto p-4 sm:p-6",
209
+ contentClassName,
210
+ )}
211
+ >
212
+ {selectedTab?.content}
213
+ </div>
214
+ </div>
215
+ );
216
+ }
@@ -1,3 +1,8 @@
1
1
  export { SettingsPanel, type SettingsPanelProps } from "./SettingsPanel.js";
2
+ export {
3
+ SettingsTabsPage,
4
+ type SettingsTabItem,
5
+ type SettingsTabsPageProps,
6
+ } from "./SettingsTabsPage.js";
2
7
  export { useBuilderStatus } from "./useBuilderStatus.js";
3
8
  export { SecretsSection, type SecretsSectionProps } from "./SecretsSection.js";
@@ -8,6 +8,7 @@ export {
8
8
  unregisterFileUploadProvider,
9
9
  listFileUploadProviders,
10
10
  getActiveFileUploadProvider,
11
+ getActiveFileUploadProviderForRequest,
11
12
  uploadFile,
12
13
  } from "./registry.js";
13
14
  export { builderFileUploadProvider } from "./builder.js";
@@ -54,6 +54,24 @@ export function getActiveFileUploadProvider(): FileUploadProvider | null {
54
54
  return null;
55
55
  }
56
56
 
57
+ export async function getActiveFileUploadProviderForRequest(): Promise<FileUploadProvider | null> {
58
+ for (const provider of providers.values()) {
59
+ if (provider.isConfigured()) return provider;
60
+ if (provider.isConfiguredForRequest) {
61
+ try {
62
+ if (await provider.isConfiguredForRequest()) return provider;
63
+ } catch {
64
+ // Treat failed scoped credential lookups as unavailable. The upload
65
+ // call will surface real provider errors after a provider is selected.
66
+ }
67
+ }
68
+ }
69
+ if (builderFileUploadProvider.isConfigured()) {
70
+ return builderFileUploadProvider;
71
+ }
72
+ return null;
73
+ }
74
+
57
75
  /**
58
76
  * Upload a file via the active provider, or `null` if no provider is
59
77
  * configured. Callers use `null` as the signal to fall back to SQL
@@ -63,12 +81,10 @@ export function getActiveFileUploadProvider(): FileUploadProvider | null {
63
81
  export async function uploadFile(
64
82
  input: FileUploadInput,
65
83
  ): Promise<FileUploadResult | null> {
66
- const provider = getActiveFileUploadProvider();
67
- // Only trust user-registered providers (S3, etc.) from the sync check.
68
- // The builder builtin's isConfigured() only checks process.env, which causes
69
- // hard failures for authenticated non-local users on multi-tenant deployments
70
- // where BUILDER_PRIVATE_KEY is set at the deploy level but the user has no
71
- // personal credentials. Always resolve builder credentials asynchronously.
84
+ const provider = await getActiveFileUploadProviderForRequest();
85
+ // User-registered providers (S3, etc.) may be configured by sync runtime
86
+ // state or request-scoped DB secrets. Builder still gets an explicit async
87
+ // credential check below because its sync isConfigured() only checks env.
72
88
  if (provider && provider !== builderFileUploadProvider) {
73
89
  return provider.upload(input);
74
90
  }
@@ -32,8 +32,13 @@ export interface FileUploadProvider {
32
32
  id: string;
33
33
  /** Human-readable name. */
34
34
  name: string;
35
- /** Returns true if this provider is currently configured (env vars etc.). */
35
+ /** Returns true if this provider is configured from synchronous runtime state. */
36
36
  isConfigured: () => boolean;
37
+ /**
38
+ * Returns true if this provider is configured for the active request.
39
+ * Use for DB-backed user/org/workspace secrets that require request context.
40
+ */
41
+ isConfiguredForRequest?: () => Promise<boolean>;
37
42
  /** Upload a file and return a URL. Throw on failure. */
38
43
  upload: (input: FileUploadInput) => Promise<FileUploadResult>;
39
44
  }