agent-messenger 2.30.0 → 2.31.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 (233) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/bun.lock +42 -2
  3. package/dist/package.json +2 -2
  4. package/dist/src/cli.d.ts.map +1 -1
  5. package/dist/src/cli.js.map +1 -1
  6. package/dist/src/platforms/channeltalk/cli.d.ts.map +1 -1
  7. package/dist/src/platforms/channeltalk/client.d.ts.map +1 -1
  8. package/dist/src/platforms/channeltalk/commands/auth.js.map +1 -1
  9. package/dist/src/platforms/channeltalk/commands/bot.js.map +1 -1
  10. package/dist/src/platforms/channeltalk/commands/chat.js.map +1 -1
  11. package/dist/src/platforms/channeltalk/commands/group.js.map +1 -1
  12. package/dist/src/platforms/channeltalk/commands/manager.js.map +1 -1
  13. package/dist/src/platforms/channeltalk/commands/message.js.map +1 -1
  14. package/dist/src/platforms/channeltalk/commands/shared.js.map +1 -1
  15. package/dist/src/platforms/channeltalk/commands/snapshot.js.map +1 -1
  16. package/dist/src/platforms/channeltalk/commands/whoami.js.map +1 -1
  17. package/dist/src/platforms/channeltalk/credential-manager.d.ts.map +1 -1
  18. package/dist/src/platforms/channeltalk/ensure-auth.d.ts.map +1 -1
  19. package/dist/src/platforms/channeltalk/token-extractor.d.ts.map +1 -1
  20. package/dist/src/platforms/channeltalk/token-extractor.js.map +1 -1
  21. package/dist/src/platforms/channeltalk/types.d.ts.map +1 -1
  22. package/dist/src/platforms/channeltalkbot/cli.d.ts.map +1 -1
  23. package/dist/src/platforms/channeltalkbot/client.d.ts.map +1 -1
  24. package/dist/src/platforms/channeltalkbot/client.js.map +1 -1
  25. package/dist/src/platforms/channeltalkbot/credential-manager.d.ts.map +1 -1
  26. package/dist/src/platforms/channeltalkbot/types.d.ts +1 -1
  27. package/dist/src/platforms/channeltalkbot/types.d.ts.map +1 -1
  28. package/dist/src/platforms/discord/cli.d.ts.map +1 -1
  29. package/dist/src/platforms/discord/client.d.ts.map +1 -1
  30. package/dist/src/platforms/discord/client.js +36 -17
  31. package/dist/src/platforms/discord/client.js.map +1 -1
  32. package/dist/src/platforms/discord/credential-manager.d.ts.map +1 -1
  33. package/dist/src/platforms/discord/listener.d.ts.map +1 -1
  34. package/dist/src/platforms/discord/listener.js.map +1 -1
  35. package/dist/src/platforms/discord/remote-auth.js.map +1 -1
  36. package/dist/src/platforms/discord/super-properties.js.map +1 -1
  37. package/dist/src/platforms/discord/token-extractor.d.ts.map +1 -1
  38. package/dist/src/platforms/discord/token-extractor.js.map +1 -1
  39. package/dist/src/platforms/discord/types.d.ts +13 -0
  40. package/dist/src/platforms/discord/types.d.ts.map +1 -1
  41. package/dist/src/platforms/discord/types.js +6 -0
  42. package/dist/src/platforms/discord/types.js.map +1 -1
  43. package/dist/src/platforms/discordbot/cli.d.ts.map +1 -1
  44. package/dist/src/platforms/discordbot/client.d.ts.map +1 -1
  45. package/dist/src/platforms/discordbot/client.js.map +1 -1
  46. package/dist/src/platforms/discordbot/credential-manager.d.ts.map +1 -1
  47. package/dist/src/platforms/discordbot/listener.d.ts.map +1 -1
  48. package/dist/src/platforms/discordbot/listener.js.map +1 -1
  49. package/dist/src/platforms/discordbot/types.d.ts.map +1 -1
  50. package/dist/src/platforms/imessage/cli.d.ts.map +1 -1
  51. package/dist/src/platforms/imessage/client.d.ts.map +1 -1
  52. package/dist/src/platforms/imessage/client.js.map +1 -1
  53. package/dist/src/platforms/imessage/credential-manager.d.ts.map +1 -1
  54. package/dist/src/platforms/imessage/errors.js.map +1 -1
  55. package/dist/src/platforms/imessage/rpc.d.ts.map +1 -1
  56. package/dist/src/platforms/imessage/rpc.js.map +1 -1
  57. package/dist/src/platforms/imessage/types.d.ts.map +1 -1
  58. package/dist/src/platforms/imessage/types.js.map +1 -1
  59. package/dist/src/platforms/instagram/cli.d.ts.map +1 -1
  60. package/dist/src/platforms/instagram/client.d.ts.map +1 -1
  61. package/dist/src/platforms/instagram/client.js.map +1 -1
  62. package/dist/src/platforms/instagram/credential-manager.d.ts.map +1 -1
  63. package/dist/src/platforms/instagram/hybrid-listener.d.ts.map +1 -1
  64. package/dist/src/platforms/instagram/hybrid-listener.js.map +1 -1
  65. package/dist/src/platforms/instagram/listener.d.ts.map +1 -1
  66. package/dist/src/platforms/instagram/mqtt/thrift.d.ts.map +1 -1
  67. package/dist/src/platforms/instagram/mqtt/transport.d.ts.map +1 -1
  68. package/dist/src/platforms/instagram/mqtt/transport.js.map +1 -1
  69. package/dist/src/platforms/instagram/realtime-listener.d.ts.map +1 -1
  70. package/dist/src/platforms/instagram/realtime-listener.js.map +1 -1
  71. package/dist/src/platforms/instagram/token-extractor.d.ts.map +1 -1
  72. package/dist/src/platforms/instagram/types.d.ts.map +1 -1
  73. package/dist/src/platforms/instagram/types.js.map +1 -1
  74. package/dist/src/platforms/kakaotalk/cli.d.ts.map +1 -1
  75. package/dist/src/platforms/kakaotalk/client.d.ts +5 -1
  76. package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
  77. package/dist/src/platforms/kakaotalk/client.js +55 -0
  78. package/dist/src/platforms/kakaotalk/client.js.map +1 -1
  79. package/dist/src/platforms/kakaotalk/commands/chat.d.ts.map +1 -1
  80. package/dist/src/platforms/kakaotalk/commands/chat.js +16 -1
  81. package/dist/src/platforms/kakaotalk/commands/chat.js.map +1 -1
  82. package/dist/src/platforms/kakaotalk/credential-manager.d.ts.map +1 -1
  83. package/dist/src/platforms/kakaotalk/index.d.ts +2 -2
  84. package/dist/src/platforms/kakaotalk/index.d.ts.map +1 -1
  85. package/dist/src/platforms/kakaotalk/index.js +1 -1
  86. package/dist/src/platforms/kakaotalk/index.js.map +1 -1
  87. package/dist/src/platforms/kakaotalk/listener.d.ts.map +1 -1
  88. package/dist/src/platforms/kakaotalk/listener.js.map +1 -1
  89. package/dist/src/platforms/kakaotalk/protocol/connection.d.ts.map +1 -1
  90. package/dist/src/platforms/kakaotalk/protocol/connection.js.map +1 -1
  91. package/dist/src/platforms/kakaotalk/protocol/crypto.d.ts.map +1 -1
  92. package/dist/src/platforms/kakaotalk/protocol/session.d.ts +23 -0
  93. package/dist/src/platforms/kakaotalk/protocol/session.d.ts.map +1 -1
  94. package/dist/src/platforms/kakaotalk/protocol/session.js +50 -0
  95. package/dist/src/platforms/kakaotalk/protocol/session.js.map +1 -1
  96. package/dist/src/platforms/kakaotalk/token-extractor.d.ts.map +1 -1
  97. package/dist/src/platforms/kakaotalk/types.d.ts +30 -10
  98. package/dist/src/platforms/kakaotalk/types.d.ts.map +1 -1
  99. package/dist/src/platforms/kakaotalk/types.js +10 -0
  100. package/dist/src/platforms/kakaotalk/types.js.map +1 -1
  101. package/dist/src/platforms/line/cli.d.ts.map +1 -1
  102. package/dist/src/platforms/line/client.d.ts.map +1 -1
  103. package/dist/src/platforms/line/credential-manager.d.ts.map +1 -1
  104. package/dist/src/platforms/line/listener.d.ts.map +1 -1
  105. package/dist/src/platforms/line/types.d.ts +6 -6
  106. package/dist/src/platforms/line/types.d.ts.map +1 -1
  107. package/dist/src/platforms/slack/cli.d.ts.map +1 -1
  108. package/dist/src/platforms/slack/client.d.ts.map +1 -1
  109. package/dist/src/platforms/slack/client.js.map +1 -1
  110. package/dist/src/platforms/slack/credential-manager.d.ts.map +1 -1
  111. package/dist/src/platforms/slack/ensure-auth.js.map +1 -1
  112. package/dist/src/platforms/slack/listener.d.ts.map +1 -1
  113. package/dist/src/platforms/slack/qr-http-login.js.map +1 -1
  114. package/dist/src/platforms/slack/token-extractor.d.ts.map +1 -1
  115. package/dist/src/platforms/slack/token-extractor.js.map +1 -1
  116. package/dist/src/platforms/slackbot/cli.d.ts.map +1 -1
  117. package/dist/src/platforms/slackbot/client.d.ts.map +1 -1
  118. package/dist/src/platforms/slackbot/credential-manager.d.ts.map +1 -1
  119. package/dist/src/platforms/slackbot/listener.d.ts.map +1 -1
  120. package/dist/src/platforms/slackbot/listener.js.map +1 -1
  121. package/dist/src/platforms/slackbot/types.d.ts.map +1 -1
  122. package/dist/src/platforms/teams/app-config.js.map +1 -1
  123. package/dist/src/platforms/teams/cli.d.ts.map +1 -1
  124. package/dist/src/platforms/teams/client.d.ts.map +1 -1
  125. package/dist/src/platforms/teams/client.js.map +1 -1
  126. package/dist/src/platforms/teams/credential-manager.d.ts.map +1 -1
  127. package/dist/src/platforms/teams/device-code.js.map +1 -1
  128. package/dist/src/platforms/teams/device-login.d.ts.map +1 -1
  129. package/dist/src/platforms/teams/device-login.js.map +1 -1
  130. package/dist/src/platforms/teams/listener.d.ts.map +1 -1
  131. package/dist/src/platforms/teams/token-extractor.d.ts.map +1 -1
  132. package/dist/src/platforms/teams/token-extractor.js.map +1 -1
  133. package/dist/src/platforms/teams/token-provider.d.ts.map +1 -1
  134. package/dist/src/platforms/teams/token-provider.js.map +1 -1
  135. package/dist/src/platforms/teams/types.d.ts +14 -14
  136. package/dist/src/platforms/teams/types.d.ts.map +1 -1
  137. package/dist/src/platforms/telegram/cli.d.ts.map +1 -1
  138. package/dist/src/platforms/telegram/client.d.ts.map +1 -1
  139. package/dist/src/platforms/telegram/client.js.map +1 -1
  140. package/dist/src/platforms/telegram/commands/shared.js.map +1 -1
  141. package/dist/src/platforms/telegram/credential-manager.d.ts.map +1 -1
  142. package/dist/src/platforms/telegram/tdlib.d.ts.map +1 -1
  143. package/dist/src/platforms/telegram/types.d.ts.map +1 -1
  144. package/dist/src/platforms/telegram/types.js.map +1 -1
  145. package/dist/src/platforms/telegrambot/cli.d.ts.map +1 -1
  146. package/dist/src/platforms/telegrambot/client.d.ts.map +1 -1
  147. package/dist/src/platforms/telegrambot/credential-manager.d.ts.map +1 -1
  148. package/dist/src/platforms/telegrambot/listener.d.ts.map +1 -1
  149. package/dist/src/platforms/telegrambot/types.d.ts.map +1 -1
  150. package/dist/src/platforms/webex/cli.d.ts.map +1 -1
  151. package/dist/src/platforms/webex/client.d.ts.map +1 -1
  152. package/dist/src/platforms/webex/client.js.map +1 -1
  153. package/dist/src/platforms/webex/credential-manager.d.ts.map +1 -1
  154. package/dist/src/platforms/webex/encryption.d.ts.map +1 -1
  155. package/dist/src/platforms/webex/kms-key-provider.d.ts.map +1 -1
  156. package/dist/src/platforms/webex/listener.d.ts.map +1 -1
  157. package/dist/src/platforms/webex/listener.js.map +1 -1
  158. package/dist/src/platforms/webex/token-extractor.d.ts.map +1 -1
  159. package/dist/src/platforms/webex/types.d.ts +4 -4
  160. package/dist/src/platforms/webex/types.d.ts.map +1 -1
  161. package/dist/src/platforms/webexbot/cli.d.ts.map +1 -1
  162. package/dist/src/platforms/webexbot/client.d.ts.map +1 -1
  163. package/dist/src/platforms/webexbot/credential-manager.d.ts.map +1 -1
  164. package/dist/src/platforms/webexbot/listener.d.ts.map +1 -1
  165. package/dist/src/platforms/webexbot/types.d.ts.map +1 -1
  166. package/dist/src/platforms/wechatbot/cli.d.ts.map +1 -1
  167. package/dist/src/platforms/wechatbot/client.d.ts.map +1 -1
  168. package/dist/src/platforms/wechatbot/credential-manager.d.ts.map +1 -1
  169. package/dist/src/platforms/wechatbot/types.d.ts.map +1 -1
  170. package/dist/src/platforms/whatsapp/cli.d.ts.map +1 -1
  171. package/dist/src/platforms/whatsapp/client.d.ts.map +1 -1
  172. package/dist/src/platforms/whatsapp/credential-manager.d.ts.map +1 -1
  173. package/dist/src/platforms/whatsapp/types.d.ts.map +1 -1
  174. package/dist/src/platforms/whatsapp/types.js.map +1 -1
  175. package/dist/src/platforms/whatsappbot/cli.d.ts.map +1 -1
  176. package/dist/src/platforms/whatsappbot/client.d.ts.map +1 -1
  177. package/dist/src/platforms/whatsappbot/credential-manager.d.ts.map +1 -1
  178. package/dist/src/platforms/whatsappbot/types.d.ts.map +1 -1
  179. package/dist/src/shared/chromium/browsers.js.map +1 -1
  180. package/dist/src/shared/chromium/cli-options.js.map +1 -1
  181. package/dist/src/shared/chromium/cookie-reader.d.ts.map +1 -1
  182. package/dist/src/shared/chromium/decryptor.d.ts.map +1 -1
  183. package/dist/src/shared/sqlite.js.map +1 -1
  184. package/dist/src/shared/utils/derived-key-cache.d.ts.map +1 -1
  185. package/dist/src/tui/adapters/channeltalk-adapter.d.ts.map +1 -1
  186. package/dist/src/tui/adapters/discord-adapter.d.ts.map +1 -1
  187. package/dist/src/tui/adapters/imessage-adapter.d.ts.map +1 -1
  188. package/dist/src/tui/adapters/instagram-adapter.d.ts.map +1 -1
  189. package/dist/src/tui/adapters/kakaotalk-adapter.d.ts.map +1 -1
  190. package/dist/src/tui/adapters/line-adapter.d.ts.map +1 -1
  191. package/dist/src/tui/adapters/slack-adapter.d.ts.map +1 -1
  192. package/dist/src/tui/adapters/teams-adapter.d.ts.map +1 -1
  193. package/dist/src/tui/adapters/telegram-adapter.d.ts.map +1 -1
  194. package/dist/src/tui/adapters/telegram-adapter.js.map +1 -1
  195. package/dist/src/tui/adapters/webex-adapter.d.ts.map +1 -1
  196. package/dist/src/tui/adapters/whatsapp-adapter.d.ts.map +1 -1
  197. package/dist/src/tui/app.js.map +1 -1
  198. package/dist/src/tui/views/channel-picker.d.ts.map +1 -1
  199. package/dist/src/tui/views/workspace-picker.d.ts.map +1 -1
  200. package/docs/content/docs/cli/kakaotalk.mdx +16 -1
  201. package/docs/content/docs/sdk/kakaotalk.mdx +39 -0
  202. package/package.json +2 -2
  203. package/skills/agent-channeltalk/SKILL.md +1 -1
  204. package/skills/agent-channeltalkbot/SKILL.md +1 -1
  205. package/skills/agent-discord/SKILL.md +1 -1
  206. package/skills/agent-discordbot/SKILL.md +1 -1
  207. package/skills/agent-imessage/SKILL.md +1 -1
  208. package/skills/agent-instagram/SKILL.md +1 -1
  209. package/skills/agent-kakaotalk/SKILL.md +19 -2
  210. package/skills/agent-line/SKILL.md +1 -1
  211. package/skills/agent-slack/SKILL.md +1 -1
  212. package/skills/agent-slackbot/SKILL.md +1 -1
  213. package/skills/agent-teams/SKILL.md +1 -1
  214. package/skills/agent-telegram/SKILL.md +1 -1
  215. package/skills/agent-telegrambot/SKILL.md +1 -1
  216. package/skills/agent-webex/SKILL.md +1 -1
  217. package/skills/agent-webexbot/SKILL.md +1 -1
  218. package/skills/agent-wechatbot/SKILL.md +1 -1
  219. package/skills/agent-whatsapp/SKILL.md +1 -1
  220. package/skills/agent-whatsappbot/SKILL.md +1 -1
  221. package/src/platforms/discord/client.test.ts +121 -0
  222. package/src/platforms/discord/client.ts +45 -22
  223. package/src/platforms/discord/types.ts +16 -0
  224. package/src/platforms/kakaotalk/client.test.ts +194 -1
  225. package/src/platforms/kakaotalk/client.ts +58 -0
  226. package/src/platforms/kakaotalk/commands/chat.test.ts +24 -0
  227. package/src/platforms/kakaotalk/commands/chat.ts +23 -0
  228. package/src/platforms/kakaotalk/index.test.ts +10 -0
  229. package/src/platforms/kakaotalk/index.ts +4 -0
  230. package/src/platforms/kakaotalk/protocol/session.test.ts +113 -0
  231. package/src/platforms/kakaotalk/protocol/session.ts +56 -0
  232. package/src/platforms/kakaotalk/types.ts +24 -0
  233. package/tsconfig.json +8 -8
@@ -126,6 +126,43 @@ await client.sendAttachment('9876543210', [
126
126
 
127
127
  The typed helpers `sendPhoto`, `sendVideo`, `sendAudio`, `sendFile`, and `sendMultiPhoto` remain available when you want to bypass MIME sniffing and dispatch to a specific message_type explicitly.
128
128
 
129
+ ### Leave Chat
130
+
131
+ ```typescript
132
+ const result = await client.leaveChat('9876543210')
133
+ // → { success: true, status_code: 0, chat_id: '9876543210' }
134
+ ```
135
+
136
+ Leaves a chat room by its chat ID. Works on 1:1, group, open, and PlusChat rooms. Returns `success: true` on a successful leave. The departed chat is immediately evicted from the client's cached chat list — a subsequent `getChats()` call will not include it.
137
+
138
+ ### Typing Indicator
139
+
140
+ ```typescript
141
+ // Fire a single typing pulse — the recipient sees the animated "…" dots
142
+ const result = await client.sendTyping('9876543210')
143
+ // → KakaoTypingResult { success: true, status_code: 0, chat_id: '9876543210' }
144
+
145
+ // For OpenChat rooms, pass the linkId
146
+ await client.sendTyping('9876543210', { linkId: '77777' })
147
+ ```
148
+
149
+ The KakaoTalk client-side lifetime is ~5s per pulse — to keep the indicator visible while generating a long reply, re-fire faster than that (e.g. every 3s):
150
+
151
+ ```typescript
152
+ const interval = setInterval(() => {
153
+ client.sendTyping('9876543210').catch(() => {}) // ignore transient errors
154
+ }, 3000)
155
+
156
+ try {
157
+ const reply = await generateReply()
158
+ await client.sendMessage('9876543210', reply)
159
+ } finally {
160
+ clearInterval(interval)
161
+ }
162
+ ```
163
+
164
+ Errors follow the same shape as `markRead`: transport-level failures (non-zero `statusCode`, including the synthetic `-1` LocoConnection emits on disconnect) throw `KakaoTalkError('send_typing_failed')` so `executeWithReconnect` can retry on a fresh session. Non-numeric `chatId` / `linkId` throw `invalid_chat_id` / `invalid_link_id`.
165
+
129
166
  ### Cleanup
130
167
 
131
168
  ```typescript
@@ -239,6 +276,7 @@ import type {
239
276
  KakaoMember,
240
277
  KakaoMessage,
241
278
  KakaoSendResult,
279
+ KakaoTypingResult,
242
280
  KakaoAccountCredentials,
243
281
  KakaoConfig,
244
282
  KakaoDeviceType,
@@ -256,6 +294,7 @@ import {
256
294
  KakaoMemberSchema,
257
295
  KakaoMessageSchema,
258
296
  KakaoSendResultSchema,
297
+ KakaoTypingResultSchema,
259
298
  KakaoAccountCredentialsSchema,
260
299
  KakaoConfigSchema,
261
300
  } from 'agent-messenger/kakaotalk'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-messenger",
3
- "version": "2.30.0",
3
+ "version": "2.31.0",
4
4
  "description": "Multi-platform messaging CLI for AI agents (Slack, Discord, Teams, Webex, Telegram, Telegram Bot, WhatsApp, LINE, iMessage, Instagram, KakaoTalk, Channel Talk)",
5
5
  "repository": {
6
6
  "type": "git",
@@ -211,7 +211,7 @@
211
211
  "oxlint": "^1.51.0",
212
212
  "tsc-alias": "^1.8.16",
213
213
  "tsx": "^4.21.0",
214
- "typescript": "^5.9.3"
214
+ "typescript": "^7.0.2"
215
215
  },
216
216
  "optionalDependencies": {
217
217
  "koffi": "^2.15.2",
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-channeltalk
3
3
  description: Interact with Channel Talk using extracted desktop app or browser credentials - read chats, send messages, search messages, manage groups
4
- version: 2.30.0
4
+ version: 2.31.0
5
5
  allowed-tools: Bash(agent-channeltalk:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-channeltalkbot
3
3
  description: Interact with Channel Talk workspaces using API credentials - send messages, read chats, manage groups and bots
4
- version: 2.30.0
4
+ version: 2.31.0
5
5
  allowed-tools: Bash(agent-channeltalkbot:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-discord
3
3
  description: Interact with Discord servers - send messages, read channels, manage reactions
4
- version: 2.30.0
4
+ version: 2.31.0
5
5
  allowed-tools: Bash(agent-discord:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-discordbot
3
3
  description: Interact with Discord servers using bot tokens - send messages, read channels, manage reactions
4
- version: 2.30.0
4
+ version: 2.31.0
5
5
  allowed-tools: Bash(agent-discordbot:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-imessage
3
3
  description: Interact with iMessage on a Mac via the imsg tool - send messages, read chats, watch for new messages
4
- version: 2.30.0
4
+ version: 2.31.0
5
5
  allowed-tools: Bash(agent-imessage:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-instagram
3
3
  description: Interact with Instagram DMs - send messages, read conversations, manage accounts
4
- version: 2.30.0
4
+ version: 2.31.0
5
5
  allowed-tools: Bash(agent-instagram:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-kakaotalk
3
3
  description: Interact with KakaoTalk - send messages, read chats, manage conversations
4
- version: 2.30.0
4
+ version: 2.31.0
5
5
  allowed-tools: Bash(agent-kakaotalk:*)
6
6
  metadata:
7
7
  openclaw:
@@ -303,6 +303,23 @@ Output includes:
303
303
  - `unread_count` — unread message count
304
304
  - `last_message` — most recent message preview, including `author_name` when the sender's nickname is known from the chat list (otherwise `null`)
305
305
 
306
+ ### Chat Leave
307
+
308
+ ```bash
309
+ # Leave a chat room (works on 1:1, group, open, and PlusChat rooms)
310
+ agent-kakaotalk chat leave <chat-id>
311
+ agent-kakaotalk chat leave <chat-id> --pretty
312
+ agent-kakaotalk chat leave <chat-id> --account <account-id>
313
+ ```
314
+
315
+ Output (JSON by default; `--pretty` pretty-prints the same JSON):
316
+
317
+ ```json
318
+ { "success": true, "status_code": 0, "chat_id": "9876543210" }
319
+ ```
320
+
321
+ The process exits non-zero when `success` is `false`.
322
+
306
323
  ### Member Commands
307
324
 
308
325
  ```bash
@@ -623,7 +640,7 @@ See the [KakaoTalk SDK documentation](https://agent-messenger.dev/docs/sdk/kakao
623
640
  ## Limitations
624
641
 
625
642
  - Auto-extraction of email/password from the desktop app is **macOS and Windows only** (KakaoTalk desktop is not available on Linux). Linux users must pass `--email` and `--password` (or `--password-file`) explicitly — the LOCO protocol, login flow, and all messaging features work on Linux.
626
- - No channel/chat room creation or management
643
+ - No chat room creation
627
644
  - No friend list management
628
645
  - No reactions or emoji
629
646
  - No message editing or deletion
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-line
3
3
  description: Interact with LINE - send messages, read chats, manage conversations
4
- version: 2.30.0
4
+ version: 2.31.0
5
5
  allowed-tools: Bash(agent-line:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-slack
3
3
  description: Interact with Slack workspaces - send messages, read channels, manage reactions
4
- version: 2.30.0
4
+ version: 2.31.0
5
5
  allowed-tools: Bash(agent-slack:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-slackbot
3
3
  description: Interact with Slack workspaces using bot tokens - send messages, read channels, manage reactions
4
- version: 2.30.0
4
+ version: 2.31.0
5
5
  allowed-tools: Bash(agent-slackbot:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-teams
3
3
  description: Interact with Microsoft Teams - send messages, read channels, manage reactions
4
- version: 2.30.0
4
+ version: 2.31.0
5
5
  allowed-tools: Bash(agent-teams:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-telegram
3
3
  description: Interact with Telegram through TDLib - authenticate, inspect chats, and send messages
4
- version: 2.30.0
4
+ version: 2.31.0
5
5
  allowed-tools: Bash(agent-telegram:*)
6
6
  ---
7
7
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-telegrambot
3
3
  description: Interact with Telegram using bot tokens - send messages, read chats, manage reactions
4
- version: 2.30.0
4
+ version: 2.31.0
5
5
  allowed-tools: Bash(agent-telegrambot:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-webex
3
3
  description: Interact with Cisco Webex - send messages, read spaces, manage memberships
4
- version: 2.30.0
4
+ version: 2.31.0
5
5
  allowed-tools: Bash(agent-webex:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-webexbot
3
3
  description: Interact with Cisco Webex using bot tokens - send messages, reply in threads, upload and download files, look up people, read spaces, manage memberships, stream real-time events
4
- version: 2.30.0
4
+ version: 2.31.0
5
5
  allowed-tools: Bash(agent-webexbot:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-wechatbot
3
3
  description: Interact with WeChat Official Account using API credentials - send messages, manage templates, list followers
4
- version: 2.30.0
4
+ version: 2.31.0
5
5
  allowed-tools: Bash(agent-wechatbot:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-whatsapp
3
3
  description: Interact with WhatsApp - send messages, read chats, manage conversations
4
- version: 2.30.0
4
+ version: 2.31.0
5
5
  allowed-tools: Bash(agent-whatsapp:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-whatsappbot
3
3
  description: Interact with WhatsApp using Cloud API credentials - send messages, manage templates
4
- version: 2.30.0
4
+ version: 2.31.0
5
5
  allowed-tools: Bash(agent-whatsappbot:*)
6
6
  metadata:
7
7
  openclaw:
@@ -447,6 +447,127 @@ describe('DiscordClient', () => {
447
447
  })
448
448
  })
449
449
 
450
+ describe('searchMessages', () => {
451
+ const searchResult = {
452
+ id: 'msg1',
453
+ channel_id: 'ch1',
454
+ guild_id: 'guild1',
455
+ author: { id: 'user1', username: 'user1' },
456
+ content: 'matching message',
457
+ timestamp: '2026-01-01T00:00:00.000Z',
458
+ hit: true,
459
+ }
460
+
461
+ it('flattens grouped search results and keeps hits', async () => {
462
+ mockResponse({
463
+ doing_deep_historical_index: false,
464
+ total_results: 1,
465
+ messages: [[searchResult, { ...searchResult, id: 'context', hit: false }]],
466
+ })
467
+
468
+ const client = await new DiscordClient().login({ token: 'test-token' })
469
+ const result = await client.searchMessages('guild1', 'matching', { limit: 25 })
470
+
471
+ expect(result).toEqual({ results: [searchResult], total: 1 })
472
+ expect(fetchCalls[0].url).toBe(
473
+ 'https://discord.com/api/v10/guilds/guild1/messages/search?content=matching&limit=25',
474
+ )
475
+ })
476
+
477
+ it('retries when the search index is not ready', async () => {
478
+ mockResponse(
479
+ {
480
+ message: 'Index not yet available. Try again later',
481
+ code: 110000,
482
+ documents_indexed: 0,
483
+ retry_after: 0,
484
+ },
485
+ 202,
486
+ )
487
+ mockResponse({
488
+ doing_deep_historical_index: false,
489
+ total_results: 1,
490
+ messages: [[searchResult]],
491
+ })
492
+
493
+ const client = await new DiscordClient().login({ token: 'test-token' })
494
+ const startTime = Date.now()
495
+ const result = await client.searchMessages('guild1', 'matching')
496
+ const elapsed = Date.now() - startTime
497
+
498
+ expect(result).toEqual({ results: [searchResult], total: 1 })
499
+ expect(elapsed).toBeGreaterThanOrEqual(50)
500
+ expect(fetchCalls).toHaveLength(2)
501
+ })
502
+
503
+ it('throws a clear error when the search index remains unavailable', async () => {
504
+ for (let attempt = 0; attempt < 4; attempt++) {
505
+ mockResponse(
506
+ {
507
+ message: 'Index not yet available. Try again later',
508
+ code: 110000,
509
+ documents_indexed: 0,
510
+ retry_after: 0.001,
511
+ },
512
+ 202,
513
+ )
514
+ }
515
+
516
+ const client = await new DiscordClient().login({ token: 'test-token' })
517
+
518
+ await expect(client.searchMessages('guild1', 'matching')).rejects.toThrow(
519
+ 'Search index is not ready after retries',
520
+ )
521
+ expect(fetchCalls).toHaveLength(4)
522
+ })
523
+
524
+ it('rejects unexpected successful response payloads', async () => {
525
+ mockResponse(
526
+ {
527
+ message: 'Unexpected response',
528
+ code: 999999,
529
+ retry_after: 0,
530
+ },
531
+ 202,
532
+ )
533
+
534
+ const client = await new DiscordClient().login({ token: 'test-token' })
535
+
536
+ try {
537
+ await client.searchMessages('guild1', 'matching')
538
+ throw new Error('Expected searchMessages to throw')
539
+ } catch (error) {
540
+ expect(error).toBeInstanceOf(DiscordError)
541
+ if (!(error instanceof DiscordError)) throw error
542
+ expect(error.code).toBe('invalid_search_response')
543
+ }
544
+ expect(fetchCalls).toHaveLength(1)
545
+ })
546
+
547
+ it('rejects malformed message and retry payloads', async () => {
548
+ mockResponse({ total_results: 1, messages: null })
549
+ mockResponse(
550
+ {
551
+ message: 'Index not yet available. Try again later',
552
+ code: 110000,
553
+ documents_indexed: 0,
554
+ retry_after: -1,
555
+ },
556
+ 202,
557
+ )
558
+
559
+ const client = await new DiscordClient().login({ token: 'test-token' })
560
+
561
+ await expect(client.searchMessages('guild1', 'matching')).rejects.toThrow(
562
+ 'Discord returned an invalid search response',
563
+ )
564
+ await expect(client.searchMessages('guild1', 'matching')).rejects.toThrow(
565
+ 'Discord returned an invalid search response',
566
+ )
567
+ expect(fetchCalls).toHaveLength(2)
568
+ })
569
+ })
570
+
450
571
  describe('rate limiting', () => {
451
572
  it('waits when bucket is exhausted before making request', async () => {
452
573
  mockResponse({ id: '1', username: 'user1' }, 200, {
@@ -1,6 +1,7 @@
1
1
  import { readFile } from 'node:fs/promises'
2
2
 
3
3
  import { getDiscordHeaders } from './super-properties'
4
+ import { DiscordSearchIndexNotReadyResponseSchema, DiscordSearchResponseSchema } from './types'
4
5
  import type {
5
6
  DiscordChannel,
6
7
  DiscordDMChannel,
@@ -11,7 +12,6 @@ import type {
11
12
  DiscordMessage,
12
13
  DiscordRelationship,
13
14
  DiscordSearchOptions,
14
- DiscordSearchResponse,
15
15
  DiscordSearchResult,
16
16
  DiscordUser,
17
17
  DiscordUserNote,
@@ -37,6 +37,7 @@ interface RateLimitBucket {
37
37
  const BASE_URL = 'https://discord.com/api/v10'
38
38
  const MAX_RETRIES = 3
39
39
  const BASE_BACKOFF_MS = 100
40
+ const MAX_SEARCH_INDEX_RETRY_MS = 30_000
40
41
 
41
42
  export class DiscordClient {
42
43
  private token: string | null = null
@@ -393,28 +394,50 @@ export class DiscordClient {
393
394
  params.set('offset', options.offset.toString())
394
395
  }
395
396
 
396
- const response = await this.request<DiscordSearchResponse>(
397
- 'GET',
398
- `/guilds/${guildId}/messages/search?${params.toString()}`,
399
- )
400
-
401
- const results = response.messages
402
- .flat()
403
- .filter((msg) => msg.hit)
404
- .map((msg) => ({
405
- id: msg.id,
406
- channel_id: msg.channel_id,
407
- guild_id: msg.guild_id,
408
- content: msg.content,
409
- author: msg.author,
410
- timestamp: msg.timestamp,
411
- hit: msg.hit,
412
- }))
413
-
414
- return {
415
- results,
416
- total: response.total_results,
397
+ const path = `/guilds/${guildId}/messages/search?${params.toString()}`
398
+
399
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
400
+ const response: unknown = await this.request('GET', path)
401
+ const searchResponse = DiscordSearchResponseSchema.safeParse(response)
402
+
403
+ if (searchResponse.success) {
404
+ const results = searchResponse.data.messages
405
+ .flat()
406
+ .filter((msg) => msg.hit)
407
+ .map((msg) => ({
408
+ id: msg.id,
409
+ channel_id: msg.channel_id,
410
+ guild_id: msg.guild_id,
411
+ content: msg.content,
412
+ author: msg.author,
413
+ timestamp: msg.timestamp,
414
+ hit: msg.hit,
415
+ }))
416
+
417
+ return {
418
+ results,
419
+ total: searchResponse.data.total_results,
420
+ }
421
+ }
422
+
423
+ const indexNotReadyResponse = DiscordSearchIndexNotReadyResponseSchema.safeParse(response)
424
+ if (!indexNotReadyResponse.success) {
425
+ throw new DiscordError('Discord returned an invalid search response', 'invalid_search_response')
426
+ }
427
+
428
+ if (attempt === MAX_RETRIES) {
429
+ throw new DiscordError('Search index is not ready after retries', 'search_index_not_ready')
430
+ }
431
+
432
+ const requestedDelayMs = indexNotReadyResponse.data.retry_after * 1000
433
+ const retryDelayMs = Math.min(
434
+ requestedDelayMs === 0 ? BASE_BACKOFF_MS : requestedDelayMs,
435
+ MAX_SEARCH_INDEX_RETRY_MS,
436
+ )
437
+ await this.sleep(retryDelayMs)
417
438
  }
439
+
440
+ throw new DiscordError('Search failed after retries', 'max_retries')
418
441
  }
419
442
 
420
443
  async getUserProfile(userId: string): Promise<DiscordUserProfile> {
@@ -109,6 +109,15 @@ export interface DiscordSearchResponse {
109
109
  messages: DiscordSearchResult[][]
110
110
  }
111
111
 
112
+ export interface DiscordSearchIndexNotReadyResponse {
113
+ message: string
114
+ code: 110000
115
+ documents_indexed: number
116
+ retry_after: number
117
+ }
118
+
119
+ export type DiscordSearchApiResponse = DiscordSearchResponse | DiscordSearchIndexNotReadyResponse
120
+
112
121
  export interface DiscordSearchOptions {
113
122
  channelId?: string
114
123
  authorId?: string
@@ -267,6 +276,13 @@ export const DiscordSearchResponseSchema = z.object({
267
276
  messages: z.array(z.array(DiscordSearchResultSchema)),
268
277
  })
269
278
 
279
+ export const DiscordSearchIndexNotReadyResponseSchema = z.object({
280
+ message: z.string(),
281
+ code: z.literal(110000),
282
+ documents_indexed: z.number(),
283
+ retry_after: z.number().nonnegative(),
284
+ })
285
+
270
286
  export const DiscordCredentialsSchema = z.object({
271
287
  token: z.string(),
272
288
  })