@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,7 +1,7 @@
1
1
  import { defineEventHandler, getMethod, setResponseStatus, } from "h3";
2
2
  import { canUpdateAutomationResource } from "../automations/service.js";
3
3
  import { getDbExec } from "../db/client.js";
4
- import { nextOccurrence, describeCron, isValidCron } from "../jobs/cron.js";
4
+ import { nextOccurrence, describeCron, effectiveTimezone, isValidCron, } from "../jobs/cron.js";
5
5
  import { buildJobResourceContent, parseJobResource, } from "../jobs/frontmatter.js";
6
6
  import { getOrgContext } from "../org/context.js";
7
7
  import { organizationIdFromResourceOwner, organizationResourceOwner, resourceGetByPath, resourceListAllOwners, resourcePut, SHARED_OWNER, } from "../resources/store.js";
@@ -31,11 +31,11 @@ function normalizeAutomationPath(input) {
31
31
  }
32
32
  return path;
33
33
  }
34
- function scheduleDescription(schedule) {
34
+ function scheduleDescription(schedule, timezone) {
35
35
  if (!schedule)
36
36
  return undefined;
37
37
  try {
38
- return describeCron(schedule);
38
+ return describeCron(schedule, effectiveTimezone(timezone));
39
39
  }
40
40
  catch {
41
41
  return schedule;
@@ -49,7 +49,7 @@ function nextRunForMeta(meta) {
49
49
  meta.schedule &&
50
50
  isValidCron(meta.schedule)) {
51
51
  try {
52
- return nextOccurrence(meta.schedule).toISOString();
52
+ return nextOccurrence(meta.schedule, undefined, meta.timezone).toISOString();
53
53
  }
54
54
  catch {
55
55
  return undefined;
@@ -121,7 +121,7 @@ async function resourceToAutomationItem(event, userEmail, resource) {
121
121
  triggerType: meta.triggerType,
122
122
  event: meta.event,
123
123
  schedule: meta.schedule || undefined,
124
- scheduleDescription: scheduleDescription(meta.schedule),
124
+ scheduleDescription: scheduleDescription(meta.schedule, meta.timezone),
125
125
  condition: meta.condition,
126
126
  mode: meta.mode,
127
127
  domain: meta.domain,
@@ -191,7 +191,7 @@ export async function setAutomationEnabledForOwner(event, userEmail, input) {
191
191
  meta.triggerType !== "event" &&
192
192
  meta.schedule &&
193
193
  isValidCron(meta.schedule)) {
194
- parsed.meta.nextRun = nextOccurrence(meta.schedule).toISOString();
194
+ parsed.meta.nextRun = nextOccurrence(meta.schedule, undefined, meta.timezone).toISOString();
195
195
  }
196
196
  const updatedContent = buildJobResourceContent(parsed.meta, parsed.body);
197
197
  await resourcePut(resource.owner, resource.path, updatedContent);
@@ -1 +1 @@
1
- {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/triggers/routes.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n defineEventHandler,\n getMethod,\n setResponseStatus,\n type H3Event,\n} from \"h3\";\n\nimport { canUpdateAutomationResource } from \"../automations/service.js\";\nimport { getDbExec } from \"../db/client.js\";\nimport { nextOccurrence, describeCron, isValidCron } from \"../jobs/cron.js\";\nimport {\n buildJobResourceContent,\n parseJobResource,\n type JobFrontmatter,\n} from \"../jobs/frontmatter.js\";\nimport { getOrgContext } from \"../org/context.js\";\nimport {\n organizationIdFromResourceOwner,\n organizationResourceOwner,\n resourceGetByPath,\n resourceListAllOwners,\n resourcePut,\n SHARED_OWNER,\n type Resource,\n} from \"../resources/store.js\";\nimport { getSession } from \"../server/auth.js\";\nimport { readBody } from \"../server/h3-helpers.js\";\nimport { refreshEventSubscriptions } from \"./dispatcher.js\";\nimport type { TriggerFrontmatter } from \"./types.js\";\n\nexport interface AutomationRouteItem {\n id: string;\n name: string;\n path: string;\n owner: string;\n canUpdate: boolean;\n triggerType: TriggerFrontmatter[\"triggerType\"];\n event?: string;\n schedule?: string;\n scheduleDescription?: string;\n condition?: string;\n mode: TriggerFrontmatter[\"mode\"];\n domain?: string;\n enabled: boolean;\n lastStatus?: TriggerFrontmatter[\"lastStatus\"];\n lastRun?: string;\n lastError?: string;\n nextRun?: string;\n createdBy?: string;\n body: string;\n}\n\ninterface SetAutomationEnabledInput {\n owner?: string;\n path?: string;\n name?: string;\n enabled: boolean;\n}\n\nfunction automationName(path: string): string {\n return path.replace(/^jobs\\//, \"\").replace(/\\.md$/, \"\");\n}\n\nfunction asTriggerFrontmatter(meta: JobFrontmatter): TriggerFrontmatter {\n return {\n ...meta,\n triggerType: meta.triggerType ?? \"schedule\",\n mode: meta.mode ?? \"agentic\",\n };\n}\n\nfunction normalizeAutomationPath(input: SetAutomationEnabledInput): string {\n const rawPath = input.path ?? (input.name ? `jobs/${input.name}.md` : \"\");\n const path = rawPath.replace(/^\\/+/, \"\");\n if (\n !path.startsWith(\"jobs/\") ||\n !path.endsWith(\".md\") ||\n path.endsWith(\".keep\") ||\n path.includes(\"..\")\n ) {\n throw Object.assign(\n new Error(\"A valid jobs/*.md automation path is required\"),\n {\n statusCode: 400,\n },\n );\n }\n return path;\n}\n\nfunction scheduleDescription(schedule: string | undefined): string | undefined {\n if (!schedule) return undefined;\n try {\n return describeCron(schedule);\n } catch {\n return schedule;\n }\n}\n\nfunction nextRunForMeta(meta: TriggerFrontmatter): string | undefined {\n if (meta.nextRun) return meta.nextRun;\n if (\n meta.enabled &&\n meta.triggerType !== \"event\" &&\n meta.schedule &&\n isValidCron(meta.schedule)\n ) {\n try {\n return nextOccurrence(meta.schedule).toISOString();\n } catch {\n return undefined;\n }\n }\n return undefined;\n}\n\nasync function currentUserCanUpdateAutomation(\n event: H3Event,\n userEmail: string,\n resource: Resource,\n meta: TriggerFrontmatter,\n): Promise<boolean> {\n const resourceOwner = resource.owner;\n if (resourceOwner === userEmail) return true;\n const resourceOrgId = organizationIdFromResourceOwner(resourceOwner);\n if (resourceOrgId) {\n try {\n const org = await getOrgContext(event);\n if (org.orgId !== resourceOrgId) return false;\n return await canUpdateAutomationResource(\n { userEmail, orgId: resourceOrgId },\n resource,\n );\n } catch {\n return false;\n }\n }\n if (resourceOwner !== SHARED_OWNER) return false;\n\n if (meta.orgId) {\n const activeOrgId = await getOrgContext(event)\n .then((context) => context.orgId)\n .catch(() => null);\n if (activeOrgId !== meta.orgId) return false;\n }\n\n if (\n meta.createdBy &&\n meta.createdBy.toLowerCase() === userEmail.toLowerCase()\n ) {\n return true;\n }\n\n let orgId = meta.orgId;\n if (!orgId) {\n try {\n orgId = (await getOrgContext(event)).orgId ?? undefined;\n } catch {\n orgId = undefined;\n }\n }\n if (!orgId) return false;\n\n try {\n const { rows } = await getDbExec().execute({\n sql: `SELECT role FROM org_members WHERE org_id = ? AND LOWER(email) = ? LIMIT 1`,\n args: [orgId, userEmail.toLowerCase()],\n });\n const role = String((rows[0] as any)?.role ?? \"\").toLowerCase();\n return role === \"owner\" || role === \"admin\";\n } catch {\n return false;\n }\n}\n\nasync function resourceToAutomationItem(\n event: H3Event,\n userEmail: string,\n resource: Resource,\n): Promise<AutomationRouteItem> {\n const parsed = parseJobResource(resource.content);\n const meta = asTriggerFrontmatter(parsed.meta);\n return {\n id: resource.id,\n name: automationName(resource.path),\n path: resource.path,\n owner: resource.owner,\n canUpdate: await currentUserCanUpdateAutomation(\n event,\n userEmail,\n resource,\n meta,\n ),\n triggerType: meta.triggerType,\n event: meta.event,\n schedule: meta.schedule || undefined,\n scheduleDescription: scheduleDescription(meta.schedule),\n condition: meta.condition,\n mode: meta.mode,\n domain: meta.domain,\n enabled: meta.enabled,\n lastStatus: meta.lastStatus,\n lastRun: meta.lastRun,\n lastError: meta.lastError,\n nextRun: nextRunForMeta(meta),\n createdBy: meta.createdBy,\n body: parsed.body,\n };\n}\n\nexport async function listAutomationsForOwner(\n event: H3Event,\n userEmail: string,\n): Promise<AutomationRouteItem[]> {\n const allResources = await resourceListAllOwners(\"jobs/\");\n const activeOrgId = await getOrgContext(event)\n .then((context) => context.orgId)\n .catch(() => null);\n const activeOrgOwner = activeOrgId\n ? organizationResourceOwner(activeOrgId)\n : null;\n const resources = allResources.filter((resource) => {\n if (!resource.path.endsWith(\".md\") || resource.path.endsWith(\".keep\")) {\n return false;\n }\n if (\n resource.owner !== userEmail &&\n resource.owner !== SHARED_OWNER &&\n resource.owner !== activeOrgOwner\n ) {\n return false;\n }\n if (resource.owner !== SHARED_OWNER) return true;\n const { meta } = parseJobResource(resource.content);\n return !meta.orgId || meta.orgId === activeOrgId;\n });\n return Promise.all(\n resources.map((resource) =>\n resourceToAutomationItem(event, userEmail, resource),\n ),\n );\n}\n\nexport async function setAutomationEnabledForOwner(\n event: H3Event,\n userEmail: string,\n input: SetAutomationEnabledInput,\n): Promise<AutomationRouteItem> {\n if (typeof input.enabled !== \"boolean\") {\n throw Object.assign(new Error(\"enabled must be a boolean\"), {\n statusCode: 400,\n });\n }\n\n const path = normalizeAutomationPath(input);\n const activeOrgId = await getOrgContext(event)\n .then((context) => context.orgId)\n .catch(() => null);\n const activeOrgOwner = activeOrgId\n ? organizationResourceOwner(activeOrgId)\n : null;\n const requestedOwner =\n input.owner === SHARED_OWNER\n ? SHARED_OWNER\n : input.owner === activeOrgOwner\n ? activeOrgOwner\n : userEmail;\n const resource = await resourceGetByPath(requestedOwner, path);\n if (!resource) {\n throw Object.assign(new Error(\"Automation not found\"), { statusCode: 404 });\n }\n\n const parsed = parseJobResource(resource.content);\n const meta = asTriggerFrontmatter(parsed.meta);\n const allowed = await currentUserCanUpdateAutomation(\n event,\n userEmail,\n resource,\n meta,\n );\n if (!allowed) {\n throw Object.assign(\n new Error(\"Only the automation creator or an org admin can update it.\"),\n { statusCode: 403 },\n );\n }\n\n parsed.meta.enabled = input.enabled;\n if (\n parsed.meta.enabled &&\n meta.triggerType !== \"event\" &&\n meta.schedule &&\n isValidCron(meta.schedule)\n ) {\n parsed.meta.nextRun = nextOccurrence(meta.schedule).toISOString();\n }\n\n const updatedContent = buildJobResourceContent(parsed.meta, parsed.body);\n await resourcePut(resource.owner, resource.path, updatedContent);\n await refreshEventSubscriptions();\n\n return resourceToAutomationItem(event, userEmail, {\n ...resource,\n content: updatedContent,\n });\n}\n\nfunction routeError(event: H3Event, err: unknown, fallback: string) {\n const statusCode =\n typeof (err as any)?.statusCode === \"number\"\n ? (err as any).statusCode\n : 500;\n setResponseStatus(event, statusCode);\n return { error: (err as any)?.message ?? fallback };\n}\n\nexport function createAutomationsHandler() {\n return defineEventHandler(async (event: H3Event) => {\n const method = getMethod(event);\n const pathname = (event.path || event.url?.pathname || \"\")\n .split(\"?\")[0]\n .replace(/^\\/+/, \"\")\n .replace(/\\/+$/, \"\");\n\n const session = await getSession(event).catch(() => null);\n if (!session?.email) {\n setResponseStatus(event, 401);\n return { error: \"Unauthenticated\" };\n }\n\n if (\n (pathname === \"fire-test\" || pathname.endsWith(\"/fire-test\")) &&\n method === \"POST\"\n ) {\n try {\n const { emit } = await import(\"../event-bus/index.js\");\n const body = (await readBody(event).catch(() => ({}))) as Record<\n string,\n unknown\n >;\n emit(\n \"test.event.fired\",\n { data: body.data ?? {} },\n { owner: session.email },\n );\n return { ok: true };\n } catch (err) {\n return routeError(event, err, \"Failed to emit test event\");\n }\n }\n\n if (method === \"GET\") {\n try {\n return await listAutomationsForOwner(event, session.email);\n } catch (err) {\n return routeError(event, err, \"Failed to list automations\");\n }\n }\n\n if (method === \"PATCH\" || method === \"PUT\") {\n try {\n const body = (await readBody(event).catch(\n () => ({}),\n )) as SetAutomationEnabledInput | null;\n return await setAutomationEnabledForOwner(event, session.email, {\n owner: body?.owner,\n path: body?.path,\n name: body?.name,\n enabled: (body as any)?.enabled,\n } as SetAutomationEnabledInput);\n } catch (err) {\n return routeError(event, err, \"Failed to update automation\");\n }\n }\n\n setResponseStatus(event, 405);\n return { error: \"Method not allowed\" };\n });\n}\n"]}
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/triggers/routes.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n defineEventHandler,\n getMethod,\n setResponseStatus,\n type H3Event,\n} from \"h3\";\n\nimport { canUpdateAutomationResource } from \"../automations/service.js\";\nimport { getDbExec } from \"../db/client.js\";\nimport {\n nextOccurrence,\n describeCron,\n effectiveTimezone,\n isValidCron,\n} from \"../jobs/cron.js\";\nimport {\n buildJobResourceContent,\n parseJobResource,\n type JobFrontmatter,\n} from \"../jobs/frontmatter.js\";\nimport { getOrgContext } from \"../org/context.js\";\nimport {\n organizationIdFromResourceOwner,\n organizationResourceOwner,\n resourceGetByPath,\n resourceListAllOwners,\n resourcePut,\n SHARED_OWNER,\n type Resource,\n} from \"../resources/store.js\";\nimport { getSession } from \"../server/auth.js\";\nimport { readBody } from \"../server/h3-helpers.js\";\nimport { refreshEventSubscriptions } from \"./dispatcher.js\";\nimport type { TriggerFrontmatter } from \"./types.js\";\n\nexport interface AutomationRouteItem {\n id: string;\n name: string;\n path: string;\n owner: string;\n canUpdate: boolean;\n triggerType: TriggerFrontmatter[\"triggerType\"];\n event?: string;\n schedule?: string;\n scheduleDescription?: string;\n condition?: string;\n mode: TriggerFrontmatter[\"mode\"];\n domain?: string;\n enabled: boolean;\n lastStatus?: TriggerFrontmatter[\"lastStatus\"];\n lastRun?: string;\n lastError?: string;\n nextRun?: string;\n createdBy?: string;\n body: string;\n}\n\ninterface SetAutomationEnabledInput {\n owner?: string;\n path?: string;\n name?: string;\n enabled: boolean;\n}\n\nfunction automationName(path: string): string {\n return path.replace(/^jobs\\//, \"\").replace(/\\.md$/, \"\");\n}\n\nfunction asTriggerFrontmatter(meta: JobFrontmatter): TriggerFrontmatter {\n return {\n ...meta,\n triggerType: meta.triggerType ?? \"schedule\",\n mode: meta.mode ?? \"agentic\",\n };\n}\n\nfunction normalizeAutomationPath(input: SetAutomationEnabledInput): string {\n const rawPath = input.path ?? (input.name ? `jobs/${input.name}.md` : \"\");\n const path = rawPath.replace(/^\\/+/, \"\");\n if (\n !path.startsWith(\"jobs/\") ||\n !path.endsWith(\".md\") ||\n path.endsWith(\".keep\") ||\n path.includes(\"..\")\n ) {\n throw Object.assign(\n new Error(\"A valid jobs/*.md automation path is required\"),\n {\n statusCode: 400,\n },\n );\n }\n return path;\n}\n\nfunction scheduleDescription(schedule?: string, timezone?: string) {\n if (!schedule) return undefined;\n try {\n return describeCron(schedule, effectiveTimezone(timezone));\n } catch {\n return schedule;\n }\n}\n\nfunction nextRunForMeta(meta: TriggerFrontmatter): string | undefined {\n if (meta.nextRun) return meta.nextRun;\n if (\n meta.enabled &&\n meta.triggerType !== \"event\" &&\n meta.schedule &&\n isValidCron(meta.schedule)\n ) {\n try {\n return nextOccurrence(\n meta.schedule,\n undefined,\n meta.timezone,\n ).toISOString();\n } catch {\n return undefined;\n }\n }\n return undefined;\n}\n\nasync function currentUserCanUpdateAutomation(\n event: H3Event,\n userEmail: string,\n resource: Resource,\n meta: TriggerFrontmatter,\n): Promise<boolean> {\n const resourceOwner = resource.owner;\n if (resourceOwner === userEmail) return true;\n const resourceOrgId = organizationIdFromResourceOwner(resourceOwner);\n if (resourceOrgId) {\n try {\n const org = await getOrgContext(event);\n if (org.orgId !== resourceOrgId) return false;\n return await canUpdateAutomationResource(\n { userEmail, orgId: resourceOrgId },\n resource,\n );\n } catch {\n return false;\n }\n }\n if (resourceOwner !== SHARED_OWNER) return false;\n\n if (meta.orgId) {\n const activeOrgId = await getOrgContext(event)\n .then((context) => context.orgId)\n .catch(() => null);\n if (activeOrgId !== meta.orgId) return false;\n }\n\n if (\n meta.createdBy &&\n meta.createdBy.toLowerCase() === userEmail.toLowerCase()\n ) {\n return true;\n }\n\n let orgId = meta.orgId;\n if (!orgId) {\n try {\n orgId = (await getOrgContext(event)).orgId ?? undefined;\n } catch {\n orgId = undefined;\n }\n }\n if (!orgId) return false;\n\n try {\n const { rows } = await getDbExec().execute({\n sql: `SELECT role FROM org_members WHERE org_id = ? AND LOWER(email) = ? LIMIT 1`,\n args: [orgId, userEmail.toLowerCase()],\n });\n const role = String((rows[0] as any)?.role ?? \"\").toLowerCase();\n return role === \"owner\" || role === \"admin\";\n } catch {\n return false;\n }\n}\n\nasync function resourceToAutomationItem(\n event: H3Event,\n userEmail: string,\n resource: Resource,\n): Promise<AutomationRouteItem> {\n const parsed = parseJobResource(resource.content);\n const meta = asTriggerFrontmatter(parsed.meta);\n return {\n id: resource.id,\n name: automationName(resource.path),\n path: resource.path,\n owner: resource.owner,\n canUpdate: await currentUserCanUpdateAutomation(\n event,\n userEmail,\n resource,\n meta,\n ),\n triggerType: meta.triggerType,\n event: meta.event,\n schedule: meta.schedule || undefined,\n scheduleDescription: scheduleDescription(meta.schedule, meta.timezone),\n condition: meta.condition,\n mode: meta.mode,\n domain: meta.domain,\n enabled: meta.enabled,\n lastStatus: meta.lastStatus,\n lastRun: meta.lastRun,\n lastError: meta.lastError,\n nextRun: nextRunForMeta(meta),\n createdBy: meta.createdBy,\n body: parsed.body,\n };\n}\n\nexport async function listAutomationsForOwner(\n event: H3Event,\n userEmail: string,\n): Promise<AutomationRouteItem[]> {\n const allResources = await resourceListAllOwners(\"jobs/\");\n const activeOrgId = await getOrgContext(event)\n .then((context) => context.orgId)\n .catch(() => null);\n const activeOrgOwner = activeOrgId\n ? organizationResourceOwner(activeOrgId)\n : null;\n const resources = allResources.filter((resource) => {\n if (!resource.path.endsWith(\".md\") || resource.path.endsWith(\".keep\")) {\n return false;\n }\n if (\n resource.owner !== userEmail &&\n resource.owner !== SHARED_OWNER &&\n resource.owner !== activeOrgOwner\n ) {\n return false;\n }\n if (resource.owner !== SHARED_OWNER) return true;\n const { meta } = parseJobResource(resource.content);\n return !meta.orgId || meta.orgId === activeOrgId;\n });\n return Promise.all(\n resources.map((resource) =>\n resourceToAutomationItem(event, userEmail, resource),\n ),\n );\n}\n\nexport async function setAutomationEnabledForOwner(\n event: H3Event,\n userEmail: string,\n input: SetAutomationEnabledInput,\n): Promise<AutomationRouteItem> {\n if (typeof input.enabled !== \"boolean\") {\n throw Object.assign(new Error(\"enabled must be a boolean\"), {\n statusCode: 400,\n });\n }\n\n const path = normalizeAutomationPath(input);\n const activeOrgId = await getOrgContext(event)\n .then((context) => context.orgId)\n .catch(() => null);\n const activeOrgOwner = activeOrgId\n ? organizationResourceOwner(activeOrgId)\n : null;\n const requestedOwner =\n input.owner === SHARED_OWNER\n ? SHARED_OWNER\n : input.owner === activeOrgOwner\n ? activeOrgOwner\n : userEmail;\n const resource = await resourceGetByPath(requestedOwner, path);\n if (!resource) {\n throw Object.assign(new Error(\"Automation not found\"), { statusCode: 404 });\n }\n\n const parsed = parseJobResource(resource.content);\n const meta = asTriggerFrontmatter(parsed.meta);\n const allowed = await currentUserCanUpdateAutomation(\n event,\n userEmail,\n resource,\n meta,\n );\n if (!allowed) {\n throw Object.assign(\n new Error(\"Only the automation creator or an org admin can update it.\"),\n { statusCode: 403 },\n );\n }\n\n parsed.meta.enabled = input.enabled;\n if (\n parsed.meta.enabled &&\n meta.triggerType !== \"event\" &&\n meta.schedule &&\n isValidCron(meta.schedule)\n ) {\n parsed.meta.nextRun = nextOccurrence(\n meta.schedule,\n undefined,\n meta.timezone,\n ).toISOString();\n }\n\n const updatedContent = buildJobResourceContent(parsed.meta, parsed.body);\n await resourcePut(resource.owner, resource.path, updatedContent);\n await refreshEventSubscriptions();\n\n return resourceToAutomationItem(event, userEmail, {\n ...resource,\n content: updatedContent,\n });\n}\n\nfunction routeError(event: H3Event, err: unknown, fallback: string) {\n const statusCode =\n typeof (err as any)?.statusCode === \"number\"\n ? (err as any).statusCode\n : 500;\n setResponseStatus(event, statusCode);\n return { error: (err as any)?.message ?? fallback };\n}\n\nexport function createAutomationsHandler() {\n return defineEventHandler(async (event: H3Event) => {\n const method = getMethod(event);\n const pathname = (event.path || event.url?.pathname || \"\")\n .split(\"?\")[0]\n .replace(/^\\/+/, \"\")\n .replace(/\\/+$/, \"\");\n\n const session = await getSession(event).catch(() => null);\n if (!session?.email) {\n setResponseStatus(event, 401);\n return { error: \"Unauthenticated\" };\n }\n\n if (\n (pathname === \"fire-test\" || pathname.endsWith(\"/fire-test\")) &&\n method === \"POST\"\n ) {\n try {\n const { emit } = await import(\"../event-bus/index.js\");\n const body = (await readBody(event).catch(() => ({}))) as Record<\n string,\n unknown\n >;\n emit(\n \"test.event.fired\",\n { data: body.data ?? {} },\n { owner: session.email },\n );\n return { ok: true };\n } catch (err) {\n return routeError(event, err, \"Failed to emit test event\");\n }\n }\n\n if (method === \"GET\") {\n try {\n return await listAutomationsForOwner(event, session.email);\n } catch (err) {\n return routeError(event, err, \"Failed to list automations\");\n }\n }\n\n if (method === \"PATCH\" || method === \"PUT\") {\n try {\n const body = (await readBody(event).catch(\n () => ({}),\n )) as SetAutomationEnabledInput | null;\n return await setAutomationEnabledForOwner(event, session.email, {\n owner: body?.owner,\n path: body?.path,\n name: body?.name,\n enabled: (body as any)?.enabled,\n } as SetAutomationEnabledInput);\n } catch (err) {\n return routeError(event, err, \"Failed to update automation\");\n }\n }\n\n setResponseStatus(event, 405);\n return { error: \"Method not allowed\" };\n });\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import type { ViteUserConfig } from "vitest/config";
2
+ /**
3
+ * Resolve the worker lid from the environment, throwing on anything that is
4
+ * neither a percentage nor a worker count. A bad value must not quietly become
5
+ * the default — that reads as "configured" while running at some other size.
6
+ */
7
+ export declare function resolveMaxWorkers(env?: NodeJS.ProcessEnv): string | number;
8
+ declare const vitestBaseConfig: ViteUserConfig;
9
+ export default vitestBaseConfig;
10
+ //# sourceMappingURL=vitest-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest-config.d.ts","sourceRoot":"","sources":["../src/vitest-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAuBpD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,GAAG,MAAM,CA6BjB;AAED,QAAA,MAAM,gBAAgB,EAAE,cAIvB,CAAC;eAEa,gBAAgB"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Base vitest config that caps a suite's worker pool at a share of the machine.
3
+ *
4
+ * Vitest sizes its pool at `cores - 1` with no awareness of peer processes, so
5
+ * several suites running at once — concurrent agent sessions, or a workspace
6
+ * running packages in parallel — each try to claim the whole box. Set
7
+ * `VITEST_CONCURRENCY` to a percentage or a worker count to change the lid; a
8
+ * package that needs its own value sets `test.maxWorkers` in its own config,
9
+ * which wins the merge.
10
+ *
11
+ * This ships from core rather than living at the monorepo root because template
12
+ * directories are scaffolded out verbatim as standalone apps — a config that
13
+ * reaches above the template would resolve to nothing in the generated app.
14
+ *
15
+ * Keep this module free of runtime imports so loading it costs a config file
16
+ * nothing beyond reading the environment.
17
+ */
18
+ const DEFAULT_MAX_WORKERS = "25%";
19
+ const ENV_KEYS = ["VITEST_CONCURRENCY", "AGENT_NATIVE_VITEST_CONCURRENCY"];
20
+ /**
21
+ * Resolve the worker lid from the environment, throwing on anything that is
22
+ * neither a percentage nor a worker count. A bad value must not quietly become
23
+ * the default — that reads as "configured" while running at some other size.
24
+ */
25
+ export function resolveMaxWorkers(env = process.env) {
26
+ const rawMaxWorkers = env.VITEST_MAX_WORKERS;
27
+ if (rawMaxWorkers?.includes("%")) {
28
+ throw new Error(`VITEST_MAX_WORKERS=${rawMaxWorkers} is not a percentage to vitest — it parses as ` +
29
+ `${Number.parseInt(rawMaxWorkers)} workers. Use VITEST_CONCURRENCY for percentages, ` +
30
+ `or an integer for VITEST_MAX_WORKERS.`);
31
+ }
32
+ const key = ENV_KEYS.find((name) => env[name]);
33
+ if (!key)
34
+ return DEFAULT_MAX_WORKERS;
35
+ const value = env[key].trim();
36
+ if (/^\d+%$/.test(value)) {
37
+ const percent = Number.parseInt(value);
38
+ if (percent < 1 || percent > 100) {
39
+ throw new Error(`${key}=${value} is out of range — use 1% to 100%.`);
40
+ }
41
+ return value;
42
+ }
43
+ const count = Number(value);
44
+ if (!Number.isInteger(count) || count < 1) {
45
+ throw new Error(`${key}=${value} is not a percentage like "25%" or a worker count like "2".`);
46
+ }
47
+ return count;
48
+ }
49
+ const vitestBaseConfig = {
50
+ test: {
51
+ maxWorkers: resolveMaxWorkers(),
52
+ },
53
+ };
54
+ export default vitestBaseConfig;
55
+ //# sourceMappingURL=vitest-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest-config.js","sourceRoot":"","sources":["../src/vitest-config.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,mBAAmB,GAAG,KAAK,CAAC;AAClC,MAAM,QAAQ,GAAG,CAAC,oBAAoB,EAAE,iCAAiC,CAAC,CAAC;AAE3E;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAC/B,GAAG,GAAsB,OAAO,CAAC,GAAG;IAEpC,MAAM,aAAa,GAAG,GAAG,CAAC,kBAAkB,CAAC;IAC7C,IAAI,aAAa,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,sBAAsB,aAAa,gDAAgD;YACjF,GAAG,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,oDAAoD;YACrF,uCAAuC,CAC1C,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/C,IAAI,CAAC,GAAG;QAAE,OAAO,mBAAmB,CAAC;IAErC,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAE,CAAC,IAAI,EAAE,CAAC;IAC/B,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACvC,IAAI,OAAO,GAAG,CAAC,IAAI,OAAO,GAAG,GAAG,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,GAAG,GAAG,IAAI,KAAK,oCAAoC,CAAC,CAAC;QACvE,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CACb,GAAG,GAAG,IAAI,KAAK,6DAA6D,CAC7E,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,gBAAgB,GAAmB;IACvC,IAAI,EAAE;QACJ,UAAU,EAAE,iBAAiB,EAAE;KAChC;CACF,CAAC;AAEF,eAAe,gBAAgB,CAAC","sourcesContent":["import type { ViteUserConfig } from \"vitest/config\";\n\n/**\n * Base vitest config that caps a suite's worker pool at a share of the machine.\n *\n * Vitest sizes its pool at `cores - 1` with no awareness of peer processes, so\n * several suites running at once — concurrent agent sessions, or a workspace\n * running packages in parallel — each try to claim the whole box. Set\n * `VITEST_CONCURRENCY` to a percentage or a worker count to change the lid; a\n * package that needs its own value sets `test.maxWorkers` in its own config,\n * which wins the merge.\n *\n * This ships from core rather than living at the monorepo root because template\n * directories are scaffolded out verbatim as standalone apps — a config that\n * reaches above the template would resolve to nothing in the generated app.\n *\n * Keep this module free of runtime imports so loading it costs a config file\n * nothing beyond reading the environment.\n */\n\nconst DEFAULT_MAX_WORKERS = \"25%\";\nconst ENV_KEYS = [\"VITEST_CONCURRENCY\", \"AGENT_NATIVE_VITEST_CONCURRENCY\"];\n\n/**\n * Resolve the worker lid from the environment, throwing on anything that is\n * neither a percentage nor a worker count. A bad value must not quietly become\n * the default — that reads as \"configured\" while running at some other size.\n */\nexport function resolveMaxWorkers(\n env: NodeJS.ProcessEnv = process.env,\n): string | number {\n const rawMaxWorkers = env.VITEST_MAX_WORKERS;\n if (rawMaxWorkers?.includes(\"%\")) {\n throw new Error(\n `VITEST_MAX_WORKERS=${rawMaxWorkers} is not a percentage to vitest — it parses as ` +\n `${Number.parseInt(rawMaxWorkers)} workers. Use VITEST_CONCURRENCY for percentages, ` +\n `or an integer for VITEST_MAX_WORKERS.`,\n );\n }\n\n const key = ENV_KEYS.find((name) => env[name]);\n if (!key) return DEFAULT_MAX_WORKERS;\n\n const value = env[key]!.trim();\n if (/^\\d+%$/.test(value)) {\n const percent = Number.parseInt(value);\n if (percent < 1 || percent > 100) {\n throw new Error(`${key}=${value} is out of range — use 1% to 100%.`);\n }\n return value;\n }\n\n const count = Number(value);\n if (!Number.isInteger(count) || count < 1) {\n throw new Error(\n `${key}=${value} is not a percentage like \"25%\" or a worker count like \"2\".`,\n );\n }\n return count;\n}\n\nconst vitestBaseConfig: ViteUserConfig = {\n test: {\n maxWorkers: resolveMaxWorkers(),\n },\n};\n\nexport default vitestBaseConfig;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.134.1",
3
+ "version": "0.135.0",
4
4
  "description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -61,6 +61,7 @@
61
61
  "default": "./dist/index.js"
62
62
  },
63
63
  "./vite": "./dist/vite/index.js",
64
+ "./vitest-config": "./dist/vitest-config.js",
64
65
  "./server": "./dist/server/index.js",
65
66
  "./server/edge": "./dist/server/edge.js",
66
67
  "./server/entry-server": "./dist/server/entry-server.js",
@@ -395,8 +396,8 @@
395
396
  "y-protocols": "^1.0.7",
396
397
  "yjs": "^13.6.31",
397
398
  "zod": "^4.3.6",
398
- "@agent-native/recap-cli": "0.5.1",
399
- "@agent-native/toolkit": "^0.12.1"
399
+ "@agent-native/recap-cli": "0.5.2",
400
+ "@agent-native/toolkit": "^0.12.2"
400
401
  },
401
402
  "devDependencies": {
402
403
  "@ai-sdk/anthropic": "^3.0.71",
@@ -586,6 +587,6 @@
586
587
  "build": "tsc && tsc -p tsconfig.cli.json && node scripts/finalize-build.mjs && node scripts/check-dist-imports.mjs",
587
588
  "dev": "tsc --watch",
588
589
  "typecheck": "tsc --noEmit",
589
- "test": "vitest --run --dir src --maxWorkers=25%"
590
+ "test": "vitest --run --dir src"
590
591
  }
591
592
  }
@@ -1,11 +1,13 @@
1
1
  import { getDbExec } from "../db/client.js";
2
- import { isValidCron, nextOccurrence } from "../jobs/cron.js";
2
+ import { isValidCron, isValidTimezone, nextOccurrence } from "../jobs/cron.js";
3
3
  import {
4
4
  buildJobResourceContent,
5
5
  normalizeJobMcpTools,
6
6
  parseJobResource,
7
7
  type JobFrontmatter,
8
8
  } from "../jobs/frontmatter.js";
9
+ import { deleteAutomationRuns } from "../jobs/run-history.js";
10
+ import { resolveUserSchedulingTimezone } from "../localization/user-timezone.js";
9
11
  import {
10
12
  organizationIdFromResourceOwner,
11
13
  organizationResourceOwner,
@@ -49,6 +51,7 @@ export interface DefineAutomationInput {
49
51
  triggerType: "schedule" | "event";
50
52
  body: string;
51
53
  schedule?: string;
54
+ timezone?: string;
52
55
  event?: string;
53
56
  condition?: string;
54
57
  domain?: string;
@@ -68,6 +71,7 @@ export interface UpdateAutomationInput {
68
71
  condition?: string | null;
69
72
  delegatedPolicyId?: string | null;
70
73
  schedule?: string;
74
+ timezone?: string;
71
75
  model?: string | null;
72
76
  mcpTools?: unknown;
73
77
  }
@@ -309,9 +313,20 @@ export async function defineAutomation(
309
313
  throw httpError("event is required for event-triggered automations.", 400);
310
314
  }
311
315
 
316
+ if (input.timezone && !isValidTimezone(input.timezone)) {
317
+ throw httpError(`Unknown timezone "${input.timezone}".`, 400);
318
+ }
319
+ // Resolve now and persist it: a schedule whose zone is implicit means
320
+ // something different the moment it is read on a differently-zoned host.
321
+ const timezone =
322
+ input.triggerType === "schedule"
323
+ ? input.timezone || (await resolveUserSchedulingTimezone(actor.userEmail))
324
+ : undefined;
325
+
312
326
  const mcpTools = normalizeJobMcpTools(input.mcpTools);
313
327
  const meta: JobFrontmatter = {
314
328
  schedule: input.triggerType === "schedule" ? schedule : "",
329
+ timezone,
315
330
  enabled: true,
316
331
  triggerType: input.triggerType,
317
332
  event: input.triggerType === "event" ? event : undefined,
@@ -324,7 +339,7 @@ export async function defineAutomation(
324
339
  runAs: "creator",
325
340
  nextRun:
326
341
  input.triggerType === "schedule"
327
- ? nextOccurrence(schedule).toISOString()
342
+ ? nextOccurrence(schedule, undefined, timezone).toISOString()
328
343
  : undefined,
329
344
  model: input.model?.trim() || undefined,
330
345
  mcpTools: mcpTools?.length ? mcpTools : undefined,
@@ -365,7 +380,22 @@ export async function updateAutomation(
365
380
  throw httpError(`Invalid cron expression "${input.schedule}".`, 400);
366
381
  }
367
382
  meta.schedule = input.schedule;
368
- meta.nextRun = nextOccurrence(input.schedule).toISOString();
383
+ }
384
+ if (input.timezone !== undefined) {
385
+ if (!isValidTimezone(input.timezone)) {
386
+ throw httpError(`Unknown timezone "${input.timezone}".`, 400);
387
+ }
388
+ if (meta.triggerType !== "schedule") {
389
+ throw httpError("Event automations do not have a timezone.", 400);
390
+ }
391
+ meta.timezone = input.timezone;
392
+ }
393
+ if (input.schedule !== undefined || input.timezone !== undefined) {
394
+ meta.nextRun = nextOccurrence(
395
+ meta.schedule,
396
+ undefined,
397
+ meta.timezone,
398
+ ).toISOString();
369
399
  }
370
400
  if (input.enabled !== undefined) {
371
401
  meta.enabled = input.enabled;
@@ -374,7 +404,11 @@ export async function updateAutomation(
374
404
  meta.triggerType === "schedule" &&
375
405
  isValidCron(meta.schedule)
376
406
  ) {
377
- meta.nextRun = nextOccurrence(meta.schedule).toISOString();
407
+ meta.nextRun = nextOccurrence(
408
+ meta.schedule,
409
+ undefined,
410
+ meta.timezone,
411
+ ).toISOString();
378
412
  }
379
413
  }
380
414
  if (input.condition !== undefined) {
@@ -417,6 +451,9 @@ export async function deleteAutomation(
417
451
  );
418
452
  }
419
453
  await resourceDelete(definition.resource.id);
454
+ // Names are reusable, so leaving history behind would attach these runs to
455
+ // whatever automation is created under the same name next.
456
+ await deleteAutomationRuns(definition.resource.owner, name);
420
457
  }
421
458
 
422
459
  export interface AutomationExecutionIdentity {
@@ -259,6 +259,24 @@ export function configPathFor(
259
259
  // JSON client configs (Claude Code, Claude Code CLI, Cowork)
260
260
  // ---------------------------------------------------------------------------
261
261
 
262
+ /**
263
+ * Read an existing MCP config without conflating missing and unreadable files.
264
+ * Only ENOENT means the config is absent; any other read failure must abort
265
+ * before a caller can overwrite existing user configuration.
266
+ */
267
+ function readExistingConfigFile(file: string): string | undefined {
268
+ try {
269
+ return fs.readFileSync(file, "utf-8");
270
+ } catch (error) {
271
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
272
+ const detail = error instanceof Error ? ` (${error.message})` : "";
273
+ throw new Error(
274
+ `Cannot read MCP config file: ${file}\n` +
275
+ `Check that the file is readable and re-run. The file has not been modified.${detail}`,
276
+ );
277
+ }
278
+ }
279
+
262
280
  /**
263
281
  * Read and parse a JSON config file.
264
282
  *
@@ -269,13 +287,8 @@ export function configPathFor(
269
287
  * file with only the new MCP entry (data-loss hazard).
270
288
  */
271
289
  function readJsonFile(file: string): Record<string, any> {
272
- let raw: string;
273
- try {
274
- raw = fs.readFileSync(file, "utf-8");
275
- } catch {
276
- // Missing (ENOENT) or unreadable file — treat as empty.
277
- return {};
278
- }
290
+ const raw = readExistingConfigFile(file);
291
+ if (raw === undefined) return {};
279
292
  if (!raw.trim()) return {};
280
293
  try {
281
294
  const parsed = JSON.parse(raw);
@@ -576,12 +589,7 @@ export function writeCodexBlock(
576
589
  name: string,
577
590
  block: string | null,
578
591
  ): void {
579
- let content = "";
580
- try {
581
- content = fs.readFileSync(file, "utf-8");
582
- } catch {
583
- content = "";
584
- }
592
+ const content = readExistingConfigFile(file) ?? "";
585
593
 
586
594
  const lines = content.split(/\r?\n/);
587
595
  const out: string[] = [];
@@ -616,14 +624,12 @@ export function writeCodexBlock(
616
624
  }
617
625
 
618
626
  export function codexHasBlock(file: string, name: string): boolean {
619
- try {
620
- const content = fs.readFileSync(file, "utf-8");
621
- return content
622
- .split(/\r?\n/)
623
- .some((line) => codexServerNameOfHeader(line) === name);
624
- } catch {
625
- return false;
626
- }
627
+ const content = readExistingConfigFile(file);
628
+ return (
629
+ content
630
+ ?.split(/\r?\n/)
631
+ .some((line) => codexServerNameOfHeader(line) === name) ?? false
632
+ );
627
633
  }
628
634
 
629
635
  // ---------------------------------------------------------------------------