@rubytech/create-sitedesk-code 0.1.520 → 0.1.522

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 (283) hide show
  1. package/dist/index.js +17 -3
  2. package/package.json +1 -1
  3. package/payload/platform/lib/account-schema-regions/package-lock.json +12 -0
  4. package/payload/platform/lib/active-rules/package-lock.json +12 -0
  5. package/payload/platform/lib/mcp-lifeline/package-lock.json +12 -0
  6. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +46 -7
  7. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +12 -0
  8. package/payload/platform/plugins/docs/references/admin-ui.md +11 -1
  9. package/payload/platform/plugins/docs/references/internals.md +4 -0
  10. package/payload/platform/plugins/docs/references/outlook-guide.md +27 -4
  11. package/payload/platform/plugins/email/mcp/dist/__tests__/attachment-metadata-calendar.test.js +46 -1
  12. package/payload/platform/plugins/email/mcp/dist/__tests__/attachment-metadata-calendar.test.js.map +1 -1
  13. package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts +14 -0
  14. package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts.map +1 -1
  15. package/payload/platform/plugins/email/mcp/dist/lib/imap.js +35 -4
  16. package/payload/platform/plugins/email/mcp/dist/lib/imap.js.map +1 -1
  17. package/payload/platform/plugins/email/references/email-reference.md +1 -1
  18. package/payload/platform/plugins/outlook/.claude-plugin/plugin.json +1 -1
  19. package/payload/platform/plugins/outlook/PLUGIN.md +9 -1
  20. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.d.ts +2 -0
  21. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.d.ts.map +1 -0
  22. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.js +151 -0
  23. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.js.map +1 -0
  24. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.d.ts +2 -0
  25. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.d.ts.map +1 -0
  26. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.js +151 -0
  27. package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.js.map +1 -0
  28. package/payload/platform/plugins/outlook/mcp/dist/__tests__/device-flow.test.js +38 -4
  29. package/payload/platform/plugins/outlook/mcp/dist/__tests__/device-flow.test.js.map +1 -1
  30. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.d.ts +2 -0
  31. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.d.ts.map +1 -0
  32. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.js +146 -0
  33. package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.js.map +1 -0
  34. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.d.ts +2 -0
  35. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.d.ts.map +1 -0
  36. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.js +120 -0
  37. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.js.map +1 -0
  38. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list.test.js +8 -8
  39. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list.test.js.map +1 -1
  40. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.d.ts +2 -0
  41. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.d.ts.map +1 -0
  42. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.js +27 -0
  43. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.js.map +1 -0
  44. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.d.ts +2 -0
  45. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.d.ts.map +1 -0
  46. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.js +70 -0
  47. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.js.map +1 -0
  48. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.d.ts +2 -0
  49. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.d.ts.map +1 -0
  50. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.js +152 -0
  51. package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.js.map +1 -0
  52. package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.d.ts +57 -0
  53. package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.d.ts.map +1 -0
  54. package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.js +163 -0
  55. package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.js.map +1 -0
  56. package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.d.ts +12 -0
  57. package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.d.ts.map +1 -1
  58. package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.js +21 -1
  59. package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.js.map +1 -1
  60. package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.d.ts +35 -4
  61. package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.d.ts.map +1 -1
  62. package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.js +92 -10
  63. package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.js.map +1 -1
  64. package/payload/platform/plugins/outlook/mcp/dist/index.js +106 -3
  65. package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -1
  66. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts +49 -0
  67. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts.map +1 -1
  68. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js +126 -8
  69. package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js.map +1 -1
  70. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.d.ts +13 -0
  71. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.d.ts.map +1 -1
  72. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.js +9 -0
  73. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.js.map +1 -1
  74. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.d.ts +42 -8
  75. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.d.ts.map +1 -1
  76. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.js +59 -19
  77. package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.js.map +1 -1
  78. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.d.ts +47 -0
  79. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.d.ts.map +1 -0
  80. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.js +67 -0
  81. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.js.map +1 -0
  82. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.d.ts +12 -0
  83. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.d.ts.map +1 -0
  84. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.js +18 -0
  85. package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.js.map +1 -0
  86. package/payload/platform/plugins/outlook/references/auth.md +15 -2
  87. package/payload/platform/plugins/outlook/references/graph-surfaces.md +8 -0
  88. package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +21 -1
  89. package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +3 -1
  90. package/payload/platform/scripts/check-architecture-skill-no-drift.mjs +20 -6
  91. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  92. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +2 -0
  93. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  94. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +17 -5
  95. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
  96. package/payload/platform/services/whatsapp-channel/dist/notification.js +30 -6
  97. package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
  98. package/payload/platform/services/whatsapp-channel/dist/server.d.ts.map +1 -1
  99. package/payload/platform/services/whatsapp-channel/dist/server.js +53 -21
  100. package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
  101. package/payload/platform/services/whatsapp-channel/dist/turn-follow.d.ts +16 -0
  102. package/payload/platform/services/whatsapp-channel/dist/turn-follow.d.ts.map +1 -1
  103. package/payload/platform/services/whatsapp-channel/dist/turn-follow.js +41 -2
  104. package/payload/platform/services/whatsapp-channel/dist/turn-follow.js.map +1 -1
  105. package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
  106. package/payload/server/{chunk-TIIWMF5G.js → chunk-K7CJGZPU.js} +459 -196
  107. package/payload/server/{manager-B6OFOXSW.js → manager-RKL7JZ4Y.js} +3 -1
  108. package/payload/server/public/activity.html +5 -5
  109. package/payload/server/public/agents.html +4 -4
  110. package/payload/server/public/assets/{AdminLoginScreens-SW7mEMcP.js → AdminLoginScreens-zc2_qqh-.js} +1 -1
  111. package/payload/server/public/assets/AdminLoginScreens-zc2_qqh-.js.br +0 -0
  112. package/payload/server/public/assets/AdminLoginScreens-zc2_qqh-.js.gz +0 -0
  113. package/payload/server/public/assets/AdminShell-IGUdTx5q.js +2 -0
  114. package/payload/server/public/assets/AdminShell-IGUdTx5q.js.br +0 -0
  115. package/payload/server/public/assets/AdminShell-IGUdTx5q.js.gz +0 -0
  116. package/payload/server/public/assets/{activity-sqbyX0pX.js → activity-DVgO8YuM.js} +1 -1
  117. package/payload/server/public/assets/activity-DVgO8YuM.js.br +0 -0
  118. package/payload/server/public/assets/activity-DVgO8YuM.js.gz +0 -0
  119. package/payload/server/public/assets/admin-xtpM7fgl.js +1 -0
  120. package/payload/server/public/assets/admin-xtpM7fgl.js.br +0 -0
  121. package/payload/server/public/assets/admin-xtpM7fgl.js.gz +0 -0
  122. package/payload/server/public/assets/{agents-BFYM5EDA.js → agents-P0nTIxo-.js} +1 -1
  123. package/payload/server/public/assets/agents-P0nTIxo-.js.br +0 -0
  124. package/payload/server/public/assets/agents-P0nTIxo-.js.gz +0 -0
  125. package/payload/server/public/assets/{browser-DP5GZe4G.js → browser-Mw7DY3ft.js} +1 -1
  126. package/payload/server/public/assets/browser-Mw7DY3ft.js.br +0 -0
  127. package/payload/server/public/assets/browser-Mw7DY3ft.js.gz +0 -0
  128. package/payload/server/public/assets/{calendar-BKaemLDG.js → calendar-B9gvL_1_.js} +1 -1
  129. package/payload/server/public/assets/calendar-B9gvL_1_.js.br +0 -0
  130. package/payload/server/public/assets/calendar-B9gvL_1_.js.gz +0 -0
  131. package/payload/server/public/assets/chat-DBGqh4wg.js +1 -0
  132. package/payload/server/public/assets/chat-DBGqh4wg.js.br +2 -0
  133. package/payload/server/public/assets/chat-DBGqh4wg.js.gz +0 -0
  134. package/payload/server/public/assets/chevron-left-Bl4vpOM0.js +1 -0
  135. package/payload/server/public/assets/chevron-left-Bl4vpOM0.js.br +0 -0
  136. package/payload/server/public/assets/chevron-right-BExigCnp.js +1 -0
  137. package/payload/server/public/assets/chevron-right-BExigCnp.js.br +0 -0
  138. package/payload/server/public/assets/clock-D8TJvgJC.js +1 -0
  139. package/payload/server/public/assets/clock-D8TJvgJC.js.br +0 -0
  140. package/payload/server/public/assets/data-HfFSxaC2.js +1 -0
  141. package/payload/server/public/assets/data-HfFSxaC2.js.br +0 -0
  142. package/payload/server/public/assets/data-HfFSxaC2.js.gz +0 -0
  143. package/payload/server/public/assets/{file-text-BKK7R-k_.js → file-text-dYBnXNEX.js} +1 -1
  144. package/payload/server/public/assets/file-text-dYBnXNEX.js.br +0 -0
  145. package/payload/server/public/assets/file-text-dYBnXNEX.js.gz +0 -0
  146. package/payload/server/public/assets/{graph-FuxYySjK.js → graph-CDyzd1Et.js} +2 -2
  147. package/payload/server/public/assets/graph-CDyzd1Et.js.br +0 -0
  148. package/payload/server/public/assets/graph-CDyzd1Et.js.gz +0 -0
  149. package/payload/server/public/assets/{graph-labels-Cip6fmKK.js → graph-labels-D1LkKiJr.js} +1 -1
  150. package/payload/server/public/assets/graph-labels-D1LkKiJr.js.br +0 -0
  151. package/payload/server/public/assets/graph-labels-D1LkKiJr.js.gz +0 -0
  152. package/payload/server/public/assets/{maximize-2-D79AhVDo.js → maximize-2-M-iSaznd.js} +1 -1
  153. package/payload/server/public/assets/maximize-2-M-iSaznd.js.br +0 -0
  154. package/payload/server/public/assets/maximize-2-M-iSaznd.js.gz +0 -0
  155. package/payload/server/public/assets/{operator-BPAfgFhP.js → operator-9jfCv1Fg.js} +1 -1
  156. package/payload/server/public/assets/operator-9jfCv1Fg.js.br +0 -0
  157. package/payload/server/public/assets/operator-9jfCv1Fg.js.gz +0 -0
  158. package/payload/server/public/assets/page-CZ-73FAk.js +32 -0
  159. package/payload/server/public/assets/page-CZ-73FAk.js.br +0 -0
  160. package/payload/server/public/assets/page-CZ-73FAk.js.gz +0 -0
  161. package/payload/server/public/assets/{page-BqEyS0W4.js → page-CjwSt7kx.js} +1 -1
  162. package/payload/server/public/assets/page-CjwSt7kx.js.br +0 -0
  163. package/payload/server/public/assets/page-CjwSt7kx.js.gz +0 -0
  164. package/payload/server/public/assets/{public-D1xs3FzD.js → public-CN-ltC8x.js} +1 -1
  165. package/payload/server/public/assets/public-CN-ltC8x.js.br +0 -0
  166. package/payload/server/public/assets/public-CN-ltC8x.js.gz +0 -0
  167. package/payload/server/public/assets/{rotate-ccw-CcU_XD8B.js → rotate-ccw-Buopsc22.js} +1 -1
  168. package/payload/server/public/assets/rotate-ccw-Buopsc22.js.br +0 -0
  169. package/payload/server/public/assets/rotate-ccw-Buopsc22.js.gz +0 -0
  170. package/payload/server/public/assets/routines-C-yr8ddT.js +1 -0
  171. package/payload/server/public/assets/routines-C-yr8ddT.js.br +0 -0
  172. package/payload/server/public/assets/routines-C-yr8ddT.js.gz +0 -0
  173. package/payload/server/public/assets/skills-DiiuVzDK.js +1 -0
  174. package/payload/server/public/assets/skills-DiiuVzDK.js.br +0 -0
  175. package/payload/server/public/assets/skills-DiiuVzDK.js.gz +0 -0
  176. package/payload/server/public/assets/tasks-CnHIfn2_.js +1 -0
  177. package/payload/server/public/assets/tasks-CnHIfn2_.js.br +0 -0
  178. package/payload/server/public/assets/tasks-CnHIfn2_.js.gz +0 -0
  179. package/payload/server/public/assets/{time-entry-format-IFqinQJj.js → time-entry-format-BBokCF-D.js} +1 -1
  180. package/payload/server/public/assets/time-entry-format-BBokCF-D.js.br +0 -0
  181. package/payload/server/public/assets/time-entry-format-BBokCF-D.js.gz +0 -0
  182. package/payload/server/public/assets/{triangle-alert-C5vH__sq.js → triangle-alert-C_AcpTW5.js} +1 -1
  183. package/payload/server/public/assets/triangle-alert-C_AcpTW5.js.br +0 -0
  184. package/payload/server/public/assets/triangle-alert-C_AcpTW5.js.gz +0 -0
  185. package/payload/server/public/assets/{useCopyFeedback-BeL2gPl6.js → useCopyFeedback-TdimmPVR.js} +1 -1
  186. package/payload/server/public/assets/useCopyFeedback-TdimmPVR.js.br +0 -0
  187. package/payload/server/public/assets/useCopyFeedback-TdimmPVR.js.gz +0 -0
  188. package/payload/server/public/assets/useSubAccountSwitcher-BwHqV55p.js +14 -0
  189. package/payload/server/public/assets/useSubAccountSwitcher-BwHqV55p.js.br +0 -0
  190. package/payload/server/public/assets/useSubAccountSwitcher-BwHqV55p.js.gz +0 -0
  191. package/payload/server/public/assets/useSubAccountSwitcher-CsrPYqtm.css +1 -0
  192. package/payload/server/public/assets/useSubAccountSwitcher-CsrPYqtm.css.br +0 -0
  193. package/payload/server/public/assets/useSubAccountSwitcher-CsrPYqtm.css.gz +0 -0
  194. package/payload/server/public/assets/{useVoiceRecorder-Cpw3aOSt.js → useVoiceRecorder-BT2738ZS.js} +1 -1
  195. package/payload/server/public/assets/useVoiceRecorder-BT2738ZS.js.br +0 -0
  196. package/payload/server/public/assets/useVoiceRecorder-BT2738ZS.js.gz +0 -0
  197. package/payload/server/public/assets/{wrench-BBEup3hd.js → wrench-B51s5kGA.js} +1 -1
  198. package/payload/server/public/assets/wrench-B51s5kGA.js.br +0 -0
  199. package/payload/server/public/assets/wrench-B51s5kGA.js.gz +0 -0
  200. package/payload/server/public/browser.html +4 -4
  201. package/payload/server/public/calendar.html +7 -7
  202. package/payload/server/public/chat.html +13 -13
  203. package/payload/server/public/data.html +11 -11
  204. package/payload/server/public/graph.html +9 -9
  205. package/payload/server/public/index.html +14 -14
  206. package/payload/server/public/operator.html +14 -14
  207. package/payload/server/public/public.html +13 -13
  208. package/payload/server/public/routines.html +6 -6
  209. package/payload/server/public/skills.html +5 -5
  210. package/payload/server/public/tasks.html +6 -6
  211. package/payload/server/server.js +1238 -653
  212. package/payload/server/public/assets/AdminLoginScreens-SW7mEMcP.js.br +0 -0
  213. package/payload/server/public/assets/AdminLoginScreens-SW7mEMcP.js.gz +0 -0
  214. package/payload/server/public/assets/AdminShell-Djg6ndne.js +0 -2
  215. package/payload/server/public/assets/AdminShell-Djg6ndne.js.br +0 -0
  216. package/payload/server/public/assets/AdminShell-Djg6ndne.js.gz +0 -0
  217. package/payload/server/public/assets/activity-sqbyX0pX.js.br +0 -0
  218. package/payload/server/public/assets/activity-sqbyX0pX.js.gz +0 -0
  219. package/payload/server/public/assets/admin-D9Bt47GY.js +0 -1
  220. package/payload/server/public/assets/admin-D9Bt47GY.js.br +0 -0
  221. package/payload/server/public/assets/admin-D9Bt47GY.js.gz +0 -0
  222. package/payload/server/public/assets/agents-BFYM5EDA.js.br +0 -0
  223. package/payload/server/public/assets/agents-BFYM5EDA.js.gz +0 -0
  224. package/payload/server/public/assets/browser-DP5GZe4G.js.br +0 -0
  225. package/payload/server/public/assets/browser-DP5GZe4G.js.gz +0 -0
  226. package/payload/server/public/assets/calendar-BKaemLDG.js.br +0 -0
  227. package/payload/server/public/assets/calendar-BKaemLDG.js.gz +0 -0
  228. package/payload/server/public/assets/chat-DPhT6urD.js +0 -1
  229. package/payload/server/public/assets/chat-DPhT6urD.js.br +0 -1
  230. package/payload/server/public/assets/chat-DPhT6urD.js.gz +0 -0
  231. package/payload/server/public/assets/chevron-left-BSdXvJcH.js +0 -1
  232. package/payload/server/public/assets/chevron-right-DI7vI3IB.js +0 -1
  233. package/payload/server/public/assets/chevron-right-DI7vI3IB.js.br +0 -0
  234. package/payload/server/public/assets/clock-YUTPXZH5.js +0 -1
  235. package/payload/server/public/assets/clock-YUTPXZH5.js.br +0 -0
  236. package/payload/server/public/assets/clock-YUTPXZH5.js.gz +0 -0
  237. package/payload/server/public/assets/data-CfjttTvR.js +0 -1
  238. package/payload/server/public/assets/data-CfjttTvR.js.br +0 -2
  239. package/payload/server/public/assets/data-CfjttTvR.js.gz +0 -0
  240. package/payload/server/public/assets/file-text-BKK7R-k_.js.br +0 -0
  241. package/payload/server/public/assets/file-text-BKK7R-k_.js.gz +0 -0
  242. package/payload/server/public/assets/graph-FuxYySjK.js.br +0 -0
  243. package/payload/server/public/assets/graph-FuxYySjK.js.gz +0 -0
  244. package/payload/server/public/assets/graph-labels-Cip6fmKK.js.br +0 -0
  245. package/payload/server/public/assets/graph-labels-Cip6fmKK.js.gz +0 -0
  246. package/payload/server/public/assets/maximize-2-D79AhVDo.js.br +0 -0
  247. package/payload/server/public/assets/maximize-2-D79AhVDo.js.gz +0 -0
  248. package/payload/server/public/assets/operator-BPAfgFhP.js.br +0 -0
  249. package/payload/server/public/assets/operator-BPAfgFhP.js.gz +0 -0
  250. package/payload/server/public/assets/page--78CbdFs.js +0 -32
  251. package/payload/server/public/assets/page--78CbdFs.js.br +0 -0
  252. package/payload/server/public/assets/page--78CbdFs.js.gz +0 -0
  253. package/payload/server/public/assets/page-BqEyS0W4.js.br +0 -0
  254. package/payload/server/public/assets/page-BqEyS0W4.js.gz +0 -0
  255. package/payload/server/public/assets/public-D1xs3FzD.js.br +0 -0
  256. package/payload/server/public/assets/public-D1xs3FzD.js.gz +0 -0
  257. package/payload/server/public/assets/rotate-ccw-CcU_XD8B.js.br +0 -0
  258. package/payload/server/public/assets/rotate-ccw-CcU_XD8B.js.gz +0 -0
  259. package/payload/server/public/assets/routines-CA7D0myr.js +0 -1
  260. package/payload/server/public/assets/routines-CA7D0myr.js.br +0 -0
  261. package/payload/server/public/assets/routines-CA7D0myr.js.gz +0 -0
  262. package/payload/server/public/assets/skills-CfVPPSxO.js +0 -1
  263. package/payload/server/public/assets/skills-CfVPPSxO.js.br +0 -0
  264. package/payload/server/public/assets/skills-CfVPPSxO.js.gz +0 -0
  265. package/payload/server/public/assets/tasks-DgieyZqz.js +0 -1
  266. package/payload/server/public/assets/tasks-DgieyZqz.js.br +0 -0
  267. package/payload/server/public/assets/tasks-DgieyZqz.js.gz +0 -0
  268. package/payload/server/public/assets/time-entry-format-IFqinQJj.js.br +0 -0
  269. package/payload/server/public/assets/time-entry-format-IFqinQJj.js.gz +0 -0
  270. package/payload/server/public/assets/triangle-alert-C5vH__sq.js.br +0 -0
  271. package/payload/server/public/assets/triangle-alert-C5vH__sq.js.gz +0 -0
  272. package/payload/server/public/assets/useCopyFeedback-BeL2gPl6.js.br +0 -0
  273. package/payload/server/public/assets/useCopyFeedback-BeL2gPl6.js.gz +0 -0
  274. package/payload/server/public/assets/useSubAccountSwitcher-CH9qm2Ad.js +0 -14
  275. package/payload/server/public/assets/useSubAccountSwitcher-CH9qm2Ad.js.br +0 -0
  276. package/payload/server/public/assets/useSubAccountSwitcher-CH9qm2Ad.js.gz +0 -0
  277. package/payload/server/public/assets/useSubAccountSwitcher-DLqUQzOl.css +0 -1
  278. package/payload/server/public/assets/useSubAccountSwitcher-DLqUQzOl.css.br +0 -0
  279. package/payload/server/public/assets/useSubAccountSwitcher-DLqUQzOl.css.gz +0 -0
  280. package/payload/server/public/assets/useVoiceRecorder-Cpw3aOSt.js.br +0 -0
  281. package/payload/server/public/assets/useVoiceRecorder-Cpw3aOSt.js.gz +0 -0
  282. package/payload/server/public/assets/wrench-BBEup3hd.js.br +0 -0
  283. package/payload/server/public/assets/wrench-BBEup3hd.js.gz +0 -0
@@ -332,32 +332,32 @@ function ledgerFilePath(account) {
332
332
  }
333
333
  return join(root, `${account}.jsonl`);
334
334
  }
335
- function appendStanza(entry) {
335
+ function appendStanza(entry2) {
336
336
  try {
337
337
  const row = {
338
- msgId: entry.msgId,
339
- from: entry.from,
340
- type: entry.type,
338
+ msgId: entry2.msgId,
339
+ from: entry2.from,
340
+ type: entry2.type,
341
341
  ts: (/* @__PURE__ */ new Date()).toISOString()
342
342
  };
343
- appendFileSync(ledgerFilePath(entry.account), JSON.stringify(row) + "\n");
343
+ appendFileSync(ledgerFilePath(entry2.account), JSON.stringify(row) + "\n");
344
344
  } catch (err) {
345
345
  console.error(
346
- `${TAG3} FAIL op=append msgId=${entry.msgId} err=${err instanceof Error ? err.message : String(err)}`
346
+ `${TAG3} FAIL op=append msgId=${entry2.msgId} err=${err instanceof Error ? err.message : String(err)}`
347
347
  );
348
348
  }
349
349
  }
350
- function markDeclined(entry) {
350
+ function markDeclined(entry2) {
351
351
  try {
352
352
  const row = {
353
- declined: entry.msgId,
354
- reason: entry.reason,
353
+ declined: entry2.msgId,
354
+ reason: entry2.reason,
355
355
  ts: (/* @__PURE__ */ new Date()).toISOString()
356
356
  };
357
- appendFileSync(ledgerFilePath(entry.account), JSON.stringify(row) + "\n");
357
+ appendFileSync(ledgerFilePath(entry2.account), JSON.stringify(row) + "\n");
358
358
  } catch (err) {
359
359
  console.error(
360
- `${TAG3} FAIL op=decline msgId=${entry.msgId} err=${err instanceof Error ? err.message : String(err)}`
360
+ `${TAG3} FAIL op=decline msgId=${entry2.msgId} err=${err instanceof Error ? err.message : String(err)}`
361
361
  );
362
362
  }
363
363
  }
@@ -375,36 +375,36 @@ function readDeclined(account) {
375
375
  }
376
376
  return out;
377
377
  }
378
- function appendNotifyEligible(entry) {
378
+ function appendNotifyEligible(entry2) {
379
379
  try {
380
380
  const row = {
381
- notifyEligible: entry.msgId,
382
- upsertType: entry.upsertType,
383
- fromMe: entry.fromMe,
384
- allowed: entry.allowed,
385
- reason: entry.reason,
386
- passive: entry.passive,
387
- persistAccount: entry.persistAccount,
381
+ notifyEligible: entry2.msgId,
382
+ upsertType: entry2.upsertType,
383
+ fromMe: entry2.fromMe,
384
+ allowed: entry2.allowed,
385
+ reason: entry2.reason,
386
+ passive: entry2.passive,
387
+ persistAccount: entry2.persistAccount,
388
388
  ts: (/* @__PURE__ */ new Date()).toISOString()
389
389
  };
390
- appendFileSync(ledgerFilePath(entry.account), JSON.stringify(row) + "\n");
390
+ appendFileSync(ledgerFilePath(entry2.account), JSON.stringify(row) + "\n");
391
391
  } catch (err) {
392
392
  console.error(
393
- `${TAG3} FAIL op=append-notify-eligible msgId=${entry.msgId} err=${err instanceof Error ? err.message : String(err)}`
393
+ `${TAG3} FAIL op=append-notify-eligible msgId=${entry2.msgId} err=${err instanceof Error ? err.message : String(err)}`
394
394
  );
395
395
  }
396
396
  }
397
- function appendNotifyPublished(entry) {
397
+ function appendNotifyPublished(entry2) {
398
398
  try {
399
399
  const row = {
400
- notifyPublished: entry.msgId,
401
- subscribers: entry.subscribers,
400
+ notifyPublished: entry2.msgId,
401
+ subscribers: entry2.subscribers,
402
402
  ts: (/* @__PURE__ */ new Date()).toISOString()
403
403
  };
404
- appendFileSync(ledgerFilePath(entry.account), JSON.stringify(row) + "\n");
404
+ appendFileSync(ledgerFilePath(entry2.account), JSON.stringify(row) + "\n");
405
405
  } catch (err) {
406
406
  console.error(
407
- `${TAG3} FAIL op=append-notify-published msgId=${entry.msgId} err=${err instanceof Error ? err.message : String(err)}`
407
+ `${TAG3} FAIL op=append-notify-published msgId=${entry2.msgId} err=${err instanceof Error ? err.message : String(err)}`
408
408
  );
409
409
  }
410
410
  }
@@ -971,12 +971,6 @@ function jidToE164(jid) {
971
971
  if (lidMatch) return lidMatch[1];
972
972
  return null;
973
973
  }
974
- function senderJidFromTelephone(senderTelephone) {
975
- if (!senderTelephone) return null;
976
- if (isGroupJid(senderTelephone)) return null;
977
- if (isUserJid(senderTelephone)) return senderTelephone;
978
- return null;
979
- }
980
974
  function isGroupJid(value) {
981
975
  const candidate = stripWhatsAppPrefix(value);
982
976
  const lower = candidate.toLowerCase();
@@ -1224,16 +1218,16 @@ function toMillis(v) {
1224
1218
  if (typeof v.low === "number") return (v.low >>> 0) + (v.high ?? 0) * 4294967296;
1225
1219
  return null;
1226
1220
  }
1227
- function readReactionEntry(entry) {
1228
- const targetMsgKeyId = entry.key?.id ?? void 0;
1221
+ function readReactionEntry(entry2) {
1222
+ const targetMsgKeyId = entry2.key?.id ?? void 0;
1229
1223
  if (!targetMsgKeyId) return null;
1230
- const chatJid = entry.key?.remoteJid ?? entry.reaction?.key?.remoteJid ?? void 0;
1224
+ const chatJid = entry2.key?.remoteJid ?? entry2.reaction?.key?.remoteJid ?? void 0;
1231
1225
  if (!chatJid) return null;
1232
1226
  const isGroup = isGroupJid(chatJid);
1233
- const reactorKey = entry.reaction?.key;
1227
+ const reactorKey = entry2.reaction?.key;
1234
1228
  const reactorJid = isGroup ? reactorKey?.participant ?? void 0 : reactorKey?.remoteJid ?? chatJid;
1235
1229
  if (!reactorJid) return null;
1236
- const emoji = entry.reaction?.text ?? "";
1230
+ const emoji = entry2.reaction?.text ?? "";
1237
1231
  return {
1238
1232
  targetMsgKeyId,
1239
1233
  chatJid,
@@ -1242,7 +1236,7 @@ function readReactionEntry(entry) {
1242
1236
  emoji,
1243
1237
  removed: !emoji,
1244
1238
  fromMe: !!reactorKey?.fromMe,
1245
- timestampMs: toMillis(entry.reaction?.senderTimestampMs)
1239
+ timestampMs: toMillis(entry2.reaction?.senderTimestampMs)
1246
1240
  };
1247
1241
  }
1248
1242
 
@@ -1387,7 +1381,7 @@ function summariseRecords(records) {
1387
1381
  return {
1388
1382
  first: sorted[0],
1389
1383
  last: sorted[sorted.length - 1],
1390
- firstNamedSender: sorted.find((r) => r.senderName)?.senderName ?? null
1384
+ firstNamedSender: sorted.find((r) => !r.fromMe && r.senderName)?.senderName ?? null
1391
1385
  };
1392
1386
  }
1393
1387
  function readConversationSummaries(platformAccountId) {
@@ -1399,10 +1393,10 @@ function readConversationSummaries(platformAccountId) {
1399
1393
  let bytesRead = 0;
1400
1394
  try {
1401
1395
  if (!existsSync3(dir)) return out;
1402
- for (const entry of readdirSync2(dir)) {
1403
- if (!entry.endsWith(".jsonl")) continue;
1404
- const remoteJid = entry.slice(0, -".jsonl".length);
1405
- const file = join2(dir, entry);
1396
+ for (const entry2 of readdirSync2(dir)) {
1397
+ if (!entry2.endsWith(".jsonl")) continue;
1398
+ const remoteJid = entry2.slice(0, -".jsonl".length);
1399
+ const file = join2(dir, entry2);
1406
1400
  seen.add(file);
1407
1401
  let size;
1408
1402
  let mtimeMs;
@@ -1451,9 +1445,9 @@ function readAllConversations(platformAccountId) {
1451
1445
  const out = /* @__PURE__ */ new Map();
1452
1446
  try {
1453
1447
  if (!existsSync3(dir)) return out;
1454
- for (const entry of readdirSync2(dir)) {
1455
- if (!entry.endsWith(".jsonl")) continue;
1456
- const remoteJid = entry.slice(0, -".jsonl".length);
1448
+ for (const entry2 of readdirSync2(dir)) {
1449
+ if (!entry2.endsWith(".jsonl")) continue;
1450
+ const remoteJid = entry2.slice(0, -".jsonl".length);
1457
1451
  out.set(remoteJid, readConversation(platformAccountId, remoteJid));
1458
1452
  }
1459
1453
  } catch (err) {
@@ -1474,16 +1468,16 @@ function normalizeEntry(v) {
1474
1468
  return typeof v === "string" ? { account: v, mode: "active" } : { account: v.managesAccount, mode: v.mode ?? "active" };
1475
1469
  }
1476
1470
  function managedAccountFor(accountManagers, phone) {
1477
- for (const [managerPhone, entry] of Object.entries(accountManagers)) {
1478
- const { account, mode } = normalizeEntry(entry);
1471
+ for (const [managerPhone, entry2] of Object.entries(accountManagers)) {
1472
+ const { account, mode } = normalizeEntry(entry2);
1479
1473
  if (account && phonesMatch(managerPhone, phone)) return { account, mode };
1480
1474
  }
1481
1475
  return null;
1482
1476
  }
1483
1477
  function managerPhonesForAccount(accountManagers, subAccountId) {
1484
1478
  const phones = [];
1485
- for (const [managerPhone, entry] of Object.entries(accountManagers)) {
1486
- const { account } = normalizeEntry(entry);
1479
+ for (const [managerPhone, entry2] of Object.entries(accountManagers)) {
1480
+ const { account } = normalizeEntry(entry2);
1487
1481
  if (account && account === subAccountId) phones.push(managerPhone);
1488
1482
  }
1489
1483
  return phones;
@@ -1504,7 +1498,7 @@ function resolveAccountConfig(config, accountConfig) {
1504
1498
  function isAdminPhone(phone, adminPhones) {
1505
1499
  const normalized = normalizeE164(phone);
1506
1500
  if (!normalized) return false;
1507
- return adminPhones.some((entry) => phonesMatch(entry, normalized));
1501
+ return adminPhones.some((entry2) => phonesMatch(entry2, normalized));
1508
1502
  }
1509
1503
  function checkDmAccess(params) {
1510
1504
  const { senderPhone, selfPhone, accountId } = params;
@@ -1526,9 +1520,9 @@ function checkDmAccess(params) {
1526
1520
  case "open":
1527
1521
  return { allowed: true, reason: "dm-policy-open", agentType: "public", effectiveAccountId: accountId };
1528
1522
  case "allowlist": {
1529
- const inList = cfg.allowFrom.some((entry) => {
1530
- if (entry === "*") return true;
1531
- return phonesMatch(entry, senderPhone);
1523
+ const inList = cfg.allowFrom.some((entry2) => {
1524
+ if (entry2 === "*") return true;
1525
+ return phonesMatch(entry2, senderPhone);
1532
1526
  });
1533
1527
  if (inList) {
1534
1528
  return { allowed: true, reason: "allowlist-match", agentType: "public", effectiveAccountId: accountId };
@@ -1603,8 +1597,8 @@ var NOTIFY_MAX = 5e3;
1603
1597
  var notifyCache = /* @__PURE__ */ new Map();
1604
1598
  function isNotifyDuplicate(msgKeyId) {
1605
1599
  const now = Date.now();
1606
- const entry = notifyCache.get(msgKeyId);
1607
- if (entry && now - entry.ts <= NOTIFY_TTL_MS) return true;
1600
+ const entry2 = notifyCache.get(msgKeyId);
1601
+ if (entry2 && now - entry2.ts <= NOTIFY_TTL_MS) return true;
1608
1602
  for (const [key, e] of notifyCache) {
1609
1603
  if (now - e.ts > NOTIFY_TTL_MS) notifyCache.delete(key);
1610
1604
  }
@@ -1681,15 +1675,15 @@ var ACCESS_LOCKOUT_MS = 15 * 60 * 1e3;
1681
1675
  var ACCESS_WINDOW_MS = 15 * 60 * 1e3;
1682
1676
  function checkAccessRateLimit(ip, agentSlug) {
1683
1677
  const key = `${ip}:${agentSlug}`;
1684
- const entry = rateLimitMap.get(key);
1685
- if (!entry) return null;
1678
+ const entry2 = rateLimitMap.get(key);
1679
+ if (!entry2) return null;
1686
1680
  const now = Date.now();
1687
- if (entry.lockedUntil && now < entry.lockedUntil) {
1688
- const remainingMs = entry.lockedUntil - now;
1681
+ if (entry2.lockedUntil && now < entry2.lockedUntil) {
1682
+ const remainingMs = entry2.lockedUntil - now;
1689
1683
  const remainingS = Math.ceil(remainingMs / 1e3);
1690
1684
  return `Too many attempts. Try again in ${remainingS}s`;
1691
1685
  }
1692
- if (now - entry.firstAttempt > ACCESS_WINDOW_MS) {
1686
+ if (now - entry2.firstAttempt > ACCESS_WINDOW_MS) {
1693
1687
  rateLimitMap.delete(key);
1694
1688
  return null;
1695
1689
  }
@@ -1698,14 +1692,14 @@ function checkAccessRateLimit(ip, agentSlug) {
1698
1692
  function recordAccessFailedAttempt(ip, agentSlug) {
1699
1693
  const key = `${ip}:${agentSlug}`;
1700
1694
  const now = Date.now();
1701
- const entry = rateLimitMap.get(key);
1702
- if (!entry || now - entry.firstAttempt > ACCESS_WINDOW_MS) {
1695
+ const entry2 = rateLimitMap.get(key);
1696
+ if (!entry2 || now - entry2.firstAttempt > ACCESS_WINDOW_MS) {
1703
1697
  rateLimitMap.set(key, { attempts: 1, firstAttempt: now });
1704
1698
  return;
1705
1699
  }
1706
- entry.attempts++;
1707
- if (entry.attempts >= ACCESS_MAX_ATTEMPTS) {
1708
- entry.lockedUntil = now + ACCESS_LOCKOUT_MS;
1700
+ entry2.attempts++;
1701
+ if (entry2.attempts >= ACCESS_MAX_ATTEMPTS) {
1702
+ entry2.lockedUntil = now + ACCESS_LOCKOUT_MS;
1709
1703
  }
1710
1704
  }
1711
1705
  function clearAccessRateLimit(ip, agentSlug) {
@@ -1715,15 +1709,15 @@ var contactRateMap = /* @__PURE__ */ new Map();
1715
1709
  var REQUEST_LINK_MAX = 3;
1716
1710
  var REQUEST_LINK_WINDOW_MS = 60 * 60 * 1e3;
1717
1711
  function checkRequestLinkRateLimit(contactValue) {
1718
- const entry = contactRateMap.get(contactValue);
1719
- if (!entry) return null;
1712
+ const entry2 = contactRateMap.get(contactValue);
1713
+ if (!entry2) return null;
1720
1714
  const now = Date.now();
1721
- if (now - entry.windowStart > REQUEST_LINK_WINDOW_MS) {
1715
+ if (now - entry2.windowStart > REQUEST_LINK_WINDOW_MS) {
1722
1716
  contactRateMap.delete(contactValue);
1723
1717
  return null;
1724
1718
  }
1725
- if (entry.count >= REQUEST_LINK_MAX) {
1726
- const remainingMs = REQUEST_LINK_WINDOW_MS - (now - entry.windowStart);
1719
+ if (entry2.count >= REQUEST_LINK_MAX) {
1720
+ const remainingMs = REQUEST_LINK_WINDOW_MS - (now - entry2.windowStart);
1727
1721
  const remainingM = Math.ceil(remainingMs / 6e4);
1728
1722
  return `Too many requests. Try again in ${remainingM} minute(s)`;
1729
1723
  }
@@ -1731,12 +1725,12 @@ function checkRequestLinkRateLimit(contactValue) {
1731
1725
  }
1732
1726
  function recordRequestLinkAttempt(contactValue) {
1733
1727
  const now = Date.now();
1734
- const entry = contactRateMap.get(contactValue);
1735
- if (!entry || now - entry.windowStart > REQUEST_LINK_WINDOW_MS) {
1728
+ const entry2 = contactRateMap.get(contactValue);
1729
+ if (!entry2 || now - entry2.windowStart > REQUEST_LINK_WINDOW_MS) {
1736
1730
  contactRateMap.set(contactValue, { count: 1, windowStart: now });
1737
1731
  return;
1738
1732
  }
1739
- entry.count++;
1733
+ entry2.count++;
1740
1734
  }
1741
1735
  function maskContact(value) {
1742
1736
  if (value.includes("@")) {
@@ -1925,8 +1919,8 @@ var agentSentCache = /* @__PURE__ */ new Map();
1925
1919
  var inboundCache = /* @__PURE__ */ new Map();
1926
1920
  function pruneCache(cache3, ttl, max) {
1927
1921
  const now = Date.now();
1928
- for (const [key, entry] of cache3) {
1929
- if (now - entry.ts > ttl) cache3.delete(key);
1922
+ for (const [key, entry2] of cache3) {
1923
+ if (now - entry2.ts > ttl) cache3.delete(key);
1930
1924
  }
1931
1925
  if (cache3.size > max) {
1932
1926
  const excess = cache3.size - max;
@@ -1943,22 +1937,22 @@ function trackAgentSentMessage(messageId) {
1943
1937
  agentSentCache.set(messageId, { ts: Date.now() });
1944
1938
  }
1945
1939
  function isAgentSentMessage(messageId) {
1946
- const entry = agentSentCache.get(messageId);
1947
- if (!entry) return false;
1948
- if (Date.now() - entry.ts > AGENT_SENT_TTL_MS) {
1940
+ const entry2 = agentSentCache.get(messageId);
1941
+ if (!entry2) return false;
1942
+ if (Date.now() - entry2.ts > AGENT_SENT_TTL_MS) {
1949
1943
  agentSentCache.delete(messageId);
1950
1944
  return false;
1951
1945
  }
1952
1946
  return true;
1953
1947
  }
1954
1948
  function isDuplicateInbound(key) {
1955
- const entry = inboundCache.get(key);
1956
- if (!entry) {
1949
+ const entry2 = inboundCache.get(key);
1950
+ if (!entry2) {
1957
1951
  pruneCache(inboundCache, INBOUND_TTL_MS, INBOUND_MAX);
1958
1952
  inboundCache.set(key, { ts: Date.now() });
1959
1953
  return false;
1960
1954
  }
1961
- if (Date.now() - entry.ts > INBOUND_TTL_MS) {
1955
+ if (Date.now() - entry2.ts > INBOUND_TTL_MS) {
1962
1956
  inboundCache.delete(key);
1963
1957
  inboundCache.set(key, { ts: Date.now() });
1964
1958
  return false;
@@ -2019,7 +2013,7 @@ function recordActivity(event) {
2019
2013
  }
2020
2014
  acct[direction]++;
2021
2015
  acct.lastEventAt = now;
2022
- const entry = {
2016
+ const entry2 = {
2023
2017
  channel: "whatsapp",
2024
2018
  accountId,
2025
2019
  direction,
@@ -2027,7 +2021,7 @@ function recordActivity(event) {
2027
2021
  timestamp: now,
2028
2022
  messageType
2029
2023
  };
2030
- recentEvents.push(entry);
2024
+ recentEvents.push(entry2);
2031
2025
  if (recentEvents.length > RECENT_EVENTS_MAX) {
2032
2026
  recentEvents.splice(0, recentEvents.length - RECENT_EVENTS_MAX);
2033
2027
  }
@@ -2241,12 +2235,16 @@ async function persistWhatsAppMessage(input) {
2241
2235
  body: input.body,
2242
2236
  fromMe: input.fromMe,
2243
2237
  senderTelephone,
2238
+ // Spread rather than assigned, so a record with no sender JID has no key at
2239
+ // all on disk instead of an explicit `null` a reader would have to special-case.
2240
+ ...input.senderJid ? { senderJid: input.senderJid } : {},
2244
2241
  senderName: input.pushName ?? null,
2245
2242
  remoteJid: canonicalRemoteJid,
2246
2243
  msgKeyId: input.msgKeyId,
2247
2244
  quotedId: input.quoted?.id ?? null,
2248
2245
  quotedSender: input.quoted?.sender ?? null,
2249
2246
  scope,
2247
+ ...input.origin ? { origin: input.origin } : {},
2250
2248
  ...input.attachmentId ? { attachmentId: input.attachmentId } : {},
2251
2249
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
2252
2250
  };
@@ -2297,6 +2295,216 @@ function sanitizeReason(err) {
2297
2295
  return `${err.name}:${msg}`;
2298
2296
  }
2299
2297
 
2298
+ // app/lib/whatsapp/chat-metadata-store.ts
2299
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3, renameSync, statSync as statSync2 } from "fs";
2300
+ import { dirname as dirname2, join as join4 } from "path";
2301
+
2302
+ // app/lib/data-path.ts
2303
+ import { realpathSync } from "fs";
2304
+ import { resolve as resolve2, normalize, sep, relative, basename, join as join3 } from "path";
2305
+ var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve2(process.cwd(), "../platform");
2306
+ var DATA_ROOT = resolve2(PLATFORM_ROOT2, "..", "data");
2307
+ var CLAUDE_UPLOADS_ROOT = process.env.CLAUDE_CONFIG_DIR ? resolve2(process.env.CLAUDE_CONFIG_DIR, "uploads") : null;
2308
+ var ACCOUNT_PARTITION_DIRS = /* @__PURE__ */ new Set(["accounts"]);
2309
+ var ACCOUNT_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
2310
+ function crossesForeignAccountPartition(relPath, accountId) {
2311
+ const segs = relPath.split("/").filter(Boolean);
2312
+ if (segs.length < 2) return false;
2313
+ if (!ACCOUNT_PARTITION_DIRS.has(segs[0])) return false;
2314
+ const owner = segs[1];
2315
+ if (!ACCOUNT_UUID_RE.test(owner)) return false;
2316
+ return owner !== accountId;
2317
+ }
2318
+ function isWithinOwnAccountPartition(relPath, accountId) {
2319
+ const segs = relPath.split("/").filter(Boolean);
2320
+ return segs.length >= 2 && segs[0] === "accounts" && segs[1] === accountId;
2321
+ }
2322
+ function resolveUnderRoot(raw, rootAbs, rootLabel) {
2323
+ const cleaned = normalize("/" + (raw ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
2324
+ const absolute = resolve2(rootAbs, cleaned);
2325
+ let rootReal;
2326
+ try {
2327
+ rootReal = realpathSync(rootAbs);
2328
+ } catch (err) {
2329
+ return {
2330
+ ok: false,
2331
+ status: 500,
2332
+ error: `${rootLabel} not accessible: ${err instanceof Error ? err.message : String(err)}`
2333
+ };
2334
+ }
2335
+ let resolvedReal;
2336
+ try {
2337
+ resolvedReal = realpathSync(absolute);
2338
+ } catch (err) {
2339
+ const code = err.code;
2340
+ if (code === "ENOENT") {
2341
+ if (absolute !== rootReal && !absolute.startsWith(rootReal + sep)) {
2342
+ return { ok: false, status: 403, error: `Path escapes ${rootLabel}`, resolved: absolute };
2343
+ }
2344
+ return { ok: false, status: 404, error: "Not found" };
2345
+ }
2346
+ return {
2347
+ ok: false,
2348
+ status: 500,
2349
+ error: err instanceof Error ? err.message : String(err)
2350
+ };
2351
+ }
2352
+ if (resolvedReal !== rootReal && !resolvedReal.startsWith(rootReal + sep)) {
2353
+ return { ok: false, status: 403, error: `Path escapes ${rootLabel}`, resolved: resolvedReal };
2354
+ }
2355
+ const relPath = relative(rootReal, resolvedReal) || ".";
2356
+ return { ok: true, absolute: resolvedReal, dataRootReal: rootReal, relative: relPath };
2357
+ }
2358
+ function resolveDataPath(raw) {
2359
+ return resolveUnderRoot(raw, DATA_ROOT, "DATA_ROOT");
2360
+ }
2361
+ function resolveClaudeUploadsPath(raw) {
2362
+ if (!CLAUDE_UPLOADS_ROOT) {
2363
+ return { ok: false, status: 404, error: "Not found" };
2364
+ }
2365
+ return resolveUnderRoot(raw, CLAUDE_UPLOADS_ROOT, "CLAUDE_UPLOADS_ROOT");
2366
+ }
2367
+ function scratchpadTmpBase() {
2368
+ return process.env.MAXY_SCRATCHPAD_BASE ?? "/tmp";
2369
+ }
2370
+ function sessionScratchpadRoot(projectDir, sessionId) {
2371
+ const uid = typeof process.geteuid === "function" ? process.geteuid() : 0;
2372
+ return join3(scratchpadTmpBase(), `claude-${uid}`, basename(projectDir), sessionId, "scratchpad");
2373
+ }
2374
+ function resolveScratchpadPath(raw, scratchpadRootAbs) {
2375
+ return resolveUnderRoot(raw, scratchpadRootAbs, "scratchpad");
2376
+ }
2377
+
2378
+ // app/lib/whatsapp/chat-metadata-store.ts
2379
+ var SOURCE_RANK = { contact: 3, group: 2, chat: 1 };
2380
+ function isSource(v) {
2381
+ return v === "contact" || v === "group" || v === "chat";
2382
+ }
2383
+ function preferEntry(existing, incoming) {
2384
+ if (existing === void 0) return incoming;
2385
+ const a = SOURCE_RANK[existing.source];
2386
+ const b = SOURCE_RANK[incoming.source];
2387
+ if (b > a) return incoming;
2388
+ if (b < a) return existing;
2389
+ return incoming.updatedAt > existing.updatedAt ? incoming : existing;
2390
+ }
2391
+ function parseChatMetadata(raw) {
2392
+ const out = /* @__PURE__ */ new Map();
2393
+ let parsed;
2394
+ try {
2395
+ parsed = JSON.parse(raw);
2396
+ } catch {
2397
+ return out;
2398
+ }
2399
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return out;
2400
+ for (const [jid, value] of Object.entries(parsed)) {
2401
+ if (value === null || typeof value !== "object") continue;
2402
+ const v = value;
2403
+ if (typeof v.name !== "string" || v.name.length === 0) continue;
2404
+ if (!isSource(v.source)) continue;
2405
+ if (typeof v.updatedAt !== "string") continue;
2406
+ out.set(jid, {
2407
+ jid,
2408
+ name: v.name,
2409
+ source: v.source,
2410
+ isGroup: v.isGroup === true,
2411
+ updatedAt: v.updatedAt
2412
+ });
2413
+ }
2414
+ return out;
2415
+ }
2416
+ function serializeChatMetadata(map) {
2417
+ const obj = {};
2418
+ for (const [jid, entry2] of map) obj[jid] = entry2;
2419
+ return JSON.stringify(obj);
2420
+ }
2421
+ function chatMetadataPath(accountId) {
2422
+ return join4(DATA_ROOT, "accounts", accountId, "chat-metadata.json");
2423
+ }
2424
+ var readCache = /* @__PURE__ */ new Map();
2425
+ function readChatMetadata(accountId) {
2426
+ const path3 = chatMetadataPath(accountId);
2427
+ try {
2428
+ if (!existsSync4(path3)) return /* @__PURE__ */ new Map();
2429
+ const st = statSync2(path3);
2430
+ const cached = readCache.get(path3);
2431
+ if (cached && cached.size === st.size && cached.mtimeMs === st.mtimeMs) return cached.map;
2432
+ const map = parseChatMetadata(readFileSync5(path3, "utf8"));
2433
+ readCache.set(path3, { size: st.size, mtimeMs: st.mtimeMs, map });
2434
+ return map;
2435
+ } catch (err) {
2436
+ console.error(
2437
+ `[wa-chatmeta] op=read outcome=fail account=${accountId.slice(0, 8)} reason=${(err instanceof Error ? err.message : String(err)).slice(0, 100)}`
2438
+ );
2439
+ return /* @__PURE__ */ new Map();
2440
+ }
2441
+ }
2442
+ function writeChatMetadataMerged(accountId, updates) {
2443
+ if (updates.length === 0) return -1;
2444
+ const path3 = chatMetadataPath(accountId);
2445
+ try {
2446
+ mkdirSync3(dirname2(path3), { recursive: true });
2447
+ let merged = /* @__PURE__ */ new Map();
2448
+ if (existsSync4(path3)) merged = parseChatMetadata(readFileSync5(path3, "utf8"));
2449
+ for (const entry2 of updates) merged.set(entry2.jid, preferEntry(merged.get(entry2.jid), entry2));
2450
+ const tmp = `${path3}.tmp`;
2451
+ writeFileSync3(tmp, serializeChatMetadata(merged), "utf8");
2452
+ renameSync(tmp, path3);
2453
+ return parseChatMetadata(readFileSync5(path3, "utf8")).size;
2454
+ } catch (err) {
2455
+ console.error(
2456
+ `[wa-chatmeta] op=persist outcome=fail account=${accountId.slice(0, 8)} reason=${(err instanceof Error ? err.message : String(err)).slice(0, 100)}`
2457
+ );
2458
+ return -1;
2459
+ }
2460
+ }
2461
+
2462
+ // app/lib/whatsapp/chat-metadata-events.ts
2463
+ var NON_CHAT_JIDS = /* @__PURE__ */ new Set(["status@broadcast"]);
2464
+ function isGroupJid2(jid) {
2465
+ return jid.endsWith("@g.us");
2466
+ }
2467
+ function entry(jid, name, source, nowIso) {
2468
+ return { jid, name, source, isGroup: isGroupJid2(jid), updatedAt: nowIso };
2469
+ }
2470
+ function conversationKey(jid, phoneNumber) {
2471
+ if (!jid.endsWith("@lid")) return jid;
2472
+ if (typeof phoneNumber !== "string") return jid;
2473
+ const trimmed = phoneNumber.trim();
2474
+ return trimmed.endsWith("@s.whatsapp.net") ? trimmed : jid;
2475
+ }
2476
+ function usable(jid, name, phoneNumber) {
2477
+ if (typeof jid !== "string" || jid.length === 0) return null;
2478
+ if (NON_CHAT_JIDS.has(jid)) return null;
2479
+ const trimmed = typeof name === "string" ? name.trim() : "";
2480
+ if (trimmed.length === 0) return null;
2481
+ return { jid: conversationKey(jid, phoneNumber), name: trimmed };
2482
+ }
2483
+ function chatEntriesFromChats(chats, nowIso) {
2484
+ const out = [];
2485
+ for (const c of chats) {
2486
+ const ok = usable(c.id, c.name, c.phoneNumber);
2487
+ if (ok) out.push(entry(ok.jid, ok.name, "chat", nowIso));
2488
+ }
2489
+ return out;
2490
+ }
2491
+ function chatEntriesFromContacts(contacts, nowIso) {
2492
+ const out = [];
2493
+ for (const c of contacts) {
2494
+ const ok = usable(c.id, c.name, c.phoneNumber);
2495
+ if (ok) out.push(entry(ok.jid, ok.name, isGroupJid2(ok.jid) ? "chat" : "contact", nowIso));
2496
+ }
2497
+ return out;
2498
+ }
2499
+ function groupEntriesFromUpdates(updates, nowIso) {
2500
+ const out = [];
2501
+ for (const g of updates) {
2502
+ const ok = usable(g.id, g.subject);
2503
+ if (ok) out.push(entry(ok.jid, ok.name, "group", nowIso));
2504
+ }
2505
+ return out;
2506
+ }
2507
+
2300
2508
  // app/lib/whatsapp/ensure-conversation.ts
2301
2509
  var TAG10 = "[whatsapp-persist]";
2302
2510
  async function ensureWhatsAppConversation(input) {
@@ -2329,8 +2537,8 @@ async function ensureWhatsAppConversation(input) {
2329
2537
  }
2330
2538
 
2331
2539
  // ../lib/account-enumeration/src/index.ts
2332
- import { readdirSync as readdirSync3, readFileSync as readFileSync5 } from "fs";
2333
- import { resolve as resolve2 } from "path";
2540
+ import { readdirSync as readdirSync3, readFileSync as readFileSync6 } from "fs";
2541
+ import { resolve as resolve3 } from "path";
2334
2542
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
2335
2543
  var cache2 = /* @__PURE__ */ new Map();
2336
2544
  function enumerateValidAccountIds(accountsDir) {
@@ -2349,9 +2557,9 @@ function enumerateValidAccountIds(accountsDir) {
2349
2557
  const valid = [];
2350
2558
  for (const name of names) {
2351
2559
  if (!UUID_RE.test(name)) continue;
2352
- const configPath = resolve2(accountsDir, name, "account.json");
2560
+ const configPath = resolve3(accountsDir, name, "account.json");
2353
2561
  try {
2354
- JSON.parse(readFileSync5(configPath, "utf-8"));
2562
+ JSON.parse(readFileSync6(configPath, "utf-8"));
2355
2563
  valid.push(name);
2356
2564
  } catch (err) {
2357
2565
  const code = err.code;
@@ -2365,9 +2573,9 @@ function resolveHouseOrSoleAccountId(accountsDir) {
2365
2573
  const valid = enumerateValidAccountIds(accountsDir);
2366
2574
  const houses = [];
2367
2575
  for (const id of valid) {
2368
- const configPath = resolve2(accountsDir, id, "account.json");
2576
+ const configPath = resolve3(accountsDir, id, "account.json");
2369
2577
  try {
2370
- const cfg = JSON.parse(readFileSync5(configPath, "utf-8"));
2578
+ const cfg = JSON.parse(readFileSync6(configPath, "utf-8"));
2371
2579
  if (cfg.role === "house") houses.push(id);
2372
2580
  } catch {
2373
2581
  }
@@ -2385,7 +2593,7 @@ function getAccountsDirFromEnv() {
2385
2593
  "[graph-write] MAXY_PLATFORM_ROOT not set \u2014 cannot enforce accountId gate. Set MAXY_PLATFORM_ROOT in the spawning process or pass `accountsDir` explicitly."
2386
2594
  );
2387
2595
  }
2388
- return resolve2(root, "..", "data/accounts");
2596
+ return resolve3(root, "..", "data/accounts");
2389
2597
  }
2390
2598
  function validateAccountIdEnv(envValue, diskIds) {
2391
2599
  if (!envValue) {
@@ -2401,8 +2609,8 @@ function validateAccountIdEnv(envValue, diskIds) {
2401
2609
  }
2402
2610
 
2403
2611
  // app/lib/whatsapp/platform-account-id.ts
2404
- import { readFileSync as readFileSync6 } from "fs";
2405
- import { resolve as resolve3 } from "path";
2612
+ import { readFileSync as readFileSync7 } from "fs";
2613
+ import { resolve as resolve4 } from "path";
2406
2614
  var cachedHouseId = null;
2407
2615
  var cachedHouseAccountsDir = null;
2408
2616
  function resolveHouseId(accountsDir) {
@@ -2413,10 +2621,10 @@ function resolveHouseId(accountsDir) {
2413
2621
  return cachedHouseId;
2414
2622
  }
2415
2623
  function readChannelRoutingOverride(accountsDir, houseId) {
2416
- const configPath = resolve3(accountsDir, houseId, "account.json");
2624
+ const configPath = resolve4(accountsDir, houseId, "account.json");
2417
2625
  let raw;
2418
2626
  try {
2419
- raw = readFileSync6(configPath, "utf-8");
2627
+ raw = readFileSync7(configPath, "utf-8");
2420
2628
  } catch {
2421
2629
  return null;
2422
2630
  }
@@ -2457,8 +2665,8 @@ function warnNonUuidAccount(caller, accountId) {
2457
2665
  }
2458
2666
 
2459
2667
  // app/lib/managed-service.ts
2460
- import { join as join3 } from "path";
2461
- import { readFileSync as readFileSync7 } from "fs";
2668
+ import { join as join5 } from "path";
2669
+ import { readFileSync as readFileSync8 } from "fs";
2462
2670
  function isHouseManagedService(accountsDir = ACCOUNTS_DIR) {
2463
2671
  let houseId;
2464
2672
  try {
@@ -2468,7 +2676,7 @@ function isHouseManagedService(accountsDir = ACCOUNTS_DIR) {
2468
2676
  }
2469
2677
  try {
2470
2678
  const cfg = JSON.parse(
2471
- readFileSync7(join3(accountsDir, houseId, "account.json"), "utf-8")
2679
+ readFileSync8(join5(accountsDir, houseId, "account.json"), "utf-8")
2472
2680
  );
2473
2681
  return cfg.managedService === true;
2474
2682
  } catch {
@@ -2482,7 +2690,7 @@ function isCustomerTurnSuppressed(role, managedService) {
2482
2690
  // app/lib/whatsapp/inbound/media.ts
2483
2691
  import { randomUUID as randomUUID4, createHash } from "crypto";
2484
2692
  import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
2485
- import { join as join5 } from "path";
2693
+ import { join as join6 } from "path";
2486
2694
  import {
2487
2695
  downloadMediaMessage,
2488
2696
  downloadContentFromMessage,
@@ -2492,85 +2700,9 @@ import {
2492
2700
  // app/lib/attachments.ts
2493
2701
  import { randomUUID as randomUUID3 } from "crypto";
2494
2702
  import { mkdir, writeFile } from "fs/promises";
2495
- import { realpathSync as realpathSync2, existsSync as existsSync4, readFileSync as readFileSync8, readdirSync as readdirSync4 } from "fs";
2703
+ import { realpathSync as realpathSync2, existsSync as existsSync5, readFileSync as readFileSync9, readdirSync as readdirSync4 } from "fs";
2496
2704
  import { resolve as resolve5, extname } from "path";
2497
2705
 
2498
- // app/lib/data-path.ts
2499
- import { realpathSync } from "fs";
2500
- import { resolve as resolve4, normalize, sep, relative, basename, join as join4 } from "path";
2501
- var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve4(process.cwd(), "../platform");
2502
- var DATA_ROOT = resolve4(PLATFORM_ROOT2, "..", "data");
2503
- var CLAUDE_UPLOADS_ROOT = process.env.CLAUDE_CONFIG_DIR ? resolve4(process.env.CLAUDE_CONFIG_DIR, "uploads") : null;
2504
- var ACCOUNT_PARTITION_DIRS = /* @__PURE__ */ new Set(["accounts"]);
2505
- var ACCOUNT_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
2506
- function crossesForeignAccountPartition(relPath, accountId) {
2507
- const segs = relPath.split("/").filter(Boolean);
2508
- if (segs.length < 2) return false;
2509
- if (!ACCOUNT_PARTITION_DIRS.has(segs[0])) return false;
2510
- const owner = segs[1];
2511
- if (!ACCOUNT_UUID_RE.test(owner)) return false;
2512
- return owner !== accountId;
2513
- }
2514
- function isWithinOwnAccountPartition(relPath, accountId) {
2515
- const segs = relPath.split("/").filter(Boolean);
2516
- return segs.length >= 2 && segs[0] === "accounts" && segs[1] === accountId;
2517
- }
2518
- function resolveUnderRoot(raw, rootAbs, rootLabel) {
2519
- const cleaned = normalize("/" + (raw ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
2520
- const absolute = resolve4(rootAbs, cleaned);
2521
- let rootReal;
2522
- try {
2523
- rootReal = realpathSync(rootAbs);
2524
- } catch (err) {
2525
- return {
2526
- ok: false,
2527
- status: 500,
2528
- error: `${rootLabel} not accessible: ${err instanceof Error ? err.message : String(err)}`
2529
- };
2530
- }
2531
- let resolvedReal;
2532
- try {
2533
- resolvedReal = realpathSync(absolute);
2534
- } catch (err) {
2535
- const code = err.code;
2536
- if (code === "ENOENT") {
2537
- if (absolute !== rootReal && !absolute.startsWith(rootReal + sep)) {
2538
- return { ok: false, status: 403, error: `Path escapes ${rootLabel}`, resolved: absolute };
2539
- }
2540
- return { ok: false, status: 404, error: "Not found" };
2541
- }
2542
- return {
2543
- ok: false,
2544
- status: 500,
2545
- error: err instanceof Error ? err.message : String(err)
2546
- };
2547
- }
2548
- if (resolvedReal !== rootReal && !resolvedReal.startsWith(rootReal + sep)) {
2549
- return { ok: false, status: 403, error: `Path escapes ${rootLabel}`, resolved: resolvedReal };
2550
- }
2551
- const relPath = relative(rootReal, resolvedReal) || ".";
2552
- return { ok: true, absolute: resolvedReal, dataRootReal: rootReal, relative: relPath };
2553
- }
2554
- function resolveDataPath(raw) {
2555
- return resolveUnderRoot(raw, DATA_ROOT, "DATA_ROOT");
2556
- }
2557
- function resolveClaudeUploadsPath(raw) {
2558
- if (!CLAUDE_UPLOADS_ROOT) {
2559
- return { ok: false, status: 404, error: "Not found" };
2560
- }
2561
- return resolveUnderRoot(raw, CLAUDE_UPLOADS_ROOT, "CLAUDE_UPLOADS_ROOT");
2562
- }
2563
- function scratchpadTmpBase() {
2564
- return process.env.MAXY_SCRATCHPAD_BASE ?? "/tmp";
2565
- }
2566
- function sessionScratchpadRoot(projectDir, sessionId) {
2567
- const uid = typeof process.geteuid === "function" ? process.geteuid() : 0;
2568
- return join4(scratchpadTmpBase(), `claude-${uid}`, basename(projectDir), sessionId, "scratchpad");
2569
- }
2570
- function resolveScratchpadPath(raw, scratchpadRootAbs) {
2571
- return resolveUnderRoot(raw, scratchpadRootAbs, "scratchpad");
2572
- }
2573
-
2574
2706
  // app/lib/doc-to-text.ts
2575
2707
  import { unzipSync, strFromU8 } from "fflate";
2576
2708
  var DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
@@ -2591,10 +2723,10 @@ function bareMime(mimeType) {
2591
2723
  function unescapeXml(s) {
2592
2724
  return s.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&amp;/g, "&");
2593
2725
  }
2594
- function unzipEntry(buffer, entry) {
2726
+ function unzipEntry(buffer, entry2) {
2595
2727
  const files = unzipSync(new Uint8Array(buffer));
2596
- const bytes = files[entry];
2597
- if (!bytes) throw new Error(`zip entry not found: ${entry}`);
2728
+ const bytes = files[entry2];
2729
+ if (!bytes) throw new Error(`zip entry not found: ${entry2}`);
2598
2730
  return strFromU8(bytes);
2599
2731
  }
2600
2732
  function docxToText(buffer) {
@@ -2816,14 +2948,14 @@ async function storeMediaBuffer(accountId, attachmentId, filename, mimeType, buf
2816
2948
  return writeAttachment(accountId, "", filename, mimeType, buffer.byteLength, buffer, attachmentId);
2817
2949
  }
2818
2950
  function attachmentExists(accountId, attachmentId) {
2819
- return existsSync4(resolve5(uploadsDirFor(accountId, "", attachmentId), `${attachmentId}.meta.json`));
2951
+ return existsSync5(resolve5(uploadsDirFor(accountId, "", attachmentId), `${attachmentId}.meta.json`));
2820
2952
  }
2821
2953
  function resolveStoredAttachment(accountId, attachmentId) {
2822
2954
  if (!ATTACHMENT_ID_RE.test(attachmentId)) return null;
2823
2955
  const dir = uploadsDirFor(accountId, "", attachmentId);
2824
2956
  let meta;
2825
2957
  try {
2826
- meta = JSON.parse(readFileSync8(resolve5(dir, `${attachmentId}.meta.json`), "utf8"));
2958
+ meta = JSON.parse(readFileSync9(resolve5(dir, `${attachmentId}.meta.json`), "utf8"));
2827
2959
  } catch {
2828
2960
  return null;
2829
2961
  }
@@ -3173,7 +3305,7 @@ async function downloadInboundMedia(msg, sock, opts) {
3173
3305
  if (!res) return void 0;
3174
3306
  await mkdir2(MEDIA_DIR, { recursive: true });
3175
3307
  const ext = mimeToExt(res.mimetype);
3176
- const filePath = join5(MEDIA_DIR, `${randomUUID4()}.${ext}`);
3308
+ const filePath = join6(MEDIA_DIR, `${randomUUID4()}.${ext}`);
3177
3309
  await writeFile2(filePath, res.buffer);
3178
3310
  const sizeKB = (res.buffer.length / 1024).toFixed(0);
3179
3311
  console.error(`${TAG12} media downloaded type=${res.mimetype} size=${sizeKB}KB path=${filePath}`);
@@ -3578,6 +3710,7 @@ var connections = /* @__PURE__ */ new Map();
3578
3710
  var configDir = null;
3579
3711
  var whatsAppConfig = {};
3580
3712
  var onInboundMessage = null;
3713
+ var onMediaStanza = null;
3581
3714
  var onNotify = null;
3582
3715
  function buildNotifyPending(args) {
3583
3716
  const { body, truncated } = formatNotifyBody(args.text, args.mediaType);
@@ -3608,13 +3741,13 @@ var messageStore = /* @__PURE__ */ new Map();
3608
3741
  function messageStoreKey(accountId, remoteJid) {
3609
3742
  return `${accountId}:${remoteJid}`;
3610
3743
  }
3611
- function storeMessage(storeKey, entry) {
3744
+ function storeMessage(storeKey, entry2) {
3612
3745
  let entries = messageStore.get(storeKey);
3613
3746
  if (!entries) {
3614
3747
  entries = [];
3615
3748
  messageStore.set(storeKey, entries);
3616
3749
  }
3617
- entries.push(entry);
3750
+ entries.push(entry2);
3618
3751
  if (entries.length > MESSAGE_STORE_MAX) {
3619
3752
  const trimmed = entries.length - MESSAGE_STORE_MAX;
3620
3753
  entries.splice(0, trimmed);
@@ -3668,6 +3801,7 @@ async function init(opts) {
3668
3801
  configDir = opts.configDir;
3669
3802
  onInboundMessage = opts.onMessage;
3670
3803
  onNotify = opts.onNotify ?? null;
3804
+ onMediaStanza = opts.onMediaStanza ?? null;
3671
3805
  loadConfig(opts.accountConfig);
3672
3806
  const accountIds = listCredentialAccountIds(configDir);
3673
3807
  if (accountIds.length === 0) {
@@ -3870,6 +4004,7 @@ async function registerLoginSocket(accountId, sock, authDir) {
3870
4004
  applySelfIdentity(conn, sock, "register");
3871
4005
  connections.set(accountId, conn);
3872
4006
  monitorInbound(conn);
4007
+ monitorChatMetadata(conn);
3873
4008
  try {
3874
4009
  await sock.sendPresenceUpdate("available");
3875
4010
  console.error(`${TAG16} presence set to available account=${accountId}`);
@@ -3991,6 +4126,7 @@ async function connectWithReconnect(conn) {
3991
4126
  conn.sock = sock;
3992
4127
  applySelfIdentity(conn, sock, "pre-listener");
3993
4128
  monitorInbound(conn);
4129
+ monitorChatMetadata(conn);
3994
4130
  console.error(`${TAG16} socket created account=${conn.accountId} \u2014 waiting for connection`);
3995
4131
  await waitForConnection(sock);
3996
4132
  connectedAt = Date.now();
@@ -4122,6 +4258,95 @@ function applySelfIdentity(conn, sock, phase) {
4122
4258
  );
4123
4259
  }
4124
4260
  }
4261
+ function monitorChatMetadata(conn) {
4262
+ if (!conn.sock) return;
4263
+ const sock = conn.sock;
4264
+ const account = conn.platformAccountId;
4265
+ const canonicalise = async (entries) => {
4266
+ const out = [];
4267
+ for (const e of entries) {
4268
+ if (!e.jid.endsWith("@lid")) {
4269
+ out.push(e);
4270
+ continue;
4271
+ }
4272
+ const { canonical, resolved } = await resolveCanonicalConversationJid(e.jid, conn.lidMapping);
4273
+ if (resolved === "lid2pn") {
4274
+ console.error(`${TAG16} [wa-chatmeta] op=canonical raw=${e.jid} canonical=${canonical}`);
4275
+ out.push({ ...e, jid: canonical });
4276
+ } else {
4277
+ out.push(e);
4278
+ }
4279
+ }
4280
+ return out;
4281
+ };
4282
+ const persist = (kind, entries, perJid) => {
4283
+ if (entries.length === 0) return;
4284
+ if (perJid) {
4285
+ for (const e of entries) {
4286
+ console.error(
4287
+ `${TAG16} [wa-chatmeta] op=event kind=${kind} jid=${e.jid} name=${e.name.length} source=${e.source}`
4288
+ );
4289
+ }
4290
+ }
4291
+ const total = writeChatMetadataMerged(account, entries);
4292
+ console.error(
4293
+ `${TAG16} [wa-chatmeta] op=persist kind=${kind} account=${account.slice(0, 8)} updates=${entries.length} outcome=${total < 0 ? "fail" : "ok"} entries=${total}`
4294
+ );
4295
+ };
4296
+ sock.ev.on("messaging-history.set", ({ chats, contacts }) => {
4297
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
4298
+ const chatEntries = chatEntriesFromChats(chats ?? [], nowIso);
4299
+ const contactEntries = chatEntriesFromContacts(contacts ?? [], nowIso);
4300
+ console.error(
4301
+ `${TAG16} [wa-chatmeta] op=history-set account=${account.slice(0, 8)} chats=${chats?.length ?? 0} contacts=${contacts?.length ?? 0} groups=${chatEntries.filter((e) => e.isGroup).length} named=${chatEntries.length + contactEntries.length}`
4302
+ );
4303
+ void canonicalise([...chatEntries, ...contactEntries]).then((e) => persist("history-set", e, false));
4304
+ });
4305
+ sock.ev.on("chats.upsert", (chats) => {
4306
+ void canonicalise(chatEntriesFromChats(chats ?? [], (/* @__PURE__ */ new Date()).toISOString())).then((e) => persist("chats.upsert", e, true));
4307
+ });
4308
+ sock.ev.on("chats.update", (chats) => {
4309
+ void canonicalise(chatEntriesFromChats(chats ?? [], (/* @__PURE__ */ new Date()).toISOString())).then((e) => persist("chats.update", e, true));
4310
+ });
4311
+ sock.ev.on("contacts.upsert", (contacts) => {
4312
+ void canonicalise(chatEntriesFromContacts(contacts ?? [], (/* @__PURE__ */ new Date()).toISOString())).then((e) => persist("contacts.upsert", e, true));
4313
+ });
4314
+ sock.ev.on("contacts.update", (contacts) => {
4315
+ void canonicalise(chatEntriesFromContacts(contacts ?? [], (/* @__PURE__ */ new Date()).toISOString())).then((e) => persist("contacts.update", e, true));
4316
+ });
4317
+ sock.ev.on("groups.update", (updates) => {
4318
+ persist("groups.update", groupEntriesFromUpdates(updates ?? [], (/* @__PURE__ */ new Date()).toISOString()), true);
4319
+ });
4320
+ const groupFetchAttempted = /* @__PURE__ */ new Set();
4321
+ sock.ev.on("messages.upsert", async (upsert) => {
4322
+ for (const msg of upsert.messages ?? []) {
4323
+ const jid = msg.key?.remoteJid;
4324
+ if (!jid || !jid.endsWith("@g.us")) continue;
4325
+ if (groupFetchAttempted.has(jid)) continue;
4326
+ if (readChatMetadata(account).get(jid)?.source === "group") continue;
4327
+ groupFetchAttempted.add(jid);
4328
+ const startedAt = Date.now();
4329
+ try {
4330
+ const meta = await sock.groupMetadata(jid);
4331
+ console.error(
4332
+ `${TAG16} [wa-chatmeta] op=group-fetch jid=${jid} ms=${Date.now() - startedAt} outcome=ok`
4333
+ );
4334
+ persist(
4335
+ "group-fetch",
4336
+ groupEntriesFromUpdates([{ id: jid, subject: meta.subject }], (/* @__PURE__ */ new Date()).toISOString()),
4337
+ true
4338
+ );
4339
+ } catch (err) {
4340
+ console.error(
4341
+ `${TAG16} [wa-chatmeta] op=group-fetch jid=${jid} ms=${Date.now() - startedAt} outcome=fail reason=${(err instanceof Error ? err.message : String(err)).slice(0, 100)}`
4342
+ );
4343
+ }
4344
+ }
4345
+ });
4346
+ }
4347
+ function listConnections() {
4348
+ return [...connections.values()];
4349
+ }
4125
4350
  function monitorInbound(conn) {
4126
4351
  if (!conn.sock || !onInboundMessage) return;
4127
4352
  const sock = conn.sock;
@@ -4130,8 +4355,8 @@ function monitorInbound(conn) {
4130
4355
  conn.debouncer = createInboundDebouncer({
4131
4356
  debounceMs,
4132
4357
  buildKey: (payload) => {
4133
- const conversationKey = payload.isGroup ? payload.groupJid : payload.senderPhone;
4134
- return `${payload.accountId}:${conversationKey ?? payload.senderPhone}:${payload.senderPhone}`;
4358
+ const conversationKey2 = payload.isGroup ? payload.groupJid : payload.senderPhone;
4359
+ return `${payload.accountId}:${conversationKey2 ?? payload.senderPhone}:${payload.senderPhone}`;
4135
4360
  },
4136
4361
  onFlush: (entries) => {
4137
4362
  if (!onInboundMessage) return;
@@ -4326,7 +4551,38 @@ function monitorInbound(conn) {
4326
4551
  msgKeyId: msg.key.id,
4327
4552
  fromMe,
4328
4553
  senderPhone,
4554
+ // Task 2096 — the sender's wire JID, carried so the reader can
4555
+ // identify a group participant. `senderPhone` above cannot: an
4556
+ // unmapped LID reaches it as bare digits indistinguishable from a
4557
+ // phone number, and anything built from those digits addresses a
4558
+ // different WhatsApp account.
4559
+ //
4560
+ // Inbound only. On a `fromMe` echo the sender is this install, and
4561
+ // `senderJid` above holds the counterparty for a DM (the message's
4562
+ // remoteJid), so passing it would record the wrong person as the
4563
+ // sender of the operator's own message.
4564
+ senderJid: fromMe ? void 0 : senderJid,
4329
4565
  selfPhone: conn.selfPhone,
4566
+ // Task 2096 — the group-outbound audit's discriminator. This cache
4567
+ // holds the ids of sends THIS process made through the outbound
4568
+ // helpers, so a stamp here means the agent path produced the
4569
+ // message; an operator typing on their phone or another linked
4570
+ // device leaves it absent.
4571
+ //
4572
+ // Two routes write their own record synchronously after their send
4573
+ // resolves, under the same messageId and store account: the reader
4574
+ // composer (stamping `operator-manual`) and the agent's own
4575
+ // `/api/whatsapp/reply` (stamping `agent` itself). This expression
4576
+ // is still evaluated for their echoes — every `fromMe` message
4577
+ // reaches it — but `appendMessage` then dedupes on `messageId` and
4578
+ // the value computed here is discarded. So the stamp that survives
4579
+ // for those two is the one their route wrote, and this site is what
4580
+ // covers every other agent send, the document path among them.
4581
+ //
4582
+ // Read-only against the same cache the echo suppression below uses
4583
+ // (a hit does not consume the entry), so the ordering of the two is
4584
+ // free.
4585
+ origin: fromMe && msg.key.id && isAgentSentMessage(msg.key.id) ? "agent" : void 0,
4330
4586
  timestamp: ts,
4331
4587
  pushName: msg.pushName ?? void 0,
4332
4588
  quoted: extracted.quotedMessage ? {
@@ -4449,9 +4705,9 @@ function monitorInbound(conn) {
4449
4705
  });
4450
4706
  console.error(`[whatsapp-reaction] op=subscribed account=${conn.platformAccountId}`);
4451
4707
  sock.ev.on("messages.reaction", async (reactions) => {
4452
- for (const entry of reactions) {
4708
+ for (const entry2 of reactions) {
4453
4709
  try {
4454
- const sig = readReactionEntry(entry);
4710
+ const sig = readReactionEntry(entry2);
4455
4711
  if (!sig) continue;
4456
4712
  if (sig.fromMe) continue;
4457
4713
  const reactorTelephone = await resolveJidToE164(sig.reactorJid, conn.lidMapping) ?? sig.reactorJid;
@@ -4609,6 +4865,9 @@ async function handleInboundMessage(conn, msg) {
4609
4865
  sendReadReceipt(conn.sock, remoteJid, [msg.key.id], isGroup ? senderJid : void 0, conn.accountId);
4610
4866
  }
4611
4867
  let mediaResult;
4868
+ if (extracted.mediaType && extracted.mediaType !== "audio") {
4869
+ onMediaStanza?.(senderPhone, Date.now());
4870
+ }
4612
4871
  if (extracted.mediaType && conn.sock) {
4613
4872
  const maxMb = whatsAppConfig.mediaMaxMb ?? 50;
4614
4873
  mediaResult = await downloadInboundMedia(msg, conn.sock, {
@@ -4619,8 +4878,8 @@ async function handleInboundMessage(conn, msg) {
4619
4878
  }
4620
4879
  }
4621
4880
  if (extracted.mediaType === "audio" && mediaResult?.path) {
4622
- const conversationKey = isGroup ? remoteJid : senderPhone;
4623
- const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
4881
+ const conversationKey2 = isGroup ? remoteJid : senderPhone;
4882
+ const debounceKey = `${conn.accountId}:${conversationKey2}:${senderPhone}`;
4624
4883
  let resolvePending;
4625
4884
  const sttPending = new Promise((resolve6) => {
4626
4885
  resolvePending = resolve6;
@@ -4751,7 +5010,6 @@ export {
4751
5010
  normalizeE164,
4752
5011
  toWhatsappJid,
4753
5012
  jidToE164,
4754
- senderJidFromTelephone,
4755
5013
  isGroupJid,
4756
5014
  isUserJid,
4757
5015
  normalizeTarget,
@@ -4762,6 +5020,7 @@ export {
4762
5020
  managerPhonesForAccount,
4763
5021
  isAdminPhone,
4764
5022
  shouldDispatchInbound,
5023
+ formatSenderPhone,
4765
5024
  checkAccessRateLimit,
4766
5025
  recordAccessFailedAttempt,
4767
5026
  clearAccessRateLimit,
@@ -4780,10 +5039,6 @@ export {
4780
5039
  sendComposing,
4781
5040
  sendPaused,
4782
5041
  sendMediaMessage,
4783
- resolvePlatformAccountId,
4784
- warnNonUuidAccount,
4785
- isHouseManagedService,
4786
- isCustomerTurnSuppressed,
4787
5042
  DATA_ROOT,
4788
5043
  crossesForeignAccountPartition,
4789
5044
  isWithinOwnAccountPartition,
@@ -4791,6 +5046,13 @@ export {
4791
5046
  resolveClaudeUploadsPath,
4792
5047
  sessionScratchpadRoot,
4793
5048
  resolveScratchpadPath,
5049
+ readChatMetadata,
5050
+ writeChatMetadataMerged,
5051
+ groupEntriesFromUpdates,
5052
+ resolvePlatformAccountId,
5053
+ warnNonUuidAccount,
5054
+ isHouseManagedService,
5055
+ isCustomerTurnSuppressed,
4794
5056
  SUPPORTED_MIME_TYPES,
4795
5057
  MAX_FILE_SIZE_BYTES,
4796
5058
  MAX_FILES_PER_MESSAGE,
@@ -4833,5 +5095,6 @@ export {
4833
5095
  getMessages,
4834
5096
  listConversationJids,
4835
5097
  listStoreAccountKeys,
4836
- shutdown
5098
+ shutdown,
5099
+ listConnections
4837
5100
  };