@agent-native/core 0.134.1 → 0.135.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 (463) hide show
  1. package/corpus/README.md +3 -3
  2. package/corpus/core/CHANGELOG.md +113 -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/access.ts +44 -2
  48. package/corpus/core/src/sharing/index.ts +5 -0
  49. package/corpus/core/src/sharing/recipients.ts +67 -0
  50. package/corpus/core/src/triggers/actions/list-automations.ts +27 -8
  51. package/corpus/core/src/triggers/actions/manage-automation.ts +17 -6
  52. package/corpus/core/src/triggers/actions.ts +16 -4
  53. package/corpus/core/src/triggers/dispatcher.ts +44 -30
  54. package/corpus/core/src/triggers/routes.ts +19 -6
  55. package/corpus/core/src/vitest-config.ts +68 -0
  56. package/corpus/core/vitest.config.ts +5 -0
  57. package/corpus/templates/analytics/app/pages/Settings.tsx +37 -53
  58. package/corpus/templates/analytics/changelog/2026-08-01-new-error-alerts-now-reach-you-by-email-and-a-scheduled-dash.md +6 -0
  59. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +43 -0
  60. package/corpus/templates/analytics/server/lib/error-capture.ts +5 -1
  61. package/corpus/templates/analytics/vitest.config.ts +16 -12
  62. package/corpus/templates/assets/actions/refresh-generation-run.ts +3 -0
  63. package/corpus/templates/assets/app/components/ui/switch.tsx +1 -0
  64. package/corpus/templates/assets/app/hooks/use-assets-prefs.ts +55 -0
  65. package/corpus/templates/assets/app/i18n-data.ts +30 -0
  66. package/corpus/templates/assets/app/routes/settings.tsx +58 -55
  67. package/corpus/templates/assets/changelog/2026-08-01-get-an-email-when-a-generation-you-started-finishes-or-fails.md +6 -0
  68. package/corpus/templates/assets/server/lib/generation-run-notifications.ts +89 -0
  69. package/corpus/templates/assets/server/lib/video-runs.ts +8 -1
  70. package/corpus/templates/assets/server/routes/_agent-native/assets/user-prefs.get.ts +17 -0
  71. package/corpus/templates/assets/server/routes/_agent-native/assets/user-prefs.put.ts +36 -0
  72. package/corpus/templates/assets/shared/assets-user-prefs.ts +10 -0
  73. package/corpus/templates/assets/vitest.config.ts +16 -12
  74. package/corpus/templates/brain/app/routes/settings.tsx +15 -15
  75. package/corpus/templates/brain/vitest.config.ts +16 -12
  76. package/corpus/templates/calendar/app/pages/Settings.tsx +32 -40
  77. package/corpus/templates/calendar/changelog/2026-08-01-language-and-appearance-now-sit-together-in-one-preferences-.md +6 -0
  78. package/corpus/templates/calendar/vitest.config.ts +16 -12
  79. package/corpus/templates/chat/app/routes/settings.tsx +14 -22
  80. package/corpus/templates/chat/vitest.config.ts +9 -0
  81. package/corpus/templates/clips/.agents/skills/video-editing/SKILL.md +31 -0
  82. package/corpus/templates/clips/AGENTS.md +1 -0
  83. package/corpus/templates/clips/actions/add-comment.ts +12 -1
  84. package/corpus/templates/clips/actions/create-meeting.ts +2 -2
  85. package/corpus/templates/clips/actions/create-recording.ts +2 -2
  86. package/corpus/templates/clips/actions/finalize-recording.ts +42 -6
  87. package/corpus/templates/clips/actions/generate-filmstrip.ts +154 -0
  88. package/corpus/templates/clips/actions/get-recording-player-data.ts +6 -0
  89. package/corpus/templates/clips/actions/import-loom-recording.ts +2 -2
  90. package/corpus/templates/clips/actions/lib/ensure-recording-filmstrip.ts +258 -0
  91. package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +11 -3
  92. package/corpus/templates/clips/actions/react-to-comment.ts +13 -0
  93. package/corpus/templates/clips/actions/react-to-recording.ts +10 -1
  94. package/corpus/templates/clips/actions/reply-to-comment.ts +12 -1
  95. package/corpus/templates/clips/actions/stitch-recordings.ts +2 -2
  96. package/corpus/templates/clips/app/components/editor/editor-layout.tsx +152 -23
  97. package/corpus/templates/clips/app/components/editor/waveform.tsx +237 -75
  98. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +23 -1
  99. package/corpus/templates/clips/app/components/settings/ai-setup-section.tsx +441 -0
  100. package/corpus/templates/clips/app/components/settings/slack-section.tsx +329 -0
  101. package/corpus/templates/clips/app/components/settings/types.ts +14 -0
  102. package/corpus/templates/clips/app/components/settings/video-storage-section.tsx +443 -0
  103. package/corpus/templates/clips/app/hooks/use-secret-status.ts +69 -0
  104. package/corpus/templates/clips/app/i18n/en-US.ts +9 -1
  105. package/corpus/templates/clips/app/lib/video-filmstrip.ts +262 -0
  106. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +165 -1351
  107. package/corpus/templates/clips/changelog/2026-07-27-saved-clips-now-resume-from-the-last-uploaded-byte-after-an-.md +6 -0
  108. package/corpus/templates/clips/changelog/2026-07-31-editor-timeline-now-shows-video-frames-behind-the-waveform-g.md +6 -0
  109. package/corpus/templates/clips/changelog/2026-07-31-email-notifications-now-go-out-when-someone-comments-on-or-r.md +6 -0
  110. package/corpus/templates/clips/changelog/2026-07-31-you-can-now-set-a-default-visibility-for-new-recordings-in-s.md +6 -0
  111. package/corpus/templates/clips/changelog/2026-08-01-email-notifications-for-comments-and-reactions-now-actually-.md +6 -0
  112. package/corpus/templates/clips/changelog/2026-08-03-the-desktop-app-now-opens-directly-beneath-its-tray-icon-on-.md +6 -0
  113. package/corpus/templates/clips/chrome-extension/package.json +1 -0
  114. package/corpus/templates/clips/chrome-extension/vitest.config.ts +9 -0
  115. package/corpus/templates/clips/desktop/package.json +1 -0
  116. package/corpus/templates/clips/desktop/src/app.tsx +17 -1
  117. package/corpus/templates/clips/desktop/src/lib/recorder.ts +331 -36
  118. package/corpus/templates/clips/desktop/src/lib/upload-recovery.ts +129 -0
  119. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +815 -15
  120. package/corpus/templates/clips/desktop/src-tauri/src/tray.rs +61 -11
  121. package/corpus/templates/clips/desktop/vitest.config.ts +9 -0
  122. package/corpus/templates/clips/server/db/schema.ts +17 -0
  123. package/corpus/templates/clips/server/jobs/media-verification.ts +4 -0
  124. package/corpus/templates/clips/server/lib/activity-notifications.ts +185 -0
  125. package/corpus/templates/clips/server/lib/recording-media-cleanup.ts +2 -0
  126. package/corpus/templates/clips/server/lib/recording-upload-state.ts +46 -9
  127. package/corpus/templates/clips/server/lib/recordings.ts +24 -0
  128. package/corpus/templates/clips/server/lib/resumable-session.ts +10 -4
  129. package/corpus/templates/clips/server/lib/transactional-email-templates.ts +109 -0
  130. package/corpus/templates/clips/server/lib/upload-interruption.ts +8 -0
  131. package/corpus/templates/clips/server/lib/upload-lease.ts +50 -4
  132. package/corpus/templates/clips/server/lib/video-filmstrip-sprite.ts +207 -0
  133. package/corpus/templates/clips/server/lib/video-remux.ts +13 -4
  134. package/corpus/templates/clips/server/plugins/db.ts +16 -0
  135. package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +4 -0
  136. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +123 -36
  137. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +159 -29
  138. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/interrupt.post.ts +197 -0
  139. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/reset-chunks.post.ts +193 -39
  140. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/resume.get.ts +191 -12
  141. package/corpus/templates/clips/shared/clips-ai-prefs.ts +6 -0
  142. package/corpus/templates/clips/shared/feature-flags.ts +12 -0
  143. package/corpus/templates/clips/shared/recording-core.ts +4 -0
  144. package/corpus/templates/clips/vitest.config.ts +23 -19
  145. package/corpus/templates/content/actions/_database-utils.ts +113 -4
  146. package/corpus/templates/content/actions/add-comment.ts +15 -1
  147. package/corpus/templates/content/actions/get-document.ts +84 -3
  148. package/corpus/templates/content/actions/list-documents.ts +1 -0
  149. package/corpus/templates/content/app/components/editor/BuilderBodySyncingNotice.tsx +9 -2
  150. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +34 -15
  151. package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +55 -13
  152. package/corpus/templates/content/app/components/editor/body-hydration.ts +12 -6
  153. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +2 -3
  154. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +5 -12
  155. package/corpus/templates/content/app/components/ui/switch.tsx +1 -0
  156. package/corpus/templates/content/app/hooks/use-content-database.ts +16 -27
  157. package/corpus/templates/content/app/hooks/use-content-prefs.ts +55 -0
  158. package/corpus/templates/content/app/hooks/use-create-page.ts +3 -6
  159. package/corpus/templates/content/app/hooks/use-document-properties.ts +9 -16
  160. package/corpus/templates/content/app/hooks/use-document-versions.ts +3 -3
  161. package/corpus/templates/content/app/hooks/use-documents.ts +85 -29
  162. package/corpus/templates/content/app/hooks/use-notion.ts +15 -13
  163. package/corpus/templates/content/app/i18n-data.ts +72 -1
  164. package/corpus/templates/content/app/lib/document-query.ts +36 -0
  165. package/corpus/templates/content/app/routes/_app.settings.tsx +47 -18
  166. package/corpus/templates/content/changelog/2026-07-30-breadcrumb-menus-now-hide-internal-system-pages.md +6 -0
  167. package/corpus/templates/content/changelog/2026-08-01-get-an-email-when-someone-comments-on-replies-in-or-mentions.md +6 -0
  168. package/corpus/templates/content/changelog/2026-08-02-pages-opened-from-a-database-now-keep-that-database-s-fields.md +6 -0
  169. package/corpus/templates/content/server/lib/comment-notifications.ts +146 -0
  170. package/corpus/templates/content/server/lib/document-context.ts +11 -6
  171. package/corpus/templates/content/server/routes/_agent-native/content/user-prefs.get.ts +17 -0
  172. package/corpus/templates/content/server/routes/_agent-native/content/user-prefs.put.ts +36 -0
  173. package/corpus/templates/content/shared/api.ts +8 -0
  174. package/corpus/templates/content/shared/content-user-prefs.ts +11 -0
  175. package/corpus/templates/content/vitest.config.ts +20 -16
  176. package/corpus/templates/crm/app/routes/settings.tsx +14 -22
  177. package/corpus/templates/crm/vitest.config.ts +8 -4
  178. package/corpus/templates/design/app/routes/settings.tsx +14 -22
  179. package/corpus/templates/design/changelog/2026-08-01-review-comments-replies-and-mentions-now-send-an-email-to-th.md +6 -0
  180. package/corpus/templates/design/vitest.config.ts +18 -12
  181. package/corpus/templates/dispatch/app/routes/settings.tsx +26 -40
  182. package/corpus/templates/dispatch/vitest.config.ts +11 -7
  183. package/corpus/templates/factory/app/routes/settings.tsx +14 -22
  184. package/corpus/templates/factory/vitest.config.ts +9 -0
  185. package/corpus/templates/forms/app/routes/_app.settings.tsx +14 -22
  186. package/corpus/templates/forms/vitest.config.ts +27 -23
  187. package/corpus/templates/macros/app/routes/settings.tsx +14 -22
  188. package/corpus/templates/macros/vitest.config.ts +16 -12
  189. package/corpus/templates/mail/app/pages/SettingsPage.tsx +14 -16
  190. package/corpus/templates/mail/vitest.config.ts +16 -12
  191. package/corpus/templates/plan/app/routes/settings.tsx +30 -44
  192. package/corpus/templates/plan/vitest.config.ts +24 -20
  193. package/corpus/templates/slides/actions/add-slide-comment.ts +12 -1
  194. package/corpus/templates/slides/app/components/ui/switch.tsx +1 -0
  195. package/corpus/templates/slides/app/hooks/use-slides-prefs.ts +55 -0
  196. package/corpus/templates/slides/app/i18n/en-US.ts +4 -0
  197. package/corpus/templates/slides/app/routes/settings.tsx +45 -22
  198. package/corpus/templates/slides/changelog/2026-08-01-get-an-email-when-someone-comments-on-or-replies-in-your-dec.md +6 -0
  199. package/corpus/templates/slides/server/lib/comment-notifications.ts +182 -0
  200. package/corpus/templates/slides/server/routes/_agent-native/slides/user-prefs.get.ts +16 -0
  201. package/corpus/templates/slides/server/routes/_agent-native/slides/user-prefs.put.ts +36 -0
  202. package/corpus/templates/slides/shared/slides-user-prefs.ts +11 -0
  203. package/corpus/templates/slides/vitest.config.ts +17 -13
  204. package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +2 -0
  205. package/corpus/templates/tasks/app/routes/settings.tsx +14 -22
  206. package/corpus/templates/tasks/vitest.config.ts +17 -13
  207. package/corpus/toolkit/CHANGELOG.md +9 -0
  208. package/corpus/toolkit/package.json +1 -1
  209. package/corpus/toolkit/vitest.config.ts +3 -0
  210. package/dist/automations/service.d.ts +2 -0
  211. package/dist/automations/service.d.ts.map +1 -1
  212. package/dist/automations/service.js +29 -4
  213. package/dist/automations/service.js.map +1 -1
  214. package/dist/cli/mcp-config-writers.d.ts.map +1 -1
  215. package/dist/cli/mcp-config-writers.js +24 -23
  216. package/dist/cli/mcp-config-writers.js.map +1 -1
  217. package/dist/client/agent-page/AgentJobsTab.d.ts.map +1 -1
  218. package/dist/client/agent-page/AgentJobsTab.js +86 -29
  219. package/dist/client/agent-page/AgentJobsTab.js.map +1 -1
  220. package/dist/client/agent-page/AutomationDetailsDialog.d.ts +21 -0
  221. package/dist/client/agent-page/AutomationDetailsDialog.d.ts.map +1 -0
  222. package/dist/client/agent-page/AutomationDetailsDialog.js +33 -0
  223. package/dist/client/agent-page/AutomationDetailsDialog.js.map +1 -0
  224. package/dist/client/agent-page/AutomationScheduleDialog.d.ts +15 -0
  225. package/dist/client/agent-page/AutomationScheduleDialog.d.ts.map +1 -0
  226. package/dist/client/agent-page/AutomationScheduleDialog.js +46 -0
  227. package/dist/client/agent-page/AutomationScheduleDialog.js.map +1 -0
  228. package/dist/client/agent-page/TimezoneSelect.d.ts +17 -0
  229. package/dist/client/agent-page/TimezoneSelect.d.ts.map +1 -0
  230. package/dist/client/agent-page/TimezoneSelect.js +107 -0
  231. package/dist/client/agent-page/TimezoneSelect.js.map +1 -0
  232. package/dist/client/agent-page/use-jobs.d.ts +20 -7
  233. package/dist/client/agent-page/use-jobs.d.ts.map +1 -1
  234. package/dist/client/agent-page/use-jobs.js +24 -4
  235. package/dist/client/agent-page/use-jobs.js.map +1 -1
  236. package/dist/client/org/OrgSwitcher.d.ts +8 -1
  237. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  238. package/dist/client/org/OrgSwitcher.js +8 -2
  239. package/dist/client/org/OrgSwitcher.js.map +1 -1
  240. package/dist/client/settings/AccountSettingsCard.d.ts.map +1 -1
  241. package/dist/client/settings/AccountSettingsCard.js +10 -9
  242. package/dist/client/settings/AccountSettingsCard.js.map +1 -1
  243. package/dist/client/settings/SchedulingTimezoneField.d.ts +10 -0
  244. package/dist/client/settings/SchedulingTimezoneField.d.ts.map +1 -0
  245. package/dist/client/settings/SchedulingTimezoneField.js +30 -0
  246. package/dist/client/settings/SchedulingTimezoneField.js.map +1 -0
  247. package/dist/client/settings/SettingsRow.d.ts +36 -0
  248. package/dist/client/settings/SettingsRow.d.ts.map +1 -0
  249. package/dist/client/settings/SettingsRow.js +15 -0
  250. package/dist/client/settings/SettingsRow.js.map +1 -0
  251. package/dist/client/settings/agent-settings-search.d.ts.map +1 -1
  252. package/dist/client/settings/agent-settings-search.js +1 -1
  253. package/dist/client/settings/agent-settings-search.js.map +1 -1
  254. package/dist/client/settings/index.d.ts +1 -0
  255. package/dist/client/settings/index.d.ts.map +1 -1
  256. package/dist/client/settings/index.js +1 -0
  257. package/dist/client/settings/index.js.map +1 -1
  258. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  259. package/dist/integrations/identity.d.ts.map +1 -1
  260. package/dist/integrations/identity.js +2 -11
  261. package/dist/integrations/identity.js.map +1 -1
  262. package/dist/jobs/actions/list-automation-runs.d.ts +8 -0
  263. package/dist/jobs/actions/list-automation-runs.d.ts.map +1 -0
  264. package/dist/jobs/actions/list-automation-runs.js +29 -0
  265. package/dist/jobs/actions/list-automation-runs.js.map +1 -0
  266. package/dist/jobs/actions/list-recurring-jobs.d.ts +2 -0
  267. package/dist/jobs/actions/list-recurring-jobs.d.ts.map +1 -1
  268. package/dist/jobs/actions/list-recurring-jobs.js +20 -7
  269. package/dist/jobs/actions/list-recurring-jobs.js.map +1 -1
  270. package/dist/jobs/actions/manage-recurring-job.d.ts +2 -0
  271. package/dist/jobs/actions/manage-recurring-job.js +30 -9
  272. package/dist/jobs/actions/manage-recurring-job.js.map +1 -1
  273. package/dist/jobs/background-automation-runner.d.ts.map +1 -1
  274. package/dist/jobs/background-automation-runner.js +60 -0
  275. package/dist/jobs/background-automation-runner.js.map +1 -1
  276. package/dist/jobs/cron.d.ts +16 -2
  277. package/dist/jobs/cron.d.ts.map +1 -1
  278. package/dist/jobs/cron.js +43 -6
  279. package/dist/jobs/cron.js.map +1 -1
  280. package/dist/jobs/frontmatter.d.ts +12 -0
  281. package/dist/jobs/frontmatter.d.ts.map +1 -1
  282. package/dist/jobs/frontmatter.js +8 -0
  283. package/dist/jobs/frontmatter.js.map +1 -1
  284. package/dist/jobs/run-history.d.ts +56 -0
  285. package/dist/jobs/run-history.d.ts.map +1 -0
  286. package/dist/jobs/run-history.js +169 -0
  287. package/dist/jobs/run-history.js.map +1 -0
  288. package/dist/jobs/scheduler.d.ts.map +1 -1
  289. package/dist/jobs/scheduler.js +51 -18
  290. package/dist/jobs/scheduler.js.map +1 -1
  291. package/dist/jobs/tools.d.ts.map +1 -1
  292. package/dist/jobs/tools.js +37 -6
  293. package/dist/jobs/tools.js.map +1 -1
  294. package/dist/localization/actions/get-localization-preference.d.ts +1 -2
  295. package/dist/localization/actions/get-localization-preference.d.ts.map +1 -1
  296. package/dist/localization/actions/get-localization-preference.js.map +1 -1
  297. package/dist/localization/actions/set-localization-preference.d.ts +3 -3
  298. package/dist/localization/actions/set-localization-preference.d.ts.map +1 -1
  299. package/dist/localization/actions/set-localization-preference.js +33 -8
  300. package/dist/localization/actions/set-localization-preference.js.map +1 -1
  301. package/dist/localization/default-messages.d.ts +25 -0
  302. package/dist/localization/default-messages.d.ts.map +1 -1
  303. package/dist/localization/default-messages.js +25 -0
  304. package/dist/localization/default-messages.js.map +1 -1
  305. package/dist/localization/shared.d.ts +16 -1
  306. package/dist/localization/shared.d.ts.map +1 -1
  307. package/dist/localization/shared.js +22 -4
  308. package/dist/localization/shared.js.map +1 -1
  309. package/dist/localization/user-timezone.d.ts +11 -0
  310. package/dist/localization/user-timezone.d.ts.map +1 -0
  311. package/dist/localization/user-timezone.js +31 -0
  312. package/dist/localization/user-timezone.js.map +1 -0
  313. package/dist/notifications/routes.d.ts +4 -4
  314. package/dist/observability/routes.d.ts +3 -3
  315. package/dist/org/app-roles-handlers.d.ts.map +1 -1
  316. package/dist/org/app-roles-handlers.js +1 -8
  317. package/dist/org/app-roles-handlers.js.map +1 -1
  318. package/dist/org/index.d.ts +1 -0
  319. package/dist/org/index.d.ts.map +1 -1
  320. package/dist/org/index.js +1 -0
  321. package/dist/org/index.js.map +1 -1
  322. package/dist/org/membership.d.ts +6 -0
  323. package/dist/org/membership.d.ts.map +1 -0
  324. package/dist/org/membership.js +16 -0
  325. package/dist/org/membership.js.map +1 -0
  326. package/dist/progress/routes.d.ts +1 -1
  327. package/dist/provider-api/actions/custom-provider-registration.d.ts +13 -13
  328. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  329. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  330. package/dist/review/actions/create-review-comment.d.ts +32 -1
  331. package/dist/review/actions/create-review-comment.d.ts.map +1 -1
  332. package/dist/review/actions/create-review-comment.js +3 -1
  333. package/dist/review/actions/create-review-comment.js.map +1 -1
  334. package/dist/review/actions/reply-review-comment.d.ts +32 -1
  335. package/dist/review/actions/reply-review-comment.d.ts.map +1 -1
  336. package/dist/review/actions/reply-review-comment.js +3 -1
  337. package/dist/review/actions/reply-review-comment.js.map +1 -1
  338. package/dist/review/index.d.ts +1 -0
  339. package/dist/review/index.d.ts.map +1 -1
  340. package/dist/review/index.js +1 -0
  341. package/dist/review/index.js.map +1 -1
  342. package/dist/review/notifications.d.ts +24 -0
  343. package/dist/review/notifications.d.ts.map +1 -0
  344. package/dist/review/notifications.js +123 -0
  345. package/dist/review/notifications.js.map +1 -0
  346. package/dist/review/types.d.ts +5 -0
  347. package/dist/review/types.d.ts.map +1 -1
  348. package/dist/review/types.js.map +1 -1
  349. package/dist/server/action-discovery.d.ts.map +1 -1
  350. package/dist/server/action-discovery.js +4 -0
  351. package/dist/server/action-discovery.js.map +1 -1
  352. package/dist/server/activity-notifications.d.ts +61 -0
  353. package/dist/server/activity-notifications.d.ts.map +1 -0
  354. package/dist/server/activity-notifications.js +103 -0
  355. package/dist/server/activity-notifications.js.map +1 -0
  356. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  357. package/dist/server/app-name.d.ts +18 -0
  358. package/dist/server/app-name.d.ts.map +1 -1
  359. package/dist/server/app-name.js +31 -0
  360. package/dist/server/app-name.js.map +1 -1
  361. package/dist/server/better-auth-instance.js +4 -4
  362. package/dist/server/better-auth-instance.js.map +1 -1
  363. package/dist/server/email-templates.d.ts +12 -0
  364. package/dist/server/email-templates.d.ts.map +1 -1
  365. package/dist/server/email-templates.js +57 -14
  366. package/dist/server/email-templates.js.map +1 -1
  367. package/dist/server/email.d.ts +12 -0
  368. package/dist/server/email.d.ts.map +1 -1
  369. package/dist/server/email.js +52 -5
  370. package/dist/server/email.js.map +1 -1
  371. package/dist/server/index.d.ts +1 -0
  372. package/dist/server/index.d.ts.map +1 -1
  373. package/dist/server/index.js +1 -0
  374. package/dist/server/index.js.map +1 -1
  375. package/dist/server/transcribe-voice.d.ts +1 -1
  376. package/dist/sharing/access.d.ts.map +1 -1
  377. package/dist/sharing/access.js +32 -2
  378. package/dist/sharing/access.js.map +1 -1
  379. package/dist/sharing/index.d.ts +1 -0
  380. package/dist/sharing/index.d.ts.map +1 -1
  381. package/dist/sharing/index.js +1 -0
  382. package/dist/sharing/index.js.map +1 -1
  383. package/dist/sharing/recipients.d.ts +31 -0
  384. package/dist/sharing/recipients.d.ts.map +1 -0
  385. package/dist/sharing/recipients.js +40 -0
  386. package/dist/sharing/recipients.js.map +1 -0
  387. package/dist/templates/chat/app/routes/settings.tsx +14 -22
  388. package/dist/templates/chat/vitest.config.ts +9 -0
  389. package/dist/triggers/actions/list-automations.d.ts +2 -0
  390. package/dist/triggers/actions/list-automations.d.ts.map +1 -1
  391. package/dist/triggers/actions/list-automations.js +19 -8
  392. package/dist/triggers/actions/list-automations.js.map +1 -1
  393. package/dist/triggers/actions/manage-automation.d.ts +6 -0
  394. package/dist/triggers/actions/manage-automation.js +12 -6
  395. package/dist/triggers/actions/manage-automation.js.map +1 -1
  396. package/dist/triggers/actions.d.ts.map +1 -1
  397. package/dist/triggers/actions.js +15 -4
  398. package/dist/triggers/actions.js.map +1 -1
  399. package/dist/triggers/dispatcher.d.ts.map +1 -1
  400. package/dist/triggers/dispatcher.js +19 -17
  401. package/dist/triggers/dispatcher.js.map +1 -1
  402. package/dist/triggers/routes.d.ts.map +1 -1
  403. package/dist/triggers/routes.js +6 -6
  404. package/dist/triggers/routes.js.map +1 -1
  405. package/dist/vitest-config.d.ts +10 -0
  406. package/dist/vitest-config.d.ts.map +1 -0
  407. package/dist/vitest-config.js +55 -0
  408. package/dist/vitest-config.js.map +1 -0
  409. package/package.json +5 -4
  410. package/src/automations/service.ts +41 -4
  411. package/src/cli/mcp-config-writers.ts +27 -21
  412. package/src/client/agent-page/AgentJobsTab.tsx +187 -85
  413. package/src/client/agent-page/AutomationDetailsDialog.tsx +208 -0
  414. package/src/client/agent-page/AutomationScheduleDialog.tsx +180 -0
  415. package/src/client/agent-page/TimezoneSelect.tsx +241 -0
  416. package/src/client/agent-page/use-jobs.ts +50 -10
  417. package/src/client/org/OrgSwitcher.tsx +26 -0
  418. package/src/client/settings/AccountSettingsCard.tsx +35 -31
  419. package/src/client/settings/SchedulingTimezoneField.tsx +71 -0
  420. package/src/client/settings/SettingsRow.tsx +113 -0
  421. package/src/client/settings/agent-settings-search.ts +2 -1
  422. package/src/client/settings/index.ts +6 -0
  423. package/src/integrations/identity.ts +2 -12
  424. package/src/jobs/actions/list-automation-runs.ts +33 -0
  425. package/src/jobs/actions/list-recurring-jobs.ts +27 -6
  426. package/src/jobs/actions/manage-recurring-job.ts +41 -9
  427. package/src/jobs/background-automation-runner.ts +88 -0
  428. package/src/jobs/cron.ts +52 -6
  429. package/src/jobs/frontmatter.ts +20 -0
  430. package/src/jobs/run-history.ts +242 -0
  431. package/src/jobs/scheduler.ts +73 -18
  432. package/src/jobs/tools.ts +54 -6
  433. package/src/localization/actions/get-localization-preference.ts +2 -2
  434. package/src/localization/actions/set-localization-preference.ts +49 -11
  435. package/src/localization/default-messages.ts +27 -0
  436. package/src/localization/shared.ts +39 -6
  437. package/src/localization/user-timezone.ts +36 -0
  438. package/src/org/app-roles-handlers.ts +1 -9
  439. package/src/org/index.ts +2 -0
  440. package/src/org/membership.ts +18 -0
  441. package/src/review/actions/create-review-comment.ts +4 -1
  442. package/src/review/actions/reply-review-comment.ts +4 -1
  443. package/src/review/index.ts +5 -0
  444. package/src/review/notifications.ts +163 -0
  445. package/src/review/types.ts +7 -0
  446. package/src/server/action-discovery.ts +4 -0
  447. package/src/server/activity-notifications.ts +174 -0
  448. package/src/server/app-name.ts +31 -0
  449. package/src/server/better-auth-instance.ts +4 -4
  450. package/src/server/email-templates.ts +70 -14
  451. package/src/server/email.ts +66 -3
  452. package/src/server/index.ts +10 -0
  453. package/src/sharing/access.ts +44 -2
  454. package/src/sharing/index.ts +5 -0
  455. package/src/sharing/recipients.ts +67 -0
  456. package/src/templates/chat/app/routes/settings.tsx +14 -22
  457. package/src/templates/chat/vitest.config.ts +9 -0
  458. package/src/triggers/actions/list-automations.ts +27 -8
  459. package/src/triggers/actions/manage-automation.ts +17 -6
  460. package/src/triggers/actions.ts +16 -4
  461. package/src/triggers/dispatcher.ts +44 -30
  462. package/src/triggers/routes.ts +19 -6
  463. package/src/vitest-config.ts +68 -0
@@ -82,6 +82,31 @@ const _eventSubscriptions = new Map<string, string>();
82
82
  const _dispatchingTriggers = new Set<string>();
83
83
  let _deps: TriggerDispatcherDeps | null = null;
84
84
 
85
+ /**
86
+ * Record that a tick evaluated this trigger and declined to dispatch it.
87
+ * `lastRun` stays untouched — nothing ran — and an unchanged outcome is not
88
+ * re-persisted, so a permanently blocked trigger neither reports phantom runs
89
+ * nor rewrites its resource on every matching event.
90
+ */
91
+ async function recordTriggerSkip(
92
+ resource: Resource,
93
+ meta: TriggerFrontmatter,
94
+ body: string,
95
+ status: "skipped" | "error",
96
+ reason: string | undefined,
97
+ ): Promise<void> {
98
+ const unchanged = meta.lastStatus === status && meta.lastError === reason;
99
+ meta.lastCheck = new Date().toISOString();
100
+ meta.lastStatus = status;
101
+ meta.lastError = reason;
102
+ if (unchanged) return;
103
+ await resourcePut(
104
+ resource.owner,
105
+ resource.path,
106
+ buildTriggerContent(meta, body),
107
+ );
108
+ }
109
+
85
110
  /**
86
111
  * Initialize the trigger dispatcher. Call once at server startup.
87
112
  * Loads all event-triggered jobs and subscribes to their events.
@@ -173,25 +198,22 @@ async function handleEvent(
173
198
  meta,
174
199
  );
175
200
  } catch {
176
- meta.lastRun = new Date().toISOString();
177
- meta.lastStatus = "skipped";
178
- meta.lastError =
179
- "Could not verify the automation execution identity.";
180
- await resourcePut(
181
- resource.owner,
182
- resource.path,
183
- buildTriggerContent(meta, body),
201
+ await recordTriggerSkip(
202
+ resource,
203
+ meta,
204
+ body,
205
+ "skipped",
206
+ "Could not verify the automation execution identity.",
184
207
  );
185
208
  continue;
186
209
  }
187
210
  if (!resolved.ok) {
188
- meta.lastRun = new Date().toISOString();
189
- meta.lastStatus = "skipped";
190
- meta.lastError = resolved.reason;
191
- await resourcePut(
192
- resource.owner,
193
- resource.path,
194
- buildTriggerContent(meta, body),
211
+ await recordTriggerSkip(
212
+ resource,
213
+ meta,
214
+ body,
215
+ "skipped",
216
+ resolved.reason,
195
217
  );
196
218
  continue;
197
219
  }
@@ -206,13 +228,12 @@ async function handleEvent(
206
228
  const userApiKey = await getOwnerActiveApiKey(owner);
207
229
  const apiKey = userApiKey || _deps.apiKey;
208
230
  if (!apiKey) {
209
- meta.lastRun = new Date().toISOString();
210
- meta.lastStatus = "error";
211
- meta.lastError = "No API key is available for this automation";
212
- await resourcePut(
213
- resource.owner,
214
- resource.path,
215
- buildTriggerContent(meta, body),
231
+ await recordTriggerSkip(
232
+ resource,
233
+ meta,
234
+ body,
235
+ "error",
236
+ "No API key is available for this automation",
216
237
  );
217
238
  console.warn(`[triggers] ${meta.lastError}: "${resource.path}"`);
218
239
  continue;
@@ -221,14 +242,7 @@ async function handleEvent(
221
242
  // Evaluate condition
222
243
  const matches = await evaluateCondition(meta.condition, payload, apiKey);
223
244
  if (!matches) {
224
- meta.lastRun = new Date().toISOString();
225
- meta.lastStatus = "skipped";
226
- meta.lastError = undefined;
227
- await resourcePut(
228
- resource.owner,
229
- resource.path,
230
- buildTriggerContent(meta, body),
231
- );
245
+ await recordTriggerSkip(resource, meta, body, "skipped", undefined);
232
246
  continue;
233
247
  }
234
248
 
@@ -7,7 +7,12 @@ import {
7
7
 
8
8
  import { canUpdateAutomationResource } from "../automations/service.js";
9
9
  import { getDbExec } from "../db/client.js";
10
- import { nextOccurrence, describeCron, isValidCron } from "../jobs/cron.js";
10
+ import {
11
+ nextOccurrence,
12
+ describeCron,
13
+ effectiveTimezone,
14
+ isValidCron,
15
+ } from "../jobs/cron.js";
11
16
  import {
12
17
  buildJobResourceContent,
13
18
  parseJobResource,
@@ -88,10 +93,10 @@ function normalizeAutomationPath(input: SetAutomationEnabledInput): string {
88
93
  return path;
89
94
  }
90
95
 
91
- function scheduleDescription(schedule: string | undefined): string | undefined {
96
+ function scheduleDescription(schedule?: string, timezone?: string) {
92
97
  if (!schedule) return undefined;
93
98
  try {
94
- return describeCron(schedule);
99
+ return describeCron(schedule, effectiveTimezone(timezone));
95
100
  } catch {
96
101
  return schedule;
97
102
  }
@@ -106,7 +111,11 @@ function nextRunForMeta(meta: TriggerFrontmatter): string | undefined {
106
111
  isValidCron(meta.schedule)
107
112
  ) {
108
113
  try {
109
- return nextOccurrence(meta.schedule).toISOString();
114
+ return nextOccurrence(
115
+ meta.schedule,
116
+ undefined,
117
+ meta.timezone,
118
+ ).toISOString();
110
119
  } catch {
111
120
  return undefined;
112
121
  }
@@ -194,7 +203,7 @@ async function resourceToAutomationItem(
194
203
  triggerType: meta.triggerType,
195
204
  event: meta.event,
196
205
  schedule: meta.schedule || undefined,
197
- scheduleDescription: scheduleDescription(meta.schedule),
206
+ scheduleDescription: scheduleDescription(meta.schedule, meta.timezone),
198
207
  condition: meta.condition,
199
208
  mode: meta.mode,
200
209
  domain: meta.domain,
@@ -292,7 +301,11 @@ export async function setAutomationEnabledForOwner(
292
301
  meta.schedule &&
293
302
  isValidCron(meta.schedule)
294
303
  ) {
295
- parsed.meta.nextRun = nextOccurrence(meta.schedule).toISOString();
304
+ parsed.meta.nextRun = nextOccurrence(
305
+ meta.schedule,
306
+ undefined,
307
+ meta.timezone,
308
+ ).toISOString();
296
309
  }
297
310
 
298
311
  const updatedContent = buildJobResourceContent(parsed.meta, parsed.body);
@@ -0,0 +1,68 @@
1
+ import type { ViteUserConfig } from "vitest/config";
2
+
3
+ /**
4
+ * Base vitest config that caps a suite's worker pool at a share of the machine.
5
+ *
6
+ * Vitest sizes its pool at `cores - 1` with no awareness of peer processes, so
7
+ * several suites running at once — concurrent agent sessions, or a workspace
8
+ * running packages in parallel — each try to claim the whole box. Set
9
+ * `VITEST_CONCURRENCY` to a percentage or a worker count to change the lid; a
10
+ * package that needs its own value sets `test.maxWorkers` in its own config,
11
+ * which wins the merge.
12
+ *
13
+ * This ships from core rather than living at the monorepo root because template
14
+ * directories are scaffolded out verbatim as standalone apps — a config that
15
+ * reaches above the template would resolve to nothing in the generated app.
16
+ *
17
+ * Keep this module free of runtime imports so loading it costs a config file
18
+ * nothing beyond reading the environment.
19
+ */
20
+
21
+ const DEFAULT_MAX_WORKERS = "25%";
22
+ const ENV_KEYS = ["VITEST_CONCURRENCY", "AGENT_NATIVE_VITEST_CONCURRENCY"];
23
+
24
+ /**
25
+ * Resolve the worker lid from the environment, throwing on anything that is
26
+ * neither a percentage nor a worker count. A bad value must not quietly become
27
+ * the default — that reads as "configured" while running at some other size.
28
+ */
29
+ export function resolveMaxWorkers(
30
+ env: NodeJS.ProcessEnv = process.env,
31
+ ): string | number {
32
+ const rawMaxWorkers = env.VITEST_MAX_WORKERS;
33
+ if (rawMaxWorkers?.includes("%")) {
34
+ throw new Error(
35
+ `VITEST_MAX_WORKERS=${rawMaxWorkers} is not a percentage to vitest — it parses as ` +
36
+ `${Number.parseInt(rawMaxWorkers)} workers. Use VITEST_CONCURRENCY for percentages, ` +
37
+ `or an integer for VITEST_MAX_WORKERS.`,
38
+ );
39
+ }
40
+
41
+ const key = ENV_KEYS.find((name) => env[name]);
42
+ if (!key) return DEFAULT_MAX_WORKERS;
43
+
44
+ const value = env[key]!.trim();
45
+ if (/^\d+%$/.test(value)) {
46
+ const percent = Number.parseInt(value);
47
+ if (percent < 1 || percent > 100) {
48
+ throw new Error(`${key}=${value} is out of range — use 1% to 100%.`);
49
+ }
50
+ return value;
51
+ }
52
+
53
+ const count = Number(value);
54
+ if (!Number.isInteger(count) || count < 1) {
55
+ throw new Error(
56
+ `${key}=${value} is not a percentage like "25%" or a worker count like "2".`,
57
+ );
58
+ }
59
+ return count;
60
+ }
61
+
62
+ const vitestBaseConfig: ViteUserConfig = {
63
+ test: {
64
+ maxWorkers: resolveMaxWorkers(),
65
+ },
66
+ };
67
+
68
+ export default vitestBaseConfig;
@@ -0,0 +1,5 @@
1
+ // Core owns the shared base config, so it reads the source directly rather than
2
+ // going through the monorepo-root re-export.
3
+ import baseConfig from "./src/vitest-config";
4
+
5
+ export default baseConfig;
@@ -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 SettingsTabItem,
@@ -68,47 +70,41 @@ export default function Settings() {
68
70
  generalSearchEntries={generalSearchEntries}
69
71
  general={
70
72
  <div className="mx-auto w-full max-w-2xl space-y-6">
71
- <Card
72
- id="credentials"
73
- className="bg-card border-border/50 scroll-mt-16"
74
- >
75
- <CardHeader>
76
- <CardTitle className="text-base">
77
- {t("settings.credentials")}
78
- </CardTitle>
79
- </CardHeader>
80
- <CardContent>
81
- <p className="text-sm text-muted-foreground mb-3">
82
- {t("settings.credentialsDescription")}
83
- </p>
84
- <Button variant="outline" size="sm" asChild>
85
- <Link to="/data-sources">
86
- {t("settings.manageDataSources")}
87
- </Link>
88
- </Button>
89
- </CardContent>
90
- </Card>
91
-
92
- <Card
93
- id="dashboard-templates"
94
- className="bg-card border-border/50 scroll-mt-16"
95
- >
96
- <CardHeader>
97
- <CardTitle className="text-base">
98
- {t("settings.dashboardTemplates")}
99
- </CardTitle>
100
- <CardDescription>
101
- {t("settings.dashboardTemplatesDescription")}
102
- </CardDescription>
103
- </CardHeader>
104
- <CardContent>
105
- <Button variant="outline" size="sm" asChild>
106
- <Link to="/catalog">
107
- {t("settings.openDashboardTemplates")}
108
- </Link>
109
- </Button>
110
- </CardContent>
111
- </Card>
73
+ <SettingsGroup className="bg-card border-border/50">
74
+ <SettingsRow
75
+ id="credentials"
76
+ label={t("settings.credentials")}
77
+ description={t("settings.credentialsDescription")}
78
+ control={
79
+ <Button variant="outline" size="sm" asChild>
80
+ <Link to="/data-sources">
81
+ {t("settings.manageDataSources")}
82
+ </Link>
83
+ </Button>
84
+ }
85
+ />
86
+ <SettingsRow
87
+ id="dashboard-templates"
88
+ label={t("settings.dashboardTemplates")}
89
+ description={t("settings.dashboardTemplatesDescription")}
90
+ control={
91
+ <Button variant="outline" size="sm" asChild>
92
+ <Link to="/catalog">
93
+ {t("settings.openDashboardTemplates")}
94
+ </Link>
95
+ </Button>
96
+ }
97
+ />
98
+ <SettingsRow
99
+ id="language"
100
+ label={t("settings.languageTitle")}
101
+ control={
102
+ <div className="w-56">
103
+ <LanguagePicker label={t("settings.languageLabel")} />
104
+ </div>
105
+ }
106
+ />
107
+ </SettingsGroup>
112
108
 
113
109
  {replayStorageStatus.data?.configured ? (
114
110
  <Card
@@ -129,18 +125,6 @@ export default function Settings() {
129
125
  </Card>
130
126
  ) : null}
131
127
 
132
- <Card id="language" className="bg-card border-border/50 scroll-mt-16">
133
- <CardHeader>
134
- <CardTitle className="text-base">
135
- {t("settings.languageTitle")}
136
- </CardTitle>
137
- </CardHeader>
138
- <CardContent className="max-w-xs space-y-1.5">
139
- <Label>{t("settings.languageLabel")}</Label>
140
- <LanguagePicker label={t("settings.languageLabel")} />
141
- </CardContent>
142
- </Card>
143
-
144
128
  <Card id="about" className="bg-card border-border/50 scroll-mt-16">
145
129
  <CardHeader>
146
130
  <CardTitle className="text-base">{t("settings.about")}</CardTitle>
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-08-01
4
+ ---
5
+
6
+ New error alerts now reach you by email, and a scheduled dashboard report that gives up retrying tells its owner instead of only logging
@@ -1,3 +1,4 @@
1
+ import { notifyWithDelivery } from "@agent-native/core/notifications";
1
2
  import { runWithRequestContext } from "@agent-native/core/server/request-context";
2
3
 
3
4
  import { sendDashboardReportSubscription } from "../lib/dashboard-report";
@@ -57,6 +58,46 @@ async function persistDashboardReportCaptureOutcome(
57
58
  }
58
59
  }
59
60
 
61
+ /**
62
+ * Tell the owner when a scheduled report is finally given up on. Retries stay
63
+ * silent — only an exhausted retry window means the report they expected will
64
+ * never arrive, and a log line alone leaves them waiting on nothing.
65
+ */
66
+ async function notifyDashboardReportGaveUp(
67
+ sub: {
68
+ id: string;
69
+ ownerEmail: string;
70
+ orgId?: string | null;
71
+ dashboardId?: string;
72
+ },
73
+ reason: string,
74
+ ): Promise<void> {
75
+ try {
76
+ await notifyWithDelivery(
77
+ {
78
+ severity: "warning",
79
+ title: "Scheduled dashboard report failed",
80
+ body: `The scheduled report for subscription ${sub.id} could not be delivered: ${reason}`,
81
+ channels: ["inbox", "email"],
82
+ metadata: {
83
+ kind: "dashboard_report_failure",
84
+ subscriptionId: sub.id,
85
+ path: "/dashboard-reports",
86
+ // The email channel is a no-op without explicit recipients.
87
+ emailRecipients: [sub.ownerEmail],
88
+ emailSubject: "Your scheduled dashboard report did not send",
89
+ },
90
+ },
91
+ { owner: sub.ownerEmail },
92
+ );
93
+ } catch (err) {
94
+ console.error(
95
+ `[dashboard-report] Could not notify owner of subscription ${sub.id} about the failure:`,
96
+ err instanceof Error ? err.message : String(err),
97
+ );
98
+ }
99
+ }
100
+
60
101
  function maxReportsPerSweep(): number {
61
102
  if (serverlessDashboardReportRuntime()) {
62
103
  return SERVERLESS_MAX_REPORTS_PER_SWEEP;
@@ -127,6 +168,7 @@ export async function runDashboardReportsOnce(): Promise<{
127
168
  });
128
169
  } else {
129
170
  await persistDashboardReportResult(sub, "error", retryMessage);
171
+ await notifyDashboardReportGaveUp(sub, degradedReason);
130
172
  }
131
173
  continue;
132
174
  }
@@ -155,6 +197,7 @@ export async function runDashboardReportsOnce(): Promise<{
155
197
  });
156
198
  } else {
157
199
  await persistDashboardReportResult(sub, "error", message);
200
+ await notifyDashboardReportGaveUp(sub, message);
158
201
  }
159
202
  }
160
203
  }
@@ -996,12 +996,16 @@ async function notifyNewIssue(
996
996
  severity: issue.level === "fatal" ? "critical" : "warning",
997
997
  title: `New error: ${issue.title}`,
998
998
  body: "A new JavaScript error was captured in your app.",
999
- channels: ["inbox"],
999
+ channels: ["inbox", "email"],
1000
1000
  metadata: {
1001
1001
  kind: "error_issue",
1002
1002
  issueId: issue.issueId,
1003
1003
  level: issue.level,
1004
1004
  path: `/monitoring?view=errors&issue=${issue.issueId}`,
1005
+ // The email channel is a no-op without explicit recipients, so a
1006
+ // captured issue would otherwise never leave the in-app inbox.
1007
+ emailRecipients: [scope.ownerEmail],
1008
+ emailSubject: `New error in your app: ${issue.title}`,
1005
1009
  },
1006
1010
  },
1007
1011
  // The notification inbox is owner-scoped; the issue's owner is the analytics
@@ -1,16 +1,20 @@
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: ["**/node_modules/**", "**/.git/**", "**/dist/**"],
15
- },
16
- });
15
+ test: {
16
+ include: ["**/*.{test,spec}.?(c|m)[jt]s?(x)"],
17
+ exclude: ["**/node_modules/**", "**/.git/**", "**/dist/**"],
18
+ },
19
+ }),
20
+ );
@@ -4,6 +4,7 @@ import { eq } from "drizzle-orm";
4
4
  import { z } from "zod";
5
5
 
6
6
  import { getDb, schema } from "../server/db/index.js";
7
+ import { notifyGenerationRunFinished } from "../server/lib/generation-run-notifications.js";
7
8
  import { nowIso, parseJson } from "../server/lib/json.js";
8
9
  import { completeVideoGenerationRun } from "../server/lib/video-runs.js";
9
10
  import { serializeAsset, serializeGenerationRun } from "./_helpers.js";
@@ -88,6 +89,7 @@ async function refreshImageRun(
88
89
  .set({ status: "completed", completedAt })
89
90
  .where(eq(schema.assetGenerationRuns.id, run.id));
90
91
  nextRun = { ...run, status: "completed", completedAt };
92
+ await notifyGenerationRunFinished(nextRun, "completed");
91
93
  }
92
94
  await syncImageVariantSlot(nextRun, "ready", { asset: outputAsset });
93
95
  return { run: nextRun, assets };
@@ -119,6 +121,7 @@ async function refreshImageRun(
119
121
  await syncImageVariantSlot(failedRun, "failed", {
120
122
  error: INTERRUPTED_IMAGE_RUN_ERROR,
121
123
  });
124
+ await notifyGenerationRunFinished(failedRun, "failed");
122
125
  return { run: failedRun, assets: [] };
123
126
  }
124
127
 
@@ -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 { AssetsUserPrefs } from "@shared/assets-user-prefs";
3
+ import { useCallback, useEffect, useState } from "react";
4
+
5
+ const PREFS_PATH = "/_agent-native/assets/user-prefs";
6
+
7
+ export interface AssetsPrefsState {
8
+ prefs: AssetsUserPrefs;
9
+ loading: boolean;
10
+ /** Applies the patch optimistically and rolls back if the write fails. */
11
+ save: (patch: AssetsUserPrefs) => Promise<void>;
12
+ }
13
+
14
+ export function useAssetsPrefs(): AssetsPrefsState {
15
+ const [prefs, setPrefs] = useState<AssetsUserPrefs>({});
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 AssetsUserPrefs);
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: AssetsUserPrefs) => {
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
+ }
@@ -628,6 +628,9 @@ const enUS = {
628
628
  languageDescription:
629
629
  "Choose the interface language for this Assets workspace.",
630
630
  languageLabel: "Interface language",
631
+ emailNotifications: "Email notifications",
632
+ emailNotificationsDescription:
633
+ "Get an email when a generation you started finishes or fails.",
631
634
  setupTitle: "Assets setup",
632
635
  setupDescription: "Two essentials: generation and durable storage.",
633
636
  setupReady: "ready",
@@ -3926,6 +3929,9 @@ export const messagesByLocale = {
3926
3929
  languageTitle: "语言",
3927
3930
  languageDescription: "选择此 Assets 工作区的界面语言。",
3928
3931
  languageLabel: "界面语言",
3932
+ emailNotifications: "邮件通知",
3933
+ emailNotificationsDescription:
3934
+ "当你发起的生成完成或失败时,收到邮件通知。",
3929
3935
 
3930
3936
  addBuilderGeminiOrOpenAI:
3931
3937
  "在生成新资产之前添加 Builder、Gemini 或 OpenAI。",
@@ -4727,6 +4733,9 @@ export const messagesByLocale = {
4727
4733
  languageDescription:
4728
4734
  "Elige el idioma de la interfaz para este workspace.",
4729
4735
  languageLabel: "Idioma de la interfaz",
4736
+ emailNotifications: "Notificaciones por correo",
4737
+ emailNotificationsDescription:
4738
+ "Recibe un correo cuando una generación que iniciaste termine o falle.",
4730
4739
 
4731
4740
  addBuilderGeminiOrOpenAI:
4732
4741
  "Agregue Builder, Gemini o OpenAI antes de generar nuevos activos.",
@@ -5191,6 +5200,9 @@ export const messagesByLocale = {
5191
5200
  languageDescription:
5192
5201
  "Choisissez la langue de l'interface pour cet espace Assets.",
5193
5202
  languageLabel: "Langue de l'interface",
5203
+ emailNotifications: "Notifications par e-mail",
5204
+ emailNotificationsDescription:
5205
+ "Recevez un e-mail lorsqu’une génération que vous avez lancée se termine ou échoue.",
5194
5206
 
5195
5207
  addBuilderGeminiOrOpenAI:
5196
5208
  "Ajoutez Builder, Gemini ou OpenAI avant de générer de nouveaux actifs.",
@@ -5644,6 +5656,9 @@ export const messagesByLocale = {
5644
5656
  languageDescription:
5645
5657
  "Wähle die Oberflächensprache für diesen Assets-Workspace.",
5646
5658
  languageLabel: "Oberflächensprache",
5659
+ emailNotifications: "E-Mail-Benachrichtigungen",
5660
+ emailNotificationsDescription:
5661
+ "Erhalte eine E-Mail, wenn eine von dir gestartete Generierung fertig ist oder fehlschlägt.",
5647
5662
  setupTitle: "Assets-Einrichtung",
5648
5663
  setupDescription:
5649
5664
  "Zwei Grundlagen: Generierung und dauerhafter Speicher.",
@@ -5993,6 +6008,9 @@ export const messagesByLocale = {
5993
6008
  languageDescription:
5994
6009
  "この Assets ワークスペースのインターフェース言語を選択します。",
5995
6010
  languageLabel: "インターフェース言語",
6011
+ emailNotifications: "メール通知",
6012
+ emailNotificationsDescription:
6013
+ "開始した生成が完了または失敗したときにメールを受け取ります。",
5996
6014
  setupTitle: "Assets の設定",
5997
6015
  setupDescription: "必要なものは生成と永続ストレージの 2 つです。",
5998
6016
  setupReady: "準備完了",
@@ -6338,6 +6356,9 @@ export const messagesByLocale = {
6338
6356
  languageDescription:
6339
6357
  "이 Assets 워크스페이스의 인터페이스 언어를 선택하세요.",
6340
6358
  languageLabel: "인터페이스 언어",
6359
+ emailNotifications: "이메일 알림",
6360
+ emailNotificationsDescription:
6361
+ "내가 시작한 생성이 완료되거나 실패하면 이메일을 받습니다.",
6341
6362
  setupTitle: "Assets 설정",
6342
6363
  setupDescription: "필수 항목 두 가지: 생성과 영구 저장소.",
6343
6364
  setupReady: "준비됨",
@@ -6685,6 +6706,9 @@ export const messagesByLocale = {
6685
6706
  languageDescription:
6686
6707
  "Escolha o idioma da interface deste workspace de Assets.",
6687
6708
  languageLabel: "Idioma da interface",
6709
+ emailNotifications: "Notificações por e-mail",
6710
+ emailNotificationsDescription:
6711
+ "Receba um e-mail quando uma geração que você iniciou terminar ou falhar.",
6688
6712
 
6689
6713
  addBuilderGeminiOrOpenAI:
6690
6714
  "Adicione Builder, Gemini ou OpenAI antes de gerar novos ativos.",
@@ -7130,6 +7154,9 @@ export const messagesByLocale = {
7130
7154
  languageTitle: "भाषा",
7131
7155
  languageDescription: "इस Assets workspace की इंटरफ़ेस भाषा चुनें।",
7132
7156
  languageLabel: "इंटरफ़ेस भाषा",
7157
+ emailNotifications: "ईमेल सूचनाएँ",
7158
+ emailNotificationsDescription:
7159
+ "आपके द्वारा शुरू किया गया जनरेशन पूरा या विफल होने पर ईमेल पाएँ।",
7133
7160
  setupTitle: "Assets सेटअप",
7134
7161
  setupDescription: "दो जरूरी चीजें: generation और durable storage.",
7135
7162
  setupReady: "तैयार",
@@ -7475,6 +7502,9 @@ export const messagesByLocale = {
7475
7502
  languageTitle: "اللغة",
7476
7503
  languageDescription: "اختر لغة الواجهة لمساحة عمل Assets هذه.",
7477
7504
  languageLabel: "لغة الواجهة",
7505
+ emailNotifications: "إشعارات البريد الإلكتروني",
7506
+ emailNotificationsDescription:
7507
+ "احصل على بريد إلكتروني عند اكتمال عملية إنشاء بدأتها أو فشلها.",
7478
7508
 
7479
7509
  addBuilderGeminiOrOpenAI:
7480
7510
  "قم بإضافة Builder، أو Gemini، أو OpenAI قبل إنشاء أصول جديدة.",