agent-messenger 2.12.2 → 2.13.1

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 (104) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dist/package.json +1 -1
  3. package/dist/src/platforms/discordbot/index.d.ts +2 -1
  4. package/dist/src/platforms/discordbot/index.d.ts.map +1 -1
  5. package/dist/src/platforms/discordbot/index.js.map +1 -1
  6. package/dist/src/platforms/discordbot/listener.d.ts +4 -3
  7. package/dist/src/platforms/discordbot/listener.d.ts.map +1 -1
  8. package/dist/src/platforms/discordbot/listener.js.map +1 -1
  9. package/dist/src/platforms/discordbot/types.d.ts +21 -0
  10. package/dist/src/platforms/discordbot/types.d.ts.map +1 -1
  11. package/dist/src/platforms/kakaotalk/chat-classifier.d.ts +18 -0
  12. package/dist/src/platforms/kakaotalk/chat-classifier.d.ts.map +1 -0
  13. package/dist/src/platforms/kakaotalk/chat-classifier.js +29 -0
  14. package/dist/src/platforms/kakaotalk/chat-classifier.js.map +1 -0
  15. package/dist/src/platforms/kakaotalk/cli.d.ts.map +1 -1
  16. package/dist/src/platforms/kakaotalk/cli.js +2 -1
  17. package/dist/src/platforms/kakaotalk/cli.js.map +1 -1
  18. package/dist/src/platforms/kakaotalk/client.d.ts +13 -1
  19. package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
  20. package/dist/src/platforms/kakaotalk/client.js +225 -8
  21. package/dist/src/platforms/kakaotalk/client.js.map +1 -1
  22. package/dist/src/platforms/kakaotalk/commands/chat.d.ts.map +1 -1
  23. package/dist/src/platforms/kakaotalk/commands/chat.js +2 -1
  24. package/dist/src/platforms/kakaotalk/commands/chat.js.map +1 -1
  25. package/dist/src/platforms/kakaotalk/commands/index.d.ts +1 -0
  26. package/dist/src/platforms/kakaotalk/commands/index.d.ts.map +1 -1
  27. package/dist/src/platforms/kakaotalk/commands/index.js +1 -0
  28. package/dist/src/platforms/kakaotalk/commands/index.js.map +1 -1
  29. package/dist/src/platforms/kakaotalk/commands/member.d.ts +3 -0
  30. package/dist/src/platforms/kakaotalk/commands/member.d.ts.map +1 -0
  31. package/dist/src/platforms/kakaotalk/commands/member.js +22 -0
  32. package/dist/src/platforms/kakaotalk/commands/member.js.map +1 -0
  33. package/dist/src/platforms/kakaotalk/index.d.ts +4 -2
  34. package/dist/src/platforms/kakaotalk/index.d.ts.map +1 -1
  35. package/dist/src/platforms/kakaotalk/index.js +2 -1
  36. package/dist/src/platforms/kakaotalk/index.js.map +1 -1
  37. package/dist/src/platforms/kakaotalk/listener.d.ts.map +1 -1
  38. package/dist/src/platforms/kakaotalk/listener.js +5 -2
  39. package/dist/src/platforms/kakaotalk/listener.js.map +1 -1
  40. package/dist/src/platforms/kakaotalk/protocol/session.d.ts +28 -0
  41. package/dist/src/platforms/kakaotalk/protocol/session.d.ts.map +1 -1
  42. package/dist/src/platforms/kakaotalk/protocol/session.js +44 -0
  43. package/dist/src/platforms/kakaotalk/protocol/session.js.map +1 -1
  44. package/dist/src/platforms/kakaotalk/types.d.ts +37 -0
  45. package/dist/src/platforms/kakaotalk/types.d.ts.map +1 -1
  46. package/dist/src/platforms/kakaotalk/types.js +17 -0
  47. package/dist/src/platforms/kakaotalk/types.js.map +1 -1
  48. package/dist/src/platforms/slackbot/client.d.ts +5 -0
  49. package/dist/src/platforms/slackbot/client.d.ts.map +1 -1
  50. package/dist/src/platforms/slackbot/client.js +5 -0
  51. package/dist/src/platforms/slackbot/client.js.map +1 -1
  52. package/dist/src/platforms/slackbot/index.d.ts +2 -2
  53. package/dist/src/platforms/slackbot/index.d.ts.map +1 -1
  54. package/dist/src/platforms/slackbot/index.js +1 -1
  55. package/dist/src/platforms/slackbot/index.js.map +1 -1
  56. package/dist/src/tui/adapters/kakaotalk-adapter.js +3 -3
  57. package/dist/src/tui/adapters/kakaotalk-adapter.js.map +1 -1
  58. package/docs/content/docs/cli/kakaotalk.mdx +26 -1
  59. package/docs/content/docs/sdk/kakaotalk.mdx +45 -13
  60. package/docs/content/docs/sdk/slackbot.mdx +11 -0
  61. package/package.json +1 -1
  62. package/scripts/kakao-loco-capture.ts +466 -0
  63. package/skills/agent-channeltalk/SKILL.md +1 -1
  64. package/skills/agent-channeltalkbot/SKILL.md +1 -1
  65. package/skills/agent-discord/SKILL.md +1 -1
  66. package/skills/agent-discordbot/SKILL.md +1 -1
  67. package/skills/agent-instagram/SKILL.md +1 -1
  68. package/skills/agent-kakaotalk/SKILL.md +30 -3
  69. package/skills/agent-kakaotalk/references/common-patterns.md +49 -1
  70. package/skills/agent-line/SKILL.md +1 -1
  71. package/skills/agent-slack/SKILL.md +1 -1
  72. package/skills/agent-slackbot/SKILL.md +1 -2
  73. package/skills/agent-teams/SKILL.md +1 -1
  74. package/skills/agent-telegram/SKILL.md +1 -1
  75. package/skills/agent-telegrambot/SKILL.md +1 -1
  76. package/skills/agent-webex/SKILL.md +1 -1
  77. package/skills/agent-wechatbot/SKILL.md +1 -1
  78. package/skills/agent-whatsapp/SKILL.md +1 -1
  79. package/skills/agent-whatsappbot/SKILL.md +1 -1
  80. package/src/platforms/discordbot/index.test.ts +76 -0
  81. package/src/platforms/discordbot/index.ts +3 -0
  82. package/src/platforms/discordbot/listener.test.ts +41 -0
  83. package/src/platforms/discordbot/listener.ts +5 -1
  84. package/src/platforms/discordbot/types.ts +23 -1
  85. package/src/platforms/kakaotalk/chat-classifier.test.ts +33 -0
  86. package/src/platforms/kakaotalk/chat-classifier.ts +31 -0
  87. package/src/platforms/kakaotalk/cli.ts +2 -1
  88. package/src/platforms/kakaotalk/client.test.ts +782 -1
  89. package/src/platforms/kakaotalk/client.ts +262 -10
  90. package/src/platforms/kakaotalk/commands/chat.ts +3 -1
  91. package/src/platforms/kakaotalk/commands/index.ts +1 -0
  92. package/src/platforms/kakaotalk/commands/member.test.ts +102 -0
  93. package/src/platforms/kakaotalk/commands/member.ts +32 -0
  94. package/src/platforms/kakaotalk/index.test.ts +5 -0
  95. package/src/platforms/kakaotalk/index.ts +4 -0
  96. package/src/platforms/kakaotalk/listener.test.ts +29 -0
  97. package/src/platforms/kakaotalk/listener.ts +5 -2
  98. package/src/platforms/kakaotalk/protocol/session.ts +44 -0
  99. package/src/platforms/kakaotalk/types.ts +39 -0
  100. package/src/platforms/slackbot/client.test.ts +67 -0
  101. package/src/platforms/slackbot/client.ts +17 -1
  102. package/src/platforms/slackbot/index.test.ts +10 -0
  103. package/src/platforms/slackbot/index.ts +4 -0
  104. package/src/tui/adapters/kakaotalk-adapter.ts +3 -3
@@ -149,6 +149,50 @@ export class LocoSession {
149
149
  return this.connection.sendPacket('CHATONROOM', { chatId })
150
150
  }
151
151
 
152
+ /**
153
+ * Fetch detailed channel info (CHATINFO). Unlike CHATONROOM, this returns
154
+ * a `chatInfo` sub-document containing `chatMetas` (room title, notice, etc.)
155
+ * and `displayMembers` (user_id ↔ nickname pairs). Used to resolve the
156
+ * canonical user-set room title.
157
+ */
158
+ async getChannelInfo(chatId: Long): Promise<LocoPacket> {
159
+ if (!this.connection) throw new Error('Not connected')
160
+ return this.connection.sendPacket('CHATINFO', { chatId })
161
+ }
162
+
163
+ /**
164
+ * Fetch open-link info (INFOLINK) for one or more openchat links. The
165
+ * response body has shape `{ ols: OpenLinkStruct[] }` where each struct's
166
+ * `ln` field carries the open-chat link name — the canonical fallback when
167
+ * an open chat has no user-set TITLE meta.
168
+ */
169
+ async getOpenLinkInfo(linkIds: Long[]): Promise<LocoPacket> {
170
+ if (!this.connection) throw new Error('Not connected')
171
+ return this.connection.sendPacket('INFOLINK', { lis: linkIds })
172
+ }
173
+
174
+ /**
175
+ * Fetch the full member list for a chat (GETMEM). Response body has shape
176
+ * `{ members: NormalMemberStruct[] | OpenMemberStruct[], token: number }`.
177
+ * Use this to resolve nicknames for users not present in the chat list's
178
+ * "display members" cache — necessary for groups with more than ~5 members.
179
+ */
180
+ async getAllMembers(chatId: Long): Promise<LocoPacket> {
181
+ if (!this.connection) throw new Error('Not connected')
182
+ return this.connection.sendPacket('GETMEM', { chatId })
183
+ }
184
+
185
+ /**
186
+ * Fetch info for a specific subset of members in a chat (MEMBER). Response
187
+ * body has shape `{ chatId, members: NormalMemberStruct[] | OpenMemberStruct[] }`.
188
+ * Useful when you already have user IDs (e.g. from a CHATONROOM `mi` array
189
+ * for >100-member rooms) and only need to resolve a few of them.
190
+ */
191
+ async getMembersByIds(chatId: Long, memberIds: Long[]): Promise<LocoPacket> {
192
+ if (!this.connection) throw new Error('Not connected')
193
+ return this.connection.sendPacket('MEMBER', { chatId, memberIds })
194
+ }
195
+
152
196
  async getChatList(lastTokenId?: Long, lastChatId?: Long): Promise<LocoPacket> {
153
197
  if (!this.connection) throw new Error('Not connected')
154
198
  return this.connection.sendPacket('LCHATLIST', {
@@ -73,10 +73,12 @@ export interface KakaoChat {
73
73
  chat_id: string
74
74
  type: number
75
75
  display_name: string | null
76
+ title: string | null
76
77
  active_members: number
77
78
  unread_count: number
78
79
  last_message: {
79
80
  author_id: number
81
+ author_name: string | null
80
82
  message: string
81
83
  sent_at: number
82
84
  } | null
@@ -86,10 +88,28 @@ export interface KakaoMessage {
86
88
  log_id: string
87
89
  type: number
88
90
  author_id: number
91
+ author_name: string | null
89
92
  message: string
90
93
  sent_at: number
91
94
  }
92
95
 
96
+ export interface KakaoMember {
97
+ user_id: string
98
+ nickname: string
99
+ profile_image_url: string | null
100
+ full_profile_image_url: string | null
101
+ original_profile_image_url: string | null
102
+ status_message: string | null
103
+ country_iso: string | null
104
+ /** KakaoTalk UserType: 100=FRIEND, 1000=OPEN_PROFILE, etc. `null` when the server omits the field. */
105
+ user_type: number | null
106
+ /** Open-chat-only fields below; `null` for normal chats. */
107
+ open_token: number | null
108
+ open_profile_link_id: string | null
109
+ /** OpenChannelUserPerm bitfield: 1=OWNER, 2=NONE, 4=MANAGER, 8=BOT. Forward-compatible with future values. */
110
+ open_permission: number | null
111
+ }
112
+
93
113
  export interface KakaoSendResult {
94
114
  success: boolean
95
115
  status_code: number
@@ -102,11 +122,13 @@ export const KakaoChatSchema = z.object({
102
122
  chat_id: z.string(),
103
123
  type: z.number(),
104
124
  display_name: z.string().nullable(),
125
+ title: z.string().nullable(),
105
126
  active_members: z.number(),
106
127
  unread_count: z.number(),
107
128
  last_message: z
108
129
  .object({
109
130
  author_id: z.number(),
131
+ author_name: z.string().nullable(),
110
132
  message: z.string(),
111
133
  sent_at: z.number(),
112
134
  })
@@ -117,10 +139,25 @@ export const KakaoMessageSchema = z.object({
117
139
  log_id: z.string(),
118
140
  type: z.number(),
119
141
  author_id: z.number(),
142
+ author_name: z.string().nullable(),
120
143
  message: z.string(),
121
144
  sent_at: z.number(),
122
145
  })
123
146
 
147
+ export const KakaoMemberSchema = z.object({
148
+ user_id: z.string(),
149
+ nickname: z.string(),
150
+ profile_image_url: z.string().nullable(),
151
+ full_profile_image_url: z.string().nullable(),
152
+ original_profile_image_url: z.string().nullable(),
153
+ status_message: z.string().nullable(),
154
+ country_iso: z.string().nullable(),
155
+ user_type: z.number().nullable(),
156
+ open_token: z.number().nullable(),
157
+ open_profile_link_id: z.string().nullable(),
158
+ open_permission: z.number().nullable(),
159
+ })
160
+
124
161
  export const KakaoSendResultSchema = z.object({
125
162
  success: z.boolean(),
126
163
  status_code: z.number(),
@@ -183,6 +220,7 @@ export interface KakaoTalkPushMessageEvent {
183
220
  chat_id: string
184
221
  log_id: string
185
222
  author_id: number
223
+ author_name: string | null
186
224
  message: string
187
225
  message_type: number
188
226
  sent_at: number
@@ -228,6 +266,7 @@ export const KakaoTalkPushMessageEventSchema = z.object({
228
266
  chat_id: z.string(),
229
267
  log_id: z.string(),
230
268
  author_id: z.number(),
269
+ author_name: z.string().nullable(),
231
270
  message: z.string(),
232
271
  message_type: z.number(),
233
272
  sent_at: z.number(),
@@ -243,6 +243,73 @@ describe('SlackBotClient', () => {
243
243
  expect(result.ts).toBe('1234567890.123456')
244
244
  expect(result.text).toBe('Hello')
245
245
  })
246
+
247
+ it('does not pass blocks/attachments when omitted', async () => {
248
+ // given
249
+ const client = await new SlackBotClient().login({ token: 'xoxb-test-token' })
250
+
251
+ // when
252
+ await client.postMessage('C123', 'Hello')
253
+
254
+ // then
255
+ expect(mockChat.postMessage).toHaveBeenCalledWith({
256
+ channel: 'C123',
257
+ text: 'Hello',
258
+ thread_ts: undefined,
259
+ blocks: undefined,
260
+ attachments: undefined,
261
+ unfurl_links: undefined,
262
+ unfurl_media: undefined,
263
+ mrkdwn: undefined,
264
+ })
265
+ })
266
+
267
+ it('forwards blocks to chat.postMessage', async () => {
268
+ // given
269
+ const client = await new SlackBotClient().login({ token: 'xoxb-test-token' })
270
+ const blocks = [
271
+ {
272
+ type: 'section',
273
+ text: { type: 'mrkdwn', text: '*hello*' },
274
+ },
275
+ ]
276
+
277
+ // when
278
+ await client.postMessage('C123', 'Hello', { blocks })
279
+
280
+ // then
281
+ expect(mockChat.postMessage).toHaveBeenCalledWith(
282
+ expect.objectContaining({ channel: 'C123', text: 'Hello', blocks }),
283
+ )
284
+ })
285
+
286
+ it('forwards thread_ts, attachments, and unfurl/mrkdwn flags', async () => {
287
+ // given
288
+ const client = await new SlackBotClient().login({ token: 'xoxb-test-token' })
289
+ const attachments = [{ text: 'attached' }]
290
+
291
+ // when
292
+ await client.postMessage('C123', 'Hello', {
293
+ thread_ts: '1234567890.000001',
294
+ attachments,
295
+ unfurl_links: false,
296
+ unfurl_media: false,
297
+ mrkdwn: true,
298
+ })
299
+
300
+ // then
301
+ expect(mockChat.postMessage).toHaveBeenCalledWith(
302
+ expect.objectContaining({
303
+ channel: 'C123',
304
+ text: 'Hello',
305
+ thread_ts: '1234567890.000001',
306
+ attachments,
307
+ unfurl_links: false,
308
+ unfurl_media: false,
309
+ mrkdwn: true,
310
+ }),
311
+ )
312
+ })
246
313
  })
247
314
 
248
315
  describe('getConversationHistory', () => {
@@ -103,12 +103,28 @@ export class SlackBotClient {
103
103
  })
104
104
  }
105
105
 
106
- async postMessage(channel: string, text: string, options?: { thread_ts?: string }): Promise<SlackMessage> {
106
+ async postMessage(
107
+ channel: string,
108
+ text: string,
109
+ options?: {
110
+ thread_ts?: string
111
+ blocks?: unknown[]
112
+ attachments?: unknown[]
113
+ unfurl_links?: boolean
114
+ unfurl_media?: boolean
115
+ mrkdwn?: boolean
116
+ },
117
+ ): Promise<SlackMessage> {
107
118
  return this.withRetry(async () => {
108
119
  const response = await this.ensureAuth().chat.postMessage({
109
120
  channel,
110
121
  text,
111
122
  thread_ts: options?.thread_ts,
123
+ blocks: options?.blocks as any,
124
+ attachments: options?.attachments as any,
125
+ unfurl_links: options?.unfurl_links,
126
+ unfurl_media: options?.unfurl_media,
127
+ mrkdwn: options?.mrkdwn,
112
128
  })
113
129
  this.checkResponse(response)
114
130
 
@@ -5,8 +5,10 @@ import {
5
5
  SlackBotConfigSchema,
6
6
  SlackBotCredentialManager,
7
7
  SlackBotCredentialsSchema,
8
+ SlackBotEntrySchema,
8
9
  SlackBotError,
9
10
  SlackBotListener,
11
+ SlackBotWorkspaceSchema,
10
12
  SlackChannelSchema,
11
13
  SlackFileSchema,
12
14
  SlackMessageSchema,
@@ -38,6 +40,14 @@ it('SlackBotCredentialsSchema is exported from barrel', () => {
38
40
  expect(typeof SlackBotCredentialsSchema.parse).toBe('function')
39
41
  })
40
42
 
43
+ it('SlackBotEntrySchema is exported from barrel', () => {
44
+ expect(typeof SlackBotEntrySchema.parse).toBe('function')
45
+ })
46
+
47
+ it('SlackBotWorkspaceSchema is exported from barrel', () => {
48
+ expect(typeof SlackBotWorkspaceSchema.parse).toBe('function')
49
+ })
50
+
41
51
  it('SlackChannelSchema is exported from barrel', () => {
42
52
  expect(typeof SlackChannelSchema.parse).toBe('function')
43
53
  })
@@ -5,7 +5,9 @@ export type { SlackBotListenerOptions } from './listener'
5
5
  export type {
6
6
  SlackBotConfig,
7
7
  SlackBotCredentials,
8
+ SlackBotEntry,
8
9
  SlackBotListenerEventMap,
10
+ SlackBotWorkspace,
9
11
  SlackChannel,
10
12
  SlackFile,
11
13
  SlackMessage,
@@ -34,7 +36,9 @@ export type {
34
36
  export {
35
37
  SlackBotConfigSchema,
36
38
  SlackBotCredentialsSchema,
39
+ SlackBotEntrySchema,
37
40
  SlackBotError,
41
+ SlackBotWorkspaceSchema,
38
42
  SlackChannelSchema,
39
43
  SlackFileSchema,
40
44
  SlackMessageSchema,
@@ -26,10 +26,10 @@ export class KakaoTalkAdapter implements PlatformAdapter {
26
26
 
27
27
  async getChannels(): Promise<UnifiedChannel[]> {
28
28
  const client = this.ensureClient()
29
- const chats = await client.getChats()
29
+ const chats = await client.getChats({ resolveTitles: true })
30
30
  return chats.map((chat) => ({
31
31
  id: chat.chat_id,
32
- name: chat.display_name || `Chat ${chat.chat_id}`,
32
+ name: chat.title || chat.display_name || `Chat ${chat.chat_id}`,
33
33
  }))
34
34
  }
35
35
 
@@ -39,7 +39,7 @@ export class KakaoTalkAdapter implements PlatformAdapter {
39
39
  return messages.map((msg) => ({
40
40
  id: msg.log_id,
41
41
  channelId,
42
- author: String(msg.author_id),
42
+ author: msg.author_name || String(msg.author_id),
43
43
  content: msg.message ?? '',
44
44
  timestamp: String(msg.sent_at),
45
45
  }))