agent-messenger 2.10.2 → 2.11.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 (330) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.env.template +4 -1
  3. package/README.md +77 -27
  4. package/bun.lock +26 -0
  5. package/dist/package.json +14 -1
  6. package/dist/src/platforms/channeltalk/commands/auth.d.ts +2 -1
  7. package/dist/src/platforms/channeltalk/commands/auth.d.ts.map +1 -1
  8. package/dist/src/platforms/channeltalk/commands/auth.js +5 -3
  9. package/dist/src/platforms/channeltalk/commands/auth.js.map +1 -1
  10. package/dist/src/platforms/channeltalk/token-extractor.d.ts +2 -1
  11. package/dist/src/platforms/channeltalk/token-extractor.d.ts.map +1 -1
  12. package/dist/src/platforms/channeltalk/token-extractor.js +22 -6
  13. package/dist/src/platforms/channeltalk/token-extractor.js.map +1 -1
  14. package/dist/src/platforms/channeltalkbot/cli.d.ts.map +1 -1
  15. package/dist/src/platforms/channeltalkbot/cli.js +11 -1
  16. package/dist/src/platforms/channeltalkbot/cli.js.map +1 -1
  17. package/dist/src/platforms/channeltalkbot/commands/auth.d.ts.map +1 -1
  18. package/dist/src/platforms/channeltalkbot/commands/auth.js +1 -5
  19. package/dist/src/platforms/channeltalkbot/commands/auth.js.map +1 -1
  20. package/dist/src/platforms/channeltalkbot/commands/bot.d.ts.map +1 -1
  21. package/dist/src/platforms/channeltalkbot/commands/bot.js +1 -6
  22. package/dist/src/platforms/channeltalkbot/commands/bot.js.map +1 -1
  23. package/dist/src/platforms/channeltalkbot/commands/chat.d.ts.map +1 -1
  24. package/dist/src/platforms/channeltalkbot/commands/chat.js +1 -6
  25. package/dist/src/platforms/channeltalkbot/commands/chat.js.map +1 -1
  26. package/dist/src/platforms/channeltalkbot/commands/group.d.ts.map +1 -1
  27. package/dist/src/platforms/channeltalkbot/commands/group.js +1 -6
  28. package/dist/src/platforms/channeltalkbot/commands/group.js.map +1 -1
  29. package/dist/src/platforms/channeltalkbot/commands/manager.d.ts.map +1 -1
  30. package/dist/src/platforms/channeltalkbot/commands/manager.js +1 -6
  31. package/dist/src/platforms/channeltalkbot/commands/manager.js.map +1 -1
  32. package/dist/src/platforms/channeltalkbot/commands/message.d.ts.map +1 -1
  33. package/dist/src/platforms/channeltalkbot/commands/message.js +1 -6
  34. package/dist/src/platforms/channeltalkbot/commands/message.js.map +1 -1
  35. package/dist/src/platforms/channeltalkbot/commands/whoami.d.ts.map +1 -1
  36. package/dist/src/platforms/channeltalkbot/commands/whoami.js +1 -6
  37. package/dist/src/platforms/channeltalkbot/commands/whoami.js.map +1 -1
  38. package/dist/src/platforms/channeltalkbot/credential-manager.d.ts +5 -0
  39. package/dist/src/platforms/channeltalkbot/credential-manager.d.ts.map +1 -1
  40. package/dist/src/platforms/channeltalkbot/credential-manager.js +34 -4
  41. package/dist/src/platforms/channeltalkbot/credential-manager.js.map +1 -1
  42. package/dist/src/platforms/discord/commands/auth.d.ts +1 -0
  43. package/dist/src/platforms/discord/commands/auth.d.ts.map +1 -1
  44. package/dist/src/platforms/discord/commands/auth.js +3 -1
  45. package/dist/src/platforms/discord/commands/auth.js.map +1 -1
  46. package/dist/src/platforms/discord/listener.d.ts +2 -0
  47. package/dist/src/platforms/discord/listener.d.ts.map +1 -1
  48. package/dist/src/platforms/discord/listener.js +51 -21
  49. package/dist/src/platforms/discord/listener.js.map +1 -1
  50. package/dist/src/platforms/discord/token-extractor.d.ts +2 -1
  51. package/dist/src/platforms/discord/token-extractor.d.ts.map +1 -1
  52. package/dist/src/platforms/discord/token-extractor.js +21 -6
  53. package/dist/src/platforms/discord/token-extractor.js.map +1 -1
  54. package/dist/src/platforms/discordbot/cli.d.ts.map +1 -1
  55. package/dist/src/platforms/discordbot/cli.js +12 -1
  56. package/dist/src/platforms/discordbot/cli.js.map +1 -1
  57. package/dist/src/platforms/discordbot/client.d.ts +3 -0
  58. package/dist/src/platforms/discordbot/client.d.ts.map +1 -1
  59. package/dist/src/platforms/discordbot/client.js +3 -0
  60. package/dist/src/platforms/discordbot/client.js.map +1 -1
  61. package/dist/src/platforms/discordbot/commands/auth.d.ts.map +1 -1
  62. package/dist/src/platforms/discordbot/commands/auth.js +1 -5
  63. package/dist/src/platforms/discordbot/commands/auth.js.map +1 -1
  64. package/dist/src/platforms/discordbot/commands/message.d.ts.map +1 -1
  65. package/dist/src/platforms/discordbot/commands/message.js +1 -6
  66. package/dist/src/platforms/discordbot/commands/message.js.map +1 -1
  67. package/dist/src/platforms/discordbot/commands/server.d.ts.map +1 -1
  68. package/dist/src/platforms/discordbot/commands/server.js +1 -4
  69. package/dist/src/platforms/discordbot/commands/server.js.map +1 -1
  70. package/dist/src/platforms/discordbot/commands/whoami.d.ts.map +1 -1
  71. package/dist/src/platforms/discordbot/commands/whoami.js +1 -6
  72. package/dist/src/platforms/discordbot/commands/whoami.js.map +1 -1
  73. package/dist/src/platforms/discordbot/index.d.ts +3 -1
  74. package/dist/src/platforms/discordbot/index.d.ts.map +1 -1
  75. package/dist/src/platforms/discordbot/index.js +2 -1
  76. package/dist/src/platforms/discordbot/index.js.map +1 -1
  77. package/dist/src/platforms/discordbot/listener.d.ts +43 -0
  78. package/dist/src/platforms/discordbot/listener.d.ts.map +1 -0
  79. package/dist/src/platforms/discordbot/listener.js +292 -0
  80. package/dist/src/platforms/discordbot/listener.js.map +1 -0
  81. package/dist/src/platforms/discordbot/types.d.ts +161 -0
  82. package/dist/src/platforms/discordbot/types.d.ts.map +1 -1
  83. package/dist/src/platforms/discordbot/types.js +34 -0
  84. package/dist/src/platforms/discordbot/types.js.map +1 -1
  85. package/dist/src/platforms/instagram/commands/auth.d.ts.map +1 -1
  86. package/dist/src/platforms/instagram/commands/auth.js +3 -1
  87. package/dist/src/platforms/instagram/commands/auth.js.map +1 -1
  88. package/dist/src/platforms/instagram/token-extractor.d.ts +2 -1
  89. package/dist/src/platforms/instagram/token-extractor.d.ts.map +1 -1
  90. package/dist/src/platforms/instagram/token-extractor.js +11 -2
  91. package/dist/src/platforms/instagram/token-extractor.js.map +1 -1
  92. package/dist/src/platforms/slack/commands/auth.d.ts.map +1 -1
  93. package/dist/src/platforms/slack/commands/auth.js +4 -2
  94. package/dist/src/platforms/slack/commands/auth.js.map +1 -1
  95. package/dist/src/platforms/slack/token-extractor.d.ts +4 -1
  96. package/dist/src/platforms/slack/token-extractor.d.ts.map +1 -1
  97. package/dist/src/platforms/slack/token-extractor.js +64 -15
  98. package/dist/src/platforms/slack/token-extractor.js.map +1 -1
  99. package/dist/src/platforms/slackbot/cli.d.ts.map +1 -1
  100. package/dist/src/platforms/slackbot/cli.js +15 -3
  101. package/dist/src/platforms/slackbot/cli.js.map +1 -1
  102. package/dist/src/platforms/slackbot/client.d.ts +22 -1
  103. package/dist/src/platforms/slackbot/client.d.ts.map +1 -1
  104. package/dist/src/platforms/slackbot/client.js +104 -1
  105. package/dist/src/platforms/slackbot/client.js.map +1 -1
  106. package/dist/src/platforms/slackbot/commands/auth.d.ts.map +1 -1
  107. package/dist/src/platforms/slackbot/commands/auth.js +1 -5
  108. package/dist/src/platforms/slackbot/commands/auth.js.map +1 -1
  109. package/dist/src/platforms/slackbot/commands/file.d.ts +3 -0
  110. package/dist/src/platforms/slackbot/commands/file.d.ts.map +1 -0
  111. package/dist/src/platforms/slackbot/commands/file.js +164 -0
  112. package/dist/src/platforms/slackbot/commands/file.js.map +1 -0
  113. package/dist/src/platforms/slackbot/commands/index.d.ts +1 -0
  114. package/dist/src/platforms/slackbot/commands/index.d.ts.map +1 -1
  115. package/dist/src/platforms/slackbot/commands/index.js +1 -0
  116. package/dist/src/platforms/slackbot/commands/index.js.map +1 -1
  117. package/dist/src/platforms/slackbot/commands/message.d.ts.map +1 -1
  118. package/dist/src/platforms/slackbot/commands/message.js +19 -0
  119. package/dist/src/platforms/slackbot/commands/message.js.map +1 -1
  120. package/dist/src/platforms/slackbot/commands/whoami.d.ts.map +1 -1
  121. package/dist/src/platforms/slackbot/commands/whoami.js +1 -6
  122. package/dist/src/platforms/slackbot/commands/whoami.js.map +1 -1
  123. package/dist/src/platforms/slackbot/credential-manager.d.ts +1 -0
  124. package/dist/src/platforms/slackbot/credential-manager.d.ts.map +1 -1
  125. package/dist/src/platforms/slackbot/credential-manager.js +30 -2
  126. package/dist/src/platforms/slackbot/credential-manager.js.map +1 -1
  127. package/dist/src/platforms/slackbot/index.d.ts +4 -1
  128. package/dist/src/platforms/slackbot/index.d.ts.map +1 -1
  129. package/dist/src/platforms/slackbot/index.js +1 -0
  130. package/dist/src/platforms/slackbot/index.js.map +1 -1
  131. package/dist/src/platforms/slackbot/listener.d.ts +44 -0
  132. package/dist/src/platforms/slackbot/listener.d.ts.map +1 -0
  133. package/dist/src/platforms/slackbot/listener.js +313 -0
  134. package/dist/src/platforms/slackbot/listener.js.map +1 -0
  135. package/dist/src/platforms/slackbot/types.d.ts +196 -1
  136. package/dist/src/platforms/slackbot/types.d.ts.map +1 -1
  137. package/dist/src/platforms/slackbot/types.js +4 -1
  138. package/dist/src/platforms/slackbot/types.js.map +1 -1
  139. package/dist/src/platforms/teams/commands/auth.d.ts +1 -0
  140. package/dist/src/platforms/teams/commands/auth.d.ts.map +1 -1
  141. package/dist/src/platforms/teams/commands/auth.js +37 -6
  142. package/dist/src/platforms/teams/commands/auth.js.map +1 -1
  143. package/dist/src/platforms/teams/ensure-auth.js +31 -9
  144. package/dist/src/platforms/teams/ensure-auth.js.map +1 -1
  145. package/dist/src/platforms/teams/token-extractor.d.ts +4 -1
  146. package/dist/src/platforms/teams/token-extractor.d.ts.map +1 -1
  147. package/dist/src/platforms/teams/token-extractor.js +71 -29
  148. package/dist/src/platforms/teams/token-extractor.js.map +1 -1
  149. package/dist/src/platforms/webex/commands/auth.d.ts +1 -0
  150. package/dist/src/platforms/webex/commands/auth.d.ts.map +1 -1
  151. package/dist/src/platforms/webex/commands/auth.js +3 -1
  152. package/dist/src/platforms/webex/commands/auth.js.map +1 -1
  153. package/dist/src/platforms/webex/token-extractor.d.ts +3 -1
  154. package/dist/src/platforms/webex/token-extractor.d.ts.map +1 -1
  155. package/dist/src/platforms/webex/token-extractor.js +16 -2
  156. package/dist/src/platforms/webex/token-extractor.js.map +1 -1
  157. package/dist/src/platforms/wechatbot/cli.d.ts.map +1 -1
  158. package/dist/src/platforms/wechatbot/cli.js +11 -1
  159. package/dist/src/platforms/wechatbot/cli.js.map +1 -1
  160. package/dist/src/platforms/wechatbot/commands/auth.d.ts.map +1 -1
  161. package/dist/src/platforms/wechatbot/commands/auth.js +1 -5
  162. package/dist/src/platforms/wechatbot/commands/auth.js.map +1 -1
  163. package/dist/src/platforms/wechatbot/commands/message.d.ts.map +1 -1
  164. package/dist/src/platforms/wechatbot/commands/message.js +1 -6
  165. package/dist/src/platforms/wechatbot/commands/message.js.map +1 -1
  166. package/dist/src/platforms/wechatbot/commands/template.d.ts.map +1 -1
  167. package/dist/src/platforms/wechatbot/commands/template.js +1 -6
  168. package/dist/src/platforms/wechatbot/commands/template.js.map +1 -1
  169. package/dist/src/platforms/wechatbot/commands/user.d.ts.map +1 -1
  170. package/dist/src/platforms/wechatbot/commands/user.js +1 -6
  171. package/dist/src/platforms/wechatbot/commands/user.js.map +1 -1
  172. package/dist/src/platforms/wechatbot/commands/whoami.d.ts.map +1 -1
  173. package/dist/src/platforms/wechatbot/commands/whoami.js +1 -6
  174. package/dist/src/platforms/wechatbot/commands/whoami.js.map +1 -1
  175. package/dist/src/platforms/whatsappbot/cli.d.ts.map +1 -1
  176. package/dist/src/platforms/whatsappbot/cli.js +11 -1
  177. package/dist/src/platforms/whatsappbot/cli.js.map +1 -1
  178. package/dist/src/platforms/whatsappbot/commands/auth.d.ts.map +1 -1
  179. package/dist/src/platforms/whatsappbot/commands/auth.js +1 -5
  180. package/dist/src/platforms/whatsappbot/commands/auth.js.map +1 -1
  181. package/dist/src/platforms/whatsappbot/commands/message.d.ts.map +1 -1
  182. package/dist/src/platforms/whatsappbot/commands/message.js +1 -6
  183. package/dist/src/platforms/whatsappbot/commands/message.js.map +1 -1
  184. package/dist/src/platforms/whatsappbot/commands/template.d.ts.map +1 -1
  185. package/dist/src/platforms/whatsappbot/commands/template.js +1 -6
  186. package/dist/src/platforms/whatsappbot/commands/template.js.map +1 -1
  187. package/dist/src/platforms/whatsappbot/commands/whoami.d.ts.map +1 -1
  188. package/dist/src/platforms/whatsappbot/commands/whoami.js +1 -6
  189. package/dist/src/platforms/whatsappbot/commands/whoami.js.map +1 -1
  190. package/dist/src/shared/chromium/browsers.d.ts +8 -0
  191. package/dist/src/shared/chromium/browsers.d.ts.map +1 -1
  192. package/dist/src/shared/chromium/browsers.js +58 -3
  193. package/dist/src/shared/chromium/browsers.js.map +1 -1
  194. package/dist/src/shared/chromium/cli-options.d.ts +5 -0
  195. package/dist/src/shared/chromium/cli-options.d.ts.map +1 -0
  196. package/dist/src/shared/chromium/cli-options.js +8 -0
  197. package/dist/src/shared/chromium/cli-options.js.map +1 -0
  198. package/dist/src/shared/chromium/index.d.ts +3 -1
  199. package/dist/src/shared/chromium/index.d.ts.map +1 -1
  200. package/dist/src/shared/chromium/index.js +2 -1
  201. package/dist/src/shared/chromium/index.js.map +1 -1
  202. package/dist/src/shared/utils/cli-output.d.ts +7 -0
  203. package/dist/src/shared/utils/cli-output.d.ts.map +1 -0
  204. package/dist/src/shared/utils/cli-output.js +7 -0
  205. package/dist/src/shared/utils/cli-output.js.map +1 -0
  206. package/dist/src/tui/app.d.ts.map +1 -1
  207. package/dist/src/tui/app.js +73 -20
  208. package/dist/src/tui/app.js.map +1 -1
  209. package/docs/content/docs/cli/channeltalk.mdx +4 -0
  210. package/docs/content/docs/cli/discord.mdx +5 -0
  211. package/docs/content/docs/cli/instagram.mdx +3 -0
  212. package/docs/content/docs/cli/slack.mdx +5 -0
  213. package/docs/content/docs/cli/slackbot.mdx +60 -22
  214. package/docs/content/docs/cli/teams.mdx +5 -0
  215. package/docs/content/docs/cli/webex.mdx +3 -0
  216. package/docs/content/docs/sdk/channeltalkbot.mdx +38 -1
  217. package/docs/content/docs/sdk/discordbot.mdx +501 -0
  218. package/docs/content/docs/sdk/meta.json +2 -0
  219. package/docs/content/docs/sdk/slackbot.mdx +576 -0
  220. package/e2e/README.md +1 -1
  221. package/e2e/config.ts +9 -4
  222. package/examples/discordbot-listen.ts +65 -0
  223. package/examples/slackbot-listen.ts +65 -0
  224. package/package.json +14 -1
  225. package/skills/agent-channeltalk/SKILL.md +5 -1
  226. package/skills/agent-channeltalk/references/authentication.md +5 -1
  227. package/skills/agent-channeltalkbot/SKILL.md +17 -3
  228. package/skills/agent-channeltalkbot/references/authentication.md +7 -5
  229. package/skills/agent-discord/SKILL.md +5 -1
  230. package/skills/agent-discord/references/authentication.md +7 -1
  231. package/skills/agent-discordbot/SKILL.md +13 -2
  232. package/skills/agent-discordbot/references/common-patterns.md +1 -1
  233. package/skills/agent-instagram/SKILL.md +7 -1
  234. package/skills/agent-instagram/references/authentication.md +6 -0
  235. package/skills/agent-kakaotalk/SKILL.md +1 -1
  236. package/skills/agent-line/SKILL.md +1 -1
  237. package/skills/agent-slack/SKILL.md +5 -1
  238. package/skills/agent-slack/references/authentication.md +7 -1
  239. package/skills/agent-slackbot/SKILL.md +56 -4
  240. package/skills/agent-slackbot/references/authentication.md +4 -0
  241. package/skills/agent-teams/SKILL.md +5 -1
  242. package/skills/agent-teams/references/authentication.md +7 -1
  243. package/skills/agent-telegram/SKILL.md +1 -1
  244. package/skills/agent-webex/SKILL.md +7 -1
  245. package/skills/agent-webex/references/authentication.md +6 -0
  246. package/skills/agent-wechatbot/SKILL.md +16 -1
  247. package/skills/agent-wechatbot/references/authentication.md +219 -0
  248. package/skills/agent-wechatbot/references/common-patterns.md +358 -0
  249. package/skills/agent-wechatbot/templates/account-summary.sh +122 -0
  250. package/skills/agent-wechatbot/templates/post-message.sh +122 -0
  251. package/skills/agent-wechatbot/templates/send-template.sh +152 -0
  252. package/skills/agent-whatsapp/SKILL.md +1 -1
  253. package/skills/agent-whatsappbot/SKILL.md +30 -1
  254. package/src/platforms/channeltalk/commands/auth.test.ts +15 -3
  255. package/src/platforms/channeltalk/commands/auth.ts +15 -5
  256. package/src/platforms/channeltalk/token-extractor.ts +24 -5
  257. package/src/platforms/channeltalkbot/cli.ts +9 -0
  258. package/src/platforms/channeltalkbot/commands/auth.ts +1 -5
  259. package/src/platforms/channeltalkbot/commands/bot.ts +1 -6
  260. package/src/platforms/channeltalkbot/commands/chat.ts +1 -6
  261. package/src/platforms/channeltalkbot/commands/group.ts +1 -6
  262. package/src/platforms/channeltalkbot/commands/manager.ts +1 -6
  263. package/src/platforms/channeltalkbot/commands/message.ts +1 -6
  264. package/src/platforms/channeltalkbot/commands/whoami.test.ts +2 -0
  265. package/src/platforms/channeltalkbot/commands/whoami.ts +1 -6
  266. package/src/platforms/channeltalkbot/credential-manager.test.ts +96 -2
  267. package/src/platforms/channeltalkbot/credential-manager.ts +37 -4
  268. package/src/platforms/discord/commands/auth.ts +13 -2
  269. package/src/platforms/discord/listener.test.ts +59 -1
  270. package/src/platforms/discord/listener.ts +43 -19
  271. package/src/platforms/discord/token-extractor.ts +30 -6
  272. package/src/platforms/discordbot/cli.ts +10 -0
  273. package/src/platforms/discordbot/client.ts +4 -0
  274. package/src/platforms/discordbot/commands/auth.ts +1 -5
  275. package/src/platforms/discordbot/commands/message.ts +1 -6
  276. package/src/platforms/discordbot/commands/server.ts +1 -5
  277. package/src/platforms/discordbot/commands/whoami.ts +1 -6
  278. package/src/platforms/discordbot/index.test.ts +82 -0
  279. package/src/platforms/discordbot/index.ts +27 -9
  280. package/src/platforms/discordbot/listener.test.ts +1002 -0
  281. package/src/platforms/discordbot/listener.ts +321 -0
  282. package/src/platforms/discordbot/types.ts +163 -0
  283. package/src/platforms/instagram/commands/auth.ts +9 -1
  284. package/src/platforms/instagram/token-extractor.ts +13 -1
  285. package/src/platforms/slack/commands/auth.ts +11 -2
  286. package/src/platforms/slack/token-extractor.test.ts +96 -0
  287. package/src/platforms/slack/token-extractor.ts +76 -13
  288. package/src/platforms/slackbot/cli.ts +13 -1
  289. package/src/platforms/slackbot/client.test.ts +274 -0
  290. package/src/platforms/slackbot/client.ts +130 -2
  291. package/src/platforms/slackbot/commands/auth.ts +1 -5
  292. package/src/platforms/slackbot/commands/file.test.ts +201 -0
  293. package/src/platforms/slackbot/commands/file.ts +212 -0
  294. package/src/platforms/slackbot/commands/index.ts +1 -0
  295. package/src/platforms/slackbot/commands/message.ts +22 -0
  296. package/src/platforms/slackbot/commands/whoami.ts +1 -6
  297. package/src/platforms/slackbot/credential-manager.test.ts +62 -2
  298. package/src/platforms/slackbot/credential-manager.ts +32 -2
  299. package/src/platforms/slackbot/index.test.ts +59 -0
  300. package/src/platforms/slackbot/index.ts +31 -7
  301. package/src/platforms/slackbot/listener.test.ts +1012 -0
  302. package/src/platforms/slackbot/listener.ts +362 -0
  303. package/src/platforms/slackbot/types.ts +224 -1
  304. package/src/platforms/teams/commands/auth.test.ts +1 -1
  305. package/src/platforms/teams/commands/auth.ts +66 -7
  306. package/src/platforms/teams/ensure-auth.test.ts +56 -5
  307. package/src/platforms/teams/ensure-auth.ts +39 -11
  308. package/src/platforms/teams/token-extractor.test.ts +146 -24
  309. package/src/platforms/teams/token-extractor.ts +87 -29
  310. package/src/platforms/webex/commands/auth.ts +13 -2
  311. package/src/platforms/webex/token-extractor.ts +25 -3
  312. package/src/platforms/wechatbot/cli.ts +9 -0
  313. package/src/platforms/wechatbot/commands/auth.ts +1 -5
  314. package/src/platforms/wechatbot/commands/message.ts +1 -6
  315. package/src/platforms/wechatbot/commands/template.ts +1 -6
  316. package/src/platforms/wechatbot/commands/user.ts +1 -6
  317. package/src/platforms/wechatbot/commands/whoami.ts +1 -6
  318. package/src/platforms/whatsappbot/cli.ts +9 -0
  319. package/src/platforms/whatsappbot/commands/auth.ts +1 -5
  320. package/src/platforms/whatsappbot/commands/message.ts +1 -6
  321. package/src/platforms/whatsappbot/commands/template.ts +1 -6
  322. package/src/platforms/whatsappbot/commands/whoami.ts +1 -6
  323. package/src/shared/chromium/browsers.test.ts +80 -0
  324. package/src/shared/chromium/browsers.ts +72 -3
  325. package/src/shared/chromium/cli-options.test.ts +22 -0
  326. package/src/shared/chromium/cli-options.ts +12 -0
  327. package/src/shared/chromium/index.ts +3 -0
  328. package/src/shared/utils/cli-output.test.ts +57 -0
  329. package/src/shared/utils/cli-output.ts +8 -0
  330. package/src/tui/app.ts +129 -20
@@ -0,0 +1,576 @@
1
+ ---
2
+ title: Slack Bot
3
+ description: TypeScript SDK reference for Slack Bot — client, real-time Socket Mode listener, credential management, and types.
4
+ ---
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ npm install agent-messenger
10
+ ```
11
+
12
+ ```typescript
13
+ import { SlackBotClient, SlackBotCredentialManager, SlackBotError, SlackBotListener } from 'agent-messenger/slackbot'
14
+ ```
15
+
16
+ ## Slack vs. Slack Bot
17
+
18
+ `agent-messenger/slack` and `agent-messenger/slackbot` are two distinct entry points for two distinct authentication models:
19
+
20
+ | Module | Token type | Source | Use when |
21
+ | -------------------------- | -------------- | ------------------------------- | ------------------------------------- |
22
+ | `agent-messenger/slack` | User (`xoxc-`) | Auto-extracted from desktop app | Acting as yourself, all features |
23
+ | `agent-messenger/slackbot` | Bot (`xoxb-`) | Slack App config (manual) | Server-side, CI/CD, multi-tenant bots |
24
+
25
+ This page documents `agent-messenger/slackbot`. For the user-token client, see the [Slack SDK reference](./slack).
26
+
27
+ ## SlackBotClient
28
+
29
+ The main client for interacting with Slack's Web API using a bot token. Built on `@slack/web-api`. All methods include automatic retry with exponential backoff on `slack_webapi_rate_limited_error` (up to 3 retries, honoring Slack's `retryAfter` hint).
30
+
31
+ ```typescript
32
+ import { SlackBotClient } from 'agent-messenger/slackbot'
33
+
34
+ const client = await new SlackBotClient().login({ token: 'xoxb-...' })
35
+ ```
36
+
37
+ Or use stored credentials — credentials are read from `~/.config/agent-messenger/slackbot-credentials.json` (managed by `agent-slackbot auth set`):
38
+
39
+ ```typescript
40
+ const client = await new SlackBotClient().login()
41
+ ```
42
+
43
+ ### Authentication
44
+
45
+ ```typescript
46
+ // Verify bot credentials and resolve the bot's identity in the workspace
47
+ const me = await client.testAuth()
48
+ // → { user_id, team_id, bot_id?, user?, team? }
49
+ ```
50
+
51
+ ### Messages
52
+
53
+ ```typescript
54
+ // Send a message to a channel
55
+ const msg = await client.postMessage('C0ACZKTDDC0', 'Hello world')
56
+
57
+ // Reply in a thread
58
+ const reply = await client.postMessage('C0ACZKTDDC0', 'Reply', { thread_ts: '1234567890.123456' })
59
+ // → SlackMessage { ts, text, type, user?, thread_ts? }
60
+
61
+ // Read a channel's recent messages (default limit: 20, supports cursor pagination)
62
+ const history = await client.getConversationHistory('C0ACZKTDDC0')
63
+ const limited = await client.getConversationHistory('C0ACZKTDDC0', { limit: 50 })
64
+ const next = await client.getConversationHistory('C0ACZKTDDC0', { cursor: 'dXNlcjpVMD...' })
65
+ // → SlackMessage[]
66
+
67
+ // Get a single message by timestamp
68
+ const message = await client.getMessage('C0ACZKTDDC0', '1234567890.123456')
69
+ // → SlackMessage | null
70
+
71
+ // Get all replies in a thread (parent message included as the first entry)
72
+ const replies = await client.getThreadReplies('C0ACZKTDDC0', '1234567890.123456')
73
+ const paginated = await client.getThreadReplies('C0ACZKTDDC0', '1234567890.123456', { limit: 100 })
74
+ // → SlackMessage[]
75
+
76
+ // Update a message (bot can only edit its own messages)
77
+ const edited = await client.updateMessage('C0ACZKTDDC0', '1234567890.123456', 'Updated text')
78
+ // → SlackMessage
79
+
80
+ // Delete a message (bot can only delete its own messages)
81
+ await client.deleteMessage('C0ACZKTDDC0', '1234567890.123456')
82
+
83
+ // Show a typing/status indicator in an AI Assistant thread (e.g. "Bot is typing...")
84
+ await client.setAssistantStatus('C0ACZKTDDC0', '1234567890.123456', 'is typing...')
85
+ await client.setAssistantStatus('C0ACZKTDDC0', '1234567890.123456', 'is analyzing your code...')
86
+
87
+ // Clear the status manually (status also auto-clears when the bot posts a message,
88
+ // or after a 2-minute timeout). Only works inside AI Assistant threads — requires `chat:write`.
89
+ await client.setAssistantStatus('C0ACZKTDDC0', '1234567890.123456', '')
90
+ ```
91
+
92
+ ### Reactions
93
+
94
+ ```typescript
95
+ // Add a reaction (emoji can be wrapped in colons or bare)
96
+ await client.addReaction('C0ACZKTDDC0', '1234567890.123456', 'thumbsup')
97
+ await client.addReaction('C0ACZKTDDC0', '1234567890.123456', ':white_check_mark:')
98
+
99
+ // Remove a reaction the bot added
100
+ await client.removeReaction('C0ACZKTDDC0', '1234567890.123456', 'thumbsup')
101
+ ```
102
+
103
+ ### Channels
104
+
105
+ ```typescript
106
+ // List channels the bot can see (auto-paginates unless a `limit` is given)
107
+ const channels = await client.listChannels()
108
+ const firstPage = await client.listChannels({ limit: 50 })
109
+ const next = await client.listChannels({ limit: 50, cursor: 'dGVhbTpD...' })
110
+ // → SlackChannel[]
111
+
112
+ // Get a specific channel
113
+ const channel = await client.getChannelInfo('C0ACZKTDDC0')
114
+ // → SlackChannel { id, name, is_private, is_archived, created, creator, topic?, purpose? }
115
+
116
+ // Resolve a channel by ID or name — returns the channel ID
117
+ const id = await client.resolveChannel('C0ACZKTDDC0')
118
+ const sameId = await client.resolveChannel('#general')
119
+ const fromName = await client.resolveChannel('general')
120
+
121
+ // Join a public channel (required before posting in some workspaces)
122
+ await client.joinChannel('C0ACZKTDDC0')
123
+ ```
124
+
125
+ ### Users
126
+
127
+ ```typescript
128
+ // List workspace members (auto-paginates unless a `limit` is given)
129
+ const users = await client.listUsers()
130
+ const firstPage = await client.listUsers({ limit: 100 })
131
+ // → SlackUser[]
132
+
133
+ // Get a single user
134
+ const user = await client.getUserInfo('U012ABC3DE')
135
+ // → SlackUser { id, name, real_name, is_admin, is_owner, is_bot, is_app_user, profile? }
136
+ ```
137
+
138
+ ## Real-Time Events
139
+
140
+ `SlackBotListener` connects to Slack's [Socket Mode](https://api.slack.com/apis/socket-mode) WebSocket for instant event streaming — Events API events, slash commands, and interactive components — without running an HTTP webhook server.
141
+
142
+ ```typescript
143
+ import { SlackBotClient, SlackBotListener } from 'agent-messenger/slackbot'
144
+
145
+ const client = await new SlackBotClient().login({ token: 'xoxb-...' })
146
+ const listener = new SlackBotListener(client, {
147
+ appToken: process.env.SLACK_APP_TOKEN!, // xapp-...
148
+ })
149
+
150
+ listener.on('connected', (info) => {
151
+ console.log(`Connected to Slack (app ${info.app_id})`)
152
+ })
153
+
154
+ listener.on('message', ({ ack, event }) => {
155
+ ack()
156
+ if (event.subtype || event.bot_id) return
157
+ console.log(`#${event.channel} <${event.user}>: ${event.text}`)
158
+ })
159
+
160
+ listener.on('slash_commands', ({ ack, body }) => {
161
+ ack({ text: `Got \`${body.command} ${body.text}\`` })
162
+ })
163
+
164
+ listener.on('error', (err) => console.error(err))
165
+
166
+ await listener.start() // opens the Socket Mode WebSocket
167
+ // listener.stop() // clean shutdown
168
+ ```
169
+
170
+ ### Two tokens
171
+
172
+ Slack Socket Mode is the only place in this SDK where you need **two separate tokens** for one bot:
173
+
174
+ | Token | Purpose | Where to get it |
175
+ | ---------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
176
+ | Bot (`xoxb-...`) | Web API calls — `chat.postMessage`, `conversations.list`, etc. | Slack App config → **OAuth & Permissions** → Bot User OAuth Token |
177
+ | App (`xapp-...`) | Socket Mode WebSocket — opens `apps.connections.open`, receives events | Slack App config → **Basic Information** → App-Level Tokens, with the `connections:write` scope |
178
+
179
+ The bot token is passed to `SlackBotClient` (or stored via `agent-slackbot auth set`); the app token is passed to `SlackBotListener` via the `appToken` option. The two tokens are not interchangeable — calling `apps.connections.open` with a bot token returns `not_allowed_token_type`.
180
+
181
+ You also need to enable **Socket Mode** in the Slack App config (Settings → Socket Mode → On) and subscribe the app to the [Events API events](https://api.slack.com/apis/connections/events-api) you want to receive.
182
+
183
+ ### Acknowledging envelopes
184
+
185
+ Every Socket Mode envelope except `hello` and `disconnect` carries an `envelope_id` that the client **must** acknowledge — otherwise Slack will retry delivery. The handler arguments include an `ack` callback for that purpose:
186
+
187
+ ```typescript
188
+ listener.on('message', ({ ack, event }) => {
189
+ // Always ack first so Slack stops retrying. Do this even if you decide to
190
+ // ignore the event — failure to ack causes duplicate deliveries.
191
+ ack()
192
+ // ...your work
193
+ })
194
+
195
+ // Slash commands and interactive components support a response payload that
196
+ // becomes the immediate user-visible reply (when accepts_response_payload is
197
+ // true on the envelope).
198
+ listener.on('slash_commands', ({ ack, body }) => {
199
+ ack({ text: 'Working on it...' })
200
+ })
201
+
202
+ listener.on('interactive', ({ ack, body }) => {
203
+ if (body.type === 'view_submission') {
204
+ ack({ response_action: 'clear' })
205
+ } else {
206
+ ack()
207
+ }
208
+ })
209
+ ```
210
+
211
+ The `ack` callback is **idempotent** (only the first call hits the wire) and a **no-op after the listener stops or reconnects** — so it is safe to hold across async work. Calling `ack()` with no argument sends `{ envelope_id }`; calling with a payload sends `{ envelope_id, payload }`.
212
+
213
+ ### Available Events
214
+
215
+ | Event | Description |
216
+ | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
217
+ | `message` | New message in a channel or DM |
218
+ | `app_mention` | Message that mentions the bot |
219
+ | `reaction_added` / `reaction_removed` | Reaction added or removed |
220
+ | `member_joined_channel` / `member_left_channel` | Member joins or leaves a channel |
221
+ | `channel_created` / `channel_deleted` / `channel_rename` / `channel_archive` / `channel_unarchive` | Channel lifecycle |
222
+ | `slash_commands` | Slash command invoked (envelope-level event, not Events API) |
223
+ | `interactive` | Block action, view submission, shortcut, or other interactive component |
224
+ | `slack_event` | Catch-all — fires for every Events API dispatch and every unknown envelope type |
225
+ | `connected` / `disconnected` | Socket Mode connection lifecycle |
226
+ | `error` | Connection or protocol error |
227
+
228
+ Any inner Events API event type that Slack delivers is forwarded by name (e.g., `team_join`, `user_change`, `pin_added`) and is also delivered to `slack_event`.
229
+
230
+ ### Lifecycle Features
231
+
232
+ - **Auto-reconnect** with exponential backoff (1s → 30s, capped). On every reconnect the listener calls `apps.connections.open` to fetch a fresh single-use Socket Mode URL.
233
+ - **Server-requested reconnect** — when Slack sends a `disconnect` envelope with reason `warning` or `refresh_requested`, the listener reconnects immediately and resets backoff.
234
+ - **Terminal disconnect detection** — when the disconnect reason is `link_disabled`, the listener emits `error` and stops; reconnecting against a disabled app would loop forever.
235
+ - **`hello` timeout** — if the WebSocket opens but Slack does not send `hello` within 10 seconds, the listener force-closes the socket and reconnects.
236
+ - **WebSocket-level keepalive** — RFC 6455 ping/pong every 30 seconds; if no pong arrives within 10 seconds the connection is closed and re-established.
237
+ - **Rate-limit awareness** — if `apps.connections.open` returns a rate-limit error, the listener uses the server's `retryAfter` value as a floor on the next reconnect delay.
238
+ - **Stale-socket safety** — every async callback is generation-guarded so a stop+start cycle cannot leave the new connection in a broken state.
239
+ - **Fatal-error short-circuit** — `apps.connections.open` errors that indicate a permanently broken state (`not_authed`, `invalid_auth`, `account_inactive`, `user_removed_from_team`, `team_disabled`, `not_allowed_token_type`) emit `error` and stop without retrying.
240
+
241
+ ### Debugging reconnects
242
+
243
+ To force frequent reconnects during development (Slack closes the socket every ~360s instead of the usual ~12 hours), enable `debugReconnects`:
244
+
245
+ ```typescript
246
+ const listener = new SlackBotListener(client, {
247
+ appToken: process.env.SLACK_APP_TOKEN!,
248
+ debugReconnects: true,
249
+ })
250
+ ```
251
+
252
+ This appends `&debug_reconnects=true` to the WebSocket URL returned by `apps.connections.open`.
253
+
254
+ ## SlackBotCredentialManager
255
+
256
+ Manages bot credentials stored at `~/.config/agent-messenger/slackbot-credentials.json` with `0o600` permissions. Supports multiple bots per workspace and multiple workspaces in the same config (e.g., a deploy bot and an alerts bot in two different workspaces). The environment variables `E2E_SLACKBOT_TOKEN`, `E2E_SLACKBOT_WORKSPACE_ID`, and `E2E_SLACKBOT_WORKSPACE_NAME` take precedence over file-based credentials.
257
+
258
+ ```typescript
259
+ import { SlackBotCredentialManager } from 'agent-messenger/slackbot'
260
+
261
+ const manager = new SlackBotCredentialManager()
262
+ // Custom path: new SlackBotCredentialManager('/custom/config/dir')
263
+ ```
264
+
265
+ ```typescript
266
+ // Load full config from disk (returns defaults if file doesn't exist)
267
+ const config = await manager.load()
268
+ // → SlackBotConfig { current, workspaces }
269
+
270
+ // Save full config to disk (also enforces 0o600 permissions)
271
+ await manager.save(config)
272
+
273
+ // Get the credentials for the current bot, a specific bot ID, or
274
+ // "workspace_id/bot_id" to disambiguate a bot ID that exists in multiple
275
+ // workspaces.
276
+ const creds = await manager.getCredentials()
277
+ const specific = await manager.getCredentials('deploy')
278
+ const disambiguated = await manager.getCredentials('T0ABCDE/deploy')
279
+ // → SlackBotCredentials | null
280
+
281
+ // Store credentials for a bot (also marks it as the current bot)
282
+ await manager.setCredentials({
283
+ token: 'xoxb-...',
284
+ workspace_id: 'T0ABCDE',
285
+ workspace_name: 'Acme Corp',
286
+ bot_id: 'deploy',
287
+ bot_name: 'Deploy Bot',
288
+ })
289
+
290
+ // Switch the active bot — accepts the same lookup formats as getCredentials
291
+ await manager.setCurrent('alerts')
292
+
293
+ // List all stored bots (across workspaces) with current marker
294
+ const bots = await manager.listAll()
295
+ // → Array<SlackBotCredentials & { is_current: boolean }>
296
+
297
+ // Remove a stored bot (drops the workspace too if it was the last bot in it)
298
+ await manager.removeBot('alerts')
299
+
300
+ // Clear all stored credentials
301
+ await manager.clearCredentials()
302
+ ```
303
+
304
+ ## Types
305
+
306
+ ```typescript
307
+ import type {
308
+ SlackBotConfig,
309
+ SlackBotCredentials,
310
+ SlackBotListenerEventMap,
311
+ SlackBotListenerOptions,
312
+ SlackChannel,
313
+ SlackFile,
314
+ SlackMessage,
315
+ SlackReaction,
316
+ SlackSocketModeAck,
317
+ SlackSocketModeAppMentionEvent,
318
+ SlackSocketModeChannelEvent,
319
+ SlackSocketModeDisconnectEnvelope,
320
+ SlackSocketModeDisconnectReason,
321
+ SlackSocketModeEnvelope,
322
+ SlackSocketModeEvent,
323
+ SlackSocketModeEventsApiArgs,
324
+ SlackSocketModeEventsApiEnvelope,
325
+ SlackSocketModeGenericEnvelope,
326
+ SlackSocketModeGenericEvent,
327
+ SlackSocketModeHelloEnvelope,
328
+ SlackSocketModeInteractiveArgs,
329
+ SlackSocketModeInteractiveEnvelope,
330
+ SlackSocketModeMemberChannelEvent,
331
+ SlackSocketModeMessageEvent,
332
+ SlackSocketModeReactionEvent,
333
+ SlackSocketModeSlashCommandArgs,
334
+ SlackSocketModeSlashCommandEnvelope,
335
+ SlackUser,
336
+ } from 'agent-messenger/slackbot'
337
+ ```
338
+
339
+ ### Zod Schemas
340
+
341
+ Runtime-validated schemas for parsing API responses and config files:
342
+
343
+ ```typescript
344
+ import {
345
+ SlackBotConfigSchema,
346
+ SlackBotCredentialsSchema,
347
+ SlackChannelSchema,
348
+ SlackFileSchema,
349
+ SlackMessageSchema,
350
+ SlackReactionSchema,
351
+ SlackUserSchema,
352
+ } from 'agent-messenger/slackbot'
353
+ ```
354
+
355
+ ### Errors
356
+
357
+ `SlackBotError` carries a stable `code` (e.g., `not_authed`, `missing_app_token`, `slack_webapi_rate_limited_error`, `disconnect_terminal`) and an optional `retryAfter` (seconds) when Slack rate-limits a request:
358
+
359
+ ```typescript
360
+ import { SlackBotError } from 'agent-messenger/slackbot'
361
+
362
+ try {
363
+ await client.postMessage('C0ACZKTDDC0', 'Hello')
364
+ } catch (err) {
365
+ if (err instanceof SlackBotError && err.code === 'slack_webapi_rate_limited_error') {
366
+ console.log(`Rate limited; retry after ${err.retryAfter}s`)
367
+ }
368
+ }
369
+ ```
370
+
371
+ ## Examples
372
+
373
+ ### Auto-Reply Bot
374
+
375
+ Listen for `app_mention` events and reply in the same thread.
376
+
377
+ ```typescript
378
+ import { SlackBotClient, SlackBotListener } from 'agent-messenger/slackbot'
379
+
380
+ const client = await new SlackBotClient().login()
381
+ const listener = new SlackBotListener(client, { appToken: process.env.SLACK_APP_TOKEN! })
382
+
383
+ listener.on('app_mention', async ({ ack, event }) => {
384
+ ack()
385
+ await client.postMessage(event.channel, `👋 Hi <@${event.user}>! How can I help?`, {
386
+ thread_ts: event.thread_ts ?? event.ts,
387
+ })
388
+ })
389
+
390
+ await listener.start()
391
+ ```
392
+
393
+ ### Reaction-Driven Approval Workflow
394
+
395
+ Post a request and watch for ✅ or ❌ reactions to advance state.
396
+
397
+ ```typescript
398
+ import { SlackBotClient, SlackBotListener } from 'agent-messenger/slackbot'
399
+
400
+ const client = await new SlackBotClient().login()
401
+ const me = await client.testAuth()
402
+ const listener = new SlackBotListener(client, { appToken: process.env.SLACK_APP_TOKEN! })
403
+
404
+ const post = await client.postMessage('C0DEPLOY', '🚀 Deploy v2.1.0 to production?')
405
+ await client.addReaction('C0DEPLOY', post.ts, 'white_check_mark')
406
+ await client.addReaction('C0DEPLOY', post.ts, 'x')
407
+
408
+ listener.on('reaction_added', async ({ ack, event }) => {
409
+ ack()
410
+ if (event.item.channel !== 'C0DEPLOY' || event.item.ts !== post.ts) return
411
+ if (event.user === me.user_id) return
412
+
413
+ if (event.reaction === 'white_check_mark') {
414
+ await client.updateMessage('C0DEPLOY', post.ts, '🚀 Deploy v2.1.0 — *approved*')
415
+ } else if (event.reaction === 'x') {
416
+ await client.updateMessage('C0DEPLOY', post.ts, '🚀 Deploy v2.1.0 — *rejected*')
417
+ }
418
+ })
419
+
420
+ await listener.start()
421
+ ```
422
+
423
+ ### Slash Command Handler
424
+
425
+ React to `/deploy` slash command invocations in real time. The slash command must be registered in the Slack App config first.
426
+
427
+ ```typescript
428
+ import { SlackBotClient, SlackBotListener } from 'agent-messenger/slackbot'
429
+
430
+ const client = await new SlackBotClient().login()
431
+ const listener = new SlackBotListener(client, { appToken: process.env.SLACK_APP_TOKEN! })
432
+
433
+ listener.on('slash_commands', async ({ ack, body }) => {
434
+ if (body.command !== '/deploy') {
435
+ ack()
436
+ return
437
+ }
438
+
439
+ // Reply immediately so Slack shows feedback to the user.
440
+ ack({ text: `Queued deploy of \`${body.text}\`...` })
441
+
442
+ // Then do the long-running work and post a follow-up.
443
+ await runDeploy(body.text)
444
+ await client.postMessage(body.channel_id, `✅ Deploy of \`${body.text}\` complete`)
445
+ })
446
+
447
+ await listener.start()
448
+ ```
449
+
450
+ ### Interactive Block Action Handler
451
+
452
+ Handle a button click from a Block Kit message.
453
+
454
+ ```typescript
455
+ import { SlackBotClient, SlackBotListener } from 'agent-messenger/slackbot'
456
+
457
+ const client = await new SlackBotClient().login()
458
+ const listener = new SlackBotListener(client, { appToken: process.env.SLACK_APP_TOKEN! })
459
+
460
+ listener.on('interactive', async ({ ack, body }) => {
461
+ ack()
462
+
463
+ if (body.type !== 'block_actions') return
464
+ const action = body.actions?.[0] as { action_id?: string; value?: string } | undefined
465
+ if (action?.action_id !== 'approve_pr') return
466
+
467
+ await client.postMessage(
468
+ (body as { channel?: { id: string } }).channel!.id,
469
+ `<@${body.user!.id}> approved PR ${action.value}`,
470
+ )
471
+ })
472
+
473
+ await listener.start()
474
+ ```
475
+
476
+ ### AI Assistant Thread with Typing Indicator
477
+
478
+ Show a "Bot is typing..." status while your agent is processing, then reply. Slack auto-clears the status when the bot posts its message. Requires the app to be configured as an AI Assistant (`assistant` scope, `assistant_thread_started` event) and a `chat:write` bot scope.
479
+
480
+ ```typescript
481
+ import { SlackBotClient, SlackBotListener } from 'agent-messenger/slackbot'
482
+
483
+ const client = await new SlackBotClient().login()
484
+ const listener = new SlackBotListener(client, { appToken: process.env.SLACK_APP_TOKEN! })
485
+
486
+ listener.on('message', async ({ ack, event }) => {
487
+ ack()
488
+ // Only handle messages inside an AI Assistant thread (skip top-level posts and bot echoes).
489
+ if (!event.thread_ts || event.subtype === 'bot_message') return
490
+
491
+ // Show "Bot is typing..." while the agent thinks.
492
+ await client.setAssistantStatus(event.channel, event.thread_ts, 'is typing...')
493
+
494
+ try {
495
+ const reply = await runAgent(event.text ?? '')
496
+ // Posting a reply auto-clears the status indicator.
497
+ await client.postMessage(event.channel, reply, { thread_ts: event.thread_ts })
498
+ } catch (err) {
499
+ // Clear the status on failure so it doesn't linger until the 2-minute timeout.
500
+ await client.setAssistantStatus(event.channel, event.thread_ts, '')
501
+ throw err
502
+ }
503
+ })
504
+
505
+ await listener.start()
506
+ ```
507
+
508
+ ### CI Status Notifier
509
+
510
+ Post a status message at the start and finish of a CI job, with a reaction to indicate state.
511
+
512
+ ```typescript
513
+ import { SlackBotClient } from 'agent-messenger/slackbot'
514
+
515
+ const client = await new SlackBotClient().login()
516
+ const channel = 'C0BUILDS'
517
+
518
+ const start = await client.postMessage(channel, `🛠️ Build #${runId} started`)
519
+ await client.addReaction(channel, start.ts, 'hourglass_flowing_sand')
520
+
521
+ try {
522
+ await runBuild()
523
+ await client.removeReaction(channel, start.ts, 'hourglass_flowing_sand')
524
+ await client.addReaction(channel, start.ts, 'white_check_mark')
525
+ await client.postMessage(channel, `✅ Build #${runId} succeeded`, { thread_ts: start.ts })
526
+ } catch (err) {
527
+ await client.removeReaction(channel, start.ts, 'hourglass_flowing_sand')
528
+ await client.addReaction(channel, start.ts, 'x')
529
+ await client.postMessage(channel, `❌ Build #${runId} failed: ${(err as Error).message}`, {
530
+ thread_ts: start.ts,
531
+ })
532
+ throw err
533
+ }
534
+ ```
535
+
536
+ ### Multi-Workspace, Multi-Bot Configuration
537
+
538
+ Use `SlackBotCredentialManager` to store and switch between bots across multiple workspaces.
539
+
540
+ ```typescript
541
+ import { SlackBotClient, SlackBotCredentialManager } from 'agent-messenger/slackbot'
542
+
543
+ const manager = new SlackBotCredentialManager()
544
+
545
+ // Two bots in the same workspace
546
+ await manager.setCredentials({
547
+ token: 'xoxb-DEPLOY',
548
+ workspace_id: 'T0ACME',
549
+ workspace_name: 'Acme Corp',
550
+ bot_id: 'deploy',
551
+ bot_name: 'Deploy Bot',
552
+ })
553
+ await manager.setCredentials({
554
+ token: 'xoxb-ALERTS',
555
+ workspace_id: 'T0ACME',
556
+ workspace_name: 'Acme Corp',
557
+ bot_id: 'alerts',
558
+ bot_name: 'Alerts Bot',
559
+ })
560
+
561
+ // A bot in a second workspace, with the same bot_id
562
+ await manager.setCredentials({
563
+ token: 'xoxb-DEPLOY-2',
564
+ workspace_id: 'T0WIDGET',
565
+ workspace_name: 'Widget Co',
566
+ bot_id: 'deploy',
567
+ bot_name: 'Widget Deploy Bot',
568
+ })
569
+
570
+ // Switch using "workspace_id/bot_id" to disambiguate
571
+ await manager.setCurrent('T0WIDGET/deploy')
572
+ const creds = await manager.getCredentials()
573
+ const client = await new SlackBotClient().login({ token: creds!.token })
574
+
575
+ await client.postMessage('C0RELEASES', '🚀 Widget Co release v3.0.0 deployed')
576
+ ```
package/e2e/README.md CHANGED
@@ -333,7 +333,7 @@ When triggering manually, you can select which platform to test:
333
333
  | `bot` | list |
334
334
  | `snapshot` | default |
335
335
 
336
- > ⚠️ Channel Talk Bot tests require `E2E_CHANNELBOT_WORKSPACE_ID`.
336
+ > ⚠️ Channel Talk Bot tests require `E2E_CHANNELTALKBOT_WORKSPACE_ID` (legacy `E2E_CHANNELBOT_WORKSPACE_ID` still accepted as fallback).
337
337
 
338
338
  ## Troubleshooting
339
339
 
package/e2e/config.ts CHANGED
@@ -149,16 +149,21 @@ export async function validateTeamsEnvironment(): Promise<boolean> {
149
149
  return true
150
150
  }
151
151
 
152
- // ChannelBot Test Environment — requires E2E_CHANNELBOT_WORKSPACE_ID to opt-in.
152
+ // ChannelBot Test Environment — requires E2E_CHANNELTALKBOT_WORKSPACE_ID to opt-in.
153
+ // E2E_CHANNELBOT_WORKSPACE_ID is also accepted as a legacy fallback.
153
154
  // The E2E group is auto-discovered by name from the workspace's group list.
154
155
  // Never run against a real business workspace automatically.
155
- export const CHANNELBOT_TEST_WORKSPACE_ID = process.env.E2E_CHANNELBOT_WORKSPACE_ID || ''
156
- export const CHANNELBOT_TEST_WORKSPACE_NAME = process.env.E2E_CHANNELBOT_WORKSPACE_NAME || ''
156
+ export const CHANNELBOT_TEST_WORKSPACE_ID =
157
+ process.env.E2E_CHANNELTALKBOT_WORKSPACE_ID || process.env.E2E_CHANNELBOT_WORKSPACE_ID || ''
158
+ export const CHANNELBOT_TEST_WORKSPACE_NAME =
159
+ process.env.E2E_CHANNELTALKBOT_WORKSPACE_NAME || process.env.E2E_CHANNELBOT_WORKSPACE_NAME || ''
157
160
  export const E2E_GROUP_NAME = 'E2E'
158
161
 
159
162
  export async function validateChannelBotEnvironment(): Promise<{ groupId: string; groupName: string } | null> {
160
163
  if (!CHANNELBOT_TEST_WORKSPACE_ID) {
161
- console.warn('Skipping ChannelBot E2E: set E2E_CHANNELBOT_WORKSPACE_ID to run against a dedicated test workspace.')
164
+ console.warn(
165
+ 'Skipping ChannelBot E2E: set E2E_CHANNELTALKBOT_WORKSPACE_ID to run against a dedicated test workspace.',
166
+ )
162
167
  return null
163
168
  }
164
169
 
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env bun
2
+ import { DiscordBotClient } from '../src/platforms/discordbot/client'
3
+ import { DiscordBotListener } from '../src/platforms/discordbot/listener'
4
+ import { DiscordIntent } from '../src/platforms/discordbot/types'
5
+
6
+ async function main() {
7
+ const client = await new DiscordBotClient().login()
8
+
9
+ // MessageContent is privileged — enable it in the Discord Developer Portal first.
10
+ // Without it, `content` will be empty for messages that don't mention the bot.
11
+ const listener = new DiscordBotListener(client, {
12
+ intents:
13
+ DiscordIntent.Guilds |
14
+ DiscordIntent.GuildMessages |
15
+ DiscordIntent.GuildMessageReactions |
16
+ DiscordIntent.DirectMessages |
17
+ DiscordIntent.MessageContent,
18
+ })
19
+
20
+ listener.on('connected', (info) => {
21
+ console.log(`Connected (bot: ${info.user.username} ${info.user.id}, session: ${info.sessionId})`)
22
+ console.log('Listening for events. Press Ctrl+C to stop.\n')
23
+ })
24
+
25
+ listener.on('disconnected', () => {
26
+ console.log('[disconnected] reconnecting...')
27
+ })
28
+
29
+ listener.on('message_create', (event) => {
30
+ if (event.author.bot) return
31
+ const time = new Date(event.timestamp).toLocaleTimeString()
32
+ console.log(`[${time}] message #${event.channel_id} <${event.author.username}>: ${event.content}`)
33
+ })
34
+
35
+ listener.on('message_update', (event) => {
36
+ console.log(`[update] message ${event.id} in #${event.channel_id}`)
37
+ })
38
+
39
+ listener.on('message_delete', (event) => {
40
+ console.log(`[delete] message ${event.id} in #${event.channel_id}`)
41
+ })
42
+
43
+ listener.on('message_reaction_add', (event) => {
44
+ console.log(`[reaction] :${event.emoji.name}: by ${event.user_id} on ${event.channel_id}/${event.message_id}`)
45
+ })
46
+
47
+ listener.on('interaction_create', (event) => {
48
+ const name = (event.data as { name?: string } | undefined)?.name ?? '(unknown)'
49
+ console.log(`[interaction] ${name} from ${event.user?.username ?? 'unknown'}`)
50
+ })
51
+
52
+ listener.on('error', (err) => {
53
+ console.error(`[error] ${err.message}`)
54
+ })
55
+
56
+ process.on('SIGINT', () => {
57
+ console.log('\nStopping...')
58
+ listener.stop()
59
+ process.exit(130)
60
+ })
61
+
62
+ await listener.start()
63
+ }
64
+
65
+ main()