@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
@@ -125,6 +125,8 @@ export async function trackSignupEvent({
125
125
  // Persistent auth secret
126
126
  // ---------------------------------------------------------------------------
127
127
 
128
+ let inMemoryDevAuthSecret: string | undefined;
129
+
128
130
  /**
129
131
  * Resolve the Better Auth signing secret.
130
132
  *
@@ -133,13 +135,9 @@ export async function trackSignupEvent({
133
135
  * 2. Hosted workspace deploys can derive a per-purpose secret from the
134
136
  * already-required `A2A_SECRET` root. This keeps fresh workspace branches
135
137
  * bootable without reusing the raw A2A key as a cookie-signing key.
136
- * 3. `.env.local` in the template cwd — a per-workspace persistent secret
137
- * that the framework writes once on first boot when no secret is set.
138
- * Gitignored by convention (`.env*` in template .gitignore files), so
139
- * it's safe to persist credentials here.
140
- * 4. Generate a new random 32-byte hex, write it to `.env.local`, and use
141
- * it. Subsequent restarts re-read the same file — so session cookies
142
- * signed by a previous boot remain valid across dev-server restarts.
138
+ * 3. Existing `.env.local` values in the template cwd — read-only
139
+ * compatibility for projects that already configured this secret.
140
+ * 4. Generate a per-process in-memory random 32-byte hex in development.
143
141
  *
144
142
  * Why this matters: before this helper existed, missing `BETTER_AUTH_SECRET`
145
143
  * fell through to `GOOGLE_CLIENT_SECRET` / `ACCESS_TOKEN` / a hardcoded
@@ -147,8 +145,8 @@ export async function trackSignupEvent({
147
145
  * boot would re-fall back to the hardcoded value (still stable) — but
148
146
  * rotating Google credentials, toggling `ACCESS_TOKEN`, or churning the
149
147
  * fallback chain would invalidate every signed cookie and force everyone
150
- * to sign in again. Pinning the secret to `.env.local` on first boot
151
- * removes that footgun.
148
+ * to sign in again. We still read explicit env configuration, but never
149
+ * auto-write a generated secret into env files.
152
150
  */
153
151
  function resolveAuthSecret(): string {
154
152
  if (process.env.BETTER_AUTH_SECRET) return process.env.BETTER_AUTH_SECRET;
@@ -177,47 +175,26 @@ function resolveAuthSecret(): string {
177
175
  );
178
176
  }
179
177
 
180
- // Dev: persist a generated secret to .env.local so sessions survive
181
- // dev-server restarts. Falls back to an in-memory random secret only if
182
- // the filesystem isn't writable (rare in dev, e.g. read-only mounts) —
183
- // sessions reset on every dev-process restart in that case, which is
184
- // fine.
185
- //
186
178
  // SECURITY (audit 09 LOW-2): the previous fallback chain
187
179
  // (`GOOGLE_CLIENT_SECRET || ACCESS_TOKEN || hardcoded`) reused
188
180
  // cross-purpose secrets and a public hardcoded literal as the cookie
189
181
  // HMAC. Dropped entirely — better to mint an ephemeral secret than to
190
182
  // re-use a Google client secret or a known string.
191
- try {
192
- const envLocalPath = path.resolve(process.cwd(), ".env.local");
193
- const existing = readEnvLocalSecret(envLocalPath);
194
- if (existing) {
195
- process.env.BETTER_AUTH_SECRET = existing; // guard:allow-env-mutation — boot-time secret resolution from .env.local, runs once at module init
196
- return existing;
197
- }
183
+ const existing = readEnvLocalSecret(
184
+ path.resolve(process.cwd(), ".env.local"),
185
+ );
186
+ if (existing) return existing;
198
187
 
199
- const generated = crypto.randomBytes(32).toString("hex");
200
- appendEnvLocalSecret(envLocalPath, generated);
201
- process.env.BETTER_AUTH_SECRET = generated; // guard:allow-env-mutation — boot-time secret generation, runs once at module init before any request
202
- console.log(
203
- "[agent-native] Generated a persistent BETTER_AUTH_SECRET in .env.local. " +
204
- "Sessions will now survive dev-server restarts. " +
205
- "(Delete .env.local to rotate; set BETTER_AUTH_SECRET in .env to override.)",
206
- );
207
- return generated;
208
- } catch {
209
- // Filesystem unwritable (read-only mount, sandboxed test env, etc.).
210
- // Mint a per-process random secret so cookies stay unique per boot.
211
- // Sessions reset when the dev process restarts — acceptable for dev.
212
- const ephemeral = crypto.randomBytes(32).toString("hex");
188
+ if (!inMemoryDevAuthSecret) {
189
+ inMemoryDevAuthSecret = crypto.randomBytes(32).toString("hex");
213
190
  console.warn(
214
- "[agent-native] Could not persist BETTER_AUTH_SECRET to .env.local " +
215
- "(filesystem unwritable). Using an ephemeral in-memory secret. " +
216
- "Sessions will reset every time this process restarts. " +
217
- "Set BETTER_AUTH_SECRET in your environment to keep sessions valid across restarts.",
191
+ "[agent-native] BETTER_AUTH_SECRET is not configured. Using an ephemeral " +
192
+ "in-memory development secret. Sessions will reset every time this " +
193
+ "process restarts. Set BETTER_AUTH_SECRET in your environment to keep " +
194
+ "sessions valid across restarts.",
218
195
  );
219
- return ephemeral;
220
196
  }
197
+ return inMemoryDevAuthSecret;
221
198
  }
222
199
 
223
200
  function readEnvLocalSecret(envLocalPath: string): string | undefined {
@@ -234,26 +211,6 @@ function readEnvLocalSecret(envLocalPath: string): string | undefined {
234
211
  }
235
212
  }
236
213
 
237
- function appendEnvLocalSecret(envLocalPath: string, secret: string): void {
238
- const header =
239
- "# Auto-generated by agent-native on first boot. Gitignored.\n" +
240
- "# Keeps signed session cookies valid across dev-server restarts.\n" +
241
- "# Delete this file (or this line) to rotate the secret.\n";
242
- const line = `BETTER_AUTH_SECRET=${secret}\n`;
243
-
244
- // If the file already exists, just append; otherwise create with header.
245
- if (fs.existsSync(envLocalPath)) {
246
- const existing = fs.readFileSync(envLocalPath, "utf8");
247
- const needsLeadingNewline = existing.length > 0 && !existing.endsWith("\n");
248
- fs.appendFileSync(
249
- envLocalPath,
250
- (needsLeadingNewline ? "\n" : "") + "\n" + header + line,
251
- );
252
- } else {
253
- fs.writeFileSync(envLocalPath, header + line, { mode: 0o600 });
254
- }
255
- }
256
-
257
214
  function normalizeTrackingSlug(value: string | undefined): string | undefined {
258
215
  const trimmed = value?.trim();
259
216
  if (!trimmed) return undefined;
@@ -912,7 +869,7 @@ async function createBetterAuthInstance(
912
869
  const appUrl = getAppProductionUrl();
913
870
  const cookieNamespace = resolveAuthCookieNamespace();
914
871
  const requireEmailVerification =
915
- isEmailConfigured() && !shouldSkipEmailVerification();
872
+ (await isEmailConfigured()) && !shouldSkipEmailVerification();
916
873
 
917
874
  const shouldMirrorGoogleAccountTokens =
918
875
  (config?.googleScopes?.length ?? 0) > 0;
@@ -1,5 +1,3 @@
1
- import path from "node:path";
2
-
3
1
  import {
4
2
  defineEventHandler,
5
3
  setResponseStatus,
@@ -46,7 +44,7 @@ import { mountDbAdminRoutes } from "../db-admin/routes.js";
46
44
  import { getDbExec } from "../db/client.js";
47
45
  import {
48
46
  uploadFile,
49
- getActiveFileUploadProvider,
47
+ getActiveFileUploadProviderForRequest,
50
48
  listFileUploadProviders,
51
49
  } from "../file-upload/index.js";
52
50
  import { handleMcpConnect } from "../mcp/connect-route.js";
@@ -57,10 +55,8 @@ import {
57
55
  } from "../mcp/oauth-route.js";
58
56
  import { registerBuiltinNotificationChannels } from "../notifications/channels.js";
59
57
  import { createNotificationsHandler } from "../notifications/routes.js";
60
- import { listOnboardingSteps } from "../onboarding/registry.js";
61
58
  import { getOrgContext } from "../org/context.js";
62
59
  import { createProgressHandler } from "../progress/routes.js";
63
- import { findWorkspaceRoot } from "../scripts/utils.js";
64
60
  import { registerFrameworkSecrets } from "../secrets/register-framework-secrets.js";
65
61
  import {
66
62
  createListSecretsHandler,
@@ -124,14 +120,12 @@ import {
124
120
  getAllowedCorsOrigin,
125
121
  readCorsAllowedOrigins,
126
122
  } from "./cors-origins.js";
127
- import { upsertEnvFile } from "./create-server.js";
128
123
  import type { EnvKeyConfig } from "./create-server.js";
129
124
  import {
130
125
  canUseDeployCredentialFallbackForRequest,
131
126
  resolveSecret,
132
127
  } from "./credential-provider.js";
133
128
  import { createEmbedStartRouteHandler } from "./embed-route.js";
134
- import { isEnvVarWriteAllowed } from "./env-var-writes.js";
135
129
  import {
136
130
  getH3App,
137
131
  awaitBootstrap,
@@ -151,6 +145,12 @@ import { createOpenRouteHandler } from "./open-route.js";
151
145
  import { createPollEventsHandler } from "./poll-events.js";
152
146
  import { createPollHandler } from "./poll.js";
153
147
  import { runWithRequestContext } from "./request-context.js";
148
+ import {
149
+ findUnsupportedScopedKeyNames,
150
+ saveKeyValuesToScopedSecrets,
151
+ ScopedKeyStorageError,
152
+ type ScopedKeySaveRequestScope,
153
+ } from "./scoped-key-storage.js";
154
154
  import { createTranscribeVoiceHandler } from "./transcribe-voice.js";
155
155
  import { createVoiceProvidersStatusHandler } from "./voice-providers-status.js";
156
156
 
@@ -340,10 +340,6 @@ function parseBuilderCallbackBoolean(
340
340
  return /^(1|true)$/i.test(value);
341
341
  }
342
342
 
343
- const PROVIDER_ENV_VAR_KEYS = new Set(
344
- Object.values(PROVIDER_ENV_META).map(({ envVar }) => envVar),
345
- );
346
-
347
343
  // Raster-only data-URI allowlist for avatar writes. SVG is deliberately absent:
348
344
  // data:image/svg+xml payloads can carry inline <script> and event-handler
349
345
  // attributes that execute when the browser renders them as an <img> src or
@@ -652,7 +648,7 @@ export interface CoreRoutesPluginOptions {
652
648
  * GET /_agent-native/ping — health check
653
649
  * GET /_agent-native/health — DB liveness probe + scale-to-zero warmup
654
650
  * GET /_agent-native/env-status — env key configuration status (when envKeys provided)
655
- * POST /_agent-native/env-vars — save env vars to .env (when envKeys provided)
651
+ * POST /_agent-native/env-vars — compatibility route that saves keys to scoped DB secrets
656
652
  * GET /_agent-native/application-state/:key — read application state
657
653
  * PUT /_agent-native/application-state/:key — write application state
658
654
  * DELETE /_agent-native/application-state/:key — delete application state
@@ -681,20 +677,9 @@ export function createCoreRoutesPlugin(
681
677
  try {
682
678
  await awaitBootstrap(nitroApp);
683
679
 
684
- // Restore env vars from the settings table. On serverless, .env
685
- // writes don't persist across invocations the DB is the durable
686
- // store. Only set keys that are currently empty so explicit env
687
- // vars (Netlify dashboard, process-level) always win.
688
- //
689
- // GATED: only rehydrate into `process.env` on local-dev SQLite (or
690
- // with the explicit single-tenant opt-in). On a shared-DB hosted
691
- // multi-tenant deploy the `persisted-env-vars` row is deployment-wide
692
- // global state — pushing user-supplied values into `process.env` from
693
- // it would let any one tenant's writes (or a stale dev seed) leak
694
- // into every other tenant's process. The opt-out scrub of legacy
695
- // BUILDER_* values still runs unconditionally so existing rows on
696
- // multi-tenant deploys self-heal, but new env-var writes never land
697
- // in `process.env` outside the allowed contexts.
680
+ // Legacy cleanup: key saves now go to scoped app_secrets rows. Do not
681
+ // rehydrate the old deployment-global `persisted-env-vars` row into
682
+ // process.env; keep only the Builder scrub so stale leaked keys self-heal.
698
683
  try {
699
684
  const persisted = (await getSetting("persisted-env-vars")) as Record<
700
685
  string,
@@ -702,15 +687,10 @@ export function createCoreRoutesPlugin(
702
687
  > | null;
703
688
  if (persisted) {
704
689
  const builderKeys = new Set<string>(BUILDER_ENV_KEYS);
705
- const writesAllowed = isEnvVarWriteAllowed();
706
690
  let scrubbed = 0;
707
- for (const [k, v] of Object.entries(persisted)) {
691
+ for (const k of Object.keys(persisted)) {
708
692
  if (builderKeys.has(k)) {
709
693
  scrubbed++;
710
- continue;
711
- }
712
- if (writesAllowed && typeof v === "string" && !process.env[k]) {
713
- process.env[k] = v;
714
694
  }
715
695
  }
716
696
  if (scrubbed > 0) {
@@ -2229,24 +2209,7 @@ export function createCoreRoutesPlugin(
2229
2209
  ];
2230
2210
  {
2231
2211
  const envKeys = [...frameworkEnvKeys, ...(options.envKeys ?? [])];
2232
-
2233
- // Onboarding form fields are resolved per-request so late-registered
2234
- // steps (and template overrides) are picked up without a restart.
2235
- // Builder CLI auth writes scoped Builder credentials through the
2236
- // credential provider, never through the deploy-global env sink.
2237
- const collectOnboardingKeys = (): Set<string> => {
2238
- const keys = new Set<string>();
2239
- for (const step of listOnboardingSteps()) {
2240
- for (const method of step.methods) {
2241
- if (method.kind === "form") {
2242
- for (const field of method.payload.fields) {
2243
- if (field?.key) keys.add(field.key);
2244
- }
2245
- }
2246
- }
2247
- }
2248
- return keys;
2249
- };
2212
+ const allowedEnvKeyNames = envKeys.map(({ key }) => key);
2250
2213
 
2251
2214
  getH3App(nitroApp).use(
2252
2215
  `${P}/env-status`,
@@ -2264,12 +2227,11 @@ export function createCoreRoutesPlugin(
2264
2227
  }
2265
2228
  return Promise.all(
2266
2229
  envKeys.map(async (cfg) => {
2267
- const isProviderKey = PROVIDER_ENV_VAR_KEYS.has(cfg.key);
2268
- const configured = isProviderKey
2269
- ? await runWithRequestContext({ userEmail, orgId }, () =>
2270
- resolveSecret(cfg.key).then(Boolean),
2271
- )
2272
- : !!process.env[cfg.key];
2230
+ const configured =
2231
+ Boolean(process.env[cfg.key]) ||
2232
+ (await runWithRequestContext({ userEmail, orgId }, () =>
2233
+ resolveSecret(cfg.key).then(Boolean),
2234
+ ));
2273
2235
  return {
2274
2236
  key: cfg.key,
2275
2237
  label: cfg.label,
@@ -2290,101 +2252,37 @@ export function createCoreRoutesPlugin(
2290
2252
  return { error: "Method not allowed" };
2291
2253
  }
2292
2254
 
2293
- // Env vars are deployment-wide globals, not per-tenant. On any
2294
- // shared-DB multi-tenant deploy, allowing authenticated users to
2295
- // write here lets one tenant overwrite Stripe / OpenAI / Sentry
2296
- // keys for every other tenant. Disable the endpoint outside of
2297
- // local-dev SQLite or an explicit single-tenant opt-in, and
2298
- // direct callers to scoped secret/credential stores instead.
2299
- if (!isEnvVarWriteAllowed()) {
2300
- setResponseStatus(event, 403);
2301
- return {
2302
- error:
2303
- "env-vars endpoint disabled on multi-tenant deployments. Use scoped secrets or credentials for user/org API keys.",
2304
- };
2305
- }
2306
-
2307
2255
  const body = await readBody(event);
2308
- const { vars } = body as {
2256
+ const { vars, scope } = body as {
2309
2257
  vars?: Array<{ key: string; value: string }>;
2258
+ scope?: ScopedKeySaveRequestScope;
2310
2259
  };
2311
-
2312
- if (!Array.isArray(vars) || vars.length === 0) {
2313
- setResponseStatus(event, 400);
2314
- return { error: "vars array required" };
2315
- }
2316
-
2317
- const allowedKeys = new Set<string>([
2318
- ...envKeys.map((k) => k.key),
2319
- ...collectOnboardingKeys(),
2320
- ]);
2321
- const blockedEnvVarWriteKeys = new Set<string>(BUILDER_ENV_KEYS);
2322
- const isWritableEnvKey = (key: string) =>
2323
- allowedKeys.has(key) && !blockedEnvVarWriteKeys.has(key);
2324
-
2325
- const filtered = vars.filter(
2326
- (v) =>
2327
- typeof v.key === "string" &&
2328
- isWritableEnvKey(v.key) &&
2329
- typeof v.value === "string" &&
2330
- v.value.trim().length > 0,
2260
+ const unsupportedKeys = findUnsupportedScopedKeyNames(
2261
+ vars,
2262
+ allowedEnvKeyNames,
2331
2263
  );
2332
- if (filtered.length === 0) {
2264
+ if (unsupportedKeys.length > 0) {
2333
2265
  setResponseStatus(event, 400);
2334
- const rejectedEmpty = vars.some(
2335
- (v) =>
2336
- typeof v.key === "string" &&
2337
- isWritableEnvKey(v.key) &&
2338
- (typeof v.value !== "string" || v.value.trim().length === 0),
2339
- );
2340
2266
  return {
2341
- error: rejectedEmpty
2342
- ? "Env values must be non-empty — refusing to clear a saved key"
2343
- : "No recognized env keys in request",
2267
+ error: `Unsupported env key${unsupportedKeys.length === 1 ? "" : "s"}: ${unsupportedKeys.join(", ")}`,
2344
2268
  };
2345
2269
  }
2346
2270
 
2347
- // Write to .env file. When inside a workspace, write to the
2348
- // workspace root .env so keys are shared across every app. The
2349
- // per-app .env still wins at load time if it also defines a key.
2350
2271
  try {
2351
- const scope =
2352
- (body as { scope?: "workspace" | "app" })?.scope ?? "auto";
2353
- const workspaceRoot = findWorkspaceRoot(process.cwd());
2354
- const envPath =
2355
- scope === "app"
2356
- ? path.join(process.cwd(), ".env")
2357
- : workspaceRoot
2358
- ? path.join(workspaceRoot, ".env")
2359
- : path.join(process.cwd(), ".env");
2360
- await upsertEnvFile(envPath, filtered);
2361
- } catch {
2362
- // Edge runtime — skip file write
2363
- }
2364
-
2365
- // Update process.env immediately
2366
- for (const { key, value } of filtered) {
2367
- process.env[key] = value;
2368
- }
2369
-
2370
- // Persist to settings table for serverless cold-start recovery.
2371
- try {
2372
- const envMap: Record<string, string> = {};
2373
- for (const { key, value } of filtered) envMap[key] = value;
2374
- const existing =
2375
- ((await getSetting("persisted-env-vars")) as Record<
2376
- string,
2377
- string
2378
- > | null) ?? {};
2379
- await putSetting("persisted-env-vars", {
2380
- ...existing,
2381
- ...envMap,
2382
- });
2383
- } catch {
2384
- // DB not ready yet — skip
2272
+ const result = await saveKeyValuesToScopedSecrets(
2273
+ event,
2274
+ vars,
2275
+ scope,
2276
+ );
2277
+ return { saved: result.saved, storage: "scoped-secrets" };
2278
+ } catch (err) {
2279
+ if (err instanceof ScopedKeyStorageError) {
2280
+ setResponseStatus(event, err.statusCode);
2281
+ return { error: err.message };
2282
+ }
2283
+ setResponseStatus(event, 500);
2284
+ return { error: "Failed to save keys" };
2385
2285
  }
2386
-
2387
- return { saved: filtered.map((v) => v.key) };
2388
2286
  }),
2389
2287
  );
2390
2288
  }
@@ -2729,54 +2627,69 @@ export function createCoreRoutesPlugin(
2729
2627
  getH3App(nitroApp).use(
2730
2628
  `${P}/file-upload/status`,
2731
2629
  defineEventHandler(async (event) => {
2732
- const active = getActiveFileUploadProvider();
2733
2630
  // resolveBuilderPrivateKey() reads per-user credentials from app_secrets
2734
2631
  // (DB), which requires request context (AsyncLocalStorage) to know which
2735
2632
  // user to scope by. Without runWithRequestContext() the ALS store is empty
2736
2633
  // and it falls back to process.env only — missing OAuth-connected users.
2737
2634
  const session = await getSession(event).catch(() => null);
2738
2635
  const userEmail = session?.email;
2739
- let builderConfigured = !!process.env.BUILDER_PRIVATE_KEY;
2740
- try {
2741
- const { resolveBuilderPrivateKey } =
2742
- await import("./credential-provider.js");
2743
- const resolve = () => resolveBuilderPrivateKey().then((k) => !!k);
2744
- builderConfigured = userEmail
2745
- ? await runWithRequestContext(
2746
- { userEmail, orgId: session?.orgId },
2747
- resolve,
2748
- )
2749
- : await resolve();
2750
- } catch {
2751
- // fall back to env check above
2752
- }
2753
- // When the builder builtin is selected via env var, its sync
2754
- // isConfigured() doesn't reflect per-user OAuth credentials. Use the
2755
- // async builderConfigured check so the status accurately represents
2756
- // whether this specific user can actually upload (thread 7 fix).
2757
- const isBuilderEnvActive = active?.id === "builder";
2758
- const configured = isBuilderEnvActive
2759
- ? builderConfigured
2760
- : !!active || builderConfigured;
2761
- const activeProvider = isBuilderEnvActive
2762
- ? builderConfigured
2763
- ? { id: "builder", name: "Builder.io" }
2764
- : null
2765
- : active
2766
- ? { id: active.id, name: active.name }
2767
- : builderConfigured
2636
+ const resolveStatus = async () => {
2637
+ const active = await getActiveFileUploadProviderForRequest();
2638
+ let builderConfigured = !!process.env.BUILDER_PRIVATE_KEY;
2639
+ try {
2640
+ const { resolveBuilderPrivateKey } =
2641
+ await import("./credential-provider.js");
2642
+ builderConfigured = await resolveBuilderPrivateKey().then(
2643
+ (k) => !!k,
2644
+ );
2645
+ } catch {
2646
+ // fall back to env check above
2647
+ }
2648
+
2649
+ const providers = await Promise.all(
2650
+ listFileUploadProviders().map(async (p) => {
2651
+ const scopedConfigured = p.isConfiguredForRequest
2652
+ ? await p.isConfiguredForRequest().catch(() => false)
2653
+ : false;
2654
+ return {
2655
+ id: p.id,
2656
+ name: p.name,
2657
+ configured: p.isConfigured() || scopedConfigured,
2658
+ };
2659
+ }),
2660
+ );
2661
+
2662
+ // When the builder builtin is selected via env var, its sync
2663
+ // isConfigured() doesn't reflect per-user OAuth credentials. Use
2664
+ // builderConfigured so status reflects this specific request.
2665
+ const isBuilderEnvActive = active?.id === "builder";
2666
+ const configured = isBuilderEnvActive
2667
+ ? builderConfigured
2668
+ : !!active || builderConfigured;
2669
+ const activeProvider = isBuilderEnvActive
2670
+ ? builderConfigured
2768
2671
  ? { id: "builder", name: "Builder.io" }
2769
- : null;
2770
- return {
2771
- configured,
2772
- activeProvider,
2773
- providers: listFileUploadProviders().map((p) => ({
2774
- id: p.id,
2775
- name: p.name,
2776
- configured: p.isConfigured(),
2777
- })),
2778
- builderConfigured,
2672
+ : null
2673
+ : active
2674
+ ? { id: active.id, name: active.name }
2675
+ : builderConfigured
2676
+ ? { id: "builder", name: "Builder.io" }
2677
+ : null;
2678
+
2679
+ return {
2680
+ configured,
2681
+ activeProvider,
2682
+ providers,
2683
+ builderConfigured,
2684
+ };
2779
2685
  };
2686
+
2687
+ return userEmail
2688
+ ? runWithRequestContext(
2689
+ { userEmail, orgId: session?.orgId },
2690
+ resolveStatus,
2691
+ )
2692
+ : resolveStatus();
2780
2693
  }),
2781
2694
  );
2782
2695