@open-mercato/core 0.6.5-develop.4384.1.ce2ec6eaaa → 0.6.5-develop.4397.1.9a65481757

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 +269 -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 +442 -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,252 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql'
2
+ import type { JobContext, QueuedJob, WorkerMeta } from '@open-mercato/queue'
3
+ import type { CommandBus } from '@open-mercato/shared/lib/commands'
4
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
5
+ import { CommunicationChannel } from '../data/entities'
6
+ import {
7
+ COMMUNICATION_CHANNELS_INGEST_INBOUND_COMMAND_ID,
8
+ type IngestInboundMessageInput,
9
+ } from '../commands/ingest-inbound-message'
10
+ import { COMMUNICATION_CHANNELS_QUEUES } from '../lib/queue'
11
+ import { refreshCredentialsIfNeeded } from '../lib/credential-refresh'
12
+ import { classifyOutboundError } from '../lib/error-classification'
13
+ import type { ChannelAdapterRegistry } from '../lib/registry'
14
+ import type {
15
+ ChannelImportHistoryJobPayload,
16
+ } from '../commands/queue-import-history'
17
+ import type {
18
+ ProgressService,
19
+ ProgressServiceContext,
20
+ } from '../../progress/lib/progressService'
21
+
22
+ /**
23
+ * Spec B § Phase B6 — operator-triggered backlog import worker.
24
+ *
25
+ * Distinct from `poll-channel` (which runs every scheduler tick and ingests
26
+ * *new* mail since the channel cursor). This worker reaches backward in time
27
+ * by calling `adapter.importHistory` with explicit `sinceDays` / `contactEmails`
28
+ * filters, paginating until the adapter signals `hasMore: false` or the
29
+ * `maxMessages` cap is reached.
30
+ *
31
+ * Runs with `concurrency: 1` to avoid hammering the provider with multiple
32
+ * historical sweeps in parallel; per-channel concurrency is additionally
33
+ * enforced at enqueue time by `queueImportHistory`.
34
+ */
35
+ export const metadata: WorkerMeta = {
36
+ queue: COMMUNICATION_CHANNELS_QUEUES.importHistory,
37
+ id: 'communication_channels:channel-import-history',
38
+ concurrency: 1,
39
+ }
40
+
41
+ type HandlerContext = JobContext & {
42
+ resolve: <T = unknown>(name: string) => T
43
+ }
44
+
45
+ type CredentialsServiceLike = {
46
+ resolve: (
47
+ integrationId: string,
48
+ scope: { organizationId: string; tenantId: string; userId?: string | null },
49
+ ) => Promise<Record<string, unknown> | null>
50
+ save?: (
51
+ integrationId: string,
52
+ credentials: Record<string, unknown>,
53
+ scope: { organizationId: string; tenantId: string; userId?: string | null },
54
+ ) => Promise<void>
55
+ }
56
+
57
+ export default async function handle(
58
+ job: QueuedJob<ChannelImportHistoryJobPayload>,
59
+ ctx: HandlerContext,
60
+ ): Promise<void> {
61
+ const payload = job.payload
62
+ const { progressJobId, channelId, sinceDays, contactEmails, maxMessages, scope } = payload
63
+
64
+ const progressService = ctx.resolve<ProgressService>('progressService')
65
+ const progressContext: ProgressServiceContext = {
66
+ tenantId: scope.tenantId,
67
+ organizationId: scope.organizationId,
68
+ }
69
+
70
+ const em = (ctx.resolve('em') as EntityManager).fork()
71
+ const adapterRegistry = ctx.resolve<ChannelAdapterRegistry>('channelAdapterRegistry')
72
+
73
+ try {
74
+ await progressService.startJob(progressJobId, progressContext)
75
+
76
+ const channel = await findOneWithDecryption(
77
+ em,
78
+ CommunicationChannel,
79
+ {
80
+ id: channelId,
81
+ tenantId: scope.tenantId,
82
+ organizationId: scope.organizationId,
83
+ deletedAt: null,
84
+ },
85
+ undefined,
86
+ scope,
87
+ )
88
+ if (!channel) {
89
+ await progressService.failJob(
90
+ progressJobId,
91
+ { errorMessage: 'Channel not found' },
92
+ progressContext,
93
+ )
94
+ return
95
+ }
96
+ if (!channel.isActive || channel.status !== 'connected') {
97
+ await progressService.failJob(
98
+ progressJobId,
99
+ { errorMessage: `Channel is not connected (status=${channel.status})` },
100
+ progressContext,
101
+ )
102
+ return
103
+ }
104
+
105
+ const adapter = adapterRegistry?.get(channel.providerKey)
106
+ if (!adapter || typeof adapter.importHistory !== 'function') {
107
+ await progressService.failJob(
108
+ progressJobId,
109
+ {
110
+ errorMessage: `Provider "${channel.providerKey}" does not support history import`,
111
+ },
112
+ progressContext,
113
+ )
114
+ return
115
+ }
116
+
117
+ // Resolve + refresh credentials. Same flow as poll-channel.
118
+ let credentialsService: CredentialsServiceLike | null = null
119
+ try {
120
+ credentialsService = ctx.resolve<CredentialsServiceLike>('integrationCredentialsService')
121
+ } catch {
122
+ credentialsService = null
123
+ }
124
+ const credentialsScope = {
125
+ tenantId: scope.tenantId,
126
+ organizationId: scope.organizationId,
127
+ userId: channel.userId ?? null,
128
+ }
129
+ let credentials: Record<string, unknown> = {}
130
+ if (channel.credentialsRef && credentialsService) {
131
+ try {
132
+ credentials =
133
+ (await credentialsService.resolve(`channel_${channel.providerKey}`, credentialsScope)) ?? {}
134
+ } catch {
135
+ credentials = {}
136
+ }
137
+ }
138
+ const refreshed = await refreshCredentialsIfNeeded(
139
+ { adapter, channelId: channel.id, credentials, scope: credentialsScope },
140
+ { credentialsService },
141
+ )
142
+ credentials = refreshed.credentials
143
+
144
+ const commandBus = ctx.resolve<CommandBus>('commandBus')
145
+ const containerProxy = { resolve: ctx.resolve.bind(ctx) }
146
+ const commandCtx = {
147
+ container: containerProxy as never,
148
+ auth: null,
149
+ organizationScope: null,
150
+ selectedOrganizationId: scope.organizationId,
151
+ organizationIds: [scope.organizationId],
152
+ }
153
+
154
+ let cursor: string | undefined
155
+ let processedCount = 0
156
+ let totalCount = maxMessages
157
+ let firstPage = true
158
+
159
+ // Bounded loop guards against a misbehaving adapter (infinite hasMore).
160
+ // 100 pages * HARD_CAP (default 200) = 20k messages — far above the 5k cap
161
+ // enforced by the schema, so this only trips on adapter bugs.
162
+ const MAX_PAGES = 100
163
+ for (let pageIndex = 0; pageIndex < MAX_PAGES; pageIndex += 1) {
164
+ if (await progressService.isCancellationRequested(progressJobId, scope.tenantId)) {
165
+ await progressService.markCancelled(progressJobId, progressContext)
166
+ return
167
+ }
168
+
169
+ const page = await adapter.importHistory!({
170
+ credentials,
171
+ scope: { tenantId: scope.tenantId, organizationId: scope.organizationId },
172
+ sinceDays,
173
+ contactEmails,
174
+ maxMessages,
175
+ cursor,
176
+ })
177
+
178
+ if (firstPage && typeof page.totalCandidates === 'number') {
179
+ totalCount = Math.min(maxMessages, page.totalCandidates)
180
+ await progressService.updateProgress(
181
+ progressJobId,
182
+ { totalCount, processedCount: 0 },
183
+ progressContext,
184
+ )
185
+ firstPage = false
186
+ }
187
+
188
+ for (const message of page.messages) {
189
+ if (processedCount >= maxMessages) break
190
+ try {
191
+ const input: IngestInboundMessageInput = {
192
+ channelId: channel.id,
193
+ providerKey: channel.providerKey,
194
+ channelType: channel.channelType,
195
+ scope: { tenantId: scope.tenantId, organizationId: scope.organizationId },
196
+ message,
197
+ }
198
+ await commandBus.execute(COMMUNICATION_CHANNELS_INGEST_INBOUND_COMMAND_ID, {
199
+ input,
200
+ ctx: commandCtx as never,
201
+ })
202
+ } catch (err) {
203
+ // Import-history is best-effort per-message. Same classification
204
+ // logic as poll-channel: a transient failure aborts the whole job
205
+ // (the operator can retry), permanent failures are logged and
206
+ // skipped (the bad message would loop forever otherwise).
207
+ const classification = classifyOutboundError(err)
208
+ if (classification.transient) {
209
+ throw err
210
+ }
211
+ console.warn(
212
+ `[communication_channels:channel-import-history] permanent ingest failure on channel ${channel.id}; skipping message ${message.externalMessageId}. Reason: ${classification.message}`,
213
+ )
214
+ }
215
+ processedCount += 1
216
+ }
217
+
218
+ await progressService.updateProgress(
219
+ progressJobId,
220
+ { processedCount, totalCount },
221
+ progressContext,
222
+ )
223
+
224
+ if (processedCount >= maxMessages) break
225
+ if (!page.hasMore || !page.nextCursor) break
226
+ cursor = page.nextCursor
227
+ }
228
+
229
+ await progressService.completeJob(
230
+ progressJobId,
231
+ { resultSummary: { importedCount: processedCount, channelId } },
232
+ progressContext,
233
+ )
234
+ } catch (err) {
235
+ const message = err instanceof Error ? err.message : 'Import history job failed'
236
+ const stack = err instanceof Error ? err.stack : undefined
237
+ try {
238
+ await progressService.failJob(
239
+ progressJobId,
240
+ { errorMessage: message.slice(0, 2000), errorStack: stack?.slice(0, 10000) },
241
+ progressContext,
242
+ )
243
+ } catch (failErr) {
244
+ console.error(
245
+ `[communication_channels:channel-import-history] failed to mark progress job ${progressJobId} as failed: ${
246
+ failErr instanceof Error ? failErr.message : String(failErr)
247
+ }`,
248
+ )
249
+ }
250
+ throw err
251
+ }
252
+ }
@@ -0,0 +1,223 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql'
2
+ import type { JobContext, QueuedJob, WorkerMeta } from '@open-mercato/queue'
3
+ import type { CommandBus } from '@open-mercato/shared/lib/commands'
4
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
5
+ import { CommunicationChannel } from '../data/entities'
6
+ import {
7
+ COMMUNICATION_CHANNELS_INGEST_INBOUND_COMMAND_ID,
8
+ type IngestInboundMessageInput,
9
+ } from '../commands/ingest-inbound-message'
10
+ import { COMMUNICATION_CHANNELS_QUEUES } from '../lib/queue'
11
+ import { preservePushState } from '../lib/push-state'
12
+ import { refreshCredentialsIfNeeded } from '../lib/credential-refresh'
13
+ import { classifyOutboundError } from '../lib/error-classification'
14
+ import { writeIngestDeadLetter } from '../lib/dead-letter'
15
+ import type { ChannelAdapterRegistry } from '../lib/registry'
16
+
17
+ /**
18
+ * Spec C § Phase C2 — Gmail Pub/Sub push delivery worker.
19
+ *
20
+ * One job per verified webhook notification. Calls
21
+ * `adapter.applyPushNotification(...)` to walk `users.history.list` from the
22
+ * channel's stored `historyId`, then dispatches each message through
23
+ * `ingest-inbound-message` (same path as the polling worker).
24
+ *
25
+ * Concurrency: bounded by the queue's default (10). Per-channel ordering is
26
+ * NOT guaranteed across notifications — but the ingest command is idempotent
27
+ * on `(channel_id, external_message_id)`, and Gmail's history cursor advances
28
+ * monotonically per channel, so out-of-order replay is safe.
29
+ */
30
+ export type GmailHistorySyncJobPayload = {
31
+ channelId: string
32
+ scope: { tenantId: string; organizationId: string | null }
33
+ notification: { emailAddress: string; historyId: string }
34
+ /** Self-re-enqueue drain counter (bounds the multi-page drain loop). */
35
+ drainPage?: number
36
+ }
37
+
38
+ /** Hard cap on self-re-enqueue drain pages — guards against an adapter that
39
+ * returns `hasMore` with a non-advancing cursor (a tight, unbounded loop). */
40
+ const MAX_DRAIN_PAGES = 100
41
+
42
+ export const metadata: WorkerMeta = {
43
+ queue: COMMUNICATION_CHANNELS_QUEUES.gmailHistorySync,
44
+ id: 'communication_channels:gmail-history-sync',
45
+ concurrency: 5,
46
+ }
47
+
48
+ type HandlerContext = JobContext & {
49
+ resolve: <T = unknown>(name: string) => T
50
+ }
51
+
52
+ type CredentialsServiceLike = {
53
+ resolve: (
54
+ integrationId: string,
55
+ scope: { organizationId: string; tenantId: string; userId?: string | null },
56
+ ) => Promise<Record<string, unknown> | null>
57
+ save?: (
58
+ integrationId: string,
59
+ credentials: Record<string, unknown>,
60
+ scope: { organizationId: string; tenantId: string; userId?: string | null },
61
+ ) => Promise<void>
62
+ }
63
+
64
+ export default async function handle(
65
+ job: QueuedJob<GmailHistorySyncJobPayload>,
66
+ ctx: HandlerContext,
67
+ ): Promise<void> {
68
+ const { channelId, scope } = job.payload
69
+ const em = (ctx.resolve('em') as EntityManager).fork()
70
+ const adapterRegistry = ctx.resolve<ChannelAdapterRegistry>('channelAdapterRegistry')
71
+
72
+ const channel = await findOneWithDecryption(
73
+ em,
74
+ CommunicationChannel,
75
+ {
76
+ id: channelId,
77
+ tenantId: scope.tenantId,
78
+ organizationId: scope.organizationId ?? null,
79
+ deletedAt: null,
80
+ },
81
+ undefined,
82
+ scope,
83
+ )
84
+ if (!channel) {
85
+ console.warn(`[gmail-history-sync] channel ${channelId} not found (skip)`)
86
+ return
87
+ }
88
+ if (!channel.isActive || channel.status !== 'connected') return
89
+
90
+ const adapter = adapterRegistry?.get(channel.providerKey)
91
+ if (!adapter || typeof adapter.applyPushNotification !== 'function') {
92
+ console.warn(
93
+ `[gmail-history-sync] adapter for '${channel.providerKey}' does not support applyPushNotification`,
94
+ )
95
+ return
96
+ }
97
+
98
+ let credentialsService: CredentialsServiceLike | null = null
99
+ try {
100
+ credentialsService = ctx.resolve<CredentialsServiceLike>('integrationCredentialsService')
101
+ } catch {
102
+ credentialsService = null
103
+ }
104
+ const credentialsScope = {
105
+ tenantId: scope.tenantId,
106
+ organizationId: scope.organizationId ?? scope.tenantId,
107
+ userId: channel.userId ?? null,
108
+ }
109
+ let credentials: Record<string, unknown> = {}
110
+ if (channel.credentialsRef && credentialsService) {
111
+ try {
112
+ credentials =
113
+ (await credentialsService.resolve(`channel_${channel.providerKey}`, credentialsScope)) ?? {}
114
+ } catch {
115
+ credentials = {}
116
+ }
117
+ }
118
+ const refreshed = await refreshCredentialsIfNeeded(
119
+ { adapter, channelId: channel.id, credentials, scope: credentialsScope },
120
+ { credentialsService },
121
+ )
122
+ credentials = refreshed.credentials
123
+
124
+ let page
125
+ try {
126
+ page = await adapter.applyPushNotification({
127
+ credentials,
128
+ scope: { tenantId: scope.tenantId, organizationId: scope.organizationId ?? scope.tenantId },
129
+ channelState: (channel.channelState as Record<string, unknown> | null) ?? {},
130
+ notification: job.payload.notification as unknown as Record<string, unknown>,
131
+ })
132
+ } catch (err) {
133
+ const classification = classifyOutboundError(err)
134
+ if (classification.transient) throw err
135
+ console.warn(
136
+ `[gmail-history-sync] permanent failure applying push for channel ${channel.id}: ${classification.message}`,
137
+ )
138
+ return
139
+ }
140
+
141
+ const messages = Array.isArray(page?.messages) ? page.messages : []
142
+ if (messages.length > 0) {
143
+ const commandBus = ctx.resolve<CommandBus>('commandBus')
144
+ const containerProxy = { resolve: ctx.resolve.bind(ctx) }
145
+ const commandCtx = {
146
+ container: containerProxy as never,
147
+ auth: null,
148
+ organizationScope: null,
149
+ selectedOrganizationId: scope.organizationId ?? null,
150
+ organizationIds: scope.organizationId ? [scope.organizationId] : null,
151
+ }
152
+ for (const message of messages) {
153
+ try {
154
+ const input: IngestInboundMessageInput = {
155
+ channelId: channel.id,
156
+ providerKey: channel.providerKey,
157
+ channelType: channel.channelType,
158
+ scope: { tenantId: scope.tenantId, organizationId: scope.organizationId ?? null },
159
+ message,
160
+ }
161
+ await commandBus.execute(COMMUNICATION_CHANNELS_INGEST_INBOUND_COMMAND_ID, {
162
+ input,
163
+ ctx: commandCtx as never,
164
+ })
165
+ } catch (err) {
166
+ const classification = classifyOutboundError(err)
167
+ if (classification.transient) {
168
+ // Per-message commit semantics from Spec B: re-throw so queue
169
+ // retry re-runs from a safe point.
170
+ throw err
171
+ }
172
+ console.warn(
173
+ `[gmail-history-sync] permanent ingest failure for channel ${channel.id}: ${classification.message}`,
174
+ )
175
+ await writeIngestDeadLetter({ em, scope, channel, message, err, errorMessage: classification.message })
176
+ }
177
+ }
178
+ }
179
+
180
+ // Persist the adapter's advanced cursor (encoded in nextCursor) back to
181
+ // channel.channelState. fetchHistory's nextCursor is a base64-JSON blob
182
+ // mirroring the new GmailChannelState — same as the polling worker writes.
183
+ if (page?.nextCursor) {
184
+ try {
185
+ const decoded = JSON.parse(Buffer.from(page.nextCursor, 'base64').toString('utf-8')) as Record<string, unknown>
186
+ // Carry the push keys forward via preservePushState rather than spreading
187
+ // the decoded cursor wholesale: a blind spread would retain a stale
188
+ // `pendingHistoryPageToken` after a completed drain and mis-route the next
189
+ // push notification.
190
+ channel.channelState = preservePushState(channel.channelState, decoded)
191
+ channel.lastPolledAt = new Date()
192
+ await em.flush()
193
+ } catch (err) {
194
+ console.warn(
195
+ `[gmail-history-sync] failed to persist next cursor for channel ${channel.id}: ${
196
+ err instanceof Error ? err.message : String(err)
197
+ }`,
198
+ )
199
+ }
200
+ }
201
+
202
+ if (page?.hasMore && page?.nextCursor) {
203
+ // Re-enqueue self so the drain continues. The Pub/Sub notification fired
204
+ // once, but `history.list` may need multiple pages on a busy mailbox.
205
+ // Bound the drain and add a small delay so an adapter that returns
206
+ // `hasMore: true` with a non-advancing cursor cannot spin a tight,
207
+ // unthrottled re-enqueue loop against the provider/queue.
208
+ const drainPage = job.payload.drainPage ?? 0
209
+ if (drainPage < MAX_DRAIN_PAGES) {
210
+ const queue = (await import('../lib/queue')).getCommunicationChannelsQueue(
211
+ COMMUNICATION_CHANNELS_QUEUES.gmailHistorySync,
212
+ )
213
+ await queue.enqueue(
214
+ { ...job.payload, drainPage: drainPage + 1 } as unknown as Record<string, unknown>,
215
+ { delayMs: 250 },
216
+ )
217
+ } else {
218
+ console.warn(
219
+ `[gmail-history-sync] drain page cap (${MAX_DRAIN_PAGES}) reached for channel ${channelId}; stopping re-enqueue`,
220
+ )
221
+ }
222
+ }
223
+ }
@@ -0,0 +1,141 @@
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 } from '../lib/queue'
6
+ import { pushRenew } from '../commands/push-renew'
7
+ import { emitCommunicationChannelsEvent } from '../events'
8
+
9
+ /**
10
+ * Spec C § Phase C4 — Daily cron that re-issues `gmail.users.watch` for
11
+ * channels whose `watchExpirationMs` is within the renewal lead window.
12
+ *
13
+ * Gmail watch expires after ~7 days. We renew with a lead time of
14
+ * `OM_PUSH_RENEWAL_GMAIL_LEAD_HOURS` (default 24h) so a missed cron tick
15
+ * doesn't drop us into the polling fallback.
16
+ *
17
+ * The actual renewal is delegated to `pushRenew` (which in turn calls
18
+ * `pushRegister`) — Gmail's `users.watch` is idempotent, so re-calling it
19
+ * just returns a fresh `historyId` + `expiration` and persists them.
20
+ *
21
+ * Scoped to `job.payload.scope` (the cron is registered per-org in
22
+ * `setup.ts`); each per-org cron tick only walks channels for its own
23
+ * `(tenantId, organizationId)`. A missing scope means the legacy global
24
+ * sweep variant — supported for backwards compatibility, but new
25
+ * registrations always carry a scope.
26
+ */
27
+ export type GmailRenewWatchPayload = {
28
+ scope?: { tenantId: string; organizationId: string | null }
29
+ }
30
+
31
+ export const metadata: WorkerMeta = {
32
+ queue: COMMUNICATION_CHANNELS_QUEUES.gmailRenewWatch,
33
+ id: 'communication_channels:gmail-renew-watch',
34
+ concurrency: 1,
35
+ }
36
+
37
+ type HandlerContext = JobContext & { resolve: <T = unknown>(name: string) => T }
38
+
39
+ const LEAD_HOURS = Math.max(
40
+ 1,
41
+ Number.parseInt(process.env.OM_PUSH_RENEWAL_GMAIL_LEAD_HOURS ?? '24', 10) || 24,
42
+ )
43
+
44
+ export default async function handle(
45
+ job: QueuedJob<GmailRenewWatchPayload>,
46
+ ctx: HandlerContext,
47
+ ): Promise<void> {
48
+ const em = (ctx.resolve('em') as EntityManager).fork()
49
+ const cutoffMs = Date.now() + LEAD_HOURS * 60 * 60 * 1000
50
+ const scope = job?.payload?.scope ?? null
51
+
52
+ // Per-org cron: filter by the scope embedded in the payload. The unscoped
53
+ // fallback is preserved so a deploy that hand-enqueues a renewal sweep
54
+ // (e.g. an operator-triggered backfill) still works.
55
+ const where: Record<string, unknown> = {
56
+ providerKey: 'gmail',
57
+ isActive: true,
58
+ deletedAt: null,
59
+ }
60
+ if (scope?.tenantId) where.tenantId = scope.tenantId
61
+ if (scope?.organizationId) where.organizationId = scope.organizationId
62
+ const channels = await findWithDecryption(
63
+ em,
64
+ CommunicationChannel,
65
+ where,
66
+ undefined,
67
+ scope ? { tenantId: scope.tenantId, organizationId: scope.organizationId ?? null } : undefined,
68
+ )
69
+
70
+ const containerProxy = (ctx as unknown as { container?: unknown }).container
71
+ let renewed = 0
72
+ let failed = 0
73
+ for (const channel of channels) {
74
+ const state =
75
+ (channel.channelState as { watchExpirationMs?: number; pushStatus?: string } | null) ?? null
76
+ if (!state || state.pushStatus !== 'active') continue
77
+ if (typeof state.watchExpirationMs !== 'number') continue
78
+ if (state.watchExpirationMs > cutoffMs) continue
79
+
80
+ const organizationId = channel.organizationId
81
+ if (!organizationId) {
82
+ console.warn(
83
+ `[gmail-renew-watch] skipping channel ${channel.id} — no organizationId on row`,
84
+ )
85
+ continue
86
+ }
87
+
88
+ try {
89
+ const container = resolveContainer(containerProxy, ctx)
90
+ const result = await pushRenew({
91
+ container,
92
+ scope: {
93
+ tenantId: channel.tenantId,
94
+ organizationId,
95
+ userId: channel.userId ?? null,
96
+ },
97
+ input: { channelId: channel.id },
98
+ })
99
+ if (result.pushStatus === 'active') {
100
+ renewed += 1
101
+ await emitCommunicationChannelsEvent(
102
+ 'communication_channels.push.renewed',
103
+ {
104
+ channelId: channel.id,
105
+ providerKey: channel.providerKey,
106
+ tenantId: channel.tenantId,
107
+ organizationId,
108
+ },
109
+ { persistent: false },
110
+ )
111
+ } else {
112
+ failed += 1
113
+ }
114
+ } catch (err) {
115
+ failed += 1
116
+ console.warn(
117
+ `[gmail-renew-watch] failed to renew channel ${channel.id}: ${
118
+ err instanceof Error ? err.message : String(err)
119
+ }`,
120
+ )
121
+ }
122
+ }
123
+ if (renewed > 0 || failed > 0) {
124
+ console.info(`[gmail-renew-watch] renewed=${renewed} failed=${failed}`)
125
+ }
126
+ }
127
+
128
+ function resolveContainer(
129
+ containerProxy: unknown,
130
+ ctx: { resolve: <T = unknown>(name: string) => T },
131
+ ): import('awilix').AwilixContainer {
132
+ // The queue runtime exposes container access via `ctx.container` in
133
+ // dedicated worker harnesses, but the bare-worker contract only provides
134
+ // `ctx.resolve(name)`. Both shapes appear in production (test harness,
135
+ // AUTO_SPAWN_WORKERS) so we accept either.
136
+ if (containerProxy && typeof containerProxy === 'object' && 'resolve' in containerProxy) {
137
+ return containerProxy as import('awilix').AwilixContainer
138
+ }
139
+ // Synthesize a minimal container shape from `resolve`.
140
+ return { resolve: ctx.resolve.bind(ctx) } as unknown as import('awilix').AwilixContainer
141
+ }