@rubytech/create-sitedesk-code 0.1.522 → 0.1.523

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 (338) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/graph-style/dist/index.d.ts.map +1 -1
  3. package/payload/platform/lib/graph-style/dist/index.js +28 -0
  4. package/payload/platform/lib/graph-style/dist/index.js.map +1 -1
  5. package/payload/platform/lib/graph-style/src/__tests__/caption-coverage.test.ts +7 -0
  6. package/payload/platform/lib/graph-style/src/index.ts +24 -0
  7. package/payload/platform/lib/graph-write/dist/__tests__/merge-on.test.d.ts.map +1 -0
  8. package/payload/platform/lib/graph-write/dist/__tests__/merge-on.test.js.map +1 -0
  9. package/payload/platform/lib/graph-write/dist/index.d.ts +16 -0
  10. package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -1
  11. package/payload/platform/lib/graph-write/dist/index.js +83 -6
  12. package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
  13. package/payload/platform/lib/graph-write/src/index.ts +117 -10
  14. package/payload/platform/lib/routine-templates/dist/index.d.ts +151 -0
  15. package/payload/platform/lib/routine-templates/dist/index.d.ts.map +1 -0
  16. package/payload/platform/lib/routine-templates/dist/index.js +207 -0
  17. package/payload/platform/lib/routine-templates/dist/index.js.map +1 -0
  18. package/payload/platform/lib/routine-templates/dist/roster.d.ts +57 -0
  19. package/payload/platform/lib/routine-templates/dist/roster.d.ts.map +1 -0
  20. package/payload/platform/lib/routine-templates/dist/roster.js +182 -0
  21. package/payload/platform/lib/routine-templates/dist/roster.js.map +1 -0
  22. package/payload/platform/lib/routine-templates/dist/seed-cli.d.ts +2 -0
  23. package/payload/platform/lib/routine-templates/dist/seed-cli.d.ts.map +1 -0
  24. package/payload/platform/lib/routine-templates/dist/seed-cli.js +64 -0
  25. package/payload/platform/lib/routine-templates/dist/seed-cli.js.map +1 -0
  26. package/payload/platform/lib/routine-templates/package.json +6 -0
  27. package/payload/platform/lib/routine-templates/src/__tests__/roster.test.ts +170 -0
  28. package/payload/platform/lib/routine-templates/src/__tests__/seed.test.ts +298 -0
  29. package/payload/platform/lib/routine-templates/src/index.ts +299 -0
  30. package/payload/platform/lib/routine-templates/src/roster.ts +206 -0
  31. package/payload/platform/lib/routine-templates/src/seed-cli.ts +65 -0
  32. package/payload/platform/lib/routine-templates/tsconfig.json +9 -0
  33. package/payload/platform/lib/routine-templates/vitest.config.ts +9 -0
  34. package/payload/platform/neo4j/edge-annotations.json +2 -2
  35. package/payload/platform/neo4j/schema.cypher +25 -1
  36. package/payload/platform/package.json +3 -2
  37. package/payload/platform/plugins/admin/PLUGIN.md +2 -2
  38. package/payload/platform/plugins/admin/mcp/dist/index.js +39 -1
  39. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  40. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +277 -61
  41. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +6 -0
  42. package/payload/platform/plugins/docs/references/admin-ui.md +265 -59
  43. package/payload/platform/plugins/docs/references/memory-guide.md +11 -1
  44. package/payload/platform/plugins/graph/.claude-plugin/plugin.json +1 -1
  45. package/payload/platform/plugins/graph/PLUGIN.md +1 -1
  46. package/payload/platform/plugins/memory/PLUGIN.md +1 -1
  47. package/payload/platform/plugins/memory/mcp/dist/index.js +12 -5
  48. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  49. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-estate-census.test.d.ts +2 -0
  50. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-estate-census.test.d.ts.map +1 -0
  51. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-estate-census.test.js +197 -0
  52. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-estate-census.test.js.map +1 -0
  53. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.js +55 -9
  54. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.js.map +1 -1
  55. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.js +96 -0
  56. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.js.map +1 -1
  57. package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/index.d.ts +7 -0
  58. package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/index.d.ts.map +1 -1
  59. package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-estate-census.d.ts +29 -0
  60. package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-estate-census.d.ts.map +1 -0
  61. package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-estate-census.js +126 -0
  62. package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-estate-census.js.map +1 -0
  63. package/payload/platform/plugins/memory/mcp/dist/scripts/dream-cycle-sweep.js +7 -4
  64. package/payload/platform/plugins/memory/mcp/dist/scripts/dream-cycle-sweep.js.map +1 -1
  65. package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.d.ts +1 -0
  66. package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.d.ts.map +1 -1
  67. package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.js +18 -1
  68. package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.js.map +1 -1
  69. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts +7 -0
  70. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
  71. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +4 -3
  72. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
  73. package/payload/platform/plugins/memory/mcp/vitest.config.ts +2 -0
  74. package/payload/platform/plugins/memory/references/schema-estate-agent.md +2 -2
  75. package/payload/platform/plugins/memory/skills/conversation-archive-enrich/SKILL.md +4 -4
  76. package/payload/platform/plugins/quickbooks/PLUGIN.md +10 -10
  77. package/payload/platform/plugins/scheduling/PLUGIN.md +49 -1
  78. package/payload/platform/plugins/scheduling/mcp/dist/index.js +48 -0
  79. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  80. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/notes-cap.test.d.ts +2 -0
  81. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/notes-cap.test.d.ts.map +1 -0
  82. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/notes-cap.test.js +78 -0
  83. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/notes-cap.test.js.map +1 -0
  84. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-run.test.d.ts +2 -0
  85. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-run.test.d.ts.map +1 -0
  86. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-run.test.js +457 -0
  87. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-run.test.js.map +1 -0
  88. package/payload/platform/plugins/scheduling/mcp/dist/lib/notes-cap.d.ts +35 -0
  89. package/payload/platform/plugins/scheduling/mcp/dist/lib/notes-cap.d.ts.map +1 -0
  90. package/payload/platform/plugins/scheduling/mcp/dist/lib/notes-cap.js +39 -0
  91. package/payload/platform/plugins/scheduling/mcp/dist/lib/notes-cap.js.map +1 -0
  92. package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.d.ts +175 -0
  93. package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.d.ts.map +1 -0
  94. package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.js +533 -0
  95. package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.js.map +1 -0
  96. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js +123 -9
  97. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js.map +1 -1
  98. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/routine-roster-audit.test.d.ts +2 -0
  99. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/routine-roster-audit.test.d.ts.map +1 -0
  100. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/routine-roster-audit.test.js +189 -0
  101. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/routine-roster-audit.test.js.map +1 -0
  102. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/suspension-census.test.d.ts +2 -0
  103. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/suspension-census.test.d.ts.map +1 -0
  104. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/suspension-census.test.js +95 -0
  105. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/suspension-census.test.js.map +1 -0
  106. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/sweep-excludes-suspended.test.d.ts +2 -0
  107. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/sweep-excludes-suspended.test.d.ts.map +1 -0
  108. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/sweep-excludes-suspended.test.js +114 -0
  109. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/sweep-excludes-suspended.test.js.map +1 -0
  110. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts +109 -2
  111. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts.map +1 -1
  112. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js +340 -9
  113. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js.map +1 -1
  114. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.d.ts +9 -1
  115. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.d.ts.map +1 -1
  116. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +210 -45
  117. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
  118. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js +43 -1
  119. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js.map +1 -1
  120. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-get-agent.test.js +55 -7
  121. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-get-agent.test.js.map +1 -1
  122. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-list.test.js +45 -1
  123. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-list.test.js.map +1 -1
  124. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-runs.test.d.ts +2 -0
  125. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-runs.test.d.ts.map +1 -0
  126. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-runs.test.js +96 -0
  127. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-runs.test.js.map +1 -0
  128. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.d.ts.map +1 -1
  129. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.js +2 -1
  130. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.js.map +1 -1
  131. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.d.ts +5 -0
  132. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.d.ts.map +1 -1
  133. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.js +13 -0
  134. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.js.map +1 -1
  135. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.d.ts +4 -0
  136. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.d.ts.map +1 -1
  137. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.js +24 -3
  138. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.js.map +1 -1
  139. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-runs.d.ts +33 -0
  140. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-runs.d.ts.map +1 -0
  141. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-runs.js +51 -0
  142. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-runs.js.map +1 -0
  143. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.d.ts.map +1 -1
  144. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js +12 -2
  145. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js.map +1 -1
  146. package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +30 -2
  147. package/payload/platform/plugins/whatsapp/skills/connect-whatsapp/SKILL.md +1 -1
  148. package/payload/platform/scripts/check-specialist-tool-surface.mjs +92 -16
  149. package/payload/platform/scripts/seed-neo4j.sh +41 -0
  150. package/payload/platform/scripts/verify-skill-tool-surface.sh +91 -2
  151. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  152. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +1 -0
  153. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  154. package/payload/platform/templates/specialists/agents/librarian.md +1 -1
  155. package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
  156. package/payload/server/{chunk-K7CJGZPU.js → chunk-VUXKXOJU.js} +342 -152
  157. package/payload/server/{chunk-UX2FRNO7.js → chunk-WT2PFGY4.js} +37 -265
  158. package/payload/server/{chunk-PXNY2TAP.js → chunk-XCR5D2G3.js} +239 -10
  159. package/payload/server/{manager-RKL7JZ4Y.js → manager-4TJK3YOY.js} +2 -2
  160. package/payload/server/maxy-edge.js +2 -2
  161. package/payload/server/public/activity.html +5 -5
  162. package/payload/server/public/agents.html +4 -4
  163. package/payload/server/public/assets/{AdminLoginScreens-zc2_qqh-.js → AdminLoginScreens-uknqh2Pm.js} +1 -1
  164. package/payload/server/public/assets/AdminLoginScreens-uknqh2Pm.js.br +0 -0
  165. package/payload/server/public/assets/AdminLoginScreens-uknqh2Pm.js.gz +0 -0
  166. package/payload/server/public/assets/AdminShell-GqtMImyQ.js +2 -0
  167. package/payload/server/public/assets/AdminShell-GqtMImyQ.js.br +0 -0
  168. package/payload/server/public/assets/AdminShell-GqtMImyQ.js.gz +0 -0
  169. package/payload/server/public/assets/{activity-DVgO8YuM.js → activity-B4KfMJqO.js} +1 -1
  170. package/payload/server/public/assets/activity-B4KfMJqO.js.br +0 -0
  171. package/payload/server/public/assets/activity-B4KfMJqO.js.gz +0 -0
  172. package/payload/server/public/assets/admin-DxCZeVw0.js +1 -0
  173. package/payload/server/public/assets/admin-DxCZeVw0.js.br +0 -0
  174. package/payload/server/public/assets/admin-DxCZeVw0.js.gz +0 -0
  175. package/payload/server/public/assets/agents-BtwBuO4G.js +1 -0
  176. package/payload/server/public/assets/agents-BtwBuO4G.js.br +0 -0
  177. package/payload/server/public/assets/agents-BtwBuO4G.js.gz +0 -0
  178. package/payload/server/public/assets/{browser-Mw7DY3ft.js → browser-B3hSzI3O.js} +1 -1
  179. package/payload/server/public/assets/browser-B3hSzI3O.js.br +0 -0
  180. package/payload/server/public/assets/browser-B3hSzI3O.js.gz +0 -0
  181. package/payload/server/public/assets/{calendar-B9gvL_1_.js → calendar-BggxXxhS.js} +1 -1
  182. package/payload/server/public/assets/calendar-BggxXxhS.js.br +0 -0
  183. package/payload/server/public/assets/calendar-BggxXxhS.js.gz +0 -0
  184. package/payload/server/public/assets/chat-flivHt0t.js +1 -0
  185. package/payload/server/public/assets/chat-flivHt0t.js.br +0 -0
  186. package/payload/server/public/assets/chat-flivHt0t.js.gz +0 -0
  187. package/payload/server/public/assets/chevron-left-nVRThX7f.js +1 -0
  188. package/payload/server/public/assets/chevron-right-CLqfrkRA.js +1 -0
  189. package/payload/server/public/assets/chevron-right-CLqfrkRA.js.br +0 -0
  190. package/payload/server/public/assets/clock-Dj3SRZKo.js +1 -0
  191. package/payload/server/public/assets/clock-Dj3SRZKo.js.br +0 -0
  192. package/payload/server/public/assets/clock-Dj3SRZKo.js.gz +0 -0
  193. package/payload/server/public/assets/data-Ckjkne54.js +1 -0
  194. package/payload/server/public/assets/data-Ckjkne54.js.br +3 -0
  195. package/payload/server/public/assets/data-Ckjkne54.js.gz +0 -0
  196. package/payload/server/public/assets/{file-text-dYBnXNEX.js → file-text-BKs9eyiw.js} +1 -1
  197. package/payload/server/public/assets/file-text-BKs9eyiw.js.br +0 -0
  198. package/payload/server/public/assets/file-text-BKs9eyiw.js.gz +0 -0
  199. package/payload/server/public/assets/{graph-CDyzd1Et.js → graph-DTi71NaZ.js} +3 -3
  200. package/payload/server/public/assets/graph-DTi71NaZ.js.br +0 -0
  201. package/payload/server/public/assets/graph-DTi71NaZ.js.gz +0 -0
  202. package/payload/server/public/assets/graph-labels-BT3S6e9H.js +1 -0
  203. package/payload/server/public/assets/graph-labels-BT3S6e9H.js.br +0 -0
  204. package/payload/server/public/assets/graph-labels-BT3S6e9H.js.gz +0 -0
  205. package/payload/server/public/assets/{maximize-2-M-iSaznd.js → maximize-2-zC94tsxY.js} +1 -1
  206. package/payload/server/public/assets/maximize-2-zC94tsxY.js.br +0 -0
  207. package/payload/server/public/assets/maximize-2-zC94tsxY.js.gz +0 -0
  208. package/payload/server/public/assets/operator-BtWNEMh-.js +1 -0
  209. package/payload/server/public/assets/operator-BtWNEMh-.js.br +0 -0
  210. package/payload/server/public/assets/operator-BtWNEMh-.js.gz +0 -0
  211. package/payload/server/public/assets/page-BFm91p0e.js +1 -0
  212. package/payload/server/public/assets/page-BFm91p0e.js.br +0 -0
  213. package/payload/server/public/assets/page-BFm91p0e.js.gz +0 -0
  214. package/payload/server/public/assets/{page-CZ-73FAk.js → page-Ccl0FazM.js} +3 -3
  215. package/payload/server/public/assets/page-Ccl0FazM.js.br +0 -0
  216. package/payload/server/public/assets/page-Ccl0FazM.js.gz +0 -0
  217. package/payload/server/public/assets/{public-CN-ltC8x.js → public-BpC6cn9S.js} +1 -1
  218. package/payload/server/public/assets/public-BpC6cn9S.js.br +0 -0
  219. package/payload/server/public/assets/public-BpC6cn9S.js.gz +0 -0
  220. package/payload/server/public/assets/{rotate-ccw-Buopsc22.js → rotate-ccw-BNcqbHkZ.js} +1 -1
  221. package/payload/server/public/assets/rotate-ccw-BNcqbHkZ.js.br +0 -0
  222. package/payload/server/public/assets/rotate-ccw-BNcqbHkZ.js.gz +0 -0
  223. package/payload/server/public/assets/routines-D5iia-XG.js +1 -0
  224. package/payload/server/public/assets/routines-D5iia-XG.js.br +0 -0
  225. package/payload/server/public/assets/routines-D5iia-XG.js.gz +0 -0
  226. package/payload/server/public/assets/{skills-DiiuVzDK.js → skills-COLAw_jt.js} +1 -1
  227. package/payload/server/public/assets/skills-COLAw_jt.js.br +0 -0
  228. package/payload/server/public/assets/skills-COLAw_jt.js.gz +0 -0
  229. package/payload/server/public/assets/tasks-CZDQI4nB.js +1 -0
  230. package/payload/server/public/assets/tasks-CZDQI4nB.js.br +0 -0
  231. package/payload/server/public/assets/tasks-CZDQI4nB.js.gz +0 -0
  232. package/payload/server/public/assets/{time-entry-format-BBokCF-D.js → time-entry-format-laTFzawg.js} +1 -1
  233. package/payload/server/public/assets/time-entry-format-laTFzawg.js.br +0 -0
  234. package/payload/server/public/assets/time-entry-format-laTFzawg.js.gz +0 -0
  235. package/payload/server/public/assets/{triangle-alert-C_AcpTW5.js → triangle-alert-DrwqhEog.js} +1 -1
  236. package/payload/server/public/assets/triangle-alert-DrwqhEog.js.br +0 -0
  237. package/payload/server/public/assets/triangle-alert-DrwqhEog.js.gz +0 -0
  238. package/payload/server/public/assets/{useCopyFeedback-TdimmPVR.js → useCopyFeedback-Cxme5GK6.js} +1 -1
  239. package/payload/server/public/assets/useCopyFeedback-Cxme5GK6.js.br +0 -0
  240. package/payload/server/public/assets/useCopyFeedback-Cxme5GK6.js.gz +0 -0
  241. package/payload/server/public/assets/useSubAccountSwitcher-B-K8y5a4.css +1 -0
  242. package/payload/server/public/assets/useSubAccountSwitcher-B-K8y5a4.css.br +0 -0
  243. package/payload/server/public/assets/useSubAccountSwitcher-B-K8y5a4.css.gz +0 -0
  244. package/payload/server/public/assets/useSubAccountSwitcher-C_1E11Dv.js +14 -0
  245. package/payload/server/public/assets/useSubAccountSwitcher-C_1E11Dv.js.br +0 -0
  246. package/payload/server/public/assets/useSubAccountSwitcher-C_1E11Dv.js.gz +0 -0
  247. package/payload/server/public/assets/{useVoiceRecorder-BT2738ZS.js → useVoiceRecorder-DUBpVa2T.js} +1 -1
  248. package/payload/server/public/assets/useVoiceRecorder-DUBpVa2T.js.br +0 -0
  249. package/payload/server/public/assets/useVoiceRecorder-DUBpVa2T.js.gz +0 -0
  250. package/payload/server/public/assets/{wrench-B51s5kGA.js → wrench-DWuxPnzF.js} +1 -1
  251. package/payload/server/public/assets/wrench-DWuxPnzF.js.br +0 -0
  252. package/payload/server/public/assets/wrench-DWuxPnzF.js.gz +0 -0
  253. package/payload/server/public/browser.html +4 -4
  254. package/payload/server/public/calendar.html +7 -7
  255. package/payload/server/public/chat.html +13 -13
  256. package/payload/server/public/data.html +11 -11
  257. package/payload/server/public/graph.html +9 -9
  258. package/payload/server/public/index.html +14 -14
  259. package/payload/server/public/operator.html +14 -14
  260. package/payload/server/public/public.html +13 -13
  261. package/payload/server/public/routines.html +6 -6
  262. package/payload/server/public/skills.html +5 -5
  263. package/payload/server/public/tasks.html +6 -6
  264. package/payload/server/server.js +5910 -5369
  265. package/payload/server/public/assets/AdminLoginScreens-zc2_qqh-.js.br +0 -0
  266. package/payload/server/public/assets/AdminLoginScreens-zc2_qqh-.js.gz +0 -0
  267. package/payload/server/public/assets/AdminShell-IGUdTx5q.js +0 -2
  268. package/payload/server/public/assets/AdminShell-IGUdTx5q.js.br +0 -0
  269. package/payload/server/public/assets/AdminShell-IGUdTx5q.js.gz +0 -0
  270. package/payload/server/public/assets/activity-DVgO8YuM.js.br +0 -0
  271. package/payload/server/public/assets/activity-DVgO8YuM.js.gz +0 -0
  272. package/payload/server/public/assets/admin-xtpM7fgl.js +0 -1
  273. package/payload/server/public/assets/admin-xtpM7fgl.js.br +0 -0
  274. package/payload/server/public/assets/admin-xtpM7fgl.js.gz +0 -0
  275. package/payload/server/public/assets/agents-P0nTIxo-.js +0 -1
  276. package/payload/server/public/assets/agents-P0nTIxo-.js.br +0 -0
  277. package/payload/server/public/assets/agents-P0nTIxo-.js.gz +0 -0
  278. package/payload/server/public/assets/browser-Mw7DY3ft.js.br +0 -0
  279. package/payload/server/public/assets/browser-Mw7DY3ft.js.gz +0 -0
  280. package/payload/server/public/assets/calendar-B9gvL_1_.js.br +0 -0
  281. package/payload/server/public/assets/calendar-B9gvL_1_.js.gz +0 -0
  282. package/payload/server/public/assets/chat-DBGqh4wg.js +0 -1
  283. package/payload/server/public/assets/chat-DBGqh4wg.js.br +0 -2
  284. package/payload/server/public/assets/chat-DBGqh4wg.js.gz +0 -0
  285. package/payload/server/public/assets/chevron-left-Bl4vpOM0.js +0 -1
  286. package/payload/server/public/assets/chevron-left-Bl4vpOM0.js.br +0 -0
  287. package/payload/server/public/assets/chevron-right-BExigCnp.js +0 -1
  288. package/payload/server/public/assets/chevron-right-BExigCnp.js.br +0 -0
  289. package/payload/server/public/assets/clock-D8TJvgJC.js +0 -1
  290. package/payload/server/public/assets/clock-D8TJvgJC.js.br +0 -0
  291. package/payload/server/public/assets/data-HfFSxaC2.js +0 -1
  292. package/payload/server/public/assets/data-HfFSxaC2.js.br +0 -0
  293. package/payload/server/public/assets/data-HfFSxaC2.js.gz +0 -0
  294. package/payload/server/public/assets/file-text-dYBnXNEX.js.br +0 -0
  295. package/payload/server/public/assets/file-text-dYBnXNEX.js.gz +0 -0
  296. package/payload/server/public/assets/graph-CDyzd1Et.js.br +0 -0
  297. package/payload/server/public/assets/graph-CDyzd1Et.js.gz +0 -0
  298. package/payload/server/public/assets/graph-labels-D1LkKiJr.js +0 -1
  299. package/payload/server/public/assets/graph-labels-D1LkKiJr.js.br +0 -0
  300. package/payload/server/public/assets/graph-labels-D1LkKiJr.js.gz +0 -0
  301. package/payload/server/public/assets/maximize-2-M-iSaznd.js.br +0 -0
  302. package/payload/server/public/assets/maximize-2-M-iSaznd.js.gz +0 -0
  303. package/payload/server/public/assets/operator-9jfCv1Fg.js +0 -1
  304. package/payload/server/public/assets/operator-9jfCv1Fg.js.br +0 -0
  305. package/payload/server/public/assets/operator-9jfCv1Fg.js.gz +0 -0
  306. package/payload/server/public/assets/page-CZ-73FAk.js.br +0 -0
  307. package/payload/server/public/assets/page-CZ-73FAk.js.gz +0 -0
  308. package/payload/server/public/assets/page-CjwSt7kx.js +0 -1
  309. package/payload/server/public/assets/page-CjwSt7kx.js.br +0 -0
  310. package/payload/server/public/assets/page-CjwSt7kx.js.gz +0 -0
  311. package/payload/server/public/assets/public-CN-ltC8x.js.br +0 -0
  312. package/payload/server/public/assets/public-CN-ltC8x.js.gz +0 -0
  313. package/payload/server/public/assets/rotate-ccw-Buopsc22.js.br +0 -0
  314. package/payload/server/public/assets/rotate-ccw-Buopsc22.js.gz +0 -0
  315. package/payload/server/public/assets/routines-C-yr8ddT.js +0 -1
  316. package/payload/server/public/assets/routines-C-yr8ddT.js.br +0 -0
  317. package/payload/server/public/assets/routines-C-yr8ddT.js.gz +0 -0
  318. package/payload/server/public/assets/skills-DiiuVzDK.js.br +0 -0
  319. package/payload/server/public/assets/skills-DiiuVzDK.js.gz +0 -0
  320. package/payload/server/public/assets/tasks-CnHIfn2_.js +0 -1
  321. package/payload/server/public/assets/tasks-CnHIfn2_.js.br +0 -0
  322. package/payload/server/public/assets/tasks-CnHIfn2_.js.gz +0 -0
  323. package/payload/server/public/assets/time-entry-format-BBokCF-D.js.br +0 -0
  324. package/payload/server/public/assets/time-entry-format-BBokCF-D.js.gz +0 -0
  325. package/payload/server/public/assets/triangle-alert-C_AcpTW5.js.br +0 -0
  326. package/payload/server/public/assets/triangle-alert-C_AcpTW5.js.gz +0 -0
  327. package/payload/server/public/assets/useCopyFeedback-TdimmPVR.js.br +0 -0
  328. package/payload/server/public/assets/useCopyFeedback-TdimmPVR.js.gz +0 -0
  329. package/payload/server/public/assets/useSubAccountSwitcher-BwHqV55p.js +0 -14
  330. package/payload/server/public/assets/useSubAccountSwitcher-BwHqV55p.js.br +0 -0
  331. package/payload/server/public/assets/useSubAccountSwitcher-BwHqV55p.js.gz +0 -0
  332. package/payload/server/public/assets/useSubAccountSwitcher-CsrPYqtm.css +0 -1
  333. package/payload/server/public/assets/useSubAccountSwitcher-CsrPYqtm.css.br +0 -0
  334. package/payload/server/public/assets/useSubAccountSwitcher-CsrPYqtm.css.gz +0 -0
  335. package/payload/server/public/assets/useVoiceRecorder-BT2738ZS.js.br +0 -0
  336. package/payload/server/public/assets/useVoiceRecorder-BT2738ZS.js.gz +0 -0
  337. package/payload/server/public/assets/wrench-B51s5kGA.js.br +0 -0
  338. package/payload/server/public/assets/wrench-B51s5kGA.js.gz +0 -0
@@ -6,8 +6,9 @@ import {
6
6
  ensureConversation,
7
7
  getSession,
8
8
  getUserTimezone,
9
- isValidAccountId
10
- } from "./chunk-PXNY2TAP.js";
9
+ isValidAccountId,
10
+ registerLoop
11
+ } from "./chunk-XCR5D2G3.js";
11
12
 
12
13
  // app/lib/whatsapp/schema.ts
13
14
  import { z } from "zod";
@@ -257,7 +258,6 @@ import { inspect } from "util";
257
258
  import {
258
259
  Browsers,
259
260
  DisconnectReason,
260
- fetchLatestBaileysVersion,
261
261
  makeCacheableSignalKeyStore,
262
262
  makeWASocket,
263
263
  useMultiFileAuthState
@@ -318,6 +318,18 @@ function resolveDebugWindow(authDir, nowMs = Date.now()) {
318
318
  return state;
319
319
  }
320
320
 
321
+ // app/lib/whatsapp/companion-reg.ts
322
+ var PRIMARY_IDENTITY_PUB = "primary_identity_pub";
323
+ function classifyCompanionReg(node) {
324
+ const outer = node?.content;
325
+ const child = Array.isArray(outer) ? outer.find((c) => c?.tag === "link_code_companion_reg") : void 0;
326
+ const rawStage = child?.attrs?.stage;
327
+ const stage = typeof rawStage === "string" && rawStage ? rawStage : "absent";
328
+ const inner = child?.content;
329
+ const children = Array.isArray(inner) ? inner.map((c) => c?.tag).filter((t) => typeof t === "string") : [];
330
+ return { stage, children, parseable: children.includes(PRIMARY_IDENTITY_PUB) };
331
+ }
332
+
321
333
  // app/lib/whatsapp/ingest-ledger.ts
322
334
  import { existsSync, mkdirSync, appendFileSync, readFileSync, writeFileSync, readdirSync } from "fs";
323
335
  import { join } from "path";
@@ -471,8 +483,102 @@ function listLedgerAccounts() {
471
483
  }
472
484
  }
473
485
 
486
+ // app/lib/whatsapp/version-census.ts
487
+ import { DEFAULT_CONNECTION_CONFIG, fetchLatestBaileysVersion } from "@whiskeysockets/baileys";
488
+ var TAG4 = "[whatsapp:version]";
489
+ var BAILEYS_VERSION_URL = "https://raw.githubusercontent.com/WhiskeySockets/Baileys/master/src/Defaults/index.ts";
490
+ var VERSION_RE = /const version = \[(\d+),\s*(\d+),\s*(\d+)\]/;
491
+ var presented = /* @__PURE__ */ new Map();
492
+ var behindSince = null;
493
+ function packagedVersion() {
494
+ return DEFAULT_CONNECTION_CONFIG.version.join(".");
495
+ }
496
+ function messageOf(err) {
497
+ return err instanceof Error ? err.message : String(err);
498
+ }
499
+ var PROBE_TIMEOUT_MS = 5e3;
500
+ async function probeSourceAge(fetchImpl) {
501
+ const none = (probe) => ({ sourceAgeSec: null, edge: null, probe });
502
+ try {
503
+ const res = await fetchImpl(BAILEYS_VERSION_URL, {
504
+ method: "HEAD",
505
+ signal: AbortSignal.timeout(PROBE_TIMEOUT_MS)
506
+ });
507
+ if (!res.ok) return none(`http-${res.status}`);
508
+ const header = res.headers.get("age") !== null ? "age" : "source-age";
509
+ const raw = res.headers.get(header);
510
+ const n = raw === null ? Number.NaN : Number.parseInt(raw, 10);
511
+ if (!Number.isFinite(n)) return none("no-header");
512
+ return { sourceAgeSec: n, edge: res.headers.get("x-served-by"), probe: header };
513
+ } catch (err) {
514
+ return none(err instanceof Error && err.name === "TimeoutError" ? "timeout" : "net-error");
515
+ }
516
+ }
517
+ async function resolveWaVersion(opts) {
518
+ const { account } = opts;
519
+ const fetchImpl = opts.fetchImpl ?? fetch;
520
+ const fetchVersion = opts.fetchVersion ?? fetchLatestBaileysVersion;
521
+ const now = opts.now ?? Date.now;
522
+ const timed = (async () => {
523
+ const t0 = now();
524
+ const r2 = await fetchVersion();
525
+ return { r: r2, ms: now() - t0 };
526
+ })();
527
+ const [{ r, ms }, probe] = await Promise.all([timed, probeSourceAge(fetchImpl)]);
528
+ const err = r.error;
529
+ const origin = r.isLatest === false || err !== void 0 ? "packaged-fallback" : "fetched";
530
+ const versionStr = r.version.join(".");
531
+ const packaged = packagedVersion();
532
+ presented.set(account, versionStr);
533
+ console.error(
534
+ `${TAG4} op=resolve account=${account} version=${versionStr} origin=${origin} sourceAgeSec=${probe.sourceAgeSec ?? "unknown"} probe=${probe.probe} edge=${probe.edge ?? "unknown"} packaged=${packaged} matchesPackaged=${versionStr === packaged} fetchMs=${ms} error=${err === void 0 ? "none" : `"${messageOf(err)}"`}`
535
+ );
536
+ return { version: r.version };
537
+ }
538
+ var CENSUS_INTERVAL_MS = 15 * 60 * 1e3;
539
+ async function runVersionDriftCensus(opts = {}) {
540
+ const fetchImpl = opts.fetchImpl ?? fetch;
541
+ const now = opts.now ?? (() => /* @__PURE__ */ new Date());
542
+ let upstream = null;
543
+ let reason = "net-error";
544
+ try {
545
+ const res = await fetchImpl(BAILEYS_VERSION_URL);
546
+ if (!res.ok) {
547
+ reason = `http-${res.status}`;
548
+ } else {
549
+ const m = VERSION_RE.exec(await res.text());
550
+ if (m) upstream = `${m[1]}.${m[2]}.${m[3]}`;
551
+ else reason = "parse-failed";
552
+ }
553
+ } catch {
554
+ upstream = null;
555
+ reason = "net-error";
556
+ }
557
+ const distinct = [...new Set(presented.values())].sort();
558
+ const presentedStr = distinct.length === 0 ? "none" : distinct.join(",");
559
+ if (upstream !== null && distinct.length > 0) {
560
+ if (distinct.some((v) => v !== upstream)) {
561
+ if (behindSince === null) behindSince = now().toISOString();
562
+ } else {
563
+ behindSince = null;
564
+ }
565
+ }
566
+ console.error(
567
+ `${TAG4} op=drift-census presented=${presentedStr} upstream=${upstream ?? reason} behindSince=${behindSince ?? "none"}`
568
+ );
569
+ return `presented=${presentedStr} upstream=${upstream ?? reason}`;
570
+ }
571
+ function startVersionDriftCensus(intervalMs = CENSUS_INTERVAL_MS) {
572
+ return registerLoop({
573
+ name: "whatsapp-version-drift",
574
+ intervalMs,
575
+ firstRunDelayMs: 0,
576
+ run: () => runVersionDriftCensus()
577
+ });
578
+ }
579
+
474
580
  // app/lib/whatsapp/session.ts
475
- var TAG4 = "[whatsapp:session]";
581
+ var TAG5 = "[whatsapp:session]";
476
582
  var WIRE_TAG = "[whatsapp:wire]";
477
583
  var baileysPkgVersion = (() => {
478
584
  try {
@@ -482,7 +588,7 @@ var baileysPkgVersion = (() => {
482
588
  return "unknown";
483
589
  }
484
590
  })();
485
- console.error(`${TAG4} baileys package loaded version=${baileysPkgVersion}`);
591
+ console.error(`${TAG5} baileys package loaded version=${baileysPkgVersion}`);
486
592
  var __BAILEYS_TAG = "[whatsapp:baileys]";
487
593
  var INSPECT_OPTS = {
488
594
  depth: null,
@@ -543,7 +649,7 @@ function createBaileysLogger(bindings = {}, authDir) {
543
649
  }
544
650
  var credsSaveQueue = Promise.resolve();
545
651
  async function drainCredsSaveQueue(timeoutMs = 5e3) {
546
- console.error(`${TAG4} draining credential save queue\u2026`);
652
+ console.error(`${TAG5} draining credential save queue\u2026`);
547
653
  const timer = new Promise(
548
654
  (resolve6) => setTimeout(() => resolve6("timeout"), timeoutMs)
549
655
  );
@@ -552,9 +658,9 @@ async function drainCredsSaveQueue(timeoutMs = 5e3) {
552
658
  timer
553
659
  ]);
554
660
  if (result === "timeout") {
555
- console.error(`${TAG4} credential save queue drain timed out after ${timeoutMs}ms`);
661
+ console.error(`${TAG5} credential save queue drain timed out after ${timeoutMs}ms`);
556
662
  } else {
557
- console.error(`${TAG4} credential save queue drained`);
663
+ console.error(`${TAG5} credential save queue drained`);
558
664
  }
559
665
  }
560
666
  function readCredsJsonRaw2(filePath) {
@@ -577,11 +683,11 @@ async function safeSaveCreds(authDir, saveCreds) {
577
683
  JSON.parse(raw);
578
684
  fsSync2.copyFileSync(credsPath, backupPath);
579
685
  } catch (err) {
580
- console.warn(`${TAG4} backup pre-copy failed (parse or write) authDir=${authDir}: ${String(err)}`);
686
+ console.warn(`${TAG5} backup pre-copy failed (parse or write) authDir=${authDir}: ${String(err)}`);
581
687
  }
582
688
  }
583
689
  } catch (err) {
584
- console.warn(`${TAG4} backup preparation failed authDir=${authDir}: ${String(err)}`);
690
+ console.warn(`${TAG5} backup preparation failed authDir=${authDir}: ${String(err)}`);
585
691
  }
586
692
  try {
587
693
  await Promise.resolve(saveCreds());
@@ -589,39 +695,39 @@ async function safeSaveCreds(authDir, saveCreds) {
589
695
  try {
590
696
  const stats = await fs3.stat(credsPath);
591
697
  if (stats.size <= 1) {
592
- console.error(`${TAG4} WARNING: creds file empty/truncated after save path=${credsPath} size=${stats.size}`);
698
+ console.error(`${TAG5} WARNING: creds file empty/truncated after save path=${credsPath} size=${stats.size}`);
593
699
  } else {
594
- console.error(`${TAG4} creds saved and verified path=${credsPath} size=${stats.size}`);
700
+ console.error(`${TAG5} creds saved and verified path=${credsPath} size=${stats.size}`);
595
701
  }
596
702
  } catch (statErr) {
597
- console.error(`${TAG4} creds NOT found on disk after save path=${credsPath}: ${String(statErr)}`);
703
+ console.error(`${TAG5} creds NOT found on disk after save path=${credsPath}: ${String(statErr)}`);
598
704
  }
599
705
  } catch (err) {
600
- console.error(`${TAG4} failed saving creds to ${authDir}: ${String(err)}`);
706
+ console.error(`${TAG5} failed saving creds to ${authDir}: ${String(err)}`);
601
707
  }
602
708
  }
603
709
  function enqueueSaveCreds(authDir, saveCreds) {
604
710
  credsSaveQueue = credsSaveQueue.then(() => safeSaveCreds(authDir, saveCreds)).catch((err) => {
605
- console.error(`${TAG4} creds save queue error: ${String(err)}`);
711
+ console.error(`${TAG5} creds save queue error: ${String(err)}`);
606
712
  });
607
713
  }
608
714
  async function createWaSocket(opts) {
609
- const { authDir, onQr, onConnectionUpdate, silent, correlationId, account, getCodeIssuedTs } = opts;
715
+ const { authDir, onQr, onConnectionUpdate, silent, correlationId, account, getCodeIssuedTs, onCompanionReg } = opts;
610
716
  const cid = correlationId ?? "none";
611
717
  const acct = account ?? "unknown";
612
718
  await fs3.mkdir(authDir, { recursive: true });
613
719
  maybeRestoreCredsFromBackup(authDir);
614
720
  const { state, saveCreds } = await useMultiFileAuthState(authDir);
615
- const { version } = await fetchLatestBaileysVersion();
721
+ const { version } = await resolveWaVersion({ account: acct });
616
722
  if (!silent) {
617
723
  const preCensus = credsCensus(authDir);
618
724
  console.error(
619
- `${TAG4} op=creds-pre cid=${cid} account=${acct} credsPresent=${preCensus.credsPresent} registered=${preCensus.registered} hasAccount=${preCensus.hasAccountSignature}`
725
+ `${TAG5} op=creds-pre cid=${cid} account=${acct} credsPresent=${preCensus.credsPresent} registered=${preCensus.registered} hasAccount=${preCensus.hasAccountSignature}`
620
726
  );
621
727
  }
622
728
  const openTs = Date.now();
623
729
  console.error(
624
- `${TAG4} op=socket-open cid=${cid} account=${acct} baileysVersion=${baileysPkgVersion} waProtocolVersion=${version.join(".")} openTs=${openTs}`
730
+ `${TAG5} op=socket-open cid=${cid} account=${acct} baileysVersion=${baileysPkgVersion} waProtocolVersion=${version.join(".")} openTs=${openTs}`
625
731
  );
626
732
  const baileysLogger = createBaileysLogger({}, authDir);
627
733
  const sock = makeWASocket({
@@ -638,7 +744,7 @@ async function createWaSocket(opts) {
638
744
  fireInitQueries: false
639
745
  });
640
746
  sock.ev.on("creds.update", () => {
641
- console.error(`${TAG4} creds.update received \u2014 saving to ${authDir}`);
747
+ console.error(`${TAG5} creds.update received \u2014 saving to ${authDir}`);
642
748
  enqueueSaveCreds(authDir, saveCreds);
643
749
  });
644
750
  let qrSequence = 0;
@@ -658,14 +764,14 @@ async function createWaSocket(opts) {
658
764
  parts.push(`disconnectReason=${formatError(update.lastDisconnect.error)}`);
659
765
  }
660
766
  if (!silent && parts.length > 0) {
661
- console.error(`${TAG4} connection.update ${parts.join(" ")}`);
767
+ console.error(`${TAG5} connection.update ${parts.join(" ")}`);
662
768
  }
663
769
  if (update.qr && !silent) {
664
770
  const now = Date.now();
665
771
  const sinceLastRefMs = lastQrTs ? now - lastQrTs : null;
666
772
  lastQrTs = now;
667
773
  console.error(
668
- `${TAG4} op=qr cid=${cid} qr=#${qrSequence} sinceOpenMs=${now - openTs} sinceLastRefMs=${sinceLastRefMs ?? "n/a"}`
774
+ `${TAG5} op=qr cid=${cid} qr=#${qrSequence} sinceOpenMs=${now - openTs} sinceLastRefMs=${sinceLastRefMs ?? "n/a"}`
669
775
  );
670
776
  }
671
777
  if (update.isNewLogin === true && !pairSuccessLogged && !silent) {
@@ -674,7 +780,7 @@ async function createWaSocket(opts) {
674
780
  const sinceCodeIssuedMs = codeIssuedTs ? Date.now() - codeIssuedTs : null;
675
781
  const afterCensus = credsCensus(authDir);
676
782
  console.error(
677
- `${TAG4} op=pair-success cid=${cid} account=${acct} sinceCodeIssuedMs=${sinceCodeIssuedMs ?? "n/a"} hasAccountAfter=${afterCensus.hasAccountSignature}`
783
+ `${TAG5} op=pair-success cid=${cid} account=${acct} sinceCodeIssuedMs=${sinceCodeIssuedMs ?? "n/a"} hasAccountAfter=${afterCensus.hasAccountSignature}`
678
784
  );
679
785
  }
680
786
  if (update.qr && onQr) {
@@ -684,25 +790,25 @@ async function createWaSocket(opts) {
684
790
  const status = getStatusCode(update.lastDisconnect?.error);
685
791
  if (!silent) {
686
792
  console.error(
687
- `${TAG4} op=close cid=${cid} account=${acct} status=${status ?? "unknown"} reason="${formatError(update.lastDisconnect?.error)}" socketLifeMs=${Date.now() - openTs} kind=${closeKind(status, everOpen)}`
793
+ `${TAG5} op=close cid=${cid} account=${acct} status=${status ?? "unknown"} reason="${formatError(update.lastDisconnect?.error)}" socketLifeMs=${Date.now() - openTs} kind=${closeKind(status, everOpen)}`
688
794
  );
689
795
  }
690
796
  if (status === DisconnectReason.loggedOut && !silent) {
691
- console.error(`${TAG4} session logged out (401) \u2014 re-link required`);
797
+ console.error(`${TAG5} session logged out (401) \u2014 re-link required`);
692
798
  }
693
799
  }
694
800
  if (update.connection === "open") {
695
801
  everOpen = true;
696
- if (!silent) console.error(`${TAG4} connected`);
802
+ if (!silent) console.error(`${TAG5} connected`);
697
803
  }
698
804
  onConnectionUpdate?.(update);
699
805
  } catch (err) {
700
- console.error(`${TAG4} connection.update handler error: ${String(err)}`);
806
+ console.error(`${TAG5} connection.update handler error: ${String(err)}`);
701
807
  }
702
808
  });
703
809
  if (sock.ws && typeof sock.ws.on === "function") {
704
810
  sock.ws.on("error", (err) => {
705
- console.error(`${TAG4} WebSocket error: ${String(err)}`);
811
+ console.error(`${TAG5} WebSocket error: ${String(err)}`);
706
812
  });
707
813
  sock.ws.on("CB:message", (node) => {
708
814
  const attrs = node?.attrs ?? {};
@@ -718,6 +824,21 @@ async function createWaSocket(opts) {
718
824
  });
719
825
  }
720
826
  });
827
+ if (!silent) {
828
+ sock.ws.on("CB:notification,type:link_code_companion_reg", (node) => {
829
+ try {
830
+ const census = classifyCompanionReg(node);
831
+ console.error(
832
+ `${TAG5} op=companion-reg cid=${cid} account=${acct} stage=${census.stage} children=${census.children.join(",") || "none"} parseable=${census.parseable}`
833
+ );
834
+ onCompanionReg?.(census);
835
+ } catch (err) {
836
+ console.error(
837
+ `${TAG5} op=companion-reg-observer-error cid=${cid} account=${acct} err=${String(err)}`
838
+ );
839
+ }
840
+ });
841
+ }
721
842
  }
722
843
  return sock;
723
844
  }
@@ -826,6 +947,26 @@ function computeBackoff(attempt, config = {}) {
826
947
  return Math.round(jitter);
827
948
  }
828
949
 
950
+ // app/lib/whatsapp/link-census.ts
951
+ var HEALTH_TAG = "[whatsapp-health]";
952
+ var LINK_CENSUS_INTERVAL_MS = 15 * 6e4;
953
+ function downMsFor(row, now) {
954
+ if (row.connected) return "0";
955
+ if (row.lastConnectedAt === void 0) return "unknown";
956
+ return String(now - row.lastConnectedAt);
957
+ }
958
+ function linkCensusLines(rows, now) {
959
+ if (rows.length === 0) return [`${HEALTH_TAG} op=link-census accounts=0`];
960
+ return rows.map(
961
+ (row) => `${HEALTH_TAG} op=link-census account=${row.accountId} connected=${row.connected} stuck=${Boolean(row.sessionStuckReason)} downMs=${downMsFor(row, now)} lastError=${row.lastError ?? "none"}`
962
+ );
963
+ }
964
+ function runLinkCensus(read, now) {
965
+ const rows = read();
966
+ for (const line of linkCensusLines(rows, now)) console.error(line);
967
+ return rows.length;
968
+ }
969
+
829
970
  // app/lib/whatsapp/init-queries.ts
830
971
  import { inspect as inspect2 } from "util";
831
972
 
@@ -855,7 +996,7 @@ function withTimeout(label, promise, timeoutMs) {
855
996
  }
856
997
 
857
998
  // app/lib/whatsapp/init-queries.ts
858
- var TAG5 = "[whatsapp:init]";
999
+ var TAG6 = "[whatsapp:init]";
859
1000
  var DEFAULT_QUERY_TIMEOUT_MS = 15e3;
860
1001
  var INSPECT_OPTS2 = {
861
1002
  depth: null,
@@ -883,20 +1024,20 @@ async function runInitQueries(sock, ctx) {
883
1024
  elapsedMs: 0
884
1025
  };
885
1026
  console.error(
886
- `${TAG5} runInitQueries start account=${ctx.accountId} attempt=${ctx.attempt} selfPhone=${ctx.selfPhone ?? "unknown"} authDir=${ctx.authDir} queryTimeoutMs=${queryTimeoutMs}`
1027
+ `${TAG6} runInitQueries start account=${ctx.accountId} attempt=${ctx.attempt} selfPhone=${ctx.selfPhone ?? "unknown"} authDir=${ctx.authDir} queryTimeoutMs=${queryTimeoutMs}`
887
1028
  );
888
1029
  console.error(
889
- `${TAG5} fetchProps SKIPPED account=${ctx.accountId} reason=no-platform-consumer`
1030
+ `${TAG6} fetchProps SKIPPED account=${ctx.accountId} reason=no-platform-consumer`
890
1031
  );
891
1032
  if (ctx.signal?.aborted) {
892
1033
  result.aborted = true;
893
1034
  result.elapsedMs = Date.now() - startedAt;
894
1035
  console.error(
895
- `${TAG5} runInitQueries aborted before start account=${ctx.accountId}`
1036
+ `${TAG6} runInitQueries aborted before start account=${ctx.accountId}`
896
1037
  );
897
1038
  return result;
898
1039
  }
899
- console.error(`${TAG5} fetchBlocklist start account=${ctx.accountId}`);
1040
+ console.error(`${TAG6} fetchBlocklist start account=${ctx.accountId}`);
900
1041
  try {
901
1042
  const blocklist = await withTimeout(
902
1043
  "fetchBlocklist",
@@ -906,24 +1047,24 @@ async function runInitQueries(sock, ctx) {
906
1047
  const count = Array.isArray(blocklist) ? blocklist.length : 0;
907
1048
  result.blocklistOk = true;
908
1049
  console.error(
909
- `${TAG5} fetchBlocklist ok account=${ctx.accountId} count=${count}`
1050
+ `${TAG6} fetchBlocklist ok account=${ctx.accountId} count=${count}`
910
1051
  );
911
1052
  } catch (err) {
912
1053
  const formatted = formatErr(err);
913
1054
  result.errors.blocklist = formatted;
914
1055
  console.error(
915
- `${TAG5} fetchBlocklist FAILED account=${ctx.accountId}: ${formatted}`
1056
+ `${TAG6} fetchBlocklist FAILED account=${ctx.accountId}: ${formatted}`
916
1057
  );
917
1058
  }
918
1059
  if (ctx.signal?.aborted) {
919
1060
  result.aborted = true;
920
1061
  result.elapsedMs = Date.now() - startedAt;
921
1062
  console.error(
922
- `${TAG5} runInitQueries aborted mid-sequence account=${ctx.accountId} after=fetchBlocklist`
1063
+ `${TAG6} runInitQueries aborted mid-sequence account=${ctx.accountId} after=fetchBlocklist`
923
1064
  );
924
1065
  return result;
925
1066
  }
926
- console.error(`${TAG5} fetchPrivacySettings start account=${ctx.accountId}`);
1067
+ console.error(`${TAG6} fetchPrivacySettings start account=${ctx.accountId}`);
927
1068
  try {
928
1069
  const settings = await withTimeout(
929
1070
  "fetchPrivacySettings",
@@ -933,18 +1074,18 @@ async function runInitQueries(sock, ctx) {
933
1074
  );
934
1075
  result.privacySettingsOk = true;
935
1076
  console.error(
936
- `${TAG5} fetchPrivacySettings ok account=${ctx.accountId} keys=${settings ? Object.keys(settings).join(",") : ""}`
1077
+ `${TAG6} fetchPrivacySettings ok account=${ctx.accountId} keys=${settings ? Object.keys(settings).join(",") : ""}`
937
1078
  );
938
1079
  } catch (err) {
939
1080
  const formatted = formatErr(err);
940
1081
  result.errors.privacySettings = formatted;
941
1082
  console.error(
942
- `${TAG5} fetchPrivacySettings FAILED account=${ctx.accountId}: ${formatted}`
1083
+ `${TAG6} fetchPrivacySettings FAILED account=${ctx.accountId}: ${formatted}`
943
1084
  );
944
1085
  }
945
1086
  result.elapsedMs = Date.now() - startedAt;
946
1087
  console.error(
947
- `${TAG5} runInitQueries done account=${ctx.accountId} blocklistOk=${result.blocklistOk} privacySettingsOk=${result.privacySettingsOk} elapsedMs=${result.elapsedMs}`
1088
+ `${TAG6} runInitQueries done account=${ctx.accountId} blocklistOk=${result.blocklistOk} privacySettingsOk=${result.privacySettingsOk} elapsedMs=${result.elapsedMs}`
948
1089
  );
949
1090
  return result;
950
1091
  }
@@ -1961,7 +2102,7 @@ function isDuplicateInbound(key) {
1961
2102
  }
1962
2103
 
1963
2104
  // app/lib/whatsapp/inbound/self-chat.ts
1964
- var TAG6 = "[whatsapp:self-chat]";
2105
+ var TAG7 = "[whatsapp:self-chat]";
1965
2106
  function normalizeJid(jid) {
1966
2107
  const m = jid.match(/^(\d+)(?::\d+)?@(s\.whatsapp\.net|lid)$/i);
1967
2108
  return m ? `${m[1]}@${m[2].toLowerCase()}` : jid;
@@ -1981,10 +2122,10 @@ function evaluateSelfChatCommand(input) {
1981
2122
  const connAtMs = input.lastConnectedAtMs ?? 0;
1982
2123
  const msgId = input.msgId ?? "?";
1983
2124
  console.error(
1984
- `${TAG6} op=seen account=${input.accountId} msgId=${msgId} tsMs=${tsMs} connAtMs=${connAtMs} fromMe=true`
2125
+ `${TAG7} op=seen account=${input.accountId} msgId=${msgId} tsMs=${tsMs} connAtMs=${connAtMs} fromMe=true`
1985
2126
  );
1986
2127
  const skip = (reason) => {
1987
- console.error(`${TAG6} op=skip account=${input.accountId} msgId=${msgId} reason=${reason}`);
2128
+ console.error(`${TAG7} op=skip account=${input.accountId} msgId=${msgId} reason=${reason}`);
1988
2129
  return { kind: "skip", reason };
1989
2130
  };
1990
2131
  if (input.msgId && isAgentSentMessage(input.msgId)) return skip("echo");
@@ -1992,13 +2133,13 @@ function evaluateSelfChatCommand(input) {
1992
2133
  if (isDuplicateInbound(`${input.accountId}:${input.remoteJid}:${input.msgId}`)) return skip("duplicate");
1993
2134
  if (!input.text) return skip("no-text");
1994
2135
  console.error(
1995
- `${TAG6} op=dispatch account=${input.accountId} msgId=${msgId} bytes=${Buffer.byteLength(input.text, "utf8")} agentType=admin`
2136
+ `${TAG7} op=dispatch account=${input.accountId} msgId=${msgId} bytes=${Buffer.byteLength(input.text, "utf8")} agentType=admin`
1996
2137
  );
1997
2138
  return { kind: "dispatch", text: input.text };
1998
2139
  }
1999
2140
 
2000
2141
  // app/lib/whatsapp/activity.ts
2001
- var TAG7 = "[whatsapp:activity]";
2142
+ var TAG8 = "[whatsapp:activity]";
2002
2143
  var RECENT_EVENTS_MAX = 200;
2003
2144
  var counters = /* @__PURE__ */ new Map();
2004
2145
  var recentEvents = [];
@@ -2026,10 +2167,10 @@ function recordActivity(event) {
2026
2167
  recentEvents.splice(0, recentEvents.length - RECENT_EVENTS_MAX);
2027
2168
  }
2028
2169
  console.error(
2029
- `${TAG7} channel-activity: direction=${direction} account=${accountId} jid=${jid}` + (messageType ? ` type=${messageType}` : "")
2170
+ `${TAG8} channel-activity: direction=${direction} account=${accountId} jid=${jid}` + (messageType ? ` type=${messageType}` : "")
2030
2171
  );
2031
2172
  } catch (err) {
2032
- console.error(`${TAG7} recording failed: ${String(err)}`);
2173
+ console.error(`${TAG8} recording failed: ${String(err)}`);
2033
2174
  }
2034
2175
  }
2035
2176
  function getChannelActivity(accountId) {
@@ -2050,7 +2191,7 @@ function getChannelActivity(accountId) {
2050
2191
  }
2051
2192
 
2052
2193
  // app/lib/whatsapp/outbound/send.ts
2053
- var TAG8 = "[whatsapp:outbound]";
2194
+ var TAG9 = "[whatsapp:outbound]";
2054
2195
  function buildQuoted(q) {
2055
2196
  return {
2056
2197
  key: {
@@ -2071,14 +2212,14 @@ async function sendTextMessage(sock, to, text, opts) {
2071
2212
  const messageId = result?.key?.id;
2072
2213
  if (messageId) {
2073
2214
  trackAgentSentMessage(messageId);
2074
- console.error(`${TAG8} sent text to=${jid} id=${messageId}`);
2215
+ console.error(`${TAG9} sent text to=${jid} id=${messageId}`);
2075
2216
  }
2076
2217
  if (opts?.accountId) {
2077
2218
  recordActivity({ accountId: opts.accountId, direction: "outbound", jid, messageType: "text" });
2078
2219
  }
2079
2220
  return { success: true, messageId: messageId ?? void 0 };
2080
2221
  } catch (err) {
2081
- console.error(`${TAG8} send failed to=${to}: ${String(err)}`);
2222
+ console.error(`${TAG9} send failed to=${to}: ${String(err)}`);
2082
2223
  return { success: false, error: String(err) };
2083
2224
  }
2084
2225
  }
@@ -2155,20 +2296,20 @@ async function sendMediaMessage(sock, to, media, opts) {
2155
2296
  const messageId = result?.key?.id;
2156
2297
  if (messageId) {
2157
2298
  trackAgentSentMessage(messageId);
2158
- console.error(`${TAG8} sent ${media.type} to=${jid} id=${messageId}`);
2299
+ console.error(`${TAG9} sent ${media.type} to=${jid} id=${messageId}`);
2159
2300
  }
2160
2301
  if (opts?.accountId) {
2161
2302
  recordActivity({ accountId: opts.accountId, direction: "outbound", jid, messageType: media.type });
2162
2303
  }
2163
2304
  return { success: true, messageId: messageId ?? void 0 };
2164
2305
  } catch (err) {
2165
- console.error(`${TAG8} send media failed to=${to}: ${String(err)}`);
2306
+ console.error(`${TAG9} send media failed to=${to}: ${String(err)}`);
2166
2307
  return { success: false, error: String(err) };
2167
2308
  }
2168
2309
  }
2169
2310
 
2170
2311
  // app/lib/whatsapp/persist-message.ts
2171
- var TAG9 = "[whatsapp-persist]";
2312
+ var TAG10 = "[whatsapp-persist]";
2172
2313
  var sessionWriteLocks = /* @__PURE__ */ new Map();
2173
2314
  async function persistWhatsAppMessage(input) {
2174
2315
  if (!input.body && !input.attachmentId || !input.msgKeyId) return null;
@@ -2219,14 +2360,14 @@ async function persistWhatsAppMessage(input) {
2219
2360
  };
2220
2361
  const result = await session.run(cypher, params);
2221
2362
  if (result.records.length === 0) {
2222
- console.error(`${TAG9} skip reason=conversation-not-found accountId=${input.platformAccountId} cacheKey=${input.cacheKey} messageId=${messageId}`);
2363
+ console.error(`${TAG10} skip reason=conversation-not-found accountId=${input.platformAccountId} cacheKey=${input.cacheKey} messageId=${messageId}`);
2223
2364
  return null;
2224
2365
  }
2225
2366
  const rec = result.records[0];
2226
2367
  const senderElementId = rec.get("senderElementId");
2227
2368
  const senderReused = rec.get("senderReused") === true;
2228
2369
  console.error(
2229
- `${TAG9} sender-resolved telephone=${senderTelephone} nodeKind=Person elementId=${senderElementId} reused=${senderReused}`
2370
+ `${TAG10} sender-resolved telephone=${senderTelephone} nodeKind=Person elementId=${senderElementId} reused=${senderReused}`
2230
2371
  );
2231
2372
  const diskRecord = {
2232
2373
  messageId,
@@ -2252,18 +2393,18 @@ async function persistWhatsAppMessage(input) {
2252
2393
  const { created } = appendMessage(input.platformAccountId, diskRecord, altDedupeRemoteJids);
2253
2394
  const ms = Date.now() - t0;
2254
2395
  if (!created) {
2255
- console.error(`${TAG9} noop reason=existing-message messageId=${messageId}`);
2396
+ console.error(`${TAG10} noop reason=existing-message messageId=${messageId}`);
2256
2397
  return { messageId, created: false, senderElementId };
2257
2398
  }
2258
2399
  console.error(
2259
- `${TAG9} write messageId=${messageId} accountId=${input.platformAccountId} cacheKey=${input.cacheKey} fromMe=${input.fromMe} dateSent=${dateSentIso} bodyBytes=${Buffer.byteLength(input.body, "utf8")} attachment=${input.attachmentId ?? "none"} ms=${ms}`
2400
+ `${TAG10} write messageId=${messageId} accountId=${input.platformAccountId} cacheKey=${input.cacheKey} fromMe=${input.fromMe} dateSent=${dateSentIso} bodyBytes=${Buffer.byteLength(input.body, "utf8")} attachment=${input.attachmentId ?? "none"} ms=${ms}`
2260
2401
  );
2261
2402
  return { messageId, created: true, senderElementId };
2262
2403
  } catch (err) {
2263
2404
  const ms = Date.now() - t0;
2264
2405
  const reason = sanitizeReason(err);
2265
2406
  console.error(
2266
- `${TAG9} FAIL accountId=${input.platformAccountId} waname=${input.accountId} remoteJid=${input.remoteJid} msgKey=${input.msgKeyId} reason=${reason} ms=${ms}`
2407
+ `${TAG10} FAIL accountId=${input.platformAccountId} waname=${input.accountId} remoteJid=${input.remoteJid} msgKey=${input.msgKeyId} reason=${reason} ms=${ms}`
2267
2408
  );
2268
2409
  return null;
2269
2410
  } finally {
@@ -2506,7 +2647,7 @@ function groupEntriesFromUpdates(updates, nowIso) {
2506
2647
  }
2507
2648
 
2508
2649
  // app/lib/whatsapp/ensure-conversation.ts
2509
- var TAG10 = "[whatsapp-persist]";
2650
+ var TAG11 = "[whatsapp-persist]";
2510
2651
  async function ensureWhatsAppConversation(input) {
2511
2652
  const t0 = Date.now();
2512
2653
  try {
@@ -2518,19 +2659,19 @@ async function ensureWhatsAppConversation(input) {
2518
2659
  const ms = Date.now() - t0;
2519
2660
  if (!result.sessionId) {
2520
2661
  console.error(
2521
- `${TAG10} conversation-merged FAIL cacheKey=${input.cacheKey} reason=null-sessionId ms=${ms}`
2662
+ `${TAG11} conversation-merged FAIL cacheKey=${input.cacheKey} reason=null-sessionId ms=${ms}`
2522
2663
  );
2523
2664
  return null;
2524
2665
  }
2525
2666
  console.error(
2526
- `${TAG10} conversation-merged cacheKey=${input.cacheKey} agentType=${input.agentType} channel=whatsapp accountId=${input.platformAccountId} connectionAccountId=${input.connectionAccountId ?? input.platformAccountId} created=${result.created} ms=${ms}`
2667
+ `${TAG11} conversation-merged cacheKey=${input.cacheKey} agentType=${input.agentType} channel=whatsapp accountId=${input.platformAccountId} connectionAccountId=${input.connectionAccountId ?? input.platformAccountId} created=${result.created} ms=${ms}`
2527
2668
  );
2528
2669
  return { sessionId: result.sessionId, created: result.created };
2529
2670
  } catch (err) {
2530
2671
  const ms = Date.now() - t0;
2531
2672
  const reason = err instanceof Error ? `${err.name}:${err.message.slice(0, 200)}` : String(err).slice(0, 200);
2532
2673
  console.error(
2533
- `${TAG10} conversation-merged FAIL cacheKey=${input.cacheKey} reason=${reason} ms=${ms}`
2674
+ `${TAG11} conversation-merged FAIL cacheKey=${input.cacheKey} reason=${reason} ms=${ms}`
2534
2675
  );
2535
2676
  return null;
2536
2677
  }
@@ -3103,7 +3244,7 @@ async function storeComponentArtefact(accountId, attachmentId, mimeType, content
3103
3244
  }
3104
3245
 
3105
3246
  // app/lib/whatsapp/inbound/media-download-guard.ts
3106
- var TAG11 = "[media-guard]";
3247
+ var TAG12 = "[media-guard]";
3107
3248
  var inFlight = 0;
3108
3249
  var containedCount = 0;
3109
3250
  function containedSinceBoot() {
@@ -3133,13 +3274,13 @@ function handleEscapedMediaError(err, channel) {
3133
3274
  if (undiciFrame && inFlight > 0) {
3134
3275
  containedCount += 1;
3135
3276
  console.error(
3136
- `${TAG11} op=contained channel=${channel} inFlight=${inFlight} err=${err instanceof Error ? err.message : String(err)} cause=${causeCode(err)}
3277
+ `${TAG12} op=contained channel=${channel} inFlight=${inFlight} err=${err instanceof Error ? err.message : String(err)} cause=${causeCode(err)}
3137
3278
  ${stack}`
3138
3279
  );
3139
3280
  return true;
3140
3281
  }
3141
3282
  console.error(
3142
- `${TAG11} op=escaped channel=${channel} inFlight=${inFlight} undiciFrame=${undiciFrame} err=${err instanceof Error ? err.message : String(err)} cause=${causeCode(err)} contained=false`
3283
+ `${TAG12} op=escaped channel=${channel} inFlight=${inFlight} undiciFrame=${undiciFrame} err=${err instanceof Error ? err.message : String(err)} cause=${causeCode(err)} contained=false`
3143
3284
  );
3144
3285
  return false;
3145
3286
  }
@@ -3164,11 +3305,11 @@ function installMediaDownloadGuard() {
3164
3305
  );
3165
3306
  process.exit(1);
3166
3307
  });
3167
- console.error(`${TAG11} op=installed`);
3308
+ console.error(`${TAG12} op=installed`);
3168
3309
  }
3169
3310
 
3170
3311
  // app/lib/whatsapp/inbound/media.ts
3171
- var TAG12 = "[whatsapp:media]";
3312
+ var TAG13 = "[whatsapp:media]";
3172
3313
  var MEDIA_DIR = "/tmp/maxy-media";
3173
3314
  var DOWNLOAD_TIMEOUT_MS = 45e3;
3174
3315
  var DETACHED_GRACE_MS = 3e5;
@@ -3258,25 +3399,25 @@ async function downloadInboundMediaBuffer(msg, sock, opts) {
3258
3399
  }
3259
3400
  );
3260
3401
  if (!buffer || buffer.length === 0) {
3261
- console.error(`${TAG12} primary download returned empty, trying direct fallback`);
3402
+ console.error(`${TAG13} primary download returned empty, trying direct fallback`);
3262
3403
  const downloadable = getDownloadableContent(content);
3263
3404
  if (downloadable) {
3264
3405
  try {
3265
3406
  const stream = await downloadContentFromMessage(downloadable.downloadable, downloadable.mediaType);
3266
3407
  buffer = await streamToBuffer(stream);
3267
3408
  } catch (fallbackErr) {
3268
- console.error(`${TAG12} direct download fallback failed: ${String(fallbackErr)}`);
3409
+ console.error(`${TAG13} direct download fallback failed: ${String(fallbackErr)}`);
3269
3410
  }
3270
3411
  }
3271
3412
  }
3272
3413
  if (!buffer || buffer.length === 0) {
3273
- console.error(`${TAG12} download failed: empty buffer for ${mimetype ?? "unknown"}`);
3414
+ console.error(`${TAG13} download failed: empty buffer for ${mimetype ?? "unknown"}`);
3274
3415
  return void 0;
3275
3416
  }
3276
3417
  if (buffer.length > maxBytes) {
3277
3418
  const sizeMB = (buffer.length / (1024 * 1024)).toFixed(1);
3278
3419
  const limitMB = (maxBytes / (1024 * 1024)).toFixed(0);
3279
- console.error(`${TAG12} media too large type=${mimetype ?? "unknown"} size=${sizeMB}MB limit=${limitMB}MB`);
3420
+ console.error(`${TAG13} media too large type=${mimetype ?? "unknown"} size=${sizeMB}MB limit=${limitMB}MB`);
3280
3421
  return void 0;
3281
3422
  }
3282
3423
  return {
@@ -3290,7 +3431,7 @@ async function downloadInboundMediaBuffer(msg, sock, opts) {
3290
3431
  endMediaDownload();
3291
3432
  return settled;
3292
3433
  } catch (err) {
3293
- console.error(`${TAG12} media download failed type=${mimetype ?? "unknown"} error=${String(err)}`);
3434
+ console.error(`${TAG13} media download failed type=${mimetype ?? "unknown"} error=${String(err)}`);
3294
3435
  if (isTimeoutError(err)) {
3295
3436
  const t = setTimeout(endMediaDownload, DETACHED_GRACE_MS);
3296
3437
  t.unref?.();
@@ -3308,7 +3449,7 @@ async function downloadInboundMedia(msg, sock, opts) {
3308
3449
  const filePath = join6(MEDIA_DIR, `${randomUUID4()}.${ext}`);
3309
3450
  await writeFile2(filePath, res.buffer);
3310
3451
  const sizeKB = (res.buffer.length / 1024).toFixed(0);
3311
- console.error(`${TAG12} media downloaded type=${res.mimetype} size=${sizeKB}KB path=${filePath}`);
3452
+ console.error(`${TAG13} media downloaded type=${res.mimetype} size=${sizeKB}KB path=${filePath}`);
3312
3453
  return { path: filePath, mimetype: res.mimetype, size: res.buffer.length };
3313
3454
  }
3314
3455
  async function storeInboundMediaServable(msg, sock, accountId, seed, opts) {
@@ -3317,15 +3458,15 @@ async function storeInboundMediaServable(msg, sock, accountId, seed, opts) {
3317
3458
  if (attachmentExists(accountId, attachmentId)) return attachmentId;
3318
3459
  const res = await downloadInboundMediaBufferCached(msg, sock, opts);
3319
3460
  if (!res) {
3320
- console.error(`${TAG12} servable-store skip reason=no-buffer id=${attachmentId}`);
3461
+ console.error(`${TAG13} servable-store skip reason=no-buffer id=${attachmentId}`);
3321
3462
  return null;
3322
3463
  }
3323
3464
  const filename = res.filename ?? `${attachmentId}.${mimeToExt(res.mimetype)}`;
3324
3465
  await storeMediaBuffer(accountId, attachmentId, filename, res.mimetype, res.buffer);
3325
- console.error(`${TAG12} servable-store ok id=${attachmentId} mime=${res.mimetype} bytes=${res.buffer.length}`);
3466
+ console.error(`${TAG13} servable-store ok id=${attachmentId} mime=${res.mimetype} bytes=${res.buffer.length}`);
3326
3467
  return attachmentId;
3327
3468
  } catch (err) {
3328
- console.error(`${TAG12} servable-store FAIL id=${attachmentId} reason=${String(err).slice(0, 120)}`);
3469
+ console.error(`${TAG13} servable-store FAIL id=${attachmentId} reason=${String(err).slice(0, 120)}`);
3329
3470
  return null;
3330
3471
  }
3331
3472
  }
@@ -3373,7 +3514,7 @@ function createBoundedQueue(limit, opts) {
3373
3514
  }
3374
3515
 
3375
3516
  // app/lib/whatsapp/inbound/debounce.ts
3376
- var TAG13 = "[whatsapp:debounce]";
3517
+ var TAG14 = "[whatsapp:debounce]";
3377
3518
  var STT_TAG = "[whatsapp:stt-await]";
3378
3519
  function createInboundDebouncer(opts) {
3379
3520
  const { debounceMs, buildKey, onFlush, onError } = opts;
@@ -3404,7 +3545,7 @@ function createInboundDebouncer(opts) {
3404
3545
  pending.delete(key);
3405
3546
  const batchSize = batch.entries.length;
3406
3547
  try {
3407
- console.error(`${TAG13} debounce flush key=${key} batchSize=${batchSize}`);
3548
+ console.error(`${TAG14} debounce flush key=${key} batchSize=${batchSize}`);
3408
3549
  const result = onFlush(batch.entries);
3409
3550
  if (result && typeof result.catch === "function") {
3410
3551
  result.catch(onError);
@@ -3484,7 +3625,7 @@ function combineInboundBatch(entries) {
3484
3625
  }
3485
3626
 
3486
3627
  // app/lib/whatsapp/opening-hours.ts
3487
- var TAG14 = "[whatsapp:hours]";
3628
+ var TAG15 = "[whatsapp:hours]";
3488
3629
  async function isBusinessOpen(accountId) {
3489
3630
  try {
3490
3631
  const timezone = await resolveTimezone(accountId);
@@ -3502,7 +3643,7 @@ async function isBusinessOpen(accountId) {
3502
3643
  { accountId, dayOfWeek, previousDayOfWeek }
3503
3644
  );
3504
3645
  if (result.records.length === 0) {
3505
- console.error(`${TAG14} [${accountId}] business hours check: no opening hours configured, treating as open`);
3646
+ console.error(`${TAG15} [${accountId}] business hours check: no opening hours configured, treating as open`);
3506
3647
  return { open: true, reason: "no opening hours configured" };
3507
3648
  }
3508
3649
  const specs = result.records.filter((r) => r.get("opens") != null && r.get("closes") != null).map((r) => ({
@@ -3511,7 +3652,7 @@ async function isBusinessOpen(accountId) {
3511
3652
  closes: String(r.get("closes")).trim()
3512
3653
  }));
3513
3654
  if (specs.length === 0) {
3514
- console.error(`${TAG14} [${accountId}] business hours check: no opening hours configured, treating as open`);
3655
+ console.error(`${TAG15} [${accountId}] business hours check: no opening hours configured, treating as open`);
3515
3656
  return { open: true, reason: "no opening hours configured" };
3516
3657
  }
3517
3658
  for (const spec of specs) {
@@ -3521,7 +3662,7 @@ async function isBusinessOpen(accountId) {
3521
3662
  if (spec.opens > spec.closes && currentTime < spec.closes) {
3522
3663
  const hoursStr = `${spec.opens}-${spec.closes} (${spec.day})`;
3523
3664
  console.error(
3524
- `${TAG14} [${accountId}] business hours check: open (day=${dayOfWeek}, time=${currentTime}, hours=${hoursStr})`
3665
+ `${TAG15} [${accountId}] business hours check: open (day=${dayOfWeek}, time=${currentTime}, hours=${hoursStr})`
3525
3666
  );
3526
3667
  return {
3527
3668
  open: true,
@@ -3534,7 +3675,7 @@ async function isBusinessOpen(accountId) {
3534
3675
  } else if (isTimeInRange(currentTime, spec.opens, spec.closes)) {
3535
3676
  const hoursStr = `${spec.opens}-${spec.closes}`;
3536
3677
  console.error(
3537
- `${TAG14} [${accountId}] business hours check: open (day=${dayOfWeek}, time=${currentTime}, hours=${hoursStr})`
3678
+ `${TAG15} [${accountId}] business hours check: open (day=${dayOfWeek}, time=${currentTime}, hours=${hoursStr})`
3538
3679
  );
3539
3680
  return {
3540
3681
  open: true,
@@ -3548,7 +3689,7 @@ async function isBusinessOpen(accountId) {
3548
3689
  const todaySpecs = specs.filter((s) => s.day === dayOfWeek);
3549
3690
  const allHours = todaySpecs.map((s) => `${s.opens}-${s.closes}`).join(", ") || "none today";
3550
3691
  console.error(
3551
- `${TAG14} [${accountId}] business hours check: closed (day=${dayOfWeek}, time=${currentTime}, hours=${allHours})`
3692
+ `${TAG15} [${accountId}] business hours check: closed (day=${dayOfWeek}, time=${currentTime}, hours=${allHours})`
3552
3693
  );
3553
3694
  return {
3554
3695
  open: false,
@@ -3562,7 +3703,7 @@ async function isBusinessOpen(accountId) {
3562
3703
  }
3563
3704
  } catch (err) {
3564
3705
  console.error(
3565
- `${TAG14} [${accountId}] business hours check failed, treating as open: ${err instanceof Error ? err.message : String(err)}`
3706
+ `${TAG15} [${accountId}] business hours check failed, treating as open: ${err instanceof Error ? err.message : String(err)}`
3566
3707
  );
3567
3708
  return { open: true, reason: "hours check failed (treating as open)" };
3568
3709
  }
@@ -3608,7 +3749,7 @@ import { execFile } from "child_process";
3608
3749
  import { unlink, stat } from "fs/promises";
3609
3750
  import { promisify } from "util";
3610
3751
  var execFileAsync = promisify(execFile);
3611
- var TAG15 = "[stt]";
3752
+ var TAG16 = "[stt]";
3612
3753
  var WHISPER_BINARY = process.env.WHISPER_BINARY ?? "/opt/whisper.cpp/build/bin/whisper-cli";
3613
3754
  var WHISPER_MODEL = process.env.WHISPER_MODEL ?? "/opt/whisper.cpp/models/ggml-base.bin";
3614
3755
  var WHISPER_TIMEOUT_MS = 20 * 60 * 1e3;
@@ -3627,11 +3768,11 @@ async function transcribe(audioPath, mimetype) {
3627
3768
  const s = await stat(audioPath);
3628
3769
  audioBytes = s.size;
3629
3770
  } catch {
3630
- console.error(`${TAG15} failed: file not readable path=${audioPath}`);
3771
+ console.error(`${TAG16} failed: file not readable path=${audioPath}`);
3631
3772
  return void 0;
3632
3773
  }
3633
3774
  console.error(
3634
- `${TAG15} start provider=whisper-local audio_bytes=${audioBytes} mimetype=${mimetype} path=${audioPath}`
3775
+ `${TAG16} start provider=whisper-local audio_bytes=${audioBytes} mimetype=${mimetype} path=${audioPath}`
3635
3776
  );
3636
3777
  const wavPath = audioPath.replace(/\.[^.]+$/, "") + ".wav";
3637
3778
  try {
@@ -3650,7 +3791,7 @@ async function transcribe(audioPath, mimetype) {
3650
3791
  ], { timeout: 3e4 });
3651
3792
  } catch (err) {
3652
3793
  const reason = err instanceof Error ? err.message : String(err);
3653
- console.error(`${TAG15} failed: ffmpeg conversion error=${reason}`);
3794
+ console.error(`${TAG16} failed: ffmpeg conversion error=${reason}`);
3654
3795
  return void 0;
3655
3796
  }
3656
3797
  try {
@@ -3666,20 +3807,20 @@ async function transcribe(audioPath, mimetype) {
3666
3807
  const durationMs = Date.now() - startMs;
3667
3808
  const { text, count: stripped } = stripSpecialTokens(stdout.trim());
3668
3809
  if (!text) {
3669
- console.error(`${TAG15} failed: whisper returned empty output duration_ms=${durationMs}`);
3810
+ console.error(`${TAG16} failed: whisper returned empty output duration_ms=${durationMs}`);
3670
3811
  return void 0;
3671
3812
  }
3672
3813
  const langMatch = stderr.match(/auto-detected language:\s*(\w+)/);
3673
3814
  const language = langMatch?.[1] ?? "unknown";
3674
3815
  const words = text.split(/\s+/).filter(Boolean).length;
3675
3816
  console.error(
3676
- `${TAG15} done provider=whisper-local duration_ms=${durationMs} words=${words} stripped=${stripped} lang=${language}`
3817
+ `${TAG16} done provider=whisper-local duration_ms=${durationMs} words=${words} stripped=${stripped} lang=${language}`
3677
3818
  );
3678
3819
  return { text, language, durationMs };
3679
3820
  } catch (err) {
3680
3821
  const durationMs = Date.now() - startMs;
3681
3822
  const reason = err instanceof Error ? err.message : String(err);
3682
- console.error(`${TAG15} failed provider=whisper-local duration_ms=${durationMs} error=${reason}`);
3823
+ console.error(`${TAG16} failed provider=whisper-local duration_ms=${durationMs} error=${reason}`);
3683
3824
  return void 0;
3684
3825
  } finally {
3685
3826
  unlink(wavPath).catch(() => {
@@ -3698,8 +3839,9 @@ var WaReplyError = class extends Error {
3698
3839
  };
3699
3840
 
3700
3841
  // app/lib/whatsapp/manager.ts
3701
- var TAG16 = "[whatsapp:manager]";
3842
+ var TAG17 = "[whatsapp:manager]";
3702
3843
  var MAX_RECONNECT_ATTEMPTS = 10;
3844
+ var REARM_INTERVAL_MS = 15 * 6e4;
3703
3845
  var MESSAGE_STORE_MAX = 500;
3704
3846
  var APPEND_MEDIA_CONCURRENCY = 3;
3705
3847
  var APPEND_MEDIA_MAX_PENDING = 24;
@@ -3751,7 +3893,7 @@ function storeMessage(storeKey, entry2) {
3751
3893
  if (entries.length > MESSAGE_STORE_MAX) {
3752
3894
  const trimmed = entries.length - MESSAGE_STORE_MAX;
3753
3895
  entries.splice(0, trimmed);
3754
- console.error(`${TAG16} message store trimmed for ${storeKey}: ${trimmed} oldest messages removed`);
3896
+ console.error(`${TAG17} message store trimmed for ${storeKey}: ${trimmed} oldest messages removed`);
3755
3897
  }
3756
3898
  }
3757
3899
  function deriveCacheKey(input) {
@@ -3795,7 +3937,7 @@ function buildSelfChatDispatch(input) {
3795
3937
  }
3796
3938
  async function init(opts) {
3797
3939
  if (initialized) {
3798
- console.error(`${TAG16} already initialized`);
3940
+ console.error(`${TAG17} already initialized`);
3799
3941
  return;
3800
3942
  }
3801
3943
  configDir = opts.configDir;
@@ -3805,20 +3947,20 @@ async function init(opts) {
3805
3947
  loadConfig(opts.accountConfig);
3806
3948
  const accountIds = listCredentialAccountIds(configDir);
3807
3949
  if (accountIds.length === 0) {
3808
- console.error(`${TAG16} init: no stored WhatsApp credentials found`);
3950
+ console.error(`${TAG17} init: no stored WhatsApp credentials found`);
3809
3951
  initialized = true;
3810
3952
  return;
3811
3953
  }
3812
- console.error(`${TAG16} init: found ${accountIds.length} credentialed account(s): ${accountIds.join(", ")}`);
3954
+ console.error(`${TAG17} init: found ${accountIds.length} credentialed account(s): ${accountIds.join(", ")}`);
3813
3955
  initialized = true;
3814
3956
  for (const accountId of accountIds) {
3815
3957
  const accountCfg = whatsAppConfig.accounts?.[accountId];
3816
3958
  if (accountCfg?.enabled === false) {
3817
- console.error(`${TAG16} skipping disabled account=${accountId}`);
3959
+ console.error(`${TAG17} skipping disabled account=${accountId}`);
3818
3960
  continue;
3819
3961
  }
3820
3962
  startConnection(accountId).catch((err) => {
3821
- console.error(`${TAG16} failed to auto-start account=${accountId}: ${formatError(err)}`);
3963
+ console.error(`${TAG17} failed to auto-start account=${accountId}: ${formatError(err)}`);
3822
3964
  });
3823
3965
  }
3824
3966
  }
@@ -3827,7 +3969,7 @@ async function startConnection(accountId) {
3827
3969
  const authDir = resolveAuthDir(configDir, accountId);
3828
3970
  const hasAuth = await authExists(authDir);
3829
3971
  if (!hasAuth) {
3830
- console.error(`${TAG16} no credentials for account=${accountId}`);
3972
+ console.error(`${TAG17} no credentials for account=${accountId}`);
3831
3973
  return;
3832
3974
  }
3833
3975
  let platformAccountId;
@@ -3858,6 +4000,7 @@ async function startConnection(accountId) {
3858
4000
  selfJid: null,
3859
4001
  selfLid: null,
3860
4002
  reconnectAttempts: 0,
4003
+ rearmAttempts: 0,
3861
4004
  abortController: new AbortController(),
3862
4005
  debouncer: null,
3863
4006
  lidMapping: null
@@ -3881,11 +4024,11 @@ async function stopConnection(accountId) {
3881
4024
  conn.sock.ev.removeAllListeners("creds.update");
3882
4025
  conn.sock.ws?.close?.();
3883
4026
  } catch (err) {
3884
- console.warn(`${TAG16} socket cleanup error during stop account=${accountId}: ${String(err)}`);
4027
+ console.warn(`${TAG17} socket cleanup error during stop account=${accountId}: ${String(err)}`);
3885
4028
  }
3886
4029
  }
3887
4030
  connections.delete(accountId);
3888
- console.error(`${TAG16} stopped account=${accountId}`);
4031
+ console.error(`${TAG17} stopped account=${accountId}`);
3889
4032
  }
3890
4033
  function getStatus() {
3891
4034
  return Array.from(connections.values()).map((conn) => ({
@@ -3996,6 +4139,7 @@ async function registerLoginSocket(accountId, sock, authDir) {
3996
4139
  selfJid: null,
3997
4140
  selfLid: null,
3998
4141
  reconnectAttempts: 0,
4142
+ rearmAttempts: 0,
3999
4143
  lastConnectedAt: Date.now(),
4000
4144
  abortController: new AbortController(),
4001
4145
  debouncer: null,
@@ -4007,9 +4151,9 @@ async function registerLoginSocket(accountId, sock, authDir) {
4007
4151
  monitorChatMetadata(conn);
4008
4152
  try {
4009
4153
  await sock.sendPresenceUpdate("available");
4010
- console.error(`${TAG16} presence set to available account=${accountId}`);
4154
+ console.error(`${TAG17} presence set to available account=${accountId}`);
4011
4155
  } catch (err) {
4012
- console.error(`${TAG16} presence update failed account=${accountId}: ${String(err)}`);
4156
+ console.error(`${TAG17} presence update failed account=${accountId}: ${String(err)}`);
4013
4157
  }
4014
4158
  await runInitQueries(sock, {
4015
4159
  accountId,
@@ -4020,7 +4164,7 @@ async function registerLoginSocket(accountId, sock, authDir) {
4020
4164
  });
4021
4165
  watchForDisconnect(conn);
4022
4166
  attachSelfIdRefreshOnCredsUpdate(conn);
4023
- console.error(`${TAG16} registered login socket for account=${accountId} phone=${conn.selfPhone ?? "unknown"}`);
4167
+ console.error(`${TAG17} registered login socket for account=${accountId} phone=${conn.selfPhone ?? "unknown"}`);
4024
4168
  }
4025
4169
  function attachSelfIdRefreshOnCredsUpdate(conn) {
4026
4170
  if (!conn.sock) return;
@@ -4079,7 +4223,7 @@ async function shutdown() {
4079
4223
  const ids = Array.from(connections.keys());
4080
4224
  await Promise.all(ids.map((id) => stopConnection(id)));
4081
4225
  initialized = false;
4082
- console.error(`${TAG16} shutdown complete`);
4226
+ console.error(`${TAG17} shutdown complete`);
4083
4227
  }
4084
4228
  function loadConfig(accountConfig) {
4085
4229
  try {
@@ -4090,30 +4234,43 @@ function loadConfig(accountConfig) {
4090
4234
  if (result.ok) {
4091
4235
  whatsAppConfig = result.data;
4092
4236
  if (result.droppedKeys.length > 0) {
4093
- console.error(`${TAG16} config: dropped unknown keys=[${result.droppedKeys.join(",")}] preserved known config`);
4237
+ console.error(`${TAG17} config: dropped unknown keys=[${result.droppedKeys.join(",")}] preserved known config`);
4094
4238
  }
4095
4239
  } else {
4096
- console.error(`${TAG16} config validation failed: ${result.error.message}`);
4240
+ console.error(`${TAG17} config validation failed: ${result.error.message}`);
4097
4241
  whatsAppConfig = {};
4098
4242
  }
4099
4243
  const diskAdmin = Array.isArray(wa.adminPhones) ? wa.adminPhones : [];
4100
4244
  if (diskAdmin.length > 0 && (whatsAppConfig.adminPhones?.length ?? 0) === 0) {
4101
- console.error(`${TAG16} WARN adminPhones present on disk but empty after load`);
4245
+ console.error(`${TAG17} WARN adminPhones present on disk but empty after load`);
4102
4246
  }
4103
4247
  }
4104
4248
  } catch (err) {
4105
- console.error(`${TAG16} config load error: ${String(err)}`);
4249
+ console.error(`${TAG17} config load error: ${String(err)}`);
4106
4250
  whatsAppConfig = {};
4107
4251
  }
4108
4252
  }
4253
+ function sinceStuckMs(conn) {
4254
+ return conn.stuckSinceMs === void 0 ? 0 : Date.now() - conn.stuckSinceMs;
4255
+ }
4256
+ function clearStuck(conn) {
4257
+ if (!conn.sessionStuckReason) return;
4258
+ console.error(
4259
+ `${HEALTH_TAG} op=rearm account=${conn.accountId} attempt=${conn.rearmAttempts} sinceStuckMs=${sinceStuckMs(conn)} outcome=connected`
4260
+ );
4261
+ conn.sessionStuckReason = void 0;
4262
+ conn.stuckSinceMs = void 0;
4263
+ conn.rearmAttempts = 0;
4264
+ }
4109
4265
  async function connectWithReconnect(conn) {
4110
4266
  const maxAttempts = MAX_RECONNECT_ATTEMPTS;
4111
4267
  while (!conn.abortController.signal.aborted) {
4112
4268
  let uptimeMs = 0;
4113
4269
  let cycleError = null;
4114
4270
  let connectedAt;
4271
+ let heldTimer;
4115
4272
  try {
4116
- console.error(`${TAG16} connecting account=${conn.accountId} attempt=${conn.reconnectAttempts}`);
4273
+ console.error(`${TAG17} connecting account=${conn.accountId} attempt=${conn.reconnectAttempts}`);
4117
4274
  if (conn.debouncer) {
4118
4275
  await conn.debouncer.destroy();
4119
4276
  conn.debouncer = null;
@@ -4127,7 +4284,7 @@ async function connectWithReconnect(conn) {
4127
4284
  applySelfIdentity(conn, sock, "pre-listener");
4128
4285
  monitorInbound(conn);
4129
4286
  monitorChatMetadata(conn);
4130
- console.error(`${TAG16} socket created account=${conn.accountId} \u2014 waiting for connection`);
4287
+ console.error(`${TAG17} socket created account=${conn.accountId} \u2014 waiting for connection`);
4131
4288
  await waitForConnection(sock);
4132
4289
  connectedAt = Date.now();
4133
4290
  conn.connected = true;
@@ -4135,12 +4292,19 @@ async function connectWithReconnect(conn) {
4135
4292
  conn.lastError = void 0;
4136
4293
  conn.terminalReason = void 0;
4137
4294
  applySelfIdentity(conn, sock, "post-connect");
4138
- console.error(`${TAG16} connected account=${conn.accountId} phone=${conn.selfPhone ?? "unknown"}`);
4295
+ console.error(`${TAG17} connected account=${conn.accountId} phone=${conn.selfPhone ?? "unknown"}`);
4296
+ if (conn.sessionStuckReason) {
4297
+ heldTimer = setTimeout(() => {
4298
+ if (!conn.connected || connections.get(conn.accountId) !== conn) return;
4299
+ clearStuck(conn);
4300
+ }, STABLE_UPTIME_MS);
4301
+ if (typeof heldTimer.unref === "function") heldTimer.unref();
4302
+ }
4139
4303
  try {
4140
4304
  await sock.sendPresenceUpdate("available");
4141
- console.error(`${TAG16} presence set to available account=${conn.accountId}`);
4305
+ console.error(`${TAG17} presence set to available account=${conn.accountId}`);
4142
4306
  } catch (err) {
4143
- console.error(`${TAG16} presence update failed account=${conn.accountId}: ${String(err)}`);
4307
+ console.error(`${TAG17} presence update failed account=${conn.accountId}: ${String(err)}`);
4144
4308
  }
4145
4309
  await runInitQueries(sock, {
4146
4310
  accountId: conn.accountId,
@@ -4163,13 +4327,20 @@ async function connectWithReconnect(conn) {
4163
4327
  }
4164
4328
  const classification = classifyDisconnect(err);
4165
4329
  conn.lastError = classification.message;
4166
- console.error(`${TAG16} disconnect account=${conn.accountId}: ${classification.kind} \u2014 ${classification.message}`);
4330
+ console.error(`${TAG17} disconnect account=${conn.accountId}: ${classification.kind} \u2014 ${classification.message}`);
4167
4331
  if (!classification.shouldRetry) {
4168
4332
  conn.terminalReason = classification.message;
4169
- console.error(`${TAG16} terminal disconnect for account=${conn.accountId}, stopping reconnection`);
4333
+ if (conn.sessionStuckReason) {
4334
+ console.error(
4335
+ `${HEALTH_TAG} op=rearm account=${conn.accountId} attempt=${conn.rearmAttempts} sinceStuckMs=${sinceStuckMs(conn)} outcome=skipped-terminal`
4336
+ );
4337
+ }
4338
+ console.error(`${TAG17} terminal disconnect for account=${conn.accountId}, stopping reconnection`);
4339
+ if (heldTimer !== void 0) clearTimeout(heldTimer);
4170
4340
  return;
4171
4341
  }
4172
4342
  }
4343
+ if (heldTimer !== void 0) clearTimeout(heldTimer);
4173
4344
  const decision = computeReconnectNextState({
4174
4345
  reconnectAttempts: conn.reconnectAttempts,
4175
4346
  uptimeMs,
@@ -4177,28 +4348,44 @@ async function connectWithReconnect(conn) {
4177
4348
  maxAttempts
4178
4349
  });
4179
4350
  if (decision.action === "abort") {
4180
- const stuckReason = `GIVING UP account=${conn.accountId} attempts=${decision.finalAttempts}/${maxAttempts} uptimeMsLast=${uptimeMs} stableThresholdMs=${STABLE_UPTIME_MS} lastError=${conn.lastError ?? "(none)"}`;
4351
+ const stuckReason = `RETRYING SLOWLY account=${conn.accountId} attempts=${decision.finalAttempts}/${maxAttempts} rearmIntervalMs=${REARM_INTERVAL_MS} uptimeMsLast=${uptimeMs} stableThresholdMs=${STABLE_UPTIME_MS} lastError=${conn.lastError ?? "(none)"}`;
4352
+ if (conn.rearmAttempts > 0) {
4353
+ console.error(
4354
+ `${HEALTH_TAG} op=rearm account=${conn.accountId} attempt=${conn.rearmAttempts} sinceStuckMs=${sinceStuckMs(conn)} outcome=failed`
4355
+ );
4356
+ }
4181
4357
  console.error(
4182
- `${TAG16} ${stuckReason} \u2014 re-pair via QR or restart required; WhatsApp will not reconnect automatically`
4358
+ `${TAG17} ${stuckReason} \u2014 re-arming in ${REARM_INTERVAL_MS}ms and every ${REARM_INTERVAL_MS}ms after; a re-pair via QR ends the wait sooner`
4183
4359
  );
4184
4360
  conn.sessionStuckReason = stuckReason;
4185
4361
  conn.lastError = stuckReason;
4186
- return;
4362
+ if (conn.stuckSinceMs === void 0) conn.stuckSinceMs = Date.now();
4363
+ await new Promise((resolve6) => {
4364
+ const timer = setTimeout(resolve6, REARM_INTERVAL_MS);
4365
+ conn.abortController.signal.addEventListener("abort", () => {
4366
+ clearTimeout(timer);
4367
+ resolve6();
4368
+ }, { once: true });
4369
+ });
4370
+ if (conn.abortController.signal.aborted) return;
4371
+ conn.rearmAttempts += 1;
4372
+ continue;
4187
4373
  }
4188
4374
  conn.reconnectAttempts = decision.nextAttempts;
4189
4375
  if (decision.reason === "short-lived") {
4190
4376
  console.error(
4191
- `${TAG16} short-lived session account=${conn.accountId} uptimeMs=${uptimeMs} attempt=${decision.nextAttempts}/${maxAttempts} lastError=${conn.lastError ?? "(clean disconnect)"}`
4377
+ `${TAG17} short-lived session account=${conn.accountId} uptimeMs=${uptimeMs} attempt=${decision.nextAttempts}/${maxAttempts} lastError=${conn.lastError ?? "(clean disconnect)"}`
4192
4378
  );
4193
4379
  } else {
4380
+ clearStuck(conn);
4194
4381
  console.error(
4195
- `${TAG16} session stable account=${conn.accountId} uptimeMs=${uptimeMs} \u2014 reconnect counter reset`
4382
+ `${TAG17} session stable account=${conn.accountId} uptimeMs=${uptimeMs} \u2014 reconnect counter reset`
4196
4383
  );
4197
4384
  }
4198
4385
  if (decision.reason === "short-lived" || cycleError) {
4199
4386
  const delay = computeBackoff(Math.max(1, decision.nextAttempts));
4200
4387
  console.error(
4201
- `${TAG16} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
4388
+ `${TAG17} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
4202
4389
  );
4203
4390
  await new Promise((resolve6) => {
4204
4391
  const timer = setTimeout(resolve6, delay);
@@ -4232,11 +4419,11 @@ function watchForDisconnect(conn) {
4232
4419
  conn.sock.ev.on("connection.update", (update) => {
4233
4420
  if (update.connection === "close") {
4234
4421
  if (connections.get(conn.accountId) !== conn) return;
4235
- console.error(`${TAG16} socket disconnected for account=${conn.accountId}`);
4422
+ console.error(`${TAG17} socket disconnected for account=${conn.accountId}`);
4236
4423
  conn.connected = false;
4237
4424
  conn.sock = null;
4238
4425
  connectWithReconnect(conn).catch((err) => {
4239
- console.error(`${TAG16} reconnection failed for account=${conn.accountId}: ${formatError(err)}`);
4426
+ console.error(`${TAG17} reconnection failed for account=${conn.accountId}: ${formatError(err)}`);
4240
4427
  });
4241
4428
  }
4242
4429
  });
@@ -4271,7 +4458,7 @@ function monitorChatMetadata(conn) {
4271
4458
  }
4272
4459
  const { canonical, resolved } = await resolveCanonicalConversationJid(e.jid, conn.lidMapping);
4273
4460
  if (resolved === "lid2pn") {
4274
- console.error(`${TAG16} [wa-chatmeta] op=canonical raw=${e.jid} canonical=${canonical}`);
4461
+ console.error(`${TAG17} [wa-chatmeta] op=canonical raw=${e.jid} canonical=${canonical}`);
4275
4462
  out.push({ ...e, jid: canonical });
4276
4463
  } else {
4277
4464
  out.push(e);
@@ -4284,13 +4471,13 @@ function monitorChatMetadata(conn) {
4284
4471
  if (perJid) {
4285
4472
  for (const e of entries) {
4286
4473
  console.error(
4287
- `${TAG16} [wa-chatmeta] op=event kind=${kind} jid=${e.jid} name=${e.name.length} source=${e.source}`
4474
+ `${TAG17} [wa-chatmeta] op=event kind=${kind} jid=${e.jid} name=${e.name.length} source=${e.source}`
4288
4475
  );
4289
4476
  }
4290
4477
  }
4291
4478
  const total = writeChatMetadataMerged(account, entries);
4292
4479
  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}`
4480
+ `${TAG17} [wa-chatmeta] op=persist kind=${kind} account=${account.slice(0, 8)} updates=${entries.length} outcome=${total < 0 ? "fail" : "ok"} entries=${total}`
4294
4481
  );
4295
4482
  };
4296
4483
  sock.ev.on("messaging-history.set", ({ chats, contacts }) => {
@@ -4298,7 +4485,7 @@ function monitorChatMetadata(conn) {
4298
4485
  const chatEntries = chatEntriesFromChats(chats ?? [], nowIso);
4299
4486
  const contactEntries = chatEntriesFromContacts(contacts ?? [], nowIso);
4300
4487
  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}`
4488
+ `${TAG17} [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
4489
  );
4303
4490
  void canonicalise([...chatEntries, ...contactEntries]).then((e) => persist("history-set", e, false));
4304
4491
  });
@@ -4329,7 +4516,7 @@ function monitorChatMetadata(conn) {
4329
4516
  try {
4330
4517
  const meta = await sock.groupMetadata(jid);
4331
4518
  console.error(
4332
- `${TAG16} [wa-chatmeta] op=group-fetch jid=${jid} ms=${Date.now() - startedAt} outcome=ok`
4519
+ `${TAG17} [wa-chatmeta] op=group-fetch jid=${jid} ms=${Date.now() - startedAt} outcome=ok`
4333
4520
  );
4334
4521
  persist(
4335
4522
  "group-fetch",
@@ -4338,7 +4525,7 @@ function monitorChatMetadata(conn) {
4338
4525
  );
4339
4526
  } catch (err) {
4340
4527
  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)}`
4528
+ `${TAG17} [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
4529
  );
4343
4530
  }
4344
4531
  }
@@ -4351,7 +4538,7 @@ function monitorInbound(conn) {
4351
4538
  if (!conn.sock || !onInboundMessage) return;
4352
4539
  const sock = conn.sock;
4353
4540
  const debounceMs = whatsAppConfig.accounts?.[conn.accountId]?.debounceMs ?? whatsAppConfig.debounceMs ?? 0;
4354
- console.error(`${TAG16} monitorInbound started account=${conn.accountId} debounceMs=${debounceMs}`);
4541
+ console.error(`${TAG17} monitorInbound started account=${conn.accountId} debounceMs=${debounceMs}`);
4355
4542
  conn.debouncer = createInboundDebouncer({
4356
4543
  debounceMs,
4357
4544
  buildKey: (payload) => {
@@ -4361,12 +4548,12 @@ function monitorInbound(conn) {
4361
4548
  onFlush: (entries) => {
4362
4549
  if (!onInboundMessage) return;
4363
4550
  if (entries.length > 1) {
4364
- console.error(`${TAG16} debounce: combining ${entries.length} messages account=${conn.accountId} from=${entries[0].senderPhone}`);
4551
+ console.error(`${TAG17} debounce: combining ${entries.length} messages account=${conn.accountId} from=${entries[0].senderPhone}`);
4365
4552
  }
4366
4553
  dispatchInboundPayload(conn, combineInboundBatch(entries));
4367
4554
  },
4368
4555
  onError: (err) => {
4369
- console.error(`${TAG16} debounce flush error account=${conn.accountId}: ${String(err)}`);
4556
+ console.error(`${TAG17} debounce flush error account=${conn.accountId}: ${String(err)}`);
4370
4557
  }
4371
4558
  });
4372
4559
  sock.ev.on("messages.upsert", async (upsert) => {
@@ -4479,7 +4666,7 @@ function monitorInbound(conn) {
4479
4666
  }
4480
4667
  const entries = messageStore.get(storeKey);
4481
4668
  console.error(
4482
- `${TAG16} stored message ${msg.key.id ?? "?"} for ${remoteJid} (type: ${upsert.type}, store size: ${entries?.length ?? 0}/${MESSAGE_STORE_MAX}) storeAccount=${persistAccountId} socketOwner=${conn.platformAccountId}`
4669
+ `${TAG17} stored message ${msg.key.id ?? "?"} for ${remoteJid} (type: ${upsert.type}, store size: ${entries?.length ?? 0}/${MESSAGE_STORE_MAX}) storeAccount=${persistAccountId} socketOwner=${conn.platformAccountId}`
4483
4670
  );
4484
4671
  recordActivity({
4485
4672
  accountId: conn.accountId,
@@ -4533,7 +4720,7 @@ function monitorInbound(conn) {
4533
4720
  const wantsServableMedia = mediaServable && !!conn.sock && (!fromMe || !alreadyRecorded);
4534
4721
  if (fromMe && mediaServable && !!conn.sock) {
4535
4722
  console.error(
4536
- `${TAG16} fromMe-media-echo msgKey=${msg.key.id} decision=${alreadyRecorded ? "skip" : "store"} reason=${alreadyRecorded ? "already-recorded" : "no-prior-record"}`
4723
+ `${TAG17} fromMe-media-echo msgKey=${msg.key.id} decision=${alreadyRecorded ? "skip" : "store"} reason=${alreadyRecorded ? "already-recorded" : "no-prior-record"}`
4537
4724
  );
4538
4725
  }
4539
4726
  const mediaSeed = `whatsapp-live:${conn.accountId}:${canonicalRemoteJid}:${msg.key.id}`;
@@ -4616,7 +4803,7 @@ function monitorInbound(conn) {
4616
4803
  receiptDeferredToWorker = true;
4617
4804
  if (!accepted) {
4618
4805
  console.error(
4619
- `${TAG16} append-media overflow account=${conn.platformAccountId} msgKey=${msgKeyId ?? "?"} \u2014 pending cap ${APPEND_MEDIA_MAX_PENDING} reached, text-fallback persist, inline media shed`
4806
+ `${TAG17} append-media overflow account=${conn.platformAccountId} msgKey=${msgKeyId ?? "?"} \u2014 pending cap ${APPEND_MEDIA_MAX_PENDING} reached, text-fallback persist, inline media shed`
4620
4807
  );
4621
4808
  const persisted = await persistWhatsAppMessage({
4622
4809
  ...basePersist,
@@ -4693,13 +4880,13 @@ function monitorInbound(conn) {
4693
4880
  const skippedSenderJid = isGroupJid(remoteJid) ? msg.key.participant ?? remoteJid : remoteJid;
4694
4881
  const skippedSenderPhone = await resolveJidToE164(skippedSenderJid, conn.lidMapping) ?? skippedSenderJid;
4695
4882
  console.error(
4696
- `${TAG16} append-type message ${msg.key.id ?? "?"} dispatch skipped from=${skippedSenderPhone}${msg.pushName ? ` name=${msg.pushName}` : ""} account=${conn.accountId} (persistence not confirmed here)`
4883
+ `${TAG17} append-type message ${msg.key.id ?? "?"} dispatch skipped from=${skippedSenderPhone}${msg.pushName ? ` name=${msg.pushName}` : ""} account=${conn.accountId} (persistence not confirmed here)`
4697
4884
  );
4698
4885
  continue;
4699
4886
  }
4700
4887
  await handleInboundMessage(conn, msg);
4701
4888
  } catch (err) {
4702
- console.error(`${TAG16} inbound handler error account=${conn.accountId}: ${String(err)}`);
4889
+ console.error(`${TAG17} inbound handler error account=${conn.accountId}: ${String(err)}`);
4703
4890
  }
4704
4891
  }
4705
4892
  });
@@ -4755,31 +4942,31 @@ async function handleInboundMessage(conn, msg) {
4755
4942
  const remoteJid = msg.key.remoteJid;
4756
4943
  if (!remoteJid) return;
4757
4944
  if (remoteJid === "status@broadcast") {
4758
- console.error(`${TAG16} drop: status broadcast account=${conn.accountId}`);
4945
+ console.error(`${TAG17} drop: status broadcast account=${conn.accountId}`);
4759
4946
  return;
4760
4947
  }
4761
4948
  if (!msg.message) {
4762
- console.error(`${TAG16} drop: empty message account=${conn.accountId} from=${remoteJid}`);
4949
+ console.error(`${TAG17} drop: empty message account=${conn.accountId} from=${remoteJid}`);
4763
4950
  return;
4764
4951
  }
4765
4952
  const dedupKey = `${conn.accountId}:${remoteJid}:${msg.key.id}`;
4766
4953
  if (isDuplicateInbound(dedupKey)) {
4767
- console.error(`${TAG16} drop: duplicate account=${conn.accountId} key=${dedupKey}`);
4954
+ console.error(`${TAG17} drop: duplicate account=${conn.accountId} key=${dedupKey}`);
4768
4955
  return;
4769
4956
  }
4770
4957
  if (msg.key.fromMe) {
4771
4958
  if (msg.key.id && isAgentSentMessage(msg.key.id)) {
4772
- console.error(`${TAG16} drop: echo suppression account=${conn.accountId} msgId=${msg.key.id}`);
4959
+ console.error(`${TAG17} drop: echo suppression account=${conn.accountId} msgId=${msg.key.id}`);
4773
4960
  return;
4774
4961
  }
4775
4962
  const extracted2 = extractMessage(msg);
4776
4963
  if (!extracted2.text) {
4777
- console.error(`${TAG16} owner reply skipped \u2014 no text content account=${conn.accountId}`);
4964
+ console.error(`${TAG17} owner reply skipped \u2014 no text content account=${conn.accountId}`);
4778
4965
  return;
4779
4966
  }
4780
4967
  const isGroup2 = isGroupJid(remoteJid);
4781
4968
  const senderPhone2 = conn.selfPhone ?? "owner";
4782
- console.error(`${TAG16} owner reply mirrored to session from=${senderPhone2} account=${conn.accountId}`);
4969
+ console.error(`${TAG17} owner reply mirrored to session from=${senderPhone2} account=${conn.accountId}`);
4783
4970
  const reply2 = async (text) => {
4784
4971
  const currentSock = conn.sock;
4785
4972
  if (!currentSock) throw new Error("WhatsApp disconnected \u2014 cannot reply");
@@ -4813,7 +5000,7 @@ async function handleInboundMessage(conn, msg) {
4813
5000
  }
4814
5001
  const extracted = extractMessage(msg);
4815
5002
  if (!extracted.text && !extracted.mediaType) {
4816
- console.error(`${TAG16} drop: no text or media account=${conn.accountId} from=${remoteJid}`);
5003
+ console.error(`${TAG17} drop: no text or media account=${conn.accountId} from=${remoteJid}`);
4817
5004
  return;
4818
5005
  }
4819
5006
  const isGroup = isGroupJid(remoteJid);
@@ -4837,7 +5024,7 @@ async function handleInboundMessage(conn, msg) {
4837
5024
  });
4838
5025
  }
4839
5026
  console.error(
4840
- `${TAG16} inbound account=${conn.accountId} from=${senderPhone} group=${isGroup} access=${accessResult.allowed ? "allowed" : "blocked"}(${accessResult.reason}) agent=${accessResult.agentType}` + (extracted.mediaType ? ` media=${extracted.mediaType}` : "") + (extracted.quotedMessage ? ` replyTo=${extracted.quotedMessage.id}` : "")
5027
+ `${TAG17} inbound account=${conn.accountId} from=${senderPhone} group=${isGroup} access=${accessResult.allowed ? "allowed" : "blocked"}(${accessResult.reason}) agent=${accessResult.agentType}` + (extracted.mediaType ? ` media=${extracted.mediaType}` : "") + (extracted.quotedMessage ? ` replyTo=${extracted.quotedMessage.id}` : "")
4841
5028
  );
4842
5029
  if (!accessResult.allowed && accessResult.reason === "account-manager-unresolved") {
4843
5030
  console.error(
@@ -4874,7 +5061,7 @@ async function handleInboundMessage(conn, msg) {
4874
5061
  maxBytes: maxMb * 1024 * 1024
4875
5062
  });
4876
5063
  if (!mediaResult) {
4877
- console.error(`${TAG16} media download returned undefined account=${conn.accountId} type=${extracted.mediaType} from=${remoteJid}`);
5064
+ console.error(`${TAG17} media download returned undefined account=${conn.accountId} type=${extracted.mediaType} from=${remoteJid}`);
4878
5065
  }
4879
5066
  }
4880
5067
  if (extracted.mediaType === "audio" && mediaResult?.path) {
@@ -4896,7 +5083,7 @@ async function handleInboundMessage(conn, msg) {
4896
5083
  }
4897
5084
  const reply = async (text) => {
4898
5085
  if (isGroupJid(remoteJid)) {
4899
- console.error(`${TAG16} op=group-reply-blocked jid=${remoteJid}`);
5086
+ console.error(`${TAG17} op=group-reply-blocked jid=${remoteJid}`);
4900
5087
  throw new WaReplyError("group reply blocked \u2014 observe-only", false);
4901
5088
  }
4902
5089
  const currentSock = conn.sock;
@@ -4943,15 +5130,15 @@ async function handleInboundMessage(conn, msg) {
4943
5130
  if (accessResult.agentType === "public") {
4944
5131
  const hoursResult = await isBusinessOpen(conn.accountId);
4945
5132
  if (!hoursResult.open) {
4946
- console.error(`${TAG16} [${conn.accountId}] dispatch skipped: business closed`);
5133
+ console.error(`${TAG17} [${conn.accountId}] dispatch skipped: business closed`);
4947
5134
  const afterHoursMessage = whatsAppConfig.accounts?.[conn.accountId]?.afterHoursMessage ?? whatsAppConfig.afterHoursMessage;
4948
5135
  if (afterHoursMessage) {
4949
5136
  try {
4950
5137
  await reply(afterHoursMessage);
4951
- console.error(`${TAG16} [${conn.accountId}] after-hours auto-reply sent to ${remoteJid}`);
5138
+ console.error(`${TAG17} [${conn.accountId}] after-hours auto-reply sent to ${remoteJid}`);
4952
5139
  } catch (err) {
4953
5140
  console.error(
4954
- `${TAG16} [${conn.accountId}] after-hours auto-reply failed: ${err instanceof Error ? err.message : String(err)}`
5141
+ `${TAG17} [${conn.accountId}] after-hours auto-reply failed: ${err instanceof Error ? err.message : String(err)}`
4955
5142
  );
4956
5143
  }
4957
5144
  }
@@ -5000,6 +5187,7 @@ export {
5000
5187
  getAuthAgeMs,
5001
5188
  clearAuth,
5002
5189
  discardAuthDirSync,
5190
+ startVersionDriftCensus,
5003
5191
  drainCredsSaveQueue,
5004
5192
  createWaSocket,
5005
5193
  waitForConnection,
@@ -5007,6 +5195,8 @@ export {
5007
5195
  formatError,
5008
5196
  closeKind,
5009
5197
  classifyDisconnect,
5198
+ LINK_CENSUS_INTERVAL_MS,
5199
+ runLinkCensus,
5010
5200
  normalizeE164,
5011
5201
  toWhatsappJid,
5012
5202
  jidToE164,