@open-mercato/core 0.6.5-develop.4384.1.ce2ec6eaaa → 0.6.5-develop.4393.1.de282b5dfd

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 (533) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/channel_ingest_dead_letter/index.js +25 -0
  3. package/dist/generated/entities/channel_ingest_dead_letter/index.js.map +7 -0
  4. package/dist/generated/entities/channel_thread_mapping/index.js +25 -0
  5. package/dist/generated/entities/channel_thread_mapping/index.js.map +7 -0
  6. package/dist/generated/entities/channel_thread_token/index.js +17 -0
  7. package/dist/generated/entities/channel_thread_token/index.js.map +7 -0
  8. package/dist/generated/entities/communication_channel/index.js +43 -0
  9. package/dist/generated/entities/communication_channel/index.js.map +7 -0
  10. package/dist/generated/entities/customer_interaction/index.js +4 -0
  11. package/dist/generated/entities/customer_interaction/index.js.map +2 -2
  12. package/dist/generated/entities/external_conversation/index.js +25 -0
  13. package/dist/generated/entities/external_conversation/index.js.map +7 -0
  14. package/dist/generated/entities/external_message/index.js +25 -0
  15. package/dist/generated/entities/external_message/index.js.map +7 -0
  16. package/dist/generated/entities/integration_credentials/index.js +3 -1
  17. package/dist/generated/entities/integration_credentials/index.js.map +2 -2
  18. package/dist/generated/entities/message/index.js +2 -0
  19. package/dist/generated/entities/message/index.js.map +2 -2
  20. package/dist/generated/entities/message_channel_link/index.js +33 -0
  21. package/dist/generated/entities/message_channel_link/index.js.map +7 -0
  22. package/dist/generated/entities/message_reaction/index.js +25 -0
  23. package/dist/generated/entities/message_reaction/index.js.map +7 -0
  24. package/dist/generated/entities.ids.generated.js +11 -0
  25. package/dist/generated/entities.ids.generated.js.map +2 -2
  26. package/dist/generated/entity-fields-registry.js +117 -0
  27. package/dist/generated/entity-fields-registry.js.map +2 -2
  28. package/dist/helpers/integration/authFixtures.js +2 -1
  29. package/dist/helpers/integration/authFixtures.js.map +2 -2
  30. package/dist/helpers/integration/communicationChannelsFixtures.js +58 -0
  31. package/dist/helpers/integration/communicationChannelsFixtures.js.map +7 -0
  32. package/dist/modules/communication_channels/acl.js +47 -0
  33. package/dist/modules/communication_channels/acl.js.map +7 -0
  34. package/dist/modules/communication_channels/api/delete/channels/[id]/route.js +133 -0
  35. package/dist/modules/communication_channels/api/delete/channels/[id]/route.js.map +7 -0
  36. package/dist/modules/communication_channels/api/delete/messages/[messageId]/reactions/[reactionId]/route.js +113 -0
  37. package/dist/modules/communication_channels/api/delete/messages/[messageId]/reactions/[reactionId]/route.js.map +7 -0
  38. package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js +138 -0
  39. package/dist/modules/communication_channels/api/get/channels/[id]/health/route.js.map +7 -0
  40. package/dist/modules/communication_channels/api/get/channels/[id]/route.js +93 -0
  41. package/dist/modules/communication_channels/api/get/channels/[id]/route.js.map +7 -0
  42. package/dist/modules/communication_channels/api/get/channels/route.js +96 -0
  43. package/dist/modules/communication_channels/api/get/channels/route.js.map +7 -0
  44. package/dist/modules/communication_channels/api/get/me/channels/route.js +82 -0
  45. package/dist/modules/communication_channels/api/get/me/channels/route.js.map +7 -0
  46. package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js +274 -0
  47. package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js.map +7 -0
  48. package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js +168 -0
  49. package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js.map +7 -0
  50. package/dist/modules/communication_channels/api/post/channels/[id]/poll-now/route.js +143 -0
  51. package/dist/modules/communication_channels/api/post/channels/[id]/poll-now/route.js.map +7 -0
  52. package/dist/modules/communication_channels/api/post/channels/[id]/push/register/route.js +127 -0
  53. package/dist/modules/communication_channels/api/post/channels/[id]/push/register/route.js.map +7 -0
  54. package/dist/modules/communication_channels/api/post/channels/[id]/set-primary/route.js +99 -0
  55. package/dist/modules/communication_channels/api/post/channels/[id]/set-primary/route.js.map +7 -0
  56. package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js +197 -0
  57. package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js.map +7 -0
  58. package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js +124 -0
  59. package/dist/modules/communication_channels/api/post/channels/connect/credentials/route.js.map +7 -0
  60. package/dist/modules/communication_channels/api/post/messages/[messageId]/reactions/route.js +120 -0
  61. package/dist/modules/communication_channels/api/post/messages/[messageId]/reactions/route.js.map +7 -0
  62. package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js +157 -0
  63. package/dist/modules/communication_channels/api/post/oauth/[provider]/initiate/route.js.map +7 -0
  64. package/dist/modules/communication_channels/api/post/send-as-user/route.js +115 -0
  65. package/dist/modules/communication_channels/api/post/send-as-user/route.js.map +7 -0
  66. package/dist/modules/communication_channels/api/post/test-seed/route.js +217 -0
  67. package/dist/modules/communication_channels/api/post/test-seed/route.js.map +7 -0
  68. package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js +175 -0
  69. package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js.map +7 -0
  70. package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js +123 -0
  71. package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js.map +7 -0
  72. package/dist/modules/communication_channels/api/put/threads/[threadId]/assign/route.js +117 -0
  73. package/dist/modules/communication_channels/api/put/threads/[threadId]/assign/route.js.map +7 -0
  74. package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.js +180 -0
  75. package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.js.map +7 -0
  76. package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.meta.js +36 -0
  77. package/dist/modules/communication_channels/backend/communication_channels/channels/[id]/page.meta.js.map +7 -0
  78. package/dist/modules/communication_channels/backend/communication_channels/channels/page.js +107 -0
  79. package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +7 -0
  80. package/dist/modules/communication_channels/backend/communication_channels/channels/page.meta.js +38 -0
  81. package/dist/modules/communication_channels/backend/communication_channels/channels/page.meta.js.map +7 -0
  82. package/dist/modules/communication_channels/backend/profile/communication-channels/page.js +727 -0
  83. package/dist/modules/communication_channels/backend/profile/communication-channels/page.js.map +7 -0
  84. package/dist/modules/communication_channels/backend/profile/communication-channels/page.meta.js +38 -0
  85. package/dist/modules/communication_channels/backend/profile/communication-channels/page.meta.js.map +7 -0
  86. package/dist/modules/communication_channels/commands/connect-credential-channel.js +154 -0
  87. package/dist/modules/communication_channels/commands/connect-credential-channel.js.map +7 -0
  88. package/dist/modules/communication_channels/commands/delete-channel.js +137 -0
  89. package/dist/modules/communication_channels/commands/delete-channel.js.map +7 -0
  90. package/dist/modules/communication_channels/commands/deliver-outbound-message.js +400 -0
  91. package/dist/modules/communication_channels/commands/deliver-outbound-message.js.map +7 -0
  92. package/dist/modules/communication_channels/commands/disconnect-channel.js +163 -0
  93. package/dist/modules/communication_channels/commands/disconnect-channel.js.map +7 -0
  94. package/dist/modules/communication_channels/commands/ingest-inbound-message.js +413 -0
  95. package/dist/modules/communication_channels/commands/ingest-inbound-message.js.map +7 -0
  96. package/dist/modules/communication_channels/commands/interceptors.js +68 -0
  97. package/dist/modules/communication_channels/commands/interceptors.js.map +7 -0
  98. package/dist/modules/communication_channels/commands/process-inbound-reaction.js +198 -0
  99. package/dist/modules/communication_channels/commands/process-inbound-reaction.js.map +7 -0
  100. package/dist/modules/communication_channels/commands/push-register.js +146 -0
  101. package/dist/modules/communication_channels/commands/push-register.js.map +7 -0
  102. package/dist/modules/communication_channels/commands/push-renew.js +23 -0
  103. package/dist/modules/communication_channels/commands/push-renew.js.map +7 -0
  104. package/dist/modules/communication_channels/commands/push-unregister.js +108 -0
  105. package/dist/modules/communication_channels/commands/push-unregister.js.map +7 -0
  106. package/dist/modules/communication_channels/commands/queue-import-history.js +113 -0
  107. package/dist/modules/communication_channels/commands/queue-import-history.js.map +7 -0
  108. package/dist/modules/communication_channels/commands/reassign-conversation.js +193 -0
  109. package/dist/modules/communication_channels/commands/reassign-conversation.js.map +7 -0
  110. package/dist/modules/communication_channels/commands/set-primary-channel.js +114 -0
  111. package/dist/modules/communication_channels/commands/set-primary-channel.js.map +7 -0
  112. package/dist/modules/communication_channels/commands/toggle-outbound-reaction.js +260 -0
  113. package/dist/modules/communication_channels/commands/toggle-outbound-reaction.js.map +7 -0
  114. package/dist/modules/communication_channels/data/enrichers.js +286 -0
  115. package/dist/modules/communication_channels/data/enrichers.js.map +7 -0
  116. package/dist/modules/communication_channels/data/entities.js +447 -0
  117. package/dist/modules/communication_channels/data/entities.js.map +7 -0
  118. package/dist/modules/communication_channels/data/extensions.js +67 -0
  119. package/dist/modules/communication_channels/data/extensions.js.map +7 -0
  120. package/dist/modules/communication_channels/data/validators.js +123 -0
  121. package/dist/modules/communication_channels/data/validators.js.map +7 -0
  122. package/dist/modules/communication_channels/di.js +35 -0
  123. package/dist/modules/communication_channels/di.js.map +7 -0
  124. package/dist/modules/communication_channels/encryption.js +12 -0
  125. package/dist/modules/communication_channels/encryption.js.map +7 -0
  126. package/dist/modules/communication_channels/events.js +124 -0
  127. package/dist/modules/communication_channels/events.js.map +7 -0
  128. package/dist/modules/communication_channels/index.js +20 -0
  129. package/dist/modules/communication_channels/index.js.map +7 -0
  130. package/dist/modules/communication_channels/lib/access-control.js +43 -0
  131. package/dist/modules/communication_channels/lib/access-control.js.map +7 -0
  132. package/dist/modules/communication_channels/lib/adapter-compat.js +36 -0
  133. package/dist/modules/communication_channels/lib/adapter-compat.js.map +7 -0
  134. package/dist/modules/communication_channels/lib/adapter-registry-singleton.js +22 -0
  135. package/dist/modules/communication_channels/lib/adapter-registry-singleton.js.map +7 -0
  136. package/dist/modules/communication_channels/lib/adapter.js +1 -0
  137. package/dist/modules/communication_channels/lib/adapter.js.map +7 -0
  138. package/dist/modules/communication_channels/lib/connect-channel.js +95 -0
  139. package/dist/modules/communication_channels/lib/connect-channel.js.map +7 -0
  140. package/dist/modules/communication_channels/lib/contact-resolver.js +79 -0
  141. package/dist/modules/communication_channels/lib/contact-resolver.js.map +7 -0
  142. package/dist/modules/communication_channels/lib/credential-refresh.js +97 -0
  143. package/dist/modules/communication_channels/lib/credential-refresh.js.map +7 -0
  144. package/dist/modules/communication_channels/lib/dead-letter.js +62 -0
  145. package/dist/modules/communication_channels/lib/dead-letter.js.map +7 -0
  146. package/dist/modules/communication_channels/lib/email-capabilities.js +47 -0
  147. package/dist/modules/communication_channels/lib/email-capabilities.js.map +7 -0
  148. package/dist/modules/communication_channels/lib/email-contact.js +14 -0
  149. package/dist/modules/communication_channels/lib/email-contact.js.map +7 -0
  150. package/dist/modules/communication_channels/lib/email-mime.js +259 -0
  151. package/dist/modules/communication_channels/lib/email-mime.js.map +7 -0
  152. package/dist/modules/communication_channels/lib/error-classification.js +101 -0
  153. package/dist/modules/communication_channels/lib/error-classification.js.map +7 -0
  154. package/dist/modules/communication_channels/lib/gmail-pubsub-jwt.js +185 -0
  155. package/dist/modules/communication_channels/lib/gmail-pubsub-jwt.js.map +7 -0
  156. package/dist/modules/communication_channels/lib/mutation-guards.js +114 -0
  157. package/dist/modules/communication_channels/lib/mutation-guards.js.map +7 -0
  158. package/dist/modules/communication_channels/lib/oauth-client-config.js +32 -0
  159. package/dist/modules/communication_channels/lib/oauth-client-config.js.map +7 -0
  160. package/dist/modules/communication_channels/lib/oauth-state.js +128 -0
  161. package/dist/modules/communication_channels/lib/oauth-state.js.map +7 -0
  162. package/dist/modules/communication_channels/lib/oauth-token.js +45 -0
  163. package/dist/modules/communication_channels/lib/oauth-token.js.map +7 -0
  164. package/dist/modules/communication_channels/lib/pg-errors.js +11 -0
  165. package/dist/modules/communication_channels/lib/pg-errors.js.map +7 -0
  166. package/dist/modules/communication_channels/lib/provider-health.js +24 -0
  167. package/dist/modules/communication_channels/lib/provider-health.js.map +7 -0
  168. package/dist/modules/communication_channels/lib/push-state.js +19 -0
  169. package/dist/modules/communication_channels/lib/push-state.js.map +7 -0
  170. package/dist/modules/communication_channels/lib/queue.js +54 -0
  171. package/dist/modules/communication_channels/lib/queue.js.map +7 -0
  172. package/dist/modules/communication_channels/lib/reaction-processor-types.js +5 -0
  173. package/dist/modules/communication_channels/lib/reaction-processor-types.js.map +7 -0
  174. package/dist/modules/communication_channels/lib/reaction-semantics.js +11 -0
  175. package/dist/modules/communication_channels/lib/reaction-semantics.js.map +7 -0
  176. package/dist/modules/communication_channels/lib/registry.js +67 -0
  177. package/dist/modules/communication_channels/lib/registry.js.map +7 -0
  178. package/dist/modules/communication_channels/lib/route-mutation-guard.js +43 -0
  179. package/dist/modules/communication_channels/lib/route-mutation-guard.js.map +7 -0
  180. package/dist/modules/communication_channels/lib/sanitize-channel-html.js +96 -0
  181. package/dist/modules/communication_channels/lib/sanitize-channel-html.js.map +7 -0
  182. package/dist/modules/communication_channels/lib/send-as-user.js +194 -0
  183. package/dist/modules/communication_channels/lib/send-as-user.js.map +7 -0
  184. package/dist/modules/communication_channels/lib/system-user.js +22 -0
  185. package/dist/modules/communication_channels/lib/system-user.js.map +7 -0
  186. package/dist/modules/communication_channels/lib/test-seed.js +68 -0
  187. package/dist/modules/communication_channels/lib/test-seed.js.map +7 -0
  188. package/dist/modules/communication_channels/lib/thread-matcher.js +263 -0
  189. package/dist/modules/communication_channels/lib/thread-matcher.js.map +7 -0
  190. package/dist/modules/communication_channels/lib/thread-token.js +219 -0
  191. package/dist/modules/communication_channels/lib/thread-token.js.map +7 -0
  192. package/dist/modules/communication_channels/lib/use-connect-channel.js +61 -0
  193. package/dist/modules/communication_channels/lib/use-connect-channel.js.map +7 -0
  194. package/dist/modules/communication_channels/migrations/Migration20260526134719_communication_channels.js +50 -0
  195. package/dist/modules/communication_channels/migrations/Migration20260526134719_communication_channels.js.map +7 -0
  196. package/dist/modules/communication_channels/migrations/Migration20260527195446_communication_channels.js +19 -0
  197. package/dist/modules/communication_channels/migrations/Migration20260527195446_communication_channels.js.map +7 -0
  198. package/dist/modules/communication_channels/migrations/Migration20260529231848_communication_channels.js +13 -0
  199. package/dist/modules/communication_channels/migrations/Migration20260529231848_communication_channels.js.map +7 -0
  200. package/dist/modules/communication_channels/migrations/Migration20260531120000_communication_channels.js +17 -0
  201. package/dist/modules/communication_channels/migrations/Migration20260531120000_communication_channels.js.map +7 -0
  202. package/dist/modules/communication_channels/notifications.client.js +51 -0
  203. package/dist/modules/communication_channels/notifications.client.js.map +7 -0
  204. package/dist/modules/communication_channels/notifications.handlers.js +53 -0
  205. package/dist/modules/communication_channels/notifications.handlers.js.map +7 -0
  206. package/dist/modules/communication_channels/notifications.js +56 -0
  207. package/dist/modules/communication_channels/notifications.js.map +7 -0
  208. package/dist/modules/communication_channels/setup.js +105 -0
  209. package/dist/modules/communication_channels/setup.js.map +7 -0
  210. package/dist/modules/communication_channels/subscribers/channel-requires-reauth-notification.js +71 -0
  211. package/dist/modules/communication_channels/subscribers/channel-requires-reauth-notification.js.map +7 -0
  212. package/dist/modules/communication_channels/subscribers/outbound-bridge.js +103 -0
  213. package/dist/modules/communication_channels/subscribers/outbound-bridge.js.map +7 -0
  214. package/dist/modules/communication_channels/subscribers/user-deleted-cascade.js +51 -0
  215. package/dist/modules/communication_channels/subscribers/user-deleted-cascade.js.map +7 -0
  216. package/dist/modules/communication_channels/widgets/components.js +7 -0
  217. package/dist/modules/communication_channels/widgets/components.js.map +7 -0
  218. package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.client.js +18 -0
  219. package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.client.js.map +7 -0
  220. package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.js +30 -0
  221. package/dist/modules/communication_channels/widgets/injection/channel-badge/widget.js.map +7 -0
  222. package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.client.js +185 -0
  223. package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.client.js.map +7 -0
  224. package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.js +17 -0
  225. package/dist/modules/communication_channels/widgets/injection/channel-info-panel/widget.js.map +7 -0
  226. package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.js +44 -0
  227. package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.js.map +7 -0
  228. package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.js +17 -0
  229. package/dist/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.js.map +7 -0
  230. package/dist/modules/communication_channels/widgets/injection/profile-channels-menu/widget.js +23 -0
  231. package/dist/modules/communication_channels/widgets/injection/profile-channels-menu/widget.js.map +7 -0
  232. package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.client.js +141 -0
  233. package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.client.js.map +7 -0
  234. package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.js +17 -0
  235. package/dist/modules/communication_channels/widgets/injection/reaction-bar/widget.js.map +7 -0
  236. package/dist/modules/communication_channels/widgets/injection-table.js +38 -0
  237. package/dist/modules/communication_channels/widgets/injection-table.js.map +7 -0
  238. package/dist/modules/communication_channels/widgets/notifications/ChannelRequiresReauthRenderer.js +25 -0
  239. package/dist/modules/communication_channels/widgets/notifications/ChannelRequiresReauthRenderer.js.map +7 -0
  240. package/dist/modules/communication_channels/widgets/notifications/MessageReceivedRenderer.js +19 -0
  241. package/dist/modules/communication_channels/widgets/notifications/MessageReceivedRenderer.js.map +7 -0
  242. package/dist/modules/communication_channels/widgets/notifications/index.js +7 -0
  243. package/dist/modules/communication_channels/widgets/notifications/index.js.map +7 -0
  244. package/dist/modules/communication_channels/workers/channel-import-history.js +185 -0
  245. package/dist/modules/communication_channels/workers/channel-import-history.js.map +7 -0
  246. package/dist/modules/communication_channels/workers/gmail-history-sync.js +154 -0
  247. package/dist/modules/communication_channels/workers/gmail-history-sync.js.map +7 -0
  248. package/dist/modules/communication_channels/workers/gmail-renew-watch.js +95 -0
  249. package/dist/modules/communication_channels/workers/gmail-renew-watch.js.map +7 -0
  250. package/dist/modules/communication_channels/workers/inbound-processor.js +56 -0
  251. package/dist/modules/communication_channels/workers/inbound-processor.js.map +7 -0
  252. package/dist/modules/communication_channels/workers/outbound-delivery.js +85 -0
  253. package/dist/modules/communication_channels/workers/outbound-delivery.js.map +7 -0
  254. package/dist/modules/communication_channels/workers/poll-channel.js +240 -0
  255. package/dist/modules/communication_channels/workers/poll-channel.js.map +7 -0
  256. package/dist/modules/communication_channels/workers/poll-tick.js +132 -0
  257. package/dist/modules/communication_channels/workers/poll-tick.js.map +7 -0
  258. package/dist/modules/communication_channels/workers/reaction-processor.js +192 -0
  259. package/dist/modules/communication_channels/workers/reaction-processor.js.map +7 -0
  260. package/dist/modules/customers/acl.js +18 -0
  261. package/dist/modules/customers/acl.js.map +2 -2
  262. package/dist/modules/customers/api/activities/route.js +9 -0
  263. package/dist/modules/customers/api/activities/route.js.map +2 -2
  264. package/dist/modules/customers/api/companies/[id]/route.js +18 -7
  265. package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
  266. package/dist/modules/customers/api/interactions/[id]/visibility/route.js +151 -0
  267. package/dist/modules/customers/api/interactions/[id]/visibility/route.js.map +7 -0
  268. package/dist/modules/customers/api/interactions/counts/route.js +6 -0
  269. package/dist/modules/customers/api/interactions/counts/route.js.map +2 -2
  270. package/dist/modules/customers/api/interactions/route.js +26 -7
  271. package/dist/modules/customers/api/interactions/route.js.map +2 -2
  272. package/dist/modules/customers/api/people/[id]/email-threads/route.js +82 -0
  273. package/dist/modules/customers/api/people/[id]/email-threads/route.js.map +7 -0
  274. package/dist/modules/customers/api/people/[id]/emails/route.js +157 -0
  275. package/dist/modules/customers/api/people/[id]/emails/route.js.map +7 -0
  276. package/dist/modules/customers/api/people/[id]/route.js +12 -4
  277. package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
  278. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +10 -0
  279. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
  280. package/dist/modules/customers/commands/deals.js +46 -5
  281. package/dist/modules/customers/commands/deals.js.map +2 -2
  282. package/dist/modules/customers/commands/interactions.js +16 -0
  283. package/dist/modules/customers/commands/interactions.js.map +2 -2
  284. package/dist/modules/customers/components/detail/ActivityCard.js +32 -0
  285. package/dist/modules/customers/components/detail/ActivityCard.js.map +2 -2
  286. package/dist/modules/customers/components/detail/ComposeEmailDialog.js +242 -0
  287. package/dist/modules/customers/components/detail/ComposeEmailDialog.js.map +7 -0
  288. package/dist/modules/customers/components/detail/DealForm.js +2 -1
  289. package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
  290. package/dist/modules/customers/components/detail/DealsSection.js +10 -0
  291. package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
  292. package/dist/modules/customers/components/detail/EmailCardActions.js +179 -0
  293. package/dist/modules/customers/components/detail/EmailCardActions.js.map +7 -0
  294. package/dist/modules/customers/components/detail/EmailReplyForwardActions.js +52 -0
  295. package/dist/modules/customers/components/detail/EmailReplyForwardActions.js.map +7 -0
  296. package/dist/modules/customers/components/detail/PersonDetailTabs.js +7 -1
  297. package/dist/modules/customers/components/detail/PersonDetailTabs.js.map +2 -2
  298. package/dist/modules/customers/components/detail/PersonEmailThreadsTab.js +366 -0
  299. package/dist/modules/customers/components/detail/PersonEmailThreadsTab.js.map +7 -0
  300. package/dist/modules/customers/data/enrichers.js +133 -2
  301. package/dist/modules/customers/data/enrichers.js.map +2 -2
  302. package/dist/modules/customers/data/entities.js +18 -0
  303. package/dist/modules/customers/data/entities.js.map +2 -2
  304. package/dist/modules/customers/data/extensions.js +16 -0
  305. package/dist/modules/customers/data/extensions.js.map +7 -0
  306. package/dist/modules/customers/encryption.js +11 -0
  307. package/dist/modules/customers/encryption.js.map +2 -2
  308. package/dist/modules/customers/events.js +4 -1
  309. package/dist/modules/customers/events.js.map +2 -2
  310. package/dist/modules/customers/lib/findPeopleByAddresses.js +64 -0
  311. package/dist/modules/customers/lib/findPeopleByAddresses.js.map +7 -0
  312. package/dist/modules/customers/lib/kysely.js.map +2 -2
  313. package/dist/modules/customers/lib/link-channel-message-handler.js +303 -0
  314. package/dist/modules/customers/lib/link-channel-message-handler.js.map +7 -0
  315. package/dist/modules/customers/lib/personEmailThreads.js +205 -0
  316. package/dist/modules/customers/lib/personEmailThreads.js.map +7 -0
  317. package/dist/modules/customers/lib/visibilityFilter.js +51 -0
  318. package/dist/modules/customers/lib/visibilityFilter.js.map +7 -0
  319. package/dist/modules/customers/migrations/Migration20260527012240_customers.js +20 -0
  320. package/dist/modules/customers/migrations/Migration20260527012240_customers.js.map +7 -0
  321. package/dist/modules/customers/setup.js +2 -1
  322. package/dist/modules/customers/setup.js.map +2 -2
  323. package/dist/modules/customers/subscribers/link-channel-message-received.js +12 -0
  324. package/dist/modules/customers/subscribers/link-channel-message-received.js.map +7 -0
  325. package/dist/modules/customers/subscribers/link-channel-message-sent.js +12 -0
  326. package/dist/modules/customers/subscribers/link-channel-message-sent.js.map +7 -0
  327. package/dist/modules/integrations/data/entities.js +8 -1
  328. package/dist/modules/integrations/data/entities.js.map +2 -2
  329. package/dist/modules/integrations/lib/credentials-service.js +29 -14
  330. package/dist/modules/integrations/lib/credentials-service.js.map +2 -2
  331. package/dist/modules/integrations/migrations/Migration20260526154136_integrations.js +15 -0
  332. package/dist/modules/integrations/migrations/Migration20260526154136_integrations.js.map +7 -0
  333. package/dist/modules/messages/commands/messages.js +70 -8
  334. package/dist/modules/messages/commands/messages.js.map +2 -2
  335. package/dist/modules/messages/components/ComposeMessagePageClient.js +24 -13
  336. package/dist/modules/messages/components/ComposeMessagePageClient.js.map +2 -2
  337. package/dist/modules/messages/components/MessageDetailPageClient.js +39 -2
  338. package/dist/modules/messages/components/MessageDetailPageClient.js.map +2 -2
  339. package/dist/modules/messages/components/MessagesInboxPageClient.js +1 -0
  340. package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
  341. package/dist/modules/messages/data/entities.js +8 -1
  342. package/dist/modules/messages/data/entities.js.map +2 -2
  343. package/dist/modules/messages/migrations/Migration20260531130000.js +15 -0
  344. package/dist/modules/messages/migrations/Migration20260531130000.js.map +7 -0
  345. package/dist/modules/messages/widgets/injection-table.js +7 -0
  346. package/dist/modules/messages/widgets/injection-table.js.map +7 -0
  347. package/generated/entities/channel_ingest_dead_letter/index.ts +11 -0
  348. package/generated/entities/channel_thread_mapping/index.ts +11 -0
  349. package/generated/entities/channel_thread_token/index.ts +7 -0
  350. package/generated/entities/communication_channel/index.ts +20 -0
  351. package/generated/entities/customer_interaction/index.ts +2 -0
  352. package/generated/entities/external_conversation/index.ts +11 -0
  353. package/generated/entities/external_message/index.ts +11 -0
  354. package/generated/entities/integration_credentials/index.ts +1 -0
  355. package/generated/entities/message/index.ts +1 -0
  356. package/generated/entities/message_channel_link/index.ts +15 -0
  357. package/generated/entities/message_reaction/index.ts +11 -0
  358. package/generated/entities.ids.generated.ts +11 -0
  359. package/generated/entity-fields-registry.ts +117 -0
  360. package/package.json +9 -7
  361. package/src/helpers/integration/authFixtures.ts +4 -1
  362. package/src/helpers/integration/communicationChannelsFixtures.ts +124 -0
  363. package/src/modules/communication_channels/acl.ts +43 -0
  364. package/src/modules/communication_channels/api/delete/channels/[id]/route.ts +163 -0
  365. package/src/modules/communication_channels/api/delete/messages/[messageId]/reactions/[reactionId]/route.ts +143 -0
  366. package/src/modules/communication_channels/api/get/channels/[id]/health/route.ts +173 -0
  367. package/src/modules/communication_channels/api/get/channels/[id]/route.ts +111 -0
  368. package/src/modules/communication_channels/api/get/channels/route.ts +109 -0
  369. package/src/modules/communication_channels/api/get/me/channels/route.ts +100 -0
  370. package/src/modules/communication_channels/api/get/oauth/[provider]/callback/route.ts +355 -0
  371. package/src/modules/communication_channels/api/post/channels/[id]/import-history/route.ts +206 -0
  372. package/src/modules/communication_channels/api/post/channels/[id]/poll-now/route.ts +174 -0
  373. package/src/modules/communication_channels/api/post/channels/[id]/push/register/route.ts +158 -0
  374. package/src/modules/communication_channels/api/post/channels/[id]/set-primary/route.ts +114 -0
  375. package/src/modules/communication_channels/api/post/channels/[id]/test-send/route.ts +241 -0
  376. package/src/modules/communication_channels/api/post/channels/connect/credentials/route.ts +134 -0
  377. package/src/modules/communication_channels/api/post/messages/[messageId]/reactions/route.ts +143 -0
  378. package/src/modules/communication_channels/api/post/oauth/[provider]/initiate/route.ts +192 -0
  379. package/src/modules/communication_channels/api/post/send-as-user/route.ts +125 -0
  380. package/src/modules/communication_channels/api/post/test-seed/route.ts +267 -0
  381. package/src/modules/communication_channels/api/post/webhook/[provider]/route.ts +227 -0
  382. package/src/modules/communication_channels/api/post/webhooks/gmail/route.ts +161 -0
  383. package/src/modules/communication_channels/api/put/threads/[threadId]/assign/route.ts +132 -0
  384. package/src/modules/communication_channels/backend/communication_channels/channels/[id]/page.meta.ts +34 -0
  385. package/src/modules/communication_channels/backend/communication_channels/channels/[id]/page.tsx +250 -0
  386. package/src/modules/communication_channels/backend/communication_channels/channels/page.meta.ts +36 -0
  387. package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +137 -0
  388. package/src/modules/communication_channels/backend/profile/communication-channels/page.meta.ts +36 -0
  389. package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +907 -0
  390. package/src/modules/communication_channels/commands/connect-credential-channel.ts +243 -0
  391. package/src/modules/communication_channels/commands/delete-channel.ts +193 -0
  392. package/src/modules/communication_channels/commands/deliver-outbound-message.ts +579 -0
  393. package/src/modules/communication_channels/commands/disconnect-channel.ts +241 -0
  394. package/src/modules/communication_channels/commands/ingest-inbound-message.ts +602 -0
  395. package/src/modules/communication_channels/commands/interceptors.ts +104 -0
  396. package/src/modules/communication_channels/commands/process-inbound-reaction.ts +265 -0
  397. package/src/modules/communication_channels/commands/push-register.ts +203 -0
  398. package/src/modules/communication_channels/commands/push-renew.ts +49 -0
  399. package/src/modules/communication_channels/commands/push-unregister.ts +168 -0
  400. package/src/modules/communication_channels/commands/queue-import-history.ts +180 -0
  401. package/src/modules/communication_channels/commands/reassign-conversation.ts +273 -0
  402. package/src/modules/communication_channels/commands/set-primary-channel.ts +154 -0
  403. package/src/modules/communication_channels/commands/toggle-outbound-reaction.ts +347 -0
  404. package/src/modules/communication_channels/data/enrichers.ts +413 -0
  405. package/src/modules/communication_channels/data/entities.ts +546 -0
  406. package/src/modules/communication_channels/data/extensions.ts +76 -0
  407. package/src/modules/communication_channels/data/validators.ts +138 -0
  408. package/src/modules/communication_channels/di.ts +40 -0
  409. package/src/modules/communication_channels/encryption.ts +44 -0
  410. package/src/modules/communication_channels/events.ts +122 -0
  411. package/src/modules/communication_channels/i18n/de.json +138 -0
  412. package/src/modules/communication_channels/i18n/en.json +138 -0
  413. package/src/modules/communication_channels/i18n/es.json +138 -0
  414. package/src/modules/communication_channels/i18n/pl.json +138 -0
  415. package/src/modules/communication_channels/index.ts +19 -0
  416. package/src/modules/communication_channels/lib/access-control.ts +110 -0
  417. package/src/modules/communication_channels/lib/adapter-compat.ts +57 -0
  418. package/src/modules/communication_channels/lib/adapter-registry-singleton.ts +35 -0
  419. package/src/modules/communication_channels/lib/adapter.ts +605 -0
  420. package/src/modules/communication_channels/lib/connect-channel.ts +163 -0
  421. package/src/modules/communication_channels/lib/contact-resolver.ts +162 -0
  422. package/src/modules/communication_channels/lib/credential-refresh.ts +197 -0
  423. package/src/modules/communication_channels/lib/dead-letter.ts +87 -0
  424. package/src/modules/communication_channels/lib/email-capabilities.ts +60 -0
  425. package/src/modules/communication_channels/lib/email-contact.ts +17 -0
  426. package/src/modules/communication_channels/lib/email-mime.ts +425 -0
  427. package/src/modules/communication_channels/lib/error-classification.ts +144 -0
  428. package/src/modules/communication_channels/lib/gmail-pubsub-jwt.ts +278 -0
  429. package/src/modules/communication_channels/lib/mutation-guards.ts +215 -0
  430. package/src/modules/communication_channels/lib/oauth-client-config.ts +79 -0
  431. package/src/modules/communication_channels/lib/oauth-state.ts +228 -0
  432. package/src/modules/communication_channels/lib/oauth-token.ts +81 -0
  433. package/src/modules/communication_channels/lib/pg-errors.ts +12 -0
  434. package/src/modules/communication_channels/lib/provider-health.ts +47 -0
  435. package/src/modules/communication_channels/lib/push-state.ts +38 -0
  436. package/src/modules/communication_channels/lib/queue.ts +66 -0
  437. package/src/modules/communication_channels/lib/reaction-processor-types.ts +51 -0
  438. package/src/modules/communication_channels/lib/reaction-semantics.ts +48 -0
  439. package/src/modules/communication_channels/lib/registry.ts +99 -0
  440. package/src/modules/communication_channels/lib/route-mutation-guard.ts +68 -0
  441. package/src/modules/communication_channels/lib/sanitize-channel-html.ts +129 -0
  442. package/src/modules/communication_channels/lib/send-as-user.ts +284 -0
  443. package/src/modules/communication_channels/lib/system-user.ts +74 -0
  444. package/src/modules/communication_channels/lib/test-seed.ts +140 -0
  445. package/src/modules/communication_channels/lib/thread-matcher.ts +430 -0
  446. package/src/modules/communication_channels/lib/thread-token.ts +355 -0
  447. package/src/modules/communication_channels/lib/use-connect-channel.ts +73 -0
  448. package/src/modules/communication_channels/migrations/.snapshot-open-mercato.json +2142 -0
  449. package/src/modules/communication_channels/migrations/Migration20260526134719_communication_channels.ts +55 -0
  450. package/src/modules/communication_channels/migrations/Migration20260527195446_communication_channels.ts +20 -0
  451. package/src/modules/communication_channels/migrations/Migration20260529231848_communication_channels.ts +13 -0
  452. package/src/modules/communication_channels/migrations/Migration20260531120000_communication_channels.ts +24 -0
  453. package/src/modules/communication_channels/notifications.client.ts +50 -0
  454. package/src/modules/communication_channels/notifications.handlers.ts +86 -0
  455. package/src/modules/communication_channels/notifications.ts +52 -0
  456. package/src/modules/communication_channels/setup.ts +158 -0
  457. package/src/modules/communication_channels/subscribers/channel-requires-reauth-notification.ts +118 -0
  458. package/src/modules/communication_channels/subscribers/outbound-bridge.ts +175 -0
  459. package/src/modules/communication_channels/subscribers/user-deleted-cascade.ts +100 -0
  460. package/src/modules/communication_channels/widgets/components.ts +36 -0
  461. package/src/modules/communication_channels/widgets/injection/channel-badge/widget.client.tsx +38 -0
  462. package/src/modules/communication_channels/widgets/injection/channel-badge/widget.ts +51 -0
  463. package/src/modules/communication_channels/widgets/injection/channel-info-panel/widget.client.tsx +278 -0
  464. package/src/modules/communication_channels/widgets/injection/channel-info-panel/widget.ts +24 -0
  465. package/src/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.client.tsx +63 -0
  466. package/src/modules/communication_channels/widgets/injection/channel-payload-renderer/widget.ts +29 -0
  467. package/src/modules/communication_channels/widgets/injection/profile-channels-menu/widget.ts +34 -0
  468. package/src/modules/communication_channels/widgets/injection/reaction-bar/widget.client.tsx +177 -0
  469. package/src/modules/communication_channels/widgets/injection/reaction-bar/widget.ts +26 -0
  470. package/src/modules/communication_channels/widgets/injection-table.ts +47 -0
  471. package/src/modules/communication_channels/widgets/notifications/ChannelRequiresReauthRenderer.tsx +48 -0
  472. package/src/modules/communication_channels/widgets/notifications/MessageReceivedRenderer.tsx +45 -0
  473. package/src/modules/communication_channels/widgets/notifications/index.ts +2 -0
  474. package/src/modules/communication_channels/workers/channel-import-history.ts +252 -0
  475. package/src/modules/communication_channels/workers/gmail-history-sync.ts +223 -0
  476. package/src/modules/communication_channels/workers/gmail-renew-watch.ts +141 -0
  477. package/src/modules/communication_channels/workers/inbound-processor.ts +114 -0
  478. package/src/modules/communication_channels/workers/outbound-delivery.ts +155 -0
  479. package/src/modules/communication_channels/workers/poll-channel.ts +391 -0
  480. package/src/modules/communication_channels/workers/poll-tick.ts +210 -0
  481. package/src/modules/communication_channels/workers/reaction-processor.ts +264 -0
  482. package/src/modules/customers/acl.ts +18 -0
  483. package/src/modules/customers/api/activities/route.ts +13 -0
  484. package/src/modules/customers/api/companies/[id]/route.ts +21 -1
  485. package/src/modules/customers/api/interactions/[id]/visibility/route.ts +179 -0
  486. package/src/modules/customers/api/interactions/counts/route.ts +10 -0
  487. package/src/modules/customers/api/interactions/route.ts +51 -5
  488. package/src/modules/customers/api/people/[id]/email-threads/route.ts +92 -0
  489. package/src/modules/customers/api/people/[id]/emails/route.ts +184 -0
  490. package/src/modules/customers/api/people/[id]/route.ts +17 -2
  491. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +11 -1
  492. package/src/modules/customers/commands/deals.ts +65 -6
  493. package/src/modules/customers/commands/interactions.ts +30 -0
  494. package/src/modules/customers/components/detail/ActivityCard.tsx +48 -0
  495. package/src/modules/customers/components/detail/ComposeEmailDialog.tsx +329 -0
  496. package/src/modules/customers/components/detail/DealForm.tsx +2 -1
  497. package/src/modules/customers/components/detail/DealsSection.tsx +26 -0
  498. package/src/modules/customers/components/detail/EmailCardActions.tsx +258 -0
  499. package/src/modules/customers/components/detail/EmailReplyForwardActions.tsx +53 -0
  500. package/src/modules/customers/components/detail/PersonDetailTabs.tsx +8 -1
  501. package/src/modules/customers/components/detail/PersonEmailThreadsTab.tsx +448 -0
  502. package/src/modules/customers/data/enrichers.ts +252 -1
  503. package/src/modules/customers/data/entities.ts +46 -1
  504. package/src/modules/customers/data/extensions.ts +26 -0
  505. package/src/modules/customers/encryption.ts +11 -0
  506. package/src/modules/customers/events.ts +4 -0
  507. package/src/modules/customers/i18n/de.json +41 -0
  508. package/src/modules/customers/i18n/en.json +41 -0
  509. package/src/modules/customers/i18n/es.json +41 -0
  510. package/src/modules/customers/i18n/pl.json +41 -0
  511. package/src/modules/customers/lib/findPeopleByAddresses.ts +107 -0
  512. package/src/modules/customers/lib/kysely.ts +16 -0
  513. package/src/modules/customers/lib/link-channel-message-handler.ts +571 -0
  514. package/src/modules/customers/lib/personEmailThreads.ts +325 -0
  515. package/src/modules/customers/lib/visibilityFilter.ts +152 -0
  516. package/src/modules/customers/migrations/.snapshot-open-mercato.json +61 -0
  517. package/src/modules/customers/migrations/Migration20260527012240_customers.ts +23 -0
  518. package/src/modules/customers/setup.ts +1 -0
  519. package/src/modules/customers/subscribers/link-channel-message-received.ts +21 -0
  520. package/src/modules/customers/subscribers/link-channel-message-sent.ts +21 -0
  521. package/src/modules/integrations/AGENTS.md +9 -0
  522. package/src/modules/integrations/data/entities.ts +21 -1
  523. package/src/modules/integrations/lib/credentials-service.ts +49 -13
  524. package/src/modules/integrations/migrations/.snapshot-open-mercato.json +26 -1
  525. package/src/modules/integrations/migrations/Migration20260526154136_integrations.ts +15 -0
  526. package/src/modules/messages/commands/messages.ts +101 -8
  527. package/src/modules/messages/components/ComposeMessagePageClient.tsx +17 -0
  528. package/src/modules/messages/components/MessageDetailPageClient.tsx +43 -0
  529. package/src/modules/messages/components/MessagesInboxPageClient.tsx +4 -0
  530. package/src/modules/messages/data/entities.ts +11 -0
  531. package/src/modules/messages/migrations/.snapshot-open-mercato.json +18 -0
  532. package/src/modules/messages/migrations/Migration20260531130000.ts +15 -0
  533. package/src/modules/messages/widgets/injection-table.ts +29 -0
@@ -0,0 +1,210 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql'
2
+ import type { JobContext, QueuedJob, WorkerMeta } from '@open-mercato/queue'
3
+ import { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
4
+ import { CommunicationChannel } from '../data/entities'
5
+ import { COMMUNICATION_CHANNELS_QUEUES, getCommunicationChannelsQueue } from '../lib/queue'
6
+ import type { PollChannelJobPayload } from './poll-channel'
7
+
8
+ /**
9
+ * Scheduler tick payload. Fired by the `@open-mercato/scheduler` cron entry
10
+ * registered in the hub's `setup.ts` (`communication_channels:poll-tick` schedule).
11
+ */
12
+ export type PollTickPayload = {
13
+ scope: {
14
+ tenantId: string
15
+ organizationId: string | null
16
+ }
17
+ }
18
+
19
+ const POLL_ENUMERATION_CAP = Math.max(
20
+ 1,
21
+ Number.parseInt(process.env.COMMUNICATION_CHANNELS_POLL_ENUMERATION_CAP ?? '500', 10) || 500,
22
+ )
23
+
24
+ export const metadata: WorkerMeta = {
25
+ queue: COMMUNICATION_CHANNELS_QUEUES.pollTick,
26
+ id: 'communication_channels:poll-tick',
27
+ concurrency: 1, // single-flight per tenant — one tick at a time
28
+ }
29
+
30
+ type HandlerContext = JobContext & {
31
+ resolve: <T = unknown>(name: string) => T
32
+ }
33
+
34
+ /**
35
+ * Enumerate channels due for polling and enqueue per-channel jobs.
36
+ *
37
+ * Per email integration spec § Hub Deltas → Delta 6:
38
+ * SELECT id FROM communication_channels
39
+ * WHERE is_active = true
40
+ * AND deleted_at IS NULL
41
+ * AND status = 'connected'
42
+ * AND poll_interval_seconds IS NOT NULL
43
+ * AND (last_polled_at IS NULL
44
+ * OR last_polled_at + poll_interval_seconds * '1 sec' <= NOW())
45
+ * ORDER BY last_polled_at NULLS FIRST
46
+ * LIMIT 500
47
+ *
48
+ * Implementation note: MikroORM's QueryBuilder is the canonical entry point;
49
+ * the raw SQL above is the conceptual query. We use the entity-level filter to
50
+ * keep things portable and let MikroORM compile it.
51
+ */
52
+ export default async function handle(
53
+ job: QueuedJob<PollTickPayload>,
54
+ ctx: HandlerContext,
55
+ ): Promise<void> {
56
+ // The scheduler module (`@open-mercato/scheduler`) spreads the configured
57
+ // `targetPayload` and then adds `tenantId` / `organizationId` at the TOP
58
+ // level of the enqueued payload (see
59
+ // packages/scheduler/.../execute-schedule.worker.ts). Our setup.ts originally
60
+ // stored `{ scope: { tenantId, organizationId } }` under targetPayload, so
61
+ // at runtime the payload looks like:
62
+ // { scope: { tenantId, organizationId }, tenantId, organizationId, _idempotencyKey }
63
+ // Accept either path so the handler is robust to both how operators originally
64
+ // configured the schedule (nested `scope`) and how the scheduler flattens it.
65
+ const raw = (job?.payload ?? {}) as Partial<PollTickPayload> & {
66
+ tenantId?: string | null
67
+ organizationId?: string | null
68
+ }
69
+ const tenantId = raw.scope?.tenantId ?? raw.tenantId ?? null
70
+ const organizationId =
71
+ raw.scope?.organizationId ?? raw.organizationId ?? null
72
+ if (!tenantId) {
73
+ console.warn(
74
+ '[communication_channels:poll-tick] skipping tick — payload has no tenantId',
75
+ { payload: raw },
76
+ )
77
+ return
78
+ }
79
+ const scope = { tenantId, organizationId }
80
+ const em = (ctx.resolve('em') as EntityManager).fork()
81
+
82
+ const now = new Date()
83
+ // Find candidate channels — we enumerate two pools:
84
+ // (1) status='connected' channels due for their normal poll cycle.
85
+ // (2) Spec B § Auto-recovery sweep: status='error' channels whose
86
+ // `lastPolledAt` is older than OM_CHANNEL_AUTO_RECOVER_MINUTES
87
+ // (default 30 min). At most one retry per recovery window per
88
+ // channel — when we enqueue a recovery job below we bump that
89
+ // channel's `lastPolledAt` to `now` so it falls back under the
90
+ // cutoff and is NOT re-selected on the immediately-following ticks
91
+ // (poll-channel only advances `lastPolledAt` on a SUCCESSFUL poll,
92
+ // so without this a persistently-failing channel would re-enqueue
93
+ // every tick). On success `poll-channel` flips the status back to
94
+ // 'connected' so the channel rejoins the normal pool.
95
+ //
96
+ // Due-ness for (1) is computed in JS to avoid cross-DB interval
97
+ // arithmetic; the (2) cutoff is a single timestamp compare.
98
+ const connectedCandidates = await findWithDecryption(
99
+ em,
100
+ CommunicationChannel,
101
+ {
102
+ tenantId: scope.tenantId,
103
+ organizationId: scope.organizationId ?? null,
104
+ isActive: true,
105
+ deletedAt: null,
106
+ status: 'connected',
107
+ pollIntervalSeconds: { $ne: null },
108
+ },
109
+ {
110
+ limit: POLL_ENUMERATION_CAP,
111
+ orderBy: { lastPolledAt: 'asc' },
112
+ },
113
+ scope,
114
+ )
115
+
116
+ const recoverMinutesRaw = Number.parseInt(
117
+ process.env.OM_CHANNEL_AUTO_RECOVER_MINUTES ?? '',
118
+ 10,
119
+ )
120
+ // `0` is a valid override meaning "recover on the very next tick" (used by
121
+ // TC-CHANNEL-EMAIL-027 and operators who want aggressive recovery). Only a
122
+ // negative or non-numeric value falls back to the 30-minute default.
123
+ const recoverMinutes =
124
+ Number.isFinite(recoverMinutesRaw) && recoverMinutesRaw >= 0 ? recoverMinutesRaw : 30
125
+ const recoverCutoff = new Date(now.getTime() - recoverMinutes * 60 * 1000)
126
+ const errorCandidates = await findWithDecryption(
127
+ em,
128
+ CommunicationChannel,
129
+ {
130
+ tenantId: scope.tenantId,
131
+ organizationId: scope.organizationId ?? null,
132
+ isActive: true,
133
+ deletedAt: null,
134
+ status: 'error',
135
+ // Polling channels only. Push-only channels (Gmail) have
136
+ // `pollIntervalSeconds = null` and are intentionally excluded: poll-channel
137
+ // returns early for push providers, so their recovery is owner-driven
138
+ // (re-register push / reconnect), not this poll-recovery sweep.
139
+ pollIntervalSeconds: { $ne: null },
140
+ // `lastPolledAt` advances only on a SUCCESSFUL poll (poll-channel does
141
+ // not touch it in `handlePollError`). To keep recovery to one retry per
142
+ // window — rather than re-enqueuing a persistently-failing channel every
143
+ // tick — the recovery-enqueue loop below bumps `lastPolledAt` to `now`
144
+ // when it schedules a recovery job, so the channel only re-enters this
145
+ // pool after another `recoverMinutes` have elapsed.
146
+ //
147
+ // A channel that fails its FIRST poll (before any success) still has
148
+ // `lastPolledAt = null`; a bare `$lt` would exclude it forever (SQL
149
+ // `NULL < ts` is NULL, not true), stranding it in `error`. Include the
150
+ // null case so never-polled error channels get their first recovery
151
+ // attempt on the next tick (the enqueue bump below then throttles it).
152
+ $or: [{ lastPolledAt: null }, { lastPolledAt: { $lt: recoverCutoff } }],
153
+ },
154
+ {
155
+ limit: POLL_ENUMERATION_CAP,
156
+ orderBy: { lastPolledAt: 'asc' },
157
+ },
158
+ scope,
159
+ )
160
+
161
+ const queue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.poll)
162
+ let enqueued = 0
163
+ let recovered = 0
164
+ for (const channel of connectedCandidates as CommunicationChannel[]) {
165
+ const intervalSeconds = channel.pollIntervalSeconds
166
+ if (!intervalSeconds || intervalSeconds <= 0) continue
167
+ if (!isDue(channel.lastPolledAt ?? null, intervalSeconds, now)) continue
168
+ const payload: PollChannelJobPayload = {
169
+ channelId: channel.id,
170
+ scope: {
171
+ tenantId: channel.tenantId,
172
+ organizationId: channel.organizationId ?? scope.organizationId ?? null,
173
+ },
174
+ attempt: 1,
175
+ }
176
+ await queue.enqueue(payload as unknown as Record<string, unknown>)
177
+ enqueued += 1
178
+ }
179
+ // Auto-recovery: at most one retry per recovery window per error-state
180
+ // channel. We bump `lastPolledAt` to `now` as we enqueue so the same channel
181
+ // drops back under `recoverCutoff` and is NOT re-selected on the next ticks
182
+ // (poll-channel leaves `lastPolledAt` untouched on failure, so without this a
183
+ // persistently-failing channel would be re-enqueued every tick).
184
+ for (const channel of errorCandidates as CommunicationChannel[]) {
185
+ const payload: PollChannelJobPayload = {
186
+ channelId: channel.id,
187
+ scope: {
188
+ tenantId: channel.tenantId,
189
+ organizationId: channel.organizationId ?? scope.organizationId ?? null,
190
+ },
191
+ attempt: 1,
192
+ }
193
+ await queue.enqueue(payload as unknown as Record<string, unknown>)
194
+ channel.lastPolledAt = now
195
+ recovered += 1
196
+ }
197
+ if (recovered > 0) await em.flush()
198
+
199
+ if (enqueued > 0 || recovered > 0) {
200
+ console.log(
201
+ `[communication_channels:poll-tick] enqueued ${enqueued} normal + ${recovered} auto-recover poll job(s) for tenant ${scope.tenantId}`,
202
+ )
203
+ }
204
+ }
205
+
206
+ function isDue(lastPolledAt: Date | null, intervalSeconds: number, now: Date): boolean {
207
+ if (!lastPolledAt) return true
208
+ const dueAt = new Date(lastPolledAt.getTime() + intervalSeconds * 1000)
209
+ return now >= dueAt
210
+ }
@@ -0,0 +1,264 @@
1
+ import type { JobContext, QueuedJob, WorkerMeta } from '@open-mercato/queue'
2
+ import type { CommandBus } from '@open-mercato/shared/lib/commands'
3
+ import type { EntityManager } from '@mikro-orm/postgresql'
4
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
5
+ import {
6
+ COMMUNICATION_CHANNELS_PROCESS_INBOUND_REACTION_COMMAND_ID,
7
+ type ProcessInboundReactionInput,
8
+ type ProcessInboundReactionResult,
9
+ } from '../commands/process-inbound-reaction'
10
+ import { COMMUNICATION_CHANNELS_QUEUES, getCommunicationChannelsQueue } from '../lib/queue'
11
+ import { classifyOutboundError, computeBackoffMs } from '../lib/error-classification'
12
+ import { CommunicationChannel } from '../data/entities'
13
+ import {
14
+ REACTION_PROCESSOR_MAX_ATTEMPTS,
15
+ type ReactionInboundJob,
16
+ type ReactionOutboundRemoveJob,
17
+ type ReactionOutboundSendJob,
18
+ type ReactionProcessorPayload,
19
+ } from '../lib/reaction-processor-types'
20
+ import { refreshCredentialsIfNeeded } from '../lib/credential-refresh'
21
+ import type { ChannelAdapterRegistry } from '../lib/registry'
22
+
23
+ export type { ReactionProcessorPayload }
24
+
25
+ export const metadata: WorkerMeta = {
26
+ queue: COMMUNICATION_CHANNELS_QUEUES.reactions,
27
+ id: 'communication_channels:reaction-processor',
28
+ concurrency: 10,
29
+ }
30
+
31
+ type HandlerContext = JobContext & {
32
+ resolve: <T = unknown>(name: string) => T
33
+ }
34
+
35
+ /**
36
+ * Unified reaction worker — handles inbound, outbound-send, and outbound-remove
37
+ * jobs from the `communication-channels-reactions` queue via a discriminated payload.
38
+ *
39
+ * - `inbound` — dispatches the `process_inbound_reaction` command which
40
+ * applies provider semantics and emits `.reaction.added/.removed`.
41
+ * - `outbound_send` — calls `adapter.sendReaction?(...)`. The local
42
+ * `MessageReaction` row was already created by the API/command layer
43
+ * for UX responsiveness; this worker just notifies the provider.
44
+ * - `outbound_remove` — calls `adapter.removeReaction?(...)`. The local
45
+ * row was already deleted by the API/command layer.
46
+ *
47
+ * Retry: transient failures re-enqueue with exponential backoff up to
48
+ * `REACTION_PROCESSOR_MAX_ATTEMPTS = 3`. Permanent failures stop. Reactions
49
+ * are inherently low-stakes (a missed reaction is annoying, not data loss),
50
+ * so we do not surface failures via a dedicated event; provider errors are
51
+ * logged through `integrationLogService` when available.
52
+ */
53
+ export default async function handle(
54
+ job: QueuedJob<ReactionProcessorPayload>,
55
+ ctx: HandlerContext,
56
+ ): Promise<void> {
57
+ switch (job.payload.kind) {
58
+ case 'inbound':
59
+ await handleInbound(job.payload, ctx)
60
+ return
61
+ case 'outbound_send':
62
+ await handleOutboundSend(job.payload, ctx)
63
+ return
64
+ case 'outbound_remove':
65
+ await handleOutboundRemove(job.payload, ctx)
66
+ return
67
+ default: {
68
+ // exhaustiveness check
69
+ const exhaustive: never = job.payload
70
+ throw new Error(`Unknown reaction job kind: ${JSON.stringify(exhaustive)}`)
71
+ }
72
+ }
73
+ }
74
+
75
+ async function handleInbound(payload: ReactionInboundJob, ctx: HandlerContext): Promise<void> {
76
+ const commandBus = ctx.resolve<CommandBus>('commandBus')
77
+ const containerProxy = { resolve: ctx.resolve.bind(ctx) }
78
+ const commandCtx = {
79
+ container: containerProxy as never,
80
+ auth: null,
81
+ organizationScope: null,
82
+ selectedOrganizationId: payload.scope.organizationId ?? null,
83
+ organizationIds: payload.scope.organizationId ? [payload.scope.organizationId] : null,
84
+ }
85
+ const input: ProcessInboundReactionInput = {
86
+ channelId: payload.channelId,
87
+ providerKey: payload.providerKey,
88
+ channelType: payload.channelType,
89
+ scope: payload.scope,
90
+ event: payload.event,
91
+ }
92
+ await commandBus.execute<ProcessInboundReactionInput, ProcessInboundReactionResult>(
93
+ COMMUNICATION_CHANNELS_PROCESS_INBOUND_REACTION_COMMAND_ID,
94
+ { input, ctx: commandCtx as never },
95
+ )
96
+ }
97
+
98
+ async function handleOutboundSend(payload: ReactionOutboundSendJob, ctx: HandlerContext): Promise<void> {
99
+ const result = await callAdapterOutbound(payload, ctx, 'send')
100
+ await maybeRetry(result, payload, ctx)
101
+ }
102
+
103
+ async function handleOutboundRemove(
104
+ payload: ReactionOutboundRemoveJob,
105
+ ctx: HandlerContext,
106
+ ): Promise<void> {
107
+ const result = await callAdapterOutbound(payload, ctx, 'remove')
108
+ await maybeRetry(result, payload, ctx)
109
+ }
110
+
111
+ type AdapterCallResult =
112
+ | { status: 'ok' }
113
+ | { status: 'no_adapter'; message: string }
114
+ | { status: 'channel_inactive'; message: string }
115
+ | { status: 'failed'; transient: boolean; message: string }
116
+
117
+ async function callAdapterOutbound(
118
+ payload: ReactionOutboundSendJob | ReactionOutboundRemoveJob,
119
+ ctx: HandlerContext,
120
+ action: 'send' | 'remove',
121
+ ): Promise<AdapterCallResult> {
122
+ const adapterRegistry = ctx.resolve<ChannelAdapterRegistry>('channelAdapterRegistry')
123
+ const adapter = adapterRegistry?.get(payload.providerKey)
124
+ if (!adapter) {
125
+ return { status: 'no_adapter', message: `No adapter for provider '${payload.providerKey}'` }
126
+ }
127
+ const em = (ctx.resolve('em') as EntityManager).fork()
128
+ const channel = await findOneWithDecryption(
129
+ em,
130
+ CommunicationChannel,
131
+ {
132
+ id: payload.channelId,
133
+ tenantId: payload.scope.tenantId,
134
+ organizationId: payload.scope.organizationId ?? null,
135
+ deletedAt: null,
136
+ },
137
+ undefined,
138
+ payload.scope,
139
+ )
140
+ if (!channel) {
141
+ return { status: 'no_adapter', message: `Channel ${payload.channelId} not found` }
142
+ }
143
+ if (!channel.isActive) {
144
+ return { status: 'channel_inactive', message: `Channel ${payload.channelId} is inactive` }
145
+ }
146
+
147
+ // Credentials. Per-user scoping via `channel.userId` (review R2-C1 / N1).
148
+ type CredentialsServiceLike = {
149
+ resolve: (
150
+ integrationId: string,
151
+ scope: { organizationId: string; tenantId: string; userId?: string | null },
152
+ ) => Promise<Record<string, unknown> | null>
153
+ save?: (
154
+ integrationId: string,
155
+ credentials: Record<string, unknown>,
156
+ scope: { organizationId: string; tenantId: string; userId?: string | null },
157
+ ) => Promise<void>
158
+ }
159
+ let credentials: Record<string, unknown> = {}
160
+ let credentialsService: CredentialsServiceLike | null = null
161
+ try {
162
+ credentialsService = ctx.resolve<CredentialsServiceLike>('integrationCredentialsService')
163
+ } catch {
164
+ credentialsService = null
165
+ }
166
+ const credentialsScope = {
167
+ tenantId: payload.scope.tenantId,
168
+ organizationId: payload.scope.organizationId ?? payload.scope.tenantId,
169
+ userId: channel.userId ?? null,
170
+ }
171
+ if (channel.credentialsRef && credentialsService) {
172
+ try {
173
+ credentials =
174
+ (await credentialsService.resolve(`channel_${channel.providerKey}`, credentialsScope)) ?? {}
175
+ } catch {
176
+ credentials = {}
177
+ }
178
+ }
179
+ const refreshed = await refreshCredentialsIfNeeded(
180
+ {
181
+ adapter,
182
+ channelId: channel.id,
183
+ credentials,
184
+ scope: credentialsScope,
185
+ },
186
+ { credentialsService },
187
+ )
188
+ credentials = refreshed.credentials
189
+
190
+ try {
191
+ if (action === 'send') {
192
+ if (typeof adapter.sendReaction !== 'function') {
193
+ return { status: 'no_adapter', message: `Adapter '${adapter.providerKey}' has no sendReaction` }
194
+ }
195
+ const sendPayload = payload as ReactionOutboundSendJob
196
+ await adapter.sendReaction({
197
+ externalMessageId: sendPayload.messageId, // platform message id; the adapter maps to provider id via its own channel-link lookup if needed
198
+ conversationId: sendPayload.conversationId ?? '',
199
+ emoji: sendPayload.emoji,
200
+ credentials,
201
+ scope: {
202
+ tenantId: payload.scope.tenantId,
203
+ organizationId: payload.scope.organizationId ?? payload.scope.tenantId,
204
+ },
205
+ })
206
+ } else {
207
+ if (typeof adapter.removeReaction !== 'function') {
208
+ return { status: 'no_adapter', message: `Adapter '${adapter.providerKey}' has no removeReaction` }
209
+ }
210
+ const removePayload = payload as ReactionOutboundRemoveJob
211
+ await adapter.removeReaction({
212
+ externalMessageId: removePayload.externalReactionId ?? removePayload.messageId,
213
+ conversationId: removePayload.conversationId ?? '',
214
+ emoji: removePayload.emoji,
215
+ credentials,
216
+ scope: {
217
+ tenantId: payload.scope.tenantId,
218
+ organizationId: payload.scope.organizationId ?? payload.scope.tenantId,
219
+ },
220
+ })
221
+ }
222
+ return { status: 'ok' }
223
+ } catch (err) {
224
+ const classification = classifyOutboundError(err)
225
+ return {
226
+ status: 'failed',
227
+ transient: classification.transient,
228
+ message: classification.message,
229
+ }
230
+ }
231
+ }
232
+
233
+ async function maybeRetry(
234
+ result: AdapterCallResult,
235
+ payload: ReactionProcessorPayload,
236
+ ctx: HandlerContext,
237
+ ): Promise<void> {
238
+ if (result.status === 'ok') return
239
+ if (result.status === 'no_adapter' || result.status === 'channel_inactive') {
240
+ // Permanent — log and stop.
241
+ console.error(
242
+ `[communication_channels:reaction-processor] ${result.status}: ${result.message}`,
243
+ )
244
+ return
245
+ }
246
+ if (!result.transient) {
247
+ console.error(
248
+ `[communication_channels:reaction-processor] permanent failure: ${result.message}`,
249
+ )
250
+ return
251
+ }
252
+ const attempt = payload.attempt ?? 1
253
+ if (attempt >= REACTION_PROCESSOR_MAX_ATTEMPTS) {
254
+ console.error(
255
+ `[communication_channels:reaction-processor] giving up after attempt ${attempt}: ${result.message}`,
256
+ )
257
+ return
258
+ }
259
+ const next: ReactionProcessorPayload = { ...payload, attempt: attempt + 1 }
260
+ const queue = getCommunicationChannelsQueue(COMMUNICATION_CHANNELS_QUEUES.reactions)
261
+ await queue.enqueue(next as unknown as Record<string, unknown>, {
262
+ delayMs: computeBackoffMs(attempt),
263
+ })
264
+ }
@@ -78,6 +78,24 @@ export const features = [
78
78
  module: 'customers',
79
79
  dependsOn: ['customers.roles.view'],
80
80
  },
81
+ // Email integration (2026-05-27)
82
+ {
83
+ id: 'customers.email.compose',
84
+ title: 'Compose / send emails from CRM',
85
+ module: 'customers',
86
+ dependsOn: ['customers.people.view'],
87
+ },
88
+ // Reserved for a future v2 admin-oversight capability. In v1 the email
89
+ // privacy model is strict owner-only with NO admin bypass, so this feature is
90
+ // declared but INERT — granting it does not unlock other users' private emails
91
+ // (the visibility filter and the visibility-change gate ignore it). See
92
+ // .ai/specs/2026-05-27-crm-email-integration.md (v1 strict owner-only).
93
+ {
94
+ id: 'customers.email.view_private',
95
+ title: 'View other users\' private emails (reserved — inert in v1)',
96
+ module: 'customers',
97
+ dependsOn: ['customers.interactions.view'],
98
+ },
81
99
  ]
82
100
 
83
101
  export default features
@@ -28,6 +28,7 @@ import { resolveCustomerInteractionFeatureFlags } from '../../lib/interactionFea
28
28
  import { resolveCustomersRequestContext } from '../../lib/interactionRequestContext'
29
29
  import { hydrateCanonicalInteractions } from '../../lib/interactionReadModel'
30
30
  import { resolveCanonicalActivityTargetId } from '../../lib/legacyActivityBridge'
31
+ import { buildEmailVisibilityMikroFilter } from '../../lib/visibilityFilter'
31
32
 
32
33
  const listSchema = z.object({
33
34
  page: z.coerce.number().min(1).default(1),
@@ -291,6 +292,18 @@ async function listCanonicalActivities(
291
292
  where.source = Array.isArray(options.source) ? { $in: options.source } : options.source
292
293
  }
293
294
 
295
+ // Per-user email privacy: exclude other users' private email interactions from
296
+ // the deprecated /activities surface (mirrors the /interactions Layer-1 filter).
297
+ // v1 strict owner-only — no admin bypass (the filter ignores caller features).
298
+ const activitiesViewerUserId = auth.keyId ? null : (auth.sub ?? auth.userId ?? null)
299
+ Object.assign(
300
+ where,
301
+ buildEmailVisibilityMikroFilter({
302
+ currentUserId: activitiesViewerUserId,
303
+ userFeatures: undefined,
304
+ }),
305
+ )
306
+
294
307
  const findOptions = {
295
308
  orderBy: buildCanonicalOrderBy(query.sortField, query.sortDir ?? 'desc'),
296
309
  ...(options?.paginate === false
@@ -37,6 +37,7 @@ import {
37
37
  } from '../../../lib/interactionCompatibility'
38
38
  import { resolveCustomerInteractionFeatureFlags } from '../../../lib/interactionFeatureFlags'
39
39
  import { hydrateCanonicalInteractions } from '../../../lib/interactionReadModel'
40
+ import { buildEmailVisibilityMikroFilter } from '../../../lib/visibilityFilter'
40
41
  import type { QueryEngine } from '@open-mercato/shared/lib/query/types'
41
42
  import type { EntityId } from '@open-mercato/shared/modules/entities'
42
43
  import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
@@ -396,6 +397,20 @@ export async function GET(_req: Request, ctx: { params?: { id?: string } }) {
396
397
  organizationId: company.organizationId ?? scope?.selectedId ?? auth.orgId ?? null,
397
398
  }
398
399
 
400
+ // Per-user email privacy (CRM email integration): exclude private email
401
+ // interactions owned by other users from every read path that returns
402
+ // customer_interactions. Non-email rows, shared emails, and legacy
403
+ // null-visibility rows pass through. v1 is strict owner-only: there is NO
404
+ // admin bypass — the filter ignores caller features, and
405
+ // `customers.email.view_private` is reserved (inert) for v2 oversight.
406
+ // Mirrors the people detail route. API-key callers resolve to null
407
+ // (no author match — shared/non-email rows only).
408
+ const viewerUserId = auth.isApiKey ? null : (auth.sub ?? null)
409
+ const emailVisibilityFilter = buildEmailVisibilityMikroFilter({
410
+ currentUserId: viewerUserId,
411
+ userFeatures: undefined,
412
+ })
413
+
399
414
  const profile = company.companyProfile
400
415
  ? await findOneWithDecryption(
401
416
  em,
@@ -480,11 +495,13 @@ export async function GET(_req: Request, ctx: { params?: { id?: string } }) {
480
495
  tenantId: company.tenantId,
481
496
  organizationId: company.organizationId,
482
497
  deletedAt: null,
498
+ ...emailVisibilityFilter,
483
499
  }
484
500
  : {
485
501
  entity: company.id,
486
502
  tenantId: company.tenantId,
487
503
  organizationId: company.organizationId,
504
+ ...emailVisibilityFilter,
488
505
  },
489
506
  { orderBy: { scheduledAt: 'asc', createdAt: 'desc' }, limit: 100 },
490
507
  companyScope,
@@ -523,6 +540,7 @@ export async function GET(_req: Request, ctx: { params?: { id?: string } }) {
523
540
  deletedAt: null,
524
541
  status: 'planned',
525
542
  interactionType: { $ne: 'task' },
543
+ ...emailVisibilityFilter,
526
544
  },
527
545
  { orderBy: { scheduledAt: 'ASC', createdAt: 'ASC' }, limit: plannedPreviewLimit },
528
546
  companyScope,
@@ -609,7 +627,6 @@ export async function GET(_req: Request, ctx: { params?: { id?: string } }) {
609
627
  if (comment.authorUserId) authorIds.add(comment.authorUserId)
610
628
  }
611
629
  }
612
- const viewerUserId = auth.isApiKey ? null : auth.sub ?? null
613
630
  if (viewerUserId) authorIds.add(viewerUserId)
614
631
 
615
632
  let userMap = new Map<string, { name: string | null; email: string | null }>()
@@ -795,12 +812,14 @@ export async function GET(_req: Request, ctx: { params?: { id?: string } }) {
795
812
  tenantId: company.tenantId,
796
813
  deletedAt: null,
797
814
  interactionType: { $ne: 'task' },
815
+ ...emailVisibilityFilter,
798
816
  })
799
817
  const interactionCount = await em.count(CustomerInteraction, {
800
818
  entity: company.id,
801
819
  organizationId: company.organizationId,
802
820
  tenantId: company.tenantId,
803
821
  deletedAt: null,
822
+ ...emailVisibilityFilter,
804
823
  })
805
824
  const todoCount = interactionFlags.unified
806
825
  ? await em.count(CustomerInteraction, {
@@ -858,6 +877,7 @@ export async function GET(_req: Request, ctx: { params?: { id?: string } }) {
858
877
  organizationId: company.organizationId,
859
878
  tenantId: company.tenantId,
860
879
  deletedAt: null,
880
+ ...emailVisibilityFilter,
861
881
  },
862
882
  {
863
883
  fields: ['id', 'occurredAt', 'scheduledAt', 'createdAt'],