@kodelyth/matrix 2026.5.39 → 2026.5.42

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 (321) hide show
  1. package/CHANGELOG.md +321 -0
  2. package/SPEC-SUPPORT.md +116 -0
  3. package/api.ts +38 -0
  4. package/auth-presence.ts +56 -0
  5. package/channel-plugin-api.ts +3 -0
  6. package/cli-metadata.ts +11 -0
  7. package/contract-api.ts +17 -0
  8. package/dist/account-selection-Y50DNJ2l.js +158 -0
  9. package/dist/active-client-CmFdvPdO.js +20 -0
  10. package/dist/api.js +12 -0
  11. package/dist/approval-handler.runtime-BIi4fL0R.js +377 -0
  12. package/dist/approval-ids-BGHK7PnZ.js +7 -0
  13. package/dist/approval-reaction-auth-CL0-nCNV.js +27 -0
  14. package/dist/approval-reactions-nDm2x-K5.js +162 -0
  15. package/dist/async-lock-SsmtFXtt.js +19 -0
  16. package/dist/auth-presence.js +26 -0
  17. package/dist/backup-health-3BHbHxyd.js +60 -0
  18. package/dist/channel-C0kCyTNB.js +1380 -0
  19. package/dist/channel-plugin-api.js +2 -0
  20. package/dist/channel.runtime-CdrdEN-0.js +250 -0
  21. package/dist/cli-FtY6Nuzw.js +1338 -0
  22. package/dist/cli-metadata-Dkwua7CB.js +22 -0
  23. package/dist/cli-metadata.js +2 -0
  24. package/dist/client-BnohYygh.js +25 -0
  25. package/dist/client-PhrTwuC4.js +30 -0
  26. package/dist/client-bootstrap-Mcj8ChJ5.js +114 -0
  27. package/dist/config-paths-DVvt6vM3.js +114 -0
  28. package/dist/config-schema-BMGOlhdI.js +308 -0
  29. package/dist/config-secret-input.runtime-Dv_4Br_f.js +2 -0
  30. package/dist/contract-api.js +8 -0
  31. package/dist/create-client-J0htTaRj.js +64 -0
  32. package/dist/credentials-B7GsBbgQ.js +56 -0
  33. package/dist/credentials-read-8fE4qoWs.js +112 -0
  34. package/dist/credentials-write.runtime-BibplB4Y.js +17 -0
  35. package/dist/crypto-node.runtime-D9qxgRPa.js +12 -0
  36. package/dist/crypto-runtime-1pKW4O2F.js +1214 -0
  37. package/dist/deps-DVpDS81G.js +208 -0
  38. package/dist/device-health-Ct2wDSPG.js +16 -0
  39. package/dist/directory-live-i3T8uORc.js +150 -0
  40. package/dist/doctor-contract-BLzYHl_9.js +246 -0
  41. package/dist/doctor-contract-api.js +2 -0
  42. package/dist/doctor-diR5gE7D.js +153 -0
  43. package/dist/draft-stream-HpPJ_VJt.js +143 -0
  44. package/dist/encryption-guidance-BNEgckrZ.js +15 -0
  45. package/dist/env-auth-UFiTGkDM.js +63 -0
  46. package/dist/env-vars-EQKQv-FE.js +63 -0
  47. package/dist/errors-BETj3zr9.js +17 -0
  48. package/dist/exec-approval-resolver-BxPorU_t.js +15 -0
  49. package/dist/helper-api.js +4 -0
  50. package/dist/http-client-DoQgbQsU.js +331 -0
  51. package/dist/index.js +46 -0
  52. package/dist/legacy-crypto-inspector-zK0hDCbt.js +41 -0
  53. package/dist/legacy-crypto-restore-DSFIXuDo.js +85 -0
  54. package/dist/logging-Df7aPD1z.js +99 -0
  55. package/dist/matrix-migration.runtime-BNoT1Prt.js +525 -0
  56. package/dist/media-text-ZhGA8Pcs.js +146 -0
  57. package/dist/messages-CRA9WGg0.js +140 -0
  58. package/dist/migration-snapshot-backup-BR-xD7Ew.js +69 -0
  59. package/dist/migration-snapshot.runtime-BLcy_Nvw.js +2 -0
  60. package/dist/monitor-DQm7_13y.js +4331 -0
  61. package/dist/plugin-entry.handlers.runtime.js +51 -0
  62. package/dist/probe.runtime-CjJS53Kz.js +3 -0
  63. package/dist/profile-update-DqkPgZ1P.js +68 -0
  64. package/dist/reaction-common-CmVLzP-u.js +71 -0
  65. package/dist/reaction-events-D0nUJuZV.js +121 -0
  66. package/dist/record-shared-DGvSFn5M.js +2 -0
  67. package/dist/resolve-targets-ChECUzD2.js +140 -0
  68. package/dist/resolver.runtime-hdY3n0GO.js +5 -0
  69. package/dist/rolldown-runtime-DUslC3ob.js +14 -0
  70. package/dist/route-xRKj_ESW.js +161 -0
  71. package/dist/runtime-B-Fyrmxo.js +8 -0
  72. package/dist/runtime-api-BYXXkxq2.js +24 -0
  73. package/dist/runtime-api.js +25 -0
  74. package/dist/runtime-heavy-api.js +3 -0
  75. package/dist/runtime-lwTSy9Yt.js +6 -0
  76. package/dist/runtime-setter-api.js +2 -0
  77. package/dist/sdk-Jhq7mLtD.js +1704 -0
  78. package/dist/secret-contract-DEMcDsjl.js +120 -0
  79. package/dist/secret-contract-api.js +2 -0
  80. package/dist/send-CJunc6QM.js +1517 -0
  81. package/dist/setup-bootstrap-rJ0qZWPe.js +62 -0
  82. package/dist/setup-core-BEYoXF3J.js +677 -0
  83. package/dist/setup-entry.js +19 -0
  84. package/dist/setup-plugin-api.js +43 -0
  85. package/dist/setup-surface-c28ON6jq.js +537 -0
  86. package/dist/shared-D6MFMnpG.js +642 -0
  87. package/dist/startup-abort-B2J3MU_h.js +109 -0
  88. package/dist/startup-verification-CkD4Cwce.js +132 -0
  89. package/dist/storage-nyO0DOFE.js +281 -0
  90. package/dist/storage-paths-BTAketfg.js +52 -0
  91. package/dist/subagent-hooks-api-Dr_xnMRG.js +170 -0
  92. package/dist/subagent-hooks-api.js +2 -0
  93. package/dist/sync-state-Bx0gPaGA.js +12 -0
  94. package/dist/target-ids-Bsazo8si.js +77 -0
  95. package/dist/test-api.js +4 -0
  96. package/dist/thread-binding-api-IGU0-L70.js +17 -0
  97. package/dist/thread-binding-api.js +2 -0
  98. package/dist/thread-bindings-FjAZmDUP.js +352 -0
  99. package/dist/thread-bindings-runtime.js +2 -0
  100. package/dist/thread-bindings-shared-fvfP7jVs.js +97 -0
  101. package/dist/timeout-abort-signal-DpSHDHhR.js +2 -0
  102. package/dist/tool-actions.runtime-Cbo7YcYZ.js +532 -0
  103. package/dist/url-validation-DlrXNjAE.js +36 -0
  104. package/dist/verification-7tDPRpJU.js +345 -0
  105. package/doctor-contract-api.ts +1 -0
  106. package/helper-api.ts +3 -0
  107. package/index.ts +55 -0
  108. package/klaw.plugin.json +3 -891
  109. package/package.json +4 -4
  110. package/plugin-entry.handlers.runtime.ts +1 -0
  111. package/runtime-api.ts +72 -0
  112. package/runtime-heavy-api.ts +1 -0
  113. package/runtime-setter-api.ts +3 -0
  114. package/secret-contract-api.ts +5 -0
  115. package/setup-entry.ts +17 -0
  116. package/setup-plugin-api.ts +3 -0
  117. package/src/account-selection.ts +223 -0
  118. package/src/actions.ts +346 -0
  119. package/src/approval-auth.ts +25 -0
  120. package/src/approval-handler.runtime.ts +592 -0
  121. package/src/approval-ids.ts +6 -0
  122. package/src/approval-native.ts +345 -0
  123. package/src/approval-reaction-auth.ts +45 -0
  124. package/src/approval-reactions.ts +313 -0
  125. package/src/auth-precedence.ts +61 -0
  126. package/src/channel-account-paths.ts +97 -0
  127. package/src/channel.runtime.ts +17 -0
  128. package/src/channel.setup.ts +48 -0
  129. package/src/channel.ts +667 -0
  130. package/src/cli-metadata.ts +19 -0
  131. package/src/cli.ts +2298 -0
  132. package/src/config-adapter.ts +41 -0
  133. package/src/config-schema.ts +159 -0
  134. package/src/config-ui-hints.ts +56 -0
  135. package/src/directory-live.ts +238 -0
  136. package/src/doctor-contract.ts +287 -0
  137. package/src/doctor.ts +262 -0
  138. package/src/env-vars.ts +92 -0
  139. package/src/exec-approval-resolver.ts +23 -0
  140. package/src/exec-approvals.ts +287 -0
  141. package/src/group-mentions.ts +41 -0
  142. package/src/legacy-crypto-inspector-availability.ts +60 -0
  143. package/src/legacy-crypto.ts +531 -0
  144. package/src/legacy-state.ts +156 -0
  145. package/src/matrix/account-config.ts +175 -0
  146. package/src/matrix/accounts.ts +194 -0
  147. package/src/matrix/actions/client.ts +31 -0
  148. package/src/matrix/actions/devices.ts +34 -0
  149. package/src/matrix/actions/limits.ts +6 -0
  150. package/src/matrix/actions/messages.ts +129 -0
  151. package/src/matrix/actions/pins.ts +63 -0
  152. package/src/matrix/actions/polls.ts +109 -0
  153. package/src/matrix/actions/profile.ts +37 -0
  154. package/src/matrix/actions/reactions.ts +59 -0
  155. package/src/matrix/actions/room.ts +71 -0
  156. package/src/matrix/actions/summary.ts +88 -0
  157. package/src/matrix/actions/types.ts +63 -0
  158. package/src/matrix/actions/verification.ts +589 -0
  159. package/src/matrix/actions.ts +37 -0
  160. package/src/matrix/active-client.ts +26 -0
  161. package/src/matrix/async-lock.ts +18 -0
  162. package/src/matrix/backup-health.ts +124 -0
  163. package/src/matrix/client/config-runtime-api.ts +9 -0
  164. package/src/matrix/client/config-secret-input.runtime.ts +1 -0
  165. package/src/matrix/client/config.ts +853 -0
  166. package/src/matrix/client/create-client.ts +105 -0
  167. package/src/matrix/client/env-auth.ts +95 -0
  168. package/src/matrix/client/file-sync-store.ts +289 -0
  169. package/src/matrix/client/logging.ts +140 -0
  170. package/src/matrix/client/migration-snapshot.runtime.ts +1 -0
  171. package/src/matrix/client/private-network-host.ts +1 -0
  172. package/src/matrix/client/runtime.ts +4 -0
  173. package/src/matrix/client/shared.ts +316 -0
  174. package/src/matrix/client/storage.ts +543 -0
  175. package/src/matrix/client/types.ts +50 -0
  176. package/src/matrix/client/url-validation.ts +73 -0
  177. package/src/matrix/client-bootstrap.ts +173 -0
  178. package/src/matrix/client.ts +23 -0
  179. package/src/matrix/config-paths.ts +31 -0
  180. package/src/matrix/config-update.ts +292 -0
  181. package/src/matrix/credentials-read.ts +208 -0
  182. package/src/matrix/credentials-write.runtime.ts +35 -0
  183. package/src/matrix/credentials.ts +95 -0
  184. package/src/matrix/deps.ts +309 -0
  185. package/src/matrix/device-health.ts +29 -0
  186. package/src/matrix/direct-management.ts +349 -0
  187. package/src/matrix/direct-room.ts +128 -0
  188. package/src/matrix/draft-stream.ts +225 -0
  189. package/src/matrix/encryption-guidance.ts +24 -0
  190. package/src/matrix/errors.ts +21 -0
  191. package/src/matrix/format.ts +426 -0
  192. package/src/matrix/legacy-crypto-inspector.ts +95 -0
  193. package/src/matrix/media-errors.ts +20 -0
  194. package/src/matrix/media-text.ts +162 -0
  195. package/src/matrix/monitor/access-state.ts +145 -0
  196. package/src/matrix/monitor/ack-config.ts +27 -0
  197. package/src/matrix/monitor/allowlist.ts +89 -0
  198. package/src/matrix/monitor/auto-join.ts +86 -0
  199. package/src/matrix/monitor/config.ts +569 -0
  200. package/src/matrix/monitor/context-summary.ts +43 -0
  201. package/src/matrix/monitor/direct.ts +296 -0
  202. package/src/matrix/monitor/events.ts +397 -0
  203. package/src/matrix/monitor/handler.ts +2266 -0
  204. package/src/matrix/monitor/inbound-dedupe.ts +267 -0
  205. package/src/matrix/monitor/index.ts +540 -0
  206. package/src/matrix/monitor/legacy-crypto-restore.ts +139 -0
  207. package/src/matrix/monitor/location.ts +108 -0
  208. package/src/matrix/monitor/media.ts +119 -0
  209. package/src/matrix/monitor/mentions.ts +256 -0
  210. package/src/matrix/monitor/reaction-events.ts +197 -0
  211. package/src/matrix/monitor/recent-invite.ts +30 -0
  212. package/src/matrix/monitor/replies.ts +136 -0
  213. package/src/matrix/monitor/reply-context.ts +92 -0
  214. package/src/matrix/monitor/room-history.ts +301 -0
  215. package/src/matrix/monitor/room-info.ts +126 -0
  216. package/src/matrix/monitor/rooms.ts +52 -0
  217. package/src/matrix/monitor/route.ts +179 -0
  218. package/src/matrix/monitor/runtime-api.ts +28 -0
  219. package/src/matrix/monitor/startup-verification.ts +237 -0
  220. package/src/matrix/monitor/startup.ts +218 -0
  221. package/src/matrix/monitor/status.ts +120 -0
  222. package/src/matrix/monitor/sync-lifecycle.ts +91 -0
  223. package/src/matrix/monitor/task-runner.ts +38 -0
  224. package/src/matrix/monitor/test-events.ts +21 -0
  225. package/src/matrix/monitor/thread-context.ts +108 -0
  226. package/src/matrix/monitor/threads.ts +85 -0
  227. package/src/matrix/monitor/types.ts +30 -0
  228. package/src/matrix/monitor/verification-events.ts +643 -0
  229. package/src/matrix/monitor/verification-utils.ts +46 -0
  230. package/src/matrix/outbound-media-runtime.ts +1 -0
  231. package/src/matrix/poll-summary.ts +110 -0
  232. package/src/matrix/poll-types.ts +429 -0
  233. package/src/matrix/probe.runtime.ts +4 -0
  234. package/src/matrix/probe.ts +97 -0
  235. package/src/matrix/profile.ts +184 -0
  236. package/src/matrix/reaction-common.ts +147 -0
  237. package/src/matrix/sdk/crypto-bootstrap.ts +438 -0
  238. package/src/matrix/sdk/crypto-facade.ts +242 -0
  239. package/src/matrix/sdk/crypto-node.runtime.ts +17 -0
  240. package/src/matrix/sdk/crypto-runtime.ts +14 -0
  241. package/src/matrix/sdk/decrypt-bridge.ts +410 -0
  242. package/src/matrix/sdk/event-helpers.ts +83 -0
  243. package/src/matrix/sdk/http-client.ts +87 -0
  244. package/src/matrix/sdk/idb-persistence-lock.ts +51 -0
  245. package/src/matrix/sdk/idb-persistence.ts +288 -0
  246. package/src/matrix/sdk/logger.ts +108 -0
  247. package/src/matrix/sdk/read-response-with-limit.ts +19 -0
  248. package/src/matrix/sdk/recovery-key-store.ts +453 -0
  249. package/src/matrix/sdk/timeout-abort-signal.ts +1 -0
  250. package/src/matrix/sdk/transport-runtime-api.ts +18 -0
  251. package/src/matrix/sdk/transport.ts +352 -0
  252. package/src/matrix/sdk/types.ts +245 -0
  253. package/src/matrix/sdk/verification-manager.ts +795 -0
  254. package/src/matrix/sdk/verification-status.ts +23 -0
  255. package/src/matrix/sdk.ts +2152 -0
  256. package/src/matrix/send/client.ts +93 -0
  257. package/src/matrix/send/formatting.ts +189 -0
  258. package/src/matrix/send/media.ts +244 -0
  259. package/src/matrix/send/targets.ts +104 -0
  260. package/src/matrix/send/types.ts +131 -0
  261. package/src/matrix/send.ts +660 -0
  262. package/src/matrix/session-store-metadata.ts +108 -0
  263. package/src/matrix/startup-abort.ts +44 -0
  264. package/src/matrix/subagent-hooks.ts +308 -0
  265. package/src/matrix/sync-state.ts +27 -0
  266. package/src/matrix/target-ids.ts +79 -0
  267. package/src/matrix/thread-bindings-shared.ts +206 -0
  268. package/src/matrix/thread-bindings.ts +580 -0
  269. package/src/matrix-migration.runtime.ts +9 -0
  270. package/src/migration-config.ts +243 -0
  271. package/src/migration-snapshot-backup.ts +116 -0
  272. package/src/migration-snapshot.ts +53 -0
  273. package/src/onboarding.ts +775 -0
  274. package/src/outbound.ts +248 -0
  275. package/src/plugin-entry.runtime.js +115 -0
  276. package/src/plugin-entry.runtime.ts +70 -0
  277. package/src/profile-update.ts +71 -0
  278. package/src/record-shared.ts +3 -0
  279. package/src/resolve-targets.ts +175 -0
  280. package/src/resolver.runtime.ts +5 -0
  281. package/src/resolver.ts +21 -0
  282. package/src/runtime-api.ts +106 -0
  283. package/src/runtime.ts +13 -0
  284. package/src/secret-contract.ts +174 -0
  285. package/src/session-route.ts +126 -0
  286. package/src/setup-bootstrap.ts +102 -0
  287. package/src/setup-config.ts +222 -0
  288. package/src/setup-contract.ts +90 -0
  289. package/src/setup-core.ts +146 -0
  290. package/src/setup-dm-policy.ts +15 -0
  291. package/src/setup-surface.ts +4 -0
  292. package/src/startup-maintenance.ts +114 -0
  293. package/src/storage-paths.ts +92 -0
  294. package/src/thread-binding-api.ts +23 -0
  295. package/src/tool-actions.runtime.ts +1 -0
  296. package/src/tool-actions.ts +498 -0
  297. package/src/types.ts +257 -0
  298. package/subagent-hooks-api.ts +31 -0
  299. package/test-api.ts +21 -0
  300. package/thread-binding-api.ts +4 -0
  301. package/thread-bindings-runtime.ts +4 -0
  302. package/tsconfig.json +16 -0
  303. package/api.js +0 -7
  304. package/auth-presence.js +0 -7
  305. package/channel-plugin-api.js +0 -7
  306. package/cli-metadata.js +0 -7
  307. package/contract-api.js +0 -7
  308. package/doctor-contract-api.js +0 -7
  309. package/helper-api.js +0 -7
  310. package/index.js +0 -7
  311. package/plugin-entry.handlers.runtime.js +0 -7
  312. package/runtime-api.js +0 -7
  313. package/runtime-heavy-api.js +0 -7
  314. package/runtime-setter-api.js +0 -7
  315. package/secret-contract-api.js +0 -7
  316. package/setup-entry.js +0 -7
  317. package/setup-plugin-api.js +0 -7
  318. package/subagent-hooks-api.js +0 -7
  319. package/test-api.js +0 -7
  320. package/thread-binding-api.js +0 -7
  321. package/thread-bindings-runtime.js +0 -7
@@ -0,0 +1,1517 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-DUslC3ob.js";
2
+ import { r as normalizeMatrixResolvableTarget, t as isMatrixQualifiedUserId } from "./target-ids-Bsazo8si.js";
3
+ import { c as resolveMatrixAccountConfig } from "./config-paths-DVvt6vM3.js";
4
+ import { t as getMatrixRuntime } from "./runtime-B-Fyrmxo.js";
5
+ import { n as MATRIX_REACTION_EVENT_TYPE, r as buildMatrixReactionContent, t as MATRIX_ANNOTATION_RELATION_TYPE } from "./reaction-common-CmVLzP-u.js";
6
+ import { normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeOptionalStringifiedId } from "klaw/plugin-sdk/string-coerce-runtime";
7
+ import { createMessageReceiptFromOutboundResults } from "klaw/plugin-sdk/channel-message";
8
+ import { requireRuntimeConfig } from "klaw/plugin-sdk/plugin-config-runtime";
9
+ import { loadOutboundMediaFromUrl } from "klaw/plugin-sdk/outbound-media";
10
+ import { normalizePollInput } from "klaw/plugin-sdk/poll-runtime";
11
+ import { isAutoLinkedFileRef } from "klaw/plugin-sdk/text-autolink-runtime";
12
+ import MarkdownIt from "markdown-it";
13
+ import { parseBuffer } from "music-metadata";
14
+ import { KeyedAsyncQueue } from "klaw/plugin-sdk/keyed-async-queue";
15
+ //#region extensions/matrix/src/matrix/poll-types.ts
16
+ /**
17
+ * Matrix Poll Types (MSC3381)
18
+ *
19
+ * Defines types for Matrix poll events:
20
+ * - m.poll.start - Creates a new poll
21
+ * - m.poll.response - Records a vote
22
+ * - m.poll.end - Closes a poll
23
+ */
24
+ const M_POLL_START = "m.poll.start";
25
+ const M_POLL_RESPONSE = "m.poll.response";
26
+ const M_POLL_END = "m.poll.end";
27
+ const ORG_POLL_START = "org.matrix.msc3381.poll.start";
28
+ const ORG_POLL_RESPONSE = "org.matrix.msc3381.poll.response";
29
+ const ORG_POLL_END = "org.matrix.msc3381.poll.end";
30
+ const POLL_EVENT_TYPES = [
31
+ M_POLL_START,
32
+ M_POLL_RESPONSE,
33
+ M_POLL_END,
34
+ ORG_POLL_START,
35
+ ORG_POLL_RESPONSE,
36
+ ORG_POLL_END
37
+ ];
38
+ const POLL_START_TYPES = [M_POLL_START, ORG_POLL_START];
39
+ const POLL_RESPONSE_TYPES = [M_POLL_RESPONSE, ORG_POLL_RESPONSE];
40
+ const POLL_END_TYPES = [M_POLL_END, ORG_POLL_END];
41
+ function isPollStartType(eventType) {
42
+ return POLL_START_TYPES.includes(eventType);
43
+ }
44
+ function isPollResponseType(eventType) {
45
+ return POLL_RESPONSE_TYPES.includes(eventType);
46
+ }
47
+ function isPollEndType(eventType) {
48
+ return POLL_END_TYPES.includes(eventType);
49
+ }
50
+ function isPollEventType(eventType) {
51
+ return POLL_EVENT_TYPES.includes(eventType);
52
+ }
53
+ function getTextContent(text) {
54
+ if (!text) return "";
55
+ return text["m.text"] ?? text["org.matrix.msc1767.text"] ?? text.body ?? "";
56
+ }
57
+ function parsePollStart(content) {
58
+ const poll = content["m.poll.start"] ?? content[ORG_POLL_START] ?? content["m.poll"];
59
+ if (!poll) return null;
60
+ const question = getTextContent(poll.question).trim();
61
+ if (!question) return null;
62
+ const answers = poll.answers.map((answer) => ({
63
+ id: answer.id,
64
+ text: getTextContent(answer).trim()
65
+ })).filter((answer) => answer.id.trim().length > 0 && answer.text.length > 0);
66
+ if (answers.length === 0) return null;
67
+ const maxSelectionsRaw = poll.max_selections;
68
+ const maxSelections = typeof maxSelectionsRaw === "number" && Number.isFinite(maxSelectionsRaw) ? Math.floor(maxSelectionsRaw) : 1;
69
+ return {
70
+ question,
71
+ answers,
72
+ kind: poll.kind ?? "m.poll.disclosed",
73
+ maxSelections: Math.min(Math.max(maxSelections, 1), answers.length)
74
+ };
75
+ }
76
+ function parsePollStartContent(content) {
77
+ const parsed = parsePollStart(content);
78
+ if (!parsed) return null;
79
+ return {
80
+ eventId: "",
81
+ roomId: "",
82
+ sender: "",
83
+ senderName: "",
84
+ question: parsed.question,
85
+ answers: parsed.answers.map((answer) => answer.text),
86
+ kind: parsed.kind,
87
+ maxSelections: parsed.maxSelections
88
+ };
89
+ }
90
+ function formatPollAsText(summary) {
91
+ return [
92
+ "[Poll]",
93
+ summary.question,
94
+ "",
95
+ ...summary.answers.map((answer, idx) => `${idx + 1}. ${answer}`)
96
+ ].join("\n");
97
+ }
98
+ function resolvePollReferenceEventId(content) {
99
+ if (!content || typeof content !== "object") return null;
100
+ const relates = content["m.relates_to"];
101
+ if (!relates || typeof relates.event_id !== "string") return null;
102
+ const eventId = relates.event_id.trim();
103
+ return eventId.length > 0 ? eventId : null;
104
+ }
105
+ function parsePollResponseAnswerIds(content) {
106
+ if (!content || typeof content !== "object") return null;
107
+ const response = content[M_POLL_RESPONSE] ?? content[ORG_POLL_RESPONSE];
108
+ if (!response || !Array.isArray(response.answers)) return null;
109
+ return response.answers.filter((answer) => typeof answer === "string");
110
+ }
111
+ function buildPollResultsSummary(params) {
112
+ const parsed = parsePollStart(params.content);
113
+ if (!parsed) return null;
114
+ let pollClosedAt = Number.POSITIVE_INFINITY;
115
+ for (const event of params.relationEvents) {
116
+ if (event.unsigned?.redacted_because) continue;
117
+ if (!isPollEndType(typeof event.type === "string" ? event.type : "")) continue;
118
+ if (event.sender !== params.sender) continue;
119
+ const ts = typeof event.origin_server_ts === "number" && Number.isFinite(event.origin_server_ts) ? event.origin_server_ts : Number.POSITIVE_INFINITY;
120
+ if (ts < pollClosedAt) pollClosedAt = ts;
121
+ }
122
+ const answerIds = new Set(parsed.answers.map((answer) => answer.id));
123
+ const latestVoteBySender = /* @__PURE__ */ new Map();
124
+ const orderedRelationEvents = [...params.relationEvents].toSorted((left, right) => {
125
+ const leftTs = typeof left.origin_server_ts === "number" && Number.isFinite(left.origin_server_ts) ? left.origin_server_ts : Number.POSITIVE_INFINITY;
126
+ const rightTs = typeof right.origin_server_ts === "number" && Number.isFinite(right.origin_server_ts) ? right.origin_server_ts : Number.POSITIVE_INFINITY;
127
+ if (leftTs !== rightTs) return leftTs - rightTs;
128
+ return (left.event_id ?? "").localeCompare(right.event_id ?? "");
129
+ });
130
+ for (const event of orderedRelationEvents) {
131
+ if (event.unsigned?.redacted_because) continue;
132
+ if (!isPollResponseType(typeof event.type === "string" ? event.type : "")) continue;
133
+ const senderId = normalizeOptionalString(event.sender) ?? "";
134
+ if (!senderId) continue;
135
+ const eventTs = typeof event.origin_server_ts === "number" && Number.isFinite(event.origin_server_ts) ? event.origin_server_ts : Number.POSITIVE_INFINITY;
136
+ if (eventTs > pollClosedAt) continue;
137
+ const rawAnswers = parsePollResponseAnswerIds(event.content) ?? [];
138
+ const normalizedAnswers = Array.from(new Set(rawAnswers.map((answerId) => normalizeOptionalString(answerId) ?? "").filter((answerId) => answerIds.has(answerId)).slice(0, parsed.maxSelections)));
139
+ latestVoteBySender.set(senderId, {
140
+ ts: eventTs,
141
+ eventId: typeof event.event_id === "string" ? event.event_id : "",
142
+ answerIds: normalizedAnswers
143
+ });
144
+ }
145
+ const voteCounts = new Map(parsed.answers.map((answer) => [answer.id, 0]));
146
+ let totalVotes = 0;
147
+ for (const latestVote of latestVoteBySender.values()) {
148
+ if (latestVote.answerIds.length === 0) continue;
149
+ totalVotes += 1;
150
+ for (const answerId of latestVote.answerIds) voteCounts.set(answerId, (voteCounts.get(answerId) ?? 0) + 1);
151
+ }
152
+ return {
153
+ eventId: params.pollEventId,
154
+ roomId: params.roomId,
155
+ sender: params.sender,
156
+ senderName: params.senderName,
157
+ question: parsed.question,
158
+ answers: parsed.answers.map((answer) => answer.text),
159
+ kind: parsed.kind,
160
+ maxSelections: parsed.maxSelections,
161
+ entries: parsed.answers.map((answer) => ({
162
+ id: answer.id,
163
+ text: answer.text,
164
+ votes: voteCounts.get(answer.id) ?? 0
165
+ })),
166
+ totalVotes,
167
+ closed: Number.isFinite(pollClosedAt)
168
+ };
169
+ }
170
+ function formatPollResultsAsText(summary) {
171
+ const lines = [
172
+ summary.closed ? "[Poll closed]" : "[Poll]",
173
+ summary.question,
174
+ ""
175
+ ];
176
+ const revealResults = summary.kind === "m.poll.disclosed" || summary.closed;
177
+ for (const [index, entry] of summary.entries.entries()) {
178
+ if (!revealResults) {
179
+ lines.push(`${index + 1}. ${entry.text}`);
180
+ continue;
181
+ }
182
+ lines.push(`${index + 1}. ${entry.text} (${entry.votes} vote${entry.votes === 1 ? "" : "s"})`);
183
+ }
184
+ lines.push("");
185
+ if (!revealResults) lines.push("Responses are hidden until the poll closes.");
186
+ else lines.push(`Total voters: ${summary.totalVotes}`);
187
+ return lines.join("\n");
188
+ }
189
+ function buildTextContent$1(body) {
190
+ return {
191
+ "m.text": body,
192
+ "org.matrix.msc1767.text": body
193
+ };
194
+ }
195
+ function buildPollFallbackText(question, answers) {
196
+ if (answers.length === 0) return question;
197
+ return `${question}\n${answers.map((answer, idx) => `${idx + 1}. ${answer}`).join("\n")}`;
198
+ }
199
+ function buildPollStartContent(poll) {
200
+ const normalized = normalizePollInput(poll);
201
+ const answers = normalized.options.map((option, idx) => ({
202
+ id: `answer${idx + 1}`,
203
+ ...buildTextContent$1(option)
204
+ }));
205
+ const isMultiple = normalized.maxSelections > 1;
206
+ const fallbackText = buildPollFallbackText(normalized.question, answers.map((answer) => getTextContent(answer)));
207
+ return {
208
+ [M_POLL_START]: {
209
+ question: buildTextContent$1(normalized.question),
210
+ kind: isMultiple ? "m.poll.undisclosed" : "m.poll.disclosed",
211
+ max_selections: normalized.maxSelections,
212
+ answers
213
+ },
214
+ "m.text": fallbackText,
215
+ "org.matrix.msc1767.text": fallbackText
216
+ };
217
+ }
218
+ function buildPollResponseContent(pollEventId, answerIds) {
219
+ return {
220
+ [M_POLL_RESPONSE]: { answers: answerIds },
221
+ [ORG_POLL_RESPONSE]: { answers: answerIds },
222
+ "m.relates_to": {
223
+ rel_type: "m.reference",
224
+ event_id: pollEventId
225
+ }
226
+ };
227
+ }
228
+ //#endregion
229
+ //#region extensions/matrix/src/matrix/send/client.ts
230
+ let matrixSendClientRuntimePromise = null;
231
+ async function loadMatrixSendClientRuntime() {
232
+ matrixSendClientRuntimePromise ??= import("./client-bootstrap-Mcj8ChJ5.js").then((n) => n.t);
233
+ return await matrixSendClientRuntimePromise;
234
+ }
235
+ function resolveMediaMaxBytes(accountId, cfg) {
236
+ if (!cfg) throw new Error("Matrix media limits requires a resolved runtime config. Load and resolve config at the command or gateway boundary, then pass cfg through the runtime path.");
237
+ const matrixCfg = resolveMatrixAccountConfig({
238
+ cfg: requireRuntimeConfig(cfg, "Matrix media limits"),
239
+ accountId
240
+ });
241
+ const mediaMaxMb = typeof matrixCfg.mediaMaxMb === "number" ? matrixCfg.mediaMaxMb : void 0;
242
+ if (typeof mediaMaxMb === "number") return mediaMaxMb * 1024 * 1024;
243
+ }
244
+ async function withResolvedMatrixSendClient(opts, run) {
245
+ return await withResolvedMatrixClient({
246
+ ...opts,
247
+ readiness: "started"
248
+ }, run, "persist");
249
+ }
250
+ async function withResolvedMatrixControlClient(opts, run) {
251
+ return await withResolvedMatrixClient({
252
+ ...opts,
253
+ readiness: "none"
254
+ }, run);
255
+ }
256
+ async function withResolvedMatrixClient(opts, run, shutdownBehavior) {
257
+ if (opts.client) return await run(opts.client);
258
+ const { withResolvedRuntimeMatrixClient } = await loadMatrixSendClientRuntime();
259
+ return await withResolvedRuntimeMatrixClient(opts, run, shutdownBehavior);
260
+ }
261
+ //#endregion
262
+ //#region extensions/matrix/src/matrix/format.ts
263
+ const md = new MarkdownIt({
264
+ html: false,
265
+ linkify: true,
266
+ breaks: true,
267
+ typographer: false
268
+ });
269
+ md.enable("strikethrough");
270
+ const { escapeHtml } = md.utils;
271
+ const ESCAPED_MENTION_SENTINEL = "";
272
+ const MENTION_PATTERN = /@[A-Za-z0-9._=+\-/:[\]]+/g;
273
+ const MATRIX_MENTION_USER_ID_PATTERN = /^@[A-Za-z0-9._=+\-/]+:(?:[A-Za-z0-9.-]+|\[[0-9A-Fa-f:.]+\])(?::\d+)?$/;
274
+ const TRIMMABLE_MENTION_SUFFIX = /[),.!?:;\]]/;
275
+ function shouldSuppressAutoLink(tokens, idx) {
276
+ const token = tokens[idx];
277
+ if (token?.type !== "link_open" || token.info !== "auto") return false;
278
+ const href = token.attrGet("href") ?? "";
279
+ const label = tokens[idx + 1]?.type === "text" ? tokens[idx + 1]?.content ?? "" : "";
280
+ return Boolean(href && label && isAutoLinkedFileRef(href, label));
281
+ }
282
+ md.renderer.rules.image = (tokens, idx) => escapeHtml(tokens[idx]?.content ?? "");
283
+ md.renderer.rules.html_block = (tokens, idx) => escapeHtml(tokens[idx]?.content ?? "");
284
+ md.renderer.rules.html_inline = (tokens, idx) => escapeHtml(tokens[idx]?.content ?? "");
285
+ md.renderer.rules.link_open = (tokens, idx, _options, _env, self) => shouldSuppressAutoLink(tokens, idx) ? "" : self.renderToken(tokens, idx, _options);
286
+ md.renderer.rules.link_close = (tokens, idx, _options, _env, self) => {
287
+ const openIdx = idx - 2;
288
+ if (openIdx >= 0 && shouldSuppressAutoLink(tokens, openIdx)) return "";
289
+ return self.renderToken(tokens, idx, _options);
290
+ };
291
+ function maskEscapedMentions(markdown) {
292
+ let masked = "";
293
+ let idx = 0;
294
+ let codeFenceLength = 0;
295
+ while (idx < markdown.length) {
296
+ if (markdown[idx] === "`" && !isMarkdownEscaped(markdown, idx)) {
297
+ let runLength = 1;
298
+ while (markdown[idx + runLength] === "`") runLength += 1;
299
+ if (codeFenceLength === 0) codeFenceLength = runLength;
300
+ else if (runLength === codeFenceLength) codeFenceLength = 0;
301
+ masked += markdown.slice(idx, idx + runLength);
302
+ idx += runLength;
303
+ continue;
304
+ }
305
+ if (codeFenceLength === 0 && markdown[idx] === "\\" && markdown[idx + 1] === "@") {
306
+ masked += ESCAPED_MENTION_SENTINEL;
307
+ idx += 2;
308
+ continue;
309
+ }
310
+ masked += markdown[idx] ?? "";
311
+ idx += 1;
312
+ }
313
+ return masked;
314
+ }
315
+ function isMarkdownEscaped(markdown, idx) {
316
+ let slashCount = 0;
317
+ let cursor = idx - 1;
318
+ while (cursor >= 0 && markdown[cursor] === "\\") {
319
+ slashCount += 1;
320
+ cursor -= 1;
321
+ }
322
+ return slashCount % 2 === 1;
323
+ }
324
+ function restoreEscapedMentions(text) {
325
+ return text.replaceAll(ESCAPED_MENTION_SENTINEL, "@");
326
+ }
327
+ function restoreEscapedMentionsInCode(text) {
328
+ return text.replaceAll(ESCAPED_MENTION_SENTINEL, "\\@");
329
+ }
330
+ function restoreEscapedMentionsInBlockTokens(tokens) {
331
+ for (const token of tokens) if ((token.type === "fence" || token.type === "code_block") && token.content) token.content = restoreEscapedMentionsInCode(token.content);
332
+ }
333
+ function isMentionStartBoundary(charBefore) {
334
+ return !charBefore || !/[A-Za-z0-9_]/.test(charBefore);
335
+ }
336
+ function trimMentionSuffix(raw, end) {
337
+ while (raw.length > 1 && TRIMMABLE_MENTION_SUFFIX.test(raw.at(-1) ?? "")) {
338
+ if (raw.at(-1) === "]" && /\[[0-9A-Fa-f:.]+\](?::\d+)?$/i.test(raw)) break;
339
+ raw = raw.slice(0, -1);
340
+ end -= 1;
341
+ }
342
+ if (!raw.startsWith("@") || raw === "@") return null;
343
+ return {
344
+ raw,
345
+ end
346
+ };
347
+ }
348
+ function isMatrixMentionUserId(raw) {
349
+ return isMatrixQualifiedUserId(raw) && MATRIX_MENTION_USER_ID_PATTERN.test(raw);
350
+ }
351
+ function buildMentionCandidate(raw, start) {
352
+ const normalized = trimMentionSuffix(raw, start + raw.length);
353
+ if (!normalized) return null;
354
+ const kind = normalizeLowercaseStringOrEmpty(normalized.raw) === "@room" ? "room" : "user";
355
+ const base = {
356
+ raw: normalized.raw,
357
+ start,
358
+ end: normalized.end,
359
+ kind
360
+ };
361
+ if (kind === "room") return base;
362
+ const userCandidate = isMatrixMentionUserId(normalized.raw) ? {
363
+ ...base,
364
+ userId: normalized.raw
365
+ } : null;
366
+ if (!userCandidate) return null;
367
+ return userCandidate;
368
+ }
369
+ function collectMentionCandidates(text) {
370
+ const mentions = [];
371
+ for (const match of text.matchAll(MENTION_PATTERN)) {
372
+ const raw = match[0];
373
+ const start = match.index ?? -1;
374
+ if (start < 0 || !raw) continue;
375
+ if (!isMentionStartBoundary(text[start - 1])) continue;
376
+ const candidate = buildMentionCandidate(raw, start);
377
+ if (!candidate) continue;
378
+ mentions.push(candidate);
379
+ }
380
+ return mentions;
381
+ }
382
+ function createToken(sample, type, tag, nesting) {
383
+ const TokenCtor = sample.constructor;
384
+ return new TokenCtor(type, tag, nesting);
385
+ }
386
+ function createTextToken(sample, content) {
387
+ const token = createToken(sample, "text", "", 0);
388
+ token.content = content;
389
+ return token;
390
+ }
391
+ function createMentionLinkTokens(params) {
392
+ const open = createToken(params.sample, "link_open", "a", 1);
393
+ open.attrSet("href", params.href);
394
+ return [
395
+ open,
396
+ createTextToken(params.sample, params.label),
397
+ createToken(params.sample, "link_close", "a", -1)
398
+ ];
399
+ }
400
+ function resolveMentionUserId(match) {
401
+ if (match.kind !== "user") return null;
402
+ return match.userId ?? null;
403
+ }
404
+ async function resolveMatrixSelfUserId(client) {
405
+ const getUserId = client.getUserId;
406
+ if (typeof getUserId !== "function") return null;
407
+ return await Promise.resolve(getUserId.call(client)).catch(() => null);
408
+ }
409
+ function mutateInlineTokensWithMentions(params) {
410
+ const nextChildren = [];
411
+ let roomMentioned = false;
412
+ let insideLinkDepth = 0;
413
+ for (const child of params.children) {
414
+ if (child.type === "link_open") {
415
+ insideLinkDepth += 1;
416
+ nextChildren.push(child);
417
+ continue;
418
+ }
419
+ if (child.type === "link_close") {
420
+ insideLinkDepth = Math.max(0, insideLinkDepth - 1);
421
+ nextChildren.push(child);
422
+ continue;
423
+ }
424
+ if (child.type !== "text" || !child.content) {
425
+ nextChildren.push(child);
426
+ continue;
427
+ }
428
+ const visibleContent = restoreEscapedMentions(child.content);
429
+ if (insideLinkDepth > 0) {
430
+ nextChildren.push(createTextToken(child, visibleContent));
431
+ continue;
432
+ }
433
+ const matches = collectMentionCandidates(child.content);
434
+ if (matches.length === 0) {
435
+ nextChildren.push(createTextToken(child, visibleContent));
436
+ continue;
437
+ }
438
+ let cursor = 0;
439
+ for (const match of matches) {
440
+ if (match.start > cursor) nextChildren.push(createTextToken(child, restoreEscapedMentions(child.content.slice(cursor, match.start))));
441
+ cursor = match.end;
442
+ if (match.kind === "room") {
443
+ roomMentioned = true;
444
+ nextChildren.push(createTextToken(child, match.raw));
445
+ continue;
446
+ }
447
+ const resolvedUserId = resolveMentionUserId(match);
448
+ if (!resolvedUserId || resolvedUserId === params.selfUserId) {
449
+ nextChildren.push(createTextToken(child, match.raw));
450
+ continue;
451
+ }
452
+ if (!params.seenUserIds.has(resolvedUserId)) {
453
+ params.seenUserIds.add(resolvedUserId);
454
+ params.userIds.push(resolvedUserId);
455
+ }
456
+ nextChildren.push(...createMentionLinkTokens({
457
+ sample: child,
458
+ href: `https://matrix.to/#/${encodeURIComponent(resolvedUserId)}`,
459
+ label: match.raw
460
+ }));
461
+ }
462
+ if (cursor < child.content.length) nextChildren.push(createTextToken(child, restoreEscapedMentions(child.content.slice(cursor))));
463
+ }
464
+ return {
465
+ children: nextChildren,
466
+ roomMentioned
467
+ };
468
+ }
469
+ function compactLooseListTokens(tokens) {
470
+ const listItemStack = [];
471
+ for (const [index, token] of tokens.entries()) {
472
+ if (token.type === "list_item_open") {
473
+ listItemStack.push({
474
+ level: token.level,
475
+ immediateParagraphOpenIndexes: [],
476
+ immediateParagraphCloseIndexes: []
477
+ });
478
+ continue;
479
+ }
480
+ if (token.type === "list_item_close") {
481
+ const item = listItemStack.pop();
482
+ if (item && item.immediateParagraphOpenIndexes.length === 1 && item.immediateParagraphCloseIndexes.length === 1) {
483
+ tokens[item.immediateParagraphOpenIndexes[0]].hidden = true;
484
+ tokens[item.immediateParagraphCloseIndexes[0]].hidden = true;
485
+ }
486
+ continue;
487
+ }
488
+ const currentItem = listItemStack.at(-1);
489
+ if (!currentItem || token.level !== currentItem.level + 1) continue;
490
+ if (token.type === "paragraph_open") currentItem.immediateParagraphOpenIndexes.push(index);
491
+ else if (token.type === "paragraph_close") currentItem.immediateParagraphCloseIndexes.push(index);
492
+ }
493
+ }
494
+ function markdownToMatrixHtml(markdown) {
495
+ const tokens = md.parse(markdown ?? "", {});
496
+ compactLooseListTokens(tokens);
497
+ return md.renderer.render(tokens, md.options, {}).trimEnd();
498
+ }
499
+ async function resolveMarkdownMentionState(params) {
500
+ const markdown = maskEscapedMentions(params.markdown ?? "");
501
+ const tokens = md.parse(markdown, {});
502
+ restoreEscapedMentionsInBlockTokens(tokens);
503
+ const selfUserId = await resolveMatrixSelfUserId(params.client);
504
+ const userIds = [];
505
+ const seenUserIds = /* @__PURE__ */ new Set();
506
+ let roomMentioned = false;
507
+ for (const token of tokens) {
508
+ if (!token.children?.length) continue;
509
+ const mutated = mutateInlineTokensWithMentions({
510
+ children: token.children,
511
+ userIds,
512
+ seenUserIds,
513
+ selfUserId
514
+ });
515
+ token.children = mutated.children;
516
+ roomMentioned ||= mutated.roomMentioned;
517
+ }
518
+ const mentions = {};
519
+ if (userIds.length > 0) mentions.user_ids = userIds;
520
+ if (roomMentioned) mentions.room = true;
521
+ return {
522
+ tokens,
523
+ mentions
524
+ };
525
+ }
526
+ async function resolveMatrixMentionsInMarkdown(params) {
527
+ return (await resolveMarkdownMentionState(params)).mentions;
528
+ }
529
+ async function renderMarkdownToMatrixHtmlWithMentions(params) {
530
+ const state = await resolveMarkdownMentionState(params);
531
+ compactLooseListTokens(state.tokens);
532
+ return {
533
+ html: md.renderer.render(state.tokens, md.options, {}).trimEnd() || void 0,
534
+ mentions: state.mentions
535
+ };
536
+ }
537
+ //#endregion
538
+ //#region extensions/matrix/src/matrix/send/types.ts
539
+ const MsgType = {
540
+ Text: "m.text",
541
+ Image: "m.image",
542
+ Audio: "m.audio",
543
+ Video: "m.video",
544
+ File: "m.file",
545
+ Notice: "m.notice"
546
+ };
547
+ const RelationType = {
548
+ Annotation: MATRIX_ANNOTATION_RELATION_TYPE,
549
+ Replace: "m.replace",
550
+ Thread: "m.thread"
551
+ };
552
+ const EventType = {
553
+ Direct: "m.direct",
554
+ Reaction: MATRIX_REACTION_EVENT_TYPE,
555
+ RoomMessage: "m.room.message"
556
+ };
557
+ const MATRIX_KLAW_FINALIZED_PREVIEW_KEY = "com.klaw.finalized_preview";
558
+ /**
559
+ * MSC4357 live marker key.
560
+ * When present on event content, signals that the message is still being
561
+ * streamed (e.g. an LLM generating a response). Supporting clients render
562
+ * the message with a streaming animation until an edit without this marker
563
+ * arrives, indicating the stream is complete.
564
+ * @see https://github.com/matrix-org/matrix-spec-proposals/pull/4357
565
+ */
566
+ const MSC4357_LIVE_KEY = "org.matrix.msc4357.live";
567
+ //#endregion
568
+ //#region extensions/matrix/src/matrix/send/formatting.ts
569
+ const getCore$2 = () => getMatrixRuntime();
570
+ async function renderMatrixFormattedContent(params) {
571
+ const markdown = params.markdown ?? "";
572
+ if (params.includeMentions === false) return { html: markdownToMatrixHtml(markdown).trimEnd() || void 0 };
573
+ const { html, mentions } = await renderMarkdownToMatrixHtmlWithMentions({
574
+ markdown,
575
+ client: params.client
576
+ });
577
+ return {
578
+ html,
579
+ mentions
580
+ };
581
+ }
582
+ function buildTextContent(body, relation, opts = {}) {
583
+ const msgtype = opts.msgtype ?? MsgType.Text;
584
+ return relation ? {
585
+ msgtype,
586
+ body,
587
+ "m.relates_to": relation
588
+ } : {
589
+ msgtype,
590
+ body
591
+ };
592
+ }
593
+ async function enrichMatrixFormattedContent(params) {
594
+ const { html, mentions } = await renderMatrixFormattedContent({
595
+ client: params.client,
596
+ markdown: params.markdown,
597
+ includeMentions: params.includeMentions
598
+ });
599
+ if (mentions) params.content["m.mentions"] = mentions;
600
+ else delete params.content["m.mentions"];
601
+ if (!html) {
602
+ delete params.content.format;
603
+ delete params.content.formatted_body;
604
+ return;
605
+ }
606
+ params.content.format = "org.matrix.custom.html";
607
+ params.content.formatted_body = html;
608
+ }
609
+ async function resolveMatrixMentionsForBody(params) {
610
+ return await resolveMatrixMentionsInMarkdown({
611
+ markdown: params.body ?? "",
612
+ client: params.client
613
+ });
614
+ }
615
+ function normalizeMentionUserIds(value) {
616
+ return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [];
617
+ }
618
+ function extractMatrixMentions(content) {
619
+ const rawMentions = content?.["m.mentions"];
620
+ if (!rawMentions || typeof rawMentions !== "object") return {};
621
+ const mentions = rawMentions;
622
+ const normalized = {};
623
+ const userIds = normalizeMentionUserIds(mentions.user_ids);
624
+ if (userIds.length > 0) normalized.user_ids = userIds;
625
+ if (mentions.room === true) normalized.room = true;
626
+ return normalized;
627
+ }
628
+ function diffMatrixMentions(current, previous) {
629
+ const previousUserIds = new Set(previous.user_ids ?? []);
630
+ const newUserIds = (current.user_ids ?? []).filter((userId) => !previousUserIds.has(userId));
631
+ const delta = {};
632
+ if (newUserIds.length > 0) delta.user_ids = newUserIds;
633
+ if (current.room && !previous.room) delta.room = true;
634
+ return delta;
635
+ }
636
+ function buildReplyRelation(replyToId) {
637
+ const trimmed = replyToId?.trim();
638
+ if (!trimmed) return;
639
+ return { "m.in_reply_to": { event_id: trimmed } };
640
+ }
641
+ function buildThreadRelation(threadId, replyToId) {
642
+ const trimmed = threadId.trim();
643
+ return {
644
+ rel_type: RelationType.Thread,
645
+ event_id: trimmed,
646
+ is_falling_back: true,
647
+ "m.in_reply_to": { event_id: replyToId?.trim() || trimmed }
648
+ };
649
+ }
650
+ function resolveMatrixMsgType(contentType, _fileName) {
651
+ switch (getCore$2().media.mediaKindFromMime(contentType ?? "")) {
652
+ case "image": return MsgType.Image;
653
+ case "audio": return MsgType.Audio;
654
+ case "video": return MsgType.Video;
655
+ default: return MsgType.File;
656
+ }
657
+ }
658
+ function resolveMatrixVoiceDecision(opts) {
659
+ if (!opts.wantsVoice) return { useVoice: false };
660
+ if (isMatrixVoiceCompatibleAudio(opts)) return { useVoice: true };
661
+ return { useVoice: false };
662
+ }
663
+ function isMatrixVoiceCompatibleAudio(opts) {
664
+ return getCore$2().media.isVoiceCompatibleAudio({
665
+ contentType: opts.contentType,
666
+ fileName: opts.fileName
667
+ });
668
+ }
669
+ //#endregion
670
+ //#region extensions/matrix/src/matrix/send/media.ts
671
+ const getCore$1 = () => getMatrixRuntime();
672
+ function buildMatrixMediaInfo(params) {
673
+ const base = {};
674
+ if (Number.isFinite(params.size)) base.size = params.size;
675
+ if (params.mimetype) base.mimetype = params.mimetype;
676
+ if (params.imageInfo) {
677
+ const dimensional = {
678
+ ...base,
679
+ ...params.imageInfo
680
+ };
681
+ if (typeof params.durationMs === "number") return {
682
+ ...dimensional,
683
+ duration: params.durationMs
684
+ };
685
+ return dimensional;
686
+ }
687
+ if (typeof params.durationMs === "number") return {
688
+ ...base,
689
+ duration: params.durationMs
690
+ };
691
+ if (Object.keys(base).length === 0) return;
692
+ return base;
693
+ }
694
+ function buildMediaContent(params) {
695
+ const info = buildMatrixMediaInfo({
696
+ size: params.size,
697
+ mimetype: params.mimetype,
698
+ durationMs: params.durationMs,
699
+ imageInfo: params.imageInfo
700
+ });
701
+ const base = {
702
+ msgtype: params.msgtype,
703
+ body: params.body,
704
+ filename: params.filename,
705
+ info: info ?? void 0
706
+ };
707
+ if (!params.file && params.url) base.url = params.url;
708
+ if (params.file) base.file = params.file;
709
+ if (params.isVoice) {
710
+ base["org.matrix.msc3245.voice"] = {};
711
+ if (typeof params.durationMs === "number") base["org.matrix.msc1767.audio"] = { duration: params.durationMs };
712
+ }
713
+ if (params.relation) base["m.relates_to"] = params.relation;
714
+ return base;
715
+ }
716
+ const THUMBNAIL_MAX_SIDE = 800;
717
+ const THUMBNAIL_QUALITY = 80;
718
+ async function prepareImageInfo(params) {
719
+ const meta = await getCore$1().media.getImageMetadata(params.buffer).catch(() => null);
720
+ if (!meta) return;
721
+ const imageInfo = {
722
+ w: meta.width,
723
+ h: meta.height
724
+ };
725
+ if (Math.max(meta.width, meta.height) > THUMBNAIL_MAX_SIDE) try {
726
+ const thumbBuffer = await getCore$1().media.resizeToJpeg({
727
+ buffer: params.buffer,
728
+ maxSide: THUMBNAIL_MAX_SIDE,
729
+ quality: THUMBNAIL_QUALITY,
730
+ withoutEnlargement: true
731
+ });
732
+ const thumbMeta = await getCore$1().media.getImageMetadata(thumbBuffer).catch(() => null);
733
+ const result = await uploadMediaWithEncryption(params.client, thumbBuffer, {
734
+ contentType: "image/jpeg",
735
+ filename: "thumbnail.jpg",
736
+ encrypted: params.encrypted === true
737
+ });
738
+ if (result.file) imageInfo.thumbnail_file = result.file;
739
+ else imageInfo.thumbnail_url = result.url;
740
+ if (thumbMeta) imageInfo.thumbnail_info = {
741
+ w: thumbMeta.width,
742
+ h: thumbMeta.height,
743
+ mimetype: "image/jpeg",
744
+ size: thumbBuffer.byteLength
745
+ };
746
+ } catch {}
747
+ return imageInfo;
748
+ }
749
+ async function resolveMediaDurationMs(params) {
750
+ if (params.kind !== "audio" && params.kind !== "video") return;
751
+ try {
752
+ const fileInfo = params.contentType || params.fileName ? {
753
+ mimeType: params.contentType,
754
+ size: params.buffer.byteLength,
755
+ path: params.fileName
756
+ } : void 0;
757
+ const durationSeconds = (await parseBuffer(params.buffer, fileInfo, {
758
+ duration: true,
759
+ skipCovers: true
760
+ })).format.duration;
761
+ if (typeof durationSeconds === "number" && Number.isFinite(durationSeconds)) return Math.max(0, Math.round(durationSeconds * 1e3));
762
+ } catch {}
763
+ }
764
+ async function uploadFile(client, file, params) {
765
+ return await client.uploadContent(file, params.contentType, params.filename);
766
+ }
767
+ async function uploadMediaWithEncryption(client, buffer, params) {
768
+ if (params.encrypted && client.crypto) {
769
+ const encrypted = await client.crypto.encryptMedia(buffer);
770
+ const mxc = await client.uploadContent(encrypted.buffer, params.contentType, params.filename);
771
+ return {
772
+ url: mxc,
773
+ file: {
774
+ url: mxc,
775
+ ...encrypted.file
776
+ }
777
+ };
778
+ }
779
+ return { url: await uploadFile(client, buffer, params) };
780
+ }
781
+ /**
782
+ * Upload media with optional encryption for E2EE rooms.
783
+ */
784
+ async function uploadMediaMaybeEncrypted(client, roomId, buffer, params) {
785
+ const isEncrypted = Boolean(client.crypto && await client.crypto.isRoomEncrypted(roomId));
786
+ return await uploadMediaWithEncryption(client, buffer, {
787
+ ...params,
788
+ encrypted: isEncrypted
789
+ });
790
+ }
791
+ //#endregion
792
+ //#region extensions/matrix/src/matrix/direct-room.ts
793
+ var direct_room_exports = /* @__PURE__ */ __exportAll({
794
+ hasDirectMatrixMemberFlag: () => hasDirectMatrixMemberFlag,
795
+ inspectMatrixDirectRoomEvidence: () => inspectMatrixDirectRoomEvidence,
796
+ isStrictDirectMembership: () => isStrictDirectMembership,
797
+ isStrictDirectRoom: () => isStrictDirectRoom,
798
+ normalizeJoinedMatrixMembers: () => normalizeJoinedMatrixMembers,
799
+ readJoinedMatrixMembers: () => readJoinedMatrixMembers
800
+ });
801
+ function trimMaybeString(value) {
802
+ if (typeof value !== "string") return null;
803
+ const trimmed = value.trim();
804
+ return trimmed.length > 0 ? trimmed : null;
805
+ }
806
+ function normalizeJoinedMatrixMembers(joinedMembers) {
807
+ if (!Array.isArray(joinedMembers)) return [];
808
+ return joinedMembers.map((entry) => trimMaybeString(entry)).filter((entry) => Boolean(entry));
809
+ }
810
+ function isStrictDirectMembership(params) {
811
+ const selfUserId = trimMaybeString(params.selfUserId);
812
+ const remoteUserId = trimMaybeString(params.remoteUserId);
813
+ const joinedMembers = params.joinedMembers ?? [];
814
+ return Boolean(selfUserId && remoteUserId && joinedMembers.length === 2 && joinedMembers.includes(selfUserId) && joinedMembers.includes(remoteUserId));
815
+ }
816
+ async function readJoinedMatrixMembers(client, roomId) {
817
+ try {
818
+ return normalizeJoinedMatrixMembers(await client.getJoinedRoomMembers(roomId));
819
+ } catch {
820
+ return null;
821
+ }
822
+ }
823
+ async function hasDirectMatrixMemberFlag(client, roomId, userId) {
824
+ const normalizedUserId = trimMaybeString(userId);
825
+ if (!normalizedUserId) return null;
826
+ try {
827
+ const state = await client.getRoomStateEvent(roomId, "m.room.member", normalizedUserId);
828
+ if (state?.is_direct === true) return true;
829
+ if (state?.is_direct === false) return false;
830
+ return null;
831
+ } catch {
832
+ return null;
833
+ }
834
+ }
835
+ async function inspectMatrixDirectRoomEvidence(params) {
836
+ const selfUserId = params.selfUserId !== void 0 ? trimMaybeString(params.selfUserId) : trimMaybeString(await params.client.getUserId().catch(() => null));
837
+ const joinedMembers = await readJoinedMatrixMembers(params.client, params.roomId);
838
+ const strict = isStrictDirectMembership({
839
+ selfUserId,
840
+ remoteUserId: params.remoteUserId,
841
+ joinedMembers
842
+ });
843
+ if (!strict) return {
844
+ joinedMembers,
845
+ strict: false,
846
+ viaMemberState: false,
847
+ memberStateFlag: null
848
+ };
849
+ const memberStateFlag = await hasDirectMatrixMemberFlag(params.client, params.roomId, selfUserId);
850
+ return {
851
+ joinedMembers,
852
+ strict,
853
+ viaMemberState: memberStateFlag === true,
854
+ memberStateFlag
855
+ };
856
+ }
857
+ async function isStrictDirectRoom(params) {
858
+ return (await inspectMatrixDirectRoomEvidence({
859
+ client: params.client,
860
+ roomId: params.roomId,
861
+ remoteUserId: params.remoteUserId,
862
+ selfUserId: params.selfUserId
863
+ })).strict;
864
+ }
865
+ //#endregion
866
+ //#region extensions/matrix/src/matrix/direct-management.ts
867
+ var direct_management_exports = /* @__PURE__ */ __exportAll({
868
+ inspectMatrixDirectRooms: () => inspectMatrixDirectRooms,
869
+ persistMatrixDirectRoomMapping: () => persistMatrixDirectRoomMapping,
870
+ promoteMatrixDirectRoomCandidate: () => promoteMatrixDirectRoomCandidate,
871
+ repairMatrixDirectRooms: () => repairMatrixDirectRooms
872
+ });
873
+ const DIRECT_ACCOUNT_DATA_QUEUE_KEY = EventType.Direct;
874
+ const directAccountDataWriteQueues = /* @__PURE__ */ new WeakMap();
875
+ async function readMatrixDirectAccountData(client) {
876
+ try {
877
+ const direct = await client.getAccountData(EventType.Direct);
878
+ return direct && typeof direct === "object" && !Array.isArray(direct) ? direct : {};
879
+ } catch {
880
+ return {};
881
+ }
882
+ }
883
+ function normalizeRemoteUserId(remoteUserId) {
884
+ const normalized = normalizeOptionalString(remoteUserId) ?? "";
885
+ if (!isMatrixQualifiedUserId(normalized)) throw new Error(`Matrix user IDs must be fully qualified (got "${remoteUserId}")`);
886
+ return normalized;
887
+ }
888
+ function normalizeMappedRoomIds(direct, remoteUserId) {
889
+ const current = direct[remoteUserId];
890
+ if (!Array.isArray(current)) return [];
891
+ const seen = /* @__PURE__ */ new Set();
892
+ const normalized = [];
893
+ for (const value of current) {
894
+ const roomId = normalizeOptionalString(value) ?? "";
895
+ if (!roomId || seen.has(roomId)) continue;
896
+ seen.add(roomId);
897
+ normalized.push(roomId);
898
+ }
899
+ return normalized;
900
+ }
901
+ function normalizeRoomIdList(values) {
902
+ const seen = /* @__PURE__ */ new Set();
903
+ const normalized = [];
904
+ for (const value of values) {
905
+ const roomId = value.trim();
906
+ if (!roomId || seen.has(roomId)) continue;
907
+ seen.add(roomId);
908
+ normalized.push(roomId);
909
+ }
910
+ return normalized;
911
+ }
912
+ function hasMatrixDirectRoomMappings(params) {
913
+ const current = normalizeMappedRoomIds(params.directContent, params.remoteUserId);
914
+ const next = normalizeRoomIdList([...params.roomIds, ...current]);
915
+ return current.length === next.length && current.every((roomId, index) => roomId === next[index]);
916
+ }
917
+ function resolveDirectAccountDataWriteQueue(client) {
918
+ const existing = directAccountDataWriteQueues.get(client);
919
+ if (existing) return existing;
920
+ const created = new KeyedAsyncQueue();
921
+ directAccountDataWriteQueues.set(client, created);
922
+ return created;
923
+ }
924
+ async function writeMatrixDirectRoomMappings(params) {
925
+ return await resolveDirectAccountDataWriteQueue(params.client).enqueue(DIRECT_ACCOUNT_DATA_QUEUE_KEY, async () => {
926
+ const directContentBefore = await readMatrixDirectAccountData(params.client);
927
+ const directContentAfter = buildNextDirectContent({
928
+ directContent: directContentBefore,
929
+ remoteUserId: params.remoteUserId,
930
+ roomIds: params.roomIds
931
+ });
932
+ const changed = !hasMatrixDirectRoomMappings({
933
+ directContent: directContentBefore,
934
+ remoteUserId: params.remoteUserId,
935
+ roomIds: params.roomIds
936
+ });
937
+ if (changed) await params.client.setAccountData(EventType.Direct, directContentAfter);
938
+ return {
939
+ changed,
940
+ directContentBefore,
941
+ directContentAfter
942
+ };
943
+ });
944
+ }
945
+ async function classifyDirectRoomCandidate(params) {
946
+ const evidence = await inspectMatrixDirectRoomEvidence({
947
+ client: params.client,
948
+ roomId: params.roomId,
949
+ remoteUserId: params.remoteUserId,
950
+ selfUserId: params.selfUserId
951
+ });
952
+ return {
953
+ roomId: params.roomId,
954
+ joinedMembers: evidence.joinedMembers,
955
+ strict: evidence.strict && (params.source === "account-data" || evidence.memberStateFlag !== false),
956
+ explicit: evidence.strict && (params.source === "account-data" || evidence.memberStateFlag !== false) && (params.source === "account-data" || evidence.viaMemberState),
957
+ source: params.source
958
+ };
959
+ }
960
+ function buildNextDirectContent(params) {
961
+ const current = normalizeMappedRoomIds(params.directContent, params.remoteUserId);
962
+ const nextRooms = normalizeRoomIdList([...params.roomIds, ...current]);
963
+ return {
964
+ ...params.directContent,
965
+ [params.remoteUserId]: nextRooms
966
+ };
967
+ }
968
+ async function persistMatrixDirectRoomMapping(params) {
969
+ const remoteUserId = normalizeRemoteUserId(params.remoteUserId);
970
+ return (await writeMatrixDirectRoomMappings({
971
+ client: params.client,
972
+ remoteUserId,
973
+ roomIds: [params.roomId]
974
+ })).changed;
975
+ }
976
+ async function promoteMatrixDirectRoomCandidate(params) {
977
+ const remoteUserId = normalizeRemoteUserId(params.remoteUserId);
978
+ const evidence = await inspectMatrixDirectRoomEvidence({
979
+ client: params.client,
980
+ roomId: params.roomId,
981
+ remoteUserId,
982
+ selfUserId: params.selfUserId
983
+ });
984
+ if (!evidence.strict) return {
985
+ classifyAsDirect: false,
986
+ repaired: false,
987
+ reason: "not-strict"
988
+ };
989
+ if (evidence.memberStateFlag === false) return {
990
+ classifyAsDirect: false,
991
+ repaired: false,
992
+ reason: "local-explicit-false"
993
+ };
994
+ try {
995
+ const repaired = await persistMatrixDirectRoomMapping({
996
+ client: params.client,
997
+ remoteUserId,
998
+ roomId: params.roomId
999
+ });
1000
+ return {
1001
+ classifyAsDirect: true,
1002
+ repaired,
1003
+ roomId: params.roomId,
1004
+ reason: repaired ? "promoted" : "already-mapped"
1005
+ };
1006
+ } catch {
1007
+ return {
1008
+ classifyAsDirect: true,
1009
+ repaired: false,
1010
+ roomId: params.roomId,
1011
+ reason: "repair-failed"
1012
+ };
1013
+ }
1014
+ }
1015
+ async function inspectMatrixDirectRooms(params) {
1016
+ const remoteUserId = normalizeRemoteUserId(params.remoteUserId);
1017
+ const selfUserId = normalizeOptionalString(await params.client.getUserId().catch(() => null)) ?? null;
1018
+ const mappedRoomIds = normalizeMappedRoomIds(await readMatrixDirectAccountData(params.client), remoteUserId);
1019
+ const mappedRooms = await Promise.all(mappedRoomIds.map(async (roomId) => await classifyDirectRoomCandidate({
1020
+ client: params.client,
1021
+ roomId,
1022
+ remoteUserId,
1023
+ selfUserId,
1024
+ source: "account-data"
1025
+ })));
1026
+ const mappedStrict = mappedRooms.find((room) => room.strict);
1027
+ let joinedRooms = [];
1028
+ if (typeof params.client.getJoinedRooms === "function") try {
1029
+ const resolved = await params.client.getJoinedRooms();
1030
+ joinedRooms = Array.isArray(resolved) ? resolved : [];
1031
+ } catch {
1032
+ joinedRooms = [];
1033
+ }
1034
+ const discoveredStrictRooms = [];
1035
+ for (const roomId of normalizeRoomIdList(joinedRooms)) {
1036
+ if (mappedRoomIds.includes(roomId)) continue;
1037
+ const candidate = await classifyDirectRoomCandidate({
1038
+ client: params.client,
1039
+ roomId,
1040
+ remoteUserId,
1041
+ selfUserId,
1042
+ source: "joined"
1043
+ });
1044
+ if (candidate.strict) discoveredStrictRooms.push(candidate);
1045
+ }
1046
+ const discoveredStrictRoomIds = discoveredStrictRooms.map((room) => room.roomId);
1047
+ const discoveredExplicit = discoveredStrictRooms.find((room) => room.explicit);
1048
+ return {
1049
+ selfUserId,
1050
+ remoteUserId,
1051
+ mappedRoomIds,
1052
+ mappedRooms,
1053
+ discoveredStrictRoomIds,
1054
+ activeRoomId: mappedStrict?.roomId ?? discoveredExplicit?.roomId ?? discoveredStrictRoomIds[0] ?? null
1055
+ };
1056
+ }
1057
+ async function repairMatrixDirectRooms(params) {
1058
+ const remoteUserId = normalizeRemoteUserId(params.remoteUserId);
1059
+ const inspected = await inspectMatrixDirectRooms({
1060
+ client: params.client,
1061
+ remoteUserId
1062
+ });
1063
+ const activeRoomId = inspected.activeRoomId ?? await params.client.createDirectRoom(remoteUserId, { encrypted: params.encrypted === true });
1064
+ const createdRoomId = inspected.activeRoomId ? null : activeRoomId;
1065
+ const mappingWrite = await writeMatrixDirectRoomMappings({
1066
+ client: params.client,
1067
+ remoteUserId,
1068
+ roomIds: [activeRoomId, ...inspected.discoveredStrictRoomIds]
1069
+ });
1070
+ return {
1071
+ ...inspected,
1072
+ activeRoomId,
1073
+ createdRoomId,
1074
+ changed: mappingWrite.changed,
1075
+ directContentBefore: mappingWrite.directContentBefore,
1076
+ directContentAfter: mappingWrite.directContentAfter
1077
+ };
1078
+ }
1079
+ //#endregion
1080
+ //#region extensions/matrix/src/matrix/send/targets.ts
1081
+ function normalizeTarget(raw) {
1082
+ const trimmed = raw.trim();
1083
+ if (!trimmed) throw new Error("Matrix target is required (room:<id> or #alias)");
1084
+ return trimmed;
1085
+ }
1086
+ function normalizeThreadId(raw) {
1087
+ return normalizeOptionalStringifiedId(raw) ?? null;
1088
+ }
1089
+ const MAX_DIRECT_ROOM_CACHE_SIZE = 1024;
1090
+ const directRoomCacheByClient = /* @__PURE__ */ new WeakMap();
1091
+ function resolveDirectRoomCache(client) {
1092
+ const existing = directRoomCacheByClient.get(client);
1093
+ if (existing) return existing;
1094
+ const created = /* @__PURE__ */ new Map();
1095
+ directRoomCacheByClient.set(client, created);
1096
+ return created;
1097
+ }
1098
+ function setDirectRoomCached(client, key, value) {
1099
+ const directRoomCache = resolveDirectRoomCache(client);
1100
+ directRoomCache.set(key, value);
1101
+ if (directRoomCache.size > MAX_DIRECT_ROOM_CACHE_SIZE) {
1102
+ const oldest = directRoomCache.keys().next().value;
1103
+ if (oldest !== void 0) directRoomCache.delete(oldest);
1104
+ }
1105
+ }
1106
+ async function resolveDirectRoomId(client, userId) {
1107
+ const trimmed = userId.trim();
1108
+ if (!isMatrixQualifiedUserId(trimmed)) throw new Error(`Matrix user IDs must be fully qualified (got "${trimmed}")`);
1109
+ const selfUserId = (await client.getUserId().catch(() => null))?.trim() || null;
1110
+ const directRoomCache = resolveDirectRoomCache(client);
1111
+ const cached = directRoomCache.get(trimmed);
1112
+ if (cached && await isStrictDirectRoom({
1113
+ client,
1114
+ roomId: cached,
1115
+ remoteUserId: trimmed,
1116
+ selfUserId
1117
+ })) return cached;
1118
+ if (cached) directRoomCache.delete(trimmed);
1119
+ const inspection = await inspectMatrixDirectRooms({
1120
+ client,
1121
+ remoteUserId: trimmed
1122
+ });
1123
+ if (inspection.activeRoomId) {
1124
+ setDirectRoomCached(client, trimmed, inspection.activeRoomId);
1125
+ if (inspection.mappedRoomIds[0] !== inspection.activeRoomId) await persistMatrixDirectRoomMapping({
1126
+ client,
1127
+ remoteUserId: trimmed,
1128
+ roomId: inspection.activeRoomId
1129
+ }).catch(() => {});
1130
+ return inspection.activeRoomId;
1131
+ }
1132
+ throw new Error(`No direct room found for ${trimmed} (m.direct missing)`);
1133
+ }
1134
+ async function resolveMatrixRoomId(client, raw) {
1135
+ const target = normalizeMatrixResolvableTarget(normalizeTarget(raw));
1136
+ if (normalizeLowercaseStringOrEmpty(target).startsWith("user:")) return await resolveDirectRoomId(client, target.slice(5));
1137
+ if (isMatrixQualifiedUserId(target)) return await resolveDirectRoomId(client, target);
1138
+ if (target.startsWith("#")) {
1139
+ const resolved = await client.resolveRoom(target);
1140
+ if (!resolved) throw new Error(`Matrix alias ${target} could not be resolved`);
1141
+ return resolved;
1142
+ }
1143
+ return target;
1144
+ }
1145
+ //#endregion
1146
+ //#region extensions/matrix/src/matrix/send.ts
1147
+ var send_exports = /* @__PURE__ */ __exportAll({
1148
+ chunkMatrixText: () => chunkMatrixText,
1149
+ editMessageMatrix: () => editMessageMatrix,
1150
+ prepareMatrixSingleText: () => prepareMatrixSingleText,
1151
+ reactMatrixMessage: () => reactMatrixMessage,
1152
+ resolveMatrixRoomId: () => resolveMatrixRoomId,
1153
+ sendMessageMatrix: () => sendMessageMatrix,
1154
+ sendPollMatrix: () => sendPollMatrix,
1155
+ sendReadReceiptMatrix: () => sendReadReceiptMatrix,
1156
+ sendSingleTextMessageMatrix: () => sendSingleTextMessageMatrix,
1157
+ sendTypingMatrix: () => sendTypingMatrix
1158
+ });
1159
+ const MATRIX_TEXT_LIMIT = 4e3;
1160
+ const getCore = () => getMatrixRuntime();
1161
+ function createMatrixSendReceipt(params) {
1162
+ return createMessageReceiptFromOutboundResults({
1163
+ kind: params.kind,
1164
+ ...params.replyToId ? { replyToId: params.replyToId } : {},
1165
+ ...params.threadId ? { threadId: params.threadId } : {},
1166
+ results: params.platformMessageIds.map((messageId) => ({
1167
+ channel: "matrix",
1168
+ messageId,
1169
+ roomId: params.roomId
1170
+ }))
1171
+ });
1172
+ }
1173
+ function isMatrixClient(value) {
1174
+ return typeof value.sendEvent === "function";
1175
+ }
1176
+ function normalizeMatrixClientResolveOpts(opts) {
1177
+ if (!opts) return {};
1178
+ if (isMatrixClient(opts)) return { client: opts };
1179
+ return {
1180
+ client: opts.client,
1181
+ cfg: opts.cfg,
1182
+ timeoutMs: opts.timeoutMs,
1183
+ accountId: opts.accountId
1184
+ };
1185
+ }
1186
+ function resolvePreviousEditContent(previousEvent) {
1187
+ if (!previousEvent || typeof previousEvent !== "object") return;
1188
+ const eventRecord = previousEvent;
1189
+ if (!eventRecord.content || typeof eventRecord.content !== "object") return;
1190
+ const content = eventRecord.content;
1191
+ const newContent = content["m.new_content"];
1192
+ return newContent && typeof newContent === "object" ? newContent : content;
1193
+ }
1194
+ function hasMatrixMentionsMetadata(content) {
1195
+ return Boolean(content && Object.hasOwn(content, "m.mentions"));
1196
+ }
1197
+ function withMatrixExtraContentFields(content, extraContent) {
1198
+ if (!extraContent) return content;
1199
+ return {
1200
+ ...content,
1201
+ ...extraContent
1202
+ };
1203
+ }
1204
+ async function resolvePreviousEditMentions(params) {
1205
+ if (hasMatrixMentionsMetadata(params.content)) return extractMatrixMentions(params.content);
1206
+ const body = typeof params.content?.body === "string" ? params.content.body : "";
1207
+ if (!body) return {};
1208
+ return await resolveMatrixMentionsForBody({
1209
+ client: params.client,
1210
+ body
1211
+ });
1212
+ }
1213
+ function prepareMatrixSingleText(text, opts) {
1214
+ const trimmedText = text.trim();
1215
+ const cfg = requireRuntimeConfig(opts.cfg, "Matrix text preparation");
1216
+ const tableMode = opts.tableMode ?? getCore().channel.text.resolveMarkdownTableMode({
1217
+ cfg,
1218
+ channel: "matrix",
1219
+ accountId: opts.accountId
1220
+ });
1221
+ const convertedText = getCore().channel.text.convertMarkdownTables(trimmedText, tableMode);
1222
+ const singleEventLimit = Math.min(getCore().channel.text.resolveTextChunkLimit(cfg, "matrix", opts.accountId), MATRIX_TEXT_LIMIT);
1223
+ return {
1224
+ trimmedText,
1225
+ convertedText,
1226
+ singleEventLimit,
1227
+ fitsInSingleEvent: convertedText.length <= singleEventLimit
1228
+ };
1229
+ }
1230
+ function chunkMatrixText(text, opts) {
1231
+ const preparedText = prepareMatrixSingleText(text, opts);
1232
+ const cfg = requireRuntimeConfig(opts.cfg, "Matrix text chunking");
1233
+ const chunkMode = getCore().channel.text.resolveChunkMode(cfg, "matrix", opts.accountId);
1234
+ return {
1235
+ ...preparedText,
1236
+ chunks: getCore().channel.text.chunkMarkdownTextWithMode(preparedText.convertedText, preparedText.singleEventLimit, chunkMode)
1237
+ };
1238
+ }
1239
+ async function sendMessageMatrix(to, message, opts) {
1240
+ const trimmedMessage = message?.trim() ?? "";
1241
+ if (!trimmedMessage && !opts.mediaUrl) throw new Error("Matrix send requires text or media");
1242
+ return await withResolvedMatrixSendClient({
1243
+ client: opts.client,
1244
+ cfg: opts.cfg,
1245
+ timeoutMs: opts.timeoutMs,
1246
+ accountId: opts.accountId
1247
+ }, async (client) => {
1248
+ const roomId = await resolveMatrixRoomId(client, to);
1249
+ const cfg = requireRuntimeConfig(opts.cfg, "Matrix send");
1250
+ const { chunks } = chunkMatrixText(trimmedMessage, {
1251
+ cfg,
1252
+ accountId: opts.accountId
1253
+ });
1254
+ const threadId = normalizeThreadId(opts.threadId);
1255
+ const relation = threadId ? buildThreadRelation(threadId, opts.replyToId) : buildReplyRelation(opts.replyToId);
1256
+ let pendingExtraContent = opts.extraContent;
1257
+ const sendContent = async (content) => {
1258
+ const contentWithExtra = withMatrixExtraContentFields(content, pendingExtraContent);
1259
+ pendingExtraContent = void 0;
1260
+ return await client.sendMessage(roomId, contentWithExtra);
1261
+ };
1262
+ const platformMessageIds = [];
1263
+ let lastMessageId = "";
1264
+ let receiptKind = "text";
1265
+ if (opts.mediaUrl) {
1266
+ const maxBytes = resolveMediaMaxBytes(opts.accountId, cfg);
1267
+ const media = await loadOutboundMediaFromUrl(opts.mediaUrl, {
1268
+ maxBytes,
1269
+ mediaAccess: opts.mediaAccess,
1270
+ mediaLocalRoots: opts.mediaLocalRoots,
1271
+ mediaReadFile: opts.mediaReadFile
1272
+ });
1273
+ const uploaded = await uploadMediaMaybeEncrypted(client, roomId, media.buffer, {
1274
+ contentType: media.contentType,
1275
+ filename: media.fileName
1276
+ });
1277
+ const durationMs = await resolveMediaDurationMs({
1278
+ buffer: media.buffer,
1279
+ contentType: media.contentType,
1280
+ fileName: media.fileName,
1281
+ kind: media.kind ?? "unknown"
1282
+ });
1283
+ const baseMsgType = resolveMatrixMsgType(media.contentType, media.fileName);
1284
+ const { useVoice } = resolveMatrixVoiceDecision({
1285
+ wantsVoice: opts.audioAsVoice === true,
1286
+ contentType: media.contentType,
1287
+ fileName: media.fileName
1288
+ });
1289
+ const msgtype = useVoice ? MsgType.Audio : baseMsgType;
1290
+ receiptKind = useVoice ? "voice" : "media";
1291
+ const imageInfo = msgtype === MsgType.Image ? await prepareImageInfo({
1292
+ buffer: media.buffer,
1293
+ client,
1294
+ encrypted: Boolean(uploaded.file)
1295
+ }) : void 0;
1296
+ const [firstChunk, ...rest] = chunks;
1297
+ const captionMarkdown = useVoice ? "" : firstChunk ?? "";
1298
+ const content = buildMediaContent({
1299
+ msgtype,
1300
+ body: useVoice ? "Voice message" : captionMarkdown || media.fileName || "(file)",
1301
+ url: uploaded.url,
1302
+ file: uploaded.file,
1303
+ filename: media.fileName,
1304
+ mimetype: media.contentType,
1305
+ size: media.buffer.byteLength,
1306
+ durationMs,
1307
+ relation,
1308
+ isVoice: useVoice,
1309
+ imageInfo
1310
+ });
1311
+ await enrichMatrixFormattedContent({
1312
+ client,
1313
+ content,
1314
+ markdown: captionMarkdown
1315
+ });
1316
+ const eventId = await sendContent(content);
1317
+ lastMessageId = eventId ?? lastMessageId;
1318
+ if (eventId) platformMessageIds.push(eventId);
1319
+ const textChunks = useVoice ? chunks : rest;
1320
+ const followupRelation = useVoice || threadId ? relation : void 0;
1321
+ for (const chunk of textChunks) {
1322
+ const text = chunk.trim();
1323
+ if (!text) continue;
1324
+ const followup = buildTextContent(text, followupRelation);
1325
+ await enrichMatrixFormattedContent({
1326
+ client,
1327
+ content: followup,
1328
+ markdown: text
1329
+ });
1330
+ const followupEventId = await sendContent(followup);
1331
+ lastMessageId = followupEventId ?? lastMessageId;
1332
+ if (followupEventId) platformMessageIds.push(followupEventId);
1333
+ }
1334
+ } else for (const chunk of chunks.length ? chunks : [""]) {
1335
+ const text = chunk.trim();
1336
+ if (!text) continue;
1337
+ const content = buildTextContent(text, relation);
1338
+ await enrichMatrixFormattedContent({
1339
+ client,
1340
+ content,
1341
+ markdown: text
1342
+ });
1343
+ const eventId = await sendContent(content);
1344
+ lastMessageId = eventId ?? lastMessageId;
1345
+ if (eventId) platformMessageIds.push(eventId);
1346
+ }
1347
+ return {
1348
+ messageId: lastMessageId || "unknown",
1349
+ roomId,
1350
+ primaryMessageId: platformMessageIds[0] ?? (lastMessageId || "unknown"),
1351
+ receipt: createMatrixSendReceipt({
1352
+ roomId,
1353
+ platformMessageIds,
1354
+ kind: receiptKind,
1355
+ replyToId: opts.replyToId,
1356
+ threadId
1357
+ })
1358
+ };
1359
+ });
1360
+ }
1361
+ async function sendPollMatrix(to, poll, opts) {
1362
+ if (!poll.question?.trim()) throw new Error("Matrix poll requires a question");
1363
+ if (!poll.options?.length) throw new Error("Matrix poll requires options");
1364
+ return await withResolvedMatrixSendClient({
1365
+ client: opts.client,
1366
+ cfg: opts.cfg,
1367
+ timeoutMs: opts.timeoutMs,
1368
+ accountId: opts.accountId
1369
+ }, async (client) => {
1370
+ const roomId = await resolveMatrixRoomId(client, to);
1371
+ const pollContent = buildPollStartContent(poll);
1372
+ const mentions = await resolveMatrixMentionsForBody({
1373
+ client,
1374
+ body: pollContent["m.text"] ?? pollContent["org.matrix.msc1767.text"] ?? poll.question ?? ""
1375
+ });
1376
+ const threadId = normalizeThreadId(opts.threadId);
1377
+ const pollPayload = threadId ? {
1378
+ ...pollContent,
1379
+ "m.relates_to": buildThreadRelation(threadId)
1380
+ } : { ...pollContent };
1381
+ pollPayload["m.mentions"] = mentions;
1382
+ return {
1383
+ eventId: await client.sendEvent(roomId, "m.poll.start", pollPayload) ?? "unknown",
1384
+ roomId
1385
+ };
1386
+ });
1387
+ }
1388
+ async function sendTypingMatrix(roomId, typing, optsOrTimeoutMs, client) {
1389
+ const opts = typeof optsOrTimeoutMs === "number" ? {
1390
+ timeoutMs: optsOrTimeoutMs,
1391
+ ...client ? { client } : {}
1392
+ } : {
1393
+ ...normalizeMatrixClientResolveOpts(optsOrTimeoutMs),
1394
+ ...client ? { client } : {}
1395
+ };
1396
+ await withResolvedMatrixControlClient({
1397
+ client: opts.client,
1398
+ cfg: opts.cfg,
1399
+ timeoutMs: opts.timeoutMs,
1400
+ accountId: opts.accountId
1401
+ }, async (resolved) => {
1402
+ const resolvedRoom = await resolveMatrixRoomId(resolved, roomId);
1403
+ const resolvedTimeoutMs = typeof opts.timeoutMs === "number" ? opts.timeoutMs : 3e4;
1404
+ await resolved.setTyping(resolvedRoom, typing, resolvedTimeoutMs);
1405
+ });
1406
+ }
1407
+ async function sendReadReceiptMatrix(roomId, eventId, client) {
1408
+ if (!eventId?.trim()) return;
1409
+ await withResolvedMatrixControlClient({ client }, async (resolved) => {
1410
+ const resolvedRoom = await resolveMatrixRoomId(resolved, roomId);
1411
+ await resolved.sendReadReceipt(resolvedRoom, eventId.trim());
1412
+ });
1413
+ }
1414
+ async function sendSingleTextMessageMatrix(roomId, text, opts) {
1415
+ const { trimmedText, convertedText, singleEventLimit, fitsInSingleEvent } = prepareMatrixSingleText(text, {
1416
+ cfg: opts.cfg,
1417
+ accountId: opts.accountId
1418
+ });
1419
+ if (!trimmedText) throw new Error("Matrix single-message send requires text");
1420
+ if (!fitsInSingleEvent) throw new Error(`Matrix single-message text exceeds limit (${convertedText.length} > ${singleEventLimit})`);
1421
+ return await withResolvedMatrixSendClient({
1422
+ client: opts.client,
1423
+ cfg: opts.cfg,
1424
+ accountId: opts.accountId
1425
+ }, async (client) => {
1426
+ const resolvedRoom = await resolveMatrixRoomId(client, roomId);
1427
+ const normalizedThreadId = normalizeThreadId(opts.threadId);
1428
+ const content = withMatrixExtraContentFields(buildTextContent(convertedText, normalizedThreadId ? buildThreadRelation(normalizedThreadId, opts.replyToId) : buildReplyRelation(opts.replyToId), { msgtype: opts.msgtype }), opts.extraContent);
1429
+ await enrichMatrixFormattedContent({
1430
+ client,
1431
+ content,
1432
+ markdown: convertedText,
1433
+ includeMentions: opts.includeMentions
1434
+ });
1435
+ if (opts.live) content[MSC4357_LIVE_KEY] = {};
1436
+ const eventId = await client.sendMessage(resolvedRoom, content);
1437
+ return {
1438
+ messageId: eventId ?? "unknown",
1439
+ roomId: resolvedRoom,
1440
+ primaryMessageId: eventId ?? "unknown",
1441
+ receipt: createMatrixSendReceipt({
1442
+ roomId: resolvedRoom,
1443
+ platformMessageIds: eventId ? [eventId] : [],
1444
+ kind: "text",
1445
+ replyToId: opts.replyToId,
1446
+ threadId: normalizedThreadId
1447
+ })
1448
+ };
1449
+ });
1450
+ }
1451
+ async function getPreviousMatrixEvent(client, roomId, eventId) {
1452
+ const getEvent = client.getEvent;
1453
+ if (typeof getEvent !== "function") return null;
1454
+ return await Promise.resolve(getEvent.call(client, roomId, eventId)).catch(() => null);
1455
+ }
1456
+ async function editMessageMatrix(roomId, originalEventId, newText, opts) {
1457
+ return await withResolvedMatrixSendClient({
1458
+ client: opts.client,
1459
+ cfg: opts.cfg,
1460
+ accountId: opts.accountId,
1461
+ timeoutMs: opts.timeoutMs
1462
+ }, async (client) => {
1463
+ const resolvedRoom = await resolveMatrixRoomId(client, roomId);
1464
+ const cfg = requireRuntimeConfig(opts.cfg, "Matrix message edit");
1465
+ const tableMode = getCore().channel.text.resolveMarkdownTableMode({
1466
+ cfg,
1467
+ channel: "matrix",
1468
+ accountId: opts.accountId
1469
+ });
1470
+ const convertedText = getCore().channel.text.convertMarkdownTables(newText, tableMode);
1471
+ const newContent = withMatrixExtraContentFields(buildTextContent(convertedText, void 0, { msgtype: opts.msgtype }), opts.extraContent);
1472
+ await enrichMatrixFormattedContent({
1473
+ client,
1474
+ content: newContent,
1475
+ markdown: convertedText,
1476
+ includeMentions: opts.includeMentions
1477
+ });
1478
+ const replaceMentions = opts.includeMentions === false ? void 0 : diffMatrixMentions(extractMatrixMentions(newContent), await resolvePreviousEditMentions({
1479
+ client,
1480
+ content: resolvePreviousEditContent(await getPreviousMatrixEvent(client, resolvedRoom, originalEventId))
1481
+ }));
1482
+ const replaceRelation = {
1483
+ rel_type: RelationType.Replace,
1484
+ event_id: originalEventId
1485
+ };
1486
+ const threadId = normalizeThreadId(opts.threadId);
1487
+ if (threadId) replaceRelation["m.in_reply_to"] = { event_id: threadId };
1488
+ const content = {
1489
+ ...newContent,
1490
+ body: `* ${convertedText}`,
1491
+ ...typeof newContent.formatted_body === "string" ? { formatted_body: `* ${newContent.formatted_body}` } : {},
1492
+ "m.new_content": newContent,
1493
+ "m.relates_to": replaceRelation
1494
+ };
1495
+ if (replaceMentions !== void 0) content["m.mentions"] = replaceMentions;
1496
+ if (opts.live) {
1497
+ content[MSC4357_LIVE_KEY] = {};
1498
+ content["m.new_content"][MSC4357_LIVE_KEY] = {};
1499
+ }
1500
+ return await client.sendMessage(resolvedRoom, content) ?? "";
1501
+ });
1502
+ }
1503
+ async function reactMatrixMessage(roomId, messageId, emoji, opts) {
1504
+ const clientOpts = normalizeMatrixClientResolveOpts(opts);
1505
+ await withResolvedMatrixSendClient({
1506
+ client: clientOpts.client,
1507
+ cfg: clientOpts.cfg,
1508
+ timeoutMs: clientOpts.timeoutMs,
1509
+ accountId: clientOpts.accountId ?? void 0
1510
+ }, async (resolved) => {
1511
+ const resolvedRoom = await resolveMatrixRoomId(resolved, roomId);
1512
+ const reaction = buildMatrixReactionContent(messageId, emoji);
1513
+ await resolved.sendEvent(resolvedRoom, EventType.Reaction, reaction);
1514
+ });
1515
+ }
1516
+ //#endregion
1517
+ export { formatPollResultsAsText as C, parsePollStartContent as D, parsePollStart as E, resolvePollReferenceEventId as O, formatPollAsText as S, isPollStartType as T, readJoinedMatrixMembers as _, sendMessageMatrix as a, buildPollResponseContent as b, sendTypingMatrix as c, direct_management_exports as d, promoteMatrixDirectRoomCandidate as f, isStrictDirectMembership as g, hasDirectMatrixMemberFlag as h, reactMatrixMessage as i, send_exports as l, direct_room_exports as m, editMessageMatrix as n, sendPollMatrix as o, repairMatrixDirectRooms as p, prepareMatrixSingleText as r, sendSingleTextMessageMatrix as s, chunkMatrixText as t, resolveMatrixRoomId as u, MATRIX_KLAW_FINALIZED_PREVIEW_KEY as v, isPollEventType as w, buildPollResultsSummary as x, MsgType as y };