@ardennguyen/zalo-agent-cli 2.5.8 → 3.0.0

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 (1492) hide show
  1. package/.env.example +23 -2
  2. package/DISCLAIMER.md +3 -3
  3. package/LICENSE +2 -3
  4. package/NOTICE +14 -34
  5. package/README.md +166 -72
  6. package/package.json +37 -19
  7. package/src/commands/account.js +369 -40
  8. package/src/commands/auto-reply.js +8 -25
  9. package/src/commands/bank.js +25 -6
  10. package/src/commands/catalog.js +54 -11
  11. package/src/commands/cloud.js +459 -0
  12. package/src/commands/conv.js +470 -287
  13. package/src/commands/db.js +442 -0
  14. package/src/commands/fleet.js +449 -41
  15. package/src/commands/friend.js +198 -45
  16. package/src/commands/group.js +256 -43
  17. package/src/commands/label.js +16 -2
  18. package/src/commands/listen.js +495 -122
  19. package/src/commands/login.js +313 -117
  20. package/src/commands/mcp-approver.js +103 -0
  21. package/src/commands/mcp-webhook.js +13 -5
  22. package/src/commands/mcp.js +800 -104
  23. package/src/commands/msg-media.js +187 -0
  24. package/src/commands/msg-search.js +219 -0
  25. package/src/commands/msg.js +414 -110
  26. package/src/commands/oa/calls.js +134 -0
  27. package/src/commands/oa/content.js +266 -0
  28. package/src/commands/oa/gmf.js +429 -0
  29. package/src/commands/oa/messages.js +355 -0
  30. package/src/commands/oa/users.js +502 -0
  31. package/src/commands/oa-init.js +122 -123
  32. package/src/commands/oa-listen.js +374 -150
  33. package/src/commands/oa.js +83 -598
  34. package/src/commands/policy.js +224 -0
  35. package/src/commands/poll.js +3 -0
  36. package/src/commands/presence.js +175 -0
  37. package/src/commands/profile.js +45 -94
  38. package/src/commands/quick-msg.js +26 -0
  39. package/src/commands/read.js +172 -0
  40. package/src/commands/reminder.js +34 -4
  41. package/src/commands/social.js +223 -0
  42. package/src/commands/sync.js +482 -158
  43. package/src/commands/write.js +85 -0
  44. package/src/commands/zbs.js +502 -0
  45. package/src/core/account-devices.js +76 -0
  46. package/src/core/account-state.js +167 -0
  47. package/src/core/account-status.js +355 -0
  48. package/src/core/accounts.js +11 -4
  49. package/src/core/admin-api.js +392 -0
  50. package/src/core/approval.js +271 -0
  51. package/src/core/auto-reply.js +27 -0
  52. package/src/core/bank-accounts.js +1 -1
  53. package/src/core/block-sharing.js +43 -0
  54. package/src/core/board-pass.js +94 -0
  55. package/src/core/cached-message.js +78 -8
  56. package/src/core/cloud-plan.js +285 -0
  57. package/src/core/cloud-verify.js +305 -0
  58. package/src/core/confirm-guard.js +808 -0
  59. package/src/core/contact-names.js +504 -0
  60. package/src/core/control-events.js +525 -0
  61. package/src/core/control-replay.js +106 -0
  62. package/src/core/conversation-actions.js +214 -0
  63. package/src/core/credentials.js +112 -11
  64. package/src/core/current-user-names.js +60 -26
  65. package/src/core/daemon-channel.js +296 -15
  66. package/src/core/daemon-health.js +110 -7
  67. package/src/core/daemon-presence.js +455 -0
  68. package/src/core/daemon-sync.js +58 -18
  69. package/src/core/data-export.js +610 -0
  70. package/src/core/db-tools.js +473 -0
  71. package/src/core/db.js +1327 -83
  72. package/src/core/deploy-policy.js +560 -0
  73. package/src/core/device-since.js +123 -0
  74. package/src/core/e2ee-notice.js +41 -0
  75. package/src/core/exit-codes.js +7 -0
  76. package/src/core/export-crypt.js +290 -0
  77. package/src/core/fleet-controller.js +340 -0
  78. package/src/core/fleet-purge.js +144 -0
  79. package/src/core/fleet-qr.js +273 -0
  80. package/src/core/fleet-serve.js +292 -0
  81. package/src/core/fleet-supervisor.js +176 -0
  82. package/src/core/fleet-templates.js +568 -39
  83. package/src/core/fleet.js +232 -20
  84. package/src/core/friend-requests.js +80 -16
  85. package/src/core/friend-search.js +1 -1
  86. package/src/core/group-admin.js +2 -29
  87. package/src/core/group-departure.js +233 -0
  88. package/src/core/group-exit.js +621 -0
  89. package/src/core/group-members.js +21 -9
  90. package/src/core/group-settings.js +5 -207
  91. package/src/core/hidden-chat-actions.js +96 -0
  92. package/src/core/hidden-chats.js +425 -0
  93. package/src/core/history-fetch.js +81 -27
  94. package/src/core/keystore/config.js +163 -0
  95. package/src/core/keystore/container.js +208 -0
  96. package/src/core/keystore/index.js +483 -0
  97. package/src/core/keystore/keyring.js +588 -0
  98. package/src/core/keystore/kms.js +157 -0
  99. package/src/core/keystore/media-seal.js +711 -0
  100. package/src/core/keystore/providers.js +492 -0
  101. package/src/core/keystore/stores.js +177 -0
  102. package/src/core/keystore/worker.js +80 -0
  103. package/src/core/kick-marker.js +61 -0
  104. package/src/core/last-online.js +236 -0
  105. package/src/core/list-all.js +280 -0
  106. package/src/core/listener-lifecycle.js +101 -10
  107. package/src/core/live-api.js +1 -1
  108. package/src/core/live-store.js +111 -53
  109. package/src/core/local-reads.js +16 -0
  110. package/src/core/logout.js +102 -140
  111. package/src/core/media-deliver.js +732 -0
  112. package/src/core/media-route.js +347 -0
  113. package/src/core/media-store.js +408 -0
  114. package/src/core/mutual-groups.js +94 -0
  115. package/src/core/name-refresh.js +135 -0
  116. package/src/core/oa/calls.js +212 -0
  117. package/src/core/oa/content.js +390 -0
  118. package/src/core/oa/gmf.js +537 -0
  119. package/src/core/oa/messages.js +470 -0
  120. package/src/core/oa/oauth.js +422 -0
  121. package/src/core/oa/transport.js +645 -0
  122. package/src/core/oa/users.js +694 -0
  123. package/src/core/oa-client.js +49 -415
  124. package/src/core/overflow-repair.js +221 -0
  125. package/src/core/policy-layer.js +55 -0
  126. package/src/core/policy-schema.js +401 -0
  127. package/src/core/policy-store.js +1058 -0
  128. package/src/core/presence-config.js +100 -0
  129. package/src/core/presence-leases.js +211 -0
  130. package/src/core/presence.js +688 -0
  131. package/src/core/profile-settings.js +243 -0
  132. package/src/core/qr-login-flow.js +237 -0
  133. package/src/core/read-state.js +145 -22
  134. package/src/core/receipts.js +355 -548
  135. package/src/core/recent-conversations.js +28 -6
  136. package/src/core/row-mac.js +405 -0
  137. package/src/core/search.js +271 -0
  138. package/src/core/secure-page.js +771 -0
  139. package/src/core/self-heal.js +493 -34
  140. package/src/core/session-expiry.js +103 -0
  141. package/src/core/session-notice.js +249 -0
  142. package/src/core/social/client.js +296 -0
  143. package/src/core/social/dpop.js +54 -0
  144. package/src/core/socket-host.js +437 -0
  145. package/src/core/sqlite.js +520 -0
  146. package/src/core/startup-gate.js +49 -5
  147. package/src/core/storage-quota.js +796 -0
  148. package/src/core/sync-action.js +180 -0
  149. package/src/core/sync-v2/auto-sync.js +356 -0
  150. package/src/core/sync-v2/board.js +130 -92
  151. package/src/core/sync-v2/conv-state.js +21 -4
  152. package/src/core/sync-v2/gap-advice.js +2 -3
  153. package/src/core/sync-v2/index.js +739 -323
  154. package/src/core/sync-v2/media.js +116 -27
  155. package/src/core/sync-v2/plan.js +16 -2
  156. package/src/core/sync-v2/ranges.js +98 -0
  157. package/src/core/sync-v2/reactions.js +12 -196
  158. package/src/core/sync-v2/senders.js +32 -14
  159. package/src/core/sync-v2/zcloud.js +177 -579
  160. package/src/core/sync.js +23 -21
  161. package/src/core/test-fake-session.js +87 -0
  162. package/src/core/typing.js +78 -0
  163. package/src/core/unknown-acts.js +240 -0
  164. package/src/core/voice-text.js +171 -0
  165. package/src/core/webhook-outbox.js +110 -12
  166. package/src/core/zalo-client.js +147 -188
  167. package/src/core/zbs/api.js +505 -0
  168. package/src/core/zbs/image.js +85 -0
  169. package/src/core/zbs/phone.js +59 -0
  170. package/src/core/zbs/rsa.js +129 -0
  171. package/src/core/zbs/template-check.js +449 -0
  172. package/src/index.js +60 -10
  173. package/src/mcp/action-tool.js +188 -0
  174. package/src/mcp/actions-business.js +488 -0
  175. package/src/mcp/actions-conversation.js +633 -0
  176. package/src/mcp/actions-media.js +268 -0
  177. package/src/mcp/actions-profile.js +281 -0
  178. package/src/mcp/actions-social.js +518 -0
  179. package/src/mcp/instructions.js +19 -5
  180. package/src/mcp/mcp-config.js +112 -10
  181. package/src/mcp/mcp-http-transport.js +162 -25
  182. package/src/mcp/mcp-server.js +14 -3
  183. package/src/mcp/mcp-tools.js +1686 -384
  184. package/src/mcp/message-buffer.js +58 -12
  185. package/src/mcp/notifier.js +1 -1
  186. package/src/mcp/official-tool.js +298 -0
  187. package/src/mcp/sync-jobs.js +7 -1
  188. package/src/mcp/sync-stages.js +72 -0
  189. package/src/mcp/thread-name-cache.js +43 -5
  190. package/src/mcp/tool-presence.js +153 -0
  191. package/src/mcp/wait-batch.js +27 -0
  192. package/src/mcp/wait-result.js +19 -21
  193. package/src/utils/bank-helpers.js +52 -1
  194. package/src/utils/cli-args.js +12 -12
  195. package/src/utils/date-range.js +50 -0
  196. package/src/utils/hidden-access.js +141 -0
  197. package/src/utils/interrupt.js +37 -0
  198. package/src/utils/json-input.js +41 -0
  199. package/src/utils/list-replies.js +60 -0
  200. package/src/utils/my-documents.js +2 -3
  201. package/src/utils/output.js +66 -2
  202. package/src/utils/qr-display.js +14 -8
  203. package/src/utils/qr-http-server.js +674 -134
  204. package/src/utils/quote.js +8 -8
  205. package/src/utils/secret-input.js +57 -0
  206. package/src/utils/vn-text.js +72 -0
  207. package/src/zalo/README.md +46 -0
  208. package/src/zalo/anomaly-store.js +49 -0
  209. package/src/zalo/api/actions.js +670 -0
  210. package/src/zalo/api/boards.js +1036 -0
  211. package/src/zalo/api/business.js +662 -0
  212. package/src/zalo/api/conversations.js +329 -0
  213. package/src/zalo/api/groups.js +631 -0
  214. package/src/zalo/api/messages.js +695 -0
  215. package/src/zalo/api/people.js +711 -0
  216. package/src/zalo/api/profile.js +374 -0
  217. package/src/zalo/api/session.js +45 -0
  218. package/src/zalo/api/upload.js +976 -0
  219. package/src/zalo/api.js +117 -0
  220. package/src/zalo/client.js +206 -0
  221. package/src/zalo/custom/forward.js +199 -0
  222. package/src/zalo/custom/gid.js +123 -0
  223. package/src/zalo/custom/group-history.js +246 -0
  224. package/src/zalo/custom/group-invite.js +53 -0
  225. package/src/zalo/custom/group-settings.js +186 -0
  226. package/src/zalo/custom/group-visibility.js +71 -0
  227. package/src/zalo/custom/pin.js +220 -0
  228. package/src/zalo/custom/renewlink.js +115 -0
  229. package/src/zalo/device-fingerprint.js +33 -0
  230. package/src/zalo/download-fetch.js +45 -0
  231. package/src/zalo/enums.js +25 -0
  232. package/src/zalo/errors.js +195 -0
  233. package/src/zalo/format/mentions.js +140 -0
  234. package/src/zalo/format/split-message.js +222 -0
  235. package/src/zalo/format/urgency.js +29 -0
  236. package/src/zalo/headers.js +93 -0
  237. package/src/zalo/index.js +72 -0
  238. package/src/zalo/kernel/atomic-file.js +217 -0
  239. package/src/zalo/kernel/bound.js +46 -0
  240. package/src/zalo/kernel/cipher.js +88 -0
  241. package/src/zalo/kernel/cookies.js +125 -0
  242. package/src/zalo/kernel/endpoint.js +211 -0
  243. package/src/zalo/kernel/http.js +360 -0
  244. package/src/zalo/kernel/json-ids.js +118 -0
  245. package/src/zalo/kernel/login-cipher.js +165 -0
  246. package/src/zalo/kernel/secret-file.js +45 -0
  247. package/src/zalo/kernel/session.js +261 -0
  248. package/src/zalo/kernel/zcloud-cipher.js +83 -0
  249. package/src/zalo/kickout.js +136 -0
  250. package/src/zalo/listener/dispatch.js +404 -0
  251. package/src/zalo/listener/frames.js +222 -0
  252. package/src/zalo/listener/friend-events.js +113 -0
  253. package/src/zalo/listener/group-events.js +117 -0
  254. package/src/zalo/listener/keepalive.js +191 -0
  255. package/src/zalo/listener/listener.js +469 -0
  256. package/src/zalo/listener/models.js +220 -0
  257. package/src/zalo/listener/reactions.js +224 -0
  258. package/src/zalo/listener/socket-tap.js +228 -0
  259. package/src/zalo/login.js +387 -0
  260. package/src/zalo/logout.js +192 -0
  261. package/src/zalo/maintenance.js +183 -0
  262. package/src/zalo/official-logout.js +254 -0
  263. package/src/zalo/paging.js +114 -0
  264. package/src/zalo/profile.js +184 -0
  265. package/src/zalo/pulls.js +129 -0
  266. package/src/zalo/qr-image.js +90 -0
  267. package/src/zalo/qr-login.js +494 -0
  268. package/src/zalo/queues/control-queue.js +193 -0
  269. package/src/zalo/queues/offline-queue.js +275 -0
  270. package/src/zalo/queues/queue-verify.js +306 -0
  271. package/src/zalo/reads.js +723 -0
  272. package/src/zalo/receipts.js +550 -0
  273. package/src/zalo/req-id.js +24 -0
  274. package/src/zalo/retry.js +343 -0
  275. package/src/zalo/session-call.js +55 -0
  276. package/src/zalo/socket.js +762 -0
  277. package/src/zalo/transfer-sync/assets.js +181 -0
  278. package/src/zalo/transfer-sync/channel.js +459 -0
  279. package/src/zalo/transfer-sync/download.js +161 -0
  280. package/src/zalo/transfer-sync/message-types.js +520 -0
  281. package/src/zalo/transfer-sync/transfer.js +468 -0
  282. package/src/zalo/trust-store.js +99 -0
  283. package/src/zalo/trust-wasm.js +393 -0
  284. package/src/zalo/upkeep.js +127 -0
  285. package/src/zalo/web-call.js +103 -0
  286. package/src/zalo/writes.js +704 -0
  287. package/src/zalo/zcloud-e2ee.js +214 -0
  288. package/src/zalo/zcloud.js +802 -0
  289. package/node_modules/asynckit/LICENSE +0 -21
  290. package/node_modules/asynckit/README.md +0 -233
  291. package/node_modules/asynckit/bench.js +0 -76
  292. package/node_modules/asynckit/index.js +0 -6
  293. package/node_modules/asynckit/lib/abort.js +0 -29
  294. package/node_modules/asynckit/lib/async.js +0 -34
  295. package/node_modules/asynckit/lib/defer.js +0 -26
  296. package/node_modules/asynckit/lib/iterate.js +0 -75
  297. package/node_modules/asynckit/lib/readable_asynckit.js +0 -91
  298. package/node_modules/asynckit/lib/readable_parallel.js +0 -25
  299. package/node_modules/asynckit/lib/readable_serial.js +0 -25
  300. package/node_modules/asynckit/lib/readable_serial_ordered.js +0 -29
  301. package/node_modules/asynckit/lib/state.js +0 -37
  302. package/node_modules/asynckit/lib/streamify.js +0 -141
  303. package/node_modules/asynckit/lib/terminator.js +0 -29
  304. package/node_modules/asynckit/package.json +0 -63
  305. package/node_modules/asynckit/parallel.js +0 -43
  306. package/node_modules/asynckit/serial.js +0 -17
  307. package/node_modules/asynckit/serialOrdered.js +0 -75
  308. package/node_modules/asynckit/stream.js +0 -21
  309. package/node_modules/bignumber.js/CHANGELOG.md +0 -381
  310. package/node_modules/bignumber.js/LICENCE.md +0 -26
  311. package/node_modules/bignumber.js/README.md +0 -289
  312. package/node_modules/bignumber.js/bignumber.d.mts +0 -6
  313. package/node_modules/bignumber.js/bignumber.d.ts +0 -5
  314. package/node_modules/bignumber.js/bignumber.js +0 -2922
  315. package/node_modules/bignumber.js/bignumber.mjs +0 -2907
  316. package/node_modules/bignumber.js/doc/API.html +0 -2249
  317. package/node_modules/bignumber.js/package.json +0 -60
  318. package/node_modules/bignumber.js/types.d.ts +0 -1821
  319. package/node_modules/call-bind-apply-helpers/.eslintrc +0 -17
  320. package/node_modules/call-bind-apply-helpers/.github/FUNDING.yml +0 -12
  321. package/node_modules/call-bind-apply-helpers/.nycrc +0 -9
  322. package/node_modules/call-bind-apply-helpers/CHANGELOG.md +0 -30
  323. package/node_modules/call-bind-apply-helpers/LICENSE +0 -21
  324. package/node_modules/call-bind-apply-helpers/README.md +0 -62
  325. package/node_modules/call-bind-apply-helpers/actualApply.d.ts +0 -1
  326. package/node_modules/call-bind-apply-helpers/actualApply.js +0 -10
  327. package/node_modules/call-bind-apply-helpers/applyBind.d.ts +0 -19
  328. package/node_modules/call-bind-apply-helpers/applyBind.js +0 -10
  329. package/node_modules/call-bind-apply-helpers/functionApply.d.ts +0 -1
  330. package/node_modules/call-bind-apply-helpers/functionApply.js +0 -4
  331. package/node_modules/call-bind-apply-helpers/functionCall.d.ts +0 -1
  332. package/node_modules/call-bind-apply-helpers/functionCall.js +0 -4
  333. package/node_modules/call-bind-apply-helpers/index.d.ts +0 -64
  334. package/node_modules/call-bind-apply-helpers/index.js +0 -15
  335. package/node_modules/call-bind-apply-helpers/package.json +0 -85
  336. package/node_modules/call-bind-apply-helpers/reflectApply.d.ts +0 -3
  337. package/node_modules/call-bind-apply-helpers/reflectApply.js +0 -4
  338. package/node_modules/call-bind-apply-helpers/test/index.js +0 -63
  339. package/node_modules/call-bind-apply-helpers/tsconfig.json +0 -9
  340. package/node_modules/combined-stream/License +0 -19
  341. package/node_modules/combined-stream/Readme.md +0 -138
  342. package/node_modules/combined-stream/lib/combined_stream.js +0 -208
  343. package/node_modules/combined-stream/package.json +0 -25
  344. package/node_modules/crypto-js/CONTRIBUTING.md +0 -28
  345. package/node_modules/crypto-js/LICENSE +0 -24
  346. package/node_modules/crypto-js/README.md +0 -275
  347. package/node_modules/crypto-js/aes.js +0 -234
  348. package/node_modules/crypto-js/blowfish.js +0 -471
  349. package/node_modules/crypto-js/bower.json +0 -39
  350. package/node_modules/crypto-js/cipher-core.js +0 -895
  351. package/node_modules/crypto-js/core.js +0 -807
  352. package/node_modules/crypto-js/crypto-js.js +0 -6657
  353. package/node_modules/crypto-js/docs/QuickStartGuide.wiki +0 -470
  354. package/node_modules/crypto-js/enc-base64.js +0 -136
  355. package/node_modules/crypto-js/enc-base64url.js +0 -148
  356. package/node_modules/crypto-js/enc-hex.js +0 -18
  357. package/node_modules/crypto-js/enc-latin1.js +0 -18
  358. package/node_modules/crypto-js/enc-utf16.js +0 -149
  359. package/node_modules/crypto-js/enc-utf8.js +0 -18
  360. package/node_modules/crypto-js/evpkdf.js +0 -134
  361. package/node_modules/crypto-js/format-hex.js +0 -66
  362. package/node_modules/crypto-js/format-openssl.js +0 -18
  363. package/node_modules/crypto-js/hmac-md5.js +0 -18
  364. package/node_modules/crypto-js/hmac-ripemd160.js +0 -18
  365. package/node_modules/crypto-js/hmac-sha1.js +0 -18
  366. package/node_modules/crypto-js/hmac-sha224.js +0 -18
  367. package/node_modules/crypto-js/hmac-sha256.js +0 -18
  368. package/node_modules/crypto-js/hmac-sha3.js +0 -18
  369. package/node_modules/crypto-js/hmac-sha384.js +0 -18
  370. package/node_modules/crypto-js/hmac-sha512.js +0 -18
  371. package/node_modules/crypto-js/hmac.js +0 -143
  372. package/node_modules/crypto-js/index.js +0 -18
  373. package/node_modules/crypto-js/lib-typedarrays.js +0 -76
  374. package/node_modules/crypto-js/md5.js +0 -268
  375. package/node_modules/crypto-js/mode-cfb.js +0 -80
  376. package/node_modules/crypto-js/mode-ctr-gladman.js +0 -116
  377. package/node_modules/crypto-js/mode-ctr.js +0 -58
  378. package/node_modules/crypto-js/mode-ecb.js +0 -40
  379. package/node_modules/crypto-js/mode-ofb.js +0 -54
  380. package/node_modules/crypto-js/package.json +0 -42
  381. package/node_modules/crypto-js/pad-ansix923.js +0 -49
  382. package/node_modules/crypto-js/pad-iso10126.js +0 -44
  383. package/node_modules/crypto-js/pad-iso97971.js +0 -40
  384. package/node_modules/crypto-js/pad-nopadding.js +0 -30
  385. package/node_modules/crypto-js/pad-pkcs7.js +0 -18
  386. package/node_modules/crypto-js/pad-zeropadding.js +0 -47
  387. package/node_modules/crypto-js/pbkdf2.js +0 -145
  388. package/node_modules/crypto-js/rabbit-legacy.js +0 -190
  389. package/node_modules/crypto-js/rabbit.js +0 -192
  390. package/node_modules/crypto-js/rc4.js +0 -139
  391. package/node_modules/crypto-js/ripemd160.js +0 -267
  392. package/node_modules/crypto-js/sha1.js +0 -150
  393. package/node_modules/crypto-js/sha224.js +0 -80
  394. package/node_modules/crypto-js/sha256.js +0 -199
  395. package/node_modules/crypto-js/sha3.js +0 -326
  396. package/node_modules/crypto-js/sha384.js +0 -83
  397. package/node_modules/crypto-js/sha512.js +0 -326
  398. package/node_modules/crypto-js/tripledes.js +0 -779
  399. package/node_modules/crypto-js/x64-core.js +0 -304
  400. package/node_modules/delayed-stream/.npmignore +0 -1
  401. package/node_modules/delayed-stream/License +0 -19
  402. package/node_modules/delayed-stream/Makefile +0 -7
  403. package/node_modules/delayed-stream/Readme.md +0 -141
  404. package/node_modules/delayed-stream/lib/delayed_stream.js +0 -107
  405. package/node_modules/delayed-stream/package.json +0 -27
  406. package/node_modules/dunder-proto/.eslintrc +0 -5
  407. package/node_modules/dunder-proto/.github/FUNDING.yml +0 -12
  408. package/node_modules/dunder-proto/.nycrc +0 -13
  409. package/node_modules/dunder-proto/CHANGELOG.md +0 -24
  410. package/node_modules/dunder-proto/LICENSE +0 -21
  411. package/node_modules/dunder-proto/README.md +0 -54
  412. package/node_modules/dunder-proto/get.d.ts +0 -5
  413. package/node_modules/dunder-proto/get.js +0 -30
  414. package/node_modules/dunder-proto/package.json +0 -76
  415. package/node_modules/dunder-proto/set.d.ts +0 -5
  416. package/node_modules/dunder-proto/set.js +0 -35
  417. package/node_modules/dunder-proto/test/get.js +0 -34
  418. package/node_modules/dunder-proto/test/index.js +0 -4
  419. package/node_modules/dunder-proto/test/set.js +0 -50
  420. package/node_modules/dunder-proto/tsconfig.json +0 -9
  421. package/node_modules/es-define-property/.eslintrc +0 -13
  422. package/node_modules/es-define-property/.github/FUNDING.yml +0 -12
  423. package/node_modules/es-define-property/.nycrc +0 -9
  424. package/node_modules/es-define-property/CHANGELOG.md +0 -29
  425. package/node_modules/es-define-property/LICENSE +0 -21
  426. package/node_modules/es-define-property/README.md +0 -49
  427. package/node_modules/es-define-property/index.d.ts +0 -3
  428. package/node_modules/es-define-property/index.js +0 -14
  429. package/node_modules/es-define-property/package.json +0 -81
  430. package/node_modules/es-define-property/test/index.js +0 -56
  431. package/node_modules/es-define-property/tsconfig.json +0 -10
  432. package/node_modules/es-errors/.eslintrc +0 -5
  433. package/node_modules/es-errors/.github/FUNDING.yml +0 -12
  434. package/node_modules/es-errors/CHANGELOG.md +0 -40
  435. package/node_modules/es-errors/LICENSE +0 -21
  436. package/node_modules/es-errors/README.md +0 -55
  437. package/node_modules/es-errors/eval.d.ts +0 -3
  438. package/node_modules/es-errors/eval.js +0 -4
  439. package/node_modules/es-errors/index.d.ts +0 -3
  440. package/node_modules/es-errors/index.js +0 -4
  441. package/node_modules/es-errors/package.json +0 -80
  442. package/node_modules/es-errors/range.d.ts +0 -3
  443. package/node_modules/es-errors/range.js +0 -4
  444. package/node_modules/es-errors/ref.d.ts +0 -3
  445. package/node_modules/es-errors/ref.js +0 -4
  446. package/node_modules/es-errors/syntax.d.ts +0 -3
  447. package/node_modules/es-errors/syntax.js +0 -4
  448. package/node_modules/es-errors/test/index.js +0 -19
  449. package/node_modules/es-errors/tsconfig.json +0 -49
  450. package/node_modules/es-errors/type.d.ts +0 -3
  451. package/node_modules/es-errors/type.js +0 -4
  452. package/node_modules/es-errors/uri.d.ts +0 -3
  453. package/node_modules/es-errors/uri.js +0 -4
  454. package/node_modules/es-object-atoms/.eslintrc +0 -16
  455. package/node_modules/es-object-atoms/.github/FUNDING.yml +0 -12
  456. package/node_modules/es-object-atoms/CHANGELOG.md +0 -44
  457. package/node_modules/es-object-atoms/LICENSE +0 -21
  458. package/node_modules/es-object-atoms/README.md +0 -63
  459. package/node_modules/es-object-atoms/RequireObjectCoercible.d.ts +0 -3
  460. package/node_modules/es-object-atoms/RequireObjectCoercible.js +0 -11
  461. package/node_modules/es-object-atoms/ToObject.d.ts +0 -7
  462. package/node_modules/es-object-atoms/ToObject.js +0 -10
  463. package/node_modules/es-object-atoms/index.d.ts +0 -3
  464. package/node_modules/es-object-atoms/index.js +0 -4
  465. package/node_modules/es-object-atoms/isObject.d.ts +0 -3
  466. package/node_modules/es-object-atoms/isObject.js +0 -6
  467. package/node_modules/es-object-atoms/package.json +0 -79
  468. package/node_modules/es-object-atoms/test/index.js +0 -38
  469. package/node_modules/es-object-atoms/tsconfig.json +0 -7
  470. package/node_modules/es-set-tostringtag/.eslintrc +0 -13
  471. package/node_modules/es-set-tostringtag/.nycrc +0 -9
  472. package/node_modules/es-set-tostringtag/CHANGELOG.md +0 -67
  473. package/node_modules/es-set-tostringtag/LICENSE +0 -21
  474. package/node_modules/es-set-tostringtag/README.md +0 -53
  475. package/node_modules/es-set-tostringtag/index.d.ts +0 -10
  476. package/node_modules/es-set-tostringtag/index.js +0 -35
  477. package/node_modules/es-set-tostringtag/package.json +0 -78
  478. package/node_modules/es-set-tostringtag/test/index.js +0 -85
  479. package/node_modules/es-set-tostringtag/tsconfig.json +0 -9
  480. package/node_modules/form-data/CHANGELOG.md +0 -686
  481. package/node_modules/form-data/License +0 -19
  482. package/node_modules/form-data/README.md +0 -355
  483. package/node_modules/form-data/index.d.ts +0 -62
  484. package/node_modules/form-data/lib/browser.js +0 -4
  485. package/node_modules/form-data/lib/form_data.js +0 -506
  486. package/node_modules/form-data/lib/populate.js +0 -10
  487. package/node_modules/form-data/node_modules/mime-db/HISTORY.md +0 -507
  488. package/node_modules/form-data/node_modules/mime-db/LICENSE +0 -23
  489. package/node_modules/form-data/node_modules/mime-db/README.md +0 -100
  490. package/node_modules/form-data/node_modules/mime-db/db.json +0 -8519
  491. package/node_modules/form-data/node_modules/mime-db/index.js +0 -12
  492. package/node_modules/form-data/node_modules/mime-db/package.json +0 -60
  493. package/node_modules/form-data/node_modules/mime-types/HISTORY.md +0 -397
  494. package/node_modules/form-data/node_modules/mime-types/LICENSE +0 -23
  495. package/node_modules/form-data/node_modules/mime-types/README.md +0 -113
  496. package/node_modules/form-data/node_modules/mime-types/index.js +0 -188
  497. package/node_modules/form-data/node_modules/mime-types/package.json +0 -44
  498. package/node_modules/form-data/package.json +0 -82
  499. package/node_modules/function-bind/.eslintrc +0 -21
  500. package/node_modules/function-bind/.github/FUNDING.yml +0 -12
  501. package/node_modules/function-bind/.github/SECURITY.md +0 -3
  502. package/node_modules/function-bind/.nycrc +0 -13
  503. package/node_modules/function-bind/CHANGELOG.md +0 -136
  504. package/node_modules/function-bind/LICENSE +0 -20
  505. package/node_modules/function-bind/README.md +0 -46
  506. package/node_modules/function-bind/implementation.js +0 -84
  507. package/node_modules/function-bind/index.js +0 -5
  508. package/node_modules/function-bind/package.json +0 -87
  509. package/node_modules/function-bind/test/.eslintrc +0 -9
  510. package/node_modules/function-bind/test/index.js +0 -252
  511. package/node_modules/get-intrinsic/.eslintrc +0 -42
  512. package/node_modules/get-intrinsic/.github/FUNDING.yml +0 -12
  513. package/node_modules/get-intrinsic/.nycrc +0 -9
  514. package/node_modules/get-intrinsic/CHANGELOG.md +0 -186
  515. package/node_modules/get-intrinsic/LICENSE +0 -21
  516. package/node_modules/get-intrinsic/README.md +0 -71
  517. package/node_modules/get-intrinsic/index.js +0 -378
  518. package/node_modules/get-intrinsic/package.json +0 -97
  519. package/node_modules/get-intrinsic/test/GetIntrinsic.js +0 -274
  520. package/node_modules/get-proto/.eslintrc +0 -10
  521. package/node_modules/get-proto/.github/FUNDING.yml +0 -12
  522. package/node_modules/get-proto/.nycrc +0 -9
  523. package/node_modules/get-proto/CHANGELOG.md +0 -21
  524. package/node_modules/get-proto/LICENSE +0 -21
  525. package/node_modules/get-proto/Object.getPrototypeOf.d.ts +0 -5
  526. package/node_modules/get-proto/Object.getPrototypeOf.js +0 -6
  527. package/node_modules/get-proto/README.md +0 -50
  528. package/node_modules/get-proto/Reflect.getPrototypeOf.d.ts +0 -3
  529. package/node_modules/get-proto/Reflect.getPrototypeOf.js +0 -4
  530. package/node_modules/get-proto/index.d.ts +0 -5
  531. package/node_modules/get-proto/index.js +0 -27
  532. package/node_modules/get-proto/package.json +0 -81
  533. package/node_modules/get-proto/test/index.js +0 -68
  534. package/node_modules/get-proto/tsconfig.json +0 -9
  535. package/node_modules/gopd/.eslintrc +0 -16
  536. package/node_modules/gopd/.github/FUNDING.yml +0 -12
  537. package/node_modules/gopd/CHANGELOG.md +0 -45
  538. package/node_modules/gopd/LICENSE +0 -21
  539. package/node_modules/gopd/README.md +0 -40
  540. package/node_modules/gopd/gOPD.d.ts +0 -1
  541. package/node_modules/gopd/gOPD.js +0 -4
  542. package/node_modules/gopd/index.d.ts +0 -5
  543. package/node_modules/gopd/index.js +0 -15
  544. package/node_modules/gopd/package.json +0 -77
  545. package/node_modules/gopd/test/index.js +0 -36
  546. package/node_modules/gopd/tsconfig.json +0 -9
  547. package/node_modules/has-symbols/.eslintrc +0 -11
  548. package/node_modules/has-symbols/.github/FUNDING.yml +0 -12
  549. package/node_modules/has-symbols/.nycrc +0 -9
  550. package/node_modules/has-symbols/CHANGELOG.md +0 -91
  551. package/node_modules/has-symbols/LICENSE +0 -21
  552. package/node_modules/has-symbols/README.md +0 -46
  553. package/node_modules/has-symbols/index.d.ts +0 -3
  554. package/node_modules/has-symbols/index.js +0 -14
  555. package/node_modules/has-symbols/package.json +0 -111
  556. package/node_modules/has-symbols/shams.d.ts +0 -3
  557. package/node_modules/has-symbols/shams.js +0 -45
  558. package/node_modules/has-symbols/test/index.js +0 -22
  559. package/node_modules/has-symbols/test/shams/core-js.js +0 -29
  560. package/node_modules/has-symbols/test/shams/get-own-property-symbols.js +0 -29
  561. package/node_modules/has-symbols/test/tests.js +0 -58
  562. package/node_modules/has-symbols/tsconfig.json +0 -10
  563. package/node_modules/has-tostringtag/.eslintrc +0 -5
  564. package/node_modules/has-tostringtag/.github/FUNDING.yml +0 -12
  565. package/node_modules/has-tostringtag/.nycrc +0 -13
  566. package/node_modules/has-tostringtag/CHANGELOG.md +0 -42
  567. package/node_modules/has-tostringtag/LICENSE +0 -21
  568. package/node_modules/has-tostringtag/README.md +0 -46
  569. package/node_modules/has-tostringtag/index.d.ts +0 -3
  570. package/node_modules/has-tostringtag/index.js +0 -8
  571. package/node_modules/has-tostringtag/package.json +0 -108
  572. package/node_modules/has-tostringtag/shams.d.ts +0 -3
  573. package/node_modules/has-tostringtag/shams.js +0 -8
  574. package/node_modules/has-tostringtag/test/index.js +0 -21
  575. package/node_modules/has-tostringtag/test/shams/core-js.js +0 -31
  576. package/node_modules/has-tostringtag/test/shams/get-own-property-symbols.js +0 -30
  577. package/node_modules/has-tostringtag/test/tests.js +0 -15
  578. package/node_modules/has-tostringtag/tsconfig.json +0 -49
  579. package/node_modules/hasown/.github/FUNDING.yml +0 -12
  580. package/node_modules/hasown/.nycrc +0 -13
  581. package/node_modules/hasown/CHANGELOG.md +0 -58
  582. package/node_modules/hasown/LICENSE +0 -21
  583. package/node_modules/hasown/README.md +0 -40
  584. package/node_modules/hasown/eslint.config.mjs +0 -6
  585. package/node_modules/hasown/index.d.ts +0 -3
  586. package/node_modules/hasown/index.js +0 -8
  587. package/node_modules/hasown/package.json +0 -91
  588. package/node_modules/hasown/tsconfig.json +0 -6
  589. package/node_modules/json-bigint/LICENSE +0 -20
  590. package/node_modules/json-bigint/README.md +0 -240
  591. package/node_modules/json-bigint/index.js +0 -12
  592. package/node_modules/json-bigint/lib/parse.js +0 -443
  593. package/node_modules/json-bigint/lib/stringify.js +0 -384
  594. package/node_modules/json-bigint/package.json +0 -34
  595. package/node_modules/math-intrinsics/.eslintrc +0 -16
  596. package/node_modules/math-intrinsics/.github/FUNDING.yml +0 -12
  597. package/node_modules/math-intrinsics/CHANGELOG.md +0 -24
  598. package/node_modules/math-intrinsics/LICENSE +0 -21
  599. package/node_modules/math-intrinsics/README.md +0 -50
  600. package/node_modules/math-intrinsics/abs.d.ts +0 -1
  601. package/node_modules/math-intrinsics/abs.js +0 -4
  602. package/node_modules/math-intrinsics/constants/maxArrayLength.d.ts +0 -3
  603. package/node_modules/math-intrinsics/constants/maxArrayLength.js +0 -4
  604. package/node_modules/math-intrinsics/constants/maxSafeInteger.d.ts +0 -3
  605. package/node_modules/math-intrinsics/constants/maxSafeInteger.js +0 -5
  606. package/node_modules/math-intrinsics/constants/maxValue.d.ts +0 -3
  607. package/node_modules/math-intrinsics/constants/maxValue.js +0 -5
  608. package/node_modules/math-intrinsics/floor.d.ts +0 -1
  609. package/node_modules/math-intrinsics/floor.js +0 -4
  610. package/node_modules/math-intrinsics/isFinite.d.ts +0 -3
  611. package/node_modules/math-intrinsics/isFinite.js +0 -12
  612. package/node_modules/math-intrinsics/isInteger.d.ts +0 -3
  613. package/node_modules/math-intrinsics/isInteger.js +0 -16
  614. package/node_modules/math-intrinsics/isNaN.d.ts +0 -1
  615. package/node_modules/math-intrinsics/isNaN.js +0 -6
  616. package/node_modules/math-intrinsics/isNegativeZero.d.ts +0 -3
  617. package/node_modules/math-intrinsics/isNegativeZero.js +0 -6
  618. package/node_modules/math-intrinsics/max.d.ts +0 -1
  619. package/node_modules/math-intrinsics/max.js +0 -4
  620. package/node_modules/math-intrinsics/min.d.ts +0 -1
  621. package/node_modules/math-intrinsics/min.js +0 -4
  622. package/node_modules/math-intrinsics/mod.d.ts +0 -3
  623. package/node_modules/math-intrinsics/mod.js +0 -9
  624. package/node_modules/math-intrinsics/package.json +0 -86
  625. package/node_modules/math-intrinsics/pow.d.ts +0 -1
  626. package/node_modules/math-intrinsics/pow.js +0 -4
  627. package/node_modules/math-intrinsics/round.d.ts +0 -1
  628. package/node_modules/math-intrinsics/round.js +0 -4
  629. package/node_modules/math-intrinsics/sign.d.ts +0 -3
  630. package/node_modules/math-intrinsics/sign.js +0 -11
  631. package/node_modules/math-intrinsics/test/index.js +0 -192
  632. package/node_modules/math-intrinsics/tsconfig.json +0 -3
  633. package/node_modules/pako/LICENSE +0 -21
  634. package/node_modules/pako/README.md +0 -155
  635. package/node_modules/pako/dist/pako.es5.js +0 -6760
  636. package/node_modules/pako/dist/pako.es5.min.js +0 -2
  637. package/node_modules/pako/dist/pako.esm.mjs +0 -6945
  638. package/node_modules/pako/dist/pako.js +0 -6964
  639. package/node_modules/pako/dist/pako.min.js +0 -2
  640. package/node_modules/pako/dist/pako_deflate.es5.js +0 -3953
  641. package/node_modules/pako/dist/pako_deflate.es5.min.js +0 -2
  642. package/node_modules/pako/dist/pako_deflate.js +0 -4154
  643. package/node_modules/pako/dist/pako_deflate.min.js +0 -2
  644. package/node_modules/pako/dist/pako_inflate.es5.js +0 -3272
  645. package/node_modules/pako/dist/pako_inflate.es5.min.js +0 -2
  646. package/node_modules/pako/dist/pako_inflate.js +0 -3279
  647. package/node_modules/pako/dist/pako_inflate.min.js +0 -2
  648. package/node_modules/pako/index.js +0 -18
  649. package/node_modules/pako/lib/deflate.js +0 -387
  650. package/node_modules/pako/lib/inflate.js +0 -456
  651. package/node_modules/pako/lib/utils/common.js +0 -48
  652. package/node_modules/pako/lib/utils/strings.js +0 -174
  653. package/node_modules/pako/lib/zlib/README +0 -59
  654. package/node_modules/pako/lib/zlib/adler32.js +0 -51
  655. package/node_modules/pako/lib/zlib/constants.js +0 -68
  656. package/node_modules/pako/lib/zlib/crc32.js +0 -59
  657. package/node_modules/pako/lib/zlib/deflate.js +0 -2069
  658. package/node_modules/pako/lib/zlib/gzheader.js +0 -58
  659. package/node_modules/pako/lib/zlib/inffast.js +0 -344
  660. package/node_modules/pako/lib/zlib/inflate.js +0 -1575
  661. package/node_modules/pako/lib/zlib/inftrees.js +0 -340
  662. package/node_modules/pako/lib/zlib/messages.js +0 -32
  663. package/node_modules/pako/lib/zlib/trees.js +0 -1179
  664. package/node_modules/pako/lib/zlib/zstream.js +0 -47
  665. package/node_modules/pako/package.json +0 -73
  666. package/node_modules/semver/LICENSE +0 -15
  667. package/node_modules/semver/README.md +0 -680
  668. package/node_modules/semver/bin/semver.js +0 -195
  669. package/node_modules/semver/classes/comparator.js +0 -143
  670. package/node_modules/semver/classes/index.js +0 -7
  671. package/node_modules/semver/classes/range.js +0 -577
  672. package/node_modules/semver/classes/semver.js +0 -350
  673. package/node_modules/semver/functions/clean.js +0 -8
  674. package/node_modules/semver/functions/cmp.js +0 -54
  675. package/node_modules/semver/functions/coerce.js +0 -62
  676. package/node_modules/semver/functions/compare-build.js +0 -9
  677. package/node_modules/semver/functions/compare-loose.js +0 -5
  678. package/node_modules/semver/functions/compare.js +0 -7
  679. package/node_modules/semver/functions/diff.js +0 -60
  680. package/node_modules/semver/functions/eq.js +0 -5
  681. package/node_modules/semver/functions/gt.js +0 -5
  682. package/node_modules/semver/functions/gte.js +0 -5
  683. package/node_modules/semver/functions/inc.js +0 -21
  684. package/node_modules/semver/functions/lt.js +0 -5
  685. package/node_modules/semver/functions/lte.js +0 -5
  686. package/node_modules/semver/functions/major.js +0 -5
  687. package/node_modules/semver/functions/minor.js +0 -5
  688. package/node_modules/semver/functions/neq.js +0 -5
  689. package/node_modules/semver/functions/parse.js +0 -18
  690. package/node_modules/semver/functions/patch.js +0 -5
  691. package/node_modules/semver/functions/prerelease.js +0 -8
  692. package/node_modules/semver/functions/rcompare.js +0 -5
  693. package/node_modules/semver/functions/rsort.js +0 -5
  694. package/node_modules/semver/functions/satisfies.js +0 -12
  695. package/node_modules/semver/functions/sort.js +0 -5
  696. package/node_modules/semver/functions/truncate.js +0 -48
  697. package/node_modules/semver/functions/valid.js +0 -8
  698. package/node_modules/semver/index.js +0 -93
  699. package/node_modules/semver/internal/constants.js +0 -37
  700. package/node_modules/semver/internal/debug.js +0 -11
  701. package/node_modules/semver/internal/identifiers.js +0 -29
  702. package/node_modules/semver/internal/lrucache.js +0 -42
  703. package/node_modules/semver/internal/parse-options.js +0 -17
  704. package/node_modules/semver/internal/re.js +0 -223
  705. package/node_modules/semver/package.json +0 -78
  706. package/node_modules/semver/preload.js +0 -4
  707. package/node_modules/semver/range.bnf +0 -17
  708. package/node_modules/semver/ranges/gtr.js +0 -6
  709. package/node_modules/semver/ranges/intersects.js +0 -9
  710. package/node_modules/semver/ranges/ltr.js +0 -6
  711. package/node_modules/semver/ranges/max-satisfying.js +0 -27
  712. package/node_modules/semver/ranges/min-satisfying.js +0 -26
  713. package/node_modules/semver/ranges/min-version.js +0 -63
  714. package/node_modules/semver/ranges/outside.js +0 -82
  715. package/node_modules/semver/ranges/simplify.js +0 -49
  716. package/node_modules/semver/ranges/subset.js +0 -249
  717. package/node_modules/semver/ranges/to-comparators.js +0 -10
  718. package/node_modules/semver/ranges/valid.js +0 -13
  719. package/node_modules/spark-md5/LICENSE +0 -13
  720. package/node_modules/spark-md5/README.md +0 -191
  721. package/node_modules/spark-md5/package.json +0 -35
  722. package/node_modules/spark-md5/spark-md5.js +0 -751
  723. package/node_modules/spark-md5/spark-md5.min.js +0 -1
  724. package/node_modules/tldts/LICENSE +0 -13
  725. package/node_modules/tldts/README.md +0 -327
  726. package/node_modules/tldts/bin/cli.js +0 -21
  727. package/node_modules/tldts/dist/cjs/index.js +0 -666
  728. package/node_modules/tldts/dist/cjs/index.js.map +0 -1
  729. package/node_modules/tldts/dist/cjs/src/data/trie.js +0 -14
  730. package/node_modules/tldts/dist/cjs/src/data/trie.js.map +0 -1
  731. package/node_modules/tldts/dist/cjs/src/suffix-trie.js +0 -67
  732. package/node_modules/tldts/dist/cjs/src/suffix-trie.js.map +0 -1
  733. package/node_modules/tldts/dist/cjs/tsconfig.tsbuildinfo +0 -1
  734. package/node_modules/tldts/dist/es6/index.js +0 -33
  735. package/node_modules/tldts/dist/es6/index.js.map +0 -1
  736. package/node_modules/tldts/dist/es6/src/data/trie.js +0 -11
  737. package/node_modules/tldts/dist/es6/src/data/trie.js.map +0 -1
  738. package/node_modules/tldts/dist/es6/src/suffix-trie.js +0 -64
  739. package/node_modules/tldts/dist/es6/src/suffix-trie.js.map +0 -1
  740. package/node_modules/tldts/dist/es6/tsconfig.bundle.tsbuildinfo +0 -1
  741. package/node_modules/tldts/dist/index.cjs.min.js +0 -2
  742. package/node_modules/tldts/dist/index.cjs.min.js.map +0 -1
  743. package/node_modules/tldts/dist/index.esm.min.js +0 -2
  744. package/node_modules/tldts/dist/index.esm.min.js.map +0 -1
  745. package/node_modules/tldts/dist/index.umd.min.js +0 -2
  746. package/node_modules/tldts/dist/index.umd.min.js.map +0 -1
  747. package/node_modules/tldts/dist/types/index.d.ts +0 -7
  748. package/node_modules/tldts/dist/types/src/data/trie.d.ts +0 -5
  749. package/node_modules/tldts/dist/types/src/suffix-trie.d.ts +0 -5
  750. package/node_modules/tldts/index.ts +0 -62
  751. package/node_modules/tldts/package.json +0 -91
  752. package/node_modules/tldts/src/data/trie.ts +0 -14
  753. package/node_modules/tldts/src/suffix-trie.ts +0 -110
  754. package/node_modules/tldts-core/LICENSE +0 -13
  755. package/node_modules/tldts-core/README.md +0 -3
  756. package/node_modules/tldts-core/dist/cjs/index.js +0 -561
  757. package/node_modules/tldts-core/dist/cjs/index.js.map +0 -1
  758. package/node_modules/tldts-core/dist/cjs/src/domain-without-suffix.js +0 -15
  759. package/node_modules/tldts-core/dist/cjs/src/domain-without-suffix.js.map +0 -1
  760. package/node_modules/tldts-core/dist/cjs/src/domain.js +0 -83
  761. package/node_modules/tldts-core/dist/cjs/src/domain.js.map +0 -1
  762. package/node_modules/tldts-core/dist/cjs/src/extract-hostname.js +0 -149
  763. package/node_modules/tldts-core/dist/cjs/src/extract-hostname.js.map +0 -1
  764. package/node_modules/tldts-core/dist/cjs/src/factory.js +0 -104
  765. package/node_modules/tldts-core/dist/cjs/src/factory.js.map +0 -1
  766. package/node_modules/tldts-core/dist/cjs/src/is-ip.js +0 -72
  767. package/node_modules/tldts-core/dist/cjs/src/is-ip.js.map +0 -1
  768. package/node_modules/tldts-core/dist/cjs/src/is-valid.js +0 -69
  769. package/node_modules/tldts-core/dist/cjs/src/is-valid.js.map +0 -1
  770. package/node_modules/tldts-core/dist/cjs/src/lookup/fast-path.js +0 -69
  771. package/node_modules/tldts-core/dist/cjs/src/lookup/fast-path.js.map +0 -1
  772. package/node_modules/tldts-core/dist/cjs/src/lookup/interface.js +0 -3
  773. package/node_modules/tldts-core/dist/cjs/src/lookup/interface.js.map +0 -1
  774. package/node_modules/tldts-core/dist/cjs/src/options.js +0 -22
  775. package/node_modules/tldts-core/dist/cjs/src/options.js.map +0 -1
  776. package/node_modules/tldts-core/dist/cjs/src/subdomain.js +0 -14
  777. package/node_modules/tldts-core/dist/cjs/src/subdomain.js.map +0 -1
  778. package/node_modules/tldts-core/dist/cjs/tsconfig.tsbuildinfo +0 -1
  779. package/node_modules/tldts-core/dist/es6/index.js +0 -4
  780. package/node_modules/tldts-core/dist/es6/index.js.map +0 -1
  781. package/node_modules/tldts-core/dist/es6/src/domain-without-suffix.js +0 -12
  782. package/node_modules/tldts-core/dist/es6/src/domain-without-suffix.js.map +0 -1
  783. package/node_modules/tldts-core/dist/es6/src/domain.js +0 -80
  784. package/node_modules/tldts-core/dist/es6/src/domain.js.map +0 -1
  785. package/node_modules/tldts-core/dist/es6/src/extract-hostname.js +0 -146
  786. package/node_modules/tldts-core/dist/es6/src/extract-hostname.js.map +0 -1
  787. package/node_modules/tldts-core/dist/es6/src/factory.js +0 -99
  788. package/node_modules/tldts-core/dist/es6/src/factory.js.map +0 -1
  789. package/node_modules/tldts-core/dist/es6/src/is-ip.js +0 -69
  790. package/node_modules/tldts-core/dist/es6/src/is-ip.js.map +0 -1
  791. package/node_modules/tldts-core/dist/es6/src/is-valid.js +0 -66
  792. package/node_modules/tldts-core/dist/es6/src/is-valid.js.map +0 -1
  793. package/node_modules/tldts-core/dist/es6/src/lookup/fast-path.js +0 -66
  794. package/node_modules/tldts-core/dist/es6/src/lookup/fast-path.js.map +0 -1
  795. package/node_modules/tldts-core/dist/es6/src/lookup/interface.js +0 -2
  796. package/node_modules/tldts-core/dist/es6/src/lookup/interface.js.map +0 -1
  797. package/node_modules/tldts-core/dist/es6/src/options.js +0 -19
  798. package/node_modules/tldts-core/dist/es6/src/options.js.map +0 -1
  799. package/node_modules/tldts-core/dist/es6/src/subdomain.js +0 -11
  800. package/node_modules/tldts-core/dist/es6/src/subdomain.js.map +0 -1
  801. package/node_modules/tldts-core/dist/es6/tsconfig.bundle.tsbuildinfo +0 -1
  802. package/node_modules/tldts-core/dist/types/index.d.ts +0 -4
  803. package/node_modules/tldts-core/dist/types/src/domain-without-suffix.d.ts +0 -6
  804. package/node_modules/tldts-core/dist/types/src/domain.d.ts +0 -5
  805. package/node_modules/tldts-core/dist/types/src/extract-hostname.d.ts +0 -5
  806. package/node_modules/tldts-core/dist/types/src/factory.d.ts +0 -28
  807. package/node_modules/tldts-core/dist/types/src/is-ip.d.ts +0 -6
  808. package/node_modules/tldts-core/dist/types/src/is-valid.d.ts +0 -15
  809. package/node_modules/tldts-core/dist/types/src/lookup/fast-path.d.ts +0 -2
  810. package/node_modules/tldts-core/dist/types/src/lookup/interface.d.ts +0 -9
  811. package/node_modules/tldts-core/dist/types/src/options.d.ts +0 -10
  812. package/node_modules/tldts-core/dist/types/src/subdomain.d.ts +0 -4
  813. package/node_modules/tldts-core/index.ts +0 -10
  814. package/node_modules/tldts-core/package.json +0 -68
  815. package/node_modules/tldts-core/src/domain-without-suffix.ts +0 -14
  816. package/node_modules/tldts-core/src/domain.ts +0 -100
  817. package/node_modules/tldts-core/src/extract-hostname.ts +0 -170
  818. package/node_modules/tldts-core/src/factory.ts +0 -160
  819. package/node_modules/tldts-core/src/is-ip.ts +0 -87
  820. package/node_modules/tldts-core/src/is-valid.ts +0 -79
  821. package/node_modules/tldts-core/src/lookup/fast-path.ts +0 -80
  822. package/node_modules/tldts-core/src/lookup/interface.ts +0 -10
  823. package/node_modules/tldts-core/src/options.ts +0 -39
  824. package/node_modules/tldts-core/src/subdomain.ts +0 -11
  825. package/node_modules/tough-cookie/LICENSE +0 -12
  826. package/node_modules/tough-cookie/README.md +0 -154
  827. package/node_modules/tough-cookie/dist/cookie/canonicalDomain.d.ts +0 -33
  828. package/node_modules/tough-cookie/dist/cookie/canonicalDomain.js +0 -65
  829. package/node_modules/tough-cookie/dist/cookie/constants.d.ts +0 -54
  830. package/node_modules/tough-cookie/dist/cookie/constants.js +0 -38
  831. package/node_modules/tough-cookie/dist/cookie/cookie.d.ts +0 -326
  832. package/node_modules/tough-cookie/dist/cookie/cookie.js +0 -831
  833. package/node_modules/tough-cookie/dist/cookie/cookieCompare.d.ts +0 -58
  834. package/node_modules/tough-cookie/dist/cookie/cookieCompare.js +0 -84
  835. package/node_modules/tough-cookie/dist/cookie/cookieJar.d.ts +0 -621
  836. package/node_modules/tough-cookie/dist/cookie/cookieJar.js +0 -1000
  837. package/node_modules/tough-cookie/dist/cookie/defaultPath.d.ts +0 -40
  838. package/node_modules/tough-cookie/dist/cookie/defaultPath.js +0 -60
  839. package/node_modules/tough-cookie/dist/cookie/domainMatch.d.ts +0 -38
  840. package/node_modules/tough-cookie/dist/cookie/domainMatch.js +0 -94
  841. package/node_modules/tough-cookie/dist/cookie/formatDate.d.ts +0 -15
  842. package/node_modules/tough-cookie/dist/cookie/formatDate.js +0 -20
  843. package/node_modules/tough-cookie/dist/cookie/index.d.ts +0 -29
  844. package/node_modules/tough-cookie/dist/cookie/index.js +0 -54
  845. package/node_modules/tough-cookie/dist/cookie/parseDate.d.ts +0 -103
  846. package/node_modules/tough-cookie/dist/cookie/parseDate.js +0 -323
  847. package/node_modules/tough-cookie/dist/cookie/permutePath.d.ts +0 -14
  848. package/node_modules/tough-cookie/dist/cookie/permutePath.js +0 -32
  849. package/node_modules/tough-cookie/dist/getPublicSuffix.d.ts +0 -55
  850. package/node_modules/tough-cookie/dist/getPublicSuffix.js +0 -71
  851. package/node_modules/tough-cookie/dist/memstore.d.ts +0 -220
  852. package/node_modules/tough-cookie/dist/memstore.js +0 -187
  853. package/node_modules/tough-cookie/dist/pathMatch.d.ts +0 -17
  854. package/node_modules/tough-cookie/dist/pathMatch.js +0 -40
  855. package/node_modules/tough-cookie/dist/permuteDomain.d.ts +0 -15
  856. package/node_modules/tough-cookie/dist/permuteDomain.js +0 -44
  857. package/node_modules/tough-cookie/dist/store.d.ts +0 -211
  858. package/node_modules/tough-cookie/dist/store.js +0 -76
  859. package/node_modules/tough-cookie/dist/utils.d.ts +0 -34
  860. package/node_modules/tough-cookie/dist/utils.js +0 -99
  861. package/node_modules/tough-cookie/dist/validators.d.ts +0 -24
  862. package/node_modules/tough-cookie/dist/validators.js +0 -90
  863. package/node_modules/tough-cookie/dist/version.d.ts +0 -5
  864. package/node_modules/tough-cookie/dist/version.js +0 -8
  865. package/node_modules/tough-cookie/package.json +0 -144
  866. package/node_modules/ws/LICENSE +0 -20
  867. package/node_modules/ws/README.md +0 -548
  868. package/node_modules/ws/browser.js +0 -8
  869. package/node_modules/ws/index.js +0 -22
  870. package/node_modules/ws/lib/buffer-util.js +0 -131
  871. package/node_modules/ws/lib/constants.js +0 -19
  872. package/node_modules/ws/lib/event-target.js +0 -292
  873. package/node_modules/ws/lib/extension.js +0 -203
  874. package/node_modules/ws/lib/limiter.js +0 -55
  875. package/node_modules/ws/lib/permessage-deflate.js +0 -530
  876. package/node_modules/ws/lib/receiver.js +0 -743
  877. package/node_modules/ws/lib/sender.js +0 -607
  878. package/node_modules/ws/lib/stream.js +0 -161
  879. package/node_modules/ws/lib/subprotocol.js +0 -62
  880. package/node_modules/ws/lib/validation.js +0 -152
  881. package/node_modules/ws/lib/websocket-server.js +0 -562
  882. package/node_modules/ws/lib/websocket.js +0 -1407
  883. package/node_modules/ws/package.json +0 -74
  884. package/node_modules/ws/wrapper.mjs +0 -21
  885. package/node_modules/zca-js/.prettierrc +0 -5
  886. package/node_modules/zca-js/CODE_OF_CONDUCT.md +0 -163
  887. package/node_modules/zca-js/CONTRIBUTING.md +0 -470
  888. package/node_modules/zca-js/LICENSE +0 -21
  889. package/node_modules/zca-js/README.md +0 -225
  890. package/node_modules/zca-js/SECURITY.md +0 -83
  891. package/node_modules/zca-js/dist/Errors/ZaloApiError.d.ts +0 -4
  892. package/node_modules/zca-js/dist/Errors/ZaloApiError.js +0 -7
  893. package/node_modules/zca-js/dist/Errors/ZaloApiLoginQRAborted.d.ts +0 -4
  894. package/node_modules/zca-js/dist/Errors/ZaloApiLoginQRAborted.js +0 -7
  895. package/node_modules/zca-js/dist/Errors/ZaloApiLoginQRDeclined.d.ts +0 -4
  896. package/node_modules/zca-js/dist/Errors/ZaloApiLoginQRDeclined.js +0 -7
  897. package/node_modules/zca-js/dist/Errors/ZaloApiMissingImageMetadataGetter.d.ts +0 -4
  898. package/node_modules/zca-js/dist/Errors/ZaloApiMissingImageMetadataGetter.js +0 -7
  899. package/node_modules/zca-js/dist/Errors/index.d.ts +0 -4
  900. package/node_modules/zca-js/dist/Errors/index.js +0 -4
  901. package/node_modules/zca-js/dist/apis/acceptFriendRequest.d.ts +0 -2
  902. package/node_modules/zca-js/dist/apis/acceptFriendRequest.js +0 -28
  903. package/node_modules/zca-js/dist/apis/addGroupBlockedMember.d.ts +0 -2
  904. package/node_modules/zca-js/dist/apis/addGroupBlockedMember.js +0 -28
  905. package/node_modules/zca-js/dist/apis/addGroupDeputy.d.ts +0 -2
  906. package/node_modules/zca-js/dist/apis/addGroupDeputy.js +0 -30
  907. package/node_modules/zca-js/dist/apis/addPollOptions.d.ts +0 -14
  908. package/node_modules/zca-js/dist/apis/addPollOptions.js +0 -26
  909. package/node_modules/zca-js/dist/apis/addQuickMessage.d.ts +0 -11
  910. package/node_modules/zca-js/dist/apis/addQuickMessage.js +0 -60
  911. package/node_modules/zca-js/dist/apis/addReaction.d.ts +0 -18
  912. package/node_modules/zca-js/dist/apis/addReaction.js +0 -294
  913. package/node_modules/zca-js/dist/apis/addUnreadMark.d.ts +0 -8
  914. package/node_modules/zca-js/dist/apis/addUnreadMark.js +0 -52
  915. package/node_modules/zca-js/dist/apis/addUserToGroup.d.ts +0 -5
  916. package/node_modules/zca-js/dist/apis/addUserToGroup.js +0 -34
  917. package/node_modules/zca-js/dist/apis/blockUser.d.ts +0 -2
  918. package/node_modules/zca-js/dist/apis/blockUser.js +0 -28
  919. package/node_modules/zca-js/dist/apis/blockViewFeed.d.ts +0 -2
  920. package/node_modules/zca-js/dist/apis/blockViewFeed.js +0 -30
  921. package/node_modules/zca-js/dist/apis/changeAccountAvatar.d.ts +0 -3
  922. package/node_modules/zca-js/dist/apis/changeAccountAvatar.js +0 -52
  923. package/node_modules/zca-js/dist/apis/changeFriendAlias.d.ts +0 -2
  924. package/node_modules/zca-js/dist/apis/changeFriendAlias.js +0 -27
  925. package/node_modules/zca-js/dist/apis/changeGroupAvatar.d.ts +0 -3
  926. package/node_modules/zca-js/dist/apis/changeGroupAvatar.js +0 -44
  927. package/node_modules/zca-js/dist/apis/changeGroupName.d.ts +0 -4
  928. package/node_modules/zca-js/dist/apis/changeGroupName.js +0 -32
  929. package/node_modules/zca-js/dist/apis/changeGroupOwner.d.ts +0 -4
  930. package/node_modules/zca-js/dist/apis/changeGroupOwner.js +0 -30
  931. package/node_modules/zca-js/dist/apis/createAutoReply.d.ts +0 -14
  932. package/node_modules/zca-js/dist/apis/createAutoReply.js +0 -37
  933. package/node_modules/zca-js/dist/apis/createBankAccount.d.ts +0 -8
  934. package/node_modules/zca-js/dist/apis/createBankAccount.js +0 -32
  935. package/node_modules/zca-js/dist/apis/createCatalog.d.ts +0 -7
  936. package/node_modules/zca-js/dist/apis/createCatalog.js +0 -29
  937. package/node_modules/zca-js/dist/apis/createGroup.d.ts +0 -28
  938. package/node_modules/zca-js/dist/apis/createGroup.js +0 -43
  939. package/node_modules/zca-js/dist/apis/createNote.d.ts +0 -7
  940. package/node_modules/zca-js/dist/apis/createNote.js +0 -47
  941. package/node_modules/zca-js/dist/apis/createPoll.d.ts +0 -36
  942. package/node_modules/zca-js/dist/apis/createPoll.js +0 -40
  943. package/node_modules/zca-js/dist/apis/createProductCatalog.d.ts +0 -23
  944. package/node_modules/zca-js/dist/apis/createProductCatalog.js +0 -50
  945. package/node_modules/zca-js/dist/apis/createReminder.d.ts +0 -12
  946. package/node_modules/zca-js/dist/apis/createReminder.js +0 -63
  947. package/node_modules/zca-js/dist/apis/custom.d.ts +0 -9
  948. package/node_modules/zca-js/dist/apis/custom.js +0 -15
  949. package/node_modules/zca-js/dist/apis/deleteAutoReply.d.ts +0 -5
  950. package/node_modules/zca-js/dist/apis/deleteAutoReply.js +0 -29
  951. package/node_modules/zca-js/dist/apis/deleteAvatar.d.ts +0 -9
  952. package/node_modules/zca-js/dist/apis/deleteAvatar.js +0 -27
  953. package/node_modules/zca-js/dist/apis/deleteBankAccount.d.ts +0 -11
  954. package/node_modules/zca-js/dist/apis/deleteBankAccount.js +0 -32
  955. package/node_modules/zca-js/dist/apis/deleteCatalog.d.ts +0 -2
  956. package/node_modules/zca-js/dist/apis/deleteCatalog.js +0 -28
  957. package/node_modules/zca-js/dist/apis/deleteChat.d.ts +0 -19
  958. package/node_modules/zca-js/dist/apis/deleteChat.js +0 -50
  959. package/node_modules/zca-js/dist/apis/deleteGroupInviteBox.d.ts +0 -9
  960. package/node_modules/zca-js/dist/apis/deleteGroupInviteBox.js +0 -27
  961. package/node_modules/zca-js/dist/apis/deleteMessage.d.ts +0 -14
  962. package/node_modules/zca-js/dist/apis/deleteMessage.js +0 -52
  963. package/node_modules/zca-js/dist/apis/deleteProductCatalog.d.ts +0 -10
  964. package/node_modules/zca-js/dist/apis/deleteProductCatalog.js +0 -31
  965. package/node_modules/zca-js/dist/apis/disableGroupLink.d.ts +0 -2
  966. package/node_modules/zca-js/dist/apis/disableGroupLink.js +0 -24
  967. package/node_modules/zca-js/dist/apis/disperseGroup.d.ts +0 -2
  968. package/node_modules/zca-js/dist/apis/disperseGroup.js +0 -28
  969. package/node_modules/zca-js/dist/apis/editNote.d.ts +0 -17
  970. package/node_modules/zca-js/dist/apis/editNote.js +0 -46
  971. package/node_modules/zca-js/dist/apis/editReminder.d.ts +0 -13
  972. package/node_modules/zca-js/dist/apis/editReminder.js +0 -61
  973. package/node_modules/zca-js/dist/apis/enableGroupLink.d.ts +0 -6
  974. package/node_modules/zca-js/dist/apis/enableGroupLink.js +0 -25
  975. package/node_modules/zca-js/dist/apis/fetchAccountInfo.d.ts +0 -5
  976. package/node_modules/zca-js/dist/apis/fetchAccountInfo.js +0 -10
  977. package/node_modules/zca-js/dist/apis/findUser.d.ts +0 -3
  978. package/node_modules/zca-js/dist/apis/findUser.js +0 -42
  979. package/node_modules/zca-js/dist/apis/findUserByUsername.d.ts +0 -3
  980. package/node_modules/zca-js/dist/apis/findUserByUsername.js +0 -27
  981. package/node_modules/zca-js/dist/apis/forwardMessage.d.ts +0 -24
  982. package/node_modules/zca-js/dist/apis/forwardMessage.js +0 -114
  983. package/node_modules/zca-js/dist/apis/getAliasList.d.ts +0 -8
  984. package/node_modules/zca-js/dist/apis/getAliasList.js +0 -27
  985. package/node_modules/zca-js/dist/apis/getAllFriends.d.ts +0 -3
  986. package/node_modules/zca-js/dist/apis/getAllFriends.js +0 -34
  987. package/node_modules/zca-js/dist/apis/getAllGroups.d.ts +0 -7
  988. package/node_modules/zca-js/dist/apis/getAllGroups.js +0 -10
  989. package/node_modules/zca-js/dist/apis/getArchivedChatList.d.ts +0 -5
  990. package/node_modules/zca-js/dist/apis/getArchivedChatList.js +0 -23
  991. package/node_modules/zca-js/dist/apis/getAutoDeleteChat.d.ts +0 -9
  992. package/node_modules/zca-js/dist/apis/getAutoDeleteChat.js +0 -21
  993. package/node_modules/zca-js/dist/apis/getAutoReplyList.d.ts +0 -6
  994. package/node_modules/zca-js/dist/apis/getAutoReplyList.js +0 -24
  995. package/node_modules/zca-js/dist/apis/getAvatarList.d.ts +0 -12
  996. package/node_modules/zca-js/dist/apis/getAvatarList.js +0 -28
  997. package/node_modules/zca-js/dist/apis/getAvatarUrlProfile.d.ts +0 -7
  998. package/node_modules/zca-js/dist/apis/getAvatarUrlProfile.js +0 -30
  999. package/node_modules/zca-js/dist/apis/getBizAccount.d.ts +0 -24
  1000. package/node_modules/zca-js/dist/apis/getBizAccount.js +0 -27
  1001. package/node_modules/zca-js/dist/apis/getCatalogList.d.ts +0 -18
  1002. package/node_modules/zca-js/dist/apis/getCatalogList.js +0 -32
  1003. package/node_modules/zca-js/dist/apis/getCloseFriends.d.ts +0 -3
  1004. package/node_modules/zca-js/dist/apis/getCloseFriends.js +0 -20
  1005. package/node_modules/zca-js/dist/apis/getContext.d.ts +0 -1
  1006. package/node_modules/zca-js/dist/apis/getContext.js +0 -4
  1007. package/node_modules/zca-js/dist/apis/getCookie.d.ts +0 -1
  1008. package/node_modules/zca-js/dist/apis/getCookie.js +0 -9
  1009. package/node_modules/zca-js/dist/apis/getFriendBoardList.d.ts +0 -5
  1010. package/node_modules/zca-js/dist/apis/getFriendBoardList.js +0 -26
  1011. package/node_modules/zca-js/dist/apis/getFriendOnlines.d.ts +0 -10
  1012. package/node_modules/zca-js/dist/apis/getFriendOnlines.js +0 -35
  1013. package/node_modules/zca-js/dist/apis/getFriendRecommendations.d.ts +0 -42
  1014. package/node_modules/zca-js/dist/apis/getFriendRecommendations.js +0 -27
  1015. package/node_modules/zca-js/dist/apis/getFriendRequestStatus.d.ts +0 -8
  1016. package/node_modules/zca-js/dist/apis/getFriendRequestStatus.js +0 -25
  1017. package/node_modules/zca-js/dist/apis/getFullAvatar.d.ts +0 -5
  1018. package/node_modules/zca-js/dist/apis/getFullAvatar.js +0 -25
  1019. package/node_modules/zca-js/dist/apis/getGroupBlockedMember.d.ts +0 -23
  1020. package/node_modules/zca-js/dist/apis/getGroupBlockedMember.js +0 -29
  1021. package/node_modules/zca-js/dist/apis/getGroupChatHistory.d.ts +0 -8
  1022. package/node_modules/zca-js/dist/apis/getGroupChatHistory.js +0 -36
  1023. package/node_modules/zca-js/dist/apis/getGroupInfo.d.ts +0 -16
  1024. package/node_modules/zca-js/dist/apis/getGroupInfo.js +0 -32
  1025. package/node_modules/zca-js/dist/apis/getGroupInviteBoxInfo.d.ts +0 -32
  1026. package/node_modules/zca-js/dist/apis/getGroupInviteBoxInfo.js +0 -38
  1027. package/node_modules/zca-js/dist/apis/getGroupInviteBoxList.d.ts +0 -38
  1028. package/node_modules/zca-js/dist/apis/getGroupInviteBoxList.js +0 -31
  1029. package/node_modules/zca-js/dist/apis/getGroupLinkDetail.d.ts +0 -9
  1030. package/node_modules/zca-js/dist/apis/getGroupLinkDetail.js +0 -25
  1031. package/node_modules/zca-js/dist/apis/getGroupLinkInfo.d.ts +0 -34
  1032. package/node_modules/zca-js/dist/apis/getGroupLinkInfo.js +0 -28
  1033. package/node_modules/zca-js/dist/apis/getGroupMembersInfo.d.ts +0 -17
  1034. package/node_modules/zca-js/dist/apis/getGroupMembersInfo.js +0 -24
  1035. package/node_modules/zca-js/dist/apis/getHiddenConversations.d.ts +0 -11
  1036. package/node_modules/zca-js/dist/apis/getHiddenConversations.js +0 -23
  1037. package/node_modules/zca-js/dist/apis/getLabels.d.ts +0 -7
  1038. package/node_modules/zca-js/dist/apis/getLabels.js +0 -28
  1039. package/node_modules/zca-js/dist/apis/getListBank.d.ts +0 -5
  1040. package/node_modules/zca-js/dist/apis/getListBank.js +0 -20
  1041. package/node_modules/zca-js/dist/apis/getListBankAccount.d.ts +0 -7
  1042. package/node_modules/zca-js/dist/apis/getListBankAccount.js +0 -27
  1043. package/node_modules/zca-js/dist/apis/getListBoard.d.ts +0 -21
  1044. package/node_modules/zca-js/dist/apis/getListBoard.js +0 -45
  1045. package/node_modules/zca-js/dist/apis/getListDevice.d.ts +0 -10
  1046. package/node_modules/zca-js/dist/apis/getListDevice.js +0 -22
  1047. package/node_modules/zca-js/dist/apis/getListReminder.d.ts +0 -28
  1048. package/node_modules/zca-js/dist/apis/getListReminder.js +0 -47
  1049. package/node_modules/zca-js/dist/apis/getMultiUsersByPhones.d.ts +0 -5
  1050. package/node_modules/zca-js/dist/apis/getMultiUsersByPhones.js +0 -39
  1051. package/node_modules/zca-js/dist/apis/getMute.d.ts +0 -13
  1052. package/node_modules/zca-js/dist/apis/getMute.js +0 -22
  1053. package/node_modules/zca-js/dist/apis/getOwnId.d.ts +0 -1
  1054. package/node_modules/zca-js/dist/apis/getOwnId.js +0 -4
  1055. package/node_modules/zca-js/dist/apis/getPendingGroupMembers.d.ts +0 -11
  1056. package/node_modules/zca-js/dist/apis/getPendingGroupMembers.js +0 -27
  1057. package/node_modules/zca-js/dist/apis/getPinConversations.d.ts +0 -5
  1058. package/node_modules/zca-js/dist/apis/getPinConversations.js +0 -22
  1059. package/node_modules/zca-js/dist/apis/getPollDetail.d.ts +0 -3
  1060. package/node_modules/zca-js/dist/apis/getPollDetail.js +0 -30
  1061. package/node_modules/zca-js/dist/apis/getProductCatalogList.d.ts +0 -20
  1062. package/node_modules/zca-js/dist/apis/getProductCatalogList.js +0 -33
  1063. package/node_modules/zca-js/dist/apis/getQR.d.ts +0 -4
  1064. package/node_modules/zca-js/dist/apis/getQR.js +0 -29
  1065. package/node_modules/zca-js/dist/apis/getQuickMessageList.d.ts +0 -7
  1066. package/node_modules/zca-js/dist/apis/getQuickMessageList.js +0 -24
  1067. package/node_modules/zca-js/dist/apis/getRelatedFriendGroup.d.ts +0 -6
  1068. package/node_modules/zca-js/dist/apis/getRelatedFriendGroup.js +0 -30
  1069. package/node_modules/zca-js/dist/apis/getReminder.d.ts +0 -3
  1070. package/node_modules/zca-js/dist/apis/getReminder.js +0 -25
  1071. package/node_modules/zca-js/dist/apis/getReminderResponses.d.ts +0 -5
  1072. package/node_modules/zca-js/dist/apis/getReminderResponses.js +0 -24
  1073. package/node_modules/zca-js/dist/apis/getSentFriendRequest.d.ts +0 -18
  1074. package/node_modules/zca-js/dist/apis/getSentFriendRequest.js +0 -24
  1075. package/node_modules/zca-js/dist/apis/getSettings.d.ts +0 -3
  1076. package/node_modules/zca-js/dist/apis/getSettings.js +0 -20
  1077. package/node_modules/zca-js/dist/apis/getStickerCategoryDetail.d.ts +0 -3
  1078. package/node_modules/zca-js/dist/apis/getStickerCategoryDetail.js +0 -24
  1079. package/node_modules/zca-js/dist/apis/getStickers.d.ts +0 -1
  1080. package/node_modules/zca-js/dist/apis/getStickers.js +0 -40
  1081. package/node_modules/zca-js/dist/apis/getStickersDetail.d.ts +0 -3
  1082. package/node_modules/zca-js/dist/apis/getStickersDetail.js +0 -40
  1083. package/node_modules/zca-js/dist/apis/getUnreadMark.d.ts +0 -14
  1084. package/node_modules/zca-js/dist/apis/getUnreadMark.js +0 -30
  1085. package/node_modules/zca-js/dist/apis/getUserInfo.d.ts +0 -8
  1086. package/node_modules/zca-js/dist/apis/getUserInfo.js +0 -44
  1087. package/node_modules/zca-js/dist/apis/inviteUserToGroups.d.ts +0 -10
  1088. package/node_modules/zca-js/dist/apis/inviteUserToGroups.js +0 -30
  1089. package/node_modules/zca-js/dist/apis/joinGroupInviteBox.d.ts +0 -2
  1090. package/node_modules/zca-js/dist/apis/joinGroupInviteBox.js +0 -25
  1091. package/node_modules/zca-js/dist/apis/joinGroupLink.d.ts +0 -2
  1092. package/node_modules/zca-js/dist/apis/joinGroupLink.js +0 -27
  1093. package/node_modules/zca-js/dist/apis/keepAlive.d.ts +0 -4
  1094. package/node_modules/zca-js/dist/apis/keepAlive.js +0 -22
  1095. package/node_modules/zca-js/dist/apis/lastOnline.d.ts +0 -7
  1096. package/node_modules/zca-js/dist/apis/lastOnline.js +0 -26
  1097. package/node_modules/zca-js/dist/apis/leaveGroup.d.ts +0 -4
  1098. package/node_modules/zca-js/dist/apis/leaveGroup.js +0 -33
  1099. package/node_modules/zca-js/dist/apis/listen.d.ts +0 -102
  1100. package/node_modules/zca-js/dist/apis/listen.js +0 -425
  1101. package/node_modules/zca-js/dist/apis/lockPoll.d.ts +0 -2
  1102. package/node_modules/zca-js/dist/apis/lockPoll.js +0 -28
  1103. package/node_modules/zca-js/dist/apis/login.d.ts +0 -3
  1104. package/node_modules/zca-js/dist/apis/login.js +0 -96
  1105. package/node_modules/zca-js/dist/apis/loginQR.d.ts +0 -71
  1106. package/node_modules/zca-js/dist/apis/loginQR.js +0 -376
  1107. package/node_modules/zca-js/dist/apis/logoutV2.js +0 -79
  1108. package/node_modules/zca-js/dist/apis/lostFocus.d.ts +0 -4
  1109. package/node_modules/zca-js/dist/apis/lostFocus.js +0 -23
  1110. package/node_modules/zca-js/dist/apis/parseLink.d.ts +0 -21
  1111. package/node_modules/zca-js/dist/apis/parseLink.js +0 -27
  1112. package/node_modules/zca-js/dist/apis/registerCatalog.d.ts +0 -2
  1113. package/node_modules/zca-js/dist/apis/registerCatalog.js +0 -27
  1114. package/node_modules/zca-js/dist/apis/rejectFriendRequest.d.ts +0 -2
  1115. package/node_modules/zca-js/dist/apis/rejectFriendRequest.js +0 -27
  1116. package/node_modules/zca-js/dist/apis/removeFriend.d.ts +0 -2
  1117. package/node_modules/zca-js/dist/apis/removeFriend.js +0 -28
  1118. package/node_modules/zca-js/dist/apis/removeFriendAlias.d.ts +0 -2
  1119. package/node_modules/zca-js/dist/apis/removeFriendAlias.js +0 -24
  1120. package/node_modules/zca-js/dist/apis/removeGroupBlockedMember.d.ts +0 -2
  1121. package/node_modules/zca-js/dist/apis/removeGroupBlockedMember.js +0 -28
  1122. package/node_modules/zca-js/dist/apis/removeGroupDeputy.d.ts +0 -2
  1123. package/node_modules/zca-js/dist/apis/removeGroupDeputy.js +0 -30
  1124. package/node_modules/zca-js/dist/apis/removeQuickMessage.d.ts +0 -5
  1125. package/node_modules/zca-js/dist/apis/removeQuickMessage.js +0 -27
  1126. package/node_modules/zca-js/dist/apis/removeReminder.d.ts +0 -3
  1127. package/node_modules/zca-js/dist/apis/removeReminder.js +0 -40
  1128. package/node_modules/zca-js/dist/apis/removeUnreadMark.d.ts +0 -8
  1129. package/node_modules/zca-js/dist/apis/removeUnreadMark.js +0 -50
  1130. package/node_modules/zca-js/dist/apis/removeUserFromGroup.d.ts +0 -4
  1131. package/node_modules/zca-js/dist/apis/removeUserFromGroup.js +0 -34
  1132. package/node_modules/zca-js/dist/apis/resetHiddenConversPin.d.ts +0 -2
  1133. package/node_modules/zca-js/dist/apis/resetHiddenConversPin.js +0 -20
  1134. package/node_modules/zca-js/dist/apis/reuseAvatar.d.ts +0 -2
  1135. package/node_modules/zca-js/dist/apis/reuseAvatar.js +0 -26
  1136. package/node_modules/zca-js/dist/apis/reviewPendingMemberRequest.d.ts +0 -14
  1137. package/node_modules/zca-js/dist/apis/reviewPendingMemberRequest.js +0 -38
  1138. package/node_modules/zca-js/dist/apis/scanURL.d.ts +0 -4
  1139. package/node_modules/zca-js/dist/apis/scanURL.js +0 -28
  1140. package/node_modules/zca-js/dist/apis/searchSticker.d.ts +0 -3
  1141. package/node_modules/zca-js/dist/apis/searchSticker.js +0 -28
  1142. package/node_modules/zca-js/dist/apis/sendBankCard.d.ts +0 -9
  1143. package/node_modules/zca-js/dist/apis/sendBankCard.js +0 -53
  1144. package/node_modules/zca-js/dist/apis/sendCard.d.ts +0 -10
  1145. package/node_modules/zca-js/dist/apis/sendCard.js +0 -75
  1146. package/node_modules/zca-js/dist/apis/sendDeliveredEvent.d.ts +0 -16
  1147. package/node_modules/zca-js/dist/apis/sendDeliveredEvent.js +0 -55
  1148. package/node_modules/zca-js/dist/apis/sendFriendRequest.d.ts +0 -2
  1149. package/node_modules/zca-js/dist/apis/sendFriendRequest.js +0 -39
  1150. package/node_modules/zca-js/dist/apis/sendLink.d.ts +0 -10
  1151. package/node_modules/zca-js/dist/apis/sendLink.js +0 -78
  1152. package/node_modules/zca-js/dist/apis/sendMessage.d.ts +0 -105
  1153. package/node_modules/zca-js/dist/apis/sendMessage.js +0 -486
  1154. package/node_modules/zca-js/dist/apis/sendReport.d.ts +0 -17
  1155. package/node_modules/zca-js/dist/apis/sendReport.js +0 -53
  1156. package/node_modules/zca-js/dist/apis/sendSeenEvent.d.ts +0 -16
  1157. package/node_modules/zca-js/dist/apis/sendSeenEvent.js +0 -63
  1158. package/node_modules/zca-js/dist/apis/sendSticker.d.ts +0 -10
  1159. package/node_modules/zca-js/dist/apis/sendSticker.js +0 -74
  1160. package/node_modules/zca-js/dist/apis/sendTypingEvent.d.ts +0 -5
  1161. package/node_modules/zca-js/dist/apis/sendTypingEvent.js +0 -33
  1162. package/node_modules/zca-js/dist/apis/sendVideo.d.ts +0 -35
  1163. package/node_modules/zca-js/dist/apis/sendVideo.js +0 -134
  1164. package/node_modules/zca-js/dist/apis/sendVoice.d.ts +0 -12
  1165. package/node_modules/zca-js/dist/apis/sendVoice.js +0 -89
  1166. package/node_modules/zca-js/dist/apis/setHiddenConversations.d.ts +0 -3
  1167. package/node_modules/zca-js/dist/apis/setHiddenConversations.js +0 -39
  1168. package/node_modules/zca-js/dist/apis/setMute.d.ts +0 -20
  1169. package/node_modules/zca-js/dist/apis/setMute.js +0 -67
  1170. package/node_modules/zca-js/dist/apis/setPinnedConversations.d.ts +0 -3
  1171. package/node_modules/zca-js/dist/apis/setPinnedConversations.js +0 -34
  1172. package/node_modules/zca-js/dist/apis/sharePoll.d.ts +0 -2
  1173. package/node_modules/zca-js/dist/apis/sharePoll.js +0 -28
  1174. package/node_modules/zca-js/dist/apis/unblockUser.d.ts +0 -2
  1175. package/node_modules/zca-js/dist/apis/unblockUser.js +0 -28
  1176. package/node_modules/zca-js/dist/apis/undo.d.ts +0 -9
  1177. package/node_modules/zca-js/dist/apis/undo.js +0 -42
  1178. package/node_modules/zca-js/dist/apis/undoFriendRequest.d.ts +0 -2
  1179. package/node_modules/zca-js/dist/apis/undoFriendRequest.js +0 -27
  1180. package/node_modules/zca-js/dist/apis/updateActiveStatus.d.ts +0 -4
  1181. package/node_modules/zca-js/dist/apis/updateActiveStatus.js +0 -27
  1182. package/node_modules/zca-js/dist/apis/updateArchivedChatList.d.ts +0 -10
  1183. package/node_modules/zca-js/dist/apis/updateArchivedChatList.js +0 -34
  1184. package/node_modules/zca-js/dist/apis/updateAutoDeleteChat.d.ts +0 -9
  1185. package/node_modules/zca-js/dist/apis/updateAutoDeleteChat.js +0 -40
  1186. package/node_modules/zca-js/dist/apis/updateAutoReply.d.ts +0 -15
  1187. package/node_modules/zca-js/dist/apis/updateAutoReply.js +0 -38
  1188. package/node_modules/zca-js/dist/apis/updateBankAccount.d.ts +0 -9
  1189. package/node_modules/zca-js/dist/apis/updateBankAccount.js +0 -31
  1190. package/node_modules/zca-js/dist/apis/updateCatalog.d.ts +0 -11
  1191. package/node_modules/zca-js/dist/apis/updateCatalog.js +0 -30
  1192. package/node_modules/zca-js/dist/apis/updateGroupSettings.d.ts +0 -40
  1193. package/node_modules/zca-js/dist/apis/updateGroupSettings.js +0 -43
  1194. package/node_modules/zca-js/dist/apis/updateHiddenConversPin.d.ts +0 -2
  1195. package/node_modules/zca-js/dist/apis/updateHiddenConversPin.js +0 -30
  1196. package/node_modules/zca-js/dist/apis/updateLabels.d.ts +0 -15
  1197. package/node_modules/zca-js/dist/apis/updateLabels.js +0 -34
  1198. package/node_modules/zca-js/dist/apis/updateLang.d.ts +0 -6
  1199. package/node_modules/zca-js/dist/apis/updateLang.js +0 -30
  1200. package/node_modules/zca-js/dist/apis/updateProductCatalog.d.ts +0 -25
  1201. package/node_modules/zca-js/dist/apis/updateProductCatalog.js +0 -51
  1202. package/node_modules/zca-js/dist/apis/updateProfile.d.ts +0 -20
  1203. package/node_modules/zca-js/dist/apis/updateProfile.js +0 -43
  1204. package/node_modules/zca-js/dist/apis/updateProfileBio.d.ts +0 -2
  1205. package/node_modules/zca-js/dist/apis/updateProfileBio.js +0 -27
  1206. package/node_modules/zca-js/dist/apis/updateQuickMessage.d.ts +0 -11
  1207. package/node_modules/zca-js/dist/apis/updateQuickMessage.js +0 -61
  1208. package/node_modules/zca-js/dist/apis/updateSettings.d.ts +0 -16
  1209. package/node_modules/zca-js/dist/apis/updateSettings.js +0 -90
  1210. package/node_modules/zca-js/dist/apis/upgradeGroupToCommunity.d.ts +0 -2
  1211. package/node_modules/zca-js/dist/apis/upgradeGroupToCommunity.js +0 -27
  1212. package/node_modules/zca-js/dist/apis/uploadAttachment.d.ts +0 -50
  1213. package/node_modules/zca-js/dist/apis/uploadAttachment.js +0 -197
  1214. package/node_modules/zca-js/dist/apis/uploadProductPhoto.d.ts +0 -14
  1215. package/node_modules/zca-js/dist/apis/uploadProductPhoto.js +0 -50
  1216. package/node_modules/zca-js/dist/apis/votePoll.d.ts +0 -5
  1217. package/node_modules/zca-js/dist/apis/votePoll.js +0 -29
  1218. package/node_modules/zca-js/dist/apis.d.ts +0 -317
  1219. package/node_modules/zca-js/dist/apis.js +0 -319
  1220. package/node_modules/zca-js/dist/cjs/Errors/ZaloApiError.cjs +0 -11
  1221. package/node_modules/zca-js/dist/cjs/Errors/ZaloApiLoginQRAborted.cjs +0 -12
  1222. package/node_modules/zca-js/dist/cjs/Errors/ZaloApiLoginQRDeclined.cjs +0 -12
  1223. package/node_modules/zca-js/dist/cjs/Errors/ZaloApiMissingImageMetadataGetter.cjs +0 -12
  1224. package/node_modules/zca-js/dist/cjs/apis/acceptFriendRequest.cjs +0 -33
  1225. package/node_modules/zca-js/dist/cjs/apis/addGroupBlockedMember.cjs +0 -33
  1226. package/node_modules/zca-js/dist/cjs/apis/addGroupDeputy.cjs +0 -35
  1227. package/node_modules/zca-js/dist/cjs/apis/addPollOptions.cjs +0 -31
  1228. package/node_modules/zca-js/dist/cjs/apis/addQuickMessage.cjs +0 -65
  1229. package/node_modules/zca-js/dist/cjs/apis/addReaction.cjs +0 -308
  1230. package/node_modules/zca-js/dist/cjs/apis/addUnreadMark.cjs +0 -66
  1231. package/node_modules/zca-js/dist/cjs/apis/addUserToGroup.cjs +0 -39
  1232. package/node_modules/zca-js/dist/cjs/apis/blockUser.cjs +0 -33
  1233. package/node_modules/zca-js/dist/cjs/apis/blockViewFeed.cjs +0 -35
  1234. package/node_modules/zca-js/dist/cjs/apis/changeAccountAvatar.cjs +0 -57
  1235. package/node_modules/zca-js/dist/cjs/apis/changeFriendAlias.cjs +0 -32
  1236. package/node_modules/zca-js/dist/cjs/apis/changeGroupAvatar.cjs +0 -49
  1237. package/node_modules/zca-js/dist/cjs/apis/changeGroupName.cjs +0 -37
  1238. package/node_modules/zca-js/dist/cjs/apis/changeGroupOwner.cjs +0 -35
  1239. package/node_modules/zca-js/dist/cjs/apis/createAutoReply.cjs +0 -42
  1240. package/node_modules/zca-js/dist/cjs/apis/createBankAccount.cjs +0 -37
  1241. package/node_modules/zca-js/dist/cjs/apis/createCatalog.cjs +0 -34
  1242. package/node_modules/zca-js/dist/cjs/apis/createGroup.cjs +0 -48
  1243. package/node_modules/zca-js/dist/cjs/apis/createNote.cjs +0 -52
  1244. package/node_modules/zca-js/dist/cjs/apis/createPoll.cjs +0 -45
  1245. package/node_modules/zca-js/dist/cjs/apis/createProductCatalog.cjs +0 -55
  1246. package/node_modules/zca-js/dist/cjs/apis/createReminder.cjs +0 -77
  1247. package/node_modules/zca-js/dist/cjs/apis/custom.cjs +0 -19
  1248. package/node_modules/zca-js/dist/cjs/apis/deleteAutoReply.cjs +0 -34
  1249. package/node_modules/zca-js/dist/cjs/apis/deleteAvatar.cjs +0 -32
  1250. package/node_modules/zca-js/dist/cjs/apis/deleteBankAccount.cjs +0 -37
  1251. package/node_modules/zca-js/dist/cjs/apis/deleteCatalog.cjs +0 -33
  1252. package/node_modules/zca-js/dist/cjs/apis/deleteChat.cjs +0 -64
  1253. package/node_modules/zca-js/dist/cjs/apis/deleteGroupInviteBox.cjs +0 -32
  1254. package/node_modules/zca-js/dist/cjs/apis/deleteMessage.cjs +0 -66
  1255. package/node_modules/zca-js/dist/cjs/apis/deleteProductCatalog.cjs +0 -36
  1256. package/node_modules/zca-js/dist/cjs/apis/disableGroupLink.cjs +0 -29
  1257. package/node_modules/zca-js/dist/cjs/apis/disperseGroup.cjs +0 -33
  1258. package/node_modules/zca-js/dist/cjs/apis/editNote.cjs +0 -51
  1259. package/node_modules/zca-js/dist/cjs/apis/editReminder.cjs +0 -75
  1260. package/node_modules/zca-js/dist/cjs/apis/enableGroupLink.cjs +0 -30
  1261. package/node_modules/zca-js/dist/cjs/apis/fetchAccountInfo.cjs +0 -15
  1262. package/node_modules/zca-js/dist/cjs/apis/findUser.cjs +0 -56
  1263. package/node_modules/zca-js/dist/cjs/apis/findUserByUsername.cjs +0 -41
  1264. package/node_modules/zca-js/dist/cjs/apis/forwardMessage.cjs +0 -129
  1265. package/node_modules/zca-js/dist/cjs/apis/getAliasList.cjs +0 -32
  1266. package/node_modules/zca-js/dist/cjs/apis/getAllFriends.cjs +0 -48
  1267. package/node_modules/zca-js/dist/cjs/apis/getAllGroups.cjs +0 -15
  1268. package/node_modules/zca-js/dist/cjs/apis/getArchivedChatList.cjs +0 -28
  1269. package/node_modules/zca-js/dist/cjs/apis/getAutoDeleteChat.cjs +0 -26
  1270. package/node_modules/zca-js/dist/cjs/apis/getAutoReplyList.cjs +0 -29
  1271. package/node_modules/zca-js/dist/cjs/apis/getAvatarList.cjs +0 -33
  1272. package/node_modules/zca-js/dist/cjs/apis/getAvatarUrlProfile.cjs +0 -44
  1273. package/node_modules/zca-js/dist/cjs/apis/getBizAccount.cjs +0 -32
  1274. package/node_modules/zca-js/dist/cjs/apis/getCatalogList.cjs +0 -37
  1275. package/node_modules/zca-js/dist/cjs/apis/getCloseFriends.cjs +0 -25
  1276. package/node_modules/zca-js/dist/cjs/apis/getContext.cjs +0 -9
  1277. package/node_modules/zca-js/dist/cjs/apis/getCookie.cjs +0 -14
  1278. package/node_modules/zca-js/dist/cjs/apis/getFriendBoardList.cjs +0 -31
  1279. package/node_modules/zca-js/dist/cjs/apis/getFriendOnlines.cjs +0 -40
  1280. package/node_modules/zca-js/dist/cjs/apis/getFriendRecommendations.cjs +0 -32
  1281. package/node_modules/zca-js/dist/cjs/apis/getFriendRequestStatus.cjs +0 -30
  1282. package/node_modules/zca-js/dist/cjs/apis/getFullAvatar.cjs +0 -30
  1283. package/node_modules/zca-js/dist/cjs/apis/getGroupBlockedMember.cjs +0 -34
  1284. package/node_modules/zca-js/dist/cjs/apis/getGroupChatHistory.cjs +0 -51
  1285. package/node_modules/zca-js/dist/cjs/apis/getGroupInfo.cjs +0 -37
  1286. package/node_modules/zca-js/dist/cjs/apis/getGroupInviteBoxInfo.cjs +0 -43
  1287. package/node_modules/zca-js/dist/cjs/apis/getGroupInviteBoxList.cjs +0 -36
  1288. package/node_modules/zca-js/dist/cjs/apis/getGroupLinkDetail.cjs +0 -30
  1289. package/node_modules/zca-js/dist/cjs/apis/getGroupLinkInfo.cjs +0 -33
  1290. package/node_modules/zca-js/dist/cjs/apis/getGroupMembersInfo.cjs +0 -29
  1291. package/node_modules/zca-js/dist/cjs/apis/getHiddenConversations.cjs +0 -28
  1292. package/node_modules/zca-js/dist/cjs/apis/getLabels.cjs +0 -33
  1293. package/node_modules/zca-js/dist/cjs/apis/getListBank.cjs +0 -25
  1294. package/node_modules/zca-js/dist/cjs/apis/getListBankAccount.cjs +0 -32
  1295. package/node_modules/zca-js/dist/cjs/apis/getListBoard.cjs +0 -59
  1296. package/node_modules/zca-js/dist/cjs/apis/getListDevice.cjs +0 -27
  1297. package/node_modules/zca-js/dist/cjs/apis/getListReminder.cjs +0 -61
  1298. package/node_modules/zca-js/dist/cjs/apis/getMultiUsersByPhones.cjs +0 -53
  1299. package/node_modules/zca-js/dist/cjs/apis/getMute.cjs +0 -27
  1300. package/node_modules/zca-js/dist/cjs/apis/getOwnId.cjs +0 -9
  1301. package/node_modules/zca-js/dist/cjs/apis/getPendingGroupMembers.cjs +0 -32
  1302. package/node_modules/zca-js/dist/cjs/apis/getPinConversations.cjs +0 -27
  1303. package/node_modules/zca-js/dist/cjs/apis/getPollDetail.cjs +0 -35
  1304. package/node_modules/zca-js/dist/cjs/apis/getProductCatalogList.cjs +0 -38
  1305. package/node_modules/zca-js/dist/cjs/apis/getQR.cjs +0 -34
  1306. package/node_modules/zca-js/dist/cjs/apis/getQuickMessageList.cjs +0 -29
  1307. package/node_modules/zca-js/dist/cjs/apis/getRelatedFriendGroup.cjs +0 -35
  1308. package/node_modules/zca-js/dist/cjs/apis/getReminder.cjs +0 -30
  1309. package/node_modules/zca-js/dist/cjs/apis/getReminderResponses.cjs +0 -29
  1310. package/node_modules/zca-js/dist/cjs/apis/getSentFriendRequest.cjs +0 -29
  1311. package/node_modules/zca-js/dist/cjs/apis/getSettings.cjs +0 -25
  1312. package/node_modules/zca-js/dist/cjs/apis/getStickerCategoryDetail.cjs +0 -29
  1313. package/node_modules/zca-js/dist/cjs/apis/getStickers.cjs +0 -45
  1314. package/node_modules/zca-js/dist/cjs/apis/getStickersDetail.cjs +0 -45
  1315. package/node_modules/zca-js/dist/cjs/apis/getUnreadMark.cjs +0 -35
  1316. package/node_modules/zca-js/dist/cjs/apis/getUserInfo.cjs +0 -58
  1317. package/node_modules/zca-js/dist/cjs/apis/inviteUserToGroups.cjs +0 -35
  1318. package/node_modules/zca-js/dist/cjs/apis/joinGroupInviteBox.cjs +0 -30
  1319. package/node_modules/zca-js/dist/cjs/apis/joinGroupLink.cjs +0 -32
  1320. package/node_modules/zca-js/dist/cjs/apis/keepAlive.cjs +0 -27
  1321. package/node_modules/zca-js/dist/cjs/apis/lastOnline.cjs +0 -31
  1322. package/node_modules/zca-js/dist/cjs/apis/leaveGroup.cjs +0 -38
  1323. package/node_modules/zca-js/dist/cjs/apis/listen.cjs +0 -440
  1324. package/node_modules/zca-js/dist/cjs/apis/lockPoll.cjs +0 -33
  1325. package/node_modules/zca-js/dist/cjs/apis/login.cjs +0 -102
  1326. package/node_modules/zca-js/dist/cjs/apis/loginQR.cjs +0 -368
  1327. package/node_modules/zca-js/dist/cjs/apis/logoutV2.cjs +0 -83
  1328. package/node_modules/zca-js/dist/cjs/apis/lostFocus.cjs +0 -28
  1329. package/node_modules/zca-js/dist/cjs/apis/parseLink.cjs +0 -32
  1330. package/node_modules/zca-js/dist/cjs/apis/registerCatalog.cjs +0 -32
  1331. package/node_modules/zca-js/dist/cjs/apis/rejectFriendRequest.cjs +0 -32
  1332. package/node_modules/zca-js/dist/cjs/apis/removeFriend.cjs +0 -33
  1333. package/node_modules/zca-js/dist/cjs/apis/removeFriendAlias.cjs +0 -29
  1334. package/node_modules/zca-js/dist/cjs/apis/removeGroupBlockedMember.cjs +0 -33
  1335. package/node_modules/zca-js/dist/cjs/apis/removeGroupDeputy.cjs +0 -35
  1336. package/node_modules/zca-js/dist/cjs/apis/removeQuickMessage.cjs +0 -32
  1337. package/node_modules/zca-js/dist/cjs/apis/removeReminder.cjs +0 -54
  1338. package/node_modules/zca-js/dist/cjs/apis/removeUnreadMark.cjs +0 -64
  1339. package/node_modules/zca-js/dist/cjs/apis/removeUserFromGroup.cjs +0 -39
  1340. package/node_modules/zca-js/dist/cjs/apis/resetHiddenConversPin.cjs +0 -25
  1341. package/node_modules/zca-js/dist/cjs/apis/reuseAvatar.cjs +0 -31
  1342. package/node_modules/zca-js/dist/cjs/apis/reviewPendingMemberRequest.cjs +0 -43
  1343. package/node_modules/zca-js/dist/cjs/apis/scanURL.cjs +0 -33
  1344. package/node_modules/zca-js/dist/cjs/apis/searchSticker.cjs +0 -33
  1345. package/node_modules/zca-js/dist/cjs/apis/sendBankCard.cjs +0 -68
  1346. package/node_modules/zca-js/dist/cjs/apis/sendCard.cjs +0 -89
  1347. package/node_modules/zca-js/dist/cjs/apis/sendDeliveredEvent.cjs +0 -69
  1348. package/node_modules/zca-js/dist/cjs/apis/sendFriendRequest.cjs +0 -44
  1349. package/node_modules/zca-js/dist/cjs/apis/sendLink.cjs +0 -92
  1350. package/node_modules/zca-js/dist/cjs/apis/sendMessage.cjs +0 -500
  1351. package/node_modules/zca-js/dist/cjs/apis/sendReport.cjs +0 -67
  1352. package/node_modules/zca-js/dist/cjs/apis/sendSeenEvent.cjs +0 -77
  1353. package/node_modules/zca-js/dist/cjs/apis/sendSticker.cjs +0 -88
  1354. package/node_modules/zca-js/dist/cjs/apis/sendTypingEvent.cjs +0 -47
  1355. package/node_modules/zca-js/dist/cjs/apis/sendVideo.cjs +0 -148
  1356. package/node_modules/zca-js/dist/cjs/apis/sendVoice.cjs +0 -103
  1357. package/node_modules/zca-js/dist/cjs/apis/setHiddenConversations.cjs +0 -53
  1358. package/node_modules/zca-js/dist/cjs/apis/setMute.cjs +0 -81
  1359. package/node_modules/zca-js/dist/cjs/apis/setPinnedConversations.cjs +0 -48
  1360. package/node_modules/zca-js/dist/cjs/apis/sharePoll.cjs +0 -33
  1361. package/node_modules/zca-js/dist/cjs/apis/unblockUser.cjs +0 -33
  1362. package/node_modules/zca-js/dist/cjs/apis/undo.cjs +0 -56
  1363. package/node_modules/zca-js/dist/cjs/apis/undoFriendRequest.cjs +0 -32
  1364. package/node_modules/zca-js/dist/cjs/apis/updateActiveStatus.cjs +0 -32
  1365. package/node_modules/zca-js/dist/cjs/apis/updateArchivedChatList.cjs +0 -39
  1366. package/node_modules/zca-js/dist/cjs/apis/updateAutoDeleteChat.cjs +0 -54
  1367. package/node_modules/zca-js/dist/cjs/apis/updateAutoReply.cjs +0 -43
  1368. package/node_modules/zca-js/dist/cjs/apis/updateBankAccount.cjs +0 -36
  1369. package/node_modules/zca-js/dist/cjs/apis/updateCatalog.cjs +0 -35
  1370. package/node_modules/zca-js/dist/cjs/apis/updateGroupSettings.cjs +0 -48
  1371. package/node_modules/zca-js/dist/cjs/apis/updateHiddenConversPin.cjs +0 -35
  1372. package/node_modules/zca-js/dist/cjs/apis/updateLabels.cjs +0 -39
  1373. package/node_modules/zca-js/dist/cjs/apis/updateLang.cjs +0 -35
  1374. package/node_modules/zca-js/dist/cjs/apis/updateProductCatalog.cjs +0 -56
  1375. package/node_modules/zca-js/dist/cjs/apis/updateProfile.cjs +0 -48
  1376. package/node_modules/zca-js/dist/cjs/apis/updateProfileBio.cjs +0 -32
  1377. package/node_modules/zca-js/dist/cjs/apis/updateQuickMessage.cjs +0 -66
  1378. package/node_modules/zca-js/dist/cjs/apis/updateSettings.cjs +0 -95
  1379. package/node_modules/zca-js/dist/cjs/apis/upgradeGroupToCommunity.cjs +0 -32
  1380. package/node_modules/zca-js/dist/cjs/apis/uploadAttachment.cjs +0 -211
  1381. package/node_modules/zca-js/dist/cjs/apis/uploadProductPhoto.cjs +0 -55
  1382. package/node_modules/zca-js/dist/cjs/apis/votePoll.cjs +0 -34
  1383. package/node_modules/zca-js/dist/cjs/apis.cjs +0 -324
  1384. package/node_modules/zca-js/dist/cjs/context.cjs +0 -34
  1385. package/node_modules/zca-js/dist/cjs/index.cjs +0 -159
  1386. package/node_modules/zca-js/dist/cjs/models/AutoReply.cjs +0 -9
  1387. package/node_modules/zca-js/dist/cjs/models/Bank.cjs +0 -263
  1388. package/node_modules/zca-js/dist/cjs/models/Board.cjs +0 -8
  1389. package/node_modules/zca-js/dist/cjs/models/DeliveredMessage.cjs +0 -23
  1390. package/node_modules/zca-js/dist/cjs/models/Enum.cjs +0 -27
  1391. package/node_modules/zca-js/dist/cjs/models/FriendEvent.cjs +0 -87
  1392. package/node_modules/zca-js/dist/cjs/models/Group.cjs +0 -13
  1393. package/node_modules/zca-js/dist/cjs/models/GroupEvent.cjs +0 -94
  1394. package/node_modules/zca-js/dist/cjs/models/Message.cjs +0 -35
  1395. package/node_modules/zca-js/dist/cjs/models/Reaction.cjs +0 -74
  1396. package/node_modules/zca-js/dist/cjs/models/Reminder.cjs +0 -9
  1397. package/node_modules/zca-js/dist/cjs/models/SeenMessage.cjs +0 -23
  1398. package/node_modules/zca-js/dist/cjs/models/Typing.cjs +0 -23
  1399. package/node_modules/zca-js/dist/cjs/models/Undo.cjs +0 -16
  1400. package/node_modules/zca-js/dist/cjs/models/ZBusiness.cjs +0 -39
  1401. package/node_modules/zca-js/dist/cjs/update.cjs +0 -34
  1402. package/node_modules/zca-js/dist/cjs/utils.cjs +0 -703
  1403. package/node_modules/zca-js/dist/cjs/zalo.cjs +0 -112
  1404. package/node_modules/zca-js/dist/context.d.ts +0 -179
  1405. package/node_modules/zca-js/dist/context.js +0 -28
  1406. package/node_modules/zca-js/dist/index.d.ts +0 -166
  1407. package/node_modules/zca-js/dist/index.js +0 -14
  1408. package/node_modules/zca-js/dist/models/Attachment.d.ts +0 -9
  1409. package/node_modules/zca-js/dist/models/Attachment.js +0 -1
  1410. package/node_modules/zca-js/dist/models/AutoReply.d.ts +0 -20
  1411. package/node_modules/zca-js/dist/models/AutoReply.js +0 -7
  1412. package/node_modules/zca-js/dist/models/Bank.d.ts +0 -291
  1413. package/node_modules/zca-js/dist/models/Bank.js +0 -262
  1414. package/node_modules/zca-js/dist/models/Board.d.ts +0 -60
  1415. package/node_modules/zca-js/dist/models/Board.js +0 -6
  1416. package/node_modules/zca-js/dist/models/Catalog.d.ts +0 -16
  1417. package/node_modules/zca-js/dist/models/Catalog.js +0 -1
  1418. package/node_modules/zca-js/dist/models/DeliveredMessage.d.ts +0 -27
  1419. package/node_modules/zca-js/dist/models/DeliveredMessage.js +0 -17
  1420. package/node_modules/zca-js/dist/models/Enum.d.ts +0 -21
  1421. package/node_modules/zca-js/dist/models/Enum.js +0 -25
  1422. package/node_modules/zca-js/dist/models/FriendEvent.d.ts +0 -107
  1423. package/node_modules/zca-js/dist/models/FriendEvent.js +0 -83
  1424. package/node_modules/zca-js/dist/models/Group.d.ts +0 -125
  1425. package/node_modules/zca-js/dist/models/Group.js +0 -11
  1426. package/node_modules/zca-js/dist/models/GroupEvent.d.ts +0 -178
  1427. package/node_modules/zca-js/dist/models/GroupEvent.js +0 -90
  1428. package/node_modules/zca-js/dist/models/Label.d.ts +0 -10
  1429. package/node_modules/zca-js/dist/models/Label.js +0 -1
  1430. package/node_modules/zca-js/dist/models/Message.d.ts +0 -91
  1431. package/node_modules/zca-js/dist/models/Message.js +0 -29
  1432. package/node_modules/zca-js/dist/models/ProductCatalog.d.ts +0 -17
  1433. package/node_modules/zca-js/dist/models/ProductCatalog.js +0 -1
  1434. package/node_modules/zca-js/dist/models/QuickMessage.d.ts +0 -25
  1435. package/node_modules/zca-js/dist/models/QuickMessage.js +0 -1
  1436. package/node_modules/zca-js/dist/models/Reaction.d.ts +0 -85
  1437. package/node_modules/zca-js/dist/models/Reaction.js +0 -70
  1438. package/node_modules/zca-js/dist/models/Reminder.d.ts +0 -51
  1439. package/node_modules/zca-js/dist/models/Reminder.js +0 -7
  1440. package/node_modules/zca-js/dist/models/SeenMessage.d.ts +0 -26
  1441. package/node_modules/zca-js/dist/models/SeenMessage.js +0 -17
  1442. package/node_modules/zca-js/dist/models/Sticker.d.ts +0 -27
  1443. package/node_modules/zca-js/dist/models/Sticker.js +0 -1
  1444. package/node_modules/zca-js/dist/models/Typing.d.ts +0 -24
  1445. package/node_modules/zca-js/dist/models/Typing.js +0 -17
  1446. package/node_modules/zca-js/dist/models/Undo.d.ts +0 -34
  1447. package/node_modules/zca-js/dist/models/Undo.js +0 -12
  1448. package/node_modules/zca-js/dist/models/User.d.ts +0 -70
  1449. package/node_modules/zca-js/dist/models/User.js +0 -1
  1450. package/node_modules/zca-js/dist/models/ZBusiness.d.ts +0 -22
  1451. package/node_modules/zca-js/dist/models/ZBusiness.js +0 -35
  1452. package/node_modules/zca-js/dist/models/index.d.ts +0 -22
  1453. package/node_modules/zca-js/dist/models/index.js +0 -22
  1454. package/node_modules/zca-js/dist/update.d.ts +0 -2
  1455. package/node_modules/zca-js/dist/update.js +0 -29
  1456. package/node_modules/zca-js/dist/utils.d.ts +0 -167
  1457. package/node_modules/zca-js/dist/utils.js +0 -719
  1458. package/node_modules/zca-js/dist/zalo.d.ts +0 -41
  1459. package/node_modules/zca-js/dist/zalo.js +0 -107
  1460. package/node_modules/zca-js/eslint.config.js +0 -21
  1461. package/node_modules/zca-js/index.d.ts +0 -2
  1462. package/node_modules/zca-js/package.json +0 -78
  1463. package/node_modules/zca-js/rollup.config.js +0 -28
  1464. package/patches/zca-js+2.2.0.patch +0 -1445
  1465. package/src/cli.test.js +0 -127
  1466. package/src/core/forward.js +0 -112
  1467. package/src/core/group-history.js +0 -248
  1468. package/src/core/group-invite.js +0 -53
  1469. package/src/core/offline-queue.js +0 -194
  1470. package/src/core/pin.js +0 -222
  1471. package/src/core/socket-tap.js +0 -154
  1472. package/src/core/sync-v2/assets.js +0 -181
  1473. package/src/core/sync-v2/gid.js +0 -106
  1474. package/src/core/sync-v2/keepalive.js +0 -177
  1475. package/src/core/sync-v2/message-types.js +0 -522
  1476. package/src/core/sync-v2/renewlink.js +0 -124
  1477. package/src/core/sync-v2/zcloud-cipher.js +0 -88
  1478. package/src/core/zca-custom.js +0 -78
  1479. package/src/mcp/message-buffer.test.js +0 -360
  1480. package/src/mcp/notifier.test.js +0 -275
  1481. package/src/mcp/thread-filter.test.js +0 -206
  1482. package/src/mcp/thread-name-cache.test.js +0 -142
  1483. package/src/utils/atomic-file.js +0 -209
  1484. package/src/utils/bank-helpers.test.js +0 -66
  1485. package/src/utils/device-fingerprint.js +0 -72
  1486. package/src/utils/mentions.js +0 -140
  1487. package/src/utils/proxy-helpers.test.js +0 -36
  1488. package/src/utils/split-message.js +0 -166
  1489. package/src/utils/urgency.js +0 -29
  1490. /package/src/{core → zalo/custom}/user-info.js +0 -0
  1491. /package/src/{utils → zalo/format}/extract-message-text.js +0 -0
  1492. /package/src/{utils → zalo/format}/markdown.js +0 -0
@@ -1,32 +1,57 @@
1
1
  /**
2
2
  * MCP tool registrations for Zalo message access, sending, user lookup, and group productivity.
3
- * Registers 25 tools: zalo_get_messages, zalo_get_history, zalo_send_message, zalo_list_threads,
3
+ * Registers 36 tools: zalo_get_messages, zalo_get_history, zalo_send_message, zalo_list_threads,
4
4
  * zalo_search_threads, zalo_mark_read, zalo_view_media, zalo_react, zalo_undo, zalo_get_group_members,
5
5
  * zalo_list_conversations, zalo_coverage, zalo_sync, zalo_sync_status, zalo_search_messages,
6
6
  * zalo_lookup_user, zalo_forward_message, zalo_delete_message, zalo_reminder, zalo_note, zalo_poll,
7
- * zalo_wait_for_message, zalo_label, zalo_friend, zalo_group.
7
+ * zalo_wait_for_message, zalo_label, zalo_friend, zalo_group, zalo_read, zalo_write, and the action tools
8
+ * zalo_conversation, zalo_profile, zalo_auto_reply, zalo_quick_message, zalo_catalog, zalo_bank, zalo_cloud,
9
+ * zalo_sticker and zalo_link (their action tables are src/mcp/actions-*.js, run by src/mcp/action-tool.js), and
10
+ * zalo_official, which runs the official-API commands (src/mcp/official-tool.js).
11
+ * Every CLI command has an action here, or a reason it has none; tests/unit/mcp-cli-parity.test.js holds the map.
8
12
  *
9
13
  * A tool that mirrors a CLI command calls the same code the command does (src/core/cached-message.js,
10
14
  * forward.js, pin.js, group-members.js, recent-conversations.js, src/utils/my-documents.js, urgency.js),
11
15
  * so the two cannot drift apart.
12
16
  */
13
17
 
14
- import { existsSync } from "node:fs";
18
+ import { existsSync, unlinkSync } from "node:fs";
15
19
  import { resolve } from "node:path";
16
20
  import { z } from "zod";
21
+ import { officialMeta, officialTool } from "./official-tool.js";
17
22
  import { openFile } from "../utils/open-file.js";
18
23
  import {
19
24
  getMessages,
25
+ getMessagesInRange,
26
+ getAllMessagesInRange,
20
27
  getMessageById,
21
- searchMessages,
28
+ getMessageForAction,
22
29
  getDisplayName,
30
+ getStoredName,
31
+ setContactName,
23
32
  getThreadType,
24
33
  getThreadNames,
25
34
  getConvState,
26
35
  getReadStates,
27
36
  } from "../core/db.js";
28
- import { downloadSyncedMedia } from "../core/sync-v2/media.js";
37
+ import { searchPage } from "../core/search.js";
38
+ import { parsePeriod } from "../utils/date-range.js";
39
+ import { downloadSyncedMedia, DOWNLOADABLE_KINDS } from "../core/sync-v2/media.js";
40
+ import { mediaPlainSize } from "../core/keystore/media-seal.js";
41
+ import { scopeOfDir } from "../core/keystore/keyring.js";
42
+ import { getAccountDir } from "../core/credentials.js";
43
+ import {
44
+ chooseDelivery,
45
+ decryptToTempfile,
46
+ deliverCachedMedia,
47
+ inlineContent,
48
+ mediaInfo,
49
+ removeMediaTemp,
50
+ } from "../core/media-deliver.js";
51
+ import { ensureMediaServed } from "../core/media-route.js";
29
52
  import { describeZaloError } from "../core/sync-v2/board.js";
53
+ import { boardPage, groupReminderPage } from "../zalo/api/boards.js";
54
+ import { allAliases, allBoardItems, allReminders } from "../core/list-all.js";
30
55
  import { fetchAndCacheHistory } from "../core/history-fetch.js";
31
56
  import {
32
57
  cachedCliMsgId,
@@ -35,42 +60,90 @@ import {
35
60
  cachedMessageById,
36
61
  reactionCliMsgId,
37
62
  recallCliMsgId,
63
+ quotedReply,
64
+ cachedReplyTo,
65
+ resolveReplyTo,
38
66
  } from "../core/cached-message.js";
39
- import { buildForwardReference, sendForward, LOG_SRC_TYPE } from "../core/forward.js";
40
- import { pinMessage, unpinMessage, textPinParams } from "../core/pin.js";
67
+ import {
68
+ buildForwardReference,
69
+ sendForward,
70
+ LOG_SRC_TYPE,
71
+ videoResendOptions,
72
+ voiceResendOptions,
73
+ } from "../zalo/custom/forward.js";
74
+ import { undoPayload } from "../zalo/custom/group-visibility.js";
75
+ import { pinMessage, unpinMessage, textPinParams } from "../zalo/custom/pin.js";
41
76
  import { coverageReport } from "../core/coverage.js";
42
77
  import { groupMemberUids, fetchMemberNames } from "../core/group-members.js";
78
+ import { getGroupInfoWatched, recordOwnExit, goneFields, goneGroupMap, sendRefusal } from "../core/group-exit.js";
79
+ import { departureMessage, planLeave, runLeave } from "../core/group-departure.js";
80
+ import { mutualGroupsOrError } from "../core/mutual-groups.js";
43
81
  import { recentConversations } from "../core/recent-conversations.js";
82
+ import { callReadAll, describeReads, READ_NAMES, ReadUsageError } from "../zalo/reads.js";
83
+ import "../core/local-reads.js";
84
+ import { buildWrite, callWrite, describeWrites, WRITE_NAMES, WRITES } from "../zalo/writes.js";
44
85
  import { describeReadState, readOnZalo } from "../core/read-state.js";
45
- import { extractMessageText } from "../utils/extract-message-text.js";
46
- import { expandMentions, shiftStyles, ALL_MENTION_UID } from "../utils/mentions.js";
86
+ import { extractMessageText } from "../zalo/format/extract-message-text.js";
87
+ import { expandMentions, shiftStyles, ALL_MENTION_UID } from "../zalo/format/mentions.js";
47
88
  import { resolveSelfThread } from "../utils/my-documents.js";
48
89
  import { buildQuote, resolveQuoteSender } from "../utils/quote.js";
49
- import { urgencyLevel } from "../utils/urgency.js";
50
- import { splitMessage, sendParts } from "../utils/split-message.js";
90
+ import { urgencyLevel } from "../zalo/format/urgency.js";
91
+ import { splitMessage, sendParts } from "../zalo/format/split-message.js";
51
92
  import { hasLiteralNewline, LITERAL_NEWLINE_WARNING } from "../utils/literal-newline.js";
52
- import { markdownToZalo, resolveSendFormat } from "../utils/markdown.js";
53
- import { MAX_BATCH, MAX_SETTLE_MS, batchParams, waitResult } from "./wait-result.js";
93
+ import { markdownToZalo, resolveSendFormat } from "../zalo/format/markdown.js";
94
+ import { MAX_SETTLE_MS, batchParams, waitResult } from "./wait-result.js";
95
+ import { SYNC_STAGES } from "./sync-jobs.js";
54
96
  import {
55
97
  describeFriendAddError,
98
+ describeFriendUndoError,
56
99
  extractErrorCode,
57
100
  friendStatusFrom,
58
101
  friendsFrom,
102
+ readSentRequests,
59
103
  receivedRequestsFrom,
60
104
  sentRequestsFrom,
61
105
  } from "../core/friend-requests.js";
106
+ import { errorCodeOf } from "../zalo/errors.js";
62
107
  import {
63
- CONFIRMED_ACTIONS,
64
108
  NOT_A_MEMBER_CODES,
65
109
  REMOVE_NOT_IN_GROUP,
66
110
  createdGroupFrom,
67
- groupConfirmRequired,
68
111
  groupInfoFrom,
69
112
  memberChangeFrom,
70
113
  } from "../core/group-admin.js";
114
+ import { gateMcp, removedLabels } from "../core/confirm-guard.js";
115
+ import { getSecurePage } from "../core/secure-page.js";
116
+ import { hiddenChatsFor, hiddenEventView, readThreadIds, withoutHidden } from "../core/hidden-chats.js";
117
+
118
+ /** The conversation ids a raw read names (moved to src/core/hidden-chats.js; `read run` uses it too). */
119
+ export { readThreadIds } from "../core/hidden-chats.js";
120
+ import { resolveHiddenChats, resolveMediaDelivery } from "../core/deploy-policy.js";
71
121
  import { resolveCurrentUserNames, withCurrentSenderNames } from "../core/current-user-names.js";
122
+ import { aliasFor, noteAliasSet, personNameFields, senderNameFields } from "../core/contact-names.js";
123
+ import { actionTool, confirmInput, dryRunInput, ticketInput } from "./action-tool.js";
124
+ import { gateToolsByPresence } from "./tool-presence.js";
125
+ import { BIN_TO_DISPLAY, generateQrTransferImage, qrTransferCaption, resolveBankBin } from "../utils/bank-helpers.js";
126
+ import { CONVERSATION_ACTIONS, CONVERSATION_META } from "./actions-conversation.js";
127
+ import { PROFILE_ACTIONS, PROFILE_META } from "./actions-profile.js";
128
+ import {
129
+ AUTO_REPLY_ACTIONS,
130
+ AUTO_REPLY_META,
131
+ BANK_ACTIONS,
132
+ BANK_META,
133
+ CATALOG_ACTIONS,
134
+ CATALOG_META,
135
+ QUICK_MESSAGE_ACTIONS,
136
+ QUICK_MESSAGE_META,
137
+ } from "./actions-business.js";
138
+ import { CLOUD_ACTIONS, CLOUD_META, LINK_ACTIONS, LINK_META, STICKER_ACTIONS, STICKER_META } from "./actions-media.js";
139
+ import {
140
+ FRIEND_EXTRA_ACTIONS,
141
+ FRIEND_EXTRA_INPUTS,
142
+ GROUP_EXTRA_ACTIONS,
143
+ GROUP_EXTRA_INPUTS,
144
+ } from "./actions-social.js";
72
145
 
73
- /** Repeat mode codes matching zca-js ReminderRepeatMode enum and src/commands/reminder.js. */
146
+ /** Repeat mode codes matching Zalo's reminder repeat modes and src/commands/reminder.js. */
74
147
  const REMINDER_REPEAT_MODES = { none: 0, daily: 1, weekly: 2, monthly: 3 };
75
148
  const REMINDER_REPEAT_LABELS = { 0: "none", 1: "daily", 2: "weekly", 3: "monthly" };
76
149
 
@@ -99,7 +172,7 @@ function parseReminderTime(val) {
99
172
  /**
100
173
  * Build the source descriptor `buildForwardReference` expects from a cached message row.
101
174
  *
102
- * @param {object} api - logged-in zca-js API instance
175
+ * @param {object} api - logged-in session
103
176
  * @param {object} row - cached message row from zalo.db
104
177
  * @returns {Promise<object>}
105
178
  */
@@ -160,11 +233,12 @@ async function forwardSourceForMcp(api, row) {
160
233
  * Omitted means "work it out"; an explicit 0 or 1 is always obeyed.
161
234
  *
162
235
  * @param {string} threadId
163
- * @param {number} [explicit] - the caller's threadType, if they gave one
236
+ * @param {"dm"|"group"} [explicit] - the caller's threadType, if they gave one
164
237
  * @returns {number} THREAD_USER or THREAD_GROUP
165
238
  */
166
239
  function resolveThreadType(threadId, explicit) {
167
- if (explicit !== undefined && explicit !== null) return Number(explicit);
240
+ if (explicit === "group") return THREAD_GROUP;
241
+ if (explicit === "dm") return THREAD_USER;
168
242
  try {
169
243
  return getThreadType(String(threadId)) === "group" ? THREAD_GROUP : THREAD_USER;
170
244
  } catch {
@@ -173,19 +247,62 @@ function resolveThreadType(threadId, explicit) {
173
247
  }
174
248
  }
175
249
 
176
- /** Thread type constants matching zca-js ThreadType enum */
250
+ /** A group's conversation id in a label: `g` and the group id. */
251
+ const LABEL_GROUP_ID = /^g\d+$/;
252
+
253
+ /**
254
+ * A thread as a conversation label names it: `g<groupId>` for a group, the bare id for a user (Zalo's
255
+ * conversation ids). An id already given as `g<digits>` is kept.
256
+ *
257
+ * @param {string} threadId
258
+ * @param {"dm"|"group"} [explicit] - the caller's threadType, if they gave one
259
+ * @returns {string}
260
+ */
261
+ function labelConversationId(threadId, explicit) {
262
+ const id = String(threadId);
263
+ if (LABEL_GROUP_ID.test(id)) return id;
264
+ return resolveThreadType(id, explicit) === THREAD_GROUP ? `g${id}` : id;
265
+ }
266
+
267
+ /** Thread type constants matching the engine's THREAD_TYPE (src/zalo/enums.js) */
177
268
  const THREAD_USER = 0;
178
269
 
179
270
  /** Thread type for groups — the only kind Zalo delivers mentions in. */
180
271
  const THREAD_GROUP = 1;
181
272
 
273
+ /** What a reply calls a thread type: the same words an input takes. */
274
+ const threadTypeName = (type) => (Number(type) === THREAD_GROUP ? "group" : "dm");
275
+
182
276
  /**
183
277
  * How many member ids `zalo_get_group_members` asks Zalo to name per request.
184
- * zca-js puts them in the request URL, and Zalo Web asks in bounded batches
278
+ * The session puts them in the request URL, and Zalo asks in bounded batches
185
279
  * too; 50 is the batch the group commands already use for `getGroupInfo`.
186
280
  */
187
281
  const MEMBER_NAME_BATCH = 50;
188
282
 
283
+ /** The kinds of content zalo_send_message sends besides text; one per call. */
284
+ const SEND_MODES = ["filePath/files", "link", "cardUserId", "sticker", "voiceUrl", "video", "bankCard", "qrTransfer"];
285
+
286
+ /**
287
+ * Which of SEND_MODES a zalo_send_message call gives, by its input names.
288
+ *
289
+ * @param {object} val - the call's input
290
+ * @returns {string[]}
291
+ */
292
+ function sendModes(val) {
293
+ const hasFile = Boolean(val.filePath || (Array.isArray(val.files) && val.files.length > 0));
294
+ return [
295
+ hasFile && "files",
296
+ val.link && "link",
297
+ val.cardUserId && "cardUserId",
298
+ val.sticker && "sticker",
299
+ val.voiceUrl && "voiceUrl",
300
+ val.video && "video",
301
+ val.bankCard && "bankCard",
302
+ val.qrTransfer && "qrTransfer",
303
+ ].filter(Boolean);
304
+ }
305
+
189
306
  /** How a refusal tells an agent to supply a message's cliMsgId itself. */
190
307
  const CLI_MSG_ID_HINT = "the `cliMsgId` parameter (zalo_send_message returns it for a message you sent)";
191
308
 
@@ -224,9 +341,37 @@ const CLI_MSG_ID_HINT = "the `cliMsgId` parameter (zalo_send_message returns it
224
341
  * @param {string} uid
225
342
  * @returns {string|null}
226
343
  */
344
+ /**
345
+ * Record the name this account shows for a user (the nickname just set, or the profile name after a nickname was
346
+ * removed) in `contacts`, where `getDisplayName` reads first. Best effort, like cachedDisplayName: the MCP tests run
347
+ * without a db, and a name that fails to persist only costs the next lookup.
348
+ * @param {string} uid
349
+ * @param {string} name
350
+ */
351
+ function rememberContactName(uid, name) {
352
+ try {
353
+ setContactName(uid, name);
354
+ } catch {
355
+ /* no db open in this process */
356
+ }
357
+ }
358
+
227
359
  function cachedDisplayName(uid) {
228
360
  try {
229
361
  return getDisplayName(uid);
362
+ } catch {
363
+ return aliasFor(uid); // no db in this process: the owner's alias, when the daemon holds one
364
+ }
365
+ }
366
+
367
+ /**
368
+ * The name the cache holds for a user without the owner's alias (`db.getStoredName`), or null.
369
+ * @param {string} uid
370
+ * @returns {string|null}
371
+ */
372
+ function cachedStoredName(uid) {
373
+ try {
374
+ return getStoredName(uid);
230
375
  } catch {
231
376
  return null; // no db in this process
232
377
  }
@@ -235,7 +380,7 @@ function cachedDisplayName(uid) {
235
380
  /**
236
381
  * This account's own uid, or "" when the session cannot say.
237
382
  *
238
- * @param {object} api - zca-js API instance
383
+ * @param {object} api - session object
239
384
  * @returns {string}
240
385
  */
241
386
  function ownIdOf(api) {
@@ -252,7 +397,7 @@ function ownIdOf(api) {
252
397
  * device -- for the tools that list conversations. Empty when the cache
253
398
  * cannot answer, so a listing never fails for want of it.
254
399
  *
255
- * @param {object} api - zca-js API instance
400
+ * @param {object} api - session object
256
401
  * @param {string[]} threadIds
257
402
  * @returns {Map<string, object>} threadId -> an entry of db.js getReadStates
258
403
  */
@@ -271,11 +416,11 @@ function readStatesOf(api, threadIds) {
271
416
  * `me`, or this account's own uid, means My Documents: a 1-1 conversation of
272
417
  * its own whose id is the session's `loginInfo.send2me_id`, never the own uid
273
418
  * (src/utils/my-documents.js, the same function `msg send me` calls). An
274
- * explicit `threadType: 1` is refused, as `msg send me -t 1` is.
419
+ * explicit `threadType: "group"` is refused, as `msg send me -t 1` is.
275
420
  *
276
- * @param {object} api - zca-js API instance
421
+ * @param {object} api - session object
277
422
  * @param {string} threadId - as the caller gave it
278
- * @param {number} [threadTypeIn] - the caller's threadType, if they gave one
423
+ * @param {"dm"|"group"} [threadTypeIn] - the caller's threadType, if they gave one
279
424
  * @returns {null|{threadId: string, notice: string}|{error: string}} null when it is not a self reference
280
425
  */
281
426
  function selfThread(api, threadId, threadTypeIn) {
@@ -288,7 +433,7 @@ function selfThread(api, threadId, threadTypeIn) {
288
433
  return resolveSelfThread(threadId, {
289
434
  ownId: ownId === null ? null : String(ownId),
290
435
  send2meId: () => api.getContext().loginInfo?.send2me_id,
291
- groupRequested: Number(threadTypeIn) === THREAD_GROUP,
436
+ groupRequested: threadTypeIn === "group",
292
437
  });
293
438
  }
294
439
 
@@ -299,9 +444,9 @@ function selfThread(api, threadId, threadTypeIn) {
299
444
  * typed by {@link resolveThreadType}: the caller's explicit threadType, else
300
445
  * the cache's.
301
446
  *
302
- * @param {object} api - zca-js API instance
447
+ * @param {object} api - session object
303
448
  * @param {string} threadIdIn - as the caller gave it
304
- * @param {number} [threadTypeIn] - the caller's threadType, if they gave one
449
+ * @param {"dm"|"group"} [threadTypeIn] - the caller's threadType, if they gave one
305
450
  * @returns {{threadId: string, threadType: number, notice?: string}|{error: string}} `notice` only for My Documents
306
451
  */
307
452
  function actionThread(api, threadIdIn, threadTypeIn) {
@@ -329,6 +474,113 @@ function err(message) {
329
474
  return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
330
475
  }
331
476
 
477
+ /**
478
+ * One cached message as zalo_get_history returns it.
479
+ * @param {object} m - a `messages` row
480
+ * @returns {object}
481
+ */
482
+ function cachedHistoryRow(m) {
483
+ return {
484
+ msgId: m.msgId,
485
+ cliMsgId: cachedCliMsgId(m) ?? undefined,
486
+ threadId: m.threadId,
487
+ senderId: m.senderId || null,
488
+ senderName: m.senderName || null,
489
+ text: m.text,
490
+ timestamp: m.timestamp,
491
+ type: m.type,
492
+ mediaCached: m.localPath ? true : undefined,
493
+ hasAttachment: m.has_attachment ? true : undefined,
494
+ msgStatus: m.msgStatus ?? undefined,
495
+ replyTo: cachedReplyTo(m),
496
+ };
497
+ }
498
+
499
+ /** The largest sticker picture zalo_view_media fetches. */
500
+ const STICKER_MAX_BYTES = 2 * 1024 * 1024;
501
+
502
+ /**
503
+ * A sticker's picture, fetched for zalo_view_media and not kept: stickers are not downloaded with the messages. The
504
+ * URL comes from the live message, else from Zalo's sticker detail for the id the cached message names.
505
+ *
506
+ * @param {object} api
507
+ * @param {object|null} row - the cached message
508
+ * @param {object|null} buffered - the live message
509
+ * @param {(url: string, init: object) => Promise<Response>} [fetchImpl]
510
+ * @returns {Promise<{bytes: Buffer, mime: string} | {error: string}>}
511
+ */
512
+ export async function stickerPicture(api, row, buffered, fetchImpl = (...a) => fetch(...a)) {
513
+ let url = buffered?.attachment?.url || null;
514
+ if (!url) {
515
+ let content = null;
516
+ try {
517
+ content = JSON.parse(row?.raw_data || "{}")?.content ?? null;
518
+ } catch {
519
+ /* no stored content */
520
+ }
521
+ const id = Number(content?.id);
522
+ if (!Number.isFinite(id) || id <= 0) return { error: "This sticker message does not say which sticker it is." };
523
+ try {
524
+ const [detail] = (await api.getStickersDetail([id])) ?? [];
525
+ url = detail?.stickerWebpUrl || detail?.stickerUrl || null;
526
+ } catch (e) {
527
+ return { error: `Zalo did not describe the sticker: ${e.message}` };
528
+ }
529
+ if (!url) return { error: "Zalo did not give a picture for this sticker." };
530
+ }
531
+ try {
532
+ const res = await fetchImpl(url, { signal: AbortSignal.timeout(15_000), redirect: "follow" });
533
+ const mime = String(res.headers.get("content-type") || "")
534
+ .split(";")[0]
535
+ .trim()
536
+ .toLowerCase();
537
+ if (!res.ok || !mime.startsWith("image/")) {
538
+ return { error: `The sticker's picture could not be fetched (HTTP ${res.status}).` };
539
+ }
540
+ const bytes = Buffer.from(await res.arrayBuffer());
541
+ if (bytes.length > STICKER_MAX_BYTES) return { error: "The sticker's picture is too large to show." };
542
+ return { bytes, mime };
543
+ } catch (e) {
544
+ return { error: `The sticker's picture could not be fetched: ${e.message}` };
545
+ }
546
+ }
547
+
548
+ /**
549
+ * A thread's cached messages between two times, oldest first: every one, or one page of `limit` (bug-reports #5).
550
+ *
551
+ * Cache only: Zalo's server serves nothing older than this login, so a date range is the cache's question.
552
+ *
553
+ * @param {string} threadId
554
+ * @param {{since: number|null, until: number|null, limit: number}} range - `limit` Infinity: every message in the period
555
+ * @param {object} [nameCache]
556
+ * @returns {object} tool payload
557
+ */
558
+ function cacheHistoryRange(threadId, { since, until, limit }, nameCache) {
559
+ let rows;
560
+ let nextSince = null;
561
+ if (Number.isFinite(limit)) ({ rows, nextSince } = getMessagesInRange(String(threadId), { since, until, limit }));
562
+ else rows = getAllMessagesInRange(String(threadId), { since, until }).rows;
563
+ const messages = rows.map(cachedHistoryRow);
564
+ const info = nameCache?.get(String(threadId));
565
+ if (info) for (const m of messages) m.threadName = info.name;
566
+ return {
567
+ threadId: String(threadId),
568
+ source: "cache",
569
+ since,
570
+ until,
571
+ count: messages.length,
572
+ messages,
573
+ // Pass `nextSince` back as `since` (same `until`) for the next page.
574
+ hasMore: nextSince !== null,
575
+ nextSince,
576
+ ...(messages.length === 0 && {
577
+ note:
578
+ "Nothing cached for this conversation in this period. Zalo's server serves only messages since this " +
579
+ "login; older ones are restored with `zalo-agent sync` (the owner's phone confirms it).",
580
+ }),
581
+ };
582
+ }
583
+
332
584
  /**
333
585
  * Read a thread's history out of the local cache.
334
586
  *
@@ -336,7 +588,7 @@ function err(message) {
336
588
  * to fall back to asking Zalo.
337
589
  *
338
590
  * @param {string} threadId
339
- * @param {number} limit
591
+ * @param {number} limit - Infinity: every cached message
340
592
  * @param {number} [before] - epoch ms; return messages older than this
341
593
  * @param {object} [nameCache]
342
594
  * @returns {object|null} tool payload, or null when the cache is empty/unopened
@@ -344,26 +596,13 @@ function err(message) {
344
596
  function cacheHistory(threadId, limit, before, nameCache) {
345
597
  let rows;
346
598
  try {
347
- rows = getMessages(String(threadId), limit, before);
599
+ rows = getMessages(String(threadId), Number.isFinite(limit) ? limit : -1, before);
348
600
  } catch {
349
601
  return null; // no db open in this process — server path it is
350
602
  }
351
603
  if (!rows || rows.length === 0) return null;
352
604
 
353
- const messages = rows
354
- .map((m) => ({
355
- msgId: m.msgId,
356
- threadId: m.threadId,
357
- senderId: m.senderId || null,
358
- senderName: m.senderName || null,
359
- text: m.text,
360
- timestamp: m.timestamp,
361
- type: m.type,
362
- localPath: m.localPath || undefined,
363
- hasAttachment: m.has_attachment ? true : undefined,
364
- msgStatus: m.msgStatus ?? undefined,
365
- }))
366
- .sort((a, b) => (a.timestamp || 0) - (b.timestamp || 0));
605
+ const messages = rows.map(cachedHistoryRow).sort((a, b) => (a.timestamp || 0) - (b.timestamp || 0));
367
606
 
368
607
  const info = nameCache?.get(String(threadId));
369
608
  if (info) for (const m of messages) m.threadName = info.name;
@@ -375,14 +614,23 @@ function cacheHistory(threadId, limit, before, nameCache) {
375
614
  messages,
376
615
  // Oldest returned timestamp: pass it back as `before` for the next page.
377
616
  cursor: messages.length ? messages[0].timestamp : (before ?? null),
378
- hasMore: rows.length >= limit,
617
+ hasMore: Number.isFinite(limit) && rows.length >= limit,
379
618
  };
380
619
  }
381
620
 
621
+ /** What zalo_get_messages and zalo_wait_for_message say about the system events in their list. */
622
+ const SYSTEM_EVENTS_NOTE =
623
+ "A system event arrives in the same list with `system: true` and `msgId` null, only to a read or wait that names no `threadId`; each has a `type` and a `text` to show a person. " +
624
+ "`account_state`: the account's connection state changed (`state` idle, connecting, connected, kicked, yielded, login_needed, signed_out or maintenance, `previous`, `detail`); yielded and signed_out refuse every call until a person acts, as `text` says; a park and the wake that ends it are not in the buffer. " +
625
+ "`group_gone`: the account left, was removed or blocked from, or saw the disbanding of the group in `threadId` (`reason`, `leftAt`, `mode` purge or keep, `purged` counts). " +
626
+ "`queue_overflow`: Zalo's offline queue overflowed and messages from `fromTs` to `toTs` were dropped (`queues`, `groups` repair counts, `pending`; when pending, `command` is the sync the owner runs, which taps the phone). " +
627
+ "`session_expiring`: the account's saved login ends by itself on `expiresAt` (`daysLeft`, `threshold` 14, 7, 3, 1 or 0 once `expired`); the owner renews it with a QR login (`command`), and once it ends every call is refused until then. " +
628
+ "`storage_quota`: this account's storage on the server is near or over its quota, or was purged, or is back within it (`level` warn, over, grace_ending, purged or ok; `usedBytes`, `limitBytes`, `purgeAt`); zalo_profile 'usage' reads the current usage. ";
629
+
382
630
  /**
383
631
  * Register all Zalo MCP tools on the server.
384
632
  * @param {import("@modelcontextprotocol/sdk/server/mcp.js").McpServer} server
385
- * @param {object} api - zca-js API instance
633
+ * @param {object} api - session object
386
634
  * @param {import("./message-buffer.js").MessageBuffer} buffer
387
635
  * @param {import("./thread-filter.js").ThreadFilter} filter
388
636
  * @param {object} config - MCP config
@@ -391,9 +639,23 @@ function cacheHistory(threadId, limit, before, nameCache) {
391
639
  * @param {ReturnType<import("../core/daemon-channel.js").createStageLock>} [stageLock] - the
392
640
  * daemon's one-stage-at-a-time lock; a server-side history fetch takes it, as `listen`'s does
393
641
  * @param {object} [syncJobs] - the daemon's account-local sync job tracker
642
+ * @param {{presence?: object|null}} [extras] - `presence`: the daemon's on-demand presence
643
+ * (src/core/daemon-presence.js); every tool goes through it by its class (./tool-presence.js)
394
644
  */
395
- export function registerTools(server, api, buffer, filter, config, nameCache, accountDir, stageLock, syncJobs) {
396
- const maxPerPoll = config.limits?.maxMessagesPerPoll ?? 20;
645
+ export function registerTools(
646
+ server,
647
+ api,
648
+ buffer,
649
+ filter,
650
+ config,
651
+ nameCache,
652
+ accountDir,
653
+ stageLock,
654
+ syncJobs,
655
+ extras = {},
656
+ ) {
657
+ // On-demand presence: refused while yielded or signed out, a hold per call, warming by the tool's class.
658
+ gateToolsByPresence(server, extras?.presence ?? null);
397
659
 
398
660
  // Several bots can share one listener, each with its own read cursor
399
661
  // (triage M7). A bot that names none reads as "default", as before.
@@ -408,6 +670,74 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
408
670
  "so one bot's zalo_mark_read does not hide messages from another. Omit it for a single bot.",
409
671
  );
410
672
 
673
+ // Every tool with an action Zalo asks about first takes `confirm` and `dryRun` (confirmInput, dryRunInput
674
+ // in action-tool.js), and settles it through gate() (src/core/confirm-guard.js) after its arguments are checked
675
+ // and before anything is sent.
676
+ // Over the stateless HTTP transport this server lives for one request (mcp-http-transport.js marks it), so
677
+ // "human" mode cannot ask there; the guard hands out a one-time approve page and the call is repeated with
678
+ // `ticket` (src/core/secure-page.js).
679
+ const gate = (key, target, { confirm, dryRun, ticket } = {}, extra, more = {}) =>
680
+ gateMcp(key, target, {
681
+ confirm,
682
+ dryRun,
683
+ ticket,
684
+ config,
685
+ accountId: ownIdOf(api),
686
+ server,
687
+ extra,
688
+ stateless: server?.statelessHttp === true,
689
+ // approval.secret: the approver password lives in the account folder, the PIN's hash in the hidden set.
690
+ accountDir: accountDir ?? null,
691
+ hidden: () => hiddenSet(),
692
+ ...more,
693
+ });
694
+
695
+ // Hidden chats (src/core/hidden-chats.js), shown by the deployment's hiddenChats policy (src/core/deploy-policy.js):
696
+ // `exclude` (default) leaves them out of every list, search, history and event; `contentless` turns an event in
697
+ // one into `{event, hidden: true}`; `include` shows them.
698
+ const hiddenSet = () => hiddenChatsFor(ownIdOf(api) ?? "", { stateDir: accountDir ?? null });
699
+ const hiddenMode = () => resolveHiddenChats({ config, accountId: ownIdOf(api) ?? undefined }).mode;
700
+ const hiddenIds = () => (hiddenMode() === "include" ? [] : [...hiddenSet().ids]);
701
+ const isHiddenThread = (id) =>
702
+ id !== undefined && id !== null && hiddenMode() !== "include" && hiddenSet().isHidden(id);
703
+ const eventsView = (list) => {
704
+ const set = hiddenSet();
705
+ const mode = hiddenMode();
706
+ return list
707
+ .map((m) => hiddenEventView(m, set, mode, { name: m?.type === "undo" ? "undo" : "message" }))
708
+ .filter(Boolean);
709
+ };
710
+ const HIDDEN_THREAD =
711
+ "That conversation is one of the account's hidden chats, which this server leaves out (hiddenChats policy). " +
712
+ "The account owner can unhide it, or the deployment can set hiddenChats to include.";
713
+
714
+ // What an action table's rows read (src/mcp/action-tool.js): the session, the guard, and the helpers that type
715
+ // a thread and name this account, so a row resolves a thread the way every other tool here does.
716
+ const toolCtx = {
717
+ api,
718
+ ok,
719
+ err,
720
+ gate,
721
+ ownId: () => ownIdOf(api),
722
+ threadType: resolveThreadType,
723
+ threadTypeName,
724
+ onSelfName: (name) => syncJobs?.onSelfName?.(name),
725
+ // The hidden-chat actions: the deployment's policy, the PIN page, and a live elicitation where there is one.
726
+ config,
727
+ server,
728
+ stateless: server?.statelessHttp === true,
729
+ hidden: () => hiddenSet(),
730
+ hiddenMode: () => hiddenMode(),
731
+ securePage: () => getSecurePage(),
732
+ threadName: (id) => nameCache?.get?.(String(id))?.name ?? null,
733
+ // zalo_profile 'usage': the media folder this server downloads into.
734
+ mediaRoot: config.media?.downloadDir ?? null,
735
+ // zalo_profile 'presence_resume': the daemon's presence (null under a server that runs none).
736
+ presence: extras?.presence ?? null,
737
+ };
738
+ const friendExtra = actionTool("zalo_friend", FRIEND_EXTRA_ACTIONS, toolCtx);
739
+ const groupExtra = actionTool("zalo_group", GROUP_EXTRA_ACTIONS, toolCtx);
740
+
411
741
  // --- zalo_get_messages ---
412
742
  server.registerTool(
413
743
  "zalo_get_messages",
@@ -416,8 +746,10 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
416
746
  description:
417
747
  "Read the new incoming Zalo messages (DMs and groups) from the live buffer, in order. It returns the messages this consumer has not yet marked read; when you have handled them, call zalo_mark_read with the reply's `cursor`. " +
418
748
  "To continue exactly after the last reply, pass its `cursor` as `since`. To wait for the next message instead of polling, use zalo_wait_for_message. " +
419
- "Each message has `msgId` (pass it as `msgId` or `quoteMsgId` to other tools; `id` is the same value, kept for older clients), `threadId`, `threadType` (dm or group), `senderId`, `senderDisplayName` (the current name: your alias, else the Zalo profile name, else the uid; `senderDisplayNameSource` says which), `senderName` (the name when the message was first seen), `text`, `timestamp` (epoch ms), `type`, `threadName` and `readOnZalo`. " +
420
- "`readOnZalo` is true or false when the account owner has or has not read the message on Zalo (phone or Zalo Web), and null when that is unknown; zalo_mark_read does not change it. " +
749
+ "Each message has `msgId` (pass it as `msgId` or `quoteMsgId` to other tools), `threadId`, `threadType` (dm or group), `senderId`, `senderName` (the name to show: your alias for the sender when you set one, else the name the message carried), `senderAlias` (your alias, or null), `senderZaloName` (the name the message carried), `senderDisplayName` (the current name: your alias, else the phonebook name, else the Zalo name, else the display name, else the uid; `senderDisplayNameSource` says which: alias, phonebook, zalo, display or uid), `text`, `timestamp` (epoch ms), `type`, `threadName` and `readOnZalo`. " +
750
+ "`readOnZalo` is true or false when the account owner has or has not read the message on Zalo (on the phone or another Zalo app), and null when that is unknown; zalo_mark_read does not change it. " +
751
+ 'A recall arrives in the same list with `type` "undo": its `msgId` is the recalled message (null when only its `cliMsgId` is known), its `text` is empty, and the earlier message is gone from the cache. ' +
752
+ SYSTEM_EVENTS_NOTE +
421
753
  "Returns `{ messages, cursor, hasMore }`.",
422
754
  inputSchema: z.object({
423
755
  threadId: z
@@ -436,17 +768,21 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
436
768
  .number()
437
769
  .int()
438
770
  .min(1)
439
- .max(100)
440
- .default(maxPerPoll)
441
- .describe("Most messages to return in one call (1 to 100)."),
771
+ .optional()
772
+ .describe("Most messages to return in one call. Omit it for every unread message buffered."),
442
773
  consumer: consumerSchema,
443
774
  }),
444
775
  },
445
776
  async ({ threadId, since, limit, consumer }) => {
446
777
  try {
778
+ if (isHiddenThread(threadId)) return err(HIDDEN_THREAD);
447
779
  // An explicit cursor is honored as given; 0 starts after what this consumer marked read.
448
780
  const from = since > 0 ? since : buffer.readCursor(consumer);
449
781
  const result = buffer.read(threadId, from, limit);
782
+ // A system event (account_state, group_gone, queue_overflow, session_expiring, storage_quota) is passed on as it is.
783
+ const order = result.messages;
784
+ const system = new Map(order.filter((m) => m?.system).map((m) => [m._cursor, m]));
785
+ result.messages = order.filter((m) => !m?.system);
450
786
  // Enrich messages with thread name from cache
451
787
  if (nameCache) {
452
788
  for (const msg of result.messages) {
@@ -475,8 +811,13 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
475
811
  };
476
812
  result.messages = result.messages.map((msg) => ({
477
813
  ...msg,
478
- readOnZalo: readOnZalo(msg.id, watermarkOf(String(msg.threadId))),
814
+ readOnZalo: readOnZalo(msg.msgId, watermarkOf(String(msg.threadId))),
479
815
  }));
816
+ if (system.size) {
817
+ const byCursor = new Map(result.messages.map((m) => [m._cursor, m]));
818
+ result.messages = order.map((m) => system.get(m._cursor) ?? byCursor.get(m._cursor));
819
+ }
820
+ result.messages = eventsView(result.messages);
480
821
  return ok(result);
481
822
  } catch (e) {
482
823
  console.error("[mcp-tools] zalo_get_messages error:", e.message);
@@ -491,12 +832,12 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
491
832
  {
492
833
  title: "Send Zalo Message",
493
834
  description:
494
- "Send a message to a Zalo conversation: text, files or images, a link preview card, or a contact card. Give `threadId` and one kind of content: `text`, `filePath` or `files`, `link`, or `cardUserId`. With files or a link, `text` is an optional caption. " +
495
- 'Text: use real line breaks, never the two characters backslash and n (Zalo shows those as typed). Markdown is converted to Zalo formatting by default; `format: "plain"` sends the text exactly as written. A text over 3000 bytes (a Chinese character is 3) is split into several messages at paragraph or sentence ends, as Zalo Web does, and the reply then lists every part (`splitInto`, `parts`). ' +
835
+ "Send a message to a Zalo conversation: text, files or images, a link preview card, a contact card, a sticker, a voice message or a video from a URL, a bank account card, or a VietQR transfer image. Give `threadId` and one kind of content: `text`, `filePath` or `files`, `link`, `cardUserId`, `sticker`, `voiceUrl`, `video`, `bankCard` or `qrTransfer`. With files, a link, a video or a QR transfer, `text` is an optional caption. " +
836
+ 'Text: use real line breaks, never the two characters backslash and n (Zalo shows those as typed). Markdown is converted to Zalo formatting by default; `format: "plain"` sends the text exactly as written. A text over 3000 bytes (a Chinese, Japanese, Korean, Thai or Hindi character is 3, a Cyrillic or Arabic one 2) is split into several messages at paragraph or sentence ends, as Zalo does, and the reply then lists every part (`splitInto`, `parts`). ' +
496
837
  "Mention in a group: put `@[userId]` in the text where the tag belongs (`@[-1]` is @All). Take the user id from a message's `senderId` (zalo_get_messages, zalo_get_history) or from zalo_get_group_members. It is sent as `@Their Name` and notifies that person; a user id the cache cannot name is tagged as the bare id and listed in `unresolvedMentions`. Mentions only work in groups. " +
497
838
  "Reply to a message: pass its msgId as `quoteMsgId` (text messages only, and the message must be in the local cache). " +
498
839
  '`urgency` marks the message Important or Urgent, as the apps do. `threadId: "me"` sends to the owner\'s My Documents. ' +
499
- "Returns `{ success, msgId, cliMsgId, threadType }` (`messageId` is the same value as `msgId`, kept for older clients) and, when they apply, `parts` and `warnings`. Keep `msgId` and `cliMsgId` if you may need to react to or recall this message (zalo_react, zalo_undo).",
840
+ "Returns `{ success, msgId, cliMsgId, threadType }` and, when they apply, `parts` and `warnings`. Keep `msgId` and `cliMsgId` if you may need to react to or recall this message (zalo_react, zalo_undo).",
500
841
  inputSchema: z
501
842
  .object({
502
843
  threadId: z
@@ -509,7 +850,7 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
509
850
  .min(1)
510
851
  .optional()
511
852
  .describe(
512
- "The message. Real line breaks only: never the two characters backslash and n. Markdown is converted unless `format` is 'plain'. Required unless `filePath`, `files`, `link` or `cardUserId` is given; with those it is the caption. `@[userId]` tokens become @-mentions (`@[-1]` is @All).",
853
+ "The message. Real line breaks only: never the two characters backslash and n. Markdown is converted unless `format` is 'plain'. Required unless another kind of content is given; with files, a link, a video or a QR transfer it is the caption. `@[userId]` tokens become @-mentions (`@[-1]` is @All).",
513
854
  ),
514
855
  filePath: z
515
856
  .string()
@@ -535,14 +876,77 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
535
876
  .min(1)
536
877
  .optional()
537
878
  .describe("The user id whose contact card to share in this conversation."),
879
+ sticker: z
880
+ .string()
881
+ .min(1)
882
+ .max(100)
883
+ .optional()
884
+ .describe(
885
+ "A keyword: sends the first sticker Zalo finds for it (zalo_sticker 'search' lists them).",
886
+ ),
887
+ voiceUrl: z
888
+ .string()
889
+ .url()
890
+ .optional()
891
+ .describe("The URL of an audio file to send as a voice message (Zalo fetches it from there)."),
892
+ video: z
893
+ .object({
894
+ source: z
895
+ .string()
896
+ .min(1)
897
+ .describe(
898
+ "The video: an http(s) URL Zalo fetches, or a local file path on the machine this server runs on (uploaded)",
899
+ ),
900
+ thumbnail: z
901
+ .string()
902
+ .min(1)
903
+ .describe("Its thumbnail: an image URL, or a local jpg/png/webp path (uploaded)"),
904
+ durationMs: z.number().int().min(0).optional().describe("Its length in milliseconds"),
905
+ width: z.number().int().min(1).optional().describe("Its width in pixels (default 1280)"),
906
+ height: z.number().int().min(1).optional().describe("Its height in pixels (default 720)"),
907
+ })
908
+ .strict()
909
+ .optional()
910
+ .describe(
911
+ "A video to send, from a URL or a local file, with its thumbnail. `text` is the caption.",
912
+ ),
913
+ bankCard: z
914
+ .object({
915
+ bank: z
916
+ .string()
917
+ .min(1)
918
+ .describe("The bank: a short name such as vcb, ocb or bidv, or its BIN"),
919
+ accountNumber: z.string().min(1).max(40).describe("The account number"),
920
+ holderName: z.string().min(1).max(100).optional().describe("The account holder's name"),
921
+ })
922
+ .strict()
923
+ .optional()
924
+ .describe("A bank account card (số tài khoản) to send; zalo_bank 'banks' lists the banks."),
925
+ qrTransfer: z
926
+ .object({
927
+ bank: z
928
+ .string()
929
+ .min(1)
930
+ .describe("The bank: a short name such as vcb, ocb or bidv, or its BIN"),
931
+ accountNumber: z.string().min(1).max(40).describe("The account number to pay into"),
932
+ amount: z.number().int().min(1).optional().describe("The amount in VND"),
933
+ content: z.string().max(50).optional().describe("The transfer note, at most 50 characters"),
934
+ template: z
935
+ .enum(["compact", "print", "qronly"])
936
+ .optional()
937
+ .describe("The QR image's style (default compact)"),
938
+ })
939
+ .strict()
940
+ .optional()
941
+ .describe(
942
+ "A VietQR transfer image to generate and send. `text` replaces its default caption " +
943
+ "(bank, account, amount and note).",
944
+ ),
538
945
  threadType: z
539
- .number()
540
- .int()
541
- .min(0)
542
- .max(1)
946
+ .enum(["dm", "group"])
543
947
  .optional()
544
948
  .describe(
545
- "0 = DM, 1 = group. Leave it out: the type stored for this thread is used. Pass it only to override that.",
949
+ 'The conversation type: "dm" or "group". Leave it out: the type stored for this thread is used. Pass it only to override that.',
546
950
  ),
547
951
  quoteMsgId: z
548
952
  .string()
@@ -568,22 +972,19 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
568
972
  "for an ordinary message",
569
973
  ),
570
974
  })
975
+ .strict()
571
976
  .refine(
572
- (val) =>
573
- Boolean(
574
- val.text ||
575
- val.link ||
576
- val.cardUserId ||
577
- val.filePath ||
578
- (Array.isArray(val.files) && val.files.length > 0),
579
- ),
580
- "Provide at least one of text, filePath, files, link, or cardUserId",
977
+ (val) => Boolean(val.text) || sendModes(val).length > 0,
978
+ `Provide text, or one of ${SEND_MODES.join(", ")}`,
979
+ )
980
+ .refine(
981
+ (val) => sendModes(val).length <= 1,
982
+ `Send one kind of content per call: ${SEND_MODES.join(", ")} cannot be combined`,
581
983
  )
582
- .refine((val) => {
583
- const hasFile = Boolean(val.filePath || (Array.isArray(val.files) && val.files.length > 0));
584
- const modes = [Boolean(val.link), Boolean(val.cardUserId), hasFile].filter(Boolean).length;
585
- return modes <= 1;
586
- }, "Cannot combine link, cardUserId, and file attachments in a single call"),
984
+ .refine(
985
+ (val) => !(val.text && ["sticker", "voiceUrl", "bankCard"].includes(sendModes(val)[0])),
986
+ "A sticker, a voice message or a bank card carries no text: send the text as its own message",
987
+ ),
587
988
  },
588
989
  async ({
589
990
  threadId: threadIdIn,
@@ -592,6 +993,11 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
592
993
  files,
593
994
  link,
594
995
  cardUserId,
996
+ sticker,
997
+ voiceUrl,
998
+ video,
999
+ bankCard,
1000
+ qrTransfer,
595
1001
  threadType: threadTypeIn,
596
1002
  quoteMsgId,
597
1003
  urgency: urgencyIn,
@@ -605,7 +1011,118 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
605
1011
  return err(thread.error);
606
1012
  }
607
1013
  const { threadId, threadType } = thread;
1014
+ // A group this account left or lost (src/core/group-exit.js): refused here, not at the server.
1015
+ const goneRefusal = sendRefusal(threadId);
1016
+ if (goneRefusal) return err(goneRefusal);
1017
+ // The reply every non-text send shares: the new message's ids, as a text send reports them.
1018
+ const sentReply = (kind, result, more = {}) => {
1019
+ const msgId = result?.msgId ?? result?.message?.msgId ?? null;
1020
+ const cliMsgId = result?.cliMsgId ?? result?.message?.cliMsgId ?? null;
1021
+ return ok({
1022
+ success: true,
1023
+ kind,
1024
+ ...more,
1025
+ msgId: msgId === null ? null : String(msgId),
1026
+ ...(cliMsgId !== null && { cliMsgId: String(cliMsgId) }),
1027
+ threadType: threadTypeName(threadType),
1028
+ ...(thread.notice && { threadId, notice: thread.notice }),
1029
+ });
1030
+ };
608
1031
  try {
1032
+ // The kinds `msg sticker`, `send-voice`, `send-video`, `send-bank` and `send-qr-transfer` send,
1033
+ // through the same session calls.
1034
+ if (sticker) {
1035
+ const found = (await api.searchSticker(sticker))?.[0];
1036
+ if (!found) return err(`No sticker found for "${sticker}".`);
1037
+ const stickerObj = {
1038
+ id: found.sticker_id || found.stickerId || found.id,
1039
+ cateId: found.cate_id || found.cateId,
1040
+ type: found.type || 7,
1041
+ };
1042
+ return sentReply("sticker", await api.sendSticker(stickerObj, threadId, Number(threadType)), {
1043
+ stickerId: String(stickerObj.id),
1044
+ });
1045
+ }
1046
+ if (voiceUrl) {
1047
+ return sentReply("voice", await api.sendVoice({ voiceUrl, ttl: 0 }, threadId, Number(threadType)));
1048
+ }
1049
+ if (video) {
1050
+ // As `msg send-video`: a URL Zalo fetches, or a local file uploaded on this server's socket; a
1051
+ // thumbnail likewise.
1052
+ const isUrl = (v) => /^https?:\/\//i.test(v);
1053
+ const size = {
1054
+ duration: video.durationMs,
1055
+ width: video.width ?? 1280,
1056
+ height: video.height ?? 720,
1057
+ };
1058
+ if (isUrl(video.source) && isUrl(video.thumbnail)) {
1059
+ const result = await api.sendVideo(
1060
+ { videoUrl: video.source, thumbnailUrl: video.thumbnail, msg: text ?? "", ...size },
1061
+ threadId,
1062
+ Number(threadType),
1063
+ );
1064
+ return sentReply("video", result);
1065
+ }
1066
+ if (isUrl(video.source)) {
1067
+ return err(
1068
+ "A video sent from a URL takes a thumbnail URL too; a local thumbnail goes with a local video.",
1069
+ );
1070
+ }
1071
+ const file = resolve(video.source.trim());
1072
+ const thumb = isUrl(video.thumbnail) ? null : resolve(video.thumbnail.trim());
1073
+ const missing = [file, thumb].filter((p) => p && !existsSync(p));
1074
+ if (missing.length > 0) return err(`File not found: ${missing.join(", ")}`);
1075
+ const result = await api.sendVideoFile(
1076
+ {
1077
+ file,
1078
+ msg: text ?? "",
1079
+ ...(thumb ? { thumbnailFile: thumb } : { thumbnailUrl: video.thumbnail }),
1080
+ ...size,
1081
+ },
1082
+ threadId,
1083
+ Number(threadType),
1084
+ );
1085
+ return sentReply("video", result);
1086
+ }
1087
+ if (bankCard || qrTransfer) {
1088
+ const spec = bankCard || qrTransfer;
1089
+ const bin = resolveBankBin(spec.bank);
1090
+ if (!bin)
1091
+ return err(`Unknown bank: '${spec.bank}'. zalo_bank 'banks' lists the banks Zalo supports.`);
1092
+ if (bankCard) {
1093
+ const payload = { binBank: bin, numAccBank: bankCard.accountNumber };
1094
+ if (bankCard.holderName) payload.nameAccBank = bankCard.holderName;
1095
+ const result = await api.sendBankCard(payload, threadId, Number(threadType));
1096
+ return sentReply("bank_card", result, { bank: BIN_TO_DISPLAY[bin] || String(bin) });
1097
+ }
1098
+ const qrPath = await generateQrTransferImage(
1099
+ bin,
1100
+ qrTransfer.accountNumber,
1101
+ qrTransfer.amount,
1102
+ qrTransfer.content,
1103
+ qrTransfer.template ?? "compact",
1104
+ );
1105
+ if (!qrPath) return err("Could not generate the VietQR image; nothing was sent.");
1106
+ try {
1107
+ const caption = text || qrTransferCaption(bin, qrTransfer);
1108
+ const result = await api.sendMessage(
1109
+ { msg: caption, attachments: [qrPath] },
1110
+ threadId,
1111
+ Number(threadType),
1112
+ );
1113
+ const att = Array.isArray(result?.attachment) ? result.attachment[0] : result?.attachment;
1114
+ return sentReply("qr_transfer", result?.message ?? att ?? result, {
1115
+ bank: BIN_TO_DISPLAY[bin] || String(bin),
1116
+ });
1117
+ } finally {
1118
+ try {
1119
+ unlinkSync(qrPath);
1120
+ } catch {
1121
+ /* a temp file left behind is harmless */
1122
+ }
1123
+ }
1124
+ }
1125
+
609
1126
  if (cardUserId) {
610
1127
  const result = await api.sendCard({ userId: cardUserId }, threadId, Number(threadType));
611
1128
  const messageId = result?.msgId ?? result?.message?.msgId ?? null;
@@ -614,10 +1131,9 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
614
1131
  success: true,
615
1132
  kind: "card",
616
1133
  cardUserId,
617
- messageId,
618
1134
  msgId: messageId,
619
1135
  ...(cliMsgId !== null && { cliMsgId: String(cliMsgId) }),
620
- threadType,
1136
+ threadType: threadTypeName(threadType),
621
1137
  ...(thread.notice && { threadId, notice: thread.notice }),
622
1138
  });
623
1139
  }
@@ -631,10 +1147,9 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
631
1147
  success: true,
632
1148
  kind: "link",
633
1149
  link,
634
- messageId,
635
1150
  msgId: messageId,
636
1151
  ...(cliMsgId !== null && { cliMsgId: String(cliMsgId) }),
637
- threadType,
1152
+ threadType: threadTypeName(threadType),
638
1153
  ...(thread.notice && { threadId, notice: thread.notice }),
639
1154
  });
640
1155
  }
@@ -677,16 +1192,15 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
677
1192
  kind: "attachment",
678
1193
  files: absPaths,
679
1194
  sent,
680
- messageId,
681
1195
  msgId: messageId,
682
1196
  ...(cliMsgId !== null && { cliMsgId: String(cliMsgId) }),
683
- threadType,
1197
+ threadType: threadTypeName(threadType),
684
1198
  ...(thread.notice && { threadId, notice: thread.notice }),
685
1199
  });
686
1200
  }
687
1201
 
688
1202
  if (!text) {
689
- return err("Provide text, filePath, files, link, or cardUserId.");
1203
+ return err(`Provide text, or one of ${SEND_MODES.join(", ")}.`);
690
1204
  }
691
1205
 
692
1206
  const warnings = [];
@@ -712,12 +1226,12 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
712
1226
  const mentions = expanded.mentions;
713
1227
  const styles = shiftStyles(converted.styles, expanded.edits);
714
1228
 
715
- // zca-js drops mentions outside a group, so a DM send would
1229
+ // The session drops mentions outside a group, so a DM send would
716
1230
  // quietly arrive with the names as plain text and nobody tagged.
717
1231
  if (mentions.length > 0 && Number(threadType) !== THREAD_GROUP) {
718
1232
  warnings.push(
719
1233
  "Mentions only apply to group messages. This was sent with threadType " +
720
- `${Number(threadType)}, so the names went out as plain text and nobody was tagged.`,
1234
+ `${threadTypeName(threadType)}, so the names went out as plain text and nobody was tagged.`,
721
1235
  );
722
1236
  }
723
1237
 
@@ -728,12 +1242,13 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
728
1242
  // open reads as "not cached", whose message names the fix.
729
1243
  let row = null;
730
1244
  try {
731
- row = getMessageById(quoteMsgId);
1245
+ row = getMessageForAction(quoteMsgId);
732
1246
  } catch (e) {
1247
+ if (e?.code === "ROW_MAC") return err(e.message);
733
1248
  console.error("[mcp-tools] cache unavailable for quote:", e.message);
734
1249
  }
735
1250
  const built = buildQuote(row, { msgId: quoteMsgId, threadId });
736
- // Reported through err() rather than letting zca-js raise a
1251
+ // Reported through err() rather than letting the session raise a
737
1252
  // raw ZaloApiError: "stickers can't be quoted" and "not
738
1253
  // cached, fetch the thread" are different problems and the
739
1254
  // agent can act on both.
@@ -760,11 +1275,11 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
760
1275
  if (hasLiteralNewline(text)) warnings.push(LITERAL_NEWLINE_WARNING);
761
1276
 
762
1277
  // 1 (important) or 2 (urgent), mapped exactly as `msg send
763
- // --urgency` maps it; zca-js turns it into the `metaData:
764
- // {urgency}` Zalo Web sends. "normal" is no urgency at all.
1278
+ // --urgency` maps it; the session turns it into the `metaData:
1279
+ // {urgency}` Zalo sends. "normal" is no urgency at all.
765
1280
  const urgency = urgencyIn ? urgencyLevel(urgencyIn) : null;
766
1281
 
767
- // Zalo refuses one oversized message (114 plain, 112 styled), so a long text goes out as Web sends
1282
+ // Zalo refuses one oversized message (114 plain, 112 styled), so a long text goes out as Zalo sends
768
1283
  // it: split at 3000 bytes of text plus styles, in order, 300 ms apart. The quote and the urgency
769
1284
  // belong to the first part only.
770
1285
  const parts = splitMessage({ text: expanded.text, styles, mentions });
@@ -795,10 +1310,9 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
795
1310
  const cliMsgId = result?.message?.cliMsgId ?? result?.cliMsgId ?? null;
796
1311
  return ok({
797
1312
  success: true,
798
- messageId,
799
1313
  msgId: messageId,
800
1314
  ...(cliMsgId !== null && { cliMsgId: String(cliMsgId) }),
801
- threadType,
1315
+ threadType: threadTypeName(threadType),
802
1316
  // Only for `me`: the thread it really went to, which is
803
1317
  // what a later zalo_get_history names.
804
1318
  ...(thread.notice && { threadId, notice: thread.notice }),
@@ -815,7 +1329,6 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
815
1329
  const partCli = r?.message?.cliMsgId ?? r?.cliMsgId ?? null;
816
1330
  return {
817
1331
  msgId: partId,
818
- messageId: partId,
819
1332
  ...(partCli !== null && { cliMsgId: String(partCli) }),
820
1333
  };
821
1334
  }),
@@ -848,7 +1361,7 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
848
1361
  async ({ type, consumer }) => {
849
1362
  try {
850
1363
  // Unread is per consumer: after what this consumer marked read.
851
- const stats = buffer.getStats(buffer.readCursor(consumer));
1364
+ const stats = withoutHidden(buffer.getStats(buffer.readCursor(consumer)), hiddenSet(), hiddenMode());
852
1365
  const dmNames = await resolveCurrentUserNames(
853
1366
  api,
854
1367
  stats.filter((t) => buffer.getThreadType(t.threadId) === "dm").map((t) => t.threadId),
@@ -858,6 +1371,7 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
858
1371
  stats.map((t) => String(t.threadId)),
859
1372
  );
860
1373
  // Enrich each stat entry with threadType, thread name and read state
1374
+ const gone = goneGroupMap();
861
1375
  const enriched = stats.map((t) => {
862
1376
  const threadType = buffer.getThreadType(t.threadId) ?? "unknown";
863
1377
  const cached = nameCache?.get(t.threadId);
@@ -867,6 +1381,8 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
867
1381
  name: dmNames.get(String(t.threadId))?.name ?? cached?.name ?? null,
868
1382
  ...(cached?.memberCount !== undefined && { memberCount: cached.memberCount }),
869
1383
  readState: describeReadState(reads.get(String(t.threadId))),
1384
+ // Left, removed, blocked or disbanded (src/core/group-exit.js); sends to it are refused.
1385
+ ...goneFields(gone.get(String(t.threadId)) ?? null),
870
1386
  };
871
1387
  });
872
1388
  const filtered = type === "all" ? enriched : enriched.filter((t) => t.threadType === type);
@@ -884,7 +1400,7 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
884
1400
  {
885
1401
  title: "Search Zalo Threads",
886
1402
  description:
887
- "Find a conversation by name, to get its `threadId`. It is a fuzzy search that ignores case and Vietnamese accents, over the names this server knows (cached contacts and groups). To find a person by phone number or username instead, use zalo_lookup_user. " +
1403
+ "Find a conversation by name, to get its `threadId`. It is a fuzzy search that ignores case and Vietnamese accents, over the names this server knows (cached contacts and groups); every match is returned unless you pass `limit`. To find a person by phone number or username instead, use zalo_lookup_user. " +
888
1404
  "Returns `{ results, total }`; use a result's `threadId` in zalo_send_message, zalo_get_history and the other tools.",
889
1405
  inputSchema: z.object({
890
1406
  query: z.string().min(1).describe("Search keyword (fuzzy match, case-insensitive, accent-insensitive)"),
@@ -892,7 +1408,7 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
892
1408
  .enum(["group", "dm", "all"])
893
1409
  .default("all")
894
1410
  .describe("Filter by thread type: 'dm', 'group', or 'all'"),
895
- limit: z.number().int().min(1).max(50).default(10).describe("Max results to return"),
1411
+ limit: z.number().int().min(1).optional().describe("Max results to return (omit for every match)"),
896
1412
  }),
897
1413
  },
898
1414
  async ({ query, type, limit }) => {
@@ -901,7 +1417,13 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
901
1417
  return err("Thread name cache not initialized yet. Try again shortly.");
902
1418
  }
903
1419
  await nameCache.refreshUsers?.(api);
904
- const results = nameCache.search(query, type, limit);
1420
+ // Over-fetch by the hidden count, so a left-out conversation does not shorten the list.
1421
+ const all = withoutHidden(
1422
+ nameCache.search(query, type, limit ? limit + hiddenIds().length : null),
1423
+ hiddenSet(),
1424
+ hiddenMode(),
1425
+ );
1426
+ const results = limit ? all.slice(0, limit) : all;
905
1427
  return ok({ results, total: results.length });
906
1428
  } catch (e) {
907
1429
  console.error("[mcp-tools] zalo_search_threads error:", e.message);
@@ -945,31 +1467,35 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
945
1467
  {
946
1468
  title: "Get Zalo Message History",
947
1469
  description:
948
- "Get the stored messages of one conversation (DM or group), for context older than the live buffer. It reads the local cache first (everything the listener and `zalo-agent sync` have saved, which can be the full history) and asks the Zalo server only when the cache has nothing for the thread. " +
949
- "Page back with `before` (epoch ms: the `cursor` of the previous reply) or, on the server path, `lastMsgId` (the previous reply's `cursor`). Zalo's server serves only messages since this login: `filtered: true` in the reply means older ones exist that only `zalo-agent sync` can restore, and that needs the owner's phone. " +
950
- "Each message has `msgId`, `senderId`, `senderDisplayName` (the current name: your alias, else the Zalo profile name, else the uid; `senderDisplayNameSource` says which), `senderName` (the name when first seen), `text`, `timestamp` and `type`. Reply: `{ threadId, threadType, count, messages, cursor, hasMore }`. " +
951
- "Start with a small `limit` and page; a large one costs memory and time. To find messages by their text, use zalo_search_messages.",
1470
+ "Get the stored messages of one conversation (DM or group), for context older than the live buffer. It reads the local cache first (everything the listener and `zalo-agent sync` have saved, which can be the full history) and asks the Zalo server only when the cache has nothing for the thread: a group's message store, paged back until it has nothing older. A DM has no server history (Zalo's own apps show 1-1 history from the device's own store), so for a DM only `scan` asks Zalo, by scanning the socket's old-message stream. " +
1471
+ "Without `limit` the reply holds every message (all cached ones, or everything the group's store serves); pass `limit` to page, then page back with `before` (epoch ms: the `cursor` of the previous reply) or, on the server path, `lastMsgId` (the previous reply's `cursor`). Zalo's server serves only messages since this login: `filtered: true` in the reply means older ones exist that only `zalo-agent sync` can restore, and that needs the owner's phone. " +
1472
+ "Each message has `msgId`, `senderId`, `senderName` (your alias for the sender when you set one, else the name the message carried), `senderAlias`, `senderZaloName` (the name the message carried), `senderDisplayName` (the current name: your alias, else the phonebook name, else the Zalo name, else the display name, else the uid; `senderDisplayNameSource` says which), `text`, `timestamp` and `type`. Reply: `{ threadId, threadType, count, messages, cursor, hasMore }`. " +
1473
+ "For one period, pass `since` and/or `until` (epoch ms) or `last` (7d, 2w, 3m, 1y): the cached messages in it come oldest first, all of them, or `limit` per page with `hasMore` and `nextSince` (pass it back as `since`, same `until`) for the rest. Reply: `{ threadId, since, until, count, messages, hasMore, nextSince }`. It reads only the cache. " +
1474
+ "A long conversation is a large reply; pass `limit` to page through it. To find messages by their text, use zalo_search_messages.",
952
1475
  inputSchema: z.object({
953
1476
  threadId: z
954
1477
  .string()
955
1478
  .describe("The conversation: the other person's user id for a DM, the group id for a group."),
956
1479
  threadType: z
957
- .number()
958
- .int()
959
- .min(0)
960
- .max(1)
1480
+ .enum(["dm", "group"])
961
1481
  .optional()
962
1482
  .describe(
963
- "0 = DM, 1 = group. Leave it out: the type stored for this thread is used. " +
964
- "Pass it only to override that.",
1483
+ 'The conversation type: "dm" or "group". Leave it out: the type stored for this thread is used. Pass it only to override that.',
965
1484
  ),
966
1485
  limit: z
967
1486
  .number()
968
1487
  .int()
969
1488
  .min(1)
970
- .max(200)
971
- .default(50)
972
- .describe("Most messages to return (1 to 200; default 50)."),
1489
+ .optional()
1490
+ .describe("Page size: the most messages to return. Leave it out for every message."),
1491
+ scan: z
1492
+ .number()
1493
+ .int()
1494
+ .min(1)
1495
+ .optional()
1496
+ .describe(
1497
+ "Server path only, when the cache has nothing: also scan the socket's old-message stream, at most this many raw messages (a DM, or a group whose store failed). Off unless given.",
1498
+ ),
973
1499
  lastMsgId: z
974
1500
  .string()
975
1501
  .optional()
@@ -983,10 +1509,56 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
983
1509
  .describe(
984
1510
  "Cache path: only messages older than this epoch-ms timestamp (the `cursor` of the previous reply), to page back.",
985
1511
  ),
1512
+ since: z
1513
+ .number()
1514
+ .int()
1515
+ .nonnegative()
1516
+ .optional()
1517
+ .describe(
1518
+ "Only messages at or after this time (epoch ms), oldest first; or the `nextSince` of the previous reply.",
1519
+ ),
1520
+ until: z
1521
+ .number()
1522
+ .int()
1523
+ .positive()
1524
+ .optional()
1525
+ .describe("Only messages at or before this time (epoch ms)."),
1526
+ last: z
1527
+ .string()
1528
+ .optional()
1529
+ .describe("Only the last period, ending now: 7d, 2w, 3m, 1y or 12h (replaces `since`)."),
986
1530
  }),
987
1531
  },
988
- async ({ threadId, threadType: threadTypeIn, limit, lastMsgId, before }) => {
1532
+ async ({ threadId, threadType: threadTypeIn, limit: limitIn, scan, lastMsgId, before, since, until, last }) => {
1533
+ if (isHiddenThread(threadId)) return err(HIDDEN_THREAD);
989
1534
  const threadType = resolveThreadType(threadId, threadTypeIn);
1535
+ // No limit: every message. A limit is a page.
1536
+ const limit = limitIn ?? Infinity;
1537
+ if (since !== undefined || until !== undefined || last !== undefined) {
1538
+ if (before !== undefined || lastMsgId) {
1539
+ return err("Use either a period (`since`, `until`, `last`) or `before`/`lastMsgId`, not both.");
1540
+ }
1541
+ let from;
1542
+ try {
1543
+ from = last !== undefined ? parsePeriod(last) : (since ?? null);
1544
+ } catch (e) {
1545
+ return err(e.message);
1546
+ }
1547
+ const to = until ?? null;
1548
+ if (from !== null && to !== null && from > to) return err("`since` is after `until`.");
1549
+ try {
1550
+ const page = cacheHistoryRange(threadId, { since: from, until: to, limit }, nameCache);
1551
+ page.threadType = threadType === THREAD_USER ? "dm" : "group";
1552
+ page.messages = await withCurrentSenderNames(
1553
+ api,
1554
+ page.messages,
1555
+ threadType === THREAD_USER ? [threadId] : [],
1556
+ );
1557
+ return ok(page);
1558
+ } catch (e) {
1559
+ return err(`The local cache could not be read: ${e.message}`);
1560
+ }
1561
+ }
990
1562
  try {
991
1563
  // The cache is the better source and usually the only one that
992
1564
  // answers: Zalo returns an empty set for the socket history
@@ -1021,7 +1593,9 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1021
1593
  let fetched;
1022
1594
  try {
1023
1595
  fetched = await fetchAndCacheHistory(api, threadId, threadType, {
1024
- limit,
1596
+ limit: Number.isFinite(limit) ? limit : null,
1597
+ // The socket scan is opt-in, as `msg history --scan`.
1598
+ ...(scan && { scanLimit: scan }),
1025
1599
  fromMsgId: lastMsgId || null,
1026
1600
  });
1027
1601
  } finally {
@@ -1034,12 +1608,14 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1034
1608
  const isText = typeof rawContent === "string";
1035
1609
  return {
1036
1610
  msgId: d.msgId,
1611
+ cliMsgId: d.cliMsgId ? String(d.cliMsgId) : undefined,
1037
1612
  threadId: f.threadId,
1038
1613
  senderId: d.uidFrom || null,
1039
- senderName: d.dName || null,
1614
+ ...senderNameFields(d.uidFrom, d.dName),
1040
1615
  text: isText ? rawContent : extractMessageText(rawContent, d.msgType),
1041
1616
  timestamp: d.ts ? Number(d.ts) : null,
1042
1617
  type: isText ? "text" : d.msgType || "attachment",
1618
+ replyTo: resolveReplyTo(quotedReply(d.quote), f.threadId),
1043
1619
  };
1044
1620
  });
1045
1621
 
@@ -1070,14 +1646,21 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1070
1646
  cursor,
1071
1647
  hasMore: Boolean(fetched.more) || (fetched.source === "socket" && allMessages.length >= limit),
1072
1648
  filtered: fetched.filtered === true,
1073
- ...(fetched.filtered || allMessages.length === 0
1649
+ ...(fetched.source === "none" && threadType === THREAD_USER
1074
1650
  ? {
1075
1651
  note:
1076
- "Zalo serves a conversation's messages only since this login; older ones are " +
1077
- "withheld. `zalo-agent sync` restores them from the owner's phone (it asks for a " +
1078
- "tap), and zalo_get_history then reads them from the cache.",
1652
+ "Nothing is cached for this conversation, and Zalo keeps no server history for a " +
1653
+ "1-1 conversation. `scan` asks the socket's old-message stream; `zalo-agent sync` " +
1654
+ "restores from the owner's phone (it asks for a tap).",
1079
1655
  }
1080
- : {}),
1656
+ : fetched.filtered || allMessages.length === 0
1657
+ ? {
1658
+ note:
1659
+ "Zalo serves a conversation's messages only since this login; older ones are " +
1660
+ "withheld. `zalo-agent sync` restores them from the owner's phone (it asks for a " +
1661
+ "tap), and zalo_get_history then reads them from the cache.",
1662
+ }
1663
+ : {}),
1081
1664
  });
1082
1665
  } catch (e) {
1083
1666
  console.error("[mcp-tools] zalo_get_history error:", e.message);
@@ -1088,29 +1671,40 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1088
1671
 
1089
1672
  // --- zalo_view_media ---
1090
1673
  const mediaConfig = config.media || {};
1674
+ /** The account the cached media is sealed for (its folder holds the keyring). */
1675
+ const mediaScope = () => scopeOfDir(accountDir || getAccountDir(String(ownIdOf(api) ?? "")));
1091
1676
  server.registerTool(
1092
1677
  "zalo_view_media",
1093
1678
  {
1094
1679
  title: "View Zalo Media",
1095
1680
  description:
1096
- "Get a received photo, voice message, video or file as a local file, and by default open it with the viewer of the machine this server runs on. Media is downloaded when a message arrives; if the file is not on disk yet it is downloaded first. " +
1097
- "Give the message's `msgId`. Returns `{ success, path, mediaType }`: `path` is where the file is on the server's machine. Pass `open: false` when you only need the path (for example on a remote or headless server).",
1681
+ "Get a received photo, sticker, voice message, video or file. The cached file is encrypted; it reaches you one of three ways (`deliver`): " +
1682
+ '"inline" (the picture or voice itself, as image or audio content, up to about 5 MB), "link" (`url`, a link to this server that works for 10 minutes; ' +
1683
+ 'it may need the same Authorization as this server), or "tempfile" (`path`, a private decrypted copy on the machine this server runs on, deleted at `expiresAt`; only for a client on that machine). ' +
1684
+ "By default pictures and voice come inline and videos and files as a link, as the server is configured. Media not downloaded yet is downloaded first. " +
1685
+ "Returns `{ success, msgId, mediaType, delivery, mimeType, size }` plus `url`/`path` and `expiresAt`, and `notes` when the delivery differs from the one asked for.",
1098
1686
  inputSchema: z.object({
1099
1687
  msgId: z
1100
1688
  .string()
1101
1689
  .describe(
1102
- "msgId of a message that has a photo, voice message, video or file (from zalo_get_messages, zalo_wait_for_message or zalo_get_history).",
1690
+ "msgId of a message that has a photo, sticker, voice message, video or file (from zalo_get_messages, zalo_wait_for_message or zalo_get_history).",
1103
1691
  ),
1104
1692
  threadId: z.string().optional().describe("Only look in this conversation. Omit to search all of them."),
1693
+ deliver: z
1694
+ .enum(["inline", "link", "tempfile"])
1695
+ .optional()
1696
+ .describe(
1697
+ 'How to get it: "inline", "link" or "tempfile". Omit for the server\'s default for this kind of media. The server may forbid a mode (media.allowDeliver).',
1698
+ ),
1105
1699
  open: z
1106
1700
  .boolean()
1107
- .default(mediaConfig.autoOpen ?? true)
1701
+ .optional()
1108
1702
  .describe(
1109
- "Also open the file with the viewer of the machine this server runs on. Pass false when you only need the path.",
1703
+ "Also open it with the viewer of the machine this server runs on (a local stdio server only; it delivers a tempfile). Omit for the server's default (media.autoOpen).",
1110
1704
  ),
1111
1705
  }),
1112
1706
  },
1113
- async ({ msgId, threadId, open }) => {
1707
+ async ({ msgId, threadId, deliver, open }) => {
1114
1708
  try {
1115
1709
  // The cached row is authoritative: it records where a fetched
1116
1710
  // file actually landed, so a message that arrived before this
@@ -1118,12 +1712,64 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1118
1712
  // fallback for a thread id.
1119
1713
  let row = null;
1120
1714
  try {
1121
- row = getMessageById(msgId);
1715
+ row = getMessageForAction(msgId);
1122
1716
  } catch (e) {
1717
+ if (e?.code === "ROW_MAC") return err(e.message);
1123
1718
  console.error("[mcp-tools] cache unavailable:", e.message);
1124
1719
  }
1125
- const buffered = buffer.read(threadId, 0, 9999).messages.find((m) => m.id === msgId) || null;
1720
+ const buffered = buffer.read(threadId, 0).messages.find((m) => m.msgId === msgId) || null;
1126
1721
  if (!row && !buffered) return err(`Message ${msgId} not found in cache or buffer`);
1722
+ const kind = String(row?.type || buffered?.type || "");
1723
+ const msgThread = row?.threadId ?? buffered?.threadId ?? threadId ?? null;
1724
+ // A hidden chat's media is left out like its messages: no inline content, no link, no copy.
1725
+ if (isHiddenThread(msgThread)) return err(HIDDEN_THREAD);
1726
+
1727
+ const accountId = String(ownIdOf(api) ?? "");
1728
+ const policy = resolveMediaDelivery({ config, accountId: accountId || undefined });
1729
+ const stdio = server?.statelessHttp !== true;
1730
+ const choose = (size, mime) =>
1731
+ chooseDelivery({
1732
+ kind,
1733
+ size,
1734
+ mime,
1735
+ requested: deliver ?? null,
1736
+ open,
1737
+ policy,
1738
+ stdio,
1739
+ clientLoopback: server?.clientLoopback === true,
1740
+ });
1741
+
1742
+ // A sticker is not downloaded with the messages: its picture is fetched now, shown inline and not kept.
1743
+ if (kind === "sticker" && !row?.localPath) {
1744
+ const sticker = await stickerPicture(api, row, buffered);
1745
+ if (sticker.error) return err(sticker.error);
1746
+ const pick = choose(sticker.bytes.length, sticker.mime);
1747
+ if (pick.error) return err(pick.error);
1748
+ if (pick.mode !== "inline") {
1749
+ return err(
1750
+ "A sticker is not stored on this server, so it can only be sent inline" +
1751
+ (pick.notes.length ? ` (${pick.notes.join("; ")})` : "") +
1752
+ ".",
1753
+ );
1754
+ }
1755
+ return {
1756
+ content: [
1757
+ {
1758
+ type: "text",
1759
+ text: JSON.stringify({
1760
+ success: true,
1761
+ msgId,
1762
+ mediaType: kind,
1763
+ delivery: "inline",
1764
+ mimeType: sticker.mime,
1765
+ size: sticker.bytes.length,
1766
+ }),
1767
+ },
1768
+ inlineContent(sticker.bytes, kind, sticker.mime),
1769
+ ],
1770
+ };
1771
+ }
1772
+
1127
1773
  if (row && !row.has_attachment && !buffered?.attachment?.url) {
1128
1774
  return err(`Message ${msgId} has no media attachment`);
1129
1775
  }
@@ -1131,13 +1777,15 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1131
1777
  let localPath = row?.localPath || null;
1132
1778
  if (!localPath) {
1133
1779
  // Same downloader as the CLI, so the file lands in the one
1134
- // per-conversation folder every other command reads from.
1780
+ // per-conversation folder every other command reads from. It is asked for this message by
1781
+ // id: a scan of the thread's newest pending attachments could leave an older one out.
1135
1782
  const stats = await downloadSyncedMedia({
1136
1783
  api,
1137
1784
  accountDir,
1138
- threadId: String(row?.threadId || buffered?.threadId || threadId || ""),
1139
- limit: 50,
1140
- concurrency: 2,
1785
+ threadId: String(msgThread || ""),
1786
+ msgIds: [String(row?.msgId ?? msgId)],
1787
+ limit: 1,
1788
+ concurrency: 1,
1141
1789
  mediaRoot: process.env.ZALO_MEDIA_DIR || mediaConfig.downloadDir || undefined,
1142
1790
  });
1143
1791
  localPath = getMessageById(msgId)?.localPath || null;
@@ -1152,9 +1800,42 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1152
1800
  }
1153
1801
  }
1154
1802
 
1155
- if (open) openFile(localPath);
1156
-
1157
- return ok({ success: true, path: localPath, mediaType: row?.type || buffered?.type || null });
1803
+ // The cached file stays sealed (keystore/media-seal.js): it is delivered inline, as a link served from
1804
+ // the sealed file, or as a private decrypted copy (src/core/media-deliver.js).
1805
+ const info = mediaInfo(localPath);
1806
+ const pick = choose(info.size, info.mime);
1807
+ if (pick.error) return err(pick.error);
1808
+ const out = await deliverCachedMedia({
1809
+ mode: pick.mode,
1810
+ file: localPath,
1811
+ scope: mediaScope(),
1812
+ msgId: row?.msgId ?? msgId,
1813
+ threadId: msgThread,
1814
+ accountId,
1815
+ info,
1816
+ policy,
1817
+ ensureServed: ensureMediaServed,
1818
+ });
1819
+ if (pick.open && out.path) openFile(out.path);
1820
+ const reply = {
1821
+ success: true,
1822
+ msgId,
1823
+ mediaType: kind || null,
1824
+ delivery: out.mode,
1825
+ mimeType: out.mimeType,
1826
+ size: out.size,
1827
+ ...(out.url ? { url: out.url, auth: out.auth } : {}),
1828
+ ...(out.path ? { path: out.path, opened: pick.open } : {}),
1829
+ ...(out.expiresAt ? { expiresAt: new Date(out.expiresAt).toISOString() } : {}),
1830
+ ...(pick.notes.length ? { notes: pick.notes } : {}),
1831
+ };
1832
+ if (out.mode !== "inline") return ok(reply);
1833
+ return {
1834
+ content: [
1835
+ { type: "text", text: JSON.stringify(reply) },
1836
+ inlineContent(out.bytes, kind, out.mimeType),
1837
+ ],
1838
+ };
1158
1839
  } catch (e) {
1159
1840
  console.error("[mcp-tools] zalo_view_media error:", e.message);
1160
1841
  return err(e.message);
@@ -1164,14 +1845,10 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1164
1845
 
1165
1846
  // The threadType parameter of the message actions, worded as the send's is.
1166
1847
  const threadTypeSchema = z
1167
- .number()
1168
- .int()
1169
- .min(0)
1170
- .max(1)
1848
+ .enum(["dm", "group"])
1171
1849
  .optional()
1172
1850
  .describe(
1173
- "0 = DM, 1 = group. Leave it out: the type stored for this thread is used. " +
1174
- "Pass it only to override that.",
1851
+ 'The conversation type: "dm" or "group". Leave it out: the type stored for this thread is used. Pass it only to override that.',
1175
1852
  );
1176
1853
  const cliMsgIdSchema = z
1177
1854
  .string()
@@ -1238,7 +1915,7 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1238
1915
  msgId,
1239
1916
  cliMsgId: target.cliMsgId,
1240
1917
  threadId,
1241
- threadType,
1918
+ threadType: threadTypeName(threadType),
1242
1919
  ...(thread.notice && { notice: thread.notice }),
1243
1920
  });
1244
1921
  } catch (e) {
@@ -1254,7 +1931,7 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1254
1931
  {
1255
1932
  title: "Recall a Zalo Message",
1256
1933
  description:
1257
- 'Recall (unsend) one of your own messages for everyone in the conversation, as the apps\' "Thu hồi" does. Zalo accepts a recall only for a while after the message was sent. ' +
1934
+ 'Recall (unsend) one of your own messages for everyone in the conversation, as the apps\' "Thu hồi" does. Zalo accepts a recall only for a while after the message was sent. It cannot be undone, so it is asked about first (`confirm`, `dryRun`). ' +
1258
1935
  "Give the message's `msgId` (from the zalo_send_message reply, or from zalo_get_history) and its `cliMsgId` (also in the send reply; read from the local cache when you leave it out); with neither, nothing is sent. To remove a message from your own view only, or someone else's message in a group as an admin, use zalo_delete_message. `threadId` may be `me` for My Documents.",
1259
1936
  inputSchema: z.object({
1260
1937
  msgId: z
@@ -1271,9 +1948,12 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1271
1948
  ),
1272
1949
  threadType: threadTypeSchema,
1273
1950
  cliMsgId: cliMsgIdSchema,
1951
+ confirm: confirmInput("the recall"),
1952
+ ticket: ticketInput(),
1953
+ dryRun: dryRunInput("the recall"),
1274
1954
  }),
1275
1955
  },
1276
- async ({ msgId, threadId: threadIdIn, threadType: threadTypeIn, cliMsgId }) => {
1956
+ async ({ msgId, threadId: threadIdIn, threadType: threadTypeIn, cliMsgId, confirm, dryRun, ticket }, extra) => {
1277
1957
  const thread = actionThread(api, threadIdIn, threadTypeIn);
1278
1958
  if (thread.error) {
1279
1959
  console.error("[mcp-tools] zalo_undo refused:", thread.error);
@@ -1287,13 +1967,30 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1287
1967
  console.error("[mcp-tools] zalo_undo refused:", target.error);
1288
1968
  return err(target.error);
1289
1969
  }
1290
- await api.undo({ msgId, cliMsgId: target.cliMsgId }, threadId, Number(threadType));
1970
+ const held = await gate(
1971
+ "msg.undo",
1972
+ { msgId, threadId, cliMsgId: target.cliMsgId },
1973
+ { confirm, dryRun, ticket },
1974
+ extra,
1975
+ );
1976
+ if (held) return held;
1977
+ // A group recall carries the group's own visibility, from the thread cache or Zalo, as `msg undo`.
1978
+ const payload = await undoPayload(
1979
+ api,
1980
+ { msgId, cliMsgId: target.cliMsgId },
1981
+ threadId,
1982
+ Number(threadType),
1983
+ {
1984
+ cache: nameCache,
1985
+ },
1986
+ );
1987
+ await api.undo(payload, threadId, Number(threadType));
1291
1988
  return ok({
1292
1989
  success: true,
1293
1990
  msgId,
1294
1991
  cliMsgId: target.cliMsgId,
1295
1992
  threadId,
1296
- threadType,
1993
+ threadType: threadTypeName(threadType),
1297
1994
  ...(thread.notice && { notice: thread.notice }),
1298
1995
  });
1299
1996
  } catch (e) {
@@ -1309,7 +2006,7 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1309
2006
  {
1310
2007
  title: "List Zalo Group Members",
1311
2008
  description:
1312
- "List the members of a group: each member's `userId` (`uid` is the same value, kept for older clients) and `displayName` (from the cache, or from Zalo for members the cache cannot name), and `totalMember`, Zalo's own count (it can be larger than the members listed). Use a `userId` as `@[userId]` in zalo_send_message to mention that member.",
2009
+ "List the members of a group: each member's `userId`, `displayName` (your alias for them when you set one, else their name from the cache, or from Zalo for members the cache cannot name), `alias` and `zaloName`, and `totalMember`, Zalo's own count (it can be larger than the members listed). Use a `userId` as `@[userId]` in zalo_send_message to mention that member.",
1313
2010
  inputSchema: z.object({
1314
2011
  groupId: z
1315
2012
  .string()
@@ -1330,12 +2027,13 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1330
2027
  // The cache first, as a mention is named; one batched lookup
1331
2028
  // (the one `msg send` uses) for the members it cannot name.
1332
2029
  // Nothing is written back: zalo.db has three writers.
2030
+ // `names` holds the name each member goes by on Zalo; the owner's alias is applied below.
1333
2031
  const names = new Map();
1334
2032
  const unnamed = [];
1335
2033
  for (const uid of group.uids) {
1336
- const name = cachedDisplayName(uid);
2034
+ const name = cachedStoredName(uid);
1337
2035
  if (name) names.set(uid, name);
1338
- else unnamed.push(uid);
2036
+ else if (!aliasFor(uid)) unnamed.push(uid);
1339
2037
  }
1340
2038
  const warnings = [];
1341
2039
  if (unnamed.length > 0) {
@@ -1348,7 +2046,12 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1348
2046
  }
1349
2047
  for (const w of warnings) console.error("[mcp-tools] zalo_get_group_members warning:", w);
1350
2048
 
1351
- const members = group.uids.map((uid) => ({ userId: uid, uid, displayName: names.get(uid) ?? null }));
2049
+ // `displayName` alias first, as Zalo's member list shows them; `alias` and `zaloName` beside it.
2050
+ const members = group.uids.map((uid) => {
2051
+ const alias = aliasFor(uid);
2052
+ const zaloName = names.get(uid) ?? null;
2053
+ return { userId: uid, displayName: alias ?? zaloName, alias, zaloName };
2054
+ });
1352
2055
  return ok({
1353
2056
  groupId,
1354
2057
  name: group.name,
@@ -1371,18 +2074,18 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1371
2074
  title: "List Recent Zalo Conversations",
1372
2075
  description:
1373
2076
  "List the conversations this account was most recently active in, newest first, from the local cache, to get a `threadId` or to see what is current. Unlike zalo_list_threads it is not limited to messages that arrived since this server started. " +
1374
- "Each has its thread id, type, name and last activity, plus `readState`: how far the account owner has read it on Zalo (on any device), meaning the newest message read, how many cached messages from others came after it, and whether it is marked unread (null when unknown). `limit` is per type: with `type` 'all' that is up to `limit` DMs and `limit` groups.",
2077
+ "Each has its `threadId`, `threadType`, `name` and last activity, plus `readState`: how far the account owner has read it on Zalo (on any device), meaning the newest message read, how many cached messages from others came after it, and whether it is marked unread (null when unknown). Every cached conversation is returned unless you pass `limit`, which is per type: with `type` 'all' that is up to `limit` DMs and `limit` groups.",
1375
2078
  inputSchema: z.object({
1376
2079
  type: z
1377
2080
  .enum(["group", "dm", "all"])
1378
2081
  .default("all")
1379
2082
  .describe("Filter by thread type: 'dm', 'group', or 'all'"),
1380
- limit: z.number().int().min(1).max(200).default(20).describe("Max conversations of each type"),
2083
+ limit: z.number().int().min(1).optional().describe("Max conversations of each type (omit for all)"),
1381
2084
  }),
1382
2085
  },
1383
2086
  async ({ type, limit }) => {
1384
2087
  try {
1385
- const threads = recentConversations(limit, type);
2088
+ const threads = recentConversations(limit, type, { excludeThreadIds: hiddenIds() });
1386
2089
  const dmNames = await resolveCurrentUserNames(
1387
2090
  api,
1388
2091
  threads.filter((t) => t.type !== "group").map((t) => String(t.threadId)),
@@ -1396,8 +2099,7 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1396
2099
  const ts = Number(t.lastUpdate) || null;
1397
2100
  return {
1398
2101
  threadId: String(t.threadId),
1399
- type: isGroup ? "group" : "dm",
1400
- threadType: isGroup ? THREAD_GROUP : THREAD_USER,
2102
+ threadType: isGroup ? "group" : "dm",
1401
2103
  name:
1402
2104
  dmNames.get(String(t.threadId))?.name ||
1403
2105
  t.name ||
@@ -1406,6 +2108,8 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1406
2108
  lastActivity: ts,
1407
2109
  lastActivityAt: ts ? new Date(ts).toISOString() : null,
1408
2110
  readState: describeReadState(reads.get(String(t.threadId))),
2111
+ // A group this account left or lost, kept by groups.onLeave "keep" (src/core/group-exit.js).
2112
+ ...(t.gone && { gone: true, leftAt: t.leftAt, goneReason: t.goneReason }),
1409
2113
  };
1410
2114
  });
1411
2115
  return ok({
@@ -1450,7 +2154,7 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1450
2154
  {
1451
2155
  title: "Request Zalo Sync",
1452
2156
  description:
1453
- "OWNER ONLY: call it only when the owner explicitly asks. Start a restore of messages (or of the reaction backlog) on this server's existing Zalo connection. The `messages` stage makes the owner's phone ask for a confirmation, which the owner must tap. " +
2157
+ "OWNER ONLY: call it only when the owner explicitly asks. Start a restore of messages, or another sync stage (the reaction backlog, the zCloud index, attachment downloads, group boards), on this server's existing Zalo connection. The `messages` stage makes the owner's phone ask for a confirmation, which the owner must tap; the other stages do not. " +
1454
2158
  "It returns a job at once: poll zalo_sync_status every few seconds. Reuse the same `requestId` and arguments when retrying, so a retry does not start a second sync. It updates the local cache, not Zalo itself. `messages` restores every conversation in the window; it is not per conversation, and it downloads no media, boards, cloud index or conversation state.",
1455
2159
  inputSchema: z
1456
2160
  .object({
@@ -1463,10 +2167,10 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1463
2167
  "Unique request key; reuse the same key and arguments after a timeout to avoid duplicate sync.",
1464
2168
  ),
1465
2169
  stage: z
1466
- .enum(["messages", "reactions"])
2170
+ .enum(SYNC_STAGES)
1467
2171
  .default("messages")
1468
2172
  .describe(
1469
- "`messages` restores chat history from the owner's phone (the phone asks for a confirmation); `reactions` drains Zalo's backlog of reactions (no phone tap).",
2173
+ "`messages` restores chat history from the owner's phone (the phone asks for a confirmation); `reactions` drains Zalo's backlog of reactions; `cloud` walks the zCloud index (zalo_cloud reads it); `media` downloads the attachments a restore recorded; `boards` stores notes, pinned messages, polls and reminders. Only `messages` taps the phone.",
1470
2174
  ),
1471
2175
  days: z
1472
2176
  .number()
@@ -1474,7 +2178,34 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1474
2178
  .min(1)
1475
2179
  .max(36500)
1476
2180
  .optional()
1477
- .describe("Restore the last N days; omit both days and from for full phone history."),
2181
+ .describe(
2182
+ "Messages: restore the last N days (omit both days and from for full phone history). Media: only attachments from the last N days (default: the window the last restore covered).",
2183
+ ),
2184
+ threadId: z
2185
+ .string()
2186
+ .min(1)
2187
+ .optional()
2188
+ .describe("Media and boards only: just this conversation (default: all cached ones)."),
2189
+ kinds: z
2190
+ .array(z.enum([...DOWNLOADABLE_KINDS]))
2191
+ .min(1)
2192
+ .optional()
2193
+ .describe("Media only: just these kinds of attachment (default: all)."),
2194
+ limit: z
2195
+ .number()
2196
+ .int()
2197
+ .min(1)
2198
+ .max(10000)
2199
+ .optional()
2200
+ .describe(
2201
+ "Media: download at most this many files (the rest wait for the next run). Boards: visit at most this many threads, most recent first (default: every thread).",
2202
+ ),
2203
+ full: z
2204
+ .boolean()
2205
+ .optional()
2206
+ .describe(
2207
+ "Cloud only: walk from the top and drop every indexed item no page lists (corrects a stale index).",
2208
+ ),
1478
2209
  from: z
1479
2210
  .string()
1480
2211
  .regex(/^\d{4}-\d{2}-\d{2}$/)
@@ -1495,20 +2226,48 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1495
2226
  .boolean()
1496
2227
  .optional()
1497
2228
  .describe("Reactions only: apply observed removals; defaults to true."),
2229
+ fullResync: z
2230
+ .boolean()
2231
+ .optional()
2232
+ .describe(
2233
+ "Messages only: ask the phone for every conversation in full, ignoring what earlier runs " +
2234
+ "settled. A long run; use it when the cache looks incomplete, not by default.",
2235
+ ),
2236
+ shardSize: z
2237
+ .number()
2238
+ .int()
2239
+ .min(1)
2240
+ .max(30)
2241
+ .optional()
2242
+ .describe(
2243
+ "Messages only: conversations per batch (1 to 30, default 30). Fewer means more, smaller " +
2244
+ "batches: the first comes back sooner and less is lost if the connection drops.",
2245
+ ),
1498
2246
  })
1499
2247
  .strict()
1500
2248
  .refine(
1501
2249
  (value) => !(value.days !== undefined && value.from !== undefined),
1502
2250
  "Choose days or from, not both",
1503
2251
  )
1504
- .refine(
1505
- (value) => value.stage !== "reactions" || (value.days === undefined && value.from === undefined),
1506
- "Reactions sync has no date window",
1507
- )
1508
- .refine(
1509
- (value) => value.stage !== "messages" || value.applyRemovals === undefined,
1510
- "applyRemovals is reactions-only",
1511
- ),
2252
+ .refine((value) => {
2253
+ // Each option belongs to the stages named in its description; any other stage refuses it.
2254
+ const only = {
2255
+ days: ["messages", "media"],
2256
+ from: ["messages"],
2257
+ waitMs: ["messages", "reactions"],
2258
+ applyRemovals: ["reactions"],
2259
+ fullResync: ["messages"],
2260
+ shardSize: ["messages"],
2261
+ threadId: ["media", "boards"],
2262
+ kinds: ["media"],
2263
+ limit: ["media", "boards"],
2264
+ full: ["cloud"],
2265
+ };
2266
+ const stage = value.stage ?? "messages";
2267
+ return Object.entries(only).every(
2268
+ ([k, stages]) => value[k] === undefined || stages.includes(stage),
2269
+ );
2270
+ }, "An option was given for a stage it does not apply to: each option's description names its stages"),
1512
2271
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
1513
2272
  },
1514
2273
  async (args) => {
@@ -1554,10 +2313,16 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1554
2313
  {
1555
2314
  title: "Search Zalo Messages",
1556
2315
  description:
1557
- "Search the text of messages in the local cache (`zalo.db`), across all conversations or one. No network call. It is a case-insensitive substring match. " +
1558
- "Narrow it with `threadId`, `senderId`, `since` or `until` (epoch ms). Results are newest first.",
2316
+ "Search the text of messages in the local cache, across all conversations or one. No network call. Accents and case are ignored, as in Zalo's own search ('duoc' finds 'được'; set `accents` to 'exact' to require them). " +
2317
+ "Narrow it with `threadId`, `senderId`, `since` / `until` (epoch ms) or `last` (7d, 2w, 3m, 1y), and `types` (text, photo, video, file, link, sticker...; files and links match on their titles). Results are newest first, every hit unless you pass `limit`; with a `limit`, pass the reply's `nextCursor` back as `cursor` for the next page. " +
2318
+ "Each message carries a `snippet` (70 characters before the match, 30 after, with its position); `context` adds that many messages around each hit. `counts` gives the matches by type, and `coverage` says how far back the cache reaches and which `sync` command extends it.",
1559
2319
  inputSchema: z.object({
1560
- query: z.string().min(1).describe("Case-insensitive text substring to search for in cached messages"),
2320
+ query: z
2321
+ .string()
2322
+ .min(1)
2323
+ .describe(
2324
+ "Text to search for in cached messages (cut to 100 characters; accents and case ignored)",
2325
+ ),
1561
2326
  threadId: z
1562
2327
  .string()
1563
2328
  .min(1)
@@ -1580,21 +2345,64 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1580
2345
  .positive()
1581
2346
  .optional()
1582
2347
  .describe("Only messages with timestamp <= this epoch-ms value"),
2348
+ last: z
2349
+ .string()
2350
+ .regex(/^\d{1,4}\s*[hdwmyHDWMY]$/)
2351
+ .optional()
2352
+ .describe("Only the last period: 7d, 2w, 3m, 1y (replaces `since`)"),
2353
+ types: z
2354
+ .array(z.string().min(1))
2355
+ .max(12)
2356
+ .optional()
2357
+ .describe("Only these message types, e.g. ['file','link']; 'media' means photo and video"),
2358
+ accents: z
2359
+ .enum(["fold", "exact"])
2360
+ .default("fold")
2361
+ .describe(
2362
+ "fold (default): accents and case ignored; exact: accents must match, case still does not",
2363
+ ),
1583
2364
  limit: z
1584
2365
  .number()
1585
2366
  .int()
1586
2367
  .min(1)
1587
- .max(200)
1588
- .default(50)
1589
- .describe("Max matching messages to return (default 50, max 200)"),
2368
+ .optional()
2369
+ .describe("Max matching messages per page (omit for every hit, in one page)"),
2370
+ cursor: z.string().min(1).optional().describe("The nextCursor of the previous page, unchanged"),
2371
+ context: z
2372
+ .number()
2373
+ .int()
2374
+ .min(0)
2375
+ .max(10)
2376
+ .default(0)
2377
+ .describe("Messages to show before and after each hit (default 0, max 10)"),
1590
2378
  }),
1591
2379
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
1592
2380
  },
1593
- async ({ query, threadId, senderId, since, until, limit }) => {
2381
+ async ({ query, threadId, senderId, since, until, last, types, accents, limit, cursor, context }) => {
1594
2382
  try {
1595
- const rows = searchMessages({ query, threadId, senderId, since, until, limit });
2383
+ const page = searchPage({
2384
+ query,
2385
+ accents,
2386
+ threadId,
2387
+ senderId,
2388
+ since: last ? parsePeriod(last) : since,
2389
+ until,
2390
+ types,
2391
+ limit,
2392
+ cursor,
2393
+ context,
2394
+ excludeThreadIds: hiddenIds(),
2395
+ });
1596
2396
  const threadNames = getThreadNames();
1597
- const messages = rows.map((r) => {
2397
+ const lite = (m) => ({
2398
+ msgId: m.msgId,
2399
+ senderId: m.senderId,
2400
+ ...senderNameFields(m.senderId, m.senderName),
2401
+ text: m.text,
2402
+ timestamp: m.timestamp,
2403
+ type: m.type,
2404
+ });
2405
+ const messages = page.hits.map(({ row: r, snippet, context: around }) => {
1598
2406
  let cliMsgId = null;
1599
2407
  try {
1600
2408
  cliMsgId = cachedCliMsgId(r);
@@ -1608,15 +2416,29 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1608
2416
  threadName:
1609
2417
  threadNames.get(String(r.threadId))?.name ?? nameCache?.get(r.threadId)?.name ?? null,
1610
2418
  senderId: r.senderId,
1611
- senderName: r.senderDisplayName || r.senderName || null,
2419
+ // Alias first (src/core/contact-names.js); `senderZaloName` is the name the message carried.
2420
+ ...senderNameFields(r.senderId, r.senderName),
1612
2421
  text: r.text,
2422
+ snippet,
1613
2423
  timestamp: r.timestamp,
1614
2424
  type: r.type,
1615
2425
  hasAttachment: Boolean(r.has_attachment),
1616
- localPath: r.localPath || null,
2426
+ mediaCached: Boolean(r.localPath),
2427
+ replyTo: cachedReplyTo(r),
2428
+ ...(around && { context: { before: around.before.map(lite), after: around.after.map(lite) } }),
1617
2429
  };
1618
2430
  });
1619
- return ok({ query, count: messages.length, messages });
2431
+ return ok({
2432
+ query: page.query,
2433
+ accents: page.accents,
2434
+ ...(page.truncatedKeyword && { truncatedKeyword: true }),
2435
+ count: messages.length,
2436
+ hasMore: page.hasMore,
2437
+ nextCursor: page.nextCursor,
2438
+ ...(page.counts && { counts: page.counts }),
2439
+ coverage: page.coverage,
2440
+ messages,
2441
+ });
1620
2442
  } catch (e) {
1621
2443
  console.error("[mcp-tools] zalo_search_messages error:", e.message);
1622
2444
  return err(e.message);
@@ -1630,7 +2452,8 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1630
2452
  {
1631
2453
  title: "Lookup Zalo User",
1632
2454
  description:
1633
- "Look up a Zalo user by phone number (`phone`), username (`username`) or user id (`userId`), or read this logged-in account's own profile (`self: true`). Use it to find someone's `userId`, which is also the `threadId` of your 1-1 conversation with them, before sending a message or a contact card.",
2455
+ "Look up a Zalo user by phone number (`phone`), username (`username`) or user id (`userId`), or read this logged-in account's own profile (`self: true`). Use it to find someone's `userId`, which is also the `threadId` of your 1-1 conversation with them, before sending a message or a contact card. " +
2456
+ "A found user's reply also has `mutualGroups`: `{count, groups: [{groupId, name}]}`, the groups you share with them as Zalo shows on a profile (friends and strangers alike; groups in hidden chats are left out unless hidden chats are included), or `{error}` when that read failed.",
1634
2457
  inputSchema: z
1635
2458
  .object({
1636
2459
  self: z
@@ -1670,28 +2493,38 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1670
2493
  profile: p,
1671
2494
  });
1672
2495
  }
2496
+ // The groups shared with a found user, as Zalo shows on a profile (src/core/mutual-groups.js).
2497
+ const shared = (id) =>
2498
+ mutualGroupsOrError(api, id, {
2499
+ ownId: ownIdOf(api) || null,
2500
+ hidden: hiddenMode() === "include" ? null : hiddenSet(),
2501
+ });
1673
2502
  if (phone) {
1674
2503
  const phoneNumber = phone;
1675
2504
  const user = await api.findUser(phoneNumber);
2505
+ const found = user?.uid || user?.userId || null;
1676
2506
  return ok({
1677
2507
  mode: "phone",
1678
2508
  query: phoneNumber,
1679
2509
  userId: user?.uid || user?.userId || null,
1680
- displayName: user?.display_name || user?.zalo_name || null,
1681
- zaloName: user?.zalo_name || null,
2510
+ // Alias first, as Zalo shows the person to this account (src/core/contact-names.js).
2511
+ ...personNameFields(user?.uid || user?.userId, user),
1682
2512
  avatar: user?.avatar || null,
2513
+ mutualGroups: await shared(found),
1683
2514
  user: user || null,
1684
2515
  });
1685
2516
  }
1686
2517
  if (username) {
1687
2518
  const user = await api.findUserByUsername(username);
2519
+ const found = user?.uid || user?.userId || null;
1688
2520
  return ok({
1689
2521
  mode: "username",
1690
2522
  query: username,
1691
2523
  userId: user?.uid || user?.userId || null,
1692
- displayName: user?.display_name || user?.zalo_name || null,
1693
- zaloName: user?.zalo_name || null,
2524
+ // Alias first, as Zalo shows the person to this account (src/core/contact-names.js).
2525
+ ...personNameFields(user?.uid || user?.userId, user),
1694
2526
  avatar: user?.avatar || null,
2527
+ mutualGroups: await shared(found),
1695
2528
  user: user || null,
1696
2529
  });
1697
2530
  }
@@ -1700,9 +2533,11 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1700
2533
  return ok({
1701
2534
  mode: "userId",
1702
2535
  userId,
1703
- displayName: changed?.displayName || changed?.zaloName || cachedDisplayName(userId) || null,
1704
- zaloName: changed?.zaloName || null,
2536
+ // Alias first, as Zalo shows the person to this account (src/core/contact-names.js).
2537
+ ...personNameFields(userId, changed),
2538
+ displayName: personNameFields(userId, changed).displayName ?? (cachedDisplayName(userId) || null),
1705
2539
  avatar: changed?.avatar || null,
2540
+ mutualGroups: await shared(userId),
1706
2541
  profile: changed || info || null,
1707
2542
  });
1708
2543
  } catch (e) {
@@ -1729,12 +2564,11 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1729
2564
  .min(1)
1730
2565
  .describe("Destination thread ID (`me` or your own uid resolves to My Documents)"),
1731
2566
  threadType: z
1732
- .number()
1733
- .int()
1734
- .min(0)
1735
- .max(1)
2567
+ .enum(["dm", "group"])
1736
2568
  .optional()
1737
- .describe("Destination thread type: 0=DM(User), 1=Group. Inferred from cache when omitted."),
2569
+ .describe(
2570
+ 'The destination conversation type: "dm" or "group". Inferred from the cache when omitted.',
2571
+ ),
1738
2572
  }),
1739
2573
  },
1740
2574
  async ({ msgId, threadId: threadIdIn, threadType: threadTypeIn }) => {
@@ -1747,9 +2581,9 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1747
2581
  try {
1748
2582
  let row = null;
1749
2583
  try {
1750
- row = getMessageById(msgId);
2584
+ row = getMessageForAction(msgId);
1751
2585
  } catch (e) {
1752
- return err(`Local cache unavailable (${e.message}).`);
2586
+ return err(e?.code === "ROW_MAC" ? e.message : `Local cache unavailable (${e.message}).`);
1753
2587
  }
1754
2588
  if (!row) {
1755
2589
  return err(
@@ -1771,11 +2605,20 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1771
2605
  `Message ${msgId} is "${row.type}" but its media is not downloaded locally. Call zalo_view_media with msgId="${msgId}" first.`,
1772
2606
  );
1773
2607
  }
1774
- return api.sendMessage(
1775
- { msg: "", attachments: [resolve(row.localPath)] },
1776
- threadId,
1777
- Number(threadType),
1778
- );
2608
+ // Sealed on disk: the upload gets a private decrypted copy for the send only (media-deliver.js).
2609
+ const copy = decryptToTempfile(resolve(row.localPath), mediaScope(), {
2610
+ msgId: row.msgId,
2611
+ ttlMs: 0,
2612
+ });
2613
+ try {
2614
+ return await api.sendMessage(
2615
+ { msg: "", attachments: [copy.path] },
2616
+ threadId,
2617
+ Number(threadType),
2618
+ );
2619
+ } finally {
2620
+ removeMediaTemp(copy);
2621
+ }
1779
2622
  };
1780
2623
 
1781
2624
  const forwarders = {
@@ -1788,26 +2631,14 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1788
2631
  Number(threadType),
1789
2632
  ),
1790
2633
  link: async () => api.sendLink({ link: c?.href }, threadId, Number(threadType)),
1791
- video: async () => {
1792
- let p = {};
1793
- try {
1794
- p = typeof c?.params === "string" ? JSON.parse(c.params) : (c?.params ?? {});
1795
- } catch {
1796
- /* default geometry */
1797
- }
1798
- return api.sendVideo(
1799
- {
1800
- videoUrl: c?.href,
1801
- thumbnailUrl: c?.thumb || "",
1802
- duration: Number(p.duration) || 0,
1803
- width: Number(p.video_width || p.video_original_width) || 1280,
1804
- height: Number(p.video_height || p.video_original_height) || 720,
1805
- },
2634
+ // As `msg forward`: a size the cache knows spares the HEAD, and one still needed is bounded.
2635
+ video: async () =>
2636
+ api.sendVideo(
2637
+ videoResendOptions(c, mediaPlainSize(row.localPath)),
1806
2638
  threadId,
1807
2639
  Number(threadType),
1808
- );
1809
- },
1810
- voice: async () => api.sendVoice({ voiceUrl: c?.href }, threadId, Number(threadType)),
2640
+ ),
2641
+ voice: async () => api.sendVoice(voiceResendOptions(c), threadId, Number(threadType)),
1811
2642
  photo: resendLocal,
1812
2643
  file: resendLocal,
1813
2644
  gif: resendLocal,
@@ -1825,7 +2656,7 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1825
2656
  success: true,
1826
2657
  kind,
1827
2658
  threadId,
1828
- threadType,
2659
+ threadType: threadTypeName(threadType),
1829
2660
  ...(thread.notice && { notice: thread.notice }),
1830
2661
  result: res,
1831
2662
  });
@@ -1861,7 +2692,7 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1861
2692
  success: true,
1862
2693
  kind: "text",
1863
2694
  threadId,
1864
- threadType,
2695
+ threadType: threadTypeName(threadType),
1865
2696
  ...(thread.notice && { notice: thread.notice }),
1866
2697
  sent,
1867
2698
  });
@@ -1878,7 +2709,7 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1878
2709
  {
1879
2710
  title: "Delete Zalo Message",
1880
2711
  description:
1881
- "Delete a message from your own view only (`everyone: false`, the default), or, as a group admin, delete someone else's message for everyone in the group (`everyone: true`). To recall your own message for both sides, use zalo_undo. `cliMsgId` and `uidFrom` are read from the local cache when you leave them out.",
2712
+ "Delete a message from your own view only (`everyone: false`, the default), or, as a group admin, delete someone else's message for everyone in the group (`everyone: true`). To recall your own message for both sides, use zalo_undo. `cliMsgId` and `uidFrom` are read from the local cache when you leave them out. A delete cannot be undone, so it is asked about first (`confirm`, `dryRun`).",
1882
2713
  inputSchema: z.object({
1883
2714
  msgId: z
1884
2715
  .string()
@@ -1889,12 +2720,9 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1889
2720
  .min(1)
1890
2721
  .describe("The conversation the message is in: the user id for a DM, the group id for a group."),
1891
2722
  threadType: z
1892
- .number()
1893
- .int()
1894
- .min(0)
1895
- .max(1)
2723
+ .enum(["dm", "group"])
1896
2724
  .optional()
1897
- .describe("Thread type: 0=DM(User), 1=Group. Inferred from cache when omitted."),
2725
+ .describe('The conversation type: "dm" or "group". Inferred from the cache when omitted.'),
1898
2726
  cliMsgId: z
1899
2727
  .string()
1900
2728
  .min(1)
@@ -1911,16 +2739,25 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1911
2739
  .boolean()
1912
2740
  .default(false)
1913
2741
  .describe("Set true to delete someone else's message for everyone in a group (admin/owner)"),
2742
+ confirm: confirmInput("the delete"),
2743
+ ticket: ticketInput(),
2744
+ dryRun: dryRunInput("the delete"),
1914
2745
  }),
1915
2746
  },
1916
- async ({
1917
- msgId,
1918
- threadId: threadIdIn,
1919
- threadType: threadTypeIn,
1920
- cliMsgId: cliMsgIdIn,
1921
- senderId: senderIdIn,
1922
- everyone,
1923
- }) => {
2747
+ async (
2748
+ {
2749
+ msgId,
2750
+ threadId: threadIdIn,
2751
+ threadType: threadTypeIn,
2752
+ cliMsgId: cliMsgIdIn,
2753
+ senderId: senderIdIn,
2754
+ everyone,
2755
+ confirm,
2756
+ dryRun,
2757
+ ticket,
2758
+ },
2759
+ extra,
2760
+ ) => {
1924
2761
  const thread = actionThread(api, threadIdIn, threadTypeIn);
1925
2762
  if (thread.error) {
1926
2763
  console.error("[mcp-tools] zalo_delete_message refused:", thread.error);
@@ -1947,13 +2784,20 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1947
2784
  type: Number(threadType),
1948
2785
  };
1949
2786
  const onlyMe = Boolean(everyone) === false;
2787
+ const held = await gate(
2788
+ "msg.delete",
2789
+ { msgId, threadId, cliMsgId: String(cliMsgId), senderId: String(uidFrom), everyone: !onlyMe },
2790
+ { confirm, dryRun, ticket },
2791
+ extra,
2792
+ );
2793
+ if (held) return held;
1950
2794
  const result = await api.deleteMessage(dest, onlyMe);
1951
2795
  return ok({
1952
2796
  success: true,
1953
2797
  msgId,
1954
2798
  cliMsgId: String(cliMsgId),
1955
2799
  threadId,
1956
- threadType,
2800
+ threadType: threadTypeName(threadType),
1957
2801
  everyone: Boolean(everyone),
1958
2802
  ...(thread.notice && { notice: thread.notice }),
1959
2803
  result,
@@ -1974,19 +2818,16 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
1974
2818
  "Create, list, inspect, view responses, edit, or remove reminders in a Zalo DM or group thread. " +
1975
2819
  "Actions: 'create' (requires threadId, title), 'list' (requires threadId), 'info' (requires reminderId, group only), " +
1976
2820
  "'responses' (requires reminderId, group only), 'edit' (requires reminderId, threadId, title), " +
1977
- "'remove' (requires reminderId, threadId).",
2821
+ "'remove' (requires reminderId, threadId; it cannot be undone, so it is asked about first: `confirm`, `dryRun`).",
1978
2822
  inputSchema: z.object({
1979
2823
  action: z
1980
2824
  .enum(["create", "list", "info", "responses", "edit", "remove"])
1981
2825
  .describe("Reminder operation to perform"),
1982
2826
  threadId: z.string().min(1).optional().describe("Thread ID (required for create, list, edit, remove)"),
1983
2827
  threadType: z
1984
- .number()
1985
- .int()
1986
- .min(0)
1987
- .max(1)
2828
+ .enum(["dm", "group"])
1988
2829
  .optional()
1989
- .describe("Thread type: 0=DM(User), 1=Group. Inferred from cache when omitted."),
2830
+ .describe('The conversation type: "dm" or "group". Inferred from the cache when omitted.'),
1990
2831
  reminderId: z
1991
2832
  .string()
1992
2833
  .min(1)
@@ -2004,28 +2845,44 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2004
2845
  .enum(["none", "daily", "weekly", "monthly"])
2005
2846
  .optional()
2006
2847
  .describe("Repeat schedule: 'none', 'daily', 'weekly', or 'monthly'"),
2007
- page: z.number().int().min(1).default(1).describe("Page number for 'list' (default 1)"),
2848
+ page: z
2849
+ .number()
2850
+ .int()
2851
+ .min(1)
2852
+ .optional()
2853
+ .describe("Only this page for 'list' (omit both page and count for every reminder)"),
2008
2854
  count: z
2009
2855
  .number()
2010
2856
  .int()
2011
2857
  .min(1)
2012
2858
  .max(100)
2013
- .default(20)
2014
- .describe("Max items per page for 'list' (default 20)"),
2859
+ .optional()
2860
+ .describe(
2861
+ "Reminders per page for 'list' (one page is read when page or count is given; 20 a page)",
2862
+ ),
2863
+ confirm: confirmInput("'remove'"),
2864
+ ticket: ticketInput(),
2865
+ dryRun: dryRunInput("'remove'"),
2015
2866
  }),
2016
2867
  },
2017
- async ({
2018
- action,
2019
- threadId: threadIdIn,
2020
- threadType: threadTypeIn,
2021
- reminderId,
2022
- title,
2023
- time,
2024
- emoji,
2025
- repeat,
2026
- page,
2027
- count,
2028
- }) => {
2868
+ async (
2869
+ {
2870
+ action,
2871
+ threadId: threadIdIn,
2872
+ threadType: threadTypeIn,
2873
+ reminderId,
2874
+ title,
2875
+ time,
2876
+ emoji,
2877
+ repeat,
2878
+ page,
2879
+ count,
2880
+ confirm,
2881
+ dryRun,
2882
+ ticket,
2883
+ },
2884
+ extra,
2885
+ ) => {
2029
2886
  try {
2030
2887
  if (action === "info") {
2031
2888
  if (!reminderId) return err("reminderId is required for action='info'.");
@@ -2060,7 +2917,7 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2060
2917
  action,
2061
2918
  success: true,
2062
2919
  threadId,
2063
- threadType,
2920
+ threadType: threadTypeName(threadType),
2064
2921
  reminderId: result?.reminderId ?? result?.id ?? null,
2065
2922
  startTime,
2066
2923
  repeat: REMINDER_REPEAT_LABELS[repeatMode],
@@ -2070,13 +2927,37 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2070
2927
 
2071
2928
  if (action === "list") {
2072
2929
  let result = null;
2930
+ let note = null;
2073
2931
  try {
2074
- result = await api.getListReminder({ page, count }, threadId, Number(threadType));
2932
+ if (page === undefined && count === undefined) {
2933
+ // No page asked: every page, one request at a time (src/core/list-all.js).
2934
+ const all = await allReminders(api, threadId, Number(threadType));
2935
+ result = all.reminders;
2936
+ note = all.paging.note;
2937
+ } else {
2938
+ // A group's page 2 and later carry the cursor of the page before, as Zalo pages them;
2939
+ // a DM's have none (Zalo always sends 0 there) and go by page number.
2940
+ result =
2941
+ Number(threadType) === THREAD_GROUP
2942
+ ? await groupReminderPage(api, threadId, page ?? 1, count ?? 20)
2943
+ : await api.getListReminder(
2944
+ { page: page ?? 1, count: count ?? 20 },
2945
+ threadId,
2946
+ Number(threadType),
2947
+ );
2948
+ }
2075
2949
  } catch {
2076
2950
  result = null;
2077
2951
  }
2078
2952
  const reminders = Array.isArray(result) ? result : [];
2079
- return ok({ action, threadId, threadType, count: reminders.length, reminders });
2953
+ return ok({
2954
+ action,
2955
+ threadId,
2956
+ threadType: threadTypeName(threadType),
2957
+ count: reminders.length,
2958
+ reminders,
2959
+ ...(note && { note }),
2960
+ });
2080
2961
  }
2081
2962
 
2082
2963
  if (action === "edit") {
@@ -2097,13 +2978,34 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2097
2978
  editOpts.repeat = REMINDER_REPEAT_MODES[repeat];
2098
2979
  }
2099
2980
  const result = await api.editReminder(editOpts, threadId, Number(threadType));
2100
- return ok({ action, success: true, reminderId, threadId, threadType, result });
2981
+ return ok({
2982
+ action,
2983
+ success: true,
2984
+ reminderId,
2985
+ threadId,
2986
+ threadType: threadTypeName(threadType),
2987
+ result,
2988
+ });
2101
2989
  }
2102
2990
 
2103
2991
  if (action === "remove") {
2104
2992
  if (!reminderId) return err("reminderId is required for action='remove'.");
2993
+ const held = await gate(
2994
+ "reminder.remove",
2995
+ { reminderId, threadId },
2996
+ { confirm, dryRun, ticket },
2997
+ extra,
2998
+ );
2999
+ if (held) return held;
2105
3000
  const result = await api.removeReminder(reminderId, threadId, Number(threadType));
2106
- return ok({ action, success: true, reminderId, threadId, threadType, result });
3001
+ return ok({
3002
+ action,
3003
+ success: true,
3004
+ reminderId,
3005
+ threadId,
3006
+ threadType: threadTypeName(threadType),
3007
+ result,
3008
+ });
2107
3009
  }
2108
3010
 
2109
3011
  return err(`Unsupported reminder action: ${action}`);
@@ -2123,7 +3025,7 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2123
3025
  "Create or edit group notes, list group board items (notes, pinned messages, polls), or pin/unpin " +
2124
3026
  "a cached text message at the top of a conversation. " +
2125
3027
  "Actions: 'create' (requires groupId, title), 'edit' (requires groupId, noteId, title), " +
2126
- "'list' (requires groupId), 'pin_message' (requires msgId; threadId optional if cached), " +
3028
+ "'list' (requires groupId; every board item unless page or count is given), 'pin_message' (requires msgId; threadId optional if cached), " +
2127
3029
  "'unpin_message' (requires msgId; threadId optional if cached).",
2128
3030
  inputSchema: z.object({
2129
3031
  action: z
@@ -2142,13 +3044,10 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2142
3044
  "Thread ID for 'pin_message' / 'unpin_message' (defaults to the message's cached threadId)",
2143
3045
  ),
2144
3046
  threadType: z
2145
- .number()
2146
- .int()
2147
- .min(0)
2148
- .max(1)
3047
+ .enum(["dm", "group"])
2149
3048
  .optional()
2150
3049
  .describe(
2151
- "Thread type for 'pin_message' / 'unpin_message': 0=DM(User), 1=Group. Inferred from cache when omitted.",
3050
+ "The conversation type for 'pin_message' / 'unpin_message': \"dm\" or \"group\". Inferred from the cache when omitted.",
2152
3051
  ),
2153
3052
  noteId: z
2154
3053
  .string()
@@ -2167,8 +3066,19 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2167
3066
  .describe(
2168
3067
  "msgId of a cached text message to pin or unpin (required for 'pin_message' and 'unpin_message').",
2169
3068
  ),
2170
- page: z.number().int().min(1).default(1).describe("Page number for 'list' (default 1)"),
2171
- count: z.number().int().min(1).max(100).default(20).describe("Items per page for 'list' (default 20)"),
3069
+ page: z
3070
+ .number()
3071
+ .int()
3072
+ .min(1)
3073
+ .optional()
3074
+ .describe("Only this page for 'list' (omit both page and count for every board item)"),
3075
+ count: z
3076
+ .number()
3077
+ .int()
3078
+ .min(1)
3079
+ .max(100)
3080
+ .optional()
3081
+ .describe("Items per page for 'list' (one page is read when page or count is given; 20 a page)"),
2172
3082
  }),
2173
3083
  },
2174
3084
  async ({
@@ -2204,7 +3114,19 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2204
3114
 
2205
3115
  if (action === "list") {
2206
3116
  if (!groupId) return err("groupId is required for action='list'.");
2207
- const result = await api.getListBoard({ page, count }, groupId);
3117
+ if (page === undefined && count === undefined) {
3118
+ // No page asked: every page, one request at a time (src/core/list-all.js).
3119
+ const all = await allBoardItems(api, groupId);
3120
+ return ok({
3121
+ action,
3122
+ groupId,
3123
+ items: all.items,
3124
+ count: all.items.length,
3125
+ ...(all.paging.note && { note: all.paging.note }),
3126
+ });
3127
+ }
3128
+ // Page 2 and later carry the cursor of the page before, as Zalo pages the board.
3129
+ const result = await boardPage(api, groupId, page ?? 1, count ?? 20);
2208
3130
  return ok({
2209
3131
  action,
2210
3132
  groupId,
@@ -2217,8 +3139,9 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2217
3139
  if (!msgId) return err("msgId is required for action='pin_message'.");
2218
3140
  let row = null;
2219
3141
  try {
2220
- row = getMessageById(msgId);
2221
- } catch {
3142
+ row = getMessageForAction(msgId);
3143
+ } catch (e) {
3144
+ if (e?.code === "ROW_MAC") return err(e.message);
2222
3145
  row = null;
2223
3146
  }
2224
3147
  if (!row) {
@@ -2259,8 +3182,9 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2259
3182
  if (!msgId) return err("msgId is required for action='unpin_message'.");
2260
3183
  let row = null;
2261
3184
  try {
2262
- row = getMessageById(msgId);
2263
- } catch {
3185
+ row = getMessageForAction(msgId);
3186
+ } catch (e) {
3187
+ if (e?.code === "ROW_MAC") return err(e.message);
2264
3188
  row = null;
2265
3189
  }
2266
3190
  const targetThread = threadIdIn || groupId;
@@ -2299,7 +3223,8 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2299
3223
  "Create, inspect, vote, unvote, add options, lock, or share a poll in a Zalo group. " +
2300
3224
  "Actions: 'create' (requires groupId, question, options with >=2 items), 'info' (requires pollId), " +
2301
3225
  "'vote' (requires pollId, optionIds), 'unvote' (requires pollId), " +
2302
- "'add_options' (requires pollId, options), 'lock' (requires pollId), 'share' (requires pollId).",
3226
+ "'add_options' (requires pollId, options), 'lock' (requires pollId; closing a poll cannot be undone, so it is " +
3227
+ "asked about first: `confirm`, `dryRun`), 'share' (requires pollId).",
2303
3228
  inputSchema: z.object({
2304
3229
  action: z
2305
3230
  .enum(["create", "info", "vote", "unvote", "add_options", "lock", "share"])
@@ -2341,22 +3266,31 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2341
3266
  .boolean()
2342
3267
  .default(false)
2343
3268
  .describe("Also cast your vote for the newly added options ('add_options' only)"),
3269
+ confirm: confirmInput("'lock'"),
3270
+ ticket: ticketInput(),
3271
+ dryRun: dryRunInput("'lock'"),
2344
3272
  }),
2345
3273
  },
2346
- async ({
2347
- action,
2348
- groupId,
2349
- pollId,
2350
- question,
2351
- options,
2352
- optionIds,
2353
- multi,
2354
- allowAddOptions,
2355
- anonymous,
2356
- hidePreview,
2357
- expireMinutes,
2358
- voteNewOptions,
2359
- }) => {
3274
+ async (
3275
+ {
3276
+ action,
3277
+ groupId,
3278
+ pollId,
3279
+ question,
3280
+ options,
3281
+ optionIds,
3282
+ multi,
3283
+ allowAddOptions,
3284
+ anonymous,
3285
+ hidePreview,
3286
+ expireMinutes,
3287
+ voteNewOptions,
3288
+ confirm,
3289
+ dryRun,
3290
+ ticket,
3291
+ },
3292
+ extra,
3293
+ ) => {
2360
3294
  try {
2361
3295
  if (action === "create") {
2362
3296
  if (!groupId) return err("groupId is required for action='create'.");
@@ -2424,6 +3358,8 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2424
3358
  }
2425
3359
 
2426
3360
  if (action === "lock") {
3361
+ const held = await gate("poll.lock", { pollId: numericPollId }, { confirm, dryRun, ticket }, extra);
3362
+ if (held) return held;
2427
3363
  const result = await api.lockPoll(numericPollId);
2428
3364
  return ok({ action, success: true, pollId: numericPollId, result });
2429
3365
  }
@@ -2448,7 +3384,9 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2448
3384
  title: "Wait for Incoming Zalo Message",
2449
3385
  description:
2450
3386
  'Wait for the next incoming message, without polling. It returns at once when a buffered message newer than `since` is already waiting; otherwise it waits up to `timeoutSec` seconds (default 55, max 300) and then returns `status: "timeout"` with no message. Call it in a loop to listen, and call zalo_mark_read with the `cursor` when a message is handled. ' +
2451
- "`limit` (default 1, max 100) returns up to that many buffered messages in one call, in order, in `messages` (`message` is the first, `cursor` is the last, so `since: cursor` continues where the batch ended; `hasMore` says more are already waiting); `settleMs` waits that long after the first message for the rest of a burst. A message's `id` is its msgId. Mirrors GET /events/wait for remote HTTPS/OAuth2 MCP clients.",
3387
+ "It returns every message already waiting, in order, in `messages` (`message` is the first, `cursor` is the last, so `since: cursor` continues where the batch ended); `limit` caps how many (then `hasMore` says more are already waiting); `settleMs` waits that long after the first message for the rest of a burst. A message's `id` is its msgId. " +
3388
+ SYSTEM_EVENTS_NOTE +
3389
+ "Mirrors GET /events/wait for remote HTTPS/OAuth2 MCP clients.",
2452
3390
  inputSchema: z.object({
2453
3391
  threadId: z
2454
3392
  .string()
@@ -2475,9 +3413,8 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2475
3413
  .number()
2476
3414
  .int()
2477
3415
  .min(1)
2478
- .max(MAX_BATCH)
2479
- .default(1)
2480
- .describe(`Most messages to return in one call (default 1, max ${MAX_BATCH})`),
3416
+ .optional()
3417
+ .describe("Most messages to return in one call (omit for every message already waiting)"),
2481
3418
  settleMs: z
2482
3419
  .number()
2483
3420
  .int()
@@ -2489,7 +3426,7 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2489
3426
  ),
2490
3427
  }),
2491
3428
  },
2492
- async ({ threadId, since, timeoutSec = 55, limit = 1, settleMs = 0 }) => {
3429
+ async ({ threadId, since, timeoutSec = 55, limit, settleMs = 0 }) => {
2493
3430
  try {
2494
3431
  if (typeof buffer?.waitForMessages !== "function") {
2495
3432
  return err("Live message buffer does not support waitForMessages in this context.");
@@ -2497,22 +3434,27 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2497
3434
  const cleanThreadId = typeof threadId === "string" && threadId.trim() ? threadId.trim() : null;
2498
3435
  const clampedSec = Math.max(1, Math.min(300, Number(timeoutSec) || 55));
2499
3436
  const batch = batchParams({ limit, settleMs });
2500
- const { messages, hasMore } = await buffer.waitForMessages({
3437
+ if (isHiddenThread(cleanThreadId)) return err(HIDDEN_THREAD);
3438
+ const waited = await buffer.waitForMessages({
2501
3439
  threadId: cleanThreadId,
2502
3440
  since: typeof since === "number" && Number.isFinite(since) ? since : undefined,
2503
3441
  timeoutMs: clampedSec * 1000,
2504
3442
  ...batch,
2505
3443
  });
3444
+ const messages = eventsView(waited.messages);
3445
+ const hasMore = waited.hasMore;
2506
3446
  return ok(
2507
3447
  waitResult({
2508
3448
  messages,
2509
3449
  hasMore,
2510
3450
  cursorWhenEmpty: Math.max(
3451
+ Number(waited.messages.at(-1)?._cursor) || 0,
2511
3452
  typeof since === "number" && Number.isFinite(since) ? since : 0,
2512
3453
  Number(buffer._globalCursor) || 0,
2513
3454
  typeof buffer.readCursor === "function" ? buffer.readCursor() : 0,
2514
3455
  ),
2515
3456
  decorate: (msg) => {
3457
+ if (msg.hidden || msg.system) return msg;
2516
3458
  const tid = String(msg.threadId || "");
2517
3459
  let threadName = nameCache?.get(tid)?.name || null;
2518
3460
  if (!threadName && tid) {
@@ -2543,7 +3485,9 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2543
3485
  "Actions: 'list' (returns all labels with their IDs, text, colors, conversations array, and version), " +
2544
3486
  "'assign' (adds threadId or threadIds to a label identified by labelId or labelText), " +
2545
3487
  "'unassign' (removes threadId or threadIds from a label identified by labelId or labelText), " +
2546
- "'update' (raw update with full labelData array and version, matching 'label update').",
3488
+ "'update' (raw update with full labelData array and version, matching 'label update'; the list " +
3489
+ "replaces the current one, so a label left out of it is deleted: that is asked about first, " +
3490
+ "`confirm`, `dryRun`).",
2547
3491
  inputSchema: z.object({
2548
3492
  action: z
2549
3493
  .enum(["list", "assign", "unassign", "update"])
@@ -2569,6 +3513,12 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2569
3513
  .array(z.string().min(1))
2570
3514
  .optional()
2571
3515
  .describe("Multiple thread IDs to assign or unassign in one call"),
3516
+ threadType: z
3517
+ .enum(["dm", "group"])
3518
+ .optional()
3519
+ .describe(
3520
+ 'The type of every threadId/threadIds given: "dm" or "group". Leave it out: the type stored for each thread is used. A label stores a group as "g<groupId>".',
3521
+ ),
2572
3522
  labelData: z
2573
3523
  .array(z.any())
2574
3524
  .optional()
@@ -2578,9 +3528,27 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2578
3528
  .int()
2579
3529
  .optional()
2580
3530
  .describe("Label version integer from action='list' (required for raw action='update')"),
3531
+ confirm: confirmInput("an 'update' that removes labels"),
3532
+ ticket: ticketInput(),
3533
+ dryRun: dryRunInput("'update'"),
2581
3534
  }),
2582
3535
  },
2583
- async ({ action, labelId, labelText, threadId, threadIds, labelData, version }) => {
3536
+ async (
3537
+ {
3538
+ action,
3539
+ labelId,
3540
+ labelText,
3541
+ threadId,
3542
+ threadIds,
3543
+ threadType: threadTypeIn,
3544
+ labelData,
3545
+ version,
3546
+ confirm,
3547
+ dryRun,
3548
+ ticket,
3549
+ },
3550
+ extra,
3551
+ ) => {
2584
3552
  try {
2585
3553
  if (action === "list") {
2586
3554
  const res = await api.getLabels();
@@ -2596,6 +3564,16 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2596
3564
  if (!Array.isArray(labelData) || typeof version !== "number" || !Number.isFinite(version)) {
2597
3565
  return err("labelData (array) and version (number) are required for action='update'.");
2598
3566
  }
3567
+ // Only a label dropped from the list is asked about; knowing which takes the current list.
3568
+ const removed = removedLabels((await api.getLabels())?.labelData, labelData);
3569
+ const held = await gate(
3570
+ "label.update",
3571
+ { removedLabels: removed, version: Number(version) },
3572
+ { confirm, dryRun, ticket },
3573
+ extra,
3574
+ { applies: removed.length > 0 },
3575
+ );
3576
+ if (held) return held;
2599
3577
  const updated = await api.updateLabels({
2600
3578
  labelData,
2601
3579
  version: Number(version),
@@ -2647,15 +3625,24 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2647
3625
  );
2648
3626
  }
2649
3627
 
2650
- const targetSet = new Set(targets);
3628
+ // A label names a group as "g<groupId>" and a user by the bare id (spec-actions 9): a bare
3629
+ // group id would be stored as a user conversation. Unassign drops both forms, so it also
3630
+ // clears a bare group id an earlier version stored (a user id is never a group's).
3631
+ const convIds = targets.map((t) => labelConversationId(t, threadTypeIn));
3632
+ const dropped = new Set(
3633
+ convIds.flatMap((c) => {
3634
+ const bare = LABEL_GROUP_ID.test(c) ? c.slice(1) : c;
3635
+ return [bare, `g${bare}`];
3636
+ }),
3637
+ );
2651
3638
  let updatedConvList = [];
2652
3639
  const nextLabelData = list.map((lbl) => {
2653
3640
  if (Number(lbl.id) !== Number(matched.id)) return lbl;
2654
3641
  const existing = Array.isArray(lbl.conversations) ? lbl.conversations.map(String) : [];
2655
3642
  const nextConvs =
2656
3643
  action === "assign"
2657
- ? [...new Set([...existing, ...targets])]
2658
- : existing.filter((c) => !targetSet.has(String(c)));
3644
+ ? [...new Set([...existing, ...convIds])]
3645
+ : existing.filter((c) => !dropped.has(String(c)));
2659
3646
  updatedConvList = nextConvs;
2660
3647
  return { ...lbl, conversations: nextConvs };
2661
3648
  });
@@ -2690,43 +3677,85 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2690
3677
  {
2691
3678
  title: "Manage Zalo Friends and Friend Requests",
2692
3679
  description:
2693
- "Check, list and answer friend requests, and list friends. Actions: " +
3680
+ "Friends and friend requests: check, list and answer requests, list, remove and block friends. Actions: " +
2694
3681
  "'status' (the relationship with userId: is_friend, and is_requested / is_requesting, Zalo's two flags " +
2695
3682
  "for a request pending between you and them; to tell which direction a request goes, use the two lists), " +
2696
3683
  "'sent_requests' (requests you sent that are still pending), " +
2697
3684
  "'received_requests' (requests others sent you, newest first, with their message), " +
2698
3685
  "'send' (friend request to userId, with an optional message), " +
2699
- "'accept' / 'reject' (the request from userId), " +
2700
- "'undo' (cancel the request you sent to userId), " +
2701
- "'list' (your friends, most recently active first, capped by limit). " +
3686
+ "'accept' / 'reject' (the request from userId; 'reject' is asked about first), " +
3687
+ "'undo' (cancel the request you sent to userId; asked about first: `confirm`, `dryRun`), " +
3688
+ "'list' (your friends, most recently active first: all of them unless limit is given), " +
3689
+ "'alias' (set the nickname this account shows for userId to `alias`; an empty alias removes it; " +
3690
+ "the nickname is what `senderDisplayName` and `@[userId]` mentions then use), " +
3691
+ "'alias_list' (the nicknames set on this account: all of them unless limit is given), " +
3692
+ "'online' (friends online now: dead in practice, Zalo answers 404 and Zalo's own apps discard it too), " +
3693
+ "'close' (close friends), 'last_online' (when userId was last seen, with hiddenBy \"you\" or \"them\" " +
3694
+ "when hidden; nothing is asked when this account hides its own status, unless `askAnyway`; " +
3695
+ "`cached` reads the listener's presence cache instead), " +
3696
+ "'remove' (unfriend userId), 'block' / 'unblock' (userId), 'block_feed' (hide userId's posts from the " +
3697
+ "feed; `unblock: true` shows them again), 'find_phones' (users by `phones`), 'qr' (the QR code URL of " +
3698
+ "each of userIds), 'common_groups' (the groups you share with each of userIds), 'biz' (userId's " +
3699
+ "business account details). 'reject', 'undo', 'remove' and 'block' are asked about first: " +
3700
+ "`confirm`, `dryRun`. " +
2702
3701
  "userId is the numeric Zalo id (zalo_lookup_user finds one from a phone number). " +
2703
3702
  "Zalo restricts accounts that send many friend requests, so send sparingly.",
2704
- inputSchema: z.object({
2705
- action: z
2706
- .enum(["status", "sent_requests", "received_requests", "send", "accept", "reject", "undo", "list"])
2707
- .describe("Friend operation to perform"),
2708
- userId: z
2709
- .string()
2710
- .min(1)
2711
- .optional()
2712
- .describe("Numeric Zalo user id (required for status, send, accept, reject and undo)"),
2713
- message: z
2714
- .string()
2715
- .optional()
2716
- .describe("Message sent with the friend request (only for 'send'; default: none)"),
2717
- limit: z
2718
- .number()
2719
- .int()
2720
- .min(1)
2721
- .max(1000)
2722
- .optional()
2723
- .describe(
2724
- "Most friends to return for 'list' (default 200; the reply says how many there are in all)",
2725
- ),
2726
- }),
3703
+ inputSchema: z
3704
+ .object({
3705
+ action: z
3706
+ .enum([
3707
+ "status",
3708
+ "sent_requests",
3709
+ "received_requests",
3710
+ "send",
3711
+ "accept",
3712
+ "reject",
3713
+ "undo",
3714
+ "list",
3715
+ "alias",
3716
+ "alias_list",
3717
+ ...Object.keys(FRIEND_EXTRA_ACTIONS),
3718
+ ])
3719
+ .describe("Friend operation to perform"),
3720
+ userId: z
3721
+ .string()
3722
+ .min(1)
3723
+ .optional()
3724
+ .describe(
3725
+ "Numeric Zalo user id (required for status, send, accept, reject, undo, alias, remove, block, " +
3726
+ "unblock, last_online, biz and block_feed)",
3727
+ ),
3728
+ message: z
3729
+ .string()
3730
+ .optional()
3731
+ .describe("Message sent with the friend request (only for 'send'; default: none)"),
3732
+ alias: z
3733
+ .string()
3734
+ .max(200)
3735
+ .optional()
3736
+ .describe(
3737
+ "Only for 'alias': the nickname to show for userId; an empty string removes the nickname",
3738
+ ),
3739
+ limit: z
3740
+ .number()
3741
+ .int()
3742
+ .min(1)
3743
+ .optional()
3744
+ .describe(
3745
+ "Most entries to return for 'list' and 'alias_list' (omit for all); 'list' says how many there are in all",
3746
+ ),
3747
+ ...FRIEND_EXTRA_INPUTS,
3748
+ confirm: confirmInput("'reject', 'undo', 'remove' and 'block'"),
3749
+ ticket: ticketInput(),
3750
+ dryRun: dryRunInput("'reject', 'undo', 'remove' and 'block'"),
3751
+ })
3752
+ .strict(),
2727
3753
  },
2728
- async ({ action, userId, message, limit }) => {
2729
- const needsUser = ["status", "send", "accept", "reject", "undo"].includes(action);
3754
+ async (args, extra) => {
3755
+ // The actions added with the action tools run through the shared runner (src/mcp/action-tool.js).
3756
+ if (Object.hasOwn(FRIEND_EXTRA_ACTIONS, args.action)) return friendExtra(args, extra);
3757
+ const { action, userId, message, limit, alias, confirm, dryRun, ticket } = args;
3758
+ const needsUser = ["status", "send", "accept", "reject", "undo", "alias"].includes(action);
2730
3759
  const target = userId === undefined ? "" : String(userId).trim();
2731
3760
  if (needsUser) {
2732
3761
  if (!target) return err(`userId is required for action='${action}'.`);
@@ -2736,19 +3765,18 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2736
3765
  );
2737
3766
  }
2738
3767
  }
3768
+ if (action === "reject" || action === "undo") {
3769
+ const key = action === "reject" ? "friend.reject" : "friend.undoRequest";
3770
+ const held = await gate(key, { userId: target }, { confirm, dryRun, ticket }, extra);
3771
+ if (held) return held;
3772
+ }
2739
3773
  try {
2740
3774
  switch (action) {
2741
3775
  case "status":
2742
3776
  return ok({ action, ...friendStatusFrom(await api.getFriendRequestStatus(target), target) });
2743
3777
  case "sent_requests": {
2744
- let res = null;
2745
- try {
2746
- res = await api.getSentFriendRequest();
2747
- } catch (e) {
2748
- // Zalo answers code 112 when there are none, which is an empty list, not a failure.
2749
- if (!/\b112\b/.test(String(e?.message))) throw e;
2750
- }
2751
- const requests = sentRequestsFrom(res);
3778
+ // Zalo's "none sent" (112) is an empty list, not a failure.
3779
+ const requests = sentRequestsFrom(await readSentRequests(api));
2752
3780
  return ok({ action, count: requests.length, requests });
2753
3781
  }
2754
3782
  case "received_requests": {
@@ -2768,17 +3796,66 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2768
3796
  await api.undoFriendRequest(target);
2769
3797
  return ok({ action, success: true, userId: target });
2770
3798
  case "list": {
2771
- const { total, friends } = friendsFrom(await api.getAllFriends(), limit ?? 200);
3799
+ const { total, friends } = friendsFrom(await api.getAllFriends(), limit);
2772
3800
  return ok({ action, total, count: friends.length, truncated: friends.length < total, friends });
2773
3801
  }
3802
+ case "alias": {
3803
+ const nick = alias === undefined ? "" : String(alias).trim();
3804
+ if (nick) {
3805
+ // The same call as the CLI's `friend alias` (the session takes the alias first).
3806
+ await api.changeFriendAlias(nick, target);
3807
+ // `contacts.name` is what senderDisplayName and mentions read (db.getDisplayName);
3808
+ // the nickname is the name Zalo will show from now on, so it is written there at once.
3809
+ rememberContactName(target, nick);
3810
+ noteAliasSet(target, nick);
3811
+ return ok({ action, success: true, userId: target, alias: nick });
3812
+ }
3813
+ await api.removeFriendAlias(target);
3814
+ noteAliasSet(target, null);
3815
+ // Without the nickname the contact is shown by their profile name again; fetch it so the
3816
+ // cached name does not stay the removed nickname. Best effort: the removal already stuck.
3817
+ try {
3818
+ const info = await api.getUserInfo(target);
3819
+ const p = info?.changed_profiles?.[target] || info?.unchanged_profiles?.[target] || null;
3820
+ const shown = p?.displayName || p?.zaloName || null;
3821
+ if (shown) rememberContactName(target, shown);
3822
+ } catch (e) {
3823
+ console.error(`[mcp-tools] zalo_friend alias: profile name not refreshed: ${e.message}`);
3824
+ }
3825
+ return ok({ action, success: true, userId: target, alias: null });
3826
+ }
3827
+ case "alias_list": {
3828
+ // No limit: every page, as Zalo pages its alias list (src/core/list-all.js); a limit is
3829
+ // one page of that size.
3830
+ let items;
3831
+ let note = null;
3832
+ if (limit === undefined) {
3833
+ const all = await allAliases(api);
3834
+ items = all.items;
3835
+ note = all.paging.note;
3836
+ } else {
3837
+ const res = await api.getAliasList(limit, 1);
3838
+ items = Array.isArray(res?.items) ? res.items : [];
3839
+ }
3840
+ const aliases = items
3841
+ .filter((i) => i && i.userId !== undefined && i.userId !== null)
3842
+ .map((i) => ({ userId: String(i.userId), alias: i.alias ?? null }));
3843
+ return ok({ action, count: aliases.length, aliases, ...(note && { note }) });
3844
+ }
2774
3845
  default:
2775
3846
  return err(`Unsupported friend action: ${action}`);
2776
3847
  }
2777
3848
  } catch (e) {
2778
3849
  console.error("[mcp-tools] zalo_friend error:", e.message);
2779
- // 'send' keeps the CLI's wording for Zalo's refusal codes (already friends, blocked, ...).
3850
+ // 'send' and 'undo' keep the CLI's wording for Zalo's refusal codes (already friends, blocked,
3851
+ // too many requests taken back, ...), read from the error's code.
3852
+ const code = errorCodeOf(e);
2780
3853
  const known =
2781
- action === "send" ? describeFriendAddError(e.code ?? extractErrorCode(e.message), target) : null;
3854
+ action === "send"
3855
+ ? describeFriendAddError(code, target)
3856
+ : action === "undo"
3857
+ ? describeFriendUndoError(code, target)
3858
+ : null;
2782
3859
  return err(known || e.message);
2783
3860
  }
2784
3861
  },
@@ -2797,48 +3874,101 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2797
3874
  "'create' (a new group named `name` with the members in userIds), " +
2798
3875
  "'add_members' (add userIds to the group), " +
2799
3876
  "'remove_member' (remove the one member userId from the group), " +
2800
- "'leave' (leave the group; `silent` hides the usual notice from ordinary members; the owner still sees it). " +
3877
+ "'leave' (only this account leaves; `silent` hides the usual notice from ordinary members, the owner " +
3878
+ "still sees it; the owner of a group with other members must give `newOwner` (a member's userId: " +
3879
+ "ownership is handed over first, as Zalo does) or `randomOwner: true` (Zalo picks a member)), " +
3880
+ "'rename' (set the group's name to `name`; every member sees the change), " +
3881
+ "'avatar' (set the group's avatar from a local image, `filePath`), " +
3882
+ "'add_admin' / 'remove_admin' (userIds), 'transfer_owner' (to userId), " +
3883
+ "'block_member' / 'unblock_member' (userIds; a blocked member cannot rejoin), 'blocked' (the blocked " +
3884
+ "members), 'settings' (change `settings`; the rest keep their values), 'upgrade_community', " +
3885
+ "'disperse' (owner only: disband the group, deleting it for every member), 'pending' / 'approve' / " +
3886
+ "'reject_member' (join requests, " +
3887
+ "admins only), 'enable_link' / 'disable_link' / 'link_info' (the invite link), 'link_preview' / " +
3888
+ "'join' (a group by its invite `link`), 'invite_boxes' (invitations you received), 'invite_info' / " +
3889
+ "'join_invite' (one of them, by groupId), 'delete_invite' (groupIds), 'invite_to' (invite userId to " +
3890
+ "groupIds), 'members_info' (profiles of userIds). " +
2801
3891
  "groupId is the group's thread id (zalo_search_threads or zalo_list_conversations finds it); " +
2802
3892
  "user ids are numeric Zalo ids (zalo_lookup_user finds one from a phone number). " +
2803
- "'leave' and 'remove_member' cannot be undone from here, so they need confirm: true (a plain " +
2804
- "parameter, no prompt; a deployment can turn the check off with groupConfirm: false in " +
2805
- "mcp-config.json or ZALO_MCP_GROUP_CONFIRM=0). Zalo can refuse some members " +
3893
+ "'leave', 'remove_member', 'remove_admin', 'transfer_owner', 'block_member', 'disperse', " +
3894
+ "'disable_link', and 'settings' when it turns join approval off, are asked about first: " +
3895
+ "`confirm` and `dryRun`, as the deployment's confirmMode decides. Zalo can refuse some members " +
2806
3896
  "of a create or add_members: the reply lists the members it did and did not take.",
2807
- inputSchema: z.object({
2808
- action: z
2809
- .enum(["leave", "info", "create", "add_members", "remove_member"])
2810
- .describe("Group operation to perform"),
2811
- groupId: z
2812
- .string()
2813
- .min(1)
2814
- .optional()
2815
- .describe("The group's id (required for leave, info, add_members and remove_member)"),
2816
- name: z.string().optional().describe("The new group's name (required for 'create')"),
2817
- userIds: z
2818
- .array(z.string().min(1))
2819
- .min(1)
2820
- .max(100)
2821
- .optional()
2822
- .describe("Numeric Zalo user ids (required for 'create' and 'add_members')"),
2823
- userId: z.string().min(1).optional().describe("Numeric Zalo user id (required for 'remove_member')"),
2824
- silent: z
2825
- .boolean()
2826
- .optional()
2827
- .describe(
2828
- "Only for 'leave': hide the usual notice from ordinary members; the owner still sees it (default false)",
2829
- ),
2830
- confirm: z
2831
- .boolean()
2832
- .optional()
2833
- .describe(
2834
- "Must be true for 'leave' and 'remove_member', which cannot be undone from here " +
2835
- "(ignored when the deployment has switched the check off)",
2836
- ),
2837
- }),
3897
+ inputSchema: z
3898
+ .object({
3899
+ action: z
3900
+ .enum([
3901
+ "leave",
3902
+ "info",
3903
+ "create",
3904
+ "add_members",
3905
+ "remove_member",
3906
+ "rename",
3907
+ ...Object.keys(GROUP_EXTRA_ACTIONS),
3908
+ ])
3909
+ .describe("Group operation to perform"),
3910
+ groupId: z
3911
+ .string()
3912
+ .min(1)
3913
+ .optional()
3914
+ .describe(
3915
+ "The group's id (required for every action except create, link_preview, join, invite_boxes, " +
3916
+ "delete_invite, invite_to and members_info)",
3917
+ ),
3918
+ name: z
3919
+ .string()
3920
+ .optional()
3921
+ .describe("The group's name: the new group's for 'create', the name to set for 'rename'"),
3922
+ userIds: z
3923
+ .array(z.string().min(1))
3924
+ .min(1)
3925
+ .max(100)
3926
+ .optional()
3927
+ .describe(
3928
+ "Numeric Zalo user ids (required for 'create', 'add_members', 'add_admin', 'remove_admin', " +
3929
+ "'block_member', 'unblock_member', 'approve', 'reject_member' and 'members_info')",
3930
+ ),
3931
+ userId: z
3932
+ .string()
3933
+ .min(1)
3934
+ .optional()
3935
+ .describe(
3936
+ "Numeric Zalo user id (required for 'remove_member', 'transfer_owner' and 'invite_to')",
3937
+ ),
3938
+ silent: z
3939
+ .boolean()
3940
+ .optional()
3941
+ .describe(
3942
+ "Only for 'leave': hide the usual notice from ordinary members; the owner still sees it (default false)",
3943
+ ),
3944
+ newOwner: z
3945
+ .string()
3946
+ .min(1)
3947
+ .optional()
3948
+ .describe(
3949
+ "Only for 'leave' by the group's owner: the member (numeric user id) who owns the group next; " +
3950
+ "ownership is handed over, then the account leaves",
3951
+ ),
3952
+ randomOwner: z
3953
+ .boolean()
3954
+ .optional()
3955
+ .describe(
3956
+ "Only for 'leave' by the group's owner: leave and let Zalo give ownership to a random member",
3957
+ ),
3958
+ ...GROUP_EXTRA_INPUTS,
3959
+ confirm: confirmInput("the actions asked about first (listed above)"),
3960
+ ticket: ticketInput(),
3961
+ dryRun: dryRunInput("the actions asked about first (listed above)"),
3962
+ })
3963
+ .strict(),
2838
3964
  },
2839
- async ({ action, groupId, name, userIds, userId, silent, confirm }) => {
3965
+ async (args, extra) => {
3966
+ // The actions added with the action tools run through the shared runner (src/mcp/action-tool.js).
3967
+ if (Object.hasOwn(GROUP_EXTRA_ACTIONS, args.action)) return groupExtra(args, extra);
3968
+ const { action, groupId, name, userIds, userId, silent, newOwner, randomOwner, confirm, dryRun, ticket } =
3969
+ args;
2840
3970
  const gid = groupId === undefined ? "" : String(groupId).trim();
2841
- if (["leave", "info", "add_members", "remove_member"].includes(action)) {
3971
+ if (["leave", "info", "add_members", "remove_member", "rename"].includes(action)) {
2842
3972
  if (!gid) return err(`groupId is required for action='${action}'.`);
2843
3973
  if (!/^\d{1,30}$/.test(gid)) {
2844
3974
  return err(
@@ -2868,22 +3998,54 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2868
3998
  }
2869
3999
  }
2870
4000
  const groupName = name === undefined ? "" : String(name).trim();
2871
- if (action === "create" && !groupName) return err("name is required for action='create'.");
4001
+ if ((action === "create" || action === "rename") && !groupName) {
4002
+ return err(`name is required for action='${action}'.`);
4003
+ }
4004
+
4005
+ if ((newOwner !== undefined || randomOwner !== undefined) && action !== "leave") {
4006
+ return err("newOwner and randomOwner are only for action='leave'.");
4007
+ }
4008
+ // A leave is read first: the owner's leave without a successor, or a successor who is not a member, is
4009
+ // refused before the guard and before anything is sent (src/core/group-departure.js).
4010
+ let leavePlan = null;
4011
+ if (action === "leave") {
4012
+ try {
4013
+ leavePlan = await planLeave(api, {
4014
+ groupId: gid,
4015
+ ownId: ownIdOf(api) || null,
4016
+ newOwner,
4017
+ randomOwner: randomOwner === true,
4018
+ });
4019
+ } catch (e) {
4020
+ return err(departureMessage(e, "mcp"));
4021
+ }
4022
+ }
2872
4023
 
2873
4024
  // After validation, before Zalo: a refused call changes nothing and says exactly how to retry.
2874
- if (CONFIRMED_ACTIONS.includes(action) && confirm !== true && groupConfirmRequired({ config })) {
2875
- const what = action === "leave" ? `leave group ${gid}` : `remove ${target} from group ${gid}`;
2876
- return err(
2877
- `Not done: this would ${what}, which cannot be undone from here. Repeat the call with ` +
2878
- "confirm: true to proceed. (An automatic or headless deployment can turn this check off " +
2879
- "with groupConfirm: false in mcp-config.json, or ZALO_MCP_GROUP_CONFIRM=0 in the environment.)",
4025
+ if (action === "leave" || action === "remove_member") {
4026
+ const held = await gate(
4027
+ action === "leave" ? "group.leave" : "group.removeMember",
4028
+ action === "leave"
4029
+ ? {
4030
+ groupId: gid,
4031
+ silent: silent === true,
4032
+ newOwner: leavePlan.newOwner,
4033
+ randomOwner: leavePlan.handOver === "random",
4034
+ }
4035
+ : { groupId: gid, userIds: [target] },
4036
+ { confirm, dryRun, ticket },
4037
+ extra,
2880
4038
  );
4039
+ if (held) return held;
2881
4040
  }
2882
4041
 
2883
4042
  try {
2884
4043
  switch (action) {
2885
4044
  case "info": {
2886
- const info = groupInfoFrom(await api.getGroupInfo([gid]), gid);
4045
+ const info = groupInfoFrom(
4046
+ await getGroupInfoWatched(api, ownIdOf(api) || null, [String(gid)]),
4047
+ gid,
4048
+ );
2887
4049
  if (!info) {
2888
4050
  return err(
2889
4051
  `Zalo's answer does not include group ${gid}: this account is not in it, or it is not ` +
@@ -2932,16 +4094,26 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2932
4094
  }
2933
4095
  return ok({ action, success: true, groupId: gid, userId: target });
2934
4096
  }
4097
+ case "rename": {
4098
+ // The same call as the CLI's `group rename` (the session takes the name first).
4099
+ await api.changeGroupName(groupName, gid);
4100
+ return ok({ action, success: true, groupId: gid, name: groupName });
4101
+ }
2935
4102
  case "leave": {
2936
4103
  const quiet = silent === true;
2937
- const res = await api.leaveGroup(gid, quiet);
4104
+ const res = await runLeave(api, leavePlan, { silent: quiet });
2938
4105
  const memberError = Array.isArray(res?.memberError) ? res.memberError : [];
4106
+ // As Zalo does on the API's success: the group's local history goes now (or is kept flagged,
4107
+ // groups.onLeave), whether or not a listener sees the echo.
4108
+ const exit = await recordOwnExit({ ownId: ownIdOf(api), groupId: gid, reason: "leave" });
2939
4109
  return ok({
2940
4110
  action,
2941
4111
  success: true,
2942
4112
  groupId: gid,
2943
4113
  silent: quiet,
4114
+ ...(res.handOver && { ownership: res.handOver, newOwner: res.newOwner }),
2944
4115
  ...(memberError.length > 0 && { memberError }),
4116
+ ...exit,
2945
4117
  });
2946
4118
  }
2947
4119
  default:
@@ -2966,4 +4138,134 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
2966
4138
  }
2967
4139
  },
2968
4140
  );
4141
+
4142
+ // --- The action tools: one per area, each action the call its CLI command makes (src/mcp/actions-*.js) ---
4143
+ server.registerTool(
4144
+ "zalo_conversation",
4145
+ CONVERSATION_META,
4146
+ actionTool("zalo_conversation", CONVERSATION_ACTIONS, toolCtx),
4147
+ );
4148
+ server.registerTool("zalo_profile", PROFILE_META, actionTool("zalo_profile", PROFILE_ACTIONS, toolCtx));
4149
+ server.registerTool("zalo_auto_reply", AUTO_REPLY_META, actionTool("zalo_auto_reply", AUTO_REPLY_ACTIONS, toolCtx));
4150
+ server.registerTool(
4151
+ "zalo_quick_message",
4152
+ QUICK_MESSAGE_META,
4153
+ actionTool("zalo_quick_message", QUICK_MESSAGE_ACTIONS, toolCtx),
4154
+ );
4155
+ server.registerTool("zalo_catalog", CATALOG_META, actionTool("zalo_catalog", CATALOG_ACTIONS, toolCtx));
4156
+ server.registerTool("zalo_bank", BANK_META, actionTool("zalo_bank", BANK_ACTIONS, toolCtx));
4157
+ server.registerTool("zalo_cloud", CLOUD_META, actionTool("zalo_cloud", CLOUD_ACTIONS, toolCtx));
4158
+ server.registerTool("zalo_sticker", STICKER_META, actionTool("zalo_sticker", STICKER_ACTIONS, toolCtx));
4159
+ server.registerTool("zalo_link", LINK_META, actionTool("zalo_link", LINK_ACTIONS, toolCtx));
4160
+
4161
+ // --- zalo_official: the oa / zbs / social commands, run as the CLI runs them (./official-tool.js) ---
4162
+ server.registerTool("zalo_official", officialMeta(), officialTool(toolCtx));
4163
+
4164
+ // --- zalo_read ---
4165
+ server.registerTool(
4166
+ "zalo_read",
4167
+ {
4168
+ title: "Read From Zalo's API",
4169
+ description:
4170
+ "Ask Zalo one of the read-only endpoints Zalo calls that no other tool covers: the sticker and GIF " +
4171
+ "catalogs and GIF search, a profile's rank and extra fields, the block list, the nickname list, the " +
4172
+ "search history Zalo keeps, delivery and seen status, the group quota and the account's role in a " +
4173
+ "group, friend profile checks, the emoji statuses of friends, the server's client settings, and " +
4174
+ "more. Omit `name` to list every read with what it takes and whether a live account has answered it; " +
4175
+ "give `name` and `args` to run one. It only reads: nothing here sends, marks, changes or removes " +
4176
+ "anything. `args` is an object named by the read's own argument list (ids are strings).",
4177
+ inputSchema: z.object({
4178
+ name: z
4179
+ .enum(READ_NAMES)
4180
+ .optional()
4181
+ .describe("The read to run (omit to list them all, with their arguments)"),
4182
+ args: z
4183
+ .record(z.string(), z.any())
4184
+ .optional()
4185
+ .describe('The read\'s arguments by name, e.g. {"id": "123"} for profile-rank'),
4186
+ }),
4187
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
4188
+ },
4189
+ async ({ name, args }) => {
4190
+ if (!name) return ok({ reads: describeReads() });
4191
+ // A read about one conversation names it in its args; a hidden one is left out like everywhere else.
4192
+ if (readThreadIds(args).some(isHiddenThread)) return err(HIDDEN_THREAD);
4193
+ try {
4194
+ // A paged read with no page chosen is read to its end (READ_PAGERS in src/zalo/reads.js).
4195
+ const { data, paging } = await callReadAll(api, name, args || {});
4196
+ return ok({ name, data: data ?? null, ...(paging?.note && { note: paging.note }) });
4197
+ } catch (e) {
4198
+ if (!(e instanceof ReadUsageError)) console.error("[mcp-tools] zalo_read error:", e.message);
4199
+ return err(e.message);
4200
+ }
4201
+ },
4202
+ );
4203
+
4204
+ // --- zalo_write ---
4205
+ server.registerTool(
4206
+ "zalo_write",
4207
+ {
4208
+ title: "Change Something Through Zalo's API",
4209
+ description:
4210
+ "Make one of the state-changing calls Zalo makes that no other tool covers: reminder RSVP, creating " +
4211
+ "or changing a group note or reminder with a repeat rule, pinning and unpinning board items, removing a " +
4212
+ "topic, recalling several messages at once, suggested-friend cleanup, blocking an " +
4213
+ "Official Account, removing one account from several groups, the search history. Omit `name` to list " +
4214
+ "every write with what it touches (`risk`: private, visible to others, or destructive), what it takes, " +
4215
+ "whether a live success was seen (`verified`) and, if not, why (`note`). EVERY write changes " +
4216
+ 'something, so a real call is asked about first, even where the deployment\'s confirmMode is "off" ' +
4217
+ '(`confirm: true`; in "human" mode the server asks the user), and `dryRun: true` ' +
4218
+ "returns the request that would be sent without sending anything: use it first, and try an unverified " +
4219
+ "write on something disposable. Enum-like arguments take Zalo's own names or numbers (`list` " +
4220
+ "shows them). `args` is an object named by the write's own argument list (ids are strings).",
4221
+ inputSchema: z.object({
4222
+ name: z
4223
+ .enum(WRITE_NAMES)
4224
+ .optional()
4225
+ .describe("The write to make (omit to list them all, with their arguments and risk)"),
4226
+ args: z
4227
+ .record(z.string(), z.any())
4228
+ .optional()
4229
+ .describe('The write\'s arguments by name, e.g. {"groupId": "123", "topicId": "45"}'),
4230
+ dryRun: z
4231
+ .boolean()
4232
+ .optional()
4233
+ .describe("Return the request that would be sent, with its body decrypted, and send nothing"),
4234
+ confirm: confirmInput("a real call (not needed with dryRun)"),
4235
+ ticket: ticketInput(),
4236
+ }),
4237
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
4238
+ },
4239
+ async ({ name, args, dryRun, confirm, ticket }, extra) => {
4240
+ if (!name) return ok({ writes: describeWrites() });
4241
+ // Arguments first, as for every guarded action: a call missing one is refused as the typo it is, before
4242
+ // anyone is asked to confirm a request that could not be sent. Building the request sends nothing; a
4243
+ // failure that is not about the arguments is left to the call itself.
4244
+ try {
4245
+ buildWrite(api, name, args || {});
4246
+ } catch (e) {
4247
+ if (e instanceof ReadUsageError) return err(e.message);
4248
+ }
4249
+ // dryRun is the write's own: it returns the request itself, so only a real call is gated. A raw
4250
+ // endpoint runner stays gated even in confirmMode "off".
4251
+ if (!dryRun) {
4252
+ const def = WRITES[name];
4253
+ const held = await gate(
4254
+ "write.run",
4255
+ { name, risk: def?.risk, verified: def?.verified, args: args || {} },
4256
+ { confirm, ticket },
4257
+ extra,
4258
+ { always: true },
4259
+ );
4260
+ if (held) return held;
4261
+ }
4262
+ try {
4263
+ const data = await callWrite(api, name, args || {}, { dryRun: dryRun === true });
4264
+ return ok(dryRun ? data : { name, data: data ?? null });
4265
+ } catch (e) {
4266
+ if (!(e instanceof ReadUsageError)) console.error("[mcp-tools] zalo_write error:", e.message);
4267
+ return err(e.message);
4268
+ }
4269
+ },
4270
+ );
2969
4271
  }