@agent-native/core 0.134.1 → 0.135.0

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 (442) hide show
  1. package/corpus/README.md +3 -3
  2. package/corpus/core/CHANGELOG.md +107 -0
  3. package/corpus/core/package.json +3 -2
  4. package/corpus/core/src/automations/service.ts +41 -4
  5. package/corpus/core/src/cli/mcp-config-writers.ts +27 -21
  6. package/corpus/core/src/client/agent-page/AgentJobsTab.tsx +187 -85
  7. package/corpus/core/src/client/agent-page/AutomationDetailsDialog.tsx +208 -0
  8. package/corpus/core/src/client/agent-page/AutomationScheduleDialog.tsx +180 -0
  9. package/corpus/core/src/client/agent-page/TimezoneSelect.tsx +241 -0
  10. package/corpus/core/src/client/agent-page/use-jobs.ts +50 -10
  11. package/corpus/core/src/client/org/OrgSwitcher.tsx +26 -0
  12. package/corpus/core/src/client/settings/AccountSettingsCard.tsx +35 -31
  13. package/corpus/core/src/client/settings/SchedulingTimezoneField.tsx +71 -0
  14. package/corpus/core/src/client/settings/SettingsRow.tsx +113 -0
  15. package/corpus/core/src/client/settings/agent-settings-search.ts +2 -1
  16. package/corpus/core/src/client/settings/index.ts +6 -0
  17. package/corpus/core/src/integrations/identity.ts +2 -12
  18. package/corpus/core/src/jobs/actions/list-automation-runs.ts +33 -0
  19. package/corpus/core/src/jobs/actions/list-recurring-jobs.ts +27 -6
  20. package/corpus/core/src/jobs/actions/manage-recurring-job.ts +41 -9
  21. package/corpus/core/src/jobs/background-automation-runner.ts +88 -0
  22. package/corpus/core/src/jobs/cron.ts +52 -6
  23. package/corpus/core/src/jobs/frontmatter.ts +20 -0
  24. package/corpus/core/src/jobs/run-history.ts +242 -0
  25. package/corpus/core/src/jobs/scheduler.ts +73 -18
  26. package/corpus/core/src/jobs/tools.ts +54 -6
  27. package/corpus/core/src/localization/actions/get-localization-preference.ts +2 -2
  28. package/corpus/core/src/localization/actions/set-localization-preference.ts +49 -11
  29. package/corpus/core/src/localization/default-messages.ts +27 -0
  30. package/corpus/core/src/localization/shared.ts +39 -6
  31. package/corpus/core/src/localization/user-timezone.ts +36 -0
  32. package/corpus/core/src/org/app-roles-handlers.ts +1 -9
  33. package/corpus/core/src/org/index.ts +2 -0
  34. package/corpus/core/src/org/membership.ts +18 -0
  35. package/corpus/core/src/review/actions/create-review-comment.ts +4 -1
  36. package/corpus/core/src/review/actions/reply-review-comment.ts +4 -1
  37. package/corpus/core/src/review/index.ts +5 -0
  38. package/corpus/core/src/review/notifications.ts +163 -0
  39. package/corpus/core/src/review/types.ts +7 -0
  40. package/corpus/core/src/server/action-discovery.ts +4 -0
  41. package/corpus/core/src/server/activity-notifications.ts +174 -0
  42. package/corpus/core/src/server/app-name.ts +31 -0
  43. package/corpus/core/src/server/better-auth-instance.ts +4 -4
  44. package/corpus/core/src/server/email-templates.ts +70 -14
  45. package/corpus/core/src/server/email.ts +66 -3
  46. package/corpus/core/src/server/index.ts +10 -0
  47. package/corpus/core/src/sharing/index.ts +5 -0
  48. package/corpus/core/src/sharing/recipients.ts +67 -0
  49. package/corpus/core/src/triggers/actions/list-automations.ts +27 -8
  50. package/corpus/core/src/triggers/actions/manage-automation.ts +17 -6
  51. package/corpus/core/src/triggers/actions.ts +16 -4
  52. package/corpus/core/src/triggers/dispatcher.ts +44 -30
  53. package/corpus/core/src/triggers/routes.ts +19 -6
  54. package/corpus/core/src/vitest-config.ts +68 -0
  55. package/corpus/core/vitest.config.ts +5 -0
  56. package/corpus/templates/analytics/app/pages/Settings.tsx +37 -53
  57. package/corpus/templates/analytics/changelog/2026-08-01-new-error-alerts-now-reach-you-by-email-and-a-scheduled-dash.md +6 -0
  58. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +43 -0
  59. package/corpus/templates/analytics/server/lib/error-capture.ts +5 -1
  60. package/corpus/templates/analytics/vitest.config.ts +16 -12
  61. package/corpus/templates/assets/actions/refresh-generation-run.ts +3 -0
  62. package/corpus/templates/assets/app/components/ui/switch.tsx +1 -0
  63. package/corpus/templates/assets/app/hooks/use-assets-prefs.ts +55 -0
  64. package/corpus/templates/assets/app/i18n-data.ts +30 -0
  65. package/corpus/templates/assets/app/routes/settings.tsx +58 -55
  66. package/corpus/templates/assets/changelog/2026-08-01-get-an-email-when-a-generation-you-started-finishes-or-fails.md +6 -0
  67. package/corpus/templates/assets/server/lib/generation-run-notifications.ts +89 -0
  68. package/corpus/templates/assets/server/lib/video-runs.ts +8 -1
  69. package/corpus/templates/assets/server/routes/_agent-native/assets/user-prefs.get.ts +17 -0
  70. package/corpus/templates/assets/server/routes/_agent-native/assets/user-prefs.put.ts +36 -0
  71. package/corpus/templates/assets/shared/assets-user-prefs.ts +10 -0
  72. package/corpus/templates/assets/vitest.config.ts +16 -12
  73. package/corpus/templates/brain/app/routes/settings.tsx +15 -15
  74. package/corpus/templates/brain/vitest.config.ts +16 -12
  75. package/corpus/templates/calendar/app/pages/Settings.tsx +32 -40
  76. package/corpus/templates/calendar/changelog/2026-08-01-language-and-appearance-now-sit-together-in-one-preferences-.md +6 -0
  77. package/corpus/templates/calendar/vitest.config.ts +16 -12
  78. package/corpus/templates/chat/app/routes/settings.tsx +14 -22
  79. package/corpus/templates/chat/vitest.config.ts +9 -0
  80. package/corpus/templates/clips/.agents/skills/video-editing/SKILL.md +31 -0
  81. package/corpus/templates/clips/AGENTS.md +1 -0
  82. package/corpus/templates/clips/actions/add-comment.ts +12 -1
  83. package/corpus/templates/clips/actions/create-meeting.ts +2 -2
  84. package/corpus/templates/clips/actions/create-recording.ts +2 -2
  85. package/corpus/templates/clips/actions/finalize-recording.ts +42 -6
  86. package/corpus/templates/clips/actions/generate-filmstrip.ts +154 -0
  87. package/corpus/templates/clips/actions/get-recording-player-data.ts +6 -0
  88. package/corpus/templates/clips/actions/import-loom-recording.ts +2 -2
  89. package/corpus/templates/clips/actions/lib/ensure-recording-filmstrip.ts +258 -0
  90. package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +11 -3
  91. package/corpus/templates/clips/actions/react-to-comment.ts +13 -0
  92. package/corpus/templates/clips/actions/react-to-recording.ts +10 -1
  93. package/corpus/templates/clips/actions/reply-to-comment.ts +12 -1
  94. package/corpus/templates/clips/actions/stitch-recordings.ts +2 -2
  95. package/corpus/templates/clips/app/components/editor/editor-layout.tsx +152 -23
  96. package/corpus/templates/clips/app/components/editor/waveform.tsx +237 -75
  97. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +23 -1
  98. package/corpus/templates/clips/app/components/settings/ai-setup-section.tsx +441 -0
  99. package/corpus/templates/clips/app/components/settings/slack-section.tsx +329 -0
  100. package/corpus/templates/clips/app/components/settings/types.ts +14 -0
  101. package/corpus/templates/clips/app/components/settings/video-storage-section.tsx +443 -0
  102. package/corpus/templates/clips/app/hooks/use-secret-status.ts +69 -0
  103. package/corpus/templates/clips/app/i18n/en-US.ts +9 -1
  104. package/corpus/templates/clips/app/lib/video-filmstrip.ts +262 -0
  105. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +165 -1351
  106. package/corpus/templates/clips/changelog/2026-07-27-saved-clips-now-resume-from-the-last-uploaded-byte-after-an-.md +6 -0
  107. package/corpus/templates/clips/changelog/2026-07-31-editor-timeline-now-shows-video-frames-behind-the-waveform-g.md +6 -0
  108. package/corpus/templates/clips/changelog/2026-07-31-email-notifications-now-go-out-when-someone-comments-on-or-r.md +6 -0
  109. package/corpus/templates/clips/changelog/2026-07-31-you-can-now-set-a-default-visibility-for-new-recordings-in-s.md +6 -0
  110. package/corpus/templates/clips/changelog/2026-08-01-email-notifications-for-comments-and-reactions-now-actually-.md +6 -0
  111. package/corpus/templates/clips/changelog/2026-08-03-the-desktop-app-now-opens-directly-beneath-its-tray-icon-on-.md +6 -0
  112. package/corpus/templates/clips/chrome-extension/package.json +1 -0
  113. package/corpus/templates/clips/chrome-extension/vitest.config.ts +9 -0
  114. package/corpus/templates/clips/desktop/package.json +1 -0
  115. package/corpus/templates/clips/desktop/src/app.tsx +17 -1
  116. package/corpus/templates/clips/desktop/src/lib/recorder.ts +331 -36
  117. package/corpus/templates/clips/desktop/src/lib/upload-recovery.ts +129 -0
  118. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +815 -15
  119. package/corpus/templates/clips/desktop/src-tauri/src/tray.rs +61 -11
  120. package/corpus/templates/clips/desktop/vitest.config.ts +9 -0
  121. package/corpus/templates/clips/server/db/schema.ts +17 -0
  122. package/corpus/templates/clips/server/jobs/media-verification.ts +4 -0
  123. package/corpus/templates/clips/server/lib/activity-notifications.ts +185 -0
  124. package/corpus/templates/clips/server/lib/recording-media-cleanup.ts +2 -0
  125. package/corpus/templates/clips/server/lib/recording-upload-state.ts +46 -9
  126. package/corpus/templates/clips/server/lib/recordings.ts +24 -0
  127. package/corpus/templates/clips/server/lib/resumable-session.ts +10 -4
  128. package/corpus/templates/clips/server/lib/transactional-email-templates.ts +109 -0
  129. package/corpus/templates/clips/server/lib/upload-interruption.ts +8 -0
  130. package/corpus/templates/clips/server/lib/upload-lease.ts +50 -4
  131. package/corpus/templates/clips/server/lib/video-filmstrip-sprite.ts +207 -0
  132. package/corpus/templates/clips/server/lib/video-remux.ts +13 -4
  133. package/corpus/templates/clips/server/plugins/db.ts +16 -0
  134. package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +4 -0
  135. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +123 -36
  136. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +159 -29
  137. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/interrupt.post.ts +197 -0
  138. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/reset-chunks.post.ts +193 -39
  139. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/resume.get.ts +191 -12
  140. package/corpus/templates/clips/shared/clips-ai-prefs.ts +6 -0
  141. package/corpus/templates/clips/shared/feature-flags.ts +12 -0
  142. package/corpus/templates/clips/shared/recording-core.ts +4 -0
  143. package/corpus/templates/clips/vitest.config.ts +23 -19
  144. package/corpus/templates/content/actions/add-comment.ts +15 -1
  145. package/corpus/templates/content/actions/list-documents.ts +1 -0
  146. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +5 -2
  147. package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +50 -1
  148. package/corpus/templates/content/app/components/ui/switch.tsx +1 -0
  149. package/corpus/templates/content/app/hooks/use-content-prefs.ts +55 -0
  150. package/corpus/templates/content/app/i18n-data.ts +40 -0
  151. package/corpus/templates/content/app/routes/_app.settings.tsx +47 -18
  152. package/corpus/templates/content/changelog/2026-07-30-breadcrumb-menus-now-hide-internal-system-pages.md +6 -0
  153. package/corpus/templates/content/changelog/2026-08-01-get-an-email-when-someone-comments-on-replies-in-or-mentions.md +6 -0
  154. package/corpus/templates/content/server/lib/comment-notifications.ts +146 -0
  155. package/corpus/templates/content/server/routes/_agent-native/content/user-prefs.get.ts +17 -0
  156. package/corpus/templates/content/server/routes/_agent-native/content/user-prefs.put.ts +36 -0
  157. package/corpus/templates/content/shared/content-user-prefs.ts +11 -0
  158. package/corpus/templates/content/vitest.config.ts +20 -16
  159. package/corpus/templates/crm/app/routes/settings.tsx +14 -22
  160. package/corpus/templates/crm/vitest.config.ts +8 -4
  161. package/corpus/templates/design/app/routes/settings.tsx +14 -22
  162. package/corpus/templates/design/changelog/2026-08-01-review-comments-replies-and-mentions-now-send-an-email-to-th.md +6 -0
  163. package/corpus/templates/design/vitest.config.ts +18 -12
  164. package/corpus/templates/dispatch/app/routes/settings.tsx +26 -40
  165. package/corpus/templates/dispatch/vitest.config.ts +11 -7
  166. package/corpus/templates/factory/app/routes/settings.tsx +14 -22
  167. package/corpus/templates/factory/vitest.config.ts +9 -0
  168. package/corpus/templates/forms/app/routes/_app.settings.tsx +14 -22
  169. package/corpus/templates/forms/vitest.config.ts +27 -23
  170. package/corpus/templates/macros/app/routes/settings.tsx +14 -22
  171. package/corpus/templates/macros/vitest.config.ts +16 -12
  172. package/corpus/templates/mail/app/pages/SettingsPage.tsx +14 -16
  173. package/corpus/templates/mail/vitest.config.ts +16 -12
  174. package/corpus/templates/plan/app/routes/settings.tsx +30 -44
  175. package/corpus/templates/plan/vitest.config.ts +24 -20
  176. package/corpus/templates/slides/actions/add-slide-comment.ts +12 -1
  177. package/corpus/templates/slides/app/components/ui/switch.tsx +1 -0
  178. package/corpus/templates/slides/app/hooks/use-slides-prefs.ts +55 -0
  179. package/corpus/templates/slides/app/i18n/en-US.ts +4 -0
  180. package/corpus/templates/slides/app/routes/settings.tsx +45 -22
  181. package/corpus/templates/slides/changelog/2026-08-01-get-an-email-when-someone-comments-on-or-replies-in-your-dec.md +6 -0
  182. package/corpus/templates/slides/server/lib/comment-notifications.ts +182 -0
  183. package/corpus/templates/slides/server/routes/_agent-native/slides/user-prefs.get.ts +16 -0
  184. package/corpus/templates/slides/server/routes/_agent-native/slides/user-prefs.put.ts +36 -0
  185. package/corpus/templates/slides/shared/slides-user-prefs.ts +11 -0
  186. package/corpus/templates/slides/vitest.config.ts +17 -13
  187. package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +2 -0
  188. package/corpus/templates/tasks/app/routes/settings.tsx +14 -22
  189. package/corpus/templates/tasks/vitest.config.ts +17 -13
  190. package/corpus/toolkit/CHANGELOG.md +9 -0
  191. package/corpus/toolkit/package.json +1 -1
  192. package/corpus/toolkit/vitest.config.ts +3 -0
  193. package/dist/automations/service.d.ts +2 -0
  194. package/dist/automations/service.d.ts.map +1 -1
  195. package/dist/automations/service.js +29 -4
  196. package/dist/automations/service.js.map +1 -1
  197. package/dist/cli/mcp-config-writers.d.ts.map +1 -1
  198. package/dist/cli/mcp-config-writers.js +24 -23
  199. package/dist/cli/mcp-config-writers.js.map +1 -1
  200. package/dist/client/agent-page/AgentJobsTab.d.ts.map +1 -1
  201. package/dist/client/agent-page/AgentJobsTab.js +86 -29
  202. package/dist/client/agent-page/AgentJobsTab.js.map +1 -1
  203. package/dist/client/agent-page/AutomationDetailsDialog.d.ts +21 -0
  204. package/dist/client/agent-page/AutomationDetailsDialog.d.ts.map +1 -0
  205. package/dist/client/agent-page/AutomationDetailsDialog.js +33 -0
  206. package/dist/client/agent-page/AutomationDetailsDialog.js.map +1 -0
  207. package/dist/client/agent-page/AutomationScheduleDialog.d.ts +15 -0
  208. package/dist/client/agent-page/AutomationScheduleDialog.d.ts.map +1 -0
  209. package/dist/client/agent-page/AutomationScheduleDialog.js +46 -0
  210. package/dist/client/agent-page/AutomationScheduleDialog.js.map +1 -0
  211. package/dist/client/agent-page/TimezoneSelect.d.ts +17 -0
  212. package/dist/client/agent-page/TimezoneSelect.d.ts.map +1 -0
  213. package/dist/client/agent-page/TimezoneSelect.js +107 -0
  214. package/dist/client/agent-page/TimezoneSelect.js.map +1 -0
  215. package/dist/client/agent-page/use-jobs.d.ts +20 -7
  216. package/dist/client/agent-page/use-jobs.d.ts.map +1 -1
  217. package/dist/client/agent-page/use-jobs.js +24 -4
  218. package/dist/client/agent-page/use-jobs.js.map +1 -1
  219. package/dist/client/org/OrgSwitcher.d.ts +8 -1
  220. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  221. package/dist/client/org/OrgSwitcher.js +8 -2
  222. package/dist/client/org/OrgSwitcher.js.map +1 -1
  223. package/dist/client/settings/AccountSettingsCard.d.ts.map +1 -1
  224. package/dist/client/settings/AccountSettingsCard.js +10 -9
  225. package/dist/client/settings/AccountSettingsCard.js.map +1 -1
  226. package/dist/client/settings/SchedulingTimezoneField.d.ts +10 -0
  227. package/dist/client/settings/SchedulingTimezoneField.d.ts.map +1 -0
  228. package/dist/client/settings/SchedulingTimezoneField.js +30 -0
  229. package/dist/client/settings/SchedulingTimezoneField.js.map +1 -0
  230. package/dist/client/settings/SettingsRow.d.ts +36 -0
  231. package/dist/client/settings/SettingsRow.d.ts.map +1 -0
  232. package/dist/client/settings/SettingsRow.js +15 -0
  233. package/dist/client/settings/SettingsRow.js.map +1 -0
  234. package/dist/client/settings/agent-settings-search.d.ts.map +1 -1
  235. package/dist/client/settings/agent-settings-search.js +1 -1
  236. package/dist/client/settings/agent-settings-search.js.map +1 -1
  237. package/dist/client/settings/index.d.ts +1 -0
  238. package/dist/client/settings/index.d.ts.map +1 -1
  239. package/dist/client/settings/index.js +1 -0
  240. package/dist/client/settings/index.js.map +1 -1
  241. package/dist/collab/struct-routes.d.ts +1 -1
  242. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  243. package/dist/integrations/identity.d.ts.map +1 -1
  244. package/dist/integrations/identity.js +2 -11
  245. package/dist/integrations/identity.js.map +1 -1
  246. package/dist/jobs/actions/list-automation-runs.d.ts +8 -0
  247. package/dist/jobs/actions/list-automation-runs.d.ts.map +1 -0
  248. package/dist/jobs/actions/list-automation-runs.js +29 -0
  249. package/dist/jobs/actions/list-automation-runs.js.map +1 -0
  250. package/dist/jobs/actions/list-recurring-jobs.d.ts +2 -0
  251. package/dist/jobs/actions/list-recurring-jobs.d.ts.map +1 -1
  252. package/dist/jobs/actions/list-recurring-jobs.js +20 -7
  253. package/dist/jobs/actions/list-recurring-jobs.js.map +1 -1
  254. package/dist/jobs/actions/manage-recurring-job.d.ts +2 -0
  255. package/dist/jobs/actions/manage-recurring-job.js +30 -9
  256. package/dist/jobs/actions/manage-recurring-job.js.map +1 -1
  257. package/dist/jobs/background-automation-runner.d.ts.map +1 -1
  258. package/dist/jobs/background-automation-runner.js +60 -0
  259. package/dist/jobs/background-automation-runner.js.map +1 -1
  260. package/dist/jobs/cron.d.ts +16 -2
  261. package/dist/jobs/cron.d.ts.map +1 -1
  262. package/dist/jobs/cron.js +43 -6
  263. package/dist/jobs/cron.js.map +1 -1
  264. package/dist/jobs/frontmatter.d.ts +12 -0
  265. package/dist/jobs/frontmatter.d.ts.map +1 -1
  266. package/dist/jobs/frontmatter.js +8 -0
  267. package/dist/jobs/frontmatter.js.map +1 -1
  268. package/dist/jobs/run-history.d.ts +56 -0
  269. package/dist/jobs/run-history.d.ts.map +1 -0
  270. package/dist/jobs/run-history.js +169 -0
  271. package/dist/jobs/run-history.js.map +1 -0
  272. package/dist/jobs/scheduler.d.ts.map +1 -1
  273. package/dist/jobs/scheduler.js +51 -18
  274. package/dist/jobs/scheduler.js.map +1 -1
  275. package/dist/jobs/tools.d.ts.map +1 -1
  276. package/dist/jobs/tools.js +37 -6
  277. package/dist/jobs/tools.js.map +1 -1
  278. package/dist/localization/actions/get-localization-preference.d.ts +1 -2
  279. package/dist/localization/actions/get-localization-preference.d.ts.map +1 -1
  280. package/dist/localization/actions/get-localization-preference.js.map +1 -1
  281. package/dist/localization/actions/set-localization-preference.d.ts +3 -3
  282. package/dist/localization/actions/set-localization-preference.d.ts.map +1 -1
  283. package/dist/localization/actions/set-localization-preference.js +33 -8
  284. package/dist/localization/actions/set-localization-preference.js.map +1 -1
  285. package/dist/localization/default-messages.d.ts +25 -0
  286. package/dist/localization/default-messages.d.ts.map +1 -1
  287. package/dist/localization/default-messages.js +25 -0
  288. package/dist/localization/default-messages.js.map +1 -1
  289. package/dist/localization/shared.d.ts +16 -1
  290. package/dist/localization/shared.d.ts.map +1 -1
  291. package/dist/localization/shared.js +22 -4
  292. package/dist/localization/shared.js.map +1 -1
  293. package/dist/localization/user-timezone.d.ts +11 -0
  294. package/dist/localization/user-timezone.d.ts.map +1 -0
  295. package/dist/localization/user-timezone.js +31 -0
  296. package/dist/localization/user-timezone.js.map +1 -0
  297. package/dist/notifications/routes.d.ts +1 -1
  298. package/dist/observability/routes.d.ts +1 -1
  299. package/dist/org/app-roles-handlers.d.ts.map +1 -1
  300. package/dist/org/app-roles-handlers.js +1 -8
  301. package/dist/org/app-roles-handlers.js.map +1 -1
  302. package/dist/org/index.d.ts +1 -0
  303. package/dist/org/index.d.ts.map +1 -1
  304. package/dist/org/index.js +1 -0
  305. package/dist/org/index.js.map +1 -1
  306. package/dist/org/membership.d.ts +6 -0
  307. package/dist/org/membership.d.ts.map +1 -0
  308. package/dist/org/membership.js +16 -0
  309. package/dist/org/membership.js.map +1 -0
  310. package/dist/provider-api/actions/custom-provider-registration.d.ts +10 -10
  311. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  312. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  313. package/dist/resources/handlers.d.ts +1 -1
  314. package/dist/review/actions/create-review-comment.d.ts +32 -1
  315. package/dist/review/actions/create-review-comment.d.ts.map +1 -1
  316. package/dist/review/actions/create-review-comment.js +3 -1
  317. package/dist/review/actions/create-review-comment.js.map +1 -1
  318. package/dist/review/actions/reply-review-comment.d.ts +32 -1
  319. package/dist/review/actions/reply-review-comment.d.ts.map +1 -1
  320. package/dist/review/actions/reply-review-comment.js +3 -1
  321. package/dist/review/actions/reply-review-comment.js.map +1 -1
  322. package/dist/review/index.d.ts +1 -0
  323. package/dist/review/index.d.ts.map +1 -1
  324. package/dist/review/index.js +1 -0
  325. package/dist/review/index.js.map +1 -1
  326. package/dist/review/notifications.d.ts +24 -0
  327. package/dist/review/notifications.d.ts.map +1 -0
  328. package/dist/review/notifications.js +123 -0
  329. package/dist/review/notifications.js.map +1 -0
  330. package/dist/review/types.d.ts +5 -0
  331. package/dist/review/types.d.ts.map +1 -1
  332. package/dist/review/types.js.map +1 -1
  333. package/dist/server/action-discovery.d.ts.map +1 -1
  334. package/dist/server/action-discovery.js +4 -0
  335. package/dist/server/action-discovery.js.map +1 -1
  336. package/dist/server/activity-notifications.d.ts +61 -0
  337. package/dist/server/activity-notifications.d.ts.map +1 -0
  338. package/dist/server/activity-notifications.js +103 -0
  339. package/dist/server/activity-notifications.js.map +1 -0
  340. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  341. package/dist/server/app-name.d.ts +18 -0
  342. package/dist/server/app-name.d.ts.map +1 -1
  343. package/dist/server/app-name.js +31 -0
  344. package/dist/server/app-name.js.map +1 -1
  345. package/dist/server/better-auth-instance.js +4 -4
  346. package/dist/server/better-auth-instance.js.map +1 -1
  347. package/dist/server/email-templates.d.ts +12 -0
  348. package/dist/server/email-templates.d.ts.map +1 -1
  349. package/dist/server/email-templates.js +57 -14
  350. package/dist/server/email-templates.js.map +1 -1
  351. package/dist/server/email.d.ts +12 -0
  352. package/dist/server/email.d.ts.map +1 -1
  353. package/dist/server/email.js +52 -5
  354. package/dist/server/email.js.map +1 -1
  355. package/dist/server/index.d.ts +1 -0
  356. package/dist/server/index.d.ts.map +1 -1
  357. package/dist/server/index.js +1 -0
  358. package/dist/server/index.js.map +1 -1
  359. package/dist/sharing/index.d.ts +1 -0
  360. package/dist/sharing/index.d.ts.map +1 -1
  361. package/dist/sharing/index.js +1 -0
  362. package/dist/sharing/index.js.map +1 -1
  363. package/dist/sharing/recipients.d.ts +31 -0
  364. package/dist/sharing/recipients.d.ts.map +1 -0
  365. package/dist/sharing/recipients.js +40 -0
  366. package/dist/sharing/recipients.js.map +1 -0
  367. package/dist/templates/chat/app/routes/settings.tsx +14 -22
  368. package/dist/templates/chat/vitest.config.ts +9 -0
  369. package/dist/triggers/actions/list-automations.d.ts +2 -0
  370. package/dist/triggers/actions/list-automations.d.ts.map +1 -1
  371. package/dist/triggers/actions/list-automations.js +19 -8
  372. package/dist/triggers/actions/list-automations.js.map +1 -1
  373. package/dist/triggers/actions/manage-automation.d.ts +6 -0
  374. package/dist/triggers/actions/manage-automation.js +12 -6
  375. package/dist/triggers/actions/manage-automation.js.map +1 -1
  376. package/dist/triggers/actions.d.ts.map +1 -1
  377. package/dist/triggers/actions.js +15 -4
  378. package/dist/triggers/actions.js.map +1 -1
  379. package/dist/triggers/dispatcher.d.ts.map +1 -1
  380. package/dist/triggers/dispatcher.js +19 -17
  381. package/dist/triggers/dispatcher.js.map +1 -1
  382. package/dist/triggers/routes.d.ts.map +1 -1
  383. package/dist/triggers/routes.js +6 -6
  384. package/dist/triggers/routes.js.map +1 -1
  385. package/dist/vitest-config.d.ts +10 -0
  386. package/dist/vitest-config.d.ts.map +1 -0
  387. package/dist/vitest-config.js +55 -0
  388. package/dist/vitest-config.js.map +1 -0
  389. package/package.json +5 -4
  390. package/src/automations/service.ts +41 -4
  391. package/src/cli/mcp-config-writers.ts +27 -21
  392. package/src/client/agent-page/AgentJobsTab.tsx +187 -85
  393. package/src/client/agent-page/AutomationDetailsDialog.tsx +208 -0
  394. package/src/client/agent-page/AutomationScheduleDialog.tsx +180 -0
  395. package/src/client/agent-page/TimezoneSelect.tsx +241 -0
  396. package/src/client/agent-page/use-jobs.ts +50 -10
  397. package/src/client/org/OrgSwitcher.tsx +26 -0
  398. package/src/client/settings/AccountSettingsCard.tsx +35 -31
  399. package/src/client/settings/SchedulingTimezoneField.tsx +71 -0
  400. package/src/client/settings/SettingsRow.tsx +113 -0
  401. package/src/client/settings/agent-settings-search.ts +2 -1
  402. package/src/client/settings/index.ts +6 -0
  403. package/src/integrations/identity.ts +2 -12
  404. package/src/jobs/actions/list-automation-runs.ts +33 -0
  405. package/src/jobs/actions/list-recurring-jobs.ts +27 -6
  406. package/src/jobs/actions/manage-recurring-job.ts +41 -9
  407. package/src/jobs/background-automation-runner.ts +88 -0
  408. package/src/jobs/cron.ts +52 -6
  409. package/src/jobs/frontmatter.ts +20 -0
  410. package/src/jobs/run-history.ts +242 -0
  411. package/src/jobs/scheduler.ts +73 -18
  412. package/src/jobs/tools.ts +54 -6
  413. package/src/localization/actions/get-localization-preference.ts +2 -2
  414. package/src/localization/actions/set-localization-preference.ts +49 -11
  415. package/src/localization/default-messages.ts +27 -0
  416. package/src/localization/shared.ts +39 -6
  417. package/src/localization/user-timezone.ts +36 -0
  418. package/src/org/app-roles-handlers.ts +1 -9
  419. package/src/org/index.ts +2 -0
  420. package/src/org/membership.ts +18 -0
  421. package/src/review/actions/create-review-comment.ts +4 -1
  422. package/src/review/actions/reply-review-comment.ts +4 -1
  423. package/src/review/index.ts +5 -0
  424. package/src/review/notifications.ts +163 -0
  425. package/src/review/types.ts +7 -0
  426. package/src/server/action-discovery.ts +4 -0
  427. package/src/server/activity-notifications.ts +174 -0
  428. package/src/server/app-name.ts +31 -0
  429. package/src/server/better-auth-instance.ts +4 -4
  430. package/src/server/email-templates.ts +70 -14
  431. package/src/server/email.ts +66 -3
  432. package/src/server/index.ts +10 -0
  433. package/src/sharing/index.ts +5 -0
  434. package/src/sharing/recipients.ts +67 -0
  435. package/src/templates/chat/app/routes/settings.tsx +14 -22
  436. package/src/templates/chat/vitest.config.ts +9 -0
  437. package/src/triggers/actions/list-automations.ts +27 -8
  438. package/src/triggers/actions/manage-automation.ts +17 -6
  439. package/src/triggers/actions.ts +16 -4
  440. package/src/triggers/dispatcher.ts +44 -30
  441. package/src/triggers/routes.ts +19 -6
  442. package/src/vitest-config.ts +68 -0
@@ -1,23 +1,27 @@
1
1
  import path from "node:path";
2
2
 
3
- import { defineConfig } from "vitest/config";
3
+ import baseConfig from "@agent-native/core/vitest-config";
4
+ import { defineConfig, mergeConfig } from "vitest/config";
4
5
 
5
- export default defineConfig({
6
- resolve: {
7
- alias: {
8
- "@": path.resolve(__dirname, "./app"),
9
- "@shared": path.resolve(__dirname, "./shared"),
6
+ export default mergeConfig(
7
+ baseConfig,
8
+ defineConfig({
9
+ resolve: {
10
+ alias: {
11
+ "@": path.resolve(__dirname, "./app"),
12
+ "@shared": path.resolve(__dirname, "./shared"),
13
+ },
10
14
  },
11
- },
12
- test: {
13
- include: ["**/*.{test,spec}.?(c|m)[jt]s?(x)"],
14
- exclude: [
15
- "**/node_modules/**",
16
- "**/.git/**",
17
- "**/dist/**",
18
- "**/.output/**",
19
- "**/.react-router/**",
20
- "**/e2e/**",
21
- ],
22
- },
23
- });
15
+ test: {
16
+ include: ["**/*.{test,spec}.?(c|m)[jt]s?(x)"],
17
+ exclude: [
18
+ "**/node_modules/**",
19
+ "**/.git/**",
20
+ "**/dist/**",
21
+ "**/.output/**",
22
+ "**/.react-router/**",
23
+ "**/e2e/**",
24
+ ],
25
+ },
26
+ }),
27
+ );
@@ -4,6 +4,7 @@ import { assertAccess } from "@agent-native/core/sharing";
4
4
  import { z } from "zod";
5
5
 
6
6
  import { getDb, schema } from "../server/db/index.js";
7
+ import { notifyDocumentComment } from "../server/lib/comment-notifications.js";
7
8
 
8
9
  type Mention = { email: string; name: string };
9
10
 
@@ -115,6 +116,19 @@ export default defineAction({
115
116
  authorName: name,
116
117
  });
117
118
 
118
- return { id, threadId };
119
+ const notified = await notifyDocumentComment({
120
+ documentId,
121
+ documentTitle: (access.resource.title as string | null) ?? "",
122
+ orgId: (access.resource.orgId as string | null) ?? null,
123
+ threadId,
124
+ ownerEmail,
125
+ authorEmail: email,
126
+ authorName: name,
127
+ content,
128
+ mentions,
129
+ isReply: Boolean(parentId ?? args.threadId),
130
+ });
131
+
132
+ return { id, threadId, notified };
119
133
  },
120
134
  });
@@ -337,6 +337,7 @@ export default defineAction({
337
337
  id: database.id,
338
338
  documentId: database.documentId,
339
339
  title: database.title,
340
+ systemRole: database.systemRole,
340
341
  description: d.description,
341
342
  viewConfig: parseDatabaseViewConfig(database.viewConfigJson),
342
343
  createdAt: database.createdAt,
@@ -445,6 +445,7 @@ export function documentEditorBreadcrumbNavigationItems(
445
445
  | "title"
446
446
  | "icon"
447
447
  | "position"
448
+ | "database"
448
449
  | "databaseMembership"
449
450
  | "source"
450
451
  >[], // i18n-ignore type expression
@@ -457,6 +458,9 @@ export function documentEditorBreadcrumbNavigationItems(
457
458
  workspacesTitle: string;
458
459
  },
459
460
  ): ToolbarBreadcrumbItem[] {
461
+ const peerDocuments = documents.filter(
462
+ (item) => !item.database?.systemRole && item.source?.kind !== "folder",
463
+ );
460
464
  const documentById = new Map(documents.map((item) => [item.id, item]));
461
465
  const workspaceDocumentIds = new Set(
462
466
  spaces.map((space) => space.filesDocumentId),
@@ -480,9 +484,8 @@ export function documentEditorBreadcrumbNavigationItems(
480
484
  if (!current) return item;
481
485
  const membershipDocumentId =
482
486
  current.databaseMembership?.databaseDocumentId ?? null;
483
- const siblings = documents
487
+ const siblings = peerDocuments
484
488
  .filter((candidate) => {
485
- if (candidate.source?.kind === "folder") return false;
486
489
  if (candidate.parentId !== current.parentId) return false;
487
490
  if (current.parentId) return true;
488
491
  return (
@@ -318,6 +318,15 @@ export function compactToolbarBreadcrumbItems(
318
318
  ];
319
319
  }
320
320
 
321
+ export function firstSelectableBreadcrumbMenuItemId(
322
+ menuItems: ToolbarBreadcrumbItem["menuItems"],
323
+ currentDocumentId: string,
324
+ ): string | null {
325
+ return (
326
+ menuItems?.find((menuItem) => menuItem.id !== currentDocumentId)?.id ?? null
327
+ );
328
+ }
329
+
321
330
  function ToolbarBreadcrumbMenu({
322
331
  item,
323
332
  label,
@@ -337,6 +346,12 @@ function ToolbarBreadcrumbMenu({
337
346
  }) {
338
347
  const [open, setOpen] = useState(false);
339
348
  const closeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
349
+ const openedWithKeyboardRef = useRef(false);
350
+ const firstSelectableItemRef = useRef<HTMLDivElement | null>(null);
351
+ const firstSelectableItemId = firstSelectableBreadcrumbMenuItemId(
352
+ item.menuItems,
353
+ currentDocumentId,
354
+ );
340
355
 
341
356
  function cancelClose() {
342
357
  if (!closeTimerRef.current) return;
@@ -349,8 +364,25 @@ function ToolbarBreadcrumbMenu({
349
364
  closeTimerRef.current = setTimeout(() => setOpen(false), 140);
350
365
  }
351
366
 
367
+ useEffect(() => {
368
+ if (!open || !openedWithKeyboardRef.current) return;
369
+ openedWithKeyboardRef.current = false;
370
+ const frame = requestAnimationFrame(() => {
371
+ firstSelectableItemRef.current?.focus();
372
+ });
373
+ return () => cancelAnimationFrame(frame);
374
+ }, [open]);
375
+
352
376
  return (
353
- <DropdownMenu modal={false} open={open} onOpenChange={setOpen}>
377
+ <DropdownMenu
378
+ modal={false}
379
+ open={open}
380
+ onOpenChange={(nextOpen) => {
381
+ if (nextOpen) cancelClose();
382
+ else openedWithKeyboardRef.current = false;
383
+ setOpen(nextOpen);
384
+ }}
385
+ >
354
386
  <DropdownMenuTrigger asChild>
355
387
  <button
356
388
  type="button"
@@ -364,6 +396,16 @@ function ToolbarBreadcrumbMenu({
364
396
  setOpen(true);
365
397
  }}
366
398
  onPointerLeave={scheduleClose}
399
+ onKeyDown={(event) => {
400
+ if (
401
+ event.key === "Enter" ||
402
+ event.key === " " ||
403
+ event.key === "ArrowDown"
404
+ ) {
405
+ openedWithKeyboardRef.current = true;
406
+ cancelClose();
407
+ }
408
+ }}
367
409
  >
368
410
  {children}
369
411
  </button>
@@ -371,6 +413,7 @@ function ToolbarBreadcrumbMenu({
371
413
  <DropdownMenuContent
372
414
  align="start"
373
415
  className="w-64"
416
+ onKeyDown={cancelClose}
374
417
  onPointerEnter={cancelClose}
375
418
  onPointerLeave={scheduleClose}
376
419
  >
@@ -379,7 +422,13 @@ function ToolbarBreadcrumbMenu({
379
422
  return (
380
423
  <DropdownMenuItem
381
424
  key={menuItem.id}
425
+ ref={
426
+ menuItem.id === firstSelectableItemId
427
+ ? firstSelectableItemRef
428
+ : undefined
429
+ }
382
430
  className="gap-2"
431
+ disabled={menuItem.id === currentDocumentId}
383
432
  onSelect={() => onOpen(menuItem.id)}
384
433
  >
385
434
  <span className="flex size-4 shrink-0 items-center justify-center">
@@ -0,0 +1 @@
1
+ export * from "@agent-native/toolkit/ui/switch";
@@ -0,0 +1,55 @@
1
+ import { agentNativePath } from "@agent-native/core/client/api-path";
2
+ import type { ContentUserPrefs } from "@shared/content-user-prefs";
3
+ import { useCallback, useEffect, useState } from "react";
4
+
5
+ const PREFS_PATH = "/_agent-native/content/user-prefs";
6
+
7
+ export interface ContentPrefsState {
8
+ prefs: ContentUserPrefs;
9
+ loading: boolean;
10
+ /** Applies the patch optimistically and rolls back if the write fails. */
11
+ save: (patch: ContentUserPrefs) => Promise<void>;
12
+ }
13
+
14
+ export function useContentPrefs(): ContentPrefsState {
15
+ const [prefs, setPrefs] = useState<ContentUserPrefs>({});
16
+ const [loading, setLoading] = useState(true);
17
+
18
+ useEffect(() => {
19
+ let cancelled = false;
20
+ void (async () => {
21
+ try {
22
+ const res = await fetch(agentNativePath(PREFS_PATH));
23
+ const json = res.ok ? await res.json() : null;
24
+ if (cancelled) return;
25
+ if (json && typeof json === "object" && !("error" in json)) {
26
+ setPrefs(json as ContentUserPrefs);
27
+ }
28
+ } finally {
29
+ if (!cancelled) setLoading(false);
30
+ }
31
+ })();
32
+ return () => {
33
+ cancelled = true;
34
+ };
35
+ }, []);
36
+
37
+ const save = useCallback(
38
+ async (patch: ContentUserPrefs) => {
39
+ const previous = prefs;
40
+ setPrefs((current) => ({ ...current, ...patch }));
41
+ const res = await fetch(agentNativePath(PREFS_PATH), {
42
+ method: "PUT",
43
+ headers: { "Content-Type": "application/json" },
44
+ body: JSON.stringify(patch),
45
+ });
46
+ if (!res.ok) {
47
+ setPrefs(previous);
48
+ throw new Error(`Save failed (${res.status})`);
49
+ }
50
+ },
51
+ [prefs],
52
+ );
53
+
54
+ return { prefs, loading, save };
55
+ }
@@ -3055,6 +3055,10 @@ const enUS = {
3055
3055
  metaTitle: "Settings - Content",
3056
3056
  title: "Settings",
3057
3057
  description: "Language and workspace preferences for Content.",
3058
+ emailNotifications: "Email notifications",
3059
+ emailNotificationsDescription:
3060
+ "Get an email when someone comments on, replies in, or mentions you on your document.",
3061
+ saveFailed: "Failed to save",
3058
3062
  languageTitle: "Language",
3059
3063
  languageDescription:
3060
3064
  "Choose the interface language. This preference is saved for your account.",
@@ -9133,6 +9137,10 @@ export const messagesByLocale = {
9133
9137
  settings: {
9134
9138
  title: "设置",
9135
9139
  description: "Content 的语言和工作区偏好设置。",
9140
+ emailNotifications: "邮件通知",
9141
+ emailNotificationsDescription:
9142
+ "当有人评论你的文档、在讨论串中回复或提到你时,收到邮件通知。",
9143
+ saveFailed: "保存失败",
9136
9144
  languageTitle: "语言",
9137
9145
  languageDescription: "选择界面语言。此偏好会保存到你的账户。",
9138
9146
  languageLabel: "界面语言",
@@ -9325,6 +9333,10 @@ export const messagesByLocale = {
9325
9333
  settings: {
9326
9334
  title: "Ajustes",
9327
9335
  description: "Preferencias de idioma y espacio de trabajo para Content.",
9336
+ emailNotifications: "Notificaciones por correo",
9337
+ emailNotificationsDescription:
9338
+ "Recibe un correo cuando alguien comente, responda o te mencione en tu documento.",
9339
+ saveFailed: "No se pudo guardar",
9328
9340
  languageTitle: "Idioma",
9329
9341
  languageDescription:
9330
9342
  "Elige el idioma de la interfaz. Esta preferencia se guarda en tu cuenta.",
@@ -9524,6 +9536,10 @@ export const messagesByLocale = {
9524
9536
  settings: {
9525
9537
  title: "Paramètres",
9526
9538
  description: "Préférences de langue et d’espace de travail pour Content.",
9539
+ emailNotifications: "Notifications par e-mail",
9540
+ emailNotificationsDescription:
9541
+ "Recevez un e-mail lorsqu’une personne commente, répond ou vous mentionne dans votre document.",
9542
+ saveFailed: "Échec de l’enregistrement",
9527
9543
  languageTitle: "Langue",
9528
9544
  languageDescription:
9529
9545
  "Choisissez la langue de l’interface. Cette préférence est enregistrée dans votre compte.",
@@ -9721,6 +9737,10 @@ export const messagesByLocale = {
9721
9737
  settings: {
9722
9738
  title: "Einstellungen",
9723
9739
  description: "Sprach- und Arbeitsbereichseinstellungen für Content.",
9740
+ emailNotifications: "E-Mail-Benachrichtigungen",
9741
+ emailNotificationsDescription:
9742
+ "Erhalte eine E-Mail, wenn jemand dein Dokument kommentiert, antwortet oder dich erwähnt.",
9743
+ saveFailed: "Speichern fehlgeschlagen",
9724
9744
  languageTitle: "Sprache",
9725
9745
  languageDescription:
9726
9746
  "Wähle die Sprache der Oberfläche. Diese Einstellung wird in deinem Konto gespeichert.",
@@ -9917,6 +9937,10 @@ export const messagesByLocale = {
9917
9937
  settings: {
9918
9938
  title: "設定",
9919
9939
  description: "Content の言語とワークスペース設定。",
9940
+ emailNotifications: "メール通知",
9941
+ emailNotificationsDescription:
9942
+ "誰かがあなたのドキュメントにコメント、返信、またはあなたにメンションしたときにメールを受け取ります。",
9943
+ saveFailed: "保存に失敗しました",
9920
9944
  languageTitle: "言語",
9921
9945
  languageDescription:
9922
9946
  "インターフェース言語を選択します。この設定はアカウントに保存されます。",
@@ -10105,6 +10129,10 @@ export const messagesByLocale = {
10105
10129
  settings: {
10106
10130
  title: "설정",
10107
10131
  description: "Content의 언어 및 워크스페이스 환경설정입니다.",
10132
+ emailNotifications: "이메일 알림",
10133
+ emailNotificationsDescription:
10134
+ "누군가 내 문서에 댓글을 달거나 답글을 남기거나 나를 멘션하면 이메일을 받습니다.",
10135
+ saveFailed: "저장 실패",
10108
10136
  languageTitle: "언어",
10109
10137
  languageDescription:
10110
10138
  "인터페이스 언어를 선택하세요. 이 기본 설정은 계정에 저장됩니다.",
@@ -10299,6 +10327,10 @@ export const messagesByLocale = {
10299
10327
  settings: {
10300
10328
  title: "Configurações",
10301
10329
  description: "Preferências de idioma e espaço de trabalho do Content.",
10330
+ emailNotifications: "Notificações por e-mail",
10331
+ emailNotificationsDescription:
10332
+ "Receba um e-mail quando alguém comentar, responder ou mencionar você no seu documento.",
10333
+ saveFailed: "Falha ao salvar",
10302
10334
  languageTitle: "Idioma",
10303
10335
  languageDescription:
10304
10336
  "Escolha o idioma da interface. Essa preferência é salva na sua conta.",
@@ -10486,6 +10518,10 @@ export const messagesByLocale = {
10486
10518
  settings: {
10487
10519
  title: "सेटिंग्स",
10488
10520
  description: "Content के लिए भाषा और कार्यस्थान प्राथमिकताएं।",
10521
+ emailNotifications: "ईमेल सूचनाएँ",
10522
+ emailNotificationsDescription:
10523
+ "जब कोई आपके दस्तावेज़ पर टिप्पणी करे, जवाब दे या आपका उल्लेख करे तो ईमेल पाएँ।",
10524
+ saveFailed: "सहेजने में विफल",
10489
10525
  languageTitle: "भाषा",
10490
10526
  languageDescription: "इंटरफ़ेस भाषा चुनें। यह पसंद आपके खाते में सहेजी जाती है।",
10491
10527
  languageLabel: "इंटरफ़ेस भाषा",
@@ -10672,6 +10708,10 @@ export const messagesByLocale = {
10672
10708
  settings: {
10673
10709
  title: "الإعدادات",
10674
10710
  description: "تفضيلات اللغة ومساحة العمل في Content.",
10711
+ emailNotifications: "إشعارات البريد الإلكتروني",
10712
+ emailNotificationsDescription:
10713
+ "احصل على بريد إلكتروني عندما يعلّق شخص على مستندك أو يرد أو يذكرك.",
10714
+ saveFailed: "فشل الحفظ",
10675
10715
  languageTitle: "اللغة",
10676
10716
  languageDescription: "اختر لغة الواجهة. يتم حفظ هذا التفضيل في حسابك.",
10677
10717
  languageLabel: "لغة الواجهة",
@@ -3,6 +3,8 @@ import { LanguagePicker, useT } from "@agent-native/core/client/i18n";
3
3
  import { TeamPage } from "@agent-native/core/client/org";
4
4
  import {
5
5
  AccountSettingsCard,
6
+ SettingsGroup,
7
+ SettingsRow,
6
8
  SettingsTabsPage,
7
9
  useAgentSettingsTabs,
8
10
  type SettingsSearchEntry,
@@ -10,8 +12,10 @@ import {
10
12
  import { CreativeContextSettingsLink } from "@agent-native/creative-context/client";
11
13
  import { useSetPageTitle } from "@agent-native/toolkit/app-shell";
12
14
  import { useMemo } from "react";
15
+ import { toast } from "sonner";
13
16
 
14
- import { Label } from "@/components/ui/label";
17
+ import { Switch } from "@/components/ui/switch";
18
+ import { useContentPrefs } from "@/hooks/use-content-prefs";
15
19
  import { messagesByLocale } from "@/i18n-data";
16
20
 
17
21
  import changelog from "../../CHANGELOG.md?raw";
@@ -24,6 +28,7 @@ export default function SettingsRoute() {
24
28
  const t = useT();
25
29
  const agentSettingsTabs = useAgentSettingsTabs();
26
30
  useSetPageTitle(t("settings.title"));
31
+ const { prefs, loading: prefsLoading, save: savePrefs } = useContentPrefs();
27
32
 
28
33
  const generalSearchEntries = useMemo<SettingsSearchEntry[]>(
29
34
  () => [
@@ -33,6 +38,12 @@ export default function SettingsRoute() {
33
38
  keywords: "language locale translation i18n",
34
39
  hash: "language",
35
40
  },
41
+ {
42
+ id: "content-notifications",
43
+ label: t("settings.emailNotifications"),
44
+ keywords: "email notifications comments replies mentions alerts",
45
+ hash: "notifications",
46
+ },
36
47
  ],
37
48
  [t],
38
49
  );
@@ -52,23 +63,41 @@ export default function SettingsRoute() {
52
63
 
53
64
  <CreativeContextSettingsLink />
54
65
 
55
- <section
56
- id="language"
57
- className="scroll-mt-16 rounded-lg border border-border bg-card p-5"
58
- >
59
- <div className="space-y-1">
60
- <h2 className="text-base font-semibold">
61
- {t("settings.languageTitle")}
62
- </h2>
63
- <p className="text-sm leading-6 text-muted-foreground">
64
- {t("settings.languageDescription")}
65
- </p>
66
- </div>
67
- <div className="mt-4 max-w-xs space-y-1.5">
68
- <Label>{t("settings.languageLabel")}</Label>
69
- <LanguagePicker label={t("settings.languageLabel")} />
70
- </div>
71
- </section>
66
+ <SettingsGroup>
67
+ <SettingsRow
68
+ id="language"
69
+ label={t("settings.languageTitle")}
70
+ description={t("settings.languageDescription")}
71
+ control={
72
+ <div className="w-56">
73
+ <LanguagePicker label={t("settings.languageLabel")} />
74
+ </div>
75
+ }
76
+ />
77
+ <SettingsRow
78
+ id="notifications"
79
+ label={t("settings.emailNotifications")}
80
+ description={t("settings.emailNotificationsDescription")}
81
+ control={
82
+ <Switch
83
+ aria-label={t("settings.emailNotifications")}
84
+ checked={prefs.emailNotifications !== false}
85
+ disabled={prefsLoading}
86
+ onCheckedChange={(checked) => {
87
+ savePrefs({ emailNotifications: checked }).catch(
88
+ (err) => {
89
+ toast.error(
90
+ err instanceof Error
91
+ ? err.message
92
+ : t("settings.saveFailed"),
93
+ );
94
+ },
95
+ );
96
+ }}
97
+ />
98
+ }
99
+ />
100
+ </SettingsGroup>
72
101
  </main>
73
102
  }
74
103
  team={
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-30
4
+ ---
5
+
6
+ Breadcrumb menus now show only relevant page peers instead of internal system pages, with reliable keyboard selection between pages.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-08-01
4
+ ---
5
+
6
+ Get an email when someone comments on, replies in, or mentions you on your document, with a new toggle in Settings
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Email notifications for document comments, replies, and mentions.
3
+ *
4
+ * Recipient resolution, preference filtering, and delivery reporting come from
5
+ * `@agent-native/core/server`; this module owns only the Documents rows and the
6
+ * email copy. Share invites are not routed through the `emailNotifications`
7
+ * preference — they have their own delivery path.
8
+ */
9
+
10
+ import {
11
+ emailStrong,
12
+ getAppProductionUrl,
13
+ notifyActivity,
14
+ renderEmail,
15
+ runActivityNotification,
16
+ sendEmail,
17
+ type ActivityNotificationResult,
18
+ } from "@agent-native/core/server";
19
+ import { filterRecipientsByResourceAccess } from "@agent-native/core/sharing";
20
+ import { and, eq } from "drizzle-orm";
21
+
22
+ import { CONTENT_USER_PREFS_KEY } from "../../shared/content-user-prefs.js";
23
+ import { getDb, schema } from "../db/index.js";
24
+
25
+ export type DocumentCommentNotificationResult = ActivityNotificationResult;
26
+
27
+ const LOG_LABEL = "[content] comment notification";
28
+ const EXCERPT_LIMIT = 240;
29
+
30
+ function excerpt(content: string): string {
31
+ const collapsed = content.replace(/\s+/g, " ").trim();
32
+ return collapsed.length > EXCERPT_LIMIT
33
+ ? `${collapsed.slice(0, EXCERPT_LIMIT - 1)}…`
34
+ : collapsed;
35
+ }
36
+
37
+ function documentUrl(documentId: string): string {
38
+ const base = getAppProductionUrl().replace(/\/+$/, "");
39
+ return `${base}/page/${encodeURIComponent(documentId)}`;
40
+ }
41
+
42
+ async function threadParticipants(
43
+ documentId: string,
44
+ threadId: string,
45
+ ): Promise<string[]> {
46
+ const rows = await getDb()
47
+ .select({ authorEmail: schema.documentComments.authorEmail })
48
+ .from(schema.documentComments)
49
+ .where(
50
+ and(
51
+ eq(schema.documentComments.documentId, documentId),
52
+ eq(schema.documentComments.threadId, threadId),
53
+ ),
54
+ );
55
+ return rows.map((row) => row.authorEmail);
56
+ }
57
+
58
+ export interface DocumentCommentNotificationInput {
59
+ documentId: string;
60
+ /** Read from the access-checked resource by the caller, never re-queried. */
61
+ documentTitle: string;
62
+ /** The document's org, so `org` visibility resolves for its members. */
63
+ orgId?: string | null;
64
+ threadId: string;
65
+ ownerEmail: string;
66
+ authorEmail: string;
67
+ authorName?: string | null;
68
+ content: string;
69
+ mentions: { email: string; name: string }[];
70
+ isReply: boolean;
71
+ }
72
+
73
+ export async function notifyDocumentComment(
74
+ input: DocumentCommentNotificationInput,
75
+ ): Promise<DocumentCommentNotificationResult> {
76
+ return runActivityNotification(LOG_LABEL, () =>
77
+ deliverDocumentCommentEmails(input),
78
+ );
79
+ }
80
+
81
+ async function deliverDocumentCommentEmails(
82
+ input: DocumentCommentNotificationInput,
83
+ ): Promise<DocumentCommentNotificationResult> {
84
+ const title = input.documentTitle.trim() || "Untitled";
85
+ const mentioned = new Set(
86
+ input.mentions.map((mention) => mention.email.trim().toLowerCase()),
87
+ );
88
+
89
+ const candidates = [input.ownerEmail, ...mentioned];
90
+ if (input.isReply) {
91
+ candidates.push(
92
+ ...(await threadParticipants(input.documentId, input.threadId)),
93
+ );
94
+ }
95
+
96
+ // Mentions are caller-supplied and thread rows are historical; re-check both
97
+ // against the document's live ACL before mailing anyone its contents.
98
+ const allowed = await filterRecipientsByResourceAccess({
99
+ resourceType: "document",
100
+ resourceId: input.documentId,
101
+ emails: candidates,
102
+ orgId: input.orgId,
103
+ });
104
+
105
+ const actor = input.authorName?.trim() || input.authorEmail;
106
+ const url = documentUrl(input.documentId);
107
+
108
+ return notifyActivity({
109
+ candidates: allowed,
110
+ actorEmail: input.authorEmail,
111
+ preferenceKey: CONTENT_USER_PREFS_KEY,
112
+ logLabel: LOG_LABEL,
113
+ send: async (to) => {
114
+ const wasMentioned = mentioned.has(to);
115
+ const lead = wasMentioned
116
+ ? `${emailStrong(actor)} mentioned you in a comment on ${emailStrong(title)}.`
117
+ : input.isReply
118
+ ? `${emailStrong(actor)} replied in a comment thread on ${emailStrong(title)}.`
119
+ : `${emailStrong(actor)} commented on ${emailStrong(title)}.`;
120
+
121
+ const { html, text } = renderEmail({
122
+ preheader: `${actor} commented on ${title}.`,
123
+ heading: wasMentioned
124
+ ? "You were mentioned"
125
+ : input.isReply
126
+ ? "New reply on your document"
127
+ : "New comment",
128
+ paragraphs: [lead, `"${excerpt(input.content)}"`],
129
+ cta: { label: "Open document", url },
130
+ footer:
131
+ "You received this because you own, were mentioned in, or participated in this thread. Turn these off in Documents settings.",
132
+ });
133
+
134
+ await sendEmail({
135
+ to,
136
+ subject: wasMentioned
137
+ ? `${actor} mentioned you on "${title}"`
138
+ : input.isReply
139
+ ? `${actor} replied to a comment on "${title}"`
140
+ : `${actor} commented on "${title}"`,
141
+ html,
142
+ text,
143
+ });
144
+ },
145
+ });
146
+ }
@@ -0,0 +1,17 @@
1
+ import { getSession } from "@agent-native/core/server";
2
+ import { getUserSetting } from "@agent-native/core/settings";
3
+ import { defineEventHandler, setResponseStatus } from "h3";
4
+
5
+ import { CONTENT_USER_PREFS_KEY } from "../../../../shared/content-user-prefs.js";
6
+
7
+ export default defineEventHandler(async (event) => {
8
+ const session = await getSession(event);
9
+ if (!session?.email) {
10
+ setResponseStatus(event, 401);
11
+ return { error: "unauthorized" };
12
+ }
13
+
14
+ const stored = await getUserSetting(session.email, CONTENT_USER_PREFS_KEY);
15
+ // coercion-ok: a null read means this user has no preferences yet, which is a real state.
16
+ return stored ?? {};
17
+ });