@openclaw/whatsapp 2026.6.8 → 2026.6.9

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 (302) hide show
  1. package/dist/{access-control-BKaMOvIk.js → access-control-BnFVCHHH.js} +161 -50
  2. package/dist/{action-runtime-Cyp5GNIT.js → action-runtime-ClQLHIwf.js} +1 -1
  3. package/dist/action-runtime-api.js +1 -1
  4. package/dist/action-runtime.runtime.js +1 -1
  5. package/dist/api.js +10 -9
  6. package/dist/{approval-handler.runtime-BVmOu6x1.js → approval-handler.runtime-DLjPf_Mx.js} +1 -1
  7. package/dist/{auth-store-e9VzLZ-S.js → auth-store-RyuCPHE0.js} +3 -3
  8. package/dist/{channel-DyUm8hqs.js → channel-CNA0E9wp.js} +8 -9
  9. package/dist/channel-plugin-api.js +1 -1
  10. package/dist/{channel-react-action-zvbaCnGe.js → channel-react-action-CdYJKJWb.js} +3 -4
  11. package/dist/{channel.runtime-BXkzo0j1.js → channel.runtime-ZKS2C0Cw.js} +4 -4
  12. package/dist/{channel.setup-CpyAie9O.js → channel.setup-BfcLCLNn.js} +2 -2
  13. package/dist/{connection-controller-BWBoVQr4.js → connection-controller-BDuM0Kc7.js} +52 -27
  14. package/dist/contract-api.js +1 -1
  15. package/dist/{identity-Bupuq9vF.js → identity-Cm6bKAtk.js} +1 -1
  16. package/dist/light-runtime-api.js +1 -1
  17. package/dist/{login-Xv3ujrl0.js → login-B503Fwb8.js} +4 -3
  18. package/dist/{login-qr-CaJziL93.js → login-qr-AgwdQyj_.js} +16 -4
  19. package/dist/login-qr-runtime.js +1 -1
  20. package/dist/{monitor-Ggwn3FDi.js → monitor-C5_QBO1-.js} +338 -263
  21. package/dist/{quoted-message-CveINB35.js → quoted-message-DVOfpEja.js} +1 -1
  22. package/dist/runtime-api.js +7 -7
  23. package/dist/{send-DSlVXKL_.js → send-A9EqsNyz.js} +3 -1
  24. package/dist/{send-api-Cp9CUH5P.js → send-api-89SE5WKV.js} +19 -6
  25. package/dist/{session-BCKUPRkN.js → session-BW8RcCcb.js} +3 -20
  26. package/dist/{setup-core-Cp0NMO89.js → setup-core-DVEkfMVh.js} +7 -3
  27. package/dist/{setup-finalize-DnV-KlkQ.js → setup-finalize-DJepnx7S.js} +2 -2
  28. package/dist/setup-plugin-api.js +1 -1
  29. package/dist/{setup-surface-BmPcAhIF.js → setup-surface-ByAUWXyq.js} +2 -2
  30. package/dist/socket-timing-DvMNkIge.js +65 -0
  31. package/dist/{text-runtime-Dk37KYHj.js → text-runtime-BBSmIBHN.js} +6 -4
  32. package/node_modules/@protobufjs/aspromise/LICENSE +26 -0
  33. package/node_modules/@protobufjs/aspromise/README.md +13 -0
  34. package/node_modules/{protobufjs/src/util/aspromise.d.ts → @protobufjs/aspromise/index.d.ts} +13 -13
  35. package/node_modules/{protobufjs/src/util/aspromise.js → @protobufjs/aspromise/index.js} +52 -52
  36. package/node_modules/@protobufjs/aspromise/package.json +21 -0
  37. package/node_modules/@protobufjs/aspromise/tests/index.js +130 -0
  38. package/node_modules/@protobufjs/base64/LICENSE +26 -0
  39. package/node_modules/@protobufjs/base64/README.md +19 -0
  40. package/node_modules/{protobufjs/src/util/base64.d.ts → @protobufjs/base64/index.d.ts} +32 -32
  41. package/node_modules/{protobufjs/src/util/base64.js → @protobufjs/base64/index.js} +139 -146
  42. package/node_modules/@protobufjs/base64/package.json +21 -0
  43. package/node_modules/@protobufjs/base64/tests/index.js +46 -0
  44. package/node_modules/@protobufjs/codegen/LICENSE +26 -0
  45. package/node_modules/@protobufjs/codegen/README.md +49 -0
  46. package/node_modules/{protobufjs/src/util/codegen.d.ts → @protobufjs/codegen/index.d.ts} +31 -31
  47. package/node_modules/{protobufjs/src/util/codegen.js → @protobufjs/codegen/index.js} +112 -113
  48. package/node_modules/@protobufjs/codegen/package.json +13 -0
  49. package/node_modules/@protobufjs/codegen/tests/index.js +13 -0
  50. package/node_modules/@protobufjs/eventemitter/CHANGELOG.md +8 -0
  51. package/node_modules/@protobufjs/eventemitter/LICENSE +26 -0
  52. package/node_modules/@protobufjs/eventemitter/README.md +22 -0
  53. package/node_modules/{protobufjs/src/util/eventemitter.d.ts → @protobufjs/eventemitter/index.d.ts} +45 -45
  54. package/node_modules/{protobufjs/src/util/eventemitter.js → @protobufjs/eventemitter/index.js} +86 -86
  55. package/node_modules/@protobufjs/eventemitter/package.json +21 -0
  56. package/node_modules/@protobufjs/eventemitter/tests/index.js +83 -0
  57. package/node_modules/@protobufjs/fetch/CHANGELOG.md +8 -0
  58. package/node_modules/@protobufjs/fetch/LICENSE +26 -0
  59. package/node_modules/@protobufjs/fetch/README.md +13 -0
  60. package/node_modules/{protobufjs/src/util/fetch.d.ts → @protobufjs/fetch/index.d.ts} +56 -56
  61. package/node_modules/{protobufjs/src/util/fetch.js → @protobufjs/fetch/index.js} +112 -112
  62. package/node_modules/@protobufjs/fetch/package.json +27 -0
  63. package/node_modules/@protobufjs/fetch/tests/data/file.txt +1 -0
  64. package/node_modules/@protobufjs/fetch/tests/index.js +158 -0
  65. package/node_modules/@protobufjs/fetch/util/fs.js +11 -0
  66. package/node_modules/@protobufjs/float/LICENSE +26 -0
  67. package/node_modules/@protobufjs/float/README.md +102 -0
  68. package/node_modules/@protobufjs/float/bench/index.js +87 -0
  69. package/node_modules/@protobufjs/float/bench/suite.js +46 -0
  70. package/node_modules/{protobufjs/src/util/float.js → @protobufjs/float/index.js} +335 -335
  71. package/node_modules/@protobufjs/float/package.json +26 -0
  72. package/node_modules/@protobufjs/float/tests/index.js +100 -0
  73. package/node_modules/@protobufjs/inquire/CHANGELOG.md +8 -0
  74. package/node_modules/@protobufjs/inquire/LICENSE +26 -0
  75. package/node_modules/@protobufjs/inquire/README.md +13 -0
  76. package/node_modules/{protobufjs/src/util/inquire.d.ts → @protobufjs/inquire/index.d.ts} +10 -10
  77. package/node_modules/{protobufjs/src/util/inquire.js → @protobufjs/inquire/index.js} +38 -38
  78. package/node_modules/@protobufjs/inquire/package.json +21 -0
  79. package/node_modules/@protobufjs/inquire/tests/data/array.js +1 -0
  80. package/node_modules/@protobufjs/inquire/tests/data/emptyArray.js +1 -0
  81. package/node_modules/@protobufjs/inquire/tests/data/emptyObject.js +1 -0
  82. package/node_modules/@protobufjs/inquire/tests/data/object.js +1 -0
  83. package/node_modules/@protobufjs/inquire/tests/index.js +20 -0
  84. package/node_modules/@protobufjs/path/LICENSE +26 -0
  85. package/node_modules/@protobufjs/path/README.md +19 -0
  86. package/node_modules/{protobufjs/src/util/path.d.ts → @protobufjs/path/index.d.ts} +22 -22
  87. package/node_modules/{protobufjs/src/util/path.js → @protobufjs/path/index.js} +65 -72
  88. package/node_modules/@protobufjs/path/package.json +21 -0
  89. package/node_modules/@protobufjs/path/tests/index.js +60 -0
  90. package/node_modules/@protobufjs/pool/.npmignore +3 -0
  91. package/node_modules/@protobufjs/pool/LICENSE +26 -0
  92. package/node_modules/@protobufjs/pool/README.md +13 -0
  93. package/node_modules/{protobufjs/src/util/pool.d.ts → @protobufjs/pool/index.d.ts} +32 -32
  94. package/node_modules/{protobufjs/src/util/pool.js → @protobufjs/pool/index.js} +48 -48
  95. package/node_modules/@protobufjs/pool/package.json +21 -0
  96. package/node_modules/@protobufjs/pool/tests/index.js +33 -0
  97. package/node_modules/@protobufjs/utf8/LICENSE +26 -0
  98. package/node_modules/@protobufjs/utf8/README.md +20 -0
  99. package/node_modules/{protobufjs/src/util/utf8.d.ts → @protobufjs/utf8/index.d.ts} +24 -24
  100. package/node_modules/{protobufjs/src/util/utf8.js → @protobufjs/utf8/index.js} +104 -130
  101. package/node_modules/@protobufjs/utf8/package.json +21 -0
  102. package/node_modules/@protobufjs/utf8/tests/data/surrogate_pair_bug.txt +207 -0
  103. package/node_modules/@protobufjs/utf8/tests/data/utf8.txt +216 -0
  104. package/node_modules/@protobufjs/utf8/tests/index.js +74 -0
  105. package/node_modules/@types/node/LICENSE +21 -0
  106. package/node_modules/@types/node/README.md +15 -0
  107. package/node_modules/@types/node/assert/strict.d.ts +59 -0
  108. package/node_modules/@types/node/assert.d.ts +950 -0
  109. package/node_modules/@types/node/async_hooks.d.ts +711 -0
  110. package/node_modules/@types/node/buffer.buffer.d.ts +466 -0
  111. package/node_modules/@types/node/buffer.d.ts +1765 -0
  112. package/node_modules/@types/node/child_process.d.ts +1366 -0
  113. package/node_modules/@types/node/cluster.d.ts +432 -0
  114. package/node_modules/@types/node/compatibility/iterators.d.ts +21 -0
  115. package/node_modules/@types/node/console.d.ts +93 -0
  116. package/node_modules/@types/node/constants.d.ts +14 -0
  117. package/node_modules/@types/node/crypto.d.ts +4058 -0
  118. package/node_modules/@types/node/dgram.d.ts +537 -0
  119. package/node_modules/@types/node/diagnostics_channel.d.ts +552 -0
  120. package/node_modules/@types/node/dns/promises.d.ts +497 -0
  121. package/node_modules/@types/node/dns.d.ts +876 -0
  122. package/node_modules/@types/node/domain.d.ts +150 -0
  123. package/node_modules/@types/node/events.d.ts +1008 -0
  124. package/node_modules/@types/node/fs/promises.d.ts +1468 -0
  125. package/node_modules/@types/node/fs.d.ts +4780 -0
  126. package/node_modules/@types/node/globals.d.ts +150 -0
  127. package/node_modules/@types/node/globals.typedarray.d.ts +101 -0
  128. package/node_modules/@types/node/http.d.ts +2147 -0
  129. package/node_modules/@types/node/http2.d.ts +2485 -0
  130. package/node_modules/@types/node/https.d.ts +400 -0
  131. package/node_modules/@types/node/index.d.ts +117 -0
  132. package/node_modules/@types/node/inspector/promises.d.ts +35 -0
  133. package/node_modules/@types/node/inspector.d.ts +264 -0
  134. package/node_modules/@types/node/inspector.generated.d.ts +4406 -0
  135. package/node_modules/@types/node/module.d.ts +755 -0
  136. package/node_modules/@types/node/net.d.ts +970 -0
  137. package/node_modules/@types/node/os.d.ts +498 -0
  138. package/node_modules/@types/node/package.json +155 -0
  139. package/node_modules/@types/node/path/posix.d.ts +8 -0
  140. package/node_modules/@types/node/path/win32.d.ts +8 -0
  141. package/node_modules/@types/node/path.d.ts +178 -0
  142. package/node_modules/@types/node/perf_hooks.d.ts +612 -0
  143. package/node_modules/@types/node/process.d.ts +2204 -0
  144. package/node_modules/@types/node/punycode.d.ts +89 -0
  145. package/node_modules/@types/node/querystring.d.ts +139 -0
  146. package/node_modules/@types/node/quic.d.ts +897 -0
  147. package/node_modules/@types/node/readline/promises.d.ts +158 -0
  148. package/node_modules/@types/node/readline.d.ts +507 -0
  149. package/node_modules/@types/node/repl.d.ts +420 -0
  150. package/node_modules/@types/node/sea.d.ts +47 -0
  151. package/node_modules/@types/node/sqlite.d.ts +1068 -0
  152. package/node_modules/@types/node/stream/consumers.d.ts +114 -0
  153. package/node_modules/@types/node/stream/iter.d.ts +301 -0
  154. package/node_modules/@types/node/stream/promises.d.ts +211 -0
  155. package/node_modules/@types/node/stream/web.d.ts +300 -0
  156. package/node_modules/@types/node/stream.d.ts +1774 -0
  157. package/node_modules/@types/node/string_decoder.d.ts +27 -0
  158. package/node_modules/@types/node/test/reporters.d.ts +59 -0
  159. package/node_modules/@types/node/test.d.ts +2279 -0
  160. package/node_modules/@types/node/timers/promises.d.ts +93 -0
  161. package/node_modules/@types/node/timers.d.ts +149 -0
  162. package/node_modules/@types/node/tls.d.ts +1193 -0
  163. package/node_modules/@types/node/trace_events.d.ts +103 -0
  164. package/node_modules/@types/node/ts5.6/buffer.buffer.d.ts +462 -0
  165. package/node_modules/@types/node/ts5.6/compatibility/float16array.d.ts +71 -0
  166. package/node_modules/@types/node/ts5.6/globals.typedarray.d.ts +36 -0
  167. package/node_modules/@types/node/ts5.6/index.d.ts +119 -0
  168. package/node_modules/@types/node/ts5.7/compatibility/float16array.d.ts +72 -0
  169. package/node_modules/@types/node/ts5.7/index.d.ts +119 -0
  170. package/node_modules/@types/node/tty.d.ts +225 -0
  171. package/node_modules/@types/node/url.d.ts +556 -0
  172. package/node_modules/@types/node/util/types.d.ts +558 -0
  173. package/node_modules/@types/node/util.d.ts +1677 -0
  174. package/node_modules/@types/node/v8.d.ts +980 -0
  175. package/node_modules/@types/node/vm.d.ts +1136 -0
  176. package/node_modules/@types/node/wasi.d.ts +131 -0
  177. package/node_modules/@types/node/web-globals/abortcontroller.d.ts +59 -0
  178. package/node_modules/@types/node/web-globals/blob.d.ts +23 -0
  179. package/node_modules/@types/node/web-globals/console.d.ts +9 -0
  180. package/node_modules/@types/node/web-globals/crypto.d.ts +39 -0
  181. package/node_modules/@types/node/web-globals/domexception.d.ts +68 -0
  182. package/node_modules/@types/node/web-globals/encoding.d.ts +11 -0
  183. package/node_modules/@types/node/web-globals/events.d.ts +106 -0
  184. package/node_modules/@types/node/web-globals/fetch.d.ts +69 -0
  185. package/node_modules/@types/node/web-globals/importmeta.d.ts +13 -0
  186. package/node_modules/@types/node/web-globals/messaging.d.ts +23 -0
  187. package/node_modules/@types/node/web-globals/navigator.d.ts +25 -0
  188. package/node_modules/@types/node/web-globals/performance.d.ts +45 -0
  189. package/node_modules/@types/node/web-globals/storage.d.ts +24 -0
  190. package/node_modules/@types/node/web-globals/streams.d.ts +115 -0
  191. package/node_modules/@types/node/web-globals/timers.d.ts +44 -0
  192. package/node_modules/@types/node/web-globals/url.d.ts +24 -0
  193. package/node_modules/@types/node/worker_threads.d.ts +671 -0
  194. package/node_modules/@types/node/zlib/iter.d.ts +131 -0
  195. package/node_modules/@types/node/zlib.d.ts +589 -0
  196. package/node_modules/protobufjs/README.md +556 -263
  197. package/node_modules/protobufjs/dist/light/protobuf.js +1569 -1919
  198. package/node_modules/protobufjs/dist/light/protobuf.js.map +1 -1
  199. package/node_modules/protobufjs/dist/light/protobuf.min.js +3 -3
  200. package/node_modules/protobufjs/dist/light/protobuf.min.js.map +1 -1
  201. package/node_modules/protobufjs/dist/minimal/protobuf.js +906 -1132
  202. package/node_modules/protobufjs/dist/minimal/protobuf.js.map +1 -1
  203. package/node_modules/protobufjs/dist/minimal/protobuf.min.js +3 -3
  204. package/node_modules/protobufjs/dist/minimal/protobuf.min.js.map +1 -1
  205. package/node_modules/protobufjs/dist/protobuf.js +1576 -1993
  206. package/node_modules/protobufjs/dist/protobuf.js.map +1 -1
  207. package/node_modules/protobufjs/dist/protobuf.min.js +3 -3
  208. package/node_modules/protobufjs/dist/protobuf.min.js.map +1 -1
  209. package/node_modules/protobufjs/ext/descriptor/README.md +70 -3
  210. package/node_modules/protobufjs/ext/descriptor/index.d.ts +193 -0
  211. package/node_modules/protobufjs/ext/descriptor/index.js +1185 -1
  212. package/node_modules/protobufjs/ext/descriptor/test.js +54 -0
  213. package/node_modules/protobufjs/index.d.ts +720 -834
  214. package/node_modules/protobufjs/package.json +44 -20
  215. package/node_modules/protobufjs/src/converter.js +36 -60
  216. package/node_modules/protobufjs/src/decoder.js +53 -124
  217. package/node_modules/protobufjs/src/encoder.js +2 -10
  218. package/node_modules/protobufjs/src/enum.js +3 -8
  219. package/node_modules/protobufjs/src/field.js +11 -13
  220. package/node_modules/protobufjs/src/index-light.js +1 -1
  221. package/node_modules/protobufjs/src/mapfield.js +0 -1
  222. package/node_modules/protobufjs/src/message.js +11 -5
  223. package/node_modules/protobufjs/src/method.js +4 -5
  224. package/node_modules/protobufjs/src/namespace.js +3 -10
  225. package/node_modules/protobufjs/src/object.js +20 -20
  226. package/node_modules/protobufjs/src/oneof.js +0 -3
  227. package/node_modules/protobufjs/src/parse.js +16 -78
  228. package/node_modules/protobufjs/src/reader.js +31 -136
  229. package/node_modules/protobufjs/src/reader_buffer.js +3 -24
  230. package/node_modules/protobufjs/src/root.js +3 -7
  231. package/node_modules/protobufjs/src/roots.js +1 -1
  232. package/node_modules/protobufjs/src/service.js +6 -13
  233. package/node_modules/protobufjs/src/tokenize.js +1 -6
  234. package/node_modules/protobufjs/src/type.js +30 -44
  235. package/node_modules/protobufjs/src/typescript.jsdoc +15 -0
  236. package/node_modules/protobufjs/src/util/minimal.js +12 -36
  237. package/node_modules/protobufjs/src/util.js +17 -6
  238. package/node_modules/protobufjs/src/verifier.js +6 -6
  239. package/node_modules/protobufjs/src/wrappers.js +1 -0
  240. package/node_modules/protobufjs/src/writer.js +16 -42
  241. package/node_modules/protobufjs/src/writer_buffer.js +1 -18
  242. package/node_modules/protobufjs/tsconfig.json +5 -3
  243. package/node_modules/undici-types/LICENSE +21 -0
  244. package/node_modules/undici-types/README.md +6 -0
  245. package/node_modules/undici-types/agent.d.ts +32 -0
  246. package/node_modules/undici-types/api.d.ts +43 -0
  247. package/node_modules/undici-types/balanced-pool.d.ts +30 -0
  248. package/node_modules/undici-types/cache-interceptor.d.ts +179 -0
  249. package/node_modules/undici-types/cache.d.ts +36 -0
  250. package/node_modules/undici-types/client-stats.d.ts +15 -0
  251. package/node_modules/undici-types/client.d.ts +123 -0
  252. package/node_modules/undici-types/connector.d.ts +36 -0
  253. package/node_modules/undici-types/content-type.d.ts +21 -0
  254. package/node_modules/undici-types/cookies.d.ts +30 -0
  255. package/node_modules/undici-types/diagnostics-channel.d.ts +74 -0
  256. package/node_modules/undici-types/dispatcher.d.ts +279 -0
  257. package/node_modules/undici-types/env-http-proxy-agent.d.ts +22 -0
  258. package/node_modules/undici-types/errors.d.ts +177 -0
  259. package/node_modules/undici-types/eventsource.d.ts +66 -0
  260. package/node_modules/undici-types/fetch.d.ts +211 -0
  261. package/node_modules/undici-types/formdata.d.ts +108 -0
  262. package/node_modules/undici-types/global-dispatcher.d.ts +9 -0
  263. package/node_modules/undici-types/global-origin.d.ts +7 -0
  264. package/node_modules/undici-types/h2c-client.d.ts +73 -0
  265. package/node_modules/undici-types/handlers.d.ts +15 -0
  266. package/node_modules/undici-types/header.d.ts +160 -0
  267. package/node_modules/undici-types/index.d.ts +91 -0
  268. package/node_modules/undici-types/interceptors.d.ts +80 -0
  269. package/node_modules/undici-types/mock-agent.d.ts +68 -0
  270. package/node_modules/undici-types/mock-call-history.d.ts +111 -0
  271. package/node_modules/undici-types/mock-client.d.ts +27 -0
  272. package/node_modules/undici-types/mock-errors.d.ts +12 -0
  273. package/node_modules/undici-types/mock-interceptor.d.ts +94 -0
  274. package/node_modules/undici-types/mock-pool.d.ts +27 -0
  275. package/node_modules/undici-types/package.json +55 -0
  276. package/node_modules/undici-types/patch.d.ts +29 -0
  277. package/node_modules/undici-types/pool-stats.d.ts +19 -0
  278. package/node_modules/undici-types/pool.d.ts +41 -0
  279. package/node_modules/undici-types/proxy-agent.d.ts +29 -0
  280. package/node_modules/undici-types/readable.d.ts +68 -0
  281. package/node_modules/undici-types/retry-agent.d.ts +8 -0
  282. package/node_modules/undici-types/retry-handler.d.ts +125 -0
  283. package/node_modules/undici-types/round-robin-pool.d.ts +41 -0
  284. package/node_modules/undici-types/snapshot-agent.d.ts +109 -0
  285. package/node_modules/undici-types/socks5-proxy-agent.d.ts +25 -0
  286. package/node_modules/undici-types/util.d.ts +18 -0
  287. package/node_modules/undici-types/utility.d.ts +7 -0
  288. package/node_modules/undici-types/webidl.d.ts +347 -0
  289. package/node_modules/undici-types/websocket.d.ts +188 -0
  290. package/npm-shrinkwrap.json +95 -6
  291. package/package.json +4 -4
  292. package/node_modules/protobufjs/ext/README.md +0 -81
  293. package/node_modules/protobufjs/ext/descriptor.d.ts +0 -86
  294. package/node_modules/protobufjs/ext/descriptor.generated.d.ts +0 -409
  295. package/node_modules/protobufjs/ext/descriptor.js +0 -1266
  296. package/node_modules/protobufjs/ext/textformat.d.ts +0 -19
  297. package/node_modules/protobufjs/ext/textformat.generated.d.ts +0 -11
  298. package/node_modules/protobufjs/ext/textformat.js +0 -1235
  299. package/node_modules/protobufjs/google/protobuf/compiler/plugin.json +0 -126
  300. package/node_modules/protobufjs/google/protobuf/compiler/plugin.proto +0 -47
  301. package/node_modules/protobufjs/src/typescript.js +0 -25
  302. /package/node_modules/{protobufjs/src/util/float.d.ts → @protobufjs/float/index.d.ts} +0 -0
@@ -1,18 +1,18 @@
1
1
  import { a as resolveWhatsAppAccount, s as resolveWhatsAppMediaMaxBytes } from "./accounts-DgViSyJx.js";
2
- import { t as isWhatsAppGroupJid } from "./normalize-target-bVWjgftN.js";
3
- import { a as normalizeWhatsAppOutboundPayload, c as prepareWhatsAppOutboundMedia, d as maybeResolveWhatsAppApprovalReaction, g as resolveWhatsAppReactionLevel, l as sendWhatsAppOutboundWithRetry, r as sendReactionWhatsApp, s as normalizeWhatsAppPayloadTextPreservingIndentation } from "./send-DSlVXKL_.js";
2
+ import { a as normalizeWhatsAppOutboundPayload, c as prepareWhatsAppOutboundMedia, d as maybeResolveWhatsAppApprovalReaction, g as resolveWhatsAppReactionLevel, l as sendWhatsAppOutboundWithRetry, r as sendReactionWhatsApp, s as normalizeWhatsAppPayloadTextPreservingIndentation } from "./send-A9EqsNyz.js";
4
3
  import { n as getWhatsAppRuntime } from "./runtime-BfAdAEYT.js";
5
4
  import { n as getStatusCode, r as getRegisteredWhatsAppConnectionController, t as formatError } from "./session-errors-CbsoQqoy.js";
6
- import { d as markdownToWhatsApp, f as resolveJidToE164, l as isSelfChatMode, n as normalizeE164, t as convertMarkdownTables$1, u as jidToE164 } from "./text-runtime-Dk37KYHj.js";
7
- import { n as cacheInboundMessageMeta, r as lookupInboundMessageMeta, t as buildQuotedMessageOptions } from "./quoted-message-CveINB35.js";
8
- import { c as logoutWeb, g as readWebSelfId, o as getWebAuthAgeMs, r as WhatsAppAuthUnstableError, v as readWebSelfIdentityForDecision } from "./auth-store-e9VzLZ-S.js";
9
- import { a as getSelfIdentity, c as resolveComparableIdentity, i as getReplyContext, n as getMentionIdentities, o as getSenderIdentity, r as getPrimaryIdentityId, s as identitiesOverlap, t as getComparableIdentityValues } from "./identity-Bupuq9vF.js";
5
+ import { a as resolveWhatsAppSocketTiming, i as resolveWhatsAppSocketOperationTimeoutMs, n as createWhatsAppSocketOperationTimeoutAdapter, o as withWhatsAppSocketOperationTimeout, r as isWhatsAppSocketOperationTimeoutError } from "./socket-timing-DvMNkIge.js";
6
+ import { d as markdownToWhatsApp, f as resolveJidToE164, l as isSelfChatMode, n as normalizeE164, t as convertMarkdownTables$1, u as jidToE164 } from "./text-runtime-BBSmIBHN.js";
7
+ import { n as cacheInboundMessageMeta, r as lookupInboundMessageMeta, t as buildQuotedMessageOptions } from "./quoted-message-DVOfpEja.js";
8
+ import { g as readWebSelfId, o as getWebAuthAgeMs, r as WhatsAppAuthUnstableError, v as readWebSelfIdentityForDecision } from "./auth-store-RyuCPHE0.js";
9
+ import { a as getSelfIdentity, c as resolveComparableIdentity, i as getReplyContext, n as getMentionIdentities, o as getSenderIdentity, r as getPrimaryIdentityId, s as identitiesOverlap, t as getComparableIdentityValues } from "./identity-Cm6bKAtk.js";
10
10
  import { n as resolveWhatsAppGroupsConfigPath } from "./group-config-path-BGyzT9Lg.js";
11
- import { i as resolveWhatsAppInboundPolicy, r as resolveWhatsAppCommandAuthorized, t as checkInboundAccessControl } from "./access-control-BKaMOvIk.js";
12
- import { _ as extractMediaPlaceholder, a as mayContainWhatsAppOutboundMention, b as hasInboundUserContent, c as DisconnectReason, d as normalizeMessageContent, f as resolveInboundMediaMimetype, g as extractLocationData, h as extractContextInfo, i as addWhatsAppOutboundMentionsToContent, l as downloadMediaMessage, m as extractContactContext, n as listWhatsAppSendResultMessageIds, o as resolveWhatsAppOutboundMentions, p as describeReplyContext, r as normalizeWhatsAppSendResult, s as addWhatsAppImagePreviewFields, t as createWebSendApi, u as isJidGroup, v as extractMentionedJids, y as extractText } from "./send-api-Cp9CUH5P.js";
13
- import { a as resolveWhatsAppSocketTiming, r as waitForWaConnection, t as createWaSocket } from "./session-BCKUPRkN.js";
11
+ import { c as requireWhatsAppInboundAdmission, i as resolveWhatsAppInboundPolicy, l as resolveWhatsAppGroupConversationId, o as buildDeprecatedFlatWhatsAppInboundAdmission, r as resolveWhatsAppCommandAuthorized, s as requireAdmittedWhatsAppInboundMessage, t as checkInboundAccessControl } from "./access-control-BnFVCHHH.js";
12
+ import { _ as extractLocationData, a as mayContainWhatsAppOutboundMention, b as extractText, c as DisconnectReason, d as normalizeMessageContent, f as resolveInboundMediaMimetype, g as extractExternalAdReplyContext, h as extractContextInfo, i as addWhatsAppOutboundMentionsToContent, l as downloadMediaMessage, m as extractContactContext, n as listWhatsAppSendResultMessageIds, o as resolveWhatsAppOutboundMentions, p as describeReplyContext, r as normalizeWhatsAppSendResult, s as addWhatsAppImagePreviewFields, t as createWebSendApi, u as isJidGroup, v as extractMediaPlaceholder, x as hasInboundUserContent, y as extractMentionedJids } from "./send-api-89SE5WKV.js";
13
+ import { r as waitForWaConnection, t as createWaSocket } from "./session-BW8RcCcb.js";
14
14
  import { t as BufferJSON } from "./session.runtime-CyooSQvj.js";
15
- import { c as computeBackoff, d as resolveReconnectPolicy, f as sleepWithAbort, l as newConnectionId, n as WHATSAPP_WATCHDOG_TIMEOUT_ERROR, r as WhatsAppConnectionController, s as DEFAULT_RECONNECT_POLICY, u as resolveHeartbeatSeconds } from "./connection-controller-BWBoVQr4.js";
15
+ import { c as computeBackoff, d as resolveReconnectPolicy, f as sleepWithAbort, l as newConnectionId, n as WHATSAPP_WATCHDOG_TIMEOUT_ERROR, r as WhatsAppConnectionController, s as DEFAULT_RECONNECT_POLICY, u as resolveHeartbeatSeconds } from "./connection-controller-BDuM0Kc7.js";
16
16
  import { resolveAccountEntry } from "openclaw/plugin-sdk/account-core";
17
17
  import { normalizeLowercaseStringOrEmpty, normalizeStringEntries, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
18
18
  import { createChannelMessageReplyPipeline, createDurableInboundReceiveJournalFromQueue, createMessageReceiptFromOutboundResults, deliverInboundReplyWithMessageSendContext, resolveChannelMessageSourceReplyDeliveryMode } from "openclaw/plugin-sdk/channel-outbound";
@@ -23,11 +23,11 @@ import { getChildLogger } from "openclaw/plugin-sdk/logging-core";
23
23
  import { resolveMarkdownTableMode as resolveMarkdownTableMode$1 } from "openclaw/plugin-sdk/markdown-table-runtime";
24
24
  import { createSubsystemLogger, defaultRuntime, formatDurationPrecise, getChildLogger as getChildLogger$1, logVerbose, logVerbose as logVerbose$1, registerUnhandledRejectionHandler, shouldLogVerbose, shouldLogVerbose as shouldLogVerbose$1, warn } from "openclaw/plugin-sdk/runtime-env";
25
25
  import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-runtime";
26
+ import { asDateTimestampMs, parseStrictFiniteNumber, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
26
27
  import { LocalMediaAccessError, getDefaultLocalRoots, loadWebMedia as loadWebMedia$1, loadWebMediaRaw, optimizeImageToJpeg, optimizeImageToPng } from "openclaw/plugin-sdk/web-media";
27
28
  import { chunkMarkdownTextWithMode } from "openclaw/plugin-sdk/reply-chunking";
28
29
  import { isReasoningReplyPayload, resolveSendableOutboundReplyParts, sendMediaWithLeadingCaption } from "openclaw/plugin-sdk/reply-payload";
29
30
  import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, DEFAULT_MAIN_KEY, buildAgentMainSessionKey, buildAgentSessionKey, buildGroupHistoryKey, deriveLastRoutePolicy, normalizeAccountId as normalizeAccountId$1, normalizeAgentId, resolveAgentRoute, resolveInboundLastRouteSessionKey, resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
30
- import { asDateTimestampMs, parseStrictFiniteNumber, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
31
31
  import { recordInboundSession } from "openclaw/plugin-sdk/conversation-runtime";
32
32
  import { loadSessionStore, resolveStorePath, updateLastRoute, updateSessionStore } from "openclaw/plugin-sdk/session-store-runtime";
33
33
  import { buildChannelInboundEventContext, filterChannelInboundQuoteContext, formatInboundEnvelope, formatInboundEnvelope as formatInboundEnvelope$1, formatLocationText, hasVisibleInboundReplyDispatch, resolveInboundSessionEnvelopeContext, runChannelInboundEvent, toInboundMediaFacts, toLocationContext } from "openclaw/plugin-sdk/channel-inbound";
@@ -110,7 +110,7 @@ function setMediaField(msg, key, value) {
110
110
  if (value === void 0 && !msg.payload.media) return;
111
111
  ensureMedia(msg)[key] = value;
112
112
  }
113
- function defineDeprecatedAliasAccessors(msg, descriptors) {
113
+ function defineDeprecatedAccessors(msg, descriptors) {
114
114
  Object.defineProperties(msg, Object.fromEntries(Object.entries(descriptors).map(([key, descriptor]) => [key, {
115
115
  configurable: true,
116
116
  enumerable: true,
@@ -119,8 +119,62 @@ function defineDeprecatedAliasAccessors(msg, descriptors) {
119
119
  }])));
120
120
  return msg;
121
121
  }
122
+ function defineDeprecatedAliasAccessors(msg, descriptors) {
123
+ defineDeprecatedAccessors(msg, descriptors);
124
+ return msg;
125
+ }
126
+ function defineDeprecatedAdmissionTopLevelAccessors(msg) {
127
+ let fallbackConversationId = msg.conversationId || msg.from;
128
+ let fallbackAccountId = msg.accountId;
129
+ let fallbackAccessControlPassed = msg.accessControlPassed;
130
+ let fallbackChatType = msg.chatType;
131
+ const conversationId = () => msg.admission?.conversation.id ?? fallbackConversationId;
132
+ const setConversationId = (value) => {
133
+ const next = value;
134
+ fallbackConversationId = next;
135
+ if (msg.admission) {
136
+ msg.admission.conversation.id = next;
137
+ msg.admission.conversation.groupSessionId = resolveWhatsAppGroupConversationId(next);
138
+ }
139
+ };
140
+ return defineDeprecatedAccessors(msg, {
141
+ from: {
142
+ get: conversationId,
143
+ set: setConversationId
144
+ },
145
+ conversationId: {
146
+ get: conversationId,
147
+ set: setConversationId
148
+ },
149
+ accountId: {
150
+ get: () => msg.admission?.accountId ?? fallbackAccountId,
151
+ set: (value) => {
152
+ const next = value;
153
+ fallbackAccountId = next;
154
+ if (msg.admission) {
155
+ msg.admission.accountId = next;
156
+ msg.admission.account.accountId = next;
157
+ }
158
+ }
159
+ },
160
+ accessControlPassed: {
161
+ get: () => msg.admission ? msg.admission.ingress.decision === "allow" : fallbackAccessControlPassed,
162
+ set: (value) => {
163
+ fallbackAccessControlPassed = value;
164
+ }
165
+ },
166
+ chatType: {
167
+ get: () => msg.admission?.conversation.kind ?? fallbackChatType,
168
+ set: (value) => {
169
+ const next = value;
170
+ fallbackChatType = next;
171
+ if (msg.admission) msg.admission.conversation.kind = next;
172
+ }
173
+ }
174
+ });
175
+ }
122
176
  function withDeprecatedWebInboundMessageFlatAliases(msg) {
123
- return defineDeprecatedAliasAccessors(msg, {
177
+ return defineDeprecatedAliasAccessors(defineDeprecatedAdmissionTopLevelAccessors(msg), {
124
178
  id: {
125
179
  get: () => msg.event.id,
126
180
  set: (value) => msg.event.id = value
@@ -284,6 +338,7 @@ function normalizeLegacyFlatWebInboundMessage(msg) {
284
338
  } : void 0;
285
339
  return withDeprecatedWebInboundMessageFlatAliases({
286
340
  ...msg,
341
+ admission: msg.admission ?? buildDeprecatedFlatWhatsAppInboundAdmission(msg),
287
342
  event: {
288
343
  id: msg.id,
289
344
  timestamp: msg.timestamp,
@@ -571,6 +626,7 @@ function recordAcceptedInboundActivity(accountId) {
571
626
  });
572
627
  }
573
628
  function isRetryableSendDisconnectError(err) {
629
+ if (isWhatsAppSocketOperationTimeoutError(err)) return false;
574
630
  return /closed|reset|timed\s*out|disconnect|no active socket/i.test(formatError(err));
575
631
  }
576
632
  function shouldClearSocketRefAfterSendFailure(err) {
@@ -588,6 +644,7 @@ async function attachWebInboxToSocket(options) {
588
644
  const shouldRetryDisconnect = () => options.shouldRetryDisconnect?.() === true;
589
645
  const disconnectRetryPolicy = options.disconnectRetryPolicy ?? DEFAULT_RECONNECT_POLICY;
590
646
  const sendRetryMaxAttempts = disconnectRetryPolicy.maxAttempts > 0 ? disconnectRetryPolicy.maxAttempts : DEFAULT_RECONNECT_POLICY.maxAttempts;
647
+ const sendOperationTimeoutMs = resolveWhatsAppSocketOperationTimeoutMs(options.socketTiming.defaultQueryTimeoutMs);
591
648
  let onCloseResolve = null;
592
649
  const onClose = new Promise((resolve) => {
593
650
  onCloseResolve = resolve;
@@ -600,7 +657,7 @@ async function attachWebInboxToSocket(options) {
600
657
  };
601
658
  const presence = options.selfChatMode ? "unavailable" : "available";
602
659
  try {
603
- await sock.sendPresenceUpdate(presence);
660
+ await createWhatsAppSocketOperationTimeoutAdapter(sock, sendOperationTimeoutMs).sendPresenceUpdate(presence);
604
661
  logWhatsAppVerbose(options.verbose, `Sent global '${presence}' presence on connect`);
605
662
  } catch (err) {
606
663
  logWhatsAppVerbose(options.verbose, `Failed to send '${presence}' presence on connect: ${String(err)}`);
@@ -623,11 +680,11 @@ async function attachWebInboxToSocket(options) {
623
680
  const pendingDebounceKeys = /* @__PURE__ */ new Set();
624
681
  const activeInboundFlushes = /* @__PURE__ */ new Set();
625
682
  const buildInboundDebounceKey = (msg) => {
683
+ const admission = requireWhatsAppInboundAdmission(msg);
626
684
  const sender = msg.platform.sender;
627
- const senderKey = msg.chatType === "group" ? getPrimaryIdentityId(sender ?? null) ?? msg.platform.senderJid ?? msg.platform.senderE164 ?? msg.platform.senderName ?? msg.from : msg.from;
685
+ const senderKey = admission.conversation.kind === "group" ? getPrimaryIdentityId(sender ?? null) ?? msg.platform.senderJid ?? msg.platform.senderE164 ?? msg.platform.senderName ?? admission.sender.id : admission.conversation.id;
628
686
  if (!senderKey) return null;
629
- const conversationKey = msg.chatType === "group" ? msg.platform.chatJid : msg.from;
630
- return `${msg.accountId}:${conversationKey}:${senderKey}`;
687
+ return `${admission.accountId}:${admission.conversation.id}:${senderKey}`;
631
688
  };
632
689
  const shouldDebounceInboundMessage = (msg) => options.shouldDebounce?.(msg) ?? true;
633
690
  const orderDebouncedInboundEntries = (entries) => entries.toSorted((a, b) => {
@@ -723,12 +780,19 @@ async function attachWebInboxToSocket(options) {
723
780
  messageId
724
781
  });
725
782
  };
783
+ const trackLateAcceptedSend = (jid, promise) => {
784
+ promise.then((result) => {
785
+ rememberOutboundMessage(jid, result);
786
+ }, () => {});
787
+ };
726
788
  const sendTrackedMessage = async (jid, content, sendOptions) => {
727
789
  let lastErr = /* @__PURE__ */ new Error(RECONNECT_IN_PROGRESS_ERROR);
728
790
  for (let attempt = 1;; attempt++) {
729
791
  const currentSock = getCurrentSock();
730
792
  if (currentSock) try {
731
- const result = sendOptions ? await currentSock.sendMessage(jid, content, sendOptions) : await currentSock.sendMessage(jid, content);
793
+ const result = await createWhatsAppSocketOperationTimeoutAdapter(currentSock, sendOperationTimeoutMs, { onSendMessageTimeout: ({ jid: timedOutJid, promise }) => {
794
+ trackLateAcceptedSend(timedOutJid, promise);
795
+ } }).sendMessage(jid, content, sendOptions);
732
796
  rememberOutboundMessage(jid, result);
733
797
  return result;
734
798
  } catch (err) {
@@ -747,6 +811,14 @@ async function attachWebInboxToSocket(options) {
747
811
  }
748
812
  }
749
813
  };
814
+ const sendApiSocketOperations = {
815
+ sendMessage: (jid, content, sendOptions) => sendTrackedMessage(jid, content, sendOptions),
816
+ sendPresenceUpdate: async (presenceLocal, jid) => {
817
+ const currentSock = getCurrentSock();
818
+ if (!currentSock) throw new Error(RECONNECT_IN_PROGRESS_ERROR);
819
+ return await createWhatsAppSocketOperationTimeoutAdapter(currentSock, sendOperationTimeoutMs).sendPresenceUpdate(presenceLocal, jid);
820
+ }
821
+ };
750
822
  const summarizeGroupMeta = async (meta) => {
751
823
  const participantEntries = await Promise.all(meta.participants?.map(async (p) => {
752
824
  const mapped = await resolveInboundJid(p.id);
@@ -858,6 +930,7 @@ async function attachWebInboxToSocket(options) {
858
930
  from,
859
931
  selfE164: self.e164 ?? null,
860
932
  senderE164,
933
+ senderJid: participantJid,
861
934
  group,
862
935
  pushName: msg.pushName ?? void 0,
863
936
  isFromMe: Boolean(msg.key?.fromMe),
@@ -890,12 +963,12 @@ async function attachWebInboxToSocket(options) {
890
963
  if (!target || options.sendReadReceipts === false) return;
891
964
  const { id, remoteJid, participant } = target;
892
965
  try {
893
- await (getCurrentSock() ?? sock).readMessages([{
966
+ await withWhatsAppSocketOperationTimeout("readMessages", (getCurrentSock() ?? sock).readMessages([{
894
967
  remoteJid,
895
968
  id,
896
969
  participant,
897
970
  fromMe: false
898
- }]);
971
+ }]), sendOperationTimeoutMs);
899
972
  const suffix = participant ? ` (participant ${participant})` : "";
900
973
  logWhatsAppVerbose(options.verbose, `Marked message ${id} as read for ${remoteJid}${suffix}`);
901
974
  } catch (err) {
@@ -905,14 +978,7 @@ async function attachWebInboxToSocket(options) {
905
978
  const maybeLogSkippedSelfChatReadReceipt = (inbound, target) => {
906
979
  if (target?.id && inbound.access.isSelfChat && options.verbose) logWhatsAppVerbose(options.verbose, `Self-chat mode: skipping read receipt for ${target.id}`);
907
980
  };
908
- const maybeMarkReadReceiptAfterCompletedDelivery = async (inbound, target) => {
909
- if (inbound.access.isSelfChat) {
910
- maybeLogSkippedSelfChatReadReceipt(inbound, target);
911
- return;
912
- }
913
- await maybeMarkInboundAsRead(target);
914
- };
915
- const maybeMarkReadReceiptForSkippedAppend = async (inbound, target) => {
981
+ const maybeMarkNonSelfChatReadReceipt = async (inbound, target) => {
916
982
  if (inbound.access.isSelfChat) {
917
983
  maybeLogSkippedSelfChatReadReceipt(inbound, target);
918
984
  return;
@@ -943,7 +1009,7 @@ async function attachWebInboxToSocket(options) {
943
1009
  const readReceipt = stored?.metadata?.readReceipt ?? buildReadReceiptTarget(inbound);
944
1010
  const deliveryReadReceipt = inbound.access.isSelfChat ? void 0 : readReceipt;
945
1011
  if (!stored && shouldSkipStaleAppend(msg, upsertType)) {
946
- await maybeMarkReadReceiptForSkippedAppend(inbound, readReceipt);
1012
+ await maybeMarkNonSelfChatReadReceipt(inbound, readReceipt);
947
1013
  return;
948
1014
  }
949
1015
  let durableId = stored?.id ?? (inbound.id ? createWhatsAppDurableInboundMessageId({
@@ -957,7 +1023,7 @@ async function attachWebInboxToSocket(options) {
957
1023
  receivedAt: inbound.messageTimestampMs
958
1024
  });
959
1025
  if (accepted.kind === "completed") {
960
- await maybeMarkReadReceiptAfterCompletedDelivery(inbound, accepted.record.metadata?.readReceipt ?? deliveryReadReceipt);
1026
+ await maybeMarkNonSelfChatReadReceipt(inbound, accepted.record.metadata?.readReceipt ?? deliveryReadReceipt);
961
1027
  return;
962
1028
  }
963
1029
  if (accepted.kind === "pending" && accepted.record.attempts === 0) return;
@@ -970,7 +1036,7 @@ async function attachWebInboxToSocket(options) {
970
1036
  const enriched = await enrichInboundMessage(msg);
971
1037
  if (!enriched) {
972
1038
  await completeUndeliverableDurableInbound(durableId, durableMetadata);
973
- await maybeMarkReadReceiptAfterCompletedDelivery(inbound, deliveryReadReceipt);
1039
+ await maybeMarkNonSelfChatReadReceipt(inbound, deliveryReadReceipt);
974
1040
  return;
975
1041
  }
976
1042
  const dedupeKey = inbound.id ? `${options.accountId}:${inbound.remoteJid}:${inbound.id}` : "";
@@ -978,7 +1044,7 @@ async function attachWebInboxToSocket(options) {
978
1044
  if (dedupeClaim !== "claimed") {
979
1045
  if (dedupeClaim === "duplicate") {
980
1046
  await completeUndeliverableDurableInbound(durableId, durableMetadata);
981
- await maybeMarkReadReceiptAfterCompletedDelivery(inbound, deliveryReadReceipt);
1047
+ await maybeMarkNonSelfChatReadReceipt(inbound, deliveryReadReceipt);
982
1048
  }
983
1049
  return;
984
1050
  }
@@ -1001,6 +1067,7 @@ async function attachWebInboxToSocket(options) {
1001
1067
  const location = extractLocationData(msg.message ?? void 0);
1002
1068
  const locationText = location ? formatLocationText(location) : void 0;
1003
1069
  const contactContext = extractContactContext(msg.message ?? void 0);
1070
+ const externalAdReplyContext = extractExternalAdReplyContext(msg.message ?? void 0);
1004
1071
  let body = extractText(msg.message ?? void 0);
1005
1072
  if (locationText) body = [body, locationText].filter(Boolean).join("\n").trim();
1006
1073
  if (!body) {
@@ -1028,6 +1095,7 @@ async function attachWebInboxToSocket(options) {
1028
1095
  body,
1029
1096
  location: location ?? void 0,
1030
1097
  contactContext,
1098
+ externalAdReplyContext,
1031
1099
  replyContext,
1032
1100
  mediaPath,
1033
1101
  mediaType,
@@ -1037,10 +1105,9 @@ async function attachWebInboxToSocket(options) {
1037
1105
  const enqueueInboundMessage = async (msg, inbound, enriched, durable) => {
1038
1106
  const chatJid = inbound.remoteJid;
1039
1107
  const sendComposing = async () => {
1040
- const currentSock = getCurrentSock();
1041
- if (!currentSock) return;
1108
+ if (!getCurrentSock()) return;
1042
1109
  try {
1043
- await currentSock.sendPresenceUpdate("composing", chatJid);
1110
+ await sendApiSocketOperations.sendPresenceUpdate("composing", chatJid);
1044
1111
  } catch (err) {
1045
1112
  logWhatsAppVerbose(options.verbose, `Presence update failed: ${String(err)}`);
1046
1113
  }
@@ -1075,7 +1142,19 @@ async function attachWebInboxToSocket(options) {
1075
1142
  participants: inbound.groupParticipants,
1076
1143
  mentions: groupMentions
1077
1144
  } : void 0;
1145
+ const untrustedStructuredContext = [...enriched.contactContext ? [{
1146
+ label: "WhatsApp contact",
1147
+ source: "whatsapp",
1148
+ type: enriched.contactContext.kind,
1149
+ payload: enriched.contactContext
1150
+ }] : [], ...enriched.externalAdReplyContext ? [{
1151
+ label: "WhatsApp external ad reply",
1152
+ source: "whatsapp",
1153
+ type: "external_ad_reply",
1154
+ payload: enriched.externalAdReplyContext
1155
+ }] : []];
1078
1156
  const inboundMessage = withDeprecatedWebInboundMessageFlatAliases({
1157
+ admission: inbound.access.admission,
1079
1158
  event: {
1080
1159
  id: inbound.id,
1081
1160
  timestamp
@@ -1083,12 +1162,7 @@ async function attachWebInboxToSocket(options) {
1083
1162
  payload: {
1084
1163
  body: enriched.body,
1085
1164
  location: enriched.location ?? void 0,
1086
- untrustedStructuredContext: enriched.contactContext ? [{
1087
- label: "WhatsApp contact",
1088
- source: "whatsapp",
1089
- type: enriched.contactContext.kind,
1090
- payload: enriched.contactContext
1091
- }] : void 0,
1165
+ untrustedStructuredContext: untrustedStructuredContext.length > 0 ? untrustedStructuredContext : void 0,
1092
1166
  media
1093
1167
  },
1094
1168
  platform: {
@@ -1112,11 +1186,6 @@ async function attachWebInboxToSocket(options) {
1112
1186
  reply,
1113
1187
  sendMedia
1114
1188
  },
1115
- from: inbound.from,
1116
- conversationId: inbound.from,
1117
- accountId: inbound.access.resolvedAccountId,
1118
- accessControlPassed: true,
1119
- chatType: inbound.group ? "group" : "direct",
1120
1189
  quote: enriched.replyContext ? {
1121
1190
  context: enriched.replyContext,
1122
1191
  id: enriched.replyContext.id,
@@ -1138,12 +1207,15 @@ async function attachWebInboxToSocket(options) {
1138
1207
  inboundMessage.debounceKey = debounceKey;
1139
1208
  if (inboundDebounceMs > 0 && shouldDebounceInboundMessage(inboundMessage)) pendingDebounceKeys.add(debounceKey);
1140
1209
  }
1141
- if (inboundMessage.event.id) cacheInboundMessageMeta(inboundMessage.accountId, inboundMessage.platform.chatJid, inboundMessage.event.id, {
1142
- participant: inboundMessage.platform.senderJid,
1143
- participantE164: inboundMessage.chatType === "direct" ? inboundMessage.platform.senderE164 : void 0,
1144
- body: inboundMessage.payload.body,
1145
- fromMe: inboundMessage.platform.fromMe
1146
- });
1210
+ if (inboundMessage.event.id) {
1211
+ const admission = requireWhatsAppInboundAdmission(inboundMessage);
1212
+ cacheInboundMessageMeta(admission.accountId, inboundMessage.platform.chatJid, inboundMessage.event.id, {
1213
+ participant: inboundMessage.platform.senderJid,
1214
+ participantE164: admission.conversation.kind === "direct" ? inboundMessage.platform.senderE164 : void 0,
1215
+ body: inboundMessage.payload.body,
1216
+ fromMe: inboundMessage.platform.fromMe
1217
+ });
1218
+ }
1147
1219
  try {
1148
1220
  Promise.resolve(debouncer.enqueue(inboundMessage)).catch((err) => {
1149
1221
  inboundLogger.error({ error: String(err) }, "failed handling inbound web message");
@@ -1254,14 +1326,7 @@ async function attachWebInboxToSocket(options) {
1254
1326
  }
1255
1327
  })();
1256
1328
  const sendApi = createWebSendApi({
1257
- sock: {
1258
- sendMessage: (jid, content, optionsLocal) => sendTrackedMessage(jid, content, optionsLocal),
1259
- sendPresenceUpdate: async (presenceLocal, jid) => {
1260
- const currentSock = getCurrentSock();
1261
- if (!currentSock) throw new Error(RECONNECT_IN_PROGRESS_ERROR);
1262
- return currentSock.sendPresenceUpdate(presenceLocal, jid);
1263
- }
1264
- },
1329
+ sock: sendApiSocketOperations,
1265
1330
  defaultAccountId: options.accountId,
1266
1331
  resolveOutboundMentions: ({ jid, text }) => resolveOutboundMentionsForGroup(jid, text),
1267
1332
  authDir: options.authDir
@@ -1296,7 +1361,7 @@ async function attachWebInboxToSocket(options) {
1296
1361
  };
1297
1362
  }
1298
1363
  async function monitorWebInbox(options) {
1299
- const socketTiming = resolveWhatsAppSocketTiming(options.cfg);
1364
+ const socketTiming = options.socketTiming ?? resolveWhatsAppSocketTiming(options.cfg);
1300
1365
  const sock = await createWaSocket(false, options.verbose, {
1301
1366
  authDir: options.authDir,
1302
1367
  ...socketTiming
@@ -1308,12 +1373,14 @@ async function monitorWebInbox(options) {
1308
1373
  throw err;
1309
1374
  }
1310
1375
  const shouldDebounce = options.shouldDebounce;
1376
+ const normalizeAdmittedWebInboundMessage = (msg) => requireAdmittedWhatsAppInboundMessage(normalizeWebInboundMessage(msg));
1311
1377
  return attachWebInboxToSocket({
1312
1378
  ...options,
1313
1379
  onMessage: async (msg) => {
1314
- await options.onMessage(normalizeWebInboundMessage(msg));
1380
+ await options.onMessage(normalizeAdmittedWebInboundMessage(msg));
1315
1381
  },
1316
- shouldDebounce: shouldDebounce ? (msg) => shouldDebounce(normalizeWebInboundMessage(msg)) : void 0,
1382
+ shouldDebounce: shouldDebounce ? (msg) => shouldDebounce(normalizeAdmittedWebInboundMessage(msg)) : void 0,
1383
+ socketTiming,
1317
1384
  sock
1318
1385
  });
1319
1386
  }
@@ -1340,7 +1407,7 @@ function resolveMentionTargets(msg, authDir) {
1340
1407
  function isBotMentionedFromTargets(msg, mentionCfg, targets) {
1341
1408
  const clean = (text) => normalizeMentionText(text);
1342
1409
  const explicitSelfChatOverride = typeof mentionCfg.isSelfChat === "boolean";
1343
- const isGroupConversation = isWhatsAppGroupJid(msg.from);
1410
+ const isGroupConversation = requireWhatsAppInboundAdmission(msg).conversation.kind === "group";
1344
1411
  const isSelfChat = explicitSelfChatOverride ? Boolean(mentionCfg.isSelfChat) : isSelfChatMode(targets.self.e164, mentionCfg.allowFrom) && !isGroupConversation;
1345
1412
  const hasMentions = targets.normalizedMentions.length > 0;
1346
1413
  if (hasMentions && !isSelfChat) {
@@ -1364,7 +1431,7 @@ function debugMention(msg, mentionCfg, authDir) {
1364
1431
  return {
1365
1432
  wasMentioned: isBotMentionedFromTargets(msg, mentionCfg, mentionTargets),
1366
1433
  details: {
1367
- from: msg.from,
1434
+ from: requireWhatsAppInboundAdmission(msg).conversation.id,
1368
1435
  body: msg.payload.body,
1369
1436
  bodyClean: normalizeMentionText(msg.payload.body),
1370
1437
  mentionedJids: msg.group?.mentions?.jids ?? null,
@@ -1582,9 +1649,11 @@ async function resolveGroupActivationFor(params) {
1582
1649
  //#region extensions/whatsapp/src/auto-reply/monitor/ack-reaction.ts
1583
1650
  async function maybeSendAckReaction(params) {
1584
1651
  if (!params.msg.event.id) return null;
1652
+ const admission = requireWhatsAppInboundAdmission(params.msg);
1653
+ const accountId = admission.accountId;
1585
1654
  if (resolveWhatsAppReactionLevel({
1586
1655
  cfg: params.cfg,
1587
- accountId: params.accountId
1656
+ accountId
1588
1657
  }).level === "off") return null;
1589
1658
  const ackConfig = params.cfg.channels?.whatsapp?.ackReaction;
1590
1659
  const emoji = resolveWhatsAppAckEmoji({
@@ -1594,18 +1663,19 @@ async function maybeSendAckReaction(params) {
1594
1663
  });
1595
1664
  const directEnabled = ackConfig?.direct ?? true;
1596
1665
  const groupMode = ackConfig?.group ?? "mentions";
1597
- const conversationIdForCheck = params.msg.conversationId ?? params.msg.from;
1598
- const activation = params.msg.chatType === "group" ? await resolveGroupActivationFor({
1666
+ const isGroup = admission.conversation.kind === "group";
1667
+ const conversationIdForCheck = admission.conversation.id;
1668
+ const activation = isGroup ? await resolveGroupActivationFor({
1599
1669
  cfg: params.cfg,
1600
- accountId: params.accountId,
1670
+ accountId,
1601
1671
  agentId: params.agentId,
1602
1672
  sessionKey: params.sessionKey,
1603
1673
  conversationId: conversationIdForCheck
1604
1674
  }) : null;
1605
1675
  const shouldSendReaction = () => shouldAckReactionForWhatsApp({
1606
1676
  emoji,
1607
- isDirect: params.msg.chatType === "direct",
1608
- isGroup: params.msg.chatType === "group",
1677
+ isDirect: admission.conversation.kind === "direct",
1678
+ isGroup,
1609
1679
  directEnabled,
1610
1680
  groupMode,
1611
1681
  wasMentioned: params.msg.wasMentioned === true,
@@ -1622,7 +1692,7 @@ async function maybeSendAckReaction(params) {
1622
1692
  verbose: params.verbose,
1623
1693
  fromMe: false,
1624
1694
  ...sender.jid ? { participant: sender.jid } : {},
1625
- ...params.accountId ? { accountId: params.accountId } : {},
1695
+ accountId,
1626
1696
  cfg: params.cfg
1627
1697
  };
1628
1698
  return createAckReactionHandle({
@@ -1642,12 +1712,13 @@ async function maybeSendAckReaction(params) {
1642
1712
  //#endregion
1643
1713
  //#region extensions/whatsapp/src/auto-reply/monitor/broadcast.ts
1644
1714
  function buildBroadcastRouteKeys(params) {
1715
+ const admission = requireWhatsAppInboundAdmission(params.msg);
1645
1716
  const sessionKey = buildAgentSessionKey({
1646
1717
  agentId: params.agentId,
1647
1718
  channel: "whatsapp",
1648
1719
  accountId: params.route.accountId,
1649
1720
  peer: {
1650
- kind: params.msg.chatType === "group" ? "group" : "direct",
1721
+ kind: admission.conversation.kind,
1651
1722
  id: params.peerId
1652
1723
  },
1653
1724
  dmScope: params.cfg.session?.dmScope,
@@ -1674,7 +1745,8 @@ async function maybeBroadcastMessage(params) {
1674
1745
  whatsappInboundLog.info(`Broadcasting message to ${broadcastAgents.length} agents (${strategy})`);
1675
1746
  const agentIds = params.cfg.agents?.list?.map((agent) => normalizeAgentId(agent.id));
1676
1747
  const hasKnownAgents = (agentIds?.length ?? 0) > 0;
1677
- const groupHistorySnapshot = params.msg.chatType === "group" ? params.groupHistories.get(params.groupHistoryKey) ?? [] : void 0;
1748
+ const isGroupConversation = requireWhatsAppInboundAdmission(params.msg).conversation.kind === "group";
1749
+ const groupHistorySnapshot = isGroupConversation ? params.groupHistories.get(params.groupHistoryKey) ?? [] : void 0;
1678
1750
  const processForAgent = async (agentId) => {
1679
1751
  const normalizedAgentId = normalizeAgentId(agentId);
1680
1752
  if (hasKnownAgents && !agentIds?.includes(normalizedAgentId)) {
@@ -1693,7 +1765,7 @@ async function maybeBroadcastMessage(params) {
1693
1765
  agentId: normalizedAgentId,
1694
1766
  ...routeKeys
1695
1767
  };
1696
- const agentRoute = params.msg.chatType === "group" ? resolveWhatsAppGroupSessionRoute(baseAgentRoute) : baseAgentRoute;
1768
+ const agentRoute = isGroupConversation ? resolveWhatsAppGroupSessionRoute(baseAgentRoute) : baseAgentRoute;
1697
1769
  try {
1698
1770
  const opts = {
1699
1771
  groupHistory: groupHistorySnapshot,
@@ -1710,7 +1782,7 @@ async function maybeBroadcastMessage(params) {
1710
1782
  };
1711
1783
  if (strategy === "sequential") for (const agentId of broadcastAgents) await processForAgent(agentId);
1712
1784
  else await Promise.allSettled(broadcastAgents.map(processForAgent));
1713
- if (params.msg.chatType === "group") params.groupHistories.set(params.groupHistoryKey, []);
1785
+ if (isGroupConversation) params.groupHistories.set(params.groupHistoryKey, []);
1714
1786
  return true;
1715
1787
  }
1716
1788
  //#endregion
@@ -1812,26 +1884,28 @@ function skipGroupMessageAndStoreHistory(params, verboseMessage, body) {
1812
1884
  async function applyGroupGating(params) {
1813
1885
  const sender = getSenderIdentity(params.msg);
1814
1886
  const self = getSelfIdentity(params.msg, params.authDir);
1887
+ const admission = requireWhatsAppInboundAdmission(params.msg);
1888
+ const conversationId = admission.conversation.id;
1815
1889
  const inboundPolicy = resolveWhatsAppInboundPolicy({
1816
1890
  cfg: params.cfg,
1817
- accountId: params.msg.accountId,
1891
+ accountId: admission.accountId,
1818
1892
  selfE164: self.e164 ?? null
1819
1893
  });
1820
- const conversationGroupPolicy = inboundPolicy.resolveConversationGroupPolicy(params.conversationId);
1894
+ const conversationGroupPolicy = inboundPolicy.resolveConversationGroupPolicy(conversationId);
1821
1895
  if (conversationGroupPolicy.allowlistEnabled && !conversationGroupPolicy.allowed) {
1822
1896
  const accountId = inboundPolicy.account.accountId;
1823
- if (shouldWarnForGroupDrop(`${accountId}:${params.conversationId}`)) {
1897
+ if (shouldWarnForGroupDrop(`${accountId}:${conversationId}`)) {
1824
1898
  const groupsPath = resolveWhatsAppGroupsConfigPath({
1825
1899
  cfg: params.cfg,
1826
1900
  accountId
1827
1901
  });
1828
1902
  params.replyLogger.warn({
1829
- conversationId: params.conversationId,
1903
+ conversationId,
1830
1904
  accountId,
1831
1905
  groupsPath
1832
- }, `WhatsApp group ${params.conversationId} not in ${groupsPath} — inbound dropped. Add the group JID to ${groupsPath} (or add "*" there to admit all groups). Sender authorization still applies.`);
1906
+ }, `WhatsApp group ${conversationId} not in ${groupsPath} — inbound dropped. Add the group JID to ${groupsPath} (or add "*" there to admit all groups). Sender authorization still applies.`);
1833
1907
  }
1834
- params.logVerbose(`Dropping message from unregistered WhatsApp group ${params.conversationId}. Add the group JID to channels.whatsapp.groups, or add "*" there to admit all groups. Sender authorization still applies.`);
1908
+ params.logVerbose(`Dropping message from unregistered WhatsApp group ${conversationId}. Add the group JID to channels.whatsapp.groups, or add "*" there to admit all groups. Sender authorization still applies.`);
1835
1909
  return { shouldProcess: false };
1836
1910
  }
1837
1911
  noteGroupMember(params.groupMemberNames, params.groupHistoryKey, sender.e164 ?? void 0, sender.name ?? void 0);
@@ -1842,7 +1916,7 @@ async function applyGroupGating(params) {
1842
1916
  const mentionConfig = {
1843
1917
  ...buildMentionConfig(params.cfg, params.agentId, {
1844
1918
  provider: "whatsapp",
1845
- conversationId: params.conversationId,
1919
+ conversationId,
1846
1920
  providerPolicy: params.providerMentionPatterns
1847
1921
  }),
1848
1922
  allowFrom: inboundPolicy.configuredAllowFrom
@@ -1858,10 +1932,10 @@ async function applyGroupGating(params) {
1858
1932
  const activationCommand = parseActivationCommand(commandBody);
1859
1933
  const owner = isOwnerSender(baseMentionConfig, params.msg);
1860
1934
  const shouldBypassMention = owner && hasControlCommand(commandBody, params.cfg);
1861
- if (activationCommand.hasCommand && !owner) return skipGroupMessageAndStoreHistory(params, `Ignoring /activation from non-owner in group ${params.conversationId}`);
1935
+ if (activationCommand.hasCommand && !owner) return skipGroupMessageAndStoreHistory(params, `Ignoring /activation from non-owner in group ${conversationId}`);
1862
1936
  const mentionDebug = debugMention(mentionMsg, mentionConfig, params.authDir);
1863
1937
  params.replyLogger.debug({
1864
- conversationId: params.conversationId,
1938
+ conversationId,
1865
1939
  wasMentioned: mentionDebug.wasMentioned,
1866
1940
  ...mentionDebug.details
1867
1941
  }, "group mention debug");
@@ -1871,7 +1945,7 @@ async function applyGroupGating(params) {
1871
1945
  accountId: inboundPolicy.account.accountId,
1872
1946
  agentId: params.agentId,
1873
1947
  sessionKey: params.sessionKey,
1874
- conversationId: params.conversationId
1948
+ conversationId
1875
1949
  }) !== "always";
1876
1950
  const replyContext = getReplyContext(params.msg, params.authDir);
1877
1951
  const mentionDecision = resolveInboundMentionDecision({
@@ -1892,13 +1966,13 @@ async function applyGroupGating(params) {
1892
1966
  params.msg.wasMentioned = effectiveWasMentioned;
1893
1967
  if (!shouldBypassMention && requireMention && mentionDecision.shouldSkip) {
1894
1968
  if (params.deferMissingMention === true) {
1895
- params.logVerbose(`Deferring group mention skip until audio preflight completes in ${params.conversationId}`);
1969
+ params.logVerbose(`Deferring group mention skip until audio preflight completes in ${conversationId}`);
1896
1970
  return {
1897
1971
  shouldProcess: false,
1898
1972
  needsMentionText: true
1899
1973
  };
1900
1974
  }
1901
- return skipGroupMessageAndStoreHistory(params, `Group message stored for context (no mention detected) in ${params.conversationId}: ${mentionMsg.payload.body}`, params.mentionText);
1975
+ return skipGroupMessageAndStoreHistory(params, `Group message stored for context (no mention detected) in ${conversationId}: ${mentionMsg.payload.body}`, params.mentionText);
1902
1976
  }
1903
1977
  return { shouldProcess: true };
1904
1978
  }
@@ -1935,11 +2009,13 @@ function updateLastRouteInBackground(params) {
1935
2009
  //#endregion
1936
2010
  //#region extensions/whatsapp/src/auto-reply/monitor/peer.ts
1937
2011
  function resolvePeerId(msg) {
1938
- if (msg.chatType === "group") return msg.conversationId ?? msg.from;
2012
+ const admission = requireWhatsAppInboundAdmission(msg);
2013
+ if (admission.conversation.kind === "group") return admission.conversation.id;
1939
2014
  const sender = getSenderIdentity(msg);
1940
2015
  if (sender.e164) return normalizeE164(sender.e164) ?? sender.e164;
1941
- if (msg.from.includes("@")) return jidToE164(msg.from) ?? msg.from;
1942
- return normalizeE164(msg.from) ?? msg.from;
2016
+ const conversationId = admission.conversation.id;
2017
+ if (conversationId.includes("@")) return jidToE164(conversationId) ?? conversationId;
2018
+ return normalizeE164(conversationId) ?? conversationId;
1943
2019
  }
1944
2020
  //#endregion
1945
2021
  //#region extensions/whatsapp/src/system-prompt.ts
@@ -2042,6 +2118,9 @@ function createWhatsAppReplyDeliveryReceipt(results) {
2042
2118
  }
2043
2119
  async function deliverWebReply(params) {
2044
2120
  const { replyResult, msg, maxMediaBytes, textLimit, replyLogger, connectionId, skipLog } = params;
2121
+ const admission = requireWhatsAppInboundAdmission(msg);
2122
+ const conversationId = admission.conversation.id;
2123
+ const isGroupConversation = admission.conversation.kind === "group";
2045
2124
  const replyStarted = Date.now();
2046
2125
  const sendResults = [];
2047
2126
  const rememberSendResult = (result) => {
@@ -2055,7 +2134,7 @@ async function deliverWebReply(params) {
2055
2134
  };
2056
2135
  };
2057
2136
  if (isReasoningReplyPayload(replyResult)) {
2058
- whatsappOutboundLog.debug(`Suppressed reasoning payload to ${msg.from}`);
2137
+ whatsappOutboundLog.debug(`Suppressed reasoning payload to ${conversationId}`);
2059
2138
  return finishDelivery();
2060
2139
  }
2061
2140
  const tableMode = params.tableMode ?? "code";
@@ -2065,12 +2144,12 @@ async function deliverWebReply(params) {
2065
2144
  const mediaList = normalizedReply.mediaUrls ?? [];
2066
2145
  const getQuote = () => {
2067
2146
  if (!replyResult.replyToId) return;
2068
- const cached = lookupInboundMessageMeta(msg.accountId, msg.platform.chatJid, replyResult.replyToId);
2147
+ const cached = lookupInboundMessageMeta(admission.accountId, msg.platform.chatJid, replyResult.replyToId);
2069
2148
  return buildQuotedMessageOptions({
2070
2149
  messageId: replyResult.replyToId,
2071
2150
  remoteJid: msg.platform.chatJid,
2072
2151
  fromMe: cached?.fromMe ?? false,
2073
- participant: cached?.participant ?? (msg.chatType === "group" ? msg.platform.senderJid : void 0),
2152
+ participant: cached?.participant ?? (isGroupConversation ? msg.platform.senderJid : void 0),
2074
2153
  messageText: cached?.body ?? ""
2075
2154
  });
2076
2155
  };
@@ -2080,7 +2159,7 @@ async function deliverWebReply(params) {
2080
2159
  send: fn,
2081
2160
  maxAttempts,
2082
2161
  onRetry: ({ attempt, maxAttempts: retryMaxAttempts, backoffMs, errorText }) => {
2083
- logVerbose(`Retrying ${label} to ${msg.from} after failure (${attempt}/${retryMaxAttempts - 1}) in ${backoffMs}ms: ${errorText}`);
2162
+ logVerbose(`Retrying ${label} to ${conversationId} after failure (${attempt}/${retryMaxAttempts - 1}) in ${backoffMs}ms: ${errorText}`);
2084
2163
  }
2085
2164
  });
2086
2165
  } catch (error) {
@@ -2096,14 +2175,14 @@ async function deliverWebReply(params) {
2096
2175
  rememberSendResult(await sendWithRetry(() => msg.platform.reply(chunk, quote), "text"));
2097
2176
  if (!skipLog) {
2098
2177
  const durationMs = Date.now() - chunkStarted;
2099
- whatsappOutboundLog.debug(`Sent chunk ${index + 1}/${totalChunks} to ${msg.from} (${durationMs.toFixed(0)}ms)`);
2178
+ whatsappOutboundLog.debug(`Sent chunk ${index + 1}/${totalChunks} to ${conversationId} (${durationMs.toFixed(0)}ms)`);
2100
2179
  }
2101
2180
  }
2102
2181
  const delivery = finishDelivery();
2103
2182
  const logPayload = {
2104
2183
  correlationId: msg.event.id ?? newConnectionId(),
2105
2184
  connectionId: connectionId ?? null,
2106
- to: msg.from,
2185
+ to: conversationId,
2107
2186
  from: msg.platform.recipientJid,
2108
2187
  text: elide(replyResult.text, 240),
2109
2188
  mediaUrl: null,
@@ -2159,11 +2238,11 @@ async function deliverWebReply(params) {
2159
2238
  mimetype: media.mimetype
2160
2239
  }, quote), "media:document"));
2161
2240
  }
2162
- whatsappOutboundLog.info(`Sent media reply to ${msg.from} (${(media.buffer.length / (1024 * 1024)).toFixed(2)}MB)`);
2241
+ whatsappOutboundLog.info(`Sent media reply to ${conversationId} (${(media.buffer.length / (1024 * 1024)).toFixed(2)}MB)`);
2163
2242
  replyLogger.info({
2164
2243
  correlationId: msg.event.id ?? newConnectionId(),
2165
2244
  connectionId: connectionId ?? null,
2166
- to: msg.from,
2245
+ to: conversationId,
2167
2246
  from: msg.platform.recipientJid,
2168
2247
  text: caption ?? null,
2169
2248
  mediaUrl,
@@ -2173,15 +2252,19 @@ async function deliverWebReply(params) {
2173
2252
  }, "auto-reply sent (media)");
2174
2253
  },
2175
2254
  onError: async ({ error, mediaUrl, caption, isFirst }) => {
2176
- whatsappOutboundLog.error(`Failed sending web media to ${msg.from}: ${formatError(error)}`);
2255
+ whatsappOutboundLog.error(`Failed sending web media to ${conversationId}: ${formatError(error)}`);
2177
2256
  replyLogger.warn({
2178
2257
  err: error,
2179
2258
  mediaUrl
2180
2259
  }, "failed to send web media reply");
2181
- if (!isFirst) return;
2182
- const fallbackText = [remainingText.shift() ?? caption ?? "", "⚠️ Media failed."].filter(Boolean).join("\n");
2260
+ if (!isFirst) {
2261
+ whatsappOutboundLog.warn(`Trailing media failed; sent warning to ${conversationId}`);
2262
+ rememberSendResult(await sendWithRetry(() => msg.platform.reply("⚠️ Media unavailable.", getQuote()), "media:fallback-unavailable"));
2263
+ return;
2264
+ }
2265
+ const fallbackText = [caption ?? "", "⚠️ Media failed."].filter(Boolean).join("\n");
2183
2266
  if (!fallbackText) return;
2184
- whatsappOutboundLog.warn(`Media skipped; sent text-only to ${msg.from}`);
2267
+ whatsappOutboundLog.warn(`Media skipped; sent text-only to ${conversationId}`);
2185
2268
  rememberSendResult(await sendWithRetry(() => msg.platform.reply(fallbackText, getQuote()), "media:fallback-text"));
2186
2269
  }
2187
2270
  });
@@ -2218,7 +2301,7 @@ function resolveVisibleWhatsAppGroupHistory(params) {
2218
2301
  function resolveVisibleWhatsAppReplyContext(params) {
2219
2302
  const replyTo = getReplyContext(params.msg, params.authDir);
2220
2303
  if (!replyTo) return null;
2221
- const senderAllowed = params.msg.chatType !== "group" || params.groupPolicy !== "allowlist" ? true : isWhatsAppSupplementalSenderAllowed({
2304
+ const senderAllowed = requireWhatsAppInboundAdmission(params.msg).conversation.kind !== "group" || params.groupPolicy !== "allowlist" ? true : isWhatsAppSupplementalSenderAllowed({
2222
2305
  allowFrom: params.groupAllowFrom,
2223
2306
  authDir: params.authDir,
2224
2307
  sender: replyTo.sender
@@ -2251,14 +2334,15 @@ function markWhatsAppReplyDeliveryErrorVisibleAfterFlush(error, flushResult) {
2251
2334
  return flushResult.delivered > 0 ? markWhatsAppVisibleDeliveryError(error) : error;
2252
2335
  }
2253
2336
  function logWhatsAppReplyDeliveryError(params) {
2337
+ const admission = requireWhatsAppInboundAdmission(params.msg);
2254
2338
  params.replyLogger.error({
2255
2339
  err: normalizeErrForLog(params.err),
2256
2340
  replyKind: params.info.kind,
2257
2341
  correlationId: params.msg.event.id ?? null,
2258
2342
  connectionId: params.connectionId,
2259
- conversationId: params.conversationId,
2343
+ conversationId: admission.conversation.id,
2260
2344
  chatId: params.msg.platform.chatJid ?? null,
2261
- to: params.msg.from ?? null,
2345
+ to: admission.conversation.id,
2262
2346
  from: params.msg.platform.recipientJid ?? null
2263
2347
  }, "auto-reply delivery failed");
2264
2348
  }
@@ -2327,7 +2411,10 @@ function resolveWhatsAppResponsePrefix(params) {
2327
2411
  return params.pipelineResponsePrefix ?? (configuredResponsePrefix === void 0 && params.isSelfChat ? resolveIdentityNamePrefix$1(params.cfg, params.agentId) : void 0);
2328
2412
  }
2329
2413
  async function buildWhatsAppInboundContext(params) {
2330
- const inboundHistory = params.msg.chatType === "group" ? buildInboundHistoryFromEntries({
2414
+ const admission = requireWhatsAppInboundAdmission(params.msg);
2415
+ const conversationId = admission.conversation.id;
2416
+ const conversationKind = admission.conversation.kind;
2417
+ const inboundHistory = conversationKind === "group" ? buildInboundHistoryFromEntries({
2331
2418
  entries: (params.groupHistory ?? []).map((entry) => ({
2332
2419
  sender: entry.sender,
2333
2420
  body: entry.body,
@@ -2356,15 +2443,15 @@ async function buildWhatsAppInboundContext(params) {
2356
2443
  media,
2357
2444
  messageId: params.msg.event.id,
2358
2445
  timestamp: params.msg.event.timestamp,
2359
- from: params.msg.from,
2446
+ from: conversationId,
2360
2447
  sender: {
2361
2448
  id: params.sender.id ?? params.sender.e164,
2362
2449
  name: params.sender.name
2363
2450
  },
2364
2451
  conversation: {
2365
- kind: params.msg.chatType,
2366
- id: params.conversationId,
2367
- label: params.msg.chatType === "group" ? params.conversationId : params.msg.from
2452
+ kind: conversationKind,
2453
+ id: conversationId,
2454
+ label: conversationId
2368
2455
  },
2369
2456
  route: {
2370
2457
  agentId: params.route.agentId,
@@ -2373,7 +2460,7 @@ async function buildWhatsAppInboundContext(params) {
2373
2460
  },
2374
2461
  reply: {
2375
2462
  to: params.msg.platform.recipientJid,
2376
- originatingTo: params.msg.from
2463
+ originatingTo: conversationId
2377
2464
  },
2378
2465
  message: {
2379
2466
  body: params.combinedBody,
@@ -2384,7 +2471,7 @@ async function buildWhatsAppInboundContext(params) {
2384
2471
  },
2385
2472
  access: {
2386
2473
  ...params.msg.wasMentioned !== void 0 ? { mentions: {
2387
- canDetectMention: params.msg.chatType === "group",
2474
+ canDetectMention: conversationKind === "group",
2388
2475
  wasMentioned: params.msg.wasMentioned
2389
2476
  } } : {},
2390
2477
  commands: { authorized: params.commandAuthorized }
@@ -2434,10 +2521,12 @@ function normalizeCommandTurnFromContext(value) {
2434
2521
  };
2435
2522
  }
2436
2523
  function resolveWhatsAppDmRouteTarget(params) {
2437
- if (params.msg.chatType === "group") return;
2524
+ const admission = requireWhatsAppInboundAdmission(params.msg);
2525
+ const conversationId = admission.conversation.id;
2526
+ if (admission.conversation.kind === "group") return;
2438
2527
  if (params.senderE164) return params.normalizeE164(params.senderE164) ?? void 0;
2439
- if (params.msg.from.includes("@")) return jidToE164(params.msg.from) ?? void 0;
2440
- return params.normalizeE164(params.msg.from) ?? void 0;
2528
+ if (conversationId.includes("@")) return jidToE164(conversationId) ?? void 0;
2529
+ return params.normalizeE164(conversationId) ?? void 0;
2441
2530
  }
2442
2531
  function updateWhatsAppMainLastRoute(params) {
2443
2532
  const shouldUpdateMainLastRoute = !params.pinnedMainDmRecipient || params.pinnedMainDmRecipient === params.dmRouteTarget;
@@ -2462,6 +2551,9 @@ function updateWhatsAppMainLastRoute(params) {
2462
2551
  if (params.dmRouteTarget && inboundLastRouteSessionKey === params.route.mainSessionKey && params.pinnedMainDmRecipient) logVerbose$1(`Skipping main-session last route update for ${params.dmRouteTarget} (pinned owner ${params.pinnedMainDmRecipient})`);
2463
2552
  }
2464
2553
  async function dispatchWhatsAppBufferedReply(params) {
2554
+ const admission = requireWhatsAppInboundAdmission(params.msg);
2555
+ const conversationId = admission.conversation.id;
2556
+ const conversationKind = admission.conversation.kind;
2465
2557
  const statusReactionController = params.statusReactionController ?? null;
2466
2558
  const statusReactionTiming = {
2467
2559
  ...DEFAULT_TIMING,
@@ -2476,7 +2568,7 @@ async function dispatchWhatsAppBufferedReply(params) {
2476
2568
  accountId: params.route.accountId
2477
2569
  });
2478
2570
  const mediaLocalRoots = getAgentScopedMediaLocalRoots(params.cfg, params.route.agentId);
2479
- const sourceReplyChatType = typeof params.context.ChatType === "string" ? params.context.ChatType : params.msg.chatType;
2571
+ const sourceReplyChatType = typeof params.context.ChatType === "string" ? params.context.ChatType : conversationKind;
2480
2572
  const sourceReplyCommandSource = params.context.CommandSource === "native" || params.context.CommandSource === "text" ? params.context.CommandSource : void 0;
2481
2573
  const sourceReplyCommandTurn = normalizeCommandTurnFromContext(params.context.CommandTurn);
2482
2574
  const sourceReplyCommandAuthorized = typeof params.context.CommandAuthorized === "boolean" ? params.context.CommandAuthorized : void 0;
@@ -2512,9 +2604,9 @@ async function dispatchWhatsAppBufferedReply(params) {
2512
2604
  params.replyLogger.warn({
2513
2605
  correlationId: params.msg.event.id ?? null,
2514
2606
  connectionId: params.connectionId,
2515
- conversationId: params.conversationId,
2607
+ conversationId,
2516
2608
  chatId: params.msg.platform.chatJid,
2517
- to: params.msg.from,
2609
+ to: conversationId,
2518
2610
  from: params.msg.platform.recipientJid,
2519
2611
  replyKind: info.kind
2520
2612
  }, "auto-reply was not accepted by WhatsApp provider");
@@ -2527,7 +2619,7 @@ async function dispatchWhatsAppBufferedReply(params) {
2527
2619
  combinedBodySessionKey: params.route.sessionKey,
2528
2620
  logVerboseMessage: shouldLog
2529
2621
  });
2530
- const fromDisplay = params.msg.chatType === "group" ? params.conversationId : params.msg.from ?? "unknown";
2622
+ const fromDisplay = conversationId;
2531
2623
  if (shouldLogVerbose$1()) logVerbose$1(`Reply body: ${normalizedDeliveryPayload.text != null ? reply.text : "<media>"}${reply.hasMedia ? " (media)" : ""} -> ${fromDisplay}`);
2532
2624
  return whatsAppReplyDeliveryVisibility(true);
2533
2625
  };
@@ -2569,7 +2661,7 @@ async function dispatchWhatsAppBufferedReply(params) {
2569
2661
  ctxPayload: params.context,
2570
2662
  payload: normalizedDeliveryPayload,
2571
2663
  info,
2572
- to: params.msg.from,
2664
+ to: conversationId,
2573
2665
  formatting: {
2574
2666
  textLimit,
2575
2667
  tableMode,
@@ -2639,14 +2731,13 @@ async function dispatchWhatsAppBufferedReply(params) {
2639
2731
  err,
2640
2732
  info,
2641
2733
  connectionId: params.connectionId,
2642
- conversationId: params.conversationId,
2643
2734
  msg: params.msg,
2644
2735
  replyLogger: params.replyLogger
2645
2736
  });
2646
2737
  }
2647
2738
  },
2648
2739
  replyOptions: {
2649
- suppressTyping: sourceRepliesAreToolOnly && params.msg.chatType === "group" && !params.msg.wasMentioned,
2740
+ suppressTyping: sourceRepliesAreToolOnly && conversationKind === "group" && !params.msg.wasMentioned,
2650
2741
  disableBlockStreaming,
2651
2742
  ...sourceReplyDeliveryMode ? { sourceReplyDeliveryMode } : {},
2652
2743
  onModelSelected: params.onModelSelected,
@@ -2737,16 +2828,19 @@ function buildInboundLine(params) {
2737
2828
  configured: cfg.channels?.whatsapp?.messagePrefix,
2738
2829
  hasAllowFrom: (cfg.channels?.whatsapp?.allowFrom?.length ?? 0) > 0
2739
2830
  });
2831
+ const admission = requireWhatsAppInboundAdmission(msg);
2832
+ const conversationId = admission.conversation.id;
2833
+ const conversationKind = admission.conversation.kind;
2740
2834
  const prefixStr = messagePrefix ? `${messagePrefix} ` : "";
2741
2835
  const replyContext = params.visibleReplyTo === void 0 ? formatReplyContext(msg) : formatReplyTarget(params.visibleReplyTo);
2742
2836
  const baseLine = `${prefixStr}${msg.payload.body}${replyContext ? `\n\n${replyContext}` : ""}`;
2743
2837
  const sender = getSenderIdentity(msg);
2744
2838
  return formatInboundEnvelope({
2745
2839
  channel: "WhatsApp",
2746
- from: msg.chatType === "group" ? msg.from : msg.from?.replace(/^whatsapp:/, ""),
2840
+ from: conversationKind === "group" ? conversationId : conversationId.replace(/^whatsapp:/, ""),
2747
2841
  timestamp: msg.event.timestamp,
2748
2842
  body: baseLine,
2749
- chatType: msg.chatType,
2843
+ chatType: conversationKind,
2750
2844
  sender: {
2751
2845
  name: sender.name ?? void 0,
2752
2846
  e164: sender.e164 ?? void 0,
@@ -2763,9 +2857,11 @@ async function createWhatsAppStatusReactionController(params) {
2763
2857
  if (!params.msg.event.id) return null;
2764
2858
  const statusReactionsConfig = params.cfg.messages?.statusReactions;
2765
2859
  if (statusReactionsConfig?.enabled !== true) return null;
2860
+ const admission = requireWhatsAppInboundAdmission(params.msg);
2861
+ const accountId = admission.accountId;
2766
2862
  if (resolveWhatsAppReactionLevel({
2767
2863
  cfg: params.cfg,
2768
- accountId: params.accountId
2864
+ accountId
2769
2865
  }).level === "off") return null;
2770
2866
  const ackConfig = params.cfg.channels?.whatsapp?.ackReaction;
2771
2867
  const ackEmoji = resolveWhatsAppAckEmoji({
@@ -2776,18 +2872,19 @@ async function createWhatsAppStatusReactionController(params) {
2776
2872
  if (!ackEmoji) return null;
2777
2873
  const directEnabled = ackConfig?.direct ?? true;
2778
2874
  const groupMode = ackConfig?.group ?? "mentions";
2779
- const conversationIdForCheck = params.msg.conversationId ?? params.msg.from;
2780
- const activation = params.msg.chatType === "group" ? await resolveGroupActivationFor({
2875
+ const isGroup = admission.conversation.kind === "group";
2876
+ const conversationIdForCheck = admission.conversation.id;
2877
+ const activation = isGroup ? await resolveGroupActivationFor({
2781
2878
  cfg: params.cfg,
2782
- accountId: params.accountId,
2879
+ accountId,
2783
2880
  agentId: params.agentId,
2784
2881
  sessionKey: params.sessionKey,
2785
2882
  conversationId: conversationIdForCheck
2786
2883
  }) : null;
2787
2884
  if (!shouldAckReactionForWhatsApp({
2788
2885
  emoji: ackEmoji,
2789
- isDirect: params.msg.chatType === "direct",
2790
- isGroup: params.msg.chatType === "group",
2886
+ isDirect: admission.conversation.kind === "direct",
2887
+ isGroup,
2791
2888
  directEnabled,
2792
2889
  groupMode,
2793
2890
  wasMentioned: params.msg.wasMentioned === true,
@@ -2798,7 +2895,7 @@ async function createWhatsAppStatusReactionController(params) {
2798
2895
  verbose: params.verbose,
2799
2896
  fromMe: false,
2800
2897
  ...sender.jid ? { participant: sender.jid } : {},
2801
- ...params.accountId ? { accountId: params.accountId } : {},
2898
+ accountId,
2802
2899
  cfg: params.cfg
2803
2900
  };
2804
2901
  const chatId = params.msg.platform.chatJid;
@@ -2862,11 +2959,14 @@ function resolvePinnedMainDmRecipient(params) {
2862
2959
  });
2863
2960
  }
2864
2961
  async function processMessage(params) {
2865
- const conversationId = params.msg.conversationId ?? params.msg.from;
2962
+ const admission = requireWhatsAppInboundAdmission(params.msg);
2963
+ if (admission.ingress.admission !== "dispatch" && admission.ingress.admission !== "observe") return false;
2964
+ const conversationId = admission.conversation.id;
2965
+ const conversationKind = admission.conversation.kind;
2866
2966
  const self = getSelfIdentity(params.msg);
2867
2967
  const inboundPolicy = resolveWhatsAppInboundPolicy({
2868
2968
  cfg: params.cfg,
2869
- accountId: params.route.accountId ?? params.msg.accountId,
2969
+ accountId: params.route.accountId ?? admission.accountId,
2870
2970
  selfE164: self.e164 ?? null
2871
2971
  });
2872
2972
  const account = inboundPolicy.account;
@@ -2888,7 +2988,7 @@ async function processMessage(params) {
2888
2988
  ctx: {
2889
2989
  MediaPaths: [params.msg.payload.media?.path],
2890
2990
  MediaTypes: params.msg.payload.media?.type ? [params.msg.payload.media?.type] : void 0,
2891
- From: params.msg.from,
2991
+ From: conversationId,
2892
2992
  To: params.msg.platform.recipientJid,
2893
2993
  Provider: "whatsapp",
2894
2994
  Surface: "whatsapp",
@@ -2924,14 +3024,14 @@ async function processMessage(params) {
2924
3024
  visibleReplyTo
2925
3025
  });
2926
3026
  let shouldClearGroupHistory = false;
2927
- const visibleGroupHistory = params.msg.chatType === "group" ? resolveVisibleWhatsAppGroupHistory({
3027
+ const visibleGroupHistory = conversationKind === "group" ? resolveVisibleWhatsAppGroupHistory({
2928
3028
  history: params.groupHistory ?? params.groupHistories.get(params.groupHistoryKey) ?? [],
2929
3029
  mode: contextVisibilityMode,
2930
3030
  groupPolicy: inboundPolicy.groupPolicy,
2931
3031
  groupAllowFrom: inboundPolicy.groupAllowFrom,
2932
3032
  authDir: account.authDir
2933
3033
  }) : void 0;
2934
- if (params.msg.chatType === "group") {
3034
+ if (conversationKind === "group") {
2935
3035
  const history = visibleGroupHistory ?? [];
2936
3036
  if (history.length > 0) combinedBody = buildHistoryContextFromEntries({
2937
3037
  entries: history.map((m) => ({
@@ -2969,9 +3069,7 @@ async function processMessage(params) {
2969
3069
  msg: params.msg,
2970
3070
  agentId: params.route.agentId,
2971
3071
  sessionKey: params.route.sessionKey,
2972
- conversationId,
2973
- verbose: params.verbose,
2974
- accountId: account.accountId
3072
+ verbose: params.verbose
2975
3073
  }) : null);
2976
3074
  if (statusReactionController && !params.statusReactionController) statusReactionController.setQueued();
2977
3075
  let ackReaction = params.ackReaction ?? null;
@@ -2980,9 +3078,7 @@ async function processMessage(params) {
2980
3078
  msg: params.msg,
2981
3079
  agentId: params.route.agentId,
2982
3080
  sessionKey: params.route.sessionKey,
2983
- conversationId,
2984
3081
  verbose: params.verbose,
2985
- accountId: account.accountId,
2986
3082
  info: params.replyLogger.info.bind(params.replyLogger),
2987
3083
  warn: params.replyLogger.warn.bind(params.replyLogger)
2988
3084
  });
@@ -2990,15 +3086,15 @@ async function processMessage(params) {
2990
3086
  params.replyLogger.info({
2991
3087
  connectionId: params.connectionId,
2992
3088
  correlationId,
2993
- from: params.msg.chatType === "group" ? conversationId : params.msg.from,
3089
+ from: conversationId,
2994
3090
  to: params.msg.platform.recipientJid,
2995
3091
  body: elide(combinedBody, 240),
2996
3092
  mediaType: params.msg.payload.media?.type ?? null,
2997
3093
  mediaPath: params.msg.payload.media?.path ?? null
2998
3094
  }, "inbound web message");
2999
- const fromDisplay = params.msg.chatType === "group" ? conversationId : params.msg.from;
3095
+ const fromDisplay = conversationId;
3000
3096
  const kindLabel = params.msg.payload.media?.type ? `, ${params.msg.payload.media?.type}` : "";
3001
- whatsappInboundLog.info(`Inbound message ${fromDisplay} -> ${params.msg.platform.recipientJid} (${params.msg.chatType}${kindLabel}, ${combinedBody.length} chars)`);
3097
+ whatsappInboundLog.info(`Inbound message ${fromDisplay} -> ${params.msg.platform.recipientJid} (${conversationKind}${kindLabel}, ${combinedBody.length} chars)`);
3002
3098
  if (shouldLogVerbose$1()) whatsappInboundLog.debug(`Inbound body: ${elide(combinedBody, 400)}`);
3003
3099
  const sender = getSenderIdentity(params.msg);
3004
3100
  const dmRouteTarget = resolveWhatsAppDmRouteTarget({
@@ -3033,16 +3129,16 @@ async function processMessage(params) {
3033
3129
  const responsePrefix = resolveWhatsAppResponsePrefix({
3034
3130
  cfg: params.cfg,
3035
3131
  agentId: params.route.agentId,
3036
- isSelfChat: params.msg.chatType !== "group" && inboundPolicy.isSelfChat,
3132
+ isSelfChat: conversationKind !== "group" && inboundPolicy.isSelfChat,
3037
3133
  pipelineResponsePrefix: replyPipeline.responsePrefix
3038
3134
  });
3039
3135
  const replyThreading = resolveBatchedReplyThreadingPolicy(account.replyToMode ?? "off", params.msg.event.isBatched === true);
3040
- const conversationSystemPrompt = params.msg.chatType === "group" ? resolveWhatsAppGroupSystemPrompt({
3136
+ const conversationSystemPrompt = conversationKind === "group" ? resolveWhatsAppGroupSystemPrompt({
3041
3137
  accountConfig: account,
3042
3138
  groupId: conversationId
3043
3139
  }) : resolveWhatsAppDirectSystemPrompt({
3044
3140
  accountConfig: account,
3045
- peerId: dmRouteTarget ?? params.msg.from
3141
+ peerId: dmRouteTarget ?? conversationId
3046
3142
  });
3047
3143
  const ctxPayload = await buildWhatsAppInboundContext({
3048
3144
  bodyForAgent: msgForAgent.payload.body,
@@ -3050,7 +3146,6 @@ async function processMessage(params) {
3050
3146
  commandBody: params.msg.payload.body,
3051
3147
  commandAuthorized,
3052
3148
  commandTurn,
3053
- conversationId,
3054
3149
  groupHistory: visibleGroupHistory,
3055
3150
  groupMemberRoster: params.groupMemberNames.get(params.groupHistoryKey),
3056
3151
  groupSystemPrompt: conversationSystemPrompt,
@@ -3101,6 +3196,26 @@ async function processMessage(params) {
3101
3196
  textForCommands: ctxPayload.CommandBody,
3102
3197
  raw: params.msg
3103
3198
  }),
3199
+ preflight: () => {
3200
+ const reason = admission.ingress.reasonCode;
3201
+ if (admission.ingress.admission === "dispatch") return { admission: {
3202
+ kind: "dispatch",
3203
+ reason
3204
+ } };
3205
+ if (admission.ingress.admission === "observe") return { admission: {
3206
+ kind: "observeOnly",
3207
+ reason
3208
+ } };
3209
+ if (admission.ingress.admission === "skip") return { admission: {
3210
+ kind: "handled",
3211
+ reason
3212
+ } };
3213
+ return { admission: {
3214
+ kind: "drop",
3215
+ reason,
3216
+ recordHistory: false
3217
+ } };
3218
+ },
3104
3219
  resolveTurn: () => ({
3105
3220
  channel: "whatsapp",
3106
3221
  accountId: params.route.accountId,
@@ -3124,7 +3239,6 @@ async function processMessage(params) {
3124
3239
  cfg: params.cfg,
3125
3240
  connectionId: params.connectionId,
3126
3241
  context: ctxPayload,
3127
- conversationId,
3128
3242
  deliverReply: deliverWebReply,
3129
3243
  groupHistories: params.groupHistories,
3130
3244
  groupHistoryKey: params.groupHistoryKey,
@@ -3164,11 +3278,12 @@ async function processMessage(params) {
3164
3278
  //#endregion
3165
3279
  //#region extensions/whatsapp/src/auto-reply/monitor/on-message.ts
3166
3280
  function createWebOnMessageHandler(params) {
3281
+ const hasExplicitlyPassedInboundAccess = (msg) => msg.admission ? msg.admission.ingress.decision === "allow" : msg.accessControlPassed === true;
3167
3282
  const withDirectSenderPeer = (msg, peerId) => {
3168
- if (msg.chatType === "group" || msg.platform.sender?.e164 || msg.platform.senderE164 || !peerId.startsWith("+")) return msg;
3283
+ if (requireWhatsAppInboundAdmission(msg).conversation.kind === "group" || msg.platform.sender?.e164 || msg.platform.senderE164 || !peerId.startsWith("+")) return msg;
3169
3284
  const normalized = normalizeE164(peerId);
3170
3285
  if (!normalized) return msg;
3171
- return withDeprecatedWebInboundMessageFlatAliases({
3286
+ return requireAdmittedWhatsAppInboundMessage(withDeprecatedWebInboundMessageFlatAliases({
3172
3287
  ...msg,
3173
3288
  platform: {
3174
3289
  ...msg.platform,
@@ -3178,7 +3293,7 @@ function createWebOnMessageHandler(params) {
3178
3293
  },
3179
3294
  senderE164: normalized
3180
3295
  }
3181
- });
3296
+ }));
3182
3297
  };
3183
3298
  const processForRoute = async (cfg, msg, route, groupHistoryKey, opts) => {
3184
3299
  const processParams = {
@@ -3208,28 +3323,32 @@ function createWebOnMessageHandler(params) {
3208
3323
  return processMessage(processParams);
3209
3324
  };
3210
3325
  return async (rawMsg) => {
3211
- const normalizedMsg = normalizeWebInboundMessage(rawMsg);
3326
+ const canRunDirectEarlyAudioPreflight = hasExplicitlyPassedInboundAccess(rawMsg);
3327
+ const normalizedMsg = requireAdmittedWhatsAppInboundMessage(normalizeWebInboundMessage(rawMsg));
3212
3328
  const cfg = params.loadConfig?.() ?? params.cfg;
3213
3329
  const peerId = resolvePeerId(normalizedMsg);
3214
3330
  const msg = withDirectSenderPeer(normalizedMsg, peerId);
3215
- const conversationId = msg.conversationId ?? msg.from;
3331
+ const admission = requireWhatsAppInboundAdmission(msg);
3332
+ if (admission.ingress.admission !== "dispatch" && admission.ingress.admission !== "observe") return;
3333
+ const conversationId = admission.conversation.id;
3334
+ const conversationKind = admission.conversation.kind;
3216
3335
  const baseRoute = resolveAgentRoute({
3217
3336
  cfg,
3218
3337
  channel: "whatsapp",
3219
- accountId: msg.accountId,
3338
+ accountId: admission.accountId,
3220
3339
  peer: {
3221
- kind: msg.chatType === "group" ? "group" : "direct",
3340
+ kind: conversationKind,
3222
3341
  id: peerId
3223
3342
  }
3224
3343
  });
3225
- const baseConversationRoute = msg.chatType === "group" ? resolveWhatsAppGroupSessionRoute(baseRoute) : baseRoute;
3226
- const routeAccountId = baseConversationRoute.accountId ?? msg.accountId ?? params.account.accountId ?? "default";
3344
+ const baseConversationRoute = conversationKind === "group" ? resolveWhatsAppGroupSessionRoute(baseRoute) : baseRoute;
3345
+ const routeAccountId = baseConversationRoute.accountId ?? admission.accountId;
3227
3346
  const account = resolveWhatsAppAccount({
3228
3347
  cfg,
3229
3348
  accountId: routeAccountId
3230
3349
  });
3231
3350
  const baseMentionConfig = buildMentionConfig(cfg);
3232
- if (msg.from === msg.platform.recipientJid) logVerbose(`📱 Same-phone mode detected (from === to: ${msg.from})`);
3351
+ if (conversationId === msg.platform.recipientJid) logVerbose(`📱 Same-phone mode detected (from === to: ${conversationId})`);
3233
3352
  if (params.echoTracker.has(msg.payload.body)) {
3234
3353
  logVerbose("Skipping auto-reply: detected echo (message matches recently sent text)");
3235
3354
  params.echoTracker.forget(msg.payload.body);
@@ -3243,7 +3362,7 @@ function createWebOnMessageHandler(params) {
3243
3362
  conversationId: peerId
3244
3363
  });
3245
3364
  const route = configuredRoute.route;
3246
- const groupHistoryKey = msg.chatType === "group" ? buildGroupHistoryKey({
3365
+ const groupHistoryKey = conversationKind === "group" ? buildGroupHistoryKey({
3247
3366
  channel: "whatsapp",
3248
3367
  accountId: route.accountId,
3249
3368
  peerKind: "group",
@@ -3251,7 +3370,7 @@ function createWebOnMessageHandler(params) {
3251
3370
  }) : route.sessionKey;
3252
3371
  let preflightAudioTranscript;
3253
3372
  const hasAudioBody = msg.payload.media?.type?.startsWith("audio/") === true && msg.payload.body === "<media:audio>";
3254
- const canRunEarlyAudioPreflight = msg.chatType === "group" || msg.accessControlPassed === true;
3373
+ const canRunEarlyAudioPreflight = conversationKind === "group" || canRunDirectEarlyAudioPreflight;
3255
3374
  let ackAlreadySent = false;
3256
3375
  let ackReaction = null;
3257
3376
  let statusReactionController = null;
@@ -3259,13 +3378,37 @@ function createWebOnMessageHandler(params) {
3259
3378
  const clearPreDispatchReaction = async () => {
3260
3379
  try {
3261
3380
  if (statusReactionController) {
3262
- statusReactionController.cancelPending();
3263
- await statusReactionController.clear();
3381
+ const controller = statusReactionController;
3382
+ statusReactionController = null;
3383
+ controller.cancelPending();
3384
+ await controller.clear();
3264
3385
  return;
3265
3386
  }
3266
3387
  if (ackReaction && await ackReaction.ackReactionPromise) await ackReaction.remove();
3267
3388
  } catch (err) {
3268
- params.replyLogger.warn({ error: String(err) }, "whatsapp: failed to clear pre-dispatch reaction after configured ACP readiness failure");
3389
+ params.replyLogger.warn({ error: String(err) }, "whatsapp: failed to clear pre-dispatch reaction after pre-dispatch rejection");
3390
+ }
3391
+ };
3392
+ const transcribeAudioOnce = async () => {
3393
+ if (preflightAudioTranscript !== void 0 || !hasAudioBody || !msg.payload.media?.path) return;
3394
+ try {
3395
+ const { transcribeFirstAudio } = await import("./audio-preflight.runtime-C_glQhZY.js");
3396
+ preflightAudioTranscript = await transcribeFirstAudio({
3397
+ ctx: {
3398
+ MediaPaths: [msg.payload.media?.path],
3399
+ MediaTypes: msg.payload.media?.type ? [msg.payload.media?.type] : void 0,
3400
+ From: conversationId,
3401
+ To: msg.platform.recipientJid,
3402
+ Provider: "whatsapp",
3403
+ Surface: "whatsapp",
3404
+ OriginatingChannel: "whatsapp",
3405
+ OriginatingTo: conversationId,
3406
+ AccountId: route.accountId
3407
+ },
3408
+ cfg
3409
+ }) ?? null;
3410
+ } catch {
3411
+ preflightAudioTranscript = null;
3269
3412
  }
3270
3413
  };
3271
3414
  const runAudioPreflightOnce = async () => {
@@ -3276,9 +3419,7 @@ function createWebOnMessageHandler(params) {
3276
3419
  msg,
3277
3420
  agentId: route.agentId,
3278
3421
  sessionKey: route.sessionKey,
3279
- conversationId,
3280
- verbose: params.verbose,
3281
- accountId: route.accountId
3422
+ verbose: params.verbose
3282
3423
  });
3283
3424
  if (statusReactionController) await statusReactionController.setQueued();
3284
3425
  } else {
@@ -3287,42 +3428,22 @@ function createWebOnMessageHandler(params) {
3287
3428
  msg,
3288
3429
  agentId: route.agentId,
3289
3430
  sessionKey: route.sessionKey,
3290
- conversationId,
3291
3431
  verbose: params.verbose,
3292
- accountId: route.accountId,
3293
3432
  info: params.replyLogger.info.bind(params.replyLogger),
3294
3433
  warn: params.replyLogger.warn.bind(params.replyLogger)
3295
3434
  });
3296
3435
  ackAlreadySent = ackReaction !== null;
3297
3436
  }
3298
- try {
3299
- const { transcribeFirstAudio } = await import("./audio-preflight.runtime-C_glQhZY.js");
3300
- preflightAudioTranscript = await transcribeFirstAudio({
3301
- ctx: {
3302
- MediaPaths: [msg.payload.media?.path],
3303
- MediaTypes: msg.payload.media?.type ? [msg.payload.media?.type] : void 0,
3304
- From: msg.from,
3305
- To: msg.platform.recipientJid,
3306
- Provider: "whatsapp",
3307
- Surface: "whatsapp",
3308
- OriginatingChannel: "whatsapp",
3309
- OriginatingTo: conversationId,
3310
- AccountId: route.accountId
3311
- },
3312
- cfg
3313
- }) ?? null;
3314
- } catch {
3315
- preflightAudioTranscript = null;
3316
- }
3437
+ await transcribeAudioOnce();
3317
3438
  };
3318
- if (msg.chatType === "group") {
3439
+ if (conversationKind === "group") {
3319
3440
  const sender = getSenderIdentity(msg);
3320
3441
  const metaCtx = {
3321
- From: msg.from,
3442
+ From: conversationId,
3322
3443
  To: msg.platform.recipientJid,
3323
3444
  SessionKey: route.sessionKey,
3324
3445
  AccountId: route.accountId,
3325
- ChatType: msg.chatType,
3446
+ ChatType: conversationKind,
3326
3447
  ConversationLabel: conversationId,
3327
3448
  GroupSubject: msg.group?.subject,
3328
3449
  SenderName: sender.name ?? void 0,
@@ -3344,13 +3465,11 @@ function createWebOnMessageHandler(params) {
3344
3465
  ctx: metaCtx,
3345
3466
  warn: params.replyLogger.warn.bind(params.replyLogger)
3346
3467
  });
3347
- if (configuredRoute.bindingResolution) recordAcceptedConfiguredGroupRoute = recordGroupRoute;
3348
- else recordGroupRoute();
3468
+ recordAcceptedConfiguredGroupRoute = recordGroupRoute;
3349
3469
  let gating = await applyGroupGating({
3350
3470
  cfg,
3351
3471
  msg,
3352
3472
  deferMissingMention: hasAudioBody && Boolean(msg.payload.media?.path),
3353
- conversationId,
3354
3473
  groupHistoryKey,
3355
3474
  agentId: route.agentId,
3356
3475
  sessionKey: route.sessionKey,
@@ -3370,7 +3489,6 @@ function createWebOnMessageHandler(params) {
3370
3489
  cfg,
3371
3490
  msg,
3372
3491
  ...typeof preflightAudioTranscript === "string" ? { mentionText: preflightAudioTranscript } : {},
3373
- conversationId,
3374
3492
  groupHistoryKey,
3375
3493
  agentId: route.agentId,
3376
3494
  sessionKey: route.sessionKey,
@@ -3385,7 +3503,10 @@ function createWebOnMessageHandler(params) {
3385
3503
  replyLogger: params.replyLogger
3386
3504
  });
3387
3505
  }
3388
- if (!gating.shouldProcess) return;
3506
+ if (!gating.shouldProcess) {
3507
+ await clearPreDispatchReaction();
3508
+ return;
3509
+ }
3389
3510
  }
3390
3511
  if (configuredRoute.bindingResolution) {
3391
3512
  const ensured = await ensureConfiguredBindingRouteReady({
@@ -3398,8 +3519,14 @@ function createWebOnMessageHandler(params) {
3398
3519
  return;
3399
3520
  }
3400
3521
  }
3401
- recordAcceptedConfiguredGroupRoute?.();
3522
+ if (recordAcceptedConfiguredGroupRoute && !configuredRoute.bindingResolution) {
3523
+ recordAcceptedConfiguredGroupRoute();
3524
+ recordAcceptedConfiguredGroupRoute = null;
3525
+ }
3402
3526
  await runAudioPreflightOnce();
3527
+ const hasBroadcastTargets = !configuredRoute.bindingResolution && Array.isArray(cfg.broadcast?.[peerId]) && cfg.broadcast[peerId].length > 0;
3528
+ if (hasBroadcastTargets && statusReactionController) await clearPreDispatchReaction();
3529
+ if (hasBroadcastTargets && !canRunEarlyAudioPreflight) await transcribeAudioOnce();
3403
3530
  if (!configuredRoute.bindingResolution && await maybeBroadcastMessage({
3404
3531
  cfg,
3405
3532
  msg,
@@ -3408,11 +3535,12 @@ function createWebOnMessageHandler(params) {
3408
3535
  groupHistoryKey,
3409
3536
  groupHistories: params.groupHistories,
3410
3537
  ...preflightAudioTranscript !== void 0 ? { preflightAudioTranscript } : {},
3411
- ...ackAlreadySent && msg.chatType !== "group" ? { ackAlreadySent: true } : {},
3412
- ...ackReaction && msg.chatType !== "group" ? { ackReaction } : {},
3413
- ...statusReactionController && msg.chatType !== "group" ? { ackAlreadySent: true } : {},
3538
+ ...ackAlreadySent && conversationKind !== "group" ? { ackAlreadySent: true } : {},
3539
+ ...ackReaction && conversationKind !== "group" ? { ackReaction } : {},
3540
+ ...statusReactionController && conversationKind !== "group" ? { ackAlreadySent: true } : {},
3414
3541
  processMessage: (m, r, k, opts) => processForRoute(cfg, m, r, k, opts)
3415
3542
  })) return;
3543
+ recordAcceptedConfiguredGroupRoute?.();
3416
3544
  await processForRoute(cfg, msg, route, groupHistoryKey, {
3417
3545
  ...preflightAudioTranscript !== void 0 ? { preflightAudioTranscript } : {},
3418
3546
  ...ackAlreadySent ? { ackAlreadySent: true } : {},
@@ -3480,29 +3608,6 @@ function resolveExplicitWhatsAppDebounceOverride(params) {
3480
3608
  function isRetryableAuthUnstableError(error) {
3481
3609
  return error instanceof WhatsAppAuthUnstableError || typeof error === "object" && error !== null && "code" in error && error.code === "whatsapp-auth-unstable";
3482
3610
  }
3483
- async function clearTerminalWebAuthState(params) {
3484
- try {
3485
- const cleared = await logoutWeb({
3486
- authDir: params.account.authDir,
3487
- isLegacyAuthDir: params.account.isLegacyAuthDir,
3488
- runtime: params.runtime
3489
- });
3490
- params.log.warn({
3491
- accountId: params.account.accountId,
3492
- cleared,
3493
- healthState: params.healthState,
3494
- status: params.statusLabel
3495
- }, "web reconnect: cleared cached auth after terminal close");
3496
- } catch (error) {
3497
- params.log.warn({
3498
- accountId: params.account.accountId,
3499
- error: formatError(error),
3500
- healthState: params.healthState,
3501
- status: params.statusLabel
3502
- }, "web reconnect: failed clearing cached auth after terminal close");
3503
- params.runtime.error(`WhatsApp Web cleanup failed after terminal close (status ${params.statusLabel}). Run \`${formatCliCommand("openclaw channels logout --channel whatsapp")}\`, then relink with \`${formatCliCommand("openclaw channels login --channel whatsapp")}\`.`);
3504
- }
3505
- }
3506
3611
  const DEFAULT_TRANSPORT_TIMEOUT_MS = 300 * 1e3;
3507
3612
  async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSocket, keepAlive = true, replyResolver, runtime = defaultRuntime, abortSignal, tuning = {}) {
3508
3613
  const activeReplyResolver = replyResolver ?? (await loadReplyResolverRuntime()).getReplyFromConfig;
@@ -3519,8 +3624,6 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
3519
3624
  module: "web-reconnect",
3520
3625
  runId
3521
3626
  });
3522
- const statusController = createWebChannelStatusController(tuning.statusSink);
3523
- statusController.emit();
3524
3627
  const baseCfg = getRuntimeConfig$1();
3525
3628
  const sourceCfg = getRuntimeConfigSourceSnapshot();
3526
3629
  const { cfg, account } = resolveWebMonitorConfigSnapshot({
@@ -3571,6 +3674,8 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
3571
3674
  sleep,
3572
3675
  isNonRetryableStatus: isNonRetryableWebCloseStatus
3573
3676
  });
3677
+ const statusController = createWebChannelStatusController(tuning.statusSink);
3678
+ statusController.emit();
3574
3679
  try {
3575
3680
  while (true) {
3576
3681
  if (stopRequested()) break;
@@ -3621,6 +3726,7 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
3621
3726
  mediaMaxMb: account.mediaMaxMb,
3622
3727
  selfChatMode: account.selfChatMode,
3623
3728
  sendReadReceipts: account.sendReadReceipts,
3729
+ socketTiming,
3624
3730
  debounceMs: inboundDebounceMs,
3625
3731
  shouldDebounce,
3626
3732
  socketRef: controller.socketRef,
@@ -3696,25 +3802,9 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
3696
3802
  reconnectAttempts: setupDecision.reconnectAttempts,
3697
3803
  maxAttempts: reconnectPolicy.maxAttempts
3698
3804
  }, "web reconnect: setup status error; max attempts reached");
3699
- if (setupDecision.healthState === "logged-out") {
3700
- await clearTerminalWebAuthState({
3701
- account,
3702
- runtime,
3703
- statusLabel: setupDecision.normalized.statusLabel,
3704
- healthState: setupDecision.healthState,
3705
- log: reconnectLogger
3706
- });
3707
- runtime.error(`WhatsApp session logged out during setup. Run \`${formatCliCommand("openclaw channels login --channel whatsapp")}\` to relink.`);
3708
- } else if (setupDecision.healthState === "conflict") {
3709
- await clearTerminalWebAuthState({
3710
- account,
3711
- runtime,
3712
- statusLabel: setupDecision.normalized.statusLabel,
3713
- healthState: setupDecision.healthState,
3714
- log: reconnectLogger
3715
- });
3716
- runtime.error(`WhatsApp Web connection closed during setup (status ${setupDecision.normalized.statusLabel}: session conflict). Resolve conflicting WhatsApp Web sessions, then relink with \`${formatCliCommand("openclaw channels login --channel whatsapp")}\`. Stopping web monitoring.`);
3717
- } else runtime.error(`WhatsApp Web connection closed during setup (status ${setupDecision.normalized.statusLabel}) after ${setupDecision.reconnectAttempts}/${reconnectPolicy.maxAttempts} attempts. Relink with \`${formatCliCommand("openclaw channels login --channel whatsapp")}\` if the issue persists.`);
3805
+ if (setupDecision.healthState === "logged-out") runtime.error(`WhatsApp session logged out during setup. Run \`${formatCliCommand("openclaw channels login --channel whatsapp")}\` to relink.`);
3806
+ else if (setupDecision.healthState === "conflict") runtime.error(`WhatsApp Web connection closed during setup (status ${setupDecision.normalized.statusLabel}: session conflict). Resolve conflicting WhatsApp Web sessions, then restart the channel. To force a fresh QR, run \`${formatCliCommand("openclaw channels logout --channel whatsapp")}\` before \`${formatCliCommand("openclaw channels login --channel whatsapp")}\`. Stopping web monitoring.`);
3807
+ else runtime.error(`WhatsApp Web connection closed during setup (status ${setupDecision.normalized.statusLabel}) after ${setupDecision.reconnectAttempts}/${reconnectPolicy.maxAttempts} attempts. Relink with \`${formatCliCommand("openclaw channels login --channel whatsapp")}\` if the issue persists.`);
3718
3808
  await controller.shutdown();
3719
3809
  break;
3720
3810
  }
@@ -3874,29 +3964,14 @@ async function monitorWebChannel(verbose, listenerFactory = attachWebInboxToSock
3874
3964
  reconnectAttempts: decision.reconnectAttempts,
3875
3965
  healthState: decision.healthState
3876
3966
  });
3877
- if (decision.healthState === "logged-out") {
3878
- await clearTerminalWebAuthState({
3879
- account,
3880
- runtime,
3881
- statusLabel: decision.normalized.statusLabel,
3882
- healthState: decision.healthState,
3883
- log: reconnectLogger
3884
- });
3885
- runtime.error(`WhatsApp session logged out. Run \`${formatCliCommand("openclaw channels login --channel whatsapp")}\` to relink.`);
3886
- } else if (decision.healthState === "conflict") {
3887
- await clearTerminalWebAuthState({
3888
- account,
3889
- runtime,
3890
- statusLabel: decision.normalized.statusLabel,
3891
- healthState: decision.healthState,
3892
- log: reconnectLogger
3893
- });
3967
+ if (decision.healthState === "logged-out") runtime.error(`WhatsApp session logged out. Run \`${formatCliCommand("openclaw channels login --channel whatsapp")}\` to relink.`);
3968
+ else if (decision.healthState === "conflict") {
3894
3969
  reconnectLogger.warn({
3895
3970
  connectionId: connection.connectionId,
3896
3971
  status: decision.normalized.statusLabel,
3897
3972
  error: decision.normalized.errorText
3898
3973
  }, "web reconnect: non-retryable close status; stopping monitor");
3899
- runtime.error(`WhatsApp Web connection closed (status ${decision.normalized.statusLabel}: session conflict). Resolve conflicting WhatsApp Web sessions, then relink with \`${formatCliCommand("openclaw channels login --channel whatsapp")}\`. Stopping web monitoring.`);
3974
+ runtime.error(`WhatsApp Web connection closed (status ${decision.normalized.statusLabel}: session conflict). Resolve conflicting WhatsApp Web sessions, then restart the channel. To force a fresh QR, run \`${formatCliCommand("openclaw channels logout --channel whatsapp")}\` before \`${formatCliCommand("openclaw channels login --channel whatsapp")}\`. Stopping web monitoring.`);
3900
3975
  } else {
3901
3976
  reconnectLogger.warn({
3902
3977
  connectionId: connection.connectionId,