@agent-native/core 0.117.1 → 0.117.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (347) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +10 -0
  3. package/corpus/core/package.json +2 -1
  4. package/corpus/core/src/cli/context-xray-local.ts +1 -1
  5. package/corpus/core/src/client/extensions/ExtensionViewer.tsx +4 -4
  6. package/corpus/core/src/client/extensions/ExtensionsSidebarSection.tsx +11 -11
  7. package/corpus/core/src/client/extensions/agent-native-extension-runtime.ts +1 -1
  8. package/corpus/core/src/client/extensions/delete-extension.ts +2 -2
  9. package/corpus/core/src/client/org/OrgSwitcher.tsx +25 -1
  10. package/corpus/core/src/client/settings/SettingsPanel.tsx +102 -10
  11. package/corpus/core/src/client/sharing/ShareButton.tsx +7 -6
  12. package/corpus/core/src/db/client.ts +13 -10
  13. package/corpus/core/src/extensions/actions.ts +13 -5
  14. package/corpus/core/src/extensions/local.ts +1 -0
  15. package/corpus/core/src/extensions/routes.ts +10 -5
  16. package/corpus/core/src/extensions/schema.ts +5 -0
  17. package/corpus/core/src/extensions/slots/store.ts +14 -4
  18. package/corpus/core/src/extensions/store.ts +55 -19
  19. package/corpus/core/src/extensions/theme.ts +1 -1
  20. package/corpus/core/src/file-upload/registry.ts +9 -0
  21. package/corpus/core/src/index.browser.ts +5 -0
  22. package/corpus/core/src/index.ts +1 -0
  23. package/corpus/core/src/localization/default-messages.ts +23 -6
  24. package/corpus/core/src/server/action-discovery.ts +8 -0
  25. package/corpus/core/src/server/better-auth-instance.ts +7 -2
  26. package/corpus/core/src/server/email-template.ts +18 -0
  27. package/corpus/core/src/server/email-templates.ts +3 -0
  28. package/corpus/core/src/server/email.ts +40 -4
  29. package/corpus/core/src/sharing/actions/share-resource.ts +1 -0
  30. package/corpus/core/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  31. package/corpus/core/src/templates/default/app/global.css +1 -1
  32. package/corpus/core/src/templates/default/app/i18n/ar-SA.ts +6 -6
  33. package/corpus/core/src/templates/default/app/i18n/de-DE.ts +6 -6
  34. package/corpus/core/src/templates/default/app/i18n/en-US.ts +6 -6
  35. package/corpus/core/src/templates/default/app/i18n/es-ES.ts +6 -6
  36. package/corpus/core/src/templates/default/app/i18n/fr-FR.ts +6 -6
  37. package/corpus/core/src/templates/default/app/i18n/hi-IN.ts +6 -6
  38. package/corpus/core/src/templates/default/app/i18n/ja-JP.ts +6 -6
  39. package/corpus/core/src/templates/default/app/i18n/ko-KR.ts +6 -6
  40. package/corpus/core/src/templates/default/app/i18n/pt-BR.ts +6 -6
  41. package/corpus/core/src/templates/default/app/i18n/zh-CN.ts +7 -7
  42. package/corpus/core/src/templates/default/app/i18n/zh-TW.ts +7 -7
  43. package/corpus/core/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  44. package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  45. package/corpus/core/src/user-profile/actions/get-user-profile.ts +16 -0
  46. package/corpus/core/src/user-profile/actions/update-user-profile.ts +24 -0
  47. package/corpus/core/src/user-profile/index.ts +5 -0
  48. package/corpus/core/src/user-profile/shared.ts +14 -0
  49. package/corpus/core/src/user-profile/store.ts +50 -0
  50. package/corpus/templates/analytics/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  51. package/corpus/templates/assets/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  52. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +2 -2
  53. package/corpus/templates/assets/app/global.css +1 -1
  54. package/corpus/templates/assets/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
  55. package/corpus/templates/brain/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  56. package/corpus/templates/brain/app/global.css +2 -2
  57. package/corpus/templates/brain/app/lib/brain.ts +2 -2
  58. package/corpus/templates/brain/app/root.tsx +2 -2
  59. package/corpus/templates/brain/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
  60. package/corpus/templates/calendar/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  61. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +6 -2
  62. package/corpus/templates/calendar/app/global.css +1 -1
  63. package/corpus/templates/calendar/app/root.tsx +2 -2
  64. package/corpus/templates/calendar/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
  65. package/corpus/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  66. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +2 -2
  67. package/corpus/templates/chat/app/global.css +1 -1
  68. package/corpus/templates/chat/app/root.tsx +2 -2
  69. package/corpus/templates/chat/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
  70. package/corpus/templates/clips/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  71. package/corpus/templates/clips/actions/export-to-brain.ts +1 -1
  72. package/corpus/templates/clips/actions/invite-member.ts +1 -0
  73. package/corpus/templates/clips/app/components/library/library-layout.tsx +2 -2
  74. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +20 -11
  75. package/corpus/templates/clips/app/global.css +1 -1
  76. package/corpus/templates/clips/app/root.tsx +7 -2
  77. package/corpus/templates/clips/app/routes/record.tsx +2 -88
  78. package/corpus/templates/clips/changelog/2026-07-22-builder-connected-recordings-start-resumable-upload-sessions.md +6 -0
  79. package/corpus/templates/clips/changelog/2026-07-22-clip-thumbnails-now-come-from-the-recording-instead-of-the-p.md +6 -0
  80. package/corpus/templates/clips/changelog/2026-07-22-clips-emails-now-render-the-agent-native-logo-in-gmail-and-o.md +6 -0
  81. package/corpus/templates/clips/changelog/2026-07-22-dismissed-zoom-and-teams-meeting-note-prompts-now-stay-dismi.md +6 -0
  82. package/corpus/templates/clips/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
  83. package/corpus/templates/clips/changelog/2026-07-22-quieter-permissions-and-rewind-controls.md +6 -0
  84. package/corpus/templates/clips/desktop/src/app.tsx +86 -52
  85. package/corpus/templates/clips/desktop/src/lib/meeting-notification-dismissal.ts +31 -0
  86. package/corpus/templates/clips/desktop/src/lib/recorder.ts +4 -167
  87. package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +10 -1
  88. package/corpus/templates/clips/desktop/src/styles.css +98 -36
  89. package/corpus/templates/clips/desktop/src-tauri/src/adhoc_meetings_watcher.rs +67 -4
  90. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +1 -1
  91. package/corpus/templates/clips/desktop/src-tauri/src/meetings_watcher.rs +51 -0
  92. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +5 -1
  93. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +0 -189
  94. package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +62 -0
  95. package/corpus/templates/clips/server/plugins/db.ts +6 -2
  96. package/corpus/templates/content/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  97. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +3 -3
  98. package/corpus/templates/content/app/global.css +1 -1
  99. package/corpus/templates/content/app/root.tsx +2 -2
  100. package/corpus/templates/content/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
  101. package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  102. package/corpus/templates/crm/app/global.css +1 -1
  103. package/corpus/templates/design/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  104. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +19 -1
  105. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +12 -1
  106. package/corpus/templates/design/app/components/design/EditPanel.tsx +177 -52
  107. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +179 -59
  108. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +74 -27
  109. package/corpus/templates/design/app/components/design/multi-screen/types.ts +2 -0
  110. package/corpus/templates/design/app/components/layout/Sidebar.tsx +2 -2
  111. package/corpus/templates/design/app/global.css +1 -1
  112. package/corpus/templates/design/app/hooks/use-navigation-state.ts +6 -5
  113. package/corpus/templates/design/app/root.tsx +2 -2
  114. package/corpus/templates/design/changelog/2026-07-22-design-owners-can-keep-editing-their-designs-after-switching.md +6 -0
  115. package/corpus/templates/design/changelog/2026-07-22-inspector-tabs-keep-their-text-labels-at-narrow-widths.md +6 -0
  116. package/corpus/templates/design/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
  117. package/corpus/templates/design/changelog/2026-07-22-narrow-breakpoint-previews-keep-their-device-labels-readable.md +6 -0
  118. package/corpus/templates/design/changelog/2026-07-22-removed-the-draft-status-chip-from-the-design-editor.md +6 -0
  119. package/corpus/templates/design/changelog/2026-07-22-responsive-breakpoint-controls-stay-within-narrow-sidebars.md +6 -0
  120. package/corpus/templates/design/changelog/2026-07-22-responsive-peers-now-use-a-softer-selection-highlight-withou.md +6 -0
  121. package/corpus/templates/design/changelog/2026-07-22-rotate-from-context-menu.md +6 -0
  122. package/corpus/templates/design/changelog/2026-07-22-the-selection-outline-now-stays-attached-while-moving-screen.md +6 -0
  123. package/corpus/templates/design/changelog/2026-07-22-view-only-designs-now-support-figma-style-layer-inspection-w.md +6 -0
  124. package/corpus/templates/design/server/db/index.ts +4 -0
  125. package/corpus/templates/dispatch/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  126. package/corpus/templates/dispatch/app/dispatch-extensions.tsx +2 -2
  127. package/corpus/templates/dispatch/app/global.css +1 -1
  128. package/corpus/templates/dispatch/app/root.tsx +2 -2
  129. package/corpus/templates/dispatch/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
  130. package/corpus/templates/forms/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  131. package/corpus/templates/forms/app/components/layout/Layout.tsx +1 -1
  132. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +3 -3
  133. package/corpus/templates/forms/app/global.css +1 -1
  134. package/corpus/templates/forms/app/hooks/use-forms.ts +55 -2
  135. package/corpus/templates/forms/app/i18n/en-US.ts +2 -0
  136. package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +3 -3
  137. package/corpus/templates/forms/app/pages/FormsListPage.tsx +23 -6
  138. package/corpus/templates/forms/app/root.tsx +2 -2
  139. package/corpus/templates/forms/changelog/2026-07-22-forms-now-archive-with-optimistic-feedback.md +6 -0
  140. package/corpus/templates/forms/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
  141. package/corpus/templates/forms/server/plugins/agent-chat.ts +1 -1
  142. package/corpus/templates/macros/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  143. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +2 -2
  144. package/corpus/templates/macros/app/root.tsx +2 -2
  145. package/corpus/templates/macros/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
  146. package/corpus/templates/mail/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  147. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +3 -3
  148. package/corpus/templates/mail/app/components/layout/CommandPalette.tsx +2 -2
  149. package/corpus/templates/mail/app/global.css +1 -1
  150. package/corpus/templates/mail/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
  151. package/corpus/templates/plan/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  152. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +2 -2
  153. package/corpus/templates/plan/app/global.css +1 -1
  154. package/corpus/templates/plan/app/root.tsx +2 -2
  155. package/corpus/templates/plan/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
  156. package/corpus/templates/slides/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  157. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +2 -2
  158. package/corpus/templates/slides/app/root.tsx +2 -2
  159. package/corpus/templates/slides/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
  160. package/corpus/templates/slides/changelog/2026-07-22-slides-no-longer-fail-intermittently-on-fresh-server-instances.md +6 -0
  161. package/corpus/templates/slides/changelog/2026-07-22-uploaded-screenshots-can-now-be-analyzed-and-recreated-as-ed.md +6 -0
  162. package/corpus/templates/slides/server/lib/chat-attachments.ts +7 -3
  163. package/corpus/templates/slides/server/plugins/db.ts +44 -17
  164. package/corpus/templates/tasks/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  165. package/corpus/templates/tasks/app/global.css +1 -1
  166. package/dist/cli/context-xray-local.js +1 -1
  167. package/dist/cli/context-xray-local.js.map +1 -1
  168. package/dist/client/extensions/ExtensionViewer.js +2 -2
  169. package/dist/client/extensions/ExtensionViewer.js.map +1 -1
  170. package/dist/client/extensions/ExtensionsSidebarSection.js +11 -11
  171. package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -1
  172. package/dist/client/extensions/agent-native-extension-runtime.js +1 -1
  173. package/dist/client/extensions/agent-native-extension-runtime.js.map +1 -1
  174. package/dist/client/extensions/delete-extension.d.ts +1 -1
  175. package/dist/client/extensions/delete-extension.d.ts.map +1 -1
  176. package/dist/client/extensions/delete-extension.js +1 -1
  177. package/dist/client/extensions/delete-extension.js.map +1 -1
  178. package/dist/client/org/OrgSwitcher.d.ts +3 -1
  179. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  180. package/dist/client/org/OrgSwitcher.js +12 -4
  181. package/dist/client/org/OrgSwitcher.js.map +1 -1
  182. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  183. package/dist/client/settings/SettingsPanel.js +37 -4
  184. package/dist/client/settings/SettingsPanel.js.map +1 -1
  185. package/dist/client/sharing/ShareButton.d.ts +3 -4
  186. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  187. package/dist/client/sharing/ShareButton.js +3 -2
  188. package/dist/client/sharing/ShareButton.js.map +1 -1
  189. package/dist/collab/awareness.d.ts +2 -2
  190. package/dist/collab/awareness.d.ts.map +1 -1
  191. package/dist/db/client.d.ts +7 -7
  192. package/dist/db/client.d.ts.map +1 -1
  193. package/dist/db/client.js +13 -10
  194. package/dist/db/client.js.map +1 -1
  195. package/dist/extensions/actions.d.ts.map +1 -1
  196. package/dist/extensions/actions.js +8 -5
  197. package/dist/extensions/actions.js.map +1 -1
  198. package/dist/extensions/local.d.ts.map +1 -1
  199. package/dist/extensions/local.js +1 -0
  200. package/dist/extensions/local.js.map +1 -1
  201. package/dist/extensions/routes.d.ts.map +1 -1
  202. package/dist/extensions/routes.js +9 -4
  203. package/dist/extensions/routes.js.map +1 -1
  204. package/dist/extensions/schema.d.ts +23 -2
  205. package/dist/extensions/schema.d.ts.map +1 -1
  206. package/dist/extensions/schema.js +5 -0
  207. package/dist/extensions/schema.js.map +1 -1
  208. package/dist/extensions/slots/store.d.ts +1 -1
  209. package/dist/extensions/slots/store.d.ts.map +1 -1
  210. package/dist/extensions/slots/store.js +4 -4
  211. package/dist/extensions/slots/store.js.map +1 -1
  212. package/dist/extensions/store.d.ts +1 -0
  213. package/dist/extensions/store.d.ts.map +1 -1
  214. package/dist/extensions/store.js +38 -21
  215. package/dist/extensions/store.js.map +1 -1
  216. package/dist/extensions/theme.js +1 -1
  217. package/dist/extensions/theme.js.map +1 -1
  218. package/dist/file-upload/registry.d.ts.map +1 -1
  219. package/dist/file-upload/registry.js +9 -0
  220. package/dist/file-upload/registry.js.map +1 -1
  221. package/dist/index.browser.d.ts +1 -0
  222. package/dist/index.browser.d.ts.map +1 -1
  223. package/dist/index.browser.js +1 -0
  224. package/dist/index.browser.js.map +1 -1
  225. package/dist/index.d.ts +1 -0
  226. package/dist/index.d.ts.map +1 -1
  227. package/dist/index.js +1 -0
  228. package/dist/index.js.map +1 -1
  229. package/dist/localization/default-messages.d.ts +16 -0
  230. package/dist/localization/default-messages.d.ts.map +1 -1
  231. package/dist/localization/default-messages.js +22 -6
  232. package/dist/localization/default-messages.js.map +1 -1
  233. package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
  234. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  235. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  236. package/dist/secrets/routes.d.ts +6 -6
  237. package/dist/server/action-discovery.d.ts.map +1 -1
  238. package/dist/server/action-discovery.js +8 -0
  239. package/dist/server/action-discovery.js.map +1 -1
  240. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  241. package/dist/server/better-auth-instance.d.ts +7 -2
  242. package/dist/server/better-auth-instance.d.ts.map +1 -1
  243. package/dist/server/better-auth-instance.js.map +1 -1
  244. package/dist/server/email-template.d.ts +3 -0
  245. package/dist/server/email-template.d.ts.map +1 -1
  246. package/dist/server/email-template.js +13 -0
  247. package/dist/server/email-template.js.map +1 -1
  248. package/dist/server/email-templates.d.ts.map +1 -1
  249. package/dist/server/email-templates.js +3 -0
  250. package/dist/server/email-templates.js.map +1 -1
  251. package/dist/server/email.d.ts.map +1 -1
  252. package/dist/server/email.js +27 -4
  253. package/dist/server/email.js.map +1 -1
  254. package/dist/sharing/actions/share-resource.js +1 -0
  255. package/dist/sharing/actions/share-resource.js.map +1 -1
  256. package/dist/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  257. package/dist/templates/chat/app/components/layout/Sidebar.tsx +2 -2
  258. package/dist/templates/chat/app/global.css +1 -1
  259. package/dist/templates/chat/app/root.tsx +2 -2
  260. package/dist/templates/chat/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
  261. package/dist/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  262. package/dist/templates/default/app/global.css +1 -1
  263. package/dist/templates/default/app/i18n/ar-SA.ts +6 -6
  264. package/dist/templates/default/app/i18n/de-DE.ts +6 -6
  265. package/dist/templates/default/app/i18n/en-US.ts +6 -6
  266. package/dist/templates/default/app/i18n/es-ES.ts +6 -6
  267. package/dist/templates/default/app/i18n/fr-FR.ts +6 -6
  268. package/dist/templates/default/app/i18n/hi-IN.ts +6 -6
  269. package/dist/templates/default/app/i18n/ja-JP.ts +6 -6
  270. package/dist/templates/default/app/i18n/ko-KR.ts +6 -6
  271. package/dist/templates/default/app/i18n/pt-BR.ts +6 -6
  272. package/dist/templates/default/app/i18n/zh-CN.ts +7 -7
  273. package/dist/templates/default/app/i18n/zh-TW.ts +7 -7
  274. package/dist/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  275. package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  276. package/dist/user-profile/actions/get-user-profile.d.ts +4 -0
  277. package/dist/user-profile/actions/get-user-profile.d.ts.map +1 -0
  278. package/dist/user-profile/actions/get-user-profile.js +14 -0
  279. package/dist/user-profile/actions/get-user-profile.js.map +1 -0
  280. package/dist/user-profile/actions/update-user-profile.d.ts +6 -0
  281. package/dist/user-profile/actions/update-user-profile.d.ts.map +1 -0
  282. package/dist/user-profile/actions/update-user-profile.js +20 -0
  283. package/dist/user-profile/actions/update-user-profile.js.map +1 -0
  284. package/dist/user-profile/index.d.ts +2 -0
  285. package/dist/user-profile/index.d.ts.map +1 -0
  286. package/dist/user-profile/index.js +2 -0
  287. package/dist/user-profile/index.js.map +1 -0
  288. package/dist/user-profile/shared.d.ts +7 -0
  289. package/dist/user-profile/shared.d.ts.map +1 -0
  290. package/dist/user-profile/shared.js +6 -0
  291. package/dist/user-profile/shared.js.map +1 -0
  292. package/dist/user-profile/store.d.ts +4 -0
  293. package/dist/user-profile/store.d.ts.map +1 -0
  294. package/dist/user-profile/store.js +38 -0
  295. package/dist/user-profile/store.js.map +1 -0
  296. package/package.json +2 -1
  297. package/src/cli/context-xray-local.ts +1 -1
  298. package/src/client/extensions/ExtensionViewer.tsx +4 -4
  299. package/src/client/extensions/ExtensionsSidebarSection.tsx +11 -11
  300. package/src/client/extensions/agent-native-extension-runtime.ts +1 -1
  301. package/src/client/extensions/delete-extension.ts +2 -2
  302. package/src/client/org/OrgSwitcher.tsx +25 -1
  303. package/src/client/settings/SettingsPanel.tsx +102 -10
  304. package/src/client/sharing/ShareButton.tsx +7 -6
  305. package/src/db/client.ts +13 -10
  306. package/src/extensions/actions.ts +13 -5
  307. package/src/extensions/local.ts +1 -0
  308. package/src/extensions/routes.ts +10 -5
  309. package/src/extensions/schema.ts +5 -0
  310. package/src/extensions/slots/store.ts +14 -4
  311. package/src/extensions/store.ts +55 -19
  312. package/src/extensions/theme.ts +1 -1
  313. package/src/file-upload/registry.ts +9 -0
  314. package/src/index.browser.ts +5 -0
  315. package/src/index.ts +1 -0
  316. package/src/localization/default-messages.ts +23 -6
  317. package/src/server/action-discovery.ts +8 -0
  318. package/src/server/better-auth-instance.ts +7 -2
  319. package/src/server/email-template.ts +18 -0
  320. package/src/server/email-templates.ts +3 -0
  321. package/src/server/email.ts +40 -4
  322. package/src/sharing/actions/share-resource.ts +1 -0
  323. package/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  324. package/src/templates/chat/app/components/layout/Sidebar.tsx +2 -2
  325. package/src/templates/chat/app/global.css +1 -1
  326. package/src/templates/chat/app/root.tsx +2 -2
  327. package/src/templates/chat/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +6 -0
  328. package/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  329. package/src/templates/default/app/global.css +1 -1
  330. package/src/templates/default/app/i18n/ar-SA.ts +6 -6
  331. package/src/templates/default/app/i18n/de-DE.ts +6 -6
  332. package/src/templates/default/app/i18n/en-US.ts +6 -6
  333. package/src/templates/default/app/i18n/es-ES.ts +6 -6
  334. package/src/templates/default/app/i18n/fr-FR.ts +6 -6
  335. package/src/templates/default/app/i18n/hi-IN.ts +6 -6
  336. package/src/templates/default/app/i18n/ja-JP.ts +6 -6
  337. package/src/templates/default/app/i18n/ko-KR.ts +6 -6
  338. package/src/templates/default/app/i18n/pt-BR.ts +6 -6
  339. package/src/templates/default/app/i18n/zh-CN.ts +7 -7
  340. package/src/templates/default/app/i18n/zh-TW.ts +7 -7
  341. package/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  342. package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +6 -0
  343. package/src/user-profile/actions/get-user-profile.ts +16 -0
  344. package/src/user-profile/actions/update-user-profile.ts +24 -0
  345. package/src/user-profile/index.ts +5 -0
  346. package/src/user-profile/shared.ts +14 -0
  347. package/src/user-profile/store.ts +50 -0
@@ -37,6 +37,7 @@ import React, {
37
37
  } from "react";
38
38
 
39
39
  import { PROVIDER_ENV_PLACEHOLDERS } from "../../agent/engine/provider-env-vars.js";
40
+ import type { UserProfile } from "../../user-profile/shared.js";
40
41
  import { saveAgentEngineProviderSettings } from "../agent-engine-key.js";
41
42
  import { agentNativePath } from "../api-path.js";
42
43
  import { BuilderBMark } from "../builder-mark.js";
@@ -45,9 +46,14 @@ import {
45
46
  TooltipContent,
46
47
  TooltipTrigger,
47
48
  } from "../components/ui/tooltip.js";
49
+ import { useT } from "../i18n.js";
48
50
  import { TeamPage } from "../org/TeamPage.js";
49
51
  import { BuilderConnectCard } from "../setup-connections/BuilderConnectCard.js";
50
- import { callAction } from "../use-action.js";
52
+ import {
53
+ useActionMutation,
54
+ useActionQuery,
55
+ callAction,
56
+ } from "../use-action.js";
51
57
  import { uploadAvatar, useAvatarUrl } from "../use-avatar.js";
52
58
  import { useDevMode } from "../use-dev-mode.js";
53
59
  import { useSession } from "../use-session.js";
@@ -2458,21 +2464,40 @@ function AccountSectionInner({
2458
2464
  onToggle: () => void;
2459
2465
  }) {
2460
2466
  const isPage = useSettingsSurface() === "page";
2467
+ const t = useT();
2461
2468
  const { session, isLoading } = useSession();
2462
2469
  const email = session?.email;
2470
+ const profileQuery = useActionQuery<UserProfile>(
2471
+ "get-user-profile",
2472
+ undefined,
2473
+ { enabled: !!email },
2474
+ );
2475
+ const updateProfile = useActionMutation<UserProfile, { name: string }>(
2476
+ "update-user-profile",
2477
+ );
2463
2478
  const avatarUrl = useAvatarUrl(email);
2464
2479
  const fileInputRef = useRef<HTMLInputElement>(null);
2465
2480
  const [uploading, setUploading] = useState(false);
2466
2481
  const [status, setStatus] = useState<"idle" | "saved" | "error">("idle");
2467
-
2468
- const displayName = session?.name || email || "Signed out";
2469
- const initials = (session?.name || email || "?")
2482
+ const [name, setName] = useState("");
2483
+
2484
+ const displayName =
2485
+ profileQuery.data?.name ||
2486
+ session?.name ||
2487
+ email ||
2488
+ t("settings.profileSignedOut");
2489
+ const initials = (displayName || "?")
2470
2490
  .split(/[ @._-]+/)
2471
2491
  .filter(Boolean)
2472
2492
  .slice(0, 2)
2473
2493
  .map((part) => part[0]?.toUpperCase())
2474
2494
  .join("");
2475
2495
 
2496
+ useEffect(() => {
2497
+ const nextName = profileQuery.data?.name || session?.name;
2498
+ if (nextName) setName(nextName);
2499
+ }, [profileQuery.data?.name, session?.name]);
2500
+
2476
2501
  const handleAvatarChange = async (
2477
2502
  event: React.ChangeEvent<HTMLInputElement>,
2478
2503
  ) => {
@@ -2491,12 +2516,18 @@ function AccountSectionInner({
2491
2516
  }
2492
2517
  };
2493
2518
 
2519
+ const handleProfileSave = () => {
2520
+ const nextName = name.trim();
2521
+ if (!nextName || !email) return;
2522
+ updateProfile.mutate({ name: nextName });
2523
+ };
2524
+
2494
2525
  return (
2495
2526
  <SettingsSection
2496
2527
  id={settingsSectionDomId("account")}
2497
2528
  icon={<IconUserCircle size={14} />}
2498
- title="Account"
2499
- subtitle="Your profile photo and signed-in identity."
2529
+ title={t("settings.profileTitle")}
2530
+ subtitle={t("settings.profileDescription")}
2500
2531
  open={open}
2501
2532
  onToggle={onToggle}
2502
2533
  >
@@ -2524,7 +2555,7 @@ function AccountSectionInner({
2524
2555
  isPage ? "text-sm" : "text-[12px]",
2525
2556
  )}
2526
2557
  >
2527
- {isLoading ? "Loading..." : displayName}
2558
+ {isLoading ? t("settings.profileLoading") : displayName}
2528
2559
  </p>
2529
2560
  {email && (
2530
2561
  <p
@@ -2543,12 +2574,12 @@ function AccountSectionInner({
2543
2574
  subTextClass(isPage),
2544
2575
  )}
2545
2576
  >
2546
- Photo updated
2577
+ {t("settings.profilePhotoUpdated")}
2547
2578
  </p>
2548
2579
  )}
2549
2580
  {status === "error" && (
2550
2581
  <p className={cn("mt-1 text-destructive", subTextClass(isPage))}>
2551
- Could not update photo
2582
+ {t("settings.profilePhotoError")}
2552
2583
  </p>
2553
2584
  )}
2554
2585
  </div>
@@ -2568,9 +2599,70 @@ function AccountSectionInner({
2568
2599
  "shrink-0 justify-center",
2569
2600
  )}
2570
2601
  >
2571
- {uploading ? "Uploading..." : "Change photo"}
2602
+ {uploading
2603
+ ? t("settings.profileUploading")
2604
+ : t("settings.profileChangePhoto")}
2572
2605
  </button>
2573
2606
  </div>
2607
+ <form
2608
+ className="space-y-1.5"
2609
+ onSubmit={(event) => {
2610
+ event.preventDefault();
2611
+ handleProfileSave();
2612
+ }}
2613
+ >
2614
+ <label
2615
+ htmlFor="agent-native-profile-name"
2616
+ className={fieldLabelClass(isPage)}
2617
+ >
2618
+ {t("settings.profileNameLabel")}
2619
+ </label>
2620
+ <input
2621
+ id="agent-native-profile-name"
2622
+ type="text"
2623
+ value={name}
2624
+ onChange={(event) => {
2625
+ updateProfile.reset();
2626
+ setName(event.target.value);
2627
+ }}
2628
+ placeholder={t("settings.profileNamePlaceholder")}
2629
+ disabled={!email || profileQuery.isLoading || updateProfile.isPending}
2630
+ maxLength={120}
2631
+ className={textInputClass(isPage)}
2632
+ />
2633
+ <p className={cn("text-muted-foreground", noteTextClass(isPage))}>
2634
+ {t("settings.profileNameDescription")}
2635
+ </p>
2636
+ <div className="flex items-center justify-between gap-3 pt-1">
2637
+ <div className="min-h-4">
2638
+ {updateProfile.isSuccess && (
2639
+ <p className="text-green-600 dark:text-green-400">
2640
+ {t("settings.profileSaved")}
2641
+ </p>
2642
+ )}
2643
+ {updateProfile.error && (
2644
+ <p className="text-destructive">
2645
+ {t("settings.profileSaveError")}
2646
+ </p>
2647
+ )}
2648
+ </div>
2649
+ <button
2650
+ type="submit"
2651
+ disabled={
2652
+ !email ||
2653
+ profileQuery.isLoading ||
2654
+ updateProfile.isPending ||
2655
+ !name.trim() ||
2656
+ name.trim() === displayName
2657
+ }
2658
+ className={pillButtonClass(isPage, "solid")}
2659
+ >
2660
+ {updateProfile.isPending
2661
+ ? t("settings.profileSaving")
2662
+ : t("settings.profileSave")}
2663
+ </button>
2664
+ </div>
2665
+ </form>
2574
2666
  </SettingsSection>
2575
2667
  );
2576
2668
  }
@@ -46,11 +46,10 @@ export interface ShareButtonProps {
46
46
  /** @deprecated No longer affects rendering — trigger always says
47
47
  * "Share". Kept for callsite compatibility. */
48
48
  variant?: "compact" | "label";
49
- /** Optional trigger style. Defaults to the Google-Docs-style "Share" label
50
- * with a leading visibility icon. "label-icon" is kept for callsites that
51
- * want to explicitly opt into icon+label parity; "icon" is icon-only. */
49
+ /** Optional trigger style. Defaults to a text-only "Share" label.
50
+ * "label-icon" opts into an icon plus label; "icon" is icon-only. */
52
51
  trigger?: "label" | "icon" | "label-icon";
53
- /** @deprecated No longer affects rendering — triggers show visibility state. */
52
+ /** @deprecated No longer affects rendering — kept for callsite compatibility. */
54
53
  hideTriggerIcon?: boolean;
55
54
  /** Optional className applied to the trigger button. */
56
55
  triggerClassName?: string;
@@ -371,8 +370,10 @@ export function ShareButton(props: ShareButtonProps) {
371
370
  ? visibilityMeta(triggerVisibility, props.visibilityCopy)
372
371
  : null;
373
372
  const TriggerIcon = triggerMeta?.Icon ?? IconShare3;
374
- const triggerLabel = triggerMeta ? `Share (${triggerMeta.label})` : "Share";
375
373
  const iconOnly = props.trigger === "icon";
374
+ const showTriggerIcon = iconOnly || props.trigger === "label-icon";
375
+ const triggerLabel =
376
+ iconOnly && triggerMeta ? `Share (${triggerMeta.label})` : "Share";
376
377
 
377
378
  return (
378
379
  <Popover open={open} onOpenChange={handleOpenChange}>
@@ -386,7 +387,7 @@ export function ShareButton(props: ShareButtonProps) {
386
387
  aria-label={triggerLabel}
387
388
  title={triggerLabel}
388
389
  >
389
- <TriggerIcon size={16} strokeWidth={1.75} />
390
+ {showTriggerIcon && <TriggerIcon size={16} strokeWidth={1.75} />}
390
391
  {!iconOnly && <span>Share</span>}
391
392
  </button>
392
393
  </PopoverTrigger>
package/src/db/client.ts CHANGED
@@ -649,6 +649,8 @@ const CONNECTION_ERROR_CODES = new Set([
649
649
  "ETIMEDOUT",
650
650
  "EPIPE",
651
651
  "ENOTFOUND",
652
+ "EMAXCONN",
653
+ "53300",
652
654
  "CONNECT_TIMEOUT",
653
655
  "CONNECTION_ENDED",
654
656
  "CONNECTION_DESTROYED",
@@ -672,7 +674,7 @@ export function isConnectionError(err: any): boolean {
672
674
  return true;
673
675
  }
674
676
  const msg = String(err.message || err.cause?.message || "");
675
- return /ECONNRESET|ETIMEDOUT|EPIPE|connection.*(closed|ended|terminated)|socket hang up|websocket/i.test(
677
+ return /ECONNRESET|ETIMEDOUT|EPIPE|EMAXCONN|too many connections|max client connections|remaining connection slots|connection.*(closed|ended|terminated)|socket hang up|websocket/i.test(
676
678
  msg,
677
679
  );
678
680
  }
@@ -699,6 +701,7 @@ export function isTransientDatabaseError(err: unknown): boolean {
699
701
  const code = String(error?.code ?? error?.cause?.code ?? "");
700
702
  if (
701
703
  code === "ECHECKOUTTIMEOUT" ||
704
+ code === "EMAXCONN" ||
702
705
  code === "53300" ||
703
706
  code === "57014" ||
704
707
  /^08/.test(code) ||
@@ -880,10 +883,10 @@ export function isServerlessRuntime(): boolean {
880
883
 
881
884
  /**
882
885
  * postgres.js pool options tuned per runtime. A serverless instance handles
883
- * one request at a time, so a tiny pool is enough but we cap at 2 (not 1)
884
- * so a single slow query or open transaction can't serialize every other
885
- * query in the same request. Total connections stay bounded to
886
- * concurrent-instance count instead of 10×. idle_timeout is shortened on
886
+ * one request at a time, so a single connection is enough. Keeping the
887
+ * foreground pool at 1 is important because each instance can also open an
888
+ * app and Better Auth pool; a max of 2 on each pool still exhausted Neon
889
+ * under a burst of warm instances. idle_timeout is shortened on
887
890
  * serverless so a thawed-but-idle instance releases its connections quickly.
888
891
  * Long-lived Node servers keep the normal pool for throughput.
889
892
  */
@@ -891,7 +894,7 @@ export function pgPoolOptions(url: string): Record<string, unknown> {
891
894
  const serverless = isServerlessRuntime();
892
895
  return {
893
896
  onnotice: () => {},
894
- max: serverless ? 2 : 10,
897
+ max: serverless ? 1 : 10,
895
898
  idle_timeout: serverless ? 20 : 240,
896
899
  max_lifetime: 60 * 30,
897
900
  connect_timeout: 10,
@@ -903,9 +906,9 @@ export function pgPoolOptions(url: string): Record<string, unknown> {
903
906
 
904
907
  /**
905
908
  * Connection cap for the @neondatabase/serverless `Pool`. Same instance
906
- * accumulation risk as postgres.js — a small pool (2) is enough on serverless
907
- * and keeps total connections bounded while still letting a second query
908
- * proceed when one connection is busy.
909
+ * accumulation risk as postgres.js — one connection is enough for a
910
+ * foreground serverless invocation and keeps the aggregate app/framework/auth
911
+ * budget bounded.
909
912
  */
910
913
  export function neonPoolMax(): number {
911
914
  if (!isServerlessRuntime()) return 10;
@@ -921,7 +924,7 @@ export function neonPoolMax(): number {
921
924
  // burst; keep the foreground serverless pool tiny to avoid "Max client
922
925
  // connections reached" across many warm instances.
923
926
  if (isBackgroundFunctionPoolContext()) return 8;
924
- return 2;
927
+ return 1;
925
928
  }
926
929
 
927
930
  /**
@@ -839,7 +839,11 @@ export function createExtensionActionEntries(): Record<string, ActionEntry> {
839
839
 
840
840
  await ensureExtensionsTables();
841
841
  const access = await resolveAccess("extension", extensionId);
842
- if (!access || access.role === "viewer")
842
+ if (
843
+ !access ||
844
+ (access.resource as ExtensionRow | undefined)?.archivedAt ||
845
+ access.role === "viewer"
846
+ )
843
847
  return `Error: editor access required for extension ${extensionId}.`;
844
848
 
845
849
  const userEmail = getRequestUserEmail()?.toLowerCase() ?? "";
@@ -942,7 +946,11 @@ export function createExtensionActionEntries(): Record<string, ActionEntry> {
942
946
 
943
947
  await ensureExtensionsTables();
944
948
  const access = await resolveAccess("extension", extensionId);
945
- if (!access) return `Error: no access to extension ${extensionId}.`;
949
+ if (
950
+ !access ||
951
+ (access.resource as ExtensionRow | undefined)?.archivedAt
952
+ )
953
+ return `Error: no access to extension ${extensionId}.`;
946
954
 
947
955
  const userEmail = getRequestUserEmail()?.toLowerCase() ?? "";
948
956
  const scope = args?.scope ?? "user";
@@ -1012,14 +1020,14 @@ export function createExtensionActionEntries(): Record<string, ActionEntry> {
1012
1020
  "delete-extension": {
1013
1021
  tool: {
1014
1022
  description:
1015
- "Permanently delete an extension everywhere it is shared. Requires owner/admin access. If the user only wants a shared extension removed from their own sidebar/list, use hide-extension instead.",
1023
+ "Archive an extension everywhere it is shared. Archiving is a soft delete: the extension and its history, data, sharing, and slot configuration are retained, but it disappears from normal lists. Requires owner/admin access. If the user only wants a shared extension removed from their own sidebar/list, use hide-extension instead.",
1016
1024
  parameters: {
1017
1025
  type: "object",
1018
1026
  properties: {
1019
1027
  id: {
1020
1028
  type: "string",
1021
1029
  description:
1022
- "Extension id to permanently delete. Use list-extensions first if you only know the display name.",
1030
+ "Extension id to archive. Use list-extensions first if you only know the display name.",
1023
1031
  },
1024
1032
  },
1025
1033
  required: ["id"],
@@ -1036,7 +1044,7 @@ export function createExtensionActionEntries(): Record<string, ActionEntry> {
1036
1044
  try {
1037
1045
  const ok = await deleteExtension(id);
1038
1046
  if (!ok) return `Error: extension not found: ${id}`;
1039
- return { ok: true, deleted: summarizeDeletedExtension(extension) };
1047
+ return { ok: true, archived: summarizeDeletedExtension(extension) };
1040
1048
  } catch (err: any) {
1041
1049
  return {
1042
1050
  ok: false,
@@ -322,6 +322,7 @@ async function readLocalExtensionFolder(options: {
322
322
  icon: typeof manifest.icon === "string" ? manifest.icon : null,
323
323
  createdAt,
324
324
  updatedAt,
325
+ archivedAt: null,
325
326
  hiddenAt: null,
326
327
  hiddenBy: null,
327
328
  ownerEmail: "local-files",
@@ -227,8 +227,8 @@ async function dispatch(
227
227
  }
228
228
 
229
229
  const access = await resolveAccess("extension", parts[0]);
230
- const extension = access?.resource;
231
- if (!extension) {
230
+ const extension = access?.resource as ExtensionRow | undefined;
231
+ if (!access || !extension || extension.archivedAt) {
232
232
  setResponseStatus(event, 404);
233
233
  return { error: "Extension not found" };
234
234
  }
@@ -317,11 +317,12 @@ async function dispatch(
317
317
  }
318
318
 
319
319
  const access = await resolveAccess("extension", parts[0]);
320
- if (!access) {
320
+ const extension = access?.resource as ExtensionRow | undefined;
321
+ if (!access || !extension || extension.archivedAt) {
321
322
  setResponseStatus(event, 404);
322
323
  return { error: "Extension not found" };
323
324
  }
324
- return extensionResponse(access.resource as ExtensionRow, access.role);
325
+ return extensionResponse(extension, access.role);
325
326
  }
326
327
 
327
328
  // POST /:id/hide — remove from the current user's Extensions list/sidebar
@@ -433,7 +434,7 @@ async function dispatch(
433
434
  setResponseStatus(event, 404);
434
435
  return { error: "Extension not found" };
435
436
  }
436
- return { ok: true };
437
+ return { ok: true, archived: true };
437
438
  }
438
439
 
439
440
  setResponseStatus(event, 404);
@@ -662,6 +663,10 @@ async function requireExtensionDataAccess(
662
663
  minRole: ShareRole,
663
664
  ): Promise<{ ok: true } | { ok: false; response: unknown }> {
664
665
  const access = await resolveAccess("extension", extensionId);
666
+ if (access?.resource && (access.resource as ExtensionRow).archivedAt) {
667
+ setResponseStatus(event, 404);
668
+ return { ok: false, response: { error: "Extension not found" } };
669
+ }
665
670
  if (access) {
666
671
  if (ROLE_RANK[access.role] < ROLE_RANK[minRole]) {
667
672
  setResponseStatus(event, 403);
@@ -27,6 +27,7 @@ export const extensions = table("tools", {
27
27
  icon: text("icon"),
28
28
  createdAt: text("created_at").notNull().default(now()),
29
29
  updatedAt: text("updated_at").notNull().default(now()),
30
+ archivedAt: text("archived_at"),
30
31
  hiddenAt: text("hidden_at"),
31
32
  hiddenBy: text("hidden_by"),
32
33
  ...ownableColumns(),
@@ -66,6 +67,7 @@ export const EXTENSIONS_CREATE_SQL = `CREATE TABLE IF NOT EXISTS tools (
66
67
  icon TEXT,
67
68
  created_at TEXT NOT NULL DEFAULT (datetime('now')),
68
69
  updated_at TEXT NOT NULL DEFAULT (datetime('now')),
70
+ archived_at TEXT,
69
71
  hidden_at TEXT,
70
72
  hidden_by TEXT,
71
73
  owner_email TEXT NOT NULL DEFAULT 'local@localhost',
@@ -81,6 +83,7 @@ export const EXTENSIONS_CREATE_SQL_PG = `CREATE TABLE IF NOT EXISTS tools (
81
83
  icon TEXT,
82
84
  created_at TEXT NOT NULL DEFAULT now(),
83
85
  updated_at TEXT NOT NULL DEFAULT now(),
86
+ archived_at TEXT,
84
87
  hidden_at TEXT,
85
88
  hidden_by TEXT,
86
89
  owner_email TEXT NOT NULL DEFAULT 'local@localhost',
@@ -162,6 +165,8 @@ export const EXTENSION_DATA_DROP_OLD_INDEX_SQL_PG = `DROP INDEX IF EXISTS tool_d
162
165
  export const EXTENSIONS_OWNER_INDEX_SQL = `CREATE INDEX IF NOT EXISTS tools_owner_idx ON tools (owner_email)`;
163
166
  export const EXTENSIONS_ORG_INDEX_SQL = `CREATE INDEX IF NOT EXISTS tools_org_idx ON tools (org_id)`;
164
167
  export const EXTENSIONS_UPDATED_INDEX_SQL = `CREATE INDEX IF NOT EXISTS tools_updated_at_idx ON tools (updated_at)`;
168
+ export const EXTENSIONS_ARCHIVED_AT_COLUMN_SQL = `ALTER TABLE tools ADD COLUMN IF NOT EXISTS archived_at TEXT`;
169
+ export const EXTENSIONS_ARCHIVED_AT_INDEX_SQL = `CREATE INDEX IF NOT EXISTS tools_archived_at_idx ON tools (archived_at)`;
165
170
 
166
171
  // Global (admin) hide: when set, the extension row is hidden from EVERYONE's
167
172
  // list, distinct from the per-user `tool_hidden_extensions` table. Additive
@@ -1,6 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
 
3
- import { and, eq, inArray, sql } from "drizzle-orm";
3
+ import { and, eq, inArray, isNull, sql } from "drizzle-orm";
4
4
 
5
5
  import { getDbExec, isPostgres } from "../../db/client.js";
6
6
  import { createGetDb } from "../../db/create-get-db.js";
@@ -203,7 +203,12 @@ export async function listExtensionsForSlot(slotId: string): Promise<
203
203
  icon: extensions.icon,
204
204
  })
205
205
  .from(extensions)
206
- .where(accessFilter(extensions, extensionShares));
206
+ .where(
207
+ and(
208
+ accessFilter(extensions, extensionShares),
209
+ isNull(extensions.archivedAt),
210
+ ),
211
+ );
207
212
  const localRows = (await listLocalExtensions())
208
213
  .filter((extension) => extension.source.slots.includes(slotId))
209
214
  .map((extension) => ({
@@ -397,7 +402,12 @@ export async function listSlotInstallsForUser(slotId: string): Promise<
397
402
  updatedAt: extensions.updatedAt,
398
403
  })
399
404
  .from(extensions)
400
- .where(accessFilter(extensions, extensionShares));
405
+ .where(
406
+ and(
407
+ accessFilter(extensions, extensionShares),
408
+ isNull(extensions.archivedAt),
409
+ ),
410
+ );
401
411
  const byId = new Map(accessible.map((t: any) => [t.id, t]));
402
412
 
403
413
  const sqlInstalls = (installs as ExtensionSlotInstallRow[])
@@ -419,7 +429,7 @@ export async function listSlotInstallsForUser(slotId: string): Promise<
419
429
  return [...localInstalls, ...sqlInstalls];
420
430
  }
421
431
 
422
- /** Delete every slot/install row referencing a extension. Called from deleteExtension. */
432
+ /** Delete every slot/install row referencing an extension for maintenance. */
423
433
  export async function cascadeDeleteExtensionSlots(
424
434
  extensionId: string,
425
435
  ): Promise<void> {
@@ -52,6 +52,8 @@ import {
52
52
  EXTENSIONS_OWNER_INDEX_SQL,
53
53
  EXTENSIONS_ORG_INDEX_SQL,
54
54
  EXTENSIONS_UPDATED_INDEX_SQL,
55
+ EXTENSIONS_ARCHIVED_AT_COLUMN_SQL,
56
+ EXTENSIONS_ARCHIVED_AT_INDEX_SQL,
55
57
  EXTENSIONS_HIDDEN_AT_COLUMN_SQL,
56
58
  EXTENSIONS_HIDDEN_BY_COLUMN_SQL,
57
59
  EXTENSIONS_HIDDEN_AT_INDEX_SQL,
@@ -103,6 +105,11 @@ export async function ensureExtensionsTables(): Promise<void> {
103
105
  "tools_updated_at_idx",
104
106
  EXTENSIONS_UPDATED_INDEX_SQL,
105
107
  );
108
+ await ensureExtensionsArchivedColumn(client, pg);
109
+ await ensureIndexExists(
110
+ "tools_archived_at_idx",
111
+ EXTENSIONS_ARCHIVED_AT_INDEX_SQL,
112
+ );
106
113
  await ensureExtensionsGlobalHideColumns(client, pg); // ADD COLUMN — guarded inside
107
114
  await ensureIndexExists(
108
115
  "tools_hidden_at_idx",
@@ -176,6 +183,10 @@ export async function ensureExtensionsTables(): Promise<void> {
176
183
  await retryOnDdlRace(() => client.execute(EXTENSIONS_OWNER_INDEX_SQL));
177
184
  await retryOnDdlRace(() => client.execute(EXTENSIONS_ORG_INDEX_SQL));
178
185
  await retryOnDdlRace(() => client.execute(EXTENSIONS_UPDATED_INDEX_SQL));
186
+ await ensureExtensionsArchivedColumn(client, pg);
187
+ await retryOnDdlRace(() =>
188
+ client.execute(EXTENSIONS_ARCHIVED_AT_INDEX_SQL),
189
+ );
179
190
  await ensureExtensionsGlobalHideColumns(client, pg);
180
191
  await retryOnDdlRace(() =>
181
192
  client.execute(EXTENSIONS_HIDDEN_AT_INDEX_SQL),
@@ -346,6 +357,31 @@ async function ensureExtensionsGlobalHideColumns(
346
357
  await addCol(EXTENSIONS_HIDDEN_BY_COLUMN_SQL, "hidden_by", "TEXT");
347
358
  }
348
359
 
360
+ async function ensureExtensionsArchivedColumn(
361
+ client: ReturnType<typeof getDbExec>,
362
+ pg: boolean,
363
+ ): Promise<void> {
364
+ if (pg) {
365
+ await ensureColumnExists(
366
+ "tools",
367
+ "archived_at",
368
+ EXTENSIONS_ARCHIVED_AT_COLUMN_SQL,
369
+ );
370
+ return;
371
+ }
372
+ await client
373
+ .execute("ALTER TABLE tools ADD COLUMN archived_at TEXT")
374
+ .catch((err: any) => {
375
+ if (
376
+ !String(err?.message ?? err)
377
+ .toLowerCase()
378
+ .includes("duplicate")
379
+ ) {
380
+ throw err;
381
+ }
382
+ });
383
+ }
384
+
349
385
  export function registerExtensionsShareable() {
350
386
  registerShareableResource({
351
387
  type: "extension",
@@ -373,6 +409,7 @@ export interface ExtensionRow {
373
409
  icon: string | null;
374
410
  createdAt: string;
375
411
  updatedAt: string;
412
+ archivedAt: string | null;
376
413
  hiddenAt: string | null;
377
414
  hiddenBy: string | null;
378
415
  ownerEmail: string;
@@ -881,9 +918,10 @@ export async function listExtensions(
881
918
  // on repeated `.where()` calls, so combine the access filter and the
882
919
  // global-hidden filter into one condition.
883
920
  const base = accessFilter(extensions, extensionShares);
921
+ const visible = and(base, isNull(extensions.archivedAt));
884
922
  const where = options.includeGloballyHidden
885
- ? base
886
- : and(base, isNull(extensions.hiddenAt));
923
+ ? visible
924
+ : and(visible, isNull(extensions.hiddenAt));
887
925
  const rows = (await db
888
926
  .select()
889
927
  .from(extensions)
@@ -899,7 +937,8 @@ export async function listExtensions(
899
937
  export async function getExtension(id: string): Promise<ExtensionRow | null> {
900
938
  await ensureExtensionsTables();
901
939
  const access = await resolveAccess("extension", id);
902
- return (access?.resource as ExtensionRow | undefined) ?? null;
940
+ const resource = access?.resource as ExtensionRow | undefined;
941
+ return resource?.archivedAt ? null : (resource ?? null);
903
942
  }
904
943
 
905
944
  export interface ListExtensionHistoryOptions {
@@ -983,7 +1022,7 @@ export async function restoreExtensionHistoryVersion(
983
1022
  .from(extensions)
984
1023
  .where(eq(extensions.id, id));
985
1024
  const existing = existingRows[0] as ExtensionRow | undefined;
986
- if (!existing) return null;
1025
+ if (!existing || existing.archivedAt) return null;
987
1026
 
988
1027
  await ensureExtensionHistoryBaseline(existing);
989
1028
  const target = await getPersistedHistoryEntry(id, version, true);
@@ -1045,6 +1084,7 @@ export async function createExtension(
1045
1084
  icon: data.icon ?? null,
1046
1085
  createdAt: now,
1047
1086
  updatedAt: now,
1087
+ archivedAt: null,
1048
1088
  hiddenAt: null,
1049
1089
  hiddenBy: null,
1050
1090
  ownerEmail: userEmail,
@@ -1098,6 +1138,7 @@ export async function findRecentDuplicateExtension(data: {
1098
1138
  eq(extensions.description, description),
1099
1139
  icon === null ? isNull(extensions.icon) : eq(extensions.icon, icon),
1100
1140
  gte(extensions.createdAt, fiveMinutesAgo),
1141
+ isNull(extensions.archivedAt),
1101
1142
  isNull(extensions.hiddenAt),
1102
1143
  ),
1103
1144
  )
@@ -1146,7 +1187,7 @@ export async function updateExtension(
1146
1187
  .from(extensions)
1147
1188
  .where(eq(extensions.id, id));
1148
1189
  const existing = existingRows[0] as ExtensionRow | undefined;
1149
- if (!existing) return null;
1190
+ if (!existing || existing.archivedAt) return null;
1150
1191
  await ensureExtensionHistoryBaseline(existing);
1151
1192
  await db.update(extensions).set(updates).where(eq(extensions.id, id));
1152
1193
  const rows = await db.select().from(extensions).where(eq(extensions.id, id));
@@ -1200,7 +1241,9 @@ export async function updateExtensionContent(
1200
1241
  .select()
1201
1242
  .from(extensions)
1202
1243
  .where(eq(extensions.id, id));
1203
- if (!existingRows[0]) return null;
1244
+ if (!existingRows[0] || (existingRows[0] as ExtensionRow).archivedAt) {
1245
+ return null;
1246
+ }
1204
1247
  const existing = existingRows[0] as ExtensionRow;
1205
1248
  const existingContent = existing.content;
1206
1249
  await ensureExtensionHistoryBaseline(existing);
@@ -1234,20 +1277,13 @@ export async function deleteExtension(id: string): Promise<boolean> {
1234
1277
  const rows = await db.select().from(extensions).where(eq(extensions.id, id));
1235
1278
  const row = rows[0] as ExtensionRow | undefined;
1236
1279
  if (!row) return false;
1280
+ if (row.archivedAt) return true;
1237
1281
  const targets = await extensionChangeTargetsForRow(row);
1238
- await db.delete(extensionShares).where(eq(extensionShares.resourceId, id));
1239
- await db.delete(extensionHides).where(eq(extensionHides.extensionId, id));
1240
- await getDbExec().execute({
1241
- sql: `DELETE FROM tool_data WHERE tool_id = ?`,
1242
- args: [id],
1243
- });
1244
- await getDbExec().execute({
1245
- sql: `DELETE FROM tool_history WHERE tool_id = ?`,
1246
- args: [id],
1247
- });
1248
- const { cascadeDeleteExtensionSlots } = await import("./slots/store.js");
1249
- await cascadeDeleteExtensionSlots(id);
1250
- await db.delete(extensions).where(eq(extensions.id, id));
1282
+ const archivedAt = new Date().toISOString();
1283
+ await db
1284
+ .update(extensions)
1285
+ .set({ archivedAt, updatedAt: archivedAt })
1286
+ .where(eq(extensions.id, id));
1251
1287
  await notifyExtensionChanged(targets);
1252
1288
  return true;
1253
1289
  }
@@ -92,7 +92,7 @@ export function getThemeVars(_isDark?: boolean): string {
92
92
  --muted-foreground: 240 5% 64.9%;
93
93
  --accent: 240 3.7% 15.9%;
94
94
  --accent-foreground: 0 0% 98%;
95
- --destructive: 0 62.8% 30.6%;
95
+ --destructive: 0 84% 60%;
96
96
  --destructive-foreground: 0 0% 98%;
97
97
  --border: 240 3.7% 15.9%;
98
98
  --input: 240 3.7% 15.9%;
@@ -69,6 +69,15 @@ export async function getActiveFileUploadProviderForRequest(): Promise<FileUploa
69
69
  if (builderFileUploadProvider.isConfigured()) {
70
70
  return builderFileUploadProvider;
71
71
  }
72
+ try {
73
+ const { resolveHasBuilderPrivateKey } =
74
+ await import("../server/credential-provider.js");
75
+ if (await resolveHasBuilderPrivateKey()) {
76
+ return builderFileUploadProvider;
77
+ }
78
+ } catch {
79
+ // Treat failed scoped credential lookups as unavailable.
80
+ }
72
81
  return null;
73
82
  }
74
83