@agent-native/core 0.91.2 → 0.92.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (538) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +31 -0
  3. package/corpus/core/docs/content/faq.mdx +17 -0
  4. package/corpus/core/docs/content/notifications.mdx +12 -6
  5. package/corpus/core/package.json +1 -1
  6. package/corpus/core/src/agent/production-agent.ts +140 -46
  7. package/corpus/core/src/agent/tool-search.ts +71 -4
  8. package/corpus/core/src/application-state/store.ts +12 -1
  9. package/corpus/core/src/cli/index.ts +20 -0
  10. package/corpus/core/src/cli/upgrade.ts +798 -0
  11. package/corpus/core/src/client/AssistantChat.tsx +28 -15
  12. package/corpus/core/src/client/HighlightedCodeBlock.tsx +20 -3
  13. package/corpus/core/src/client/PoweredByBadge.tsx +1 -8
  14. package/corpus/core/src/client/agent-chat-adapter.ts +68 -16
  15. package/corpus/core/src/client/chat/message-components.tsx +85 -8
  16. package/corpus/core/src/client/chat/tool-call-display.tsx +355 -144
  17. package/corpus/core/src/client/chat/widgets/DataTableWidget.tsx +52 -1
  18. package/corpus/core/src/client/code-agent-chat-adapter.ts +2 -9
  19. package/corpus/core/src/client/db-admin/TableEditor.tsx +49 -0
  20. package/corpus/core/src/client/db-admin/export-utils.ts +13 -4
  21. package/corpus/core/src/client/db-admin/useAgentSync.ts +72 -0
  22. package/corpus/core/src/client/error-capture.ts +69 -7
  23. package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +5 -1
  24. package/corpus/core/src/client/extensions/ExtensionViewer.tsx +53 -8
  25. package/corpus/core/src/client/extensions/InlineExtensionFrame.tsx +5 -1
  26. package/corpus/core/src/client/extensions/portable-extension.ts +1 -0
  27. package/corpus/core/src/client/org/OrgSwitcher.tsx +12 -9
  28. package/corpus/core/src/client/org/TeamPage.tsx +77 -71
  29. package/corpus/core/src/client/session-replay.ts +3 -97
  30. package/corpus/core/src/client/settings/SettingsPanel.tsx +738 -441
  31. package/corpus/core/src/client/settings/SettingsSection.tsx +161 -17
  32. package/corpus/core/src/client/settings/SettingsTabsPage.tsx +25 -9
  33. package/corpus/core/src/client/sse-event-processor.ts +29 -2
  34. package/corpus/core/src/db-admin/agent-tools.ts +8 -2
  35. package/corpus/core/src/db-admin/operations.ts +116 -4
  36. package/corpus/core/src/db-admin/routes.ts +1 -0
  37. package/corpus/core/src/db-admin/types.ts +4 -0
  38. package/corpus/core/src/extensions/actions.ts +113 -5
  39. package/corpus/core/src/extensions/html-shell.ts +5 -4
  40. package/corpus/core/src/extensions/routes.ts +10 -0
  41. package/corpus/core/src/navigation/index.ts +1 -1
  42. package/corpus/core/src/notifications/channels.ts +88 -25
  43. package/corpus/core/src/notifications/registry.ts +17 -3
  44. package/corpus/core/src/notifications/types.ts +4 -1
  45. package/corpus/core/src/resources/handlers.ts +10 -8
  46. package/corpus/core/src/server/agent-chat-plugin.ts +7 -3
  47. package/corpus/core/src/server/poll.ts +293 -8
  48. package/corpus/core/src/server/request-context.ts +7 -0
  49. package/corpus/core/src/styles/agent-conversation.css +1 -1
  50. package/corpus/core/src/styles/agent-native.css +27 -0
  51. package/corpus/core/src/templates/default/.agents/skills/actions/SKILL.md +5 -0
  52. package/corpus/core/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +5 -0
  53. package/corpus/core/src/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
  54. package/corpus/core/src/templates/default/.agents/skills/security/SKILL.md +7 -0
  55. package/corpus/core/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +20 -2
  56. package/corpus/core/src/templates/default/.agents/skills/storing-data/SKILL.md +5 -1
  57. package/corpus/core/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  58. package/corpus/core/src/templates/default/AGENTS.md +14 -5
  59. package/corpus/core/src/templates/default/package.json +2 -1
  60. package/corpus/core/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  61. package/corpus/core/src/templates/headless/AGENTS.md +13 -3
  62. package/corpus/core/src/templates/headless/package.json +1 -0
  63. package/corpus/core/src/templates/workspace-core/.agents/skills/actions/SKILL.md +5 -0
  64. package/corpus/core/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +5 -0
  65. package/corpus/core/src/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +4 -0
  66. package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -0
  67. package/corpus/core/src/templates/workspace-core/.agents/skills/performance/SKILL.md +4 -0
  68. package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
  69. package/corpus/core/src/templates/workspace-core/.agents/skills/security/SKILL.md +7 -0
  70. package/corpus/core/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +20 -2
  71. package/corpus/core/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +5 -1
  72. package/corpus/core/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  73. package/corpus/core/src/templates/workspace-core/AGENTS.md +7 -1
  74. package/corpus/core/src/templates/workspace-root/AGENTS.md +17 -5
  75. package/corpus/core/src/templates/workspace-root/package.json +1 -0
  76. package/corpus/templates/analytics/.agents/skills/actions/SKILL.md +5 -0
  77. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +10 -0
  78. package/corpus/templates/analytics/.agents/skills/real-time-sync/SKILL.md +1 -1
  79. package/corpus/templates/analytics/.agents/skills/security/SKILL.md +7 -0
  80. package/corpus/templates/analytics/.agents/skills/self-modifying-code/SKILL.md +20 -2
  81. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +5 -6
  82. package/corpus/templates/analytics/.agents/skills/storing-data/SKILL.md +5 -1
  83. package/corpus/templates/analytics/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  84. package/corpus/templates/analytics/AGENTS.md +39 -18
  85. package/corpus/templates/analytics/actions/capture-test-error.ts +4 -0
  86. package/corpus/templates/analytics/actions/get-analytics-alert-rule-defaults.ts +23 -0
  87. package/corpus/templates/analytics/actions/get-error-issue.ts +1 -1
  88. package/corpus/templates/analytics/actions/list-analysis-revisions.ts +40 -0
  89. package/corpus/templates/analytics/actions/list-dashboard-revisions.ts +40 -0
  90. package/corpus/templates/analytics/actions/list-dashboard-usage-stats.ts +299 -0
  91. package/corpus/templates/analytics/actions/navigate.ts +4 -2
  92. package/corpus/templates/analytics/actions/restore-analysis-revision.ts +43 -0
  93. package/corpus/templates/analytics/actions/restore-dashboard-revision.ts +68 -0
  94. package/corpus/templates/analytics/actions/save-analytics-alert-rule.ts +14 -0
  95. package/corpus/templates/analytics/actions/save-monitor.ts +15 -1
  96. package/corpus/templates/analytics/actions/view-screen.ts +24 -0
  97. package/corpus/templates/analytics/app/components/analysis/AnalysisHistoryPanel.tsx +181 -0
  98. package/corpus/templates/analytics/app/components/dashboard/DashboardHistoryPanel.tsx +184 -0
  99. package/corpus/templates/analytics/app/components/dashboard/DataTable.tsx +41 -6
  100. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +122 -11
  101. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +22 -16
  102. package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -1
  103. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +27 -21
  104. package/corpus/templates/analytics/app/hooks/use-analysis-revisions.ts +43 -0
  105. package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +110 -0
  106. package/corpus/templates/analytics/app/hooks/use-dashboard-revisions.ts +43 -0
  107. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +8 -3
  108. package/corpus/templates/analytics/app/i18n/zh-TW.ts +64 -2
  109. package/corpus/templates/analytics/app/i18n-data.ts +783 -3
  110. package/corpus/templates/analytics/app/pages/Agents.tsx +359 -19
  111. package/corpus/templates/analytics/app/pages/Settings.tsx +3 -2
  112. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +34 -1
  113. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +28 -0
  114. package/corpus/templates/analytics/app/pages/analyses/AnalysisDetail.tsx +18 -1
  115. package/corpus/templates/analytics/app/pages/monitoring/ErrorsPanel.tsx +7 -10
  116. package/corpus/templates/analytics/app/pages/monitoring/MonitoringPage.tsx +8 -2
  117. package/corpus/templates/analytics/app/pages/monitoring/errors/IssueDetail.tsx +479 -23
  118. package/corpus/templates/analytics/app/pages/monitoring/errors/IssueList.tsx +0 -9
  119. package/corpus/templates/analytics/app/pages/monitoring/errors/i18n.ts +11 -0
  120. package/corpus/templates/analytics/app/pages/monitoring/errors/types.ts +9 -1
  121. package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorFormPage.tsx +157 -35
  122. package/corpus/templates/analytics/app/pages/monitoring/uptime/i18n.ts +17 -2
  123. package/corpus/templates/analytics/app/pages/monitoring/uptime/types.ts +4 -0
  124. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +80 -220
  125. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +9 -99
  126. package/corpus/templates/analytics/app/pages/settings/AlertRulesSettingsCard.tsx +262 -104
  127. package/corpus/templates/analytics/app/routes/team.tsx +1 -1
  128. package/corpus/templates/analytics/changelog/2026-07-08-admins-can-audit-dashboard-usage-ownership-and-cleanup-signa.md +6 -0
  129. package/corpus/templates/analytics/changelog/2026-07-08-agent-monitoring-stays-available-to-non-admins.md +6 -0
  130. package/corpus/templates/analytics/changelog/2026-07-08-alert-rules-are-easier-to-scan-expand-and-edit-from-settings.md +6 -0
  131. package/corpus/templates/analytics/changelog/2026-07-08-analytics-alert-forms-remember-last-email-recipients.md +6 -0
  132. package/corpus/templates/analytics/changelog/2026-07-08-analytics-alert-rules-can-now-use-per-rule-slack-and-webhook.md +6 -0
  133. package/corpus/templates/analytics/changelog/2026-07-08-dashboard-history-and-chat-context.md +5 -0
  134. package/corpus/templates/analytics/changelog/2026-07-08-dashboard-tables-keep-their-size-while-loading.md +6 -0
  135. package/corpus/templates/analytics/changelog/2026-07-08-dashboard-usage-now-counts-explorer-dashboard-traffic.md +6 -0
  136. package/corpus/templates/analytics/changelog/2026-07-08-demo-mode-session-emails-are-filtered-and-anonymized.md +6 -0
  137. package/corpus/templates/analytics/changelog/2026-07-08-error-issue-detail-adds-stack-traces-and-frequency-bars.md +6 -0
  138. package/corpus/templates/analytics/changelog/2026-07-08-error-issue-detail-now-shows-the-latest-occurrence-message-a.md +6 -0
  139. package/corpus/templates/analytics/changelog/2026-07-08-monitoring-errors-only-show-test-action-when-empty.md +6 -0
  140. package/corpus/templates/analytics/changelog/2026-07-08-monitoring-uptime-and-errors-tabs-now-clearly-show-which-view-is-active.md +6 -0
  141. package/corpus/templates/analytics/changelog/2026-07-08-production-session-replay-now-requires-private-blob-storage-.md +6 -0
  142. package/corpus/templates/analytics/changelog/2026-07-08-session-replay-uses-stock-rrweb-sizing-again-so-recorded.md +6 -0
  143. package/corpus/templates/analytics/changelog/2026-07-08-shared-extension-links-now-show-a-clear-message-when-you-do-.md +6 -0
  144. package/corpus/templates/analytics/changelog/2026-07-08-sidebar-dashboard-menus-now-open-above-the-dashboard-canvas.md +6 -0
  145. package/corpus/templates/analytics/changelog/2026-07-08-team-settings-show-access-controls-next-to-member-lists.md +6 -0
  146. package/corpus/templates/analytics/changelog/2026-07-08-uptime-alerting-clarifies-in-app-inbox-vs-email-and-lets-you-paste-slack-or-webhook-urls-per-monitor.md +6 -0
  147. package/corpus/templates/analytics/changelog/2026-07-08-uptime-monitors-now-use-a-10-second-request-timeout-by-defau.md +6 -0
  148. package/corpus/templates/analytics/docs/uptime-monitoring.md +13 -11
  149. package/corpus/templates/analytics/server/db/schema-monitoring.ts +5 -1
  150. package/corpus/templates/analytics/server/db/schema.ts +56 -0
  151. package/corpus/templates/analytics/server/lib/analytics-alerts.ts +92 -0
  152. package/corpus/templates/analytics/server/lib/dashboards-store.ts +334 -5
  153. package/corpus/templates/analytics/server/lib/error-capture.ts +231 -36
  154. package/corpus/templates/analytics/server/lib/session-replay.ts +114 -8
  155. package/corpus/templates/analytics/server/lib/uptime-monitors.ts +65 -16
  156. package/corpus/templates/analytics/server/plugins/db.ts +122 -2
  157. package/corpus/templates/analytics/server/routes/api/dashboard-agent-context.json.get.ts +1 -0
  158. package/corpus/templates/assets/.agents/skills/actions/SKILL.md +5 -0
  159. package/corpus/templates/assets/.agents/skills/real-time-sync/SKILL.md +1 -1
  160. package/corpus/templates/assets/.agents/skills/security/SKILL.md +7 -0
  161. package/corpus/templates/assets/.agents/skills/self-modifying-code/SKILL.md +20 -2
  162. package/corpus/templates/assets/.agents/skills/storing-data/SKILL.md +5 -1
  163. package/corpus/templates/assets/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  164. package/corpus/templates/assets/AGENTS.md +4 -0
  165. package/corpus/templates/assets/app/routes/settings.tsx +1 -1
  166. package/corpus/templates/assets/app/routes/team.tsx +1 -1
  167. package/corpus/templates/brain/.agents/skills/actions/SKILL.md +5 -0
  168. package/corpus/templates/brain/.agents/skills/adding-a-feature/SKILL.md +5 -0
  169. package/corpus/templates/brain/.agents/skills/real-time-sync/SKILL.md +1 -1
  170. package/corpus/templates/brain/.agents/skills/security/SKILL.md +7 -0
  171. package/corpus/templates/brain/.agents/skills/self-modifying-code/SKILL.md +20 -2
  172. package/corpus/templates/brain/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  173. package/corpus/templates/brain/AGENTS.md +4 -0
  174. package/corpus/templates/brain/app/routes/team.tsx +1 -1
  175. package/corpus/templates/calendar/.agents/skills/actions/SKILL.md +5 -0
  176. package/corpus/templates/calendar/.agents/skills/real-time-sync/SKILL.md +1 -1
  177. package/corpus/templates/calendar/.agents/skills/security/SKILL.md +7 -0
  178. package/corpus/templates/calendar/.agents/skills/self-modifying-code/SKILL.md +20 -2
  179. package/corpus/templates/calendar/.agents/skills/storing-data/SKILL.md +5 -1
  180. package/corpus/templates/calendar/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  181. package/corpus/templates/calendar/AGENTS.md +4 -0
  182. package/corpus/templates/calendar/app/components/calendar/DayView.tsx +11 -2
  183. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +21 -8
  184. package/corpus/templates/calendar/app/components/calendar/MonthView.tsx +7 -1
  185. package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +11 -2
  186. package/corpus/templates/calendar/app/lib/popover-click-guard.ts +48 -4
  187. package/corpus/templates/calendar/app/pages/Settings.tsx +1 -1
  188. package/corpus/templates/calendar/app/routes/_app.team.tsx +1 -1
  189. package/corpus/templates/calendar/changelog/2026-07-08-clicking-empty-calendar-space-closes-an-open-event-popover-b.md +6 -0
  190. package/corpus/templates/chat/.agents/skills/actions/SKILL.md +5 -0
  191. package/corpus/templates/chat/.agents/skills/adding-a-feature/SKILL.md +5 -0
  192. package/corpus/templates/chat/.agents/skills/real-time-sync/SKILL.md +1 -1
  193. package/corpus/templates/chat/.agents/skills/security/SKILL.md +7 -0
  194. package/corpus/templates/chat/.agents/skills/self-modifying-code/SKILL.md +20 -2
  195. package/corpus/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  196. package/corpus/templates/chat/AGENTS.md +4 -0
  197. package/corpus/templates/chat/app/hooks/use-navigation-state.ts +1 -1
  198. package/corpus/templates/chat/app/routes/settings.tsx +2 -2
  199. package/corpus/templates/chat/app/routes/team.tsx +1 -1
  200. package/corpus/templates/clips/.agents/skills/actions/SKILL.md +5 -0
  201. package/corpus/templates/clips/.agents/skills/security/SKILL.md +7 -0
  202. package/corpus/templates/clips/.agents/skills/self-modifying-code/SKILL.md +20 -2
  203. package/corpus/templates/clips/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  204. package/corpus/templates/clips/AGENTS.md +4 -0
  205. package/corpus/templates/clips/actions/finalize-recording.ts +9 -3
  206. package/corpus/templates/clips/actions/set-thumbnail.ts +11 -2
  207. package/corpus/templates/clips/app/components/meetings/canvas-editor.tsx +1 -7
  208. package/corpus/templates/clips/app/components/player/player-controls.tsx +21 -13
  209. package/corpus/templates/clips/app/components/player/video-player.tsx +30 -75
  210. package/corpus/templates/clips/app/i18n/ar-SA.ts +6 -5
  211. package/corpus/templates/clips/app/i18n/de-DE.ts +6 -6
  212. package/corpus/templates/clips/app/i18n/en-US.ts +6 -5
  213. package/corpus/templates/clips/app/i18n/es-ES.ts +6 -5
  214. package/corpus/templates/clips/app/i18n/fr-FR.ts +6 -6
  215. package/corpus/templates/clips/app/i18n/hi-IN.ts +6 -5
  216. package/corpus/templates/clips/app/i18n/ja-JP.ts +6 -5
  217. package/corpus/templates/clips/app/i18n/ko-KR.ts +6 -5
  218. package/corpus/templates/clips/app/i18n/pt-BR.ts +6 -5
  219. package/corpus/templates/clips/app/i18n/zh-CN.ts +6 -5
  220. package/corpus/templates/clips/app/i18n/zh-TW.ts +6 -5
  221. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +51 -118
  222. package/corpus/templates/clips/changelog/2026-07-08-desktop-meeting-notes-stay-active-from-tray-and-shortcut-controls.md +6 -0
  223. package/corpus/templates/clips/changelog/2026-07-08-desktop-meeting-popovers-can-start-notes-and-join-the-meetin.md +6 -0
  224. package/corpus/templates/clips/changelog/2026-07-08-hosted-clip-uploads-now-clear-failed-buffered-chunks.md +6 -0
  225. package/corpus/templates/clips/changelog/2026-07-08-hosted-recording-uploads-now-avoid-sql-chunk-storage.md +6 -0
  226. package/corpus/templates/clips/changelog/2026-07-08-meeting-notes-keep-their-recording-state-when-the-desktop.md +6 -0
  227. package/corpus/templates/clips/changelog/2026-07-08-meeting-recaps-now-keep-summaries-action-items-and-transcrip.md +6 -0
  228. package/corpus/templates/clips/changelog/2026-07-08-meeting-reminder-dropdowns-now-expand-fully-instead-of-being.md +6 -0
  229. package/corpus/templates/clips/changelog/2026-07-08-video-playback-controls-now-match-loom-style-5-second-skips-.md +6 -0
  230. package/corpus/templates/clips/desktop/src/app.tsx +123 -3
  231. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +15 -1
  232. package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +23 -0
  233. package/corpus/templates/clips/desktop/src/styles.css +23 -1
  234. package/corpus/templates/clips/desktop/src-tauri/src/adhoc_meetings_watcher.rs +1 -3
  235. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +40 -6
  236. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +4 -2
  237. package/corpus/templates/clips/desktop/src-tauri/src/meetings_watcher.rs +3 -5
  238. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +23 -19
  239. package/corpus/templates/clips/desktop/src-tauri/src/shortcuts.rs +3 -3
  240. package/corpus/templates/clips/desktop/src-tauri/src/silence_detector.rs +0 -1
  241. package/corpus/templates/clips/desktop/src-tauri/src/state.rs +5 -0
  242. package/corpus/templates/clips/desktop/src-tauri/src/tray.rs +18 -6
  243. package/corpus/templates/clips/server/lib/recording-upload-state.ts +63 -0
  244. package/corpus/templates/clips/server/lib/video-storage.ts +4 -0
  245. package/corpus/templates/clips/server/routes/api/recordings/[recordingId]/thumbnail.post.ts +19 -13
  246. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +40 -1
  247. package/corpus/templates/content/.agents/skills/actions/SKILL.md +5 -0
  248. package/corpus/templates/content/.agents/skills/real-time-sync/SKILL.md +1 -1
  249. package/corpus/templates/content/.agents/skills/security/SKILL.md +7 -0
  250. package/corpus/templates/content/.agents/skills/self-modifying-code/SKILL.md +20 -2
  251. package/corpus/templates/content/.agents/skills/storing-data/SKILL.md +5 -1
  252. package/corpus/templates/content/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  253. package/corpus/templates/content/AGENTS.md +4 -0
  254. package/corpus/templates/content/actions/_batch-utils.ts +8 -0
  255. package/corpus/templates/content/actions/_builder-cms-read-client.ts +7 -5
  256. package/corpus/templates/content/actions/_database-source-utils.ts +776 -319
  257. package/corpus/templates/content/actions/_database-utils.ts +6 -1
  258. package/corpus/templates/content/actions/_property-utils.ts +168 -0
  259. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +19 -12
  260. package/corpus/templates/content/actions/bind-content-database-source-field.ts +19 -12
  261. package/corpus/templates/content/actions/delete-document.ts +9 -13
  262. package/corpus/templates/content/actions/refresh-content-database-source.ts +5 -5
  263. package/corpus/templates/content/app/components/editor/body-hydration.ts +35 -11
  264. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +47 -5
  265. package/corpus/templates/content/app/hooks/use-documents.ts +8 -2
  266. package/corpus/templates/content/app/routes/_app.settings.tsx +2 -2
  267. package/corpus/templates/content/app/routes/_app.team.tsx +1 -1
  268. package/corpus/templates/content/changelog/2026-07-08-builder-source-sync-now-completes-reliably-on-hosted-databases.md +6 -0
  269. package/corpus/templates/content/changelog/2026-07-08-fixed-builder-database-sync-so-large-sources-continue-past-t.md +6 -0
  270. package/corpus/templates/content/server/plugins/db.ts +19 -0
  271. package/corpus/templates/design/.agents/skills/actions/SKILL.md +5 -0
  272. package/corpus/templates/design/.agents/skills/security/SKILL.md +7 -0
  273. package/corpus/templates/design/.agents/skills/self-modifying-code/SKILL.md +20 -2
  274. package/corpus/templates/design/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  275. package/corpus/templates/design/AGENTS.md +4 -0
  276. package/corpus/templates/design/actions/take-design-screenshot.ts +12 -9
  277. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +57 -13
  278. package/corpus/templates/design/app/routes/team.tsx +1 -1
  279. package/corpus/templates/design/changelog/2026-07-08-local-images-added-to-designs-now-upload-to-file-storage-ins.md +6 -0
  280. package/corpus/templates/dispatch/.agents/skills/actions/SKILL.md +5 -0
  281. package/corpus/templates/dispatch/.agents/skills/security/SKILL.md +7 -0
  282. package/corpus/templates/dispatch/.agents/skills/self-modifying-code/SKILL.md +20 -2
  283. package/corpus/templates/dispatch/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  284. package/corpus/templates/dispatch/AGENTS.md +4 -0
  285. package/corpus/templates/dispatch/app/hooks/use-navigation-state.ts +1 -1
  286. package/corpus/templates/dispatch/app/routes/settings.tsx +2 -2
  287. package/corpus/templates/dispatch/app/routes/team.tsx +1 -1
  288. package/corpus/templates/forms/.agents/skills/actions/SKILL.md +5 -0
  289. package/corpus/templates/forms/.agents/skills/real-time-sync/SKILL.md +1 -1
  290. package/corpus/templates/forms/.agents/skills/security/SKILL.md +7 -0
  291. package/corpus/templates/forms/.agents/skills/self-modifying-code/SKILL.md +20 -2
  292. package/corpus/templates/forms/.agents/skills/storing-data/SKILL.md +5 -1
  293. package/corpus/templates/forms/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  294. package/corpus/templates/forms/AGENTS.md +4 -0
  295. package/corpus/templates/forms/app/routes/_app.settings.tsx +2 -2
  296. package/corpus/templates/forms/app/routes/_app.team.tsx +1 -1
  297. package/corpus/templates/forms/shared/navigation.ts +1 -1
  298. package/corpus/templates/macros/.agents/skills/security/SKILL.md +7 -0
  299. package/corpus/templates/macros/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  300. package/corpus/templates/macros/AGENTS.md +4 -0
  301. package/corpus/templates/macros/app/routes/settings.tsx +2 -2
  302. package/corpus/templates/macros/app/routes/team.tsx +1 -1
  303. package/corpus/templates/mail/.agents/skills/actions/SKILL.md +5 -0
  304. package/corpus/templates/mail/.agents/skills/email-drafts/SKILL.md +1 -1
  305. package/corpus/templates/mail/.agents/skills/real-time-sync/SKILL.md +1 -1
  306. package/corpus/templates/mail/.agents/skills/security/SKILL.md +7 -0
  307. package/corpus/templates/mail/.agents/skills/self-modifying-code/SKILL.md +20 -2
  308. package/corpus/templates/mail/.agents/skills/storing-data/SKILL.md +5 -1
  309. package/corpus/templates/mail/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  310. package/corpus/templates/mail/AGENTS.md +4 -0
  311. package/corpus/templates/mail/changelog/2026-07-08-mail-attachments-in-hosted-environments-now-require-file-sto.md +6 -0
  312. package/corpus/templates/mail/server/handlers/media.ts +26 -1
  313. package/corpus/templates/mail/server/lib/outgoing-email.ts +24 -1
  314. package/corpus/templates/mail/server/lib/upload-store.ts +2 -1
  315. package/corpus/templates/plan/.agents/skills/actions/SKILL.md +5 -0
  316. package/corpus/templates/plan/.agents/skills/adding-a-feature/SKILL.md +5 -0
  317. package/corpus/templates/plan/.agents/skills/real-time-sync/SKILL.md +1 -1
  318. package/corpus/templates/plan/.agents/skills/security/SKILL.md +7 -0
  319. package/corpus/templates/plan/.agents/skills/self-modifying-code/SKILL.md +20 -2
  320. package/corpus/templates/plan/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  321. package/corpus/templates/plan/AGENTS.md +4 -0
  322. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +1 -1
  323. package/corpus/templates/plan/app/routes/settings.tsx +2 -2
  324. package/corpus/templates/plan/app/routes/team.tsx +1 -1
  325. package/corpus/templates/plan/changelog/2026-07-08-hosted-plan-images-now-require-connected-storage.md +6 -0
  326. package/corpus/templates/plan/server/lib/plan-assets.ts +33 -4
  327. package/corpus/templates/slides/.agents/skills/actions/SKILL.md +5 -0
  328. package/corpus/templates/slides/.agents/skills/security/SKILL.md +7 -0
  329. package/corpus/templates/slides/.agents/skills/self-modifying-code/SKILL.md +20 -2
  330. package/corpus/templates/slides/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  331. package/corpus/templates/slides/AGENTS.md +4 -0
  332. package/corpus/templates/slides/actions/generate-image-api.ts +15 -3
  333. package/corpus/templates/slides/app/routes/settings.tsx +2 -2
  334. package/corpus/templates/slides/app/routes/team.tsx +1 -1
  335. package/corpus/templates/slides/changelog/2026-07-08-generated-slide-images-now-return-hosted-file-storage-urls-i.md +6 -0
  336. package/corpus/templates/slides/shared/api.ts +1 -1
  337. package/corpus/templates/videos/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  338. package/corpus/templates/voice/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  339. package/dist/agent/production-agent.d.ts +5 -1
  340. package/dist/agent/production-agent.d.ts.map +1 -1
  341. package/dist/agent/production-agent.js +112 -42
  342. package/dist/agent/production-agent.js.map +1 -1
  343. package/dist/agent/tool-search.d.ts +2 -0
  344. package/dist/agent/tool-search.d.ts.map +1 -1
  345. package/dist/agent/tool-search.js +60 -4
  346. package/dist/agent/tool-search.js.map +1 -1
  347. package/dist/application-state/store.d.ts.map +1 -1
  348. package/dist/application-state/store.js +8 -2
  349. package/dist/application-state/store.js.map +1 -1
  350. package/dist/cli/index.js +19 -0
  351. package/dist/cli/index.js.map +1 -1
  352. package/dist/cli/upgrade.d.ts +100 -0
  353. package/dist/cli/upgrade.d.ts.map +1 -0
  354. package/dist/cli/upgrade.js +678 -0
  355. package/dist/cli/upgrade.js.map +1 -0
  356. package/dist/client/AssistantChat.d.ts.map +1 -1
  357. package/dist/client/AssistantChat.js +25 -14
  358. package/dist/client/AssistantChat.js.map +1 -1
  359. package/dist/client/HighlightedCodeBlock.d.ts +2 -0
  360. package/dist/client/HighlightedCodeBlock.d.ts.map +1 -1
  361. package/dist/client/HighlightedCodeBlock.js +10 -1
  362. package/dist/client/HighlightedCodeBlock.js.map +1 -1
  363. package/dist/client/PoweredByBadge.d.ts +1 -1
  364. package/dist/client/PoweredByBadge.d.ts.map +1 -1
  365. package/dist/client/PoweredByBadge.js +3 -9
  366. package/dist/client/PoweredByBadge.js.map +1 -1
  367. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  368. package/dist/client/agent-chat-adapter.js +60 -13
  369. package/dist/client/agent-chat-adapter.js.map +1 -1
  370. package/dist/client/chat/message-components.d.ts.map +1 -1
  371. package/dist/client/chat/message-components.js +61 -8
  372. package/dist/client/chat/message-components.js.map +1 -1
  373. package/dist/client/chat/tool-call-display.d.ts +12 -0
  374. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  375. package/dist/client/chat/tool-call-display.js +136 -42
  376. package/dist/client/chat/tool-call-display.js.map +1 -1
  377. package/dist/client/chat/widgets/DataTableWidget.d.ts.map +1 -1
  378. package/dist/client/chat/widgets/DataTableWidget.js +19 -2
  379. package/dist/client/chat/widgets/DataTableWidget.js.map +1 -1
  380. package/dist/client/code-agent-chat-adapter.js +2 -9
  381. package/dist/client/code-agent-chat-adapter.js.map +1 -1
  382. package/dist/client/db-admin/TableEditor.d.ts.map +1 -1
  383. package/dist/client/db-admin/TableEditor.js +19 -2
  384. package/dist/client/db-admin/TableEditor.js.map +1 -1
  385. package/dist/client/db-admin/export-utils.d.ts +2 -0
  386. package/dist/client/db-admin/export-utils.d.ts.map +1 -1
  387. package/dist/client/db-admin/export-utils.js +11 -3
  388. package/dist/client/db-admin/export-utils.js.map +1 -1
  389. package/dist/client/db-admin/useAgentSync.d.ts.map +1 -1
  390. package/dist/client/db-admin/useAgentSync.js +63 -0
  391. package/dist/client/db-admin/useAgentSync.js.map +1 -1
  392. package/dist/client/error-capture.d.ts.map +1 -1
  393. package/dist/client/error-capture.js +41 -7
  394. package/dist/client/error-capture.js.map +1 -1
  395. package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
  396. package/dist/client/extensions/EmbeddedExtension.js +3 -1
  397. package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
  398. package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
  399. package/dist/client/extensions/ExtensionViewer.js +32 -5
  400. package/dist/client/extensions/ExtensionViewer.js.map +1 -1
  401. package/dist/client/extensions/InlineExtensionFrame.d.ts.map +1 -1
  402. package/dist/client/extensions/InlineExtensionFrame.js +3 -1
  403. package/dist/client/extensions/InlineExtensionFrame.js.map +1 -1
  404. package/dist/client/extensions/portable-extension.d.ts.map +1 -1
  405. package/dist/client/extensions/portable-extension.js +1 -0
  406. package/dist/client/extensions/portable-extension.js.map +1 -1
  407. package/dist/client/org/OrgSwitcher.d.ts +1 -1
  408. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  409. package/dist/client/org/OrgSwitcher.js +11 -6
  410. package/dist/client/org/OrgSwitcher.js.map +1 -1
  411. package/dist/client/org/TeamPage.d.ts.map +1 -1
  412. package/dist/client/org/TeamPage.js +5 -5
  413. package/dist/client/org/TeamPage.js.map +1 -1
  414. package/dist/client/session-replay.d.ts +0 -8
  415. package/dist/client/session-replay.d.ts.map +1 -1
  416. package/dist/client/session-replay.js +3 -80
  417. package/dist/client/session-replay.js.map +1 -1
  418. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  419. package/dist/client/settings/SettingsPanel.js +89 -33
  420. package/dist/client/settings/SettingsPanel.js.map +1 -1
  421. package/dist/client/settings/SettingsSection.d.ts +19 -4
  422. package/dist/client/settings/SettingsSection.d.ts.map +1 -1
  423. package/dist/client/settings/SettingsSection.js +33 -4
  424. package/dist/client/settings/SettingsSection.js.map +1 -1
  425. package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -1
  426. package/dist/client/settings/SettingsTabsPage.js +21 -10
  427. package/dist/client/settings/SettingsTabsPage.js.map +1 -1
  428. package/dist/client/sse-event-processor.d.ts +9 -0
  429. package/dist/client/sse-event-processor.d.ts.map +1 -1
  430. package/dist/client/sse-event-processor.js +20 -2
  431. package/dist/client/sse-event-processor.js.map +1 -1
  432. package/dist/db-admin/agent-tools.d.ts.map +1 -1
  433. package/dist/db-admin/agent-tools.js +7 -2
  434. package/dist/db-admin/agent-tools.js.map +1 -1
  435. package/dist/db-admin/operations.d.ts.map +1 -1
  436. package/dist/db-admin/operations.js +90 -4
  437. package/dist/db-admin/operations.js.map +1 -1
  438. package/dist/db-admin/routes.d.ts.map +1 -1
  439. package/dist/db-admin/routes.js +1 -0
  440. package/dist/db-admin/routes.js.map +1 -1
  441. package/dist/db-admin/types.d.ts +4 -0
  442. package/dist/db-admin/types.d.ts.map +1 -1
  443. package/dist/db-admin/types.js.map +1 -1
  444. package/dist/extensions/actions.d.ts.map +1 -1
  445. package/dist/extensions/actions.js +85 -5
  446. package/dist/extensions/actions.js.map +1 -1
  447. package/dist/extensions/html-shell.d.ts +5 -4
  448. package/dist/extensions/html-shell.d.ts.map +1 -1
  449. package/dist/extensions/html-shell.js.map +1 -1
  450. package/dist/extensions/routes.d.ts.map +1 -1
  451. package/dist/extensions/routes.js +8 -0
  452. package/dist/extensions/routes.js.map +1 -1
  453. package/dist/navigation/index.d.ts +1 -1
  454. package/dist/navigation/index.d.ts.map +1 -1
  455. package/dist/navigation/index.js +1 -1
  456. package/dist/navigation/index.js.map +1 -1
  457. package/dist/notifications/channels.d.ts +8 -3
  458. package/dist/notifications/channels.d.ts.map +1 -1
  459. package/dist/notifications/channels.js +61 -22
  460. package/dist/notifications/channels.js.map +1 -1
  461. package/dist/notifications/registry.d.ts.map +1 -1
  462. package/dist/notifications/registry.js +14 -3
  463. package/dist/notifications/registry.js.map +1 -1
  464. package/dist/notifications/types.d.ts +4 -1
  465. package/dist/notifications/types.d.ts.map +1 -1
  466. package/dist/notifications/types.js.map +1 -1
  467. package/dist/resources/handlers.d.ts +5 -0
  468. package/dist/resources/handlers.d.ts.map +1 -1
  469. package/dist/resources/handlers.js +9 -8
  470. package/dist/resources/handlers.js.map +1 -1
  471. package/dist/server/agent-chat-plugin.d.ts +5 -1
  472. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  473. package/dist/server/agent-chat-plugin.js +2 -2
  474. package/dist/server/agent-chat-plugin.js.map +1 -1
  475. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  476. package/dist/server/poll.d.ts +12 -4
  477. package/dist/server/poll.d.ts.map +1 -1
  478. package/dist/server/poll.js +236 -9
  479. package/dist/server/poll.js.map +1 -1
  480. package/dist/server/request-context.d.ts +7 -0
  481. package/dist/server/request-context.d.ts.map +1 -1
  482. package/dist/server/request-context.js.map +1 -1
  483. package/dist/styles/agent-conversation.css +1 -1
  484. package/dist/styles/agent-native.css +27 -0
  485. package/dist/templates/default/.agents/skills/actions/SKILL.md +5 -0
  486. package/dist/templates/default/.agents/skills/adding-a-feature/SKILL.md +5 -0
  487. package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
  488. package/dist/templates/default/.agents/skills/security/SKILL.md +7 -0
  489. package/dist/templates/default/.agents/skills/self-modifying-code/SKILL.md +20 -2
  490. package/dist/templates/default/.agents/skills/storing-data/SKILL.md +5 -1
  491. package/dist/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  492. package/dist/templates/default/AGENTS.md +14 -5
  493. package/dist/templates/default/package.json +2 -1
  494. package/dist/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  495. package/dist/templates/headless/AGENTS.md +13 -3
  496. package/dist/templates/headless/package.json +1 -0
  497. package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +5 -0
  498. package/dist/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +5 -0
  499. package/dist/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +4 -0
  500. package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -0
  501. package/dist/templates/workspace-core/.agents/skills/performance/SKILL.md +4 -0
  502. package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
  503. package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +7 -0
  504. package/dist/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +20 -2
  505. package/dist/templates/workspace-core/.agents/skills/storing-data/SKILL.md +5 -1
  506. package/dist/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  507. package/dist/templates/workspace-core/AGENTS.md +7 -1
  508. package/dist/templates/workspace-root/AGENTS.md +17 -5
  509. package/dist/templates/workspace-root/package.json +1 -0
  510. package/docs/content/faq.mdx +17 -0
  511. package/docs/content/notifications.mdx +12 -6
  512. package/package.json +2 -2
  513. package/src/templates/default/.agents/skills/actions/SKILL.md +5 -0
  514. package/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +5 -0
  515. package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
  516. package/src/templates/default/.agents/skills/security/SKILL.md +7 -0
  517. package/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +20 -2
  518. package/src/templates/default/.agents/skills/storing-data/SKILL.md +5 -1
  519. package/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  520. package/src/templates/default/AGENTS.md +14 -5
  521. package/src/templates/default/package.json +2 -1
  522. package/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  523. package/src/templates/headless/AGENTS.md +13 -3
  524. package/src/templates/headless/package.json +1 -0
  525. package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +5 -0
  526. package/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +5 -0
  527. package/src/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +4 -0
  528. package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -0
  529. package/src/templates/workspace-core/.agents/skills/performance/SKILL.md +4 -0
  530. package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
  531. package/src/templates/workspace-core/.agents/skills/security/SKILL.md +7 -0
  532. package/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +20 -2
  533. package/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +5 -1
  534. package/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  535. package/src/templates/workspace-core/AGENTS.md +7 -1
  536. package/src/templates/workspace-root/AGENTS.md +17 -5
  537. package/src/templates/workspace-root/package.json +1 -0
  538. package/corpus/templates/analytics/changelog/2026-07-08-session-replay-clamps-ultra-wide-viewports-while-keeping-the.md +0 -6
@@ -10,7 +10,7 @@ export const STANDARD_SETTINGS_TABS = {
10
10
  providers: "providers",
11
11
  connections: "connections",
12
12
  secrets: "secrets",
13
- team: "team",
13
+ team: "organization",
14
14
  usage: "usage",
15
15
  language: "language",
16
16
  whatsNew: "whats-new",
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/navigation/index.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AGENT_NATIVE_OPEN_PATH,\n withCollapsedAgentSidebarParam,\n} from \"../shared/agent-sidebar-url.js\";\n\nexport const STANDARD_APP_ROUTES = {\n home: \"/\",\n settings: \"/settings\",\n team: \"/team\",\n} as const;\n\nexport const STANDARD_SETTINGS_TABS = {\n general: \"general\",\n agent: \"agent\",\n providers: \"providers\",\n connections: \"connections\",\n secrets: \"secrets\",\n team: \"team\",\n usage: \"usage\",\n language: \"language\",\n whatsNew: \"whats-new\",\n} as const;\n\nexport type StandardAppRouteId = keyof typeof STANDARD_APP_ROUTES;\nexport type StandardSettingsTabId =\n (typeof STANDARD_SETTINGS_TABS)[keyof typeof STANDARD_SETTINGS_TABS];\n\nexport interface BuildStandardAppRouteOptions {\n settingsTab?: StandardSettingsTabId | string | null;\n teamInSettings?: boolean;\n}\n\nexport interface BuildResourceRouteOptions {\n basePath?: string;\n}\n\nexport interface NavigationTarget {\n app?: string;\n view: string;\n params?: Record<string, string | number | boolean | null | undefined>;\n to?: string;\n}\n\nexport interface NavigationLink extends NavigationTarget {\n label: string;\n url: string;\n}\n\nexport type StandardOpenPathRoute =\n | string\n | ((params: Record<string, string>) => string | null | undefined);\n\nexport interface StandardOpenPathResolverOptions {\n fallback?: StandardOpenPathRoute;\n}\n\nfunction normalizeLeadingPath(path: string): string {\n const trimmed = path.trim();\n if (!trimmed || trimmed === \"/\") return \"/\";\n return `/${trimmed.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\")}`;\n}\n\nfunction normalizeTabId(tab: string): string {\n return tab\n .trim()\n .replace(/^#/, \"\")\n .toLowerCase()\n .replace(/['\"]/g, \"\")\n .replace(/[\\s_]+/g, \"-\");\n}\n\nfunction pathSegment(value: string): string {\n return encodeURIComponent(value.trim().replace(/^\\/+|\\/+$/g, \"\"));\n}\n\nfunction resolveOpenPathRoute(\n route: StandardOpenPathRoute | undefined,\n params: Record<string, string>,\n): string | null | undefined {\n if (!route) return null;\n return typeof route === \"function\" ? route(params) : route;\n}\n\nexport function buildSettingsRoute(\n tab?: StandardSettingsTabId | string | null,\n basePath = STANDARD_APP_ROUTES.settings,\n): string {\n const path = normalizeLeadingPath(basePath);\n const normalizedTab = tab ? normalizeTabId(tab) : null;\n if (!normalizedTab || normalizedTab === STANDARD_SETTINGS_TABS.general) {\n return path;\n }\n return `${path}#${encodeURIComponent(normalizedTab)}`;\n}\n\nexport function buildTeamRoute(\n options: Pick<BuildStandardAppRouteOptions, \"teamInSettings\"> = {},\n): string {\n return options.teamInSettings\n ? buildSettingsRoute(STANDARD_SETTINGS_TABS.team)\n : STANDARD_APP_ROUTES.team;\n}\n\nexport function buildStandardAppRoute(\n route: StandardAppRouteId,\n options: BuildStandardAppRouteOptions = {},\n): string {\n if (route === \"settings\") return buildSettingsRoute(options.settingsTab);\n if (route === \"team\") return buildTeamRoute(options);\n return STANDARD_APP_ROUTES[route];\n}\n\nexport function buildResourceRoute(\n collection: string,\n resourceId: string,\n options: BuildResourceRouteOptions = {},\n): string {\n const base = normalizeLeadingPath(options.basePath ?? \"/\");\n const collectionPath = collection\n .split(\"/\")\n .map((segment) => pathSegment(segment))\n .filter(Boolean)\n .join(\"/\");\n if (!collectionPath) throw new Error(\"collection is required\");\n if (!resourceId.trim()) throw new Error(\"resourceId is required\");\n const prefix = base === \"/\" ? \"\" : base;\n return `${prefix}/${collectionPath}/${pathSegment(resourceId)}`;\n}\n\nexport function buildOpenRoutePath(input: NavigationTarget): string {\n const sp = new URLSearchParams();\n if (input.app) sp.set(\"app\", input.app);\n sp.set(\"view\", input.view);\n if (input.to) sp.set(\"to\", input.to);\n for (const [key, value] of Object.entries(input.params ?? {})) {\n if (value === undefined || value === null || value === \"\") continue;\n sp.set(key, String(value));\n }\n return withCollapsedAgentSidebarParam(`${AGENT_NATIVE_OPEN_PATH}?${sp}`);\n}\n\nexport function buildOpenRouteLink(\n input: NavigationTarget & { label?: string },\n): NavigationLink {\n return {\n ...input,\n label: input.label ?? `Open ${input.view}`,\n url: buildOpenRoutePath(input),\n };\n}\n\nexport function createStandardOpenPathResolver(\n routes: Record<string, StandardOpenPathRoute>,\n options: StandardOpenPathResolverOptions = {},\n) {\n return (input: {\n app?: string;\n view?: string;\n params: Record<string, string>;\n }): string | null | undefined => {\n void input.app;\n if (!input.view)\n return resolveOpenPathRoute(options.fallback, input.params);\n return (\n resolveOpenPathRoute(routes[input.view], input.params) ??\n resolveOpenPathRoute(options.fallback, input.params) ??\n `/${input.view}`\n );\n };\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/navigation/index.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AGENT_NATIVE_OPEN_PATH,\n withCollapsedAgentSidebarParam,\n} from \"../shared/agent-sidebar-url.js\";\n\nexport const STANDARD_APP_ROUTES = {\n home: \"/\",\n settings: \"/settings\",\n team: \"/team\",\n} as const;\n\nexport const STANDARD_SETTINGS_TABS = {\n general: \"general\",\n agent: \"agent\",\n providers: \"providers\",\n connections: \"connections\",\n secrets: \"secrets\",\n team: \"organization\",\n usage: \"usage\",\n language: \"language\",\n whatsNew: \"whats-new\",\n} as const;\n\nexport type StandardAppRouteId = keyof typeof STANDARD_APP_ROUTES;\nexport type StandardSettingsTabId =\n (typeof STANDARD_SETTINGS_TABS)[keyof typeof STANDARD_SETTINGS_TABS];\n\nexport interface BuildStandardAppRouteOptions {\n settingsTab?: StandardSettingsTabId | string | null;\n teamInSettings?: boolean;\n}\n\nexport interface BuildResourceRouteOptions {\n basePath?: string;\n}\n\nexport interface NavigationTarget {\n app?: string;\n view: string;\n params?: Record<string, string | number | boolean | null | undefined>;\n to?: string;\n}\n\nexport interface NavigationLink extends NavigationTarget {\n label: string;\n url: string;\n}\n\nexport type StandardOpenPathRoute =\n | string\n | ((params: Record<string, string>) => string | null | undefined);\n\nexport interface StandardOpenPathResolverOptions {\n fallback?: StandardOpenPathRoute;\n}\n\nfunction normalizeLeadingPath(path: string): string {\n const trimmed = path.trim();\n if (!trimmed || trimmed === \"/\") return \"/\";\n return `/${trimmed.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\")}`;\n}\n\nfunction normalizeTabId(tab: string): string {\n return tab\n .trim()\n .replace(/^#/, \"\")\n .toLowerCase()\n .replace(/['\"]/g, \"\")\n .replace(/[\\s_]+/g, \"-\");\n}\n\nfunction pathSegment(value: string): string {\n return encodeURIComponent(value.trim().replace(/^\\/+|\\/+$/g, \"\"));\n}\n\nfunction resolveOpenPathRoute(\n route: StandardOpenPathRoute | undefined,\n params: Record<string, string>,\n): string | null | undefined {\n if (!route) return null;\n return typeof route === \"function\" ? route(params) : route;\n}\n\nexport function buildSettingsRoute(\n tab?: StandardSettingsTabId | string | null,\n basePath = STANDARD_APP_ROUTES.settings,\n): string {\n const path = normalizeLeadingPath(basePath);\n const normalizedTab = tab ? normalizeTabId(tab) : null;\n if (!normalizedTab || normalizedTab === STANDARD_SETTINGS_TABS.general) {\n return path;\n }\n return `${path}#${encodeURIComponent(normalizedTab)}`;\n}\n\nexport function buildTeamRoute(\n options: Pick<BuildStandardAppRouteOptions, \"teamInSettings\"> = {},\n): string {\n return options.teamInSettings\n ? buildSettingsRoute(STANDARD_SETTINGS_TABS.team)\n : STANDARD_APP_ROUTES.team;\n}\n\nexport function buildStandardAppRoute(\n route: StandardAppRouteId,\n options: BuildStandardAppRouteOptions = {},\n): string {\n if (route === \"settings\") return buildSettingsRoute(options.settingsTab);\n if (route === \"team\") return buildTeamRoute(options);\n return STANDARD_APP_ROUTES[route];\n}\n\nexport function buildResourceRoute(\n collection: string,\n resourceId: string,\n options: BuildResourceRouteOptions = {},\n): string {\n const base = normalizeLeadingPath(options.basePath ?? \"/\");\n const collectionPath = collection\n .split(\"/\")\n .map((segment) => pathSegment(segment))\n .filter(Boolean)\n .join(\"/\");\n if (!collectionPath) throw new Error(\"collection is required\");\n if (!resourceId.trim()) throw new Error(\"resourceId is required\");\n const prefix = base === \"/\" ? \"\" : base;\n return `${prefix}/${collectionPath}/${pathSegment(resourceId)}`;\n}\n\nexport function buildOpenRoutePath(input: NavigationTarget): string {\n const sp = new URLSearchParams();\n if (input.app) sp.set(\"app\", input.app);\n sp.set(\"view\", input.view);\n if (input.to) sp.set(\"to\", input.to);\n for (const [key, value] of Object.entries(input.params ?? {})) {\n if (value === undefined || value === null || value === \"\") continue;\n sp.set(key, String(value));\n }\n return withCollapsedAgentSidebarParam(`${AGENT_NATIVE_OPEN_PATH}?${sp}`);\n}\n\nexport function buildOpenRouteLink(\n input: NavigationTarget & { label?: string },\n): NavigationLink {\n return {\n ...input,\n label: input.label ?? `Open ${input.view}`,\n url: buildOpenRoutePath(input),\n };\n}\n\nexport function createStandardOpenPathResolver(\n routes: Record<string, StandardOpenPathRoute>,\n options: StandardOpenPathResolverOptions = {},\n) {\n return (input: {\n app?: string;\n view?: string;\n params: Record<string, string>;\n }): string | null | undefined => {\n void input.app;\n if (!input.view)\n return resolveOpenPathRoute(options.fallback, input.params);\n return (\n resolveOpenPathRoute(routes[input.view], input.params) ??\n resolveOpenPathRoute(options.fallback, input.params) ??\n `/${input.view}`\n );\n };\n}\n"]}
@@ -1,17 +1,22 @@
1
1
  /**
2
2
  * Built-in notification channels.
3
3
  *
4
- * Set environment variables to auto-register the webhook channel at startup.
4
+ * Webhook and Slack are always registered. Delivery uses (in order):
5
+ * 1. per-notification `metadata.webhookUrl` / `metadata.slackWebhookUrl`
6
+ * 2. the matching `NOTIFICATIONS_*` env URL (workspace default)
7
+ * If neither is set, that channel no-ops for the emission.
8
+ *
5
9
  * Extra channels can be registered at any time via
6
10
  * `registerNotificationChannel()` from a server plugin.
7
11
  *
8
- * NOTIFICATIONS_WEBHOOK_URL → POST notifications as JSON to this URL.
12
+ * NOTIFICATIONS_WEBHOOK_URL → default POST JSON URL when metadata omits one.
9
13
  * Supports `${keys.NAME}` substitution — the raw
10
14
  * value never enters the agent context.
11
15
  * NOTIFICATIONS_WEBHOOK_AUTH → optional `Authorization` header value (also
12
16
  * supports `${keys.NAME}`).
13
17
  * NOTIFICATIONS_SLACK_WEBHOOK_URL
14
- * → POST notifications to a Slack incoming webhook.
18
+ * → default Slack incoming webhook when metadata
19
+ * omits `slackWebhookUrl`.
15
20
  * Supports `${keys.NAME}` substitution.
16
21
  * NOTIFICATIONS_EMAIL_CHANNEL=1
17
22
  * → enable the built-in email channel for
@@ -1 +1 @@
1
- {"version":3,"file":"channels.d.ts","sourceRoot":"","sources":["../../src/notifications/channels.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAcH,wBAAgB,mCAAmC,IAAI,IAAI,CAkB1D"}
1
+ {"version":3,"file":"channels.d.ts","sourceRoot":"","sources":["../../src/notifications/channels.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAcH,wBAAgB,mCAAmC,IAAI,IAAI,CAa1D"}
@@ -1,17 +1,22 @@
1
1
  /**
2
2
  * Built-in notification channels.
3
3
  *
4
- * Set environment variables to auto-register the webhook channel at startup.
4
+ * Webhook and Slack are always registered. Delivery uses (in order):
5
+ * 1. per-notification `metadata.webhookUrl` / `metadata.slackWebhookUrl`
6
+ * 2. the matching `NOTIFICATIONS_*` env URL (workspace default)
7
+ * If neither is set, that channel no-ops for the emission.
8
+ *
5
9
  * Extra channels can be registered at any time via
6
10
  * `registerNotificationChannel()` from a server plugin.
7
11
  *
8
- * NOTIFICATIONS_WEBHOOK_URL → POST notifications as JSON to this URL.
12
+ * NOTIFICATIONS_WEBHOOK_URL → default POST JSON URL when metadata omits one.
9
13
  * Supports `${keys.NAME}` substitution — the raw
10
14
  * value never enters the agent context.
11
15
  * NOTIFICATIONS_WEBHOOK_AUTH → optional `Authorization` header value (also
12
16
  * supports `${keys.NAME}`).
13
17
  * NOTIFICATIONS_SLACK_WEBHOOK_URL
14
- * → POST notifications to a Slack incoming webhook.
18
+ * → default Slack incoming webhook when metadata
19
+ * omits `slackWebhookUrl`.
15
20
  * Supports `${keys.NAME}` substitution.
16
21
  * NOTIFICATIONS_EMAIL_CHANNEL=1
17
22
  * → enable the built-in email channel for
@@ -30,25 +35,26 @@ export function registerBuiltinNotificationChannels() {
30
35
  if (_registered)
31
36
  return;
32
37
  _registered = true;
33
- const url = process.env.NOTIFICATIONS_WEBHOOK_URL;
34
- if (url) {
35
- registerNotificationChannel(createWebhookChannel(url));
36
- }
37
- const slackUrl = process.env.NOTIFICATIONS_SLACK_WEBHOOK_URL;
38
- if (slackUrl) {
39
- registerNotificationChannel(createSlackWebhookChannel(slackUrl));
40
- }
41
- const emailRecipients = process.env.NOTIFICATIONS_EMAIL_RECIPIENTS?.trim();
42
- if (process.env.NOTIFICATIONS_EMAIL_CHANNEL === "1" || emailRecipients) {
43
- registerNotificationChannel(createEmailChannel());
44
- }
38
+ registerNotificationChannel(createWebhookChannel(process.env.NOTIFICATIONS_WEBHOOK_URL));
39
+ registerNotificationChannel(createSlackWebhookChannel(process.env.NOTIFICATIONS_SLACK_WEBHOOK_URL));
40
+ // Email is always registered so per-notification `metadata.emailRecipients`
41
+ // work without a workspace-wide env flag (same pattern as webhook/slack).
42
+ registerNotificationChannel(createEmailChannel());
45
43
  }
46
- function createWebhookChannel(urlTemplate) {
44
+ function createWebhookChannel(envUrlTemplate) {
47
45
  const authTemplate = process.env.NOTIFICATIONS_WEBHOOK_AUTH;
48
46
  return {
49
47
  name: "webhook",
50
48
  async deliver(input, meta) {
51
- const { url, headers } = await resolveWebhookRequest(urlTemplate, authTemplate, meta.owner, "webhook");
49
+ const overrideUrlTemplate = deliveryMetadataString(input.metadata, "webhookUrl");
50
+ const urlTemplate = overrideUrlTemplate ??
51
+ metadataString(input.metadata, "webhookUrl") ??
52
+ envUrlTemplate?.trim();
53
+ // No-op when neither a per-notification nor workspace URL is set —
54
+ // mirrors email's empty-recipients behavior so notify-all stays quiet.
55
+ if (!urlTemplate)
56
+ return false;
57
+ const { url, headers } = await resolveWebhookRequest(urlTemplate, overrideUrlTemplate ? undefined : authTemplate, meta.owner, "webhook");
52
58
  const res = await ssrfSafeFetch(url, {
53
59
  method: "POST",
54
60
  headers,
@@ -56,7 +62,7 @@ function createWebhookChannel(urlTemplate) {
56
62
  severity: input.severity,
57
63
  title: input.title,
58
64
  body: input.body,
59
- metadata: input.metadata,
65
+ metadata: scrubDeliveryMetadata(input.metadata),
60
66
  owner: meta.owner,
61
67
  emittedAt: new Date().toISOString(),
62
68
  }),
@@ -67,12 +73,18 @@ function createWebhookChannel(urlTemplate) {
67
73
  },
68
74
  };
69
75
  }
70
- function createSlackWebhookChannel(urlTemplate) {
76
+ function createSlackWebhookChannel(envUrlTemplate) {
71
77
  const authTemplate = process.env.NOTIFICATIONS_SLACK_WEBHOOK_AUTH;
72
78
  return {
73
79
  name: "slack",
74
80
  async deliver(input, meta) {
75
- const { url, headers } = await resolveWebhookRequest(urlTemplate, authTemplate, meta.owner, "Slack webhook");
81
+ const overrideUrlTemplate = deliveryMetadataString(input.metadata, "slackWebhookUrl");
82
+ const urlTemplate = overrideUrlTemplate ??
83
+ metadataString(input.metadata, "slackWebhookUrl") ??
84
+ envUrlTemplate?.trim();
85
+ if (!urlTemplate)
86
+ return false;
87
+ const { url, headers } = await resolveWebhookRequest(urlTemplate, overrideUrlTemplate ? undefined : authTemplate, meta.owner, "Slack webhook");
76
88
  const res = await ssrfSafeFetch(url, {
77
89
  method: "POST",
78
90
  headers,
@@ -121,7 +133,7 @@ function createEmailChannel() {
121
133
  async deliver(input) {
122
134
  const recipients = notificationEmailRecipients(input.metadata);
123
135
  if (recipients.length === 0)
124
- return;
136
+ return false;
125
137
  const subject = typeof input.metadata?.emailSubject === "string" &&
126
138
  input.metadata.emailSubject.trim()
127
139
  ? input.metadata.emailSubject.trim()
@@ -170,6 +182,30 @@ async function resolveWebhookRequest(urlTemplate, authTemplate, owner, label) {
170
182
  });
171
183
  return { url, headers };
172
184
  }
185
+ function metadataString(metadata, key) {
186
+ const value = metadata?.[key];
187
+ if (typeof value !== "string")
188
+ return undefined;
189
+ const trimmed = value.trim();
190
+ return trimmed || undefined;
191
+ }
192
+ function deliveryMetadataString(metadata, key) {
193
+ const delivery = metadata?.delivery;
194
+ if (!delivery || typeof delivery !== "object" || Array.isArray(delivery)) {
195
+ return undefined;
196
+ }
197
+ const value = delivery[key];
198
+ if (typeof value !== "string")
199
+ return undefined;
200
+ const trimmed = value.trim();
201
+ return trimmed || undefined;
202
+ }
203
+ function scrubDeliveryMetadata(metadata) {
204
+ if (!metadata)
205
+ return undefined;
206
+ const entries = Object.entries(metadata).filter(([key]) => key !== "delivery" && key !== "webhookUrl" && key !== "slackWebhookUrl");
207
+ return entries.length ? Object.fromEntries(entries) : undefined;
208
+ }
173
209
  function notificationEmailRecipients(metadata) {
174
210
  const fromMetadata = stringArray(metadata?.emailRecipients);
175
211
  const fromEnv = commaList(process.env.NOTIFICATIONS_EMAIL_RECIPIENTS);
@@ -200,7 +236,10 @@ function commaList(value) {
200
236
  function scrubEmailMetadata(metadata) {
201
237
  if (!metadata)
202
238
  return null;
203
- const entries = Object.entries(metadata).filter(([key]) => key !== "emailRecipients" && key !== "emailSubject");
239
+ const entries = Object.entries(metadata).filter(([key]) => key !== "emailRecipients" &&
240
+ key !== "emailSubject" &&
241
+ key !== "webhookUrl" &&
242
+ key !== "slackWebhookUrl");
204
243
  return entries.length ? Object.fromEntries(entries) : null;
205
244
  }
206
245
  function slackText(severity, title, body) {
@@ -1 +1 @@
1
- {"version":3,"file":"channels.js","sourceRoot":"","sources":["../../src/notifications/channels.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Built-in notification channels.\n *\n * Set environment variables to auto-register the webhook channel at startup.\n * Extra channels can be registered at any time via\n * `registerNotificationChannel()` from a server plugin.\n *\n * NOTIFICATIONS_WEBHOOK_URL → POST notifications as JSON to this URL.\n * Supports `${keys.NAME}` substitution — the raw\n * value never enters the agent context.\n * NOTIFICATIONS_WEBHOOK_AUTH → optional `Authorization` header value (also\n * supports `${keys.NAME}`).\n * NOTIFICATIONS_SLACK_WEBHOOK_URL\n * → POST notifications to a Slack incoming webhook.\n * Supports `${keys.NAME}` substitution.\n * NOTIFICATIONS_EMAIL_CHANNEL=1\n * → enable the built-in email channel for\n * per-notification recipients.\n * NOTIFICATIONS_EMAIL_RECIPIENTS\n * → comma-separated fallback recipients for email\n * notifications that do not pass\n * `metadata.emailRecipients`.\n */\n\nimport { ssrfSafeFetch } from \"../extensions/url-safety.js\";\nimport {\n resolveKeyReferences,\n validateUrlAllowlist,\n getKeyAllowlist,\n} from \"../secrets/substitution.js\";\nimport { sendEmail } from \"../server/email.js\";\nimport { registerNotificationChannel } from \"./registry.js\";\nimport type { NotificationChannel } from \"./types.js\";\n\nlet _registered = false;\n\nexport function registerBuiltinNotificationChannels(): void {\n if (_registered) return;\n _registered = true;\n\n const url = process.env.NOTIFICATIONS_WEBHOOK_URL;\n if (url) {\n registerNotificationChannel(createWebhookChannel(url));\n }\n\n const slackUrl = process.env.NOTIFICATIONS_SLACK_WEBHOOK_URL;\n if (slackUrl) {\n registerNotificationChannel(createSlackWebhookChannel(slackUrl));\n }\n\n const emailRecipients = process.env.NOTIFICATIONS_EMAIL_RECIPIENTS?.trim();\n if (process.env.NOTIFICATIONS_EMAIL_CHANNEL === \"1\" || emailRecipients) {\n registerNotificationChannel(createEmailChannel());\n }\n}\n\nfunction createWebhookChannel(urlTemplate: string): NotificationChannel {\n const authTemplate = process.env.NOTIFICATIONS_WEBHOOK_AUTH;\n return {\n name: \"webhook\",\n async deliver(input, meta) {\n const { url, headers } = await resolveWebhookRequest(\n urlTemplate,\n authTemplate,\n meta.owner,\n \"webhook\",\n );\n const res = await ssrfSafeFetch(\n url,\n {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n severity: input.severity,\n title: input.title,\n body: input.body,\n metadata: input.metadata,\n owner: meta.owner,\n emittedAt: new Date().toISOString(),\n }),\n },\n { maxRedirects: 3 },\n );\n if (!res.ok) {\n throw new Error(\n `[notifications] webhook ${new URL(url).origin} returned ${res.status}${\n (await readErrorSnippet(res)) || \"\"\n }`,\n );\n }\n },\n };\n}\n\nfunction createSlackWebhookChannel(urlTemplate: string): NotificationChannel {\n const authTemplate = process.env.NOTIFICATIONS_SLACK_WEBHOOK_AUTH;\n return {\n name: \"slack\",\n async deliver(input, meta) {\n const { url, headers } = await resolveWebhookRequest(\n urlTemplate,\n authTemplate,\n meta.owner,\n \"Slack webhook\",\n );\n const res = await ssrfSafeFetch(\n url,\n {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n text: slackText(input.severity, input.title, input.body),\n blocks: [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: `*${escapeSlack(input.title)}*`,\n },\n },\n ...(input.body\n ? [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: escapeSlack(input.body),\n },\n },\n ]\n : []),\n {\n type: \"context\",\n elements: [\n {\n type: \"mrkdwn\",\n text: `Severity: \\`${input.severity}\\` Owner: ${escapeSlack(meta.owner)}`,\n },\n ],\n },\n ],\n }),\n },\n { maxRedirects: 3 },\n );\n if (!res.ok) {\n throw new Error(\n `[notifications] Slack webhook ${new URL(url).origin} returned ${res.status}${\n (await readErrorSnippet(res)) || \"\"\n }`,\n );\n }\n },\n };\n}\n\nfunction createEmailChannel(): NotificationChannel {\n return {\n name: \"email\",\n async deliver(input) {\n const recipients = notificationEmailRecipients(input.metadata);\n if (recipients.length === 0) return;\n const subject =\n typeof input.metadata?.emailSubject === \"string\" &&\n input.metadata.emailSubject.trim()\n ? input.metadata.emailSubject.trim()\n : `[${input.severity}] ${input.title}`;\n const metadata = scrubEmailMetadata(input.metadata);\n const metadataText = metadata\n ? `\\n\\nMetadata:\\n${JSON.stringify(metadata, null, 2)}`\n : \"\";\n const text = `${input.title}\\n\\n${input.body ?? \"\"}${metadataText}`;\n const html = [\n `<p><strong>${escapeHtml(input.title)}</strong></p>`,\n input.body ? `<p>${escapeHtml(input.body)}</p>` : \"\",\n metadata\n ? `<pre>${escapeHtml(JSON.stringify(metadata, null, 2))}</pre>`\n : \"\",\n ].join(\"\");\n\n await Promise.all(\n recipients.map((to) =>\n sendEmail({\n to,\n subject,\n text,\n html,\n }),\n ),\n );\n },\n };\n}\n\nasync function resolveWebhookRequest(\n urlTemplate: string,\n authTemplate: string | undefined,\n owner: string,\n label: string,\n): Promise<{ url: string; headers: Record<string, string> }> {\n // Resolve `${keys.NAME}` references against the owner's user-scope secrets.\n // Missing keys throw — the error surfaces in logs and the channel is marked\n // un-delivered, but other channels still run.\n const { resolved: url } = await resolveKeyReferences(\n urlTemplate,\n \"user\",\n owner,\n );\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (authTemplate) {\n const { resolved: auth } = await resolveKeyReferences(\n authTemplate,\n \"user\",\n owner,\n );\n headers.Authorization = auth;\n }\n\n // If the user set an allowlist on a referenced key, enforce it here —\n // origin-level check, same rule the automations fetch-tool applies.\n const keyNames = Array.from(\n new Set(\n Array.from(urlTemplate.matchAll(/\\$\\{keys\\.([A-Za-z0-9_-]+)\\}/g), (m) =>\n String(m[1]),\n ),\n ),\n );\n const allowlists = await Promise.all(\n keyNames.map((name) => getKeyAllowlist(name, \"user\", owner)),\n );\n keyNames.forEach((name, i) => {\n if (!validateUrlAllowlist(url, allowlists[i])) {\n throw new Error(\n `[notifications] ${label} URL ${new URL(url).origin} is not in the allowlist for key \"${name}\"`,\n );\n }\n });\n return { url, headers };\n}\n\nfunction notificationEmailRecipients(\n metadata: Record<string, unknown> | undefined,\n): string[] {\n const fromMetadata = stringArray(metadata?.emailRecipients);\n const fromEnv = commaList(process.env.NOTIFICATIONS_EMAIL_RECIPIENTS);\n const seen = new Set<string>();\n const recipients: string[] = [];\n for (const raw of [...fromMetadata, ...fromEnv]) {\n const email = raw.trim().toLowerCase();\n if (!email || seen.has(email)) continue;\n seen.add(email);\n recipients.push(email);\n }\n return recipients;\n}\n\nfunction stringArray(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return value.filter((item): item is string => typeof item === \"string\");\n}\n\nfunction commaList(value: string | undefined): string[] {\n return value\n ? value\n .split(\",\")\n .map((item) => item.trim())\n .filter(Boolean)\n : [];\n}\n\nfunction scrubEmailMetadata(\n metadata: Record<string, unknown> | undefined,\n): Record<string, unknown> | null {\n if (!metadata) return null;\n const entries = Object.entries(metadata).filter(\n ([key]) => key !== \"emailRecipients\" && key !== \"emailSubject\",\n );\n return entries.length ? Object.fromEntries(entries) : null;\n}\n\nfunction slackText(\n severity: string,\n title: string,\n body: string | undefined,\n): string {\n return `[${severity}] ${title}${body ? `\\n${body}` : \"\"}`;\n}\n\nfunction escapeSlack(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\");\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\");\n}\n\n/**\n * Read up to ~1 KB from the body for error context. Streams chunks so a\n * misbehaving endpoint returning a large error page doesn't pin that whole\n * payload in memory per failed webhook.\n */\nasync function readErrorSnippet(res: Response): Promise<string> {\n const reader = res.body?.getReader();\n if (!reader) return \"\";\n const decoder = new TextDecoder();\n const MAX = 1024;\n let buf = \"\";\n try {\n while (buf.length < MAX) {\n const { value, done } = await reader.read();\n if (done) break;\n buf += decoder.decode(value, { stream: true });\n }\n reader.cancel().catch(() => {});\n } catch {\n return \"\";\n }\n if (!buf) return \"\";\n return `: ${buf.slice(0, 200)}`;\n}\n"]}
1
+ {"version":3,"file":"channels.js","sourceRoot":"","sources":["../../src/notifications/channels.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Built-in notification channels.\n *\n * Webhook and Slack are always registered. Delivery uses (in order):\n * 1. per-notification `metadata.webhookUrl` / `metadata.slackWebhookUrl`\n * 2. the matching `NOTIFICATIONS_*` env URL (workspace default)\n * If neither is set, that channel no-ops for the emission.\n *\n * Extra channels can be registered at any time via\n * `registerNotificationChannel()` from a server plugin.\n *\n * NOTIFICATIONS_WEBHOOK_URL → default POST JSON URL when metadata omits one.\n * Supports `${keys.NAME}` substitution — the raw\n * value never enters the agent context.\n * NOTIFICATIONS_WEBHOOK_AUTH → optional `Authorization` header value (also\n * supports `${keys.NAME}`).\n * NOTIFICATIONS_SLACK_WEBHOOK_URL\n * → default Slack incoming webhook when metadata\n * omits `slackWebhookUrl`.\n * Supports `${keys.NAME}` substitution.\n * NOTIFICATIONS_EMAIL_CHANNEL=1\n * → enable the built-in email channel for\n * per-notification recipients.\n * NOTIFICATIONS_EMAIL_RECIPIENTS\n * → comma-separated fallback recipients for email\n * notifications that do not pass\n * `metadata.emailRecipients`.\n */\n\nimport { ssrfSafeFetch } from \"../extensions/url-safety.js\";\nimport {\n resolveKeyReferences,\n validateUrlAllowlist,\n getKeyAllowlist,\n} from \"../secrets/substitution.js\";\nimport { sendEmail } from \"../server/email.js\";\nimport { registerNotificationChannel } from \"./registry.js\";\nimport type { NotificationChannel, NotificationInput } from \"./types.js\";\n\nlet _registered = false;\n\nexport function registerBuiltinNotificationChannels(): void {\n if (_registered) return;\n _registered = true;\n\n registerNotificationChannel(\n createWebhookChannel(process.env.NOTIFICATIONS_WEBHOOK_URL),\n );\n registerNotificationChannel(\n createSlackWebhookChannel(process.env.NOTIFICATIONS_SLACK_WEBHOOK_URL),\n );\n // Email is always registered so per-notification `metadata.emailRecipients`\n // work without a workspace-wide env flag (same pattern as webhook/slack).\n registerNotificationChannel(createEmailChannel());\n}\n\nfunction createWebhookChannel(\n envUrlTemplate: string | undefined,\n): NotificationChannel {\n const authTemplate = process.env.NOTIFICATIONS_WEBHOOK_AUTH;\n return {\n name: \"webhook\",\n async deliver(input, meta) {\n const overrideUrlTemplate = deliveryMetadataString(\n input.metadata,\n \"webhookUrl\",\n );\n const urlTemplate =\n overrideUrlTemplate ??\n metadataString(input.metadata, \"webhookUrl\") ??\n envUrlTemplate?.trim();\n // No-op when neither a per-notification nor workspace URL is set —\n // mirrors email's empty-recipients behavior so notify-all stays quiet.\n if (!urlTemplate) return false;\n const { url, headers } = await resolveWebhookRequest(\n urlTemplate,\n overrideUrlTemplate ? undefined : authTemplate,\n meta.owner,\n \"webhook\",\n );\n const res = await ssrfSafeFetch(\n url,\n {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n severity: input.severity,\n title: input.title,\n body: input.body,\n metadata: scrubDeliveryMetadata(input.metadata),\n owner: meta.owner,\n emittedAt: new Date().toISOString(),\n }),\n },\n { maxRedirects: 3 },\n );\n if (!res.ok) {\n throw new Error(\n `[notifications] webhook ${new URL(url).origin} returned ${res.status}${\n (await readErrorSnippet(res)) || \"\"\n }`,\n );\n }\n },\n };\n}\n\nfunction createSlackWebhookChannel(\n envUrlTemplate: string | undefined,\n): NotificationChannel {\n const authTemplate = process.env.NOTIFICATIONS_SLACK_WEBHOOK_AUTH;\n return {\n name: \"slack\",\n async deliver(input, meta) {\n const overrideUrlTemplate = deliveryMetadataString(\n input.metadata,\n \"slackWebhookUrl\",\n );\n const urlTemplate =\n overrideUrlTemplate ??\n metadataString(input.metadata, \"slackWebhookUrl\") ??\n envUrlTemplate?.trim();\n if (!urlTemplate) return false;\n const { url, headers } = await resolveWebhookRequest(\n urlTemplate,\n overrideUrlTemplate ? undefined : authTemplate,\n meta.owner,\n \"Slack webhook\",\n );\n const res = await ssrfSafeFetch(\n url,\n {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n text: slackText(input.severity, input.title, input.body),\n blocks: [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: `*${escapeSlack(input.title)}*`,\n },\n },\n ...(input.body\n ? [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: escapeSlack(input.body),\n },\n },\n ]\n : []),\n {\n type: \"context\",\n elements: [\n {\n type: \"mrkdwn\",\n text: `Severity: \\`${input.severity}\\` Owner: ${escapeSlack(meta.owner)}`,\n },\n ],\n },\n ],\n }),\n },\n { maxRedirects: 3 },\n );\n if (!res.ok) {\n throw new Error(\n `[notifications] Slack webhook ${new URL(url).origin} returned ${res.status}${\n (await readErrorSnippet(res)) || \"\"\n }`,\n );\n }\n },\n };\n}\n\nfunction createEmailChannel(): NotificationChannel {\n return {\n name: \"email\",\n async deliver(input) {\n const recipients = notificationEmailRecipients(input.metadata);\n if (recipients.length === 0) return false;\n const subject =\n typeof input.metadata?.emailSubject === \"string\" &&\n input.metadata.emailSubject.trim()\n ? input.metadata.emailSubject.trim()\n : `[${input.severity}] ${input.title}`;\n const metadata = scrubEmailMetadata(input.metadata);\n const metadataText = metadata\n ? `\\n\\nMetadata:\\n${JSON.stringify(metadata, null, 2)}`\n : \"\";\n const text = `${input.title}\\n\\n${input.body ?? \"\"}${metadataText}`;\n const html = [\n `<p><strong>${escapeHtml(input.title)}</strong></p>`,\n input.body ? `<p>${escapeHtml(input.body)}</p>` : \"\",\n metadata\n ? `<pre>${escapeHtml(JSON.stringify(metadata, null, 2))}</pre>`\n : \"\",\n ].join(\"\");\n\n await Promise.all(\n recipients.map((to) =>\n sendEmail({\n to,\n subject,\n text,\n html,\n }),\n ),\n );\n },\n };\n}\n\nasync function resolveWebhookRequest(\n urlTemplate: string,\n authTemplate: string | undefined,\n owner: string,\n label: string,\n): Promise<{ url: string; headers: Record<string, string> }> {\n // Resolve `${keys.NAME}` references against the owner's user-scope secrets.\n // Missing keys throw — the error surfaces in logs and the channel is marked\n // un-delivered, but other channels still run.\n const { resolved: url } = await resolveKeyReferences(\n urlTemplate,\n \"user\",\n owner,\n );\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (authTemplate) {\n const { resolved: auth } = await resolveKeyReferences(\n authTemplate,\n \"user\",\n owner,\n );\n headers.Authorization = auth;\n }\n\n // If the user set an allowlist on a referenced key, enforce it here —\n // origin-level check, same rule the automations fetch-tool applies.\n const keyNames = Array.from(\n new Set(\n Array.from(urlTemplate.matchAll(/\\$\\{keys\\.([A-Za-z0-9_-]+)\\}/g), (m) =>\n String(m[1]),\n ),\n ),\n );\n const allowlists = await Promise.all(\n keyNames.map((name) => getKeyAllowlist(name, \"user\", owner)),\n );\n keyNames.forEach((name, i) => {\n if (!validateUrlAllowlist(url, allowlists[i])) {\n throw new Error(\n `[notifications] ${label} URL ${new URL(url).origin} is not in the allowlist for key \"${name}\"`,\n );\n }\n });\n return { url, headers };\n}\n\nfunction metadataString(\n metadata: NotificationInput[\"metadata\"],\n key: string,\n): string | undefined {\n const value = metadata?.[key];\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n return trimmed || undefined;\n}\n\nfunction deliveryMetadataString(\n metadata: NotificationInput[\"metadata\"],\n key: string,\n): string | undefined {\n const delivery = metadata?.delivery;\n if (!delivery || typeof delivery !== \"object\" || Array.isArray(delivery)) {\n return undefined;\n }\n const value = (delivery as Record<string, unknown>)[key];\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n return trimmed || undefined;\n}\n\nfunction scrubDeliveryMetadata(\n metadata: Record<string, unknown> | undefined,\n): Record<string, unknown> | undefined {\n if (!metadata) return undefined;\n const entries = Object.entries(metadata).filter(\n ([key]) =>\n key !== \"delivery\" && key !== \"webhookUrl\" && key !== \"slackWebhookUrl\",\n );\n return entries.length ? Object.fromEntries(entries) : undefined;\n}\n\nfunction notificationEmailRecipients(\n metadata: Record<string, unknown> | undefined,\n): string[] {\n const fromMetadata = stringArray(metadata?.emailRecipients);\n const fromEnv = commaList(process.env.NOTIFICATIONS_EMAIL_RECIPIENTS);\n const seen = new Set<string>();\n const recipients: string[] = [];\n for (const raw of [...fromMetadata, ...fromEnv]) {\n const email = raw.trim().toLowerCase();\n if (!email || seen.has(email)) continue;\n seen.add(email);\n recipients.push(email);\n }\n return recipients;\n}\n\nfunction stringArray(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return value.filter((item): item is string => typeof item === \"string\");\n}\n\nfunction commaList(value: string | undefined): string[] {\n return value\n ? value\n .split(\",\")\n .map((item) => item.trim())\n .filter(Boolean)\n : [];\n}\n\nfunction scrubEmailMetadata(\n metadata: Record<string, unknown> | undefined,\n): Record<string, unknown> | null {\n if (!metadata) return null;\n const entries = Object.entries(metadata).filter(\n ([key]) =>\n key !== \"emailRecipients\" &&\n key !== \"emailSubject\" &&\n key !== \"webhookUrl\" &&\n key !== \"slackWebhookUrl\",\n );\n return entries.length ? Object.fromEntries(entries) : null;\n}\n\nfunction slackText(\n severity: string,\n title: string,\n body: string | undefined,\n): string {\n return `[${severity}] ${title}${body ? `\\n${body}` : \"\"}`;\n}\n\nfunction escapeSlack(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\");\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\");\n}\n\n/**\n * Read up to ~1 KB from the body for error context. Streams chunks so a\n * misbehaving endpoint returning a large error page doesn't pin that whole\n * payload in memory per failed webhook.\n */\nasync function readErrorSnippet(res: Response): Promise<string> {\n const reader = res.body?.getReader();\n if (!reader) return \"\";\n const decoder = new TextDecoder();\n const MAX = 1024;\n let buf = \"\";\n try {\n while (buf.length < MAX) {\n const { value, done } = await reader.read();\n if (done) break;\n buf += decoder.decode(value, { stream: true });\n }\n reader.cancel().catch(() => {});\n } catch {\n return \"\";\n }\n if (!buf) return \"\";\n return `: ${buf.slice(0, 200)}`;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/notifications/registry.ts"],"names":[],"mappings":"AAOA,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAClB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,0BAA0B;IACzC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAiCD,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,mBAAmB,GAC3B,IAAI,CAUN;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED,wBAAgB,wBAAwB,IAAI,MAAM,EAAE,CAEnD;AAgBD,wBAAsB,MAAM,CAC1B,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAEnC;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,0BAA0B,CAAC,CAoFrC;AASD,mDAAmD;AACnD,wBAAgB,2BAA2B,IAAI,IAAI,CAElD;AAED,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,wBAAwB,EACxB,kBAAkB,EAClB,WAAW,GACZ,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/notifications/registry.ts"],"names":[],"mappings":"AAOA,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAClB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,0BAA0B;IACzC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAiCD,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,mBAAmB,GAC3B,IAAI,CAUN;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED,wBAAgB,wBAAwB,IAAI,MAAM,EAAE,CAEnD;AAgBD,wBAAsB,MAAM,CAC1B,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAEnC;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,0BAA0B,CAAC,CAuFrC;AAoBD,mDAAmD;AACnD,wBAAgB,2BAA2B,IAAI,IAAI,CAElD;AAED,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,wBAAwB,EACxB,kBAAkB,EAClB,WAAW,GACZ,MAAM,YAAY,CAAC"}
@@ -70,6 +70,7 @@ export async function notifyWithDelivery(input, meta) {
70
70
  body: truncate(input.body, MAX_BODY_LEN),
71
71
  };
72
72
  const channels = selectChannels(input.channels);
73
+ const storedMetadata = scrubStoredMetadata(input.metadata);
73
74
  // The inbox channel is always included unless explicitly excluded.
74
75
  const runInbox = !input.channels || input.channels.includes("inbox");
75
76
  const delivered = [];
@@ -83,7 +84,7 @@ export async function notifyWithDelivery(input, meta) {
83
84
  severity: input.severity,
84
85
  title: input.title,
85
86
  body: input.body,
86
- metadata: input.metadata,
87
+ metadata: storedMetadata,
87
88
  deliveredChannels: ["inbox"],
88
89
  });
89
90
  delivered.push("inbox");
@@ -94,12 +95,16 @@ export async function notifyWithDelivery(input, meta) {
94
95
  }
95
96
  // Await every channel so a 500-ing webhook doesn't end up in `delivered`.
96
97
  const results = await Promise.allSettled(channels.map(async (channel) => {
97
- await channel.deliver(input, meta);
98
+ const delivered = await channel.deliver(input, meta);
99
+ // Explicit `false` means the channel skipped (no URL / recipients).
100
+ if (delivered === false)
101
+ return null;
98
102
  return channel.name;
99
103
  }));
100
104
  results.forEach((r, i) => {
101
105
  if (r.status === "fulfilled") {
102
- delivered.push(r.value);
106
+ if (r.value)
107
+ delivered.push(r.value);
103
108
  }
104
109
  else {
105
110
  console.error(`[notifications] channel "${channels[i].name}" failed:`, r.reason);
@@ -134,6 +139,12 @@ export async function notifyWithDelivery(input, meta) {
134
139
  }
135
140
  return { notification: stored, deliveredChannels: delivered };
136
141
  }
142
+ function scrubStoredMetadata(metadata) {
143
+ if (!metadata)
144
+ return undefined;
145
+ const entries = Object.entries(metadata).filter(([key]) => key !== "delivery" && key !== "webhookUrl" && key !== "slackWebhookUrl");
146
+ return entries.length ? Object.fromEntries(entries) : undefined;
147
+ }
137
148
  function selectChannels(allowlist) {
138
149
  const registry = getRegistry();
139
150
  const all = Array.from(registry.values());
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/notifications/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\n\nimport { emit as emitBusEvent } from \"../event-bus/bus.js\";\nimport { registerEvent } from \"../event-bus/registry.js\";\nimport type { EventDefinition } from \"../event-bus/types.js\";\nimport { truncate } from \"../shared/truncate.js\";\nimport { insertNotification, updateDeliveredChannels } from \"./store.js\";\nimport {\n NOTIFICATION_SEVERITIES,\n type NotificationChannel,\n type NotificationInput,\n type NotificationMeta,\n type Notification,\n} from \"./types.js\";\n\nexport interface NotificationDeliveryResult {\n notification?: Notification;\n deliveredChannels: string[];\n}\n\nregisterEvent({\n name: \"notification.sent\",\n description:\n \"Fires after notify() delivers to at least one channel. Automations can chain off this — e.g. fan critical notifications to Slack.\",\n payloadSchema: z.object({\n notificationId: z.string().optional(),\n severity: z.enum(NOTIFICATION_SEVERITIES),\n title: z.string(),\n body: z.string().optional(),\n deliveredChannels: z.array(z.string()),\n }) as unknown as EventDefinition[\"payloadSchema\"],\n example: {\n notificationId: \"ntf_abc\",\n severity: \"critical\",\n title: \"Payment failed\",\n body: \"Card ending 4242 declined\",\n deliveredChannels: [\"inbox\", \"webhook\"],\n },\n});\n\nconst REGISTRY_KEY = Symbol.for(\"@agent-native/core/notifications.registry\");\ninterface GlobalWithRegistry {\n [REGISTRY_KEY]?: Map<string, NotificationChannel>;\n}\n\nfunction getRegistry(): Map<string, NotificationChannel> {\n const g = globalThis as unknown as GlobalWithRegistry;\n if (!g[REGISTRY_KEY]) g[REGISTRY_KEY] = new Map();\n return g[REGISTRY_KEY];\n}\n\nexport function registerNotificationChannel(\n channel: NotificationChannel,\n): void {\n if (!channel?.name) {\n throw new Error(\"registerNotificationChannel: channel.name is required\");\n }\n if (typeof channel.deliver !== \"function\") {\n throw new Error(\n \"registerNotificationChannel: channel.deliver must be a function\",\n );\n }\n getRegistry().set(channel.name, channel);\n}\n\nexport function unregisterNotificationChannel(name: string): boolean {\n return getRegistry().delete(name);\n}\n\nexport function listNotificationChannels(): string[] {\n return Array.from(getRegistry().keys());\n}\n\n/**\n * Deliver a notification.\n *\n * The `inbox` channel always persists a row that drives the in-app UI\n * (bell + toast). Additional channels (webhook, custom) run in parallel,\n * best-effort. Returns the stored Notification when `inbox` ran, otherwise\n * `undefined`.\n *\n * Also emits `notification.sent` on the event bus so automations can react\n * to notifications (e.g. \"when a critical notification fires, also page me\").\n */\nconst MAX_TITLE_LEN = 100;\nconst MAX_BODY_LEN = 2000;\n\nexport async function notify(\n input: NotificationInput,\n meta: NotificationMeta,\n): Promise<Notification | undefined> {\n return (await notifyWithDelivery(input, meta)).notification;\n}\n\nexport async function notifyWithDelivery(\n input: NotificationInput,\n meta: NotificationMeta,\n): Promise<NotificationDeliveryResult> {\n if (!meta?.owner) {\n throw new Error(\"notify: meta.owner is required\");\n }\n input = {\n ...input,\n title: truncate(input.title, MAX_TITLE_LEN),\n body: truncate(input.body, MAX_BODY_LEN),\n };\n const channels = selectChannels(input.channels);\n\n // The inbox channel is always included unless explicitly excluded.\n const runInbox = !input.channels || input.channels.includes(\"inbox\");\n const delivered: string[] = [];\n let stored: Notification | undefined;\n\n if (runInbox) {\n try {\n // Stored with just \"inbox\" first; the real delivered list is written\n // after fan-out so a failing webhook doesn't claim it was delivered.\n stored = await insertNotification({\n owner: meta.owner,\n severity: input.severity,\n title: input.title,\n body: input.body,\n metadata: input.metadata,\n deliveredChannels: [\"inbox\"],\n });\n delivered.push(\"inbox\");\n } catch (err) {\n console.error(\"[notifications] inbox persist failed:\", err);\n }\n }\n\n // Await every channel so a 500-ing webhook doesn't end up in `delivered`.\n const results = await Promise.allSettled(\n channels.map(async (channel) => {\n await channel.deliver(input, meta);\n return channel.name;\n }),\n );\n results.forEach((r, i) => {\n if (r.status === \"fulfilled\") {\n delivered.push(r.value);\n } else {\n console.error(\n `[notifications] channel \"${channels[i].name}\" failed:`,\n r.reason,\n );\n }\n });\n\n const hasExtraChannel = delivered.some((c) => c !== \"inbox\");\n if (stored && hasExtraChannel) {\n try {\n await updateDeliveredChannels(stored.id, delivered);\n stored = { ...stored, deliveredChannels: delivered };\n } catch (err) {\n console.error(\"[notifications] delivered-channel update failed:\", err);\n }\n }\n\n // Only emit when at least one channel delivered — an emission with an\n // empty delivery list (and likely a null notificationId) would mislead\n // any automation chaining off this event.\n if (delivered.length > 0) {\n try {\n emitBusEvent(\n \"notification.sent\",\n {\n notificationId: stored?.id,\n severity: input.severity,\n title: input.title,\n body: input.body,\n deliveredChannels: delivered,\n },\n { owner: meta.owner },\n );\n } catch {\n // best-effort\n }\n }\n\n return { notification: stored, deliveredChannels: delivered };\n}\n\nfunction selectChannels(allowlist?: string[]): NotificationChannel[] {\n const registry = getRegistry();\n const all = Array.from(registry.values());\n if (!allowlist) return all;\n return all.filter((c) => allowlist.includes(c.name));\n}\n\n/** Test helper — drops all registered channels. */\nexport function __resetNotificationChannels(): void {\n getRegistry().clear();\n}\n\nexport {\n listNotifications,\n markNotificationRead,\n markAllNotificationsRead,\n deleteNotification,\n countUnread,\n} from \"./store.js\";\n"]}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/notifications/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\n\nimport { emit as emitBusEvent } from \"../event-bus/bus.js\";\nimport { registerEvent } from \"../event-bus/registry.js\";\nimport type { EventDefinition } from \"../event-bus/types.js\";\nimport { truncate } from \"../shared/truncate.js\";\nimport { insertNotification, updateDeliveredChannels } from \"./store.js\";\nimport {\n NOTIFICATION_SEVERITIES,\n type NotificationChannel,\n type NotificationInput,\n type NotificationMeta,\n type Notification,\n} from \"./types.js\";\n\nexport interface NotificationDeliveryResult {\n notification?: Notification;\n deliveredChannels: string[];\n}\n\nregisterEvent({\n name: \"notification.sent\",\n description:\n \"Fires after notify() delivers to at least one channel. Automations can chain off this — e.g. fan critical notifications to Slack.\",\n payloadSchema: z.object({\n notificationId: z.string().optional(),\n severity: z.enum(NOTIFICATION_SEVERITIES),\n title: z.string(),\n body: z.string().optional(),\n deliveredChannels: z.array(z.string()),\n }) as unknown as EventDefinition[\"payloadSchema\"],\n example: {\n notificationId: \"ntf_abc\",\n severity: \"critical\",\n title: \"Payment failed\",\n body: \"Card ending 4242 declined\",\n deliveredChannels: [\"inbox\", \"webhook\"],\n },\n});\n\nconst REGISTRY_KEY = Symbol.for(\"@agent-native/core/notifications.registry\");\ninterface GlobalWithRegistry {\n [REGISTRY_KEY]?: Map<string, NotificationChannel>;\n}\n\nfunction getRegistry(): Map<string, NotificationChannel> {\n const g = globalThis as unknown as GlobalWithRegistry;\n if (!g[REGISTRY_KEY]) g[REGISTRY_KEY] = new Map();\n return g[REGISTRY_KEY];\n}\n\nexport function registerNotificationChannel(\n channel: NotificationChannel,\n): void {\n if (!channel?.name) {\n throw new Error(\"registerNotificationChannel: channel.name is required\");\n }\n if (typeof channel.deliver !== \"function\") {\n throw new Error(\n \"registerNotificationChannel: channel.deliver must be a function\",\n );\n }\n getRegistry().set(channel.name, channel);\n}\n\nexport function unregisterNotificationChannel(name: string): boolean {\n return getRegistry().delete(name);\n}\n\nexport function listNotificationChannels(): string[] {\n return Array.from(getRegistry().keys());\n}\n\n/**\n * Deliver a notification.\n *\n * The `inbox` channel always persists a row that drives the in-app UI\n * (bell + toast). Additional channels (webhook, custom) run in parallel,\n * best-effort. Returns the stored Notification when `inbox` ran, otherwise\n * `undefined`.\n *\n * Also emits `notification.sent` on the event bus so automations can react\n * to notifications (e.g. \"when a critical notification fires, also page me\").\n */\nconst MAX_TITLE_LEN = 100;\nconst MAX_BODY_LEN = 2000;\n\nexport async function notify(\n input: NotificationInput,\n meta: NotificationMeta,\n): Promise<Notification | undefined> {\n return (await notifyWithDelivery(input, meta)).notification;\n}\n\nexport async function notifyWithDelivery(\n input: NotificationInput,\n meta: NotificationMeta,\n): Promise<NotificationDeliveryResult> {\n if (!meta?.owner) {\n throw new Error(\"notify: meta.owner is required\");\n }\n input = {\n ...input,\n title: truncate(input.title, MAX_TITLE_LEN),\n body: truncate(input.body, MAX_BODY_LEN),\n };\n const channels = selectChannels(input.channels);\n const storedMetadata = scrubStoredMetadata(input.metadata);\n\n // The inbox channel is always included unless explicitly excluded.\n const runInbox = !input.channels || input.channels.includes(\"inbox\");\n const delivered: string[] = [];\n let stored: Notification | undefined;\n\n if (runInbox) {\n try {\n // Stored with just \"inbox\" first; the real delivered list is written\n // after fan-out so a failing webhook doesn't claim it was delivered.\n stored = await insertNotification({\n owner: meta.owner,\n severity: input.severity,\n title: input.title,\n body: input.body,\n metadata: storedMetadata,\n deliveredChannels: [\"inbox\"],\n });\n delivered.push(\"inbox\");\n } catch (err) {\n console.error(\"[notifications] inbox persist failed:\", err);\n }\n }\n\n // Await every channel so a 500-ing webhook doesn't end up in `delivered`.\n const results = await Promise.allSettled(\n channels.map(async (channel) => {\n const delivered = await channel.deliver(input, meta);\n // Explicit `false` means the channel skipped (no URL / recipients).\n if (delivered === false) return null;\n return channel.name;\n }),\n );\n results.forEach((r, i) => {\n if (r.status === \"fulfilled\") {\n if (r.value) delivered.push(r.value);\n } else {\n console.error(\n `[notifications] channel \"${channels[i].name}\" failed:`,\n r.reason,\n );\n }\n });\n\n const hasExtraChannel = delivered.some((c) => c !== \"inbox\");\n if (stored && hasExtraChannel) {\n try {\n await updateDeliveredChannels(stored.id, delivered);\n stored = { ...stored, deliveredChannels: delivered };\n } catch (err) {\n console.error(\"[notifications] delivered-channel update failed:\", err);\n }\n }\n\n // Only emit when at least one channel delivered — an emission with an\n // empty delivery list (and likely a null notificationId) would mislead\n // any automation chaining off this event.\n if (delivered.length > 0) {\n try {\n emitBusEvent(\n \"notification.sent\",\n {\n notificationId: stored?.id,\n severity: input.severity,\n title: input.title,\n body: input.body,\n deliveredChannels: delivered,\n },\n { owner: meta.owner },\n );\n } catch {\n // best-effort\n }\n }\n\n return { notification: stored, deliveredChannels: delivered };\n}\n\nfunction scrubStoredMetadata(\n metadata: Record<string, unknown> | undefined,\n): Record<string, unknown> | undefined {\n if (!metadata) return undefined;\n const entries = Object.entries(metadata).filter(\n ([key]) =>\n key !== \"delivery\" && key !== \"webhookUrl\" && key !== \"slackWebhookUrl\",\n );\n return entries.length ? Object.fromEntries(entries) : undefined;\n}\n\nfunction selectChannels(allowlist?: string[]): NotificationChannel[] {\n const registry = getRegistry();\n const all = Array.from(registry.values());\n if (!allowlist) return all;\n return all.filter((c) => allowlist.includes(c.name));\n}\n\n/** Test helper — drops all registered channels. */\nexport function __resetNotificationChannels(): void {\n getRegistry().clear();\n}\n\nexport {\n listNotifications,\n markNotificationRead,\n markAllNotificationsRead,\n deleteNotification,\n countUnread,\n} from \"./store.js\";\n"]}
@@ -37,7 +37,10 @@ export interface NotificationChannel {
37
37
  /**
38
38
  * Deliver the notification. Must be best-effort — throwing will be logged
39
39
  * but will not block other channels from running.
40
+ *
41
+ * Return `false` to skip (e.g. no URL / no recipients configured) so the
42
+ * channel is not recorded in `deliveredChannels`.
40
43
  */
41
- deliver(input: NotificationInput, meta: NotificationMeta): void | Promise<void>;
44
+ deliver(input: NotificationInput, meta: NotificationMeta): void | boolean | Promise<void | boolean>;
42
45
  }
43
46
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/notifications/types.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,uBAAuB,YAAI,MAAM,EAAE,SAAS,EAAE,UAAU,CAAU,CAAC;AAChF,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5E,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,oBAAoB;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,4EAA4E;IAC5E,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,OAAO,CACL,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,gBAAgB,GACrB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/notifications/types.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,uBAAuB,YAAI,MAAM,EAAE,SAAS,EAAE,UAAU,CAAU,CAAC;AAChF,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5E,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,oBAAoB;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,4EAA4E;IAC5E,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,OAAO,CACL,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,gBAAgB,GACrB,IAAI,GAAG,OAAO,GAAG,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,CAAC;CAC7C"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/notifications/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,CAAU,CAAC","sourcesContent":["export const NOTIFICATION_SEVERITIES = [\"info\", \"warning\", \"critical\"] as const;\nexport type NotificationSeverity = (typeof NOTIFICATION_SEVERITIES)[number];\n\nexport interface Notification {\n id: string;\n owner: string;\n severity: NotificationSeverity;\n title: string;\n body?: string;\n /** Arbitrary JSON metadata — URLs, entity ids, the agent turn that produced it. */\n metadata?: Record<string, unknown>;\n /** ISO timestamp */\n createdAt: string;\n /** ISO timestamp — null while unread */\n readAt: string | null;\n /** Channels that delivered (or attempted delivery of) this notification. */\n deliveredChannels: string[];\n}\n\nexport interface NotificationInput {\n severity: NotificationSeverity;\n title: string;\n body?: string;\n metadata?: Record<string, unknown>;\n /**\n * Explicit channel allowlist for this emission. When omitted, every\n * registered channel runs. Use to scope a single `notify()` call to a\n * subset (e.g. `[\"inbox\"]` to skip webhooks).\n */\n channels?: string[];\n}\n\nexport interface NotificationMeta {\n /** Owner email — scopes the notification in the inbox. */\n owner: string;\n}\n\nexport interface NotificationChannel {\n /** Unique channel name, e.g. `\"inbox\"`, `\"webhook\"`, `\"slack\"`. */\n name: string;\n /**\n * Deliver the notification. Must be best-effort — throwing will be logged\n * but will not block other channels from running.\n */\n deliver(\n input: NotificationInput,\n meta: NotificationMeta,\n ): void | Promise<void>;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/notifications/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,CAAU,CAAC","sourcesContent":["export const NOTIFICATION_SEVERITIES = [\"info\", \"warning\", \"critical\"] as const;\nexport type NotificationSeverity = (typeof NOTIFICATION_SEVERITIES)[number];\n\nexport interface Notification {\n id: string;\n owner: string;\n severity: NotificationSeverity;\n title: string;\n body?: string;\n /** Arbitrary JSON metadata — URLs, entity ids, the agent turn that produced it. */\n metadata?: Record<string, unknown>;\n /** ISO timestamp */\n createdAt: string;\n /** ISO timestamp — null while unread */\n readAt: string | null;\n /** Channels that delivered (or attempted delivery of) this notification. */\n deliveredChannels: string[];\n}\n\nexport interface NotificationInput {\n severity: NotificationSeverity;\n title: string;\n body?: string;\n metadata?: Record<string, unknown>;\n /**\n * Explicit channel allowlist for this emission. When omitted, every\n * registered channel runs. Use to scope a single `notify()` call to a\n * subset (e.g. `[\"inbox\"]` to skip webhooks).\n */\n channels?: string[];\n}\n\nexport interface NotificationMeta {\n /** Owner email — scopes the notification in the inbox. */\n owner: string;\n}\n\nexport interface NotificationChannel {\n /** Unique channel name, e.g. `\"inbox\"`, `\"webhook\"`, `\"slack\"`. */\n name: string;\n /**\n * Deliver the notification. Must be best-effort — throwing will be logged\n * but will not block other channels from running.\n *\n * Return `false` to skip (e.g. no URL / no recipients configured) so the\n * channel is not recorded in `deliveredChannels`.\n */\n deliver(\n input: NotificationInput,\n meta: NotificationMeta,\n ): void | boolean | Promise<void | boolean>;\n}\n"]}
@@ -58,6 +58,7 @@ export declare function handleDeleteResource(event: any): Promise<{
58
58
  /** POST /_agent-native/resources/upload — upload a file as a resource */
59
59
  export declare function handleUploadResource(event: any): Promise<import("./store.js").Resource | {
60
60
  error: string;
61
+ storageSetupRequired?: undefined;
61
62
  } | {
62
63
  id: string;
63
64
  path: string;
@@ -76,6 +77,10 @@ export declare function handleUploadResource(event: any): Promise<import("./stor
76
77
  error?: undefined;
77
78
  url: string;
78
79
  provider: string;
80
+ storageSetupRequired?: undefined;
81
+ } | {
82
+ error: string;
83
+ storageSetupRequired: boolean;
79
84
  }>;
80
85
  export {};
81
86
  //# sourceMappingURL=handlers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/resources/handlers.ts"],"names":[],"mappings":"AAkBA,OAAO,EAML,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,aAAa,EACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAcL,KAAK,YAAY,EAClB,MAAM,YAAY,CAAC;AAoEpB,UAAU,WAAW;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,QAAQ;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,OAAO,GAAG,cAAc,CAAC;IAC3D,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,eAAe,CAAC,EAAE,mBAAmB,CAAC;CACvC;AA2DD,oDAAoD;AACpD,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,GAAG;;GAmCnD;AAED,4DAA4D;AAC5D,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,GAAG;;GA8CrD;AAED,+EAA+E;AAC/E,wBAAsB,iCAAiC,CAAC,KAAK,EAAE,GAAG;;GAYjE;AAwED;;;yEAGyE;AACzE,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,GAAG;;GAoDjD;AAED,wDAAwD;AACxD,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,GAAG;;GAoCpD;AAED,qEAAqE;AACrE,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,GAAG;;GAuEpD;AAED,8DAA8D;AAC9D,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,GAAG;;;;;;GAgCpD;AAED,yEAAyE;AACzE,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,GAAG;;;;;;;;;;;;;;;;;;;;GAkFpD"}
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/resources/handlers.ts"],"names":[],"mappings":"AAkBA,OAAO,EAML,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,aAAa,EACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAcL,KAAK,YAAY,EAClB,MAAM,YAAY,CAAC;AAoEpB,UAAU,WAAW;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,QAAQ;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,OAAO,GAAG,cAAc,CAAC;IAC3D,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,eAAe,CAAC,EAAE,mBAAmB,CAAC;CACvC;AA2DD,oDAAoD;AACpD,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,GAAG;;GAmCnD;AAED,4DAA4D;AAC5D,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,GAAG;;GA8CrD;AAED,+EAA+E;AAC/E,wBAAsB,iCAAiC,CAAC,KAAK,EAAE,GAAG;;GAYjE;AAwED;;;yEAGyE;AACzE,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,GAAG;;GAoDjD;AAED,wDAAwD;AACxD,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,GAAG;;GAoCpD;AAED,qEAAqE;AACrE,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,GAAG;;GAuEpD;AAED,8DAA8D;AAC9D,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,GAAG;;;;;;GAgCpD;AAED,yEAAyE;AACzE,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;GAoFpD"}
@@ -447,10 +447,9 @@ export async function handleUploadResource(event) {
447
447
  await assertCanEditShared(event);
448
448
  }
449
449
  const owner = await resolveOwner(event, shared);
450
- // Prefer a registered file upload provider (e.g. Builder.io) for binary
451
- // assets so we get a real CDN URL instead of a base64 blob in SQL.
452
- // Text resources still live in SQL they're edited inline and benefit
453
- // from the resource store's metadata/search features.
450
+ // Binary assets must live in file storage so resource rows do not become
451
+ // base64 blobs in SQL. Text resources still live in SQL because they are
452
+ // edited inline and benefit from the resource store's metadata/search.
454
453
  const isText = mimeType.startsWith("text/") || mimeType === "application/json";
455
454
  if (!isText) {
456
455
  // Use the actual session user email for credential resolution — not `owner`,
@@ -474,11 +473,13 @@ export async function handleUploadResource(event) {
474
473
  setResponseStatus(event, 201);
475
474
  return { ...resource, url: uploaded.url, provider: uploaded.provider };
476
475
  }
476
+ setResponseStatus(event, 503);
477
+ return {
478
+ error: "File storage is not configured. Connect Builder.io or register an S3/R2/GCS file upload provider before uploading binary resources.",
479
+ storageSetupRequired: true,
480
+ };
477
481
  }
478
- // Fallback: store contents in SQL (base64 for binary, text as-is).
479
- const content = isText
480
- ? Buffer.from(filePart.data).toString("utf-8")
481
- : Buffer.from(filePart.data).toString("base64");
482
+ const content = Buffer.from(filePart.data).toString("utf-8");
482
483
  const resource = await resourcePut(owner, path, content, mimeType);
483
484
  setResponseStatus(event, 201);
484
485
  return resource;