silentlake 2026.3.24

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 (1893) hide show
  1. package/CHANGELOG.md +4587 -0
  2. package/LICENSE +21 -0
  3. package/README.md +248 -0
  4. package/assets/avatar-placeholder.svg +19 -0
  5. package/assets/chrome-extension/icons/icon128.png +0 -0
  6. package/assets/chrome-extension/icons/icon16.png +0 -0
  7. package/assets/chrome-extension/icons/icon32.png +0 -0
  8. package/assets/chrome-extension/icons/icon48.png +0 -0
  9. package/assets/dmg-background-small.png +0 -0
  10. package/assets/dmg-background.png +0 -0
  11. package/assets/silentlake-banner.png +0 -0
  12. package/dist/APEv2Parser-BZv_dP9t.js +269 -0
  13. package/dist/APEv2Parser-CPzxFNBB.js +5 -0
  14. package/dist/AbstractID3Parser-mvDFcjYV.js +47 -0
  15. package/dist/AiffParser-BXQ9SRZk.js +145 -0
  16. package/dist/AsfParser-CmBDUlZE.js +631 -0
  17. package/dist/BasicParser-DhmXREDo.js +853 -0
  18. package/dist/DsdiffParser-CTKKGyZg.js +150 -0
  19. package/dist/DsfParser-Ds-YQe4Z.js +101 -0
  20. package/dist/FlacParser-B1XVPgXF.js +5 -0
  21. package/dist/FlacParser-DMPyL1y4.js +367 -0
  22. package/dist/ID3v1Parser-BICWWVDG.js +289 -0
  23. package/dist/ID3v2Parser-BmZHSUqs.js +650 -0
  24. package/dist/ID3v2Token-DeJf4tYQ.js +145 -0
  25. package/dist/MP4Parser-Cjf-Zs8T.js +1061 -0
  26. package/dist/MatroskaParser-DgBzBe8t.js +909 -0
  27. package/dist/MpegParser-D5swTpA1.js +744 -0
  28. package/dist/MusepackParser-D8EQXnpK.js +285 -0
  29. package/dist/OggParser-BcIYPHwP.js +390 -0
  30. package/dist/Util-D_zGsr97.js +170 -0
  31. package/dist/WavPackParser-hosU8gfo.js +166 -0
  32. package/dist/WaveParser-CA00FZrC.js +273 -0
  33. package/dist/abort-cutoff-CERmtgZI.js +56 -0
  34. package/dist/abort-cutoff.runtime-DZkGKKzv.js +61 -0
  35. package/dist/abort-signal-CsrBEr94.js +13 -0
  36. package/dist/account-helpers-D3c_eI7c.js +37 -0
  37. package/dist/account-helpers-ru3jdZSV.js +12 -0
  38. package/dist/account-id-DZnNZg8x.js +1 -0
  39. package/dist/account-id-ZCrgXl7Z.js +44 -0
  40. package/dist/account-lookup-nkoa-foB.js +10 -0
  41. package/dist/account-snapshot-fields-Cvq7803C.js +116 -0
  42. package/dist/account-summary-B5Xzvntm.js +36 -0
  43. package/dist/accounts-43SvCDEA.js +212 -0
  44. package/dist/accounts-CYgFhv2o.js +105 -0
  45. package/dist/accounts-ChlyF7cx.js +112 -0
  46. package/dist/ack-reactions-CNVwfOBj.js +43 -0
  47. package/dist/acp-cli-B4Rv7-xU.js +2033 -0
  48. package/dist/acp-runtime-BdLdT-QY.js +1 -0
  49. package/dist/actions.runtime-CqnQssoB.js +217 -0
  50. package/dist/actions.runtime-FLmCvVRd.js +236 -0
  51. package/dist/agent-CBOdzEvR.js +1 -0
  52. package/dist/agent-scope-BLhzf-o0.js +17 -0
  53. package/dist/agent-scope-DPP4Z_UU.js +193 -0
  54. package/dist/agents-D8pBK0II.js +855 -0
  55. package/dist/agents-Dihz1Ihx.js +323 -0
  56. package/dist/agents.config-C_lrnc9J.js +18 -0
  57. package/dist/agents.config-D1VqC78r.js +121 -0
  58. package/dist/allow-from-BPSBITdd.js +9 -0
  59. package/dist/allow-from-BwTLpvhp.js +20 -0
  60. package/dist/allow-from-C4iBpqFI.js +62 -0
  61. package/dist/allowlist-config-edit-CKbnMmwS.js +279 -0
  62. package/dist/allowlist-match-CYmPgg1K.js +63 -0
  63. package/dist/ansi-BEJF8NKS.js +54 -0
  64. package/dist/anthropic-vertex-provider-Dd5agCN9.js +60 -0
  65. package/dist/apply.runtime-DhKxNSJE.js +370 -0
  66. package/dist/apply.runtime-ghlh-P6X.js +211 -0
  67. package/dist/archive-Tr0wIUO-.js +532 -0
  68. package/dist/arg-split-Dtda0YDl.js +38 -0
  69. package/dist/artifacts-C_4LekPC.js +39 -0
  70. package/dist/audit-BOPSQQtd.js +54 -0
  71. package/dist/audit-C5kdrCi_.js +788 -0
  72. package/dist/audit-channel.allow-from.runtime-B7BHNblL.js +17 -0
  73. package/dist/audit-channel.collect.runtime-CjAbXFBV.js +521 -0
  74. package/dist/audit-channel.discord.runtime-BBY6S9lg.js +5 -0
  75. package/dist/audit-channel.telegram.runtime-SJnxOJH2.js +8 -0
  76. package/dist/audit-channel.zalouser.runtime-SGRWvHxT.js +5 -0
  77. package/dist/audit-extra.async-DY8v7LXH.js +817 -0
  78. package/dist/audit-fs-oDMUa5N_.js +375 -0
  79. package/dist/audit-membership-runtime-BPjFryEx.js +261 -0
  80. package/dist/audit.deep.runtime-WFf-TpsD.js +31 -0
  81. package/dist/audit.nondeep.runtime-B4BaEaRU.js +842 -0
  82. package/dist/audit.runtime-rFjCrods.js +74 -0
  83. package/dist/auth-O6LQFLHJ.js +416 -0
  84. package/dist/auth-choice-DIBaxmAQ.js +219 -0
  85. package/dist/auth-choice-PrbpIjyg.js +610 -0
  86. package/dist/auth-choice-legacy-Clyw2lVc.js +17 -0
  87. package/dist/auth-choice-options-ohUw8QR-.js +127 -0
  88. package/dist/auth-choice-prompt-Cm0s-9Du.js +215 -0
  89. package/dist/auth-choice-prompt-DI-Xl1Nv.js +36 -0
  90. package/dist/auth-choice-rKBOd02a.js +64 -0
  91. package/dist/auth-choice.apply-helpers-BibBSEl9.js +66 -0
  92. package/dist/auth-choice.plugin-providers.runtime-gqF9NO7_.js +219 -0
  93. package/dist/auth-health-TWboMYA5.js +166 -0
  94. package/dist/auth-mode-policy-DywddkT-.js +18 -0
  95. package/dist/auth-profiles-CWEIQV77.js +1047 -0
  96. package/dist/auth-profiles.runtime-B98lwopF.js +48 -0
  97. package/dist/avatar-policy-Ds9e6uHI.js +67 -0
  98. package/dist/axios-xDDnM0KG.js +12831 -0
  99. package/dist/backup-create-B6JAR6jJ.js +461 -0
  100. package/dist/banner-bez5CpOK.js +351 -0
  101. package/dist/base-session-key-Cf2rkwag.js +14 -0
  102. package/dist/bindings-BV4AtNSY.js +21 -0
  103. package/dist/bindings-kjwuC11Q.js +69 -0
  104. package/dist/bluebubbles-C1M3Geg0.js +87 -0
  105. package/dist/bluebubbles-DRW3JdOY.js +603 -0
  106. package/dist/bluebubbles-dEl4QpYz.js +37 -0
  107. package/dist/bonjour-discovery-C2oY96BG.js +376 -0
  108. package/dist/boolean-DKtCJu_W.js +29 -0
  109. package/dist/boolean-param-xAGXUSSN.js +13 -0
  110. package/dist/boundary-file-read-BP6VMpqH.js +106 -0
  111. package/dist/boundary-path-B3FFLYNx.js +557 -0
  112. package/dist/brave-CkimJe4j.js +405 -0
  113. package/dist/brew-DSwWqzLd.js +44 -0
  114. package/dist/browser-cli-D3kBUBNc.js +1502 -0
  115. package/dist/bundled/boot-md/handler.js +381 -0
  116. package/dist/bundled/bootstrap-extra-files/handler.js +56 -0
  117. package/dist/bundled/command-logger/handler.js +62 -0
  118. package/dist/bundled/session-memory/handler.js +401 -0
  119. package/dist/call-BDvaXe4i.js +44 -0
  120. package/dist/call-BmLt3xO1.js +639 -0
  121. package/dist/catalog-BwAYUfL7.js +240 -0
  122. package/dist/channel-BBCuV5OT.js +4945 -0
  123. package/dist/channel-BDBXuqeg.js +321 -0
  124. package/dist/channel-C8h1Irxm.js +1284 -0
  125. package/dist/channel-CbGpFzo4.js +1602 -0
  126. package/dist/channel-Ci3K8fI9.js +1006 -0
  127. package/dist/channel-DGT5N1v7.js +1077 -0
  128. package/dist/channel-account-context-Bwa-YH_o.js +104 -0
  129. package/dist/channel-actions-DU2CR3xW.js +37 -0
  130. package/dist/channel-activity-B8aReQoE.js +35 -0
  131. package/dist/channel-config-3Uv6ve2_.js +115 -0
  132. package/dist/channel-config-helpers-CieQWILI.js +377 -0
  133. package/dist/channel-config-schema-DEVsCZpj.js +1 -0
  134. package/dist/channel-feedback-G6zh8efr.js +245 -0
  135. package/dist/channel-inbound-DwzVf2PK.js +395 -0
  136. package/dist/channel-lifecycle-CpU1dRbh.js +354 -0
  137. package/dist/channel-options-DJaIP4Dv.js +38 -0
  138. package/dist/channel-pairing-D54mn51y.js +66 -0
  139. package/dist/channel-plugin-common-BhTxCE5t.js +1 -0
  140. package/dist/channel-plugin-ids-CFeS3qir.js +26 -0
  141. package/dist/channel-plugin-resolution-DUngfdFj.js +112 -0
  142. package/dist/channel-policy-C4GKHvhz.js +1 -0
  143. package/dist/channel-reply-pipeline-CPTuaW8n.js +15 -0
  144. package/dist/channel-send-result-By8EpCPw.js +40 -0
  145. package/dist/channel-setup-Ck35g7zI.js +49 -0
  146. package/dist/channel-shared-LkXtTPXk.js +308 -0
  147. package/dist/channel-summary-CdYLGMVt.js +137 -0
  148. package/dist/channel-summary-D33z52ft.js +41 -0
  149. package/dist/channel-targets-DfnKGXez.js +87 -0
  150. package/dist/channel.runtime-DAyBR2A5.js +324 -0
  151. package/dist/channel.runtime-DVq5tC2D.js +35 -0
  152. package/dist/channel.runtime-DaLTDGtF.js +288 -0
  153. package/dist/channel.runtime-FKfTev2g.js +512 -0
  154. package/dist/channel.runtime-owqedh1t.js +268 -0
  155. package/dist/channel.runtime-wOTeiifp.js +230 -0
  156. package/dist/channels-CIHgkPea.js +408 -0
  157. package/dist/channels-ZXK6Jiuk.js +1393 -0
  158. package/dist/channels-cli-Bj6qSlkE.js +412 -0
  159. package/dist/channels-status-issues-C_U44M8Y.js +16 -0
  160. package/dist/chat-type-C-n03mQY.js +10 -0
  161. package/dist/clack-prompter-DuzDnaLi.js +112 -0
  162. package/dist/clawbot-cli-YNPuwmTB.js +218 -0
  163. package/dist/cli/daemon-cli.js +88 -0
  164. package/dist/cli-CW46WAZn.js +254 -0
  165. package/dist/cli-name-Daok7A7-.js +25 -0
  166. package/dist/cli-runtime-aAVwbEYy.js +7 -0
  167. package/dist/cli-utils-Np4NAAtt.js +39 -0
  168. package/dist/command-format-CYK9XiUC.js +16 -0
  169. package/dist/command-format-g8YUHNir.js +2 -0
  170. package/dist/command-gating-BQXGSqc9.js +40 -0
  171. package/dist/command-options-5coRiipK.js +25 -0
  172. package/dist/command-poll-backoff-CpkSns-6.js +56 -0
  173. package/dist/command-poll-backoff.runtime-YT6EGcLN.js +7 -0
  174. package/dist/command-registry-BI2MOs89.js +242 -0
  175. package/dist/command-registry-BMsxnuoC.js +14 -0
  176. package/dist/command-secret-gateway-ChXyZwos.js +211 -0
  177. package/dist/command-secret-targets-COcwhn-D.js +88 -0
  178. package/dist/command-secret-targets-CQJT3viO.js +3 -0
  179. package/dist/commands-Bb9xUwz9.js +42 -0
  180. package/dist/commands-core-C1usZXC2.js +4923 -0
  181. package/dist/commands-core.runtime-OTZivlO2.js +232 -0
  182. package/dist/commands-registry-ChCep1KJ.js +295 -0
  183. package/dist/commands-registry.data-XyUTELK9.js +904 -0
  184. package/dist/commands-registry.runtime-m5WTxFtv.js +25 -0
  185. package/dist/commands-status.runtime-C8_hpNgj.js +211 -0
  186. package/dist/commands.runtime-BZPnQKcW.js +232 -0
  187. package/dist/common-CUBlLRXB.js +457 -0
  188. package/dist/compact.runtime-Djmzpbn6.js +216 -0
  189. package/dist/completion-cli-D8tLgE5W.js +445 -0
  190. package/dist/completion-cli-Dz89naVA.js +17 -0
  191. package/dist/config-6sZwvXJD.js +88 -0
  192. package/dist/config-B7tPwoHZ.js +38 -0
  193. package/dist/config-DdDLrP_v.js +273 -0
  194. package/dist/config-cli-C10R8azD.js +945 -0
  195. package/dist/config-guard-B1c73BYQ.js +126 -0
  196. package/dist/config-helpers-3u5wfLBu.js +117 -0
  197. package/dist/config-pn7LKJdW.js +23 -0
  198. package/dist/config-presence-BmUF_5K9.js +79 -0
  199. package/dist/config-regex-CvZFnWkO.js +39 -0
  200. package/dist/config-runtime-CstET7fq.js +142 -0
  201. package/dist/config-schema-5YkIW1xw.js +270 -0
  202. package/dist/config-schema-B1UGMwZ8.js +31 -0
  203. package/dist/config-schema-DzlnsY3D.js +33 -0
  204. package/dist/config-state-CE0CGjey.js +288 -0
  205. package/dist/config-validation-CkVqgkHr.js +272 -0
  206. package/dist/config-value-DgJrpclm.js +25 -0
  207. package/dist/configure-DMkp7Sr4.js +1126 -0
  208. package/dist/configure-DOrQthLy.js +344 -0
  209. package/dist/connection-auth-BSQJeDOU.js +30 -0
  210. package/dist/constants-C_Scc680.js +71 -0
  211. package/dist/control-ui-assets-DjqeIg6A.js +232 -0
  212. package/dist/control-ui-shared-DP000Pxd.js +29 -0
  213. package/dist/conversation-runtime-1O0Aaolb.js +1458 -0
  214. package/dist/core-C7aHA4Aq.js +187 -0
  215. package/dist/core-command-descriptors-DCUYAEZd.js +96 -0
  216. package/dist/credentials-BPwBlm1X.js +265 -0
  217. package/dist/cron-cli-N2Hw_02d.js +579 -0
  218. package/dist/daemon-cli-DgfaF9xx.js +354 -0
  219. package/dist/daemon-install-CbclJo5M.js +134 -0
  220. package/dist/daemon-install-plan.shared-DK6BHlWI.js +222 -0
  221. package/dist/daemon-runtime-CbClrCwc.js +12 -0
  222. package/dist/dangerous-config-flags-BJtLWIk7.js +15 -0
  223. package/dist/dangerous-name-matching-DZa_t0RM.js +44 -0
  224. package/dist/dangerous-tools-yGPDFTHh.js +27 -0
  225. package/dist/date-time-DCAyaBop.js +118 -0
  226. package/dist/dedupe-Cgnk5BbX.js +55 -0
  227. package/dist/defaults-CEdZhIIb.js +6 -0
  228. package/dist/delegate-D4ql5N70.js +43 -0
  229. package/dist/deliver-B004w1Mv.js +212 -0
  230. package/dist/deliver-runtime-IYvc0giI.js +211 -0
  231. package/dist/delivery-queue-B19wDCjT.js +3 -0
  232. package/dist/delivery-queue-DrrqB4Hi.js +299 -0
  233. package/dist/device-auth-GEXe9vqR.js +15 -0
  234. package/dist/device-bootstrap-CwwokLEY.js +96 -0
  235. package/dist/device-bootstrap-Dbhe6oe8.js +1 -0
  236. package/dist/device-metadata-normalization-BDSQ_eA7.js +21 -0
  237. package/dist/device-pairing-cFWbBray.js +553 -0
  238. package/dist/devices-cli-DzycjFzS.js +366 -0
  239. package/dist/diagnostic-DqJXx_4Q.js +310 -0
  240. package/dist/diagnostic-events-ktCoG8Br.js +48 -0
  241. package/dist/diagnostics-CMhyGsPu.js +33 -0
  242. package/dist/diagnostics-DpLHpQ9c.js +14 -0
  243. package/dist/directive-handling.fast-lane-toP_ri_H.js +273 -0
  244. package/dist/directive-handling.impl-BRARyrsT.js +638 -0
  245. package/dist/directive-handling.impl-CUB4MOnK.js +214 -0
  246. package/dist/directive-handling.levels-8vnMeuGX.js +2 -0
  247. package/dist/directive-handling.levels-CoruY1AA.js +13 -0
  248. package/dist/directive-handling.persist.runtime-78Du6PgL.js +170 -0
  249. package/dist/directive-handling.shared-DCGUCHjn.js +147 -0
  250. package/dist/directory-cli-DVsDcgIU.js +437 -0
  251. package/dist/directory-config-helpers-CURJ8mj7.js +129 -0
  252. package/dist/directory-runtime-DhC8QkMq.js +19 -0
  253. package/dist/directory.static-DQaG9ohH.js +44 -0
  254. package/dist/discord-CYj8s73O.js +214 -0
  255. package/dist/discord-L9zvSHVn.js +635 -0
  256. package/dist/discord-core-5tkl-BzP.js +1 -0
  257. package/dist/dm-policy-shared-6bCJzHOS.js +188 -0
  258. package/dist/dns-cli-BMvHy265.js +223 -0
  259. package/dist/docker-XFNiArwM.js +1254 -0
  260. package/dist/docs-cli-BTaH94wD.js +176 -0
  261. package/dist/doctor-completion-DKx5m2UC.js +90 -0
  262. package/dist/doctor-config-preflight-BzQgc3_t.js +40 -0
  263. package/dist/doctor-config-preflight-DxVCut8L.js +150 -0
  264. package/dist/doctor-state-migrations-CTF66iAy.js +732 -0
  265. package/dist/doctor-state-migrations-D0VP4dUh.js +212 -0
  266. package/dist/entry-status-B2OWAf0s.js +172 -0
  267. package/dist/entry.js +210 -0
  268. package/dist/env-BP70DGuy.js +30 -0
  269. package/dist/env-overrides-JneV60sd.js +434 -0
  270. package/dist/env-overrides.runtime-DLrwions.js +18 -0
  271. package/dist/env-substitution-D6t_sLS_.js +136 -0
  272. package/dist/errors-BxyFnvP3.js +58 -0
  273. package/dist/exec-Dmex2w_d.js +310 -0
  274. package/dist/exec-approvals-BJhuySBz.js +386 -0
  275. package/dist/exec-approvals-allowlist-B_wPddCb.js +384 -0
  276. package/dist/exec-approvals-cli-C2dwhSkX.js +427 -0
  277. package/dist/exec-safe-bin-runtime-policy-BZkObC8r.js +89 -0
  278. package/dist/exec-safety-CaaBy-Zw.js +24 -0
  279. package/dist/extension-shared-5txN7IXK.js +74 -0
  280. package/dist/extensionAPI.js +218 -0
  281. package/dist/extensions/amazon-bedrock/index.js +231 -0
  282. package/dist/extensions/anthropic/index.js +330 -0
  283. package/dist/extensions/bluebubbles/index.js +224 -0
  284. package/dist/extensions/bluebubbles/setup-entry.js +289 -0
  285. package/dist/extensions/brave/index.js +23 -0
  286. package/dist/extensions/byteplus/index.js +112 -0
  287. package/dist/extensions/chutes/index.js +221 -0
  288. package/dist/extensions/cloudflare-ai-gateway/index.js +218 -0
  289. package/dist/extensions/copilot-proxy/index.js +125 -0
  290. package/dist/extensions/device-pair/index.js +1040 -0
  291. package/dist/extensions/discord/index.js +215 -0
  292. package/dist/extensions/discord/setup-entry.js +215 -0
  293. package/dist/extensions/elevenlabs/index.js +223 -0
  294. package/dist/extensions/fal/index.js +112 -0
  295. package/dist/extensions/feishu/index.js +227 -0
  296. package/dist/extensions/feishu/setup-entry.js +112 -0
  297. package/dist/extensions/firecrawl/index.js +211 -0
  298. package/dist/extensions/github-copilot/index.js +490 -0
  299. package/dist/extensions/google/index.js +211 -0
  300. package/dist/extensions/huggingface/index.js +108 -0
  301. package/dist/extensions/imessage/index.js +223 -0
  302. package/dist/extensions/imessage/setup-entry.js +220 -0
  303. package/dist/extensions/irc/index.js +220 -0
  304. package/dist/extensions/irc/setup-entry.js +222 -0
  305. package/dist/extensions/kilocode/index.js +282 -0
  306. package/dist/extensions/kimi-coding/index.js +148 -0
  307. package/dist/extensions/line/index.js +57 -0
  308. package/dist/extensions/line/setup-entry.js +49 -0
  309. package/dist/extensions/llm-task/index.js +157 -0
  310. package/dist/extensions/lobster/index.js +261 -0
  311. package/dist/extensions/mattermost/index.js +220 -0
  312. package/dist/extensions/mattermost/setup-entry.js +222 -0
  313. package/dist/extensions/memory-core/index.js +36 -0
  314. package/dist/extensions/microsoft/index.js +223 -0
  315. package/dist/extensions/minimax/index.js +437 -0
  316. package/dist/extensions/mistral/index.js +149 -0
  317. package/dist/extensions/modelstudio/index.js +144 -0
  318. package/dist/extensions/moonshot/index.js +211 -0
  319. package/dist/extensions/nextcloud-talk/index.js +221 -0
  320. package/dist/extensions/nextcloud-talk/setup-entry.js +223 -0
  321. package/dist/extensions/nvidia/index.js +29 -0
  322. package/dist/extensions/ollama/index.js +118 -0
  323. package/dist/extensions/open-prose/index.js +10 -0
  324. package/dist/extensions/openai/index.js +677 -0
  325. package/dist/extensions/opencode/index.js +116 -0
  326. package/dist/extensions/opencode-go/index.js +114 -0
  327. package/dist/extensions/openrouter/index.js +398 -0
  328. package/dist/extensions/openshell/index.js +923 -0
  329. package/dist/extensions/perplexity/index.js +23 -0
  330. package/dist/extensions/phone-control/index.js +276 -0
  331. package/dist/extensions/qianfan/index.js +114 -0
  332. package/dist/extensions/qwen-portal-auth/index.js +350 -0
  333. package/dist/extensions/sglang/index.js +285 -0
  334. package/dist/extensions/signal/index.js +218 -0
  335. package/dist/extensions/signal/setup-entry.js +218 -0
  336. package/dist/extensions/slack/index.js +222 -0
  337. package/dist/extensions/slack/setup-entry.js +220 -0
  338. package/dist/extensions/synology-chat/index.js +56 -0
  339. package/dist/extensions/synology-chat/setup-entry.js +58 -0
  340. package/dist/extensions/synthetic/index.js +112 -0
  341. package/dist/extensions/talk-voice/index.js +197 -0
  342. package/dist/extensions/tavily/index.js +211 -0
  343. package/dist/extensions/telegram/index.js +221 -0
  344. package/dist/extensions/telegram/setup-entry.js +221 -0
  345. package/dist/extensions/thread-ownership/index.js +70 -0
  346. package/dist/extensions/together/index.js +113 -0
  347. package/dist/extensions/venice/index.js +132 -0
  348. package/dist/extensions/vercel-ai-gateway/index.js +86 -0
  349. package/dist/extensions/vllm/index.js +285 -0
  350. package/dist/extensions/voice-call/index.js +5715 -0
  351. package/dist/extensions/volcengine/index.js +112 -0
  352. package/dist/extensions/xai/index.js +211 -0
  353. package/dist/extensions/xiaomi/index.js +115 -0
  354. package/dist/extensions/zai/index.js +559 -0
  355. package/dist/extensions/zalo/index.js +225 -0
  356. package/dist/extensions/zalo/setup-entry.js +226 -0
  357. package/dist/external-content-BUdUOqkv.js +238 -0
  358. package/dist/feishu-CgbwAF0e.js +2664 -0
  359. package/dist/feishu-Dh5fEbh5.js +59127 -0
  360. package/dist/fetch-guard-DIyN1HW5.js +165 -0
  361. package/dist/fetch-timeout-C5xpMuGd.js +36 -0
  362. package/dist/file-identity-Cw0fQxYY.js +11 -0
  363. package/dist/file-lock-WbEmczmY.js +107 -0
  364. package/dist/filter-oMGaNOM1.js +20 -0
  365. package/dist/format-DH8ysi7s.js +19 -0
  366. package/dist/format-datetime-BGS6tLDE.js +73 -0
  367. package/dist/format-duration-CO0BGWB0.js +57 -0
  368. package/dist/format-relative-C3nDxnXz.js +54 -0
  369. package/dist/frontmatter-S5vS-I4a.js +309 -0
  370. package/dist/fs-safe-D3qzH-ab.js +731 -0
  371. package/dist/gateway-cli-PQNp7o0j.js +28378 -0
  372. package/dist/gateway-install-token-DV5KjD4F.js +164 -0
  373. package/dist/gateway-rpc-C0Ey-rik.js +26 -0
  374. package/dist/gateway-runtime-ih2e7a2K.js +42 -0
  375. package/dist/gaxios-fetch-compat-KX6bsqFm.js +165 -0
  376. package/dist/gemini-auth-B5ljg7jr.js +29 -0
  377. package/dist/git-commit-BIdLubm5.js +2 -0
  378. package/dist/git-commit-OvUvjri2.js +177 -0
  379. package/dist/github-copilot-auth-Ccm-cBwy.js +104 -0
  380. package/dist/global-singleton-4KwY5RvX.js +13 -0
  381. package/dist/globals-41sdSaKv.js +38 -0
  382. package/dist/gmail-setup-utils-BX68dZla.js +419 -0
  383. package/dist/group-access-DJZrYPx1.js +113 -0
  384. package/dist/group-keys-BD_IYSMs.js +44 -0
  385. package/dist/group-policy-CWFxv3iB.js +201 -0
  386. package/dist/group-policy-warnings-Ddu6lBkh.js +175 -0
  387. package/dist/health-Bu1sbyYy.js +573 -0
  388. package/dist/health-Cbxc9Bn3.js +59 -0
  389. package/dist/health-format-B5XfOTuJ.js +26 -0
  390. package/dist/heartbeat-7aHh0m3d.js +169 -0
  391. package/dist/heartbeat-summary-Das49TYq.js +57 -0
  392. package/dist/help-CcbF7-ha.js +81 -0
  393. package/dist/help-format-Dv45FpYu.js +15 -0
  394. package/dist/helpers-DJ-5HEbE.js +24 -0
  395. package/dist/helpers-MxyaLZUk.js +32 -0
  396. package/dist/history-CHjo8B5W.js +102 -0
  397. package/dist/hook-runtime-BnNBi_q4.js +1 -0
  398. package/dist/hooks-cli-DUYK4RM1.js +1102 -0
  399. package/dist/hooks-policy-BL6HDLUn.js +20 -0
  400. package/dist/hooks-status-gzNmo3li.js +78 -0
  401. package/dist/host-env-security-BogNN146.js +223 -0
  402. package/dist/http-body-CCiSfloA.js +237 -0
  403. package/dist/http-registry-WFFbLYRd.js +153 -0
  404. package/dist/identity-DovQV4zD.js +112 -0
  405. package/dist/identity-cyBYcoXS.js +84 -0
  406. package/dist/identity-file-EndG1nfc.js +60 -0
  407. package/dist/image-generation-CNKc-mFK.js +441 -0
  408. package/dist/image-kJ7Tbov4.js +211 -0
  409. package/dist/image-ops-j01UkxEv.js +371 -0
  410. package/dist/imessage-B5pSMT47.js +219 -0
  411. package/dist/imessage-CoIuY1Ro.js +1451 -0
  412. package/dist/imessage-Cqjsq4VW.js +190 -0
  413. package/dist/imessage-core-CsYJuaRZ.js +1 -0
  414. package/dist/inbound-envelope-4P3IIJc3.js +61 -0
  415. package/dist/inbound-reply-dispatch-i2Vekqyy.js +72 -0
  416. package/dist/includes-7XyL3p1c.js +188 -0
  417. package/dist/includes-scan-y-rS6tTw.js +55 -0
  418. package/dist/index.js +57 -0
  419. package/dist/infra/warning-filter.js +2 -0
  420. package/dist/inspect-CcxlJ1ba.js +279 -0
  421. package/dist/install-safe-path-Rwbw1XCZ.js +62 -0
  422. package/dist/installs-iHi2aSjM.js +532 -0
  423. package/dist/interactive-F7iY0yED.js +8 -0
  424. package/dist/interactive-runtime-OweOj_Vv.js +90 -0
  425. package/dist/internal-hooks-0uipqzRY.js +156 -0
  426. package/dist/io-BX49DsSJ.js +35 -0
  427. package/dist/io-jOnQRia2.js +7178 -0
  428. package/dist/ip-C8vmzVu0.js +203 -0
  429. package/dist/ipv4-DAmsJVOV.js +82 -0
  430. package/dist/irc-AZ-Ec8be.js +12 -0
  431. package/dist/irc-CCSRuEC2.js +660 -0
  432. package/dist/is-main-YViS6wOn.js +27 -0
  433. package/dist/issue-format-CBEXVico.js +31 -0
  434. package/dist/issue-format-D3HehoKZ.js +4 -0
  435. package/dist/json-file-C2zjA0Gv.js +23 -0
  436. package/dist/json-files-WW-H_psG.js +60 -0
  437. package/dist/json-pointer-f9dEnBoR.js +43 -0
  438. package/dist/json-store-O1LwpnBH.js +37 -0
  439. package/dist/kb-cli-DMZs6PCu.js +65 -0
  440. package/dist/keyed-async-queue-CPUWV5Pm.js +32 -0
  441. package/dist/kill-tree-CbjXBw3z.js +149 -0
  442. package/dist/kilocode-shared-DS7_0IMs.js +29 -0
  443. package/dist/launchd-tyqGVx9U.js +491 -0
  444. package/dist/lazy-runtime-BcXbyAaC.js +1 -0
  445. package/dist/lazy-runtime-bWkd2cs3.js +29 -0
  446. package/dist/legacy-names-CUNZ4vHN.js +7 -0
  447. package/dist/legacy-web-search-BgZjNG2h.js +222 -0
  448. package/dist/lib-CERS7N4b.js +503 -0
  449. package/dist/lib-PPICrHv1.js +1938 -0
  450. package/dist/library-CZ461krl.js +211 -0
  451. package/dist/lifecycle-core-Dnxnw0oy.js +382 -0
  452. package/dist/line/accounts.js +10 -0
  453. package/dist/line/send.js +39 -0
  454. package/dist/line/template-messages.js +2 -0
  455. package/dist/line-CJSvwApm.js +1 -0
  456. package/dist/line-N9vL-2JB.js +688 -0
  457. package/dist/line-core-BOIxkjgu.js +1 -0
  458. package/dist/links-Bilm-v0z.js +13 -0
  459. package/dist/llm-slug-generator-BuAuQ5Ft.js +68 -0
  460. package/dist/llm-slug-generator.js +212 -0
  461. package/dist/llm-task-Dx8ymRFr.js +1 -0
  462. package/dist/local-roots-DAzCjWbC.js +34 -0
  463. package/dist/location-DefAH9WS.js +42 -0
  464. package/dist/logger-CoEtkjhn.js +550 -0
  465. package/dist/logger-Cqy7-Maj.js +70 -0
  466. package/dist/logging-B2wMcpWV.js +13 -0
  467. package/dist/logging-Bz1qZDPg.js +16 -0
  468. package/dist/logging-CArEWRgI.js +36 -0
  469. package/dist/logging-CbTTfADU.js +1 -0
  470. package/dist/logs-cli-BSjKwaur.js +261 -0
  471. package/dist/magic-string.es-DJPWMt-n.js +1011 -0
  472. package/dist/main-session-DKr0lBVk.js +36 -0
  473. package/dist/manager-ChTGDe87.js +2005 -0
  474. package/dist/manager-DuwFn87U.js +4226 -0
  475. package/dist/manager-runtime-E16jsvRe.js +59 -0
  476. package/dist/manager.runtime-F9F1eFiB.js +827 -0
  477. package/dist/manifest-registry-B90TyTWl.js +1350 -0
  478. package/dist/map-size-CMTQVKUV.js +15 -0
  479. package/dist/markdown-to-line-BWwaRx5F.js +640 -0
  480. package/dist/mask-api-key-CprzEe7l.js +10 -0
  481. package/dist/matrix-DzvdUw97.js +228 -0
  482. package/dist/matrix-migration-snapshot-adoDbNii.js +702 -0
  483. package/dist/mattermost-DO0BCfF3.js +1 -0
  484. package/dist/mattermost-SjOt4QDb.js +15 -0
  485. package/dist/mcp-cli-BC_VPl_o.js +94 -0
  486. package/dist/mcp-config-Coky4zS4.js +108 -0
  487. package/dist/media-limits-Cuvmmhop.js +14 -0
  488. package/dist/media-understanding-DD2uMjK8.js +48 -0
  489. package/dist/media-understanding.runtime-Kbb2bRmk.js +216 -0
  490. package/dist/memory-DBjQ0TPd.js +1 -0
  491. package/dist/memory-cli-Cm4Df0hJ.js +215 -0
  492. package/dist/memory-cli-yzqneSF8.js +541 -0
  493. package/dist/memory-search-Das1tiuB.js +204 -0
  494. package/dist/memory-search-DxmSTjHq.js +18 -0
  495. package/dist/mention-gating-B_q-EHFx.js +25 -0
  496. package/dist/mentions-Bxys_va0.js +154 -0
  497. package/dist/message-channel-Cy-gN4K2.js +106 -0
  498. package/dist/message-hook-mappers-BBTV3JRQ.js +249 -0
  499. package/dist/method-scopes-DgypDW23.js +2649 -0
  500. package/dist/mime-C4vVTBso.js +150 -0
  501. package/dist/minimal-C5yUxtHy.js +2120 -0
  502. package/dist/model-auth-B__TJTPw.js +309 -0
  503. package/dist/model-auth-env-CF9ts7Th.js +111 -0
  504. package/dist/model-catalog.runtime-CWh17vcc.js +211 -0
  505. package/dist/model-id-normalization-Y-MIsyK_.js +16 -0
  506. package/dist/model-input-BB2wSAHb.js +20 -0
  507. package/dist/model-overrides-sIzKU2wo.js +84 -0
  508. package/dist/model-param-b-DIFEhICm.js +15 -0
  509. package/dist/model-picker-CAPjetT3.js +400 -0
  510. package/dist/model-picker-DEw9viWc.js +215 -0
  511. package/dist/model-picker.runtime-ixYl7lB5.js +224 -0
  512. package/dist/model-selection-BTpJnslv.js +437 -0
  513. package/dist/model-selection-Ci9cPkL2.js +765 -0
  514. package/dist/model-suppression.runtime-D8cIb6Y5.js +216 -0
  515. package/dist/models-BQtc3khN.js +226 -0
  516. package/dist/models-CQgBV5dW.js +2536 -0
  517. package/dist/models-cli-DbQ-QpQk.js +418 -0
  518. package/dist/models-config-D2xK-G6c.js +211 -0
  519. package/dist/models-config.providers.discovery-BaIk1NKL.js +141 -0
  520. package/dist/models-config.runtime-Cf7q9uAQ.js +211 -0
  521. package/dist/monitor-B5QmKaD7.js +3272 -0
  522. package/dist/monitor-CL5OYLih.js +878 -0
  523. package/dist/monitor-CNZxrM4d.js +3145 -0
  524. package/dist/monitor-CyQVZdDh.js +223 -0
  525. package/dist/multimodal-DC43jYNv.js +75 -0
  526. package/dist/mutable-allowlist-detectors-C6EAzWYE.js +62 -0
  527. package/dist/net-DlJFp95v.js +270 -0
  528. package/dist/network-mode-DOgvmom4.js +17 -0
  529. package/dist/nextcloud-talk-ChMP88s-.js +12 -0
  530. package/dist/nextcloud-talk-CwnkUy8E.js +1 -0
  531. package/dist/node-cli-BZDC7rXg.js +2484 -0
  532. package/dist/node-command-policy-Bg2g6Xjp.js +192 -0
  533. package/dist/node-commands-B6W6Eo0b.js +11 -0
  534. package/dist/node-require-BgDD9bTi.js +14 -0
  535. package/dist/node-resolve-BunMro3f.js +69 -0
  536. package/dist/node-service-CEZZaqba.js +65 -0
  537. package/dist/node-startup-env-Gz8ZQniA.js +50 -0
  538. package/dist/nodes-cli-B4Jr9vct.js +1330 -0
  539. package/dist/nodes-screen-CQ7IvP62.js +401 -0
  540. package/dist/normalize-secret-input-_PgpexOG.js +32 -0
  541. package/dist/note-dfjacCV8.js +109 -0
  542. package/dist/npm-pack-install-CYNRv-vM.js +574 -0
  543. package/dist/npm-resolution-Ml2aA6Nu.js +60 -0
  544. package/dist/oauth.runtime-DA_48MPQ.js +687 -0
  545. package/dist/oauth.runtime-DS1ry5__.js +318 -0
  546. package/dist/oauth.runtime-qCkidk8J.js +180 -0
  547. package/dist/ollama-defaults-asNuGW4_.js +4 -0
  548. package/dist/onboard-BM6gO6Uw.js +589 -0
  549. package/dist/onboard-D9IU-7uw.js +48 -0
  550. package/dist/onboard-DQaHGPRm.js +25 -0
  551. package/dist/onboard-channels-BdQtLjYb.js +300 -0
  552. package/dist/onboard-channels-DIVUygs5.js +1257 -0
  553. package/dist/onboard-config-DFKb-0sE.js +29 -0
  554. package/dist/onboard-config-DYykzJhx.js +2 -0
  555. package/dist/onboard-custom-CDP4w1AT.js +216 -0
  556. package/dist/onboard-custom-DhJN13UV.js +644 -0
  557. package/dist/onboard-helpers-B7XTd4Pw.js +335 -0
  558. package/dist/onboard-helpers-BUKtx5Bq.js +54 -0
  559. package/dist/onboard-hooks-BHSSLAhI.js +73 -0
  560. package/dist/onboard-remote-BOzEPdHA.js +59 -0
  561. package/dist/onboard-remote-DVza19_k.js +182 -0
  562. package/dist/onboard-search-CrS-n9_3.js +446 -0
  563. package/dist/onboard-skills-BojzIPvk.js +133 -0
  564. package/dist/onboard-skills-D7HyCVjz.js +69 -0
  565. package/dist/openai-codex-provider.runtime-BFsopDHI.js +2 -0
  566. package/dist/openai-defaults-B7FUywsh.js +10 -0
  567. package/dist/openclaw-exec-env-AcZ9we1N.js +14 -0
  568. package/dist/openclaw-root-TUHYdr9B.js +88 -0
  569. package/dist/openclaw-tools.runtime-draZJo5r.js +211 -0
  570. package/dist/outbound-media-69yrWRDt.js +11 -0
  571. package/dist/outbound-runtime-ic_7ulJJ.js +1 -0
  572. package/dist/pairing-challenge-CNrPmmi9.js +48 -0
  573. package/dist/pairing-cli-BohXW2BK.js +150 -0
  574. package/dist/pairing-labels-CNKCSmBK.js +7 -0
  575. package/dist/pairing-message-CBv2njJT.js +4 -0
  576. package/dist/pairing-store-C4lsd4pO.js +590 -0
  577. package/dist/pairing-token-gKj4SNFJ.js +55 -0
  578. package/dist/parse-duration-BBGYkY0S.js +41 -0
  579. package/dist/parse-finite-number-CP4MQF_w.js +30 -0
  580. package/dist/parse-log-line-CVh9zu3Q.js +43 -0
  581. package/dist/parse-port-COyt3COn.js +8 -0
  582. package/dist/path-alias-guards-ZTKqurNH.js +40 -0
  583. package/dist/path-env-CPkz6U0Y.js +87 -0
  584. package/dist/paths-CTjJI9l0.js +179 -0
  585. package/dist/paths-GHJ97ebE.js +268 -0
  586. package/dist/paths-nCHyK08H.js +56 -0
  587. package/dist/perplexity-Beshd9zu.js +422 -0
  588. package/dist/persistent-dedupe-bjKjVI5u.js +116 -0
  589. package/dist/pi-embedded-CSQySvOV.js +168518 -0
  590. package/dist/pi-model-discovery-CuX5CDyZ.js +125 -0
  591. package/dist/pi-model-discovery-runtime-DNsMrX1n.js +44 -0
  592. package/dist/pi-tools.before-tool-call.runtime-DxVqzMVf.js +387 -0
  593. package/dist/platform-launcher-CqGy6UhP.js +83 -0
  594. package/dist/plugin-entry-CwuwM1jC.js +17 -0
  595. package/dist/plugin-install-JJwfOXtg.js +216 -0
  596. package/dist/plugin-install-plan-cixz1_W4.js +49 -0
  597. package/dist/plugin-install-vkpI1UNd.js +184 -0
  598. package/dist/plugin-registry-C3j_DUnj.js +51 -0
  599. package/dist/plugin-registry-DB_yxabS.js +213 -0
  600. package/dist/plugin-sdk/account-helpers.js +3 -0
  601. package/dist/plugin-sdk/account-id.js +2 -0
  602. package/dist/plugin-sdk/account-resolution.js +216 -0
  603. package/dist/plugin-sdk/acp-runtime.js +46 -0
  604. package/dist/plugin-sdk/agent-runtime.js +215 -0
  605. package/dist/plugin-sdk/allow-from.js +17 -0
  606. package/dist/plugin-sdk/allowlist-config-edit.js +2 -0
  607. package/dist/plugin-sdk/bluebubbles.js +232 -0
  608. package/dist/plugin-sdk/boolean-param.js +2 -0
  609. package/dist/plugin-sdk/channel-actions.js +16 -0
  610. package/dist/plugin-sdk/channel-config-helpers.js +15 -0
  611. package/dist/plugin-sdk/channel-config-schema.js +5 -0
  612. package/dist/plugin-sdk/channel-contract.js +1 -0
  613. package/dist/plugin-sdk/channel-feedback.js +4 -0
  614. package/dist/plugin-sdk/channel-inbound.js +53 -0
  615. package/dist/plugin-sdk/channel-lifecycle.js +2 -0
  616. package/dist/plugin-sdk/channel-pairing.js +2 -0
  617. package/dist/plugin-sdk/channel-policy.js +19 -0
  618. package/dist/plugin-sdk/channel-reply-pipeline.js +23 -0
  619. package/dist/plugin-sdk/channel-runtime.js +33 -0
  620. package/dist/plugin-sdk/channel-send-result.js +2 -0
  621. package/dist/plugin-sdk/channel-setup.js +24 -0
  622. package/dist/plugin-sdk/channel-targets.js +3 -0
  623. package/dist/plugin-sdk/cli-runtime.js +5 -0
  624. package/dist/plugin-sdk/command-auth.js +212 -0
  625. package/dist/plugin-sdk/compat.js +59 -0
  626. package/dist/plugin-sdk/config-runtime.js +51 -0
  627. package/dist/plugin-sdk/conversation-runtime.js +55 -0
  628. package/dist/plugin-sdk/core.js +40 -0
  629. package/dist/plugin-sdk/device-bootstrap.js +6 -0
  630. package/dist/plugin-sdk/diagnostics-otel.js +7 -0
  631. package/dist/plugin-sdk/diffs.js +3 -0
  632. package/dist/plugin-sdk/directory-runtime.js +5 -0
  633. package/dist/plugin-sdk/discord-core.js +26 -0
  634. package/dist/plugin-sdk/discord.js +219 -0
  635. package/dist/plugin-sdk/extension-shared.js +15 -0
  636. package/dist/plugin-sdk/feishu.js +101 -0
  637. package/dist/plugin-sdk/gateway-runtime.js +46 -0
  638. package/dist/plugin-sdk/googlechat.js +93 -0
  639. package/dist/plugin-sdk/group-access.js +2 -0
  640. package/dist/plugin-sdk/hook-runtime.js +12 -0
  641. package/dist/plugin-sdk/image-generation.js +51 -0
  642. package/dist/plugin-sdk/imessage-core.js +214 -0
  643. package/dist/plugin-sdk/imessage.js +223 -0
  644. package/dist/plugin-sdk/index.js +55 -0
  645. package/dist/plugin-sdk/infra-runtime.js +223 -0
  646. package/dist/plugin-sdk/interactive-runtime.js +3 -0
  647. package/dist/plugin-sdk/irc.js +232 -0
  648. package/dist/plugin-sdk/json-store.js +9 -0
  649. package/dist/plugin-sdk/keyed-async-queue.js +2 -0
  650. package/dist/plugin-sdk/lazy-runtime.js +2 -0
  651. package/dist/plugin-sdk/line-core.js +29 -0
  652. package/dist/plugin-sdk/line.js +22 -0
  653. package/dist/plugin-sdk/llm-task.js +7 -0
  654. package/dist/plugin-sdk/matrix-runtime-heavy.js +223 -0
  655. package/dist/plugin-sdk/matrix-runtime-shared.js +2 -0
  656. package/dist/plugin-sdk/matrix.js +84 -0
  657. package/dist/plugin-sdk/mattermost.js +233 -0
  658. package/dist/plugin-sdk/media-runtime.js +212 -0
  659. package/dist/plugin-sdk/media-understanding-runtime.js +211 -0
  660. package/dist/plugin-sdk/media-understanding.js +15 -0
  661. package/dist/plugin-sdk/memory-core.js +2 -0
  662. package/dist/plugin-sdk/memory-lancedb.js +2 -0
  663. package/dist/plugin-sdk/msteams.js +244 -0
  664. package/dist/plugin-sdk/nextcloud-talk.js +231 -0
  665. package/dist/plugin-sdk/nostr.js +50 -0
  666. package/dist/plugin-sdk/ollama-setup.js +33 -0
  667. package/dist/plugin-sdk/outbound-runtime.js +23 -0
  668. package/dist/plugin-sdk/plugin-entry.js +3 -0
  669. package/dist/plugin-sdk/plugin-runtime.js +211 -0
  670. package/dist/plugin-sdk/process-runtime.js +12 -0
  671. package/dist/plugin-sdk/provider-auth-api-key.js +53 -0
  672. package/dist/plugin-sdk/provider-auth-login.js +2 -0
  673. package/dist/plugin-sdk/provider-auth.js +37 -0
  674. package/dist/plugin-sdk/provider-catalog.js +2 -0
  675. package/dist/plugin-sdk/provider-env-vars.js +2 -0
  676. package/dist/plugin-sdk/provider-google.js +3 -0
  677. package/dist/plugin-sdk/provider-models.js +27 -0
  678. package/dist/plugin-sdk/provider-onboard.js +20 -0
  679. package/dist/plugin-sdk/provider-setup.js +58 -0
  680. package/dist/plugin-sdk/provider-stream.js +211 -0
  681. package/dist/plugin-sdk/provider-usage.js +18 -0
  682. package/dist/plugin-sdk/provider-web-search.js +23 -0
  683. package/dist/plugin-sdk/provider-zai-endpoint.js +3 -0
  684. package/dist/plugin-sdk/reply-history.js +23 -0
  685. package/dist/plugin-sdk/reply-payload.js +2 -0
  686. package/dist/plugin-sdk/reply-runtime.js +211 -0
  687. package/dist/plugin-sdk/request-url.js +2 -0
  688. package/dist/plugin-sdk/routing.js +25 -0
  689. package/dist/plugin-sdk/runtime-env.js +15 -0
  690. package/dist/plugin-sdk/runtime-store.js +2 -0
  691. package/dist/plugin-sdk/runtime.js +15 -0
  692. package/dist/plugin-sdk/sandbox.js +66 -0
  693. package/dist/plugin-sdk/secret-input.js +3 -0
  694. package/dist/plugin-sdk/security-runtime.js +18 -0
  695. package/dist/plugin-sdk/self-hosted-provider-setup.js +33 -0
  696. package/dist/plugin-sdk/setup-adapter-runtime.js +2 -0
  697. package/dist/plugin-sdk/setup-runtime.js +17 -0
  698. package/dist/plugin-sdk/setup-tools.js +21 -0
  699. package/dist/plugin-sdk/setup.js +26 -0
  700. package/dist/plugin-sdk/signal.js +220 -0
  701. package/dist/plugin-sdk/slack-core.js +22 -0
  702. package/dist/plugin-sdk/slack.js +223 -0
  703. package/dist/plugin-sdk/speech-runtime.js +213 -0
  704. package/dist/plugin-sdk/speech.js +212 -0
  705. package/dist/plugin-sdk/ssrf-runtime.js +5 -0
  706. package/dist/plugin-sdk/state-paths.js +3 -0
  707. package/dist/plugin-sdk/status-helpers.js +9 -0
  708. package/dist/plugin-sdk/telegram-core.js +23 -0
  709. package/dist/plugin-sdk/telegram.js +224 -0
  710. package/dist/plugin-sdk/testing.js +13174 -0
  711. package/dist/plugin-sdk/text-runtime.js +45 -0
  712. package/dist/plugin-sdk/thread-bindings-runtime.js +2 -0
  713. package/dist/plugin-sdk/thread-ownership.js +2 -0
  714. package/dist/plugin-sdk/tlon.js +57 -0
  715. package/dist/plugin-sdk/tool-send.js +2 -0
  716. package/dist/plugin-sdk/twitch.js +46 -0
  717. package/dist/plugin-sdk/voice-call.js +213 -0
  718. package/dist/plugin-sdk/web-media.js +28 -0
  719. package/dist/plugin-sdk/webhook-ingress.js +7 -0
  720. package/dist/plugin-sdk/webhook-path.js +2 -0
  721. package/dist/plugin-sdk/whatsapp-core.js +219 -0
  722. package/dist/plugin-sdk/whatsapp-shared.js +18 -0
  723. package/dist/plugin-sdk/windows-spawn.js +2 -0
  724. package/dist/plugin-sdk/zalo.js +239 -0
  725. package/dist/plugin-sdk/zalouser.js +240 -0
  726. package/dist/plugins/build-smoke-entry.js +211 -0
  727. package/dist/plugins/runtime/index.js +229 -0
  728. package/dist/plugins-1Z50ecJ6.js +1 -0
  729. package/dist/plugins-C6fKmNuA.js +7 -0
  730. package/dist/plugins-cli-BkgQkGaU.js +1192 -0
  731. package/dist/policy-CpkbSAfm.js +60 -0
  732. package/dist/polls-B2VH7SN9.js +35 -0
  733. package/dist/ports-BjWuIIQw.js +262 -0
  734. package/dist/ports-DFiK_Jc-.js +385 -0
  735. package/dist/ports-lsof-DtJqhFOr.js +25 -0
  736. package/dist/ports-probe-BQqp8l8E.js +14 -0
  737. package/dist/preflight-audio.runtime-Fi9mofpp.js +216 -0
  738. package/dist/probe-BM9sbCgS.js +20 -0
  739. package/dist/probe-DLBOZftS.js +134 -0
  740. package/dist/probe-auth-Bjp3G4CI.js +48 -0
  741. package/dist/probe-auth-DMSPTRRk.js +45 -0
  742. package/dist/process-runtime-C7el-Ri4.js +1 -0
  743. package/dist/process-scoped-map-C4gOa-gv.js +61 -0
  744. package/dist/profile-utils-BcMYGFPT.js +15 -0
  745. package/dist/profiles-D17eMKQZ.js +683 -0
  746. package/dist/program-Ch-76sgl.js +155 -0
  747. package/dist/program-context-BMWNUfqL.js +10 -0
  748. package/dist/program-context-CD_RvRYh.js +2 -0
  749. package/dist/progress-D1r9bZU1.js +132 -0
  750. package/dist/prompt-select-styled-NUKYS9QR.js +4879 -0
  751. package/dist/prompt-style-BvciNCqy.js +7 -0
  752. package/dist/prompts-NtuylUyl.js +9 -0
  753. package/dist/prototype-keys-Cm_8mWvq.js +11 -0
  754. package/dist/provider-api-key-auth-BE0taXiB.js +108 -0
  755. package/dist/provider-api-key-auth.runtime-jDZZUAMX.js +34 -0
  756. package/dist/provider-auth-Bw8x1a3o.js +58 -0
  757. package/dist/provider-auth-api-key-BrQYvdxi.js +1 -0
  758. package/dist/provider-auth-choice-BYbPq0eC.js +128 -0
  759. package/dist/provider-auth-choice-helpers-Bj1GkOSn.js +48 -0
  760. package/dist/provider-auth-choice-preference-tKq5gaJL.js +192 -0
  761. package/dist/provider-auth-choice.runtime-DegPpvRJ.js +223 -0
  762. package/dist/provider-auth-choices-QSilukI1.js +58 -0
  763. package/dist/provider-auth-guidance-gninjjq8.js +34 -0
  764. package/dist/provider-auth-helpers-B0dS-1WK.js +86 -0
  765. package/dist/provider-auth-input-BftBdgvW.js +112 -0
  766. package/dist/provider-auth-login-D0n0lMuc.js +8 -0
  767. package/dist/provider-auth-login.runtime-LvuBkQrc.js +243 -0
  768. package/dist/provider-auth-mode-sTdccIKL.js +20 -0
  769. package/dist/provider-auth-ref-BS3gwrNr.js +168 -0
  770. package/dist/provider-auth-ref-BmEcEN7K.js +3 -0
  771. package/dist/provider-catalog--18-pW5t.js +11 -0
  772. package/dist/provider-catalog-2P2hel74.js +48 -0
  773. package/dist/provider-catalog-B0FqWSwe.js +48 -0
  774. package/dist/provider-catalog-BvORKzzD.js +91 -0
  775. package/dist/provider-catalog-C34j1_or.js +26 -0
  776. package/dist/provider-catalog-C5vmXjmb.js +11 -0
  777. package/dist/provider-catalog-CBufm2Dr.js +36 -0
  778. package/dist/provider-catalog-D7QvsUXS.js +12 -0
  779. package/dist/provider-catalog-DKy_dzQZ.js +41 -0
  780. package/dist/provider-env-vars-CsQlY7bF.js +110 -0
  781. package/dist/provider-id-BpXo5t6v.js +31 -0
  782. package/dist/provider-model-allowlist-4HSOnlX-.js +24 -0
  783. package/dist/provider-model-primary-NJ-xlhec.js +53 -0
  784. package/dist/provider-models-C2EjYMwW.js +2416 -0
  785. package/dist/provider-oauth-flow-BQN6F6EC.js +33 -0
  786. package/dist/provider-ollama-setup-DhQvDwAj.js +309 -0
  787. package/dist/provider-onboard-CjOfyeQG.js +1 -0
  788. package/dist/provider-onboarding-config-DOZ3pFA6.js +165 -0
  789. package/dist/provider-openai-codex-oauth-tls-Bo8U4D3E.js +101 -0
  790. package/dist/provider-runtime.runtime-DnP2jpoM.js +211 -0
  791. package/dist/provider-self-hosted-setup-CUrmsugW.js +182 -0
  792. package/dist/provider-usage-ClDVmkhl.js +633 -0
  793. package/dist/provider-usage-DIC6cn-3.js +211 -0
  794. package/dist/provider-web-search-NzK8ep1E.js +507 -0
  795. package/dist/provider-wizard-C6jCuyQe.js +236 -0
  796. package/dist/provider-zai-endpoint-DeDABzT4.js +106 -0
  797. package/dist/proxy-H5O2p6AP.js +121 -0
  798. package/dist/proxy-env-DG2u55RW.js +40 -0
  799. package/dist/push-apns-D4zD2tmP.js +1050 -0
  800. package/dist/pw-ai-BuPUVeUK.js +1876 -0
  801. package/dist/qmd-manager-BpygGMW9.js +1571 -0
  802. package/dist/qr-cli-DnWHXcxh.js +370 -0
  803. package/dist/qr-cli-yaZ0FZ6z.js +213 -0
  804. package/dist/query-expansion-Do45hILP.js +1114 -0
  805. package/dist/reactions-BcC_XZqD.js +281 -0
  806. package/dist/read-only-account-inspect-DPJzadPo.js +42 -0
  807. package/dist/read-only-account-inspect.discord.runtime-CW9DDKH8.js +216 -0
  808. package/dist/read-only-account-inspect.slack.runtime-BcXBPyh3.js +216 -0
  809. package/dist/read-only-account-inspect.telegram.runtime-Y7h0Jbdj.js +216 -0
  810. package/dist/redact-BDinS1q9.js +102 -0
  811. package/dist/redact-identifier-FUiWQxv5.js +13 -0
  812. package/dist/redact-snapshot-DBPmeYy2.js +2654 -0
  813. package/dist/ref-contract-CCBBbf1r.js +53 -0
  814. package/dist/register-CppP7Ddc.js +43 -0
  815. package/dist/register.agent-BOD5ROGQ.js +546 -0
  816. package/dist/register.backup-Y2VGqcRu.js +269 -0
  817. package/dist/register.configure-1qiTINph.js +354 -0
  818. package/dist/register.maintenance-shn-zigv.js +694 -0
  819. package/dist/register.message-mR4CLSoo.js +812 -0
  820. package/dist/register.onboard-CkDryVid.js +298 -0
  821. package/dist/register.setup-B0xW5olD.js +318 -0
  822. package/dist/register.status-health-sessions-CuhWc03j.js +604 -0
  823. package/dist/register.subclis-BazXM5TW.js +315 -0
  824. package/dist/register.subclis-C2d8UDhH.js +13 -0
  825. package/dist/registry-C3q59Qj0.js +55 -0
  826. package/dist/registry-CPsHw6xU.js +219 -0
  827. package/dist/registry-CxgtJ09C.js +28 -0
  828. package/dist/registry-rgYi7KoO.js +160 -0
  829. package/dist/repair-qXnOAvDy.js +105 -0
  830. package/dist/replies-EiwmmZ_W.js +122 -0
  831. package/dist/reply-history-CVCD5oE9.js +1 -0
  832. package/dist/reply-payload-DBGc074f.js +232 -0
  833. package/dist/report-cli-DB1jQx32.js +42 -0
  834. package/dist/request-url-BKfWAQx8.js +10 -0
  835. package/dist/resolve-Ckjd8TAk.js +14 -0
  836. package/dist/resolve-T2q_0ARF.js +619 -0
  837. package/dist/resolve-route-vEY3ONZ2.js +466 -0
  838. package/dist/resolve-utils-CbqJY2bs.js +102 -0
  839. package/dist/response-generator-VdoCcQ3y.js +153 -0
  840. package/dist/restart-stale-pids-CLGiqU2E.js +187 -0
  841. package/dist/retry-D15TD1S3.js +168 -0
  842. package/dist/root-help-B9Aou4ho.js +32 -0
  843. package/dist/routes-TpLEcKO8.js +7084 -0
  844. package/dist/routing-Y3m0o-kB.js +26 -0
  845. package/dist/rpc-C6MN-nVc.js +67 -0
  846. package/dist/run-command-DRKv5Lj6.js +32 -0
  847. package/dist/run-main-YZSMdx0B.js +424 -0
  848. package/dist/run-with-concurrency-BrSjWzpg.js +41 -0
  849. package/dist/runtime-B66W9flm.js +43 -0
  850. package/dist/runtime-C9VaVKYZ.js +2338 -0
  851. package/dist/runtime-CT2LIJZu.js +91 -0
  852. package/dist/runtime-CqDQ81eY.js +143 -0
  853. package/dist/runtime-CuvWMN7E.js +89 -0
  854. package/dist/runtime-D4_OpzA1.js +5 -0
  855. package/dist/runtime-DP-4DZja.js +5 -0
  856. package/dist/runtime-Dl17x_cV.js +1 -0
  857. package/dist/runtime-Z35JoYPC.js +30 -0
  858. package/dist/runtime-api-D79M0lQN.js +1 -0
  859. package/dist/runtime-api-y3zfnQGK.js +39 -0
  860. package/dist/runtime-discord-ops.runtime-Bg5h5v9-.js +234 -0
  861. package/dist/runtime-env-a_iwdJIv.js +1 -0
  862. package/dist/runtime-forwarders-DtMc8rBP.js +44 -0
  863. package/dist/runtime-group-policy-B7irU4eu.js +59 -0
  864. package/dist/runtime-guard-y62lPDGY.js +58 -0
  865. package/dist/runtime-parse-CeqXmZHJ.js +84 -0
  866. package/dist/runtime-paths-CstaCCMi.js +334 -0
  867. package/dist/runtime-slack-ops.runtime-BumgKDhS.js +226 -0
  868. package/dist/runtime-status-CgL02wYX.js +15 -0
  869. package/dist/runtime-store-Bt3Sdbrn.js +22 -0
  870. package/dist/runtime-telegram-ops.runtime-rSLQ3KrE.js +233 -0
  871. package/dist/runtime-whatsapp-boundary-xZem0NyQ.js +364 -0
  872. package/dist/safe-open-sync-Bt9R1Mnf.js +83 -0
  873. package/dist/safe-regex-tLlDZYfM.js +244 -0
  874. package/dist/safe-text-B_CQuica.js +16 -0
  875. package/dist/sandbox-CUUouiKs.js +2795 -0
  876. package/dist/sandbox-cli-BN8y0Get.js +499 -0
  877. package/dist/sandbox-paths-fqp_TZdO.js +144 -0
  878. package/dist/sandbox-qSs4h3sk.js +1 -0
  879. package/dist/sanitize-env-vars-vNSNqm0y.js +74 -0
  880. package/dist/scan-paths-BJmvUZ1E.js +28 -0
  881. package/dist/search-manager-DWhFgwyp.js +17 -0
  882. package/dist/search-manager-r8Cw4ZRv.js +392 -0
  883. package/dist/secret-equal-ObQfyZGa.js +9 -0
  884. package/dist/secret-file-Ch0yuOXR.js +11 -0
  885. package/dist/secret-file-DYJtH6kf.js +92 -0
  886. package/dist/secret-input-4REZ4sHo.js +35 -0
  887. package/dist/secrets-cli-D1df8b0o.js +2304 -0
  888. package/dist/secure-random-Cs8tw_HQ.js +10 -0
  889. package/dist/security-cli-V66ESmdT.js +676 -0
  890. package/dist/security-runtime-BuEhpJVE.js +23 -0
  891. package/dist/send-3tabvle6.js +100 -0
  892. package/dist/send-CC5J3tyW.js +1026 -0
  893. package/dist/send-deps-CrFMNvqO.js +19 -0
  894. package/dist/send-i2-mdtiE.js +250 -0
  895. package/dist/server-BTOjmlyi.js +116 -0
  896. package/dist/server-middleware-CCqKhKUb.js +106 -0
  897. package/dist/server-node-events-D6y22Tt8.js +611 -0
  898. package/dist/server-startup-matrix-migration-DHWSoS73.js +1595 -0
  899. package/dist/service-Bxc9uL2e.js +774 -0
  900. package/dist/service-CBLajPZL.js +21 -0
  901. package/dist/session-cost-usage-BmbaBvk4.js +212 -0
  902. package/dist/session-cost-usage-C30Jl2SI.js +615 -0
  903. package/dist/session-fork.runtime-BZfcC1Nc.js +51 -0
  904. package/dist/session-key-gFFk3uv9.js +216 -0
  905. package/dist/session-write-lock-DNKvpjKf.js +324 -0
  906. package/dist/sessions-BIH_j_XS.js +222 -0
  907. package/dist/sessions-D5dWcxC_.js +212 -0
  908. package/dist/sessions-DaSBVNwD.js +669 -0
  909. package/dist/setup-C2XF1YH3.js +397 -0
  910. package/dist/setup-CN-teRpz.js +8 -0
  911. package/dist/setup-adapter-runtime-Bjv2adwG.js +1 -0
  912. package/dist/setup-binary-BOJA7zdN.js +30 -0
  913. package/dist/setup-browser-BhNPCUtK.js +71 -0
  914. package/dist/setup-core-BsG09DZH.js +149 -0
  915. package/dist/setup-core-D-O1GQax.js +162 -0
  916. package/dist/setup-core-Dtm54Rcq.js +510 -0
  917. package/dist/setup-entry-B1mTa7bU.js +10 -0
  918. package/dist/setup-entry-CTMgw-K5.js +13 -0
  919. package/dist/setup-entry-Cmd_cufO.js +13 -0
  920. package/dist/setup-entry-CybgA3zP.js +12 -0
  921. package/dist/setup-entry-DED_hL6i.js +12 -0
  922. package/dist/setup-entry-WCq9VMWx.js +14 -0
  923. package/dist/setup-group-access-BtPApRvE.js +70 -0
  924. package/dist/setup-helpers-B62Ecg9r.js +362 -0
  925. package/dist/setup-surface-B7A7qowY.js +452 -0
  926. package/dist/setup-surface-BBYJVRXc.js +380 -0
  927. package/dist/setup-surface-CFUz_BJi.js +298 -0
  928. package/dist/setup-tools-BPiMjAN7.js +1 -0
  929. package/dist/setup-wizard-helpers-COZ1UAdX.js +770 -0
  930. package/dist/setup-wizard-proxy-Slwi-1gX.js +116 -0
  931. package/dist/setup.finalize-B8O01nge.js +633 -0
  932. package/dist/setup.gateway-config-BPDIFk__.js +288 -0
  933. package/dist/setup.secret-input-BL-bqJpt.js +25 -0
  934. package/dist/shared-AygSbeCK.js +50 -0
  935. package/dist/shared-BHqDLkMG.js +127 -0
  936. package/dist/shared-BPtG8PgB.js +70 -0
  937. package/dist/shared-BU0QgVMZ.js +36 -0
  938. package/dist/shared-Bzr2UyEm.js +351 -0
  939. package/dist/shared-C_XXbGIF.js +87 -0
  940. package/dist/shared-Diw3KzwZ.js +82 -0
  941. package/dist/shared-DngjQumT.js +196 -0
  942. package/dist/shared-DzH3zmAy.js +64 -0
  943. package/dist/shared-LeP8iUTz.js +54 -0
  944. package/dist/shell-argv-DWV43Vya.js +72 -0
  945. package/dist/shell-env-cD92jEyV.js +181 -0
  946. package/dist/signal-BQd9f9dF.js +315 -0
  947. package/dist/signal-Ca7y47bM.js +46 -0
  948. package/dist/signal-Did9U_fa.js +214 -0
  949. package/dist/signal-cli-install-DxoL8CgF.js +188 -0
  950. package/dist/skill-commands-CiSwTFBQ.js +652 -0
  951. package/dist/skill-commands.runtime-CEwlWT4j.js +34 -0
  952. package/dist/skill-scanner-DG7MT7pu.js +354 -0
  953. package/dist/skills-BC8GJ9Rp.js +22 -0
  954. package/dist/skills-CCgKs_NJ.js +863 -0
  955. package/dist/skills-cli-dhYXJCuL.js +339 -0
  956. package/dist/skills-install-DiriUXJd.js +763 -0
  957. package/dist/skills-status-BmQTn4jL.js +23 -0
  958. package/dist/skills-status-a9b899Y3.js +169 -0
  959. package/dist/slack-CXgv7nu7.js +730 -0
  960. package/dist/slack-CcSByPzI.js +217 -0
  961. package/dist/slack-CtcCh0Lj.js +24537 -0
  962. package/dist/slack-core-DcsbATUs.js +1 -0
  963. package/dist/slash-commands.runtime-kO8EUKYW.js +228 -0
  964. package/dist/slash-dispatch.runtime-Bu2yMeFy.js +238 -0
  965. package/dist/slash-skill-commands.runtime-wwX3tF84.js +216 -0
  966. package/dist/speech-bSreRuDH.js +1 -0
  967. package/dist/speech-runtime-y1FcnGVA.js +1 -0
  968. package/dist/src-CmXHIz5f.js +846 -0
  969. package/dist/ssh-config-ChqR6ijV.js +77 -0
  970. package/dist/ssh-tunnel-Cz51VBAt.js +159 -0
  971. package/dist/ssh-tunnel-DWze2IQS.js +16 -0
  972. package/dist/ssrf-Dk9XaoKN.js +220 -0
  973. package/dist/ssrf-policy-Dk6oMa20.js +69 -0
  974. package/dist/ssrf-runtime-C-mAQLVA.js +1 -0
  975. package/dist/stagger-DU7FjHYo.js +54 -0
  976. package/dist/state-paths-DJIGEFq_.js +1 -0
  977. package/dist/status-69r8-Zey.js +75 -0
  978. package/dist/status-BdLTvZOL.js +44 -0
  979. package/dist/status-Bt7DQmRI.js +1665 -0
  980. package/dist/status-CoUFSBgt.js +202 -0
  981. package/dist/status-DbI3Kbh5.js +235 -0
  982. package/dist/status-DeKlzu_o.js +212 -0
  983. package/dist/status-Haie42Fc.js +606 -0
  984. package/dist/status-helpers-Cda-rGLX.js +101 -0
  985. package/dist/status-json-DS1M_MWJ.js +322 -0
  986. package/dist/status.link-channel-Cb8bZ_Od.js +40 -0
  987. package/dist/status.scan.deps.runtime-7_6VUs50.js +77 -0
  988. package/dist/status.scan.runtime-DtR8BIE9.js +14 -0
  989. package/dist/status.summary-l_Bi1buR.js +600 -0
  990. package/dist/status.summary.runtime-Cng6MzRU.js +151 -0
  991. package/dist/status.update-DtbnnPKx.js +79 -0
  992. package/dist/store-CvL8MPei.js +1446 -0
  993. package/dist/store.runtime-hgnvmZgO.js +43 -0
  994. package/dist/string-normalization-CvzuCAZv.js +19 -0
  995. package/dist/string-sample-BOLqzr4Y.js +11 -0
  996. package/dist/subagent-orphan-recovery-si1z2iBu.js +407 -0
  997. package/dist/subagent-registry-runtime-CXUDI8gL.js +211 -0
  998. package/dist/subcli-descriptors-CY_nHzpZ.js +151 -0
  999. package/dist/subsystem-CUp-6QQf.js +421 -0
  1000. package/dist/synology-chat-CdejNfs0.js +12 -0
  1001. package/dist/system-cli-DkOaXHkQ.js +99 -0
  1002. package/dist/system-events-mAu6Ap6K.js +75 -0
  1003. package/dist/system-message-DA9eUYzB.js +16 -0
  1004. package/dist/system-run-command-Cxq2F1MB.js +258 -0
  1005. package/dist/systemd-CrxZBFae.js +557 -0
  1006. package/dist/systemd-hints-y-zJ9aTm.js +315 -0
  1007. package/dist/systemd-linger-BdklDcLg.js +16 -0
  1008. package/dist/systemd-linger-DLrbG9_d.js +68 -0
  1009. package/dist/table-DFMOhmNZ.js +305 -0
  1010. package/dist/tailnet-ofqBrXzu.js +38 -0
  1011. package/dist/tailscale-Cbsx-2HB.js +254 -0
  1012. package/dist/target-errors-ksphhzJg.js +26 -0
  1013. package/dist/target-registry-krAVlXi_.js +1321 -0
  1014. package/dist/telegram/audit.js +2 -0
  1015. package/dist/telegram/token.js +211 -0
  1016. package/dist/telegram-BjDUP22F.js +10910 -0
  1017. package/dist/telegram-DMiNSGAJ.js +575 -0
  1018. package/dist/telegram-Dt11B3JL.js +218 -0
  1019. package/dist/telegram-core-B4Jo-uko.js +1 -0
  1020. package/dist/template-messages-kh7VfgOb.js +214 -0
  1021. package/dist/text-chunking-CUf5WgqG.js +19 -0
  1022. package/dist/text-format-sFXlJfHH.js +8 -0
  1023. package/dist/text-runtime-C_Roi_Je.js +1418 -0
  1024. package/dist/theme-B5HDbQfl.js +2 -0
  1025. package/dist/theme-CdOoMzRk.js +34 -0
  1026. package/dist/thinking-BBD_0HSp.js +68 -0
  1027. package/dist/thinking.shared-CncvRHts.js +246 -0
  1028. package/dist/thread-bindings-messages-Cdo8jSa9.js +229 -0
  1029. package/dist/thread-bindings-policy-DMjOaNyR.js +119 -0
  1030. package/dist/thread-bindings-runtime-Ckwk3Uuz.js +1 -0
  1031. package/dist/threading-helpers-Cq55SUtb.js +14 -0
  1032. package/dist/timeouts-BwR1sGom.js +72 -0
  1033. package/dist/tmp-openclaw-dir-idKIOMmb.js +102 -0
  1034. package/dist/token-Bgv8XEsC.js +50 -0
  1035. package/dist/tool-catalog-BV6FcEWS.js +337 -0
  1036. package/dist/tool-policy-match-CHqTCSdK.js +46 -0
  1037. package/dist/tool-send-9LXKcrda.js +16 -0
  1038. package/dist/topology-cli-BhUXVViF.js +43 -0
  1039. package/dist/transcript-events-B1V6z5ct.js +29 -0
  1040. package/dist/tui-DDJMGCFK.js +3838 -0
  1041. package/dist/tui-cli-ByN-ZH6y.js +237 -0
  1042. package/dist/typebox-D0SHDJST.js +175 -0
  1043. package/dist/types-BCKGVVld.js +83 -0
  1044. package/dist/types-CtpUGsDP.js +30 -0
  1045. package/dist/types.secrets-BWSeXrF4.js +80 -0
  1046. package/dist/types.tools-BBO8HCi6.js +22 -0
  1047. package/dist/typing-DG_YqWJ7.js +224 -0
  1048. package/dist/unhandled-rejections-CDJ8dOVP.js +170 -0
  1049. package/dist/unhandled-rejections-O6cVOz2D.js +4 -0
  1050. package/dist/update-Br8U-txJ.js +1039 -0
  1051. package/dist/update-check-C3TeQaWg.js +464 -0
  1052. package/dist/update-cli-XHfIntD0.js +1625 -0
  1053. package/dist/update-offset-store-36vzzZXw.js +211 -0
  1054. package/dist/upsert-with-lock-Bb96JHpb.js +34 -0
  1055. package/dist/url-userinfo-Db63ng4y.js +14 -0
  1056. package/dist/utils-Bxk6BLTg.js +236 -0
  1057. package/dist/utils-vDeUf98G.js +7 -0
  1058. package/dist/version-DCY9_obP.js +64 -0
  1059. package/dist/version-DRF-wKTV.js +2 -0
  1060. package/dist/voice-call-D4fgwZNO.js +1 -0
  1061. package/dist/warning-filter-CgvLQB4Y.js +56 -0
  1062. package/dist/web-media-BfBb8i48.js +1 -0
  1063. package/dist/web-media-CtU6jM5V.js +498 -0
  1064. package/dist/webhook-ingress-CupqYpKM.js +338 -0
  1065. package/dist/webhook-memory-guards-BHrFZ4yq.js +129 -0
  1066. package/dist/webhook-path-BGFZ55ML.js +22 -0
  1067. package/dist/webhook-shared-Cvk3b0ac.js +349 -0
  1068. package/dist/webhooks-cli-vOAoBF9b.js +357 -0
  1069. package/dist/whatsapp-D5nD0rGG.js +58 -0
  1070. package/dist/whatsapp-DXbWlm3A.js +82 -0
  1071. package/dist/whatsapp-core-C2WGMsaY.js +89451 -0
  1072. package/dist/whatsapp-heartbeat-CSWnPQ7q.js +84 -0
  1073. package/dist/whatsapp-shared-BmHKqTtR.js +95 -0
  1074. package/dist/widearea-dns-CXimgJzu.js +125 -0
  1075. package/dist/windows-argv-IXrdWrJj.js +145 -0
  1076. package/dist/windows-spawn-vMJGZo89.js +154 -0
  1077. package/dist/with-timeout-2AKTISee.js +58 -0
  1078. package/dist/workspace-BH7CXmrr.js +479 -0
  1079. package/dist/workspace-dirs-_O4V3xCR.js +13 -0
  1080. package/dist/workspace-v5XppK5M.js +302 -0
  1081. package/dist/ws-By-QcLjg.js +11 -0
  1082. package/dist/wsl-BV3Cb66X.js +57 -0
  1083. package/dist/zalo-CHQzsLhE.js +301 -0
  1084. package/dist/zalo-CcJ3J9f2.js +13 -0
  1085. package/dist/zod-schema.agent-runtime-T_EC_6fg.js +600 -0
  1086. package/dist/zod-schema.core-BdgRr-F1.js +545 -0
  1087. package/dist/zod-schema.providers-core-Dgq7MTqU.js +1613 -0
  1088. package/docs/.i18n/README.md +31 -0
  1089. package/docs/.i18n/glossary.ja-JP.json +14 -0
  1090. package/docs/.i18n/glossary.zh-CN.json +242 -0
  1091. package/docs/.i18n/ja-JP.tm.jsonl +0 -0
  1092. package/docs/assets/install-script.svg +1 -0
  1093. package/docs/assets/macos-onboarding/01-macos-warning.jpeg +0 -0
  1094. package/docs/assets/macos-onboarding/02-local-networks.jpeg +0 -0
  1095. package/docs/assets/macos-onboarding/03-security-notice.png +0 -0
  1096. package/docs/assets/macos-onboarding/04-choose-gateway.png +0 -0
  1097. package/docs/assets/macos-onboarding/05-permissions.png +0 -0
  1098. package/docs/assets/openclaw-logo-text-dark.png +0 -0
  1099. package/docs/assets/openclaw-logo-text-dark.svg +418 -0
  1100. package/docs/assets/openclaw-logo-text.png +0 -0
  1101. package/docs/assets/openclaw-logo-text.svg +418 -0
  1102. package/docs/assets/pixel-lobster.svg +60 -0
  1103. package/docs/assets/showcase/agents-ui.jpg +0 -0
  1104. package/docs/assets/showcase/bambu-cli.png +0 -0
  1105. package/docs/assets/showcase/codexmonitor.png +0 -0
  1106. package/docs/assets/showcase/gohome-grafana.png +0 -0
  1107. package/docs/assets/showcase/ios-testflight.jpg +0 -0
  1108. package/docs/assets/showcase/oura-health.png +0 -0
  1109. package/docs/assets/showcase/padel-cli.svg +11 -0
  1110. package/docs/assets/showcase/padel-screenshot.jpg +0 -0
  1111. package/docs/assets/showcase/papla-tts.jpg +0 -0
  1112. package/docs/assets/showcase/pr-review-telegram.jpg +0 -0
  1113. package/docs/assets/showcase/roborock-screenshot.jpg +0 -0
  1114. package/docs/assets/showcase/roborock-status.svg +13 -0
  1115. package/docs/assets/showcase/roof-camera-sky.jpg +0 -0
  1116. package/docs/assets/showcase/snag.png +0 -0
  1117. package/docs/assets/showcase/tesco-shop.jpg +0 -0
  1118. package/docs/assets/showcase/wienerlinien.png +0 -0
  1119. package/docs/assets/showcase/wine-cellar-skill.jpg +0 -0
  1120. package/docs/assets/showcase/winix-air-purifier.jpg +0 -0
  1121. package/docs/assets/showcase/xuezh-pronunciation.jpeg +0 -0
  1122. package/docs/assets/sponsors/blacksmith.svg +14 -0
  1123. package/docs/assets/sponsors/convex.svg +16 -0
  1124. package/docs/assets/sponsors/openai.svg +3 -0
  1125. package/docs/assets/sponsors/vercel.svg +5 -0
  1126. package/docs/auth-credential-semantics.md +53 -0
  1127. package/docs/automation/auth-monitoring.md +44 -0
  1128. package/docs/automation/cron-jobs.md +727 -0
  1129. package/docs/automation/cron-vs-heartbeat.md +286 -0
  1130. package/docs/automation/gmail-pubsub.md +256 -0
  1131. package/docs/automation/hooks.md +1049 -0
  1132. package/docs/automation/poll.md +86 -0
  1133. package/docs/automation/standing-orders.md +251 -0
  1134. package/docs/automation/troubleshooting.md +122 -0
  1135. package/docs/automation/webhook.md +217 -0
  1136. package/docs/brave-search.md +93 -0
  1137. package/docs/channels/bluebubbles.md +347 -0
  1138. package/docs/channels/broadcast-groups.md +442 -0
  1139. package/docs/channels/channel-routing.md +139 -0
  1140. package/docs/channels/discord.md +1229 -0
  1141. package/docs/channels/feishu.md +747 -0
  1142. package/docs/channels/googlechat.md +261 -0
  1143. package/docs/channels/group-messages.md +84 -0
  1144. package/docs/channels/groups.md +379 -0
  1145. package/docs/channels/imessage.md +367 -0
  1146. package/docs/channels/index.md +47 -0
  1147. package/docs/channels/irc.md +242 -0
  1148. package/docs/channels/line.md +194 -0
  1149. package/docs/channels/location.md +56 -0
  1150. package/docs/channels/matrix.md +677 -0
  1151. package/docs/channels/mattermost.md +427 -0
  1152. package/docs/channels/msteams.md +780 -0
  1153. package/docs/channels/nextcloud-talk.md +138 -0
  1154. package/docs/channels/nostr.md +242 -0
  1155. package/docs/channels/pairing.md +114 -0
  1156. package/docs/channels/signal.md +329 -0
  1157. package/docs/channels/slack.md +603 -0
  1158. package/docs/channels/synology-chat.md +132 -0
  1159. package/docs/channels/telegram.md +987 -0
  1160. package/docs/channels/tlon.md +276 -0
  1161. package/docs/channels/troubleshooting.md +118 -0
  1162. package/docs/channels/twitch.md +379 -0
  1163. package/docs/channels/whatsapp.md +460 -0
  1164. package/docs/channels/zalo.md +243 -0
  1165. package/docs/channels/zalouser.md +181 -0
  1166. package/docs/ci.md +55 -0
  1167. package/docs/cli/acp.md +288 -0
  1168. package/docs/cli/agent.md +29 -0
  1169. package/docs/cli/agents.md +123 -0
  1170. package/docs/cli/approvals.md +50 -0
  1171. package/docs/cli/backup.md +76 -0
  1172. package/docs/cli/browser.md +106 -0
  1173. package/docs/cli/channels.md +102 -0
  1174. package/docs/cli/clawbot.md +21 -0
  1175. package/docs/cli/completion.md +35 -0
  1176. package/docs/cli/config.md +295 -0
  1177. package/docs/cli/configure.md +36 -0
  1178. package/docs/cli/cron.md +77 -0
  1179. package/docs/cli/daemon.md +53 -0
  1180. package/docs/cli/dashboard.md +22 -0
  1181. package/docs/cli/devices.md +139 -0
  1182. package/docs/cli/directory.md +63 -0
  1183. package/docs/cli/dns.md +23 -0
  1184. package/docs/cli/docs.md +15 -0
  1185. package/docs/cli/doctor.md +48 -0
  1186. package/docs/cli/gateway.md +235 -0
  1187. package/docs/cli/health.md +21 -0
  1188. package/docs/cli/hooks.md +318 -0
  1189. package/docs/cli/index.md +1147 -0
  1190. package/docs/cli/logs.md +28 -0
  1191. package/docs/cli/memory.md +66 -0
  1192. package/docs/cli/message.md +278 -0
  1193. package/docs/cli/models.md +81 -0
  1194. package/docs/cli/node.md +127 -0
  1195. package/docs/cli/nodes.md +75 -0
  1196. package/docs/cli/onboard.md +157 -0
  1197. package/docs/cli/pairing.md +32 -0
  1198. package/docs/cli/plugins.md +186 -0
  1199. package/docs/cli/qr.md +46 -0
  1200. package/docs/cli/reset.md +20 -0
  1201. package/docs/cli/sandbox.md +197 -0
  1202. package/docs/cli/secrets.md +188 -0
  1203. package/docs/cli/security.md +79 -0
  1204. package/docs/cli/sessions.md +110 -0
  1205. package/docs/cli/setup.md +29 -0
  1206. package/docs/cli/skills.md +26 -0
  1207. package/docs/cli/status.md +30 -0
  1208. package/docs/cli/system.md +60 -0
  1209. package/docs/cli/tui.md +30 -0
  1210. package/docs/cli/uninstall.md +20 -0
  1211. package/docs/cli/update.md +103 -0
  1212. package/docs/cli/voicecall.md +34 -0
  1213. package/docs/cli/webhooks.md +25 -0
  1214. package/docs/concepts/agent-loop.md +148 -0
  1215. package/docs/concepts/agent-workspace.md +236 -0
  1216. package/docs/concepts/agent.md +122 -0
  1217. package/docs/concepts/architecture.md +137 -0
  1218. package/docs/concepts/compaction.md +123 -0
  1219. package/docs/concepts/context-engine.md +268 -0
  1220. package/docs/concepts/context.md +172 -0
  1221. package/docs/concepts/delegate-architecture.md +296 -0
  1222. package/docs/concepts/features.md +73 -0
  1223. package/docs/concepts/markdown-formatting.md +130 -0
  1224. package/docs/concepts/memory.md +108 -0
  1225. package/docs/concepts/messages.md +154 -0
  1226. package/docs/concepts/model-failover.md +152 -0
  1227. package/docs/concepts/model-providers.md +607 -0
  1228. package/docs/concepts/models.md +225 -0
  1229. package/docs/concepts/multi-agent.md +552 -0
  1230. package/docs/concepts/oauth.md +158 -0
  1231. package/docs/concepts/presence.md +102 -0
  1232. package/docs/concepts/queue.md +89 -0
  1233. package/docs/concepts/retry.md +69 -0
  1234. package/docs/concepts/session-pruning.md +121 -0
  1235. package/docs/concepts/session-tool.md +242 -0
  1236. package/docs/concepts/session.md +310 -0
  1237. package/docs/concepts/streaming.md +155 -0
  1238. package/docs/concepts/system-prompt.md +132 -0
  1239. package/docs/concepts/timezone.md +91 -0
  1240. package/docs/concepts/typebox.md +291 -0
  1241. package/docs/concepts/typing-indicators.md +68 -0
  1242. package/docs/concepts/usage-tracking.md +35 -0
  1243. package/docs/date-time.md +128 -0
  1244. package/docs/debug/node-issue.md +85 -0
  1245. package/docs/diagnostics/flags.md +91 -0
  1246. package/docs/docs.json +2061 -0
  1247. package/docs/gateway/authentication.md +179 -0
  1248. package/docs/gateway/background-process.md +97 -0
  1249. package/docs/gateway/bonjour.md +177 -0
  1250. package/docs/gateway/bridge-protocol.md +91 -0
  1251. package/docs/gateway/cli-backends.md +225 -0
  1252. package/docs/gateway/configuration-examples.md +651 -0
  1253. package/docs/gateway/configuration-reference.md +3123 -0
  1254. package/docs/gateway/configuration.md +633 -0
  1255. package/docs/gateway/discovery.md +123 -0
  1256. package/docs/gateway/doctor.md +362 -0
  1257. package/docs/gateway/gateway-lock.md +34 -0
  1258. package/docs/gateway/health.md +44 -0
  1259. package/docs/gateway/heartbeat.md +393 -0
  1260. package/docs/gateway/index.md +261 -0
  1261. package/docs/gateway/local-models.md +152 -0
  1262. package/docs/gateway/logging.md +113 -0
  1263. package/docs/gateway/multiple-gateways.md +112 -0
  1264. package/docs/gateway/network-model.md +22 -0
  1265. package/docs/gateway/openai-http-api.md +132 -0
  1266. package/docs/gateway/openresponses-http-api.md +295 -0
  1267. package/docs/gateway/openshell.md +307 -0
  1268. package/docs/gateway/pairing.md +99 -0
  1269. package/docs/gateway/protocol.md +267 -0
  1270. package/docs/gateway/remote-gateway-readme.md +158 -0
  1271. package/docs/gateway/remote.md +153 -0
  1272. package/docs/gateway/sandbox-vs-tool-policy-vs-elevated.md +134 -0
  1273. package/docs/gateway/sandboxing.md +469 -0
  1274. package/docs/gateway/secrets-plan-contract.md +116 -0
  1275. package/docs/gateway/secrets.md +503 -0
  1276. package/docs/gateway/security/index.md +1213 -0
  1277. package/docs/gateway/tailscale.md +132 -0
  1278. package/docs/gateway/tools-invoke-http-api.md +110 -0
  1279. package/docs/gateway/troubleshooting.md +378 -0
  1280. package/docs/gateway/trusted-proxy-auth.md +330 -0
  1281. package/docs/help/debugging.md +168 -0
  1282. package/docs/help/environment.md +163 -0
  1283. package/docs/help/faq.md +2999 -0
  1284. package/docs/help/index.md +28 -0
  1285. package/docs/help/scripts.md +28 -0
  1286. package/docs/help/testing.md +524 -0
  1287. package/docs/help/troubleshooting.md +297 -0
  1288. package/docs/images/configure-model-picker-unsearchable.png +0 -0
  1289. package/docs/images/feishu-step2-create-app.png +0 -0
  1290. package/docs/images/feishu-step3-credentials.png +0 -0
  1291. package/docs/images/feishu-step4-permissions.png +0 -0
  1292. package/docs/images/feishu-step5-bot-capability.png +0 -0
  1293. package/docs/images/feishu-step6-event-subscription.png +0 -0
  1294. package/docs/images/feishu-verification-token.png +0 -0
  1295. package/docs/images/groups-flow.svg +52 -0
  1296. package/docs/images/mobile-ui-screenshot.png +0 -0
  1297. package/docs/index.md +196 -0
  1298. package/docs/install/ansible.md +230 -0
  1299. package/docs/install/azure.md +311 -0
  1300. package/docs/install/bun.md +55 -0
  1301. package/docs/install/development-channels.md +120 -0
  1302. package/docs/install/digitalocean.md +129 -0
  1303. package/docs/install/docker-vm-runtime.md +142 -0
  1304. package/docs/install/docker.md +375 -0
  1305. package/docs/install/exe-dev.md +126 -0
  1306. package/docs/install/fly.md +501 -0
  1307. package/docs/install/gcp.md +402 -0
  1308. package/docs/install/hetzner.md +251 -0
  1309. package/docs/install/index.md +183 -0
  1310. package/docs/install/installer.md +415 -0
  1311. package/docs/install/kubernetes.md +191 -0
  1312. package/docs/install/macos-vm.md +281 -0
  1313. package/docs/install/migrating-matrix.md +346 -0
  1314. package/docs/install/migrating.md +110 -0
  1315. package/docs/install/nix.md +89 -0
  1316. package/docs/install/node.md +138 -0
  1317. package/docs/install/northflank.mdx +54 -0
  1318. package/docs/install/oracle.md +156 -0
  1319. package/docs/install/podman.md +133 -0
  1320. package/docs/install/railway.mdx +100 -0
  1321. package/docs/install/raspberry-pi.md +159 -0
  1322. package/docs/install/render.mdx +169 -0
  1323. package/docs/install/uninstall.md +128 -0
  1324. package/docs/install/updating.md +128 -0
  1325. package/docs/ja-JP/index.md +186 -0
  1326. package/docs/ja-JP/start/getting-started.md +125 -0
  1327. package/docs/ja-JP/start/wizard.md +77 -0
  1328. package/docs/logging.md +352 -0
  1329. package/docs/nav-tabs-underline.js +100 -0
  1330. package/docs/network.md +54 -0
  1331. package/docs/nodes/audio.md +187 -0
  1332. package/docs/nodes/camera.md +162 -0
  1333. package/docs/nodes/images.md +72 -0
  1334. package/docs/nodes/index.md +393 -0
  1335. package/docs/nodes/location-command.md +98 -0
  1336. package/docs/nodes/media-understanding.md +394 -0
  1337. package/docs/nodes/talk.md +92 -0
  1338. package/docs/nodes/troubleshooting.md +114 -0
  1339. package/docs/nodes/voicewake.md +66 -0
  1340. package/docs/perplexity.md +174 -0
  1341. package/docs/pi-dev.md +80 -0
  1342. package/docs/pi.md +567 -0
  1343. package/docs/platforms/android.md +168 -0
  1344. package/docs/platforms/digitalocean.md +266 -0
  1345. package/docs/platforms/index.md +54 -0
  1346. package/docs/platforms/ios.md +220 -0
  1347. package/docs/platforms/linux.md +94 -0
  1348. package/docs/platforms/mac/bundled-gateway.md +73 -0
  1349. package/docs/platforms/mac/canvas.md +125 -0
  1350. package/docs/platforms/mac/child-process.md +69 -0
  1351. package/docs/platforms/mac/dev-setup.md +104 -0
  1352. package/docs/platforms/mac/health.md +34 -0
  1353. package/docs/platforms/mac/icon.md +31 -0
  1354. package/docs/platforms/mac/logging.md +57 -0
  1355. package/docs/platforms/mac/menu-bar.md +81 -0
  1356. package/docs/platforms/mac/peekaboo.md +65 -0
  1357. package/docs/platforms/mac/permissions.md +50 -0
  1358. package/docs/platforms/mac/remote.md +84 -0
  1359. package/docs/platforms/mac/signing.md +47 -0
  1360. package/docs/platforms/mac/skills.md +33 -0
  1361. package/docs/platforms/mac/voice-overlay.md +60 -0
  1362. package/docs/platforms/mac/voicewake.md +67 -0
  1363. package/docs/platforms/mac/webchat.md +43 -0
  1364. package/docs/platforms/mac/xpc.md +61 -0
  1365. package/docs/platforms/macos.md +226 -0
  1366. package/docs/platforms/oracle.md +303 -0
  1367. package/docs/platforms/raspberry-pi.md +412 -0
  1368. package/docs/platforms/windows.md +241 -0
  1369. package/docs/plugins/agent-tools.md +10 -0
  1370. package/docs/plugins/architecture.md +1363 -0
  1371. package/docs/plugins/building-extensions.md +10 -0
  1372. package/docs/plugins/building-plugins.md +376 -0
  1373. package/docs/plugins/bundles.md +181 -0
  1374. package/docs/plugins/community.md +141 -0
  1375. package/docs/plugins/manifest.md +145 -0
  1376. package/docs/plugins/sdk-migration.md +169 -0
  1377. package/docs/plugins/voice-call.md +380 -0
  1378. package/docs/plugins/zalouser.md +77 -0
  1379. package/docs/prose.md +134 -0
  1380. package/docs/providers/anthropic.md +259 -0
  1381. package/docs/providers/bedrock.md +176 -0
  1382. package/docs/providers/claude-max-api-proxy.md +154 -0
  1383. package/docs/providers/cloudflare-ai-gateway.md +71 -0
  1384. package/docs/providers/deepgram.md +93 -0
  1385. package/docs/providers/github-copilot.md +72 -0
  1386. package/docs/providers/glm.md +43 -0
  1387. package/docs/providers/google.md +78 -0
  1388. package/docs/providers/groq.md +96 -0
  1389. package/docs/providers/huggingface.md +209 -0
  1390. package/docs/providers/index.md +69 -0
  1391. package/docs/providers/kilocode.md +74 -0
  1392. package/docs/providers/litellm.md +154 -0
  1393. package/docs/providers/minimax.md +224 -0
  1394. package/docs/providers/mistral.md +54 -0
  1395. package/docs/providers/models.md +45 -0
  1396. package/docs/providers/modelstudio.md +66 -0
  1397. package/docs/providers/moonshot.md +175 -0
  1398. package/docs/providers/nvidia.md +55 -0
  1399. package/docs/providers/ollama.md +352 -0
  1400. package/docs/providers/openai.md +303 -0
  1401. package/docs/providers/opencode-go.md +45 -0
  1402. package/docs/providers/opencode.md +64 -0
  1403. package/docs/providers/openrouter.md +37 -0
  1404. package/docs/providers/perplexity-provider.md +62 -0
  1405. package/docs/providers/qianfan.md +38 -0
  1406. package/docs/providers/qwen.md +53 -0
  1407. package/docs/providers/sglang.md +104 -0
  1408. package/docs/providers/synthetic.md +99 -0
  1409. package/docs/providers/together.md +66 -0
  1410. package/docs/providers/venice.md +282 -0
  1411. package/docs/providers/vercel-ai-gateway.md +60 -0
  1412. package/docs/providers/vllm.md +92 -0
  1413. package/docs/providers/volcengine.md +74 -0
  1414. package/docs/providers/xai.md +60 -0
  1415. package/docs/providers/xiaomi.md +86 -0
  1416. package/docs/providers/zai.md +46 -0
  1417. package/docs/reference/AGENTS.default.md +126 -0
  1418. package/docs/reference/RELEASING.md +42 -0
  1419. package/docs/reference/api-usage-costs.md +144 -0
  1420. package/docs/reference/credits.md +30 -0
  1421. package/docs/reference/device-models.md +47 -0
  1422. package/docs/reference/memory-config.md +711 -0
  1423. package/docs/reference/prompt-caching.md +185 -0
  1424. package/docs/reference/rpc.md +43 -0
  1425. package/docs/reference/secretref-credential-surface.md +140 -0
  1426. package/docs/reference/secretref-user-supplied-credentials-matrix.json +563 -0
  1427. package/docs/reference/session-management-compaction.md +324 -0
  1428. package/docs/reference/templates/AGENTS.dev.md +83 -0
  1429. package/docs/reference/templates/AGENTS.md +219 -0
  1430. package/docs/reference/templates/BOOT.md +11 -0
  1431. package/docs/reference/templates/BOOTSTRAP.md +62 -0
  1432. package/docs/reference/templates/HEARTBEAT.md +14 -0
  1433. package/docs/reference/templates/IDENTITY.dev.md +47 -0
  1434. package/docs/reference/templates/IDENTITY.md +29 -0
  1435. package/docs/reference/templates/SOUL.dev.md +76 -0
  1436. package/docs/reference/templates/SOUL.md +43 -0
  1437. package/docs/reference/templates/TOOLS.dev.md +24 -0
  1438. package/docs/reference/templates/TOOLS.md +47 -0
  1439. package/docs/reference/templates/USER.dev.md +18 -0
  1440. package/docs/reference/templates/USER.md +23 -0
  1441. package/docs/reference/test.md +90 -0
  1442. package/docs/reference/token-use.md +175 -0
  1443. package/docs/reference/transcript-hygiene.md +151 -0
  1444. package/docs/reference/wizard.md +235 -0
  1445. package/docs/security/CONTRIBUTING-THREAT-MODEL.md +98 -0
  1446. package/docs/security/THREAT-MODEL-ATLAS.md +611 -0
  1447. package/docs/security/formal-verification.md +167 -0
  1448. package/docs/start/bootstrapping.md +41 -0
  1449. package/docs/start/docs-directory.md +66 -0
  1450. package/docs/start/getting-started.md +116 -0
  1451. package/docs/start/hubs.md +198 -0
  1452. package/docs/start/lore.md +219 -0
  1453. package/docs/start/onboarding-overview.md +67 -0
  1454. package/docs/start/onboarding.md +91 -0
  1455. package/docs/start/openclaw.md +216 -0
  1456. package/docs/start/quickstart.md +22 -0
  1457. package/docs/start/setup.md +164 -0
  1458. package/docs/start/showcase.md +418 -0
  1459. package/docs/start/wizard-cli-automation.md +215 -0
  1460. package/docs/start/wizard-cli-reference.md +299 -0
  1461. package/docs/start/wizard.md +125 -0
  1462. package/docs/style.css +37 -0
  1463. package/docs/tools/acp-agents.md +623 -0
  1464. package/docs/tools/agent-send.md +100 -0
  1465. package/docs/tools/apply-patch.md +51 -0
  1466. package/docs/tools/brave-search.md +93 -0
  1467. package/docs/tools/browser-linux-troubleshooting.md +138 -0
  1468. package/docs/tools/browser-login.md +73 -0
  1469. package/docs/tools/browser-wsl2-windows-remote-cdp-troubleshooting.md +211 -0
  1470. package/docs/tools/browser.md +731 -0
  1471. package/docs/tools/btw.md +142 -0
  1472. package/docs/tools/capability-cookbook.md +119 -0
  1473. package/docs/tools/clawhub.md +257 -0
  1474. package/docs/tools/creating-skills.md +117 -0
  1475. package/docs/tools/diffs.md +386 -0
  1476. package/docs/tools/elevated.md +114 -0
  1477. package/docs/tools/exec-approvals.md +400 -0
  1478. package/docs/tools/exec.md +204 -0
  1479. package/docs/tools/firecrawl.md +140 -0
  1480. package/docs/tools/index.md +137 -0
  1481. package/docs/tools/llm-task.md +119 -0
  1482. package/docs/tools/lobster.md +340 -0
  1483. package/docs/tools/loop-detection.md +100 -0
  1484. package/docs/tools/multi-agent-sandbox-tools.md +364 -0
  1485. package/docs/tools/pdf.md +156 -0
  1486. package/docs/tools/perplexity-search.md +174 -0
  1487. package/docs/tools/plugin.md +251 -0
  1488. package/docs/tools/reactions.md +64 -0
  1489. package/docs/tools/skills-config.md +86 -0
  1490. package/docs/tools/skills.md +306 -0
  1491. package/docs/tools/slash-commands.md +294 -0
  1492. package/docs/tools/subagents.md +295 -0
  1493. package/docs/tools/tavily.md +125 -0
  1494. package/docs/tools/thinking.md +96 -0
  1495. package/docs/tools/tts.md +406 -0
  1496. package/docs/tools/web.md +516 -0
  1497. package/docs/tts.md +406 -0
  1498. package/docs/vps.md +112 -0
  1499. package/docs/web/control-ui.md +275 -0
  1500. package/docs/web/dashboard.md +54 -0
  1501. package/docs/web/index.md +120 -0
  1502. package/docs/web/tui.md +170 -0
  1503. package/docs/web/webchat.md +61 -0
  1504. package/docs/whatsapp-openclaw-ai-zh.jpg +0 -0
  1505. package/docs/whatsapp-openclaw.jpg +0 -0
  1506. package/docs/zh-CN/AGENTS.md +61 -0
  1507. package/docs/zh-CN/automation/auth-monitoring.md +47 -0
  1508. package/docs/zh-CN/automation/cron-jobs.md +435 -0
  1509. package/docs/zh-CN/automation/cron-vs-heartbeat.md +286 -0
  1510. package/docs/zh-CN/automation/gmail-pubsub.md +249 -0
  1511. package/docs/zh-CN/automation/hooks.md +1051 -0
  1512. package/docs/zh-CN/automation/poll.md +76 -0
  1513. package/docs/zh-CN/automation/troubleshooting.md +8 -0
  1514. package/docs/zh-CN/automation/webhook.md +163 -0
  1515. package/docs/zh-CN/brave-search.md +60 -0
  1516. package/docs/zh-CN/channels/bluebubbles.md +354 -0
  1517. package/docs/zh-CN/channels/broadcast-groups.md +449 -0
  1518. package/docs/zh-CN/channels/channel-routing.md +117 -0
  1519. package/docs/zh-CN/channels/discord.md +468 -0
  1520. package/docs/zh-CN/channels/feishu.md +728 -0
  1521. package/docs/zh-CN/channels/googlechat.md +257 -0
  1522. package/docs/zh-CN/channels/grammy.md +38 -0
  1523. package/docs/zh-CN/channels/group-messages.md +91 -0
  1524. package/docs/zh-CN/channels/groups.md +379 -0
  1525. package/docs/zh-CN/channels/imessage.md +302 -0
  1526. package/docs/zh-CN/channels/index.md +53 -0
  1527. package/docs/zh-CN/channels/line.md +180 -0
  1528. package/docs/zh-CN/channels/location.md +63 -0
  1529. package/docs/zh-CN/channels/matrix.md +221 -0
  1530. package/docs/zh-CN/channels/mattermost.md +144 -0
  1531. package/docs/zh-CN/channels/msteams.md +775 -0
  1532. package/docs/zh-CN/channels/nextcloud-talk.md +142 -0
  1533. package/docs/zh-CN/channels/nostr.md +249 -0
  1534. package/docs/zh-CN/channels/pairing.md +89 -0
  1535. package/docs/zh-CN/channels/signal.md +209 -0
  1536. package/docs/zh-CN/channels/slack.md +531 -0
  1537. package/docs/zh-CN/channels/synology-chat.md +138 -0
  1538. package/docs/zh-CN/channels/telegram.md +751 -0
  1539. package/docs/zh-CN/channels/tlon.md +136 -0
  1540. package/docs/zh-CN/channels/troubleshooting.md +36 -0
  1541. package/docs/zh-CN/channels/twitch.md +385 -0
  1542. package/docs/zh-CN/channels/whatsapp.md +411 -0
  1543. package/docs/zh-CN/channels/zalo.md +196 -0
  1544. package/docs/zh-CN/channels/zalouser.md +147 -0
  1545. package/docs/zh-CN/cli/acp.md +173 -0
  1546. package/docs/zh-CN/cli/agent.md +30 -0
  1547. package/docs/zh-CN/cli/agents.md +82 -0
  1548. package/docs/zh-CN/cli/approvals.md +57 -0
  1549. package/docs/zh-CN/cli/browser.md +114 -0
  1550. package/docs/zh-CN/cli/channels.md +86 -0
  1551. package/docs/zh-CN/cli/config.md +57 -0
  1552. package/docs/zh-CN/cli/configure.md +38 -0
  1553. package/docs/zh-CN/cli/cron.md +43 -0
  1554. package/docs/zh-CN/cli/dashboard.md +23 -0
  1555. package/docs/zh-CN/cli/devices.md +74 -0
  1556. package/docs/zh-CN/cli/directory.md +70 -0
  1557. package/docs/zh-CN/cli/dns.md +30 -0
  1558. package/docs/zh-CN/cli/docs.md +22 -0
  1559. package/docs/zh-CN/cli/doctor.md +48 -0
  1560. package/docs/zh-CN/cli/gateway.md +206 -0
  1561. package/docs/zh-CN/cli/health.md +28 -0
  1562. package/docs/zh-CN/cli/hooks.md +298 -0
  1563. package/docs/zh-CN/cli/index.md +1143 -0
  1564. package/docs/zh-CN/cli/logs.md +31 -0
  1565. package/docs/zh-CN/cli/memory.md +52 -0
  1566. package/docs/zh-CN/cli/message.md +246 -0
  1567. package/docs/zh-CN/cli/models.md +85 -0
  1568. package/docs/zh-CN/cli/node.md +115 -0
  1569. package/docs/zh-CN/cli/nodes.md +80 -0
  1570. package/docs/zh-CN/cli/onboard.md +164 -0
  1571. package/docs/zh-CN/cli/pairing.md +28 -0
  1572. package/docs/zh-CN/cli/plugins.md +66 -0
  1573. package/docs/zh-CN/cli/reset.md +24 -0
  1574. package/docs/zh-CN/cli/sandbox.md +158 -0
  1575. package/docs/zh-CN/cli/security.md +33 -0
  1576. package/docs/zh-CN/cli/sessions.md +23 -0
  1577. package/docs/zh-CN/cli/setup.md +36 -0
  1578. package/docs/zh-CN/cli/skills.md +33 -0
  1579. package/docs/zh-CN/cli/status.md +33 -0
  1580. package/docs/zh-CN/cli/system.md +63 -0
  1581. package/docs/zh-CN/cli/tui.md +30 -0
  1582. package/docs/zh-CN/cli/uninstall.md +24 -0
  1583. package/docs/zh-CN/cli/update.md +101 -0
  1584. package/docs/zh-CN/cli/voicecall.md +41 -0
  1585. package/docs/zh-CN/cli/webhooks.md +32 -0
  1586. package/docs/zh-CN/concepts/agent-loop.md +146 -0
  1587. package/docs/zh-CN/concepts/agent-workspace.md +219 -0
  1588. package/docs/zh-CN/concepts/agent.md +115 -0
  1589. package/docs/zh-CN/concepts/architecture.md +123 -0
  1590. package/docs/zh-CN/concepts/compaction.md +67 -0
  1591. package/docs/zh-CN/concepts/context.md +168 -0
  1592. package/docs/zh-CN/concepts/features.md +59 -0
  1593. package/docs/zh-CN/concepts/markdown-formatting.md +117 -0
  1594. package/docs/zh-CN/concepts/memory.md +412 -0
  1595. package/docs/zh-CN/concepts/messages.md +141 -0
  1596. package/docs/zh-CN/concepts/model-failover.md +145 -0
  1597. package/docs/zh-CN/concepts/model-providers.md +606 -0
  1598. package/docs/zh-CN/concepts/models.md +225 -0
  1599. package/docs/zh-CN/concepts/multi-agent.md +372 -0
  1600. package/docs/zh-CN/concepts/oauth.md +164 -0
  1601. package/docs/zh-CN/concepts/presence.md +99 -0
  1602. package/docs/zh-CN/concepts/queue.md +94 -0
  1603. package/docs/zh-CN/concepts/retry.md +76 -0
  1604. package/docs/zh-CN/concepts/session-pruning.md +129 -0
  1605. package/docs/zh-CN/concepts/session-tool.md +200 -0
  1606. package/docs/zh-CN/concepts/session.md +166 -0
  1607. package/docs/zh-CN/concepts/streaming.md +133 -0
  1608. package/docs/zh-CN/concepts/system-prompt.md +101 -0
  1609. package/docs/zh-CN/concepts/timezone.md +96 -0
  1610. package/docs/zh-CN/concepts/typebox.md +284 -0
  1611. package/docs/zh-CN/concepts/typing-indicators.md +74 -0
  1612. package/docs/zh-CN/concepts/usage-tracking.md +42 -0
  1613. package/docs/zh-CN/date-time.md +129 -0
  1614. package/docs/zh-CN/debug/node-issue.md +90 -0
  1615. package/docs/zh-CN/diagnostics/flags.md +98 -0
  1616. package/docs/zh-CN/gateway/authentication.md +184 -0
  1617. package/docs/zh-CN/gateway/background-process.md +100 -0
  1618. package/docs/zh-CN/gateway/bonjour.md +174 -0
  1619. package/docs/zh-CN/gateway/bridge-protocol.md +86 -0
  1620. package/docs/zh-CN/gateway/cli-backends.md +213 -0
  1621. package/docs/zh-CN/gateway/configuration-examples.md +587 -0
  1622. package/docs/zh-CN/gateway/configuration-reference.md +3103 -0
  1623. package/docs/zh-CN/gateway/configuration.md +640 -0
  1624. package/docs/zh-CN/gateway/discovery.md +123 -0
  1625. package/docs/zh-CN/gateway/doctor.md +238 -0
  1626. package/docs/zh-CN/gateway/gateway-lock.md +41 -0
  1627. package/docs/zh-CN/gateway/health.md +42 -0
  1628. package/docs/zh-CN/gateway/heartbeat.md +274 -0
  1629. package/docs/zh-CN/gateway/index.md +335 -0
  1630. package/docs/zh-CN/gateway/local-models.md +159 -0
  1631. package/docs/zh-CN/gateway/logging.md +114 -0
  1632. package/docs/zh-CN/gateway/multiple-gateways.md +119 -0
  1633. package/docs/zh-CN/gateway/network-model.md +23 -0
  1634. package/docs/zh-CN/gateway/openai-http-api.md +125 -0
  1635. package/docs/zh-CN/gateway/openresponses-http-api.md +317 -0
  1636. package/docs/zh-CN/gateway/pairing.md +99 -0
  1637. package/docs/zh-CN/gateway/protocol.md +220 -0
  1638. package/docs/zh-CN/gateway/remote-gateway-readme.md +164 -0
  1639. package/docs/zh-CN/gateway/remote.md +133 -0
  1640. package/docs/zh-CN/gateway/sandbox-vs-tool-policy-vs-elevated.md +135 -0
  1641. package/docs/zh-CN/gateway/sandboxing.md +188 -0
  1642. package/docs/zh-CN/gateway/security/index.md +777 -0
  1643. package/docs/zh-CN/gateway/tailscale.md +124 -0
  1644. package/docs/zh-CN/gateway/tools-invoke-http-api.md +92 -0
  1645. package/docs/zh-CN/gateway/troubleshooting.md +771 -0
  1646. package/docs/zh-CN/help/debugging.md +160 -0
  1647. package/docs/zh-CN/help/environment.md +88 -0
  1648. package/docs/zh-CN/help/faq.md +2640 -0
  1649. package/docs/zh-CN/help/index.md +28 -0
  1650. package/docs/zh-CN/help/scripts.md +35 -0
  1651. package/docs/zh-CN/help/testing.md +375 -0
  1652. package/docs/zh-CN/help/troubleshooting.md +104 -0
  1653. package/docs/zh-CN/index.md +186 -0
  1654. package/docs/zh-CN/install/ansible.md +215 -0
  1655. package/docs/zh-CN/install/bun.md +65 -0
  1656. package/docs/zh-CN/install/development-channels.md +81 -0
  1657. package/docs/zh-CN/install/docker.md +532 -0
  1658. package/docs/zh-CN/install/exe-dev.md +133 -0
  1659. package/docs/zh-CN/install/fly.md +490 -0
  1660. package/docs/zh-CN/install/gcp.md +510 -0
  1661. package/docs/zh-CN/install/hetzner.md +337 -0
  1662. package/docs/zh-CN/install/index.md +235 -0
  1663. package/docs/zh-CN/install/installer.md +422 -0
  1664. package/docs/zh-CN/install/macos-vm.md +288 -0
  1665. package/docs/zh-CN/install/migrating.md +199 -0
  1666. package/docs/zh-CN/install/nix.md +99 -0
  1667. package/docs/zh-CN/install/node.md +8 -0
  1668. package/docs/zh-CN/install/northflank.mdx +60 -0
  1669. package/docs/zh-CN/install/railway.mdx +106 -0
  1670. package/docs/zh-CN/install/render.mdx +169 -0
  1671. package/docs/zh-CN/install/uninstall.md +135 -0
  1672. package/docs/zh-CN/install/updating.md +233 -0
  1673. package/docs/zh-CN/logging.md +329 -0
  1674. package/docs/zh-CN/network.md +59 -0
  1675. package/docs/zh-CN/nodes/audio.md +120 -0
  1676. package/docs/zh-CN/nodes/camera.md +162 -0
  1677. package/docs/zh-CN/nodes/images.md +79 -0
  1678. package/docs/zh-CN/nodes/index.md +348 -0
  1679. package/docs/zh-CN/nodes/location-command.md +120 -0
  1680. package/docs/zh-CN/nodes/media-understanding.md +380 -0
  1681. package/docs/zh-CN/nodes/talk.md +97 -0
  1682. package/docs/zh-CN/nodes/troubleshooting.md +8 -0
  1683. package/docs/zh-CN/nodes/voicewake.md +72 -0
  1684. package/docs/zh-CN/perplexity.md +102 -0
  1685. package/docs/zh-CN/pi-dev.md +77 -0
  1686. package/docs/zh-CN/pi.md +619 -0
  1687. package/docs/zh-CN/platforms/android.md +155 -0
  1688. package/docs/zh-CN/platforms/digitalocean.md +273 -0
  1689. package/docs/zh-CN/platforms/index.md +60 -0
  1690. package/docs/zh-CN/platforms/ios.md +114 -0
  1691. package/docs/zh-CN/platforms/linux.md +100 -0
  1692. package/docs/zh-CN/platforms/mac/bundled-gateway.md +75 -0
  1693. package/docs/zh-CN/platforms/mac/canvas.md +128 -0
  1694. package/docs/zh-CN/platforms/mac/child-process.md +73 -0
  1695. package/docs/zh-CN/platforms/mac/dev-setup.md +109 -0
  1696. package/docs/zh-CN/platforms/mac/health.md +41 -0
  1697. package/docs/zh-CN/platforms/mac/icon.md +38 -0
  1698. package/docs/zh-CN/platforms/mac/logging.md +64 -0
  1699. package/docs/zh-CN/platforms/mac/menu-bar.md +88 -0
  1700. package/docs/zh-CN/platforms/mac/peekaboo.md +62 -0
  1701. package/docs/zh-CN/platforms/mac/permissions.md +46 -0
  1702. package/docs/zh-CN/platforms/mac/remote.md +90 -0
  1703. package/docs/zh-CN/platforms/mac/signing.md +54 -0
  1704. package/docs/zh-CN/platforms/mac/skills.md +40 -0
  1705. package/docs/zh-CN/platforms/mac/voice-overlay.md +67 -0
  1706. package/docs/zh-CN/platforms/mac/voicewake.md +74 -0
  1707. package/docs/zh-CN/platforms/mac/webchat.md +43 -0
  1708. package/docs/zh-CN/platforms/mac/xpc.md +68 -0
  1709. package/docs/zh-CN/platforms/macos.md +193 -0
  1710. package/docs/zh-CN/platforms/oracle.md +310 -0
  1711. package/docs/zh-CN/platforms/raspberry-pi.md +416 -0
  1712. package/docs/zh-CN/platforms/windows.md +247 -0
  1713. package/docs/zh-CN/plugins/agent-tools.md +99 -0
  1714. package/docs/zh-CN/plugins/manifest.md +68 -0
  1715. package/docs/zh-CN/plugins/voice-call.md +250 -0
  1716. package/docs/zh-CN/plugins/zalouser.md +88 -0
  1717. package/docs/zh-CN/prose.md +141 -0
  1718. package/docs/zh-CN/providers/anthropic.md +265 -0
  1719. package/docs/zh-CN/providers/bedrock.md +170 -0
  1720. package/docs/zh-CN/providers/claude-max-api-proxy.md +155 -0
  1721. package/docs/zh-CN/providers/cloudflare-ai-gateway.md +78 -0
  1722. package/docs/zh-CN/providers/deepgram.md +97 -0
  1723. package/docs/zh-CN/providers/github-copilot.md +67 -0
  1724. package/docs/zh-CN/providers/glm.md +50 -0
  1725. package/docs/zh-CN/providers/huggingface.md +216 -0
  1726. package/docs/zh-CN/providers/index.md +69 -0
  1727. package/docs/zh-CN/providers/kilocode.md +80 -0
  1728. package/docs/zh-CN/providers/litellm.md +160 -0
  1729. package/docs/zh-CN/providers/minimax.md +222 -0
  1730. package/docs/zh-CN/providers/mistral.md +61 -0
  1731. package/docs/zh-CN/providers/models.md +51 -0
  1732. package/docs/zh-CN/providers/moonshot.md +182 -0
  1733. package/docs/zh-CN/providers/nvidia.md +62 -0
  1734. package/docs/zh-CN/providers/ollama.md +359 -0
  1735. package/docs/zh-CN/providers/openai.md +308 -0
  1736. package/docs/zh-CN/providers/opencode-go.md +52 -0
  1737. package/docs/zh-CN/providers/opencode.md +71 -0
  1738. package/docs/zh-CN/providers/openrouter.md +44 -0
  1739. package/docs/zh-CN/providers/qianfan.md +45 -0
  1740. package/docs/zh-CN/providers/qwen.md +55 -0
  1741. package/docs/zh-CN/providers/sglang.md +111 -0
  1742. package/docs/zh-CN/providers/synthetic.md +106 -0
  1743. package/docs/zh-CN/providers/together.md +72 -0
  1744. package/docs/zh-CN/providers/venice.md +289 -0
  1745. package/docs/zh-CN/providers/vercel-ai-gateway.md +66 -0
  1746. package/docs/zh-CN/providers/xiaomi.md +93 -0
  1747. package/docs/zh-CN/providers/zai.md +53 -0
  1748. package/docs/zh-CN/reference/AGENTS.default.md +131 -0
  1749. package/docs/zh-CN/reference/RELEASING.md +48 -0
  1750. package/docs/zh-CN/reference/api-usage-costs.md +141 -0
  1751. package/docs/zh-CN/reference/credits.md +34 -0
  1752. package/docs/zh-CN/reference/device-models.md +54 -0
  1753. package/docs/zh-CN/reference/rpc.md +48 -0
  1754. package/docs/zh-CN/reference/session-management-compaction.md +287 -0
  1755. package/docs/zh-CN/reference/templates/AGENTS.dev.md +89 -0
  1756. package/docs/zh-CN/reference/templates/AGENTS.md +225 -0
  1757. package/docs/zh-CN/reference/templates/BOOT.md +17 -0
  1758. package/docs/zh-CN/reference/templates/BOOTSTRAP.md +68 -0
  1759. package/docs/zh-CN/reference/templates/HEARTBEAT.md +18 -0
  1760. package/docs/zh-CN/reference/templates/IDENTITY.dev.md +54 -0
  1761. package/docs/zh-CN/reference/templates/IDENTITY.md +36 -0
  1762. package/docs/zh-CN/reference/templates/SOUL.dev.md +83 -0
  1763. package/docs/zh-CN/reference/templates/SOUL.md +49 -0
  1764. package/docs/zh-CN/reference/templates/TOOLS.dev.md +31 -0
  1765. package/docs/zh-CN/reference/templates/TOOLS.md +53 -0
  1766. package/docs/zh-CN/reference/templates/USER.dev.md +25 -0
  1767. package/docs/zh-CN/reference/templates/USER.md +30 -0
  1768. package/docs/zh-CN/reference/test.md +57 -0
  1769. package/docs/zh-CN/reference/token-use.md +119 -0
  1770. package/docs/zh-CN/reference/transcript-hygiene.md +109 -0
  1771. package/docs/zh-CN/reference/wizard.md +242 -0
  1772. package/docs/zh-CN/security/formal-verification.md +171 -0
  1773. package/docs/zh-CN/start/bootstrapping.md +9 -0
  1774. package/docs/zh-CN/start/docs-directory.md +70 -0
  1775. package/docs/zh-CN/start/getting-started.md +143 -0
  1776. package/docs/zh-CN/start/hubs.md +194 -0
  1777. package/docs/zh-CN/start/lore.md +226 -0
  1778. package/docs/zh-CN/start/onboarding-overview.md +58 -0
  1779. package/docs/zh-CN/start/onboarding.md +105 -0
  1780. package/docs/zh-CN/start/openclaw.md +248 -0
  1781. package/docs/zh-CN/start/quickstart.md +88 -0
  1782. package/docs/zh-CN/start/setup.md +153 -0
  1783. package/docs/zh-CN/start/showcase.md +423 -0
  1784. package/docs/zh-CN/start/wizard-cli-automation.md +222 -0
  1785. package/docs/zh-CN/start/wizard-cli-reference.md +306 -0
  1786. package/docs/zh-CN/start/wizard.md +132 -0
  1787. package/docs/zh-CN/tools/agent-send.md +59 -0
  1788. package/docs/zh-CN/tools/apply-patch.md +57 -0
  1789. package/docs/zh-CN/tools/browser-linux-troubleshooting.md +144 -0
  1790. package/docs/zh-CN/tools/browser-login.md +75 -0
  1791. package/docs/zh-CN/tools/browser.md +553 -0
  1792. package/docs/zh-CN/tools/chrome-extension.md +183 -0
  1793. package/docs/zh-CN/tools/clawhub.md +209 -0
  1794. package/docs/zh-CN/tools/creating-skills.md +61 -0
  1795. package/docs/zh-CN/tools/elevated.md +64 -0
  1796. package/docs/zh-CN/tools/exec-approvals.md +234 -0
  1797. package/docs/zh-CN/tools/exec.md +169 -0
  1798. package/docs/zh-CN/tools/firecrawl.md +68 -0
  1799. package/docs/zh-CN/tools/index.md +515 -0
  1800. package/docs/zh-CN/tools/llm-task.md +117 -0
  1801. package/docs/zh-CN/tools/lobster.md +349 -0
  1802. package/docs/zh-CN/tools/multi-agent-sandbox-tools.md +401 -0
  1803. package/docs/zh-CN/tools/plugin.md +1612 -0
  1804. package/docs/zh-CN/tools/reactions.md +29 -0
  1805. package/docs/zh-CN/tools/skills-config.md +78 -0
  1806. package/docs/zh-CN/tools/skills.md +279 -0
  1807. package/docs/zh-CN/tools/slash-commands.md +205 -0
  1808. package/docs/zh-CN/tools/subagents.md +167 -0
  1809. package/docs/zh-CN/tools/thinking.md +80 -0
  1810. package/docs/zh-CN/tools/web.md +289 -0
  1811. package/docs/zh-CN/tts.md +375 -0
  1812. package/docs/zh-CN/vps.md +47 -0
  1813. package/docs/zh-CN/web/control-ui.md +191 -0
  1814. package/docs/zh-CN/web/dashboard.md +53 -0
  1815. package/docs/zh-CN/web/index.md +118 -0
  1816. package/docs/zh-CN/web/tui.md +166 -0
  1817. package/docs/zh-CN/web/webchat.md +56 -0
  1818. package/openclaw.mjs +135 -0
  1819. package/package.json +835 -0
  1820. package/skills/1password/SKILL.md +70 -0
  1821. package/skills/1password/references/cli-examples.md +29 -0
  1822. package/skills/1password/references/get-started.md +17 -0
  1823. package/skills/apple-notes/SKILL.md +77 -0
  1824. package/skills/apple-reminders/SKILL.md +118 -0
  1825. package/skills/bear-notes/SKILL.md +107 -0
  1826. package/skills/blogwatcher/SKILL.md +69 -0
  1827. package/skills/blucli/SKILL.md +47 -0
  1828. package/skills/bluebubbles/SKILL.md +131 -0
  1829. package/skills/camsnap/SKILL.md +45 -0
  1830. package/skills/canvas/SKILL.md +198 -0
  1831. package/skills/clawhub/SKILL.md +77 -0
  1832. package/skills/coding-agent/SKILL.md +295 -0
  1833. package/skills/discord/SKILL.md +197 -0
  1834. package/skills/doubao-code/SKILL.md +43 -0
  1835. package/skills/eightctl/SKILL.md +50 -0
  1836. package/skills/gemini/SKILL.md +43 -0
  1837. package/skills/gh-issues/SKILL.md +865 -0
  1838. package/skills/gifgrep/SKILL.md +79 -0
  1839. package/skills/github/SKILL.md +163 -0
  1840. package/skills/gog/SKILL.md +116 -0
  1841. package/skills/goplaces/SKILL.md +52 -0
  1842. package/skills/healthcheck/SKILL.md +245 -0
  1843. package/skills/himalaya/SKILL.md +257 -0
  1844. package/skills/himalaya/references/configuration.md +184 -0
  1845. package/skills/himalaya/references/message-composition.md +199 -0
  1846. package/skills/imsg/SKILL.md +122 -0
  1847. package/skills/kimi-code/SKILL.md +42 -0
  1848. package/skills/mcporter/SKILL.md +61 -0
  1849. package/skills/model-usage/SKILL.md +69 -0
  1850. package/skills/model-usage/references/codexbar-cli.md +33 -0
  1851. package/skills/model-usage/scripts/model_usage.py +320 -0
  1852. package/skills/model-usage/scripts/test_model_usage.py +40 -0
  1853. package/skills/nano-pdf/SKILL.md +38 -0
  1854. package/skills/node-connect/SKILL.md +142 -0
  1855. package/skills/notion/SKILL.md +174 -0
  1856. package/skills/obsidian/SKILL.md +81 -0
  1857. package/skills/openai-image-gen/SKILL.md +92 -0
  1858. package/skills/openai-image-gen/scripts/gen.py +328 -0
  1859. package/skills/openai-image-gen/scripts/test_gen.py +140 -0
  1860. package/skills/openai-whisper/SKILL.md +38 -0
  1861. package/skills/openai-whisper-api/SKILL.md +52 -0
  1862. package/skills/openai-whisper-api/scripts/transcribe.sh +85 -0
  1863. package/skills/openhue/SKILL.md +112 -0
  1864. package/skills/oracle/SKILL.md +125 -0
  1865. package/skills/ordercli/SKILL.md +78 -0
  1866. package/skills/peekaboo/SKILL.md +190 -0
  1867. package/skills/sag/SKILL.md +87 -0
  1868. package/skills/session-logs/SKILL.md +115 -0
  1869. package/skills/sherpa-onnx-tts/SKILL.md +103 -0
  1870. package/skills/sherpa-onnx-tts/bin/sherpa-onnx-tts +178 -0
  1871. package/skills/skill-creator/SKILL.md +372 -0
  1872. package/skills/skill-creator/license.txt +202 -0
  1873. package/skills/skill-creator/scripts/init_skill.py +378 -0
  1874. package/skills/skill-creator/scripts/package_skill.py +139 -0
  1875. package/skills/skill-creator/scripts/quick_validate.py +159 -0
  1876. package/skills/skill-creator/scripts/test_package_skill.py +160 -0
  1877. package/skills/skill-creator/scripts/test_quick_validate.py +72 -0
  1878. package/skills/slack/SKILL.md +144 -0
  1879. package/skills/songsee/SKILL.md +49 -0
  1880. package/skills/sonoscli/SKILL.md +65 -0
  1881. package/skills/spotify-player/SKILL.md +64 -0
  1882. package/skills/summarize/SKILL.md +87 -0
  1883. package/skills/things-mac/SKILL.md +86 -0
  1884. package/skills/tmux/SKILL.md +153 -0
  1885. package/skills/tmux/scripts/find-sessions.sh +112 -0
  1886. package/skills/tmux/scripts/wait-for-text.sh +83 -0
  1887. package/skills/trello/SKILL.md +95 -0
  1888. package/skills/video-frames/SKILL.md +46 -0
  1889. package/skills/video-frames/scripts/frame.sh +81 -0
  1890. package/skills/voice-call/SKILL.md +45 -0
  1891. package/skills/wacli/SKILL.md +72 -0
  1892. package/skills/weather/SKILL.md +112 -0
  1893. package/skills/xurl/SKILL.md +461 -0
@@ -0,0 +1,4945 @@
1
+ import { t as formatDocsLink } from "./links-Bilm-v0z.js";
2
+ import { d as resolveThreadSessionKeys$1 } from "./session-key-gFFk3uv9.js";
3
+ import { n as normalizeAccountId, t as DEFAULT_ACCOUNT_ID } from "./account-id-ZCrgXl7Z.js";
4
+ import { r as normalizeProviderId } from "./provider-id-BpXo5t6v.js";
5
+ import { a as hasConfiguredSecretInput, c as normalizeResolvedSecretInputString, l as normalizeSecretInputString } from "./types.secrets-BWSeXrF4.js";
6
+ import { F as requireOpenAllowFrom, a as DmPolicySchema, c as GroupPolicySchema, m as MarkdownConfigSchema, n as BlockStreamingCoalesceSchema } from "./zod-schema.core-BdgRr-F1.js";
7
+ import { o as isTrustedProxyAddress, u as resolveClientIp } from "./net-DlJFp95v.js";
8
+ import { n as loadSessionStore } from "./store-CvL8MPei.js";
9
+ import { a as resolveAllowlistMatchSimple } from "./allowlist-match-CYmPgg1K.js";
10
+ import { l as resolveStorePath } from "./paths-CTjJI9l0.js";
11
+ import { i as parseStrictPositiveInteger } from "./parse-finite-number-CP4MQF_w.js";
12
+ import { t as rawDataToString } from "./ws-By-QcLjg.js";
13
+ import { t as createAccountListHelpers } from "./account-helpers-D3c_eI7c.js";
14
+ import { r as buildSecretInputSchema } from "./secret-input-4REZ4sHo.js";
15
+ import { t as normalizeOutboundThreadId } from "./routing-Y3m0o-kB.js";
16
+ import { Ea as buildModelsProviderData } from "./pi-embedded-CSQySvOV.js";
17
+ import { t as createDedupeCache } from "./dedupe-Cgnk5BbX.js";
18
+ import { t as registerPluginHttpRoute } from "./http-registry-WFFbLYRd.js";
19
+ import { t as createScopedAccountReplyToModeResolver } from "./threading-helpers-Cq55SUtb.js";
20
+ import { i as createScopedChannelConfigAdapter, o as createScopedDmSecurityResolver } from "./channel-config-helpers-CieQWILI.js";
21
+ import { a as warnMissingProviderGroupPolicyFallbackOnce, n as resolveAllowlistProviderRuntimeGroupPolicy, r as resolveDefaultGroupPolicy } from "./runtime-group-policy-B7irU4eu.js";
22
+ import { p as createAllowlistProviderRestrictSendersWarningCollector } from "./group-policy-warnings-Ddu6lBkh.js";
23
+ import { n as createChannelPairingController, r as createLoggedPairingApprovalNotifier } from "./channel-pairing-D54mn51y.js";
24
+ import { i as createAttachedChannelResultAdapter } from "./channel-send-result-By8EpCPw.js";
25
+ import { r as buildChannelConfigSchema } from "./config-schema-B1UGMwZ8.js";
26
+ import { a as migrateBaseNameToDefaultAccount, n as applySetupAccountConfigPatch, t as applyAccountNameToChannelSection } from "./setup-helpers-B62Ecg9r.js";
27
+ import { o as stripChannelTargetPrefix, s as stripTargetKindPrefix, t as buildChannelOutboundSessionRoute } from "./core-C7aHA4Aq.js";
28
+ import { i as evaluateSenderGroupAccessForPolicy } from "./group-access-DJZrYPx1.js";
29
+ import { n as resolveControlCommandGate } from "./command-gating-BQXGSqc9.js";
30
+ import { n as readStoreAllowFromForDmPolicy, o as resolveDmGroupAccessWithLists, s as resolveEffectiveAllowFromLists, t as DM_GROUP_ACCESS_REASON } from "./dm-policy-shared-6bCJzHOS.js";
31
+ import { t as getAgentScopedMediaLocalRoots } from "./local-roots-DAzCjWbC.js";
32
+ import { t as listSkillCommandsForAgents } from "./skill-commands-CiSwTFBQ.js";
33
+ import { i as resolveStoredModelOverride } from "./model-selection-Ci9cPkL2.js";
34
+ import { i as deliverTextOrMediaReply, p as resolveSendableOutboundReplyParts } from "./reply-payload-DBGc074f.js";
35
+ import { r as buildComputedAccountStatusSnapshot } from "./status-helpers-Cda-rGLX.js";
36
+ import { n as resolveChannelGroupRequireMention } from "./group-policy-CWFxv3iB.js";
37
+ import { n as isDangerousNameMatchingEnabled } from "./dangerous-name-matching-DZa_t0RM.js";
38
+ import { t as resolveChannelMediaMaxBytes } from "./media-limits-Cuvmmhop.js";
39
+ import { l as buildAgentMediaPayload } from "./axios-xDDnM0KG.js";
40
+ import { l as formatInboundFromLabel$1 } from "./channel-inbound-DwzVf2PK.js";
41
+ import { n as formatNormalizedAllowFromEntries } from "./allow-from-C4iBpqFI.js";
42
+ import { t as createMessageToolButtonsSchema } from "./channel-actions-DU2CR3xW.js";
43
+ import { a as isRequestBodyLimitError, s as readRequestBodyWithLimit } from "./http-body-CCiSfloA.js";
44
+ import { a as buildPendingHistoryContextFromMap, s as clearHistoryEntriesIfEnabled, u as recordPendingHistoryEntryIfEnabled } from "./history-CHjo8B5W.js";
45
+ import { n as logInboundDrop, r as logTypingFailure } from "./logging-Bz1qZDPg.js";
46
+ import { t as createChannelReplyPipeline } from "./channel-reply-pipeline-CPTuaW8n.js";
47
+ import { t as createChannelDirectoryAdapter } from "./directory-runtime-DhC8QkMq.js";
48
+ import { t as createAccountStatusSink } from "./channel-lifecycle-CpU1dRbh.js";
49
+ import { t as createPluginRuntimeStore } from "./runtime-store-Bt3Sdbrn.js";
50
+ import { n as buildPassiveProbedChannelStatusSummary, s as requireChannelOpenAllowFrom } from "./extension-shared-5txN7IXK.js";
51
+ import { t as loadOutboundMediaFromUrl } from "./outbound-media-69yrWRDt.js";
52
+ import { createHash, createHmac, timingSafeEqual } from "node:crypto";
53
+ import { z } from "zod";
54
+ import WebSocket$1 from "ws";
55
+ //#region extensions/mattermost/src/config-schema.ts
56
+ const DmChannelRetrySchema = z.object({
57
+ maxRetries: z.number().int().min(0).max(10).optional(),
58
+ initialDelayMs: z.number().int().min(100).max(6e4).optional(),
59
+ maxDelayMs: z.number().int().min(1e3).max(6e4).optional(),
60
+ timeoutMs: z.number().int().min(5e3).max(12e4).optional()
61
+ }).strict().refine((data) => {
62
+ if (data.initialDelayMs !== void 0 && data.maxDelayMs !== void 0) return data.initialDelayMs <= data.maxDelayMs;
63
+ return true;
64
+ }, {
65
+ message: "initialDelayMs must be less than or equal to maxDelayMs",
66
+ path: ["initialDelayMs"]
67
+ }).optional();
68
+ const MattermostSlashCommandsSchema = z.object({
69
+ native: z.union([z.boolean(), z.literal("auto")]).optional(),
70
+ nativeSkills: z.union([z.boolean(), z.literal("auto")]).optional(),
71
+ callbackPath: z.string().optional(),
72
+ callbackUrl: z.string().optional()
73
+ }).strict().optional();
74
+ const MattermostAccountSchemaBase = z.object({
75
+ name: z.string().optional(),
76
+ capabilities: z.array(z.string()).optional(),
77
+ dangerouslyAllowNameMatching: z.boolean().optional(),
78
+ markdown: MarkdownConfigSchema,
79
+ enabled: z.boolean().optional(),
80
+ configWrites: z.boolean().optional(),
81
+ botToken: buildSecretInputSchema().optional(),
82
+ baseUrl: z.string().optional(),
83
+ chatmode: z.enum([
84
+ "oncall",
85
+ "onmessage",
86
+ "onchar"
87
+ ]).optional(),
88
+ oncharPrefixes: z.array(z.string()).optional(),
89
+ requireMention: z.boolean().optional(),
90
+ dmPolicy: DmPolicySchema.optional().default("pairing"),
91
+ allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
92
+ groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
93
+ groupPolicy: GroupPolicySchema.optional().default("allowlist"),
94
+ textChunkLimit: z.number().int().positive().optional(),
95
+ chunkMode: z.enum(["length", "newline"]).optional(),
96
+ blockStreaming: z.boolean().optional(),
97
+ blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
98
+ replyToMode: z.enum([
99
+ "off",
100
+ "first",
101
+ "all"
102
+ ]).optional(),
103
+ responsePrefix: z.string().optional(),
104
+ actions: z.object({ reactions: z.boolean().optional() }).optional(),
105
+ commands: MattermostSlashCommandsSchema,
106
+ interactions: z.object({
107
+ callbackBaseUrl: z.string().optional(),
108
+ allowedSourceIps: z.array(z.string()).optional()
109
+ }).optional(),
110
+ dmChannelRetry: DmChannelRetrySchema
111
+ }).strict();
112
+ const MattermostAccountSchema = MattermostAccountSchemaBase.superRefine((value, ctx) => {
113
+ requireChannelOpenAllowFrom({
114
+ channel: "mattermost",
115
+ policy: value.dmPolicy,
116
+ allowFrom: value.allowFrom,
117
+ ctx,
118
+ requireOpenAllowFrom
119
+ });
120
+ });
121
+ const MattermostConfigSchema = MattermostAccountSchemaBase.extend({
122
+ accounts: z.record(z.string(), MattermostAccountSchema.optional()).optional(),
123
+ defaultAccount: z.string().optional()
124
+ }).superRefine((value, ctx) => {
125
+ requireChannelOpenAllowFrom({
126
+ channel: "mattermost",
127
+ policy: value.dmPolicy,
128
+ allowFrom: value.allowFrom,
129
+ ctx,
130
+ requireOpenAllowFrom
131
+ });
132
+ });
133
+ //#endregion
134
+ //#region extensions/mattermost/src/mattermost/client.ts
135
+ function normalizeMattermostBaseUrl(raw) {
136
+ const trimmed = raw?.trim();
137
+ if (!trimmed) return;
138
+ return trimmed.replace(/\/+$/, "").replace(/\/api\/v4$/i, "");
139
+ }
140
+ function buildMattermostApiUrl(baseUrl, path) {
141
+ const normalized = normalizeMattermostBaseUrl(baseUrl);
142
+ if (!normalized) throw new Error("Mattermost baseUrl is required");
143
+ return `${normalized}/api/v4${path.startsWith("/") ? path : `/${path}`}`;
144
+ }
145
+ async function readMattermostError(res) {
146
+ if ((res.headers.get("content-type") ?? "").includes("application/json")) {
147
+ const data = await res.json();
148
+ if (data?.message) return data.message;
149
+ return JSON.stringify(data);
150
+ }
151
+ return await res.text();
152
+ }
153
+ function createMattermostClient(params) {
154
+ const baseUrl = normalizeMattermostBaseUrl(params.baseUrl);
155
+ if (!baseUrl) throw new Error("Mattermost baseUrl is required");
156
+ const apiBaseUrl = `${baseUrl}/api/v4`;
157
+ const token = params.botToken.trim();
158
+ const fetchImpl = params.fetchImpl ?? fetch;
159
+ const request = async (path, init) => {
160
+ const url = buildMattermostApiUrl(baseUrl, path);
161
+ const headers = new Headers(init?.headers);
162
+ headers.set("Authorization", `Bearer ${token}`);
163
+ if (typeof init?.body === "string" && !headers.has("Content-Type")) headers.set("Content-Type", "application/json");
164
+ const res = await fetchImpl(url, {
165
+ ...init,
166
+ headers
167
+ });
168
+ if (!res.ok) {
169
+ const detail = await readMattermostError(res);
170
+ throw new Error(`Mattermost API ${res.status} ${res.statusText}: ${detail || "unknown error"}`);
171
+ }
172
+ if (res.status === 204) return;
173
+ if ((res.headers.get("content-type") ?? "").includes("application/json")) return await res.json();
174
+ return await res.text();
175
+ };
176
+ return {
177
+ baseUrl,
178
+ apiBaseUrl,
179
+ token,
180
+ request
181
+ };
182
+ }
183
+ async function fetchMattermostMe(client) {
184
+ return await client.request("/users/me");
185
+ }
186
+ async function fetchMattermostUser(client, userId) {
187
+ return await client.request(`/users/${userId}`);
188
+ }
189
+ async function fetchMattermostUserByUsername(client, username) {
190
+ return await client.request(`/users/username/${encodeURIComponent(username)}`);
191
+ }
192
+ async function fetchMattermostChannel(client, channelId) {
193
+ return await client.request(`/channels/${channelId}`);
194
+ }
195
+ async function fetchMattermostChannelByName(client, teamId, channelName) {
196
+ return await client.request(`/teams/${teamId}/channels/name/${encodeURIComponent(channelName)}`);
197
+ }
198
+ async function sendMattermostTyping(client, params) {
199
+ const payload = { channel_id: params.channelId };
200
+ const parentId = params.parentId?.trim();
201
+ if (parentId) payload.parent_id = parentId;
202
+ await client.request("/users/me/typing", {
203
+ method: "POST",
204
+ body: JSON.stringify(payload)
205
+ });
206
+ }
207
+ async function createMattermostDirectChannel(client, userIds, signal) {
208
+ return await client.request("/channels/direct", {
209
+ method: "POST",
210
+ body: JSON.stringify(userIds),
211
+ signal
212
+ });
213
+ }
214
+ const RETRYABLE_NETWORK_ERROR_CODES = new Set([
215
+ "ECONNRESET",
216
+ "ECONNREFUSED",
217
+ "ETIMEDOUT",
218
+ "ESOCKETTIMEDOUT",
219
+ "ECONNABORTED",
220
+ "ENOTFOUND",
221
+ "EAI_AGAIN",
222
+ "EHOSTUNREACH",
223
+ "ENETUNREACH",
224
+ "EPIPE",
225
+ "UND_ERR_CONNECT_TIMEOUT",
226
+ "UND_ERR_DNS_RESOLVE_FAILED",
227
+ "UND_ERR_CONNECT",
228
+ "UND_ERR_SOCKET",
229
+ "UND_ERR_HEADERS_TIMEOUT",
230
+ "UND_ERR_BODY_TIMEOUT"
231
+ ]);
232
+ const RETRYABLE_NETWORK_ERROR_NAMES = new Set([
233
+ "AbortError",
234
+ "TimeoutError",
235
+ "ConnectTimeoutError",
236
+ "HeadersTimeoutError",
237
+ "BodyTimeoutError"
238
+ ]);
239
+ const RETRYABLE_NETWORK_MESSAGE_SNIPPETS = [
240
+ "network error",
241
+ "timeout",
242
+ "timed out",
243
+ "abort",
244
+ "connection refused",
245
+ "econnreset",
246
+ "econnrefused",
247
+ "etimedout",
248
+ "enotfound",
249
+ "socket hang up",
250
+ "getaddrinfo"
251
+ ];
252
+ /**
253
+ * Creates a Mattermost DM channel with exponential backoff retry logic.
254
+ * Retries on transient errors (429, 5xx, network errors) but not on
255
+ * client errors (4xx except 429) or permanent failures.
256
+ */
257
+ async function createMattermostDirectChannelWithRetry(client, userIds, options = {}) {
258
+ const { maxRetries = 3, initialDelayMs = 1e3, maxDelayMs = 1e4, timeoutMs = 3e4, onRetry } = options;
259
+ let lastError;
260
+ for (let attempt = 0; attempt <= maxRetries; attempt++) try {
261
+ const controller = new AbortController();
262
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
263
+ try {
264
+ return await createMattermostDirectChannel(client, userIds, controller.signal);
265
+ } finally {
266
+ clearTimeout(timeoutId);
267
+ }
268
+ } catch (err) {
269
+ lastError = err instanceof Error ? err : new Error(String(err));
270
+ if (attempt >= maxRetries) break;
271
+ if (!isRetryableError(lastError)) throw lastError;
272
+ const exponentialDelay = initialDelayMs * Math.pow(2, attempt);
273
+ const jitter = Math.random() * exponentialDelay;
274
+ const delayMs = Math.min(exponentialDelay + jitter, maxDelayMs);
275
+ if (onRetry) onRetry(attempt + 1, delayMs, lastError);
276
+ await sleep(delayMs);
277
+ }
278
+ throw lastError ?? /* @__PURE__ */ new Error("Failed to create DM channel after retries");
279
+ }
280
+ function isRetryableError(error) {
281
+ const candidates = collectErrorCandidates(error);
282
+ const messages = candidates.map((candidate) => readErrorMessage(candidate)?.toLowerCase()).filter((message) => Boolean(message));
283
+ if (messages.some((message) => /mattermost api 5\d{2}\b/.test(message))) return true;
284
+ if (messages.some((message) => /mattermost api 429\b/.test(message) || message.includes("too many requests"))) return true;
285
+ for (const message of messages) {
286
+ const clientErrorMatch = message.match(/mattermost api (4\d{2})\b/);
287
+ if (!clientErrorMatch) continue;
288
+ const statusCode = parseInt(clientErrorMatch[1], 10);
289
+ if (statusCode >= 400 && statusCode < 500) return false;
290
+ }
291
+ if (messages.some((message) => /mattermost api \d{3}\b/.test(message))) return false;
292
+ if (candidates.map((candidate) => readErrorCode(candidate)).filter((code) => Boolean(code)).some((code) => RETRYABLE_NETWORK_ERROR_CODES.has(code))) return true;
293
+ if (candidates.map((candidate) => readErrorName(candidate)).filter((name) => Boolean(name)).some((name) => RETRYABLE_NETWORK_ERROR_NAMES.has(name))) return true;
294
+ return messages.some((message) => RETRYABLE_NETWORK_MESSAGE_SNIPPETS.some((pattern) => message.includes(pattern)));
295
+ }
296
+ function collectErrorCandidates(error) {
297
+ const queue = [error];
298
+ const seen = /* @__PURE__ */ new Set();
299
+ const candidates = [];
300
+ while (queue.length > 0) {
301
+ const current = queue.shift();
302
+ if (!current || seen.has(current)) continue;
303
+ seen.add(current);
304
+ candidates.push(current);
305
+ if (typeof current !== "object") continue;
306
+ const nested = current;
307
+ queue.push(nested.cause, nested.reason);
308
+ if (Array.isArray(nested.errors)) queue.push(...nested.errors);
309
+ }
310
+ return candidates;
311
+ }
312
+ function readErrorMessage(error) {
313
+ if (!error || typeof error !== "object") return;
314
+ const message = error.message;
315
+ return typeof message === "string" && message.trim() ? message : void 0;
316
+ }
317
+ function readErrorName(error) {
318
+ if (!error || typeof error !== "object") return;
319
+ const name = error.name;
320
+ return typeof name === "string" && name.trim() ? name : void 0;
321
+ }
322
+ function readErrorCode(error) {
323
+ if (!error || typeof error !== "object") return;
324
+ const { code, errno } = error;
325
+ const raw = typeof code === "string" && code.trim() ? code : errno;
326
+ if (typeof raw === "string" && raw.trim()) return raw.trim().toUpperCase();
327
+ if (typeof raw === "number" && Number.isFinite(raw)) return String(raw);
328
+ }
329
+ function sleep(ms) {
330
+ return new Promise((resolve) => setTimeout(resolve, ms));
331
+ }
332
+ async function createMattermostPost(client, params) {
333
+ const payload = {
334
+ channel_id: params.channelId,
335
+ message: params.message
336
+ };
337
+ if (params.rootId) payload.root_id = params.rootId;
338
+ if (params.fileIds?.length) payload.file_ids = params.fileIds;
339
+ if (params.props) payload.props = params.props;
340
+ return await client.request("/posts", {
341
+ method: "POST",
342
+ body: JSON.stringify(payload)
343
+ });
344
+ }
345
+ async function fetchMattermostUserTeams(client, userId) {
346
+ return await client.request(`/users/${userId}/teams`);
347
+ }
348
+ async function updateMattermostPost(client, postId, params) {
349
+ const payload = { id: postId };
350
+ if (params.message !== void 0) payload.message = params.message;
351
+ if (params.props !== void 0) payload.props = params.props;
352
+ return await client.request(`/posts/${postId}`, {
353
+ method: "PUT",
354
+ body: JSON.stringify(payload)
355
+ });
356
+ }
357
+ async function uploadMattermostFile(client, params) {
358
+ const form = new FormData();
359
+ const fileName = params.fileName?.trim() || "upload";
360
+ const bytes = Uint8Array.from(params.buffer);
361
+ const blob = params.contentType ? new Blob([bytes], { type: params.contentType }) : new Blob([bytes]);
362
+ form.append("files", blob, fileName);
363
+ form.append("channel_id", params.channelId);
364
+ const res = await fetch(`${client.apiBaseUrl}/files`, {
365
+ method: "POST",
366
+ headers: { Authorization: `Bearer ${client.token}` },
367
+ body: form
368
+ });
369
+ if (!res.ok) {
370
+ const detail = await readMattermostError(res);
371
+ throw new Error(`Mattermost API ${res.status} ${res.statusText}: ${detail || "unknown error"}`);
372
+ }
373
+ const info = (await res.json()).file_infos?.[0];
374
+ if (!info?.id) throw new Error("Mattermost file upload failed");
375
+ return info;
376
+ }
377
+ //#endregion
378
+ //#region extensions/mattermost/src/mattermost/accounts.ts
379
+ const { listAccountIds: listMattermostAccountIds, resolveDefaultAccountId: resolveDefaultMattermostAccountId } = createAccountListHelpers("mattermost");
380
+ function resolveAccountConfig(cfg, accountId) {
381
+ const accounts = cfg.channels?.mattermost?.accounts;
382
+ if (!accounts || typeof accounts !== "object") return;
383
+ return accounts[accountId];
384
+ }
385
+ function mergeMattermostAccountConfig(cfg, accountId) {
386
+ const { accounts: _ignored, defaultAccount: _ignoredDefaultAccount, ...base } = cfg.channels?.mattermost ?? {};
387
+ const account = resolveAccountConfig(cfg, accountId) ?? {};
388
+ const mergedCommands = {
389
+ ...base.commands ?? {},
390
+ ...account.commands ?? {}
391
+ };
392
+ const merged = {
393
+ ...base,
394
+ ...account
395
+ };
396
+ if (Object.keys(mergedCommands).length > 0) merged.commands = mergedCommands;
397
+ return merged;
398
+ }
399
+ function resolveMattermostRequireMention(config) {
400
+ if (config.chatmode === "oncall") return true;
401
+ if (config.chatmode === "onmessage") return false;
402
+ if (config.chatmode === "onchar") return true;
403
+ return config.requireMention;
404
+ }
405
+ function resolveMattermostAccount(params) {
406
+ const accountId = normalizeAccountId(params.accountId);
407
+ const baseEnabled = params.cfg.channels?.mattermost?.enabled !== false;
408
+ const merged = mergeMattermostAccountConfig(params.cfg, accountId);
409
+ const accountEnabled = merged.enabled !== false;
410
+ const enabled = baseEnabled && accountEnabled;
411
+ const allowEnv = accountId === DEFAULT_ACCOUNT_ID;
412
+ const envToken = allowEnv ? process.env.MATTERMOST_BOT_TOKEN?.trim() : void 0;
413
+ const envUrl = allowEnv ? process.env.MATTERMOST_URL?.trim() : void 0;
414
+ const configToken = params.allowUnresolvedSecretRef ? normalizeSecretInputString(merged.botToken) : normalizeResolvedSecretInputString({
415
+ value: merged.botToken,
416
+ path: `channels.mattermost.accounts.${accountId}.botToken`
417
+ });
418
+ const configUrl = merged.baseUrl?.trim();
419
+ const botToken = configToken || envToken;
420
+ const baseUrl = normalizeMattermostBaseUrl(configUrl || envUrl);
421
+ const requireMention = resolveMattermostRequireMention(merged);
422
+ const botTokenSource = configToken ? "config" : envToken ? "env" : "none";
423
+ const baseUrlSource = configUrl ? "config" : envUrl ? "env" : "none";
424
+ return {
425
+ accountId,
426
+ enabled,
427
+ name: merged.name?.trim() || void 0,
428
+ botToken,
429
+ baseUrl,
430
+ botTokenSource,
431
+ baseUrlSource,
432
+ config: merged,
433
+ chatmode: merged.chatmode,
434
+ oncharPrefixes: merged.oncharPrefixes,
435
+ requireMention,
436
+ textChunkLimit: merged.textChunkLimit,
437
+ blockStreaming: merged.blockStreaming,
438
+ blockStreamingCoalesce: merged.blockStreamingCoalesce
439
+ };
440
+ }
441
+ /**
442
+ * Resolve the effective replyToMode for a given chat type.
443
+ * Mattermost auto-threading only applies to channel and group messages.
444
+ */
445
+ function resolveMattermostReplyToMode(account, kind) {
446
+ if (kind === "direct") return "off";
447
+ return account.config.replyToMode ?? "off";
448
+ }
449
+ //#endregion
450
+ //#region extensions/mattermost/src/group-mentions.ts
451
+ function resolveMattermostGroupRequireMention(params) {
452
+ const account = resolveMattermostAccount({
453
+ cfg: params.cfg,
454
+ accountId: params.accountId
455
+ });
456
+ const requireMentionOverride = typeof params.requireMentionOverride === "boolean" ? params.requireMentionOverride : account.requireMention;
457
+ return resolveChannelGroupRequireMention({
458
+ cfg: params.cfg,
459
+ channel: "mattermost",
460
+ groupId: params.groupId,
461
+ accountId: params.accountId,
462
+ requireMentionOverride
463
+ });
464
+ }
465
+ //#endregion
466
+ //#region extensions/mattermost/src/mattermost/directory.ts
467
+ function buildClient(params) {
468
+ const account = resolveMattermostAccount({
469
+ cfg: params.cfg,
470
+ accountId: params.accountId
471
+ });
472
+ if (!account.enabled || !account.botToken || !account.baseUrl) return null;
473
+ return createMattermostClient({
474
+ baseUrl: account.baseUrl,
475
+ botToken: account.botToken
476
+ });
477
+ }
478
+ /**
479
+ * Build clients from ALL enabled accounts (deduplicated by token).
480
+ *
481
+ * We always scan every account because:
482
+ * - Private channels are only visible to bots that are members
483
+ * - The requesting agent's account may have an expired/invalid token
484
+ *
485
+ * This means a single healthy bot token is enough for directory discovery.
486
+ */
487
+ function buildClients(params) {
488
+ const accountIds = listMattermostAccountIds(params.cfg);
489
+ const seen = /* @__PURE__ */ new Set();
490
+ const clients = [];
491
+ for (const id of accountIds) {
492
+ const client = buildClient({
493
+ cfg: params.cfg,
494
+ accountId: id
495
+ });
496
+ if (client && !seen.has(client.token)) {
497
+ seen.add(client.token);
498
+ clients.push(client);
499
+ }
500
+ }
501
+ return clients;
502
+ }
503
+ /**
504
+ * List channels (public + private) visible to any configured bot account.
505
+ *
506
+ * NOTE: Uses per_page=200 which covers most instances. Mattermost does not
507
+ * return a "has more" indicator, so very large instances (200+ channels per bot)
508
+ * may see incomplete results. Pagination can be added if needed.
509
+ */
510
+ async function listMattermostDirectoryGroups(params) {
511
+ const clients = buildClients(params);
512
+ if (!clients.length) return [];
513
+ const q = params.query?.trim().toLowerCase() || "";
514
+ const seenIds = /* @__PURE__ */ new Set();
515
+ const entries = [];
516
+ for (const client of clients) try {
517
+ const me = await fetchMattermostMe(client);
518
+ const channels = await client.request(`/users/${me.id}/channels?per_page=200`);
519
+ for (const ch of channels) {
520
+ if (ch.type !== "O" && ch.type !== "P") continue;
521
+ if (seenIds.has(ch.id)) continue;
522
+ if (q) {
523
+ const name = (ch.name ?? "").toLowerCase();
524
+ const display = (ch.display_name ?? "").toLowerCase();
525
+ if (!name.includes(q) && !display.includes(q)) continue;
526
+ }
527
+ seenIds.add(ch.id);
528
+ entries.push({
529
+ kind: "group",
530
+ id: `channel:${ch.id}`,
531
+ name: ch.name ?? void 0,
532
+ handle: ch.display_name ?? void 0
533
+ });
534
+ }
535
+ } catch (err) {
536
+ console.debug?.("[mattermost-directory] listGroups: skipping account:", err?.message);
537
+ continue;
538
+ }
539
+ return params.limit && params.limit > 0 ? entries.slice(0, params.limit) : entries;
540
+ }
541
+ /**
542
+ * List team members as peer directory entries.
543
+ *
544
+ * Uses only the first available client since all bots in a team see the same
545
+ * user list (unlike channels where membership varies). Uses the first team
546
+ * returned — multi-team setups will only see members from that team.
547
+ *
548
+ * NOTE: per_page=200 for member listing; same pagination caveat as groups.
549
+ */
550
+ async function listMattermostDirectoryPeers(params) {
551
+ const clients = buildClients(params);
552
+ if (!clients.length) return [];
553
+ const client = clients[0];
554
+ try {
555
+ const me = await fetchMattermostMe(client);
556
+ const teams = await client.request("/users/me/teams");
557
+ if (!teams.length) return [];
558
+ const teamId = teams[0].id;
559
+ const q = params.query?.trim().toLowerCase() || "";
560
+ let users;
561
+ if (q) users = await client.request("/users/search", {
562
+ method: "POST",
563
+ body: JSON.stringify({
564
+ term: q,
565
+ team_id: teamId
566
+ })
567
+ });
568
+ else {
569
+ const userIds = (await client.request(`/teams/${teamId}/members?per_page=200`)).map((m) => m.user_id).filter((id) => id !== me.id);
570
+ if (!userIds.length) return [];
571
+ users = await client.request("/users/ids", {
572
+ method: "POST",
573
+ body: JSON.stringify(userIds)
574
+ });
575
+ }
576
+ const entries = users.filter((u) => u.id !== me.id).map((u) => ({
577
+ kind: "user",
578
+ id: `user:${u.id}`,
579
+ name: u.username ?? void 0,
580
+ handle: [u.first_name, u.last_name].filter(Boolean).join(" ").trim() || u.nickname || void 0
581
+ }));
582
+ return params.limit && params.limit > 0 ? entries.slice(0, params.limit) : entries;
583
+ } catch (err) {
584
+ console.debug?.("[mattermost-directory] listPeers failed:", err?.message);
585
+ return [];
586
+ }
587
+ }
588
+ //#endregion
589
+ //#region extensions/mattermost/src/runtime.ts
590
+ const { setRuntime: setMattermostRuntime, getRuntime: getMattermostRuntime } = createPluginRuntimeStore("Mattermost runtime not initialized");
591
+ //#endregion
592
+ //#region extensions/mattermost/src/mattermost/interactions.ts
593
+ const INTERACTION_MAX_BODY_BYTES = 64 * 1024;
594
+ const INTERACTION_BODY_TIMEOUT_MS = 1e4;
595
+ const SIGNED_CHANNEL_ID_CONTEXT_KEY = "__openclaw_channel_id";
596
+ const callbackUrls = /* @__PURE__ */ new Map();
597
+ function setInteractionCallbackUrl(accountId, url) {
598
+ callbackUrls.set(accountId, url);
599
+ }
600
+ function resolveInteractionCallbackPath(accountId) {
601
+ return `/mattermost/interactions/${accountId}`;
602
+ }
603
+ function isWildcardBindHost(rawHost) {
604
+ const trimmed = rawHost.trim();
605
+ if (!trimmed) return false;
606
+ const host = trimmed.startsWith("[") && trimmed.endsWith("]") ? trimmed.slice(1, -1) : trimmed;
607
+ return host === "0.0.0.0" || host === "::" || host === "0:0:0:0:0:0:0:0" || host === "::0";
608
+ }
609
+ function normalizeCallbackBaseUrl(baseUrl) {
610
+ return baseUrl.trim().replace(/\/+$/, "");
611
+ }
612
+ function headerValue(value) {
613
+ if (Array.isArray(value)) return value[0]?.trim() || void 0;
614
+ return value?.trim() || void 0;
615
+ }
616
+ function isAllowedInteractionSource(params) {
617
+ const { allowedSourceIps } = params;
618
+ if (!allowedSourceIps?.length) return true;
619
+ return isTrustedProxyAddress(resolveClientIp({
620
+ remoteAddr: params.req.socket?.remoteAddress,
621
+ forwardedFor: headerValue(params.req.headers["x-forwarded-for"]),
622
+ realIp: headerValue(params.req.headers["x-real-ip"]),
623
+ trustedProxies: params.trustedProxies,
624
+ allowRealIpFallback: params.allowRealIpFallback
625
+ }), allowedSourceIps);
626
+ }
627
+ /**
628
+ * Resolve the interaction callback URL for an account.
629
+ * Falls back to computing it from interactions.callbackBaseUrl or gateway host config.
630
+ */
631
+ function computeInteractionCallbackUrl(accountId, cfg) {
632
+ const path = resolveInteractionCallbackPath(accountId);
633
+ const callbackBaseUrl = cfg?.interactions?.callbackBaseUrl?.trim() ?? cfg?.channels?.mattermost?.interactions?.callbackBaseUrl?.trim();
634
+ if (callbackBaseUrl) return `${normalizeCallbackBaseUrl(callbackBaseUrl)}${path}`;
635
+ const port = typeof cfg?.gateway?.port === "number" ? cfg.gateway.port : 18789;
636
+ let host = cfg?.gateway?.customBindHost && !isWildcardBindHost(cfg.gateway.customBindHost) ? cfg.gateway.customBindHost.trim() : "localhost";
637
+ if (host.includes(":") && !(host.startsWith("[") && host.endsWith("]"))) host = `[${host}]`;
638
+ return `http://${host}:${port}${path}`;
639
+ }
640
+ /**
641
+ * Resolve the interaction callback URL for an account.
642
+ * Prefers the in-memory registered URL (set by the gateway monitor) so callers outside the
643
+ * monitor lifecycle can reuse the runtime-validated callback destination.
644
+ */
645
+ function resolveInteractionCallbackUrl(accountId, cfg) {
646
+ const cached = callbackUrls.get(accountId);
647
+ if (cached) return cached;
648
+ return computeInteractionCallbackUrl(accountId, cfg);
649
+ }
650
+ const interactionSecrets = /* @__PURE__ */ new Map();
651
+ let defaultInteractionSecret;
652
+ function deriveInteractionSecret(botToken) {
653
+ return createHmac("sha256", "openclaw-mattermost-interactions").update(botToken).digest("hex");
654
+ }
655
+ function setInteractionSecret(accountIdOrBotToken, botToken) {
656
+ if (typeof botToken === "string") {
657
+ interactionSecrets.set(accountIdOrBotToken, deriveInteractionSecret(botToken));
658
+ return;
659
+ }
660
+ defaultInteractionSecret = deriveInteractionSecret(accountIdOrBotToken);
661
+ }
662
+ function getInteractionSecret(accountId) {
663
+ const scoped = accountId ? interactionSecrets.get(accountId) : void 0;
664
+ if (scoped) return scoped;
665
+ if (defaultInteractionSecret) return defaultInteractionSecret;
666
+ if (interactionSecrets.size === 1) {
667
+ const first = interactionSecrets.values().next().value;
668
+ if (typeof first === "string") return first;
669
+ }
670
+ throw new Error("Interaction secret not initialized — call setInteractionSecret(accountId, botToken) first");
671
+ }
672
+ function canonicalizeInteractionContext(value) {
673
+ if (Array.isArray(value)) return value.map((item) => canonicalizeInteractionContext(item));
674
+ if (value && typeof value === "object") {
675
+ const entries = Object.entries(value).filter(([, entryValue]) => entryValue !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, entryValue]) => [key, canonicalizeInteractionContext(entryValue)]);
676
+ return Object.fromEntries(entries);
677
+ }
678
+ return value;
679
+ }
680
+ function generateInteractionToken(context, accountId) {
681
+ const secret = getInteractionSecret(accountId);
682
+ const payload = JSON.stringify(canonicalizeInteractionContext(context));
683
+ return createHmac("sha256", secret).update(payload).digest("hex");
684
+ }
685
+ function verifyInteractionToken(context, token, accountId) {
686
+ const expected = generateInteractionToken(context, accountId);
687
+ if (expected.length !== token.length) return false;
688
+ return timingSafeEqual(Buffer.from(expected), Buffer.from(token));
689
+ }
690
+ /**
691
+ * Build Mattermost `props.attachments` with interactive buttons.
692
+ *
693
+ * Each button includes an HMAC token in its integration context so the
694
+ * callback handler can verify the request originated from a legitimate
695
+ * button click (Mattermost's recommended security pattern).
696
+ */
697
+ /**
698
+ * Sanitize a button ID so Mattermost's action router can match it.
699
+ * Mattermost uses the action ID in the URL path `/api/v4/posts/{id}/actions/{actionId}`
700
+ * and IDs containing hyphens or underscores break the server-side routing.
701
+ * See: https://github.com/mattermost/mattermost/issues/25747
702
+ */
703
+ function sanitizeActionId(id) {
704
+ return id.replace(/[-_]/g, "");
705
+ }
706
+ function buildButtonAttachments(params) {
707
+ const actions = params.buttons.map((btn) => {
708
+ const safeId = sanitizeActionId(btn.id);
709
+ const context = {
710
+ action_id: safeId,
711
+ ...btn.context
712
+ };
713
+ const token = generateInteractionToken(context, params.accountId);
714
+ return {
715
+ id: safeId,
716
+ type: "button",
717
+ name: btn.name,
718
+ style: btn.style,
719
+ integration: {
720
+ url: params.callbackUrl,
721
+ context: {
722
+ ...context,
723
+ _token: token
724
+ }
725
+ }
726
+ };
727
+ });
728
+ return [{
729
+ text: params.text ?? "",
730
+ actions
731
+ }];
732
+ }
733
+ function buildButtonProps(params) {
734
+ const buttons = params.buttons.flatMap((item) => Array.isArray(item) ? item : [item]).map((btn) => ({
735
+ id: String(btn.id ?? btn.callback_data ?? "").trim(),
736
+ name: String(btn.text ?? btn.name ?? btn.label ?? "").trim(),
737
+ style: btn.style ?? "default",
738
+ context: typeof btn.context === "object" && btn.context !== null ? {
739
+ ...btn.context,
740
+ [SIGNED_CHANNEL_ID_CONTEXT_KEY]: params.channelId
741
+ } : { [SIGNED_CHANNEL_ID_CONTEXT_KEY]: params.channelId }
742
+ })).filter((btn) => btn.id && btn.name);
743
+ if (buttons.length === 0) return;
744
+ return { attachments: buildButtonAttachments({
745
+ callbackUrl: params.callbackUrl,
746
+ accountId: params.accountId,
747
+ buttons,
748
+ text: params.text
749
+ }) };
750
+ }
751
+ function readInteractionBody(req) {
752
+ return new Promise((resolve, reject) => {
753
+ const chunks = [];
754
+ let totalBytes = 0;
755
+ const timer = setTimeout(() => {
756
+ req.destroy();
757
+ reject(/* @__PURE__ */ new Error("Request body read timeout"));
758
+ }, INTERACTION_BODY_TIMEOUT_MS);
759
+ req.on("data", (chunk) => {
760
+ totalBytes += chunk.length;
761
+ if (totalBytes > INTERACTION_MAX_BODY_BYTES) {
762
+ req.destroy();
763
+ clearTimeout(timer);
764
+ reject(/* @__PURE__ */ new Error("Request body too large"));
765
+ return;
766
+ }
767
+ chunks.push(chunk);
768
+ });
769
+ req.on("end", () => {
770
+ clearTimeout(timer);
771
+ resolve(Buffer.concat(chunks).toString("utf8"));
772
+ });
773
+ req.on("error", (err) => {
774
+ clearTimeout(timer);
775
+ reject(err);
776
+ });
777
+ });
778
+ }
779
+ function createMattermostInteractionHandler(params) {
780
+ const { client, accountId, log } = params;
781
+ const core = getMattermostRuntime();
782
+ return async (req, res) => {
783
+ if (req.method !== "POST") {
784
+ res.statusCode = 405;
785
+ res.setHeader("Allow", "POST");
786
+ res.setHeader("Content-Type", "application/json");
787
+ res.end(JSON.stringify({ error: "Method Not Allowed" }));
788
+ return;
789
+ }
790
+ if (!isAllowedInteractionSource({
791
+ req,
792
+ allowedSourceIps: params.allowedSourceIps,
793
+ trustedProxies: params.trustedProxies,
794
+ allowRealIpFallback: params.allowRealIpFallback
795
+ })) {
796
+ log?.(`mattermost interaction: rejected callback source remote=${req.socket?.remoteAddress ?? "?"}`);
797
+ res.statusCode = 403;
798
+ res.setHeader("Content-Type", "application/json");
799
+ res.end(JSON.stringify({ error: "Forbidden origin" }));
800
+ return;
801
+ }
802
+ let payload;
803
+ try {
804
+ const raw = await readInteractionBody(req);
805
+ payload = JSON.parse(raw);
806
+ } catch (err) {
807
+ log?.(`mattermost interaction: failed to parse body: ${String(err)}`);
808
+ res.statusCode = 400;
809
+ res.setHeader("Content-Type", "application/json");
810
+ res.end(JSON.stringify({ error: "Invalid request body" }));
811
+ return;
812
+ }
813
+ const context = payload.context;
814
+ if (!context) {
815
+ res.statusCode = 400;
816
+ res.setHeader("Content-Type", "application/json");
817
+ res.end(JSON.stringify({ error: "Missing context" }));
818
+ return;
819
+ }
820
+ const token = context._token;
821
+ if (typeof token !== "string") {
822
+ log?.("mattermost interaction: missing _token in context");
823
+ res.statusCode = 403;
824
+ res.setHeader("Content-Type", "application/json");
825
+ res.end(JSON.stringify({ error: "Missing token" }));
826
+ return;
827
+ }
828
+ const { _token, ...contextWithoutToken } = context;
829
+ if (!verifyInteractionToken(contextWithoutToken, token, accountId)) {
830
+ log?.("mattermost interaction: invalid _token");
831
+ res.statusCode = 403;
832
+ res.setHeader("Content-Type", "application/json");
833
+ res.end(JSON.stringify({ error: "Invalid token" }));
834
+ return;
835
+ }
836
+ const actionId = context.action_id;
837
+ if (typeof actionId !== "string") {
838
+ res.statusCode = 400;
839
+ res.setHeader("Content-Type", "application/json");
840
+ res.end(JSON.stringify({ error: "Missing action_id in context" }));
841
+ return;
842
+ }
843
+ const signedChannelId = typeof contextWithoutToken[SIGNED_CHANNEL_ID_CONTEXT_KEY] === "string" ? contextWithoutToken[SIGNED_CHANNEL_ID_CONTEXT_KEY].trim() : "";
844
+ if (signedChannelId && signedChannelId !== payload.channel_id) {
845
+ log?.(`mattermost interaction: signed channel mismatch payload=${payload.channel_id} signed=${signedChannelId}`);
846
+ res.statusCode = 403;
847
+ res.setHeader("Content-Type", "application/json");
848
+ res.end(JSON.stringify({ error: "Channel mismatch" }));
849
+ return;
850
+ }
851
+ const userName = payload.user_name ?? payload.user_id;
852
+ let originalMessage = "";
853
+ let originalPost = null;
854
+ let clickedButtonName = null;
855
+ try {
856
+ originalPost = await client.request(`/posts/${payload.post_id}`);
857
+ const postChannelId = originalPost.channel_id?.trim();
858
+ if (!postChannelId || postChannelId !== payload.channel_id) {
859
+ log?.(`mattermost interaction: post channel mismatch payload=${payload.channel_id} post=${postChannelId ?? "<missing>"}`);
860
+ res.statusCode = 403;
861
+ res.setHeader("Content-Type", "application/json");
862
+ res.end(JSON.stringify({ error: "Post/channel mismatch" }));
863
+ return;
864
+ }
865
+ originalMessage = originalPost.message ?? "";
866
+ const postAttachments = Array.isArray(originalPost?.props?.attachments) ? originalPost.props.attachments : [];
867
+ for (const att of postAttachments) {
868
+ const match = att.actions?.find((a) => a.id === actionId);
869
+ if (match?.name) {
870
+ clickedButtonName = match.name;
871
+ break;
872
+ }
873
+ }
874
+ if (clickedButtonName === null) {
875
+ log?.(`mattermost interaction: action ${actionId} not found in post ${payload.post_id}`);
876
+ res.statusCode = 403;
877
+ res.setHeader("Content-Type", "application/json");
878
+ res.end(JSON.stringify({ error: "Unknown action" }));
879
+ return;
880
+ }
881
+ } catch (err) {
882
+ log?.(`mattermost interaction: failed to validate post ${payload.post_id}: ${String(err)}`);
883
+ res.statusCode = 500;
884
+ res.setHeader("Content-Type", "application/json");
885
+ res.end(JSON.stringify({ error: "Failed to validate interaction" }));
886
+ return;
887
+ }
888
+ if (!originalPost) {
889
+ log?.(`mattermost interaction: missing fetched post ${payload.post_id}`);
890
+ res.statusCode = 500;
891
+ res.setHeader("Content-Type", "application/json");
892
+ res.end(JSON.stringify({ error: "Failed to load interaction post" }));
893
+ return;
894
+ }
895
+ log?.(`mattermost interaction: action=${actionId} user=${payload.user_name ?? payload.user_id} post=${payload.post_id} channel=${payload.channel_id}`);
896
+ if (params.authorizeButtonClick) try {
897
+ const authorization = await params.authorizeButtonClick({
898
+ payload,
899
+ post: originalPost
900
+ });
901
+ if (!authorization.ok) {
902
+ res.statusCode = authorization.statusCode ?? 200;
903
+ res.setHeader("Content-Type", "application/json");
904
+ res.end(JSON.stringify(authorization.response ?? { ephemeral_text: "You are not allowed to use this action here." }));
905
+ return;
906
+ }
907
+ } catch (err) {
908
+ log?.(`mattermost interaction: authorization failed: ${String(err)}`);
909
+ res.statusCode = 500;
910
+ res.setHeader("Content-Type", "application/json");
911
+ res.end(JSON.stringify({ error: "Interaction authorization failed" }));
912
+ return;
913
+ }
914
+ if (params.handleInteraction) try {
915
+ const response = await params.handleInteraction({
916
+ payload,
917
+ userName,
918
+ actionId,
919
+ actionName: clickedButtonName,
920
+ originalMessage,
921
+ context: contextWithoutToken,
922
+ post: originalPost
923
+ });
924
+ if (response !== null) {
925
+ res.statusCode = 200;
926
+ res.setHeader("Content-Type", "application/json");
927
+ res.end(JSON.stringify(response));
928
+ return;
929
+ }
930
+ } catch (err) {
931
+ log?.(`mattermost interaction: custom handler failed: ${String(err)}`);
932
+ res.statusCode = 500;
933
+ res.setHeader("Content-Type", "application/json");
934
+ res.end(JSON.stringify({ error: "Interaction handler failed" }));
935
+ return;
936
+ }
937
+ try {
938
+ const eventLabel = `Mattermost button click: action="${actionId}" by ${payload.user_name ?? payload.user_id} in channel ${payload.channel_id}`;
939
+ const sessionKey = params.resolveSessionKey ? await params.resolveSessionKey({
940
+ channelId: payload.channel_id,
941
+ userId: payload.user_id,
942
+ post: originalPost
943
+ }) : `agent:main:mattermost:${accountId}:${payload.channel_id}`;
944
+ core.system.enqueueSystemEvent(eventLabel, {
945
+ sessionKey,
946
+ contextKey: `mattermost:interaction:${payload.post_id}:${actionId}`
947
+ });
948
+ } catch (err) {
949
+ log?.(`mattermost interaction: system event dispatch failed: ${String(err)}`);
950
+ }
951
+ try {
952
+ await updateMattermostPost(client, payload.post_id, {
953
+ message: originalMessage,
954
+ props: { attachments: [{ text: `✓ **${clickedButtonName}** selected by @${userName}` }] }
955
+ });
956
+ } catch (err) {
957
+ log?.(`mattermost interaction: failed to update post ${payload.post_id}: ${String(err)}`);
958
+ }
959
+ res.statusCode = 200;
960
+ res.setHeader("Content-Type", "application/json");
961
+ res.end("{}");
962
+ if (params.dispatchButtonClick) try {
963
+ await params.dispatchButtonClick({
964
+ channelId: payload.channel_id,
965
+ userId: payload.user_id,
966
+ userName,
967
+ actionId,
968
+ actionName: clickedButtonName,
969
+ postId: payload.post_id,
970
+ post: originalPost
971
+ });
972
+ } catch (err) {
973
+ log?.(`mattermost interaction: dispatchButtonClick failed: ${String(err)}`);
974
+ }
975
+ };
976
+ }
977
+ //#endregion
978
+ //#region extensions/mattermost/src/mattermost/model-picker.ts
979
+ const MATTERMOST_MODEL_PICKER_CONTEXT_KEY = "oc_model_picker";
980
+ const MODELS_PAGE_SIZE = 8;
981
+ const ACTION_IDS = {
982
+ providers: "mdlprov",
983
+ list: "mdllist",
984
+ select: "mdlsel",
985
+ back: "mdlback"
986
+ };
987
+ function splitModelRef(modelRef) {
988
+ const match = (modelRef?.trim())?.match(/^([^/]+)\/(.+)$/u);
989
+ if (!match) return null;
990
+ const provider = normalizeProviderId(match[1]);
991
+ const model = match[2].trim();
992
+ if (!provider || !model) return null;
993
+ return {
994
+ provider,
995
+ model
996
+ };
997
+ }
998
+ function normalizePage(value) {
999
+ if (!Number.isFinite(value)) return 1;
1000
+ return Math.max(1, Math.floor(value));
1001
+ }
1002
+ function paginateItems(items, page, pageSize = MODELS_PAGE_SIZE) {
1003
+ const totalPages = Math.max(1, Math.ceil(items.length / pageSize));
1004
+ const safePage = Math.max(1, Math.min(normalizePage(page), totalPages));
1005
+ const start = (safePage - 1) * pageSize;
1006
+ return {
1007
+ items: items.slice(start, start + pageSize),
1008
+ page: safePage,
1009
+ totalPages,
1010
+ hasPrev: safePage > 1,
1011
+ hasNext: safePage < totalPages,
1012
+ totalItems: items.length
1013
+ };
1014
+ }
1015
+ function buildContext(state) {
1016
+ return {
1017
+ [MATTERMOST_MODEL_PICKER_CONTEXT_KEY]: true,
1018
+ ...state
1019
+ };
1020
+ }
1021
+ function buildButtonId(state) {
1022
+ const digest = createHash("sha256").update(JSON.stringify(state)).digest("hex").slice(0, 12);
1023
+ return `${ACTION_IDS[state.action]}${digest}`;
1024
+ }
1025
+ function buildButton(params) {
1026
+ const baseState = params.action === "providers" || params.action === "back" ? {
1027
+ action: params.action,
1028
+ ownerUserId: params.ownerUserId
1029
+ } : params.action === "list" ? {
1030
+ action: "list",
1031
+ ownerUserId: params.ownerUserId,
1032
+ provider: normalizeProviderId(params.provider ?? ""),
1033
+ page: normalizePage(params.page)
1034
+ } : {
1035
+ action: "select",
1036
+ ownerUserId: params.ownerUserId,
1037
+ provider: normalizeProviderId(params.provider ?? ""),
1038
+ page: normalizePage(params.page),
1039
+ model: String(params.model ?? "").trim()
1040
+ };
1041
+ return {
1042
+ id: buildButtonId(baseState),
1043
+ text: params.text,
1044
+ ...params.style ? { style: params.style } : {},
1045
+ context: buildContext(baseState)
1046
+ };
1047
+ }
1048
+ function getProviderModels(data, provider) {
1049
+ return [...data.byProvider.get(normalizeProviderId(provider)) ?? /* @__PURE__ */ new Set()].toSorted();
1050
+ }
1051
+ function formatCurrentModelLine(currentModel) {
1052
+ const parsed = splitModelRef(currentModel);
1053
+ if (!parsed) return "Current: default";
1054
+ return `Current: ${parsed.provider}/${parsed.model}`;
1055
+ }
1056
+ function resolveMattermostModelPickerEntry(commandText) {
1057
+ const normalized = commandText.trim().replace(/\s+/g, " ");
1058
+ if (/^\/model$/i.test(normalized)) return { kind: "summary" };
1059
+ if (/^\/models$/i.test(normalized)) return { kind: "providers" };
1060
+ const providerMatch = normalized.match(/^\/models\s+(\S+)$/i);
1061
+ if (!providerMatch?.[1]) return null;
1062
+ return {
1063
+ kind: "models",
1064
+ provider: normalizeProviderId(providerMatch[1])
1065
+ };
1066
+ }
1067
+ function parseMattermostModelPickerContext(context) {
1068
+ if (!context || context[MATTERMOST_MODEL_PICKER_CONTEXT_KEY] !== true) return null;
1069
+ const ownerUserId = String(context.ownerUserId ?? "").trim();
1070
+ const action = String(context.action ?? "").trim();
1071
+ if (!ownerUserId) return null;
1072
+ if (action === "providers" || action === "back") return {
1073
+ action,
1074
+ ownerUserId
1075
+ };
1076
+ const provider = normalizeProviderId(String(context.provider ?? ""));
1077
+ const page = Number.parseInt(String(context.page ?? "1"), 10);
1078
+ if (!provider) return null;
1079
+ if (action === "list") return {
1080
+ action,
1081
+ ownerUserId,
1082
+ provider,
1083
+ page: normalizePage(page)
1084
+ };
1085
+ if (action === "select") {
1086
+ const model = String(context.model ?? "").trim();
1087
+ if (!model) return null;
1088
+ return {
1089
+ action,
1090
+ ownerUserId,
1091
+ provider,
1092
+ page: normalizePage(page),
1093
+ model
1094
+ };
1095
+ }
1096
+ return null;
1097
+ }
1098
+ function buildMattermostAllowedModelRefs(data) {
1099
+ const refs = /* @__PURE__ */ new Set();
1100
+ for (const provider of data.providers) for (const model of data.byProvider.get(provider) ?? []) refs.add(`${provider}/${model}`);
1101
+ return refs;
1102
+ }
1103
+ function resolveMattermostModelPickerCurrentModel(params) {
1104
+ const fallback = `${params.data.resolvedDefault.provider}/${params.data.resolvedDefault.model}`;
1105
+ try {
1106
+ const storePath = resolveStorePath(params.cfg.session?.store, { agentId: params.route.agentId });
1107
+ const sessionStore = params.skipCache ? loadSessionStore(storePath, { skipCache: true }) : loadSessionStore(storePath);
1108
+ const sessionEntry = sessionStore[params.route.sessionKey];
1109
+ const override = resolveStoredModelOverride({
1110
+ sessionEntry,
1111
+ sessionStore,
1112
+ sessionKey: params.route.sessionKey
1113
+ });
1114
+ if (!override?.model) return fallback;
1115
+ const provider = (override.provider || params.data.resolvedDefault.provider).trim();
1116
+ return provider ? `${provider}/${override.model}` : fallback;
1117
+ } catch {
1118
+ return fallback;
1119
+ }
1120
+ }
1121
+ function renderMattermostModelSummaryView(params) {
1122
+ return {
1123
+ text: [
1124
+ formatCurrentModelLine(params.currentModel),
1125
+ "",
1126
+ "Tap below to browse models, or use:",
1127
+ "/oc_model <provider/model> to switch",
1128
+ "/oc_model status for details"
1129
+ ].join("\n"),
1130
+ buttons: [[buildButton({
1131
+ action: "providers",
1132
+ ownerUserId: params.ownerUserId,
1133
+ text: "Browse providers",
1134
+ style: "primary"
1135
+ })]]
1136
+ };
1137
+ }
1138
+ function renderMattermostProviderPickerView(params) {
1139
+ const currentProvider = splitModelRef(params.currentModel)?.provider;
1140
+ const rows = params.data.providers.map((provider) => [buildButton({
1141
+ action: "list",
1142
+ ownerUserId: params.ownerUserId,
1143
+ text: `${provider} (${params.data.byProvider.get(provider)?.size ?? 0})`,
1144
+ provider,
1145
+ page: 1,
1146
+ style: provider === currentProvider ? "primary" : "default"
1147
+ })]);
1148
+ return {
1149
+ text: [
1150
+ formatCurrentModelLine(params.currentModel),
1151
+ "",
1152
+ "Select a provider:"
1153
+ ].join("\n"),
1154
+ buttons: rows
1155
+ };
1156
+ }
1157
+ function renderMattermostModelsPickerView(params) {
1158
+ const provider = normalizeProviderId(params.provider);
1159
+ const models = getProviderModels(params.data, provider);
1160
+ const current = splitModelRef(params.currentModel);
1161
+ if (models.length === 0) return {
1162
+ text: [
1163
+ formatCurrentModelLine(params.currentModel),
1164
+ "",
1165
+ `Unknown provider: ${provider}`
1166
+ ].join("\n"),
1167
+ buttons: [[buildButton({
1168
+ action: "back",
1169
+ ownerUserId: params.ownerUserId,
1170
+ text: "Back to providers"
1171
+ })]]
1172
+ };
1173
+ const page = paginateItems(models, params.page);
1174
+ const rows = page.items.map((model) => {
1175
+ const isCurrent = current?.provider === provider && current.model === model;
1176
+ return [buildButton({
1177
+ action: "select",
1178
+ ownerUserId: params.ownerUserId,
1179
+ text: isCurrent ? `${model} [current]` : model,
1180
+ provider,
1181
+ model,
1182
+ page: page.page,
1183
+ style: isCurrent ? "primary" : "default"
1184
+ })];
1185
+ });
1186
+ const navRow = [];
1187
+ if (page.hasPrev) navRow.push(buildButton({
1188
+ action: "list",
1189
+ ownerUserId: params.ownerUserId,
1190
+ text: "Prev",
1191
+ provider,
1192
+ page: page.page - 1
1193
+ }));
1194
+ if (page.hasNext) navRow.push(buildButton({
1195
+ action: "list",
1196
+ ownerUserId: params.ownerUserId,
1197
+ text: "Next",
1198
+ provider,
1199
+ page: page.page + 1
1200
+ }));
1201
+ if (navRow.length > 0) rows.push(navRow);
1202
+ rows.push([buildButton({
1203
+ action: "back",
1204
+ ownerUserId: params.ownerUserId,
1205
+ text: "Back to providers"
1206
+ })]);
1207
+ return {
1208
+ text: [
1209
+ `Models (${provider}) - ${page.totalItems} available`,
1210
+ formatCurrentModelLine(params.currentModel),
1211
+ `Page ${page.page}/${page.totalPages}`,
1212
+ "Select a model to switch immediately."
1213
+ ].join("\n"),
1214
+ buttons: rows
1215
+ };
1216
+ }
1217
+ //#endregion
1218
+ //#region extensions/mattermost/src/mattermost/monitor-auth.ts
1219
+ function normalizeMattermostAllowEntry(entry) {
1220
+ const trimmed = entry.trim();
1221
+ if (!trimmed) return "";
1222
+ if (trimmed === "*") return "*";
1223
+ return trimmed.replace(/^(mattermost|user):/i, "").replace(/^@/, "").toLowerCase();
1224
+ }
1225
+ function normalizeMattermostAllowList(entries) {
1226
+ const normalized = entries.map((entry) => normalizeMattermostAllowEntry(String(entry))).filter(Boolean);
1227
+ return Array.from(new Set(normalized));
1228
+ }
1229
+ function resolveMattermostEffectiveAllowFromLists(params) {
1230
+ return resolveEffectiveAllowFromLists({
1231
+ allowFrom: normalizeMattermostAllowList(params.allowFrom ?? []),
1232
+ groupAllowFrom: normalizeMattermostAllowList(params.groupAllowFrom ?? []),
1233
+ storeAllowFrom: normalizeMattermostAllowList(params.storeAllowFrom ?? []),
1234
+ dmPolicy: params.dmPolicy
1235
+ });
1236
+ }
1237
+ function isMattermostSenderAllowed(params) {
1238
+ const allowFrom = normalizeMattermostAllowList(params.allowFrom);
1239
+ if (allowFrom.length === 0) return false;
1240
+ return resolveAllowlistMatchSimple({
1241
+ allowFrom,
1242
+ senderId: normalizeMattermostAllowEntry(params.senderId),
1243
+ senderName: params.senderName ? normalizeMattermostAllowEntry(params.senderName) : void 0,
1244
+ allowNameMatching: params.allowNameMatching
1245
+ }).allowed;
1246
+ }
1247
+ function mapMattermostChannelKind(channelType) {
1248
+ const normalized = channelType?.trim().toUpperCase();
1249
+ if (normalized === "D") return "direct";
1250
+ if (normalized === "G" || normalized === "P") return "group";
1251
+ return "channel";
1252
+ }
1253
+ function authorizeMattermostCommandInvocation(params) {
1254
+ const { account, cfg, senderId, senderName, channelId, channelInfo, storeAllowFrom, allowTextCommands, hasControlCommand } = params;
1255
+ if (!channelInfo) return {
1256
+ ok: false,
1257
+ denyReason: "unknown-channel",
1258
+ commandAuthorized: false,
1259
+ channelInfo: null,
1260
+ kind: "channel",
1261
+ chatType: "channel",
1262
+ channelName: "",
1263
+ channelDisplay: "",
1264
+ roomLabel: `#${channelId}`
1265
+ };
1266
+ const kind = mapMattermostChannelKind(channelInfo.type);
1267
+ const chatType = kind;
1268
+ const channelName = channelInfo.name ?? "";
1269
+ const channelDisplay = channelInfo.display_name ?? channelName;
1270
+ const roomLabel = channelName ? `#${channelName}` : channelDisplay || `#${channelId}`;
1271
+ const dmPolicy = account.config.dmPolicy ?? "pairing";
1272
+ const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy;
1273
+ const groupPolicy = account.config.groupPolicy ?? defaultGroupPolicy ?? "allowlist";
1274
+ const allowNameMatching = isDangerousNameMatchingEnabled(account.config);
1275
+ const configAllowFrom = normalizeMattermostAllowList(account.config.allowFrom ?? []);
1276
+ const configGroupAllowFrom = normalizeMattermostAllowList(account.config.groupAllowFrom ?? []);
1277
+ const { effectiveAllowFrom, effectiveGroupAllowFrom } = resolveMattermostEffectiveAllowFromLists({
1278
+ allowFrom: configAllowFrom,
1279
+ groupAllowFrom: configGroupAllowFrom,
1280
+ storeAllowFrom: normalizeMattermostAllowList(storeAllowFrom ?? []),
1281
+ dmPolicy
1282
+ });
1283
+ const useAccessGroups = cfg.commands?.useAccessGroups !== false;
1284
+ const commandDmAllowFrom = kind === "direct" ? effectiveAllowFrom : configAllowFrom;
1285
+ const commandGroupAllowFrom = kind === "direct" ? effectiveGroupAllowFrom : configGroupAllowFrom.length > 0 ? configGroupAllowFrom : configAllowFrom;
1286
+ const senderAllowedForCommands = isMattermostSenderAllowed({
1287
+ senderId,
1288
+ senderName,
1289
+ allowFrom: commandDmAllowFrom,
1290
+ allowNameMatching
1291
+ });
1292
+ const groupAllowedForCommands = isMattermostSenderAllowed({
1293
+ senderId,
1294
+ senderName,
1295
+ allowFrom: commandGroupAllowFrom,
1296
+ allowNameMatching
1297
+ });
1298
+ const commandGate = resolveControlCommandGate({
1299
+ useAccessGroups,
1300
+ authorizers: [{
1301
+ configured: commandDmAllowFrom.length > 0,
1302
+ allowed: senderAllowedForCommands
1303
+ }, {
1304
+ configured: commandGroupAllowFrom.length > 0,
1305
+ allowed: groupAllowedForCommands
1306
+ }],
1307
+ allowTextCommands,
1308
+ hasControlCommand: allowTextCommands && hasControlCommand
1309
+ });
1310
+ const commandAuthorized = kind === "direct" ? dmPolicy === "open" || senderAllowedForCommands : commandGate.commandAuthorized;
1311
+ if (kind === "direct") {
1312
+ if (dmPolicy === "disabled") return {
1313
+ ok: false,
1314
+ denyReason: "dm-disabled",
1315
+ commandAuthorized: false,
1316
+ channelInfo,
1317
+ kind,
1318
+ chatType,
1319
+ channelName,
1320
+ channelDisplay,
1321
+ roomLabel
1322
+ };
1323
+ if (dmPolicy !== "open" && !senderAllowedForCommands) return {
1324
+ ok: false,
1325
+ denyReason: dmPolicy === "pairing" ? "dm-pairing" : "unauthorized",
1326
+ commandAuthorized: false,
1327
+ channelInfo,
1328
+ kind,
1329
+ chatType,
1330
+ channelName,
1331
+ channelDisplay,
1332
+ roomLabel
1333
+ };
1334
+ } else {
1335
+ const senderGroupAccess = evaluateSenderGroupAccessForPolicy({
1336
+ groupPolicy,
1337
+ groupAllowFrom: effectiveGroupAllowFrom,
1338
+ senderId,
1339
+ isSenderAllowed: (_senderId, allowFrom) => isMattermostSenderAllowed({
1340
+ senderId,
1341
+ senderName,
1342
+ allowFrom,
1343
+ allowNameMatching
1344
+ })
1345
+ });
1346
+ if (!senderGroupAccess.allowed && senderGroupAccess.reason === "disabled") return {
1347
+ ok: false,
1348
+ denyReason: "channels-disabled",
1349
+ commandAuthorized: false,
1350
+ channelInfo,
1351
+ kind,
1352
+ chatType,
1353
+ channelName,
1354
+ channelDisplay,
1355
+ roomLabel
1356
+ };
1357
+ if (!senderGroupAccess.allowed && senderGroupAccess.reason === "empty_allowlist") return {
1358
+ ok: false,
1359
+ denyReason: "channel-no-allowlist",
1360
+ commandAuthorized: false,
1361
+ channelInfo,
1362
+ kind,
1363
+ chatType,
1364
+ channelName,
1365
+ channelDisplay,
1366
+ roomLabel
1367
+ };
1368
+ if (!senderGroupAccess.allowed && senderGroupAccess.reason === "sender_not_allowlisted") return {
1369
+ ok: false,
1370
+ denyReason: "unauthorized",
1371
+ commandAuthorized: false,
1372
+ channelInfo,
1373
+ kind,
1374
+ chatType,
1375
+ channelName,
1376
+ channelDisplay,
1377
+ roomLabel
1378
+ };
1379
+ if (commandGate.shouldBlock) return {
1380
+ ok: false,
1381
+ denyReason: "unauthorized",
1382
+ commandAuthorized: false,
1383
+ channelInfo,
1384
+ kind,
1385
+ chatType,
1386
+ channelName,
1387
+ channelDisplay,
1388
+ roomLabel
1389
+ };
1390
+ }
1391
+ return {
1392
+ ok: true,
1393
+ commandAuthorized,
1394
+ channelInfo,
1395
+ kind,
1396
+ chatType,
1397
+ channelName,
1398
+ channelDisplay,
1399
+ roomLabel
1400
+ };
1401
+ }
1402
+ //#endregion
1403
+ //#region extensions/mattermost/src/mattermost/monitor-gating.ts
1404
+ function mapMattermostChannelTypeToChatType(channelType) {
1405
+ if (!channelType) return "channel";
1406
+ const normalized = channelType.trim().toUpperCase();
1407
+ if (normalized === "D") return "direct";
1408
+ if (normalized === "G" || normalized === "P") return "group";
1409
+ return "channel";
1410
+ }
1411
+ function evaluateMattermostMentionGate(params) {
1412
+ const shouldRequireMention = params.kind !== "direct" && params.resolveRequireMention({
1413
+ cfg: params.cfg,
1414
+ channel: "mattermost",
1415
+ accountId: params.accountId,
1416
+ groupId: params.channelId,
1417
+ requireMentionOverride: params.requireMentionOverride
1418
+ });
1419
+ const shouldBypassMention = params.isControlCommand && shouldRequireMention && !params.wasMentioned && params.commandAuthorized;
1420
+ const effectiveWasMentioned = params.wasMentioned || shouldBypassMention || params.oncharTriggered;
1421
+ if (params.oncharEnabled && !params.oncharTriggered && !params.wasMentioned && !params.isControlCommand) return {
1422
+ shouldRequireMention,
1423
+ shouldBypassMention,
1424
+ effectiveWasMentioned,
1425
+ dropReason: "onchar-not-triggered"
1426
+ };
1427
+ if (params.kind !== "direct" && shouldRequireMention && params.canDetectMention && !effectiveWasMentioned) return {
1428
+ shouldRequireMention,
1429
+ shouldBypassMention,
1430
+ effectiveWasMentioned,
1431
+ dropReason: "missing-mention"
1432
+ };
1433
+ return {
1434
+ shouldRequireMention,
1435
+ shouldBypassMention,
1436
+ effectiveWasMentioned,
1437
+ dropReason: null
1438
+ };
1439
+ }
1440
+ //#endregion
1441
+ //#region extensions/mattermost/src/mattermost/monitor-helpers.ts
1442
+ const formatInboundFromLabel = formatInboundFromLabel$1;
1443
+ function resolveThreadSessionKeys(params) {
1444
+ return resolveThreadSessionKeys$1({
1445
+ ...params,
1446
+ normalizeThreadId: (threadId) => threadId
1447
+ });
1448
+ }
1449
+ /**
1450
+ * Strip bot mention from message text while preserving newlines and
1451
+ * block-level Markdown formatting (headings, lists, blockquotes).
1452
+ */
1453
+ function normalizeMention(text, mention) {
1454
+ if (!mention) return text.trim();
1455
+ const escaped = mention.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1456
+ const hasMentionRe = new RegExp(`@${escaped}\\b`, "i");
1457
+ const leadingMentionRe = new RegExp(`^([\\t ]*)@${escaped}\\b[\\t ]*`, "i");
1458
+ const trailingMentionRe = new RegExp(`[\\t ]*@${escaped}\\b[\\t ]*$`, "i");
1459
+ const normalizedLines = text.split("\n").map((line) => {
1460
+ const hadMention = hasMentionRe.test(line);
1461
+ const normalizedLine = line.replace(leadingMentionRe, "$1").replace(trailingMentionRe, "").replace(new RegExp(`@${escaped}\\b`, "gi"), "").replace(/(\S)[ \t]{2,}/g, "$1 ");
1462
+ return {
1463
+ text: normalizedLine,
1464
+ mentionOnlyBlank: hadMention && normalizedLine.trim() === ""
1465
+ };
1466
+ });
1467
+ while (normalizedLines[0]?.mentionOnlyBlank) normalizedLines.shift();
1468
+ while (normalizedLines.at(-1)?.text.trim() === "") normalizedLines.pop();
1469
+ return normalizedLines.map((line) => line.text).join("\n");
1470
+ }
1471
+ //#endregion
1472
+ //#region extensions/mattermost/src/mattermost/monitor-onchar.ts
1473
+ const DEFAULT_ONCHAR_PREFIXES = [">", "!"];
1474
+ function resolveOncharPrefixes(prefixes) {
1475
+ const cleaned = prefixes?.map((entry) => entry.trim()).filter(Boolean) ?? DEFAULT_ONCHAR_PREFIXES;
1476
+ return cleaned.length > 0 ? cleaned : DEFAULT_ONCHAR_PREFIXES;
1477
+ }
1478
+ function stripOncharPrefix(text, prefixes) {
1479
+ const trimmed = text.trimStart();
1480
+ for (const prefix of prefixes) {
1481
+ if (!prefix) continue;
1482
+ if (trimmed.startsWith(prefix)) return {
1483
+ triggered: true,
1484
+ stripped: trimmed.slice(prefix.length).trimStart()
1485
+ };
1486
+ }
1487
+ return {
1488
+ triggered: false,
1489
+ stripped: text
1490
+ };
1491
+ }
1492
+ //#endregion
1493
+ //#region extensions/mattermost/src/mattermost/monitor-resources.ts
1494
+ const CHANNEL_CACHE_TTL_MS = 5 * 6e4;
1495
+ const USER_CACHE_TTL_MS = 10 * 6e4;
1496
+ function createMattermostMonitorResources(params) {
1497
+ const { accountId, callbackUrl, client, logger, mediaMaxBytes, fetchRemoteMedia, saveMediaBuffer, mediaKindFromMime } = params;
1498
+ const channelCache = /* @__PURE__ */ new Map();
1499
+ const userCache = /* @__PURE__ */ new Map();
1500
+ const resolveMattermostMedia = async (fileIds) => {
1501
+ const ids = (fileIds ?? []).map((id) => id?.trim()).filter(Boolean);
1502
+ if (ids.length === 0) return [];
1503
+ const out = [];
1504
+ for (const fileId of ids) try {
1505
+ const fetched = await fetchRemoteMedia({
1506
+ url: `${client.apiBaseUrl}/files/${fileId}`,
1507
+ requestInit: { headers: { Authorization: `Bearer ${client.token}` } },
1508
+ filePathHint: fileId,
1509
+ maxBytes: mediaMaxBytes,
1510
+ ssrfPolicy: { allowedHostnames: [new URL(client.baseUrl).hostname] }
1511
+ });
1512
+ const saved = await saveMediaBuffer(Buffer.from(fetched.buffer), fetched.contentType ?? void 0, "inbound", mediaMaxBytes);
1513
+ const contentType = saved.contentType ?? fetched.contentType ?? void 0;
1514
+ out.push({
1515
+ path: saved.path,
1516
+ contentType,
1517
+ kind: mediaKindFromMime(contentType) ?? "unknown"
1518
+ });
1519
+ } catch (err) {
1520
+ logger.debug?.(`mattermost: failed to download file ${fileId}: ${String(err)}`);
1521
+ }
1522
+ return out;
1523
+ };
1524
+ const sendTypingIndicator = async (channelId, parentId) => {
1525
+ await sendMattermostTyping(client, {
1526
+ channelId,
1527
+ parentId
1528
+ });
1529
+ };
1530
+ const resolveChannelInfo = async (channelId) => {
1531
+ const cached = channelCache.get(channelId);
1532
+ if (cached && cached.expiresAt > Date.now()) return cached.value;
1533
+ try {
1534
+ const info = await fetchMattermostChannel(client, channelId);
1535
+ channelCache.set(channelId, {
1536
+ value: info,
1537
+ expiresAt: Date.now() + CHANNEL_CACHE_TTL_MS
1538
+ });
1539
+ return info;
1540
+ } catch (err) {
1541
+ logger.debug?.(`mattermost: channel lookup failed: ${String(err)}`);
1542
+ channelCache.set(channelId, {
1543
+ value: null,
1544
+ expiresAt: Date.now() + CHANNEL_CACHE_TTL_MS
1545
+ });
1546
+ return null;
1547
+ }
1548
+ };
1549
+ const resolveUserInfo = async (userId) => {
1550
+ const cached = userCache.get(userId);
1551
+ if (cached && cached.expiresAt > Date.now()) return cached.value;
1552
+ try {
1553
+ const info = await fetchMattermostUser(client, userId);
1554
+ userCache.set(userId, {
1555
+ value: info,
1556
+ expiresAt: Date.now() + USER_CACHE_TTL_MS
1557
+ });
1558
+ return info;
1559
+ } catch (err) {
1560
+ logger.debug?.(`mattermost: user lookup failed: ${String(err)}`);
1561
+ userCache.set(userId, {
1562
+ value: null,
1563
+ expiresAt: Date.now() + USER_CACHE_TTL_MS
1564
+ });
1565
+ return null;
1566
+ }
1567
+ };
1568
+ const buildModelPickerProps = (channelId, buttons) => buildButtonProps({
1569
+ callbackUrl,
1570
+ accountId,
1571
+ channelId,
1572
+ buttons
1573
+ });
1574
+ const updateModelPickerPost = async (params) => {
1575
+ const props = buildModelPickerProps(params.channelId, params.buttons ?? []) ?? { attachments: [] };
1576
+ await updateMattermostPost(client, params.postId, {
1577
+ message: params.message,
1578
+ props
1579
+ });
1580
+ return {};
1581
+ };
1582
+ return {
1583
+ resolveMattermostMedia,
1584
+ sendTypingIndicator,
1585
+ resolveChannelInfo,
1586
+ resolveUserInfo,
1587
+ updateModelPickerPost
1588
+ };
1589
+ }
1590
+ //#endregion
1591
+ //#region extensions/mattermost/src/mattermost/slash-commands.ts
1592
+ /**
1593
+ * Built-in OpenClaw commands to register as native slash commands.
1594
+ * These mirror the text-based commands already handled by the gateway.
1595
+ */
1596
+ const DEFAULT_COMMAND_SPECS = [
1597
+ {
1598
+ trigger: "oc_status",
1599
+ originalName: "status",
1600
+ description: "Show session status (model, usage, uptime)",
1601
+ autoComplete: true
1602
+ },
1603
+ {
1604
+ trigger: "oc_model",
1605
+ originalName: "model",
1606
+ description: "View or change the current model",
1607
+ autoComplete: true,
1608
+ autoCompleteHint: "[model-name]"
1609
+ },
1610
+ {
1611
+ trigger: "oc_models",
1612
+ originalName: "models",
1613
+ description: "Browse available models",
1614
+ autoComplete: true,
1615
+ autoCompleteHint: "[provider]"
1616
+ },
1617
+ {
1618
+ trigger: "oc_new",
1619
+ originalName: "new",
1620
+ description: "Start a new conversation session",
1621
+ autoComplete: true
1622
+ },
1623
+ {
1624
+ trigger: "oc_help",
1625
+ originalName: "help",
1626
+ description: "Show available commands",
1627
+ autoComplete: true
1628
+ },
1629
+ {
1630
+ trigger: "oc_think",
1631
+ originalName: "think",
1632
+ description: "Set thinking/reasoning level",
1633
+ autoComplete: true,
1634
+ autoCompleteHint: "[off|low|medium|high]"
1635
+ },
1636
+ {
1637
+ trigger: "oc_reasoning",
1638
+ originalName: "reasoning",
1639
+ description: "Toggle reasoning mode",
1640
+ autoComplete: true,
1641
+ autoCompleteHint: "[on|off]"
1642
+ },
1643
+ {
1644
+ trigger: "oc_verbose",
1645
+ originalName: "verbose",
1646
+ description: "Toggle verbose mode",
1647
+ autoComplete: true,
1648
+ autoCompleteHint: "[on|off]"
1649
+ }
1650
+ ];
1651
+ /**
1652
+ * List existing custom slash commands for a team.
1653
+ */
1654
+ async function listMattermostCommands(client, teamId) {
1655
+ return await client.request(`/commands?team_id=${encodeURIComponent(teamId)}&custom_only=true`);
1656
+ }
1657
+ /**
1658
+ * Create a custom slash command on a Mattermost team.
1659
+ */
1660
+ async function createMattermostCommand(client, params) {
1661
+ return await client.request("/commands", {
1662
+ method: "POST",
1663
+ body: JSON.stringify(params)
1664
+ });
1665
+ }
1666
+ /**
1667
+ * Delete a custom slash command.
1668
+ */
1669
+ async function deleteMattermostCommand(client, commandId) {
1670
+ await client.request(`/commands/${encodeURIComponent(commandId)}`, { method: "DELETE" });
1671
+ }
1672
+ /**
1673
+ * Update an existing custom slash command.
1674
+ */
1675
+ async function updateMattermostCommand(client, params) {
1676
+ return await client.request(`/commands/${encodeURIComponent(params.id)}`, {
1677
+ method: "PUT",
1678
+ body: JSON.stringify(params)
1679
+ });
1680
+ }
1681
+ /**
1682
+ * Register all OpenClaw slash commands for a given team.
1683
+ * Skips commands that are already registered with the same trigger + callback URL.
1684
+ * Returns the list of newly created command IDs.
1685
+ */
1686
+ async function registerSlashCommands(params) {
1687
+ const { client, teamId, creatorUserId, callbackUrl, commands, log } = params;
1688
+ const normalizedCreatorUserId = creatorUserId.trim();
1689
+ if (!normalizedCreatorUserId) throw new Error("creatorUserId is required for slash command reconciliation");
1690
+ let existing = [];
1691
+ try {
1692
+ existing = await listMattermostCommands(client, teamId);
1693
+ } catch (err) {
1694
+ log?.(`mattermost: failed to list existing commands: ${String(err)}`);
1695
+ throw err;
1696
+ }
1697
+ const existingByTrigger = /* @__PURE__ */ new Map();
1698
+ for (const cmd of existing) {
1699
+ const list = existingByTrigger.get(cmd.trigger) ?? [];
1700
+ list.push(cmd);
1701
+ existingByTrigger.set(cmd.trigger, list);
1702
+ }
1703
+ const registered = [];
1704
+ for (const spec of commands) {
1705
+ const existingForTrigger = existingByTrigger.get(spec.trigger) ?? [];
1706
+ const ownedCommands = existingForTrigger.filter((cmd) => cmd.creator_id?.trim() === normalizedCreatorUserId);
1707
+ const foreignCommands = existingForTrigger.filter((cmd) => cmd.creator_id?.trim() !== normalizedCreatorUserId);
1708
+ if (ownedCommands.length === 0 && foreignCommands.length > 0) {
1709
+ log?.(`mattermost: trigger /${spec.trigger} already used by non-OpenClaw command(s); skipping to avoid mutating external integrations`);
1710
+ continue;
1711
+ }
1712
+ if (ownedCommands.length > 1) log?.(`mattermost: multiple owned commands found for /${spec.trigger}; using the first and leaving extras untouched`);
1713
+ const existingCmd = ownedCommands[0];
1714
+ if (existingCmd && existingCmd.url === callbackUrl) {
1715
+ log?.(`mattermost: command /${spec.trigger} already registered (id=${existingCmd.id})`);
1716
+ registered.push({
1717
+ id: existingCmd.id,
1718
+ trigger: spec.trigger,
1719
+ teamId,
1720
+ token: existingCmd.token,
1721
+ managed: false
1722
+ });
1723
+ continue;
1724
+ }
1725
+ if (existingCmd && existingCmd.url !== callbackUrl) {
1726
+ log?.(`mattermost: command /${spec.trigger} exists with different callback URL; updating (id=${existingCmd.id})`);
1727
+ try {
1728
+ const updated = await updateMattermostCommand(client, {
1729
+ id: existingCmd.id,
1730
+ team_id: teamId,
1731
+ trigger: spec.trigger,
1732
+ method: "P",
1733
+ url: callbackUrl,
1734
+ description: spec.description,
1735
+ auto_complete: spec.autoComplete,
1736
+ auto_complete_desc: spec.description,
1737
+ auto_complete_hint: spec.autoCompleteHint
1738
+ });
1739
+ registered.push({
1740
+ id: updated.id,
1741
+ trigger: spec.trigger,
1742
+ teamId,
1743
+ token: updated.token,
1744
+ managed: false
1745
+ });
1746
+ continue;
1747
+ } catch (err) {
1748
+ log?.(`mattermost: failed to update command /${spec.trigger} (id=${existingCmd.id}): ${String(err)}`);
1749
+ try {
1750
+ await deleteMattermostCommand(client, existingCmd.id);
1751
+ log?.(`mattermost: deleted stale command /${spec.trigger} (id=${existingCmd.id})`);
1752
+ } catch (deleteErr) {
1753
+ log?.(`mattermost: failed to delete stale command /${spec.trigger} (id=${existingCmd.id}): ${String(deleteErr)}`);
1754
+ continue;
1755
+ }
1756
+ }
1757
+ }
1758
+ try {
1759
+ const created = await createMattermostCommand(client, {
1760
+ team_id: teamId,
1761
+ trigger: spec.trigger,
1762
+ method: "P",
1763
+ url: callbackUrl,
1764
+ description: spec.description,
1765
+ auto_complete: spec.autoComplete,
1766
+ auto_complete_desc: spec.description,
1767
+ auto_complete_hint: spec.autoCompleteHint
1768
+ });
1769
+ log?.(`mattermost: registered command /${spec.trigger} (id=${created.id})`);
1770
+ registered.push({
1771
+ id: created.id,
1772
+ trigger: spec.trigger,
1773
+ teamId,
1774
+ token: created.token,
1775
+ managed: true
1776
+ });
1777
+ } catch (err) {
1778
+ log?.(`mattermost: failed to register command /${spec.trigger}: ${String(err)}`);
1779
+ }
1780
+ }
1781
+ return registered;
1782
+ }
1783
+ /**
1784
+ * Clean up all registered slash commands.
1785
+ */
1786
+ async function cleanupSlashCommands(params) {
1787
+ const { client, commands, log } = params;
1788
+ for (const cmd of commands) {
1789
+ if (!cmd.managed) continue;
1790
+ try {
1791
+ await deleteMattermostCommand(client, cmd.id);
1792
+ log?.(`mattermost: deleted command /${cmd.trigger} (id=${cmd.id})`);
1793
+ } catch (err) {
1794
+ log?.(`mattermost: failed to delete command /${cmd.trigger}: ${String(err)}`);
1795
+ }
1796
+ }
1797
+ }
1798
+ /**
1799
+ * Parse a Mattermost slash command callback payload from a URL-encoded or JSON body.
1800
+ */
1801
+ function parseSlashCommandPayload(body, contentType) {
1802
+ if (!body) return null;
1803
+ try {
1804
+ if (contentType?.includes("application/json")) {
1805
+ const parsed = JSON.parse(body);
1806
+ const token = typeof parsed.token === "string" ? parsed.token : "";
1807
+ const teamId = typeof parsed.team_id === "string" ? parsed.team_id : "";
1808
+ const channelId = typeof parsed.channel_id === "string" ? parsed.channel_id : "";
1809
+ const userId = typeof parsed.user_id === "string" ? parsed.user_id : "";
1810
+ const command = typeof parsed.command === "string" ? parsed.command : "";
1811
+ if (!token || !teamId || !channelId || !userId || !command) return null;
1812
+ return {
1813
+ token,
1814
+ team_id: teamId,
1815
+ team_domain: typeof parsed.team_domain === "string" ? parsed.team_domain : void 0,
1816
+ channel_id: channelId,
1817
+ channel_name: typeof parsed.channel_name === "string" ? parsed.channel_name : void 0,
1818
+ user_id: userId,
1819
+ user_name: typeof parsed.user_name === "string" ? parsed.user_name : void 0,
1820
+ command,
1821
+ text: typeof parsed.text === "string" ? parsed.text : "",
1822
+ trigger_id: typeof parsed.trigger_id === "string" ? parsed.trigger_id : void 0,
1823
+ response_url: typeof parsed.response_url === "string" ? parsed.response_url : void 0
1824
+ };
1825
+ }
1826
+ const params = new URLSearchParams(body);
1827
+ const token = params.get("token");
1828
+ const teamId = params.get("team_id");
1829
+ const channelId = params.get("channel_id");
1830
+ const userId = params.get("user_id");
1831
+ const command = params.get("command");
1832
+ if (!token || !teamId || !channelId || !userId || !command) return null;
1833
+ return {
1834
+ token,
1835
+ team_id: teamId,
1836
+ team_domain: params.get("team_domain") ?? void 0,
1837
+ channel_id: channelId,
1838
+ channel_name: params.get("channel_name") ?? void 0,
1839
+ user_id: userId,
1840
+ user_name: params.get("user_name") ?? void 0,
1841
+ command,
1842
+ text: params.get("text") ?? "",
1843
+ trigger_id: params.get("trigger_id") ?? void 0,
1844
+ response_url: params.get("response_url") ?? void 0
1845
+ };
1846
+ } catch {
1847
+ return null;
1848
+ }
1849
+ }
1850
+ /**
1851
+ * Map the trigger word back to the original OpenClaw command name.
1852
+ * e.g. "oc_status" -> "/status", "oc_model" -> "/model"
1853
+ */
1854
+ function resolveCommandText(trigger, text, triggerMap) {
1855
+ const commandName = triggerMap?.get(trigger) ?? (trigger.startsWith("oc_") ? trigger.slice(3) : trigger);
1856
+ const args = text.trim();
1857
+ return args ? `/${commandName} ${args}` : `/${commandName}`;
1858
+ }
1859
+ const DEFAULT_CALLBACK_PATH = "/api/channels/mattermost/command";
1860
+ /**
1861
+ * Ensure the callback path starts with a leading `/` to prevent
1862
+ * malformed URLs like `http://host:portapi/...`.
1863
+ */
1864
+ function normalizeCallbackPath(path) {
1865
+ const trimmed = path.trim();
1866
+ if (!trimmed) return DEFAULT_CALLBACK_PATH;
1867
+ return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
1868
+ }
1869
+ function resolveSlashCommandConfig(raw) {
1870
+ return {
1871
+ native: raw?.native ?? "auto",
1872
+ nativeSkills: raw?.nativeSkills ?? "auto",
1873
+ callbackPath: normalizeCallbackPath(raw?.callbackPath ?? DEFAULT_CALLBACK_PATH),
1874
+ callbackUrl: raw?.callbackUrl?.trim() || void 0
1875
+ };
1876
+ }
1877
+ function isSlashCommandsEnabled(config) {
1878
+ if (config.native === true) return true;
1879
+ if (config.native === false) return false;
1880
+ return false;
1881
+ }
1882
+ /**
1883
+ * Build the callback URL that Mattermost will POST to when a command is invoked.
1884
+ */
1885
+ function resolveCallbackUrl(params) {
1886
+ if (params.config.callbackUrl) return params.config.callbackUrl;
1887
+ const isWildcardBindHost = (rawHost) => {
1888
+ const trimmed = rawHost.trim();
1889
+ if (!trimmed) return false;
1890
+ const host = trimmed.startsWith("[") && trimmed.endsWith("]") ? trimmed.slice(1, -1) : trimmed;
1891
+ return host === "0.0.0.0" || host === "::" || host === "0:0:0:0:0:0:0:0" || host === "::0";
1892
+ };
1893
+ let host = params.gatewayHost && !isWildcardBindHost(params.gatewayHost) ? params.gatewayHost : "localhost";
1894
+ const path = normalizeCallbackPath(params.config.callbackPath);
1895
+ if (host.includes(":") && !(host.startsWith("[") && host.endsWith("]"))) host = `[${host}]`;
1896
+ return `http://${host}:${params.gatewayPort}${path}`;
1897
+ }
1898
+ //#endregion
1899
+ //#region extensions/mattermost/src/mattermost/reply-delivery.ts
1900
+ async function deliverMattermostReplyPayload(params) {
1901
+ const reply = resolveSendableOutboundReplyParts(params.payload, { text: params.core.channel.text.convertMarkdownTables(params.payload.text ?? "", params.tableMode) });
1902
+ const mediaLocalRoots = getAgentScopedMediaLocalRoots(params.cfg, params.agentId);
1903
+ const chunkMode = params.core.channel.text.resolveChunkMode(params.cfg, "mattermost", params.accountId);
1904
+ await deliverTextOrMediaReply({
1905
+ payload: params.payload,
1906
+ text: reply.text,
1907
+ chunkText: (value) => params.core.channel.text.chunkMarkdownTextWithMode(value, params.textLimit, chunkMode),
1908
+ sendText: async (chunk) => {
1909
+ await params.sendMessage(params.to, chunk, {
1910
+ accountId: params.accountId,
1911
+ replyToId: params.replyToId
1912
+ });
1913
+ },
1914
+ sendMedia: async ({ mediaUrl, caption }) => {
1915
+ await params.sendMessage(params.to, caption ?? "", {
1916
+ accountId: params.accountId,
1917
+ mediaUrl,
1918
+ mediaLocalRoots,
1919
+ replyToId: params.replyToId
1920
+ });
1921
+ }
1922
+ });
1923
+ }
1924
+ //#endregion
1925
+ //#region extensions/mattermost/src/mattermost/target-resolution.ts
1926
+ const mattermostOpaqueTargetCache = /* @__PURE__ */ new Map();
1927
+ function cacheKey$1(baseUrl, token, id) {
1928
+ return `${baseUrl}::${token}::${id}`;
1929
+ }
1930
+ /** Mattermost IDs are 26-character lowercase alphanumeric strings. */
1931
+ function isMattermostId(value) {
1932
+ return /^[a-z0-9]{26}$/.test(value);
1933
+ }
1934
+ function isExplicitMattermostTarget(raw) {
1935
+ const trimmed = raw.trim();
1936
+ if (!trimmed) return false;
1937
+ return /^(channel|user|mattermost):/i.test(trimmed) || trimmed.startsWith("@") || trimmed.startsWith("#");
1938
+ }
1939
+ function parseMattermostApiStatus(err) {
1940
+ if (!err || typeof err !== "object") return;
1941
+ const msg = "message" in err ? String(err.message ?? "") : "";
1942
+ const match = /Mattermost API (\d{3})\b/.exec(msg);
1943
+ if (!match) return;
1944
+ const code = Number(match[1]);
1945
+ return Number.isFinite(code) ? code : void 0;
1946
+ }
1947
+ async function resolveMattermostOpaqueTarget(params) {
1948
+ const input = params.input.trim();
1949
+ if (!input || isExplicitMattermostTarget(input) || !isMattermostId(input)) return null;
1950
+ const account = params.cfg && (!params.token || !params.baseUrl) ? resolveMattermostAccount({
1951
+ cfg: params.cfg,
1952
+ accountId: params.accountId
1953
+ }) : null;
1954
+ const token = params.token?.trim() || account?.botToken?.trim();
1955
+ const baseUrl = normalizeMattermostBaseUrl(params.baseUrl ?? account?.baseUrl);
1956
+ if (!token || !baseUrl) return null;
1957
+ const key = cacheKey$1(baseUrl, token, input);
1958
+ const cached = mattermostOpaqueTargetCache.get(key);
1959
+ if (cached === true) return {
1960
+ kind: "user",
1961
+ id: input,
1962
+ to: `user:${input}`
1963
+ };
1964
+ if (cached === false) return {
1965
+ kind: "channel",
1966
+ id: input,
1967
+ to: `channel:${input}`
1968
+ };
1969
+ const client = createMattermostClient({
1970
+ baseUrl,
1971
+ botToken: token
1972
+ });
1973
+ try {
1974
+ await fetchMattermostUser(client, input);
1975
+ mattermostOpaqueTargetCache.set(key, true);
1976
+ return {
1977
+ kind: "user",
1978
+ id: input,
1979
+ to: `user:${input}`
1980
+ };
1981
+ } catch (err) {
1982
+ if (parseMattermostApiStatus(err) === 404) mattermostOpaqueTargetCache.set(key, false);
1983
+ return {
1984
+ kind: "channel",
1985
+ id: input,
1986
+ to: `channel:${input}`
1987
+ };
1988
+ }
1989
+ }
1990
+ //#endregion
1991
+ //#region extensions/mattermost/src/mattermost/send.ts
1992
+ const botUserCache = /* @__PURE__ */ new Map();
1993
+ const userByNameCache = /* @__PURE__ */ new Map();
1994
+ const channelByNameCache = /* @__PURE__ */ new Map();
1995
+ const dmChannelCache = /* @__PURE__ */ new Map();
1996
+ const getCore = () => getMattermostRuntime();
1997
+ function cacheKey(baseUrl, token) {
1998
+ return `${baseUrl}::${token}`;
1999
+ }
2000
+ function normalizeMessage(text, mediaUrl) {
2001
+ return [text.trim(), mediaUrl?.trim()].filter(Boolean).join("\n");
2002
+ }
2003
+ function isHttpUrl(value) {
2004
+ return /^https?:\/\//i.test(value);
2005
+ }
2006
+ function parseMattermostTarget(raw) {
2007
+ const trimmed = raw.trim();
2008
+ if (!trimmed) throw new Error("Recipient is required for Mattermost sends");
2009
+ const lower = trimmed.toLowerCase();
2010
+ if (lower.startsWith("channel:")) {
2011
+ const id = trimmed.slice(8).trim();
2012
+ if (!id) throw new Error("Channel id is required for Mattermost sends");
2013
+ if (id.startsWith("#")) {
2014
+ const name = id.slice(1).trim();
2015
+ if (!name) throw new Error("Channel name is required for Mattermost sends");
2016
+ return {
2017
+ kind: "channel-name",
2018
+ name
2019
+ };
2020
+ }
2021
+ if (!isMattermostId(id)) return {
2022
+ kind: "channel-name",
2023
+ name: id
2024
+ };
2025
+ return {
2026
+ kind: "channel",
2027
+ id
2028
+ };
2029
+ }
2030
+ if (lower.startsWith("user:")) {
2031
+ const id = trimmed.slice(5).trim();
2032
+ if (!id) throw new Error("User id is required for Mattermost sends");
2033
+ return {
2034
+ kind: "user",
2035
+ id
2036
+ };
2037
+ }
2038
+ if (lower.startsWith("mattermost:")) {
2039
+ const id = trimmed.slice(11).trim();
2040
+ if (!id) throw new Error("User id is required for Mattermost sends");
2041
+ return {
2042
+ kind: "user",
2043
+ id
2044
+ };
2045
+ }
2046
+ if (trimmed.startsWith("@")) {
2047
+ const username = trimmed.slice(1).trim();
2048
+ if (!username) throw new Error("Username is required for Mattermost sends");
2049
+ return {
2050
+ kind: "user",
2051
+ username
2052
+ };
2053
+ }
2054
+ if (trimmed.startsWith("#")) {
2055
+ const name = trimmed.slice(1).trim();
2056
+ if (!name) throw new Error("Channel name is required for Mattermost sends");
2057
+ return {
2058
+ kind: "channel-name",
2059
+ name
2060
+ };
2061
+ }
2062
+ if (!isMattermostId(trimmed)) return {
2063
+ kind: "channel-name",
2064
+ name: trimmed
2065
+ };
2066
+ return {
2067
+ kind: "channel",
2068
+ id: trimmed
2069
+ };
2070
+ }
2071
+ async function resolveBotUser(baseUrl, token) {
2072
+ const key = cacheKey(baseUrl, token);
2073
+ const cached = botUserCache.get(key);
2074
+ if (cached) return cached;
2075
+ const user = await fetchMattermostMe(createMattermostClient({
2076
+ baseUrl,
2077
+ botToken: token
2078
+ }));
2079
+ botUserCache.set(key, user);
2080
+ return user;
2081
+ }
2082
+ async function resolveUserIdByUsername(params) {
2083
+ const { baseUrl, token, username } = params;
2084
+ const key = `${cacheKey(baseUrl, token)}::${username.toLowerCase()}`;
2085
+ const cached = userByNameCache.get(key);
2086
+ if (cached?.id) return cached.id;
2087
+ const user = await fetchMattermostUserByUsername(createMattermostClient({
2088
+ baseUrl,
2089
+ botToken: token
2090
+ }), username);
2091
+ userByNameCache.set(key, user);
2092
+ return user.id;
2093
+ }
2094
+ async function resolveChannelIdByName(params) {
2095
+ const { baseUrl, token, name } = params;
2096
+ const key = `${cacheKey(baseUrl, token)}::channel::${name.toLowerCase()}`;
2097
+ const cached = channelByNameCache.get(key);
2098
+ if (cached) return cached;
2099
+ const client = createMattermostClient({
2100
+ baseUrl,
2101
+ botToken: token
2102
+ });
2103
+ const teams = await fetchMattermostUserTeams(client, (await fetchMattermostMe(client)).id);
2104
+ for (const team of teams) try {
2105
+ const channel = await fetchMattermostChannelByName(client, team.id, name);
2106
+ if (channel?.id) {
2107
+ channelByNameCache.set(key, channel.id);
2108
+ return channel.id;
2109
+ }
2110
+ } catch {}
2111
+ throw new Error(`Mattermost channel "#${name}" not found in any team the bot belongs to`);
2112
+ }
2113
+ function mergeDmRetryOptions(base, override) {
2114
+ const merged = {
2115
+ maxRetries: override?.maxRetries ?? base?.maxRetries,
2116
+ initialDelayMs: override?.initialDelayMs ?? base?.initialDelayMs,
2117
+ maxDelayMs: override?.maxDelayMs ?? base?.maxDelayMs,
2118
+ timeoutMs: override?.timeoutMs ?? base?.timeoutMs,
2119
+ onRetry: override?.onRetry
2120
+ };
2121
+ if (merged.maxRetries === void 0 && merged.initialDelayMs === void 0 && merged.maxDelayMs === void 0 && merged.timeoutMs === void 0 && merged.onRetry === void 0) return;
2122
+ return merged;
2123
+ }
2124
+ async function resolveTargetChannelId(params) {
2125
+ if (params.target.kind === "channel") return params.target.id;
2126
+ if (params.target.kind === "channel-name") return await resolveChannelIdByName({
2127
+ baseUrl: params.baseUrl,
2128
+ token: params.token,
2129
+ name: params.target.name
2130
+ });
2131
+ const userId = params.target.id ? params.target.id : await resolveUserIdByUsername({
2132
+ baseUrl: params.baseUrl,
2133
+ token: params.token,
2134
+ username: params.target.username ?? ""
2135
+ });
2136
+ const dmKey = `${cacheKey(params.baseUrl, params.token)}::dm::${userId}`;
2137
+ const cachedDm = dmChannelCache.get(dmKey);
2138
+ if (cachedDm) return cachedDm;
2139
+ const botUser = await resolveBotUser(params.baseUrl, params.token);
2140
+ const channel = await createMattermostDirectChannelWithRetry(createMattermostClient({
2141
+ baseUrl: params.baseUrl,
2142
+ botToken: params.token
2143
+ }), [botUser.id, userId], {
2144
+ ...params.dmRetryOptions,
2145
+ onRetry: (attempt, delayMs, error) => {
2146
+ params.dmRetryOptions?.onRetry?.(attempt, delayMs, error);
2147
+ if (params.logger) params.logger.warn?.(`DM channel creation retry ${attempt} after ${delayMs}ms: ${error.message}`);
2148
+ }
2149
+ });
2150
+ dmChannelCache.set(dmKey, channel.id);
2151
+ return channel.id;
2152
+ }
2153
+ async function resolveMattermostSendContext(to, opts = {}) {
2154
+ const core = getCore();
2155
+ const logger = core.logging.getChildLogger({ module: "mattermost" });
2156
+ const cfg = opts.cfg ?? core.config.loadConfig();
2157
+ const account = resolveMattermostAccount({
2158
+ cfg,
2159
+ accountId: opts.accountId
2160
+ });
2161
+ const token = opts.botToken?.trim() || account.botToken?.trim();
2162
+ 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).`);
2163
+ const baseUrl = normalizeMattermostBaseUrl(opts.baseUrl ?? account.baseUrl);
2164
+ if (!baseUrl) throw new Error(`Mattermost baseUrl missing for account "${account.accountId}" (set channels.mattermost.accounts.${account.accountId}.baseUrl or MATTERMOST_URL for default).`);
2165
+ const trimmedTo = to?.trim() ?? "";
2166
+ const opaqueTarget = await resolveMattermostOpaqueTarget({
2167
+ input: trimmedTo,
2168
+ token,
2169
+ baseUrl
2170
+ });
2171
+ const channelId = await resolveTargetChannelId({
2172
+ target: opaqueTarget?.kind === "user" ? {
2173
+ kind: "user",
2174
+ id: opaqueTarget.id
2175
+ } : opaqueTarget?.kind === "channel" ? {
2176
+ kind: "channel",
2177
+ id: opaqueTarget.id
2178
+ } : parseMattermostTarget(trimmedTo),
2179
+ baseUrl,
2180
+ token,
2181
+ dmRetryOptions: mergeDmRetryOptions(account.config.dmChannelRetry ? {
2182
+ maxRetries: account.config.dmChannelRetry.maxRetries,
2183
+ initialDelayMs: account.config.dmChannelRetry.initialDelayMs,
2184
+ maxDelayMs: account.config.dmChannelRetry.maxDelayMs,
2185
+ timeoutMs: account.config.dmChannelRetry.timeoutMs
2186
+ } : void 0, opts.dmRetryOptions),
2187
+ logger: core.logging.shouldLogVerbose() ? logger : void 0
2188
+ });
2189
+ return {
2190
+ cfg,
2191
+ accountId: account.accountId,
2192
+ token,
2193
+ baseUrl,
2194
+ channelId
2195
+ };
2196
+ }
2197
+ async function sendMessageMattermost(to, text, opts = {}) {
2198
+ const core = getCore();
2199
+ const logger = core.logging.getChildLogger({ module: "mattermost" });
2200
+ const { cfg, accountId, token, baseUrl, channelId } = await resolveMattermostSendContext(to, opts);
2201
+ const client = createMattermostClient({
2202
+ baseUrl,
2203
+ botToken: token
2204
+ });
2205
+ let props = opts.props;
2206
+ if (!props && Array.isArray(opts.buttons) && opts.buttons.length > 0) {
2207
+ setInteractionSecret(accountId, token);
2208
+ props = buildButtonProps({
2209
+ callbackUrl: resolveInteractionCallbackUrl(accountId, {
2210
+ gateway: cfg.gateway,
2211
+ interactions: resolveMattermostAccount({
2212
+ cfg,
2213
+ accountId
2214
+ }).config?.interactions
2215
+ }),
2216
+ accountId,
2217
+ channelId,
2218
+ buttons: opts.buttons,
2219
+ text: opts.attachmentText
2220
+ });
2221
+ }
2222
+ let message = text?.trim() ?? "";
2223
+ let fileIds;
2224
+ let uploadError;
2225
+ const mediaUrl = opts.mediaUrl?.trim();
2226
+ if (mediaUrl) try {
2227
+ const media = await loadOutboundMediaFromUrl(mediaUrl, { mediaLocalRoots: opts.mediaLocalRoots });
2228
+ fileIds = [(await uploadMattermostFile(client, {
2229
+ channelId,
2230
+ buffer: media.buffer,
2231
+ fileName: media.fileName ?? "upload",
2232
+ contentType: media.contentType ?? void 0
2233
+ })).id];
2234
+ } catch (err) {
2235
+ uploadError = err instanceof Error ? err : new Error(String(err));
2236
+ if (core.logging.shouldLogVerbose()) logger.debug?.(`mattermost send: media upload failed, falling back to URL text: ${String(err)}`);
2237
+ message = normalizeMessage(message, isHttpUrl(mediaUrl) ? mediaUrl : "");
2238
+ }
2239
+ if (message) {
2240
+ const tableMode = core.channel.text.resolveMarkdownTableMode({
2241
+ cfg,
2242
+ channel: "mattermost",
2243
+ accountId
2244
+ });
2245
+ message = core.channel.text.convertMarkdownTables(message, tableMode);
2246
+ }
2247
+ if (!message && (!fileIds || fileIds.length === 0)) {
2248
+ if (uploadError) throw new Error(`Mattermost media upload failed: ${uploadError.message}`);
2249
+ throw new Error("Mattermost message is empty");
2250
+ }
2251
+ const post = await createMattermostPost(client, {
2252
+ channelId,
2253
+ message,
2254
+ rootId: opts.replyToId,
2255
+ fileIds,
2256
+ props
2257
+ });
2258
+ core.channel.activity.record({
2259
+ channel: "mattermost",
2260
+ accountId,
2261
+ direction: "outbound"
2262
+ });
2263
+ return {
2264
+ messageId: post.id ?? "unknown",
2265
+ channelId
2266
+ };
2267
+ }
2268
+ //#endregion
2269
+ //#region extensions/mattermost/src/mattermost/slash-http.ts
2270
+ const MAX_BODY_BYTES = 64 * 1024;
2271
+ const BODY_READ_TIMEOUT_MS = 5e3;
2272
+ /**
2273
+ * Read the full request body as a string.
2274
+ */
2275
+ function readBody(req, maxBytes) {
2276
+ return readRequestBodyWithLimit(req, {
2277
+ maxBytes,
2278
+ timeoutMs: BODY_READ_TIMEOUT_MS
2279
+ });
2280
+ }
2281
+ function sendJsonResponse(res, status, body) {
2282
+ res.statusCode = status;
2283
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
2284
+ res.end(JSON.stringify(body));
2285
+ }
2286
+ async function authorizeSlashInvocation(params) {
2287
+ const { account, cfg, client, commandText, channelId, senderId, senderName, log } = params;
2288
+ const core = getMattermostRuntime();
2289
+ let channelInfo = null;
2290
+ try {
2291
+ channelInfo = await fetchMattermostChannel(client, channelId);
2292
+ } catch (err) {
2293
+ log?.(`mattermost: slash channel lookup failed for ${channelId}: ${String(err)}`);
2294
+ }
2295
+ if (!channelInfo) return {
2296
+ ok: false,
2297
+ denyResponse: {
2298
+ response_type: "ephemeral",
2299
+ text: "Temporary error: unable to determine channel type. Please try again."
2300
+ },
2301
+ commandAuthorized: false,
2302
+ channelInfo: null,
2303
+ kind: "channel",
2304
+ chatType: "channel",
2305
+ channelName: "",
2306
+ channelDisplay: "",
2307
+ roomLabel: `#${channelId}`
2308
+ };
2309
+ const allowTextCommands = core.channel.commands.shouldHandleTextCommands({
2310
+ cfg,
2311
+ surface: "mattermost"
2312
+ });
2313
+ const hasControlCommand = core.channel.text.hasControlCommand(commandText, cfg);
2314
+ const storeAllowFrom = normalizeMattermostAllowList(await core.channel.pairing.readAllowFromStore({
2315
+ channel: "mattermost",
2316
+ accountId: account.accountId
2317
+ }).catch(() => []));
2318
+ const decision = authorizeMattermostCommandInvocation({
2319
+ account,
2320
+ cfg,
2321
+ senderId,
2322
+ senderName,
2323
+ channelId,
2324
+ channelInfo,
2325
+ storeAllowFrom,
2326
+ allowTextCommands,
2327
+ hasControlCommand
2328
+ });
2329
+ if (!decision.ok) {
2330
+ if (decision.denyReason === "dm-pairing") {
2331
+ const { code } = await core.channel.pairing.upsertPairingRequest({
2332
+ channel: "mattermost",
2333
+ accountId: account.accountId,
2334
+ id: senderId,
2335
+ meta: { name: senderName }
2336
+ });
2337
+ return {
2338
+ ...decision,
2339
+ denyResponse: {
2340
+ response_type: "ephemeral",
2341
+ text: core.channel.pairing.buildPairingReply({
2342
+ channel: "mattermost",
2343
+ idLine: `Your Mattermost user id: ${senderId}`,
2344
+ code
2345
+ })
2346
+ }
2347
+ };
2348
+ }
2349
+ 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.";
2350
+ return {
2351
+ ...decision,
2352
+ denyResponse: {
2353
+ response_type: "ephemeral",
2354
+ text: denyText
2355
+ }
2356
+ };
2357
+ }
2358
+ return {
2359
+ ...decision,
2360
+ denyResponse: void 0
2361
+ };
2362
+ }
2363
+ /**
2364
+ * Create the HTTP request handler for Mattermost slash command callbacks.
2365
+ *
2366
+ * This handler is registered as a plugin HTTP route and receives POSTs
2367
+ * from the Mattermost server when a user invokes a registered slash command.
2368
+ */
2369
+ function createSlashCommandHttpHandler(params) {
2370
+ const { account, cfg, runtime, commandTokens, triggerMap, log } = params;
2371
+ return async (req, res) => {
2372
+ if (req.method !== "POST") {
2373
+ res.statusCode = 405;
2374
+ res.setHeader("Allow", "POST");
2375
+ res.end("Method Not Allowed");
2376
+ return;
2377
+ }
2378
+ let body;
2379
+ try {
2380
+ body = await readBody(req, MAX_BODY_BYTES);
2381
+ } catch (error) {
2382
+ if (isRequestBodyLimitError(error, "REQUEST_BODY_TIMEOUT")) {
2383
+ res.statusCode = 408;
2384
+ res.end("Request body timeout");
2385
+ return;
2386
+ }
2387
+ res.statusCode = 413;
2388
+ res.end("Payload Too Large");
2389
+ return;
2390
+ }
2391
+ const contentType = req.headers["content-type"] ?? "";
2392
+ const payload = parseSlashCommandPayload(body, contentType);
2393
+ if (!payload) {
2394
+ sendJsonResponse(res, 400, {
2395
+ response_type: "ephemeral",
2396
+ text: "Invalid slash command payload."
2397
+ });
2398
+ return;
2399
+ }
2400
+ if (commandTokens.size === 0 || !commandTokens.has(payload.token)) {
2401
+ sendJsonResponse(res, 401, {
2402
+ response_type: "ephemeral",
2403
+ text: "Unauthorized: invalid command token."
2404
+ });
2405
+ return;
2406
+ }
2407
+ const trigger = payload.command.replace(/^\//, "").trim();
2408
+ const commandText = resolveCommandText(trigger, payload.text, triggerMap);
2409
+ const channelId = payload.channel_id;
2410
+ const senderId = payload.user_id;
2411
+ const senderName = payload.user_name ?? senderId;
2412
+ const client = createMattermostClient({
2413
+ baseUrl: account.baseUrl ?? "",
2414
+ botToken: account.botToken ?? ""
2415
+ });
2416
+ const auth = await authorizeSlashInvocation({
2417
+ account,
2418
+ cfg,
2419
+ client,
2420
+ commandText,
2421
+ channelId,
2422
+ senderId,
2423
+ senderName,
2424
+ log
2425
+ });
2426
+ if (!auth.ok) {
2427
+ sendJsonResponse(res, 200, auth.denyResponse ?? {
2428
+ response_type: "ephemeral",
2429
+ text: "Unauthorized."
2430
+ });
2431
+ return;
2432
+ }
2433
+ log?.(`mattermost: slash command /${trigger} from ${senderName} in ${channelId}`);
2434
+ sendJsonResponse(res, 200, {
2435
+ response_type: "ephemeral",
2436
+ text: "Processing..."
2437
+ });
2438
+ try {
2439
+ await handleSlashCommandAsync({
2440
+ account,
2441
+ cfg,
2442
+ runtime,
2443
+ client,
2444
+ commandText,
2445
+ channelId,
2446
+ senderId,
2447
+ senderName,
2448
+ teamId: payload.team_id,
2449
+ triggerId: payload.trigger_id,
2450
+ kind: auth.kind,
2451
+ chatType: auth.chatType,
2452
+ channelName: auth.channelName,
2453
+ channelDisplay: auth.channelDisplay,
2454
+ roomLabel: auth.roomLabel,
2455
+ commandAuthorized: auth.commandAuthorized,
2456
+ log
2457
+ });
2458
+ } catch (err) {
2459
+ log?.(`mattermost: slash command handler error: ${String(err)}`);
2460
+ try {
2461
+ await sendMessageMattermost(`channel:${channelId}`, "Sorry, something went wrong processing that command.", { accountId: account.accountId });
2462
+ } catch {}
2463
+ }
2464
+ };
2465
+ }
2466
+ async function handleSlashCommandAsync(params) {
2467
+ const { account, cfg, runtime, client, commandText, channelId, senderId, senderName, teamId, kind, chatType, channelName, channelDisplay, roomLabel, commandAuthorized, triggerId, log } = params;
2468
+ const core = getMattermostRuntime();
2469
+ const route = core.channel.routing.resolveAgentRoute({
2470
+ cfg,
2471
+ channel: "mattermost",
2472
+ accountId: account.accountId,
2473
+ teamId,
2474
+ peer: {
2475
+ kind,
2476
+ id: kind === "direct" ? senderId : channelId
2477
+ }
2478
+ });
2479
+ const fromLabel = kind === "direct" ? `Mattermost DM from ${senderName}` : `Mattermost message in ${roomLabel} from ${senderName}`;
2480
+ const to = kind === "direct" ? `user:${senderId}` : `channel:${channelId}`;
2481
+ const pickerEntry = resolveMattermostModelPickerEntry(commandText);
2482
+ if (pickerEntry) {
2483
+ const data = await buildModelsProviderData(cfg, route.agentId);
2484
+ if (data.providers.length === 0) {
2485
+ await sendMessageMattermost(to, "No models available.", { accountId: account.accountId });
2486
+ return;
2487
+ }
2488
+ const currentModel = resolveMattermostModelPickerCurrentModel({
2489
+ cfg,
2490
+ route,
2491
+ data
2492
+ });
2493
+ const view = pickerEntry.kind === "summary" ? renderMattermostModelSummaryView({
2494
+ ownerUserId: senderId,
2495
+ currentModel
2496
+ }) : pickerEntry.kind === "providers" ? renderMattermostProviderPickerView({
2497
+ ownerUserId: senderId,
2498
+ data,
2499
+ currentModel
2500
+ }) : renderMattermostModelsPickerView({
2501
+ ownerUserId: senderId,
2502
+ data,
2503
+ provider: pickerEntry.provider,
2504
+ page: 1,
2505
+ currentModel
2506
+ });
2507
+ await sendMessageMattermost(to, view.text, {
2508
+ accountId: account.accountId,
2509
+ buttons: view.buttons
2510
+ });
2511
+ runtime.log?.(`delivered model picker to ${to}`);
2512
+ return;
2513
+ }
2514
+ const ctxPayload = core.channel.reply.finalizeInboundContext({
2515
+ Body: commandText,
2516
+ BodyForAgent: commandText,
2517
+ RawBody: commandText,
2518
+ CommandBody: commandText,
2519
+ From: kind === "direct" ? `mattermost:${senderId}` : kind === "group" ? `mattermost:group:${channelId}` : `mattermost:channel:${channelId}`,
2520
+ To: to,
2521
+ SessionKey: route.sessionKey,
2522
+ AccountId: route.accountId,
2523
+ ChatType: chatType,
2524
+ ConversationLabel: fromLabel,
2525
+ GroupSubject: kind !== "direct" ? channelDisplay || roomLabel : void 0,
2526
+ SenderName: senderName,
2527
+ SenderId: senderId,
2528
+ Provider: "mattermost",
2529
+ Surface: "mattermost",
2530
+ MessageSid: triggerId ?? `slash-${Date.now()}`,
2531
+ Timestamp: Date.now(),
2532
+ WasMentioned: true,
2533
+ CommandAuthorized: commandAuthorized,
2534
+ CommandSource: "native",
2535
+ OriginatingChannel: "mattermost",
2536
+ OriginatingTo: to
2537
+ });
2538
+ const textLimit = core.channel.text.resolveTextChunkLimit(cfg, "mattermost", account.accountId, { fallbackLimit: account.textChunkLimit ?? 4e3 });
2539
+ const tableMode = core.channel.text.resolveMarkdownTableMode({
2540
+ cfg,
2541
+ channel: "mattermost",
2542
+ accountId: account.accountId
2543
+ });
2544
+ const { onModelSelected, typingCallbacks, ...replyPipeline } = createChannelReplyPipeline({
2545
+ cfg,
2546
+ agentId: route.agentId,
2547
+ channel: "mattermost",
2548
+ accountId: account.accountId,
2549
+ typing: {
2550
+ start: () => sendMattermostTyping(client, { channelId }),
2551
+ onStartError: (err) => {
2552
+ logTypingFailure({
2553
+ log: (message) => log?.(message),
2554
+ channel: "mattermost",
2555
+ target: channelId,
2556
+ error: err
2557
+ });
2558
+ }
2559
+ }
2560
+ });
2561
+ const humanDelay = core.channel.reply.resolveHumanDelayConfig(cfg, route.agentId);
2562
+ const { dispatcher, replyOptions, markDispatchIdle } = core.channel.reply.createReplyDispatcherWithTyping({
2563
+ ...replyPipeline,
2564
+ humanDelay,
2565
+ deliver: async (payload) => {
2566
+ await deliverMattermostReplyPayload({
2567
+ core,
2568
+ cfg,
2569
+ payload,
2570
+ to,
2571
+ accountId: account.accountId,
2572
+ agentId: route.agentId,
2573
+ textLimit,
2574
+ tableMode,
2575
+ sendMessage: sendMessageMattermost
2576
+ });
2577
+ runtime.log?.(`delivered slash reply to ${to}`);
2578
+ },
2579
+ onError: (err, info) => {
2580
+ runtime.error?.(`mattermost slash ${info.kind} reply failed: ${String(err)}`);
2581
+ },
2582
+ onReplyStart: typingCallbacks?.onReplyStart
2583
+ });
2584
+ await core.channel.reply.withReplyDispatcher({
2585
+ dispatcher,
2586
+ onSettled: () => {
2587
+ markDispatchIdle();
2588
+ },
2589
+ run: () => core.channel.reply.dispatchReplyFromConfig({
2590
+ ctx: ctxPayload,
2591
+ cfg,
2592
+ dispatcher,
2593
+ replyOptions: {
2594
+ ...replyOptions,
2595
+ disableBlockStreaming: typeof account.blockStreaming === "boolean" ? !account.blockStreaming : void 0,
2596
+ onModelSelected
2597
+ }
2598
+ })
2599
+ });
2600
+ }
2601
+ //#endregion
2602
+ //#region extensions/mattermost/src/mattermost/slash-state.ts
2603
+ /** Map from accountId → per-account slash command state. */
2604
+ const accountStates = /* @__PURE__ */ new Map();
2605
+ function resolveSlashHandlerForToken(token) {
2606
+ const matches = [];
2607
+ for (const [accountId, state] of accountStates) if (state.commandTokens.has(token) && state.handler) matches.push({
2608
+ accountId,
2609
+ handler: state.handler
2610
+ });
2611
+ if (matches.length === 0) return { kind: "none" };
2612
+ if (matches.length === 1) return {
2613
+ kind: "single",
2614
+ handler: matches[0].handler,
2615
+ accountIds: [matches[0].accountId]
2616
+ };
2617
+ return {
2618
+ kind: "ambiguous",
2619
+ accountIds: matches.map((entry) => entry.accountId)
2620
+ };
2621
+ }
2622
+ /**
2623
+ * Get the slash command state for a specific account, or null if not activated.
2624
+ */
2625
+ function getSlashCommandState(accountId) {
2626
+ return accountStates.get(accountId) ?? null;
2627
+ }
2628
+ /**
2629
+ * Activate slash commands for a specific account.
2630
+ * Called from the monitor after bot connects.
2631
+ */
2632
+ function activateSlashCommands(params) {
2633
+ const { account, commandTokens, registeredCommands, triggerMap, api, log } = params;
2634
+ const accountId = account.accountId;
2635
+ const tokenSet = new Set(commandTokens);
2636
+ const handler = createSlashCommandHttpHandler({
2637
+ account,
2638
+ cfg: api.cfg,
2639
+ runtime: api.runtime,
2640
+ commandTokens: tokenSet,
2641
+ triggerMap,
2642
+ log
2643
+ });
2644
+ accountStates.set(accountId, {
2645
+ commandTokens: tokenSet,
2646
+ registeredCommands,
2647
+ handler,
2648
+ account,
2649
+ triggerMap: triggerMap ?? /* @__PURE__ */ new Map()
2650
+ });
2651
+ log?.(`mattermost: slash commands activated for account ${accountId} (${registeredCommands.length} commands)`);
2652
+ }
2653
+ /**
2654
+ * Deactivate slash commands for a specific account (on shutdown/disconnect).
2655
+ */
2656
+ function deactivateSlashCommands(accountId) {
2657
+ if (accountId) {
2658
+ const state = accountStates.get(accountId);
2659
+ if (state) {
2660
+ state.commandTokens.clear();
2661
+ state.registeredCommands = [];
2662
+ state.handler = null;
2663
+ accountStates.delete(accountId);
2664
+ }
2665
+ } else {
2666
+ for (const [, state] of accountStates) {
2667
+ state.commandTokens.clear();
2668
+ state.registeredCommands = [];
2669
+ state.handler = null;
2670
+ }
2671
+ accountStates.clear();
2672
+ }
2673
+ }
2674
+ /**
2675
+ * Register the HTTP route for slash command callbacks.
2676
+ * Called during plugin registration.
2677
+ *
2678
+ * The single HTTP route dispatches to the correct per-account handler
2679
+ * by matching the inbound token against each account's registered tokens.
2680
+ */
2681
+ function registerSlashCommandRoute(api) {
2682
+ const mmConfig = api.config.channels?.mattermost;
2683
+ const callbackPaths = /* @__PURE__ */ new Set();
2684
+ const addCallbackPaths = (raw) => {
2685
+ const resolved = resolveSlashCommandConfig(raw);
2686
+ callbackPaths.add(resolved.callbackPath);
2687
+ if (resolved.callbackUrl) try {
2688
+ const urlPath = new URL(resolved.callbackUrl).pathname;
2689
+ if (urlPath && urlPath !== resolved.callbackPath) callbackPaths.add(urlPath);
2690
+ } catch {}
2691
+ };
2692
+ const commandsRaw = mmConfig?.commands;
2693
+ addCallbackPaths(commandsRaw);
2694
+ const accountsRaw = mmConfig?.accounts ?? {};
2695
+ for (const accountId of Object.keys(accountsRaw)) {
2696
+ const accountCommandsRaw = accountsRaw[accountId]?.commands;
2697
+ addCallbackPaths(accountCommandsRaw);
2698
+ }
2699
+ const routeHandler = async (req, res) => {
2700
+ if (accountStates.size === 0) {
2701
+ res.statusCode = 503;
2702
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
2703
+ res.end(JSON.stringify({
2704
+ response_type: "ephemeral",
2705
+ text: "Slash commands are not yet initialized. Please try again in a moment."
2706
+ }));
2707
+ return;
2708
+ }
2709
+ if (accountStates.size === 1) {
2710
+ const [, state] = [...accountStates.entries()][0];
2711
+ if (!state.handler) {
2712
+ res.statusCode = 503;
2713
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
2714
+ res.end(JSON.stringify({
2715
+ response_type: "ephemeral",
2716
+ text: "Slash commands are not yet initialized. Please try again in a moment."
2717
+ }));
2718
+ return;
2719
+ }
2720
+ await state.handler(req, res);
2721
+ return;
2722
+ }
2723
+ const chunks = [];
2724
+ const MAX_BODY = 64 * 1024;
2725
+ let size = 0;
2726
+ for await (const chunk of req) {
2727
+ size += chunk.length;
2728
+ if (size > MAX_BODY) {
2729
+ res.statusCode = 413;
2730
+ res.end("Payload Too Large");
2731
+ return;
2732
+ }
2733
+ chunks.push(chunk);
2734
+ }
2735
+ const bodyStr = Buffer.concat(chunks).toString("utf8");
2736
+ let token = null;
2737
+ const ct = req.headers["content-type"] ?? "";
2738
+ try {
2739
+ if (ct.includes("application/json")) token = JSON.parse(bodyStr).token ?? null;
2740
+ else token = new URLSearchParams(bodyStr).get("token");
2741
+ } catch {}
2742
+ const match = token ? resolveSlashHandlerForToken(token) : { kind: "none" };
2743
+ if (match.kind === "none") {
2744
+ res.statusCode = 401;
2745
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
2746
+ res.end(JSON.stringify({
2747
+ response_type: "ephemeral",
2748
+ text: "Unauthorized: invalid command token."
2749
+ }));
2750
+ return;
2751
+ }
2752
+ if (match.kind === "ambiguous") {
2753
+ api.logger.warn?.(`mattermost: slash callback token matched multiple accounts (${match.accountIds?.join(", ")})`);
2754
+ res.statusCode = 409;
2755
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
2756
+ res.end(JSON.stringify({
2757
+ response_type: "ephemeral",
2758
+ text: "Conflict: command token is not unique across accounts."
2759
+ }));
2760
+ return;
2761
+ }
2762
+ const matchedHandler = match.handler;
2763
+ const { Readable } = await import("node:stream");
2764
+ const syntheticReq = new Readable({ read() {
2765
+ this.push(Buffer.from(bodyStr, "utf8"));
2766
+ this.push(null);
2767
+ } });
2768
+ syntheticReq.method = req.method;
2769
+ syntheticReq.url = req.url;
2770
+ syntheticReq.headers = req.headers;
2771
+ await matchedHandler(syntheticReq, res);
2772
+ };
2773
+ for (const callbackPath of callbackPaths) {
2774
+ api.registerHttpRoute({
2775
+ path: callbackPath,
2776
+ auth: "plugin",
2777
+ handler: routeHandler
2778
+ });
2779
+ api.logger.info?.(`mattermost: registered slash command callback at ${callbackPath}`);
2780
+ }
2781
+ }
2782
+ //#endregion
2783
+ //#region extensions/mattermost/src/mattermost/monitor-slash.ts
2784
+ function isLoopbackHost$1(hostname) {
2785
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1";
2786
+ }
2787
+ function buildSlashCommands(params) {
2788
+ const commandsToRegister = [...DEFAULT_COMMAND_SPECS];
2789
+ if (!params.nativeSkills) return commandsToRegister;
2790
+ try {
2791
+ const skillCommands = listSkillCommandsForAgents({ cfg: params.cfg });
2792
+ for (const spec of skillCommands) {
2793
+ const name = typeof spec.name === "string" ? spec.name.trim() : "";
2794
+ if (!name) continue;
2795
+ const trigger = name.startsWith("oc_") ? name : `oc_${name}`;
2796
+ commandsToRegister.push({
2797
+ trigger,
2798
+ description: spec.description || `Run skill ${name}`,
2799
+ autoComplete: true,
2800
+ autoCompleteHint: "[args]",
2801
+ originalName: name
2802
+ });
2803
+ }
2804
+ } catch (err) {
2805
+ params.runtime.error?.(`mattermost: failed to list skill commands: ${String(err)}`);
2806
+ }
2807
+ return commandsToRegister;
2808
+ }
2809
+ function dedupeSlashCommands(commands) {
2810
+ const seen = /* @__PURE__ */ new Set();
2811
+ return commands.filter((cmd) => {
2812
+ const key = cmd.trigger.trim();
2813
+ if (!key || seen.has(key)) return false;
2814
+ seen.add(key);
2815
+ return true;
2816
+ });
2817
+ }
2818
+ function buildTriggerMap(commands) {
2819
+ const triggerMap = /* @__PURE__ */ new Map();
2820
+ for (const cmd of commands) if (cmd.originalName) triggerMap.set(cmd.trigger, cmd.originalName);
2821
+ return triggerMap;
2822
+ }
2823
+ function warnOnSuspiciousCallbackUrl(params) {
2824
+ try {
2825
+ const mmHost = new URL(normalizeMattermostBaseUrl(params.baseUrl) ?? params.baseUrl).hostname;
2826
+ const callbackHost = new URL(params.callbackUrl).hostname;
2827
+ if (isLoopbackHost$1(callbackHost) && !isLoopbackHost$1(mmHost)) params.runtime.error?.(`mattermost: slash commands callbackUrl resolved to ${params.callbackUrl} (loopback) while baseUrl is ${params.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).`);
2828
+ } catch {}
2829
+ }
2830
+ async function registerSlashCommandsAcrossTeams(params) {
2831
+ const registered = [];
2832
+ let teamRegistrationFailures = 0;
2833
+ for (const team of params.teams) try {
2834
+ const created = await registerSlashCommands({
2835
+ client: params.client,
2836
+ teamId: team.id,
2837
+ creatorUserId: params.botUserId,
2838
+ callbackUrl: params.callbackUrl,
2839
+ commands: params.commands,
2840
+ log: (msg) => params.runtime.log?.(msg)
2841
+ });
2842
+ registered.push(...created);
2843
+ } catch (err) {
2844
+ teamRegistrationFailures += 1;
2845
+ params.runtime.error?.(`mattermost: failed to register slash commands for team ${team.id}: ${String(err)}`);
2846
+ }
2847
+ return {
2848
+ registered,
2849
+ teamRegistrationFailures
2850
+ };
2851
+ }
2852
+ async function registerMattermostMonitorSlashCommands(params) {
2853
+ const commandsRaw = params.account.config.commands;
2854
+ const slashConfig = resolveSlashCommandConfig(commandsRaw);
2855
+ if (!isSlashCommandsEnabled(slashConfig)) return;
2856
+ try {
2857
+ const teams = await fetchMattermostUserTeams(params.client, params.botUserId);
2858
+ const slashCallbackUrl = resolveCallbackUrl({
2859
+ config: slashConfig,
2860
+ gatewayPort: parseStrictPositiveInteger(process.env.OPENCLAW_GATEWAY_PORT?.trim()) ?? params.cfg.gateway?.port ?? 18789,
2861
+ gatewayHost: params.cfg.gateway?.customBindHost ?? void 0
2862
+ });
2863
+ warnOnSuspiciousCallbackUrl({
2864
+ runtime: params.runtime,
2865
+ baseUrl: params.baseUrl,
2866
+ callbackUrl: slashCallbackUrl
2867
+ });
2868
+ const dedupedCommands = dedupeSlashCommands(buildSlashCommands({
2869
+ cfg: params.cfg,
2870
+ runtime: params.runtime,
2871
+ nativeSkills: slashConfig.nativeSkills === true
2872
+ }));
2873
+ const { registered, teamRegistrationFailures } = await registerSlashCommandsAcrossTeams({
2874
+ client: params.client,
2875
+ teams,
2876
+ botUserId: params.botUserId,
2877
+ callbackUrl: slashCallbackUrl,
2878
+ commands: dedupedCommands,
2879
+ runtime: params.runtime
2880
+ });
2881
+ if (registered.length === 0) {
2882
+ params.runtime.error?.("mattermost: native slash commands enabled but no commands could be registered; keeping slash callbacks inactive");
2883
+ return;
2884
+ }
2885
+ if (teamRegistrationFailures > 0) params.runtime.error?.(`mattermost: slash command registration completed with ${teamRegistrationFailures} team error(s)`);
2886
+ activateSlashCommands({
2887
+ account: params.account,
2888
+ commandTokens: registered.map((cmd) => cmd.token).filter(Boolean),
2889
+ registeredCommands: registered,
2890
+ triggerMap: buildTriggerMap(dedupedCommands),
2891
+ api: {
2892
+ cfg: params.cfg,
2893
+ runtime: params.runtime
2894
+ },
2895
+ log: (msg) => params.runtime.log?.(msg)
2896
+ });
2897
+ params.runtime.log?.(`mattermost: slash commands registered (${registered.length} commands across ${teams.length} teams, callback=${slashCallbackUrl})`);
2898
+ } catch (err) {
2899
+ params.runtime.error?.(`mattermost: failed to register slash commands: ${String(err)}`);
2900
+ }
2901
+ }
2902
+ //#endregion
2903
+ //#region extensions/mattermost/src/mattermost/monitor-websocket.ts
2904
+ var WebSocketClosedBeforeOpenError = class extends Error {
2905
+ constructor(code, reason) {
2906
+ super(`websocket closed before open (code ${code})`);
2907
+ this.code = code;
2908
+ this.reason = reason;
2909
+ this.name = "WebSocketClosedBeforeOpenError";
2910
+ }
2911
+ };
2912
+ const defaultMattermostWebSocketFactory = (url) => new WebSocket$1(url);
2913
+ function parsePostedPayload(payload) {
2914
+ if (payload.event !== "posted") return null;
2915
+ const postData = payload.data?.post;
2916
+ if (!postData) return null;
2917
+ let post = null;
2918
+ if (typeof postData === "string") try {
2919
+ post = JSON.parse(postData);
2920
+ } catch {
2921
+ return null;
2922
+ }
2923
+ else if (typeof postData === "object") post = postData;
2924
+ if (!post) return null;
2925
+ return {
2926
+ payload,
2927
+ post
2928
+ };
2929
+ }
2930
+ function createMattermostConnectOnce(opts) {
2931
+ const webSocketFactory = opts.webSocketFactory ?? defaultMattermostWebSocketFactory;
2932
+ return async () => {
2933
+ const ws = webSocketFactory(opts.wsUrl);
2934
+ const onAbort = () => ws.terminate();
2935
+ opts.abortSignal?.addEventListener("abort", onAbort, { once: true });
2936
+ try {
2937
+ return await new Promise((resolve, reject) => {
2938
+ let opened = false;
2939
+ let settled = false;
2940
+ const resolveOnce = () => {
2941
+ if (settled) return;
2942
+ settled = true;
2943
+ resolve();
2944
+ };
2945
+ const rejectOnce = (error) => {
2946
+ if (settled) return;
2947
+ settled = true;
2948
+ reject(error);
2949
+ };
2950
+ ws.on("open", () => {
2951
+ opened = true;
2952
+ opts.statusSink?.({
2953
+ connected: true,
2954
+ lastConnectedAt: Date.now(),
2955
+ lastError: null
2956
+ });
2957
+ ws.send(JSON.stringify({
2958
+ seq: opts.nextSeq(),
2959
+ action: "authentication_challenge",
2960
+ data: { token: opts.botToken }
2961
+ }));
2962
+ });
2963
+ ws.on("message", async (data) => {
2964
+ const raw = rawDataToString(data);
2965
+ let payload;
2966
+ try {
2967
+ payload = JSON.parse(raw);
2968
+ } catch {
2969
+ return;
2970
+ }
2971
+ if (payload.event === "reaction_added" || payload.event === "reaction_removed") {
2972
+ if (!opts.onReaction) return;
2973
+ try {
2974
+ await opts.onReaction(payload);
2975
+ } catch (err) {
2976
+ opts.runtime.error?.(`mattermost reaction handler failed: ${String(err)}`);
2977
+ }
2978
+ return;
2979
+ }
2980
+ if (payload.event !== "posted") return;
2981
+ const parsed = parsePostedPayload(payload);
2982
+ if (!parsed) return;
2983
+ try {
2984
+ await opts.onPosted(parsed.post, parsed.payload);
2985
+ } catch (err) {
2986
+ opts.runtime.error?.(`mattermost handler failed: ${String(err)}`);
2987
+ }
2988
+ });
2989
+ ws.on("close", (code, reason) => {
2990
+ const message = reasonToString(reason);
2991
+ opts.statusSink?.({
2992
+ connected: false,
2993
+ lastDisconnect: {
2994
+ at: Date.now(),
2995
+ status: code,
2996
+ error: message || void 0
2997
+ }
2998
+ });
2999
+ if (opened) {
3000
+ resolveOnce();
3001
+ return;
3002
+ }
3003
+ rejectOnce(new WebSocketClosedBeforeOpenError(code, message || void 0));
3004
+ });
3005
+ ws.on("error", (err) => {
3006
+ opts.runtime.error?.(`mattermost websocket error: ${String(err)}`);
3007
+ opts.statusSink?.({ lastError: String(err) });
3008
+ try {
3009
+ ws.close();
3010
+ } catch {}
3011
+ });
3012
+ });
3013
+ } finally {
3014
+ opts.abortSignal?.removeEventListener("abort", onAbort);
3015
+ }
3016
+ };
3017
+ }
3018
+ function reasonToString(reason) {
3019
+ if (!reason) return "";
3020
+ if (typeof reason === "string") return reason;
3021
+ return reason.length > 0 ? reason.toString("utf8") : "";
3022
+ }
3023
+ //#endregion
3024
+ //#region extensions/mattermost/src/mattermost/reconnect.ts
3025
+ /**
3026
+ * Reconnection loop with exponential backoff.
3027
+ *
3028
+ * Calls `connectFn` in a while loop. On normal resolve (connection closed),
3029
+ * the backoff resets. On thrown error (connection failed), the current delay is
3030
+ * used, then doubled for the next retry.
3031
+ * The loop exits when `abortSignal` fires.
3032
+ */
3033
+ async function runWithReconnect(connectFn, opts = {}) {
3034
+ const { initialDelayMs = 2e3, maxDelayMs = 6e4 } = opts;
3035
+ const jitterRatio = Math.max(0, opts.jitterRatio ?? 0);
3036
+ const random = opts.random ?? Math.random;
3037
+ let retryDelay = initialDelayMs;
3038
+ let attempt = 0;
3039
+ while (!opts.abortSignal?.aborted) {
3040
+ let shouldIncreaseDelay = false;
3041
+ let outcome = "resolved";
3042
+ let error;
3043
+ try {
3044
+ await connectFn();
3045
+ retryDelay = initialDelayMs;
3046
+ } catch (err) {
3047
+ if (opts.abortSignal?.aborted) return;
3048
+ outcome = "rejected";
3049
+ error = err;
3050
+ opts.onError?.(err);
3051
+ shouldIncreaseDelay = true;
3052
+ }
3053
+ if (opts.abortSignal?.aborted) return;
3054
+ const delayMs = withJitter(retryDelay, jitterRatio, random);
3055
+ if (!(opts.shouldReconnect?.({
3056
+ attempt,
3057
+ delayMs,
3058
+ outcome,
3059
+ error
3060
+ }) ?? true)) return;
3061
+ opts.onReconnect?.(delayMs);
3062
+ await sleepAbortable(delayMs, opts.abortSignal);
3063
+ if (shouldIncreaseDelay) retryDelay = Math.min(retryDelay * 2, maxDelayMs);
3064
+ attempt++;
3065
+ }
3066
+ }
3067
+ function withJitter(baseMs, jitterRatio, random) {
3068
+ if (jitterRatio <= 0) return baseMs;
3069
+ const normalized = Math.max(0, Math.min(1, random()));
3070
+ const spread = baseMs * jitterRatio;
3071
+ return Math.max(1, Math.round(baseMs - spread + normalized * spread * 2));
3072
+ }
3073
+ function sleepAbortable(ms, signal) {
3074
+ return new Promise((resolve) => {
3075
+ if (signal?.aborted) {
3076
+ resolve();
3077
+ return;
3078
+ }
3079
+ const onAbort = () => {
3080
+ clearTimeout(timer);
3081
+ resolve();
3082
+ };
3083
+ const timer = setTimeout(() => {
3084
+ signal?.removeEventListener("abort", onAbort);
3085
+ resolve();
3086
+ }, ms);
3087
+ signal?.addEventListener("abort", onAbort, { once: true });
3088
+ });
3089
+ }
3090
+ //#endregion
3091
+ //#region extensions/mattermost/src/mattermost/monitor.ts
3092
+ const RECENT_MATTERMOST_MESSAGE_TTL_MS = 5 * 6e4;
3093
+ const RECENT_MATTERMOST_MESSAGE_MAX = 2e3;
3094
+ function isLoopbackHost(hostname) {
3095
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1";
3096
+ }
3097
+ function normalizeInteractionSourceIps(values) {
3098
+ return (values ?? []).map((value) => value.trim()).filter(Boolean);
3099
+ }
3100
+ const recentInboundMessages = createDedupeCache({
3101
+ ttlMs: RECENT_MATTERMOST_MESSAGE_TTL_MS,
3102
+ maxSize: RECENT_MATTERMOST_MESSAGE_MAX
3103
+ });
3104
+ function resolveRuntime(opts) {
3105
+ return opts.runtime ?? {
3106
+ log: console.log,
3107
+ error: console.error,
3108
+ exit: (code) => {
3109
+ throw new Error(`exit ${code}`);
3110
+ }
3111
+ };
3112
+ }
3113
+ function isSystemPost(post) {
3114
+ const type = post.type?.trim();
3115
+ return Boolean(type);
3116
+ }
3117
+ function channelChatType(kind) {
3118
+ if (kind === "direct") return "direct";
3119
+ if (kind === "group") return "group";
3120
+ return "channel";
3121
+ }
3122
+ function resolveMattermostReplyRootId(params) {
3123
+ const threadRootId = params.threadRootId?.trim();
3124
+ if (threadRootId) return threadRootId;
3125
+ return params.replyToId?.trim() || void 0;
3126
+ }
3127
+ function resolveMattermostEffectiveReplyToId(params) {
3128
+ const threadRootId = params.threadRootId?.trim();
3129
+ if (threadRootId) return threadRootId;
3130
+ if (params.kind === "direct") return;
3131
+ const postId = params.postId?.trim();
3132
+ if (!postId) return;
3133
+ return params.replyToMode === "all" || params.replyToMode === "first" ? postId : void 0;
3134
+ }
3135
+ function resolveMattermostThreadSessionContext(params) {
3136
+ const effectiveReplyToId = resolveMattermostEffectiveReplyToId({
3137
+ kind: params.kind,
3138
+ postId: params.postId,
3139
+ replyToMode: params.replyToMode,
3140
+ threadRootId: params.threadRootId
3141
+ });
3142
+ const threadKeys = resolveThreadSessionKeys({
3143
+ baseSessionKey: params.baseSessionKey,
3144
+ threadId: effectiveReplyToId,
3145
+ parentSessionKey: effectiveReplyToId ? params.baseSessionKey : void 0
3146
+ });
3147
+ return {
3148
+ effectiveReplyToId,
3149
+ sessionKey: threadKeys.sessionKey,
3150
+ parentSessionKey: threadKeys.parentSessionKey
3151
+ };
3152
+ }
3153
+ function buildMattermostAttachmentPlaceholder(mediaList) {
3154
+ if (mediaList.length === 0) return "";
3155
+ if (mediaList.length === 1) return `<media:${mediaList[0].kind === "unknown" ? "document" : mediaList[0].kind}>`;
3156
+ const allImages = mediaList.every((media) => media.kind === "image");
3157
+ const label = allImages ? "image" : "file";
3158
+ const suffix = mediaList.length === 1 ? label : `${label}s`;
3159
+ return `${allImages ? "<media:image>" : "<media:document>"} (${mediaList.length} ${suffix})`;
3160
+ }
3161
+ function buildMattermostWsUrl(baseUrl) {
3162
+ const normalized = normalizeMattermostBaseUrl(baseUrl);
3163
+ if (!normalized) throw new Error("Mattermost baseUrl is required");
3164
+ return `${normalized.replace(/^http/i, "ws")}/api/v4/websocket`;
3165
+ }
3166
+ async function monitorMattermostProvider(opts = {}) {
3167
+ const core = getMattermostRuntime();
3168
+ const runtime = resolveRuntime(opts);
3169
+ const cfg = opts.config ?? core.config.loadConfig();
3170
+ const account = resolveMattermostAccount({
3171
+ cfg,
3172
+ accountId: opts.accountId
3173
+ });
3174
+ const pairing = createChannelPairingController({
3175
+ core,
3176
+ channel: "mattermost",
3177
+ accountId: account.accountId
3178
+ });
3179
+ const allowNameMatching = isDangerousNameMatchingEnabled(account.config);
3180
+ const botToken = opts.botToken?.trim() || account.botToken?.trim();
3181
+ 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).`);
3182
+ const baseUrl = normalizeMattermostBaseUrl(opts.baseUrl ?? account.baseUrl);
3183
+ if (!baseUrl) throw new Error(`Mattermost baseUrl missing for account "${account.accountId}" (set channels.mattermost.accounts.${account.accountId}.baseUrl or MATTERMOST_URL for default).`);
3184
+ const client = createMattermostClient({
3185
+ baseUrl,
3186
+ botToken
3187
+ });
3188
+ const botUser = await fetchMattermostMe(client);
3189
+ const botUserId = botUser.id;
3190
+ const botUsername = botUser.username?.trim() || void 0;
3191
+ runtime.log?.(`mattermost connected as ${botUsername ? `@${botUsername}` : botUserId}`);
3192
+ await registerMattermostMonitorSlashCommands({
3193
+ client,
3194
+ cfg,
3195
+ runtime,
3196
+ account,
3197
+ baseUrl,
3198
+ botUserId
3199
+ });
3200
+ const slashEnabled = getSlashCommandState(account.accountId) != null;
3201
+ setInteractionSecret(account.accountId, botToken);
3202
+ const interactionPath = resolveInteractionCallbackPath(account.accountId);
3203
+ const callbackUrl = computeInteractionCallbackUrl(account.accountId, {
3204
+ gateway: cfg.gateway,
3205
+ interactions: account.config.interactions
3206
+ });
3207
+ setInteractionCallbackUrl(account.accountId, callbackUrl);
3208
+ const allowedInteractionSourceIps = normalizeInteractionSourceIps(account.config.interactions?.allowedSourceIps);
3209
+ try {
3210
+ const mmHost = new URL(baseUrl).hostname;
3211
+ const callbackHost = new URL(callbackUrl).hostname;
3212
+ 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).`);
3213
+ 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.`);
3214
+ } catch {}
3215
+ const effectiveInteractionSourceIps = allowedInteractionSourceIps.length > 0 ? allowedInteractionSourceIps : ["127.0.0.1", "::1"];
3216
+ const unregisterInteractions = registerPluginHttpRoute({
3217
+ path: interactionPath,
3218
+ fallbackPath: "/mattermost/interactions/default",
3219
+ auth: "plugin",
3220
+ handler: createMattermostInteractionHandler({
3221
+ client,
3222
+ botUserId,
3223
+ accountId: account.accountId,
3224
+ allowedSourceIps: effectiveInteractionSourceIps,
3225
+ trustedProxies: cfg.gateway?.trustedProxies,
3226
+ allowRealIpFallback: cfg.gateway?.allowRealIpFallback === true,
3227
+ handleInteraction: handleModelPickerInteraction,
3228
+ authorizeButtonClick: async ({ payload, post }) => {
3229
+ const channelInfo = await resolveChannelInfo(payload.channel_id);
3230
+ const isDirect = channelInfo?.type?.trim().toUpperCase() === "D";
3231
+ const allowTextCommands = core.channel.commands.shouldHandleTextCommands({
3232
+ cfg,
3233
+ surface: "mattermost"
3234
+ });
3235
+ const decision = authorizeMattermostCommandInvocation({
3236
+ account,
3237
+ cfg,
3238
+ senderId: payload.user_id,
3239
+ senderName: payload.user_name ?? "",
3240
+ channelId: payload.channel_id,
3241
+ channelInfo,
3242
+ storeAllowFrom: isDirect ? await readStoreAllowFromForDmPolicy({
3243
+ provider: "mattermost",
3244
+ accountId: account.accountId,
3245
+ dmPolicy: account.config.dmPolicy ?? "pairing",
3246
+ readStore: pairing.readStoreForDmPolicy
3247
+ }) : void 0,
3248
+ allowTextCommands,
3249
+ hasControlCommand: false
3250
+ });
3251
+ if (decision.ok) return { ok: true };
3252
+ return {
3253
+ ok: false,
3254
+ response: {
3255
+ update: {
3256
+ message: post.message ?? "",
3257
+ props: post.props
3258
+ },
3259
+ ephemeral_text: `OpenClaw ignored this action for ${decision.roomLabel}.`
3260
+ }
3261
+ };
3262
+ },
3263
+ resolveSessionKey: async ({ channelId, userId, post }) => {
3264
+ const channelInfo = await resolveChannelInfo(channelId);
3265
+ const kind = mapMattermostChannelTypeToChatType(channelInfo?.type);
3266
+ const teamId = channelInfo?.team_id ?? void 0;
3267
+ const route = core.channel.routing.resolveAgentRoute({
3268
+ cfg,
3269
+ channel: "mattermost",
3270
+ accountId: account.accountId,
3271
+ teamId,
3272
+ peer: {
3273
+ kind,
3274
+ id: kind === "direct" ? userId : channelId
3275
+ }
3276
+ });
3277
+ const replyToMode = resolveMattermostReplyToMode(account, kind);
3278
+ return resolveMattermostThreadSessionContext({
3279
+ baseSessionKey: route.sessionKey,
3280
+ kind,
3281
+ postId: post.id || void 0,
3282
+ replyToMode,
3283
+ threadRootId: post.root_id
3284
+ }).sessionKey;
3285
+ },
3286
+ dispatchButtonClick: async (opts) => {
3287
+ const channelInfo = await resolveChannelInfo(opts.channelId);
3288
+ const kind = mapMattermostChannelTypeToChatType(channelInfo?.type);
3289
+ const chatType = channelChatType(kind);
3290
+ const teamId = channelInfo?.team_id ?? void 0;
3291
+ const channelName = channelInfo?.name ?? void 0;
3292
+ const channelDisplay = channelInfo?.display_name ?? channelName ?? opts.channelId;
3293
+ const route = core.channel.routing.resolveAgentRoute({
3294
+ cfg,
3295
+ channel: "mattermost",
3296
+ accountId: account.accountId,
3297
+ teamId,
3298
+ peer: {
3299
+ kind,
3300
+ id: kind === "direct" ? opts.userId : opts.channelId
3301
+ }
3302
+ });
3303
+ const replyToMode = resolveMattermostReplyToMode(account, kind);
3304
+ const threadContext = resolveMattermostThreadSessionContext({
3305
+ baseSessionKey: route.sessionKey,
3306
+ kind,
3307
+ postId: opts.post.id || opts.postId,
3308
+ replyToMode,
3309
+ threadRootId: opts.post.root_id
3310
+ });
3311
+ const to = kind === "direct" ? `user:${opts.userId}` : `channel:${opts.channelId}`;
3312
+ const bodyText = `[Button click: user @${opts.userName} selected "${opts.actionName}"]`;
3313
+ const ctxPayload = core.channel.reply.finalizeInboundContext({
3314
+ Body: bodyText,
3315
+ BodyForAgent: bodyText,
3316
+ RawBody: bodyText,
3317
+ CommandBody: bodyText,
3318
+ From: kind === "direct" ? `mattermost:${opts.userId}` : kind === "group" ? `mattermost:group:${opts.channelId}` : `mattermost:channel:${opts.channelId}`,
3319
+ To: to,
3320
+ SessionKey: threadContext.sessionKey,
3321
+ ParentSessionKey: threadContext.parentSessionKey,
3322
+ AccountId: route.accountId,
3323
+ ChatType: chatType,
3324
+ ConversationLabel: `mattermost:${opts.userName}`,
3325
+ GroupSubject: kind !== "direct" ? channelDisplay : void 0,
3326
+ GroupChannel: channelName ? `#${channelName}` : void 0,
3327
+ GroupSpace: teamId,
3328
+ SenderName: opts.userName,
3329
+ SenderId: opts.userId,
3330
+ Provider: "mattermost",
3331
+ Surface: "mattermost",
3332
+ MessageSid: `interaction:${opts.postId}:${opts.actionId}`,
3333
+ ReplyToId: threadContext.effectiveReplyToId,
3334
+ MessageThreadId: threadContext.effectiveReplyToId,
3335
+ WasMentioned: true,
3336
+ CommandAuthorized: false,
3337
+ OriginatingChannel: "mattermost",
3338
+ OriginatingTo: to
3339
+ });
3340
+ const textLimit = core.channel.text.resolveTextChunkLimit(cfg, "mattermost", account.accountId, { fallbackLimit: account.textChunkLimit ?? 4e3 });
3341
+ const tableMode = core.channel.text.resolveMarkdownTableMode({
3342
+ cfg,
3343
+ channel: "mattermost",
3344
+ accountId: account.accountId
3345
+ });
3346
+ const { onModelSelected, typingCallbacks, ...replyPipeline } = createChannelReplyPipeline({
3347
+ cfg,
3348
+ agentId: route.agentId,
3349
+ channel: "mattermost",
3350
+ accountId: account.accountId,
3351
+ typing: {
3352
+ start: () => sendTypingIndicator(opts.channelId, threadContext.effectiveReplyToId),
3353
+ onStartError: (err) => {
3354
+ logTypingFailure({
3355
+ log: (message) => logger.debug?.(message),
3356
+ channel: "mattermost",
3357
+ target: opts.channelId,
3358
+ error: err
3359
+ });
3360
+ }
3361
+ }
3362
+ });
3363
+ const { dispatcher, replyOptions, markDispatchIdle } = core.channel.reply.createReplyDispatcherWithTyping({
3364
+ ...replyPipeline,
3365
+ humanDelay: core.channel.reply.resolveHumanDelayConfig(cfg, route.agentId),
3366
+ deliver: async (payload) => {
3367
+ await deliverMattermostReplyPayload({
3368
+ core,
3369
+ cfg,
3370
+ payload,
3371
+ to,
3372
+ accountId: account.accountId,
3373
+ agentId: route.agentId,
3374
+ replyToId: resolveMattermostReplyRootId({
3375
+ threadRootId: threadContext.effectiveReplyToId,
3376
+ replyToId: payload.replyToId
3377
+ }),
3378
+ textLimit,
3379
+ tableMode,
3380
+ sendMessage: sendMessageMattermost
3381
+ });
3382
+ runtime.log?.(`delivered button-click reply to ${to}`);
3383
+ },
3384
+ onError: (err, info) => {
3385
+ runtime.error?.(`mattermost button-click ${info.kind} reply failed: ${String(err)}`);
3386
+ },
3387
+ onReplyStart: typingCallbacks?.onReplyStart
3388
+ });
3389
+ await core.channel.reply.dispatchReplyFromConfig({
3390
+ ctx: ctxPayload,
3391
+ cfg,
3392
+ dispatcher,
3393
+ replyOptions: {
3394
+ ...replyOptions,
3395
+ disableBlockStreaming: typeof account.blockStreaming === "boolean" ? !account.blockStreaming : void 0,
3396
+ onModelSelected
3397
+ }
3398
+ });
3399
+ markDispatchIdle();
3400
+ },
3401
+ log: (msg) => runtime.log?.(msg)
3402
+ }),
3403
+ pluginId: "mattermost",
3404
+ source: "mattermost-interactions",
3405
+ accountId: account.accountId,
3406
+ log: (msg) => runtime.log?.(msg)
3407
+ });
3408
+ const logger = core.logging.getChildLogger({ module: "mattermost" });
3409
+ const logVerboseMessage = (message) => {
3410
+ if (!core.logging.shouldLogVerbose()) return;
3411
+ logger.debug?.(message);
3412
+ };
3413
+ const mediaMaxBytes = resolveChannelMediaMaxBytes({
3414
+ cfg,
3415
+ resolveChannelLimitMb: () => void 0,
3416
+ accountId: account.accountId
3417
+ }) ?? 8 * 1024 * 1024;
3418
+ const historyLimit = Math.max(0, cfg.messages?.groupChat?.historyLimit ?? 50);
3419
+ const channelHistories = /* @__PURE__ */ new Map();
3420
+ const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
3421
+ const { groupPolicy, providerMissingFallbackApplied } = resolveAllowlistProviderRuntimeGroupPolicy({
3422
+ providerConfigPresent: cfg.channels?.mattermost !== void 0,
3423
+ groupPolicy: account.config.groupPolicy,
3424
+ defaultGroupPolicy
3425
+ });
3426
+ warnMissingProviderGroupPolicyFallbackOnce({
3427
+ providerMissingFallbackApplied,
3428
+ providerKey: "mattermost",
3429
+ accountId: account.accountId,
3430
+ log: (message) => logVerboseMessage(message)
3431
+ });
3432
+ const { resolveMattermostMedia, sendTypingIndicator, resolveChannelInfo, resolveUserInfo, updateModelPickerPost } = createMattermostMonitorResources({
3433
+ accountId: account.accountId,
3434
+ callbackUrl,
3435
+ client,
3436
+ logger: { debug: (message) => logger.debug?.(String(message)) },
3437
+ mediaMaxBytes,
3438
+ fetchRemoteMedia: (params) => core.channel.media.fetchRemoteMedia(params),
3439
+ saveMediaBuffer: (buffer, contentType, direction, maxBytes) => core.channel.media.saveMediaBuffer(Buffer.from(buffer), contentType, direction, maxBytes),
3440
+ mediaKindFromMime: (contentType) => core.media.mediaKindFromMime(contentType)
3441
+ });
3442
+ const runModelPickerCommand = async (params) => {
3443
+ const to = params.kind === "direct" ? `user:${params.senderId}` : `channel:${params.channelId}`;
3444
+ const fromLabel = params.kind === "direct" ? `Mattermost DM from ${params.senderName}` : `Mattermost message in ${params.roomLabel} from ${params.senderName}`;
3445
+ const ctxPayload = core.channel.reply.finalizeInboundContext({
3446
+ Body: params.commandText,
3447
+ BodyForAgent: params.commandText,
3448
+ RawBody: params.commandText,
3449
+ CommandBody: params.commandText,
3450
+ From: params.kind === "direct" ? `mattermost:${params.senderId}` : params.kind === "group" ? `mattermost:group:${params.channelId}` : `mattermost:channel:${params.channelId}`,
3451
+ To: to,
3452
+ SessionKey: params.sessionKey,
3453
+ ParentSessionKey: params.parentSessionKey,
3454
+ AccountId: params.route.accountId,
3455
+ ChatType: params.chatType,
3456
+ ConversationLabel: fromLabel,
3457
+ GroupSubject: params.kind !== "direct" ? params.channelDisplay || params.roomLabel : void 0,
3458
+ GroupChannel: params.channelName ? `#${params.channelName}` : void 0,
3459
+ GroupSpace: params.teamId,
3460
+ SenderName: params.senderName,
3461
+ SenderId: params.senderId,
3462
+ Provider: "mattermost",
3463
+ Surface: "mattermost",
3464
+ MessageSid: `interaction:${params.postId}:${Date.now()}`,
3465
+ ReplyToId: params.effectiveReplyToId,
3466
+ MessageThreadId: params.effectiveReplyToId,
3467
+ Timestamp: Date.now(),
3468
+ WasMentioned: true,
3469
+ CommandAuthorized: params.commandAuthorized,
3470
+ CommandSource: "native",
3471
+ OriginatingChannel: "mattermost",
3472
+ OriginatingTo: to
3473
+ });
3474
+ const tableMode = core.channel.text.resolveMarkdownTableMode({
3475
+ cfg,
3476
+ channel: "mattermost",
3477
+ accountId: account.accountId
3478
+ });
3479
+ const textLimit = core.channel.text.resolveTextChunkLimit(cfg, "mattermost", account.accountId, { fallbackLimit: account.textChunkLimit ?? 4e3 });
3480
+ const shouldDeliverReplies = params.deliverReplies === true;
3481
+ const { onModelSelected, typingCallbacks, ...replyPipeline } = createChannelReplyPipeline({
3482
+ cfg,
3483
+ agentId: params.route.agentId,
3484
+ channel: "mattermost",
3485
+ accountId: account.accountId,
3486
+ typing: shouldDeliverReplies ? {
3487
+ start: () => sendTypingIndicator(params.channelId, params.effectiveReplyToId),
3488
+ onStartError: (err) => {
3489
+ logTypingFailure({
3490
+ log: (message) => logger.debug?.(message),
3491
+ channel: "mattermost",
3492
+ target: params.channelId,
3493
+ error: err
3494
+ });
3495
+ }
3496
+ } : void 0
3497
+ });
3498
+ const capturedTexts = [];
3499
+ const { dispatcher, replyOptions, markDispatchIdle } = core.channel.reply.createReplyDispatcherWithTyping({
3500
+ ...replyPipeline,
3501
+ deliver: async (payload) => {
3502
+ const trimmedPayload = {
3503
+ ...payload,
3504
+ text: core.channel.text.convertMarkdownTables(payload.text ?? "", tableMode).trim()
3505
+ };
3506
+ if (!shouldDeliverReplies) {
3507
+ if (trimmedPayload.text) capturedTexts.push(trimmedPayload.text);
3508
+ return;
3509
+ }
3510
+ await deliverMattermostReplyPayload({
3511
+ core,
3512
+ cfg,
3513
+ payload: trimmedPayload,
3514
+ to,
3515
+ accountId: account.accountId,
3516
+ agentId: params.route.agentId,
3517
+ replyToId: resolveMattermostReplyRootId({
3518
+ threadRootId: params.effectiveReplyToId,
3519
+ replyToId: trimmedPayload.replyToId
3520
+ }),
3521
+ textLimit,
3522
+ tableMode: "off",
3523
+ sendMessage: sendMessageMattermost
3524
+ });
3525
+ },
3526
+ onError: (err, info) => {
3527
+ runtime.error?.(`mattermost model picker ${info.kind} reply failed: ${String(err)}`);
3528
+ },
3529
+ onReplyStart: typingCallbacks?.onReplyStart
3530
+ });
3531
+ await core.channel.reply.withReplyDispatcher({
3532
+ dispatcher,
3533
+ onSettled: () => {
3534
+ markDispatchIdle();
3535
+ },
3536
+ run: () => core.channel.reply.dispatchReplyFromConfig({
3537
+ ctx: ctxPayload,
3538
+ cfg,
3539
+ dispatcher,
3540
+ replyOptions: {
3541
+ ...replyOptions,
3542
+ disableBlockStreaming: typeof account.blockStreaming === "boolean" ? !account.blockStreaming : void 0,
3543
+ onModelSelected
3544
+ }
3545
+ })
3546
+ });
3547
+ return capturedTexts.join("\n\n").trim();
3548
+ };
3549
+ async function handleModelPickerInteraction(params) {
3550
+ const pickerState = parseMattermostModelPickerContext(params.context);
3551
+ if (!pickerState) return null;
3552
+ if (pickerState.ownerUserId !== params.payload.user_id) return { ephemeral_text: "Only the person who opened this picker can use it." };
3553
+ const channelInfo = await resolveChannelInfo(params.payload.channel_id);
3554
+ const pickerCommandText = pickerState.action === "select" ? `/model ${pickerState.provider}/${pickerState.model}` : pickerState.action === "list" ? `/models ${pickerState.provider}` : "/models";
3555
+ const allowTextCommands = core.channel.commands.shouldHandleTextCommands({
3556
+ cfg,
3557
+ surface: "mattermost"
3558
+ });
3559
+ const hasControlCommand = core.channel.text.hasControlCommand(pickerCommandText, cfg);
3560
+ const dmPolicy = account.config.dmPolicy ?? "pairing";
3561
+ const storeAllowFrom = normalizeMattermostAllowList(await readStoreAllowFromForDmPolicy({
3562
+ provider: "mattermost",
3563
+ accountId: account.accountId,
3564
+ dmPolicy,
3565
+ readStore: pairing.readStoreForDmPolicy
3566
+ }));
3567
+ const auth = authorizeMattermostCommandInvocation({
3568
+ account,
3569
+ cfg,
3570
+ senderId: params.payload.user_id,
3571
+ senderName: params.userName,
3572
+ channelId: params.payload.channel_id,
3573
+ channelInfo,
3574
+ storeAllowFrom,
3575
+ allowTextCommands,
3576
+ hasControlCommand
3577
+ });
3578
+ if (!auth.ok) {
3579
+ if (auth.denyReason === "dm-pairing") {
3580
+ const { code } = await pairing.upsertPairingRequest({
3581
+ id: params.payload.user_id,
3582
+ meta: { name: params.userName }
3583
+ });
3584
+ return { ephemeral_text: core.channel.pairing.buildPairingReply({
3585
+ channel: "mattermost",
3586
+ idLine: `Your Mattermost user id: ${params.payload.user_id}`,
3587
+ code
3588
+ }) };
3589
+ }
3590
+ 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." };
3591
+ }
3592
+ const kind = auth.kind;
3593
+ const chatType = auth.chatType;
3594
+ const teamId = auth.channelInfo.team_id ?? params.payload.team_id ?? void 0;
3595
+ const channelName = auth.channelName || void 0;
3596
+ const channelDisplay = auth.channelDisplay || auth.channelName || params.payload.channel_id;
3597
+ const roomLabel = auth.roomLabel;
3598
+ const route = core.channel.routing.resolveAgentRoute({
3599
+ cfg,
3600
+ channel: "mattermost",
3601
+ accountId: account.accountId,
3602
+ teamId,
3603
+ peer: {
3604
+ kind,
3605
+ id: kind === "direct" ? params.payload.user_id : params.payload.channel_id
3606
+ }
3607
+ });
3608
+ const replyToMode = resolveMattermostReplyToMode(account, kind);
3609
+ const threadContext = resolveMattermostThreadSessionContext({
3610
+ baseSessionKey: route.sessionKey,
3611
+ kind,
3612
+ postId: params.post.id || params.payload.post_id,
3613
+ replyToMode,
3614
+ threadRootId: params.post.root_id
3615
+ });
3616
+ const modelSessionRoute = {
3617
+ agentId: route.agentId,
3618
+ sessionKey: threadContext.sessionKey
3619
+ };
3620
+ const data = await buildModelsProviderData(cfg, route.agentId);
3621
+ if (data.providers.length === 0) return await updateModelPickerPost({
3622
+ channelId: params.payload.channel_id,
3623
+ postId: params.payload.post_id,
3624
+ message: "No models available."
3625
+ });
3626
+ if (pickerState.action === "providers" || pickerState.action === "back") {
3627
+ const currentModel = resolveMattermostModelPickerCurrentModel({
3628
+ cfg,
3629
+ route: modelSessionRoute,
3630
+ data
3631
+ });
3632
+ const view = renderMattermostProviderPickerView({
3633
+ ownerUserId: pickerState.ownerUserId,
3634
+ data,
3635
+ currentModel
3636
+ });
3637
+ return await updateModelPickerPost({
3638
+ channelId: params.payload.channel_id,
3639
+ postId: params.payload.post_id,
3640
+ message: view.text,
3641
+ buttons: view.buttons
3642
+ });
3643
+ }
3644
+ if (pickerState.action === "list") {
3645
+ const currentModel = resolveMattermostModelPickerCurrentModel({
3646
+ cfg,
3647
+ route: modelSessionRoute,
3648
+ data
3649
+ });
3650
+ const view = renderMattermostModelsPickerView({
3651
+ ownerUserId: pickerState.ownerUserId,
3652
+ data,
3653
+ provider: pickerState.provider,
3654
+ page: pickerState.page,
3655
+ currentModel
3656
+ });
3657
+ return await updateModelPickerPost({
3658
+ channelId: params.payload.channel_id,
3659
+ postId: params.payload.post_id,
3660
+ message: view.text,
3661
+ buttons: view.buttons
3662
+ });
3663
+ }
3664
+ const targetModelRef = `${pickerState.provider}/${pickerState.model}`;
3665
+ if (!buildMattermostAllowedModelRefs(data).has(targetModelRef)) return { ephemeral_text: `That model is no longer available: ${targetModelRef}` };
3666
+ (async () => {
3667
+ try {
3668
+ await runModelPickerCommand({
3669
+ commandText: `/model ${targetModelRef}`,
3670
+ commandAuthorized: auth.commandAuthorized,
3671
+ route,
3672
+ sessionKey: threadContext.sessionKey,
3673
+ parentSessionKey: threadContext.parentSessionKey,
3674
+ channelId: params.payload.channel_id,
3675
+ senderId: params.payload.user_id,
3676
+ senderName: params.userName,
3677
+ kind,
3678
+ chatType,
3679
+ channelName,
3680
+ channelDisplay,
3681
+ roomLabel,
3682
+ teamId,
3683
+ postId: params.payload.post_id,
3684
+ effectiveReplyToId: threadContext.effectiveReplyToId,
3685
+ deliverReplies: true
3686
+ });
3687
+ const updatedModel = resolveMattermostModelPickerCurrentModel({
3688
+ cfg,
3689
+ route: modelSessionRoute,
3690
+ data,
3691
+ skipCache: true
3692
+ });
3693
+ const view = renderMattermostModelsPickerView({
3694
+ ownerUserId: pickerState.ownerUserId,
3695
+ data,
3696
+ provider: pickerState.provider,
3697
+ page: pickerState.page,
3698
+ currentModel: updatedModel
3699
+ });
3700
+ await updateModelPickerPost({
3701
+ channelId: params.payload.channel_id,
3702
+ postId: params.payload.post_id,
3703
+ message: view.text,
3704
+ buttons: view.buttons
3705
+ });
3706
+ } catch (err) {
3707
+ runtime.error?.(`mattermost model picker select failed: ${String(err)}`);
3708
+ }
3709
+ })();
3710
+ return {};
3711
+ }
3712
+ const handlePost = async (post, payload, messageIds) => {
3713
+ const channelId = post.channel_id ?? payload.data?.channel_id ?? payload.broadcast?.channel_id;
3714
+ if (!channelId) {
3715
+ logVerboseMessage("mattermost: drop post (missing channel id)");
3716
+ return;
3717
+ }
3718
+ const allMessageIds = messageIds?.length ? messageIds : post.id ? [post.id] : [];
3719
+ if (allMessageIds.length === 0) {
3720
+ logVerboseMessage("mattermost: drop post (missing message id)");
3721
+ return;
3722
+ }
3723
+ const dedupeEntries = allMessageIds.map((id) => recentInboundMessages.check(`${account.accountId}:${id}`));
3724
+ if (dedupeEntries.length > 0 && dedupeEntries.every(Boolean)) {
3725
+ logVerboseMessage(`mattermost: drop post (dedupe account=${account.accountId} ids=${allMessageIds.length})`);
3726
+ return;
3727
+ }
3728
+ const senderId = post.user_id ?? payload.broadcast?.user_id;
3729
+ if (!senderId) {
3730
+ logVerboseMessage("mattermost: drop post (missing sender id)");
3731
+ return;
3732
+ }
3733
+ if (senderId === botUserId) {
3734
+ logVerboseMessage(`mattermost: drop post (self sender=${senderId})`);
3735
+ return;
3736
+ }
3737
+ if (isSystemPost(post)) {
3738
+ logVerboseMessage(`mattermost: drop post (system post type=${post.type ?? "unknown"})`);
3739
+ return;
3740
+ }
3741
+ const channelInfo = await resolveChannelInfo(channelId);
3742
+ const kind = mapMattermostChannelTypeToChatType(payload.data?.channel_type ?? channelInfo?.type ?? void 0);
3743
+ const chatType = channelChatType(kind);
3744
+ const senderName = payload.data?.sender_name?.trim() || (await resolveUserInfo(senderId))?.username?.trim() || senderId;
3745
+ const rawText = post.message?.trim() || "";
3746
+ const dmPolicy = account.config.dmPolicy ?? "pairing";
3747
+ const normalizedAllowFrom = normalizeMattermostAllowList(account.config.allowFrom ?? []);
3748
+ const normalizedGroupAllowFrom = normalizeMattermostAllowList(account.config.groupAllowFrom ?? []);
3749
+ const storeAllowFrom = normalizeMattermostAllowList(await readStoreAllowFromForDmPolicy({
3750
+ provider: "mattermost",
3751
+ accountId: account.accountId,
3752
+ dmPolicy,
3753
+ readStore: pairing.readStoreForDmPolicy
3754
+ }));
3755
+ const accessDecision = resolveDmGroupAccessWithLists({
3756
+ isGroup: kind !== "direct",
3757
+ dmPolicy,
3758
+ groupPolicy,
3759
+ allowFrom: normalizedAllowFrom,
3760
+ groupAllowFrom: normalizedGroupAllowFrom,
3761
+ storeAllowFrom,
3762
+ isSenderAllowed: (allowFrom) => isMattermostSenderAllowed({
3763
+ senderId,
3764
+ senderName,
3765
+ allowFrom,
3766
+ allowNameMatching
3767
+ })
3768
+ });
3769
+ const effectiveAllowFrom = accessDecision.effectiveAllowFrom;
3770
+ const effectiveGroupAllowFrom = accessDecision.effectiveGroupAllowFrom;
3771
+ const allowTextCommands = core.channel.commands.shouldHandleTextCommands({
3772
+ cfg,
3773
+ surface: "mattermost"
3774
+ });
3775
+ const hasControlCommand = core.channel.text.hasControlCommand(rawText, cfg);
3776
+ const isControlCommand = allowTextCommands && hasControlCommand;
3777
+ const useAccessGroups = cfg.commands?.useAccessGroups !== false;
3778
+ const commandDmAllowFrom = kind === "direct" ? effectiveAllowFrom : normalizedAllowFrom;
3779
+ const senderAllowedForCommands = isMattermostSenderAllowed({
3780
+ senderId,
3781
+ senderName,
3782
+ allowFrom: commandDmAllowFrom,
3783
+ allowNameMatching
3784
+ });
3785
+ const groupAllowedForCommands = isMattermostSenderAllowed({
3786
+ senderId,
3787
+ senderName,
3788
+ allowFrom: effectiveGroupAllowFrom,
3789
+ allowNameMatching
3790
+ });
3791
+ const commandGate = resolveControlCommandGate({
3792
+ useAccessGroups,
3793
+ authorizers: [{
3794
+ configured: commandDmAllowFrom.length > 0,
3795
+ allowed: senderAllowedForCommands
3796
+ }, {
3797
+ configured: effectiveGroupAllowFrom.length > 0,
3798
+ allowed: groupAllowedForCommands
3799
+ }],
3800
+ allowTextCommands,
3801
+ hasControlCommand
3802
+ });
3803
+ const commandAuthorized = commandGate.commandAuthorized;
3804
+ if (accessDecision.decision !== "allow") {
3805
+ if (kind === "direct") {
3806
+ if (accessDecision.reasonCode === DM_GROUP_ACCESS_REASON.DM_POLICY_DISABLED) {
3807
+ logVerboseMessage(`mattermost: drop dm (dmPolicy=disabled sender=${senderId})`);
3808
+ return;
3809
+ }
3810
+ if (accessDecision.decision === "pairing") {
3811
+ const { code, created } = await pairing.upsertPairingRequest({
3812
+ id: senderId,
3813
+ meta: { name: senderName }
3814
+ });
3815
+ logVerboseMessage(`mattermost: pairing request sender=${senderId} created=${created}`);
3816
+ if (created) try {
3817
+ await sendMessageMattermost(`user:${senderId}`, core.channel.pairing.buildPairingReply({
3818
+ channel: "mattermost",
3819
+ idLine: `Your Mattermost user id: ${senderId}`,
3820
+ code
3821
+ }), { accountId: account.accountId });
3822
+ opts.statusSink?.({ lastOutboundAt: Date.now() });
3823
+ } catch (err) {
3824
+ logVerboseMessage(`mattermost: pairing reply failed for ${senderId}: ${String(err)}`);
3825
+ }
3826
+ return;
3827
+ }
3828
+ logVerboseMessage(`mattermost: drop dm sender=${senderId} (dmPolicy=${dmPolicy})`);
3829
+ return;
3830
+ }
3831
+ if (accessDecision.reasonCode === DM_GROUP_ACCESS_REASON.GROUP_POLICY_DISABLED) {
3832
+ logVerboseMessage("mattermost: drop group message (groupPolicy=disabled)");
3833
+ return;
3834
+ }
3835
+ if (accessDecision.reasonCode === DM_GROUP_ACCESS_REASON.GROUP_POLICY_EMPTY_ALLOWLIST) {
3836
+ logVerboseMessage("mattermost: drop group message (no group allowlist)");
3837
+ return;
3838
+ }
3839
+ if (accessDecision.reasonCode === DM_GROUP_ACCESS_REASON.GROUP_POLICY_NOT_ALLOWLISTED) {
3840
+ logVerboseMessage(`mattermost: drop group sender=${senderId} (not in groupAllowFrom)`);
3841
+ return;
3842
+ }
3843
+ logVerboseMessage(`mattermost: drop group message (groupPolicy=${groupPolicy} reason=${accessDecision.reason})`);
3844
+ return;
3845
+ }
3846
+ if (kind !== "direct" && commandGate.shouldBlock) {
3847
+ logInboundDrop({
3848
+ log: logVerboseMessage,
3849
+ channel: "mattermost",
3850
+ reason: "control command (unauthorized)",
3851
+ target: senderId
3852
+ });
3853
+ return;
3854
+ }
3855
+ const teamId = payload.data?.team_id ?? channelInfo?.team_id ?? void 0;
3856
+ const channelName = payload.data?.channel_name ?? channelInfo?.name ?? "";
3857
+ const channelDisplay = payload.data?.channel_display_name ?? channelInfo?.display_name ?? channelName;
3858
+ const roomLabel = channelName ? `#${channelName}` : channelDisplay || `#${channelId}`;
3859
+ const route = core.channel.routing.resolveAgentRoute({
3860
+ cfg,
3861
+ channel: "mattermost",
3862
+ accountId: account.accountId,
3863
+ teamId,
3864
+ peer: {
3865
+ kind,
3866
+ id: kind === "direct" ? senderId : channelId
3867
+ }
3868
+ });
3869
+ const baseSessionKey = route.sessionKey;
3870
+ const threadRootId = post.root_id?.trim() || void 0;
3871
+ const replyToMode = resolveMattermostReplyToMode(account, kind);
3872
+ const { effectiveReplyToId, sessionKey, parentSessionKey } = resolveMattermostThreadSessionContext({
3873
+ baseSessionKey,
3874
+ kind,
3875
+ postId: post.id,
3876
+ replyToMode,
3877
+ threadRootId
3878
+ });
3879
+ const historyKey = kind === "direct" ? null : sessionKey;
3880
+ const mentionRegexes = core.channel.mentions.buildMentionRegexes(cfg, route.agentId);
3881
+ const wasMentioned = kind !== "direct" && ((botUsername ? rawText.toLowerCase().includes(`@${botUsername.toLowerCase()}`) : false) || core.channel.mentions.matchesMentionPatterns(rawText, mentionRegexes));
3882
+ const pendingBody = rawText || (post.file_ids?.length ? `[Mattermost ${post.file_ids.length === 1 ? "file" : "files"}]` : "");
3883
+ const pendingSender = senderName;
3884
+ const recordPendingHistory = () => {
3885
+ const trimmed = pendingBody.trim();
3886
+ recordPendingHistoryEntryIfEnabled({
3887
+ historyMap: channelHistories,
3888
+ limit: historyLimit,
3889
+ historyKey: historyKey ?? "",
3890
+ entry: historyKey && trimmed ? {
3891
+ sender: pendingSender,
3892
+ body: trimmed,
3893
+ timestamp: typeof post.create_at === "number" ? post.create_at : void 0,
3894
+ messageId: post.id ?? void 0
3895
+ } : null
3896
+ });
3897
+ };
3898
+ const oncharEnabled = account.chatmode === "onchar" && kind !== "direct";
3899
+ const oncharPrefixes = oncharEnabled ? resolveOncharPrefixes(account.oncharPrefixes) : [];
3900
+ const oncharResult = oncharEnabled ? stripOncharPrefix(rawText, oncharPrefixes) : {
3901
+ triggered: false,
3902
+ stripped: rawText
3903
+ };
3904
+ const oncharTriggered = oncharResult.triggered;
3905
+ const canDetectMention = Boolean(botUsername) || mentionRegexes.length > 0;
3906
+ const mentionDecision = evaluateMattermostMentionGate({
3907
+ kind,
3908
+ cfg,
3909
+ accountId: account.accountId,
3910
+ channelId,
3911
+ threadRootId,
3912
+ requireMentionOverride: account.requireMention,
3913
+ resolveRequireMention: core.channel.groups.resolveRequireMention,
3914
+ wasMentioned,
3915
+ isControlCommand,
3916
+ commandAuthorized,
3917
+ oncharEnabled,
3918
+ oncharTriggered,
3919
+ canDetectMention
3920
+ });
3921
+ const { shouldRequireMention, shouldBypassMention } = mentionDecision;
3922
+ if (mentionDecision.dropReason === "onchar-not-triggered") {
3923
+ logVerboseMessage(`mattermost: drop group message (onchar not triggered channel=${channelId} sender=${senderId})`);
3924
+ recordPendingHistory();
3925
+ return;
3926
+ }
3927
+ if (mentionDecision.dropReason === "missing-mention") {
3928
+ logVerboseMessage(`mattermost: drop group message (missing mention channel=${channelId} sender=${senderId} requireMention=${shouldRequireMention} bypass=${shouldBypassMention} canDetectMention=${canDetectMention})`);
3929
+ recordPendingHistory();
3930
+ return;
3931
+ }
3932
+ const mediaList = await resolveMattermostMedia(post.file_ids);
3933
+ const mediaPlaceholder = buildMattermostAttachmentPlaceholder(mediaList);
3934
+ const bodyText = normalizeMention([oncharTriggered ? oncharResult.stripped : rawText, mediaPlaceholder].filter(Boolean).join("\n").trim(), botUsername);
3935
+ if (!bodyText) {
3936
+ logVerboseMessage(`mattermost: drop group message (empty body after normalization channel=${channelId} sender=${senderId})`);
3937
+ return;
3938
+ }
3939
+ core.channel.activity.record({
3940
+ channel: "mattermost",
3941
+ accountId: account.accountId,
3942
+ direction: "inbound"
3943
+ });
3944
+ const fromLabel = formatInboundFromLabel({
3945
+ isGroup: kind !== "direct",
3946
+ groupLabel: channelDisplay || roomLabel,
3947
+ groupId: channelId,
3948
+ groupFallback: roomLabel || "Channel",
3949
+ directLabel: senderName,
3950
+ directId: senderId
3951
+ });
3952
+ const preview = bodyText.replace(/\s+/g, " ").slice(0, 160);
3953
+ const inboundLabel = kind === "direct" ? `Mattermost DM from ${senderName}` : `Mattermost message in ${roomLabel} from ${senderName}`;
3954
+ core.system.enqueueSystemEvent(`${inboundLabel}: ${preview}`, {
3955
+ sessionKey,
3956
+ contextKey: `mattermost:message:${channelId}:${post.id ?? "unknown"}`
3957
+ });
3958
+ const textWithId = `${bodyText}\n[mattermost message id: ${post.id ?? "unknown"} channel: ${channelId}]`;
3959
+ let combinedBody = core.channel.reply.formatInboundEnvelope({
3960
+ channel: "Mattermost",
3961
+ from: fromLabel,
3962
+ timestamp: typeof post.create_at === "number" ? post.create_at : void 0,
3963
+ body: textWithId,
3964
+ chatType,
3965
+ sender: {
3966
+ name: senderName,
3967
+ id: senderId
3968
+ }
3969
+ });
3970
+ if (historyKey) combinedBody = buildPendingHistoryContextFromMap({
3971
+ historyMap: channelHistories,
3972
+ historyKey,
3973
+ limit: historyLimit,
3974
+ currentMessage: combinedBody,
3975
+ formatEntry: (entry) => core.channel.reply.formatInboundEnvelope({
3976
+ channel: "Mattermost",
3977
+ from: fromLabel,
3978
+ timestamp: entry.timestamp,
3979
+ body: `${entry.body}${entry.messageId ? ` [id:${entry.messageId} channel:${channelId}]` : ""}`,
3980
+ chatType,
3981
+ senderLabel: entry.sender
3982
+ })
3983
+ });
3984
+ const to = kind === "direct" ? `user:${senderId}` : `channel:${channelId}`;
3985
+ const mediaPayload = buildAgentMediaPayload(mediaList);
3986
+ const commandBody = rawText.trim();
3987
+ const inboundHistory = historyKey && historyLimit > 0 ? (channelHistories.get(historyKey) ?? []).map((entry) => ({
3988
+ sender: entry.sender,
3989
+ body: entry.body,
3990
+ timestamp: entry.timestamp
3991
+ })) : void 0;
3992
+ const ctxPayload = core.channel.reply.finalizeInboundContext({
3993
+ Body: combinedBody,
3994
+ BodyForAgent: bodyText,
3995
+ InboundHistory: inboundHistory,
3996
+ RawBody: bodyText,
3997
+ CommandBody: commandBody,
3998
+ BodyForCommands: commandBody,
3999
+ From: kind === "direct" ? `mattermost:${senderId}` : kind === "group" ? `mattermost:group:${channelId}` : `mattermost:channel:${channelId}`,
4000
+ To: to,
4001
+ SessionKey: sessionKey,
4002
+ ParentSessionKey: parentSessionKey,
4003
+ AccountId: route.accountId,
4004
+ ChatType: chatType,
4005
+ ConversationLabel: fromLabel,
4006
+ GroupSubject: kind !== "direct" ? channelDisplay || roomLabel : void 0,
4007
+ GroupChannel: channelName ? `#${channelName}` : void 0,
4008
+ GroupSpace: teamId,
4009
+ SenderName: senderName,
4010
+ SenderId: senderId,
4011
+ Provider: "mattermost",
4012
+ Surface: "mattermost",
4013
+ MessageSid: post.id ?? void 0,
4014
+ MessageSids: allMessageIds.length > 1 ? allMessageIds : void 0,
4015
+ MessageSidFirst: allMessageIds.length > 1 ? allMessageIds[0] : void 0,
4016
+ MessageSidLast: allMessageIds.length > 1 ? allMessageIds[allMessageIds.length - 1] : void 0,
4017
+ ReplyToId: effectiveReplyToId,
4018
+ MessageThreadId: effectiveReplyToId,
4019
+ Timestamp: typeof post.create_at === "number" ? post.create_at : void 0,
4020
+ WasMentioned: kind !== "direct" ? mentionDecision.effectiveWasMentioned : void 0,
4021
+ CommandAuthorized: commandAuthorized,
4022
+ OriginatingChannel: "mattermost",
4023
+ OriginatingTo: to,
4024
+ ...mediaPayload
4025
+ });
4026
+ if (kind === "direct") {
4027
+ const sessionCfg = cfg.session;
4028
+ const storePath = core.channel.session.resolveStorePath(sessionCfg?.store, { agentId: route.agentId });
4029
+ await core.channel.session.updateLastRoute({
4030
+ storePath,
4031
+ sessionKey: route.mainSessionKey,
4032
+ deliveryContext: {
4033
+ channel: "mattermost",
4034
+ to,
4035
+ accountId: route.accountId
4036
+ }
4037
+ });
4038
+ }
4039
+ const previewLine = bodyText.slice(0, 200).replace(/\n/g, "\\n");
4040
+ logVerboseMessage(`mattermost inbound: from=${ctxPayload.From} len=${bodyText.length} preview="${previewLine}"`);
4041
+ const textLimit = core.channel.text.resolveTextChunkLimit(cfg, "mattermost", account.accountId, { fallbackLimit: account.textChunkLimit ?? 4e3 });
4042
+ const tableMode = core.channel.text.resolveMarkdownTableMode({
4043
+ cfg,
4044
+ channel: "mattermost",
4045
+ accountId: account.accountId
4046
+ });
4047
+ const { onModelSelected, typingCallbacks, ...replyPipeline } = createChannelReplyPipeline({
4048
+ cfg,
4049
+ agentId: route.agentId,
4050
+ channel: "mattermost",
4051
+ accountId: account.accountId,
4052
+ typing: {
4053
+ start: () => sendTypingIndicator(channelId, effectiveReplyToId),
4054
+ onStartError: (err) => {
4055
+ logTypingFailure({
4056
+ log: (message) => logger.debug?.(message),
4057
+ channel: "mattermost",
4058
+ target: channelId,
4059
+ error: err
4060
+ });
4061
+ }
4062
+ }
4063
+ });
4064
+ const { dispatcher, replyOptions, markDispatchIdle } = core.channel.reply.createReplyDispatcherWithTyping({
4065
+ ...replyPipeline,
4066
+ humanDelay: core.channel.reply.resolveHumanDelayConfig(cfg, route.agentId),
4067
+ typingCallbacks,
4068
+ deliver: async (payload) => {
4069
+ await deliverMattermostReplyPayload({
4070
+ core,
4071
+ cfg,
4072
+ payload,
4073
+ to,
4074
+ accountId: account.accountId,
4075
+ agentId: route.agentId,
4076
+ replyToId: resolveMattermostReplyRootId({
4077
+ threadRootId: effectiveReplyToId,
4078
+ replyToId: payload.replyToId
4079
+ }),
4080
+ textLimit,
4081
+ tableMode,
4082
+ sendMessage: sendMessageMattermost
4083
+ });
4084
+ runtime.log?.(`delivered reply to ${to}`);
4085
+ },
4086
+ onError: (err, info) => {
4087
+ runtime.error?.(`mattermost ${info.kind} reply failed: ${String(err)}`);
4088
+ }
4089
+ });
4090
+ await core.channel.reply.withReplyDispatcher({
4091
+ dispatcher,
4092
+ onSettled: () => {
4093
+ markDispatchIdle();
4094
+ },
4095
+ run: () => core.channel.reply.dispatchReplyFromConfig({
4096
+ ctx: ctxPayload,
4097
+ cfg,
4098
+ dispatcher,
4099
+ replyOptions: {
4100
+ ...replyOptions,
4101
+ disableBlockStreaming: typeof account.blockStreaming === "boolean" ? !account.blockStreaming : void 0,
4102
+ onModelSelected
4103
+ }
4104
+ })
4105
+ });
4106
+ if (historyKey) clearHistoryEntriesIfEnabled({
4107
+ historyMap: channelHistories,
4108
+ historyKey,
4109
+ limit: historyLimit
4110
+ });
4111
+ };
4112
+ const handleReactionEvent = async (payload) => {
4113
+ const reactionData = payload.data?.reaction;
4114
+ if (!reactionData) return;
4115
+ let reaction = null;
4116
+ if (typeof reactionData === "string") try {
4117
+ reaction = JSON.parse(reactionData);
4118
+ } catch {
4119
+ return;
4120
+ }
4121
+ else if (typeof reactionData === "object") reaction = reactionData;
4122
+ if (!reaction) return;
4123
+ const userId = reaction.user_id?.trim();
4124
+ const postId = reaction.post_id?.trim();
4125
+ const emojiName = reaction.emoji_name?.trim();
4126
+ if (!userId || !postId || !emojiName) return;
4127
+ if (userId === botUserId) return;
4128
+ const action = payload.event === "reaction_removed" ? "removed" : "added";
4129
+ const senderName = (await resolveUserInfo(userId))?.username?.trim() || userId;
4130
+ const channelId = payload.broadcast?.channel_id;
4131
+ if (!channelId) {
4132
+ logVerboseMessage(`mattermost: drop reaction (no channel_id in broadcast, cannot enforce policy)`);
4133
+ return;
4134
+ }
4135
+ const channelInfo = await resolveChannelInfo(channelId);
4136
+ if (!channelInfo?.type) {
4137
+ logVerboseMessage(`mattermost: drop reaction (cannot resolve channel type for ${channelId})`);
4138
+ return;
4139
+ }
4140
+ const kind = mapMattermostChannelTypeToChatType(channelInfo.type);
4141
+ const dmPolicy = account.config.dmPolicy ?? "pairing";
4142
+ const storeAllowFrom = normalizeMattermostAllowList(await readStoreAllowFromForDmPolicy({
4143
+ provider: "mattermost",
4144
+ accountId: account.accountId,
4145
+ dmPolicy,
4146
+ readStore: pairing.readStoreForDmPolicy
4147
+ }));
4148
+ const reactionAccess = resolveDmGroupAccessWithLists({
4149
+ isGroup: kind !== "direct",
4150
+ dmPolicy,
4151
+ groupPolicy,
4152
+ allowFrom: normalizeMattermostAllowList(account.config.allowFrom ?? []),
4153
+ groupAllowFrom: normalizeMattermostAllowList(account.config.groupAllowFrom ?? []),
4154
+ storeAllowFrom,
4155
+ isSenderAllowed: (allowFrom) => isMattermostSenderAllowed({
4156
+ senderId: userId,
4157
+ senderName,
4158
+ allowFrom,
4159
+ allowNameMatching
4160
+ })
4161
+ });
4162
+ if (reactionAccess.decision !== "allow") {
4163
+ if (kind === "direct") logVerboseMessage(`mattermost: drop reaction (dmPolicy=${dmPolicy} sender=${userId} reason=${reactionAccess.reason})`);
4164
+ else logVerboseMessage(`mattermost: drop reaction (groupPolicy=${groupPolicy} sender=${userId} reason=${reactionAccess.reason} channel=${channelId})`);
4165
+ return;
4166
+ }
4167
+ const teamId = channelInfo?.team_id ?? void 0;
4168
+ const sessionKey = core.channel.routing.resolveAgentRoute({
4169
+ cfg,
4170
+ channel: "mattermost",
4171
+ accountId: account.accountId,
4172
+ teamId,
4173
+ peer: {
4174
+ kind,
4175
+ id: kind === "direct" ? userId : channelId
4176
+ }
4177
+ }).sessionKey;
4178
+ const eventText = `Mattermost reaction ${action}: :${emojiName}: by @${senderName} on post ${postId} in channel ${channelId}`;
4179
+ core.system.enqueueSystemEvent(eventText, {
4180
+ sessionKey,
4181
+ contextKey: `mattermost:reaction:${postId}:${emojiName}:${userId}:${action}`
4182
+ });
4183
+ logVerboseMessage(`mattermost reaction: ${action} :${emojiName}: by ${senderName} on ${postId}`);
4184
+ };
4185
+ const inboundDebounceMs = core.channel.debounce.resolveInboundDebounceMs({
4186
+ cfg,
4187
+ channel: "mattermost"
4188
+ });
4189
+ const debouncer = core.channel.debounce.createInboundDebouncer({
4190
+ debounceMs: inboundDebounceMs,
4191
+ buildKey: (entry) => {
4192
+ const channelId = entry.post.channel_id ?? entry.payload.data?.channel_id ?? entry.payload.broadcast?.channel_id;
4193
+ if (!channelId) return null;
4194
+ const threadId = entry.post.root_id?.trim();
4195
+ const threadKey = threadId ? `thread:${threadId}` : "channel";
4196
+ return `mattermost:${account.accountId}:${channelId}:${threadKey}`;
4197
+ },
4198
+ shouldDebounce: (entry) => {
4199
+ if (entry.post.file_ids && entry.post.file_ids.length > 0) return false;
4200
+ const text = entry.post.message?.trim() ?? "";
4201
+ if (!text) return false;
4202
+ return !core.channel.text.hasControlCommand(text, cfg);
4203
+ },
4204
+ onFlush: async (entries) => {
4205
+ const last = entries.at(-1);
4206
+ if (!last) return;
4207
+ if (entries.length === 1) {
4208
+ await handlePost(last.post, last.payload);
4209
+ return;
4210
+ }
4211
+ const combinedText = entries.map((entry) => entry.post.message?.trim() ?? "").filter(Boolean).join("\n");
4212
+ const mergedPost = {
4213
+ ...last.post,
4214
+ message: combinedText,
4215
+ file_ids: []
4216
+ };
4217
+ const ids = entries.map((entry) => entry.post.id).filter(Boolean);
4218
+ await handlePost(mergedPost, last.payload, ids.length > 0 ? ids : void 0);
4219
+ },
4220
+ onError: (err) => {
4221
+ runtime.error?.(`mattermost debounce flush failed: ${String(err)}`);
4222
+ }
4223
+ });
4224
+ const wsUrl = buildMattermostWsUrl(baseUrl);
4225
+ let seq = 1;
4226
+ const connectOnce = createMattermostConnectOnce({
4227
+ wsUrl,
4228
+ botToken,
4229
+ abortSignal: opts.abortSignal,
4230
+ statusSink: opts.statusSink,
4231
+ runtime,
4232
+ webSocketFactory: opts.webSocketFactory,
4233
+ nextSeq: () => seq++,
4234
+ onPosted: async (post, payload) => {
4235
+ await debouncer.enqueue({
4236
+ post,
4237
+ payload
4238
+ });
4239
+ },
4240
+ onReaction: async (payload) => {
4241
+ await handleReactionEvent(payload);
4242
+ }
4243
+ });
4244
+ let slashShutdownCleanup = null;
4245
+ if (slashEnabled) {
4246
+ const runAbortCleanup = () => {
4247
+ if (slashShutdownCleanup) return;
4248
+ const commands = getSlashCommandState(account.accountId)?.registeredCommands ?? [];
4249
+ deactivateSlashCommands(account.accountId);
4250
+ slashShutdownCleanup = cleanupSlashCommands({
4251
+ client,
4252
+ commands,
4253
+ log: (msg) => runtime.log?.(msg)
4254
+ }).catch((err) => {
4255
+ runtime.error?.(`mattermost: slash cleanup failed: ${String(err)}`);
4256
+ });
4257
+ };
4258
+ if (opts.abortSignal?.aborted) runAbortCleanup();
4259
+ else opts.abortSignal?.addEventListener("abort", runAbortCleanup, { once: true });
4260
+ }
4261
+ try {
4262
+ await runWithReconnect(connectOnce, {
4263
+ abortSignal: opts.abortSignal,
4264
+ jitterRatio: .2,
4265
+ onError: (err) => {
4266
+ runtime.error?.(`mattermost connection failed: ${String(err)}`);
4267
+ opts.statusSink?.({
4268
+ lastError: String(err),
4269
+ connected: false
4270
+ });
4271
+ },
4272
+ onReconnect: (delayMs) => {
4273
+ runtime.log?.(`mattermost reconnecting in ${Math.round(delayMs / 1e3)}s`);
4274
+ }
4275
+ });
4276
+ } finally {
4277
+ unregisterInteractions?.();
4278
+ }
4279
+ if (slashShutdownCleanup) await slashShutdownCleanup;
4280
+ }
4281
+ //#endregion
4282
+ //#region extensions/mattermost/src/mattermost/probe.ts
4283
+ async function probeMattermost(baseUrl, botToken, timeoutMs = 2500) {
4284
+ const normalized = normalizeMattermostBaseUrl(baseUrl);
4285
+ if (!normalized) return {
4286
+ ok: false,
4287
+ error: "baseUrl missing"
4288
+ };
4289
+ const url = `${normalized}/api/v4/users/me`;
4290
+ const start = Date.now();
4291
+ const controller = timeoutMs > 0 ? new AbortController() : void 0;
4292
+ let timer = null;
4293
+ if (controller) timer = setTimeout(() => controller.abort(), timeoutMs);
4294
+ try {
4295
+ const res = await fetch(url, {
4296
+ headers: { Authorization: `Bearer ${botToken}` },
4297
+ signal: controller?.signal
4298
+ });
4299
+ const elapsedMs = Date.now() - start;
4300
+ if (!res.ok) {
4301
+ const detail = await readMattermostError(res);
4302
+ return {
4303
+ ok: false,
4304
+ status: res.status,
4305
+ error: detail || res.statusText,
4306
+ elapsedMs
4307
+ };
4308
+ }
4309
+ const bot = await res.json();
4310
+ return {
4311
+ ok: true,
4312
+ status: res.status,
4313
+ elapsedMs,
4314
+ bot
4315
+ };
4316
+ } catch (err) {
4317
+ return {
4318
+ ok: false,
4319
+ status: null,
4320
+ error: err instanceof Error ? err.message : String(err),
4321
+ elapsedMs: Date.now() - start
4322
+ };
4323
+ } finally {
4324
+ if (timer) clearTimeout(timer);
4325
+ }
4326
+ }
4327
+ //#endregion
4328
+ //#region extensions/mattermost/src/mattermost/reactions.ts
4329
+ const BOT_USER_CACHE_TTL_MS = 10 * 6e4;
4330
+ const botUserIdCache = /* @__PURE__ */ new Map();
4331
+ async function resolveBotUserId(client, cacheKey) {
4332
+ const cached = botUserIdCache.get(cacheKey);
4333
+ if (cached && cached.expiresAt > Date.now()) return cached.userId;
4334
+ const userId = (await fetchMattermostMe(client))?.id?.trim();
4335
+ if (!userId) return null;
4336
+ botUserIdCache.set(cacheKey, {
4337
+ userId,
4338
+ expiresAt: Date.now() + BOT_USER_CACHE_TTL_MS
4339
+ });
4340
+ return userId;
4341
+ }
4342
+ async function addMattermostReaction(params) {
4343
+ return runMattermostReaction(params, {
4344
+ action: "add",
4345
+ mutation: createReaction
4346
+ });
4347
+ }
4348
+ async function removeMattermostReaction(params) {
4349
+ return runMattermostReaction(params, {
4350
+ action: "remove",
4351
+ mutation: deleteReaction
4352
+ });
4353
+ }
4354
+ async function runMattermostReaction(params, options) {
4355
+ const resolved = resolveMattermostAccount({
4356
+ cfg: params.cfg,
4357
+ accountId: params.accountId
4358
+ });
4359
+ const baseUrl = resolved.baseUrl?.trim();
4360
+ const botToken = resolved.botToken?.trim();
4361
+ if (!baseUrl || !botToken) return {
4362
+ ok: false,
4363
+ error: "Mattermost botToken/baseUrl missing."
4364
+ };
4365
+ const client = createMattermostClient({
4366
+ baseUrl,
4367
+ botToken,
4368
+ fetchImpl: params.fetchImpl
4369
+ });
4370
+ const userId = await resolveBotUserId(client, `${baseUrl}:${botToken}`);
4371
+ if (!userId) return {
4372
+ ok: false,
4373
+ error: "Mattermost reactions failed: could not resolve bot user id."
4374
+ };
4375
+ try {
4376
+ await options.mutation(client, {
4377
+ userId,
4378
+ postId: params.postId,
4379
+ emojiName: params.emojiName
4380
+ });
4381
+ } catch (err) {
4382
+ return {
4383
+ ok: false,
4384
+ error: `Mattermost ${options.action} reaction failed: ${String(err)}`
4385
+ };
4386
+ }
4387
+ return { ok: true };
4388
+ }
4389
+ async function createReaction(client, params) {
4390
+ await client.request("/reactions", {
4391
+ method: "POST",
4392
+ body: JSON.stringify({
4393
+ user_id: params.userId,
4394
+ post_id: params.postId,
4395
+ emoji_name: params.emojiName
4396
+ })
4397
+ });
4398
+ }
4399
+ async function deleteReaction(client, params) {
4400
+ const emoji = encodeURIComponent(params.emojiName);
4401
+ await client.request(`/users/${params.userId}/posts/${params.postId}/reactions/${emoji}`, { method: "DELETE" });
4402
+ }
4403
+ //#endregion
4404
+ //#region extensions/mattermost/src/normalize.ts
4405
+ function normalizeMattermostMessagingTarget(raw) {
4406
+ const trimmed = raw.trim();
4407
+ if (!trimmed) return;
4408
+ const lower = trimmed.toLowerCase();
4409
+ if (lower.startsWith("channel:")) {
4410
+ const id = trimmed.slice(8).trim();
4411
+ return id ? `channel:${id}` : void 0;
4412
+ }
4413
+ if (lower.startsWith("group:")) {
4414
+ const id = trimmed.slice(6).trim();
4415
+ return id ? `channel:${id}` : void 0;
4416
+ }
4417
+ if (lower.startsWith("user:")) {
4418
+ const id = trimmed.slice(5).trim();
4419
+ return id ? `user:${id}` : void 0;
4420
+ }
4421
+ if (lower.startsWith("mattermost:")) {
4422
+ const id = trimmed.slice(11).trim();
4423
+ return id ? `user:${id}` : void 0;
4424
+ }
4425
+ if (trimmed.startsWith("@")) {
4426
+ const id = trimmed.slice(1).trim();
4427
+ return id ? `@${id}` : void 0;
4428
+ }
4429
+ if (trimmed.startsWith("#")) return;
4430
+ }
4431
+ function looksLikeMattermostTargetId(raw, normalized) {
4432
+ const trimmed = raw.trim();
4433
+ if (!trimmed) return false;
4434
+ if (/^(user|channel|group|mattermost):/i.test(trimmed)) return true;
4435
+ if (trimmed.startsWith("@")) return true;
4436
+ return /^[a-z0-9]{26}$/i.test(trimmed) || /^[a-z0-9]{26}__[a-z0-9]{26}$/i.test(trimmed);
4437
+ }
4438
+ //#endregion
4439
+ //#region extensions/mattermost/src/session-route.ts
4440
+ function resolveMattermostOutboundSessionRoute(params) {
4441
+ let trimmed = stripChannelTargetPrefix(params.target, "mattermost");
4442
+ if (!trimmed) return null;
4443
+ const lower = trimmed.toLowerCase();
4444
+ const resolvedKind = params.resolvedTarget?.kind;
4445
+ const isUser = resolvedKind === "user" || resolvedKind !== "channel" && resolvedKind !== "group" && (lower.startsWith("user:") || trimmed.startsWith("@"));
4446
+ if (trimmed.startsWith("@")) trimmed = trimmed.slice(1).trim();
4447
+ const rawId = stripTargetKindPrefix(trimmed);
4448
+ if (!rawId) return null;
4449
+ const baseRoute = buildChannelOutboundSessionRoute({
4450
+ cfg: params.cfg,
4451
+ agentId: params.agentId,
4452
+ channel: "mattermost",
4453
+ accountId: params.accountId,
4454
+ peer: {
4455
+ kind: isUser ? "direct" : "channel",
4456
+ id: rawId
4457
+ },
4458
+ chatType: isUser ? "direct" : "channel",
4459
+ from: isUser ? `mattermost:${rawId}` : `mattermost:channel:${rawId}`,
4460
+ to: isUser ? `user:${rawId}` : `channel:${rawId}`
4461
+ });
4462
+ const threadId = normalizeOutboundThreadId(params.replyToId ?? params.threadId);
4463
+ const threadKeys = resolveThreadSessionKeys$1({
4464
+ baseSessionKey: baseRoute.baseSessionKey,
4465
+ threadId
4466
+ });
4467
+ return {
4468
+ ...baseRoute,
4469
+ sessionKey: threadKeys.sessionKey,
4470
+ ...threadId !== void 0 ? { threadId } : {}
4471
+ };
4472
+ }
4473
+ //#endregion
4474
+ //#region extensions/mattermost/src/setup-core.ts
4475
+ const channel$1 = "mattermost";
4476
+ function isMattermostConfigured(account) {
4477
+ return (Boolean(account.botToken?.trim()) || hasConfiguredSecretInput(account.config.botToken)) && Boolean(account.baseUrl);
4478
+ }
4479
+ function resolveMattermostAccountWithSecrets(cfg, accountId) {
4480
+ return resolveMattermostAccount({
4481
+ cfg,
4482
+ accountId,
4483
+ allowUnresolvedSecretRef: true
4484
+ });
4485
+ }
4486
+ const mattermostSetupAdapter = {
4487
+ resolveAccountId: ({ accountId }) => normalizeAccountId(accountId),
4488
+ applyAccountName: ({ cfg, accountId, name }) => applyAccountNameToChannelSection({
4489
+ cfg,
4490
+ channelKey: channel$1,
4491
+ accountId,
4492
+ name
4493
+ }),
4494
+ validateInput: ({ accountId, input }) => {
4495
+ const token = input.botToken ?? input.token;
4496
+ const baseUrl = normalizeMattermostBaseUrl(input.httpUrl);
4497
+ if (input.useEnv && accountId !== "default") return "Mattermost env vars can only be used for the default account.";
4498
+ if (!input.useEnv && (!token || !baseUrl)) return "Mattermost requires --bot-token and --http-url (or --use-env).";
4499
+ if (input.httpUrl && !baseUrl) return "Mattermost --http-url must include a valid base URL.";
4500
+ return null;
4501
+ },
4502
+ applyAccountConfig: ({ cfg, accountId, input }) => {
4503
+ const token = input.botToken ?? input.token;
4504
+ const baseUrl = normalizeMattermostBaseUrl(input.httpUrl);
4505
+ const namedConfig = applyAccountNameToChannelSection({
4506
+ cfg,
4507
+ channelKey: channel$1,
4508
+ accountId,
4509
+ name: input.name
4510
+ });
4511
+ return applySetupAccountConfigPatch({
4512
+ cfg: accountId !== "default" ? migrateBaseNameToDefaultAccount({
4513
+ cfg: namedConfig,
4514
+ channelKey: channel$1
4515
+ }) : namedConfig,
4516
+ channelKey: channel$1,
4517
+ accountId,
4518
+ patch: input.useEnv ? {} : {
4519
+ ...token ? { botToken: token } : {},
4520
+ ...baseUrl ? { baseUrl } : {}
4521
+ }
4522
+ });
4523
+ }
4524
+ };
4525
+ //#endregion
4526
+ //#region extensions/mattermost/src/setup-surface.ts
4527
+ const channel = "mattermost";
4528
+ const mattermostSetupWizard = {
4529
+ channel,
4530
+ status: {
4531
+ configuredLabel: "configured",
4532
+ unconfiguredLabel: "needs token + url",
4533
+ configuredHint: "configured",
4534
+ unconfiguredHint: "needs setup",
4535
+ configuredScore: 2,
4536
+ unconfiguredScore: 1,
4537
+ resolveConfigured: ({ cfg }) => listMattermostAccountIds(cfg).some((accountId) => isMattermostConfigured(resolveMattermostAccountWithSecrets(cfg, accountId)))
4538
+ },
4539
+ introNote: {
4540
+ title: "Mattermost bot token",
4541
+ lines: [
4542
+ "1) Mattermost System Console -> Integrations -> Bot Accounts",
4543
+ "2) Create a bot + copy its token",
4544
+ "3) Use your server base URL (e.g., https://chat.example.com)",
4545
+ "Tip: the bot must be a member of any channel you want it to monitor.",
4546
+ `Docs: ${formatDocsLink("/mattermost", "mattermost")}`
4547
+ ],
4548
+ shouldShow: ({ cfg, accountId }) => !isMattermostConfigured(resolveMattermostAccountWithSecrets(cfg, accountId))
4549
+ },
4550
+ envShortcut: {
4551
+ prompt: "MATTERMOST_BOT_TOKEN + MATTERMOST_URL detected. Use env vars?",
4552
+ preferredEnvVar: "MATTERMOST_BOT_TOKEN",
4553
+ isAvailable: ({ cfg, accountId }) => {
4554
+ if (accountId !== "default") return false;
4555
+ const resolvedAccount = resolveMattermostAccountWithSecrets(cfg, accountId);
4556
+ const hasConfigValues = hasConfiguredSecretInput(resolvedAccount.config.botToken) || Boolean(resolvedAccount.config.baseUrl?.trim());
4557
+ return Boolean(process.env.MATTERMOST_BOT_TOKEN?.trim() && process.env.MATTERMOST_URL?.trim() && !hasConfigValues);
4558
+ },
4559
+ apply: ({ cfg, accountId }) => applySetupAccountConfigPatch({
4560
+ cfg,
4561
+ channelKey: channel,
4562
+ accountId,
4563
+ patch: {}
4564
+ })
4565
+ },
4566
+ credentials: [{
4567
+ inputKey: "botToken",
4568
+ providerHint: channel,
4569
+ credentialLabel: "bot token",
4570
+ preferredEnvVar: "MATTERMOST_BOT_TOKEN",
4571
+ envPrompt: "MATTERMOST_BOT_TOKEN + MATTERMOST_URL detected. Use env vars?",
4572
+ keepPrompt: "Mattermost bot token already configured. Keep it?",
4573
+ inputPrompt: "Enter Mattermost bot token",
4574
+ inspect: ({ cfg, accountId }) => {
4575
+ const resolvedAccount = resolveMattermostAccountWithSecrets(cfg, accountId);
4576
+ return {
4577
+ accountConfigured: isMattermostConfigured(resolvedAccount),
4578
+ hasConfiguredValue: hasConfiguredSecretInput(resolvedAccount.config.botToken)
4579
+ };
4580
+ }
4581
+ }],
4582
+ textInputs: [{
4583
+ inputKey: "httpUrl",
4584
+ message: "Enter Mattermost base URL",
4585
+ confirmCurrentValue: false,
4586
+ currentValue: ({ cfg, accountId }) => resolveMattermostAccountWithSecrets(cfg, accountId).baseUrl ?? process.env.MATTERMOST_URL?.trim(),
4587
+ initialValue: ({ cfg, accountId }) => resolveMattermostAccountWithSecrets(cfg, accountId).baseUrl ?? process.env.MATTERMOST_URL?.trim(),
4588
+ shouldPrompt: ({ cfg, accountId, credentialValues, currentValue }) => {
4589
+ const resolvedAccount = resolveMattermostAccountWithSecrets(cfg, accountId);
4590
+ const tokenConfigured = Boolean(resolvedAccount.botToken?.trim()) || hasConfiguredSecretInput(resolvedAccount.config.botToken);
4591
+ return Boolean(credentialValues.botToken) || !tokenConfigured || !currentValue;
4592
+ },
4593
+ validate: ({ value }) => normalizeMattermostBaseUrl(value) ? void 0 : "Mattermost base URL must include a valid base URL.",
4594
+ normalizeValue: ({ value }) => normalizeMattermostBaseUrl(value) ?? value.trim()
4595
+ }],
4596
+ disable: (cfg) => ({
4597
+ ...cfg,
4598
+ channels: {
4599
+ ...cfg.channels,
4600
+ mattermost: {
4601
+ ...cfg.channels?.mattermost,
4602
+ enabled: false
4603
+ }
4604
+ }
4605
+ })
4606
+ };
4607
+ //#endregion
4608
+ //#region extensions/mattermost/src/channel.ts
4609
+ const collectMattermostSecurityWarnings = createAllowlistProviderRestrictSendersWarningCollector({
4610
+ providerConfigPresent: (cfg) => cfg.channels?.mattermost !== void 0,
4611
+ resolveGroupPolicy: (account) => account.config.groupPolicy,
4612
+ surface: "Mattermost channels",
4613
+ openScope: "any member",
4614
+ groupPolicyPath: "channels.mattermost.groupPolicy",
4615
+ groupAllowFromPath: "channels.mattermost.groupAllowFrom"
4616
+ });
4617
+ function describeMattermostMessageTool({ cfg }) {
4618
+ const enabledAccounts = listMattermostAccountIds(cfg).map((accountId) => resolveMattermostAccount({
4619
+ cfg,
4620
+ accountId
4621
+ })).filter((account) => account.enabled).filter((account) => Boolean(account.botToken?.trim() && account.baseUrl?.trim()));
4622
+ const actions = [];
4623
+ if (enabledAccounts.length > 0) actions.push("send");
4624
+ const baseReactions = (cfg.channels?.mattermost?.actions)?.reactions;
4625
+ if (enabledAccounts.some((account) => {
4626
+ return (account.config.actions?.reactions ?? baseReactions ?? true) !== false;
4627
+ })) actions.push("react");
4628
+ return {
4629
+ actions,
4630
+ capabilities: enabledAccounts.length > 0 ? ["buttons"] : [],
4631
+ schema: enabledAccounts.length > 0 ? { properties: { buttons: createMessageToolButtonsSchema() } } : null
4632
+ };
4633
+ }
4634
+ const mattermostMessageActions = {
4635
+ describeMessageTool: describeMattermostMessageTool,
4636
+ supportsAction: ({ action }) => {
4637
+ return action === "send" || action === "react";
4638
+ },
4639
+ handleAction: async ({ action, params, cfg, accountId }) => {
4640
+ if (action === "react") {
4641
+ const mmBase = cfg?.channels?.mattermost;
4642
+ const accounts = mmBase?.accounts;
4643
+ const resolvedAccountId = accountId ?? resolveDefaultMattermostAccountId(cfg);
4644
+ const acctActions = (accounts?.[resolvedAccountId])?.actions;
4645
+ const baseActions = mmBase?.actions;
4646
+ if (!(acctActions?.reactions ?? baseActions?.reactions ?? true)) throw new Error("Mattermost reactions are disabled in config");
4647
+ const postId = (typeof params?.messageId === "string" ? params.messageId : typeof params?.postId === "string" ? params.postId : "").trim();
4648
+ if (!postId) throw new Error("Mattermost react requires messageId (post id)");
4649
+ const emojiName = (typeof params?.emoji === "string" ? params.emoji : "").trim().replace(/^:+|:+$/g, "");
4650
+ if (!emojiName) throw new Error("Mattermost react requires emoji");
4651
+ if (params?.remove === true) {
4652
+ const result = await removeMattermostReaction({
4653
+ cfg,
4654
+ postId,
4655
+ emojiName,
4656
+ accountId: resolvedAccountId
4657
+ });
4658
+ if (!result.ok) throw new Error(result.error);
4659
+ return {
4660
+ content: [{
4661
+ type: "text",
4662
+ text: `Removed reaction :${emojiName}: from ${postId}`
4663
+ }],
4664
+ details: {}
4665
+ };
4666
+ }
4667
+ const result = await addMattermostReaction({
4668
+ cfg,
4669
+ postId,
4670
+ emojiName,
4671
+ accountId: resolvedAccountId
4672
+ });
4673
+ if (!result.ok) throw new Error(result.error);
4674
+ return {
4675
+ content: [{
4676
+ type: "text",
4677
+ text: `Reacted with :${emojiName}: on ${postId}`
4678
+ }],
4679
+ details: {}
4680
+ };
4681
+ }
4682
+ if (action !== "send") throw new Error(`Unsupported Mattermost action: ${action}`);
4683
+ const to = typeof params.to === "string" ? params.to.trim() : typeof params.target === "string" ? params.target.trim() : "";
4684
+ if (!to) throw new Error("Mattermost send requires a target (to).");
4685
+ const message = typeof params.message === "string" ? params.message : "";
4686
+ const replyToId = readMattermostReplyToId(params);
4687
+ const resolvedAccountId = accountId || void 0;
4688
+ const mediaUrl = typeof params.media === "string" ? params.media.trim() || void 0 : void 0;
4689
+ const result = await sendMessageMattermost(to, message, {
4690
+ accountId: resolvedAccountId,
4691
+ replyToId,
4692
+ buttons: Array.isArray(params.buttons) ? params.buttons : void 0,
4693
+ attachmentText: typeof params.attachmentText === "string" ? params.attachmentText : void 0,
4694
+ mediaUrl
4695
+ });
4696
+ return {
4697
+ content: [{
4698
+ type: "text",
4699
+ text: JSON.stringify({
4700
+ ok: true,
4701
+ channel: "mattermost",
4702
+ messageId: result.messageId,
4703
+ channelId: result.channelId
4704
+ })
4705
+ }],
4706
+ details: {}
4707
+ };
4708
+ }
4709
+ };
4710
+ const meta = {
4711
+ id: "mattermost",
4712
+ label: "Mattermost",
4713
+ selectionLabel: "Mattermost (plugin)",
4714
+ detailLabel: "Mattermost Bot",
4715
+ docsPath: "/channels/mattermost",
4716
+ docsLabel: "mattermost",
4717
+ blurb: "self-hosted Slack-style chat; install the plugin to enable.",
4718
+ systemImage: "bubble.left.and.bubble.right",
4719
+ order: 65,
4720
+ quickstartAllowFrom: true
4721
+ };
4722
+ function readMattermostReplyToId(params) {
4723
+ const readNormalizedValue = (value) => {
4724
+ if (typeof value !== "string") return;
4725
+ return value.trim() || void 0;
4726
+ };
4727
+ return readNormalizedValue(params.replyToId) ?? readNormalizedValue(params.replyTo);
4728
+ }
4729
+ function normalizeAllowEntry(entry) {
4730
+ return entry.trim().replace(/^(mattermost|user):/i, "").replace(/^@/, "").toLowerCase();
4731
+ }
4732
+ function formatAllowEntry(entry) {
4733
+ const trimmed = entry.trim();
4734
+ if (!trimmed) return "";
4735
+ if (trimmed.startsWith("@")) {
4736
+ const username = trimmed.slice(1).trim();
4737
+ return username ? `@${username.toLowerCase()}` : "";
4738
+ }
4739
+ return trimmed.replace(/^(mattermost|user):/i, "").toLowerCase();
4740
+ }
4741
+ const mattermostConfigAdapter = createScopedChannelConfigAdapter({
4742
+ sectionKey: "mattermost",
4743
+ listAccountIds: listMattermostAccountIds,
4744
+ resolveAccount: (cfg, accountId) => resolveMattermostAccount({
4745
+ cfg,
4746
+ accountId
4747
+ }),
4748
+ defaultAccountId: resolveDefaultMattermostAccountId,
4749
+ clearBaseFields: [
4750
+ "botToken",
4751
+ "baseUrl",
4752
+ "name"
4753
+ ],
4754
+ resolveAllowFrom: (account) => account.config.allowFrom,
4755
+ formatAllowFrom: (allowFrom) => formatNormalizedAllowFromEntries({
4756
+ allowFrom,
4757
+ normalizeEntry: formatAllowEntry
4758
+ })
4759
+ });
4760
+ const resolveMattermostDmPolicy = createScopedDmSecurityResolver({
4761
+ channelKey: "mattermost",
4762
+ resolvePolicy: (account) => account.config.dmPolicy,
4763
+ resolveAllowFrom: (account) => account.config.allowFrom,
4764
+ policyPathSuffix: "dmPolicy",
4765
+ normalizeEntry: (raw) => normalizeAllowEntry(raw)
4766
+ });
4767
+ const mattermostPlugin = {
4768
+ id: "mattermost",
4769
+ meta: { ...meta },
4770
+ setup: mattermostSetupAdapter,
4771
+ setupWizard: mattermostSetupWizard,
4772
+ pairing: {
4773
+ idLabel: "mattermostUserId",
4774
+ normalizeAllowEntry: (entry) => normalizeAllowEntry(entry),
4775
+ notifyApproval: createLoggedPairingApprovalNotifier(({ id }) => `[mattermost] User ${id} approved for pairing`)
4776
+ },
4777
+ capabilities: {
4778
+ chatTypes: [
4779
+ "direct",
4780
+ "channel",
4781
+ "group",
4782
+ "thread"
4783
+ ],
4784
+ reactions: true,
4785
+ threads: true,
4786
+ media: true,
4787
+ nativeCommands: true
4788
+ },
4789
+ streaming: { blockStreamingCoalesceDefaults: {
4790
+ minChars: 1500,
4791
+ idleMs: 1e3
4792
+ } },
4793
+ threading: { resolveReplyToMode: createScopedAccountReplyToModeResolver({
4794
+ resolveAccount: (cfg, accountId) => resolveMattermostAccount({
4795
+ cfg,
4796
+ accountId: accountId ?? "default"
4797
+ }),
4798
+ resolveReplyToMode: (account, chatType) => resolveMattermostReplyToMode(account, chatType === "direct" || chatType === "group" || chatType === "channel" ? chatType : "channel")
4799
+ }) },
4800
+ reload: { configPrefixes: ["channels.mattermost"] },
4801
+ configSchema: buildChannelConfigSchema(MattermostConfigSchema),
4802
+ config: {
4803
+ ...mattermostConfigAdapter,
4804
+ isConfigured: (account) => Boolean(account.botToken && account.baseUrl),
4805
+ describeAccount: (account) => ({
4806
+ accountId: account.accountId,
4807
+ name: account.name,
4808
+ enabled: account.enabled,
4809
+ configured: Boolean(account.botToken && account.baseUrl),
4810
+ botTokenSource: account.botTokenSource,
4811
+ baseUrl: account.baseUrl
4812
+ })
4813
+ },
4814
+ security: {
4815
+ resolveDmPolicy: resolveMattermostDmPolicy,
4816
+ collectWarnings: collectMattermostSecurityWarnings
4817
+ },
4818
+ groups: { resolveRequireMention: resolveMattermostGroupRequireMention },
4819
+ actions: mattermostMessageActions,
4820
+ directory: createChannelDirectoryAdapter({
4821
+ listGroups: async (params) => listMattermostDirectoryGroups(params),
4822
+ listGroupsLive: async (params) => listMattermostDirectoryGroups(params),
4823
+ listPeers: async (params) => listMattermostDirectoryPeers(params),
4824
+ listPeersLive: async (params) => listMattermostDirectoryPeers(params)
4825
+ }),
4826
+ messaging: {
4827
+ normalizeTarget: normalizeMattermostMessagingTarget,
4828
+ resolveOutboundSessionRoute: (params) => resolveMattermostOutboundSessionRoute(params),
4829
+ targetResolver: {
4830
+ looksLikeId: looksLikeMattermostTargetId,
4831
+ hint: "<channelId|user:ID|channel:ID>",
4832
+ resolveTarget: async ({ cfg, accountId, input }) => {
4833
+ const resolved = await resolveMattermostOpaqueTarget({
4834
+ input,
4835
+ cfg,
4836
+ accountId
4837
+ });
4838
+ if (!resolved) return null;
4839
+ return {
4840
+ to: resolved.to,
4841
+ kind: resolved.kind,
4842
+ source: "directory"
4843
+ };
4844
+ }
4845
+ }
4846
+ },
4847
+ outbound: {
4848
+ deliveryMode: "direct",
4849
+ chunker: (text, limit) => getMattermostRuntime().channel.text.chunkMarkdownText(text, limit),
4850
+ chunkerMode: "markdown",
4851
+ textChunkLimit: 4e3,
4852
+ resolveTarget: ({ to }) => {
4853
+ const trimmed = to?.trim();
4854
+ if (!trimmed) return {
4855
+ ok: false,
4856
+ error: /* @__PURE__ */ new Error("Delivering to Mattermost requires --to <channelId|@username|user:ID|channel:ID>")
4857
+ };
4858
+ return {
4859
+ ok: true,
4860
+ to: trimmed
4861
+ };
4862
+ },
4863
+ ...createAttachedChannelResultAdapter({
4864
+ channel: "mattermost",
4865
+ sendText: async ({ cfg, to, text, accountId, replyToId, threadId }) => await sendMessageMattermost(to, text, {
4866
+ cfg,
4867
+ accountId: accountId ?? void 0,
4868
+ replyToId: replyToId ?? (threadId != null ? String(threadId) : void 0)
4869
+ }),
4870
+ sendMedia: async ({ cfg, to, text, mediaUrl, mediaLocalRoots, accountId, replyToId, threadId }) => await sendMessageMattermost(to, text, {
4871
+ cfg,
4872
+ accountId: accountId ?? void 0,
4873
+ mediaUrl,
4874
+ mediaLocalRoots,
4875
+ replyToId: replyToId ?? (threadId != null ? String(threadId) : void 0)
4876
+ })
4877
+ })
4878
+ },
4879
+ status: {
4880
+ defaultRuntime: {
4881
+ accountId: DEFAULT_ACCOUNT_ID,
4882
+ running: false,
4883
+ connected: false,
4884
+ lastConnectedAt: null,
4885
+ lastDisconnect: null,
4886
+ lastStartAt: null,
4887
+ lastStopAt: null,
4888
+ lastError: null
4889
+ },
4890
+ buildChannelSummary: ({ snapshot }) => buildPassiveProbedChannelStatusSummary(snapshot, {
4891
+ botTokenSource: snapshot.botTokenSource ?? "none",
4892
+ connected: snapshot.connected ?? false,
4893
+ baseUrl: snapshot.baseUrl ?? null
4894
+ }),
4895
+ probeAccount: async ({ account, timeoutMs }) => {
4896
+ const token = account.botToken?.trim();
4897
+ const baseUrl = account.baseUrl?.trim();
4898
+ if (!token || !baseUrl) return {
4899
+ ok: false,
4900
+ error: "bot token or baseUrl missing"
4901
+ };
4902
+ return await probeMattermost(baseUrl, token, timeoutMs);
4903
+ },
4904
+ buildAccountSnapshot: ({ account, runtime, probe }) => {
4905
+ return {
4906
+ ...buildComputedAccountStatusSnapshot({
4907
+ accountId: account.accountId,
4908
+ name: account.name,
4909
+ enabled: account.enabled,
4910
+ configured: Boolean(account.botToken && account.baseUrl),
4911
+ runtime,
4912
+ probe
4913
+ }),
4914
+ botTokenSource: account.botTokenSource,
4915
+ baseUrl: account.baseUrl,
4916
+ connected: runtime?.connected ?? false,
4917
+ lastConnectedAt: runtime?.lastConnectedAt ?? null,
4918
+ lastDisconnect: runtime?.lastDisconnect ?? null
4919
+ };
4920
+ }
4921
+ },
4922
+ gateway: { startAccount: async (ctx) => {
4923
+ const account = ctx.account;
4924
+ const statusSink = createAccountStatusSink({
4925
+ accountId: ctx.accountId,
4926
+ setStatus: ctx.setStatus
4927
+ });
4928
+ statusSink({
4929
+ baseUrl: account.baseUrl,
4930
+ botTokenSource: account.botTokenSource
4931
+ });
4932
+ ctx.log?.info(`[${account.accountId}] starting channel`);
4933
+ return monitorMattermostProvider({
4934
+ botToken: account.botToken ?? void 0,
4935
+ baseUrl: account.baseUrl ?? void 0,
4936
+ accountId: account.accountId,
4937
+ config: ctx.cfg,
4938
+ runtime: ctx.runtime,
4939
+ abortSignal: ctx.abortSignal,
4940
+ statusSink
4941
+ });
4942
+ } }
4943
+ };
4944
+ //#endregion
4945
+ export { registerSlashCommandRoute as n, setMattermostRuntime as r, mattermostPlugin as t };