@spinabot/brigade 1.6.1 → 1.8.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 (339) hide show
  1. package/dist/agents/agent-loop.d.ts +13 -0
  2. package/dist/agents/agent-loop.d.ts.map +1 -1
  3. package/dist/agents/agent-loop.js +5 -0
  4. package/dist/agents/agent-loop.js.map +1 -1
  5. package/dist/agents/channels/access-control/group-tool-policy.d.ts +73 -0
  6. package/dist/agents/channels/access-control/group-tool-policy.d.ts.map +1 -0
  7. package/dist/agents/channels/access-control/group-tool-policy.js +193 -0
  8. package/dist/agents/channels/access-control/group-tool-policy.js.map +1 -0
  9. package/dist/agents/channels/access-control/index.d.ts +1 -0
  10. package/dist/agents/channels/access-control/index.d.ts.map +1 -1
  11. package/dist/agents/channels/access-control/index.js +1 -0
  12. package/dist/agents/channels/access-control/index.js.map +1 -1
  13. package/dist/agents/channels/bluebubbles/account-config.d.ts +253 -0
  14. package/dist/agents/channels/bluebubbles/account-config.d.ts.map +1 -0
  15. package/dist/agents/channels/bluebubbles/account-config.js +486 -0
  16. package/dist/agents/channels/bluebubbles/account-config.js.map +1 -0
  17. package/dist/agents/channels/bluebubbles/account-registry.d.ts +33 -0
  18. package/dist/agents/channels/bluebubbles/account-registry.d.ts.map +1 -0
  19. package/dist/agents/channels/bluebubbles/account-registry.js +46 -0
  20. package/dist/agents/channels/bluebubbles/account-registry.js.map +1 -0
  21. package/dist/agents/channels/bluebubbles/adapter.d.ts +45 -0
  22. package/dist/agents/channels/bluebubbles/adapter.d.ts.map +1 -0
  23. package/dist/agents/channels/bluebubbles/adapter.js +366 -0
  24. package/dist/agents/channels/bluebubbles/adapter.js.map +1 -0
  25. package/dist/agents/channels/bluebubbles/catchup-cursor.d.ts +52 -0
  26. package/dist/agents/channels/bluebubbles/catchup-cursor.d.ts.map +1 -0
  27. package/dist/agents/channels/bluebubbles/catchup-cursor.js +118 -0
  28. package/dist/agents/channels/bluebubbles/catchup-cursor.js.map +1 -0
  29. package/dist/agents/channels/bluebubbles/catchup.d.ts +114 -0
  30. package/dist/agents/channels/bluebubbles/catchup.d.ts.map +1 -0
  31. package/dist/agents/channels/bluebubbles/catchup.js +220 -0
  32. package/dist/agents/channels/bluebubbles/catchup.js.map +1 -0
  33. package/dist/agents/channels/bluebubbles/chat.d.ts +75 -0
  34. package/dist/agents/channels/bluebubbles/chat.d.ts.map +1 -0
  35. package/dist/agents/channels/bluebubbles/chat.js +182 -0
  36. package/dist/agents/channels/bluebubbles/chat.js.map +1 -0
  37. package/dist/agents/channels/bluebubbles/connection.d.ts +95 -0
  38. package/dist/agents/channels/bluebubbles/connection.d.ts.map +1 -0
  39. package/dist/agents/channels/bluebubbles/connection.js +413 -0
  40. package/dist/agents/channels/bluebubbles/connection.js.map +1 -0
  41. package/dist/agents/channels/bluebubbles/contact-names.d.ts +79 -0
  42. package/dist/agents/channels/bluebubbles/contact-names.d.ts.map +1 -0
  43. package/dist/agents/channels/bluebubbles/contact-names.js +188 -0
  44. package/dist/agents/channels/bluebubbles/contact-names.js.map +1 -0
  45. package/dist/agents/channels/bluebubbles/debounce.d.ts +78 -0
  46. package/dist/agents/channels/bluebubbles/debounce.d.ts.map +1 -0
  47. package/dist/agents/channels/bluebubbles/debounce.js +173 -0
  48. package/dist/agents/channels/bluebubbles/debounce.js.map +1 -0
  49. package/dist/agents/channels/bluebubbles/dedupe.d.ts +25 -0
  50. package/dist/agents/channels/bluebubbles/dedupe.d.ts.map +1 -0
  51. package/dist/agents/channels/bluebubbles/dedupe.js +35 -0
  52. package/dist/agents/channels/bluebubbles/dedupe.js.map +1 -0
  53. package/dist/agents/channels/bluebubbles/effects.d.ts +17 -0
  54. package/dist/agents/channels/bluebubbles/effects.d.ts.map +1 -0
  55. package/dist/agents/channels/bluebubbles/effects.js +57 -0
  56. package/dist/agents/channels/bluebubbles/effects.js.map +1 -0
  57. package/dist/agents/channels/bluebubbles/history.d.ts +56 -0
  58. package/dist/agents/channels/bluebubbles/history.d.ts.map +1 -0
  59. package/dist/agents/channels/bluebubbles/history.js +140 -0
  60. package/dist/agents/channels/bluebubbles/history.js.map +1 -0
  61. package/dist/agents/channels/bluebubbles/index.d.ts +18 -0
  62. package/dist/agents/channels/bluebubbles/index.d.ts.map +1 -0
  63. package/dist/agents/channels/bluebubbles/index.js +18 -0
  64. package/dist/agents/channels/bluebubbles/index.js.map +1 -0
  65. package/dist/agents/channels/bluebubbles/media.d.ts +104 -0
  66. package/dist/agents/channels/bluebubbles/media.d.ts.map +1 -0
  67. package/dist/agents/channels/bluebubbles/media.js +222 -0
  68. package/dist/agents/channels/bluebubbles/media.js.map +1 -0
  69. package/dist/agents/channels/bluebubbles/messaging.d.ts +13 -0
  70. package/dist/agents/channels/bluebubbles/messaging.d.ts.map +1 -0
  71. package/dist/agents/channels/bluebubbles/messaging.js +36 -0
  72. package/dist/agents/channels/bluebubbles/messaging.js.map +1 -0
  73. package/dist/agents/channels/bluebubbles/module.d.ts +24 -0
  74. package/dist/agents/channels/bluebubbles/module.d.ts.map +1 -0
  75. package/dist/agents/channels/bluebubbles/module.js +52 -0
  76. package/dist/agents/channels/bluebubbles/module.js.map +1 -0
  77. package/dist/agents/channels/bluebubbles/normalize.d.ts +111 -0
  78. package/dist/agents/channels/bluebubbles/normalize.d.ts.map +1 -0
  79. package/dist/agents/channels/bluebubbles/normalize.js +239 -0
  80. package/dist/agents/channels/bluebubbles/normalize.js.map +1 -0
  81. package/dist/agents/channels/bluebubbles/plugin.d.ts +46 -0
  82. package/dist/agents/channels/bluebubbles/plugin.d.ts.map +1 -0
  83. package/dist/agents/channels/bluebubbles/plugin.js +242 -0
  84. package/dist/agents/channels/bluebubbles/plugin.js.map +1 -0
  85. package/dist/agents/channels/bluebubbles/probe.d.ts +79 -0
  86. package/dist/agents/channels/bluebubbles/probe.d.ts.map +1 -0
  87. package/dist/agents/channels/bluebubbles/probe.js +138 -0
  88. package/dist/agents/channels/bluebubbles/probe.js.map +1 -0
  89. package/dist/agents/channels/bluebubbles/reactions.d.ts +46 -0
  90. package/dist/agents/channels/bluebubbles/reactions.d.ts.map +1 -0
  91. package/dist/agents/channels/bluebubbles/reactions.js +207 -0
  92. package/dist/agents/channels/bluebubbles/reactions.js.map +1 -0
  93. package/dist/agents/channels/bluebubbles/send.d.ts +132 -0
  94. package/dist/agents/channels/bluebubbles/send.d.ts.map +1 -0
  95. package/dist/agents/channels/bluebubbles/send.js +327 -0
  96. package/dist/agents/channels/bluebubbles/send.js.map +1 -0
  97. package/dist/agents/channels/bluebubbles/status-issues.d.ts +57 -0
  98. package/dist/agents/channels/bluebubbles/status-issues.d.ts.map +1 -0
  99. package/dist/agents/channels/bluebubbles/status-issues.js +93 -0
  100. package/dist/agents/channels/bluebubbles/status-issues.js.map +1 -0
  101. package/dist/agents/channels/bluebubbles/types.d.ts +69 -0
  102. package/dist/agents/channels/bluebubbles/types.d.ts.map +1 -0
  103. package/dist/agents/channels/bluebubbles/types.js +118 -0
  104. package/dist/agents/channels/bluebubbles/types.js.map +1 -0
  105. package/dist/agents/channels/bluebubbles/webhook.d.ts +97 -0
  106. package/dist/agents/channels/bluebubbles/webhook.d.ts.map +1 -0
  107. package/dist/agents/channels/bluebubbles/webhook.js +249 -0
  108. package/dist/agents/channels/bluebubbles/webhook.js.map +1 -0
  109. package/dist/agents/channels/bundled-channel-metas.d.ts +13 -0
  110. package/dist/agents/channels/bundled-channel-metas.d.ts.map +1 -1
  111. package/dist/agents/channels/bundled-channel-metas.js +33 -0
  112. package/dist/agents/channels/bundled-channel-metas.js.map +1 -1
  113. package/dist/agents/channels/discord/account-config.d.ts +60 -0
  114. package/dist/agents/channels/discord/account-config.d.ts.map +1 -1
  115. package/dist/agents/channels/discord/account-config.js +89 -0
  116. package/dist/agents/channels/discord/account-config.js.map +1 -1
  117. package/dist/agents/channels/discord/adapter.d.ts +24 -1
  118. package/dist/agents/channels/discord/adapter.d.ts.map +1 -1
  119. package/dist/agents/channels/discord/adapter.js +208 -41
  120. package/dist/agents/channels/discord/adapter.js.map +1 -1
  121. package/dist/agents/channels/discord/component-blocks.d.ts +108 -0
  122. package/dist/agents/channels/discord/component-blocks.d.ts.map +1 -0
  123. package/dist/agents/channels/discord/component-blocks.js +113 -0
  124. package/dist/agents/channels/discord/component-blocks.js.map +1 -0
  125. package/dist/agents/channels/discord/components.d.ts +78 -0
  126. package/dist/agents/channels/discord/components.d.ts.map +1 -1
  127. package/dist/agents/channels/discord/components.js +89 -0
  128. package/dist/agents/channels/discord/components.js.map +1 -1
  129. package/dist/agents/channels/discord/connection.d.ts +195 -12
  130. package/dist/agents/channels/discord/connection.d.ts.map +1 -1
  131. package/dist/agents/channels/discord/connection.js +852 -38
  132. package/dist/agents/channels/discord/connection.js.map +1 -1
  133. package/dist/agents/channels/discord/directory-cache.d.ts +47 -0
  134. package/dist/agents/channels/discord/directory-cache.d.ts.map +1 -0
  135. package/dist/agents/channels/discord/directory-cache.js +131 -0
  136. package/dist/agents/channels/discord/directory-cache.js.map +1 -0
  137. package/dist/agents/channels/discord/directory-live.d.ts +61 -0
  138. package/dist/agents/channels/discord/directory-live.d.ts.map +1 -0
  139. package/dist/agents/channels/discord/directory-live.js +140 -0
  140. package/dist/agents/channels/discord/directory-live.js.map +1 -0
  141. package/dist/agents/channels/discord/format.d.ts +15 -0
  142. package/dist/agents/channels/discord/format.d.ts.map +1 -1
  143. package/dist/agents/channels/discord/format.js +56 -0
  144. package/dist/agents/channels/discord/format.js.map +1 -1
  145. package/dist/agents/channels/discord/guilds.d.ts +25 -0
  146. package/dist/agents/channels/discord/guilds.d.ts.map +1 -0
  147. package/dist/agents/channels/discord/guilds.js +46 -0
  148. package/dist/agents/channels/discord/guilds.js.map +1 -0
  149. package/dist/agents/channels/discord/inbound-extras.d.ts +166 -9
  150. package/dist/agents/channels/discord/inbound-extras.d.ts.map +1 -1
  151. package/dist/agents/channels/discord/inbound-extras.js +246 -8
  152. package/dist/agents/channels/discord/inbound-extras.js.map +1 -1
  153. package/dist/agents/channels/discord/index.d.ts +10 -3
  154. package/dist/agents/channels/discord/index.d.ts.map +1 -1
  155. package/dist/agents/channels/discord/index.js +10 -3
  156. package/dist/agents/channels/discord/index.js.map +1 -1
  157. package/dist/agents/channels/discord/modal-registry.d.ts +89 -0
  158. package/dist/agents/channels/discord/modal-registry.d.ts.map +1 -0
  159. package/dist/agents/channels/discord/modal-registry.js +104 -0
  160. package/dist/agents/channels/discord/modal-registry.js.map +1 -0
  161. package/dist/agents/channels/discord/modals.d.ts +100 -0
  162. package/dist/agents/channels/discord/modals.d.ts.map +1 -0
  163. package/dist/agents/channels/discord/modals.js +124 -0
  164. package/dist/agents/channels/discord/modals.js.map +1 -0
  165. package/dist/agents/channels/discord/permission-audit.d.ts +43 -0
  166. package/dist/agents/channels/discord/permission-audit.d.ts.map +1 -0
  167. package/dist/agents/channels/discord/permission-audit.js +192 -0
  168. package/dist/agents/channels/discord/permission-audit.js.map +1 -0
  169. package/dist/agents/channels/discord/plugin.d.ts +18 -2
  170. package/dist/agents/channels/discord/plugin.d.ts.map +1 -1
  171. package/dist/agents/channels/discord/plugin.js +73 -4
  172. package/dist/agents/channels/discord/plugin.js.map +1 -1
  173. package/dist/agents/channels/discord/probe.d.ts +23 -1
  174. package/dist/agents/channels/discord/probe.d.ts.map +1 -1
  175. package/dist/agents/channels/discord/probe.js +40 -5
  176. package/dist/agents/channels/discord/probe.js.map +1 -1
  177. package/dist/agents/channels/discord/rest-actions.d.ts +346 -0
  178. package/dist/agents/channels/discord/rest-actions.d.ts.map +1 -0
  179. package/dist/agents/channels/discord/rest-actions.js +559 -0
  180. package/dist/agents/channels/discord/rest-actions.js.map +1 -0
  181. package/dist/agents/channels/discord/rest-components.d.ts +122 -0
  182. package/dist/agents/channels/discord/rest-components.d.ts.map +1 -0
  183. package/dist/agents/channels/discord/rest-components.js +243 -0
  184. package/dist/agents/channels/discord/rest-components.js.map +1 -0
  185. package/dist/agents/channels/discord/security-audit.d.ts +29 -0
  186. package/dist/agents/channels/discord/security-audit.d.ts.map +1 -0
  187. package/dist/agents/channels/discord/security-audit.js +94 -0
  188. package/dist/agents/channels/discord/security-audit.js.map +1 -0
  189. package/dist/agents/channels/discord/security-doctor.d.ts +43 -0
  190. package/dist/agents/channels/discord/security-doctor.d.ts.map +1 -0
  191. package/dist/agents/channels/discord/security-doctor.js +83 -0
  192. package/dist/agents/channels/discord/security-doctor.js.map +1 -0
  193. package/dist/agents/channels/discord/status-issues.d.ts +37 -0
  194. package/dist/agents/channels/discord/status-issues.d.ts.map +1 -0
  195. package/dist/agents/channels/discord/status-issues.js +66 -0
  196. package/dist/agents/channels/discord/status-issues.js.map +1 -0
  197. package/dist/agents/channels/discord/subagent-thread-binding-store.d.ts +57 -0
  198. package/dist/agents/channels/discord/subagent-thread-binding-store.d.ts.map +1 -0
  199. package/dist/agents/channels/discord/subagent-thread-binding-store.js +98 -0
  200. package/dist/agents/channels/discord/subagent-thread-binding-store.js.map +1 -0
  201. package/dist/agents/channels/discord/subagent-thread-binding.d.ts +95 -0
  202. package/dist/agents/channels/discord/subagent-thread-binding.d.ts.map +1 -0
  203. package/dist/agents/channels/discord/subagent-thread-binding.js +208 -0
  204. package/dist/agents/channels/discord/subagent-thread-binding.js.map +1 -0
  205. package/dist/agents/channels/discord/system-events.d.ts +31 -0
  206. package/dist/agents/channels/discord/system-events.d.ts.map +1 -0
  207. package/dist/agents/channels/discord/system-events.js +74 -0
  208. package/dist/agents/channels/discord/system-events.js.map +1 -0
  209. package/dist/agents/channels/general-callback.d.ts +12 -0
  210. package/dist/agents/channels/general-callback.d.ts.map +1 -1
  211. package/dist/agents/channels/general-callback.js +18 -0
  212. package/dist/agents/channels/general-callback.js.map +1 -1
  213. package/dist/agents/channels/imessage/account-config.d.ts +178 -0
  214. package/dist/agents/channels/imessage/account-config.d.ts.map +1 -0
  215. package/dist/agents/channels/imessage/account-config.js +371 -0
  216. package/dist/agents/channels/imessage/account-config.js.map +1 -0
  217. package/dist/agents/channels/imessage/adapter.d.ts +36 -0
  218. package/dist/agents/channels/imessage/adapter.d.ts.map +1 -0
  219. package/dist/agents/channels/imessage/adapter.js +286 -0
  220. package/dist/agents/channels/imessage/adapter.js.map +1 -0
  221. package/dist/agents/channels/imessage/client.d.ts +99 -0
  222. package/dist/agents/channels/imessage/client.d.ts.map +1 -0
  223. package/dist/agents/channels/imessage/client.js +231 -0
  224. package/dist/agents/channels/imessage/client.js.map +1 -0
  225. package/dist/agents/channels/imessage/connection.d.ts +101 -0
  226. package/dist/agents/channels/imessage/connection.d.ts.map +1 -0
  227. package/dist/agents/channels/imessage/connection.js +367 -0
  228. package/dist/agents/channels/imessage/connection.js.map +1 -0
  229. package/dist/agents/channels/imessage/format.d.ts +45 -0
  230. package/dist/agents/channels/imessage/format.d.ts.map +1 -0
  231. package/dist/agents/channels/imessage/format.js +170 -0
  232. package/dist/agents/channels/imessage/format.js.map +1 -0
  233. package/dist/agents/channels/imessage/history.d.ts +49 -0
  234. package/dist/agents/channels/imessage/history.d.ts.map +1 -0
  235. package/dist/agents/channels/imessage/history.js +74 -0
  236. package/dist/agents/channels/imessage/history.js.map +1 -0
  237. package/dist/agents/channels/imessage/index.d.ts +25 -0
  238. package/dist/agents/channels/imessage/index.d.ts.map +1 -0
  239. package/dist/agents/channels/imessage/index.js +25 -0
  240. package/dist/agents/channels/imessage/index.js.map +1 -0
  241. package/dist/agents/channels/imessage/media.d.ts +92 -0
  242. package/dist/agents/channels/imessage/media.d.ts.map +1 -0
  243. package/dist/agents/channels/imessage/media.js +196 -0
  244. package/dist/agents/channels/imessage/media.js.map +1 -0
  245. package/dist/agents/channels/imessage/messaging.d.ts +14 -0
  246. package/dist/agents/channels/imessage/messaging.d.ts.map +1 -0
  247. package/dist/agents/channels/imessage/messaging.js +37 -0
  248. package/dist/agents/channels/imessage/messaging.js.map +1 -0
  249. package/dist/agents/channels/imessage/module.d.ts +16 -0
  250. package/dist/agents/channels/imessage/module.d.ts.map +1 -0
  251. package/dist/agents/channels/imessage/module.js +23 -0
  252. package/dist/agents/channels/imessage/module.js.map +1 -0
  253. package/dist/agents/channels/imessage/monitor.d.ts +207 -0
  254. package/dist/agents/channels/imessage/monitor.d.ts.map +1 -0
  255. package/dist/agents/channels/imessage/monitor.js +504 -0
  256. package/dist/agents/channels/imessage/monitor.js.map +1 -0
  257. package/dist/agents/channels/imessage/plugin.d.ts +53 -0
  258. package/dist/agents/channels/imessage/plugin.d.ts.map +1 -0
  259. package/dist/agents/channels/imessage/plugin.js +215 -0
  260. package/dist/agents/channels/imessage/plugin.js.map +1 -0
  261. package/dist/agents/channels/imessage/probe.d.ts +68 -0
  262. package/dist/agents/channels/imessage/probe.d.ts.map +1 -0
  263. package/dist/agents/channels/imessage/probe.js +134 -0
  264. package/dist/agents/channels/imessage/probe.js.map +1 -0
  265. package/dist/agents/channels/imessage/remote-attachments.d.ts +61 -0
  266. package/dist/agents/channels/imessage/remote-attachments.d.ts.map +1 -0
  267. package/dist/agents/channels/imessage/remote-attachments.js +131 -0
  268. package/dist/agents/channels/imessage/remote-attachments.js.map +1 -0
  269. package/dist/agents/channels/imessage/send.d.ts +61 -0
  270. package/dist/agents/channels/imessage/send.d.ts.map +1 -0
  271. package/dist/agents/channels/imessage/send.js +108 -0
  272. package/dist/agents/channels/imessage/send.js.map +1 -0
  273. package/dist/agents/channels/imessage/targets.d.ts +91 -0
  274. package/dist/agents/channels/imessage/targets.d.ts.map +1 -0
  275. package/dist/agents/channels/imessage/targets.js +245 -0
  276. package/dist/agents/channels/imessage/targets.js.map +1 -0
  277. package/dist/agents/channels/imessage/watch-error.d.ts +23 -0
  278. package/dist/agents/channels/imessage/watch-error.d.ts.map +1 -0
  279. package/dist/agents/channels/imessage/watch-error.js +69 -0
  280. package/dist/agents/channels/imessage/watch-error.js.map +1 -0
  281. package/dist/agents/channels/inbound-pipeline.d.ts +11 -0
  282. package/dist/agents/channels/inbound-pipeline.d.ts.map +1 -1
  283. package/dist/agents/channels/inbound-pipeline.js +25 -4
  284. package/dist/agents/channels/inbound-pipeline.js.map +1 -1
  285. package/dist/agents/channels/manager.d.ts +9 -0
  286. package/dist/agents/channels/manager.d.ts.map +1 -1
  287. package/dist/agents/channels/manager.js.map +1 -1
  288. package/dist/agents/channels/sdk.d.ts +4 -0
  289. package/dist/agents/channels/sdk.d.ts.map +1 -1
  290. package/dist/agents/channels/sdk.js +4 -0
  291. package/dist/agents/channels/sdk.js.map +1 -1
  292. package/dist/agents/extensions/modules/index.d.ts.map +1 -1
  293. package/dist/agents/extensions/modules/index.js +12 -0
  294. package/dist/agents/extensions/modules/index.js.map +1 -1
  295. package/dist/agents/extensions/types.d.ts +7 -0
  296. package/dist/agents/extensions/types.d.ts.map +1 -1
  297. package/dist/agents/extensions/types.js.map +1 -1
  298. package/dist/agents/session-wiring.d.ts +31 -0
  299. package/dist/agents/session-wiring.d.ts.map +1 -1
  300. package/dist/agents/session-wiring.js +45 -2
  301. package/dist/agents/session-wiring.js.map +1 -1
  302. package/dist/agents/subagent-announce-delivery.d.ts +10 -0
  303. package/dist/agents/subagent-announce-delivery.d.ts.map +1 -1
  304. package/dist/agents/subagent-announce-delivery.js +1 -0
  305. package/dist/agents/subagent-announce-delivery.js.map +1 -1
  306. package/dist/agents/subagent-completion-bridge.d.ts.map +1 -1
  307. package/dist/agents/subagent-completion-bridge.js +81 -0
  308. package/dist/agents/subagent-completion-bridge.js.map +1 -1
  309. package/dist/agents/subagent-spawn.d.ts.map +1 -1
  310. package/dist/agents/subagent-spawn.js +57 -4
  311. package/dist/agents/subagent-spawn.js.map +1 -1
  312. package/dist/agents/tools/bluebubbles-action-tool.d.ts +66 -0
  313. package/dist/agents/tools/bluebubbles-action-tool.d.ts.map +1 -0
  314. package/dist/agents/tools/bluebubbles-action-tool.js +234 -0
  315. package/dist/agents/tools/bluebubbles-action-tool.js.map +1 -0
  316. package/dist/agents/tools/discord-action-tool.d.ts +224 -0
  317. package/dist/agents/tools/discord-action-tool.d.ts.map +1 -0
  318. package/dist/agents/tools/discord-action-tool.js +848 -0
  319. package/dist/agents/tools/discord-action-tool.js.map +1 -0
  320. package/dist/agents/tools/registry.d.ts.map +1 -1
  321. package/dist/agents/tools/registry.js +39 -0
  322. package/dist/agents/tools/registry.js.map +1 -1
  323. package/dist/agents/tools/sessions/index.d.ts +8 -0
  324. package/dist/agents/tools/sessions/index.d.ts.map +1 -1
  325. package/dist/agents/tools/sessions/index.js +15 -3
  326. package/dist/agents/tools/sessions/index.js.map +1 -1
  327. package/dist/buildstamp.json +1 -1
  328. package/dist/cli/commands/channels.d.ts +2 -0
  329. package/dist/cli/commands/channels.d.ts.map +1 -1
  330. package/dist/cli/commands/channels.js +58 -1
  331. package/dist/cli/commands/channels.js.map +1 -1
  332. package/dist/core/server.d.ts.map +1 -1
  333. package/dist/core/server.js +48 -2
  334. package/dist/core/server.js.map +1 -1
  335. package/dist/infra/net/fetch-guard.d.ts +24 -2
  336. package/dist/infra/net/fetch-guard.d.ts.map +1 -1
  337. package/dist/infra/net/fetch-guard.js +78 -31
  338. package/dist/infra/net/fetch-guard.js.map +1 -1
  339. package/package.json +1 -1
@@ -0,0 +1,848 @@
1
+ /**
2
+ * `discord_action` — owner-only Discord guild action surface (Phase 4).
3
+ *
4
+ * Brigade's live Discord adapter handles inbound + the everyday send/edit/react
5
+ * path, but it does NOT expose the wide guild-management surface a full Discord
6
+ * bot needs: creating/editing channels + categories, role + member management, emoji /
7
+ * sticker / scheduled-event admin, rich embeds + polls + stickers + threads +
8
+ * search, and moderation (ban / unban / kick / timeout). This tool fills that
9
+ * gap with a SELF-CONTAINED Discord REST v10 client — exactly like `probe.ts`
10
+ * and Slack's `directory-live.ts`: it resolves the bot token via
11
+ * `resolveDiscordBotToken` and talks straight to `https://discord.com/api/v10`
12
+ * with `Authorization: Bot <token>`. It never reaches through the live
13
+ * adapter/connection, so it works the same whether or not the Gateway socket is
14
+ * up.
15
+ *
16
+ * One meta-tool with an `action` discriminator (keeps the prompt small) dispatches
17
+ * to the matching helper in `discord/rest-actions.ts`. Owner-only: the standard
18
+ * `wrapOwnerOnlyToolExecution` gate (applied at session-wiring) already refuses
19
+ * non-owner senders and unattended cron turns, so this file does not re-implement
20
+ * auth. The registry only ASSEMBLES this tool when the Discord channel is
21
+ * configured (`channels.discord.enabled`), so a non-Discord install never sees it.
22
+ *
23
+ * Each action validates its required params, calls the REST helper, and returns a
24
+ * compact `jsonResult({ action, ok, … })`. REST failures decode (permissions /
25
+ * 404 / rate-limit / unknown-resource) into an operator-readable message via the
26
+ * `DiscordRestError` carried up from the helper.
27
+ *
28
+ * `set-presence` (Phase 5) is the ONE action that is NOT a REST call: presence
29
+ * is a Gateway (websocket) operation, and this self-contained REST tool holds no
30
+ * live discord.js client handle. The clean path chosen here is CONFIG-WRITE — it
31
+ * persists `channels.discord.presence` via `mutateConfigAtomic`, and the live
32
+ * connection applies that presence on its next (re)connect (it re-reads the
33
+ * resolved presence on every start). This keeps the tool stateless + air-gap-
34
+ * safe and avoids reaching across module boundaries into the running gateway.
35
+ */
36
+ import { Type } from "typebox";
37
+ import { loadConfig } from "../../core/config.js";
38
+ import { mutateConfigAtomic } from "../../config/io.js";
39
+ import { DISCORD_DEFAULT_ACCOUNT_ID, resolveDiscordBotToken, } from "../channels/discord/account-config.js";
40
+ import { DiscordRestError, ban, categoryCreate, categoryDelete, categoryEdit, channelCreate, channelDelete, channelEdit, channelMove, emojiList, emojiUpload, eventCreate, eventList, kick, listReactions, listThreads, memberInfo, readMessages, removeReaction, roleAdd, roleInfo, roleList, roleRemove, searchMessages, sendEmbed, sendMessage, sendPoll, sendSticker, stickerUpload, threadCreate, timeout, unban, untimeout, } from "../channels/discord/rest-actions.js";
41
+ import { serializeDiscordModalTrigger, serializeDiscordSelectRow, serializeDiscordV2Message, } from "../channels/discord/rest-components.js";
42
+ import { jsonResult } from "./common.js";
43
+ /* ───────────────────────────── params ───────────────────────────── */
44
+ const EmbedSpecSchema = Type.Object({
45
+ title: Type.Optional(Type.String({ maxLength: 256 })),
46
+ description: Type.Optional(Type.String({ maxLength: 4096 })),
47
+ color: Type.Optional(Type.Number({ description: "Decimal color int, e.g. 5793266 (0x5865F2)." })),
48
+ url: Type.Optional(Type.String({ maxLength: 2048 })),
49
+ footer: Type.Optional(Type.String({ maxLength: 2048 })),
50
+ image: Type.Optional(Type.String({ description: "Image URL.", maxLength: 2048 })),
51
+ thumbnail: Type.Optional(Type.String({ description: "Thumbnail URL.", maxLength: 2048 })),
52
+ fields: Type.Optional(Type.Array(Type.Object({
53
+ name: Type.String({ maxLength: 256 }),
54
+ value: Type.String({ maxLength: 1024 }),
55
+ inline: Type.Optional(Type.Boolean()),
56
+ }), { maxItems: 25 })),
57
+ }, { additionalProperties: false });
58
+ /* ── typed interactive-component specs (Fix A1) ── */
59
+ /** A structured string-select option. */
60
+ const SelectOptionSchema = Type.Object({
61
+ label: Type.String({ maxLength: 100 }),
62
+ value: Type.String({ maxLength: 100 }),
63
+ description: Type.Optional(Type.String({ maxLength: 100 })),
64
+ }, { additionalProperties: false });
65
+ /**
66
+ * A structured select-menu spec. The tool serializes it into a Discord
67
+ * action-row whose `custom_id` carries the general-callback marker, so a press
68
+ * routes through the existing select branch and surfaces the chosen values.
69
+ */
70
+ const SelectSpecSchema = Type.Object({
71
+ kind: Type.Union([Type.Literal("string"), Type.Literal("user"), Type.Literal("role"), Type.Literal("channel"), Type.Literal("mentionable")], { description: "Select kind. `string` needs `options`; the entity kinds (user/role/channel/mentionable) don't." }),
72
+ customId: Type.String({ description: "App-defined token a press routes back to the agent (general-prefixed automatically).", maxLength: 80 }),
73
+ placeholder: Type.Optional(Type.String({ maxLength: 150 })),
74
+ minValues: Type.Optional(Type.Number({ description: "Min selections (default 1)." })),
75
+ maxValues: Type.Optional(Type.Number({ description: "Max selections (default 1)." })),
76
+ options: Type.Optional(Type.Array(SelectOptionSchema, { maxItems: 25, description: "string-select options (required for kind:string, ≤25)." })),
77
+ }, { additionalProperties: false });
78
+ /** A single modal text-input field. */
79
+ const ModalFieldSchema = Type.Object({
80
+ id: Type.String({ description: "Field id — echoed back keying the submitted value.", maxLength: 100 }),
81
+ label: Type.String({ maxLength: 45 }),
82
+ style: Type.Optional(Type.Union([Type.Literal("short"), Type.Literal("paragraph")])),
83
+ required: Type.Optional(Type.Boolean()),
84
+ placeholder: Type.Optional(Type.String({ maxLength: 100 })),
85
+ }, { additionalProperties: false });
86
+ /**
87
+ * A structured modal spec. The tool registers the form in the TTL modal registry
88
+ * and emits a trigger button whose `custom_id` is the `modal:<id>` marker the
89
+ * press-router opens via `showModal`; submitting the form routes back as a turn.
90
+ */
91
+ const ModalSpecSchema = Type.Object({
92
+ buttonLabel: Type.String({ description: "Label of the button that opens the form.", maxLength: 80 }),
93
+ title: Type.Optional(Type.String({ description: "Modal heading.", maxLength: 45 })),
94
+ fields: Type.Array(ModalFieldSchema, { minItems: 1, maxItems: 5, description: "1..5 text-input fields." }),
95
+ buttonStyle: Type.Optional(Type.Number({ description: "Trigger button style (1=primary, 2=secondary, 3=success, 4=danger)." })),
96
+ }, { additionalProperties: false });
97
+ /** A Components-V2 layout block (discriminated by `type`). */
98
+ const BlockSpecSchema = Type.Union([
99
+ Type.Object({ type: Type.Literal("text"), text: Type.String({ maxLength: 4000 }) }, { additionalProperties: false }),
100
+ Type.Object({
101
+ type: Type.Literal("section"),
102
+ texts: Type.Array(Type.String({ maxLength: 4000 }), { minItems: 1, maxItems: 3 }),
103
+ accessory: Type.Optional(Type.Union([
104
+ Type.Object({ kind: Type.Literal("thumbnail"), url: Type.String({ maxLength: 2048 }) }, { additionalProperties: false }),
105
+ Type.Object({
106
+ kind: Type.Literal("button"),
107
+ button: Type.Object({
108
+ label: Type.String({ maxLength: 80 }),
109
+ url: Type.Optional(Type.String({ maxLength: 2048 })),
110
+ customId: Type.Optional(Type.String({ maxLength: 100 })),
111
+ style: Type.Optional(Type.Number()),
112
+ }, { additionalProperties: false }),
113
+ }, { additionalProperties: false }),
114
+ ])),
115
+ }, { additionalProperties: false }),
116
+ Type.Object({
117
+ type: Type.Literal("separator"),
118
+ divider: Type.Optional(Type.Boolean()),
119
+ spacing: Type.Optional(Type.Union([Type.Literal("small"), Type.Literal("large")])),
120
+ }, { additionalProperties: false }),
121
+ Type.Object({
122
+ type: Type.Literal("actions"),
123
+ buttons: Type.Array(Type.Object({
124
+ label: Type.String({ maxLength: 80 }),
125
+ url: Type.Optional(Type.String({ maxLength: 2048 })),
126
+ customId: Type.Optional(Type.String({ maxLength: 100 })),
127
+ style: Type.Optional(Type.Number()),
128
+ }, { additionalProperties: false }), { maxItems: 5 }),
129
+ }, { additionalProperties: false }),
130
+ Type.Object({
131
+ type: Type.Literal("media-gallery"),
132
+ items: Type.Array(Type.Object({ url: Type.String({ maxLength: 2048 }), description: Type.Optional(Type.String({ maxLength: 1024 })), spoiler: Type.Optional(Type.Boolean()) }, { additionalProperties: false }), { maxItems: 10 }),
133
+ }, { additionalProperties: false }),
134
+ Type.Object({ type: Type.Literal("file"), url: Type.String({ description: "An attachment:// ref.", maxLength: 2048 }), spoiler: Type.Optional(Type.Boolean()) }, { additionalProperties: false }),
135
+ ], { description: "A Components-V2 layout block." });
136
+ /** A structured Components-V2 message spec (container of blocks). */
137
+ const BlocksSpecSchema = Type.Object({
138
+ blocks: Type.Array(BlockSpecSchema, { minItems: 1, maxItems: 40, description: "Ordered V2 layout blocks." }),
139
+ accentColor: Type.Optional(Type.Number({ description: "Container accent color (decimal int)." })),
140
+ }, { additionalProperties: false });
141
+ const DiscordActionParams = Type.Object({
142
+ action: Type.Union([
143
+ // messaging / content
144
+ Type.Literal("send"),
145
+ Type.Literal("send-embed"),
146
+ Type.Literal("poll"),
147
+ Type.Literal("sticker"),
148
+ Type.Literal("read-messages"),
149
+ Type.Literal("list-reactions"),
150
+ Type.Literal("remove-reaction"),
151
+ Type.Literal("thread-create"),
152
+ Type.Literal("list-threads"),
153
+ Type.Literal("search-messages"),
154
+ // guild-admin
155
+ Type.Literal("channel-create"),
156
+ Type.Literal("channel-edit"),
157
+ Type.Literal("channel-delete"),
158
+ Type.Literal("channel-move"),
159
+ Type.Literal("category-create"),
160
+ Type.Literal("category-edit"),
161
+ Type.Literal("category-delete"),
162
+ Type.Literal("role-list"),
163
+ Type.Literal("role-add"),
164
+ Type.Literal("role-remove"),
165
+ Type.Literal("role-info"),
166
+ Type.Literal("member-info"),
167
+ Type.Literal("emoji-list"),
168
+ Type.Literal("emoji-upload"),
169
+ Type.Literal("sticker-upload"),
170
+ Type.Literal("event-list"),
171
+ Type.Literal("event-create"),
172
+ // moderation
173
+ Type.Literal("ban"),
174
+ Type.Literal("unban"),
175
+ Type.Literal("kick"),
176
+ Type.Literal("timeout"),
177
+ Type.Literal("untimeout"),
178
+ // presence (Gateway op — persisted to config, applied on (re)connect)
179
+ Type.Literal("set-presence"),
180
+ ], {
181
+ description: "The Discord guild action to run. Messaging: send, send-embed, poll, sticker, read-messages, list-reactions, remove-reaction, thread-create, list-threads, search-messages. Guild-admin: channel-create/edit/delete/move, category-create/edit/delete, role-list/add/remove/info, member-info, emoji-list/upload, sticker-upload, event-list/create. Moderation: ban, unban, kick, timeout, untimeout. Presence: set-presence (persists channels.discord.presence; applied on next (re)connect).",
182
+ }),
183
+ accountId: Type.Optional(Type.String({ description: "Which Discord bot account to act as (default: the configured default).", maxLength: 64 })),
184
+ // common targets
185
+ to: Type.Optional(Type.String({ description: "send/send-embed/poll/sticker target: a channel id, `channel:<id>`, or `user:<id>` to DM a user.", maxLength: 64 })),
186
+ channelId: Type.Optional(Type.String({ description: "Channel id for read/reaction/thread/channel-edit/delete actions.", maxLength: 64 })),
187
+ guildId: Type.Optional(Type.String({ description: "Guild (server) id for guild-admin + moderation + search actions.", maxLength: 64 })),
188
+ userId: Type.Optional(Type.String({ description: "Target user id (member-info / role add-remove / moderation).", maxLength: 64 })),
189
+ messageId: Type.Optional(Type.String({ description: "Target message id (reactions / thread-from-message).", maxLength: 64 })),
190
+ roleId: Type.Optional(Type.String({ description: "Role id (role-add / role-remove).", maxLength: 64 })),
191
+ categoryId: Type.Optional(Type.String({ description: "Category (type-4 channel) id (category-edit / category-delete).", maxLength: 64 })),
192
+ // content
193
+ content: Type.Optional(Type.String({ description: "Message text for send/poll/sticker.", maxLength: 4000 })),
194
+ embed: Type.Optional(EmbedSpecSchema),
195
+ embeds: Type.Optional(Type.Array(EmbedSpecSchema, { maxItems: 10, description: "Rich embeds for send." })),
196
+ components: Type.Optional(Type.Array(Type.Unknown(), { description: "send (power-user): raw Discord component-row JSON, passed through verbatim. Prefer the typed `select` / `modal` / `blocks` params." })),
197
+ select: Type.Optional(SelectSpecSchema),
198
+ modal: Type.Optional(ModalSpecSchema),
199
+ blocks: Type.Optional(BlocksSpecSchema),
200
+ replyTo: Type.Optional(Type.String({ description: "send: message id to reply to.", maxLength: 64 })),
201
+ silent: Type.Optional(Type.Boolean({ description: "send: suppress the @-notification ping." })),
202
+ // poll
203
+ question: Type.Optional(Type.String({ description: "poll question.", maxLength: 300 })),
204
+ answers: Type.Optional(Type.Array(Type.String({ maxLength: 55 }), { maxItems: 10, description: "poll answers (≤10)." })),
205
+ durationHours: Type.Optional(Type.Number({ description: "poll duration in hours (default 24)." })),
206
+ allowMultiselect: Type.Optional(Type.Boolean({ description: "poll: allow selecting multiple answers." })),
207
+ // sticker / emoji
208
+ stickerIds: Type.Optional(Type.Array(Type.String({ maxLength: 32 }), { maxItems: 3, description: "sticker: sticker ids to send (≤3)." })),
209
+ emoji: Type.Optional(Type.String({ description: "Reaction emoji — raw unicode, or `name:id` for a custom emoji.", maxLength: 128 })),
210
+ emojiName: Type.Optional(Type.String({ description: "emoji-upload: the new emoji's name.", maxLength: 64 })),
211
+ emojiImage: Type.Optional(Type.String({ description: "emoji-upload: a data URI (data:image/png;base64,…) for the emoji image.", maxLength: 600_000 })),
212
+ stickerName: Type.Optional(Type.String({ description: "sticker-upload: the new sticker name.", maxLength: 30 })),
213
+ stickerDescription: Type.Optional(Type.String({ description: "sticker-upload: the sticker description.", maxLength: 100 })),
214
+ stickerTags: Type.Optional(Type.String({ description: "sticker-upload: autocomplete/suggestion tags (a related unicode emoji or short keywords).", maxLength: 200 })),
215
+ stickerImage: Type.Optional(Type.String({ description: "sticker-upload: a data URI (data:image/png;base64,... PNG/APNG, or application/json for Lottie) for the sticker file.", maxLength: 600_000 })),
216
+ // reads / search
217
+ limit: Type.Optional(Type.Number({ description: "read-messages (≤50) / list-reactions (≤50) / search-messages (≤25) cap." })),
218
+ before: Type.Optional(Type.String({ description: "read-messages: fetch messages before this id.", maxLength: 64 })),
219
+ after: Type.Optional(Type.String({ description: "read-messages: fetch messages after this id.", maxLength: 64 })),
220
+ around: Type.Optional(Type.String({ description: "read-messages: fetch messages around this id.", maxLength: 64 })),
221
+ query: Type.Optional(Type.String({ description: "search-messages: the text to search for.", maxLength: 400 })),
222
+ authorId: Type.Optional(Type.String({ description: "search-messages: restrict to this author.", maxLength: 64 })),
223
+ // thread
224
+ name: Type.Optional(Type.String({ description: "Name for channel/category/role-less create + thread-create.", maxLength: 100 })),
225
+ threadType: Type.Optional(Type.Number({ description: "thread-create (standalone): channel type (11=public, 12=private)." })),
226
+ autoArchiveMinutes: Type.Optional(Type.Number({ description: "thread-create: idle minutes before auto-archive (60/1440/4320/10080)." })),
227
+ // channel-create / edit
228
+ channelType: Type.Optional(Type.Number({ description: "channel-create: Discord channel type (0=text, 2=voice, 5=announcement, 15=forum)." })),
229
+ parentId: Type.Optional(Type.String({ description: "channel-create/edit/move: parent category id.", maxLength: 64 })),
230
+ topic: Type.Optional(Type.String({ description: "channel-create/edit: channel topic.", maxLength: 1024 })),
231
+ position: Type.Optional(Type.Number({ description: "Sort position for channel/category create/edit/move." })),
232
+ nsfw: Type.Optional(Type.Boolean({ description: "channel-create/edit: mark NSFW." })),
233
+ rateLimitPerUser: Type.Optional(Type.Number({ description: "channel-edit: slow-mode seconds (0 disables)." })),
234
+ archived: Type.Optional(Type.Boolean({ description: "channel-edit (thread): set archived." })),
235
+ locked: Type.Optional(Type.Boolean({ description: "channel-edit (thread): set locked." })),
236
+ // events
237
+ eventName: Type.Optional(Type.String({ description: "event-create: the event name.", maxLength: 100 })),
238
+ startTime: Type.Optional(Type.String({ description: "event-create: ISO-8601 start time.", maxLength: 40 })),
239
+ endTime: Type.Optional(Type.String({ description: "event-create: ISO-8601 end time (required for external events).", maxLength: 40 })),
240
+ description: Type.Optional(Type.String({ description: "event-create: description.", maxLength: 1000 })),
241
+ location: Type.Optional(Type.String({ description: "event-create (external): physical/virtual location.", maxLength: 100 })),
242
+ entityType: Type.Optional(Type.Union([Type.Literal("stage"), Type.Literal("voice"), Type.Literal("external")], {
243
+ description: "event-create: stage / voice (needs channelId) / external (needs location + endTime). Default voice.",
244
+ })),
245
+ // moderation
246
+ reason: Type.Optional(Type.String({ description: "Audit-log reason for the moderation action.", maxLength: 512 })),
247
+ deleteMessageDays: Type.Optional(Type.Number({ description: "ban: purge the user's messages from the last N days (0–7)." })),
248
+ durationMinutes: Type.Optional(Type.Number({ description: "timeout: minutes to silence the member (1–40320 = 28 days)." })),
249
+ // presence (set-presence)
250
+ status: Type.Optional(Type.Union([Type.Literal("online"), Type.Literal("idle"), Type.Literal("dnd"), Type.Literal("invisible")], { description: "set-presence: the bot's online dot." })),
251
+ activityType: Type.Optional(Type.Union([
252
+ Type.Literal("playing"),
253
+ Type.Literal("streaming"),
254
+ Type.Literal("listening"),
255
+ Type.Literal("watching"),
256
+ Type.Literal("custom"),
257
+ Type.Literal("competing"),
258
+ ], { description: "set-presence: the activity row kind (custom uses the status state line; streaming uses activityUrl)." })),
259
+ activityText: Type.Optional(Type.String({ description: "set-presence: the activity text (the 'Playing …' / status line).", maxLength: 128 })),
260
+ activityUrl: Type.Optional(Type.String({ description: "set-presence (streaming): the Twitch/YouTube stream URL.", maxLength: 512 })),
261
+ });
262
+ const MAX_DATA_CHARS = 12_000;
263
+ /** Discord allows at most 5 component (action) rows on a classic message. */
264
+ const DISCORD_MAX_COMPONENT_ROWS = 5;
265
+ /** Compact a payload so a large list result can't flood the model's context. Pure. */
266
+ export function capDiscordData(value, maxChars = MAX_DATA_CHARS) {
267
+ let s;
268
+ try {
269
+ s = JSON.stringify(value);
270
+ }
271
+ catch {
272
+ return value;
273
+ }
274
+ if (!s || s.length <= maxChars)
275
+ return value;
276
+ return {
277
+ truncated: true,
278
+ bytes: s.length,
279
+ note: `Result truncated at ${maxChars} chars — narrow the request (a lower limit) and try again.`,
280
+ preview: s.slice(0, maxChars),
281
+ };
282
+ }
283
+ /**
284
+ * Decode a `data:<mime>;base64,<payload>` URI into raw bytes + the MIME type, for
285
+ * the multipart sticker upload (which needs the file bytes, not a JSON image
286
+ * string like emoji-upload). Returns null when the input isn't a base64 data URI.
287
+ */
288
+ export function decodeStickerDataUri(input) {
289
+ const m = /^data:([^;,]+);base64,(.+)$/s.exec((input ?? "").trim());
290
+ if (!m || !m[1] || !m[2])
291
+ return null;
292
+ try {
293
+ const bytes = new Uint8Array(Buffer.from(m[2], "base64"));
294
+ if (bytes.length === 0)
295
+ return null;
296
+ return { bytes, contentType: m[1].trim() };
297
+ }
298
+ catch {
299
+ return null;
300
+ }
301
+ }
302
+ /**
303
+ * Persist a presence block under `channels.discord.presence` (single-account) or
304
+ * `channels.discord.accounts[id].presence` (when the account exists in the
305
+ * accounts list). Returns the written presence object. Pure config edit — the
306
+ * live connection re-reads + applies it on next (re)connect.
307
+ */
308
+ function writeDiscordPresence(current, accountId, presence) {
309
+ const cfg = current;
310
+ const channels = { ...(cfg.channels ?? {}) };
311
+ const discord = { ...(channels.discord ?? {}) };
312
+ const accounts = Array.isArray(discord.accounts) ? discord.accounts : undefined;
313
+ const accountEntry = accounts?.find((a) => typeof a?.id === "string" && a.id.trim() === accountId);
314
+ if (accountEntry) {
315
+ // Per-account presence.
316
+ discord.accounts = accounts.map((a) => a === accountEntry ? { ...a, presence } : a);
317
+ }
318
+ else {
319
+ // Single-account / top-level presence.
320
+ discord.presence = presence;
321
+ }
322
+ channels.discord = discord;
323
+ return { ...current, channels };
324
+ }
325
+ export function makeDiscordActionTool(opts = {}) {
326
+ const resolveToken = opts.resolveToken ??
327
+ ((accountId) => {
328
+ try {
329
+ return resolveDiscordBotToken(loadConfig(), accountId);
330
+ }
331
+ catch {
332
+ return "";
333
+ }
334
+ });
335
+ return {
336
+ name: "discord_action",
337
+ label: "Discord action",
338
+ displaySummary: "managing Discord",
339
+ ownerOnly: true,
340
+ description: [
341
+ "Manage a Discord server over the Discord REST API: post/edit content and run guild administration the everyday chat reply can't.",
342
+ "Messaging: action:send (to a channel id or user:<id> DM — content + optional embeds; interactive `select` / `modal` / `blocks` (Components-V2) specs that the user can press/submit — a press routes back to you as a turn), send-embed (a rich embed), poll, sticker, read-messages, list-reactions, remove-reaction, thread-create, list-threads, search-messages.",
343
+ "Guild-admin: channel-create/edit/delete/move, category-create/edit/delete, role-list/add/remove/info, member-info, emoji-list/upload, event-list/create (scheduled events).",
344
+ "Moderation: ban, unban, kick, timeout, untimeout — Discord enforces the bot's own permissions (a missing-permission error is decoded into a clear hint).",
345
+ "Owner-only. Most actions need ids (guildId / channelId / userId / messageId); the tool reports clearly when one is missing.",
346
+ ].join(" "),
347
+ parameters: DiscordActionParams,
348
+ execute: async (_toolCallId, args) => {
349
+ const action = args.action;
350
+ const ok = (message, data) => jsonResult({ action, ok: true, message, ...(data !== undefined ? { data: capDiscordData(data) } : {}) });
351
+ const fail = (message) => jsonResult({ action, ok: false, message });
352
+ const accountId = (args.accountId ?? "").trim() || DISCORD_DEFAULT_ACCOUNT_ID;
353
+ // set-presence is a CONFIG write (Gateway op applied on next (re)connect),
354
+ // not a REST call — handle it before the live-token guard since it never
355
+ // touches Discord directly.
356
+ if (action === "set-presence") {
357
+ const status = args.status;
358
+ const activityType = args.activityType;
359
+ const activityText = (args.activityText ?? "").trim();
360
+ const activityUrl = (args.activityUrl ?? "").trim();
361
+ if (!status && !activityType && !activityText) {
362
+ return fail("set-presence requires at least a `status` or an `activityType` + `activityText`.");
363
+ }
364
+ const presence = {};
365
+ if (status)
366
+ presence.status = status;
367
+ if (activityType)
368
+ presence.activityType = activityType;
369
+ if (activityText)
370
+ presence.activityText = activityText;
371
+ if (activityType === "streaming" && activityUrl)
372
+ presence.activityUrl = activityUrl;
373
+ const mutate = opts.mutateConfig ?? ((m) => mutateConfigAtomic(m));
374
+ try {
375
+ await mutate((current) => writeDiscordPresence(current, accountId, presence));
376
+ }
377
+ catch (err) {
378
+ return fail(`set-presence failed to persist config: ${err instanceof Error ? err.message : String(err)}`);
379
+ }
380
+ return ok("Saved Discord presence — it applies on the bot's next (re)connect.", presence);
381
+ }
382
+ const token = resolveToken(accountId);
383
+ if (!token) {
384
+ return fail("No Discord bot token is configured — connect the Discord channel first (the token seals via connect_channel / channels.discord.botToken), then retry.");
385
+ }
386
+ const rest = {
387
+ token,
388
+ ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),
389
+ };
390
+ // Tiny required-param guard → a clean, actionable refusal (no REST round-trip).
391
+ const need = (value, label) => {
392
+ const v = (value ?? "").trim();
393
+ return v ? v : null;
394
+ };
395
+ const missing = (label) => fail(`${action} requires \`${label}\`.`);
396
+ try {
397
+ switch (action) {
398
+ /* ── messaging / content ── */
399
+ case "send": {
400
+ const to = need(args.to, "to");
401
+ if (!to)
402
+ return missing("to");
403
+ // Typed interactive components (Fix A1). Each structured spec is
404
+ // serialized into raw Discord component-row JSON carrying the SAME
405
+ // custom_id codecs the press-routing expects; a `blocks` (V2) spec
406
+ // also sets the IsComponentsV2 message flag (text must live in
407
+ // TextDisplay blocks, not plain content). The raw `components`
408
+ // passthrough still works for power users.
409
+ const componentRows = Array.isArray(args.components) ? [...args.components] : [];
410
+ let v2Flags = 0;
411
+ let isV2 = false;
412
+ if (args.modal) {
413
+ const built = serializeDiscordModalTrigger({
414
+ ...args.modal,
415
+ ...(accountId ? { accountId } : {}),
416
+ });
417
+ if (!built.ok)
418
+ return fail(built.error);
419
+ componentRows.push(built.row);
420
+ }
421
+ if (args.select) {
422
+ const built = serializeDiscordSelectRow(args.select);
423
+ if (!built.ok)
424
+ return fail(built.error);
425
+ componentRows.push(built.row);
426
+ }
427
+ if (args.blocks) {
428
+ const built = serializeDiscordV2Message(args.blocks);
429
+ if (!built.ok)
430
+ return fail(built.error);
431
+ // A V2 message is its OWN components array + flag — it cannot mix
432
+ // with classic content/embeds/action-rows.
433
+ if (args.content || (Array.isArray(args.embeds) && args.embeds.length > 0) || componentRows.length > 0) {
434
+ return fail("blocks (Components-V2) cannot be combined with content, embeds, or select/modal in one send — send them separately.");
435
+ }
436
+ componentRows.push(...built.components);
437
+ v2Flags = built.flags;
438
+ isV2 = true;
439
+ }
440
+ if (componentRows.length > DISCORD_MAX_COMPONENT_ROWS && !isV2) {
441
+ return fail(`send accepts at most ${DISCORD_MAX_COMPONENT_ROWS} component rows.`);
442
+ }
443
+ const data = await sendMessage({
444
+ to,
445
+ ...(args.content && !isV2 ? { content: args.content } : {}),
446
+ ...(args.embeds && !isV2 ? { embeds: args.embeds } : {}),
447
+ ...(componentRows.length > 0 ? { components: componentRows } : {}),
448
+ ...(v2Flags ? { flags: v2Flags } : {}),
449
+ ...(args.replyTo ? { replyTo: args.replyTo } : {}),
450
+ ...(args.silent ? { silent: true } : {}),
451
+ }, rest);
452
+ return ok(`Sent to ${to}.`, data);
453
+ }
454
+ case "send-embed": {
455
+ const to = need(args.to, "to");
456
+ if (!to)
457
+ return missing("to");
458
+ if (!args.embed)
459
+ return missing("embed");
460
+ const data = await sendEmbed({ to, embed: args.embed, ...(args.content ? { content: args.content } : {}) }, rest);
461
+ return ok(`Sent embed to ${to}.`, data);
462
+ }
463
+ case "poll": {
464
+ const to = need(args.to, "to");
465
+ if (!to)
466
+ return missing("to");
467
+ const question = need(args.question, "question");
468
+ if (!question)
469
+ return missing("question");
470
+ if (!Array.isArray(args.answers) || args.answers.length < 1)
471
+ return missing("answers");
472
+ const data = await sendPoll({
473
+ to,
474
+ question,
475
+ answers: args.answers,
476
+ ...(typeof args.durationHours === "number" ? { durationHours: args.durationHours } : {}),
477
+ ...(args.allowMultiselect ? { allowMultiselect: true } : {}),
478
+ }, rest);
479
+ return ok(`Posted a poll to ${to}.`, data);
480
+ }
481
+ case "sticker": {
482
+ const to = need(args.to, "to");
483
+ if (!to)
484
+ return missing("to");
485
+ if (!Array.isArray(args.stickerIds) || args.stickerIds.length < 1)
486
+ return missing("stickerIds");
487
+ const data = await sendSticker({ to, stickerIds: args.stickerIds, ...(args.content ? { content: args.content } : {}) }, rest);
488
+ return ok(`Sent sticker(s) to ${to}.`, data);
489
+ }
490
+ case "read-messages": {
491
+ const channelId = need(args.channelId, "channelId");
492
+ if (!channelId)
493
+ return missing("channelId");
494
+ const data = await readMessages({
495
+ channelId,
496
+ ...(typeof args.limit === "number" ? { limit: args.limit } : {}),
497
+ ...(args.before ? { before: args.before } : {}),
498
+ ...(args.after ? { after: args.after } : {}),
499
+ ...(args.around ? { around: args.around } : {}),
500
+ }, rest);
501
+ const count = Array.isArray(data) ? data.length : undefined;
502
+ return ok(count !== undefined ? `Fetched ${count} message(s).` : "Fetched messages.", data);
503
+ }
504
+ case "list-reactions": {
505
+ const channelId = need(args.channelId, "channelId");
506
+ if (!channelId)
507
+ return missing("channelId");
508
+ const messageId = need(args.messageId, "messageId");
509
+ if (!messageId)
510
+ return missing("messageId");
511
+ const emoji = need(args.emoji, "emoji");
512
+ if (!emoji)
513
+ return missing("emoji");
514
+ const data = await listReactions({ channelId, messageId, emoji, ...(typeof args.limit === "number" ? { limit: args.limit } : {}) }, rest);
515
+ return ok("Listed reactions.", data);
516
+ }
517
+ case "remove-reaction": {
518
+ const channelId = need(args.channelId, "channelId");
519
+ if (!channelId)
520
+ return missing("channelId");
521
+ const messageId = need(args.messageId, "messageId");
522
+ if (!messageId)
523
+ return missing("messageId");
524
+ const emoji = need(args.emoji, "emoji");
525
+ if (!emoji)
526
+ return missing("emoji");
527
+ await removeReaction({ channelId, messageId, emoji, ...(args.userId ? { userId: args.userId } : {}) }, rest);
528
+ return ok("Removed reaction.");
529
+ }
530
+ case "thread-create": {
531
+ const channelId = need(args.channelId, "channelId");
532
+ if (!channelId)
533
+ return missing("channelId");
534
+ const name = need(args.name, "name");
535
+ if (!name)
536
+ return missing("name");
537
+ const data = await threadCreate({
538
+ channelId,
539
+ name,
540
+ ...(args.messageId ? { messageId: args.messageId } : {}),
541
+ ...(typeof args.autoArchiveMinutes === "number" ? { autoArchiveMinutes: args.autoArchiveMinutes } : {}),
542
+ ...(typeof args.threadType === "number" ? { type: args.threadType } : {}),
543
+ ...(args.content ? { content: args.content } : {}),
544
+ }, rest);
545
+ return ok(`Created thread "${name}".`, data);
546
+ }
547
+ case "list-threads": {
548
+ const guildId = need(args.guildId, "guildId");
549
+ if (!guildId)
550
+ return missing("guildId");
551
+ const data = await listThreads({ guildId }, rest);
552
+ return ok("Listed active threads.", data);
553
+ }
554
+ case "search-messages": {
555
+ const guildId = need(args.guildId, "guildId");
556
+ if (!guildId)
557
+ return missing("guildId");
558
+ const query = need(args.query, "query");
559
+ if (!query)
560
+ return missing("query");
561
+ const data = await searchMessages({
562
+ guildId,
563
+ query,
564
+ ...(args.authorId ? { authorId: args.authorId } : {}),
565
+ ...(args.channelId ? { channelId: args.channelId } : {}),
566
+ ...(typeof args.limit === "number" ? { limit: args.limit } : {}),
567
+ }, rest);
568
+ return ok("Searched messages.", data);
569
+ }
570
+ /* ── guild-admin ── */
571
+ case "channel-create": {
572
+ const guildId = need(args.guildId, "guildId");
573
+ if (!guildId)
574
+ return missing("guildId");
575
+ const name = need(args.name, "name");
576
+ if (!name)
577
+ return missing("name");
578
+ const data = await channelCreate({
579
+ guildId,
580
+ name,
581
+ ...(typeof args.channelType === "number" ? { type: args.channelType } : {}),
582
+ ...(args.parentId ? { parentId: args.parentId } : {}),
583
+ ...(args.topic ? { topic: args.topic } : {}),
584
+ ...(typeof args.position === "number" ? { position: args.position } : {}),
585
+ ...(typeof args.nsfw === "boolean" ? { nsfw: args.nsfw } : {}),
586
+ }, rest);
587
+ return ok(`Created channel "${name}".`, data);
588
+ }
589
+ case "channel-edit": {
590
+ const channelId = need(args.channelId, "channelId");
591
+ if (!channelId)
592
+ return missing("channelId");
593
+ const data = await channelEdit({
594
+ channelId,
595
+ ...(args.name !== undefined ? { name: args.name } : {}),
596
+ ...(args.topic !== undefined ? { topic: args.topic } : {}),
597
+ ...(typeof args.position === "number" ? { position: args.position } : {}),
598
+ ...(args.parentId !== undefined ? { parentId: args.parentId } : {}),
599
+ ...(typeof args.nsfw === "boolean" ? { nsfw: args.nsfw } : {}),
600
+ ...(typeof args.rateLimitPerUser === "number" ? { rateLimitPerUser: args.rateLimitPerUser } : {}),
601
+ ...(typeof args.archived === "boolean" ? { archived: args.archived } : {}),
602
+ ...(typeof args.locked === "boolean" ? { locked: args.locked } : {}),
603
+ }, rest);
604
+ return ok("Edited channel.", data);
605
+ }
606
+ case "channel-delete": {
607
+ const channelId = need(args.channelId, "channelId");
608
+ if (!channelId)
609
+ return missing("channelId");
610
+ const data = await channelDelete({ channelId }, rest);
611
+ return ok("Deleted channel.", data);
612
+ }
613
+ case "channel-move": {
614
+ const guildId = need(args.guildId, "guildId");
615
+ if (!guildId)
616
+ return missing("guildId");
617
+ const channelId = need(args.channelId, "channelId");
618
+ if (!channelId)
619
+ return missing("channelId");
620
+ await channelMove({
621
+ guildId,
622
+ channelId,
623
+ ...(typeof args.position === "number" ? { position: args.position } : {}),
624
+ ...(args.parentId !== undefined ? { parentId: args.parentId } : {}),
625
+ }, rest);
626
+ return ok("Moved channel.");
627
+ }
628
+ case "category-create": {
629
+ const guildId = need(args.guildId, "guildId");
630
+ if (!guildId)
631
+ return missing("guildId");
632
+ const name = need(args.name, "name");
633
+ if (!name)
634
+ return missing("name");
635
+ const data = await categoryCreate({ guildId, name, ...(typeof args.position === "number" ? { position: args.position } : {}) }, rest);
636
+ return ok(`Created category "${name}".`, data);
637
+ }
638
+ case "category-edit": {
639
+ const categoryId = need(args.categoryId, "categoryId");
640
+ if (!categoryId)
641
+ return missing("categoryId");
642
+ const data = await categoryEdit({
643
+ categoryId,
644
+ ...(args.name !== undefined ? { name: args.name } : {}),
645
+ ...(typeof args.position === "number" ? { position: args.position } : {}),
646
+ }, rest);
647
+ return ok("Edited category.", data);
648
+ }
649
+ case "category-delete": {
650
+ const categoryId = need(args.categoryId, "categoryId");
651
+ if (!categoryId)
652
+ return missing("categoryId");
653
+ const data = await categoryDelete({ categoryId }, rest);
654
+ return ok("Deleted category.", data);
655
+ }
656
+ case "role-list": {
657
+ const guildId = need(args.guildId, "guildId");
658
+ if (!guildId)
659
+ return missing("guildId");
660
+ const data = await roleList({ guildId }, rest);
661
+ return ok("Listed roles.", data);
662
+ }
663
+ case "role-info": {
664
+ const guildId = need(args.guildId, "guildId");
665
+ if (!guildId)
666
+ return missing("guildId");
667
+ const data = await roleInfo({ guildId }, rest);
668
+ return ok("Fetched role info.", data);
669
+ }
670
+ case "role-add": {
671
+ const guildId = need(args.guildId, "guildId");
672
+ if (!guildId)
673
+ return missing("guildId");
674
+ const userId = need(args.userId, "userId");
675
+ if (!userId)
676
+ return missing("userId");
677
+ const roleId = need(args.roleId, "roleId");
678
+ if (!roleId)
679
+ return missing("roleId");
680
+ await roleAdd({ guildId, userId, roleId, ...(args.reason ? { reason: args.reason } : {}) }, rest);
681
+ return ok(`Added role ${roleId} to ${userId}.`);
682
+ }
683
+ case "role-remove": {
684
+ const guildId = need(args.guildId, "guildId");
685
+ if (!guildId)
686
+ return missing("guildId");
687
+ const userId = need(args.userId, "userId");
688
+ if (!userId)
689
+ return missing("userId");
690
+ const roleId = need(args.roleId, "roleId");
691
+ if (!roleId)
692
+ return missing("roleId");
693
+ await roleRemove({ guildId, userId, roleId, ...(args.reason ? { reason: args.reason } : {}) }, rest);
694
+ return ok(`Removed role ${roleId} from ${userId}.`);
695
+ }
696
+ case "member-info": {
697
+ const guildId = need(args.guildId, "guildId");
698
+ if (!guildId)
699
+ return missing("guildId");
700
+ const userId = need(args.userId, "userId");
701
+ if (!userId)
702
+ return missing("userId");
703
+ const data = await memberInfo({ guildId, userId }, rest);
704
+ return ok("Fetched member info.", data);
705
+ }
706
+ case "emoji-list": {
707
+ const guildId = need(args.guildId, "guildId");
708
+ if (!guildId)
709
+ return missing("guildId");
710
+ const data = await emojiList({ guildId }, rest);
711
+ return ok("Listed emojis.", data);
712
+ }
713
+ case "emoji-upload": {
714
+ const guildId = need(args.guildId, "guildId");
715
+ if (!guildId)
716
+ return missing("guildId");
717
+ const name = need(args.emojiName, "emojiName");
718
+ if (!name)
719
+ return missing("emojiName");
720
+ const image = need(args.emojiImage, "emojiImage");
721
+ if (!image)
722
+ return missing("emojiImage");
723
+ const data = await emojiUpload({ guildId, name, image }, rest);
724
+ return ok(`Uploaded emoji "${name}".`, data);
725
+ }
726
+ case "sticker-upload": {
727
+ const guildId = need(args.guildId, "guildId");
728
+ if (!guildId)
729
+ return missing("guildId");
730
+ const name = need(args.stickerName, "stickerName");
731
+ if (!name)
732
+ return missing("stickerName");
733
+ const description = (args.stickerDescription ?? "").trim();
734
+ const tags = need(args.stickerTags, "stickerTags");
735
+ if (!tags)
736
+ return missing("stickerTags");
737
+ const image = need(args.stickerImage, "stickerImage");
738
+ if (!image)
739
+ return missing("stickerImage");
740
+ const decoded = decodeStickerDataUri(image);
741
+ if (!decoded) {
742
+ return fail("sticker-upload `stickerImage` must be a base64 data URI (data:image/png;base64,…).");
743
+ }
744
+ const data = await stickerUpload({ guildId, name, description, tags, file: decoded.bytes, contentType: decoded.contentType }, rest);
745
+ return ok(`Uploaded sticker "${name}".`, data);
746
+ }
747
+ case "event-list": {
748
+ const guildId = need(args.guildId, "guildId");
749
+ if (!guildId)
750
+ return missing("guildId");
751
+ const data = await eventList({ guildId }, rest);
752
+ return ok("Listed scheduled events.", data);
753
+ }
754
+ case "event-create": {
755
+ const guildId = need(args.guildId, "guildId");
756
+ if (!guildId)
757
+ return missing("guildId");
758
+ const name = need(args.eventName, "eventName");
759
+ if (!name)
760
+ return missing("eventName");
761
+ const startTime = need(args.startTime, "startTime");
762
+ if (!startTime)
763
+ return missing("startTime");
764
+ const data = await eventCreate({
765
+ guildId,
766
+ name,
767
+ startTime,
768
+ ...(args.endTime ? { endTime: args.endTime } : {}),
769
+ ...(args.description ? { description: args.description } : {}),
770
+ ...(args.channelId ? { channelId: args.channelId } : {}),
771
+ ...(args.location ? { location: args.location } : {}),
772
+ ...(args.entityType ? { entityType: args.entityType } : {}),
773
+ }, rest);
774
+ return ok(`Created scheduled event "${name}".`, data);
775
+ }
776
+ /* ── moderation ── */
777
+ case "ban": {
778
+ const guildId = need(args.guildId, "guildId");
779
+ if (!guildId)
780
+ return missing("guildId");
781
+ const userId = need(args.userId, "userId");
782
+ if (!userId)
783
+ return missing("userId");
784
+ await ban({
785
+ guildId,
786
+ userId,
787
+ ...(args.reason ? { reason: args.reason } : {}),
788
+ ...(typeof args.deleteMessageDays === "number" ? { deleteMessageDays: args.deleteMessageDays } : {}),
789
+ }, rest);
790
+ return ok(`Banned ${userId}.`);
791
+ }
792
+ case "unban": {
793
+ const guildId = need(args.guildId, "guildId");
794
+ if (!guildId)
795
+ return missing("guildId");
796
+ const userId = need(args.userId, "userId");
797
+ if (!userId)
798
+ return missing("userId");
799
+ await unban({ guildId, userId, ...(args.reason ? { reason: args.reason } : {}) }, rest);
800
+ return ok(`Unbanned ${userId}.`);
801
+ }
802
+ case "kick": {
803
+ const guildId = need(args.guildId, "guildId");
804
+ if (!guildId)
805
+ return missing("guildId");
806
+ const userId = need(args.userId, "userId");
807
+ if (!userId)
808
+ return missing("userId");
809
+ await kick({ guildId, userId, ...(args.reason ? { reason: args.reason } : {}) }, rest);
810
+ return ok(`Kicked ${userId}.`);
811
+ }
812
+ case "timeout": {
813
+ const guildId = need(args.guildId, "guildId");
814
+ if (!guildId)
815
+ return missing("guildId");
816
+ const userId = need(args.userId, "userId");
817
+ if (!userId)
818
+ return missing("userId");
819
+ if (typeof args.durationMinutes !== "number" || args.durationMinutes <= 0) {
820
+ return fail("timeout requires `durationMinutes` (> 0).");
821
+ }
822
+ await timeout({ guildId, userId, durationMinutes: args.durationMinutes, ...(args.reason ? { reason: args.reason } : {}) }, rest);
823
+ return ok(`Timed out ${userId} for ${args.durationMinutes} minute(s).`);
824
+ }
825
+ case "untimeout": {
826
+ const guildId = need(args.guildId, "guildId");
827
+ if (!guildId)
828
+ return missing("guildId");
829
+ const userId = need(args.userId, "userId");
830
+ if (!userId)
831
+ return missing("userId");
832
+ await untimeout({ guildId, userId, ...(args.reason ? { reason: args.reason } : {}) }, rest);
833
+ return ok(`Cleared timeout for ${userId}.`);
834
+ }
835
+ default:
836
+ return fail(`Unknown action "${String(action)}".`);
837
+ }
838
+ }
839
+ catch (err) {
840
+ if (err instanceof DiscordRestError) {
841
+ return fail(`Discord ${action} failed: ${err.message}`);
842
+ }
843
+ return fail(`Discord ${action} failed: ${err instanceof Error ? err.message : String(err)}`);
844
+ }
845
+ },
846
+ };
847
+ }
848
+ //# sourceMappingURL=discord-action-tool.js.map