agent-messenger 2.36.0 → 2.37.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 (53) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dist/package.json +1 -1
  3. package/dist/src/platforms/kakaotalk/chat-classifier.d.ts +2 -1
  4. package/dist/src/platforms/kakaotalk/chat-classifier.d.ts.map +1 -1
  5. package/dist/src/platforms/kakaotalk/chat-classifier.js +8 -6
  6. package/dist/src/platforms/kakaotalk/chat-classifier.js.map +1 -1
  7. package/dist/src/platforms/kakaotalk/client.d.ts +21 -1
  8. package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
  9. package/dist/src/platforms/kakaotalk/client.js +272 -32
  10. package/dist/src/platforms/kakaotalk/client.js.map +1 -1
  11. package/dist/src/platforms/kakaotalk/index.d.ts +3 -3
  12. package/dist/src/platforms/kakaotalk/index.d.ts.map +1 -1
  13. package/dist/src/platforms/kakaotalk/index.js +2 -2
  14. package/dist/src/platforms/kakaotalk/index.js.map +1 -1
  15. package/dist/src/platforms/kakaotalk/types.d.ts +48 -2
  16. package/dist/src/platforms/kakaotalk/types.d.ts.map +1 -1
  17. package/dist/src/platforms/kakaotalk/types.js +13 -1
  18. package/dist/src/platforms/kakaotalk/types.js.map +1 -1
  19. package/dist/src/platforms/telegram/tdlib.d.ts.map +1 -1
  20. package/dist/src/platforms/telegram/tdlib.js +5 -2
  21. package/dist/src/platforms/telegram/tdlib.js.map +1 -1
  22. package/docs/content/docs/cli/kakaotalk.mdx +7 -1
  23. package/docs/content/docs/sdk/kakaotalk.mdx +29 -1
  24. package/package.json +1 -1
  25. package/skills/agent-channeltalk/SKILL.md +1 -1
  26. package/skills/agent-channeltalkbot/SKILL.md +1 -1
  27. package/skills/agent-discord/SKILL.md +1 -1
  28. package/skills/agent-discordbot/SKILL.md +1 -1
  29. package/skills/agent-imessage/SKILL.md +1 -1
  30. package/skills/agent-instagram/SKILL.md +1 -1
  31. package/skills/agent-kakaotalk/SKILL.md +1 -1
  32. package/skills/agent-kakaotalk/references/common-patterns.md +36 -0
  33. package/skills/agent-line/SKILL.md +1 -1
  34. package/skills/agent-slack/SKILL.md +1 -1
  35. package/skills/agent-slackbot/SKILL.md +1 -1
  36. package/skills/agent-teams/SKILL.md +1 -1
  37. package/skills/agent-telegram/SKILL.md +1 -1
  38. package/skills/agent-telegrambot/SKILL.md +1 -1
  39. package/skills/agent-webex/SKILL.md +1 -1
  40. package/skills/agent-webexbot/SKILL.md +1 -1
  41. package/skills/agent-wechatbot/SKILL.md +1 -1
  42. package/skills/agent-whatsapp/SKILL.md +1 -1
  43. package/skills/agent-whatsappbot/SKILL.md +1 -1
  44. package/src/platforms/kakaotalk/chat-classifier.test.ts +1 -1
  45. package/src/platforms/kakaotalk/chat-classifier.ts +10 -7
  46. package/src/platforms/kakaotalk/client.test.ts +593 -24
  47. package/src/platforms/kakaotalk/client.ts +337 -35
  48. package/src/platforms/kakaotalk/index.test.ts +12 -0
  49. package/src/platforms/kakaotalk/index.ts +7 -1
  50. package/src/platforms/kakaotalk/types.ts +33 -2
  51. package/src/platforms/telegram/tdlib-node-test.ts +19 -0
  52. package/src/platforms/telegram/tdlib-node.test.ts +11 -0
  53. package/src/platforms/telegram/tdlib.ts +6 -2
@@ -75,6 +75,10 @@ const titled = await client.getChats({ resolveTitles: true })
75
75
 
76
76
  // Resolve a single chat's title directly (returns null on error or missing title)
77
77
  const title = await client.getChatTitle('9876543210')
78
+
79
+ // Fetch one complete KakaoChat directly via read-only CHATINFO.
80
+ // This also resolves rooms that are not present in the login-time chat list.
81
+ const chat = await client.getChat('9876543210')
78
82
  ```
79
83
 
80
84
  ### Messages
@@ -89,6 +93,17 @@ const more = await client.getMessages('9876543210', { count: 100 })
89
93
 
90
94
  // Get messages after a known log ID
91
95
  const newer = await client.getMessages('9876543210', { from: '123456789' })
96
+
97
+ // Reconcile history without dropping older messages when a protocol response
98
+ // contains more than `count`. Continue until `complete` is true.
99
+ let cursor: string | undefined = '123456789'
100
+ do {
101
+ const page = await client.getMessagePage('9876543210', { from: cursor, count: 100 })
102
+ await consume(page.messages)
103
+ if (page.complete) break
104
+ if (!page.next_cursor) throw new Error('KakaoTalk history made no cursor progress')
105
+ cursor = page.next_cursor
106
+ } while (true)
92
107
  ```
93
108
 
94
109
  Each `KakaoMessage` carries an `author_name: string | null`. The SDK auto-populates this from the same chat-list response that already arrives at login (no extra LOCO calls). It resolves for "display members" — the small set of nicknames KakaoTalk includes in the chat list — and is `null` for everyone else.
@@ -98,14 +113,27 @@ Each `KakaoMessage` also carries an `attachment: Record<string, unknown> | null`
98
113
  ### Members
99
114
 
100
115
  ```typescript
101
- // List all members of a chat (LOCO GETMEM)
116
+ // List all members through the compatibility array API
102
117
  const members = await client.getMembers('9876543210')
103
118
  // → KakaoMember[]
104
119
 
120
+ // Read the exact snapshot contract and its consistency evidence
121
+ const snapshot = await client.getMemberSnapshot('9876543210')
122
+ // → KakaoMemberSnapshot {
123
+ // chat_id, active_members, members, complete: true,
124
+ // consistency_basis: 'stable_double_read_chatinfo_getmem'
125
+ // }
126
+
105
127
  // Resolve a specific subset by user_id (LOCO MEMBER) — useful for >100-member rooms
106
128
  const subset = await client.getMembersByIds('9876543210', ['1234567890', '9876543210'])
107
129
  ```
108
130
 
131
+ `getMemberSnapshot()` compares two read-only `CHATINFO` observations
132
+ and two complete `GETMEM` observations. It succeeds only when the active
133
+ count, display-member subset, and full member-ID set are stable. It does
134
+ not call `CHATONROOM`, so it does not enter the room or advance unread
135
+ state. Any malformed, partial, or changing snapshot fails closed.
136
+
109
137
  Each `KakaoMember` includes `user_id`, `nickname`, profile image URLs, `status_message`, `country_iso`, `user_type` (nullable; `null` when the server omits the field), and open-chat-only fields (`open_token`, `open_profile_link_id`, `open_permission` — the last is the OpenChannelUserPerm bitfield: `1`=OWNER, `2`=NONE, `4`=MANAGER, `8`=BOT).
110
138
 
111
139
  ### Sending
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-messenger",
3
- "version": "2.36.0",
3
+ "version": "2.37.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",
@@ -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.36.0
4
+ version: 2.37.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.36.0
4
+ version: 2.37.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.36.0
4
+ version: 2.37.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.36.0
4
+ version: 2.37.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.36.0
4
+ version: 2.37.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.36.0
4
+ version: 2.37.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.36.0
4
+ version: 2.37.0
5
5
  allowed-tools: Bash(agent-kakaotalk:*)
6
6
  metadata:
7
7
  openclaw:
@@ -57,6 +57,17 @@ agent-kakaotalk message send "$TARGET_CHAT" "Hey Alice!"
57
57
 
58
58
  > Note: `display_name` joins the chat's member nicknames. For the user-set room title (matching the KakaoTalk app), see [Pattern 10](#pattern-10-resolve-canonical-room-titles).
59
59
 
60
+ For an exact chat ID observed from a live event, the SDK can resolve that room
61
+ directly without depending on the login-time list:
62
+
63
+ ```typescript
64
+ const chat = await client.getChat('9876543210')
65
+ ```
66
+
67
+ `getChat()` uses the read-only `CHATINFO` owner. It preserves numeric room types
68
+ and open-chat types `OM` / `OD`; open chats without a `TITLE` meta use the
69
+ OpenLink name as a display-title fallback.
70
+
60
71
  ## Pattern 3: Send Files, Photos, Videos, and Audio
61
72
 
62
73
  **Use case**: Upload an attachment to a chat (photo, video, voice, generic file, or multi-photo gallery)
@@ -187,6 +198,31 @@ NEW_MESSAGES=$(agent-kakaotalk message list "$CHAT_ID" --from "$LAST_SEEN")
187
198
 
188
199
  **Pagination details**: The CLI prefers KakaoTalk's `MCHATLOGS` flow for history reads, fetching message batches from the requested `--from` point and returning the last N messages after deduplication and ascending sort. If that path cannot provide results, it reads the maximum log ID from the login chat-list watermark (`ll`, falling back to `l.logId`) and calls `SYNCMSG` directly only when that watermark is ahead of the current cursor. The history path never uses `CHATONROOM`, which avoids room-entry side effects. If the watermark is missing or is not ahead of the cursor, the fallback fails closed and returns `[]`. As a safety net, both message flows are capped at 50 internal pages. A warning is printed to stderr only when that cap is actually hit and the returned history may be incomplete.
189
200
 
201
+ For lossless forward-only SDK pagination, carry `next_cursor` into the next
202
+ exclusive `from` request until `complete` is true:
203
+
204
+ ```typescript
205
+ let from = '123456789'
206
+ do {
207
+ const page = await client.getMessagePage(CHAT_ID, { from, count: 80 })
208
+ for (const message of page.messages) {
209
+ await archive(message)
210
+ }
211
+ if (page.complete) break
212
+ if (page.next_cursor === null) {
213
+ throw new Error('KakaoTalk history made no forward progress')
214
+ }
215
+ from = page.next_cursor
216
+ } while (true)
217
+ ```
218
+
219
+ `from` is exclusive. `next_cursor` is the last returned log ID, or `null` when
220
+ the server made no forward progress. `complete` is true only when the available
221
+ forward range is exhausted. The SDK prefers `MCHATLOGS`; for late rooms or an
222
+ empty page it reconciles with read-only `CHATINFO` and then calls `SYNCMSG` only
223
+ when the observed maximum log ID is ahead of `from`. This path does not call
224
+ `CHATONROOM`.
225
+
190
226
  ## Pattern 7: Multi-Chat Broadcast
191
227
 
192
228
  **Use case**: Send the same message to multiple chats
@@ -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.36.0
4
+ version: 2.37.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.36.0
4
+ version: 2.37.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.36.0
4
+ version: 2.37.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.36.0
4
+ version: 2.37.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.36.0
4
+ version: 2.37.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.36.0
4
+ version: 2.37.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.36.0
4
+ version: 2.37.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.36.0
4
+ version: 2.37.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.36.0
4
+ version: 2.37.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.36.0
4
+ version: 2.37.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.36.0
4
+ version: 2.37.0
5
5
  allowed-tools: Bash(agent-whatsappbot:*)
6
6
  metadata:
7
7
  openclaw:
@@ -19,7 +19,7 @@ describe('classifyKakaoChat', () => {
19
19
  expect(classifyKakaoChat({ type: 10, active_members: 3 })).toBe('group')
20
20
  })
21
21
 
22
- for (const type of [2, 13, 14, 15, 16]) {
22
+ for (const type of [2, 13, 14, 15, 16, 'OM', 'OD'] as const) {
23
23
  it(`classifies open-chat type ${type} as open regardless of member count`, () => {
24
24
  expect(classifyKakaoChat({ type, active_members: 1 })).toBe('open')
25
25
  expect(classifyKakaoChat({ type, active_members: 2 })).toBe('open')
@@ -1,12 +1,15 @@
1
- import type { KakaoChat } from './types'
1
+ import type { KakaoChat, KakaoOpenChatType } from './types'
2
2
 
3
3
  export type KakaoChatKind = 'dm' | 'group' | 'open' | 'unknown'
4
4
 
5
- // OpenChat-family `type` codes observed on the wire. KakaoTalk's LOCO
6
- // protocol exposes a numeric `type` field with no documented mapping; these
7
- // five codes consistently identify OpenChat rooms across normal OpenChat,
8
- // OpenChat DMs, and the various OpenChat sub-types seen in production.
9
- const OPEN_CHAT_TYPE_CODES: ReadonlySet<number> = new Set([2, 13, 14, 15, 16])
5
+ // OpenChat-family `type` values observed on the wire. Login/chat-list data
6
+ // uses numeric codes while CHATINFO may return the protocol string tags
7
+ // `OM` / `OD`; both forms must share this single classification owner.
8
+ const OPEN_CHAT_TYPES: ReadonlySet<KakaoOpenChatType> = new Set([2, 13, 14, 15, 16, 'OM', 'OD'])
9
+
10
+ export function isOpenKakaoChatType(type: unknown): type is KakaoOpenChatType {
11
+ return OPEN_CHAT_TYPES.has(type as KakaoOpenChatType)
12
+ }
10
13
 
11
14
  /**
12
15
  * Classify a KakaoTalk chat as `'dm'`, `'group'`, `'open'`, or `'unknown'`.
@@ -23,7 +26,7 @@ const OPEN_CHAT_TYPE_CODES: ReadonlySet<number> = new Set([2, 13, 14, 15, 16])
23
26
  * the case defensively.
24
27
  */
25
28
  export function classifyKakaoChat(chat: Pick<KakaoChat, 'type' | 'active_members'>): KakaoChatKind {
26
- if (OPEN_CHAT_TYPE_CODES.has(chat.type)) return 'open'
29
+ if (isOpenKakaoChatType(chat.type)) return 'open'
27
30
  // active_members counts the logged-in user, so a 1:1 DM is exactly 2
28
31
  // (self + one other) and a "lone" room with only self is 1.
29
32
  if (chat.active_members <= 2) return 'dm'