@open-mercato/core 0.7.0 → 0.7.1-develop.7102.1.b41f7e3e51

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 (847) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/helpers/integration/api.js +13 -1
  3. package/dist/helpers/integration/api.js.map +2 -2
  4. package/dist/helpers/integration/communicationChannelsFixtures.js +19 -0
  5. package/dist/helpers/integration/communicationChannelsFixtures.js.map +2 -2
  6. package/dist/modules/api_keys/api/keys/route.js +1 -1
  7. package/dist/modules/api_keys/api/keys/route.js.map +2 -2
  8. package/dist/modules/api_keys/backend/api-keys/page.js +3 -1
  9. package/dist/modules/api_keys/backend/api-keys/page.js.map +2 -2
  10. package/dist/modules/api_keys/di.js +9 -0
  11. package/dist/modules/api_keys/di.js.map +7 -0
  12. package/dist/modules/api_keys/services/principalService.js +31 -0
  13. package/dist/modules/api_keys/services/principalService.js.map +7 -0
  14. package/dist/modules/attachments/api/route.js +2 -2
  15. package/dist/modules/attachments/api/route.js.map +2 -2
  16. package/dist/modules/attachments/components/AttachmentLibrary.js +2 -0
  17. package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
  18. package/dist/modules/attachments/di.js +11 -1
  19. package/dist/modules/attachments/di.js.map +2 -2
  20. package/dist/modules/attachments/index.js.map +1 -1
  21. package/dist/modules/attachments/lib/attachment-service.js +313 -0
  22. package/dist/modules/attachments/lib/attachment-service.js.map +7 -0
  23. package/dist/modules/attachments/lib/scoped-upload-service.js +15 -2
  24. package/dist/modules/attachments/lib/scoped-upload-service.js.map +2 -2
  25. package/dist/modules/attachments/lib/upload-limits.js +6 -2
  26. package/dist/modules/attachments/lib/upload-limits.js.map +2 -2
  27. package/dist/modules/audit_logs/backend/audit-logs/page.js +6 -0
  28. package/dist/modules/audit_logs/backend/audit-logs/page.js.map +2 -2
  29. package/dist/modules/audit_logs/lib/display-helpers.js +19 -11
  30. package/dist/modules/audit_logs/lib/display-helpers.js.map +2 -2
  31. package/dist/modules/audit_logs/services/actionLogService.js +57 -12
  32. package/dist/modules/audit_logs/services/actionLogService.js.map +3 -3
  33. package/dist/modules/auth/acl.js +30 -5
  34. package/dist/modules/auth/acl.js.map +2 -2
  35. package/dist/modules/auth/api/feature-check.js +8 -2
  36. package/dist/modules/auth/api/feature-check.js.map +2 -2
  37. package/dist/modules/auth/api/login.js +4 -1
  38. package/dist/modules/auth/api/login.js.map +2 -2
  39. package/dist/modules/auth/api/reset/validate.js +55 -0
  40. package/dist/modules/auth/api/reset/validate.js.map +7 -0
  41. package/dist/modules/auth/api/roles/acl/route.js +28 -33
  42. package/dist/modules/auth/api/roles/acl/route.js.map +2 -2
  43. package/dist/modules/auth/api/session/refresh.js +25 -1
  44. package/dist/modules/auth/api/session/refresh.js.map +2 -2
  45. package/dist/modules/auth/api/users/acl/route.js +96 -51
  46. package/dist/modules/auth/api/users/acl/route.js.map +2 -2
  47. package/dist/modules/auth/api/users/route.js +13 -5
  48. package/dist/modules/auth/api/users/route.js.map +2 -2
  49. package/dist/modules/auth/backend/roles/[id]/edit/page.meta.js +5 -1
  50. package/dist/modules/auth/backend/roles/[id]/edit/page.meta.js.map +2 -2
  51. package/dist/modules/auth/backend/roles/page.js +3 -1
  52. package/dist/modules/auth/backend/roles/page.js.map +2 -2
  53. package/dist/modules/auth/backend/users/[id]/edit/page.meta.js +5 -1
  54. package/dist/modules/auth/backend/users/[id]/edit/page.meta.js.map +2 -2
  55. package/dist/modules/auth/backend/users/page.js +2 -1
  56. package/dist/modules/auth/backend/users/page.js.map +2 -2
  57. package/dist/modules/auth/commands/acl.js +254 -0
  58. package/dist/modules/auth/commands/acl.js.map +7 -0
  59. package/dist/modules/auth/components/AclEditor.js +39 -30
  60. package/dist/modules/auth/components/AclEditor.js.map +2 -2
  61. package/dist/modules/auth/data/entities.js.map +2 -2
  62. package/dist/modules/auth/data/validators.js +5 -1
  63. package/dist/modules/auth/data/validators.js.map +2 -2
  64. package/dist/modules/auth/di.js +35 -9
  65. package/dist/modules/auth/di.js.map +2 -2
  66. package/dist/modules/auth/frontend/reset/[token]/page.js +46 -0
  67. package/dist/modules/auth/frontend/reset/[token]/page.js.map +2 -2
  68. package/dist/modules/auth/lib/grantChecks.js.map +2 -2
  69. package/dist/modules/auth/lib/userIdFilter.js +16 -0
  70. package/dist/modules/auth/lib/userIdFilter.js.map +7 -0
  71. package/dist/modules/auth/services/authService.js +16 -2
  72. package/dist/modules/auth/services/authService.js.map +2 -2
  73. package/dist/modules/auth/services/principalService.js +200 -0
  74. package/dist/modules/auth/services/principalService.js.map +7 -0
  75. package/dist/modules/auth/services/rbacService.js +67 -24
  76. package/dist/modules/auth/services/rbacService.js.map +3 -3
  77. package/dist/modules/auth/services/roleOrganizationScope.js +41 -0
  78. package/dist/modules/auth/services/roleOrganizationScope.js.map +7 -0
  79. package/dist/modules/business_rules/backend/logs/page.js +3 -1
  80. package/dist/modules/business_rules/backend/logs/page.js.map +2 -2
  81. package/dist/modules/business_rules/backend/rules/page.js +3 -1
  82. package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
  83. package/dist/modules/business_rules/backend/sets/page.js +3 -1
  84. package/dist/modules/business_rules/backend/sets/page.js.map +2 -2
  85. package/dist/modules/business_rules/lib/expression-evaluator.js +9 -9
  86. package/dist/modules/business_rules/lib/expression-evaluator.js.map +2 -2
  87. package/dist/modules/business_rules/lib/rule-evaluator.js +10 -9
  88. package/dist/modules/business_rules/lib/rule-evaluator.js.map +2 -2
  89. package/dist/modules/catalog/ai-agents.js +1 -1
  90. package/dist/modules/catalog/ai-agents.js.map +1 -1
  91. package/dist/modules/catalog/ai-tools/merchandising-pack.js +2 -2
  92. package/dist/modules/catalog/ai-tools/merchandising-pack.js.map +2 -2
  93. package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js +5 -2
  94. package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js.map +2 -2
  95. package/dist/modules/catalog/components/categories/CategoriesDataTable.js +2 -0
  96. package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +2 -2
  97. package/dist/modules/catalog/components/products/ProductsDataTable.js +7 -1
  98. package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
  99. package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js +2 -0
  100. package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js.map +2 -2
  101. package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js +9 -1
  102. package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js.map +2 -2
  103. package/dist/modules/communication_channels/api/post/test-seed/route.js +95 -10
  104. package/dist/modules/communication_channels/api/post/test-seed/route.js.map +2 -2
  105. package/dist/modules/communication_channels/backend/communication_channels/channels/page.js +3 -0
  106. package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +2 -2
  107. package/dist/modules/communication_channels/commands/ingest-inbound-message.js +14 -1
  108. package/dist/modules/communication_channels/commands/ingest-inbound-message.js.map +2 -2
  109. package/dist/modules/communication_channels/di.js +7 -1
  110. package/dist/modules/communication_channels/di.js.map +2 -2
  111. package/dist/modules/communication_channels/lib/email-capabilities.js +4 -1
  112. package/dist/modules/communication_channels/lib/email-capabilities.js.map +2 -2
  113. package/dist/modules/communication_channels/lib/outbound-recipient.js +33 -0
  114. package/dist/modules/communication_channels/lib/outbound-recipient.js.map +7 -0
  115. package/dist/modules/communication_channels/lib/resolve-channel-type.js +51 -0
  116. package/dist/modules/communication_channels/lib/resolve-channel-type.js.map +7 -0
  117. package/dist/modules/communication_channels/lib/test-seed.js +41 -2
  118. package/dist/modules/communication_channels/lib/test-seed.js.map +2 -2
  119. package/dist/modules/configs/lib/system-status.js +9 -0
  120. package/dist/modules/configs/lib/system-status.js.map +2 -2
  121. package/dist/modules/currencies/backend/currencies/page.js +3 -1
  122. package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
  123. package/dist/modules/currencies/backend/exchange-rates/page.js +3 -1
  124. package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
  125. package/dist/modules/currencies/services/rateFetchingService.js +6 -1
  126. package/dist/modules/currencies/services/rateFetchingService.js.map +2 -2
  127. package/dist/modules/customer_accounts/api/admin/domain-mappings.js +9 -0
  128. package/dist/modules/customer_accounts/api/admin/domain-mappings.js.map +2 -2
  129. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js +3 -1
  130. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +2 -2
  131. package/dist/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.js +3 -1
  132. package/dist/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.js.map +2 -2
  133. package/dist/modules/customer_accounts/services/domainMappingService.js +10 -0
  134. package/dist/modules/customer_accounts/services/domainMappingService.js.map +2 -2
  135. package/dist/modules/customers/ai-tools/companies-pack.js +4 -1
  136. package/dist/modules/customers/ai-tools/companies-pack.js.map +2 -2
  137. package/dist/modules/customers/ai-tools/deal-analyzer-pack.js +13 -2
  138. package/dist/modules/customers/ai-tools/deal-analyzer-pack.js.map +2 -2
  139. package/dist/modules/customers/ai-tools/deals-pack.js +52 -6
  140. package/dist/modules/customers/ai-tools/deals-pack.js.map +2 -2
  141. package/dist/modules/customers/ai-tools/people-pack.js +4 -1
  142. package/dist/modules/customers/ai-tools/people-pack.js.map +2 -2
  143. package/dist/modules/customers/api/activities/route.js +48 -6
  144. package/dist/modules/customers/api/activities/route.js.map +2 -2
  145. package/dist/modules/customers/api/companies/[id]/people/route.js +20 -64
  146. package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
  147. package/dist/modules/customers/api/companies/[id]/route.js +17 -94
  148. package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
  149. package/dist/modules/customers/api/dashboard/widgets/customer-todos/route.js +8 -5
  150. package/dist/modules/customers/api/dashboard/widgets/customer-todos/route.js.map +2 -2
  151. package/dist/modules/customers/api/deals/[id]/companies/route.js +1 -1
  152. package/dist/modules/customers/api/deals/[id]/companies/route.js.map +2 -2
  153. package/dist/modules/customers/api/deals/[id]/people/route.js +1 -1
  154. package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
  155. package/dist/modules/customers/api/deals/[id]/route.js +8 -25
  156. package/dist/modules/customers/api/deals/[id]/route.js.map +2 -2
  157. package/dist/modules/customers/api/deals/[id]/stats/route.js +3 -22
  158. package/dist/modules/customers/api/deals/[id]/stats/route.js.map +2 -2
  159. package/dist/modules/customers/api/deals/aggregate/route.js +11 -4
  160. package/dist/modules/customers/api/deals/aggregate/route.js.map +2 -2
  161. package/dist/modules/customers/api/deals/map/route.js +2 -1
  162. package/dist/modules/customers/api/deals/map/route.js.map +2 -2
  163. package/dist/modules/customers/api/deals/route.js +38 -1
  164. package/dist/modules/customers/api/deals/route.js.map +2 -2
  165. package/dist/modules/customers/api/entity-roles-factory.js +4 -4
  166. package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
  167. package/dist/modules/customers/api/interactions/route.js +3 -1
  168. package/dist/modules/customers/api/interactions/route.js.map +2 -2
  169. package/dist/modules/customers/api/interactions/tasks/route.js +27 -4
  170. package/dist/modules/customers/api/interactions/tasks/route.js.map +2 -2
  171. package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +4 -3
  172. package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +2 -2
  173. package/dist/modules/customers/api/people/[id]/companies/context.js +1 -1
  174. package/dist/modules/customers/api/people/[id]/companies/context.js.map +2 -2
  175. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +1 -1
  176. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
  177. package/dist/modules/customers/api/people/[id]/route.js +11 -10
  178. package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
  179. package/dist/modules/customers/api/people/check-phone/route.js +51 -9
  180. package/dist/modules/customers/api/people/check-phone/route.js.map +2 -2
  181. package/dist/modules/customers/api/todos/route.js +30 -5
  182. package/dist/modules/customers/api/todos/route.js.map +2 -2
  183. package/dist/modules/customers/backend/customers/companies/page.js +4 -2
  184. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  185. package/dist/modules/customers/backend/customers/deals/[id]/page.js +6 -1
  186. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  187. package/dist/modules/customers/backend/customers/deals/page.js +5 -2
  188. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  189. package/dist/modules/customers/backend/customers/deals/pipeline/components/Lane.js +4 -13
  190. package/dist/modules/customers/backend/customers/deals/pipeline/components/Lane.js.map +2 -2
  191. package/dist/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.js +85 -14
  192. package/dist/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.js.map +2 -2
  193. package/dist/modules/customers/backend/customers/deals/pipeline/components/toneClasses.js +18 -0
  194. package/dist/modules/customers/backend/customers/deals/pipeline/components/toneClasses.js.map +7 -0
  195. package/dist/modules/customers/backend/customers/people/page.js +4 -2
  196. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  197. package/dist/modules/customers/backend/hooks/useEmailDuplicateCheck.js +9 -3
  198. package/dist/modules/customers/backend/hooks/useEmailDuplicateCheck.js.map +2 -2
  199. package/dist/modules/customers/commands/deals.js +21 -34
  200. package/dist/modules/customers/commands/deals.js.map +2 -2
  201. package/dist/modules/customers/commands/interactions.js.map +1 -1
  202. package/dist/modules/customers/commands/personCompanyLinks.js +131 -2
  203. package/dist/modules/customers/commands/personCompanyLinks.js.map +2 -2
  204. package/dist/modules/customers/components/CustomerTodosTable.js +1 -0
  205. package/dist/modules/customers/components/CustomerTodosTable.js.map +2 -2
  206. package/dist/modules/customers/components/DealsKpiStrip.js +7 -6
  207. package/dist/modules/customers/components/DealsKpiStrip.js.map +2 -2
  208. package/dist/modules/customers/components/calendar/AgendaList.js +2 -2
  209. package/dist/modules/customers/components/calendar/AgendaList.js.map +2 -2
  210. package/dist/modules/customers/components/calendar/CalendarEventEditor.js +1 -1
  211. package/dist/modules/customers/components/calendar/CalendarEventEditor.js.map +2 -2
  212. package/dist/modules/customers/components/calendar/CalendarScreen.js +1 -1
  213. package/dist/modules/customers/components/calendar/CalendarScreen.js.map +2 -2
  214. package/dist/modules/customers/components/calendar/CalendarSettingsModal.js +1 -1
  215. package/dist/modules/customers/components/calendar/CalendarSettingsModal.js.map +2 -2
  216. package/dist/modules/customers/components/calendar/EventBlock.js +1 -1
  217. package/dist/modules/customers/components/calendar/EventBlock.js.map +2 -2
  218. package/dist/modules/customers/components/calendar/EventPeekPopover.js +3 -2
  219. package/dist/modules/customers/components/calendar/EventPeekPopover.js.map +2 -2
  220. package/dist/modules/customers/components/calendar/editor/PeopleField.js +40 -5
  221. package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
  222. package/dist/modules/customers/components/calendar/editor/hooks.js +2 -1
  223. package/dist/modules/customers/components/calendar/editor/hooks.js.map +2 -2
  224. package/dist/modules/customers/components/calendar/types.js +1 -1
  225. package/dist/modules/customers/components/calendar/types.js.map +2 -2
  226. package/dist/modules/customers/components/detail/CompanyKpiBar.js +5 -4
  227. package/dist/modules/customers/components/detail/CompanyKpiBar.js.map +2 -2
  228. package/dist/modules/customers/components/detail/CompanyPeopleSection.js +3 -1
  229. package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
  230. package/dist/modules/customers/components/detail/DealForm.js +19 -7
  231. package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
  232. package/dist/modules/customers/components/detail/DealsSection.js +6 -5
  233. package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
  234. package/dist/modules/customers/components/detail/EntityTagsDialog.js +35 -11
  235. package/dist/modules/customers/components/detail/EntityTagsDialog.js.map +2 -2
  236. package/dist/modules/customers/components/detail/PersonCompaniesSection.js +3 -1
  237. package/dist/modules/customers/components/detail/PersonCompaniesSection.js.map +2 -2
  238. package/dist/modules/customers/components/detail/hooks/usePersonTasks.js +19 -13
  239. package/dist/modules/customers/components/detail/hooks/usePersonTasks.js.map +2 -2
  240. package/dist/modules/customers/components/detail/schedule/ParticipantsField.js +4 -4
  241. package/dist/modules/customers/components/detail/schedule/ParticipantsField.js.map +2 -2
  242. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js +3 -3
  243. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
  244. package/dist/modules/customers/components/detail/utils.js +5 -5
  245. package/dist/modules/customers/components/detail/utils.js.map +2 -2
  246. package/dist/modules/customers/data/entities.js.map +1 -1
  247. package/dist/modules/customers/data/validators.js +32 -3
  248. package/dist/modules/customers/data/validators.js.map +2 -2
  249. package/dist/modules/customers/events.js +5 -0
  250. package/dist/modules/customers/events.js.map +2 -2
  251. package/dist/modules/customers/lib/calendar/conflicts.js +8 -2
  252. package/dist/modules/customers/lib/calendar/conflicts.js.map +2 -2
  253. package/dist/modules/customers/lib/calendar/editorPayload.js +3 -2
  254. package/dist/modules/customers/lib/calendar/editorPayload.js.map +2 -2
  255. package/dist/modules/customers/lib/calendar/mapItem.js +8 -3
  256. package/dist/modules/customers/lib/calendar/mapItem.js.map +2 -2
  257. package/dist/modules/customers/lib/calendar/participantIdentity.js +9 -0
  258. package/dist/modules/customers/lib/calendar/participantIdentity.js.map +7 -0
  259. package/dist/modules/customers/lib/closureStage.js +45 -0
  260. package/dist/modules/customers/lib/closureStage.js.map +7 -0
  261. package/dist/modules/customers/lib/dealStatus.js +30 -0
  262. package/dist/modules/customers/lib/dealStatus.js.map +2 -2
  263. package/dist/modules/customers/lib/detailReadAccess.js +12 -0
  264. package/dist/modules/customers/lib/detailReadAccess.js.map +7 -0
  265. package/dist/modules/customers/lib/findPeopleByAddresses.js +1 -0
  266. package/dist/modules/customers/lib/findPeopleByAddresses.js.map +2 -2
  267. package/dist/modules/customers/lib/personCompanies.js +100 -2
  268. package/dist/modules/customers/lib/personCompanies.js.map +2 -2
  269. package/dist/modules/customers/lib/todoCompatibility.js +12 -0
  270. package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
  271. package/dist/modules/dashboards/lib/widgetScope.js +6 -1
  272. package/dist/modules/dashboards/lib/widgetScope.js.map +2 -2
  273. package/dist/modules/data_sync/backend/data-sync/page.js +3 -1
  274. package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
  275. package/dist/modules/data_sync/lib/abandoned-run.js +19 -0
  276. package/dist/modules/data_sync/lib/abandoned-run.js.map +7 -0
  277. package/dist/modules/data_sync/lib/sync-engine.js +72 -6
  278. package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
  279. package/dist/modules/data_sync/workers/sync-export.js +5 -1
  280. package/dist/modules/data_sync/workers/sync-export.js.map +2 -2
  281. package/dist/modules/data_sync/workers/sync-import.js +5 -1
  282. package/dist/modules/data_sync/workers/sync-import.js.map +2 -2
  283. package/dist/modules/devices/acl.js +9 -1
  284. package/dist/modules/devices/acl.js.map +2 -2
  285. package/dist/modules/devices/api/admin/devices/[id]/route.js +5 -32
  286. package/dist/modules/devices/api/admin/devices/[id]/route.js.map +2 -2
  287. package/dist/modules/devices/api/admin/devices/route.js +14 -1
  288. package/dist/modules/devices/api/admin/devices/route.js.map +2 -2
  289. package/dist/modules/devices/api/deviceList.js +99 -12
  290. package/dist/modules/devices/api/deviceList.js.map +2 -2
  291. package/dist/modules/devices/api/deviceSerialization.js +46 -0
  292. package/dist/modules/devices/api/deviceSerialization.js.map +7 -0
  293. package/dist/modules/devices/api/openapi.js +3 -1
  294. package/dist/modules/devices/api/openapi.js.map +2 -2
  295. package/dist/modules/devices/api/route.js +9 -1
  296. package/dist/modules/devices/api/route.js.map +2 -2
  297. package/dist/modules/devices/backend/devices/[id]/page.js +10 -26
  298. package/dist/modules/devices/backend/devices/[id]/page.js.map +2 -2
  299. package/dist/modules/devices/backend/devices/create/page.js +17 -1
  300. package/dist/modules/devices/backend/devices/create/page.js.map +2 -2
  301. package/dist/modules/devices/backend/devices/page.js +28 -34
  302. package/dist/modules/devices/backend/devices/page.js.map +2 -2
  303. package/dist/modules/devices/backend/devices/useDeviceUserLabels.js +34 -0
  304. package/dist/modules/devices/backend/devices/useDeviceUserLabels.js.map +7 -0
  305. package/dist/modules/devices/backend/devices/userOptions.js +52 -0
  306. package/dist/modules/devices/backend/devices/userOptions.js.map +7 -0
  307. package/dist/modules/devices/setup.js +6 -2
  308. package/dist/modules/devices/setup.js.map +2 -2
  309. package/dist/modules/directory/backend/directory/organizations/page.js +2 -1
  310. package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
  311. package/dist/modules/directory/backend/directory/tenants/page.js +2 -1
  312. package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
  313. package/dist/modules/directory/di.js +9 -0
  314. package/dist/modules/directory/di.js.map +2 -2
  315. package/dist/modules/directory/services/organizationHierarchyService.js +29 -0
  316. package/dist/modules/directory/services/organizationHierarchyService.js.map +7 -0
  317. package/dist/modules/directory/services/organizationScopeService.js +56 -0
  318. package/dist/modules/directory/services/organizationScopeService.js.map +7 -0
  319. package/dist/modules/directory/utils/organizationScope.js.map +2 -2
  320. package/dist/modules/entities/api/records.js +4 -2
  321. package/dist/modules/entities/api/records.js.map +2 -2
  322. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +3 -1
  323. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  324. package/dist/modules/eudr/api/plots/route.js +5 -1
  325. package/dist/modules/eudr/api/plots/route.js.map +2 -2
  326. package/dist/modules/eudr/backend/eudr/evidence-submissions/page.js +3 -0
  327. package/dist/modules/eudr/backend/eudr/evidence-submissions/page.js.map +2 -2
  328. package/dist/modules/eudr/backend/eudr/plots/page.js +3 -0
  329. package/dist/modules/eudr/backend/eudr/plots/page.js.map +2 -2
  330. package/dist/modules/eudr/backend/eudr/product-mappings/page.js +3 -0
  331. package/dist/modules/eudr/backend/eudr/product-mappings/page.js.map +2 -2
  332. package/dist/modules/eudr/backend/eudr/risk-assessments/page.js +3 -0
  333. package/dist/modules/eudr/backend/eudr/risk-assessments/page.js.map +2 -2
  334. package/dist/modules/eudr/backend/eudr/statements/page.js +3 -0
  335. package/dist/modules/eudr/backend/eudr/statements/page.js.map +2 -2
  336. package/dist/modules/eudr/commands/statements.js +0 -3
  337. package/dist/modules/eudr/commands/statements.js.map +2 -2
  338. package/dist/modules/eudr/data/validators.js +4 -1
  339. package/dist/modules/eudr/data/validators.js.map +2 -2
  340. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js +1 -0
  341. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
  342. package/dist/modules/feature_toggles/components/OverridesTable.js +1 -0
  343. package/dist/modules/feature_toggles/components/OverridesTable.js.map +2 -2
  344. package/dist/modules/feature_toggles/defaults.json +0 -8
  345. package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js +3 -0
  346. package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js.map +2 -2
  347. package/dist/modules/inbox_ops/backend/inbox-ops/page.js +3 -0
  348. package/dist/modules/inbox_ops/backend/inbox-ops/page.js.map +2 -2
  349. package/dist/modules/inbox_ops/lib/htmlToPlainText.js +1 -12
  350. package/dist/modules/inbox_ops/lib/htmlToPlainText.js.map +2 -2
  351. package/dist/modules/integrations/api/[id]/credentials/route.js +6 -1
  352. package/dist/modules/integrations/api/[id]/credentials/route.js.map +2 -2
  353. package/dist/modules/integrations/backend/integrations/[id]/page.js +38 -29
  354. package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
  355. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +36 -9
  356. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +2 -2
  357. package/dist/modules/integrations/backend/integrations/credential-secret-fields.js +55 -0
  358. package/dist/modules/integrations/backend/integrations/credential-secret-fields.js.map +7 -0
  359. package/dist/modules/integrations/data/validators.js +11 -3
  360. package/dist/modules/integrations/data/validators.js.map +2 -2
  361. package/dist/modules/integrations/lib/credentials-masking.js +6 -2
  362. package/dist/modules/integrations/lib/credentials-masking.js.map +2 -2
  363. package/dist/modules/messages/api/[id]/route.js +29 -1
  364. package/dist/modules/messages/api/[id]/route.js.map +2 -2
  365. package/dist/modules/messages/api/openapi.js +17 -2
  366. package/dist/modules/messages/api/openapi.js.map +2 -2
  367. package/dist/modules/messages/api/route.js +17 -2
  368. package/dist/modules/messages/api/route.js.map +2 -2
  369. package/dist/modules/messages/components/MessagesInboxPageClient.js +7 -14
  370. package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
  371. package/dist/modules/messages/components/inboxFilters.js +15 -0
  372. package/dist/modules/messages/components/inboxFilters.js.map +2 -2
  373. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.js +11 -1
  374. package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.js.map +2 -2
  375. package/dist/modules/messages/components/message-detail/panels/MessageHeader.js +3 -1
  376. package/dist/modules/messages/components/message-detail/panels/MessageHeader.js.map +2 -2
  377. package/dist/modules/messages/components/message-detail/panels/thread-panel.js +2 -1
  378. package/dist/modules/messages/components/message-detail/panels/thread-panel.js.map +2 -2
  379. package/dist/modules/messages/components/messageListLabels.js +14 -2
  380. package/dist/modules/messages/components/messageListLabels.js.map +2 -2
  381. package/dist/modules/messages/data/validators.js +18 -4
  382. package/dist/modules/messages/data/validators.js.map +2 -2
  383. package/dist/modules/messages/lib/channel-sender-identity.js +26 -0
  384. package/dist/modules/messages/lib/channel-sender-identity.js.map +7 -0
  385. package/dist/modules/messages/lib/composeSourceChannelType.js +41 -0
  386. package/dist/modules/messages/lib/composeSourceChannelType.js.map +7 -0
  387. package/dist/modules/payment_gateways/api/webhook/[provider]/route.js +4 -6
  388. package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +2 -2
  389. package/dist/modules/payment_gateways/backend/payment-gateways/page.js +3 -1
  390. package/dist/modules/payment_gateways/backend/payment-gateways/page.js.map +2 -2
  391. package/dist/modules/payment_gateways/lib/webhook-processor.js +7 -0
  392. package/dist/modules/payment_gateways/lib/webhook-processor.js.map +2 -2
  393. package/dist/modules/planner/backend/planner/availability-rulesets/page.js +3 -0
  394. package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
  395. package/dist/modules/push_notifications/backend/push_notifications/page.js +3 -1
  396. package/dist/modules/push_notifications/backend/push_notifications/page.js.map +2 -2
  397. package/dist/modules/push_notifications/backend/push_notifications/send/page.js +1 -1
  398. package/dist/modules/push_notifications/backend/push_notifications/send/page.js.map +2 -2
  399. package/dist/modules/query_index/lib/engine.js +150 -51
  400. package/dist/modules/query_index/lib/engine.js.map +2 -2
  401. package/dist/modules/query_index/lib/search-entity-policy.js +14 -0
  402. package/dist/modules/query_index/lib/search-entity-policy.js.map +7 -0
  403. package/dist/modules/query_index/lib/search-tokens.js +79 -3
  404. package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
  405. package/dist/modules/resources/backend/resources/resource-types/page.js +3 -1
  406. package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
  407. package/dist/modules/resources/backend/resources/resources/page.js +3 -1
  408. package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
  409. package/dist/modules/sales/backend/sales/channels/offers/page.js +3 -0
  410. package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
  411. package/dist/modules/sales/backend/sales/channels/page.js +3 -0
  412. package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
  413. package/dist/modules/sales/backend/sales/documents/[id]/page.js +10 -8
  414. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  415. package/dist/modules/sales/commands/documents.js +79 -1
  416. package/dist/modules/sales/commands/documents.js.map +2 -2
  417. package/dist/modules/sales/components/PriceWithCurrency.js +7 -5
  418. package/dist/modules/sales/components/PriceWithCurrency.js.map +2 -2
  419. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +3 -0
  420. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
  421. package/dist/modules/sales/components/documents/AdjustmentsSection.js +8 -6
  422. package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
  423. package/dist/modules/sales/components/documents/ItemsSection.js +51 -22
  424. package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
  425. package/dist/modules/sales/components/documents/LineItemDialog.js +83 -18
  426. package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
  427. package/dist/modules/sales/components/documents/PaymentsSection.js +23 -9
  428. package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
  429. package/dist/modules/sales/components/documents/ReturnsSection.js +8 -6
  430. package/dist/modules/sales/components/documents/ReturnsSection.js.map +2 -2
  431. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +68 -14
  432. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  433. package/dist/modules/sales/components/documents/SalesOrderDraftLines.js +6 -5
  434. package/dist/modules/sales/components/documents/SalesOrderDraftLines.js.map +2 -2
  435. package/dist/modules/sales/components/documents/ShipmentDialog.js +5 -4
  436. package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
  437. package/dist/modules/sales/components/documents/ShipmentsSection.js +8 -6
  438. package/dist/modules/sales/components/documents/ShipmentsSection.js.map +2 -2
  439. package/dist/modules/sales/components/documents/lineItemShipmentLock.js +54 -0
  440. package/dist/modules/sales/components/documents/lineItemShipmentLock.js.map +7 -0
  441. package/dist/modules/sales/components/documents/lineItemUtils.js +2 -2
  442. package/dist/modules/sales/components/documents/lineItemUtils.js.map +2 -2
  443. package/dist/modules/sales/data/entities.js +8 -2
  444. package/dist/modules/sales/data/entities.js.map +2 -2
  445. package/dist/modules/sales/extension-points.js +1 -0
  446. package/dist/modules/sales/extension-points.js.map +2 -2
  447. package/dist/modules/sales/lib/salesChannelsToggleSeed.js +33 -0
  448. package/dist/modules/sales/lib/salesChannelsToggleSeed.js.map +7 -0
  449. package/dist/modules/sales/migrations/Migration20260821120000_sales_notes_addresses_context_idx.js +32 -0
  450. package/dist/modules/sales/migrations/Migration20260821120000_sales_notes_addresses_context_idx.js.map +7 -0
  451. package/dist/modules/sales/setup.js +2 -0
  452. package/dist/modules/sales/setup.js.map +2 -2
  453. package/dist/modules/sales/widgets/injection/payment-gateway-status-column/widget.js +10 -2
  454. package/dist/modules/sales/widgets/injection/payment-gateway-status-column/widget.js.map +2 -2
  455. package/dist/modules/sales/widgets/injection-table.js +4 -7
  456. package/dist/modules/sales/widgets/injection-table.js.map +2 -2
  457. package/dist/modules/staff/backend/staff/leave-requests/page.js +3 -0
  458. package/dist/modules/staff/backend/staff/leave-requests/page.js.map +2 -2
  459. package/dist/modules/staff/backend/staff/my-leave-requests/page.js +3 -0
  460. package/dist/modules/staff/backend/staff/my-leave-requests/page.js.map +2 -2
  461. package/dist/modules/staff/backend/staff/team-members/page.js +3 -0
  462. package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
  463. package/dist/modules/staff/backend/staff/team-roles/page.js +3 -0
  464. package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
  465. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +3 -0
  466. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  467. package/dist/modules/staff/backend/staff/teams/page.js +3 -0
  468. package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
  469. package/dist/modules/staff/backend/staff/timesheets/page.js +29 -18
  470. package/dist/modules/staff/backend/staff/timesheets/page.js.map +2 -2
  471. package/dist/modules/staff/backend/staff/timesheets/projects/page.js +3 -0
  472. package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +2 -2
  473. package/dist/modules/sync_excel/lib/adapters/customers.js +1 -0
  474. package/dist/modules/sync_excel/lib/adapters/customers.js.map +2 -2
  475. package/dist/modules/warranty_claims/api/assignees/route.js +3 -3
  476. package/dist/modules/warranty_claims/api/assignees/route.js.map +2 -2
  477. package/dist/modules/warranty_claims/api/portal/attachments/route.js +2 -2
  478. package/dist/modules/warranty_claims/api/portal/attachments/route.js.map +2 -2
  479. package/dist/modules/warranty_claims/api/portal/claims/route.js +12 -9
  480. package/dist/modules/warranty_claims/api/portal/claims/route.js.map +2 -2
  481. package/dist/modules/warranty_claims/backend/page.js +3 -0
  482. package/dist/modules/warranty_claims/backend/page.js.map +2 -2
  483. package/dist/modules/warranty_claims/backend/warranty_claims/registrations/page.js +3 -0
  484. package/dist/modules/warranty_claims/backend/warranty_claims/registrations/page.js.map +2 -2
  485. package/dist/modules/warranty_claims/backend/warranty_claims/troubleshooting-guides/page.js +3 -0
  486. package/dist/modules/warranty_claims/backend/warranty_claims/troubleshooting-guides/page.js.map +2 -2
  487. package/dist/modules/warranty_claims/backend/warranty_claims/vendor-policies/page.js +3 -0
  488. package/dist/modules/warranty_claims/backend/warranty_claims/vendor-policies/page.js.map +2 -2
  489. package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.js +3 -0
  490. package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.js.map +2 -2
  491. package/dist/modules/wms/api/warehouseSearch.js +21 -0
  492. package/dist/modules/wms/api/warehouseSearch.js.map +7 -0
  493. package/dist/modules/wms/api/warehouses/route.js +13 -9
  494. package/dist/modules/wms/api/warehouses/route.js.map +2 -2
  495. package/dist/modules/wms/api/zones/route.js +15 -2
  496. package/dist/modules/wms/api/zones/route.js.map +2 -2
  497. package/dist/modules/wms/commands/configuration.js +121 -9
  498. package/dist/modules/wms/commands/configuration.js.map +2 -2
  499. package/dist/modules/wms/commands/inventory-actions.js +3 -0
  500. package/dist/modules/wms/commands/inventory-actions.js.map +2 -2
  501. package/dist/modules/wms/commands/shared.js +9 -1
  502. package/dist/modules/wms/commands/shared.js.map +2 -2
  503. package/dist/modules/wms/components/backend/WarehouseEditDialog.js +158 -0
  504. package/dist/modules/wms/components/backend/WarehouseEditDialog.js.map +7 -0
  505. package/dist/modules/wms/components/backend/WmsConfigurationPage.js +76 -111
  506. package/dist/modules/wms/components/backend/WmsConfigurationPage.js.map +3 -3
  507. package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js +1 -0
  508. package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js.map +2 -2
  509. package/dist/modules/wms/components/backend/WmsLotsListPage.js +1 -0
  510. package/dist/modules/wms/components/backend/WmsLotsListPage.js.map +2 -2
  511. package/dist/modules/wms/components/backend/inventoryMutationLoaders.js +27 -20
  512. package/dist/modules/wms/components/backend/inventoryMutationLoaders.js.map +2 -2
  513. package/dist/modules/wms/components/backend/warehouseFormOptions.js +66 -0
  514. package/dist/modules/wms/components/backend/warehouseFormOptions.js.map +7 -0
  515. package/dist/modules/workflows/backend/definitions/page.js +3 -1
  516. package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
  517. package/dist/modules/workflows/backend/events/page.js +3 -1
  518. package/dist/modules/workflows/backend/events/page.js.map +2 -2
  519. package/dist/modules/workflows/backend/instances/page.js +3 -1
  520. package/dist/modules/workflows/backend/instances/page.js.map +2 -2
  521. package/dist/modules/workflows/backend/tasks/page.js +3 -1
  522. package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
  523. package/dist/modules/workflows/lib/activity-executor.js +4 -0
  524. package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
  525. package/package.json +9 -10
  526. package/src/helpers/integration/api.ts +33 -1
  527. package/src/helpers/integration/communicationChannelsFixtures.ts +69 -1
  528. package/src/modules/api_keys/api/keys/route.ts +3 -1
  529. package/src/modules/api_keys/backend/api-keys/page.tsx +4 -1
  530. package/src/modules/api_keys/di.ts +7 -0
  531. package/src/modules/api_keys/services/principalService.ts +33 -0
  532. package/src/modules/attachments/AGENTS.md +15 -0
  533. package/src/modules/attachments/api/route.ts +2 -2
  534. package/src/modules/attachments/components/AttachmentLibrary.tsx +3 -0
  535. package/src/modules/attachments/di.ts +16 -0
  536. package/src/modules/attachments/i18n/de.json +5 -0
  537. package/src/modules/attachments/i18n/en.json +5 -0
  538. package/src/modules/attachments/i18n/es.json +5 -0
  539. package/src/modules/attachments/i18n/ko.json +5 -0
  540. package/src/modules/attachments/i18n/pl.json +5 -0
  541. package/src/modules/attachments/index.ts +10 -0
  542. package/src/modules/attachments/lib/attachment-service.ts +468 -0
  543. package/src/modules/attachments/lib/scoped-upload-service.ts +46 -0
  544. package/src/modules/attachments/lib/upload-limits.ts +6 -2
  545. package/src/modules/audit_logs/backend/audit-logs/page.tsx +8 -0
  546. package/src/modules/audit_logs/lib/display-helpers.tsx +42 -32
  547. package/src/modules/audit_logs/services/actionLogService.ts +74 -11
  548. package/src/modules/auth/acl.ts +30 -6
  549. package/src/modules/auth/api/feature-check.ts +10 -3
  550. package/src/modules/auth/api/login.ts +9 -1
  551. package/src/modules/auth/api/reset/validate.ts +57 -0
  552. package/src/modules/auth/api/roles/acl/route.ts +42 -39
  553. package/src/modules/auth/api/session/refresh.ts +32 -1
  554. package/src/modules/auth/api/users/acl/route.ts +177 -53
  555. package/src/modules/auth/api/users/route.ts +18 -5
  556. package/src/modules/auth/backend/roles/[id]/edit/page.meta.ts +5 -2
  557. package/src/modules/auth/backend/roles/page.tsx +4 -2
  558. package/src/modules/auth/backend/users/[id]/edit/page.meta.ts +5 -2
  559. package/src/modules/auth/backend/users/page.tsx +3 -2
  560. package/src/modules/auth/commands/acl.ts +495 -0
  561. package/src/modules/auth/components/AclEditor.tsx +63 -33
  562. package/src/modules/auth/data/entities.ts +4 -1
  563. package/src/modules/auth/data/validators.ts +5 -0
  564. package/src/modules/auth/di.ts +46 -16
  565. package/src/modules/auth/frontend/reset/[token]/page.tsx +74 -0
  566. package/src/modules/auth/i18n/de.json +389 -0
  567. package/src/modules/auth/i18n/en.json +389 -0
  568. package/src/modules/auth/i18n/es.json +389 -0
  569. package/src/modules/auth/i18n/ko.json +389 -0
  570. package/src/modules/auth/i18n/pl.json +391 -2
  571. package/src/modules/auth/lib/grantChecks.ts +6 -0
  572. package/src/modules/auth/lib/userIdFilter.ts +31 -0
  573. package/src/modules/auth/services/authService.ts +18 -2
  574. package/src/modules/auth/services/principalService.ts +266 -0
  575. package/src/modules/auth/services/rbacService.ts +110 -24
  576. package/src/modules/auth/services/roleOrganizationScope.ts +70 -0
  577. package/src/modules/business_rules/backend/logs/page.tsx +4 -1
  578. package/src/modules/business_rules/backend/rules/page.tsx +4 -1
  579. package/src/modules/business_rules/backend/sets/page.tsx +4 -1
  580. package/src/modules/business_rules/lib/expression-evaluator.ts +9 -9
  581. package/src/modules/business_rules/lib/rule-evaluator.ts +10 -9
  582. package/src/modules/catalog/ai-agents.ts +1 -1
  583. package/src/modules/catalog/ai-tools/merchandising-pack.ts +2 -2
  584. package/src/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.tsx +12 -5
  585. package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +3 -0
  586. package/src/modules/catalog/components/products/ProductsDataTable.tsx +7 -0
  587. package/src/modules/catalog/i18n/de.json +1 -0
  588. package/src/modules/catalog/i18n/en.json +1 -0
  589. package/src/modules/catalog/i18n/es.json +1 -0
  590. package/src/modules/catalog/i18n/ko.json +1 -0
  591. package/src/modules/catalog/i18n/pl.json +1 -0
  592. package/src/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.tsx +4 -0
  593. package/src/modules/communication_channels/api/post/channels/[id]/test-send/route.ts +17 -1
  594. package/src/modules/communication_channels/api/post/test-seed/route.ts +155 -16
  595. package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +4 -0
  596. package/src/modules/communication_channels/commands/ingest-inbound-message.ts +38 -1
  597. package/src/modules/communication_channels/di.ts +7 -0
  598. package/src/modules/communication_channels/lib/adapter.ts +14 -0
  599. package/src/modules/communication_channels/lib/email-capabilities.ts +4 -0
  600. package/src/modules/communication_channels/lib/outbound-recipient.ts +71 -0
  601. package/src/modules/communication_channels/lib/resolve-channel-type.ts +99 -0
  602. package/src/modules/communication_channels/lib/test-seed.ts +64 -4
  603. package/src/modules/configs/i18n/de.json +2 -0
  604. package/src/modules/configs/i18n/en.json +2 -0
  605. package/src/modules/configs/i18n/es.json +2 -0
  606. package/src/modules/configs/i18n/ko.json +2 -0
  607. package/src/modules/configs/i18n/pl.json +2 -0
  608. package/src/modules/configs/lib/system-status.ts +9 -0
  609. package/src/modules/currencies/AGENTS.md +3 -0
  610. package/src/modules/currencies/backend/currencies/page.tsx +4 -1
  611. package/src/modules/currencies/backend/exchange-rates/page.tsx +4 -1
  612. package/src/modules/currencies/services/README.md +45 -0
  613. package/src/modules/currencies/services/rateFetchingService.ts +6 -1
  614. package/src/modules/customer_accounts/api/admin/domain-mappings.ts +7 -0
  615. package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +4 -1
  616. package/src/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.tsx +4 -1
  617. package/src/modules/customer_accounts/services/domainMappingService.ts +10 -0
  618. package/src/modules/customers/ai-tools/companies-pack.ts +4 -1
  619. package/src/modules/customers/ai-tools/deal-analyzer-pack.ts +17 -2
  620. package/src/modules/customers/ai-tools/deals-pack.ts +66 -1
  621. package/src/modules/customers/ai-tools/people-pack.ts +4 -1
  622. package/src/modules/customers/api/activities/route.ts +45 -0
  623. package/src/modules/customers/api/companies/[id]/people/route.ts +24 -73
  624. package/src/modules/customers/api/companies/[id]/route.ts +24 -107
  625. package/src/modules/customers/api/dashboard/widgets/customer-todos/route.ts +16 -8
  626. package/src/modules/customers/api/deals/[id]/companies/route.ts +4 -1
  627. package/src/modules/customers/api/deals/[id]/people/route.ts +4 -1
  628. package/src/modules/customers/api/deals/[id]/route.ts +17 -28
  629. package/src/modules/customers/api/deals/[id]/stats/route.ts +10 -25
  630. package/src/modules/customers/api/deals/aggregate/route.ts +14 -4
  631. package/src/modules/customers/api/deals/map/route.ts +1 -0
  632. package/src/modules/customers/api/deals/route.ts +50 -1
  633. package/src/modules/customers/api/entity-roles-factory.ts +8 -4
  634. package/src/modules/customers/api/interactions/route.ts +8 -2
  635. package/src/modules/customers/api/interactions/tasks/route.ts +24 -1
  636. package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +12 -4
  637. package/src/modules/customers/api/people/[id]/companies/context.ts +4 -1
  638. package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +4 -1
  639. package/src/modules/customers/api/people/[id]/route.ts +16 -11
  640. package/src/modules/customers/api/people/check-phone/route.ts +75 -10
  641. package/src/modules/customers/api/todos/route.ts +27 -2
  642. package/src/modules/customers/backend/customers/companies/page.tsx +5 -2
  643. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +7 -2
  644. package/src/modules/customers/backend/customers/deals/page.tsx +8 -2
  645. package/src/modules/customers/backend/customers/deals/pipeline/components/Lane.tsx +5 -14
  646. package/src/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.tsx +109 -27
  647. package/src/modules/customers/backend/customers/deals/pipeline/components/toneClasses.ts +21 -0
  648. package/src/modules/customers/backend/customers/people/page.tsx +5 -2
  649. package/src/modules/customers/backend/hooks/useEmailDuplicateCheck.ts +12 -4
  650. package/src/modules/customers/commands/deals.ts +34 -59
  651. package/src/modules/customers/commands/interactions.ts +1 -1
  652. package/src/modules/customers/commands/personCompanyLinks.ts +224 -8
  653. package/src/modules/customers/components/CustomerTodosTable.tsx +2 -0
  654. package/src/modules/customers/components/DealsKpiStrip.tsx +7 -7
  655. package/src/modules/customers/components/calendar/AgendaList.tsx +3 -3
  656. package/src/modules/customers/components/calendar/CalendarEventEditor.tsx +1 -1
  657. package/src/modules/customers/components/calendar/CalendarScreen.tsx +1 -1
  658. package/src/modules/customers/components/calendar/CalendarSettingsModal.tsx +1 -1
  659. package/src/modules/customers/components/calendar/EventBlock.tsx +2 -2
  660. package/src/modules/customers/components/calendar/EventPeekPopover.tsx +4 -2
  661. package/src/modules/customers/components/calendar/editor/PeopleField.tsx +61 -5
  662. package/src/modules/customers/components/calendar/editor/hooks.ts +7 -1
  663. package/src/modules/customers/components/calendar/types.ts +2 -2
  664. package/src/modules/customers/components/detail/CompanyKpiBar.tsx +5 -4
  665. package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +8 -1
  666. package/src/modules/customers/components/detail/DealForm.tsx +22 -2
  667. package/src/modules/customers/components/detail/DealsSection.tsx +11 -5
  668. package/src/modules/customers/components/detail/EntityTagsDialog.tsx +58 -12
  669. package/src/modules/customers/components/detail/PersonCompaniesSection.tsx +8 -1
  670. package/src/modules/customers/components/detail/hooks/usePersonTasks.ts +23 -14
  671. package/src/modules/customers/components/detail/schedule/ParticipantsField.tsx +5 -5
  672. package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +5 -5
  673. package/src/modules/customers/components/detail/types.ts +1 -1
  674. package/src/modules/customers/components/detail/utils.ts +15 -5
  675. package/src/modules/customers/data/entities.ts +1 -1
  676. package/src/modules/customers/data/validators.ts +50 -9
  677. package/src/modules/customers/events.ts +5 -0
  678. package/src/modules/customers/i18n/de.json +9 -1
  679. package/src/modules/customers/i18n/en.json +9 -1
  680. package/src/modules/customers/i18n/es.json +9 -1
  681. package/src/modules/customers/i18n/ko.json +9 -1
  682. package/src/modules/customers/i18n/pl.json +9 -1
  683. package/src/modules/customers/lib/calendar/conflicts.ts +10 -2
  684. package/src/modules/customers/lib/calendar/editorPayload.ts +4 -3
  685. package/src/modules/customers/lib/calendar/mapItem.ts +8 -3
  686. package/src/modules/customers/lib/calendar/participantIdentity.ts +17 -0
  687. package/src/modules/customers/lib/closureStage.ts +76 -0
  688. package/src/modules/customers/lib/dealStatus.ts +42 -0
  689. package/src/modules/customers/lib/detailReadAccess.ts +35 -0
  690. package/src/modules/customers/lib/findPeopleByAddresses.ts +8 -6
  691. package/src/modules/customers/lib/personCompanies.ts +136 -1
  692. package/src/modules/customers/lib/todoCompatibility.ts +19 -1
  693. package/src/modules/dashboards/i18n/es.json +4 -4
  694. package/src/modules/dashboards/i18n/pl.json +7 -7
  695. package/src/modules/dashboards/lib/widgetScope.ts +6 -0
  696. package/src/modules/data_sync/AGENTS.md +1 -1
  697. package/src/modules/data_sync/backend/data-sync/page.tsx +4 -1
  698. package/src/modules/data_sync/lib/abandoned-run.ts +56 -0
  699. package/src/modules/data_sync/lib/adapter.ts +36 -0
  700. package/src/modules/data_sync/lib/sync-engine.ts +113 -5
  701. package/src/modules/data_sync/workers/sync-export.ts +4 -0
  702. package/src/modules/data_sync/workers/sync-import.ts +4 -0
  703. package/src/modules/devices/AGENTS.md +20 -1
  704. package/src/modules/devices/acl.ts +9 -1
  705. package/src/modules/devices/api/admin/devices/[id]/route.ts +5 -34
  706. package/src/modules/devices/api/admin/devices/route.ts +14 -1
  707. package/src/modules/devices/api/deviceList.ts +135 -11
  708. package/src/modules/devices/api/deviceSerialization.ts +50 -0
  709. package/src/modules/devices/api/openapi.ts +9 -1
  710. package/src/modules/devices/api/route.ts +9 -1
  711. package/src/modules/devices/backend/devices/[id]/page.tsx +18 -32
  712. package/src/modules/devices/backend/devices/create/page.tsx +17 -1
  713. package/src/modules/devices/backend/devices/page.tsx +44 -45
  714. package/src/modules/devices/backend/devices/useDeviceUserLabels.ts +45 -0
  715. package/src/modules/devices/backend/devices/userOptions.ts +99 -0
  716. package/src/modules/devices/i18n/de.json +3 -2
  717. package/src/modules/devices/i18n/en.json +3 -2
  718. package/src/modules/devices/i18n/es.json +3 -2
  719. package/src/modules/devices/i18n/ko.json +3 -2
  720. package/src/modules/devices/i18n/pl.json +3 -2
  721. package/src/modules/devices/setup.ts +6 -2
  722. package/src/modules/directory/backend/directory/organizations/page.tsx +3 -1
  723. package/src/modules/directory/backend/directory/tenants/page.tsx +3 -1
  724. package/src/modules/directory/di.ts +14 -2
  725. package/src/modules/directory/services/organizationHierarchyService.ts +34 -0
  726. package/src/modules/directory/services/organizationScopeService.ts +85 -0
  727. package/src/modules/directory/utils/organizationScope.ts +4 -15
  728. package/src/modules/entities/api/records.ts +2 -0
  729. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +4 -1
  730. package/src/modules/eudr/api/plots/route.ts +4 -0
  731. package/src/modules/eudr/backend/eudr/evidence-submissions/page.tsx +4 -0
  732. package/src/modules/eudr/backend/eudr/plots/page.tsx +4 -0
  733. package/src/modules/eudr/backend/eudr/product-mappings/page.tsx +4 -0
  734. package/src/modules/eudr/backend/eudr/risk-assessments/page.tsx +4 -0
  735. package/src/modules/eudr/backend/eudr/statements/page.tsx +4 -0
  736. package/src/modules/eudr/commands/statements.ts +0 -3
  737. package/src/modules/eudr/data/validators.ts +16 -1
  738. package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +2 -1
  739. package/src/modules/feature_toggles/components/OverridesTable.tsx +2 -1
  740. package/src/modules/feature_toggles/defaults.json +0 -8
  741. package/src/modules/inbox_ops/backend/inbox-ops/log/page.tsx +4 -0
  742. package/src/modules/inbox_ops/backend/inbox-ops/page.tsx +4 -0
  743. package/src/modules/inbox_ops/lib/htmlToPlainText.ts +1 -17
  744. package/src/modules/integrations/api/[id]/credentials/route.ts +6 -4
  745. package/src/modules/integrations/backend/integrations/[id]/page.tsx +61 -31
  746. package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +44 -9
  747. package/src/modules/integrations/backend/integrations/credential-secret-fields.ts +84 -0
  748. package/src/modules/integrations/data/validators.ts +12 -2
  749. package/src/modules/integrations/i18n/de.json +1 -0
  750. package/src/modules/integrations/i18n/en.json +1 -0
  751. package/src/modules/integrations/i18n/es.json +1 -0
  752. package/src/modules/integrations/i18n/ko.json +1 -0
  753. package/src/modules/integrations/i18n/pl.json +1 -0
  754. package/src/modules/integrations/lib/credentials-masking.ts +10 -6
  755. package/src/modules/messages/api/[id]/route.ts +55 -1
  756. package/src/modules/messages/api/openapi.ts +20 -0
  757. package/src/modules/messages/api/route.ts +31 -2
  758. package/src/modules/messages/components/MessagesInboxPageClient.tsx +9 -16
  759. package/src/modules/messages/components/inboxFilters.ts +36 -0
  760. package/src/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.ts +14 -1
  761. package/src/modules/messages/components/message-detail/panels/MessageHeader.tsx +3 -1
  762. package/src/modules/messages/components/message-detail/panels/thread-panel.tsx +2 -1
  763. package/src/modules/messages/components/message-detail/types.ts +3 -0
  764. package/src/modules/messages/components/messageListLabels.ts +52 -3
  765. package/src/modules/messages/data/validators.ts +43 -4
  766. package/src/modules/messages/lib/channel-sender-identity.ts +55 -0
  767. package/src/modules/messages/lib/composeSourceChannelType.ts +95 -0
  768. package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +4 -6
  769. package/src/modules/payment_gateways/backend/payment-gateways/page.tsx +4 -1
  770. package/src/modules/payment_gateways/lib/webhook-processor.ts +11 -0
  771. package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +4 -0
  772. package/src/modules/push_notifications/backend/push_notifications/page.tsx +4 -1
  773. package/src/modules/push_notifications/backend/push_notifications/send/page.tsx +4 -4
  774. package/src/modules/query_index/lib/engine.ts +249 -61
  775. package/src/modules/query_index/lib/search-entity-policy.ts +46 -0
  776. package/src/modules/query_index/lib/search-tokens.ts +135 -3
  777. package/src/modules/resources/backend/resources/resource-types/page.tsx +4 -1
  778. package/src/modules/resources/backend/resources/resources/page.tsx +4 -1
  779. package/src/modules/sales/backend/sales/channels/offers/page.tsx +4 -0
  780. package/src/modules/sales/backend/sales/channels/page.tsx +4 -0
  781. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +14 -8
  782. package/src/modules/sales/commands/documents.ts +93 -0
  783. package/src/modules/sales/components/PriceWithCurrency.tsx +17 -5
  784. package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +4 -0
  785. package/src/modules/sales/components/documents/AdjustmentsSection.tsx +6 -5
  786. package/src/modules/sales/components/documents/ItemsSection.tsx +56 -18
  787. package/src/modules/sales/components/documents/LineItemDialog.tsx +137 -20
  788. package/src/modules/sales/components/documents/PaymentsSection.tsx +20 -9
  789. package/src/modules/sales/components/documents/ReturnsSection.tsx +6 -5
  790. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +106 -14
  791. package/src/modules/sales/components/documents/SalesOrderDraftLines.tsx +6 -5
  792. package/src/modules/sales/components/documents/ShipmentDialog.tsx +5 -4
  793. package/src/modules/sales/components/documents/ShipmentsSection.tsx +6 -5
  794. package/src/modules/sales/components/documents/lineItemShipmentLock.ts +74 -0
  795. package/src/modules/sales/components/documents/lineItemUtils.ts +7 -2
  796. package/src/modules/sales/data/entities.ts +6 -0
  797. package/src/modules/sales/extension-points.ts +1 -0
  798. package/src/modules/sales/i18n/de.json +8 -0
  799. package/src/modules/sales/i18n/en.json +8 -0
  800. package/src/modules/sales/i18n/es.json +8 -0
  801. package/src/modules/sales/i18n/ko.json +8 -0
  802. package/src/modules/sales/i18n/pl.json +8 -0
  803. package/src/modules/sales/lib/salesChannelsToggleSeed.ts +42 -0
  804. package/src/modules/sales/migrations/.snapshot-open-mercato.json +60 -0
  805. package/src/modules/sales/migrations/Migration20260821120000_sales_notes_addresses_context_idx.ts +87 -0
  806. package/src/modules/sales/setup.ts +2 -0
  807. package/src/modules/sales/widgets/injection/payment-gateway-status-column/widget.ts +9 -1
  808. package/src/modules/sales/widgets/injection-table.ts +4 -7
  809. package/src/modules/staff/backend/staff/leave-requests/page.tsx +4 -0
  810. package/src/modules/staff/backend/staff/my-leave-requests/page.tsx +4 -0
  811. package/src/modules/staff/backend/staff/team-members/page.tsx +6 -0
  812. package/src/modules/staff/backend/staff/team-roles/page.tsx +4 -0
  813. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +4 -0
  814. package/src/modules/staff/backend/staff/teams/page.tsx +4 -0
  815. package/src/modules/staff/backend/staff/timesheets/page.tsx +12 -1
  816. package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +4 -0
  817. package/src/modules/sync_excel/lib/adapters/customers.ts +5 -0
  818. package/src/modules/warranty_claims/api/assignees/route.ts +3 -3
  819. package/src/modules/warranty_claims/api/portal/attachments/route.ts +2 -2
  820. package/src/modules/warranty_claims/api/portal/claims/route.ts +12 -9
  821. package/src/modules/warranty_claims/backend/page.tsx +4 -0
  822. package/src/modules/warranty_claims/backend/warranty_claims/registrations/page.tsx +4 -0
  823. package/src/modules/warranty_claims/backend/warranty_claims/troubleshooting-guides/page.tsx +4 -0
  824. package/src/modules/warranty_claims/backend/warranty_claims/vendor-policies/page.tsx +4 -0
  825. package/src/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.tsx +4 -0
  826. package/src/modules/wms/api/warehouseSearch.ts +23 -0
  827. package/src/modules/wms/api/warehouses/route.ts +13 -9
  828. package/src/modules/wms/api/zones/route.ts +16 -0
  829. package/src/modules/wms/commands/configuration.ts +159 -10
  830. package/src/modules/wms/commands/inventory-actions.ts +3 -0
  831. package/src/modules/wms/commands/shared.ts +17 -0
  832. package/src/modules/wms/components/backend/WarehouseEditDialog.tsx +216 -0
  833. package/src/modules/wms/components/backend/WmsConfigurationPage.tsx +95 -141
  834. package/src/modules/wms/components/backend/WmsInventoryConsolePage.tsx +2 -0
  835. package/src/modules/wms/components/backend/WmsLotsListPage.tsx +2 -0
  836. package/src/modules/wms/components/backend/inventoryMutationLoaders.ts +36 -20
  837. package/src/modules/wms/components/backend/warehouseFormOptions.ts +72 -0
  838. package/src/modules/wms/i18n/de.json +2 -0
  839. package/src/modules/wms/i18n/en.json +2 -0
  840. package/src/modules/wms/i18n/es.json +2 -0
  841. package/src/modules/wms/i18n/ko.json +2 -0
  842. package/src/modules/wms/i18n/pl.json +2 -0
  843. package/src/modules/workflows/backend/definitions/page.tsx +4 -1
  844. package/src/modules/workflows/backend/events/page.tsx +4 -1
  845. package/src/modules/workflows/backend/instances/page.tsx +4 -1
  846. package/src/modules/workflows/backend/tasks/page.tsx +4 -1
  847. package/src/modules/workflows/lib/activity-executor.ts +9 -0
@@ -1,7 +1,7 @@
1
1
  import type { ComparisonOperator, LogicalOperator } from '../data/validators'
2
2
  import { testLinearRegex } from '@open-mercato/shared/lib/regex/linear'
3
3
  import { getNestedValue, resolveSpecialValue } from './value-resolver'
4
- import { createLogger } from '@open-mercato/shared/lib/logger'
4
+ import { createLogger, isLevelEnabled } from '@open-mercato/shared/lib/logger'
5
5
 
6
6
  const logger = createLogger('business_rules').child({ component: 'expression-evaluator' })
7
7
 
@@ -86,14 +86,14 @@ function evaluateSimpleCondition(
86
86
 
87
87
  const result = applyOperator(leftValue, condition.operator, rightValue)
88
88
 
89
- logger.debug('Simple condition evaluated', {
90
- field: condition.field,
91
- operator: condition.operator,
92
- expectedValue: rightValue,
93
- actualValue: leftValue,
94
- actualValueType: typeof leftValue,
95
- passed: result,
96
- })
89
+ if (isLevelEnabled('debug')) {
90
+ logger.debug('Simple condition evaluated', {
91
+ field: condition.field,
92
+ operator: condition.operator,
93
+ actualValueType: typeof leftValue,
94
+ passed: result,
95
+ })
96
+ }
97
97
 
98
98
  return result
99
99
  }
@@ -1,6 +1,6 @@
1
1
  import type { BusinessRule } from '../data/entities'
2
2
  import { evaluateExpression, type EvaluationContext, type ConditionExpression } from './expression-evaluator'
3
- import { createLogger } from '@open-mercato/shared/lib/logger'
3
+ import { createLogger, isLevelEnabled } from '@open-mercato/shared/lib/logger'
4
4
 
5
5
  const logger = createLogger('business_rules').child({ component: 'rule-evaluator' })
6
6
 
@@ -105,14 +105,15 @@ export async function evaluateSingleRule(
105
105
  ): Promise<SingleRuleResult> {
106
106
  const startTime = Date.now()
107
107
 
108
- logger.debug('Evaluating rule', {
109
- ruleId: rule.ruleId,
110
- ruleName: rule.ruleName,
111
- ruleType: rule.ruleType,
112
- entityType: rule.entityType,
113
- eventType: rule.eventType,
114
- conditions: rule.conditionExpression,
115
- })
108
+ if (isLevelEnabled('debug')) {
109
+ logger.debug('Evaluating rule', {
110
+ ruleId: rule.ruleId,
111
+ ruleName: rule.ruleName,
112
+ ruleType: rule.ruleType,
113
+ entityType: rule.entityType,
114
+ eventType: rule.eventType,
115
+ })
116
+ }
116
117
 
117
118
  try {
118
119
  // Check if rule is enabled
@@ -407,7 +407,7 @@ const MERCHANDISING_PROMPT_SECTIONS: PromptSection[] = [
407
407
  '- Selection-first: if `pageContext.recordId` contains a non-empty comma-separated UUID list (or `pageContext.extra.selectedCount > 0`), the operator has selected rows in the grid and EXPECTS you to act on those first. Call catalog.list_selected_products with those IDs, present what you found, and THEN ask whether to expand to the full catalog or to a broader search. Do NOT silently fall back to catalog.search_products when a selection is present.',
408
408
  '- "list products" with no selection → call catalog.search_products with NO parameters (returns all active products, paginated; default limit=50, max=100).',
409
409
  '- User mentions a product name → call catalog.search_products with q=that name.',
410
- '- If catalog.search_products returns more rows than the page (i.e. `total` > `limit + offset`), say so and offer to fetch the next page; do NOT raise `limit` above 100.',
410
+ '- If catalog.search_products fills the page (as many items as `limit`), more may follow: say so and offer to fetch the next page; do NOT raise `limit` above 100. When `totalIsCapped` is true, `total` is a floor — phrase it as "at least N", never as an exact count.',
411
411
  'Present results first, then offer refinement options. The user does NOT want to answer questions before seeing data.',
412
412
  ].join('\n'),
413
413
  },
@@ -97,7 +97,7 @@ function resolveSearchService(ctx: CatalogToolContext): SearchServiceLike | null
97
97
  const searchProductsInput = z
98
98
  .object({
99
99
  q: z.string().trim().optional().describe('Optional fulltext query (title / subtitle / sku / handle). Omit or leave empty (or do NOT pass it at all) to list all products. NEVER use "*", "**", or "%" — they are not wildcards and will be discarded.'),
100
- limit: z.number().int().min(1).max(100).optional().describe('Page size. Default 50, hard maximum 100. Results are paginated: when `total` exceeds `limit + offset`, call again with the next `offset` instead of asking for more than 100 rows.'),
100
+ limit: z.number().int().min(1).max(100).optional().describe('Page size. Default 50, hard maximum 100. Results are paginated: keep calling with the next `offset` until a page returns fewer than `limit` items, instead of asking for more than 100 rows. Do not treat `offset >= total` as the end — `total` is a floor when `totalIsCapped` is true.'),
101
101
  offset: z.number().int().min(0).optional().describe('Rows to skip for pagination (default 0). Combine with `limit` to fetch subsequent pages — for example offset=100, limit=100 fetches rows 101..200.'),
102
102
  categoryId: z.string().optional().describe('Restrict to products assigned to this catalog category UUID. Only use a category ID returned by a previous tool call — do NOT guess. Empty string is treated the same as omitting the field.'),
103
103
  priceMin: z.number().optional().describe('Lower-bound (inclusive) on the gross unit price. OMIT this field when you do not want a lower bound — do NOT pass 0 as "no minimum", because 0 is a real bound that combined with priceMax=0 will return only free products.'),
@@ -266,7 +266,7 @@ const searchProductsTool: CatalogAiToolDefinition = {
266
266
  displayName: 'Search products',
267
267
  description:
268
268
  'Hybrid search + filter across tenant products. When `q` is non-empty, routes through the search service (tenant + organization scoped) then hydrates tenant-scoped product summaries; when `q` is empty or omitted, runs the catalog query engine with the supplied filters and returns ALL products. ' +
269
- 'Pagination: response shape is `{ items, total, limit, offset, source }`. Hard cap is 100 rows per call (`limit` max=100, default=50); if `total` exceeds `limit + offset`, call again with the next `offset` rather than asking for >100. ' +
269
+ 'Pagination: response shape is `{ items, total, totalIsCapped, limit, offset, source }`. Hard cap is 100 rows per call (`limit` max=100, default=50); keep calling with the next `offset` until a page returns fewer than `limit` items, rather than asking for >100. When `totalIsCapped` is true, `total` is a floor — render it as "at least N" and never treat `offset >= total` as exhaustion. ' +
270
270
  'Empty / sentinel inputs: pass an empty `q` (or omit it) to list all products. NEVER pass `0` for `priceMin`/`priceMax` to mean "no bound" — `0` is a real numeric bound (priceMin=0 + priceMax=0 returns only free products). To remove a bound, OMIT the field. Empty string `categoryId` and empty `tags` arrays are ignored. ' +
271
271
  '`source` in the output indicates which path executed (`search_service` vs `query_engine`).',
272
272
  inputSchema: searchProductsInput,
@@ -73,6 +73,8 @@ export interface MerchandisingPageContext {
73
73
  extra: {
74
74
  filter: MerchandisingPageContextFilter
75
75
  totalMatching: number
76
+ /** `totalMatching` is a floor, not an exact count (server-capped list count). */
77
+ totalMatchingIsCapped?: boolean
76
78
  selectedCount: number
77
79
  }
78
80
  }
@@ -181,7 +183,7 @@ function useContextItems(pageContext: MerchandisingPageContext): AiChatContextIt
181
183
  const t = useT()
182
184
  return React.useMemo(() => {
183
185
  const items: AiChatContextItem[] = []
184
- const { selectedCount, totalMatching, filter } = pageContext.extra
186
+ const { selectedCount, totalMatching, totalMatchingIsCapped, filter } = pageContext.extra
185
187
  if (selectedCount > 0) {
186
188
  items.push({
187
189
  label: t(
@@ -191,10 +193,15 @@ function useContextItems(pageContext: MerchandisingPageContext): AiChatContextIt
191
193
  })
192
194
  } else if (totalMatching > 0) {
193
195
  items.push({
194
- label: t(
195
- 'catalog.merchandising_assistant.context.matchingProducts',
196
- '{count} products in view',
197
- ).replace('{count}', String(totalMatching)),
196
+ label: totalMatchingIsCapped
197
+ ? t(
198
+ 'catalog.merchandising_assistant.context.matchingProductsCapped',
199
+ '{count}+ products in view',
200
+ ).replace('{count}', String(totalMatching))
201
+ : t(
202
+ 'catalog.merchandising_assistant.context.matchingProducts',
203
+ '{count} products in view',
204
+ ).replace('{count}', String(totalMatching)),
198
205
  })
199
206
  }
200
207
  if (filter.categoryId) {
@@ -39,6 +39,7 @@ type CategoriesResponse = {
39
39
  page: number
40
40
  pageSize: number
41
41
  totalPages: number
42
+ totalIsCapped?: boolean
42
43
  }
43
44
 
44
45
  const PAGE_SIZE = 50
@@ -114,6 +115,7 @@ export default function CategoriesDataTable() {
114
115
  const rows = data?.items ?? []
115
116
  const total = data?.total ?? 0
116
117
  const totalPages = data?.totalPages ?? 0
118
+ const totalIsCapped = data?.totalIsCapped === true
117
119
 
118
120
  const columns = React.useMemo<ColumnDef<CategoryRow>[]>(() => [
119
121
  {
@@ -249,6 +251,7 @@ export default function CategoriesDataTable() {
249
251
  pageSize: PAGE_SIZE,
250
252
  total,
251
253
  totalPages,
254
+ totalIsCapped,
252
255
  onPageChange: setPage,
253
256
  }}
254
257
  isLoading={isLoading}
@@ -86,6 +86,7 @@ type ProductsResponse = {
86
86
  items?: ProductRow[]
87
87
  total?: number
88
88
  totalPages?: number
89
+ totalIsCapped?: boolean
89
90
  }
90
91
 
91
92
  const PAGE_SIZE = 25
@@ -177,6 +178,7 @@ export default function ProductsDataTable({
177
178
  const [page, setPage] = React.useState(1)
178
179
  const [total, setTotal] = React.useState(0)
179
180
  const [totalPages, setTotalPages] = React.useState(1)
181
+ const [totalIsCapped, setTotalIsCapped] = React.useState(false)
180
182
  const [cacheStatus, setCacheStatus] = React.useState<'hit' | 'miss' | null>(null)
181
183
  const [sorting, setSorting] = React.useState<SortingState>([{ id: 'title', desc: false }])
182
184
  const [search, setSearch] = React.useState('')
@@ -598,6 +600,7 @@ export default function ProductsDataTable({
598
600
  setRows(normalized)
599
601
  setTotal(typeof payload.total === 'number' ? payload.total : normalized.length)
600
602
  setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : 1)
603
+ setTotalIsCapped(payload.totalIsCapped === true)
601
604
  } catch (error) {
602
605
  if (!cancelled) {
603
606
  setCacheStatus(null)
@@ -712,12 +715,16 @@ export default function ProductsDataTable({
712
715
  // dependency on the host page.
713
716
  total,
714
717
  totalMatching: total,
718
+ // `total` is a floor when the server capped the count, so the
719
+ // merchandising widget must not present it as an exact match count.
720
+ totalIsCapped,
715
721
  }}
716
722
  pagination={{
717
723
  page,
718
724
  pageSize: PAGE_SIZE,
719
725
  total,
720
726
  totalPages,
727
+ totalIsCapped,
721
728
  onPageChange: setPage,
722
729
  cacheStatus,
723
730
  }}
@@ -90,6 +90,7 @@
90
90
  "catalog.merchandising_assistant.agents.merchandising.label": "Merchandising-Assistent",
91
91
  "catalog.merchandising_assistant.context.filteredByCategory": "Filtered by category",
92
92
  "catalog.merchandising_assistant.context.matchingProducts": "{count} Produkte in der Ansicht",
93
+ "catalog.merchandising_assistant.context.matchingProductsCapped": "{count}+ Produkte in der Ansicht",
93
94
  "catalog.merchandising_assistant.context.selectedProducts": "{count} Produkte ausgewählt",
94
95
  "catalog.merchandising_assistant.context.tags": "{count} tags",
95
96
  "catalog.merchandising_assistant.dock.subtitle": "Catalog",
@@ -90,6 +90,7 @@
90
90
  "catalog.merchandising_assistant.agents.merchandising.label": "Merchandising Assistant",
91
91
  "catalog.merchandising_assistant.context.filteredByCategory": "Filtered by category",
92
92
  "catalog.merchandising_assistant.context.matchingProducts": "{count} products in view",
93
+ "catalog.merchandising_assistant.context.matchingProductsCapped": "{count}+ products in view",
93
94
  "catalog.merchandising_assistant.context.selectedProducts": "{count} products selected",
94
95
  "catalog.merchandising_assistant.context.tags": "{count} tags",
95
96
  "catalog.merchandising_assistant.dock.subtitle": "Catalog",
@@ -90,6 +90,7 @@
90
90
  "catalog.merchandising_assistant.agents.merchandising.label": "Asistente de Merchandising",
91
91
  "catalog.merchandising_assistant.context.filteredByCategory": "Filtered by category",
92
92
  "catalog.merchandising_assistant.context.matchingProducts": "{count} productos en la vista",
93
+ "catalog.merchandising_assistant.context.matchingProductsCapped": "{count}+ productos en la vista",
93
94
  "catalog.merchandising_assistant.context.selectedProducts": "{count} productos seleccionados",
94
95
  "catalog.merchandising_assistant.context.tags": "{count} tags",
95
96
  "catalog.merchandising_assistant.dock.subtitle": "Catalog",
@@ -90,6 +90,7 @@
90
90
  "catalog.merchandising_assistant.agents.merchandising.label": "머천다이징 어시스턴트",
91
91
  "catalog.merchandising_assistant.context.filteredByCategory": "카테고리로 필터링됨",
92
92
  "catalog.merchandising_assistant.context.matchingProducts": "{count} 제품 보기",
93
+ "catalog.merchandising_assistant.context.matchingProductsCapped": "조회 중 {count}+개 제품",
93
94
  "catalog.merchandising_assistant.context.selectedProducts": "{count} 제품 선택됨",
94
95
  "catalog.merchandising_assistant.context.tags": "태그 {count}개",
95
96
  "catalog.merchandising_assistant.dock.subtitle": "카탈로그",
@@ -90,6 +90,7 @@
90
90
  "catalog.merchandising_assistant.agents.merchandising.label": "Asystent merchandisingu",
91
91
  "catalog.merchandising_assistant.context.filteredByCategory": "Filtrowane po kategorii",
92
92
  "catalog.merchandising_assistant.context.matchingProducts": "{count} produktów w widoku",
93
+ "catalog.merchandising_assistant.context.matchingProductsCapped": "{count}+ produktów w widoku",
93
94
  "catalog.merchandising_assistant.context.selectedProducts": "Wybrano {count} produktów",
94
95
  "catalog.merchandising_assistant.context.tags": "Tagi: {count}",
95
96
  "catalog.merchandising_assistant.dock.subtitle": "Katalog",
@@ -31,6 +31,8 @@ interface HostInjectionContext {
31
31
  scopeVersion?: unknown
32
32
  total?: number | string
33
33
  totalMatching?: number | string
34
+ /** `totalMatching` is a floor, not an exact count (server-capped list count). */
35
+ totalIsCapped?: boolean
34
36
  /** Selected row IDs from DataTable (auto-enriched when bulk actions are present). */
35
37
  selectedRowIds?: string[]
36
38
  selectedCount?: number
@@ -83,6 +85,7 @@ export function computeCatalogMerchandisingPageContext(
83
85
  context: HostInjectionContext | undefined,
84
86
  ): MerchandisingPageContext {
85
87
  const totalMatching = readNumber(context?.totalMatching ?? context?.total)
88
+ const totalMatchingIsCapped = context?.totalIsCapped === true
86
89
  const selectedRowIds = Array.isArray(context?.selectedRowIds) ? context.selectedRowIds : []
87
90
  const selectedCount = selectedRowIds.length > 0 ? selectedRowIds.length : readNumber(context?.selectedCount)
88
91
  return {
@@ -93,6 +96,7 @@ export function computeCatalogMerchandisingPageContext(
93
96
  extra: {
94
97
  filter: normalizeFilters(context),
95
98
  totalMatching,
99
+ totalMatchingIsCapped,
96
100
  selectedCount,
97
101
  },
98
102
  }
@@ -13,6 +13,10 @@ import {
13
13
  channelOrgScopeWhere,
14
14
  } from '../../../../../lib/access-control'
15
15
  import { refreshCredentialsIfNeeded } from '../../../../../lib/credential-refresh'
16
+ import {
17
+ MAX_OUTBOUND_RECIPIENT_LENGTH,
18
+ validateOutboundRecipient,
19
+ } from '../../../../../lib/outbound-recipient'
16
20
  import { validateRouteMutationGuard } from '../../../../../lib/route-mutation-guard'
17
21
 
18
22
  type RbacServiceLike = {
@@ -33,8 +37,15 @@ export const metadata = {
33
37
  },
34
38
  }
35
39
 
40
+ // `to` is deliberately NOT typed as an email here. The recipient shape depends
41
+ // on the provider (an email address for Gmail/IMAP, a channel snowflake for
42
+ // Discord), and the adapter is only known once the channel is loaded — so the
43
+ // schema accepts any non-empty single-line string and `validateOutboundRecipient`
44
+ // applies the provider-appropriate rules below, defaulting to email. The CR/LF
45
+ // rejection stays at the schema level so a header-injection attempt is refused
46
+ // before the request reaches the channel lookup or the mutation guard.
36
47
  const bodySchema = z.object({
37
- to: z.string().email(),
48
+ to: z.string().min(1).max(MAX_OUTBOUND_RECIPIENT_LENGTH).regex(/^[^\r\n]*$/),
38
49
  subject: z.string().min(1).max(500).optional(),
39
50
  body: z.string().max(50_000).optional(),
40
51
  })
@@ -170,6 +181,11 @@ export async function POST(req: Request, context: RouteContext): Promise<Respons
170
181
  )
171
182
  }
172
183
 
184
+ const recipientCheck = validateOutboundRecipient(body.to, adapter.capabilities)
185
+ if (!recipientCheck.ok) {
186
+ return NextResponse.json({ error: recipientCheck.error }, { status: 422 })
187
+ }
188
+
173
189
  // Resolve credentials + optionally refresh.
174
190
  let credentials: Record<string, unknown> = {}
175
191
  let credentialsService: CredentialsServiceLike | null = null
@@ -20,10 +20,16 @@ import {
20
20
  } from '../../../commands/connect-credential-channel'
21
21
  import { emitCommunicationChannelsEvent } from '../../../events'
22
22
  import {
23
+ TEST_SEED_CHAT_PROVIDER_KEY,
23
24
  TEST_SEED_PROVIDER_KEY,
24
25
  ensureTestSeedAdapterRegistered,
25
26
  isTestChannelSeedingEnabled,
26
27
  } from '../../../lib/test-seed'
28
+ import {
29
+ COMMUNICATION_CHANNELS_INGEST_INBOUND_COMMAND_ID,
30
+ type IngestInboundMessageInput,
31
+ type IngestInboundMessageResult,
32
+ } from '../../../commands/ingest-inbound-message'
27
33
 
28
34
  /**
29
35
  * TEST-ONLY channel seeding endpoint.
@@ -32,15 +38,22 @@ import {
32
38
  * production default) every request returns 404, so this route is invisible and
33
39
  * inert in production. See `lib/test-seed.ts` for the full rationale.
34
40
  *
35
- * Two actions, both scoped to the caller's tenant/org:
36
- * - `connect-channel`: connect a network-free `__test_seed__` channel owned by
37
- * the caller (delegates to the real connect-credential command so the channel
38
- * persists credentials + lands in `status='connected'`). Enables the outbound
39
- * compose → deliver → `.sent` chain to complete in CI.
41
+ * Three actions, all scoped to the caller's tenant/org:
42
+ * - `connect-channel`: connect a network-free stub channel owned by the caller
43
+ * (delegates to the real connect-credential command so the channel persists
44
+ * credentials + lands in `status='connected'`). Enables the outbound
45
+ * compose → deliver → `.sent` chain to complete in CI. `providerFlavor: 'chat'`
46
+ * connects the non-email stub instead, for tests about sender identity.
47
+ * - `ingest-inbound`: run the REAL `ingest_inbound_message` command over an
48
+ * adapter-normalized chat frame, so the platform compose path — and every
49
+ * validation rule on it — actually executes. Use this for anything that
50
+ * claims inbound works.
40
51
  * - `emit-inbound`: insert an inbound `MessageChannelLink` (+ a `messages.message`
41
52
  * row for threading) and emit `communication_channels.message.received` so the
42
53
  * customers link-channel-message subscriber runs against real Postgres. Enables
43
- * the inbound auto-link tests (TC-CRM-EMAIL-002..005).
54
+ * the inbound auto-link tests (TC-CRM-EMAIL-002..005). NOTE: this action
55
+ * deliberately bypasses `messages.messages.compose` — it can never prove that
56
+ * the hub accepts a message, only that downstream subscribers fire.
44
57
  */
45
58
  type RbacServiceLike = {
46
59
  loadAcl: (
@@ -68,6 +81,35 @@ const connectChannelSchema = z.object({
68
81
  action: z.literal('connect-channel'),
69
82
  displayName: z.string().min(1).max(255).optional(),
70
83
  externalIdentifier: z.string().min(1).max(255).optional(),
84
+ /**
85
+ * Which stub provider to connect. `email` (default) keeps the historical
86
+ * email-shaped channel; `chat` connects a channel whose senders have no
87
+ * address, so a test can exercise the hub's non-email identity contract
88
+ * without inventing one (#4975).
89
+ */
90
+ providerFlavor: z.enum(['email', 'chat']).optional(),
91
+ })
92
+
93
+ /**
94
+ * Drive the REAL `ingest_inbound_message` command with a chat-shaped frame.
95
+ *
96
+ * `emit-inbound` below deliberately bypasses the platform compose path (it
97
+ * inserts the `messages` row with raw SQL) because it only ever needed a
98
+ * landing zone for the CRM-link subscribers. That bypass is why an inbound test
99
+ * could pass while `composeMessageSchema` rejected every real message (#4975).
100
+ * This action takes the opposite approach: it hands the frame to the adapter and
101
+ * the ingest command and asserts nothing on the way, so whatever the hub's
102
+ * contract really is, the test feels it.
103
+ */
104
+ const ingestInboundSchema = z.object({
105
+ action: z.literal('ingest-inbound'),
106
+ channelId: z.string().uuid(),
107
+ /** Opaque sender handle — a Discord snowflake, a Slack member id, etc. */
108
+ senderIdentifier: z.string().min(1).max(255),
109
+ senderDisplayName: z.string().max(255).optional(),
110
+ body: z.string().max(50_000).optional(),
111
+ externalMessageId: z.string().min(1).max(255),
112
+ externalConversationId: z.string().min(1).max(255),
71
113
  })
72
114
 
73
115
  const emitInboundSchema = z.object({
@@ -103,7 +145,11 @@ const emitInboundSchema = z.object({
103
145
  createThreadMapping: z.boolean().optional(),
104
146
  })
105
147
 
106
- const bodySchema = z.discriminatedUnion('action', [connectChannelSchema, emitInboundSchema])
148
+ const bodySchema = z.discriminatedUnion('action', [
149
+ connectChannelSchema,
150
+ ingestInboundSchema,
151
+ emitInboundSchema,
152
+ ])
107
153
 
108
154
  export async function POST(req: Request): Promise<Response> {
109
155
  // Fail-closed: invisible in production. Mirrors an unknown route (404) rather
@@ -139,13 +185,23 @@ export async function POST(req: Request): Promise<Response> {
139
185
  if (body.action === 'connect-channel') {
140
186
  const stamp = Date.now()
141
187
  const commandBus = container.resolve('commandBus') as CommandBus
188
+ const isChatFlavor = body.providerFlavor === 'chat'
189
+ // A chat channel is deliberately connected WITHOUT an email-ish credential
190
+ // key, so `connect-credential-channel` derives no `externalIdentifier` and
191
+ // the row is shaped exactly like a real Discord channel (identifier NULL —
192
+ // the condition #4977 describes). Inventing one here would recreate the
193
+ // fixture dishonesty that hid #4975.
194
+ const credentials = isChatFlavor
195
+ ? { handle: body.externalIdentifier ?? `test-seed-chat-${stamp}` }
196
+ : {
197
+ username: body.externalIdentifier ?? `test-seed-${stamp}@test-seed.local`,
198
+ fromAddress: body.externalIdentifier ?? `test-seed-${stamp}@test-seed.local`,
199
+ }
142
200
  const input: ConnectCredentialChannelInput = {
143
- providerKey: TEST_SEED_PROVIDER_KEY,
144
- displayName: body.displayName ?? `Test Seed Channel ${stamp}`,
145
- credentials: {
146
- username: body.externalIdentifier ?? `test-seed-${stamp}@test-seed.local`,
147
- fromAddress: body.externalIdentifier ?? `test-seed-${stamp}@test-seed.local`,
148
- },
201
+ providerKey: isChatFlavor ? TEST_SEED_CHAT_PROVIDER_KEY : TEST_SEED_PROVIDER_KEY,
202
+ displayName:
203
+ body.displayName ?? `Test Seed ${isChatFlavor ? 'Chat ' : ''}Channel ${stamp}`,
204
+ credentials,
149
205
  userId,
150
206
  scope: { tenantId, organizationId },
151
207
  }
@@ -174,9 +230,12 @@ export async function POST(req: Request): Promise<Response> {
174
230
  )
175
231
  }
176
232
 
177
- // action === 'emit-inbound'
233
+ // action === 'ingest-inbound' | 'emit-inbound' — both address an existing channel.
178
234
  const em = (container.resolve('em') as EntityManager).fork()
179
- const providerKey = body.providerKey ?? TEST_SEED_PROVIDER_KEY
235
+ // Only the `emit-inbound` branch stamps a caller-chosen provider key onto the
236
+ // rows it seeds; `ingest-inbound` takes the channel's own (see below).
237
+ const providerKey =
238
+ body.action === 'emit-inbound' ? body.providerKey ?? TEST_SEED_PROVIDER_KEY : TEST_SEED_PROVIDER_KEY
180
239
 
181
240
  // `channelId` is caller-supplied, so confirm it names a channel this tenant/org
182
241
  // actually owns before seeding rows that reference it. Mirrors the ownership
@@ -227,6 +286,85 @@ export async function POST(req: Request): Promise<Response> {
227
286
  )
228
287
  }
229
288
 
289
+ if (body.action === 'ingest-inbound') {
290
+ // Everything below this point is the real path: the adapter normalizes the
291
+ // frame and `ingest_inbound_message` composes the platform message through
292
+ // `messages.messages.compose`. Nothing is short-circuited, so a hub contract
293
+ // the provider cannot satisfy surfaces here as a failure instead of hiding
294
+ // behind seeded rows (#4975).
295
+ // The channel's own provider key, never a hardcoded one: ingest does not
296
+ // check that `providerKey` matches the channel it names, so passing a
297
+ // different one would silently stamp the wrong provider onto the link.
298
+ const channelProviderKey = ownedChannel.providerKey
299
+ if (channelProviderKey !== TEST_SEED_CHAT_PROVIDER_KEY) {
300
+ return NextResponse.json(
301
+ {
302
+ error:
303
+ 'ingest-inbound requires a channel connected with providerFlavor: "chat"; ' +
304
+ `channel ${body.channelId} is '${channelProviderKey}'`,
305
+ },
306
+ { status: 422 },
307
+ )
308
+ }
309
+
310
+ const commandBus = container.resolve('commandBus') as CommandBus
311
+ const adapterRegistry = container.resolve('channelAdapterRegistry') as {
312
+ get: (key: string) => { normalizeInbound: (raw: unknown) => Promise<unknown> } | undefined
313
+ }
314
+ const adapter = adapterRegistry.get(channelProviderKey)
315
+ if (!adapter) {
316
+ return NextResponse.json(
317
+ { error: '[internal] test-seed chat adapter is not registered' },
318
+ { status: 500 },
319
+ )
320
+ }
321
+
322
+ const normalized = await adapter.normalizeInbound({
323
+ raw: {
324
+ externalMessageId: body.externalMessageId,
325
+ externalConversationId: body.externalConversationId,
326
+ senderIdentifier: body.senderIdentifier,
327
+ senderDisplayName: body.senderDisplayName,
328
+ body: body.body ?? '',
329
+ },
330
+ eventType: 'message',
331
+ metadata: {},
332
+ })
333
+
334
+ const ingestInput = {
335
+ channelId: body.channelId,
336
+ providerKey: channelProviderKey,
337
+ channelType: ownedChannel.channelType,
338
+ scope: { tenantId, organizationId },
339
+ message: normalized,
340
+ } as IngestInboundMessageInput
341
+
342
+ const { result } = await commandBus.execute<
343
+ IngestInboundMessageInput,
344
+ IngestInboundMessageResult
345
+ >(COMMUNICATION_CHANNELS_INGEST_INBOUND_COMMAND_ID, {
346
+ input: ingestInput,
347
+ ctx: {
348
+ container,
349
+ auth: auth as never,
350
+ organizationScope: null,
351
+ selectedOrganizationId: organizationId,
352
+ organizationIds: organizationId ? [organizationId] : null,
353
+ },
354
+ })
355
+
356
+ return NextResponse.json(
357
+ {
358
+ status: result.status,
359
+ messageId: result.messageId ?? null,
360
+ conversationId: result.externalConversationId ?? null,
361
+ channelLinkId: result.channelLinkId ?? null,
362
+ channelType: ownedChannel.channelType,
363
+ },
364
+ { status: 201 },
365
+ )
366
+ }
367
+
230
368
  // A MessageChannelLink requires a non-null external_conversation_id (FK) and
231
369
  // message_id. Create a synthetic conversation + (optionally threaded) message
232
370
  // so the link is shaped like a real inbound row the subscriber can consume.
@@ -341,7 +479,8 @@ export const openApi = {
341
479
  tags: ['CommunicationChannels'],
342
480
  methods: {
343
481
  POST: {
344
- summary: 'Test-only: seed a connected channel or emit an inbound message (env-gated)',
482
+ summary:
483
+ 'Test-only: seed a connected channel, ingest a real inbound message, or emit a seeded inbound link (env-gated)',
345
484
  tags: ['CommunicationChannels'],
346
485
  responses: [
347
486
  { status: 201, description: 'Channel seeded / inbound message emitted' },
@@ -24,6 +24,7 @@ type ChannelListResponse = {
24
24
  items?: ChannelRow[]
25
25
  total?: number
26
26
  totalPages?: number
27
+ totalIsCapped?: boolean
27
28
  }
28
29
 
29
30
  export default function ChannelsListPage() {
@@ -31,6 +32,7 @@ export default function ChannelsListPage() {
31
32
  const [rows, setRows] = React.useState<ChannelRow[]>([])
32
33
  const [total, setTotal] = React.useState(0)
33
34
  const [totalPages, setTotalPages] = React.useState(1)
35
+ const [totalIsCapped, setTotalIsCapped] = React.useState(false)
34
36
  const [page, setPage] = React.useState(1)
35
37
  const [isLoading, setIsLoading] = React.useState(true)
36
38
  const [errorMessage, setErrorMessage] = React.useState<string | null>(null)
@@ -72,6 +74,7 @@ export default function ChannelsListPage() {
72
74
  setRows(Array.isArray(data.items) ? data.items : [])
73
75
  setTotal(typeof data.total === 'number' ? data.total : 0)
74
76
  setTotalPages(typeof data.totalPages === 'number' ? data.totalPages : 1)
77
+ setTotalIsCapped(data.totalIsCapped === true)
75
78
  }
76
79
  setIsLoading(false)
77
80
  }
@@ -139,6 +142,7 @@ export default function ChannelsListPage() {
139
142
  pageSize,
140
143
  total,
141
144
  totalPages,
145
+ totalIsCapped,
142
146
  onPageChange: setPage,
143
147
  }}
144
148
  />
@@ -4,6 +4,7 @@ import type { EntityManager } from '@mikro-orm/postgresql'
4
4
  import type { CommandBus, CommandHandler, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'
5
5
  import { registerCommand } from '@open-mercato/shared/lib/commands'
6
6
  import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
7
+ import { htmlToPlainText } from '@open-mercato/shared/lib/html/htmlToPlainText'
7
8
  import { emitCommunicationChannelsEvent } from '../events'
8
9
  import { resolveContact } from '../lib/contact-resolver'
9
10
  import type { ChannelAdapterRegistry } from '../lib/registry'
@@ -336,10 +337,41 @@ const ingestInboundMessageCommand: CommandHandler<IngestInboundMessageInput, Ing
336
337
  // ExternalMessage.rawPayload if needed for forensic / forward use.
337
338
  // - Some legitimate messages have no subject (notifications, bounce
338
339
  // digests). Substitute a placeholder instead of failing ingest.
340
+ // - HTML bodies land in the platform body as plain text rather than being
341
+ // merely relabelled as `text`: `MarkdownContent` renders a `text` body
342
+ // verbatim, so unconverted markup shows up as literal doctype, <style>
343
+ // blocks and tags in the inbox preview and the detail body. The source
344
+ // markup is untouched in `channelPayload.html`, which `data/enrichers.ts`
345
+ // sanitizes into `_channelPayload.sanitizedHtml` for the channel-payload
346
+ // renderer widget mounted at `detail:messages:message:body:after`.
347
+ // - The plain-text alternative wins over conversion when the sender's
348
+ // client supplied one (`multipart/alternative`), because that part is
349
+ // what a human actually wrote; `html-to-text` output — inlined link
350
+ // URLs, flattened tables, synthesized list markers — is the fallback for
351
+ // HTML-only senders. `lib/email-mime.ts` preferred `bodyHtml` when
352
+ // picking `body`, but preserved the text part at `channelPayload.text`.
339
353
  const MAX_COMPOSE_BODY = 50_000
354
+ // Bound the synchronous parse: inbound mail is untrusted and the 5MB body
355
+ // ceiling from `lib/email-capabilities.ts` would otherwise block the ingest
356
+ // worker on a single oversized document. This cap counts markup bytes while
357
+ // MAX_COMPOSE_BODY counts text characters, so it can bite first — an
358
+ // ordinary marketing template or a long quoted thread is easily over 512KB
359
+ // of markup and still well under 50k characters of text. It therefore
360
+ // carries the same truncation marker, rather than ending mid-sentence with
361
+ // nothing to tell the reader the rest exists.
362
+ const MAX_HTML_PARSE_INPUT = 512 * 1024
340
363
  const TRUNCATE_MARKER =
341
364
  '\n\n[…message truncated by Open Mercato — full body preserved in ExternalMessage.rawPayload]'
342
- const rawBody = m.body ?? ''
365
+ const sourceBody = m.body ?? ''
366
+ const plainAlternative =
367
+ typeof m.channelPayload?.text === 'string' ? m.channelPayload.text.trim() : ''
368
+ const convertHtmlBody = () => {
369
+ const converted = htmlToPlainText(sourceBody.slice(0, MAX_HTML_PARSE_INPUT))
370
+ return sourceBody.length > MAX_HTML_PARSE_INPUT ? converted + TRUNCATE_MARKER : converted
371
+ }
372
+ const rawBody = m.bodyFormat === 'html'
373
+ ? (plainAlternative || convertHtmlBody())
374
+ : sourceBody
343
375
  const truncatedBody =
344
376
  rawBody.length > MAX_COMPOSE_BODY
345
377
  ? rawBody.slice(0, MAX_COMPOSE_BODY - TRUNCATE_MARKER.length) + TRUNCATE_MARKER
@@ -353,6 +385,11 @@ const ingestInboundMessageCommand: CommandHandler<IngestInboundMessageInput, Ing
353
385
  sourceEntityId: conversation.id,
354
386
  externalEmail: contactHint?.email ?? undefined,
355
387
  externalName: contactHint?.displayName ?? m.senderDisplayName,
388
+ // #4975: tells the hub which identity contract applies to this sender.
389
+ // Email-typed channels keep the mandatory `externalEmail`; providers whose
390
+ // senders have no address (Discord, Slack, SMS…) are validated without it.
391
+ // The messages validator fails closed on any type it does not recognize.
392
+ sourceChannelType: input.channelType,
356
393
  recipients: mapping?.assignedUserId
357
394
  ? [{ userId: mapping.assignedUserId, type: 'to' as const }]
358
395
  : [],