@spacebar_ai/moldclaw-core 2026.3.43 → 2026.3.45

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 (1131) hide show
  1. package/dist/accounts-5qY-dKca.d.ts +103 -0
  2. package/dist/accounts-CxV5zzzp.js +114 -0
  3. package/dist/acp-cli-2JoKJ0xt.js +2093 -0
  4. package/dist/actions.runtime-BJPd9nat.js +119 -0
  5. package/dist/actions.runtime-DVs4a2cB.js +133 -0
  6. package/dist/agent-scope-lZlwP1At.js +208 -0
  7. package/dist/agents-CmT3Jwvj.js +222 -0
  8. package/dist/agents-D7Pebhph.js +853 -0
  9. package/dist/agents.config-CX9CPNfP.js +17 -0
  10. package/dist/agents.config-DF9Zwn9n.js +121 -0
  11. package/dist/allow-list-B3f4EqAM.js +81 -0
  12. package/dist/allowlist-_YpK0-4h.js +142 -0
  13. package/dist/api-B44giplI.js +117 -0
  14. package/dist/audit-C3InXm7H.js +787 -0
  15. package/dist/audit-ChWS6RiF.js +54 -0
  16. package/dist/audit-channel.collect.runtime-DZzrS6v2.js +605 -0
  17. package/dist/audit-channel.runtime-B2XTOsSF.js +121 -0
  18. package/dist/audit-extra.async-C2G0mqmk.js +813 -0
  19. package/dist/audit-membership-runtime-DOv5-eHo.js +162 -0
  20. package/dist/audit.deep.runtime-DyL9O_sU.js +25 -0
  21. package/dist/audit.nondeep.runtime-C6jFgJfH.js +832 -0
  22. package/dist/audit.runtime-Cvh2wmWM.js +118 -0
  23. package/dist/auth-Ch3Rchm4.js +101 -0
  24. package/dist/auth-choice-Beq0dWPS.js +268 -0
  25. package/dist/auth-choice-DZYg94gS.js +122 -0
  26. package/dist/auth-choice-TVG_8lJj.js +507 -0
  27. package/dist/auth-choice-options-B_qxsjdj.js +123 -0
  28. package/dist/auth-choice-prompt-BayzDobT.js +115 -0
  29. package/dist/auth-choice-prompt-_UES859x.js +36 -0
  30. package/dist/auth-choice.apply-helpers-BhbNIV8X.js +66 -0
  31. package/dist/auth-choice.plugin-providers.runtime-CVC4Jzhp.js +119 -0
  32. package/dist/auth-profiles-C-Ul-Qkn.js +128139 -0
  33. package/dist/auth-profiles.runtime-Clyka_jV.js +116 -0
  34. package/dist/banner-CojBHPWr.js +342 -0
  35. package/dist/bluebubbles-BnLsj2Fy.d.ts +6 -0
  36. package/dist/bluebubbles-irYTGbfn.js +64 -0
  37. package/dist/bot-DdyrB2z9.d.ts +478 -0
  38. package/dist/brave-w4Fo8WZ3.js +24 -0
  39. package/dist/browser-cli-DWFs3P_i.js +1494 -0
  40. package/dist/build-info.json +3 -3
  41. package/dist/bundled/boot-md/handler.d.ts +1 -1
  42. package/dist/bundled/boot-md/handler.js +35 -35
  43. package/dist/bundled/bootstrap-extra-files/handler.d.ts +1 -1
  44. package/dist/bundled/bootstrap-extra-files/handler.js +1 -1
  45. package/dist/bundled/command-logger/handler.d.ts +1 -1
  46. package/dist/bundled/session-memory/handler.d.ts +1 -1
  47. package/dist/bundled/session-memory/handler.js +36 -36
  48. package/dist/call-Do7wTSr7.js +39 -0
  49. package/dist/call-gdDAt07d.js +640 -0
  50. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  51. package/dist/channel-34O87pvK.js +949 -0
  52. package/dist/channel-B3Xo_4d8.js +1598 -0
  53. package/dist/channel-BEqDvL49.js +397 -0
  54. package/dist/channel-BLOk9WRE.js +214 -0
  55. package/dist/channel-BY5H2cdy.js +803 -0
  56. package/dist/channel-BeOtzXIH.js +4681 -0
  57. package/dist/channel-Bt8x_5Kj.js +306 -0
  58. package/dist/channel-C3E8XWLF.js +1321 -0
  59. package/dist/channel-C6ngVfGr.js +943 -0
  60. package/dist/channel-CVDBca2b.js +226 -0
  61. package/dist/channel-Cctli72q.js +562 -0
  62. package/dist/channel-DEAbU9Dj2.js +316 -0
  63. package/dist/channel-DIULkrBN.js +575 -0
  64. package/dist/channel-DXb4gsSd.js +619 -0
  65. package/dist/channel-Dap9LY-9.js +352 -0
  66. package/dist/channel-DcJdUnab.js +538 -0
  67. package/dist/channel-ZYCQj3hP.js +207 -0
  68. package/dist/channel-account-context-Baszl3_B.js +103 -0
  69. package/dist/channel-hlzhFHDl.js +497 -0
  70. package/dist/channel-options-PwBzYYtq.js +50 -0
  71. package/dist/channel-plugin-ids-TZIY4hFs.js +26 -0
  72. package/dist/channel-summary-CCEAV13o.js +111 -0
  73. package/dist/channel-tY2Hg5ac.js +542 -0
  74. package/dist/channel-vMWcWq1U.js +920 -0
  75. package/dist/channel.runtime-8qWgkCxX.js +404 -0
  76. package/dist/channel.runtime-C4jtgrbI.js +179 -0
  77. package/dist/channel.runtime-CJ4ug6Mp.js +127 -0
  78. package/dist/channel.runtime-ChKRJAZ8.js +4011 -0
  79. package/dist/channel.runtime-Cj54LYU-.js +418 -0
  80. package/dist/channel.runtime-KH06-bu1.js +870 -0
  81. package/dist/channel.runtime-_GYnBh2X.js +199 -0
  82. package/dist/channel.runtime-go7Pzt6N.js +241 -0
  83. package/dist/channel.runtime-iypIZ0EU.js +171 -0
  84. package/dist/channel.runtime-mukTWpon.js +182 -0
  85. package/dist/channel.runtime-oNtF1sc3.js +218 -0
  86. package/dist/channel.setup-BZfok6kX.js +11 -0
  87. package/dist/channel.setup-CbXfZ2dq.js +9 -0
  88. package/dist/channel.setup-CfyM55np.js +8 -0
  89. package/dist/channel.setup-CoG-gJ6n.js +9 -0
  90. package/dist/channel.setup-Cp4PH_rc.js +57 -0
  91. package/dist/channel.setup-V2w9Jk9j.js +6 -0
  92. package/dist/channel.setup-t7F5WZE7.js +8 -0
  93. package/dist/channels/plugins/actions/discord.d.ts +2 -2
  94. package/dist/channels/plugins/actions/discord.js +35 -35
  95. package/dist/channels/plugins/actions/signal.d.ts +1 -1
  96. package/dist/channels/plugins/actions/signal.js +35 -35
  97. package/dist/channels/plugins/actions/telegram.d.ts +2 -2
  98. package/dist/channels/plugins/actions/telegram.js +35 -35
  99. package/dist/channels/plugins/agent-tools/whatsapp-login.d.ts +3 -3
  100. package/dist/channels/plugins/agent-tools/whatsapp-login.js +35 -35
  101. package/dist/channels-H5ThmyG8.js +1118 -0
  102. package/dist/channels-cQ-6NFCQ.js +404 -0
  103. package/dist/channels-cli-TmchKLFS.js +291 -0
  104. package/dist/channels-status-issues-DXnkXEeJ.js +16 -0
  105. package/dist/clawbot-cli-Cpbxu8dD.js +118 -0
  106. package/dist/cleanup-utils-D0L17RsX.js +96 -0
  107. package/dist/cli/daemon-cli.js +1 -1
  108. package/dist/cli--2bGSKdX.js +154 -0
  109. package/dist/command-registry-BaVPDmwn.js +242 -0
  110. package/dist/command-registry-DhLyeSbO.js +14 -0
  111. package/dist/command-secret-gateway-C5D_-jb0.js +111 -0
  112. package/dist/compact.runtime-C9a0kRAg.js +116 -0
  113. package/dist/completion-cli-Bg95upyk.js +17 -0
  114. package/dist/completion-cli-CrPdlnCH.js +445 -0
  115. package/dist/config-BbvDRSYp.js +31 -0
  116. package/dist/config-CwBv71QC.js +44 -0
  117. package/dist/config-cli-jsZsYhAl.js +678 -0
  118. package/dist/config-guard-DTaXXqly.js +118 -0
  119. package/dist/config-validation-Q6U1sP84.js +262 -0
  120. package/dist/config-value-DT3-5958.js +132 -0
  121. package/dist/configure-BJfMiVej.js +1100 -0
  122. package/dist/configure-Cist3Iza.js +243 -0
  123. package/dist/control-ui-assets-C1YDYi82.js +232 -0
  124. package/dist/control-ui-shared-Dm5Dh0Lo.js +29 -0
  125. package/dist/core-BwKq3krw.js +150 -0
  126. package/dist/core-hjBwfDsW.d.ts +87 -0
  127. package/dist/cron-cli-Bktbk7iP.js +639 -0
  128. package/dist/daemon-cli-BXyH8DBL.js +339 -0
  129. package/dist/daemon-install-mxcGYsRJ.js +180 -0
  130. package/dist/deliver-BPYUKaSp.js +111 -0
  131. package/dist/deliver-runtime-CwnLQZTN.js +111 -0
  132. package/dist/device-id-cli-XvwZbIyC.js +52 -0
  133. package/dist/device-identity-IG5DngWM.js +365 -0
  134. package/dist/devices-cli-DIsxj4xp.js +342 -0
  135. package/dist/diagnostic-DTPopFvh.js +310 -0
  136. package/dist/directory-cli-D253m0er.js +311 -0
  137. package/dist/directory-config-helpers-DpFcAbmo.d.ts +38 -0
  138. package/dist/directory.static-BXv9MXqm.js +44 -0
  139. package/dist/discord-DwFf1qp7.js +114 -0
  140. package/dist/discovery-DzRM1wzK.js +48 -0
  141. package/dist/dm-policy-shared-DKoGdUpY.d.ts +95 -0
  142. package/dist/dns-cli-BJiz6CLK.js +217 -0
  143. package/dist/docs-cli-Dq2Yi5qO.js +174 -0
  144. package/dist/doctor-completion-BPl6HiiZ.js +90 -0
  145. package/dist/doctor-config-flow-BHR1Ayyh.js +2437 -0
  146. package/dist/doctor-config-flow-PXVtrfkE.js +112 -0
  147. package/dist/enable-Cyagpq3b.js +24 -0
  148. package/dist/entry.js +4 -4
  149. package/dist/exec-approvals-cli-kLAev6bP.js +421 -0
  150. package/dist/extensions/acpx/index.d.ts +1 -1
  151. package/dist/extensions/amazon-bedrock/index.d.ts +1 -1
  152. package/dist/extensions/amazon-bedrock/index.js +4 -4
  153. package/dist/extensions/anthropic/index.d.ts +1 -1
  154. package/dist/extensions/anthropic/index.js +35 -35
  155. package/dist/extensions/bluebubbles/index.d.ts +1 -1
  156. package/dist/extensions/bluebubbles/index.js +39 -39
  157. package/dist/extensions/bluebubbles/setup-entry.d.ts +2 -2
  158. package/dist/extensions/bluebubbles/setup-entry.js +39 -39
  159. package/dist/extensions/brave/index.d.ts +1 -1
  160. package/dist/extensions/brave/index.js +5 -5
  161. package/dist/extensions/byteplus/index.d.ts +1 -1
  162. package/dist/extensions/byteplus/index.js +35 -35
  163. package/dist/extensions/cloudflare-ai-gateway/index.d.ts +1 -1
  164. package/dist/extensions/cloudflare-ai-gateway/index.js +36 -36
  165. package/dist/extensions/copilot-proxy/index.d.ts +1 -1
  166. package/dist/extensions/copilot-proxy/index.js +4 -4
  167. package/dist/extensions/device-pair/index.d.ts +1 -1
  168. package/dist/extensions/device-pair/index.js +4 -4
  169. package/dist/extensions/diagnostics-otel/index.d.ts +1 -1
  170. package/dist/extensions/diagnostics-otel/index.js +4 -4
  171. package/dist/extensions/diffs/index.d.ts +1 -1
  172. package/dist/extensions/discord/index.d.ts +1 -1
  173. package/dist/extensions/discord/index.js +40 -40
  174. package/dist/extensions/discord/setup-entry.d.ts +1 -1
  175. package/dist/extensions/discord/setup-entry.js +38 -38
  176. package/dist/extensions/elevenlabs/index.d.ts +1 -1
  177. package/dist/extensions/elevenlabs/index.js +35 -35
  178. package/dist/extensions/feishu/index.d.ts +2 -2
  179. package/dist/extensions/feishu/index.js +40 -40
  180. package/dist/extensions/feishu/setup-entry.d.ts +2 -2
  181. package/dist/extensions/feishu/setup-entry.js +37 -37
  182. package/dist/extensions/firecrawl/index.d.ts +1 -1
  183. package/dist/extensions/firecrawl/index.js +35 -35
  184. package/dist/extensions/github-copilot/index.d.ts +1 -1
  185. package/dist/extensions/github-copilot/index.js +35 -35
  186. package/dist/extensions/google/index.d.ts +1 -1
  187. package/dist/extensions/google/index.js +35 -35
  188. package/dist/extensions/googlechat/index.d.ts +1 -1
  189. package/dist/extensions/googlechat/index.js +38 -38
  190. package/dist/extensions/googlechat/setup-entry.d.ts +1 -1
  191. package/dist/extensions/googlechat/setup-entry.js +38 -38
  192. package/dist/extensions/huggingface/index.d.ts +1 -1
  193. package/dist/extensions/huggingface/index.js +35 -35
  194. package/dist/extensions/imessage/index.d.ts +1 -1
  195. package/dist/extensions/imessage/index.js +39 -39
  196. package/dist/extensions/imessage/setup-entry.d.ts +1 -1
  197. package/dist/extensions/imessage/setup-entry.js +39 -39
  198. package/dist/extensions/irc/index.d.ts +1 -1
  199. package/dist/extensions/irc/index.js +38 -38
  200. package/dist/extensions/irc/setup-entry.d.ts +2 -2
  201. package/dist/extensions/irc/setup-entry.js +38 -38
  202. package/dist/extensions/kakao-talkchannel/index.d.ts +1 -1
  203. package/dist/extensions/kakao-talkchannel/index.js +4 -4
  204. package/dist/extensions/kilocode/index.d.ts +1 -1
  205. package/dist/extensions/kilocode/index.js +35 -35
  206. package/dist/extensions/kimi-coding/index.d.ts +1 -1
  207. package/dist/extensions/kimi-coding/index.js +35 -35
  208. package/dist/extensions/line/index.d.ts +1 -1
  209. package/dist/extensions/line/index.js +37 -37
  210. package/dist/extensions/line/setup-entry.d.ts +1 -1
  211. package/dist/extensions/line/setup-entry.js +37 -37
  212. package/dist/extensions/llm-task/index.d.ts +1 -1
  213. package/dist/extensions/llm-task/index.js +35 -35
  214. package/dist/extensions/lobster/index.d.ts +1 -1
  215. package/dist/extensions/lobster/index.js +4 -4
  216. package/dist/extensions/matrix/index.d.ts +1 -1
  217. package/dist/extensions/matrix/index.js +40 -40
  218. package/dist/extensions/matrix/setup-entry.d.ts +2 -2
  219. package/dist/extensions/matrix/setup-entry.js +40 -40
  220. package/dist/extensions/mattermost/index.d.ts +1 -1
  221. package/dist/extensions/mattermost/index.js +37 -37
  222. package/dist/extensions/mattermost/setup-entry.d.ts +2 -2
  223. package/dist/extensions/mattermost/setup-entry.js +37 -37
  224. package/dist/extensions/memory-core/index.d.ts +1 -1
  225. package/dist/extensions/memory-core/index.js +4 -4
  226. package/dist/extensions/memory-lancedb/index.d.ts +1 -1
  227. package/dist/extensions/memory-lancedb/index.js +4 -4
  228. package/dist/extensions/microsoft/index.d.ts +1 -1
  229. package/dist/extensions/microsoft/index.js +35 -35
  230. package/dist/extensions/minimax/index.d.ts +1 -1
  231. package/dist/extensions/minimax/index.js +35 -35
  232. package/dist/extensions/mistral/index.d.ts +1 -1
  233. package/dist/extensions/mistral/index.js +35 -35
  234. package/dist/extensions/modelstudio/index.d.ts +1 -1
  235. package/dist/extensions/modelstudio/index.js +35 -35
  236. package/dist/extensions/moonshot/index.d.ts +1 -1
  237. package/dist/extensions/moonshot/index.js +35 -35
  238. package/dist/extensions/msteams/index.d.ts +1 -1
  239. package/dist/extensions/msteams/index.js +40 -40
  240. package/dist/extensions/msteams/setup-entry.d.ts +1 -1
  241. package/dist/extensions/msteams/setup-entry.js +40 -40
  242. package/dist/extensions/nextcloud-talk/index.d.ts +1 -1
  243. package/dist/extensions/nextcloud-talk/index.js +37 -37
  244. package/dist/extensions/nextcloud-talk/setup-entry.d.ts +2 -2
  245. package/dist/extensions/nextcloud-talk/setup-entry.js +37 -37
  246. package/dist/extensions/nostr/index.d.ts +1 -1
  247. package/dist/extensions/nostr/index.js +37 -37
  248. package/dist/extensions/nostr/setup-entry.d.ts +1 -1
  249. package/dist/extensions/nostr/setup-entry.js +37 -37
  250. package/dist/extensions/nvidia/index.d.ts +1 -1
  251. package/dist/extensions/nvidia/index.js +4 -4
  252. package/dist/extensions/ollama/index.d.ts +1 -1
  253. package/dist/extensions/ollama/index.js +7 -7
  254. package/dist/extensions/open-prose/index.d.ts +1 -1
  255. package/dist/extensions/open-prose/index.js +4 -4
  256. package/dist/extensions/openai/index.d.ts +1 -1
  257. package/dist/extensions/openai/index.js +35 -35
  258. package/dist/extensions/opencode/index.d.ts +1 -1
  259. package/dist/extensions/opencode/index.js +35 -35
  260. package/dist/extensions/opencode-go/index.d.ts +1 -1
  261. package/dist/extensions/opencode-go/index.js +35 -35
  262. package/dist/extensions/openrouter/index.d.ts +1 -1
  263. package/dist/extensions/openrouter/index.js +35 -35
  264. package/dist/extensions/openshell/index.d.ts +1 -1
  265. package/dist/extensions/openshell/index.js +35 -35
  266. package/dist/extensions/perplexity/index.d.ts +1 -1
  267. package/dist/extensions/perplexity/index.js +5 -5
  268. package/dist/extensions/phone-control/index.d.ts +1 -1
  269. package/dist/extensions/phone-control/index.js +4 -4
  270. package/dist/extensions/qianfan/index.d.ts +1 -1
  271. package/dist/extensions/qianfan/index.js +35 -35
  272. package/dist/extensions/qwen-portal-auth/index.d.ts +1 -1
  273. package/dist/extensions/qwen-portal-auth/index.js +35 -35
  274. package/dist/extensions/sglang/index.d.ts +1 -1
  275. package/dist/extensions/sglang/index.js +35 -35
  276. package/dist/extensions/signal/index.d.ts +1 -1
  277. package/dist/extensions/signal/index.js +38 -38
  278. package/dist/extensions/signal/setup-entry.d.ts +1 -1
  279. package/dist/extensions/signal/setup-entry.js +38 -38
  280. package/dist/extensions/slack/index.d.ts +1 -1
  281. package/dist/extensions/slack/index.js +39 -39
  282. package/dist/extensions/slack/setup-entry.d.ts +1 -1
  283. package/dist/extensions/slack/setup-entry.js +38 -38
  284. package/dist/extensions/synology-chat/index.d.ts +1 -1
  285. package/dist/extensions/synology-chat/index.js +37 -37
  286. package/dist/extensions/synology-chat/setup-entry.d.ts +1 -1
  287. package/dist/extensions/synology-chat/setup-entry.js +37 -37
  288. package/dist/extensions/synthetic/index.d.ts +1 -1
  289. package/dist/extensions/synthetic/index.js +35 -35
  290. package/dist/extensions/talk-voice/index.d.ts +1 -1
  291. package/dist/extensions/talk-voice/index.js +35 -35
  292. package/dist/extensions/telegram/index.d.ts +1 -1
  293. package/dist/extensions/telegram/index.js +38 -38
  294. package/dist/extensions/telegram/setup-entry.d.ts +1 -1
  295. package/dist/extensions/telegram/setup-entry.js +37 -37
  296. package/dist/extensions/thread-ownership/index.d.ts +1 -1
  297. package/dist/extensions/thread-ownership/index.js +4 -4
  298. package/dist/extensions/tlon/index.d.ts +1 -1
  299. package/dist/extensions/tlon/index.js +37 -37
  300. package/dist/extensions/tlon/setup-entry.d.ts +1 -1
  301. package/dist/extensions/tlon/setup-entry.js +37 -37
  302. package/dist/extensions/together/index.d.ts +1 -1
  303. package/dist/extensions/together/index.js +35 -35
  304. package/dist/extensions/twitch/index.d.ts +2 -2
  305. package/dist/extensions/twitch/index.js +37 -37
  306. package/dist/extensions/venice/index.d.ts +1 -1
  307. package/dist/extensions/venice/index.js +35 -35
  308. package/dist/extensions/vercel-ai-gateway/index.d.ts +1 -1
  309. package/dist/extensions/vercel-ai-gateway/index.js +36 -36
  310. package/dist/extensions/vllm/index.d.ts +1 -1
  311. package/dist/extensions/vllm/index.js +35 -35
  312. package/dist/extensions/voice-call/index.d.ts +1 -1
  313. package/dist/extensions/voice-call/index.js +35 -35
  314. package/dist/extensions/volcengine/index.d.ts +1 -1
  315. package/dist/extensions/volcengine/index.js +35 -35
  316. package/dist/extensions/whatsapp/index.d.ts +1 -1
  317. package/dist/extensions/whatsapp/index.js +38 -38
  318. package/dist/extensions/whatsapp/setup-entry.d.ts +1 -1
  319. package/dist/extensions/whatsapp/setup-entry.js +38 -38
  320. package/dist/extensions/xai/index.d.ts +1 -1
  321. package/dist/extensions/xai/index.js +35 -35
  322. package/dist/extensions/xiaomi/index.d.ts +1 -1
  323. package/dist/extensions/xiaomi/index.js +35 -35
  324. package/dist/extensions/zai/index.d.ts +1 -1
  325. package/dist/extensions/zai/index.js +35 -35
  326. package/dist/extensions/zalo/index.d.ts +1 -1
  327. package/dist/extensions/zalo/index.js +39 -39
  328. package/dist/extensions/zalo/setup-entry.d.ts +1 -1
  329. package/dist/extensions/zalo/setup-entry.js +39 -39
  330. package/dist/extensions/zalouser/index.d.ts +1 -1
  331. package/dist/extensions/zalouser/index.js +40 -40
  332. package/dist/extensions/zalouser/setup-entry.d.ts +1 -1
  333. package/dist/extensions/zalouser/setup-entry.js +40 -40
  334. package/dist/feishu-fIcnHDTd.d.ts +36 -0
  335. package/dist/gateway-cli-DofujQf3.js +26437 -0
  336. package/dist/gateway-install-token-vvtOvufH.js +163 -0
  337. package/dist/gateway-rpc-C0Vk51W7.js +26 -0
  338. package/dist/gateway-runtime-CBm3CCoA.js +69 -0
  339. package/dist/git-commit-BTWXFY41.js +177 -0
  340. package/dist/git-commit-D6GTN5Yt.js +2 -0
  341. package/dist/googlechat-BvwsCVKl.d.ts +12 -0
  342. package/dist/googlechat-DzoFLiDG.js +307 -0
  343. package/dist/group-access-DpiQnd-G.d.ts +61 -0
  344. package/dist/health-CqUU_ecm.js +570 -0
  345. package/dist/health-Dzn8BT5I.js +113 -0
  346. package/dist/heartbeat-summary-Dct2lqJj.js +57 -0
  347. package/dist/help-CtwSApfq.js +81 -0
  348. package/dist/hooks-9gokOxZ5.d.ts +6 -0
  349. package/dist/hooks-cli-DtBvd1lb.js +1000 -0
  350. package/dist/hooks-status-Bm_pGORf.js +78 -0
  351. package/dist/http-registry-D-S6a1Na.d.ts +20 -0
  352. package/dist/identity-file-Diub2a0t.js +60 -0
  353. package/dist/image-generation-CbIVzmAR.d.ts +9 -0
  354. package/dist/imessage-C7Z59nbV.js +31 -0
  355. package/dist/imessage-scXvYYHX.js +115 -0
  356. package/dist/inbound-reply-dispatch-gzPJFtkc.js +71 -0
  357. package/dist/inbound-reply-dispatch-n7U3qg15.d.ts +72 -0
  358. package/dist/index.js +2 -2
  359. package/dist/install-target-B4n74f_B.js +574 -0
  360. package/dist/installs-e1WEcS2x.js +532 -0
  361. package/dist/io-BaBxjB1v.js +9739 -0
  362. package/dist/io-CgHb1Jld.js +29 -0
  363. package/dist/irc-DxJ7uOKl.js +672 -0
  364. package/dist/library-DjjiKiBC.js +112 -0
  365. package/dist/lifecycle-core-Dn8PK6nk.js +382 -0
  366. package/dist/line/accounts.d.ts +2 -2
  367. package/dist/line/send.d.ts +1 -1
  368. package/dist/line/send.js +7 -7
  369. package/dist/line/template-messages.d.ts +1 -1
  370. package/dist/line-B5QFpgN_.d.ts +75 -0
  371. package/dist/line-BBiBiGgL.js +530 -0
  372. package/dist/llm-slug-generator-BQ09Fz6D.js +67 -0
  373. package/dist/llm-slug-generator.d.ts +1 -1
  374. package/dist/llm-slug-generator.js +36 -36
  375. package/dist/logging-CdisccbY.js +13 -0
  376. package/dist/logging-LKQSgX1d.js +30 -0
  377. package/dist/login-qr-B2ElPfmb.js +112 -0
  378. package/dist/login-qr-Di3uPIK1.js +233 -0
  379. package/dist/logs-cli-CNzOvZ2d.js +256 -0
  380. package/dist/manager-runtime-2RwW64dE.js +111 -0
  381. package/dist/manager.runtime-BacHmSFL.js +715 -0
  382. package/dist/manifest-registry-CS_p1OBQ.js +1329 -0
  383. package/dist/matrix-43_RGLZN.d.ts +68 -0
  384. package/dist/matrix-6-xpfQHf.js +1269 -0
  385. package/dist/matrix-DKYdZvGK.js +1495 -0
  386. package/dist/mcp-cli-Ci2jvv3s.js +87 -0
  387. package/dist/media-understanding.runtime-0668UZMb.js +116 -0
  388. package/dist/memory-cli-BGfRJOPh.js +111 -0
  389. package/dist/memory-search-BHhETk6u.js +17 -0
  390. package/dist/memory-search-tTD5o_rU.js +204 -0
  391. package/dist/method-scopes-B2ZKSsxQ.js +2452 -0
  392. package/dist/model-auth-markers-LqZ4qhrZ.d.ts +20 -0
  393. package/dist/model-picker-D9Gmg9vM.js +390 -0
  394. package/dist/model-picker-VOam6Dfa.js +112 -0
  395. package/dist/model-picker.runtime-B8h4yaGk.js +125 -0
  396. package/dist/model-selection-bBBxfXdb.js +653 -0
  397. package/dist/model-suppression.runtime-IpdzuuaW.js +116 -0
  398. package/dist/models-CEr8OkSA.js +118 -0
  399. package/dist/models-FgjlnyvT.js +2514 -0
  400. package/dist/models-cli-CdXULEZe.js +309 -0
  401. package/dist/models-config-UQxq76ac.js +111 -0
  402. package/dist/models-config.providers.discovery-puxTsH39.d.ts +18 -0
  403. package/dist/moldclaw-root-Cb6HRlUO.js +92 -0
  404. package/dist/monitor-BGAB5pZp.js +772 -0
  405. package/dist/monitor-C_1qA4q5.js +6823 -0
  406. package/dist/monitor-D8WcCUuA.js +782 -0
  407. package/dist/monitor-DesGoihT.js +113 -0
  408. package/dist/monitor-DhrUY3U6.js +3468 -0
  409. package/dist/monitor-DsDCIG87.js +3076 -0
  410. package/dist/monitor-dBB-luY7.js +115 -0
  411. package/dist/monitor-shared-CkicpwVc.js +444 -0
  412. package/dist/msteams-qmY7xMCK.js +852 -0
  413. package/dist/node-cli-B_2nTm80.js +2503 -0
  414. package/dist/node-resolve-BYC2FbO2.js +835 -0
  415. package/dist/nodes-cli-CBX71Wd_.js +1380 -0
  416. package/dist/nostr-BFKRoOlz.d.ts +7 -0
  417. package/dist/nostr-OB78XVlq.js +8744 -0
  418. package/dist/npm-resolution-DZJB3Hgh.js +60 -0
  419. package/dist/oauth-env-CLG8KOrz.js +10 -0
  420. package/dist/onboard-BON0C360.js +48 -0
  421. package/dist/onboard-DsKI17iq.js +25 -0
  422. package/dist/onboard-DyBwXVGT.js +589 -0
  423. package/dist/onboard-channels-BaVA02kK.js +1241 -0
  424. package/dist/onboard-channels-JJVMXpKl.js +205 -0
  425. package/dist/onboard-custom-D6YDrk9n.js +571 -0
  426. package/dist/onboard-custom-DqsNalL1.js +114 -0
  427. package/dist/onboard-helpers-DiSRTpZC.js +335 -0
  428. package/dist/onboard-helpers-DqZsjdb8.js +113 -0
  429. package/dist/onboard-hooks-pzEPZAvl.js +72 -0
  430. package/dist/onboard-remote-DFMLELsg.js +181 -0
  431. package/dist/onboard-remote-DmMYxY80.js +117 -0
  432. package/dist/onboard-search-B7FsNd7m.js +302 -0
  433. package/dist/onboard-skills-BJRdI-K3.js +133 -0
  434. package/dist/onboard-skills-Cg1YQAZa.js +117 -0
  435. package/dist/outbound-media-BHD4aJEX.d.ts +11 -0
  436. package/dist/outbound-media-CrraEXXv.js +11 -0
  437. package/dist/pairing-access-CzHpaM0R.d.ts +21 -0
  438. package/dist/pairing-cli-DSQTYgc0.js +217 -0
  439. package/dist/perplexity-CXwMDD3u.js +24 -0
  440. package/dist/persistent-dedupe-B9vrAf8t.d.ts +26 -0
  441. package/dist/pi-model-discovery-runtime-vSSbBDFG.js +111 -0
  442. package/dist/pi-tools.before-tool-call.runtime-C5yLUogH.js +381 -0
  443. package/dist/plugin-install-B1RfdrdV.js +117 -0
  444. package/dist/plugin-install-C9aaL42Y.js +184 -0
  445. package/dist/plugin-install-plan-BlfDt117.js +49 -0
  446. package/dist/plugin-registry-D2P9SYJ9.js +113 -0
  447. package/dist/plugin-registry-QtjVXmj3.js +49 -0
  448. package/dist/plugin-sdk/account-resolution.js +35 -35
  449. package/dist/plugin-sdk/acp-runtime.js +35 -35
  450. package/dist/plugin-sdk/agent-runtime.js +35 -35
  451. package/dist/plugin-sdk/bluebubbles.js +37 -37
  452. package/dist/plugin-sdk/channel-config-helpers.js +35 -35
  453. package/dist/plugin-sdk/channel-policy.js +35 -35
  454. package/dist/plugin-sdk/channel-runtime.js +35 -35
  455. package/dist/plugin-sdk/compat.js +36 -36
  456. package/dist/plugin-sdk/config-runtime.js +35 -35
  457. package/dist/plugin-sdk/conversation-runtime.js +35 -35
  458. package/dist/plugin-sdk/copilot-proxy.js +4 -4
  459. package/dist/plugin-sdk/core.js +4 -4
  460. package/dist/plugin-sdk/device-pair.js +4 -4
  461. package/dist/plugin-sdk/discord.js +35 -35
  462. package/dist/plugin-sdk/feishu.js +35 -35
  463. package/dist/plugin-sdk/gateway-runtime.js +10 -10
  464. package/dist/plugin-sdk/googlechat.js +37 -37
  465. package/dist/plugin-sdk/image-generation-runtime.js +35 -35
  466. package/dist/plugin-sdk/image-generation.js +35 -35
  467. package/dist/plugin-sdk/imessage.js +36 -36
  468. package/dist/plugin-sdk/index.js +35 -35
  469. package/dist/plugin-sdk/infra-runtime.js +35 -35
  470. package/dist/plugin-sdk/irc.js +37 -37
  471. package/dist/plugin-sdk/line.js +36 -36
  472. package/dist/plugin-sdk/llm-task.js +35 -35
  473. package/dist/plugin-sdk/lobster.js +4 -4
  474. package/dist/plugin-sdk/matrix.js +37 -37
  475. package/dist/plugin-sdk/mattermost.js +36 -36
  476. package/dist/plugin-sdk/media-runtime.js +35 -35
  477. package/dist/plugin-sdk/media-understanding-runtime.js +35 -35
  478. package/dist/plugin-sdk/media-understanding.js +35 -35
  479. package/dist/plugin-sdk/memory-lancedb.js +4 -4
  480. package/dist/plugin-sdk/minimax-portal-auth.js +4 -4
  481. package/dist/plugin-sdk/msteams.js +38 -38
  482. package/dist/plugin-sdk/nextcloud-talk.js +36 -36
  483. package/dist/plugin-sdk/nostr.js +36 -36
  484. package/dist/plugin-sdk/ollama-setup.js +9 -9
  485. package/dist/plugin-sdk/open-prose.js +4 -4
  486. package/dist/plugin-sdk/phone-control.js +4 -4
  487. package/dist/plugin-sdk/plugin-runtime.js +35 -35
  488. package/dist/plugin-sdk/provider-auth.js +35 -35
  489. package/dist/plugin-sdk/provider-models.js +5 -5
  490. package/dist/plugin-sdk/provider-onboard.js +4 -4
  491. package/dist/plugin-sdk/provider-setup.js +39 -39
  492. package/dist/plugin-sdk/provider-stream.js +4 -4
  493. package/dist/plugin-sdk/provider-usage.js +4 -4
  494. package/dist/plugin-sdk/qwen-portal-auth.js +35 -35
  495. package/dist/plugin-sdk/reply-history.js +35 -35
  496. package/dist/plugin-sdk/reply-runtime.js +35 -35
  497. package/dist/plugin-sdk/routing.js +3 -3
  498. package/dist/plugin-sdk/sandbox.js +35 -35
  499. package/dist/plugin-sdk/security-runtime.js +35 -35
  500. package/dist/plugin-sdk/self-hosted-provider-setup.js +37 -37
  501. package/dist/plugin-sdk/setup.js +35 -35
  502. package/dist/plugin-sdk/signal.js +35 -35
  503. package/dist/plugin-sdk/slack.js +35 -35
  504. package/dist/plugin-sdk/speech-runtime.js +35 -35
  505. package/dist/plugin-sdk/speech.js +35 -35
  506. package/dist/plugin-sdk/src/secrets/secure-file-store.d.ts +26 -0
  507. package/dist/plugin-sdk/src/subscription/provider.d.ts +5 -3
  508. package/dist/plugin-sdk/synology-chat.js +36 -36
  509. package/dist/plugin-sdk/talk-voice.js +4 -4
  510. package/dist/plugin-sdk/telegram.js +35 -35
  511. package/dist/plugin-sdk/text-runtime.js +7 -7
  512. package/dist/plugin-sdk/thread-ownership.js +4 -4
  513. package/dist/plugin-sdk/tlon.js +36 -36
  514. package/dist/plugin-sdk/twitch.js +35 -35
  515. package/dist/plugin-sdk/voice-call.js +35 -35
  516. package/dist/plugin-sdk/whatsapp.js +35 -35
  517. package/dist/plugin-sdk/zalo.js +38 -38
  518. package/dist/plugin-sdk/zalouser.js +38 -38
  519. package/dist/plugins/runtime/index.d.ts +1 -1
  520. package/dist/plugins/runtime/index.js +35 -35
  521. package/dist/plugins-CAULK-lt.js +111 -0
  522. package/dist/plugins-cli-BGAgYy3Z.js +917 -0
  523. package/dist/policy-WKZJ8cPK.js +143 -0
  524. package/dist/preflight-audio.runtime-Bc6Hv-u4.js +116 -0
  525. package/dist/probe-B7a_VBCB.js +47 -0
  526. package/dist/probe-BLioBOhT.js +6329 -0
  527. package/dist/probe-BkM5pykD.js +21 -0
  528. package/dist/probe-DOEaWez2.js +1793 -0
  529. package/dist/probe-DpcJ0WeP.js +129 -0
  530. package/dist/probe-auth-BcNjX8hy.js +40 -0
  531. package/dist/probe-auth-DhuAb8ls.js +48 -0
  532. package/dist/program-zG0CuBsn.js +253 -0
  533. package/dist/prompt-select-styled-B-D6NQFO.js +2673 -0
  534. package/dist/provider-api-key-auth.runtime-DgGM_pHf.js +121 -0
  535. package/dist/provider-auth-choice-BOQA0idb.js +126 -0
  536. package/dist/provider-auth-choice-helpers-hzDkh3f1.js +48 -0
  537. package/dist/provider-auth-choice-preference-BPcuoO9b.js +189 -0
  538. package/dist/provider-auth-choice.runtime-B4E0A4J8.js +123 -0
  539. package/dist/provider-auth-choices-0KaDNPBQ.js +57 -0
  540. package/dist/provider-auth-guidance-BxxMxMEt.js +34 -0
  541. package/dist/provider-auth-result-Bto1bYtS.d.ts +18 -0
  542. package/dist/provider-models-DxOmeToO.d.ts +867 -0
  543. package/dist/provider-models-xnyxy6mO.js +2113 -0
  544. package/dist/provider-ollama-setup-DBYK__ov.d.ts +32 -0
  545. package/dist/provider-ollama-setup-QzgCxj44.js +314 -0
  546. package/dist/provider-onboard-B9ionepI.js +139 -0
  547. package/dist/provider-onboard-CURxJ_UX.d.ts +40 -0
  548. package/dist/provider-runtime.runtime-DFAIaPZT.js +111 -0
  549. package/dist/provider-self-hosted-setup-CclMg3QB.js +182 -0
  550. package/dist/provider-self-hosted-setup-qeY8BYSy.d.ts +61 -0
  551. package/dist/provider-stream-Chz_EFw3.js +512 -0
  552. package/dist/provider-usage-Cn_OrdMP.js +111 -0
  553. package/dist/provider-usage-kxemdMp2.js +633 -0
  554. package/dist/provider-wizard-BMc1Dzks.js +152 -0
  555. package/dist/push-apns-CuIs_G0k.js +1038 -0
  556. package/dist/pw-ai-DUe4BbH2.js +1867 -0
  557. package/dist/qmd-manager-CAAFp7qK.js +1570 -0
  558. package/dist/qr-cli-9H9go9JE.js +113 -0
  559. package/dist/qr-cli-Bxxttvx3.js +369 -0
  560. package/dist/reactions-Dx2ypGlm.js +281 -0
  561. package/dist/read-only-account-inspect.discord.runtime-D_RVd1-r.js +116 -0
  562. package/dist/read-only-account-inspect.slack.runtime-Ckl92Rhy.js +116 -0
  563. package/dist/read-only-account-inspect.telegram.runtime-D97ssjQk.js +116 -0
  564. package/dist/redact-snapshot-ojhTflxp.js +2663 -0
  565. package/dist/register.agent-DlBmy_RL.js +439 -0
  566. package/dist/register.backup-8nOYtJqg.js +625 -0
  567. package/dist/register.configure-BV_GUQ5w.js +252 -0
  568. package/dist/register.maintenance-BafAHBum.js +574 -0
  569. package/dist/register.message-CHhfcEq2.js +709 -0
  570. package/dist/register.onboard-DjPNnlnr.js +192 -0
  571. package/dist/register.setup-BMGXI9PG.js +212 -0
  572. package/dist/register.status-health-sessions-BJMsh2AL.js +498 -0
  573. package/dist/register.subclis-B7nrik3-.js +315 -0
  574. package/dist/register.subclis-BCoAVyGn.js +13 -0
  575. package/dist/replies-BU4AvOyD.js +110 -0
  576. package/dist/resolve-channels-CDfj5NGq.js +226 -0
  577. package/dist/resolve-channels-Cz120Lvf.js +262 -0
  578. package/dist/resolve-route-DdX-HBVt.js +538 -0
  579. package/dist/resolve-users-Bc6nRqpb.js +143 -0
  580. package/dist/root-help-QAkoA7GD.js +32 -0
  581. package/dist/routes-CcJNnwTF.js +7097 -0
  582. package/dist/rpc-DDUAlBbH.js +67 -0
  583. package/dist/run-main-Cf8vXm6i.js +424 -0
  584. package/dist/runtime-Bitmi8Er.d.ts +26 -0
  585. package/dist/runtime-discord-ops.runtime-DWBnG-Hf.js +9078 -0
  586. package/dist/runtime-slack-ops.runtime-oYfbYvX4.js +4556 -0
  587. package/dist/runtime-telegram-ops.runtime-EPGDYN1f.js +133 -0
  588. package/dist/runtime-whatsapp-login.runtime-BGJoKM2h.js +114 -0
  589. package/dist/runtime-whatsapp-outbound.runtime-D3Sag6Bv.js +117 -0
  590. package/dist/sandbox-cli-waaYyhsC.js +535 -0
  591. package/dist/search-manager-BjDsOXzB.js +16 -0
  592. package/dist/search-manager-D-G9UaXe.js +386 -0
  593. package/dist/secrets-cli-BmeEhlzt.js +2070 -0
  594. package/dist/security-cli-CSsLnFuN.js +575 -0
  595. package/dist/send-6o7piqfv.js +100 -0
  596. package/dist/send-99FyFLgk.js +629 -0
  597. package/dist/send-B1pX9_Oc.js +283 -0
  598. package/dist/send-Dg5-AjXW.js +1025 -0
  599. package/dist/send-LdV9TRoN.js +631 -0
  600. package/dist/server-node-events-BAUUKNFW.js +506 -0
  601. package/dist/server-zI_K-D05.js +107 -0
  602. package/dist/sessions-DJGywtY2.js +112 -0
  603. package/dist/sessions-WR0GKdGl.js +218 -0
  604. package/dist/setup-2LvBON2J.js +387 -0
  605. package/dist/setup-CFIMq-Pz.d.ts +37 -0
  606. package/dist/setup-binary-CcAv8NXz.js +406 -0
  607. package/dist/setup-browser-C4eRV3h6.js +70 -0
  608. package/dist/setup-core-ByqF8Nvc.js +166 -0
  609. package/dist/setup-core-COkq8Zop.js +205 -0
  610. package/dist/setup-core-C_o77DVV.js +143 -0
  611. package/dist/setup-core-tAdHbqi9.js +47 -0
  612. package/dist/setup-surface-DMHtZByF.js +490 -0
  613. package/dist/setup-wizard-helpers-r0J6l8ST.d.ts +203 -0
  614. package/dist/setup.finalize-pTkXn8pV.js +522 -0
  615. package/dist/setup.gateway-config-D1wiUQe6.js +343 -0
  616. package/dist/shared-BaM_tMxk.js +102 -0
  617. package/dist/shared-BgUgXsM9.js +75 -0
  618. package/dist/shared-CezRuVfl.js +96 -0
  619. package/dist/shared-DQgf3R0B.js +298 -0
  620. package/dist/shared-RA6kxQfu.js +182 -0
  621. package/dist/signal-xxP4dGx7.js +114 -0
  622. package/dist/skills-Bio8GwTE.js +20 -0
  623. package/dist/skills-DE_MXFSN.js +853 -0
  624. package/dist/skills-cli-BGuW-tKw.js +292 -0
  625. package/dist/skills-install-BIlG41ei.js +763 -0
  626. package/dist/skills-status-B08PtBc_.js +21 -0
  627. package/dist/skills-status-CzM008aB.js +169 -0
  628. package/dist/slack-Bzt3M7d8.js +114 -0
  629. package/dist/slash-commands.runtime-EYqj8SbV.js +128 -0
  630. package/dist/slash-dispatch.runtime-vWH92PUY.js +141 -0
  631. package/dist/slash-skill-commands.runtime-sUTjM3J0.js +116 -0
  632. package/dist/src-Iso9-DPo.js +1701 -0
  633. package/dist/status-8S882KRO.js +43 -0
  634. package/dist/status-BF7h_jTB.js +126 -0
  635. package/dist/status-DxOIyRm9.js +131 -0
  636. package/dist/status-HlvixAOq.js +606 -0
  637. package/dist/status-json-CShBo8A6.js +288 -0
  638. package/dist/status-qhtgsJFd.js +1599 -0
  639. package/dist/status.link-channel-D3dLYZiH.js +143 -0
  640. package/dist/status.scan.deps.runtime-mCmOLzpG.js +126 -0
  641. package/dist/status.scan.runtime-CYuUlDeg.js +119 -0
  642. package/dist/status.summary-BjB1aTjV.js +592 -0
  643. package/dist/status.summary.runtime-DW4MOt1Z.js +118 -0
  644. package/dist/status.update-BxblMS7P.js +77 -0
  645. package/dist/subagent-orphan-recovery-CCVopsdP.js +307 -0
  646. package/dist/subagent-registry-runtime-oG47Fzvg.js +111 -0
  647. package/dist/subscription-CpFdxuFS.js +33 -0
  648. package/dist/subscription-DaA1urx-.js +102 -0
  649. package/dist/subscription-cli-Bvto9EmO.js +134 -0
  650. package/dist/synology-chat-CVy1tJGY.js +297 -0
  651. package/dist/system-cli-BvNps8sl.js +94 -0
  652. package/dist/telegram/audit.d.ts +1 -1
  653. package/dist/telegram/audit.js +1 -1
  654. package/dist/telegram/token.d.ts +1 -1
  655. package/dist/telegram/token.js +35 -35
  656. package/dist/telegram-Dug8pd82.js +114 -0
  657. package/dist/text-chunking-CQ6ttpWs.js +84 -0
  658. package/dist/text-chunking-DDUU_vAF.d.ts +79 -0
  659. package/dist/tlon-BnckVr_7.js +433 -0
  660. package/dist/tui-DXTge9Ac.js +3834 -0
  661. package/dist/tui-cli-BRUfLUTd.js +137 -0
  662. package/dist/types-2H_e7eWT.d.ts +45 -0
  663. package/dist/types-ZKnGUchG.d.ts +22692 -0
  664. package/dist/types.base-BFiQZ4J9.d.ts +188 -0
  665. package/dist/ui--iP08xRA.js +31 -0
  666. package/dist/update-CtxBD-Cf.js +1036 -0
  667. package/dist/update-cli-DPE3GCHJ.js +1503 -0
  668. package/dist/update-offset-store-BlBY7dWj.js +112 -0
  669. package/dist/update-runner-Cfcrhf2j.js +1496 -0
  670. package/dist/upsert-with-lock-BZU7Le8n.js +33 -0
  671. package/dist/usage-Czgwvg0h.js +115 -0
  672. package/dist/web-BGDXhunB.js +112 -0
  673. package/dist/web-shared-B5Q0mIJq.d.ts +45 -0
  674. package/dist/webhook-request-guards-CsKDhZJr.d.ts +76 -0
  675. package/dist/webhook-targets-CjxuEE9C.d.ts +106 -0
  676. package/dist/webhook-targets-Dtt6rH4N.js +181 -0
  677. package/dist/webhooks-cli-Wl9y6AWW.js +350 -0
  678. package/dist/whatsapp-D64cBQ_9.js +114 -0
  679. package/dist/whatsapp-actions-CVoTcagb.js +167 -0
  680. package/dist/workspace-DJ_S272u.js +484 -0
  681. package/dist/workspace-DbZSqjw0.js +289 -0
  682. package/dist/workspace-cli-D93DLmAh.js +154 -0
  683. package/dist/workspace-dirs-CGeIPpGN.js +2003 -0
  684. package/dist/zalo-CK2dlGmu.d.ts +9 -0
  685. package/dist/zalo-DxoY8M22.js +415 -0
  686. package/dist/zalouser-DTHIrPvs.js +30911 -0
  687. package/extensions/discord/src/monitor/allow-list.ts +8 -1
  688. package/extensions/discord/src/monitor/message-handler.preflight.ts +4 -1
  689. package/package.json +1 -1
  690. package/dist/accounts-CS8U4v8C.js +0 -114
  691. package/dist/accounts-gLr-Udmt.d.ts +0 -103
  692. package/dist/acp-cli-BGT0jXcC.js +0 -2093
  693. package/dist/actions.runtime-BfckTw6c.js +0 -119
  694. package/dist/actions.runtime-Cl9mBfqH.js +0 -133
  695. package/dist/agent-scope-C-YmLnnb.js +0 -208
  696. package/dist/agents-CydD54p8.js +0 -222
  697. package/dist/agents-DpQsZO6O.js +0 -853
  698. package/dist/agents.config-XU7IsYE-.js +0 -121
  699. package/dist/agents.config-ssoQXuvF.js +0 -17
  700. package/dist/allow-list-Cfn6lmMK.js +0 -81
  701. package/dist/allowlist-CCYXVpM9.js +0 -142
  702. package/dist/api-BoXoFKxy.js +0 -117
  703. package/dist/audit-Bv05N5o9.js +0 -787
  704. package/dist/audit-CIWW1Aqm.js +0 -54
  705. package/dist/audit-channel.collect.runtime-Bi7yrdcO.js +0 -605
  706. package/dist/audit-channel.runtime-C_NDweiW.js +0 -121
  707. package/dist/audit-extra.async-Dp7OKSXg.js +0 -813
  708. package/dist/audit-membership-runtime-B8FQ6VtN.js +0 -162
  709. package/dist/audit.deep.runtime-CXhobL6b.js +0 -25
  710. package/dist/audit.nondeep.runtime-CrEm3T16.js +0 -832
  711. package/dist/audit.runtime-CJPKj1Zg.js +0 -118
  712. package/dist/auth-Byfp0flq.js +0 -101
  713. package/dist/auth-choice-BgOjdeXN.js +0 -507
  714. package/dist/auth-choice-CD1Heq0M.js +0 -122
  715. package/dist/auth-choice-ePNfg0iQ.js +0 -268
  716. package/dist/auth-choice-options-BlewQWI0.js +0 -123
  717. package/dist/auth-choice-prompt-BP2b6aXz.js +0 -36
  718. package/dist/auth-choice-prompt-Cmwl4n97.js +0 -115
  719. package/dist/auth-choice.apply-helpers-Dq-nxuuX.js +0 -66
  720. package/dist/auth-choice.plugin-providers.runtime-B23kOUzQ.js +0 -119
  721. package/dist/auth-profiles-1kPLbBwI.js +0 -127823
  722. package/dist/auth-profiles.runtime-DAfSjku1.js +0 -116
  723. package/dist/banner-DeOsobLO.js +0 -342
  724. package/dist/bluebubbles-BsLGedBM.js +0 -64
  725. package/dist/bluebubbles-U2sAfO4_.d.ts +0 -6
  726. package/dist/bot-DW12K3bO.d.ts +0 -478
  727. package/dist/brave-BoWimrLe.js +0 -24
  728. package/dist/browser-cli-D_S3wEYE.js +0 -1494
  729. package/dist/call-ByEzDJ1_.js +0 -640
  730. package/dist/call-CHCWVg-O.js +0 -39
  731. package/dist/channel-3VC0oOMu.js +0 -214
  732. package/dist/channel-B9fCBPiS.js +0 -207
  733. package/dist/channel-B9q775cM.js +0 -562
  734. package/dist/channel-BG3UK54j.js +0 -803
  735. package/dist/channel-BRQAdMML.js +0 -352
  736. package/dist/channel-BmlLp933.js +0 -1321
  737. package/dist/channel-By6KvdTG.js +0 -920
  738. package/dist/channel-C8rRsdf6.js +0 -226
  739. package/dist/channel-CLEDBbXE.js +0 -943
  740. package/dist/channel-CMvBAG7o.js +0 -306
  741. package/dist/channel-CmlxxjHY.js +0 -1598
  742. package/dist/channel-CqG6_xN0.js +0 -949
  743. package/dist/channel-DNueHKs92.js +0 -316
  744. package/dist/channel-DUtyN7BX.js +0 -4681
  745. package/dist/channel-DWD6GrfZ.js +0 -538
  746. package/dist/channel-DaRYMYzj.js +0 -619
  747. package/dist/channel-Dj6BgLp8.js +0 -575
  748. package/dist/channel-account-context-Ba3u5D21.js +0 -103
  749. package/dist/channel-crabk6Em.js +0 -542
  750. package/dist/channel-i8uqQaK2.js +0 -497
  751. package/dist/channel-options-xljvwHS2.js +0 -50
  752. package/dist/channel-plugin-ids-DAgknSG4.js +0 -26
  753. package/dist/channel-summary-dHTMCG75.js +0 -111
  754. package/dist/channel-xVWQ96Ni.js +0 -397
  755. package/dist/channel.runtime-B6PoZ4BV.js +0 -182
  756. package/dist/channel.runtime-BPZmo57e.js +0 -404
  757. package/dist/channel.runtime-B_1uGR-U.js +0 -199
  758. package/dist/channel.runtime-BiXnPU0d.js +0 -218
  759. package/dist/channel.runtime-BpvDc9sv.js +0 -870
  760. package/dist/channel.runtime-CUua3W80.js +0 -418
  761. package/dist/channel.runtime-CaCBTd0A.js +0 -179
  762. package/dist/channel.runtime-D0FfYvUj.js +0 -4011
  763. package/dist/channel.runtime-DhoJtpvJ.js +0 -241
  764. package/dist/channel.runtime-Kj9EXNE0.js +0 -127
  765. package/dist/channel.runtime-r4tPuPyh.js +0 -171
  766. package/dist/channel.setup-B7d_grfe.js +0 -6
  767. package/dist/channel.setup-C0vu1fhi.js +0 -9
  768. package/dist/channel.setup-CAI0FNHj.js +0 -11
  769. package/dist/channel.setup-CkDVwv5R.js +0 -57
  770. package/dist/channel.setup-Cpd00YqQ.js +0 -8
  771. package/dist/channel.setup-DbBz1-WT.js +0 -9
  772. package/dist/channel.setup-GZnAvD9g.js +0 -8
  773. package/dist/channels-5H484RSw.js +0 -1118
  774. package/dist/channels-BnPudfyx.js +0 -404
  775. package/dist/channels-cli-WIC-QeH_.js +0 -291
  776. package/dist/channels-status-issues-RDmzovJU.js +0 -16
  777. package/dist/clawbot-cli-BgutNwf8.js +0 -118
  778. package/dist/cleanup-utils-DBl1Aij1.js +0 -96
  779. package/dist/cli-1P7u6zqu.js +0 -154
  780. package/dist/command-registry-B8jovrws.js +0 -232
  781. package/dist/command-registry-DtDl1FVm.js +0 -14
  782. package/dist/command-secret-gateway-BgUo3FxJ.js +0 -111
  783. package/dist/compact.runtime-CXbXM0AU.js +0 -116
  784. package/dist/completion-cli-Cik_owAE.js +0 -17
  785. package/dist/completion-cli-RU3P2RSl.js +0 -445
  786. package/dist/config-5HUpB1L1.js +0 -31
  787. package/dist/config-cli-QHaUHoZI.js +0 -433
  788. package/dist/config-guard-C9Sn3pE-.js +0 -118
  789. package/dist/config-sW57gztj.js +0 -44
  790. package/dist/config-validation-5LkjIKNt.js +0 -262
  791. package/dist/config-value-CtTWALxG.js +0 -132
  792. package/dist/configure-BmR2TPLf.js +0 -243
  793. package/dist/configure-DaLN-5xM.js +0 -1100
  794. package/dist/control-ui-assets-CH3MYmAo.js +0 -232
  795. package/dist/control-ui-shared-CA77PTml.js +0 -29
  796. package/dist/core-CvDzLs7B.js +0 -150
  797. package/dist/core-dPA4nFkn.d.ts +0 -87
  798. package/dist/cron-cli-tguLpzyq.js +0 -639
  799. package/dist/daemon-cli-ptosOkL8.js +0 -339
  800. package/dist/daemon-install-DzU4EnVa.js +0 -180
  801. package/dist/deliver-DwxFoHM3.js +0 -111
  802. package/dist/deliver-runtime-DOdDyaPI.js +0 -111
  803. package/dist/device-id-cli-GopvlxxZ.js +0 -52
  804. package/dist/device-identity-CRfhC3_s.js +0 -365
  805. package/dist/devices-cli-ain7ESqU.js +0 -342
  806. package/dist/diagnostic-D96Xaqrj.js +0 -310
  807. package/dist/directory-cli-fh1UxGgY.js +0 -311
  808. package/dist/directory-config-helpers-CpU1oflo.d.ts +0 -38
  809. package/dist/directory.static-CKjJUNGl.js +0 -44
  810. package/dist/discord-CflhwDEM.js +0 -114
  811. package/dist/discovery-x0ZqY4AB.js +0 -48
  812. package/dist/dm-policy-shared-73A52W6E.d.ts +0 -95
  813. package/dist/dns-cli-DCHyKjGf.js +0 -217
  814. package/dist/docs-cli-D3OoqYSP.js +0 -174
  815. package/dist/doctor-completion-Bq2eP87s.js +0 -90
  816. package/dist/doctor-config-flow-D8XRG9Ku.js +0 -2437
  817. package/dist/doctor-config-flow-DGiF1HGc.js +0 -112
  818. package/dist/enable-0QSF4YGH.js +0 -24
  819. package/dist/exec-approvals-cli-Bncym0Gd.js +0 -421
  820. package/dist/feishu-B5JDcyF9.d.ts +0 -36
  821. package/dist/gateway-cli-DYscsmA-.js +0 -26437
  822. package/dist/gateway-install-token-CNv17ac9.js +0 -163
  823. package/dist/gateway-rpc-BGC1Rxvg.js +0 -26
  824. package/dist/gateway-runtime-D89mSQPB.js +0 -69
  825. package/dist/git-commit-CeLH5Ozm.js +0 -2
  826. package/dist/git-commit-DUKRiCP-.js +0 -177
  827. package/dist/googlechat-BgXeXjd1.js +0 -307
  828. package/dist/googlechat-De-T7C31.d.ts +0 -12
  829. package/dist/group-access-Deh1tVNr.d.ts +0 -61
  830. package/dist/health-BEjzWwaB.js +0 -570
  831. package/dist/health-FjqrWQL6.js +0 -113
  832. package/dist/heartbeat-summary-CfdSA9M1.js +0 -57
  833. package/dist/help-BZeVprq1.js +0 -81
  834. package/dist/hooks-B5pYs_d7.d.ts +0 -6
  835. package/dist/hooks-cli-B7uGJs2O.js +0 -1000
  836. package/dist/hooks-status-CfceaUSg.js +0 -78
  837. package/dist/http-registry-C-KXqwnj.d.ts +0 -20
  838. package/dist/identity-file-sshkKKIr.js +0 -60
  839. package/dist/image-generation-CafM5hZh.d.ts +0 -9
  840. package/dist/imessage-BcV3WGx_.js +0 -31
  841. package/dist/imessage-Dhje7Ty-.js +0 -115
  842. package/dist/inbound-reply-dispatch-C73_7SOl.js +0 -71
  843. package/dist/inbound-reply-dispatch-DmL0KWLe.d.ts +0 -72
  844. package/dist/install-target-D7NRhfzc.js +0 -574
  845. package/dist/installs-Bj6jblqc.js +0 -532
  846. package/dist/io-CMfWWPXQ.js +0 -9738
  847. package/dist/io-CV844hAM.js +0 -29
  848. package/dist/irc-DKi1fDYI.js +0 -672
  849. package/dist/library-rygTG3oA.js +0 -112
  850. package/dist/lifecycle-core-BPlvShWY.js +0 -382
  851. package/dist/line-CGsemKWJ.js +0 -530
  852. package/dist/line-CKU3ER-n.d.ts +0 -75
  853. package/dist/llm-slug-generator-DlhVyMqT.js +0 -67
  854. package/dist/logging-5wu9k6w4.js +0 -30
  855. package/dist/logging-CxP9suT8.js +0 -13
  856. package/dist/login-qr-BcDsiwHs.js +0 -233
  857. package/dist/login-qr-Y8pJ5yV4.js +0 -112
  858. package/dist/logs-cli-XI9oVXpH.js +0 -256
  859. package/dist/manager-runtime-DkIlXBhD.js +0 -111
  860. package/dist/manager.runtime-Q0q2rJCC.js +0 -715
  861. package/dist/manifest-registry-DAd0SRAP.js +0 -1329
  862. package/dist/matrix-BI0DBBrG.js +0 -1495
  863. package/dist/matrix-DiABGjJR.js +0 -1269
  864. package/dist/matrix-fC6NrFM5.d.ts +0 -68
  865. package/dist/mcp-cli-BOyn_DLL.js +0 -87
  866. package/dist/media-understanding.runtime-DjUa7Dka.js +0 -116
  867. package/dist/memory-cli-CJd_vl-Y.js +0 -111
  868. package/dist/memory-search-CEEItIFR.js +0 -17
  869. package/dist/memory-search-Cv1SBrn7.js +0 -204
  870. package/dist/method-scopes-CQE7-bZ-.js +0 -2452
  871. package/dist/model-auth-markers-B1bbs9Qd.d.ts +0 -20
  872. package/dist/model-picker-D6_89XHg.js +0 -112
  873. package/dist/model-picker-Svaw-APs.js +0 -390
  874. package/dist/model-picker.runtime-Chi9nV7A.js +0 -125
  875. package/dist/model-selection-hL8i1Jbs.js +0 -653
  876. package/dist/model-suppression.runtime-DjWJZ0X-.js +0 -116
  877. package/dist/models-7qj1dG_W.js +0 -118
  878. package/dist/models-BPOB_xJF.js +0 -2514
  879. package/dist/models-cli-DdlOVUjS.js +0 -309
  880. package/dist/models-config-CBqUS-jX.js +0 -111
  881. package/dist/models-config.providers.discovery-BKB5JH9M.d.ts +0 -18
  882. package/dist/moldclaw-root-D6PbhbZk.js +0 -88
  883. package/dist/monitor-BPYhkEqF.js +0 -782
  884. package/dist/monitor-BuTcQ24j.js +0 -3468
  885. package/dist/monitor-CuXvNhFh.js +0 -113
  886. package/dist/monitor-D-TqSIHF.js +0 -6823
  887. package/dist/monitor-DRSgo9u2.js +0 -3076
  888. package/dist/monitor-DcHch39z.js +0 -772
  889. package/dist/monitor-DsHBMrXp.js +0 -115
  890. package/dist/monitor-shared-CL8T4gt1.js +0 -444
  891. package/dist/msteams-7FMwTvQG.js +0 -852
  892. package/dist/node-cli-BCjaSCZM.js +0 -2503
  893. package/dist/node-resolve-D5Hvcgyx.js +0 -835
  894. package/dist/nodes-cli-Dd_SNbkt.js +0 -1380
  895. package/dist/nostr-D8scBiYq.d.ts +0 -7
  896. package/dist/nostr-DBTFTxKs.js +0 -8744
  897. package/dist/npm-resolution-CYfb3MHG.js +0 -60
  898. package/dist/oauth-env-zPt5RywA.js +0 -10
  899. package/dist/onboard-BEFQQeig.js +0 -25
  900. package/dist/onboard-CJHNyxJh.js +0 -48
  901. package/dist/onboard-D_3UeLEN.js +0 -589
  902. package/dist/onboard-channels-B_JL0Djc.js +0 -1241
  903. package/dist/onboard-channels-CqZzHt2C.js +0 -205
  904. package/dist/onboard-custom-CER3Ggbq.js +0 -571
  905. package/dist/onboard-custom-bNRdGECb.js +0 -114
  906. package/dist/onboard-helpers-BK0Hsb7Y.js +0 -335
  907. package/dist/onboard-helpers-CXZ5RPoR.js +0 -113
  908. package/dist/onboard-hooks-1NsxEDjH.js +0 -72
  909. package/dist/onboard-remote-DuKhC_7W.js +0 -117
  910. package/dist/onboard-remote-OwRcDuB3.js +0 -181
  911. package/dist/onboard-search-Cy8dOq2W.js +0 -302
  912. package/dist/onboard-skills-D5phRa6r.js +0 -117
  913. package/dist/onboard-skills-c9qWCNe9.js +0 -133
  914. package/dist/outbound-media-CgNYEQWb.d.ts +0 -11
  915. package/dist/outbound-media-DYRO2vTD.js +0 -11
  916. package/dist/pairing-access-Dsiu5Mvl.d.ts +0 -21
  917. package/dist/pairing-cli-BOnv0TYn.js +0 -217
  918. package/dist/perplexity-EZwC3y2b.js +0 -24
  919. package/dist/persistent-dedupe-DMLOqJ23.d.ts +0 -26
  920. package/dist/pi-model-discovery-runtime-BToY3A6K.js +0 -111
  921. package/dist/pi-tools.before-tool-call.runtime-D_acPtld.js +0 -381
  922. package/dist/plugin-install-CgJpSjYd.js +0 -184
  923. package/dist/plugin-install-Cl1A4EF6.js +0 -117
  924. package/dist/plugin-install-plan-Dc2Z4DeU.js +0 -49
  925. package/dist/plugin-registry-B1UaWrQD.js +0 -49
  926. package/dist/plugin-registry-Cy8biwnn.js +0 -113
  927. package/dist/plugins-CXwvg50F.js +0 -111
  928. package/dist/plugins-cli-Uvzp2aYV.js +0 -917
  929. package/dist/policy-DsMBbEe7.js +0 -143
  930. package/dist/preflight-audio.runtime-hWsZIYvc.js +0 -116
  931. package/dist/probe-CNsSf1Uf.js +0 -6329
  932. package/dist/probe-CqOIrPhb.js +0 -47
  933. package/dist/probe-DH6gDw-h.js +0 -129
  934. package/dist/probe-DM16PLf4.js +0 -21
  935. package/dist/probe-DvAEEWYr.js +0 -1793
  936. package/dist/probe-auth-COfgCble.js +0 -48
  937. package/dist/probe-auth-I_5TX1Eh.js +0 -40
  938. package/dist/program-Dz80sgTU.js +0 -253
  939. package/dist/prompt-select-styled-wQehwFxK.js +0 -2673
  940. package/dist/provider-api-key-auth.runtime-BR9GU4ya.js +0 -121
  941. package/dist/provider-auth-choice-CdhA84kr.js +0 -126
  942. package/dist/provider-auth-choice-helpers-kabp_0zA.js +0 -48
  943. package/dist/provider-auth-choice-preference-se3zAM_2.js +0 -189
  944. package/dist/provider-auth-choice.runtime-BMc8-xNQ.js +0 -123
  945. package/dist/provider-auth-choices-CYsCViGi.js +0 -57
  946. package/dist/provider-auth-guidance-CMjUWlNf.js +0 -34
  947. package/dist/provider-auth-result-Cw6qIhO-.d.ts +0 -18
  948. package/dist/provider-models-BCId_Lfu.js +0 -2113
  949. package/dist/provider-models-Ok-DrSiY.d.ts +0 -867
  950. package/dist/provider-ollama-setup-B6XJZ0So.js +0 -314
  951. package/dist/provider-ollama-setup-lGDdTl0b.d.ts +0 -32
  952. package/dist/provider-onboard-CSPi7jOK.d.ts +0 -40
  953. package/dist/provider-onboard-Ca0TaNud.js +0 -139
  954. package/dist/provider-runtime.runtime-DwwkHw_7.js +0 -111
  955. package/dist/provider-self-hosted-setup-BEKLVGpj.js +0 -182
  956. package/dist/provider-self-hosted-setup-Df91By-J.d.ts +0 -61
  957. package/dist/provider-stream-DrUD69ai.js +0 -512
  958. package/dist/provider-usage-BgKHCnjr.js +0 -111
  959. package/dist/provider-usage-D8EZpFz9.js +0 -633
  960. package/dist/provider-wizard-DMdb-zj_.js +0 -152
  961. package/dist/push-apns-BPH6d4VV.js +0 -1038
  962. package/dist/pw-ai-DttfldtL.js +0 -1867
  963. package/dist/qmd-manager-CybcDUfk.js +0 -1570
  964. package/dist/qr-cli-8NcmJ8Ft.js +0 -369
  965. package/dist/qr-cli-DWe0Our3.js +0 -113
  966. package/dist/reactions-D6N0LR16.js +0 -281
  967. package/dist/read-only-account-inspect.discord.runtime-CqUWTRfl.js +0 -116
  968. package/dist/read-only-account-inspect.slack.runtime-9-jpln3q.js +0 -116
  969. package/dist/read-only-account-inspect.telegram.runtime-EKPI1D7n.js +0 -116
  970. package/dist/redact-snapshot-DwJEIVk9.js +0 -2663
  971. package/dist/register.agent-D3YdDirP.js +0 -439
  972. package/dist/register.backup-dR27qCuo.js +0 -625
  973. package/dist/register.configure-BjFhkkka.js +0 -252
  974. package/dist/register.maintenance-DiMQJIOa.js +0 -574
  975. package/dist/register.message-CdZsKYH1.js +0 -709
  976. package/dist/register.onboard-B0rV1eaO.js +0 -192
  977. package/dist/register.setup-wKMvohzo.js +0 -212
  978. package/dist/register.status-health-sessions-BJ68m6pt.js +0 -498
  979. package/dist/register.subclis-CnnrWt2a.js +0 -315
  980. package/dist/register.subclis-lSvTkC6z.js +0 -13
  981. package/dist/replies-BABt9b48.js +0 -110
  982. package/dist/resolve-channels-BqZFl2Ux.js +0 -262
  983. package/dist/resolve-channels-DjQLXb7B.js +0 -226
  984. package/dist/resolve-route-CSHDsa_m.js +0 -538
  985. package/dist/resolve-users-BG6HaSR5.js +0 -143
  986. package/dist/root-help-ohmaCyC_.js +0 -32
  987. package/dist/routes-4k2kpvoT.js +0 -7097
  988. package/dist/rpc-Cnwn4Q6L.js +0 -67
  989. package/dist/run-main-VYlacKA0.js +0 -424
  990. package/dist/runtime-D61jzMiI.d.ts +0 -26
  991. package/dist/runtime-discord-ops.runtime-DafrU-rI.js +0 -9078
  992. package/dist/runtime-slack-ops.runtime-CdXBKXwd.js +0 -4556
  993. package/dist/runtime-telegram-ops.runtime-B12sF7gE.js +0 -133
  994. package/dist/runtime-whatsapp-login.runtime-CqEudH37.js +0 -114
  995. package/dist/runtime-whatsapp-outbound.runtime-D5m2qyn-.js +0 -117
  996. package/dist/sandbox-cli-CHJiEWXB.js +0 -535
  997. package/dist/search-manager-BtNC3-i_.js +0 -16
  998. package/dist/search-manager-C7J7B3_a.js +0 -386
  999. package/dist/secrets-cli-C6yIWBbN.js +0 -2070
  1000. package/dist/security-cli-BVu9BkjD.js +0 -575
  1001. package/dist/send-BSreC7rr.js +0 -631
  1002. package/dist/send-BsLHQG_B.js +0 -1025
  1003. package/dist/send-BuNhp8PH.js +0 -283
  1004. package/dist/send-DOCswVar.js +0 -100
  1005. package/dist/send-Dl0LLErk.js +0 -629
  1006. package/dist/server-node-events-Bq2067EG.js +0 -506
  1007. package/dist/server-y38L7N5H.js +0 -107
  1008. package/dist/sessions-BV8gXURR.js +0 -112
  1009. package/dist/sessions-dl1Kc-Ci.js +0 -218
  1010. package/dist/setup-BSPXdMuK.d.ts +0 -37
  1011. package/dist/setup-DGszQH0_.js +0 -387
  1012. package/dist/setup-binary-C17YnmA8.js +0 -406
  1013. package/dist/setup-browser-CPx-nEsr.js +0 -70
  1014. package/dist/setup-core-BByHN1ME.js +0 -143
  1015. package/dist/setup-core-C0KPlBmL.js +0 -47
  1016. package/dist/setup-core-Cq37G6of.js +0 -166
  1017. package/dist/setup-core-uO84_Y75.js +0 -205
  1018. package/dist/setup-surface-BEMi7Rmb.js +0 -490
  1019. package/dist/setup-wizard-helpers-Ck9wDR0b.d.ts +0 -203
  1020. package/dist/setup.finalize-BzPBa8zW.js +0 -522
  1021. package/dist/setup.gateway-config-DdwkF-8e.js +0 -343
  1022. package/dist/shared-BCw4SKjB.js +0 -96
  1023. package/dist/shared-CjNzsULP.js +0 -75
  1024. package/dist/shared-Cu1BE7ZE.js +0 -298
  1025. package/dist/shared-DSClmyUn.js +0 -182
  1026. package/dist/shared-DyJdGH6y.js +0 -102
  1027. package/dist/signal-Dyv4NZsB.js +0 -114
  1028. package/dist/skills-CbB5b27M.js +0 -853
  1029. package/dist/skills-CnfI7Szw.js +0 -20
  1030. package/dist/skills-cli-CavB1f_3.js +0 -292
  1031. package/dist/skills-install-B1OBdgd0.js +0 -763
  1032. package/dist/skills-status-B3gAmIbW.js +0 -169
  1033. package/dist/skills-status-DrHhFgU9.js +0 -21
  1034. package/dist/slack-BRzqnoAz.js +0 -114
  1035. package/dist/slash-commands.runtime-BK88kgds.js +0 -128
  1036. package/dist/slash-dispatch.runtime-COGywwJE.js +0 -141
  1037. package/dist/slash-skill-commands.runtime-Ti4brxgh.js +0 -116
  1038. package/dist/src-DUR6OQxI.js +0 -1701
  1039. package/dist/status-C6dgQY9a.js +0 -131
  1040. package/dist/status-CNK0Q7QH.js +0 -606
  1041. package/dist/status-DBcX0DSC.js +0 -43
  1042. package/dist/status-DKgFgbwv.js +0 -1599
  1043. package/dist/status-Wn5lhNAc.js +0 -126
  1044. package/dist/status-json-D2EkWqAl.js +0 -288
  1045. package/dist/status.link-channel-D3ULIdEa.js +0 -143
  1046. package/dist/status.scan.deps.runtime-BsjWTAm4.js +0 -126
  1047. package/dist/status.scan.runtime-D4HbzROD.js +0 -119
  1048. package/dist/status.summary-C3YxPrDK.js +0 -592
  1049. package/dist/status.summary.runtime-DAkXPSaK.js +0 -118
  1050. package/dist/status.update-B4NnN9P1.js +0 -77
  1051. package/dist/subagent-orphan-recovery-QiQEBv36.js +0 -307
  1052. package/dist/subagent-registry-runtime-BJatPQFK.js +0 -111
  1053. package/dist/subscription-BhZORXN9.js +0 -100
  1054. package/dist/subscription-QEUjQRMv.js +0 -33
  1055. package/dist/subscription-cli-HrULlAgc.js +0 -134
  1056. package/dist/synology-chat-DB76GWMN.js +0 -297
  1057. package/dist/system-cli-D8jDwWuL.js +0 -94
  1058. package/dist/telegram-BHiiqKkQ.js +0 -114
  1059. package/dist/text-chunking-Baonm9Lu.js +0 -84
  1060. package/dist/text-chunking-DzB11ONk.d.ts +0 -79
  1061. package/dist/tlon-DLESxNgD.js +0 -433
  1062. package/dist/tui-C75zi2Cl.js +0 -3834
  1063. package/dist/tui-cli-DFwx5e6i.js +0 -137
  1064. package/dist/types-BKldC9YN.d.ts +0 -22692
  1065. package/dist/types-MeyueBE0.d.ts +0 -45
  1066. package/dist/types.base-Cw0-zIvE.d.ts +0 -188
  1067. package/dist/ui-B55NOIB6.js +0 -31
  1068. package/dist/update--ojavYQ4.js +0 -1036
  1069. package/dist/update-cli-Cvj5aWYM.js +0 -1503
  1070. package/dist/update-offset-store-upatuWwX.js +0 -112
  1071. package/dist/update-runner-DHkY_-76.js +0 -1496
  1072. package/dist/upsert-with-lock-C171GLaR.js +0 -33
  1073. package/dist/usage-N3bxnbmt.js +0 -115
  1074. package/dist/web-RdvT7gKa.js +0 -112
  1075. package/dist/web-shared-C2qHVxw1.d.ts +0 -45
  1076. package/dist/webhook-request-guards-CosLyl01.d.ts +0 -76
  1077. package/dist/webhook-targets-Bfnag-du.js +0 -181
  1078. package/dist/webhook-targets-DP_EkQa4.d.ts +0 -106
  1079. package/dist/webhooks-cli-ZpnXrq7G.js +0 -350
  1080. package/dist/whatsapp-DNTAyZHt.js +0 -114
  1081. package/dist/whatsapp-actions-o1zKQzKZ.js +0 -167
  1082. package/dist/workspace-CpWi5wPr.js +0 -479
  1083. package/dist/workspace-Ii7aRS7c.js +0 -289
  1084. package/dist/workspace-dirs-x10McA9t.js +0 -2003
  1085. package/dist/zalo-C9OQRYRw.d.ts +0 -9
  1086. package/dist/zalo-zm_bYCKg.js +0 -415
  1087. package/dist/zalouser-CvVEUvc5.js +0 -30911
  1088. /package/dist/{account-id-B3YSn4hl.d.ts → account-id-B8ce6G_4.d.ts} +0 -0
  1089. /package/dist/{acpx-CnNv70m2.d.ts → acpx-Ci50I9T2.d.ts} +0 -0
  1090. /package/dist/{agent-media-payload-DE2pEcsz.d.ts → agent-media-payload-en-gS5p6.d.ts} +0 -0
  1091. /package/dist/{allow-from-DPpHnT2A.d.ts → allow-from-cMeQ47Ot.d.ts} +0 -0
  1092. /package/dist/{allowlist-resolution-CLFiZ6nE.d.ts → allowlist-resolution-DoAWbfXV.d.ts} +0 -0
  1093. /package/dist/{bluebubbles-Duhu-Jer.d.ts → bluebubbles-C6yYmUl0.d.ts} +0 -0
  1094. /package/dist/{boolean-param-BhFjB3gp.d.ts → boolean-param-CdO2TFTk.d.ts} +0 -0
  1095. /package/dist/{channel-config-schema-DnnVMdjR.d.ts → channel-config-schema-Chp38wel.d.ts} +0 -0
  1096. /package/dist/{channel-policy-Baq-Z06b.d.ts → channel-policy-g2h6AbYQ.d.ts} +0 -0
  1097. /package/dist/{chat-type-DpiBgwuG.d.ts → chat-type-BLt59pPT.d.ts} +0 -0
  1098. /package/dist/{command-format-vi4xq8e8.d.ts → command-format-BDJC05Jp.d.ts} +0 -0
  1099. /package/dist/{diffs-DK7fVSDo.d.ts → diffs-D_iNKCyn.d.ts} +0 -0
  1100. /package/dist/{directory-runtime-BTLPaysA.d.ts → directory-runtime-DhMex6HY.d.ts} +0 -0
  1101. /package/dist/{exec-C01wtBHu.d.ts → exec-pjfUY4KM.d.ts} +0 -0
  1102. /package/dist/{gaxios-fetch-compat-wZ38b3w3.js → gaxios-fetch-compat-B_vtINdV.js} +0 -0
  1103. /package/dist/{history-CwXuP2TW.d.ts → history-aqSS5VGQ.d.ts} +0 -0
  1104. /package/dist/{inbound-envelope-SggrBs9m.d.ts → inbound-envelope-C5hWuZod.d.ts} +0 -0
  1105. /package/dist/{index-apAZHsDo.d.ts → index-DXVQFYGX.d.ts} +0 -0
  1106. /package/dist/{json-store-r75IZGk9.d.ts → json-store-UnqQ5aV3.d.ts} +0 -0
  1107. /package/dist/{keyed-async-queue-DHIr7yNe.d.ts → keyed-async-queue-guucpLw3.d.ts} +0 -0
  1108. /package/dist/{links-HeQ3r_L0.d.ts → links-Bar0meEK.d.ts} +0 -0
  1109. /package/dist/{markdown-to-line-CDb4Jy3V.d.ts → markdown-to-line-D8uH_KOj.d.ts} +0 -0
  1110. /package/dist/{mattermost-DtCsxpgg.d.ts → mattermost-xl7jAFJL.d.ts} +0 -0
  1111. /package/dist/{net-BATPDwdQ.d.ts → net-rGOKGds6.d.ts} +0 -0
  1112. /package/dist/{nextcloud-talk-Bb2wHOwp.d.ts → nextcloud-talk-De2CZ9dV.d.ts} +0 -0
  1113. /package/dist/{oauth-utils-u567CLT0.d.ts → oauth-utils-DzN1AlEH.d.ts} +0 -0
  1114. /package/dist/{parse-finite-number-l3tNlrZh.d.ts → parse-finite-number-odgyqhi0.d.ts} +0 -0
  1115. /package/dist/{provider-usage.types-C6061OVN.d.ts → provider-usage.types-EDE9o-H_.d.ts} +0 -0
  1116. /package/dist/{reply-history-BDsFnZFl.d.ts → reply-history-CVuU31xe.d.ts} +0 -0
  1117. /package/dist/{reply-payload-CCvM4W9u.d.ts → reply-payload-CHkpBYwL.d.ts} +0 -0
  1118. /package/dist/{request-url-C54l4-xC.d.ts → request-url-DHisbiHY.d.ts} +0 -0
  1119. /package/dist/{run-command-D3RqWcHu.d.ts → run-command-y0Cndsb1.d.ts} +0 -0
  1120. /package/dist/{secret-input-schema-BLBt-NAP.d.ts → secret-input-schema-b1vpYDQN.d.ts} +0 -0
  1121. /package/dist/{session-key-BQ2-bR-9.d.ts → session-key-DTHQl57f.d.ts} +0 -0
  1122. /package/dist/{ssh-config-C4mcH9Ly.js → ssh-config-hEHBfU2_.js} +0 -0
  1123. /package/dist/{testing-DLkhGsoz.d.ts → testing-DszuZXgK.d.ts} +0 -0
  1124. /package/dist/{thinking-DRkjX18p.d.ts → thinking-IwXTGSeT.d.ts} +0 -0
  1125. /package/dist/{tool-send-CMMD1uDu.d.ts → tool-send-DWHRmKpz.d.ts} +0 -0
  1126. /package/dist/{vllm-defaults-CcGuf4hL.d.ts → vllm-defaults-CrxZgE6-.d.ts} +0 -0
  1127. /package/dist/{wait-Daog8bxM.d.ts → wait-wDWw_MTI.d.ts} +0 -0
  1128. /package/dist/{webhook-memory-guards-C5MrExwT.d.ts → webhook-memory-guards-DreORuJy.d.ts} +0 -0
  1129. /package/dist/{windows-spawn-j2l-dqu8.d.ts → windows-spawn-BIzH92x2.d.ts} +0 -0
  1130. /package/dist/{zod-schema.agent-runtime-krMrBnIn.d.ts → zod-schema.agent-runtime-CP2rmis3.d.ts} +0 -0
  1131. /package/dist/{zod-schema.core-BNDieZDZ.d.ts → zod-schema.core-Foi1tYwi.d.ts} +0 -0
@@ -1,4681 +0,0 @@
1
- import { t as formatDocsLink } from "./links-BcahUP5U.js";
2
- import { Ah as createScopedPairingAccess, Ew as applySetupAccountConfigPatch, HC as createAccountListHelpers, Is as formatInboundFromLabel$1, KC as buildComputedAccountStatusSnapshot, LT as collectAllowlistProviderRestrictSendersWarnings, Nd as resolveDmGroupAccessWithLists, Od as DM_GROUP_ACCESS_REASON, Pd as resolveEffectiveAllowFromLists, Qg as buildPendingHistoryContextFromMap, Tw as applyAccountNameToChannelSection, UT as deleteAccountFromConfigSection, Ug as logInboundDrop, Uw as resolveControlCommandGate, Vs as buildModelsProviderData, WS as createDedupeCache, WT as setAccountEnabledInConfigSection, Wg as logTypingFailure, Za as resolveStoredModelOverride, ag as createTypingCallbacks, bS as readRequestBodyWithLimit, dT as createScopedAccountConfigAccessors, e_ as clearHistoryEntriesIfEnabled, ec as listSkillCommandsForAgents, ig as buildSecretInputSchema, jh as buildAgentMediaPayload, kT as buildAccountScopedDmSecurityPolicy, kd as readStoreAllowFromForDmPolicy, kw as migrateBaseNameToDefaultAccount, nT as resolveAllowlistMatchSimple, nh as resolveChannelMediaMaxBytes, ow as resolveChannelGroupRequireMention, qy as getAgentScopedMediaLocalRoots, rC as loadSessionStore, r_ as recordPendingHistoryEntryIfEnabled, sg as createReplyPrefixOptions, tC as isDangerousNameMatchingEnabled, vS as isRequestBodyLimitError, xs as registerPluginHttpRoute } from "./auth-profiles-1kPLbBwI.js";
3
- import { w as normalizeProviderId } from "./model-selection-hL8i1Jbs.js";
4
- import { d as resolveThreadSessionKeys$1 } from "./session-key-UoG7Kfw5.js";
5
- import { n as normalizeAccountId, t as DEFAULT_ACCOUNT_ID } from "./account-id-BuyZMNja.js";
6
- import { a as hasConfiguredSecretInput, c as normalizeResolvedSecretInputString, l as normalizeSecretInputString } from "./types.secrets-Ca-9L8vU.js";
7
- import { F as requireOpenAllowFrom, a as DmPolicySchema, c as GroupPolicySchema, m as MarkdownConfigSchema, n as BlockStreamingCoalesceSchema } from "./zod-schema.core-DvwgNmpd.js";
8
- import { a as isTrustedProxyAddress, l as resolveClientIp } from "./net-K181nxTH.js";
9
- import { t as rawDataToString } from "./ws-Bd4lOIoI.js";
10
- import { c as resolveAllowlistProviderRuntimeGroupPolicy, f as warnMissingProviderGroupPolicyFallbackOnce, i as evaluateSenderGroupAccessForPolicy, l as resolveDefaultGroupPolicy } from "./group-access-UAqUyJod.js";
11
- import { r as buildChannelConfigSchema } from "./config-schema-BNU4GQh_.js";
12
- import { i as parseStrictPositiveInteger } from "./parse-finite-number-U5TetQpk.js";
13
- import { l as resolveStorePath } from "./paths-ApLcu1Uu.js";
14
- import { n as formatNormalizedAllowFromEntries } from "./allow-from-Brz0jyla.js";
15
- import { t as createPluginRuntimeStore } from "./runtime-store-DTqHvPYo.js";
16
- import { t as createAccountStatusSink } from "./channel-lifecycle-DA5pCpey.js";
17
- import { t as loadOutboundMediaFromUrl } from "./outbound-media-DYRO2vTD.js";
18
- import { n as buildPassiveProbedChannelStatusSummary } from "./channel-status-summary-DfvQV-Ir.js";
19
- import { t as requireChannelOpenAllowFrom } from "./config-schema-helpers-D4ZGZ7dA.js";
20
- import { createHash, createHmac, timingSafeEqual } from "node:crypto";
21
- import { z } from "zod";
22
- import WebSocket$1 from "ws";
23
- //#region extensions/mattermost/src/config-schema.ts
24
- const MattermostSlashCommandsSchema = z.object({
25
- native: z.union([z.boolean(), z.literal("auto")]).optional(),
26
- nativeSkills: z.union([z.boolean(), z.literal("auto")]).optional(),
27
- callbackPath: z.string().optional(),
28
- callbackUrl: z.string().optional()
29
- }).strict().optional();
30
- const MattermostAccountSchemaBase = z.object({
31
- name: z.string().optional(),
32
- capabilities: z.array(z.string()).optional(),
33
- dangerouslyAllowNameMatching: z.boolean().optional(),
34
- markdown: MarkdownConfigSchema,
35
- enabled: z.boolean().optional(),
36
- configWrites: z.boolean().optional(),
37
- botToken: buildSecretInputSchema().optional(),
38
- baseUrl: z.string().optional(),
39
- chatmode: z.enum([
40
- "oncall",
41
- "onmessage",
42
- "onchar"
43
- ]).optional(),
44
- oncharPrefixes: z.array(z.string()).optional(),
45
- requireMention: z.boolean().optional(),
46
- dmPolicy: DmPolicySchema.optional().default("pairing"),
47
- allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
48
- groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
49
- groupPolicy: GroupPolicySchema.optional().default("allowlist"),
50
- textChunkLimit: z.number().int().positive().optional(),
51
- chunkMode: z.enum(["length", "newline"]).optional(),
52
- blockStreaming: z.boolean().optional(),
53
- blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
54
- replyToMode: z.enum([
55
- "off",
56
- "first",
57
- "all"
58
- ]).optional(),
59
- responsePrefix: z.string().optional(),
60
- actions: z.object({ reactions: z.boolean().optional() }).optional(),
61
- commands: MattermostSlashCommandsSchema,
62
- interactions: z.object({
63
- callbackBaseUrl: z.string().optional(),
64
- allowedSourceIps: z.array(z.string()).optional()
65
- }).optional()
66
- }).strict();
67
- const MattermostAccountSchema = MattermostAccountSchemaBase.superRefine((value, ctx) => {
68
- requireChannelOpenAllowFrom({
69
- channel: "mattermost",
70
- policy: value.dmPolicy,
71
- allowFrom: value.allowFrom,
72
- ctx,
73
- requireOpenAllowFrom
74
- });
75
- });
76
- const MattermostConfigSchema = MattermostAccountSchemaBase.extend({
77
- accounts: z.record(z.string(), MattermostAccountSchema.optional()).optional(),
78
- defaultAccount: z.string().optional()
79
- }).superRefine((value, ctx) => {
80
- requireChannelOpenAllowFrom({
81
- channel: "mattermost",
82
- policy: value.dmPolicy,
83
- allowFrom: value.allowFrom,
84
- ctx,
85
- requireOpenAllowFrom
86
- });
87
- });
88
- //#endregion
89
- //#region extensions/mattermost/src/mattermost/client.ts
90
- function normalizeMattermostBaseUrl(raw) {
91
- const trimmed = raw?.trim();
92
- if (!trimmed) return;
93
- return trimmed.replace(/\/+$/, "").replace(/\/api\/v4$/i, "");
94
- }
95
- function buildMattermostApiUrl(baseUrl, path) {
96
- const normalized = normalizeMattermostBaseUrl(baseUrl);
97
- if (!normalized) throw new Error("Mattermost baseUrl is required");
98
- return `${normalized}/api/v4${path.startsWith("/") ? path : `/${path}`}`;
99
- }
100
- async function readMattermostError(res) {
101
- if ((res.headers.get("content-type") ?? "").includes("application/json")) {
102
- const data = await res.json();
103
- if (data?.message) return data.message;
104
- return JSON.stringify(data);
105
- }
106
- return await res.text();
107
- }
108
- function createMattermostClient(params) {
109
- const baseUrl = normalizeMattermostBaseUrl(params.baseUrl);
110
- if (!baseUrl) throw new Error("Mattermost baseUrl is required");
111
- const apiBaseUrl = `${baseUrl}/api/v4`;
112
- const token = params.botToken.trim();
113
- const fetchImpl = params.fetchImpl ?? fetch;
114
- const request = async (path, init) => {
115
- const url = buildMattermostApiUrl(baseUrl, path);
116
- const headers = new Headers(init?.headers);
117
- headers.set("Authorization", `Bearer ${token}`);
118
- if (typeof init?.body === "string" && !headers.has("Content-Type")) headers.set("Content-Type", "application/json");
119
- const res = await fetchImpl(url, {
120
- ...init,
121
- headers
122
- });
123
- if (!res.ok) {
124
- const detail = await readMattermostError(res);
125
- throw new Error(`Mattermost API ${res.status} ${res.statusText}: ${detail || "unknown error"}`);
126
- }
127
- if (res.status === 204) return;
128
- if ((res.headers.get("content-type") ?? "").includes("application/json")) return await res.json();
129
- return await res.text();
130
- };
131
- return {
132
- baseUrl,
133
- apiBaseUrl,
134
- token,
135
- request
136
- };
137
- }
138
- async function fetchMattermostMe(client) {
139
- return await client.request("/users/me");
140
- }
141
- async function fetchMattermostUser(client, userId) {
142
- return await client.request(`/users/${userId}`);
143
- }
144
- async function fetchMattermostUserByUsername(client, username) {
145
- return await client.request(`/users/username/${encodeURIComponent(username)}`);
146
- }
147
- async function fetchMattermostChannel(client, channelId) {
148
- return await client.request(`/channels/${channelId}`);
149
- }
150
- async function fetchMattermostChannelByName(client, teamId, channelName) {
151
- return await client.request(`/teams/${teamId}/channels/name/${encodeURIComponent(channelName)}`);
152
- }
153
- async function sendMattermostTyping(client, params) {
154
- const payload = { channel_id: params.channelId };
155
- const parentId = params.parentId?.trim();
156
- if (parentId) payload.parent_id = parentId;
157
- await client.request("/users/me/typing", {
158
- method: "POST",
159
- body: JSON.stringify(payload)
160
- });
161
- }
162
- async function createMattermostDirectChannel(client, userIds) {
163
- return await client.request("/channels/direct", {
164
- method: "POST",
165
- body: JSON.stringify(userIds)
166
- });
167
- }
168
- async function createMattermostPost(client, params) {
169
- const payload = {
170
- channel_id: params.channelId,
171
- message: params.message
172
- };
173
- if (params.rootId) payload.root_id = params.rootId;
174
- if (params.fileIds?.length) payload.file_ids = params.fileIds;
175
- if (params.props) payload.props = params.props;
176
- return await client.request("/posts", {
177
- method: "POST",
178
- body: JSON.stringify(payload)
179
- });
180
- }
181
- async function fetchMattermostUserTeams(client, userId) {
182
- return await client.request(`/users/${userId}/teams`);
183
- }
184
- async function updateMattermostPost(client, postId, params) {
185
- const payload = { id: postId };
186
- if (params.message !== void 0) payload.message = params.message;
187
- if (params.props !== void 0) payload.props = params.props;
188
- return await client.request(`/posts/${postId}`, {
189
- method: "PUT",
190
- body: JSON.stringify(payload)
191
- });
192
- }
193
- async function uploadMattermostFile(client, params) {
194
- const form = new FormData();
195
- const fileName = params.fileName?.trim() || "upload";
196
- const bytes = Uint8Array.from(params.buffer);
197
- const blob = params.contentType ? new Blob([bytes], { type: params.contentType }) : new Blob([bytes]);
198
- form.append("files", blob, fileName);
199
- form.append("channel_id", params.channelId);
200
- const res = await fetch(`${client.apiBaseUrl}/files`, {
201
- method: "POST",
202
- headers: { Authorization: `Bearer ${client.token}` },
203
- body: form
204
- });
205
- if (!res.ok) {
206
- const detail = await readMattermostError(res);
207
- throw new Error(`Mattermost API ${res.status} ${res.statusText}: ${detail || "unknown error"}`);
208
- }
209
- const info = (await res.json()).file_infos?.[0];
210
- if (!info?.id) throw new Error("Mattermost file upload failed");
211
- return info;
212
- }
213
- //#endregion
214
- //#region extensions/mattermost/src/mattermost/accounts.ts
215
- const { listAccountIds: listMattermostAccountIds, resolveDefaultAccountId: resolveDefaultMattermostAccountId } = createAccountListHelpers("mattermost");
216
- function resolveAccountConfig(cfg, accountId) {
217
- const accounts = cfg.channels?.mattermost?.accounts;
218
- if (!accounts || typeof accounts !== "object") return;
219
- return accounts[accountId];
220
- }
221
- function mergeMattermostAccountConfig(cfg, accountId) {
222
- const { accounts: _ignored, defaultAccount: _ignoredDefaultAccount, ...base } = cfg.channels?.mattermost ?? {};
223
- const account = resolveAccountConfig(cfg, accountId) ?? {};
224
- const mergedCommands = {
225
- ...base.commands ?? {},
226
- ...account.commands ?? {}
227
- };
228
- const merged = {
229
- ...base,
230
- ...account
231
- };
232
- if (Object.keys(mergedCommands).length > 0) merged.commands = mergedCommands;
233
- return merged;
234
- }
235
- function resolveMattermostRequireMention(config) {
236
- if (config.chatmode === "oncall") return true;
237
- if (config.chatmode === "onmessage") return false;
238
- if (config.chatmode === "onchar") return true;
239
- return config.requireMention;
240
- }
241
- function resolveMattermostAccount(params) {
242
- const accountId = normalizeAccountId(params.accountId);
243
- const baseEnabled = params.cfg.channels?.mattermost?.enabled !== false;
244
- const merged = mergeMattermostAccountConfig(params.cfg, accountId);
245
- const accountEnabled = merged.enabled !== false;
246
- const enabled = baseEnabled && accountEnabled;
247
- const allowEnv = accountId === DEFAULT_ACCOUNT_ID;
248
- const envToken = allowEnv ? process.env.MATTERMOST_BOT_TOKEN?.trim() : void 0;
249
- const envUrl = allowEnv ? process.env.MATTERMOST_URL?.trim() : void 0;
250
- const configToken = params.allowUnresolvedSecretRef ? normalizeSecretInputString(merged.botToken) : normalizeResolvedSecretInputString({
251
- value: merged.botToken,
252
- path: `channels.mattermost.accounts.${accountId}.botToken`
253
- });
254
- const configUrl = merged.baseUrl?.trim();
255
- const botToken = configToken || envToken;
256
- const baseUrl = normalizeMattermostBaseUrl(configUrl || envUrl);
257
- const requireMention = resolveMattermostRequireMention(merged);
258
- const botTokenSource = configToken ? "config" : envToken ? "env" : "none";
259
- const baseUrlSource = configUrl ? "config" : envUrl ? "env" : "none";
260
- return {
261
- accountId,
262
- enabled,
263
- name: merged.name?.trim() || void 0,
264
- botToken,
265
- baseUrl,
266
- botTokenSource,
267
- baseUrlSource,
268
- config: merged,
269
- chatmode: merged.chatmode,
270
- oncharPrefixes: merged.oncharPrefixes,
271
- requireMention,
272
- textChunkLimit: merged.textChunkLimit,
273
- blockStreaming: merged.blockStreaming,
274
- blockStreamingCoalesce: merged.blockStreamingCoalesce
275
- };
276
- }
277
- /**
278
- * Resolve the effective replyToMode for a given chat type.
279
- * Mattermost auto-threading only applies to channel and group messages.
280
- */
281
- function resolveMattermostReplyToMode(account, kind) {
282
- if (kind === "direct") return "off";
283
- return account.config.replyToMode ?? "off";
284
- }
285
- //#endregion
286
- //#region extensions/mattermost/src/group-mentions.ts
287
- function resolveMattermostGroupRequireMention(params) {
288
- const account = resolveMattermostAccount({
289
- cfg: params.cfg,
290
- accountId: params.accountId
291
- });
292
- const requireMentionOverride = typeof params.requireMentionOverride === "boolean" ? params.requireMentionOverride : account.requireMention;
293
- return resolveChannelGroupRequireMention({
294
- cfg: params.cfg,
295
- channel: "mattermost",
296
- groupId: params.groupId,
297
- accountId: params.accountId,
298
- requireMentionOverride
299
- });
300
- }
301
- //#endregion
302
- //#region extensions/mattermost/src/mattermost/directory.ts
303
- function buildClient(params) {
304
- const account = resolveMattermostAccount({
305
- cfg: params.cfg,
306
- accountId: params.accountId
307
- });
308
- if (!account.enabled || !account.botToken || !account.baseUrl) return null;
309
- return createMattermostClient({
310
- baseUrl: account.baseUrl,
311
- botToken: account.botToken
312
- });
313
- }
314
- /**
315
- * Build clients from ALL enabled accounts (deduplicated by token).
316
- *
317
- * We always scan every account because:
318
- * - Private channels are only visible to bots that are members
319
- * - The requesting agent's account may have an expired/invalid token
320
- *
321
- * This means a single healthy bot token is enough for directory discovery.
322
- */
323
- function buildClients(params) {
324
- const accountIds = listMattermostAccountIds(params.cfg);
325
- const seen = /* @__PURE__ */ new Set();
326
- const clients = [];
327
- for (const id of accountIds) {
328
- const client = buildClient({
329
- cfg: params.cfg,
330
- accountId: id
331
- });
332
- if (client && !seen.has(client.token)) {
333
- seen.add(client.token);
334
- clients.push(client);
335
- }
336
- }
337
- return clients;
338
- }
339
- /**
340
- * List channels (public + private) visible to any configured bot account.
341
- *
342
- * NOTE: Uses per_page=200 which covers most instances. Mattermost does not
343
- * return a "has more" indicator, so very large instances (200+ channels per bot)
344
- * may see incomplete results. Pagination can be added if needed.
345
- */
346
- async function listMattermostDirectoryGroups(params) {
347
- const clients = buildClients(params);
348
- if (!clients.length) return [];
349
- const q = params.query?.trim().toLowerCase() || "";
350
- const seenIds = /* @__PURE__ */ new Set();
351
- const entries = [];
352
- for (const client of clients) try {
353
- const me = await fetchMattermostMe(client);
354
- const channels = await client.request(`/users/${me.id}/channels?per_page=200`);
355
- for (const ch of channels) {
356
- if (ch.type !== "O" && ch.type !== "P") continue;
357
- if (seenIds.has(ch.id)) continue;
358
- if (q) {
359
- const name = (ch.name ?? "").toLowerCase();
360
- const display = (ch.display_name ?? "").toLowerCase();
361
- if (!name.includes(q) && !display.includes(q)) continue;
362
- }
363
- seenIds.add(ch.id);
364
- entries.push({
365
- kind: "group",
366
- id: `channel:${ch.id}`,
367
- name: ch.name ?? void 0,
368
- handle: ch.display_name ?? void 0
369
- });
370
- }
371
- } catch (err) {
372
- console.debug?.("[mattermost-directory] listGroups: skipping account:", err?.message);
373
- continue;
374
- }
375
- return params.limit && params.limit > 0 ? entries.slice(0, params.limit) : entries;
376
- }
377
- /**
378
- * List team members as peer directory entries.
379
- *
380
- * Uses only the first available client since all bots in a team see the same
381
- * user list (unlike channels where membership varies). Uses the first team
382
- * returned — multi-team setups will only see members from that team.
383
- *
384
- * NOTE: per_page=200 for member listing; same pagination caveat as groups.
385
- */
386
- async function listMattermostDirectoryPeers(params) {
387
- const clients = buildClients(params);
388
- if (!clients.length) return [];
389
- const client = clients[0];
390
- try {
391
- const me = await fetchMattermostMe(client);
392
- const teams = await client.request("/users/me/teams");
393
- if (!teams.length) return [];
394
- const teamId = teams[0].id;
395
- const q = params.query?.trim().toLowerCase() || "";
396
- let users;
397
- if (q) users = await client.request("/users/search", {
398
- method: "POST",
399
- body: JSON.stringify({
400
- term: q,
401
- team_id: teamId
402
- })
403
- });
404
- else {
405
- const userIds = (await client.request(`/teams/${teamId}/members?per_page=200`)).map((m) => m.user_id).filter((id) => id !== me.id);
406
- if (!userIds.length) return [];
407
- users = await client.request("/users/ids", {
408
- method: "POST",
409
- body: JSON.stringify(userIds)
410
- });
411
- }
412
- const entries = users.filter((u) => u.id !== me.id).map((u) => ({
413
- kind: "user",
414
- id: `user:${u.id}`,
415
- name: u.username ?? void 0,
416
- handle: [u.first_name, u.last_name].filter(Boolean).join(" ").trim() || u.nickname || void 0
417
- }));
418
- return params.limit && params.limit > 0 ? entries.slice(0, params.limit) : entries;
419
- } catch (err) {
420
- console.debug?.("[mattermost-directory] listPeers failed:", err?.message);
421
- return [];
422
- }
423
- }
424
- //#endregion
425
- //#region extensions/mattermost/src/runtime.ts
426
- const { setRuntime: setMattermostRuntime, getRuntime: getMattermostRuntime } = createPluginRuntimeStore("Mattermost runtime not initialized");
427
- //#endregion
428
- //#region extensions/mattermost/src/mattermost/interactions.ts
429
- const INTERACTION_MAX_BODY_BYTES = 64 * 1024;
430
- const INTERACTION_BODY_TIMEOUT_MS = 1e4;
431
- const SIGNED_CHANNEL_ID_CONTEXT_KEY = "__moldclaw_channel_id";
432
- const callbackUrls = /* @__PURE__ */ new Map();
433
- function setInteractionCallbackUrl(accountId, url) {
434
- callbackUrls.set(accountId, url);
435
- }
436
- function resolveInteractionCallbackPath(accountId) {
437
- return `/mattermost/interactions/${accountId}`;
438
- }
439
- function isWildcardBindHost(rawHost) {
440
- const trimmed = rawHost.trim();
441
- if (!trimmed) return false;
442
- const host = trimmed.startsWith("[") && trimmed.endsWith("]") ? trimmed.slice(1, -1) : trimmed;
443
- return host === "0.0.0.0" || host === "::" || host === "0:0:0:0:0:0:0:0" || host === "::0";
444
- }
445
- function normalizeCallbackBaseUrl(baseUrl) {
446
- return baseUrl.trim().replace(/\/+$/, "");
447
- }
448
- function headerValue(value) {
449
- if (Array.isArray(value)) return value[0]?.trim() || void 0;
450
- return value?.trim() || void 0;
451
- }
452
- function isAllowedInteractionSource(params) {
453
- const { allowedSourceIps } = params;
454
- if (!allowedSourceIps?.length) return true;
455
- return isTrustedProxyAddress(resolveClientIp({
456
- remoteAddr: params.req.socket?.remoteAddress,
457
- forwardedFor: headerValue(params.req.headers["x-forwarded-for"]),
458
- realIp: headerValue(params.req.headers["x-real-ip"]),
459
- trustedProxies: params.trustedProxies,
460
- allowRealIpFallback: params.allowRealIpFallback
461
- }), allowedSourceIps);
462
- }
463
- /**
464
- * Resolve the interaction callback URL for an account.
465
- * Falls back to computing it from interactions.callbackBaseUrl or gateway host config.
466
- */
467
- function computeInteractionCallbackUrl(accountId, cfg) {
468
- const path = resolveInteractionCallbackPath(accountId);
469
- const callbackBaseUrl = cfg?.interactions?.callbackBaseUrl?.trim() ?? cfg?.channels?.mattermost?.interactions?.callbackBaseUrl?.trim();
470
- if (callbackBaseUrl) return `${normalizeCallbackBaseUrl(callbackBaseUrl)}${path}`;
471
- const port = typeof cfg?.gateway?.port === "number" ? cfg.gateway.port : 18789;
472
- let host = cfg?.gateway?.customBindHost && !isWildcardBindHost(cfg.gateway.customBindHost) ? cfg.gateway.customBindHost.trim() : "localhost";
473
- if (host.includes(":") && !(host.startsWith("[") && host.endsWith("]"))) host = `[${host}]`;
474
- return `http://${host}:${port}${path}`;
475
- }
476
- /**
477
- * Resolve the interaction callback URL for an account.
478
- * Prefers the in-memory registered URL (set by the gateway monitor) so callers outside the
479
- * monitor lifecycle can reuse the runtime-validated callback destination.
480
- */
481
- function resolveInteractionCallbackUrl(accountId, cfg) {
482
- const cached = callbackUrls.get(accountId);
483
- if (cached) return cached;
484
- return computeInteractionCallbackUrl(accountId, cfg);
485
- }
486
- const interactionSecrets = /* @__PURE__ */ new Map();
487
- let defaultInteractionSecret;
488
- function deriveInteractionSecret(botToken) {
489
- return createHmac("sha256", "moldclaw-mattermost-interactions").update(botToken).digest("hex");
490
- }
491
- function setInteractionSecret(accountIdOrBotToken, botToken) {
492
- if (typeof botToken === "string") {
493
- interactionSecrets.set(accountIdOrBotToken, deriveInteractionSecret(botToken));
494
- return;
495
- }
496
- defaultInteractionSecret = deriveInteractionSecret(accountIdOrBotToken);
497
- }
498
- function getInteractionSecret(accountId) {
499
- const scoped = accountId ? interactionSecrets.get(accountId) : void 0;
500
- if (scoped) return scoped;
501
- if (defaultInteractionSecret) return defaultInteractionSecret;
502
- if (interactionSecrets.size === 1) {
503
- const first = interactionSecrets.values().next().value;
504
- if (typeof first === "string") return first;
505
- }
506
- throw new Error("Interaction secret not initialized — call setInteractionSecret(accountId, botToken) first");
507
- }
508
- function canonicalizeInteractionContext(value) {
509
- if (Array.isArray(value)) return value.map((item) => canonicalizeInteractionContext(item));
510
- if (value && typeof value === "object") {
511
- const entries = Object.entries(value).filter(([, entryValue]) => entryValue !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, entryValue]) => [key, canonicalizeInteractionContext(entryValue)]);
512
- return Object.fromEntries(entries);
513
- }
514
- return value;
515
- }
516
- function generateInteractionToken(context, accountId) {
517
- const secret = getInteractionSecret(accountId);
518
- const payload = JSON.stringify(canonicalizeInteractionContext(context));
519
- return createHmac("sha256", secret).update(payload).digest("hex");
520
- }
521
- function verifyInteractionToken(context, token, accountId) {
522
- const expected = generateInteractionToken(context, accountId);
523
- if (expected.length !== token.length) return false;
524
- return timingSafeEqual(Buffer.from(expected), Buffer.from(token));
525
- }
526
- /**
527
- * Build Mattermost `props.attachments` with interactive buttons.
528
- *
529
- * Each button includes an HMAC token in its integration context so the
530
- * callback handler can verify the request originated from a legitimate
531
- * button click (Mattermost's recommended security pattern).
532
- */
533
- /**
534
- * Sanitize a button ID so Mattermost's action router can match it.
535
- * Mattermost uses the action ID in the URL path `/api/v4/posts/{id}/actions/{actionId}`
536
- * and IDs containing hyphens or underscores break the server-side routing.
537
- * See: https://github.com/mattermost/mattermost/issues/25747
538
- */
539
- function sanitizeActionId(id) {
540
- return id.replace(/[-_]/g, "");
541
- }
542
- function buildButtonAttachments(params) {
543
- const actions = params.buttons.map((btn) => {
544
- const safeId = sanitizeActionId(btn.id);
545
- const context = {
546
- action_id: safeId,
547
- ...btn.context
548
- };
549
- const token = generateInteractionToken(context, params.accountId);
550
- return {
551
- id: safeId,
552
- type: "button",
553
- name: btn.name,
554
- style: btn.style,
555
- integration: {
556
- url: params.callbackUrl,
557
- context: {
558
- ...context,
559
- _token: token
560
- }
561
- }
562
- };
563
- });
564
- return [{
565
- text: params.text ?? "",
566
- actions
567
- }];
568
- }
569
- function buildButtonProps(params) {
570
- const buttons = params.buttons.flatMap((item) => Array.isArray(item) ? item : [item]).map((btn) => ({
571
- id: String(btn.id ?? btn.callback_data ?? "").trim(),
572
- name: String(btn.text ?? btn.name ?? btn.label ?? "").trim(),
573
- style: btn.style ?? "default",
574
- context: typeof btn.context === "object" && btn.context !== null ? {
575
- ...btn.context,
576
- [SIGNED_CHANNEL_ID_CONTEXT_KEY]: params.channelId
577
- } : { [SIGNED_CHANNEL_ID_CONTEXT_KEY]: params.channelId }
578
- })).filter((btn) => btn.id && btn.name);
579
- if (buttons.length === 0) return;
580
- return { attachments: buildButtonAttachments({
581
- callbackUrl: params.callbackUrl,
582
- accountId: params.accountId,
583
- buttons,
584
- text: params.text
585
- }) };
586
- }
587
- function readInteractionBody(req) {
588
- return new Promise((resolve, reject) => {
589
- const chunks = [];
590
- let totalBytes = 0;
591
- const timer = setTimeout(() => {
592
- req.destroy();
593
- reject(/* @__PURE__ */ new Error("Request body read timeout"));
594
- }, INTERACTION_BODY_TIMEOUT_MS);
595
- req.on("data", (chunk) => {
596
- totalBytes += chunk.length;
597
- if (totalBytes > INTERACTION_MAX_BODY_BYTES) {
598
- req.destroy();
599
- clearTimeout(timer);
600
- reject(/* @__PURE__ */ new Error("Request body too large"));
601
- return;
602
- }
603
- chunks.push(chunk);
604
- });
605
- req.on("end", () => {
606
- clearTimeout(timer);
607
- resolve(Buffer.concat(chunks).toString("utf8"));
608
- });
609
- req.on("error", (err) => {
610
- clearTimeout(timer);
611
- reject(err);
612
- });
613
- });
614
- }
615
- function createMattermostInteractionHandler(params) {
616
- const { client, accountId, log } = params;
617
- const core = getMattermostRuntime();
618
- return async (req, res) => {
619
- if (req.method !== "POST") {
620
- res.statusCode = 405;
621
- res.setHeader("Allow", "POST");
622
- res.setHeader("Content-Type", "application/json");
623
- res.end(JSON.stringify({ error: "Method Not Allowed" }));
624
- return;
625
- }
626
- if (!isAllowedInteractionSource({
627
- req,
628
- allowedSourceIps: params.allowedSourceIps,
629
- trustedProxies: params.trustedProxies,
630
- allowRealIpFallback: params.allowRealIpFallback
631
- })) {
632
- log?.(`mattermost interaction: rejected callback source remote=${req.socket?.remoteAddress ?? "?"}`);
633
- res.statusCode = 403;
634
- res.setHeader("Content-Type", "application/json");
635
- res.end(JSON.stringify({ error: "Forbidden origin" }));
636
- return;
637
- }
638
- let payload;
639
- try {
640
- const raw = await readInteractionBody(req);
641
- payload = JSON.parse(raw);
642
- } catch (err) {
643
- log?.(`mattermost interaction: failed to parse body: ${String(err)}`);
644
- res.statusCode = 400;
645
- res.setHeader("Content-Type", "application/json");
646
- res.end(JSON.stringify({ error: "Invalid request body" }));
647
- return;
648
- }
649
- const context = payload.context;
650
- if (!context) {
651
- res.statusCode = 400;
652
- res.setHeader("Content-Type", "application/json");
653
- res.end(JSON.stringify({ error: "Missing context" }));
654
- return;
655
- }
656
- const token = context._token;
657
- if (typeof token !== "string") {
658
- log?.("mattermost interaction: missing _token in context");
659
- res.statusCode = 403;
660
- res.setHeader("Content-Type", "application/json");
661
- res.end(JSON.stringify({ error: "Missing token" }));
662
- return;
663
- }
664
- const { _token, ...contextWithoutToken } = context;
665
- if (!verifyInteractionToken(contextWithoutToken, token, accountId)) {
666
- log?.("mattermost interaction: invalid _token");
667
- res.statusCode = 403;
668
- res.setHeader("Content-Type", "application/json");
669
- res.end(JSON.stringify({ error: "Invalid token" }));
670
- return;
671
- }
672
- const actionId = context.action_id;
673
- if (typeof actionId !== "string") {
674
- res.statusCode = 400;
675
- res.setHeader("Content-Type", "application/json");
676
- res.end(JSON.stringify({ error: "Missing action_id in context" }));
677
- return;
678
- }
679
- const signedChannelId = typeof contextWithoutToken[SIGNED_CHANNEL_ID_CONTEXT_KEY] === "string" ? contextWithoutToken[SIGNED_CHANNEL_ID_CONTEXT_KEY].trim() : "";
680
- if (signedChannelId && signedChannelId !== payload.channel_id) {
681
- log?.(`mattermost interaction: signed channel mismatch payload=${payload.channel_id} signed=${signedChannelId}`);
682
- res.statusCode = 403;
683
- res.setHeader("Content-Type", "application/json");
684
- res.end(JSON.stringify({ error: "Channel mismatch" }));
685
- return;
686
- }
687
- const userName = payload.user_name ?? payload.user_id;
688
- let originalMessage = "";
689
- let originalPost = null;
690
- let clickedButtonName = null;
691
- try {
692
- originalPost = await client.request(`/posts/${payload.post_id}`);
693
- const postChannelId = originalPost.channel_id?.trim();
694
- if (!postChannelId || postChannelId !== payload.channel_id) {
695
- log?.(`mattermost interaction: post channel mismatch payload=${payload.channel_id} post=${postChannelId ?? "<missing>"}`);
696
- res.statusCode = 403;
697
- res.setHeader("Content-Type", "application/json");
698
- res.end(JSON.stringify({ error: "Post/channel mismatch" }));
699
- return;
700
- }
701
- originalMessage = originalPost.message ?? "";
702
- const postAttachments = Array.isArray(originalPost?.props?.attachments) ? originalPost.props.attachments : [];
703
- for (const att of postAttachments) {
704
- const match = att.actions?.find((a) => a.id === actionId);
705
- if (match?.name) {
706
- clickedButtonName = match.name;
707
- break;
708
- }
709
- }
710
- if (clickedButtonName === null) {
711
- log?.(`mattermost interaction: action ${actionId} not found in post ${payload.post_id}`);
712
- res.statusCode = 403;
713
- res.setHeader("Content-Type", "application/json");
714
- res.end(JSON.stringify({ error: "Unknown action" }));
715
- return;
716
- }
717
- } catch (err) {
718
- log?.(`mattermost interaction: failed to validate post ${payload.post_id}: ${String(err)}`);
719
- res.statusCode = 500;
720
- res.setHeader("Content-Type", "application/json");
721
- res.end(JSON.stringify({ error: "Failed to validate interaction" }));
722
- return;
723
- }
724
- if (!originalPost) {
725
- log?.(`mattermost interaction: missing fetched post ${payload.post_id}`);
726
- res.statusCode = 500;
727
- res.setHeader("Content-Type", "application/json");
728
- res.end(JSON.stringify({ error: "Failed to load interaction post" }));
729
- return;
730
- }
731
- log?.(`mattermost interaction: action=${actionId} user=${payload.user_name ?? payload.user_id} post=${payload.post_id} channel=${payload.channel_id}`);
732
- if (params.authorizeButtonClick) try {
733
- const authorization = await params.authorizeButtonClick({
734
- payload,
735
- post: originalPost
736
- });
737
- if (!authorization.ok) {
738
- res.statusCode = authorization.statusCode ?? 200;
739
- res.setHeader("Content-Type", "application/json");
740
- res.end(JSON.stringify(authorization.response ?? { ephemeral_text: "You are not allowed to use this action here." }));
741
- return;
742
- }
743
- } catch (err) {
744
- log?.(`mattermost interaction: authorization failed: ${String(err)}`);
745
- res.statusCode = 500;
746
- res.setHeader("Content-Type", "application/json");
747
- res.end(JSON.stringify({ error: "Interaction authorization failed" }));
748
- return;
749
- }
750
- if (params.handleInteraction) try {
751
- const response = await params.handleInteraction({
752
- payload,
753
- userName,
754
- actionId,
755
- actionName: clickedButtonName,
756
- originalMessage,
757
- context: contextWithoutToken,
758
- post: originalPost
759
- });
760
- if (response !== null) {
761
- res.statusCode = 200;
762
- res.setHeader("Content-Type", "application/json");
763
- res.end(JSON.stringify(response));
764
- return;
765
- }
766
- } catch (err) {
767
- log?.(`mattermost interaction: custom handler failed: ${String(err)}`);
768
- res.statusCode = 500;
769
- res.setHeader("Content-Type", "application/json");
770
- res.end(JSON.stringify({ error: "Interaction handler failed" }));
771
- return;
772
- }
773
- try {
774
- const eventLabel = `Mattermost button click: action="${actionId}" by ${payload.user_name ?? payload.user_id} in channel ${payload.channel_id}`;
775
- const sessionKey = params.resolveSessionKey ? await params.resolveSessionKey({
776
- channelId: payload.channel_id,
777
- userId: payload.user_id,
778
- post: originalPost
779
- }) : `agent:main:mattermost:${accountId}:${payload.channel_id}`;
780
- core.system.enqueueSystemEvent(eventLabel, {
781
- sessionKey,
782
- contextKey: `mattermost:interaction:${payload.post_id}:${actionId}`
783
- });
784
- } catch (err) {
785
- log?.(`mattermost interaction: system event dispatch failed: ${String(err)}`);
786
- }
787
- try {
788
- await updateMattermostPost(client, payload.post_id, {
789
- message: originalMessage,
790
- props: { attachments: [{ text: `✓ **${clickedButtonName}** selected by @${userName}` }] }
791
- });
792
- } catch (err) {
793
- log?.(`mattermost interaction: failed to update post ${payload.post_id}: ${String(err)}`);
794
- }
795
- res.statusCode = 200;
796
- res.setHeader("Content-Type", "application/json");
797
- res.end("{}");
798
- if (params.dispatchButtonClick) try {
799
- await params.dispatchButtonClick({
800
- channelId: payload.channel_id,
801
- userId: payload.user_id,
802
- userName,
803
- actionId,
804
- actionName: clickedButtonName,
805
- postId: payload.post_id,
806
- post: originalPost
807
- });
808
- } catch (err) {
809
- log?.(`mattermost interaction: dispatchButtonClick failed: ${String(err)}`);
810
- }
811
- };
812
- }
813
- //#endregion
814
- //#region extensions/mattermost/src/mattermost/model-picker.ts
815
- const MATTERMOST_MODEL_PICKER_CONTEXT_KEY = "oc_model_picker";
816
- const MODELS_PAGE_SIZE = 8;
817
- const ACTION_IDS = {
818
- providers: "mdlprov",
819
- list: "mdllist",
820
- select: "mdlsel",
821
- back: "mdlback"
822
- };
823
- function splitModelRef(modelRef) {
824
- const match = (modelRef?.trim())?.match(/^([^/]+)\/(.+)$/u);
825
- if (!match) return null;
826
- const provider = normalizeProviderId(match[1]);
827
- const model = match[2].trim();
828
- if (!provider || !model) return null;
829
- return {
830
- provider,
831
- model
832
- };
833
- }
834
- function normalizePage(value) {
835
- if (!Number.isFinite(value)) return 1;
836
- return Math.max(1, Math.floor(value));
837
- }
838
- function paginateItems(items, page, pageSize = MODELS_PAGE_SIZE) {
839
- const totalPages = Math.max(1, Math.ceil(items.length / pageSize));
840
- const safePage = Math.max(1, Math.min(normalizePage(page), totalPages));
841
- const start = (safePage - 1) * pageSize;
842
- return {
843
- items: items.slice(start, start + pageSize),
844
- page: safePage,
845
- totalPages,
846
- hasPrev: safePage > 1,
847
- hasNext: safePage < totalPages,
848
- totalItems: items.length
849
- };
850
- }
851
- function buildContext(state) {
852
- return {
853
- [MATTERMOST_MODEL_PICKER_CONTEXT_KEY]: true,
854
- ...state
855
- };
856
- }
857
- function buildButtonId(state) {
858
- const digest = createHash("sha256").update(JSON.stringify(state)).digest("hex").slice(0, 12);
859
- return `${ACTION_IDS[state.action]}${digest}`;
860
- }
861
- function buildButton(params) {
862
- const baseState = params.action === "providers" || params.action === "back" ? {
863
- action: params.action,
864
- ownerUserId: params.ownerUserId
865
- } : params.action === "list" ? {
866
- action: "list",
867
- ownerUserId: params.ownerUserId,
868
- provider: normalizeProviderId(params.provider ?? ""),
869
- page: normalizePage(params.page)
870
- } : {
871
- action: "select",
872
- ownerUserId: params.ownerUserId,
873
- provider: normalizeProviderId(params.provider ?? ""),
874
- page: normalizePage(params.page),
875
- model: String(params.model ?? "").trim()
876
- };
877
- return {
878
- id: buildButtonId(baseState),
879
- text: params.text,
880
- ...params.style ? { style: params.style } : {},
881
- context: buildContext(baseState)
882
- };
883
- }
884
- function getProviderModels(data, provider) {
885
- return [...data.byProvider.get(normalizeProviderId(provider)) ?? /* @__PURE__ */ new Set()].toSorted();
886
- }
887
- function formatCurrentModelLine(currentModel) {
888
- const parsed = splitModelRef(currentModel);
889
- if (!parsed) return "Current: default";
890
- return `Current: ${parsed.provider}/${parsed.model}`;
891
- }
892
- function resolveMattermostModelPickerEntry(commandText) {
893
- const normalized = commandText.trim().replace(/\s+/g, " ");
894
- if (/^\/model$/i.test(normalized)) return { kind: "summary" };
895
- if (/^\/models$/i.test(normalized)) return { kind: "providers" };
896
- const providerMatch = normalized.match(/^\/models\s+(\S+)$/i);
897
- if (!providerMatch?.[1]) return null;
898
- return {
899
- kind: "models",
900
- provider: normalizeProviderId(providerMatch[1])
901
- };
902
- }
903
- function parseMattermostModelPickerContext(context) {
904
- if (!context || context[MATTERMOST_MODEL_PICKER_CONTEXT_KEY] !== true) return null;
905
- const ownerUserId = String(context.ownerUserId ?? "").trim();
906
- const action = String(context.action ?? "").trim();
907
- if (!ownerUserId) return null;
908
- if (action === "providers" || action === "back") return {
909
- action,
910
- ownerUserId
911
- };
912
- const provider = normalizeProviderId(String(context.provider ?? ""));
913
- const page = Number.parseInt(String(context.page ?? "1"), 10);
914
- if (!provider) return null;
915
- if (action === "list") return {
916
- action,
917
- ownerUserId,
918
- provider,
919
- page: normalizePage(page)
920
- };
921
- if (action === "select") {
922
- const model = String(context.model ?? "").trim();
923
- if (!model) return null;
924
- return {
925
- action,
926
- ownerUserId,
927
- provider,
928
- page: normalizePage(page),
929
- model
930
- };
931
- }
932
- return null;
933
- }
934
- function buildMattermostAllowedModelRefs(data) {
935
- const refs = /* @__PURE__ */ new Set();
936
- for (const provider of data.providers) for (const model of data.byProvider.get(provider) ?? []) refs.add(`${provider}/${model}`);
937
- return refs;
938
- }
939
- function resolveMattermostModelPickerCurrentModel(params) {
940
- const fallback = `${params.data.resolvedDefault.provider}/${params.data.resolvedDefault.model}`;
941
- try {
942
- const storePath = resolveStorePath(params.cfg.session?.store, { agentId: params.route.agentId });
943
- const sessionStore = params.skipCache ? loadSessionStore(storePath, { skipCache: true }) : loadSessionStore(storePath);
944
- const sessionEntry = sessionStore[params.route.sessionKey];
945
- const override = resolveStoredModelOverride({
946
- sessionEntry,
947
- sessionStore,
948
- sessionKey: params.route.sessionKey
949
- });
950
- if (!override?.model) return fallback;
951
- const provider = (override.provider || params.data.resolvedDefault.provider).trim();
952
- return provider ? `${provider}/${override.model}` : fallback;
953
- } catch {
954
- return fallback;
955
- }
956
- }
957
- function renderMattermostModelSummaryView(params) {
958
- return {
959
- text: [
960
- formatCurrentModelLine(params.currentModel),
961
- "",
962
- "Tap below to browse models, or use:",
963
- "/oc_model <provider/model> to switch",
964
- "/oc_model status for details"
965
- ].join("\n"),
966
- buttons: [[buildButton({
967
- action: "providers",
968
- ownerUserId: params.ownerUserId,
969
- text: "Browse providers",
970
- style: "primary"
971
- })]]
972
- };
973
- }
974
- function renderMattermostProviderPickerView(params) {
975
- const currentProvider = splitModelRef(params.currentModel)?.provider;
976
- const rows = params.data.providers.map((provider) => [buildButton({
977
- action: "list",
978
- ownerUserId: params.ownerUserId,
979
- text: `${provider} (${params.data.byProvider.get(provider)?.size ?? 0})`,
980
- provider,
981
- page: 1,
982
- style: provider === currentProvider ? "primary" : "default"
983
- })]);
984
- return {
985
- text: [
986
- formatCurrentModelLine(params.currentModel),
987
- "",
988
- "Select a provider:"
989
- ].join("\n"),
990
- buttons: rows
991
- };
992
- }
993
- function renderMattermostModelsPickerView(params) {
994
- const provider = normalizeProviderId(params.provider);
995
- const models = getProviderModels(params.data, provider);
996
- const current = splitModelRef(params.currentModel);
997
- if (models.length === 0) return {
998
- text: [
999
- formatCurrentModelLine(params.currentModel),
1000
- "",
1001
- `Unknown provider: ${provider}`
1002
- ].join("\n"),
1003
- buttons: [[buildButton({
1004
- action: "back",
1005
- ownerUserId: params.ownerUserId,
1006
- text: "Back to providers"
1007
- })]]
1008
- };
1009
- const page = paginateItems(models, params.page);
1010
- const rows = page.items.map((model) => {
1011
- const isCurrent = current?.provider === provider && current.model === model;
1012
- return [buildButton({
1013
- action: "select",
1014
- ownerUserId: params.ownerUserId,
1015
- text: isCurrent ? `${model} [current]` : model,
1016
- provider,
1017
- model,
1018
- page: page.page,
1019
- style: isCurrent ? "primary" : "default"
1020
- })];
1021
- });
1022
- const navRow = [];
1023
- if (page.hasPrev) navRow.push(buildButton({
1024
- action: "list",
1025
- ownerUserId: params.ownerUserId,
1026
- text: "Prev",
1027
- provider,
1028
- page: page.page - 1
1029
- }));
1030
- if (page.hasNext) navRow.push(buildButton({
1031
- action: "list",
1032
- ownerUserId: params.ownerUserId,
1033
- text: "Next",
1034
- provider,
1035
- page: page.page + 1
1036
- }));
1037
- if (navRow.length > 0) rows.push(navRow);
1038
- rows.push([buildButton({
1039
- action: "back",
1040
- ownerUserId: params.ownerUserId,
1041
- text: "Back to providers"
1042
- })]);
1043
- return {
1044
- text: [
1045
- `Models (${provider}) - ${page.totalItems} available`,
1046
- formatCurrentModelLine(params.currentModel),
1047
- `Page ${page.page}/${page.totalPages}`,
1048
- "Select a model to switch immediately."
1049
- ].join("\n"),
1050
- buttons: rows
1051
- };
1052
- }
1053
- //#endregion
1054
- //#region extensions/mattermost/src/mattermost/monitor-auth.ts
1055
- function normalizeMattermostAllowEntry(entry) {
1056
- const trimmed = entry.trim();
1057
- if (!trimmed) return "";
1058
- if (trimmed === "*") return "*";
1059
- return trimmed.replace(/^(mattermost|user):/i, "").replace(/^@/, "").toLowerCase();
1060
- }
1061
- function normalizeMattermostAllowList(entries) {
1062
- const normalized = entries.map((entry) => normalizeMattermostAllowEntry(String(entry))).filter(Boolean);
1063
- return Array.from(new Set(normalized));
1064
- }
1065
- function resolveMattermostEffectiveAllowFromLists(params) {
1066
- return resolveEffectiveAllowFromLists({
1067
- allowFrom: normalizeMattermostAllowList(params.allowFrom ?? []),
1068
- groupAllowFrom: normalizeMattermostAllowList(params.groupAllowFrom ?? []),
1069
- storeAllowFrom: normalizeMattermostAllowList(params.storeAllowFrom ?? []),
1070
- dmPolicy: params.dmPolicy
1071
- });
1072
- }
1073
- function isMattermostSenderAllowed(params) {
1074
- const allowFrom = normalizeMattermostAllowList(params.allowFrom);
1075
- if (allowFrom.length === 0) return false;
1076
- return resolveAllowlistMatchSimple({
1077
- allowFrom,
1078
- senderId: normalizeMattermostAllowEntry(params.senderId),
1079
- senderName: params.senderName ? normalizeMattermostAllowEntry(params.senderName) : void 0,
1080
- allowNameMatching: params.allowNameMatching
1081
- }).allowed;
1082
- }
1083
- function mapMattermostChannelKind(channelType) {
1084
- const normalized = channelType?.trim().toUpperCase();
1085
- if (normalized === "D") return "direct";
1086
- if (normalized === "G" || normalized === "P") return "group";
1087
- return "channel";
1088
- }
1089
- function authorizeMattermostCommandInvocation(params) {
1090
- const { account, cfg, senderId, senderName, channelId, channelInfo, storeAllowFrom, allowTextCommands, hasControlCommand } = params;
1091
- if (!channelInfo) return {
1092
- ok: false,
1093
- denyReason: "unknown-channel",
1094
- commandAuthorized: false,
1095
- channelInfo: null,
1096
- kind: "channel",
1097
- chatType: "channel",
1098
- channelName: "",
1099
- channelDisplay: "",
1100
- roomLabel: `#${channelId}`
1101
- };
1102
- const kind = mapMattermostChannelKind(channelInfo.type);
1103
- const chatType = kind;
1104
- const channelName = channelInfo.name ?? "";
1105
- const channelDisplay = channelInfo.display_name ?? channelName;
1106
- const roomLabel = channelName ? `#${channelName}` : channelDisplay || `#${channelId}`;
1107
- const dmPolicy = account.config.dmPolicy ?? "pairing";
1108
- const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy;
1109
- const groupPolicy = account.config.groupPolicy ?? defaultGroupPolicy ?? "allowlist";
1110
- const allowNameMatching = isDangerousNameMatchingEnabled(account.config);
1111
- const configAllowFrom = normalizeMattermostAllowList(account.config.allowFrom ?? []);
1112
- const configGroupAllowFrom = normalizeMattermostAllowList(account.config.groupAllowFrom ?? []);
1113
- const { effectiveAllowFrom, effectiveGroupAllowFrom } = resolveMattermostEffectiveAllowFromLists({
1114
- allowFrom: configAllowFrom,
1115
- groupAllowFrom: configGroupAllowFrom,
1116
- storeAllowFrom: normalizeMattermostAllowList(storeAllowFrom ?? []),
1117
- dmPolicy
1118
- });
1119
- const useAccessGroups = cfg.commands?.useAccessGroups !== false;
1120
- const commandDmAllowFrom = kind === "direct" ? effectiveAllowFrom : configAllowFrom;
1121
- const commandGroupAllowFrom = kind === "direct" ? effectiveGroupAllowFrom : configGroupAllowFrom.length > 0 ? configGroupAllowFrom : configAllowFrom;
1122
- const senderAllowedForCommands = isMattermostSenderAllowed({
1123
- senderId,
1124
- senderName,
1125
- allowFrom: commandDmAllowFrom,
1126
- allowNameMatching
1127
- });
1128
- const groupAllowedForCommands = isMattermostSenderAllowed({
1129
- senderId,
1130
- senderName,
1131
- allowFrom: commandGroupAllowFrom,
1132
- allowNameMatching
1133
- });
1134
- const commandGate = resolveControlCommandGate({
1135
- useAccessGroups,
1136
- authorizers: [{
1137
- configured: commandDmAllowFrom.length > 0,
1138
- allowed: senderAllowedForCommands
1139
- }, {
1140
- configured: commandGroupAllowFrom.length > 0,
1141
- allowed: groupAllowedForCommands
1142
- }],
1143
- allowTextCommands,
1144
- hasControlCommand: allowTextCommands && hasControlCommand
1145
- });
1146
- const commandAuthorized = kind === "direct" ? dmPolicy === "open" || senderAllowedForCommands : commandGate.commandAuthorized;
1147
- if (kind === "direct") {
1148
- if (dmPolicy === "disabled") return {
1149
- ok: false,
1150
- denyReason: "dm-disabled",
1151
- commandAuthorized: false,
1152
- channelInfo,
1153
- kind,
1154
- chatType,
1155
- channelName,
1156
- channelDisplay,
1157
- roomLabel
1158
- };
1159
- if (dmPolicy !== "open" && !senderAllowedForCommands) return {
1160
- ok: false,
1161
- denyReason: dmPolicy === "pairing" ? "dm-pairing" : "unauthorized",
1162
- commandAuthorized: false,
1163
- channelInfo,
1164
- kind,
1165
- chatType,
1166
- channelName,
1167
- channelDisplay,
1168
- roomLabel
1169
- };
1170
- } else {
1171
- const senderGroupAccess = evaluateSenderGroupAccessForPolicy({
1172
- groupPolicy,
1173
- groupAllowFrom: effectiveGroupAllowFrom,
1174
- senderId,
1175
- isSenderAllowed: (_senderId, allowFrom) => isMattermostSenderAllowed({
1176
- senderId,
1177
- senderName,
1178
- allowFrom,
1179
- allowNameMatching
1180
- })
1181
- });
1182
- if (!senderGroupAccess.allowed && senderGroupAccess.reason === "disabled") return {
1183
- ok: false,
1184
- denyReason: "channels-disabled",
1185
- commandAuthorized: false,
1186
- channelInfo,
1187
- kind,
1188
- chatType,
1189
- channelName,
1190
- channelDisplay,
1191
- roomLabel
1192
- };
1193
- if (!senderGroupAccess.allowed && senderGroupAccess.reason === "empty_allowlist") return {
1194
- ok: false,
1195
- denyReason: "channel-no-allowlist",
1196
- commandAuthorized: false,
1197
- channelInfo,
1198
- kind,
1199
- chatType,
1200
- channelName,
1201
- channelDisplay,
1202
- roomLabel
1203
- };
1204
- if (!senderGroupAccess.allowed && senderGroupAccess.reason === "sender_not_allowlisted") return {
1205
- ok: false,
1206
- denyReason: "unauthorized",
1207
- commandAuthorized: false,
1208
- channelInfo,
1209
- kind,
1210
- chatType,
1211
- channelName,
1212
- channelDisplay,
1213
- roomLabel
1214
- };
1215
- if (commandGate.shouldBlock) return {
1216
- ok: false,
1217
- denyReason: "unauthorized",
1218
- commandAuthorized: false,
1219
- channelInfo,
1220
- kind,
1221
- chatType,
1222
- channelName,
1223
- channelDisplay,
1224
- roomLabel
1225
- };
1226
- }
1227
- return {
1228
- ok: true,
1229
- commandAuthorized,
1230
- channelInfo,
1231
- kind,
1232
- chatType,
1233
- channelName,
1234
- channelDisplay,
1235
- roomLabel
1236
- };
1237
- }
1238
- //#endregion
1239
- //#region extensions/mattermost/src/mattermost/monitor-helpers.ts
1240
- const formatInboundFromLabel = formatInboundFromLabel$1;
1241
- function resolveThreadSessionKeys(params) {
1242
- return resolveThreadSessionKeys$1({
1243
- ...params,
1244
- normalizeThreadId: (threadId) => threadId
1245
- });
1246
- }
1247
- /**
1248
- * Strip bot mention from message text while preserving newlines and
1249
- * block-level Markdown formatting (headings, lists, blockquotes).
1250
- */
1251
- function normalizeMention(text, mention) {
1252
- if (!mention) return text.trim();
1253
- const escaped = mention.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1254
- const hasMentionRe = new RegExp(`@${escaped}\\b`, "i");
1255
- const leadingMentionRe = new RegExp(`^([\\t ]*)@${escaped}\\b[\\t ]*`, "i");
1256
- const trailingMentionRe = new RegExp(`[\\t ]*@${escaped}\\b[\\t ]*$`, "i");
1257
- const normalizedLines = text.split("\n").map((line) => {
1258
- const hadMention = hasMentionRe.test(line);
1259
- const normalizedLine = line.replace(leadingMentionRe, "$1").replace(trailingMentionRe, "").replace(new RegExp(`@${escaped}\\b`, "gi"), "").replace(/(\S)[ \t]{2,}/g, "$1 ");
1260
- return {
1261
- text: normalizedLine,
1262
- mentionOnlyBlank: hadMention && normalizedLine.trim() === ""
1263
- };
1264
- });
1265
- while (normalizedLines[0]?.mentionOnlyBlank) normalizedLines.shift();
1266
- while (normalizedLines.at(-1)?.text.trim() === "") normalizedLines.pop();
1267
- return normalizedLines.map((line) => line.text).join("\n");
1268
- }
1269
- //#endregion
1270
- //#region extensions/mattermost/src/mattermost/monitor-onchar.ts
1271
- const DEFAULT_ONCHAR_PREFIXES = [">", "!"];
1272
- function resolveOncharPrefixes(prefixes) {
1273
- const cleaned = prefixes?.map((entry) => entry.trim()).filter(Boolean) ?? DEFAULT_ONCHAR_PREFIXES;
1274
- return cleaned.length > 0 ? cleaned : DEFAULT_ONCHAR_PREFIXES;
1275
- }
1276
- function stripOncharPrefix(text, prefixes) {
1277
- const trimmed = text.trimStart();
1278
- for (const prefix of prefixes) {
1279
- if (!prefix) continue;
1280
- if (trimmed.startsWith(prefix)) return {
1281
- triggered: true,
1282
- stripped: trimmed.slice(prefix.length).trimStart()
1283
- };
1284
- }
1285
- return {
1286
- triggered: false,
1287
- stripped: text
1288
- };
1289
- }
1290
- //#endregion
1291
- //#region extensions/mattermost/src/mattermost/monitor-websocket.ts
1292
- var WebSocketClosedBeforeOpenError = class extends Error {
1293
- constructor(code, reason) {
1294
- super(`websocket closed before open (code ${code})`);
1295
- this.code = code;
1296
- this.reason = reason;
1297
- this.name = "WebSocketClosedBeforeOpenError";
1298
- }
1299
- };
1300
- const defaultMattermostWebSocketFactory = (url) => new WebSocket$1(url);
1301
- function parsePostedPayload(payload) {
1302
- if (payload.event !== "posted") return null;
1303
- const postData = payload.data?.post;
1304
- if (!postData) return null;
1305
- let post = null;
1306
- if (typeof postData === "string") try {
1307
- post = JSON.parse(postData);
1308
- } catch {
1309
- return null;
1310
- }
1311
- else if (typeof postData === "object") post = postData;
1312
- if (!post) return null;
1313
- return {
1314
- payload,
1315
- post
1316
- };
1317
- }
1318
- function createMattermostConnectOnce(opts) {
1319
- const webSocketFactory = opts.webSocketFactory ?? defaultMattermostWebSocketFactory;
1320
- return async () => {
1321
- const ws = webSocketFactory(opts.wsUrl);
1322
- const onAbort = () => ws.terminate();
1323
- opts.abortSignal?.addEventListener("abort", onAbort, { once: true });
1324
- try {
1325
- return await new Promise((resolve, reject) => {
1326
- let opened = false;
1327
- let settled = false;
1328
- const resolveOnce = () => {
1329
- if (settled) return;
1330
- settled = true;
1331
- resolve();
1332
- };
1333
- const rejectOnce = (error) => {
1334
- if (settled) return;
1335
- settled = true;
1336
- reject(error);
1337
- };
1338
- ws.on("open", () => {
1339
- opened = true;
1340
- opts.statusSink?.({
1341
- connected: true,
1342
- lastConnectedAt: Date.now(),
1343
- lastError: null
1344
- });
1345
- ws.send(JSON.stringify({
1346
- seq: opts.nextSeq(),
1347
- action: "authentication_challenge",
1348
- data: { token: opts.botToken }
1349
- }));
1350
- });
1351
- ws.on("message", async (data) => {
1352
- const raw = rawDataToString(data);
1353
- let payload;
1354
- try {
1355
- payload = JSON.parse(raw);
1356
- } catch {
1357
- return;
1358
- }
1359
- if (payload.event === "reaction_added" || payload.event === "reaction_removed") {
1360
- if (!opts.onReaction) return;
1361
- try {
1362
- await opts.onReaction(payload);
1363
- } catch (err) {
1364
- opts.runtime.error?.(`mattermost reaction handler failed: ${String(err)}`);
1365
- }
1366
- return;
1367
- }
1368
- if (payload.event !== "posted") return;
1369
- const parsed = parsePostedPayload(payload);
1370
- if (!parsed) return;
1371
- try {
1372
- await opts.onPosted(parsed.post, parsed.payload);
1373
- } catch (err) {
1374
- opts.runtime.error?.(`mattermost handler failed: ${String(err)}`);
1375
- }
1376
- });
1377
- ws.on("close", (code, reason) => {
1378
- const message = reasonToString(reason);
1379
- opts.statusSink?.({
1380
- connected: false,
1381
- lastDisconnect: {
1382
- at: Date.now(),
1383
- status: code,
1384
- error: message || void 0
1385
- }
1386
- });
1387
- if (opened) {
1388
- resolveOnce();
1389
- return;
1390
- }
1391
- rejectOnce(new WebSocketClosedBeforeOpenError(code, message || void 0));
1392
- });
1393
- ws.on("error", (err) => {
1394
- opts.runtime.error?.(`mattermost websocket error: ${String(err)}`);
1395
- opts.statusSink?.({ lastError: String(err) });
1396
- try {
1397
- ws.close();
1398
- } catch {}
1399
- });
1400
- });
1401
- } finally {
1402
- opts.abortSignal?.removeEventListener("abort", onAbort);
1403
- }
1404
- };
1405
- }
1406
- function reasonToString(reason) {
1407
- if (!reason) return "";
1408
- if (typeof reason === "string") return reason;
1409
- return reason.length > 0 ? reason.toString("utf8") : "";
1410
- }
1411
- //#endregion
1412
- //#region extensions/mattermost/src/mattermost/reconnect.ts
1413
- /**
1414
- * Reconnection loop with exponential backoff.
1415
- *
1416
- * Calls `connectFn` in a while loop. On normal resolve (connection closed),
1417
- * the backoff resets. On thrown error (connection failed), the current delay is
1418
- * used, then doubled for the next retry.
1419
- * The loop exits when `abortSignal` fires.
1420
- */
1421
- async function runWithReconnect(connectFn, opts = {}) {
1422
- const { initialDelayMs = 2e3, maxDelayMs = 6e4 } = opts;
1423
- const jitterRatio = Math.max(0, opts.jitterRatio ?? 0);
1424
- const random = opts.random ?? Math.random;
1425
- let retryDelay = initialDelayMs;
1426
- let attempt = 0;
1427
- while (!opts.abortSignal?.aborted) {
1428
- let shouldIncreaseDelay = false;
1429
- let outcome = "resolved";
1430
- let error;
1431
- try {
1432
- await connectFn();
1433
- retryDelay = initialDelayMs;
1434
- } catch (err) {
1435
- if (opts.abortSignal?.aborted) return;
1436
- outcome = "rejected";
1437
- error = err;
1438
- opts.onError?.(err);
1439
- shouldIncreaseDelay = true;
1440
- }
1441
- if (opts.abortSignal?.aborted) return;
1442
- const delayMs = withJitter(retryDelay, jitterRatio, random);
1443
- if (!(opts.shouldReconnect?.({
1444
- attempt,
1445
- delayMs,
1446
- outcome,
1447
- error
1448
- }) ?? true)) return;
1449
- opts.onReconnect?.(delayMs);
1450
- await sleepAbortable(delayMs, opts.abortSignal);
1451
- if (shouldIncreaseDelay) retryDelay = Math.min(retryDelay * 2, maxDelayMs);
1452
- attempt++;
1453
- }
1454
- }
1455
- function withJitter(baseMs, jitterRatio, random) {
1456
- if (jitterRatio <= 0) return baseMs;
1457
- const normalized = Math.max(0, Math.min(1, random()));
1458
- const spread = baseMs * jitterRatio;
1459
- return Math.max(1, Math.round(baseMs - spread + normalized * spread * 2));
1460
- }
1461
- function sleepAbortable(ms, signal) {
1462
- return new Promise((resolve) => {
1463
- if (signal?.aborted) {
1464
- resolve();
1465
- return;
1466
- }
1467
- const onAbort = () => {
1468
- clearTimeout(timer);
1469
- resolve();
1470
- };
1471
- const timer = setTimeout(() => {
1472
- signal?.removeEventListener("abort", onAbort);
1473
- resolve();
1474
- }, ms);
1475
- signal?.addEventListener("abort", onAbort, { once: true });
1476
- });
1477
- }
1478
- //#endregion
1479
- //#region extensions/mattermost/src/mattermost/reply-delivery.ts
1480
- async function deliverMattermostReplyPayload(params) {
1481
- const mediaUrls = params.payload.mediaUrls ?? (params.payload.mediaUrl ? [params.payload.mediaUrl] : []);
1482
- const text = params.core.channel.text.convertMarkdownTables(params.payload.text ?? "", params.tableMode);
1483
- if (mediaUrls.length === 0) {
1484
- const chunkMode = params.core.channel.text.resolveChunkMode(params.cfg, "mattermost", params.accountId);
1485
- const chunks = params.core.channel.text.chunkMarkdownTextWithMode(text, params.textLimit, chunkMode);
1486
- for (const chunk of chunks.length > 0 ? chunks : [text]) {
1487
- if (!chunk) continue;
1488
- await params.sendMessage(params.to, chunk, {
1489
- accountId: params.accountId,
1490
- replyToId: params.replyToId
1491
- });
1492
- }
1493
- return;
1494
- }
1495
- const mediaLocalRoots = getAgentScopedMediaLocalRoots(params.cfg, params.agentId);
1496
- let first = true;
1497
- for (const mediaUrl of mediaUrls) {
1498
- const caption = first ? text : "";
1499
- first = false;
1500
- await params.sendMessage(params.to, caption, {
1501
- accountId: params.accountId,
1502
- mediaUrl,
1503
- mediaLocalRoots,
1504
- replyToId: params.replyToId
1505
- });
1506
- }
1507
- }
1508
- //#endregion
1509
- //#region extensions/mattermost/src/mattermost/target-resolution.ts
1510
- const mattermostOpaqueTargetCache = /* @__PURE__ */ new Map();
1511
- function cacheKey$1(baseUrl, token, id) {
1512
- return `${baseUrl}::${token}::${id}`;
1513
- }
1514
- /** Mattermost IDs are 26-character lowercase alphanumeric strings. */
1515
- function isMattermostId(value) {
1516
- return /^[a-z0-9]{26}$/.test(value);
1517
- }
1518
- function isExplicitMattermostTarget(raw) {
1519
- const trimmed = raw.trim();
1520
- if (!trimmed) return false;
1521
- return /^(channel|user|mattermost):/i.test(trimmed) || trimmed.startsWith("@") || trimmed.startsWith("#");
1522
- }
1523
- function parseMattermostApiStatus(err) {
1524
- if (!err || typeof err !== "object") return;
1525
- const msg = "message" in err ? String(err.message ?? "") : "";
1526
- const match = /Mattermost API (\d{3})\b/.exec(msg);
1527
- if (!match) return;
1528
- const code = Number(match[1]);
1529
- return Number.isFinite(code) ? code : void 0;
1530
- }
1531
- async function resolveMattermostOpaqueTarget(params) {
1532
- const input = params.input.trim();
1533
- if (!input || isExplicitMattermostTarget(input) || !isMattermostId(input)) return null;
1534
- const account = params.cfg && (!params.token || !params.baseUrl) ? resolveMattermostAccount({
1535
- cfg: params.cfg,
1536
- accountId: params.accountId
1537
- }) : null;
1538
- const token = params.token?.trim() || account?.botToken?.trim();
1539
- const baseUrl = normalizeMattermostBaseUrl(params.baseUrl ?? account?.baseUrl);
1540
- if (!token || !baseUrl) return null;
1541
- const key = cacheKey$1(baseUrl, token, input);
1542
- const cached = mattermostOpaqueTargetCache.get(key);
1543
- if (cached === true) return {
1544
- kind: "user",
1545
- id: input,
1546
- to: `user:${input}`
1547
- };
1548
- if (cached === false) return {
1549
- kind: "channel",
1550
- id: input,
1551
- to: `channel:${input}`
1552
- };
1553
- const client = createMattermostClient({
1554
- baseUrl,
1555
- botToken: token
1556
- });
1557
- try {
1558
- await fetchMattermostUser(client, input);
1559
- mattermostOpaqueTargetCache.set(key, true);
1560
- return {
1561
- kind: "user",
1562
- id: input,
1563
- to: `user:${input}`
1564
- };
1565
- } catch (err) {
1566
- if (parseMattermostApiStatus(err) === 404) mattermostOpaqueTargetCache.set(key, false);
1567
- return {
1568
- kind: "channel",
1569
- id: input,
1570
- to: `channel:${input}`
1571
- };
1572
- }
1573
- }
1574
- //#endregion
1575
- //#region extensions/mattermost/src/mattermost/send.ts
1576
- const botUserCache = /* @__PURE__ */ new Map();
1577
- const userByNameCache = /* @__PURE__ */ new Map();
1578
- const channelByNameCache = /* @__PURE__ */ new Map();
1579
- const dmChannelCache = /* @__PURE__ */ new Map();
1580
- const getCore = () => getMattermostRuntime();
1581
- function cacheKey(baseUrl, token) {
1582
- return `${baseUrl}::${token}`;
1583
- }
1584
- function normalizeMessage(text, mediaUrl) {
1585
- return [text.trim(), mediaUrl?.trim()].filter(Boolean).join("\n");
1586
- }
1587
- function isHttpUrl(value) {
1588
- return /^https?:\/\//i.test(value);
1589
- }
1590
- function parseMattermostTarget(raw) {
1591
- const trimmed = raw.trim();
1592
- if (!trimmed) throw new Error("Recipient is required for Mattermost sends");
1593
- const lower = trimmed.toLowerCase();
1594
- if (lower.startsWith("channel:")) {
1595
- const id = trimmed.slice(8).trim();
1596
- if (!id) throw new Error("Channel id is required for Mattermost sends");
1597
- if (id.startsWith("#")) {
1598
- const name = id.slice(1).trim();
1599
- if (!name) throw new Error("Channel name is required for Mattermost sends");
1600
- return {
1601
- kind: "channel-name",
1602
- name
1603
- };
1604
- }
1605
- if (!isMattermostId(id)) return {
1606
- kind: "channel-name",
1607
- name: id
1608
- };
1609
- return {
1610
- kind: "channel",
1611
- id
1612
- };
1613
- }
1614
- if (lower.startsWith("user:")) {
1615
- const id = trimmed.slice(5).trim();
1616
- if (!id) throw new Error("User id is required for Mattermost sends");
1617
- return {
1618
- kind: "user",
1619
- id
1620
- };
1621
- }
1622
- if (lower.startsWith("mattermost:")) {
1623
- const id = trimmed.slice(11).trim();
1624
- if (!id) throw new Error("User id is required for Mattermost sends");
1625
- return {
1626
- kind: "user",
1627
- id
1628
- };
1629
- }
1630
- if (trimmed.startsWith("@")) {
1631
- const username = trimmed.slice(1).trim();
1632
- if (!username) throw new Error("Username is required for Mattermost sends");
1633
- return {
1634
- kind: "user",
1635
- username
1636
- };
1637
- }
1638
- if (trimmed.startsWith("#")) {
1639
- const name = trimmed.slice(1).trim();
1640
- if (!name) throw new Error("Channel name is required for Mattermost sends");
1641
- return {
1642
- kind: "channel-name",
1643
- name
1644
- };
1645
- }
1646
- if (!isMattermostId(trimmed)) return {
1647
- kind: "channel-name",
1648
- name: trimmed
1649
- };
1650
- return {
1651
- kind: "channel",
1652
- id: trimmed
1653
- };
1654
- }
1655
- async function resolveBotUser(baseUrl, token) {
1656
- const key = cacheKey(baseUrl, token);
1657
- const cached = botUserCache.get(key);
1658
- if (cached) return cached;
1659
- const user = await fetchMattermostMe(createMattermostClient({
1660
- baseUrl,
1661
- botToken: token
1662
- }));
1663
- botUserCache.set(key, user);
1664
- return user;
1665
- }
1666
- async function resolveUserIdByUsername(params) {
1667
- const { baseUrl, token, username } = params;
1668
- const key = `${cacheKey(baseUrl, token)}::${username.toLowerCase()}`;
1669
- const cached = userByNameCache.get(key);
1670
- if (cached?.id) return cached.id;
1671
- const user = await fetchMattermostUserByUsername(createMattermostClient({
1672
- baseUrl,
1673
- botToken: token
1674
- }), username);
1675
- userByNameCache.set(key, user);
1676
- return user.id;
1677
- }
1678
- async function resolveChannelIdByName(params) {
1679
- const { baseUrl, token, name } = params;
1680
- const key = `${cacheKey(baseUrl, token)}::channel::${name.toLowerCase()}`;
1681
- const cached = channelByNameCache.get(key);
1682
- if (cached) return cached;
1683
- const client = createMattermostClient({
1684
- baseUrl,
1685
- botToken: token
1686
- });
1687
- const teams = await fetchMattermostUserTeams(client, (await fetchMattermostMe(client)).id);
1688
- for (const team of teams) try {
1689
- const channel = await fetchMattermostChannelByName(client, team.id, name);
1690
- if (channel?.id) {
1691
- channelByNameCache.set(key, channel.id);
1692
- return channel.id;
1693
- }
1694
- } catch {}
1695
- throw new Error(`Mattermost channel "#${name}" not found in any team the bot belongs to`);
1696
- }
1697
- async function resolveTargetChannelId(params) {
1698
- if (params.target.kind === "channel") return params.target.id;
1699
- if (params.target.kind === "channel-name") return await resolveChannelIdByName({
1700
- baseUrl: params.baseUrl,
1701
- token: params.token,
1702
- name: params.target.name
1703
- });
1704
- const userId = params.target.id ? params.target.id : await resolveUserIdByUsername({
1705
- baseUrl: params.baseUrl,
1706
- token: params.token,
1707
- username: params.target.username ?? ""
1708
- });
1709
- const dmKey = `${cacheKey(params.baseUrl, params.token)}::dm::${userId}`;
1710
- const cachedDm = dmChannelCache.get(dmKey);
1711
- if (cachedDm) return cachedDm;
1712
- const botUser = await resolveBotUser(params.baseUrl, params.token);
1713
- const channel = await createMattermostDirectChannel(createMattermostClient({
1714
- baseUrl: params.baseUrl,
1715
- botToken: params.token
1716
- }), [botUser.id, userId]);
1717
- dmChannelCache.set(dmKey, channel.id);
1718
- return channel.id;
1719
- }
1720
- async function resolveMattermostSendContext(to, opts = {}) {
1721
- const core = getCore();
1722
- const cfg = opts.cfg ?? core.config.loadConfig();
1723
- const account = resolveMattermostAccount({
1724
- cfg,
1725
- accountId: opts.accountId
1726
- });
1727
- const token = opts.botToken?.trim() || account.botToken?.trim();
1728
- if (!token) throw new Error(`Mattermost bot token missing for account "${account.accountId}" (set channels.mattermost.accounts.${account.accountId}.botToken or MATTERMOST_BOT_TOKEN for default).`);
1729
- const baseUrl = normalizeMattermostBaseUrl(opts.baseUrl ?? account.baseUrl);
1730
- if (!baseUrl) throw new Error(`Mattermost baseUrl missing for account "${account.accountId}" (set channels.mattermost.accounts.${account.accountId}.baseUrl or MATTERMOST_URL for default).`);
1731
- const trimmedTo = to?.trim() ?? "";
1732
- const opaqueTarget = await resolveMattermostOpaqueTarget({
1733
- input: trimmedTo,
1734
- token,
1735
- baseUrl
1736
- });
1737
- const channelId = await resolveTargetChannelId({
1738
- target: opaqueTarget?.kind === "user" ? {
1739
- kind: "user",
1740
- id: opaqueTarget.id
1741
- } : opaqueTarget?.kind === "channel" ? {
1742
- kind: "channel",
1743
- id: opaqueTarget.id
1744
- } : parseMattermostTarget(trimmedTo),
1745
- baseUrl,
1746
- token
1747
- });
1748
- return {
1749
- cfg,
1750
- accountId: account.accountId,
1751
- token,
1752
- baseUrl,
1753
- channelId
1754
- };
1755
- }
1756
- async function sendMessageMattermost(to, text, opts = {}) {
1757
- const core = getCore();
1758
- const logger = core.logging.getChildLogger({ module: "mattermost" });
1759
- const { cfg, accountId, token, baseUrl, channelId } = await resolveMattermostSendContext(to, opts);
1760
- const client = createMattermostClient({
1761
- baseUrl,
1762
- botToken: token
1763
- });
1764
- let props = opts.props;
1765
- if (!props && Array.isArray(opts.buttons) && opts.buttons.length > 0) {
1766
- setInteractionSecret(accountId, token);
1767
- props = buildButtonProps({
1768
- callbackUrl: resolveInteractionCallbackUrl(accountId, {
1769
- gateway: cfg.gateway,
1770
- interactions: resolveMattermostAccount({
1771
- cfg,
1772
- accountId
1773
- }).config?.interactions
1774
- }),
1775
- accountId,
1776
- channelId,
1777
- buttons: opts.buttons,
1778
- text: opts.attachmentText
1779
- });
1780
- }
1781
- let message = text?.trim() ?? "";
1782
- let fileIds;
1783
- let uploadError;
1784
- const mediaUrl = opts.mediaUrl?.trim();
1785
- if (mediaUrl) try {
1786
- const media = await loadOutboundMediaFromUrl(mediaUrl, { mediaLocalRoots: opts.mediaLocalRoots });
1787
- fileIds = [(await uploadMattermostFile(client, {
1788
- channelId,
1789
- buffer: media.buffer,
1790
- fileName: media.fileName ?? "upload",
1791
- contentType: media.contentType ?? void 0
1792
- })).id];
1793
- } catch (err) {
1794
- uploadError = err instanceof Error ? err : new Error(String(err));
1795
- if (core.logging.shouldLogVerbose()) logger.debug?.(`mattermost send: media upload failed, falling back to URL text: ${String(err)}`);
1796
- message = normalizeMessage(message, isHttpUrl(mediaUrl) ? mediaUrl : "");
1797
- }
1798
- if (message) {
1799
- const tableMode = core.channel.text.resolveMarkdownTableMode({
1800
- cfg,
1801
- channel: "mattermost",
1802
- accountId
1803
- });
1804
- message = core.channel.text.convertMarkdownTables(message, tableMode);
1805
- }
1806
- if (!message && (!fileIds || fileIds.length === 0)) {
1807
- if (uploadError) throw new Error(`Mattermost media upload failed: ${uploadError.message}`);
1808
- throw new Error("Mattermost message is empty");
1809
- }
1810
- const post = await createMattermostPost(client, {
1811
- channelId,
1812
- message,
1813
- rootId: opts.replyToId,
1814
- fileIds,
1815
- props
1816
- });
1817
- core.channel.activity.record({
1818
- channel: "mattermost",
1819
- accountId,
1820
- direction: "outbound"
1821
- });
1822
- return {
1823
- messageId: post.id ?? "unknown",
1824
- channelId
1825
- };
1826
- }
1827
- //#endregion
1828
- //#region extensions/mattermost/src/mattermost/slash-commands.ts
1829
- /**
1830
- * Built-in MoldClaw commands to register as native slash commands.
1831
- * These mirror the text-based commands already handled by the gateway.
1832
- */
1833
- const DEFAULT_COMMAND_SPECS = [
1834
- {
1835
- trigger: "oc_status",
1836
- originalName: "status",
1837
- description: "Show session status (model, usage, uptime)",
1838
- autoComplete: true
1839
- },
1840
- {
1841
- trigger: "oc_model",
1842
- originalName: "model",
1843
- description: "View or change the current model",
1844
- autoComplete: true,
1845
- autoCompleteHint: "[model-name]"
1846
- },
1847
- {
1848
- trigger: "oc_models",
1849
- originalName: "models",
1850
- description: "Browse available models",
1851
- autoComplete: true,
1852
- autoCompleteHint: "[provider]"
1853
- },
1854
- {
1855
- trigger: "oc_new",
1856
- originalName: "new",
1857
- description: "Start a new conversation session",
1858
- autoComplete: true
1859
- },
1860
- {
1861
- trigger: "oc_help",
1862
- originalName: "help",
1863
- description: "Show available commands",
1864
- autoComplete: true
1865
- },
1866
- {
1867
- trigger: "oc_think",
1868
- originalName: "think",
1869
- description: "Set thinking/reasoning level",
1870
- autoComplete: true,
1871
- autoCompleteHint: "[off|low|medium|high]"
1872
- },
1873
- {
1874
- trigger: "oc_reasoning",
1875
- originalName: "reasoning",
1876
- description: "Toggle reasoning mode",
1877
- autoComplete: true,
1878
- autoCompleteHint: "[on|off]"
1879
- },
1880
- {
1881
- trigger: "oc_verbose",
1882
- originalName: "verbose",
1883
- description: "Toggle verbose mode",
1884
- autoComplete: true,
1885
- autoCompleteHint: "[on|off]"
1886
- }
1887
- ];
1888
- /**
1889
- * List existing custom slash commands for a team.
1890
- */
1891
- async function listMattermostCommands(client, teamId) {
1892
- return await client.request(`/commands?team_id=${encodeURIComponent(teamId)}&custom_only=true`);
1893
- }
1894
- /**
1895
- * Create a custom slash command on a Mattermost team.
1896
- */
1897
- async function createMattermostCommand(client, params) {
1898
- return await client.request("/commands", {
1899
- method: "POST",
1900
- body: JSON.stringify(params)
1901
- });
1902
- }
1903
- /**
1904
- * Delete a custom slash command.
1905
- */
1906
- async function deleteMattermostCommand(client, commandId) {
1907
- await client.request(`/commands/${encodeURIComponent(commandId)}`, { method: "DELETE" });
1908
- }
1909
- /**
1910
- * Update an existing custom slash command.
1911
- */
1912
- async function updateMattermostCommand(client, params) {
1913
- return await client.request(`/commands/${encodeURIComponent(params.id)}`, {
1914
- method: "PUT",
1915
- body: JSON.stringify(params)
1916
- });
1917
- }
1918
- /**
1919
- * Register all MoldClaw slash commands for a given team.
1920
- * Skips commands that are already registered with the same trigger + callback URL.
1921
- * Returns the list of newly created command IDs.
1922
- */
1923
- async function registerSlashCommands(params) {
1924
- const { client, teamId, creatorUserId, callbackUrl, commands, log } = params;
1925
- const normalizedCreatorUserId = creatorUserId.trim();
1926
- if (!normalizedCreatorUserId) throw new Error("creatorUserId is required for slash command reconciliation");
1927
- let existing = [];
1928
- try {
1929
- existing = await listMattermostCommands(client, teamId);
1930
- } catch (err) {
1931
- log?.(`mattermost: failed to list existing commands: ${String(err)}`);
1932
- throw err;
1933
- }
1934
- const existingByTrigger = /* @__PURE__ */ new Map();
1935
- for (const cmd of existing) {
1936
- const list = existingByTrigger.get(cmd.trigger) ?? [];
1937
- list.push(cmd);
1938
- existingByTrigger.set(cmd.trigger, list);
1939
- }
1940
- const registered = [];
1941
- for (const spec of commands) {
1942
- const existingForTrigger = existingByTrigger.get(spec.trigger) ?? [];
1943
- const ownedCommands = existingForTrigger.filter((cmd) => cmd.creator_id?.trim() === normalizedCreatorUserId);
1944
- const foreignCommands = existingForTrigger.filter((cmd) => cmd.creator_id?.trim() !== normalizedCreatorUserId);
1945
- if (ownedCommands.length === 0 && foreignCommands.length > 0) {
1946
- log?.(`mattermost: trigger /${spec.trigger} already used by non-MoldClaw command(s); skipping to avoid mutating external integrations`);
1947
- continue;
1948
- }
1949
- if (ownedCommands.length > 1) log?.(`mattermost: multiple owned commands found for /${spec.trigger}; using the first and leaving extras untouched`);
1950
- const existingCmd = ownedCommands[0];
1951
- if (existingCmd && existingCmd.url === callbackUrl) {
1952
- log?.(`mattermost: command /${spec.trigger} already registered (id=${existingCmd.id})`);
1953
- registered.push({
1954
- id: existingCmd.id,
1955
- trigger: spec.trigger,
1956
- teamId,
1957
- token: existingCmd.token,
1958
- managed: false
1959
- });
1960
- continue;
1961
- }
1962
- if (existingCmd && existingCmd.url !== callbackUrl) {
1963
- log?.(`mattermost: command /${spec.trigger} exists with different callback URL; updating (id=${existingCmd.id})`);
1964
- try {
1965
- const updated = await updateMattermostCommand(client, {
1966
- id: existingCmd.id,
1967
- team_id: teamId,
1968
- trigger: spec.trigger,
1969
- method: "P",
1970
- url: callbackUrl,
1971
- description: spec.description,
1972
- auto_complete: spec.autoComplete,
1973
- auto_complete_desc: spec.description,
1974
- auto_complete_hint: spec.autoCompleteHint
1975
- });
1976
- registered.push({
1977
- id: updated.id,
1978
- trigger: spec.trigger,
1979
- teamId,
1980
- token: updated.token,
1981
- managed: false
1982
- });
1983
- continue;
1984
- } catch (err) {
1985
- log?.(`mattermost: failed to update command /${spec.trigger} (id=${existingCmd.id}): ${String(err)}`);
1986
- try {
1987
- await deleteMattermostCommand(client, existingCmd.id);
1988
- log?.(`mattermost: deleted stale command /${spec.trigger} (id=${existingCmd.id})`);
1989
- } catch (deleteErr) {
1990
- log?.(`mattermost: failed to delete stale command /${spec.trigger} (id=${existingCmd.id}): ${String(deleteErr)}`);
1991
- continue;
1992
- }
1993
- }
1994
- }
1995
- try {
1996
- const created = await createMattermostCommand(client, {
1997
- team_id: teamId,
1998
- trigger: spec.trigger,
1999
- method: "P",
2000
- url: callbackUrl,
2001
- description: spec.description,
2002
- auto_complete: spec.autoComplete,
2003
- auto_complete_desc: spec.description,
2004
- auto_complete_hint: spec.autoCompleteHint
2005
- });
2006
- log?.(`mattermost: registered command /${spec.trigger} (id=${created.id})`);
2007
- registered.push({
2008
- id: created.id,
2009
- trigger: spec.trigger,
2010
- teamId,
2011
- token: created.token,
2012
- managed: true
2013
- });
2014
- } catch (err) {
2015
- log?.(`mattermost: failed to register command /${spec.trigger}: ${String(err)}`);
2016
- }
2017
- }
2018
- return registered;
2019
- }
2020
- /**
2021
- * Clean up all registered slash commands.
2022
- */
2023
- async function cleanupSlashCommands(params) {
2024
- const { client, commands, log } = params;
2025
- for (const cmd of commands) {
2026
- if (!cmd.managed) continue;
2027
- try {
2028
- await deleteMattermostCommand(client, cmd.id);
2029
- log?.(`mattermost: deleted command /${cmd.trigger} (id=${cmd.id})`);
2030
- } catch (err) {
2031
- log?.(`mattermost: failed to delete command /${cmd.trigger}: ${String(err)}`);
2032
- }
2033
- }
2034
- }
2035
- /**
2036
- * Parse a Mattermost slash command callback payload from a URL-encoded or JSON body.
2037
- */
2038
- function parseSlashCommandPayload(body, contentType) {
2039
- if (!body) return null;
2040
- try {
2041
- if (contentType?.includes("application/json")) {
2042
- const parsed = JSON.parse(body);
2043
- const token = typeof parsed.token === "string" ? parsed.token : "";
2044
- const teamId = typeof parsed.team_id === "string" ? parsed.team_id : "";
2045
- const channelId = typeof parsed.channel_id === "string" ? parsed.channel_id : "";
2046
- const userId = typeof parsed.user_id === "string" ? parsed.user_id : "";
2047
- const command = typeof parsed.command === "string" ? parsed.command : "";
2048
- if (!token || !teamId || !channelId || !userId || !command) return null;
2049
- return {
2050
- token,
2051
- team_id: teamId,
2052
- team_domain: typeof parsed.team_domain === "string" ? parsed.team_domain : void 0,
2053
- channel_id: channelId,
2054
- channel_name: typeof parsed.channel_name === "string" ? parsed.channel_name : void 0,
2055
- user_id: userId,
2056
- user_name: typeof parsed.user_name === "string" ? parsed.user_name : void 0,
2057
- command,
2058
- text: typeof parsed.text === "string" ? parsed.text : "",
2059
- trigger_id: typeof parsed.trigger_id === "string" ? parsed.trigger_id : void 0,
2060
- response_url: typeof parsed.response_url === "string" ? parsed.response_url : void 0
2061
- };
2062
- }
2063
- const params = new URLSearchParams(body);
2064
- const token = params.get("token");
2065
- const teamId = params.get("team_id");
2066
- const channelId = params.get("channel_id");
2067
- const userId = params.get("user_id");
2068
- const command = params.get("command");
2069
- if (!token || !teamId || !channelId || !userId || !command) return null;
2070
- return {
2071
- token,
2072
- team_id: teamId,
2073
- team_domain: params.get("team_domain") ?? void 0,
2074
- channel_id: channelId,
2075
- channel_name: params.get("channel_name") ?? void 0,
2076
- user_id: userId,
2077
- user_name: params.get("user_name") ?? void 0,
2078
- command,
2079
- text: params.get("text") ?? "",
2080
- trigger_id: params.get("trigger_id") ?? void 0,
2081
- response_url: params.get("response_url") ?? void 0
2082
- };
2083
- } catch {
2084
- return null;
2085
- }
2086
- }
2087
- /**
2088
- * Map the trigger word back to the original MoldClaw command name.
2089
- * e.g. "oc_status" -> "/status", "oc_model" -> "/model"
2090
- */
2091
- function resolveCommandText(trigger, text, triggerMap) {
2092
- const commandName = triggerMap?.get(trigger) ?? (trigger.startsWith("oc_") ? trigger.slice(3) : trigger);
2093
- const args = text.trim();
2094
- return args ? `/${commandName} ${args}` : `/${commandName}`;
2095
- }
2096
- const DEFAULT_CALLBACK_PATH = "/api/channels/mattermost/command";
2097
- /**
2098
- * Ensure the callback path starts with a leading `/` to prevent
2099
- * malformed URLs like `http://host:portapi/...`.
2100
- */
2101
- function normalizeCallbackPath(path) {
2102
- const trimmed = path.trim();
2103
- if (!trimmed) return DEFAULT_CALLBACK_PATH;
2104
- return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
2105
- }
2106
- function resolveSlashCommandConfig(raw) {
2107
- return {
2108
- native: raw?.native ?? "auto",
2109
- nativeSkills: raw?.nativeSkills ?? "auto",
2110
- callbackPath: normalizeCallbackPath(raw?.callbackPath ?? DEFAULT_CALLBACK_PATH),
2111
- callbackUrl: raw?.callbackUrl?.trim() || void 0
2112
- };
2113
- }
2114
- function isSlashCommandsEnabled(config) {
2115
- if (config.native === true) return true;
2116
- if (config.native === false) return false;
2117
- return false;
2118
- }
2119
- /**
2120
- * Build the callback URL that Mattermost will POST to when a command is invoked.
2121
- */
2122
- function resolveCallbackUrl(params) {
2123
- if (params.config.callbackUrl) return params.config.callbackUrl;
2124
- const isWildcardBindHost = (rawHost) => {
2125
- const trimmed = rawHost.trim();
2126
- if (!trimmed) return false;
2127
- const host = trimmed.startsWith("[") && trimmed.endsWith("]") ? trimmed.slice(1, -1) : trimmed;
2128
- return host === "0.0.0.0" || host === "::" || host === "0:0:0:0:0:0:0:0" || host === "::0";
2129
- };
2130
- let host = params.gatewayHost && !isWildcardBindHost(params.gatewayHost) ? params.gatewayHost : "localhost";
2131
- const path = normalizeCallbackPath(params.config.callbackPath);
2132
- if (host.includes(":") && !(host.startsWith("[") && host.endsWith("]"))) host = `[${host}]`;
2133
- return `http://${host}:${params.gatewayPort}${path}`;
2134
- }
2135
- //#endregion
2136
- //#region extensions/mattermost/src/mattermost/slash-http.ts
2137
- const MAX_BODY_BYTES = 64 * 1024;
2138
- const BODY_READ_TIMEOUT_MS = 5e3;
2139
- /**
2140
- * Read the full request body as a string.
2141
- */
2142
- function readBody(req, maxBytes) {
2143
- return readRequestBodyWithLimit(req, {
2144
- maxBytes,
2145
- timeoutMs: BODY_READ_TIMEOUT_MS
2146
- });
2147
- }
2148
- function sendJsonResponse(res, status, body) {
2149
- res.statusCode = status;
2150
- res.setHeader("Content-Type", "application/json; charset=utf-8");
2151
- res.end(JSON.stringify(body));
2152
- }
2153
- async function authorizeSlashInvocation(params) {
2154
- const { account, cfg, client, commandText, channelId, senderId, senderName, log } = params;
2155
- const core = getMattermostRuntime();
2156
- let channelInfo = null;
2157
- try {
2158
- channelInfo = await fetchMattermostChannel(client, channelId);
2159
- } catch (err) {
2160
- log?.(`mattermost: slash channel lookup failed for ${channelId}: ${String(err)}`);
2161
- }
2162
- if (!channelInfo) return {
2163
- ok: false,
2164
- denyResponse: {
2165
- response_type: "ephemeral",
2166
- text: "Temporary error: unable to determine channel type. Please try again."
2167
- },
2168
- commandAuthorized: false,
2169
- channelInfo: null,
2170
- kind: "channel",
2171
- chatType: "channel",
2172
- channelName: "",
2173
- channelDisplay: "",
2174
- roomLabel: `#${channelId}`
2175
- };
2176
- const allowTextCommands = core.channel.commands.shouldHandleTextCommands({
2177
- cfg,
2178
- surface: "mattermost"
2179
- });
2180
- const hasControlCommand = core.channel.text.hasControlCommand(commandText, cfg);
2181
- const storeAllowFrom = normalizeMattermostAllowList(await core.channel.pairing.readAllowFromStore({
2182
- channel: "mattermost",
2183
- accountId: account.accountId
2184
- }).catch(() => []));
2185
- const decision = authorizeMattermostCommandInvocation({
2186
- account,
2187
- cfg,
2188
- senderId,
2189
- senderName,
2190
- channelId,
2191
- channelInfo,
2192
- storeAllowFrom,
2193
- allowTextCommands,
2194
- hasControlCommand
2195
- });
2196
- if (!decision.ok) {
2197
- if (decision.denyReason === "dm-pairing") {
2198
- const { code } = await core.channel.pairing.upsertPairingRequest({
2199
- channel: "mattermost",
2200
- accountId: account.accountId,
2201
- id: senderId,
2202
- meta: { name: senderName }
2203
- });
2204
- return {
2205
- ...decision,
2206
- denyResponse: {
2207
- response_type: "ephemeral",
2208
- text: core.channel.pairing.buildPairingReply({
2209
- channel: "mattermost",
2210
- idLine: `Your Mattermost user id: ${senderId}`,
2211
- code
2212
- })
2213
- }
2214
- };
2215
- }
2216
- const denyText = decision.denyReason === "unknown-channel" ? "Temporary error: unable to determine channel type. Please try again." : decision.denyReason === "dm-disabled" ? "This bot is not accepting direct messages." : decision.denyReason === "channels-disabled" ? "Slash commands are disabled in channels." : decision.denyReason === "channel-no-allowlist" ? "Slash commands are not configured for this channel (no allowlist)." : "Unauthorized.";
2217
- return {
2218
- ...decision,
2219
- denyResponse: {
2220
- response_type: "ephemeral",
2221
- text: denyText
2222
- }
2223
- };
2224
- }
2225
- return {
2226
- ...decision,
2227
- denyResponse: void 0
2228
- };
2229
- }
2230
- /**
2231
- * Create the HTTP request handler for Mattermost slash command callbacks.
2232
- *
2233
- * This handler is registered as a plugin HTTP route and receives POSTs
2234
- * from the Mattermost server when a user invokes a registered slash command.
2235
- */
2236
- function createSlashCommandHttpHandler(params) {
2237
- const { account, cfg, runtime, commandTokens, triggerMap, log } = params;
2238
- return async (req, res) => {
2239
- if (req.method !== "POST") {
2240
- res.statusCode = 405;
2241
- res.setHeader("Allow", "POST");
2242
- res.end("Method Not Allowed");
2243
- return;
2244
- }
2245
- let body;
2246
- try {
2247
- body = await readBody(req, MAX_BODY_BYTES);
2248
- } catch (error) {
2249
- if (isRequestBodyLimitError(error, "REQUEST_BODY_TIMEOUT")) {
2250
- res.statusCode = 408;
2251
- res.end("Request body timeout");
2252
- return;
2253
- }
2254
- res.statusCode = 413;
2255
- res.end("Payload Too Large");
2256
- return;
2257
- }
2258
- const contentType = req.headers["content-type"] ?? "";
2259
- const payload = parseSlashCommandPayload(body, contentType);
2260
- if (!payload) {
2261
- sendJsonResponse(res, 400, {
2262
- response_type: "ephemeral",
2263
- text: "Invalid slash command payload."
2264
- });
2265
- return;
2266
- }
2267
- if (commandTokens.size === 0 || !commandTokens.has(payload.token)) {
2268
- sendJsonResponse(res, 401, {
2269
- response_type: "ephemeral",
2270
- text: "Unauthorized: invalid command token."
2271
- });
2272
- return;
2273
- }
2274
- const trigger = payload.command.replace(/^\//, "").trim();
2275
- const commandText = resolveCommandText(trigger, payload.text, triggerMap);
2276
- const channelId = payload.channel_id;
2277
- const senderId = payload.user_id;
2278
- const senderName = payload.user_name ?? senderId;
2279
- const client = createMattermostClient({
2280
- baseUrl: account.baseUrl ?? "",
2281
- botToken: account.botToken ?? ""
2282
- });
2283
- const auth = await authorizeSlashInvocation({
2284
- account,
2285
- cfg,
2286
- client,
2287
- commandText,
2288
- channelId,
2289
- senderId,
2290
- senderName,
2291
- log
2292
- });
2293
- if (!auth.ok) {
2294
- sendJsonResponse(res, 200, auth.denyResponse ?? {
2295
- response_type: "ephemeral",
2296
- text: "Unauthorized."
2297
- });
2298
- return;
2299
- }
2300
- log?.(`mattermost: slash command /${trigger} from ${senderName} in ${channelId}`);
2301
- sendJsonResponse(res, 200, {
2302
- response_type: "ephemeral",
2303
- text: "Processing..."
2304
- });
2305
- try {
2306
- await handleSlashCommandAsync({
2307
- account,
2308
- cfg,
2309
- runtime,
2310
- client,
2311
- commandText,
2312
- channelId,
2313
- senderId,
2314
- senderName,
2315
- teamId: payload.team_id,
2316
- triggerId: payload.trigger_id,
2317
- kind: auth.kind,
2318
- chatType: auth.chatType,
2319
- channelName: auth.channelName,
2320
- channelDisplay: auth.channelDisplay,
2321
- roomLabel: auth.roomLabel,
2322
- commandAuthorized: auth.commandAuthorized,
2323
- log
2324
- });
2325
- } catch (err) {
2326
- log?.(`mattermost: slash command handler error: ${String(err)}`);
2327
- try {
2328
- await sendMessageMattermost(`channel:${channelId}`, "Sorry, something went wrong processing that command.", { accountId: account.accountId });
2329
- } catch {}
2330
- }
2331
- };
2332
- }
2333
- async function handleSlashCommandAsync(params) {
2334
- const { account, cfg, runtime, client, commandText, channelId, senderId, senderName, teamId, kind, chatType, channelName, channelDisplay, roomLabel, commandAuthorized, triggerId, log } = params;
2335
- const core = getMattermostRuntime();
2336
- const route = core.channel.routing.resolveAgentRoute({
2337
- cfg,
2338
- channel: "mattermost",
2339
- accountId: account.accountId,
2340
- teamId,
2341
- peer: {
2342
- kind,
2343
- id: kind === "direct" ? senderId : channelId
2344
- }
2345
- });
2346
- const fromLabel = kind === "direct" ? `Mattermost DM from ${senderName}` : `Mattermost message in ${roomLabel} from ${senderName}`;
2347
- const to = kind === "direct" ? `user:${senderId}` : `channel:${channelId}`;
2348
- const pickerEntry = resolveMattermostModelPickerEntry(commandText);
2349
- if (pickerEntry) {
2350
- const data = await buildModelsProviderData(cfg, route.agentId);
2351
- if (data.providers.length === 0) {
2352
- await sendMessageMattermost(to, "No models available.", { accountId: account.accountId });
2353
- return;
2354
- }
2355
- const currentModel = resolveMattermostModelPickerCurrentModel({
2356
- cfg,
2357
- route,
2358
- data
2359
- });
2360
- const view = pickerEntry.kind === "summary" ? renderMattermostModelSummaryView({
2361
- ownerUserId: senderId,
2362
- currentModel
2363
- }) : pickerEntry.kind === "providers" ? renderMattermostProviderPickerView({
2364
- ownerUserId: senderId,
2365
- data,
2366
- currentModel
2367
- }) : renderMattermostModelsPickerView({
2368
- ownerUserId: senderId,
2369
- data,
2370
- provider: pickerEntry.provider,
2371
- page: 1,
2372
- currentModel
2373
- });
2374
- await sendMessageMattermost(to, view.text, {
2375
- accountId: account.accountId,
2376
- buttons: view.buttons
2377
- });
2378
- runtime.log?.(`delivered model picker to ${to}`);
2379
- return;
2380
- }
2381
- const ctxPayload = core.channel.reply.finalizeInboundContext({
2382
- Body: commandText,
2383
- BodyForAgent: commandText,
2384
- RawBody: commandText,
2385
- CommandBody: commandText,
2386
- From: kind === "direct" ? `mattermost:${senderId}` : kind === "group" ? `mattermost:group:${channelId}` : `mattermost:channel:${channelId}`,
2387
- To: to,
2388
- SessionKey: route.sessionKey,
2389
- AccountId: route.accountId,
2390
- ChatType: chatType,
2391
- ConversationLabel: fromLabel,
2392
- GroupSubject: kind !== "direct" ? channelDisplay || roomLabel : void 0,
2393
- SenderName: senderName,
2394
- SenderId: senderId,
2395
- Provider: "mattermost",
2396
- Surface: "mattermost",
2397
- MessageSid: triggerId ?? `slash-${Date.now()}`,
2398
- Timestamp: Date.now(),
2399
- WasMentioned: true,
2400
- CommandAuthorized: commandAuthorized,
2401
- CommandSource: "native",
2402
- OriginatingChannel: "mattermost",
2403
- OriginatingTo: to
2404
- });
2405
- const textLimit = core.channel.text.resolveTextChunkLimit(cfg, "mattermost", account.accountId, { fallbackLimit: account.textChunkLimit ?? 4e3 });
2406
- const tableMode = core.channel.text.resolveMarkdownTableMode({
2407
- cfg,
2408
- channel: "mattermost",
2409
- accountId: account.accountId
2410
- });
2411
- const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
2412
- cfg,
2413
- agentId: route.agentId,
2414
- channel: "mattermost",
2415
- accountId: account.accountId
2416
- });
2417
- const humanDelay = core.channel.reply.resolveHumanDelayConfig(cfg, route.agentId);
2418
- const typingCallbacks = createTypingCallbacks({
2419
- start: () => sendMattermostTyping(client, { channelId }),
2420
- onStartError: (err) => {
2421
- logTypingFailure({
2422
- log: (message) => log?.(message),
2423
- channel: "mattermost",
2424
- target: channelId,
2425
- error: err
2426
- });
2427
- }
2428
- });
2429
- const { dispatcher, replyOptions, markDispatchIdle } = core.channel.reply.createReplyDispatcherWithTyping({
2430
- ...prefixOptions,
2431
- humanDelay,
2432
- deliver: async (payload) => {
2433
- await deliverMattermostReplyPayload({
2434
- core,
2435
- cfg,
2436
- payload,
2437
- to,
2438
- accountId: account.accountId,
2439
- agentId: route.agentId,
2440
- textLimit,
2441
- tableMode,
2442
- sendMessage: sendMessageMattermost
2443
- });
2444
- runtime.log?.(`delivered slash reply to ${to}`);
2445
- },
2446
- onError: (err, info) => {
2447
- runtime.error?.(`mattermost slash ${info.kind} reply failed: ${String(err)}`);
2448
- },
2449
- onReplyStart: typingCallbacks.onReplyStart
2450
- });
2451
- await core.channel.reply.withReplyDispatcher({
2452
- dispatcher,
2453
- onSettled: () => {
2454
- markDispatchIdle();
2455
- },
2456
- run: () => core.channel.reply.dispatchReplyFromConfig({
2457
- ctx: ctxPayload,
2458
- cfg,
2459
- dispatcher,
2460
- replyOptions: {
2461
- ...replyOptions,
2462
- disableBlockStreaming: typeof account.blockStreaming === "boolean" ? !account.blockStreaming : void 0,
2463
- onModelSelected
2464
- }
2465
- })
2466
- });
2467
- }
2468
- //#endregion
2469
- //#region extensions/mattermost/src/mattermost/slash-state.ts
2470
- /** Map from accountId → per-account slash command state. */
2471
- const accountStates = /* @__PURE__ */ new Map();
2472
- function resolveSlashHandlerForToken(token) {
2473
- const matches = [];
2474
- for (const [accountId, state] of accountStates) if (state.commandTokens.has(token) && state.handler) matches.push({
2475
- accountId,
2476
- handler: state.handler
2477
- });
2478
- if (matches.length === 0) return { kind: "none" };
2479
- if (matches.length === 1) return {
2480
- kind: "single",
2481
- handler: matches[0].handler,
2482
- accountIds: [matches[0].accountId]
2483
- };
2484
- return {
2485
- kind: "ambiguous",
2486
- accountIds: matches.map((entry) => entry.accountId)
2487
- };
2488
- }
2489
- /**
2490
- * Get the slash command state for a specific account, or null if not activated.
2491
- */
2492
- function getSlashCommandState(accountId) {
2493
- return accountStates.get(accountId) ?? null;
2494
- }
2495
- /**
2496
- * Activate slash commands for a specific account.
2497
- * Called from the monitor after bot connects.
2498
- */
2499
- function activateSlashCommands(params) {
2500
- const { account, commandTokens, registeredCommands, triggerMap, api, log } = params;
2501
- const accountId = account.accountId;
2502
- const tokenSet = new Set(commandTokens);
2503
- const handler = createSlashCommandHttpHandler({
2504
- account,
2505
- cfg: api.cfg,
2506
- runtime: api.runtime,
2507
- commandTokens: tokenSet,
2508
- triggerMap,
2509
- log
2510
- });
2511
- accountStates.set(accountId, {
2512
- commandTokens: tokenSet,
2513
- registeredCommands,
2514
- handler,
2515
- account,
2516
- triggerMap: triggerMap ?? /* @__PURE__ */ new Map()
2517
- });
2518
- log?.(`mattermost: slash commands activated for account ${accountId} (${registeredCommands.length} commands)`);
2519
- }
2520
- /**
2521
- * Deactivate slash commands for a specific account (on shutdown/disconnect).
2522
- */
2523
- function deactivateSlashCommands(accountId) {
2524
- if (accountId) {
2525
- const state = accountStates.get(accountId);
2526
- if (state) {
2527
- state.commandTokens.clear();
2528
- state.registeredCommands = [];
2529
- state.handler = null;
2530
- accountStates.delete(accountId);
2531
- }
2532
- } else {
2533
- for (const [, state] of accountStates) {
2534
- state.commandTokens.clear();
2535
- state.registeredCommands = [];
2536
- state.handler = null;
2537
- }
2538
- accountStates.clear();
2539
- }
2540
- }
2541
- /**
2542
- * Register the HTTP route for slash command callbacks.
2543
- * Called during plugin registration.
2544
- *
2545
- * The single HTTP route dispatches to the correct per-account handler
2546
- * by matching the inbound token against each account's registered tokens.
2547
- */
2548
- function registerSlashCommandRoute(api) {
2549
- const mmConfig = api.config.channels?.mattermost;
2550
- const callbackPaths = /* @__PURE__ */ new Set();
2551
- const addCallbackPaths = (raw) => {
2552
- const resolved = resolveSlashCommandConfig(raw);
2553
- callbackPaths.add(resolved.callbackPath);
2554
- if (resolved.callbackUrl) try {
2555
- const urlPath = new URL(resolved.callbackUrl).pathname;
2556
- if (urlPath && urlPath !== resolved.callbackPath) callbackPaths.add(urlPath);
2557
- } catch {}
2558
- };
2559
- const commandsRaw = mmConfig?.commands;
2560
- addCallbackPaths(commandsRaw);
2561
- const accountsRaw = mmConfig?.accounts ?? {};
2562
- for (const accountId of Object.keys(accountsRaw)) {
2563
- const accountCommandsRaw = accountsRaw[accountId]?.commands;
2564
- addCallbackPaths(accountCommandsRaw);
2565
- }
2566
- const routeHandler = async (req, res) => {
2567
- if (accountStates.size === 0) {
2568
- res.statusCode = 503;
2569
- res.setHeader("Content-Type", "application/json; charset=utf-8");
2570
- res.end(JSON.stringify({
2571
- response_type: "ephemeral",
2572
- text: "Slash commands are not yet initialized. Please try again in a moment."
2573
- }));
2574
- return;
2575
- }
2576
- if (accountStates.size === 1) {
2577
- const [, state] = [...accountStates.entries()][0];
2578
- if (!state.handler) {
2579
- res.statusCode = 503;
2580
- res.setHeader("Content-Type", "application/json; charset=utf-8");
2581
- res.end(JSON.stringify({
2582
- response_type: "ephemeral",
2583
- text: "Slash commands are not yet initialized. Please try again in a moment."
2584
- }));
2585
- return;
2586
- }
2587
- await state.handler(req, res);
2588
- return;
2589
- }
2590
- const chunks = [];
2591
- const MAX_BODY = 64 * 1024;
2592
- let size = 0;
2593
- for await (const chunk of req) {
2594
- size += chunk.length;
2595
- if (size > MAX_BODY) {
2596
- res.statusCode = 413;
2597
- res.end("Payload Too Large");
2598
- return;
2599
- }
2600
- chunks.push(chunk);
2601
- }
2602
- const bodyStr = Buffer.concat(chunks).toString("utf8");
2603
- let token = null;
2604
- const ct = req.headers["content-type"] ?? "";
2605
- try {
2606
- if (ct.includes("application/json")) token = JSON.parse(bodyStr).token ?? null;
2607
- else token = new URLSearchParams(bodyStr).get("token");
2608
- } catch {}
2609
- const match = token ? resolveSlashHandlerForToken(token) : { kind: "none" };
2610
- if (match.kind === "none") {
2611
- res.statusCode = 401;
2612
- res.setHeader("Content-Type", "application/json; charset=utf-8");
2613
- res.end(JSON.stringify({
2614
- response_type: "ephemeral",
2615
- text: "Unauthorized: invalid command token."
2616
- }));
2617
- return;
2618
- }
2619
- if (match.kind === "ambiguous") {
2620
- api.logger.warn?.(`mattermost: slash callback token matched multiple accounts (${match.accountIds?.join(", ")})`);
2621
- res.statusCode = 409;
2622
- res.setHeader("Content-Type", "application/json; charset=utf-8");
2623
- res.end(JSON.stringify({
2624
- response_type: "ephemeral",
2625
- text: "Conflict: command token is not unique across accounts."
2626
- }));
2627
- return;
2628
- }
2629
- const matchedHandler = match.handler;
2630
- const { Readable } = await import("node:stream");
2631
- const syntheticReq = new Readable({ read() {
2632
- this.push(Buffer.from(bodyStr, "utf8"));
2633
- this.push(null);
2634
- } });
2635
- syntheticReq.method = req.method;
2636
- syntheticReq.url = req.url;
2637
- syntheticReq.headers = req.headers;
2638
- await matchedHandler(syntheticReq, res);
2639
- };
2640
- for (const callbackPath of callbackPaths) {
2641
- api.registerHttpRoute({
2642
- path: callbackPath,
2643
- auth: "plugin",
2644
- handler: routeHandler
2645
- });
2646
- api.logger.info?.(`mattermost: registered slash command callback at ${callbackPath}`);
2647
- }
2648
- }
2649
- //#endregion
2650
- //#region extensions/mattermost/src/mattermost/monitor.ts
2651
- const RECENT_MATTERMOST_MESSAGE_TTL_MS = 5 * 6e4;
2652
- const RECENT_MATTERMOST_MESSAGE_MAX = 2e3;
2653
- const CHANNEL_CACHE_TTL_MS = 5 * 6e4;
2654
- const USER_CACHE_TTL_MS = 10 * 6e4;
2655
- function isLoopbackHost(hostname) {
2656
- return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1";
2657
- }
2658
- function normalizeInteractionSourceIps(values) {
2659
- return (values ?? []).map((value) => value.trim()).filter(Boolean);
2660
- }
2661
- const recentInboundMessages = createDedupeCache({
2662
- ttlMs: RECENT_MATTERMOST_MESSAGE_TTL_MS,
2663
- maxSize: RECENT_MATTERMOST_MESSAGE_MAX
2664
- });
2665
- function resolveRuntime(opts) {
2666
- return opts.runtime ?? {
2667
- log: console.log,
2668
- error: console.error,
2669
- exit: (code) => {
2670
- throw new Error(`exit ${code}`);
2671
- }
2672
- };
2673
- }
2674
- function isSystemPost(post) {
2675
- const type = post.type?.trim();
2676
- return Boolean(type);
2677
- }
2678
- function mapMattermostChannelTypeToChatType(channelType) {
2679
- if (!channelType) return "channel";
2680
- const normalized = channelType.trim().toUpperCase();
2681
- if (normalized === "D") return "direct";
2682
- if (normalized === "G") return "group";
2683
- if (normalized === "P") return "group";
2684
- return "channel";
2685
- }
2686
- function channelChatType(kind) {
2687
- if (kind === "direct") return "direct";
2688
- if (kind === "group") return "group";
2689
- return "channel";
2690
- }
2691
- function evaluateMattermostMentionGate(params) {
2692
- const shouldRequireMention = params.kind !== "direct" && params.resolveRequireMention({
2693
- cfg: params.cfg,
2694
- channel: "mattermost",
2695
- accountId: params.accountId,
2696
- groupId: params.channelId,
2697
- requireMentionOverride: params.requireMentionOverride
2698
- });
2699
- const shouldBypassMention = params.isControlCommand && shouldRequireMention && !params.wasMentioned && params.commandAuthorized;
2700
- const effectiveWasMentioned = params.wasMentioned || shouldBypassMention || params.oncharTriggered;
2701
- if (params.oncharEnabled && !params.oncharTriggered && !params.wasMentioned && !params.isControlCommand) return {
2702
- shouldRequireMention,
2703
- shouldBypassMention,
2704
- effectiveWasMentioned,
2705
- dropReason: "onchar-not-triggered"
2706
- };
2707
- if (params.kind !== "direct" && shouldRequireMention && params.canDetectMention && !effectiveWasMentioned) return {
2708
- shouldRequireMention,
2709
- shouldBypassMention,
2710
- effectiveWasMentioned,
2711
- dropReason: "missing-mention"
2712
- };
2713
- return {
2714
- shouldRequireMention,
2715
- shouldBypassMention,
2716
- effectiveWasMentioned,
2717
- dropReason: null
2718
- };
2719
- }
2720
- function resolveMattermostReplyRootId(params) {
2721
- const threadRootId = params.threadRootId?.trim();
2722
- if (threadRootId) return threadRootId;
2723
- return params.replyToId?.trim() || void 0;
2724
- }
2725
- function resolveMattermostEffectiveReplyToId(params) {
2726
- const threadRootId = params.threadRootId?.trim();
2727
- if (threadRootId) return threadRootId;
2728
- if (params.kind === "direct") return;
2729
- const postId = params.postId?.trim();
2730
- if (!postId) return;
2731
- return params.replyToMode === "all" || params.replyToMode === "first" ? postId : void 0;
2732
- }
2733
- function resolveMattermostThreadSessionContext(params) {
2734
- const effectiveReplyToId = resolveMattermostEffectiveReplyToId({
2735
- kind: params.kind,
2736
- postId: params.postId,
2737
- replyToMode: params.replyToMode,
2738
- threadRootId: params.threadRootId
2739
- });
2740
- const threadKeys = resolveThreadSessionKeys({
2741
- baseSessionKey: params.baseSessionKey,
2742
- threadId: effectiveReplyToId,
2743
- parentSessionKey: effectiveReplyToId ? params.baseSessionKey : void 0
2744
- });
2745
- return {
2746
- effectiveReplyToId,
2747
- sessionKey: threadKeys.sessionKey,
2748
- parentSessionKey: threadKeys.parentSessionKey
2749
- };
2750
- }
2751
- function buildMattermostAttachmentPlaceholder(mediaList) {
2752
- if (mediaList.length === 0) return "";
2753
- if (mediaList.length === 1) return `<media:${mediaList[0].kind === "unknown" ? "document" : mediaList[0].kind}>`;
2754
- const allImages = mediaList.every((media) => media.kind === "image");
2755
- const label = allImages ? "image" : "file";
2756
- const suffix = mediaList.length === 1 ? label : `${label}s`;
2757
- return `${allImages ? "<media:image>" : "<media:document>"} (${mediaList.length} ${suffix})`;
2758
- }
2759
- function buildMattermostWsUrl(baseUrl) {
2760
- const normalized = normalizeMattermostBaseUrl(baseUrl);
2761
- if (!normalized) throw new Error("Mattermost baseUrl is required");
2762
- return `${normalized.replace(/^http/i, "ws")}/api/v4/websocket`;
2763
- }
2764
- async function monitorMattermostProvider(opts = {}) {
2765
- const core = getMattermostRuntime();
2766
- const runtime = resolveRuntime(opts);
2767
- const cfg = opts.config ?? core.config.loadConfig();
2768
- const account = resolveMattermostAccount({
2769
- cfg,
2770
- accountId: opts.accountId
2771
- });
2772
- const pairing = createScopedPairingAccess({
2773
- core,
2774
- channel: "mattermost",
2775
- accountId: account.accountId
2776
- });
2777
- const allowNameMatching = isDangerousNameMatchingEnabled(account.config);
2778
- const botToken = opts.botToken?.trim() || account.botToken?.trim();
2779
- if (!botToken) throw new Error(`Mattermost bot token missing for account "${account.accountId}" (set channels.mattermost.accounts.${account.accountId}.botToken or MATTERMOST_BOT_TOKEN for default).`);
2780
- const baseUrl = normalizeMattermostBaseUrl(opts.baseUrl ?? account.baseUrl);
2781
- if (!baseUrl) throw new Error(`Mattermost baseUrl missing for account "${account.accountId}" (set channels.mattermost.accounts.${account.accountId}.baseUrl or MATTERMOST_URL for default).`);
2782
- const client = createMattermostClient({
2783
- baseUrl,
2784
- botToken
2785
- });
2786
- const botUser = await fetchMattermostMe(client);
2787
- const botUserId = botUser.id;
2788
- const botUsername = botUser.username?.trim() || void 0;
2789
- runtime.log?.(`mattermost connected as ${botUsername ? `@${botUsername}` : botUserId}`);
2790
- const commandsRaw = account.config.commands;
2791
- const slashConfig = resolveSlashCommandConfig(commandsRaw);
2792
- const slashEnabled = isSlashCommandsEnabled(slashConfig);
2793
- if (slashEnabled) try {
2794
- const teams = await fetchMattermostUserTeams(client, botUserId);
2795
- const envPortRaw = process.env.MOLDCLAW_GATEWAY_PORT?.trim();
2796
- const slashCallbackUrl = resolveCallbackUrl({
2797
- config: slashConfig,
2798
- gatewayPort: parseStrictPositiveInteger(envPortRaw) ?? cfg.gateway?.port ?? 18789,
2799
- gatewayHost: cfg.gateway?.customBindHost ?? void 0
2800
- });
2801
- try {
2802
- const mmHost = new URL(baseUrl).hostname;
2803
- const callbackHost = new URL(slashCallbackUrl).hostname;
2804
- if (isLoopbackHost(callbackHost) && !isLoopbackHost(mmHost)) runtime.error?.(`mattermost: slash commands callbackUrl resolved to ${slashCallbackUrl} (loopback) while baseUrl is ${baseUrl}. This MAY be unreachable depending on your deployment. If native slash commands don't work, set channels.mattermost.commands.callbackUrl to a URL reachable from the Mattermost server (e.g. your public reverse proxy URL).`);
2805
- } catch {}
2806
- const commandsToRegister = [...DEFAULT_COMMAND_SPECS];
2807
- if (slashConfig.nativeSkills === true) try {
2808
- const skillCommands = listSkillCommandsForAgents({ cfg });
2809
- for (const spec of skillCommands) {
2810
- const name = typeof spec.name === "string" ? spec.name.trim() : "";
2811
- if (!name) continue;
2812
- const trigger = name.startsWith("oc_") ? name : `oc_${name}`;
2813
- commandsToRegister.push({
2814
- trigger,
2815
- description: spec.description || `Run skill ${name}`,
2816
- autoComplete: true,
2817
- autoCompleteHint: "[args]",
2818
- originalName: name
2819
- });
2820
- }
2821
- } catch (err) {
2822
- runtime.error?.(`mattermost: failed to list skill commands: ${String(err)}`);
2823
- }
2824
- const seen = /* @__PURE__ */ new Set();
2825
- const dedupedCommands = commandsToRegister.filter((cmd) => {
2826
- const key = cmd.trigger.trim();
2827
- if (!key) return false;
2828
- if (seen.has(key)) return false;
2829
- seen.add(key);
2830
- return true;
2831
- });
2832
- const allRegistered = [];
2833
- let teamRegistrationFailures = 0;
2834
- for (const team of teams) try {
2835
- const registered = await registerSlashCommands({
2836
- client,
2837
- teamId: team.id,
2838
- creatorUserId: botUserId,
2839
- callbackUrl: slashCallbackUrl,
2840
- commands: dedupedCommands,
2841
- log: (msg) => runtime.log?.(msg)
2842
- });
2843
- allRegistered.push(...registered);
2844
- } catch (err) {
2845
- teamRegistrationFailures += 1;
2846
- runtime.error?.(`mattermost: failed to register slash commands for team ${team.id}: ${String(err)}`);
2847
- }
2848
- if (allRegistered.length === 0) runtime.error?.("mattermost: native slash commands enabled but no commands could be registered; keeping slash callbacks inactive");
2849
- else {
2850
- if (teamRegistrationFailures > 0) runtime.error?.(`mattermost: slash command registration completed with ${teamRegistrationFailures} team error(s)`);
2851
- const triggerMap = /* @__PURE__ */ new Map();
2852
- for (const cmd of dedupedCommands) if (cmd.originalName) triggerMap.set(cmd.trigger, cmd.originalName);
2853
- activateSlashCommands({
2854
- account,
2855
- commandTokens: allRegistered.map((cmd) => cmd.token).filter(Boolean),
2856
- registeredCommands: allRegistered,
2857
- triggerMap,
2858
- api: {
2859
- cfg,
2860
- runtime
2861
- },
2862
- log: (msg) => runtime.log?.(msg)
2863
- });
2864
- runtime.log?.(`mattermost: slash commands registered (${allRegistered.length} commands across ${teams.length} teams, callback=${slashCallbackUrl})`);
2865
- }
2866
- } catch (err) {
2867
- runtime.error?.(`mattermost: failed to register slash commands: ${String(err)}`);
2868
- }
2869
- setInteractionSecret(account.accountId, botToken);
2870
- const interactionPath = resolveInteractionCallbackPath(account.accountId);
2871
- const callbackUrl = computeInteractionCallbackUrl(account.accountId, {
2872
- gateway: cfg.gateway,
2873
- interactions: account.config.interactions
2874
- });
2875
- setInteractionCallbackUrl(account.accountId, callbackUrl);
2876
- const allowedInteractionSourceIps = normalizeInteractionSourceIps(account.config.interactions?.allowedSourceIps);
2877
- try {
2878
- const mmHost = new URL(baseUrl).hostname;
2879
- const callbackHost = new URL(callbackUrl).hostname;
2880
- if (isLoopbackHost(callbackHost) && !isLoopbackHost(mmHost)) runtime.error?.(`mattermost: interactions callbackUrl resolved to ${callbackUrl} (loopback) while baseUrl is ${baseUrl}. This MAY be unreachable depending on your deployment. If button clicks don't work, set channels.mattermost.interactions.callbackBaseUrl to a URL reachable from the Mattermost server (e.g. your public reverse proxy URL).`);
2881
- if (!isLoopbackHost(callbackHost) && allowedInteractionSourceIps.length === 0) runtime.error?.(`mattermost: interactions callbackUrl resolved to ${callbackUrl} without channels.mattermost.interactions.allowedSourceIps. For safety, non-loopback callback sources will be rejected until you allowlist the Mattermost server or trusted ingress IPs.`);
2882
- } catch {}
2883
- const effectiveInteractionSourceIps = allowedInteractionSourceIps.length > 0 ? allowedInteractionSourceIps : ["127.0.0.1", "::1"];
2884
- const unregisterInteractions = registerPluginHttpRoute({
2885
- path: interactionPath,
2886
- fallbackPath: "/mattermost/interactions/default",
2887
- auth: "plugin",
2888
- handler: createMattermostInteractionHandler({
2889
- client,
2890
- botUserId,
2891
- accountId: account.accountId,
2892
- allowedSourceIps: effectiveInteractionSourceIps,
2893
- trustedProxies: cfg.gateway?.trustedProxies,
2894
- allowRealIpFallback: cfg.gateway?.allowRealIpFallback === true,
2895
- handleInteraction: handleModelPickerInteraction,
2896
- authorizeButtonClick: async ({ payload, post }) => {
2897
- const channelInfo = await resolveChannelInfo(payload.channel_id);
2898
- const isDirect = channelInfo?.type?.trim().toUpperCase() === "D";
2899
- const allowTextCommands = core.channel.commands.shouldHandleTextCommands({
2900
- cfg,
2901
- surface: "mattermost"
2902
- });
2903
- const decision = authorizeMattermostCommandInvocation({
2904
- account,
2905
- cfg,
2906
- senderId: payload.user_id,
2907
- senderName: payload.user_name ?? "",
2908
- channelId: payload.channel_id,
2909
- channelInfo,
2910
- storeAllowFrom: isDirect ? await readStoreAllowFromForDmPolicy({
2911
- provider: "mattermost",
2912
- accountId: account.accountId,
2913
- dmPolicy: account.config.dmPolicy ?? "pairing",
2914
- readStore: pairing.readStoreForDmPolicy
2915
- }) : void 0,
2916
- allowTextCommands,
2917
- hasControlCommand: false
2918
- });
2919
- if (decision.ok) return { ok: true };
2920
- return {
2921
- ok: false,
2922
- response: {
2923
- update: {
2924
- message: post.message ?? "",
2925
- props: post.props
2926
- },
2927
- ephemeral_text: `MoldClaw ignored this action for ${decision.roomLabel}.`
2928
- }
2929
- };
2930
- },
2931
- resolveSessionKey: async ({ channelId, userId, post }) => {
2932
- const channelInfo = await resolveChannelInfo(channelId);
2933
- const kind = mapMattermostChannelTypeToChatType(channelInfo?.type);
2934
- const teamId = channelInfo?.team_id ?? void 0;
2935
- const route = core.channel.routing.resolveAgentRoute({
2936
- cfg,
2937
- channel: "mattermost",
2938
- accountId: account.accountId,
2939
- teamId,
2940
- peer: {
2941
- kind,
2942
- id: kind === "direct" ? userId : channelId
2943
- }
2944
- });
2945
- const replyToMode = resolveMattermostReplyToMode(account, kind);
2946
- return resolveMattermostThreadSessionContext({
2947
- baseSessionKey: route.sessionKey,
2948
- kind,
2949
- postId: post.id || void 0,
2950
- replyToMode,
2951
- threadRootId: post.root_id
2952
- }).sessionKey;
2953
- },
2954
- dispatchButtonClick: async (opts) => {
2955
- const channelInfo = await resolveChannelInfo(opts.channelId);
2956
- const kind = mapMattermostChannelTypeToChatType(channelInfo?.type);
2957
- const chatType = channelChatType(kind);
2958
- const teamId = channelInfo?.team_id ?? void 0;
2959
- const channelName = channelInfo?.name ?? void 0;
2960
- const channelDisplay = channelInfo?.display_name ?? channelName ?? opts.channelId;
2961
- const route = core.channel.routing.resolveAgentRoute({
2962
- cfg,
2963
- channel: "mattermost",
2964
- accountId: account.accountId,
2965
- teamId,
2966
- peer: {
2967
- kind,
2968
- id: kind === "direct" ? opts.userId : opts.channelId
2969
- }
2970
- });
2971
- const replyToMode = resolveMattermostReplyToMode(account, kind);
2972
- const threadContext = resolveMattermostThreadSessionContext({
2973
- baseSessionKey: route.sessionKey,
2974
- kind,
2975
- postId: opts.post.id || opts.postId,
2976
- replyToMode,
2977
- threadRootId: opts.post.root_id
2978
- });
2979
- const to = kind === "direct" ? `user:${opts.userId}` : `channel:${opts.channelId}`;
2980
- const bodyText = `[Button click: user @${opts.userName} selected "${opts.actionName}"]`;
2981
- const ctxPayload = core.channel.reply.finalizeInboundContext({
2982
- Body: bodyText,
2983
- BodyForAgent: bodyText,
2984
- RawBody: bodyText,
2985
- CommandBody: bodyText,
2986
- From: kind === "direct" ? `mattermost:${opts.userId}` : kind === "group" ? `mattermost:group:${opts.channelId}` : `mattermost:channel:${opts.channelId}`,
2987
- To: to,
2988
- SessionKey: threadContext.sessionKey,
2989
- ParentSessionKey: threadContext.parentSessionKey,
2990
- AccountId: route.accountId,
2991
- ChatType: chatType,
2992
- ConversationLabel: `mattermost:${opts.userName}`,
2993
- GroupSubject: kind !== "direct" ? channelDisplay : void 0,
2994
- GroupChannel: channelName ? `#${channelName}` : void 0,
2995
- GroupSpace: teamId,
2996
- SenderName: opts.userName,
2997
- SenderId: opts.userId,
2998
- Provider: "mattermost",
2999
- Surface: "mattermost",
3000
- MessageSid: `interaction:${opts.postId}:${opts.actionId}`,
3001
- ReplyToId: threadContext.effectiveReplyToId,
3002
- MessageThreadId: threadContext.effectiveReplyToId,
3003
- WasMentioned: true,
3004
- CommandAuthorized: false,
3005
- OriginatingChannel: "mattermost",
3006
- OriginatingTo: to
3007
- });
3008
- const textLimit = core.channel.text.resolveTextChunkLimit(cfg, "mattermost", account.accountId, { fallbackLimit: account.textChunkLimit ?? 4e3 });
3009
- const tableMode = core.channel.text.resolveMarkdownTableMode({
3010
- cfg,
3011
- channel: "mattermost",
3012
- accountId: account.accountId
3013
- });
3014
- const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
3015
- cfg,
3016
- agentId: route.agentId,
3017
- channel: "mattermost",
3018
- accountId: account.accountId
3019
- });
3020
- const typingCallbacks = createTypingCallbacks({
3021
- start: () => sendTypingIndicator(opts.channelId, threadContext.effectiveReplyToId),
3022
- onStartError: (err) => {
3023
- logTypingFailure({
3024
- log: (message) => logger.debug?.(message),
3025
- channel: "mattermost",
3026
- target: opts.channelId,
3027
- error: err
3028
- });
3029
- }
3030
- });
3031
- const { dispatcher, replyOptions, markDispatchIdle } = core.channel.reply.createReplyDispatcherWithTyping({
3032
- ...prefixOptions,
3033
- humanDelay: core.channel.reply.resolveHumanDelayConfig(cfg, route.agentId),
3034
- deliver: async (payload) => {
3035
- await deliverMattermostReplyPayload({
3036
- core,
3037
- cfg,
3038
- payload,
3039
- to,
3040
- accountId: account.accountId,
3041
- agentId: route.agentId,
3042
- replyToId: resolveMattermostReplyRootId({
3043
- threadRootId: threadContext.effectiveReplyToId,
3044
- replyToId: payload.replyToId
3045
- }),
3046
- textLimit,
3047
- tableMode,
3048
- sendMessage: sendMessageMattermost
3049
- });
3050
- runtime.log?.(`delivered button-click reply to ${to}`);
3051
- },
3052
- onError: (err, info) => {
3053
- runtime.error?.(`mattermost button-click ${info.kind} reply failed: ${String(err)}`);
3054
- },
3055
- onReplyStart: typingCallbacks.onReplyStart
3056
- });
3057
- await core.channel.reply.dispatchReplyFromConfig({
3058
- ctx: ctxPayload,
3059
- cfg,
3060
- dispatcher,
3061
- replyOptions: {
3062
- ...replyOptions,
3063
- disableBlockStreaming: typeof account.blockStreaming === "boolean" ? !account.blockStreaming : void 0,
3064
- onModelSelected
3065
- }
3066
- });
3067
- markDispatchIdle();
3068
- },
3069
- log: (msg) => runtime.log?.(msg)
3070
- }),
3071
- pluginId: "mattermost",
3072
- source: "mattermost-interactions",
3073
- accountId: account.accountId,
3074
- log: (msg) => runtime.log?.(msg)
3075
- });
3076
- const channelCache = /* @__PURE__ */ new Map();
3077
- const userCache = /* @__PURE__ */ new Map();
3078
- const logger = core.logging.getChildLogger({ module: "mattermost" });
3079
- const logVerboseMessage = (message) => {
3080
- if (!core.logging.shouldLogVerbose()) return;
3081
- logger.debug?.(message);
3082
- };
3083
- const mediaMaxBytes = resolveChannelMediaMaxBytes({
3084
- cfg,
3085
- resolveChannelLimitMb: () => void 0,
3086
- accountId: account.accountId
3087
- }) ?? 8 * 1024 * 1024;
3088
- const historyLimit = Math.max(0, cfg.messages?.groupChat?.historyLimit ?? 50);
3089
- const channelHistories = /* @__PURE__ */ new Map();
3090
- const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
3091
- const { groupPolicy, providerMissingFallbackApplied } = resolveAllowlistProviderRuntimeGroupPolicy({
3092
- providerConfigPresent: cfg.channels?.mattermost !== void 0,
3093
- groupPolicy: account.config.groupPolicy,
3094
- defaultGroupPolicy
3095
- });
3096
- warnMissingProviderGroupPolicyFallbackOnce({
3097
- providerMissingFallbackApplied,
3098
- providerKey: "mattermost",
3099
- accountId: account.accountId,
3100
- log: (message) => logVerboseMessage(message)
3101
- });
3102
- const resolveMattermostMedia = async (fileIds) => {
3103
- const ids = (fileIds ?? []).map((id) => id?.trim()).filter(Boolean);
3104
- if (ids.length === 0) return [];
3105
- const out = [];
3106
- for (const fileId of ids) try {
3107
- const fetched = await core.channel.media.fetchRemoteMedia({
3108
- url: `${client.apiBaseUrl}/files/${fileId}`,
3109
- requestInit: { headers: { Authorization: `Bearer ${client.token}` } },
3110
- filePathHint: fileId,
3111
- maxBytes: mediaMaxBytes,
3112
- ssrfPolicy: { allowedHostnames: [new URL(client.baseUrl).hostname] }
3113
- });
3114
- const saved = await core.channel.media.saveMediaBuffer(fetched.buffer, fetched.contentType ?? void 0, "inbound", mediaMaxBytes);
3115
- const contentType = saved.contentType ?? fetched.contentType ?? void 0;
3116
- out.push({
3117
- path: saved.path,
3118
- contentType,
3119
- kind: core.media.mediaKindFromMime(contentType) ?? "unknown"
3120
- });
3121
- } catch (err) {
3122
- logger.debug?.(`mattermost: failed to download file ${fileId}: ${String(err)}`);
3123
- }
3124
- return out;
3125
- };
3126
- const sendTypingIndicator = async (channelId, parentId) => {
3127
- await sendMattermostTyping(client, {
3128
- channelId,
3129
- parentId
3130
- });
3131
- };
3132
- const resolveChannelInfo = async (channelId) => {
3133
- const cached = channelCache.get(channelId);
3134
- if (cached && cached.expiresAt > Date.now()) return cached.value;
3135
- try {
3136
- const info = await fetchMattermostChannel(client, channelId);
3137
- channelCache.set(channelId, {
3138
- value: info,
3139
- expiresAt: Date.now() + CHANNEL_CACHE_TTL_MS
3140
- });
3141
- return info;
3142
- } catch (err) {
3143
- logger.debug?.(`mattermost: channel lookup failed: ${String(err)}`);
3144
- channelCache.set(channelId, {
3145
- value: null,
3146
- expiresAt: Date.now() + CHANNEL_CACHE_TTL_MS
3147
- });
3148
- return null;
3149
- }
3150
- };
3151
- const resolveUserInfo = async (userId) => {
3152
- const cached = userCache.get(userId);
3153
- if (cached && cached.expiresAt > Date.now()) return cached.value;
3154
- try {
3155
- const info = await fetchMattermostUser(client, userId);
3156
- userCache.set(userId, {
3157
- value: info,
3158
- expiresAt: Date.now() + USER_CACHE_TTL_MS
3159
- });
3160
- return info;
3161
- } catch (err) {
3162
- logger.debug?.(`mattermost: user lookup failed: ${String(err)}`);
3163
- userCache.set(userId, {
3164
- value: null,
3165
- expiresAt: Date.now() + USER_CACHE_TTL_MS
3166
- });
3167
- return null;
3168
- }
3169
- };
3170
- const buildModelPickerProps = (channelId, buttons) => buildButtonProps({
3171
- callbackUrl,
3172
- accountId: account.accountId,
3173
- channelId,
3174
- buttons
3175
- });
3176
- const updateModelPickerPost = async (params) => {
3177
- const props = buildModelPickerProps(params.channelId, params.buttons ?? []) ?? { attachments: [] };
3178
- await updateMattermostPost(client, params.postId, {
3179
- message: params.message,
3180
- props
3181
- });
3182
- return {};
3183
- };
3184
- const runModelPickerCommand = async (params) => {
3185
- const to = params.kind === "direct" ? `user:${params.senderId}` : `channel:${params.channelId}`;
3186
- const fromLabel = params.kind === "direct" ? `Mattermost DM from ${params.senderName}` : `Mattermost message in ${params.roomLabel} from ${params.senderName}`;
3187
- const ctxPayload = core.channel.reply.finalizeInboundContext({
3188
- Body: params.commandText,
3189
- BodyForAgent: params.commandText,
3190
- RawBody: params.commandText,
3191
- CommandBody: params.commandText,
3192
- From: params.kind === "direct" ? `mattermost:${params.senderId}` : params.kind === "group" ? `mattermost:group:${params.channelId}` : `mattermost:channel:${params.channelId}`,
3193
- To: to,
3194
- SessionKey: params.sessionKey,
3195
- ParentSessionKey: params.parentSessionKey,
3196
- AccountId: params.route.accountId,
3197
- ChatType: params.chatType,
3198
- ConversationLabel: fromLabel,
3199
- GroupSubject: params.kind !== "direct" ? params.channelDisplay || params.roomLabel : void 0,
3200
- GroupChannel: params.channelName ? `#${params.channelName}` : void 0,
3201
- GroupSpace: params.teamId,
3202
- SenderName: params.senderName,
3203
- SenderId: params.senderId,
3204
- Provider: "mattermost",
3205
- Surface: "mattermost",
3206
- MessageSid: `interaction:${params.postId}:${Date.now()}`,
3207
- ReplyToId: params.effectiveReplyToId,
3208
- MessageThreadId: params.effectiveReplyToId,
3209
- Timestamp: Date.now(),
3210
- WasMentioned: true,
3211
- CommandAuthorized: params.commandAuthorized,
3212
- CommandSource: "native",
3213
- OriginatingChannel: "mattermost",
3214
- OriginatingTo: to
3215
- });
3216
- const tableMode = core.channel.text.resolveMarkdownTableMode({
3217
- cfg,
3218
- channel: "mattermost",
3219
- accountId: account.accountId
3220
- });
3221
- const textLimit = core.channel.text.resolveTextChunkLimit(cfg, "mattermost", account.accountId, { fallbackLimit: account.textChunkLimit ?? 4e3 });
3222
- const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
3223
- cfg,
3224
- agentId: params.route.agentId,
3225
- channel: "mattermost",
3226
- accountId: account.accountId
3227
- });
3228
- const shouldDeliverReplies = params.deliverReplies === true;
3229
- const capturedTexts = [];
3230
- const typingCallbacks = shouldDeliverReplies ? createTypingCallbacks({
3231
- start: () => sendTypingIndicator(params.channelId, params.effectiveReplyToId),
3232
- onStartError: (err) => {
3233
- logTypingFailure({
3234
- log: (message) => logger.debug?.(message),
3235
- channel: "mattermost",
3236
- target: params.channelId,
3237
- error: err
3238
- });
3239
- }
3240
- }) : void 0;
3241
- const { dispatcher, replyOptions, markDispatchIdle } = core.channel.reply.createReplyDispatcherWithTyping({
3242
- ...prefixOptions,
3243
- deliver: async (payload) => {
3244
- const trimmedPayload = {
3245
- ...payload,
3246
- text: core.channel.text.convertMarkdownTables(payload.text ?? "", tableMode).trim()
3247
- };
3248
- if (!shouldDeliverReplies) {
3249
- if (trimmedPayload.text) capturedTexts.push(trimmedPayload.text);
3250
- return;
3251
- }
3252
- await deliverMattermostReplyPayload({
3253
- core,
3254
- cfg,
3255
- payload: trimmedPayload,
3256
- to,
3257
- accountId: account.accountId,
3258
- agentId: params.route.agentId,
3259
- replyToId: resolveMattermostReplyRootId({
3260
- threadRootId: params.effectiveReplyToId,
3261
- replyToId: trimmedPayload.replyToId
3262
- }),
3263
- textLimit,
3264
- tableMode: "off",
3265
- sendMessage: sendMessageMattermost
3266
- });
3267
- },
3268
- onError: (err, info) => {
3269
- runtime.error?.(`mattermost model picker ${info.kind} reply failed: ${String(err)}`);
3270
- },
3271
- onReplyStart: typingCallbacks?.onReplyStart
3272
- });
3273
- await core.channel.reply.withReplyDispatcher({
3274
- dispatcher,
3275
- onSettled: () => {
3276
- markDispatchIdle();
3277
- },
3278
- run: () => core.channel.reply.dispatchReplyFromConfig({
3279
- ctx: ctxPayload,
3280
- cfg,
3281
- dispatcher,
3282
- replyOptions: {
3283
- ...replyOptions,
3284
- disableBlockStreaming: typeof account.blockStreaming === "boolean" ? !account.blockStreaming : void 0,
3285
- onModelSelected
3286
- }
3287
- })
3288
- });
3289
- return capturedTexts.join("\n\n").trim();
3290
- };
3291
- async function handleModelPickerInteraction(params) {
3292
- const pickerState = parseMattermostModelPickerContext(params.context);
3293
- if (!pickerState) return null;
3294
- if (pickerState.ownerUserId !== params.payload.user_id) return { ephemeral_text: "Only the person who opened this picker can use it." };
3295
- const channelInfo = await resolveChannelInfo(params.payload.channel_id);
3296
- const pickerCommandText = pickerState.action === "select" ? `/model ${pickerState.provider}/${pickerState.model}` : pickerState.action === "list" ? `/models ${pickerState.provider}` : "/models";
3297
- const allowTextCommands = core.channel.commands.shouldHandleTextCommands({
3298
- cfg,
3299
- surface: "mattermost"
3300
- });
3301
- const hasControlCommand = core.channel.text.hasControlCommand(pickerCommandText, cfg);
3302
- const dmPolicy = account.config.dmPolicy ?? "pairing";
3303
- const storeAllowFrom = normalizeMattermostAllowList(await readStoreAllowFromForDmPolicy({
3304
- provider: "mattermost",
3305
- accountId: account.accountId,
3306
- dmPolicy,
3307
- readStore: pairing.readStoreForDmPolicy
3308
- }));
3309
- const auth = authorizeMattermostCommandInvocation({
3310
- account,
3311
- cfg,
3312
- senderId: params.payload.user_id,
3313
- senderName: params.userName,
3314
- channelId: params.payload.channel_id,
3315
- channelInfo,
3316
- storeAllowFrom,
3317
- allowTextCommands,
3318
- hasControlCommand
3319
- });
3320
- if (!auth.ok) {
3321
- if (auth.denyReason === "dm-pairing") {
3322
- const { code } = await pairing.upsertPairingRequest({
3323
- id: params.payload.user_id,
3324
- meta: { name: params.userName }
3325
- });
3326
- return { ephemeral_text: core.channel.pairing.buildPairingReply({
3327
- channel: "mattermost",
3328
- idLine: `Your Mattermost user id: ${params.payload.user_id}`,
3329
- code
3330
- }) };
3331
- }
3332
- return { ephemeral_text: auth.denyReason === "unknown-channel" ? "Temporary error: unable to determine channel type. Please try again." : auth.denyReason === "dm-disabled" ? "This bot is not accepting direct messages." : auth.denyReason === "channels-disabled" ? "Model picker actions are disabled in channels." : auth.denyReason === "channel-no-allowlist" ? "Model picker actions are not configured for this channel." : "Unauthorized." };
3333
- }
3334
- const kind = auth.kind;
3335
- const chatType = auth.chatType;
3336
- const teamId = auth.channelInfo.team_id ?? params.payload.team_id ?? void 0;
3337
- const channelName = auth.channelName || void 0;
3338
- const channelDisplay = auth.channelDisplay || auth.channelName || params.payload.channel_id;
3339
- const roomLabel = auth.roomLabel;
3340
- const route = core.channel.routing.resolveAgentRoute({
3341
- cfg,
3342
- channel: "mattermost",
3343
- accountId: account.accountId,
3344
- teamId,
3345
- peer: {
3346
- kind,
3347
- id: kind === "direct" ? params.payload.user_id : params.payload.channel_id
3348
- }
3349
- });
3350
- const replyToMode = resolveMattermostReplyToMode(account, kind);
3351
- const threadContext = resolveMattermostThreadSessionContext({
3352
- baseSessionKey: route.sessionKey,
3353
- kind,
3354
- postId: params.post.id || params.payload.post_id,
3355
- replyToMode,
3356
- threadRootId: params.post.root_id
3357
- });
3358
- const modelSessionRoute = {
3359
- agentId: route.agentId,
3360
- sessionKey: threadContext.sessionKey
3361
- };
3362
- const data = await buildModelsProviderData(cfg, route.agentId);
3363
- if (data.providers.length === 0) return await updateModelPickerPost({
3364
- channelId: params.payload.channel_id,
3365
- postId: params.payload.post_id,
3366
- message: "No models available."
3367
- });
3368
- if (pickerState.action === "providers" || pickerState.action === "back") {
3369
- const currentModel = resolveMattermostModelPickerCurrentModel({
3370
- cfg,
3371
- route: modelSessionRoute,
3372
- data
3373
- });
3374
- const view = renderMattermostProviderPickerView({
3375
- ownerUserId: pickerState.ownerUserId,
3376
- data,
3377
- currentModel
3378
- });
3379
- return await updateModelPickerPost({
3380
- channelId: params.payload.channel_id,
3381
- postId: params.payload.post_id,
3382
- message: view.text,
3383
- buttons: view.buttons
3384
- });
3385
- }
3386
- if (pickerState.action === "list") {
3387
- const currentModel = resolveMattermostModelPickerCurrentModel({
3388
- cfg,
3389
- route: modelSessionRoute,
3390
- data
3391
- });
3392
- const view = renderMattermostModelsPickerView({
3393
- ownerUserId: pickerState.ownerUserId,
3394
- data,
3395
- provider: pickerState.provider,
3396
- page: pickerState.page,
3397
- currentModel
3398
- });
3399
- return await updateModelPickerPost({
3400
- channelId: params.payload.channel_id,
3401
- postId: params.payload.post_id,
3402
- message: view.text,
3403
- buttons: view.buttons
3404
- });
3405
- }
3406
- const targetModelRef = `${pickerState.provider}/${pickerState.model}`;
3407
- if (!buildMattermostAllowedModelRefs(data).has(targetModelRef)) return { ephemeral_text: `That model is no longer available: ${targetModelRef}` };
3408
- (async () => {
3409
- try {
3410
- await runModelPickerCommand({
3411
- commandText: `/model ${targetModelRef}`,
3412
- commandAuthorized: auth.commandAuthorized,
3413
- route,
3414
- sessionKey: threadContext.sessionKey,
3415
- parentSessionKey: threadContext.parentSessionKey,
3416
- channelId: params.payload.channel_id,
3417
- senderId: params.payload.user_id,
3418
- senderName: params.userName,
3419
- kind,
3420
- chatType,
3421
- channelName,
3422
- channelDisplay,
3423
- roomLabel,
3424
- teamId,
3425
- postId: params.payload.post_id,
3426
- effectiveReplyToId: threadContext.effectiveReplyToId,
3427
- deliverReplies: true
3428
- });
3429
- const updatedModel = resolveMattermostModelPickerCurrentModel({
3430
- cfg,
3431
- route: modelSessionRoute,
3432
- data,
3433
- skipCache: true
3434
- });
3435
- const view = renderMattermostModelsPickerView({
3436
- ownerUserId: pickerState.ownerUserId,
3437
- data,
3438
- provider: pickerState.provider,
3439
- page: pickerState.page,
3440
- currentModel: updatedModel
3441
- });
3442
- await updateModelPickerPost({
3443
- channelId: params.payload.channel_id,
3444
- postId: params.payload.post_id,
3445
- message: view.text,
3446
- buttons: view.buttons
3447
- });
3448
- } catch (err) {
3449
- runtime.error?.(`mattermost model picker select failed: ${String(err)}`);
3450
- }
3451
- })();
3452
- return {};
3453
- }
3454
- const handlePost = async (post, payload, messageIds) => {
3455
- const channelId = post.channel_id ?? payload.data?.channel_id ?? payload.broadcast?.channel_id;
3456
- if (!channelId) {
3457
- logVerboseMessage("mattermost: drop post (missing channel id)");
3458
- return;
3459
- }
3460
- const allMessageIds = messageIds?.length ? messageIds : post.id ? [post.id] : [];
3461
- if (allMessageIds.length === 0) {
3462
- logVerboseMessage("mattermost: drop post (missing message id)");
3463
- return;
3464
- }
3465
- const dedupeEntries = allMessageIds.map((id) => recentInboundMessages.check(`${account.accountId}:${id}`));
3466
- if (dedupeEntries.length > 0 && dedupeEntries.every(Boolean)) {
3467
- logVerboseMessage(`mattermost: drop post (dedupe account=${account.accountId} ids=${allMessageIds.length})`);
3468
- return;
3469
- }
3470
- const senderId = post.user_id ?? payload.broadcast?.user_id;
3471
- if (!senderId) {
3472
- logVerboseMessage("mattermost: drop post (missing sender id)");
3473
- return;
3474
- }
3475
- if (senderId === botUserId) {
3476
- logVerboseMessage(`mattermost: drop post (self sender=${senderId})`);
3477
- return;
3478
- }
3479
- if (isSystemPost(post)) {
3480
- logVerboseMessage(`mattermost: drop post (system post type=${post.type ?? "unknown"})`);
3481
- return;
3482
- }
3483
- const channelInfo = await resolveChannelInfo(channelId);
3484
- const kind = mapMattermostChannelTypeToChatType(payload.data?.channel_type ?? channelInfo?.type ?? void 0);
3485
- const chatType = channelChatType(kind);
3486
- const senderName = payload.data?.sender_name?.trim() || (await resolveUserInfo(senderId))?.username?.trim() || senderId;
3487
- const rawText = post.message?.trim() || "";
3488
- const dmPolicy = account.config.dmPolicy ?? "pairing";
3489
- const normalizedAllowFrom = normalizeMattermostAllowList(account.config.allowFrom ?? []);
3490
- const normalizedGroupAllowFrom = normalizeMattermostAllowList(account.config.groupAllowFrom ?? []);
3491
- const storeAllowFrom = normalizeMattermostAllowList(await readStoreAllowFromForDmPolicy({
3492
- provider: "mattermost",
3493
- accountId: account.accountId,
3494
- dmPolicy,
3495
- readStore: pairing.readStoreForDmPolicy
3496
- }));
3497
- const accessDecision = resolveDmGroupAccessWithLists({
3498
- isGroup: kind !== "direct",
3499
- dmPolicy,
3500
- groupPolicy,
3501
- allowFrom: normalizedAllowFrom,
3502
- groupAllowFrom: normalizedGroupAllowFrom,
3503
- storeAllowFrom,
3504
- isSenderAllowed: (allowFrom) => isMattermostSenderAllowed({
3505
- senderId,
3506
- senderName,
3507
- allowFrom,
3508
- allowNameMatching
3509
- })
3510
- });
3511
- const effectiveAllowFrom = accessDecision.effectiveAllowFrom;
3512
- const effectiveGroupAllowFrom = accessDecision.effectiveGroupAllowFrom;
3513
- const allowTextCommands = core.channel.commands.shouldHandleTextCommands({
3514
- cfg,
3515
- surface: "mattermost"
3516
- });
3517
- const hasControlCommand = core.channel.text.hasControlCommand(rawText, cfg);
3518
- const isControlCommand = allowTextCommands && hasControlCommand;
3519
- const useAccessGroups = cfg.commands?.useAccessGroups !== false;
3520
- const commandDmAllowFrom = kind === "direct" ? effectiveAllowFrom : normalizedAllowFrom;
3521
- const senderAllowedForCommands = isMattermostSenderAllowed({
3522
- senderId,
3523
- senderName,
3524
- allowFrom: commandDmAllowFrom,
3525
- allowNameMatching
3526
- });
3527
- const groupAllowedForCommands = isMattermostSenderAllowed({
3528
- senderId,
3529
- senderName,
3530
- allowFrom: effectiveGroupAllowFrom,
3531
- allowNameMatching
3532
- });
3533
- const commandGate = resolveControlCommandGate({
3534
- useAccessGroups,
3535
- authorizers: [{
3536
- configured: commandDmAllowFrom.length > 0,
3537
- allowed: senderAllowedForCommands
3538
- }, {
3539
- configured: effectiveGroupAllowFrom.length > 0,
3540
- allowed: groupAllowedForCommands
3541
- }],
3542
- allowTextCommands,
3543
- hasControlCommand
3544
- });
3545
- const commandAuthorized = commandGate.commandAuthorized;
3546
- if (accessDecision.decision !== "allow") {
3547
- if (kind === "direct") {
3548
- if (accessDecision.reasonCode === DM_GROUP_ACCESS_REASON.DM_POLICY_DISABLED) {
3549
- logVerboseMessage(`mattermost: drop dm (dmPolicy=disabled sender=${senderId})`);
3550
- return;
3551
- }
3552
- if (accessDecision.decision === "pairing") {
3553
- const { code, created } = await pairing.upsertPairingRequest({
3554
- id: senderId,
3555
- meta: { name: senderName }
3556
- });
3557
- logVerboseMessage(`mattermost: pairing request sender=${senderId} created=${created}`);
3558
- if (created) try {
3559
- await sendMessageMattermost(`user:${senderId}`, core.channel.pairing.buildPairingReply({
3560
- channel: "mattermost",
3561
- idLine: `Your Mattermost user id: ${senderId}`,
3562
- code
3563
- }), { accountId: account.accountId });
3564
- opts.statusSink?.({ lastOutboundAt: Date.now() });
3565
- } catch (err) {
3566
- logVerboseMessage(`mattermost: pairing reply failed for ${senderId}: ${String(err)}`);
3567
- }
3568
- return;
3569
- }
3570
- logVerboseMessage(`mattermost: drop dm sender=${senderId} (dmPolicy=${dmPolicy})`);
3571
- return;
3572
- }
3573
- if (accessDecision.reasonCode === DM_GROUP_ACCESS_REASON.GROUP_POLICY_DISABLED) {
3574
- logVerboseMessage("mattermost: drop group message (groupPolicy=disabled)");
3575
- return;
3576
- }
3577
- if (accessDecision.reasonCode === DM_GROUP_ACCESS_REASON.GROUP_POLICY_EMPTY_ALLOWLIST) {
3578
- logVerboseMessage("mattermost: drop group message (no group allowlist)");
3579
- return;
3580
- }
3581
- if (accessDecision.reasonCode === DM_GROUP_ACCESS_REASON.GROUP_POLICY_NOT_ALLOWLISTED) {
3582
- logVerboseMessage(`mattermost: drop group sender=${senderId} (not in groupAllowFrom)`);
3583
- return;
3584
- }
3585
- logVerboseMessage(`mattermost: drop group message (groupPolicy=${groupPolicy} reason=${accessDecision.reason})`);
3586
- return;
3587
- }
3588
- if (kind !== "direct" && commandGate.shouldBlock) {
3589
- logInboundDrop({
3590
- log: logVerboseMessage,
3591
- channel: "mattermost",
3592
- reason: "control command (unauthorized)",
3593
- target: senderId
3594
- });
3595
- return;
3596
- }
3597
- const teamId = payload.data?.team_id ?? channelInfo?.team_id ?? void 0;
3598
- const channelName = payload.data?.channel_name ?? channelInfo?.name ?? "";
3599
- const channelDisplay = payload.data?.channel_display_name ?? channelInfo?.display_name ?? channelName;
3600
- const roomLabel = channelName ? `#${channelName}` : channelDisplay || `#${channelId}`;
3601
- const route = core.channel.routing.resolveAgentRoute({
3602
- cfg,
3603
- channel: "mattermost",
3604
- accountId: account.accountId,
3605
- teamId,
3606
- peer: {
3607
- kind,
3608
- id: kind === "direct" ? senderId : channelId
3609
- }
3610
- });
3611
- const baseSessionKey = route.sessionKey;
3612
- const threadRootId = post.root_id?.trim() || void 0;
3613
- const replyToMode = resolveMattermostReplyToMode(account, kind);
3614
- const { effectiveReplyToId, sessionKey, parentSessionKey } = resolveMattermostThreadSessionContext({
3615
- baseSessionKey,
3616
- kind,
3617
- postId: post.id,
3618
- replyToMode,
3619
- threadRootId
3620
- });
3621
- const historyKey = kind === "direct" ? null : sessionKey;
3622
- const mentionRegexes = core.channel.mentions.buildMentionRegexes(cfg, route.agentId);
3623
- const wasMentioned = kind !== "direct" && ((botUsername ? rawText.toLowerCase().includes(`@${botUsername.toLowerCase()}`) : false) || core.channel.mentions.matchesMentionPatterns(rawText, mentionRegexes));
3624
- const pendingBody = rawText || (post.file_ids?.length ? `[Mattermost ${post.file_ids.length === 1 ? "file" : "files"}]` : "");
3625
- const pendingSender = senderName;
3626
- const recordPendingHistory = () => {
3627
- const trimmed = pendingBody.trim();
3628
- recordPendingHistoryEntryIfEnabled({
3629
- historyMap: channelHistories,
3630
- limit: historyLimit,
3631
- historyKey: historyKey ?? "",
3632
- entry: historyKey && trimmed ? {
3633
- sender: pendingSender,
3634
- body: trimmed,
3635
- timestamp: typeof post.create_at === "number" ? post.create_at : void 0,
3636
- messageId: post.id ?? void 0
3637
- } : null
3638
- });
3639
- };
3640
- const oncharEnabled = account.chatmode === "onchar" && kind !== "direct";
3641
- const oncharPrefixes = oncharEnabled ? resolveOncharPrefixes(account.oncharPrefixes) : [];
3642
- const oncharResult = oncharEnabled ? stripOncharPrefix(rawText, oncharPrefixes) : {
3643
- triggered: false,
3644
- stripped: rawText
3645
- };
3646
- const oncharTriggered = oncharResult.triggered;
3647
- const canDetectMention = Boolean(botUsername) || mentionRegexes.length > 0;
3648
- const mentionDecision = evaluateMattermostMentionGate({
3649
- kind,
3650
- cfg,
3651
- accountId: account.accountId,
3652
- channelId,
3653
- threadRootId,
3654
- requireMentionOverride: account.requireMention,
3655
- resolveRequireMention: core.channel.groups.resolveRequireMention,
3656
- wasMentioned,
3657
- isControlCommand,
3658
- commandAuthorized,
3659
- oncharEnabled,
3660
- oncharTriggered,
3661
- canDetectMention
3662
- });
3663
- const { shouldRequireMention, shouldBypassMention } = mentionDecision;
3664
- if (mentionDecision.dropReason === "onchar-not-triggered") {
3665
- logVerboseMessage(`mattermost: drop group message (onchar not triggered channel=${channelId} sender=${senderId})`);
3666
- recordPendingHistory();
3667
- return;
3668
- }
3669
- if (mentionDecision.dropReason === "missing-mention") {
3670
- logVerboseMessage(`mattermost: drop group message (missing mention channel=${channelId} sender=${senderId} requireMention=${shouldRequireMention} bypass=${shouldBypassMention} canDetectMention=${canDetectMention})`);
3671
- recordPendingHistory();
3672
- return;
3673
- }
3674
- const mediaList = await resolveMattermostMedia(post.file_ids);
3675
- const mediaPlaceholder = buildMattermostAttachmentPlaceholder(mediaList);
3676
- const bodyText = normalizeMention([oncharTriggered ? oncharResult.stripped : rawText, mediaPlaceholder].filter(Boolean).join("\n").trim(), botUsername);
3677
- if (!bodyText) {
3678
- logVerboseMessage(`mattermost: drop group message (empty body after normalization channel=${channelId} sender=${senderId})`);
3679
- return;
3680
- }
3681
- core.channel.activity.record({
3682
- channel: "mattermost",
3683
- accountId: account.accountId,
3684
- direction: "inbound"
3685
- });
3686
- const fromLabel = formatInboundFromLabel({
3687
- isGroup: kind !== "direct",
3688
- groupLabel: channelDisplay || roomLabel,
3689
- groupId: channelId,
3690
- groupFallback: roomLabel || "Channel",
3691
- directLabel: senderName,
3692
- directId: senderId
3693
- });
3694
- const preview = bodyText.replace(/\s+/g, " ").slice(0, 160);
3695
- const inboundLabel = kind === "direct" ? `Mattermost DM from ${senderName}` : `Mattermost message in ${roomLabel} from ${senderName}`;
3696
- core.system.enqueueSystemEvent(`${inboundLabel}: ${preview}`, {
3697
- sessionKey,
3698
- contextKey: `mattermost:message:${channelId}:${post.id ?? "unknown"}`
3699
- });
3700
- const textWithId = `${bodyText}\n[mattermost message id: ${post.id ?? "unknown"} channel: ${channelId}]`;
3701
- let combinedBody = core.channel.reply.formatInboundEnvelope({
3702
- channel: "Mattermost",
3703
- from: fromLabel,
3704
- timestamp: typeof post.create_at === "number" ? post.create_at : void 0,
3705
- body: textWithId,
3706
- chatType,
3707
- sender: {
3708
- name: senderName,
3709
- id: senderId
3710
- }
3711
- });
3712
- if (historyKey) combinedBody = buildPendingHistoryContextFromMap({
3713
- historyMap: channelHistories,
3714
- historyKey,
3715
- limit: historyLimit,
3716
- currentMessage: combinedBody,
3717
- formatEntry: (entry) => core.channel.reply.formatInboundEnvelope({
3718
- channel: "Mattermost",
3719
- from: fromLabel,
3720
- timestamp: entry.timestamp,
3721
- body: `${entry.body}${entry.messageId ? ` [id:${entry.messageId} channel:${channelId}]` : ""}`,
3722
- chatType,
3723
- senderLabel: entry.sender
3724
- })
3725
- });
3726
- const to = kind === "direct" ? `user:${senderId}` : `channel:${channelId}`;
3727
- const mediaPayload = buildAgentMediaPayload(mediaList);
3728
- const commandBody = rawText.trim();
3729
- const inboundHistory = historyKey && historyLimit > 0 ? (channelHistories.get(historyKey) ?? []).map((entry) => ({
3730
- sender: entry.sender,
3731
- body: entry.body,
3732
- timestamp: entry.timestamp
3733
- })) : void 0;
3734
- const ctxPayload = core.channel.reply.finalizeInboundContext({
3735
- Body: combinedBody,
3736
- BodyForAgent: bodyText,
3737
- InboundHistory: inboundHistory,
3738
- RawBody: bodyText,
3739
- CommandBody: commandBody,
3740
- BodyForCommands: commandBody,
3741
- From: kind === "direct" ? `mattermost:${senderId}` : kind === "group" ? `mattermost:group:${channelId}` : `mattermost:channel:${channelId}`,
3742
- To: to,
3743
- SessionKey: sessionKey,
3744
- ParentSessionKey: parentSessionKey,
3745
- AccountId: route.accountId,
3746
- ChatType: chatType,
3747
- ConversationLabel: fromLabel,
3748
- GroupSubject: kind !== "direct" ? channelDisplay || roomLabel : void 0,
3749
- GroupChannel: channelName ? `#${channelName}` : void 0,
3750
- GroupSpace: teamId,
3751
- SenderName: senderName,
3752
- SenderId: senderId,
3753
- Provider: "mattermost",
3754
- Surface: "mattermost",
3755
- MessageSid: post.id ?? void 0,
3756
- MessageSids: allMessageIds.length > 1 ? allMessageIds : void 0,
3757
- MessageSidFirst: allMessageIds.length > 1 ? allMessageIds[0] : void 0,
3758
- MessageSidLast: allMessageIds.length > 1 ? allMessageIds[allMessageIds.length - 1] : void 0,
3759
- ReplyToId: effectiveReplyToId,
3760
- MessageThreadId: effectiveReplyToId,
3761
- Timestamp: typeof post.create_at === "number" ? post.create_at : void 0,
3762
- WasMentioned: kind !== "direct" ? mentionDecision.effectiveWasMentioned : void 0,
3763
- CommandAuthorized: commandAuthorized,
3764
- OriginatingChannel: "mattermost",
3765
- OriginatingTo: to,
3766
- ...mediaPayload
3767
- });
3768
- if (kind === "direct") {
3769
- const sessionCfg = cfg.session;
3770
- const storePath = core.channel.session.resolveStorePath(sessionCfg?.store, { agentId: route.agentId });
3771
- await core.channel.session.updateLastRoute({
3772
- storePath,
3773
- sessionKey: route.mainSessionKey,
3774
- deliveryContext: {
3775
- channel: "mattermost",
3776
- to,
3777
- accountId: route.accountId
3778
- }
3779
- });
3780
- }
3781
- const previewLine = bodyText.slice(0, 200).replace(/\n/g, "\\n");
3782
- logVerboseMessage(`mattermost inbound: from=${ctxPayload.From} len=${bodyText.length} preview="${previewLine}"`);
3783
- const textLimit = core.channel.text.resolveTextChunkLimit(cfg, "mattermost", account.accountId, { fallbackLimit: account.textChunkLimit ?? 4e3 });
3784
- const tableMode = core.channel.text.resolveMarkdownTableMode({
3785
- cfg,
3786
- channel: "mattermost",
3787
- accountId: account.accountId
3788
- });
3789
- const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
3790
- cfg,
3791
- agentId: route.agentId,
3792
- channel: "mattermost",
3793
- accountId: account.accountId
3794
- });
3795
- const typingCallbacks = createTypingCallbacks({
3796
- start: () => sendTypingIndicator(channelId, effectiveReplyToId),
3797
- onStartError: (err) => {
3798
- logTypingFailure({
3799
- log: (message) => logger.debug?.(message),
3800
- channel: "mattermost",
3801
- target: channelId,
3802
- error: err
3803
- });
3804
- }
3805
- });
3806
- const { dispatcher, replyOptions, markDispatchIdle } = core.channel.reply.createReplyDispatcherWithTyping({
3807
- ...prefixOptions,
3808
- humanDelay: core.channel.reply.resolveHumanDelayConfig(cfg, route.agentId),
3809
- typingCallbacks,
3810
- deliver: async (payload) => {
3811
- await deliverMattermostReplyPayload({
3812
- core,
3813
- cfg,
3814
- payload,
3815
- to,
3816
- accountId: account.accountId,
3817
- agentId: route.agentId,
3818
- replyToId: resolveMattermostReplyRootId({
3819
- threadRootId: effectiveReplyToId,
3820
- replyToId: payload.replyToId
3821
- }),
3822
- textLimit,
3823
- tableMode,
3824
- sendMessage: sendMessageMattermost
3825
- });
3826
- runtime.log?.(`delivered reply to ${to}`);
3827
- },
3828
- onError: (err, info) => {
3829
- runtime.error?.(`mattermost ${info.kind} reply failed: ${String(err)}`);
3830
- }
3831
- });
3832
- await core.channel.reply.withReplyDispatcher({
3833
- dispatcher,
3834
- onSettled: () => {
3835
- markDispatchIdle();
3836
- },
3837
- run: () => core.channel.reply.dispatchReplyFromConfig({
3838
- ctx: ctxPayload,
3839
- cfg,
3840
- dispatcher,
3841
- replyOptions: {
3842
- ...replyOptions,
3843
- disableBlockStreaming: typeof account.blockStreaming === "boolean" ? !account.blockStreaming : void 0,
3844
- onModelSelected
3845
- }
3846
- })
3847
- });
3848
- if (historyKey) clearHistoryEntriesIfEnabled({
3849
- historyMap: channelHistories,
3850
- historyKey,
3851
- limit: historyLimit
3852
- });
3853
- };
3854
- const handleReactionEvent = async (payload) => {
3855
- const reactionData = payload.data?.reaction;
3856
- if (!reactionData) return;
3857
- let reaction = null;
3858
- if (typeof reactionData === "string") try {
3859
- reaction = JSON.parse(reactionData);
3860
- } catch {
3861
- return;
3862
- }
3863
- else if (typeof reactionData === "object") reaction = reactionData;
3864
- if (!reaction) return;
3865
- const userId = reaction.user_id?.trim();
3866
- const postId = reaction.post_id?.trim();
3867
- const emojiName = reaction.emoji_name?.trim();
3868
- if (!userId || !postId || !emojiName) return;
3869
- if (userId === botUserId) return;
3870
- const action = payload.event === "reaction_removed" ? "removed" : "added";
3871
- const senderName = (await resolveUserInfo(userId))?.username?.trim() || userId;
3872
- const channelId = payload.broadcast?.channel_id;
3873
- if (!channelId) {
3874
- logVerboseMessage(`mattermost: drop reaction (no channel_id in broadcast, cannot enforce policy)`);
3875
- return;
3876
- }
3877
- const channelInfo = await resolveChannelInfo(channelId);
3878
- if (!channelInfo?.type) {
3879
- logVerboseMessage(`mattermost: drop reaction (cannot resolve channel type for ${channelId})`);
3880
- return;
3881
- }
3882
- const kind = mapMattermostChannelTypeToChatType(channelInfo.type);
3883
- const dmPolicy = account.config.dmPolicy ?? "pairing";
3884
- const storeAllowFrom = normalizeMattermostAllowList(await readStoreAllowFromForDmPolicy({
3885
- provider: "mattermost",
3886
- accountId: account.accountId,
3887
- dmPolicy,
3888
- readStore: pairing.readStoreForDmPolicy
3889
- }));
3890
- const reactionAccess = resolveDmGroupAccessWithLists({
3891
- isGroup: kind !== "direct",
3892
- dmPolicy,
3893
- groupPolicy,
3894
- allowFrom: normalizeMattermostAllowList(account.config.allowFrom ?? []),
3895
- groupAllowFrom: normalizeMattermostAllowList(account.config.groupAllowFrom ?? []),
3896
- storeAllowFrom,
3897
- isSenderAllowed: (allowFrom) => isMattermostSenderAllowed({
3898
- senderId: userId,
3899
- senderName,
3900
- allowFrom,
3901
- allowNameMatching
3902
- })
3903
- });
3904
- if (reactionAccess.decision !== "allow") {
3905
- if (kind === "direct") logVerboseMessage(`mattermost: drop reaction (dmPolicy=${dmPolicy} sender=${userId} reason=${reactionAccess.reason})`);
3906
- else logVerboseMessage(`mattermost: drop reaction (groupPolicy=${groupPolicy} sender=${userId} reason=${reactionAccess.reason} channel=${channelId})`);
3907
- return;
3908
- }
3909
- const teamId = channelInfo?.team_id ?? void 0;
3910
- const sessionKey = core.channel.routing.resolveAgentRoute({
3911
- cfg,
3912
- channel: "mattermost",
3913
- accountId: account.accountId,
3914
- teamId,
3915
- peer: {
3916
- kind,
3917
- id: kind === "direct" ? userId : channelId
3918
- }
3919
- }).sessionKey;
3920
- const eventText = `Mattermost reaction ${action}: :${emojiName}: by @${senderName} on post ${postId} in channel ${channelId}`;
3921
- core.system.enqueueSystemEvent(eventText, {
3922
- sessionKey,
3923
- contextKey: `mattermost:reaction:${postId}:${emojiName}:${userId}:${action}`
3924
- });
3925
- logVerboseMessage(`mattermost reaction: ${action} :${emojiName}: by ${senderName} on ${postId}`);
3926
- };
3927
- const inboundDebounceMs = core.channel.debounce.resolveInboundDebounceMs({
3928
- cfg,
3929
- channel: "mattermost"
3930
- });
3931
- const debouncer = core.channel.debounce.createInboundDebouncer({
3932
- debounceMs: inboundDebounceMs,
3933
- buildKey: (entry) => {
3934
- const channelId = entry.post.channel_id ?? entry.payload.data?.channel_id ?? entry.payload.broadcast?.channel_id;
3935
- if (!channelId) return null;
3936
- const threadId = entry.post.root_id?.trim();
3937
- const threadKey = threadId ? `thread:${threadId}` : "channel";
3938
- return `mattermost:${account.accountId}:${channelId}:${threadKey}`;
3939
- },
3940
- shouldDebounce: (entry) => {
3941
- if (entry.post.file_ids && entry.post.file_ids.length > 0) return false;
3942
- const text = entry.post.message?.trim() ?? "";
3943
- if (!text) return false;
3944
- return !core.channel.text.hasControlCommand(text, cfg);
3945
- },
3946
- onFlush: async (entries) => {
3947
- const last = entries.at(-1);
3948
- if (!last) return;
3949
- if (entries.length === 1) {
3950
- await handlePost(last.post, last.payload);
3951
- return;
3952
- }
3953
- const combinedText = entries.map((entry) => entry.post.message?.trim() ?? "").filter(Boolean).join("\n");
3954
- const mergedPost = {
3955
- ...last.post,
3956
- message: combinedText,
3957
- file_ids: []
3958
- };
3959
- const ids = entries.map((entry) => entry.post.id).filter(Boolean);
3960
- await handlePost(mergedPost, last.payload, ids.length > 0 ? ids : void 0);
3961
- },
3962
- onError: (err) => {
3963
- runtime.error?.(`mattermost debounce flush failed: ${String(err)}`);
3964
- }
3965
- });
3966
- const wsUrl = buildMattermostWsUrl(baseUrl);
3967
- let seq = 1;
3968
- const connectOnce = createMattermostConnectOnce({
3969
- wsUrl,
3970
- botToken,
3971
- abortSignal: opts.abortSignal,
3972
- statusSink: opts.statusSink,
3973
- runtime,
3974
- webSocketFactory: opts.webSocketFactory,
3975
- nextSeq: () => seq++,
3976
- onPosted: async (post, payload) => {
3977
- await debouncer.enqueue({
3978
- post,
3979
- payload
3980
- });
3981
- },
3982
- onReaction: async (payload) => {
3983
- await handleReactionEvent(payload);
3984
- }
3985
- });
3986
- let slashShutdownCleanup = null;
3987
- if (slashEnabled) {
3988
- const runAbortCleanup = () => {
3989
- if (slashShutdownCleanup) return;
3990
- const commands = getSlashCommandState(account.accountId)?.registeredCommands ?? [];
3991
- deactivateSlashCommands(account.accountId);
3992
- slashShutdownCleanup = cleanupSlashCommands({
3993
- client,
3994
- commands,
3995
- log: (msg) => runtime.log?.(msg)
3996
- }).catch((err) => {
3997
- runtime.error?.(`mattermost: slash cleanup failed: ${String(err)}`);
3998
- });
3999
- };
4000
- if (opts.abortSignal?.aborted) runAbortCleanup();
4001
- else opts.abortSignal?.addEventListener("abort", runAbortCleanup, { once: true });
4002
- }
4003
- try {
4004
- await runWithReconnect(connectOnce, {
4005
- abortSignal: opts.abortSignal,
4006
- jitterRatio: .2,
4007
- onError: (err) => {
4008
- runtime.error?.(`mattermost connection failed: ${String(err)}`);
4009
- opts.statusSink?.({
4010
- lastError: String(err),
4011
- connected: false
4012
- });
4013
- },
4014
- onReconnect: (delayMs) => {
4015
- runtime.log?.(`mattermost reconnecting in ${Math.round(delayMs / 1e3)}s`);
4016
- }
4017
- });
4018
- } finally {
4019
- unregisterInteractions?.();
4020
- }
4021
- if (slashShutdownCleanup) await slashShutdownCleanup;
4022
- }
4023
- //#endregion
4024
- //#region extensions/mattermost/src/mattermost/probe.ts
4025
- async function probeMattermost(baseUrl, botToken, timeoutMs = 2500) {
4026
- const normalized = normalizeMattermostBaseUrl(baseUrl);
4027
- if (!normalized) return {
4028
- ok: false,
4029
- error: "baseUrl missing"
4030
- };
4031
- const url = `${normalized}/api/v4/users/me`;
4032
- const start = Date.now();
4033
- const controller = timeoutMs > 0 ? new AbortController() : void 0;
4034
- let timer = null;
4035
- if (controller) timer = setTimeout(() => controller.abort(), timeoutMs);
4036
- try {
4037
- const res = await fetch(url, {
4038
- headers: { Authorization: `Bearer ${botToken}` },
4039
- signal: controller?.signal
4040
- });
4041
- const elapsedMs = Date.now() - start;
4042
- if (!res.ok) {
4043
- const detail = await readMattermostError(res);
4044
- return {
4045
- ok: false,
4046
- status: res.status,
4047
- error: detail || res.statusText,
4048
- elapsedMs
4049
- };
4050
- }
4051
- const bot = await res.json();
4052
- return {
4053
- ok: true,
4054
- status: res.status,
4055
- elapsedMs,
4056
- bot
4057
- };
4058
- } catch (err) {
4059
- return {
4060
- ok: false,
4061
- status: null,
4062
- error: err instanceof Error ? err.message : String(err),
4063
- elapsedMs: Date.now() - start
4064
- };
4065
- } finally {
4066
- if (timer) clearTimeout(timer);
4067
- }
4068
- }
4069
- //#endregion
4070
- //#region extensions/mattermost/src/mattermost/reactions.ts
4071
- const BOT_USER_CACHE_TTL_MS = 10 * 6e4;
4072
- const botUserIdCache = /* @__PURE__ */ new Map();
4073
- async function resolveBotUserId(client, cacheKey) {
4074
- const cached = botUserIdCache.get(cacheKey);
4075
- if (cached && cached.expiresAt > Date.now()) return cached.userId;
4076
- const userId = (await fetchMattermostMe(client))?.id?.trim();
4077
- if (!userId) return null;
4078
- botUserIdCache.set(cacheKey, {
4079
- userId,
4080
- expiresAt: Date.now() + BOT_USER_CACHE_TTL_MS
4081
- });
4082
- return userId;
4083
- }
4084
- async function addMattermostReaction(params) {
4085
- return runMattermostReaction(params, {
4086
- action: "add",
4087
- mutation: createReaction
4088
- });
4089
- }
4090
- async function removeMattermostReaction(params) {
4091
- return runMattermostReaction(params, {
4092
- action: "remove",
4093
- mutation: deleteReaction
4094
- });
4095
- }
4096
- async function runMattermostReaction(params, options) {
4097
- const resolved = resolveMattermostAccount({
4098
- cfg: params.cfg,
4099
- accountId: params.accountId
4100
- });
4101
- const baseUrl = resolved.baseUrl?.trim();
4102
- const botToken = resolved.botToken?.trim();
4103
- if (!baseUrl || !botToken) return {
4104
- ok: false,
4105
- error: "Mattermost botToken/baseUrl missing."
4106
- };
4107
- const client = createMattermostClient({
4108
- baseUrl,
4109
- botToken,
4110
- fetchImpl: params.fetchImpl
4111
- });
4112
- const userId = await resolveBotUserId(client, `${baseUrl}:${botToken}`);
4113
- if (!userId) return {
4114
- ok: false,
4115
- error: "Mattermost reactions failed: could not resolve bot user id."
4116
- };
4117
- try {
4118
- await options.mutation(client, {
4119
- userId,
4120
- postId: params.postId,
4121
- emojiName: params.emojiName
4122
- });
4123
- } catch (err) {
4124
- return {
4125
- ok: false,
4126
- error: `Mattermost ${options.action} reaction failed: ${String(err)}`
4127
- };
4128
- }
4129
- return { ok: true };
4130
- }
4131
- async function createReaction(client, params) {
4132
- await client.request("/reactions", {
4133
- method: "POST",
4134
- body: JSON.stringify({
4135
- user_id: params.userId,
4136
- post_id: params.postId,
4137
- emoji_name: params.emojiName
4138
- })
4139
- });
4140
- }
4141
- async function deleteReaction(client, params) {
4142
- const emoji = encodeURIComponent(params.emojiName);
4143
- await client.request(`/users/${params.userId}/posts/${params.postId}/reactions/${emoji}`, { method: "DELETE" });
4144
- }
4145
- //#endregion
4146
- //#region extensions/mattermost/src/normalize.ts
4147
- function normalizeMattermostMessagingTarget(raw) {
4148
- const trimmed = raw.trim();
4149
- if (!trimmed) return;
4150
- const lower = trimmed.toLowerCase();
4151
- if (lower.startsWith("channel:")) {
4152
- const id = trimmed.slice(8).trim();
4153
- return id ? `channel:${id}` : void 0;
4154
- }
4155
- if (lower.startsWith("group:")) {
4156
- const id = trimmed.slice(6).trim();
4157
- return id ? `channel:${id}` : void 0;
4158
- }
4159
- if (lower.startsWith("user:")) {
4160
- const id = trimmed.slice(5).trim();
4161
- return id ? `user:${id}` : void 0;
4162
- }
4163
- if (lower.startsWith("mattermost:")) {
4164
- const id = trimmed.slice(11).trim();
4165
- return id ? `user:${id}` : void 0;
4166
- }
4167
- if (trimmed.startsWith("@")) {
4168
- const id = trimmed.slice(1).trim();
4169
- return id ? `@${id}` : void 0;
4170
- }
4171
- if (trimmed.startsWith("#")) return;
4172
- }
4173
- function looksLikeMattermostTargetId(raw, normalized) {
4174
- const trimmed = raw.trim();
4175
- if (!trimmed) return false;
4176
- if (/^(user|channel|group|mattermost):/i.test(trimmed)) return true;
4177
- if (trimmed.startsWith("@")) return true;
4178
- return /^[a-z0-9]{26}$/i.test(trimmed) || /^[a-z0-9]{26}__[a-z0-9]{26}$/i.test(trimmed);
4179
- }
4180
- //#endregion
4181
- //#region extensions/mattermost/src/setup-core.ts
4182
- const channel$1 = "mattermost";
4183
- function isMattermostConfigured(account) {
4184
- return (Boolean(account.botToken?.trim()) || hasConfiguredSecretInput(account.config.botToken)) && Boolean(account.baseUrl);
4185
- }
4186
- function resolveMattermostAccountWithSecrets(cfg, accountId) {
4187
- return resolveMattermostAccount({
4188
- cfg,
4189
- accountId,
4190
- allowUnresolvedSecretRef: true
4191
- });
4192
- }
4193
- const mattermostSetupAdapter = {
4194
- resolveAccountId: ({ accountId }) => normalizeAccountId(accountId),
4195
- applyAccountName: ({ cfg, accountId, name }) => applyAccountNameToChannelSection({
4196
- cfg,
4197
- channelKey: channel$1,
4198
- accountId,
4199
- name
4200
- }),
4201
- validateInput: ({ accountId, input }) => {
4202
- const token = input.botToken ?? input.token;
4203
- const baseUrl = normalizeMattermostBaseUrl(input.httpUrl);
4204
- if (input.useEnv && accountId !== "default") return "Mattermost env vars can only be used for the default account.";
4205
- if (!input.useEnv && (!token || !baseUrl)) return "Mattermost requires --bot-token and --http-url (or --use-env).";
4206
- if (input.httpUrl && !baseUrl) return "Mattermost --http-url must include a valid base URL.";
4207
- return null;
4208
- },
4209
- applyAccountConfig: ({ cfg, accountId, input }) => {
4210
- const token = input.botToken ?? input.token;
4211
- const baseUrl = normalizeMattermostBaseUrl(input.httpUrl);
4212
- const namedConfig = applyAccountNameToChannelSection({
4213
- cfg,
4214
- channelKey: channel$1,
4215
- accountId,
4216
- name: input.name
4217
- });
4218
- return applySetupAccountConfigPatch({
4219
- cfg: accountId !== "default" ? migrateBaseNameToDefaultAccount({
4220
- cfg: namedConfig,
4221
- channelKey: channel$1
4222
- }) : namedConfig,
4223
- channelKey: channel$1,
4224
- accountId,
4225
- patch: input.useEnv ? {} : {
4226
- ...token ? { botToken: token } : {},
4227
- ...baseUrl ? { baseUrl } : {}
4228
- }
4229
- });
4230
- }
4231
- };
4232
- //#endregion
4233
- //#region extensions/mattermost/src/setup-surface.ts
4234
- const channel = "mattermost";
4235
- const mattermostSetupWizard = {
4236
- channel,
4237
- status: {
4238
- configuredLabel: "configured",
4239
- unconfiguredLabel: "needs token + url",
4240
- configuredHint: "configured",
4241
- unconfiguredHint: "needs setup",
4242
- configuredScore: 2,
4243
- unconfiguredScore: 1,
4244
- resolveConfigured: ({ cfg }) => listMattermostAccountIds(cfg).some((accountId) => isMattermostConfigured(resolveMattermostAccountWithSecrets(cfg, accountId)))
4245
- },
4246
- introNote: {
4247
- title: "Mattermost bot token",
4248
- lines: [
4249
- "1) Mattermost System Console -> Integrations -> Bot Accounts",
4250
- "2) Create a bot + copy its token",
4251
- "3) Use your server base URL (e.g., https://chat.example.com)",
4252
- "Tip: the bot must be a member of any channel you want it to monitor.",
4253
- `Docs: ${formatDocsLink("/mattermost", "mattermost")}`
4254
- ],
4255
- shouldShow: ({ cfg, accountId }) => !isMattermostConfigured(resolveMattermostAccountWithSecrets(cfg, accountId))
4256
- },
4257
- envShortcut: {
4258
- prompt: "MATTERMOST_BOT_TOKEN + MATTERMOST_URL detected. Use env vars?",
4259
- preferredEnvVar: "MATTERMOST_BOT_TOKEN",
4260
- isAvailable: ({ cfg, accountId }) => {
4261
- if (accountId !== "default") return false;
4262
- const resolvedAccount = resolveMattermostAccountWithSecrets(cfg, accountId);
4263
- const hasConfigValues = hasConfiguredSecretInput(resolvedAccount.config.botToken) || Boolean(resolvedAccount.config.baseUrl?.trim());
4264
- return Boolean(process.env.MATTERMOST_BOT_TOKEN?.trim() && process.env.MATTERMOST_URL?.trim() && !hasConfigValues);
4265
- },
4266
- apply: ({ cfg, accountId }) => applySetupAccountConfigPatch({
4267
- cfg,
4268
- channelKey: channel,
4269
- accountId,
4270
- patch: {}
4271
- })
4272
- },
4273
- credentials: [{
4274
- inputKey: "botToken",
4275
- providerHint: channel,
4276
- credentialLabel: "bot token",
4277
- preferredEnvVar: "MATTERMOST_BOT_TOKEN",
4278
- envPrompt: "MATTERMOST_BOT_TOKEN + MATTERMOST_URL detected. Use env vars?",
4279
- keepPrompt: "Mattermost bot token already configured. Keep it?",
4280
- inputPrompt: "Enter Mattermost bot token",
4281
- inspect: ({ cfg, accountId }) => {
4282
- const resolvedAccount = resolveMattermostAccountWithSecrets(cfg, accountId);
4283
- return {
4284
- accountConfigured: isMattermostConfigured(resolvedAccount),
4285
- hasConfiguredValue: hasConfiguredSecretInput(resolvedAccount.config.botToken)
4286
- };
4287
- }
4288
- }],
4289
- textInputs: [{
4290
- inputKey: "httpUrl",
4291
- message: "Enter Mattermost base URL",
4292
- confirmCurrentValue: false,
4293
- currentValue: ({ cfg, accountId }) => resolveMattermostAccountWithSecrets(cfg, accountId).baseUrl ?? process.env.MATTERMOST_URL?.trim(),
4294
- initialValue: ({ cfg, accountId }) => resolveMattermostAccountWithSecrets(cfg, accountId).baseUrl ?? process.env.MATTERMOST_URL?.trim(),
4295
- shouldPrompt: ({ cfg, accountId, credentialValues, currentValue }) => {
4296
- const resolvedAccount = resolveMattermostAccountWithSecrets(cfg, accountId);
4297
- const tokenConfigured = Boolean(resolvedAccount.botToken?.trim()) || hasConfiguredSecretInput(resolvedAccount.config.botToken);
4298
- return Boolean(credentialValues.botToken) || !tokenConfigured || !currentValue;
4299
- },
4300
- validate: ({ value }) => normalizeMattermostBaseUrl(value) ? void 0 : "Mattermost base URL must include a valid base URL.",
4301
- normalizeValue: ({ value }) => normalizeMattermostBaseUrl(value) ?? value.trim()
4302
- }],
4303
- disable: (cfg) => ({
4304
- ...cfg,
4305
- channels: {
4306
- ...cfg.channels,
4307
- mattermost: {
4308
- ...cfg.channels?.mattermost,
4309
- enabled: false
4310
- }
4311
- }
4312
- })
4313
- };
4314
- //#endregion
4315
- //#region extensions/mattermost/src/channel.ts
4316
- const mattermostMessageActions = {
4317
- listActions: ({ cfg }) => {
4318
- const enabledAccounts = listMattermostAccountIds(cfg).map((accountId) => resolveMattermostAccount({
4319
- cfg,
4320
- accountId
4321
- })).filter((account) => account.enabled).filter((account) => Boolean(account.botToken?.trim() && account.baseUrl?.trim()));
4322
- const actions = [];
4323
- if (enabledAccounts.length > 0) actions.push("send");
4324
- const baseReactions = (cfg.channels?.mattermost?.actions)?.reactions;
4325
- if (enabledAccounts.some((account) => {
4326
- return (account.config.actions?.reactions ?? baseReactions ?? true) !== false;
4327
- })) actions.push("react");
4328
- return actions;
4329
- },
4330
- supportsAction: ({ action }) => {
4331
- return action === "send" || action === "react";
4332
- },
4333
- getCapabilities: ({ cfg }) => {
4334
- return listMattermostAccountIds(cfg).map((id) => resolveMattermostAccount({
4335
- cfg,
4336
- accountId: id
4337
- })).filter((a) => a.enabled && a.botToken?.trim() && a.baseUrl?.trim()).length > 0 ? ["buttons"] : [];
4338
- },
4339
- handleAction: async ({ action, params, cfg, accountId }) => {
4340
- if (action === "react") {
4341
- const mmBase = cfg?.channels?.mattermost;
4342
- const accounts = mmBase?.accounts;
4343
- const resolvedAccountId = accountId ?? resolveDefaultMattermostAccountId(cfg);
4344
- const acctActions = (accounts?.[resolvedAccountId])?.actions;
4345
- const baseActions = mmBase?.actions;
4346
- if (!(acctActions?.reactions ?? baseActions?.reactions ?? true)) throw new Error("Mattermost reactions are disabled in config");
4347
- const postId = (typeof params?.messageId === "string" ? params.messageId : typeof params?.postId === "string" ? params.postId : "").trim();
4348
- if (!postId) throw new Error("Mattermost react requires messageId (post id)");
4349
- const emojiName = (typeof params?.emoji === "string" ? params.emoji : "").trim().replace(/^:+|:+$/g, "");
4350
- if (!emojiName) throw new Error("Mattermost react requires emoji");
4351
- if (params?.remove === true) {
4352
- const result = await removeMattermostReaction({
4353
- cfg,
4354
- postId,
4355
- emojiName,
4356
- accountId: resolvedAccountId
4357
- });
4358
- if (!result.ok) throw new Error(result.error);
4359
- return {
4360
- content: [{
4361
- type: "text",
4362
- text: `Removed reaction :${emojiName}: from ${postId}`
4363
- }],
4364
- details: {}
4365
- };
4366
- }
4367
- const result = await addMattermostReaction({
4368
- cfg,
4369
- postId,
4370
- emojiName,
4371
- accountId: resolvedAccountId
4372
- });
4373
- if (!result.ok) throw new Error(result.error);
4374
- return {
4375
- content: [{
4376
- type: "text",
4377
- text: `Reacted with :${emojiName}: on ${postId}`
4378
- }],
4379
- details: {}
4380
- };
4381
- }
4382
- if (action !== "send") throw new Error(`Unsupported Mattermost action: ${action}`);
4383
- const to = typeof params.to === "string" ? params.to.trim() : typeof params.target === "string" ? params.target.trim() : "";
4384
- if (!to) throw new Error("Mattermost send requires a target (to).");
4385
- const message = typeof params.message === "string" ? params.message : "";
4386
- const replyToId = readMattermostReplyToId(params);
4387
- const resolvedAccountId = accountId || void 0;
4388
- const mediaUrl = typeof params.media === "string" ? params.media.trim() || void 0 : void 0;
4389
- const result = await sendMessageMattermost(to, message, {
4390
- accountId: resolvedAccountId,
4391
- replyToId,
4392
- buttons: Array.isArray(params.buttons) ? params.buttons : void 0,
4393
- attachmentText: typeof params.attachmentText === "string" ? params.attachmentText : void 0,
4394
- mediaUrl
4395
- });
4396
- return {
4397
- content: [{
4398
- type: "text",
4399
- text: JSON.stringify({
4400
- ok: true,
4401
- channel: "mattermost",
4402
- messageId: result.messageId,
4403
- channelId: result.channelId
4404
- })
4405
- }],
4406
- details: {}
4407
- };
4408
- }
4409
- };
4410
- const meta = {
4411
- id: "mattermost",
4412
- label: "Mattermost",
4413
- selectionLabel: "Mattermost (plugin)",
4414
- detailLabel: "Mattermost Bot",
4415
- docsPath: "/channels/mattermost",
4416
- docsLabel: "mattermost",
4417
- blurb: "self-hosted Slack-style chat; install the plugin to enable.",
4418
- systemImage: "bubble.left.and.bubble.right",
4419
- order: 65,
4420
- quickstartAllowFrom: true
4421
- };
4422
- function readMattermostReplyToId(params) {
4423
- const readNormalizedValue = (value) => {
4424
- if (typeof value !== "string") return;
4425
- return value.trim() || void 0;
4426
- };
4427
- return readNormalizedValue(params.replyToId) ?? readNormalizedValue(params.replyTo);
4428
- }
4429
- function normalizeAllowEntry(entry) {
4430
- return entry.trim().replace(/^(mattermost|user):/i, "").replace(/^@/, "").toLowerCase();
4431
- }
4432
- function formatAllowEntry(entry) {
4433
- const trimmed = entry.trim();
4434
- if (!trimmed) return "";
4435
- if (trimmed.startsWith("@")) {
4436
- const username = trimmed.slice(1).trim();
4437
- return username ? `@${username.toLowerCase()}` : "";
4438
- }
4439
- return trimmed.replace(/^(mattermost|user):/i, "").toLowerCase();
4440
- }
4441
- const mattermostConfigAccessors = createScopedAccountConfigAccessors({
4442
- resolveAccount: ({ cfg, accountId }) => resolveMattermostAccount({
4443
- cfg,
4444
- accountId
4445
- }),
4446
- resolveAllowFrom: (account) => account.config.allowFrom,
4447
- formatAllowFrom: (allowFrom) => formatNormalizedAllowFromEntries({
4448
- allowFrom,
4449
- normalizeEntry: formatAllowEntry
4450
- })
4451
- });
4452
- const mattermostPlugin = {
4453
- id: "mattermost",
4454
- meta: { ...meta },
4455
- setup: mattermostSetupAdapter,
4456
- setupWizard: mattermostSetupWizard,
4457
- pairing: {
4458
- idLabel: "mattermostUserId",
4459
- normalizeAllowEntry: (entry) => normalizeAllowEntry(entry),
4460
- notifyApproval: async ({ id }) => {
4461
- console.log(`[mattermost] User ${id} approved for pairing`);
4462
- }
4463
- },
4464
- capabilities: {
4465
- chatTypes: [
4466
- "direct",
4467
- "channel",
4468
- "group",
4469
- "thread"
4470
- ],
4471
- reactions: true,
4472
- threads: true,
4473
- media: true,
4474
- nativeCommands: true
4475
- },
4476
- streaming: { blockStreamingCoalesceDefaults: {
4477
- minChars: 1500,
4478
- idleMs: 1e3
4479
- } },
4480
- threading: { resolveReplyToMode: ({ cfg, accountId, chatType }) => {
4481
- return resolveMattermostReplyToMode(resolveMattermostAccount({
4482
- cfg,
4483
- accountId: accountId ?? "default"
4484
- }), chatType === "direct" || chatType === "group" || chatType === "channel" ? chatType : "channel");
4485
- } },
4486
- reload: { configPrefixes: ["channels.mattermost"] },
4487
- configSchema: buildChannelConfigSchema(MattermostConfigSchema),
4488
- config: {
4489
- listAccountIds: (cfg) => listMattermostAccountIds(cfg),
4490
- resolveAccount: (cfg, accountId) => resolveMattermostAccount({
4491
- cfg,
4492
- accountId
4493
- }),
4494
- defaultAccountId: (cfg) => resolveDefaultMattermostAccountId(cfg),
4495
- setAccountEnabled: ({ cfg, accountId, enabled }) => setAccountEnabledInConfigSection({
4496
- cfg,
4497
- sectionKey: "mattermost",
4498
- accountId,
4499
- enabled,
4500
- allowTopLevel: true
4501
- }),
4502
- deleteAccount: ({ cfg, accountId }) => deleteAccountFromConfigSection({
4503
- cfg,
4504
- sectionKey: "mattermost",
4505
- accountId,
4506
- clearBaseFields: [
4507
- "botToken",
4508
- "baseUrl",
4509
- "name"
4510
- ]
4511
- }),
4512
- isConfigured: (account) => Boolean(account.botToken && account.baseUrl),
4513
- describeAccount: (account) => ({
4514
- accountId: account.accountId,
4515
- name: account.name,
4516
- enabled: account.enabled,
4517
- configured: Boolean(account.botToken && account.baseUrl),
4518
- botTokenSource: account.botTokenSource,
4519
- baseUrl: account.baseUrl
4520
- }),
4521
- ...mattermostConfigAccessors
4522
- },
4523
- security: {
4524
- resolveDmPolicy: ({ cfg, accountId, account }) => {
4525
- return buildAccountScopedDmSecurityPolicy({
4526
- cfg,
4527
- channelKey: "mattermost",
4528
- accountId,
4529
- fallbackAccountId: account.accountId ?? "default",
4530
- policy: account.config.dmPolicy,
4531
- allowFrom: account.config.allowFrom ?? [],
4532
- policyPathSuffix: "dmPolicy",
4533
- normalizeEntry: (raw) => normalizeAllowEntry(raw)
4534
- });
4535
- },
4536
- collectWarnings: ({ account, cfg }) => {
4537
- return collectAllowlistProviderRestrictSendersWarnings({
4538
- cfg,
4539
- providerConfigPresent: cfg.channels?.mattermost !== void 0,
4540
- configuredGroupPolicy: account.config.groupPolicy,
4541
- surface: "Mattermost channels",
4542
- openScope: "any member",
4543
- groupPolicyPath: "channels.mattermost.groupPolicy",
4544
- groupAllowFromPath: "channels.mattermost.groupAllowFrom"
4545
- });
4546
- }
4547
- },
4548
- groups: { resolveRequireMention: resolveMattermostGroupRequireMention },
4549
- actions: mattermostMessageActions,
4550
- directory: {
4551
- listGroups: async (params) => listMattermostDirectoryGroups(params),
4552
- listGroupsLive: async (params) => listMattermostDirectoryGroups(params),
4553
- listPeers: async (params) => listMattermostDirectoryPeers(params),
4554
- listPeersLive: async (params) => listMattermostDirectoryPeers(params)
4555
- },
4556
- messaging: {
4557
- normalizeTarget: normalizeMattermostMessagingTarget,
4558
- targetResolver: {
4559
- looksLikeId: looksLikeMattermostTargetId,
4560
- hint: "<channelId|user:ID|channel:ID>",
4561
- resolveTarget: async ({ cfg, accountId, input }) => {
4562
- const resolved = await resolveMattermostOpaqueTarget({
4563
- input,
4564
- cfg,
4565
- accountId
4566
- });
4567
- if (!resolved) return null;
4568
- return {
4569
- to: resolved.to,
4570
- kind: resolved.kind,
4571
- source: "directory"
4572
- };
4573
- }
4574
- }
4575
- },
4576
- outbound: {
4577
- deliveryMode: "direct",
4578
- chunker: (text, limit) => getMattermostRuntime().channel.text.chunkMarkdownText(text, limit),
4579
- chunkerMode: "markdown",
4580
- textChunkLimit: 4e3,
4581
- resolveTarget: ({ to }) => {
4582
- const trimmed = to?.trim();
4583
- if (!trimmed) return {
4584
- ok: false,
4585
- error: /* @__PURE__ */ new Error("Delivering to Mattermost requires --to <channelId|@username|user:ID|channel:ID>")
4586
- };
4587
- return {
4588
- ok: true,
4589
- to: trimmed
4590
- };
4591
- },
4592
- sendText: async ({ cfg, to, text, accountId, replyToId, threadId }) => {
4593
- return {
4594
- channel: "mattermost",
4595
- ...await sendMessageMattermost(to, text, {
4596
- cfg,
4597
- accountId: accountId ?? void 0,
4598
- replyToId: replyToId ?? (threadId != null ? String(threadId) : void 0)
4599
- })
4600
- };
4601
- },
4602
- sendMedia: async ({ cfg, to, text, mediaUrl, mediaLocalRoots, accountId, replyToId, threadId }) => {
4603
- return {
4604
- channel: "mattermost",
4605
- ...await sendMessageMattermost(to, text, {
4606
- cfg,
4607
- accountId: accountId ?? void 0,
4608
- mediaUrl,
4609
- mediaLocalRoots,
4610
- replyToId: replyToId ?? (threadId != null ? String(threadId) : void 0)
4611
- })
4612
- };
4613
- }
4614
- },
4615
- status: {
4616
- defaultRuntime: {
4617
- accountId: DEFAULT_ACCOUNT_ID,
4618
- running: false,
4619
- connected: false,
4620
- lastConnectedAt: null,
4621
- lastDisconnect: null,
4622
- lastStartAt: null,
4623
- lastStopAt: null,
4624
- lastError: null
4625
- },
4626
- buildChannelSummary: ({ snapshot }) => buildPassiveProbedChannelStatusSummary(snapshot, {
4627
- botTokenSource: snapshot.botTokenSource ?? "none",
4628
- connected: snapshot.connected ?? false,
4629
- baseUrl: snapshot.baseUrl ?? null
4630
- }),
4631
- probeAccount: async ({ account, timeoutMs }) => {
4632
- const token = account.botToken?.trim();
4633
- const baseUrl = account.baseUrl?.trim();
4634
- if (!token || !baseUrl) return {
4635
- ok: false,
4636
- error: "bot token or baseUrl missing"
4637
- };
4638
- return await probeMattermost(baseUrl, token, timeoutMs);
4639
- },
4640
- buildAccountSnapshot: ({ account, runtime, probe }) => {
4641
- return {
4642
- ...buildComputedAccountStatusSnapshot({
4643
- accountId: account.accountId,
4644
- name: account.name,
4645
- enabled: account.enabled,
4646
- configured: Boolean(account.botToken && account.baseUrl),
4647
- runtime,
4648
- probe
4649
- }),
4650
- botTokenSource: account.botTokenSource,
4651
- baseUrl: account.baseUrl,
4652
- connected: runtime?.connected ?? false,
4653
- lastConnectedAt: runtime?.lastConnectedAt ?? null,
4654
- lastDisconnect: runtime?.lastDisconnect ?? null
4655
- };
4656
- }
4657
- },
4658
- gateway: { startAccount: async (ctx) => {
4659
- const account = ctx.account;
4660
- const statusSink = createAccountStatusSink({
4661
- accountId: ctx.accountId,
4662
- setStatus: ctx.setStatus
4663
- });
4664
- statusSink({
4665
- baseUrl: account.baseUrl,
4666
- botTokenSource: account.botTokenSource
4667
- });
4668
- ctx.log?.info(`[${account.accountId}] starting channel`);
4669
- return monitorMattermostProvider({
4670
- botToken: account.botToken ?? void 0,
4671
- baseUrl: account.baseUrl ?? void 0,
4672
- accountId: account.accountId,
4673
- config: ctx.cfg,
4674
- runtime: ctx.runtime,
4675
- abortSignal: ctx.abortSignal,
4676
- statusSink
4677
- });
4678
- } }
4679
- };
4680
- //#endregion
4681
- export { registerSlashCommandRoute as n, setMattermostRuntime as r, mattermostPlugin as t };