@kodelyth/matrix 2026.5.42 → 2026.6.2

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 (205) hide show
  1. package/klaw.plugin.json +891 -3
  2. package/package.json +18 -6
  3. package/CHANGELOG.md +0 -321
  4. package/SPEC-SUPPORT.md +0 -116
  5. package/api.ts +0 -38
  6. package/auth-presence.ts +0 -56
  7. package/channel-plugin-api.ts +0 -3
  8. package/cli-metadata.ts +0 -11
  9. package/contract-api.ts +0 -17
  10. package/doctor-contract-api.ts +0 -1
  11. package/helper-api.ts +0 -3
  12. package/index.ts +0 -55
  13. package/plugin-entry.handlers.runtime.ts +0 -1
  14. package/runtime-api.ts +0 -72
  15. package/runtime-heavy-api.ts +0 -1
  16. package/runtime-setter-api.ts +0 -3
  17. package/secret-contract-api.ts +0 -5
  18. package/setup-entry.ts +0 -17
  19. package/setup-plugin-api.ts +0 -3
  20. package/src/account-selection.ts +0 -223
  21. package/src/actions.ts +0 -346
  22. package/src/approval-auth.ts +0 -25
  23. package/src/approval-handler.runtime.ts +0 -592
  24. package/src/approval-ids.ts +0 -6
  25. package/src/approval-native.ts +0 -345
  26. package/src/approval-reaction-auth.ts +0 -45
  27. package/src/approval-reactions.ts +0 -313
  28. package/src/auth-precedence.ts +0 -61
  29. package/src/channel-account-paths.ts +0 -97
  30. package/src/channel.runtime.ts +0 -17
  31. package/src/channel.setup.ts +0 -48
  32. package/src/channel.ts +0 -667
  33. package/src/cli-metadata.ts +0 -19
  34. package/src/cli.ts +0 -2298
  35. package/src/config-adapter.ts +0 -41
  36. package/src/config-schema.ts +0 -159
  37. package/src/config-ui-hints.ts +0 -56
  38. package/src/directory-live.ts +0 -238
  39. package/src/doctor-contract.ts +0 -287
  40. package/src/doctor.ts +0 -262
  41. package/src/env-vars.ts +0 -92
  42. package/src/exec-approval-resolver.ts +0 -23
  43. package/src/exec-approvals.ts +0 -287
  44. package/src/group-mentions.ts +0 -41
  45. package/src/legacy-crypto-inspector-availability.ts +0 -60
  46. package/src/legacy-crypto.ts +0 -531
  47. package/src/legacy-state.ts +0 -156
  48. package/src/matrix/account-config.ts +0 -175
  49. package/src/matrix/accounts.ts +0 -194
  50. package/src/matrix/actions/client.ts +0 -31
  51. package/src/matrix/actions/devices.ts +0 -34
  52. package/src/matrix/actions/limits.ts +0 -6
  53. package/src/matrix/actions/messages.ts +0 -129
  54. package/src/matrix/actions/pins.ts +0 -63
  55. package/src/matrix/actions/polls.ts +0 -109
  56. package/src/matrix/actions/profile.ts +0 -37
  57. package/src/matrix/actions/reactions.ts +0 -59
  58. package/src/matrix/actions/room.ts +0 -71
  59. package/src/matrix/actions/summary.ts +0 -88
  60. package/src/matrix/actions/types.ts +0 -63
  61. package/src/matrix/actions/verification.ts +0 -589
  62. package/src/matrix/actions.ts +0 -37
  63. package/src/matrix/active-client.ts +0 -26
  64. package/src/matrix/async-lock.ts +0 -18
  65. package/src/matrix/backup-health.ts +0 -124
  66. package/src/matrix/client/config-runtime-api.ts +0 -9
  67. package/src/matrix/client/config-secret-input.runtime.ts +0 -1
  68. package/src/matrix/client/config.ts +0 -853
  69. package/src/matrix/client/create-client.ts +0 -105
  70. package/src/matrix/client/env-auth.ts +0 -95
  71. package/src/matrix/client/file-sync-store.ts +0 -289
  72. package/src/matrix/client/logging.ts +0 -140
  73. package/src/matrix/client/migration-snapshot.runtime.ts +0 -1
  74. package/src/matrix/client/private-network-host.ts +0 -1
  75. package/src/matrix/client/runtime.ts +0 -4
  76. package/src/matrix/client/shared.ts +0 -316
  77. package/src/matrix/client/storage.ts +0 -543
  78. package/src/matrix/client/types.ts +0 -50
  79. package/src/matrix/client/url-validation.ts +0 -73
  80. package/src/matrix/client-bootstrap.ts +0 -173
  81. package/src/matrix/client.ts +0 -23
  82. package/src/matrix/config-paths.ts +0 -31
  83. package/src/matrix/config-update.ts +0 -292
  84. package/src/matrix/credentials-read.ts +0 -208
  85. package/src/matrix/credentials-write.runtime.ts +0 -35
  86. package/src/matrix/credentials.ts +0 -95
  87. package/src/matrix/deps.ts +0 -309
  88. package/src/matrix/device-health.ts +0 -29
  89. package/src/matrix/direct-management.ts +0 -349
  90. package/src/matrix/direct-room.ts +0 -128
  91. package/src/matrix/draft-stream.ts +0 -225
  92. package/src/matrix/encryption-guidance.ts +0 -24
  93. package/src/matrix/errors.ts +0 -21
  94. package/src/matrix/format.ts +0 -426
  95. package/src/matrix/legacy-crypto-inspector.ts +0 -95
  96. package/src/matrix/media-errors.ts +0 -20
  97. package/src/matrix/media-text.ts +0 -162
  98. package/src/matrix/monitor/access-state.ts +0 -145
  99. package/src/matrix/monitor/ack-config.ts +0 -27
  100. package/src/matrix/monitor/allowlist.ts +0 -89
  101. package/src/matrix/monitor/auto-join.ts +0 -86
  102. package/src/matrix/monitor/config.ts +0 -569
  103. package/src/matrix/monitor/context-summary.ts +0 -43
  104. package/src/matrix/monitor/direct.ts +0 -296
  105. package/src/matrix/monitor/events.ts +0 -397
  106. package/src/matrix/monitor/handler.ts +0 -2266
  107. package/src/matrix/monitor/inbound-dedupe.ts +0 -267
  108. package/src/matrix/monitor/index.ts +0 -540
  109. package/src/matrix/monitor/legacy-crypto-restore.ts +0 -139
  110. package/src/matrix/monitor/location.ts +0 -108
  111. package/src/matrix/monitor/media.ts +0 -119
  112. package/src/matrix/monitor/mentions.ts +0 -256
  113. package/src/matrix/monitor/reaction-events.ts +0 -197
  114. package/src/matrix/monitor/recent-invite.ts +0 -30
  115. package/src/matrix/monitor/replies.ts +0 -136
  116. package/src/matrix/monitor/reply-context.ts +0 -92
  117. package/src/matrix/monitor/room-history.ts +0 -301
  118. package/src/matrix/monitor/room-info.ts +0 -126
  119. package/src/matrix/monitor/rooms.ts +0 -52
  120. package/src/matrix/monitor/route.ts +0 -179
  121. package/src/matrix/monitor/runtime-api.ts +0 -28
  122. package/src/matrix/monitor/startup-verification.ts +0 -237
  123. package/src/matrix/monitor/startup.ts +0 -218
  124. package/src/matrix/monitor/status.ts +0 -120
  125. package/src/matrix/monitor/sync-lifecycle.ts +0 -91
  126. package/src/matrix/monitor/task-runner.ts +0 -38
  127. package/src/matrix/monitor/test-events.ts +0 -21
  128. package/src/matrix/monitor/thread-context.ts +0 -108
  129. package/src/matrix/monitor/threads.ts +0 -85
  130. package/src/matrix/monitor/types.ts +0 -30
  131. package/src/matrix/monitor/verification-events.ts +0 -643
  132. package/src/matrix/monitor/verification-utils.ts +0 -46
  133. package/src/matrix/outbound-media-runtime.ts +0 -1
  134. package/src/matrix/poll-summary.ts +0 -110
  135. package/src/matrix/poll-types.ts +0 -429
  136. package/src/matrix/probe.runtime.ts +0 -4
  137. package/src/matrix/probe.ts +0 -97
  138. package/src/matrix/profile.ts +0 -184
  139. package/src/matrix/reaction-common.ts +0 -147
  140. package/src/matrix/sdk/crypto-bootstrap.ts +0 -438
  141. package/src/matrix/sdk/crypto-facade.ts +0 -242
  142. package/src/matrix/sdk/crypto-node.runtime.ts +0 -17
  143. package/src/matrix/sdk/crypto-runtime.ts +0 -14
  144. package/src/matrix/sdk/decrypt-bridge.ts +0 -410
  145. package/src/matrix/sdk/event-helpers.ts +0 -83
  146. package/src/matrix/sdk/http-client.ts +0 -87
  147. package/src/matrix/sdk/idb-persistence-lock.ts +0 -51
  148. package/src/matrix/sdk/idb-persistence.ts +0 -288
  149. package/src/matrix/sdk/logger.ts +0 -108
  150. package/src/matrix/sdk/read-response-with-limit.ts +0 -19
  151. package/src/matrix/sdk/recovery-key-store.ts +0 -453
  152. package/src/matrix/sdk/timeout-abort-signal.ts +0 -1
  153. package/src/matrix/sdk/transport-runtime-api.ts +0 -18
  154. package/src/matrix/sdk/transport.ts +0 -352
  155. package/src/matrix/sdk/types.ts +0 -245
  156. package/src/matrix/sdk/verification-manager.ts +0 -795
  157. package/src/matrix/sdk/verification-status.ts +0 -23
  158. package/src/matrix/sdk.ts +0 -2152
  159. package/src/matrix/send/client.ts +0 -93
  160. package/src/matrix/send/formatting.ts +0 -189
  161. package/src/matrix/send/media.ts +0 -244
  162. package/src/matrix/send/targets.ts +0 -104
  163. package/src/matrix/send/types.ts +0 -131
  164. package/src/matrix/send.ts +0 -660
  165. package/src/matrix/session-store-metadata.ts +0 -108
  166. package/src/matrix/startup-abort.ts +0 -44
  167. package/src/matrix/subagent-hooks.ts +0 -308
  168. package/src/matrix/sync-state.ts +0 -27
  169. package/src/matrix/target-ids.ts +0 -79
  170. package/src/matrix/thread-bindings-shared.ts +0 -206
  171. package/src/matrix/thread-bindings.ts +0 -580
  172. package/src/matrix-migration.runtime.ts +0 -9
  173. package/src/migration-config.ts +0 -243
  174. package/src/migration-snapshot-backup.ts +0 -116
  175. package/src/migration-snapshot.ts +0 -53
  176. package/src/onboarding.ts +0 -775
  177. package/src/outbound.ts +0 -248
  178. package/src/plugin-entry.runtime.js +0 -115
  179. package/src/plugin-entry.runtime.ts +0 -70
  180. package/src/profile-update.ts +0 -71
  181. package/src/record-shared.ts +0 -3
  182. package/src/resolve-targets.ts +0 -175
  183. package/src/resolver.runtime.ts +0 -5
  184. package/src/resolver.ts +0 -21
  185. package/src/runtime-api.ts +0 -106
  186. package/src/runtime.ts +0 -13
  187. package/src/secret-contract.ts +0 -174
  188. package/src/session-route.ts +0 -126
  189. package/src/setup-bootstrap.ts +0 -102
  190. package/src/setup-config.ts +0 -222
  191. package/src/setup-contract.ts +0 -90
  192. package/src/setup-core.ts +0 -146
  193. package/src/setup-dm-policy.ts +0 -15
  194. package/src/setup-surface.ts +0 -4
  195. package/src/startup-maintenance.ts +0 -114
  196. package/src/storage-paths.ts +0 -92
  197. package/src/thread-binding-api.ts +0 -23
  198. package/src/tool-actions.runtime.ts +0 -1
  199. package/src/tool-actions.ts +0 -498
  200. package/src/types.ts +0 -257
  201. package/subagent-hooks-api.ts +0 -31
  202. package/test-api.ts +0 -21
  203. package/thread-binding-api.ts +0 -4
  204. package/thread-bindings-runtime.ts +0 -4
  205. package/tsconfig.json +0 -16
@@ -1,108 +0,0 @@
1
- import {
2
- normalizeLowercaseStringOrEmpty,
3
- normalizeOptionalString,
4
- } from "klaw/plugin-sdk/string-coerce-runtime";
5
- import type { LocationMessageEventContent } from "../sdk.js";
6
- import { formatLocationText, toLocationContext, type NormalizedLocation } from "./runtime-api.js";
7
- import { EventType } from "./types.js";
8
-
9
- export type MatrixLocationPayload = {
10
- text: string;
11
- context: ReturnType<typeof toLocationContext>;
12
- };
13
-
14
- type GeoUriParams = {
15
- latitude: number;
16
- longitude: number;
17
- accuracy?: number;
18
- };
19
-
20
- function decodeGeoUriParamValue(value: string): string {
21
- try {
22
- return decodeURIComponent(value);
23
- } catch {
24
- return value;
25
- }
26
- }
27
-
28
- function parseGeoUri(value: string): GeoUriParams | null {
29
- const trimmed = value.trim();
30
- if (!trimmed) {
31
- return null;
32
- }
33
- if (!normalizeLowercaseStringOrEmpty(trimmed).startsWith("geo:")) {
34
- return null;
35
- }
36
- const payload = trimmed.slice(4);
37
- const [coordsPart, ...paramParts] = payload.split(";");
38
- const coords = coordsPart.split(",");
39
- if (coords.length < 2) {
40
- return null;
41
- }
42
- const latitude = Number.parseFloat(coords[0] ?? "");
43
- const longitude = Number.parseFloat(coords[1] ?? "");
44
- if (!Number.isFinite(latitude) || !Number.isFinite(longitude)) {
45
- return null;
46
- }
47
-
48
- const params = new Map<string, string>();
49
- for (const part of paramParts) {
50
- const segment = part.trim();
51
- if (!segment) {
52
- continue;
53
- }
54
- const eqIndex = segment.indexOf("=");
55
- const rawKey = eqIndex === -1 ? segment : segment.slice(0, eqIndex);
56
- const rawValue = eqIndex === -1 ? "" : segment.slice(eqIndex + 1);
57
- const key = normalizeLowercaseStringOrEmpty(rawKey);
58
- if (!key) {
59
- continue;
60
- }
61
- const valuePart = rawValue.trim();
62
- params.set(key, valuePart ? decodeGeoUriParamValue(valuePart) : "");
63
- }
64
-
65
- const accuracyRaw = params.get("u");
66
- const accuracy = accuracyRaw ? Number.parseFloat(accuracyRaw) : undefined;
67
-
68
- return {
69
- latitude,
70
- longitude,
71
- accuracy: Number.isFinite(accuracy) ? accuracy : undefined,
72
- };
73
- }
74
-
75
- export function resolveMatrixLocation(params: {
76
- eventType: string;
77
- content: LocationMessageEventContent;
78
- }): MatrixLocationPayload | null {
79
- const { eventType, content } = params;
80
- const isLocation =
81
- eventType === EventType.Location ||
82
- (eventType === EventType.RoomMessage && content.msgtype === EventType.Location);
83
- if (!isLocation) {
84
- return null;
85
- }
86
- const geoUri = normalizeOptionalString(content.geo_uri) ?? "";
87
- if (!geoUri) {
88
- return null;
89
- }
90
- const parsed = parseGeoUri(geoUri);
91
- if (!parsed) {
92
- return null;
93
- }
94
- const caption = normalizeOptionalString(content.body) ?? "";
95
- const location: NormalizedLocation = {
96
- latitude: parsed.latitude,
97
- longitude: parsed.longitude,
98
- accuracy: parsed.accuracy,
99
- caption: caption || undefined,
100
- source: "pin",
101
- isLive: false,
102
- };
103
-
104
- return {
105
- text: formatLocationText(location),
106
- context: toLocationContext(location),
107
- };
108
- }
@@ -1,119 +0,0 @@
1
- import { getMatrixRuntime } from "../../runtime.js";
2
- import { MatrixMediaSizeLimitError, isMatrixMediaSizeLimitError } from "../media-errors.js";
3
- import type { MatrixClient } from "../sdk.js";
4
-
5
- // Type for encrypted file info
6
- type EncryptedFile = {
7
- url: string;
8
- key: {
9
- kty: string;
10
- key_ops: string[];
11
- alg: string;
12
- k: string;
13
- ext: boolean;
14
- };
15
- iv: string;
16
- hashes: Record<string, string>;
17
- v: string;
18
- };
19
-
20
- const MATRIX_MEDIA_DOWNLOAD_IDLE_TIMEOUT_MS = 30_000;
21
-
22
- async function fetchMatrixMediaBuffer(params: {
23
- client: MatrixClient;
24
- mxcUrl: string;
25
- maxBytes: number;
26
- }): Promise<{ buffer: Buffer } | null> {
27
- try {
28
- const buffer = await params.client.downloadContent(params.mxcUrl, {
29
- maxBytes: params.maxBytes,
30
- readIdleTimeoutMs: MATRIX_MEDIA_DOWNLOAD_IDLE_TIMEOUT_MS,
31
- });
32
- return { buffer };
33
- } catch (err) {
34
- if (isMatrixMediaSizeLimitError(err)) {
35
- throw err;
36
- }
37
- throw new Error(`Matrix media download failed: ${String(err)}`, { cause: err });
38
- }
39
- }
40
-
41
- /**
42
- * Download and decrypt encrypted media from a Matrix room.
43
- * Uses the Matrix crypto adapter's decryptMedia helper.
44
- */
45
- async function fetchEncryptedMediaBuffer(params: {
46
- client: MatrixClient;
47
- file: EncryptedFile;
48
- maxBytes: number;
49
- }): Promise<{ buffer: Buffer } | null> {
50
- if (!params.client.crypto) {
51
- throw new Error("Cannot decrypt media: crypto not enabled");
52
- }
53
-
54
- const decrypted = await params.client.crypto.decryptMedia(
55
- params.file as Parameters<typeof params.client.crypto.decryptMedia>[0],
56
- {
57
- maxBytes: params.maxBytes,
58
- readIdleTimeoutMs: MATRIX_MEDIA_DOWNLOAD_IDLE_TIMEOUT_MS,
59
- },
60
- );
61
-
62
- if (decrypted.byteLength > params.maxBytes) {
63
- throw new MatrixMediaSizeLimitError();
64
- }
65
-
66
- return { buffer: decrypted };
67
- }
68
-
69
- export async function downloadMatrixMedia(params: {
70
- client: MatrixClient;
71
- mxcUrl: string;
72
- contentType?: string;
73
- sizeBytes?: number;
74
- maxBytes: number;
75
- file?: EncryptedFile;
76
- originalFilename?: string;
77
- }): Promise<{
78
- path: string;
79
- contentType?: string;
80
- placeholder: string;
81
- } | null> {
82
- let fetched: { buffer: Buffer; headerType?: string } | null;
83
- if (typeof params.sizeBytes === "number" && params.sizeBytes > params.maxBytes) {
84
- throw new MatrixMediaSizeLimitError();
85
- }
86
-
87
- if (params.file) {
88
- // Encrypted media
89
- fetched = await fetchEncryptedMediaBuffer({
90
- client: params.client,
91
- file: params.file,
92
- maxBytes: params.maxBytes,
93
- });
94
- } else {
95
- // Unencrypted media
96
- fetched = await fetchMatrixMediaBuffer({
97
- client: params.client,
98
- mxcUrl: params.mxcUrl,
99
- maxBytes: params.maxBytes,
100
- });
101
- }
102
-
103
- if (!fetched) {
104
- return null;
105
- }
106
- const headerType = params.contentType ?? undefined;
107
- const saved = await getMatrixRuntime().channel.media.saveMediaBuffer(
108
- fetched.buffer,
109
- headerType,
110
- "inbound",
111
- params.maxBytes,
112
- params.originalFilename,
113
- );
114
- return {
115
- path: saved.path,
116
- contentType: saved.contentType,
117
- placeholder: "[matrix media]",
118
- };
119
- }
@@ -1,256 +0,0 @@
1
- import { normalizeLowercaseStringOrEmpty } from "klaw/plugin-sdk/string-coerce-runtime";
2
- import { escapeRegExp } from "klaw/plugin-sdk/text-utility-runtime";
3
- import { getMatrixRuntime } from "../../runtime.js";
4
- import type { RoomMessageEventContent } from "./types.js";
5
-
6
- const HTML_ENTITY_REPLACEMENTS: Readonly<Record<string, string>> = {
7
- amp: "&",
8
- apos: "'",
9
- gt: ">",
10
- lt: "<",
11
- nbsp: " ",
12
- quot: '"',
13
- };
14
- const MAX_UNICODE_SCALAR_VALUE = 0x10ffff;
15
-
16
- function decodeNumericHtmlEntity(match: string, rawValue: string, radix: 10 | 16): string {
17
- const codePoint = Number.parseInt(rawValue, radix);
18
- if (
19
- !Number.isSafeInteger(codePoint) ||
20
- codePoint < 0 ||
21
- codePoint > MAX_UNICODE_SCALAR_VALUE ||
22
- (codePoint >= 0xd800 && codePoint <= 0xdfff)
23
- ) {
24
- return match;
25
- }
26
- return String.fromCodePoint(codePoint);
27
- }
28
-
29
- function decodeHtmlEntities(value: string): string {
30
- return value.replace(/&(#x?[0-9a-f]+|\w+);/gi, (match, entity: string) => {
31
- const normalized = normalizeLowercaseStringOrEmpty(entity);
32
- if (normalized.startsWith("#x")) {
33
- return decodeNumericHtmlEntity(match, normalized.slice(2), 16);
34
- }
35
- if (normalized.startsWith("#")) {
36
- return decodeNumericHtmlEntity(match, normalized.slice(1), 10);
37
- }
38
- return HTML_ENTITY_REPLACEMENTS[normalized] ?? match;
39
- });
40
- }
41
-
42
- function normalizeVisibleMentionText(value: string): string {
43
- return normalizeLowercaseStringOrEmpty(
44
- decodeHtmlEntities(
45
- value.replace(/<[^>]+>/g, " ").replace(/[\u200b-\u200f\u202a-\u202e\u2060-\u206f]/g, ""),
46
- ).replace(/\s+/g, " "),
47
- );
48
- }
49
-
50
- function extractVisibleMentionText(value?: string): string {
51
- return normalizeVisibleMentionText(value ?? "");
52
- }
53
-
54
- function resolveMatrixUserLocalpart(userId: string): string | null {
55
- const trimmed = userId.trim();
56
- if (!trimmed.startsWith("@")) {
57
- return null;
58
- }
59
- const colonIndex = trimmed.indexOf(":");
60
- if (colonIndex <= 1) {
61
- return null;
62
- }
63
- return trimmed.slice(1, colonIndex).trim() || null;
64
- }
65
-
66
- function resolveMatrixMentionPrefixCandidates(params: {
67
- userId?: string | null;
68
- displayName?: string | null;
69
- }): string[] {
70
- const candidates: string[] = [];
71
- const seen = new Set<string>();
72
-
73
- const append = (candidate?: string | null) => {
74
- const trimmed = candidate?.trim();
75
- if (!trimmed) {
76
- return;
77
- }
78
- const normalized = normalizeLowercaseStringOrEmpty(trimmed);
79
- if (seen.has(normalized)) {
80
- return;
81
- }
82
- seen.add(normalized);
83
- candidates.push(trimmed);
84
- };
85
-
86
- append(params.userId);
87
- const localpart = params.userId ? resolveMatrixUserLocalpart(params.userId) : null;
88
- append(localpart ? `@${localpart}` : null);
89
- append(params.displayName);
90
- append(params.displayName ? `@${params.displayName}` : null);
91
-
92
- return candidates;
93
- }
94
-
95
- function stripMatchedMatrixMentionPrefix(text: string, pattern: RegExp): string | null {
96
- const match = text.match(pattern);
97
- if (!match) {
98
- return null;
99
- }
100
- return text.slice(match[0].length).trimStart();
101
- }
102
-
103
- function stripNativeMatrixMentionPrefix(text: string, candidate: string): string | null {
104
- const pattern = new RegExp(`^\\s*${escapeRegExp(candidate)}(?:\\s*[:,])?(?:\\s+|$)`, "i");
105
- return stripMatchedMatrixMentionPrefix(text, pattern);
106
- }
107
-
108
- function stripRegexMatrixMentionPrefix(text: string, pattern: RegExp): string | null {
109
- const flags = pattern.flags.replace(/[gy]/g, "");
110
- const anchored = new RegExp(`^\\s*(?:${pattern.source})(?:\\s*[:,])?(?:\\s+|$)`, flags);
111
- return stripMatchedMatrixMentionPrefix(text, anchored);
112
- }
113
-
114
- export function stripMatrixMentionPrefix(params: {
115
- text: string;
116
- userId?: string | null;
117
- displayName?: string | null;
118
- mentionRegexes?: RegExp[];
119
- }): string {
120
- const text = params.text;
121
- if (!text) {
122
- return text;
123
- }
124
-
125
- for (const candidate of resolveMatrixMentionPrefixCandidates(params)) {
126
- const stripped = stripNativeMatrixMentionPrefix(text, candidate);
127
- if (stripped !== null) {
128
- return stripped;
129
- }
130
- }
131
- for (const pattern of params.mentionRegexes ?? []) {
132
- const stripped = stripRegexMatrixMentionPrefix(text, pattern);
133
- if (stripped !== null) {
134
- return stripped;
135
- }
136
- }
137
- return text;
138
- }
139
-
140
- function isVisibleMentionLabel(params: {
141
- text: string;
142
- userId: string;
143
- mentionRegexes: RegExp[];
144
- displayName?: string | null;
145
- }): boolean {
146
- const cleaned = extractVisibleMentionText(params.text);
147
- if (!cleaned) {
148
- return false;
149
- }
150
- if (params.mentionRegexes.some((pattern) => pattern.test(cleaned))) {
151
- return true;
152
- }
153
- const localpart = resolveMatrixUserLocalpart(params.userId);
154
- const candidates = [
155
- extractVisibleMentionText(params.userId),
156
- localpart ? extractVisibleMentionText(localpart) : null,
157
- localpart ? extractVisibleMentionText(`@${localpart}`) : null,
158
- params.displayName ? extractVisibleMentionText(params.displayName) : null,
159
- params.displayName ? extractVisibleMentionText(`@${params.displayName}`) : null,
160
- ].filter((value): value is string => Boolean(value));
161
- return candidates.includes(cleaned);
162
- }
163
-
164
- function hasVisibleRoomMention(value?: string): boolean {
165
- const cleaned = extractVisibleMentionText(value);
166
- return /(^|[^a-z0-9_])@room\b/i.test(cleaned);
167
- }
168
-
169
- /**
170
- * Check if formatted_body contains a matrix.to link whose visible label still
171
- * looks like a real mention for the given user. Do not trust href alone, since
172
- * senders can hide arbitrary matrix.to links behind unrelated link text.
173
- * Many Matrix clients (including Element) use HTML links in formatted_body instead of
174
- * or in addition to the m.mentions field.
175
- */
176
- function checkFormattedBodyMention(params: {
177
- formattedBody?: string;
178
- userId: string;
179
- displayName?: string | null;
180
- mentionRegexes: RegExp[];
181
- }): boolean {
182
- if (!params.formattedBody || !params.userId) {
183
- return false;
184
- }
185
- const anchorPattern = /<a\b[^>]*href=(["'])(https:\/\/matrix\.to\/#[^"']+)\1[^>]*>(.*?)<\/a>/gis;
186
- for (const match of params.formattedBody.matchAll(anchorPattern)) {
187
- const href = match[2];
188
- const visibleLabel = match[3] ?? "";
189
- if (!href) {
190
- continue;
191
- }
192
- try {
193
- const parsed = new URL(href);
194
- const fragmentTarget = decodeURIComponent(parsed.hash.replace(/^#\/?/, "").trim());
195
- if (fragmentTarget !== params.userId.trim()) {
196
- continue;
197
- }
198
- if (
199
- isVisibleMentionLabel({
200
- text: visibleLabel,
201
- userId: params.userId,
202
- mentionRegexes: params.mentionRegexes,
203
- displayName: params.displayName,
204
- })
205
- ) {
206
- return true;
207
- }
208
- } catch {
209
- continue;
210
- }
211
- }
212
- return false;
213
- }
214
-
215
- export function resolveMentions(params: {
216
- content: RoomMessageEventContent;
217
- userId?: string | null;
218
- displayName?: string | null;
219
- text?: string;
220
- mentionRegexes: RegExp[];
221
- }) {
222
- const mentions = params.content["m.mentions"];
223
- const mentionedUsers = Array.isArray(mentions?.user_ids)
224
- ? new Set(mentions.user_ids)
225
- : new Set<string>();
226
- const textMentioned = getMatrixRuntime().channel.mentions.matchesMentionPatterns(
227
- params.text ?? "",
228
- params.mentionRegexes,
229
- );
230
- const visibleRoomMention =
231
- hasVisibleRoomMention(params.text) || hasVisibleRoomMention(params.content.formatted_body);
232
-
233
- // Check formatted_body for matrix.to mention links (legacy/alternative mention format)
234
- const mentionedInFormattedBody = params.userId
235
- ? checkFormattedBodyMention({
236
- formattedBody: params.content.formatted_body,
237
- userId: params.userId,
238
- displayName: params.displayName,
239
- mentionRegexes: params.mentionRegexes,
240
- })
241
- : false;
242
- // Matrix clients can mention users through m.mentions metadata plus a visible
243
- // Matrix URI label in formatted_body. Keep the visible-mention requirement so
244
- // hidden metadata-only mentions do not trigger the handler.
245
- const metadataBackedUserMention = Boolean(
246
- params.userId &&
247
- mentionedUsers.has(params.userId) &&
248
- (mentionedInFormattedBody || textMentioned),
249
- );
250
- const metadataBackedRoomMention = Boolean(mentions?.room) && visibleRoomMention;
251
- const explicitMention =
252
- mentionedInFormattedBody || metadataBackedUserMention || metadataBackedRoomMention;
253
-
254
- const wasMentioned = explicitMention || textMentioned || visibleRoomMention;
255
- return { wasMentioned, hasExplicitMention: explicitMention };
256
- }
@@ -1,197 +0,0 @@
1
- import { getSessionBindingService } from "klaw/plugin-sdk/session-binding-runtime";
2
- import {
3
- resolveMatrixApprovalReactionTargetWithPersistence,
4
- unregisterMatrixApprovalReactionTarget,
5
- } from "../../approval-reactions.js";
6
- import type { CoreConfig } from "../../types.js";
7
- import { resolveMatrixAccountConfig } from "../account-config.js";
8
- import { extractMatrixReactionAnnotation } from "../reaction-common.js";
9
- import type { MatrixClient } from "../sdk.js";
10
- import { resolveMatrixInboundRoute } from "./route.js";
11
- import type { PluginRuntime } from "./runtime-api.js";
12
- import { resolveMatrixThreadRootId, resolveMatrixThreadRouting } from "./threads.js";
13
- import type { MatrixRawEvent, RoomMessageEventContent } from "./types.js";
14
-
15
- let approvalReactionAuthPromise:
16
- | Promise<typeof import("../../approval-reaction-auth.js")>
17
- | undefined;
18
- let execApprovalResolverPromise:
19
- | Promise<typeof import("../../exec-approval-resolver.js")>
20
- | undefined;
21
-
22
- function loadApprovalReactionAuth(): Promise<typeof import("../../approval-reaction-auth.js")> {
23
- approvalReactionAuthPromise ??= import("../../approval-reaction-auth.js");
24
- return approvalReactionAuthPromise;
25
- }
26
-
27
- function loadExecApprovalResolver(): Promise<typeof import("../../exec-approval-resolver.js")> {
28
- execApprovalResolverPromise ??= import("../../exec-approval-resolver.js");
29
- return execApprovalResolverPromise;
30
- }
31
-
32
- export type MatrixReactionNotificationMode = "off" | "own";
33
-
34
- export function resolveMatrixReactionNotificationMode(params: {
35
- cfg: CoreConfig;
36
- accountId: string;
37
- }): MatrixReactionNotificationMode {
38
- const matrixConfig = params.cfg.channels?.matrix;
39
- const accountConfig = resolveMatrixAccountConfig({
40
- cfg: params.cfg,
41
- accountId: params.accountId,
42
- });
43
- return accountConfig.reactionNotifications ?? matrixConfig?.reactionNotifications ?? "own";
44
- }
45
-
46
- async function maybeResolveMatrixApprovalReaction(params: {
47
- cfg: CoreConfig;
48
- accountId: string;
49
- senderId: string;
50
- target: Awaited<ReturnType<typeof resolveMatrixApprovalReactionTargetWithPersistence>>;
51
- targetEventId: string;
52
- roomId: string;
53
- logVerboseMessage: (message: string) => void;
54
- }): Promise<boolean> {
55
- if (!params.target) {
56
- return false;
57
- }
58
- const approvalKind = params.target.approvalId.startsWith("plugin:") ? "plugin" : "exec";
59
- const { isMatrixApprovalReactionAuthorizedSender } = await loadApprovalReactionAuth();
60
- if (!isMatrixApprovalReactionAuthorizedSender({ ...params, approvalKind })) {
61
- return false;
62
- }
63
- const { isApprovalNotFoundError, resolveMatrixApproval } = await loadExecApprovalResolver();
64
- try {
65
- await resolveMatrixApproval({
66
- cfg: params.cfg,
67
- approvalId: params.target.approvalId,
68
- decision: params.target.decision,
69
- senderId: params.senderId,
70
- });
71
- params.logVerboseMessage(
72
- `matrix: approval reaction resolved id=${params.target.approvalId} sender=${params.senderId} decision=${params.target.decision}`,
73
- );
74
- return true;
75
- } catch (err) {
76
- if (isApprovalNotFoundError(err)) {
77
- unregisterMatrixApprovalReactionTarget({
78
- roomId: params.roomId,
79
- eventId: params.targetEventId,
80
- });
81
- params.logVerboseMessage(
82
- `matrix: approval reaction ignored for expired approval id=${params.target.approvalId} sender=${params.senderId}`,
83
- );
84
- return true;
85
- }
86
- params.logVerboseMessage(
87
- `matrix: approval reaction failed id=${params.target.approvalId} sender=${params.senderId}: ${String(err)}`,
88
- );
89
- return true;
90
- }
91
- }
92
-
93
- export async function handleInboundMatrixReaction(params: {
94
- client: MatrixClient;
95
- core: PluginRuntime;
96
- cfg: CoreConfig;
97
- accountId: string;
98
- roomId: string;
99
- event: MatrixRawEvent;
100
- senderId: string;
101
- senderLabel: string;
102
- selfUserId: string;
103
- isDirectMessage: boolean;
104
- logVerboseMessage: (message: string) => void;
105
- }): Promise<void> {
106
- const reaction = extractMatrixReactionAnnotation(params.event.content);
107
- if (!reaction?.eventId) {
108
- return;
109
- }
110
- if (params.senderId === params.selfUserId) {
111
- return;
112
- }
113
- const approvalTarget = await resolveMatrixApprovalReactionTargetWithPersistence({
114
- roomId: params.roomId,
115
- eventId: reaction.eventId,
116
- reactionKey: reaction.key,
117
- });
118
- if (
119
- await maybeResolveMatrixApprovalReaction({
120
- cfg: params.cfg,
121
- accountId: params.accountId,
122
- senderId: params.senderId,
123
- target: approvalTarget,
124
- targetEventId: reaction.eventId,
125
- roomId: params.roomId,
126
- logVerboseMessage: params.logVerboseMessage,
127
- })
128
- ) {
129
- return;
130
- }
131
- const notificationMode = resolveMatrixReactionNotificationMode({
132
- cfg: params.cfg,
133
- accountId: params.accountId,
134
- });
135
- if (notificationMode === "off") {
136
- return;
137
- }
138
-
139
- const targetEvent = await params.client.getEvent(params.roomId, reaction.eventId).catch((err) => {
140
- params.logVerboseMessage(
141
- `matrix: failed resolving reaction target room=${params.roomId} id=${reaction.eventId}: ${String(err)}`,
142
- );
143
- return null;
144
- });
145
- const targetSender =
146
- targetEvent && typeof targetEvent.sender === "string" ? targetEvent.sender.trim() : "";
147
- if (!targetSender) {
148
- return;
149
- }
150
- if (notificationMode === "own" && targetSender !== params.selfUserId) {
151
- return;
152
- }
153
-
154
- const targetContent =
155
- targetEvent && targetEvent.content && typeof targetEvent.content === "object"
156
- ? (targetEvent.content as RoomMessageEventContent)
157
- : undefined;
158
- const threadRootId = targetContent
159
- ? resolveMatrixThreadRootId({
160
- event: targetEvent as MatrixRawEvent,
161
- content: targetContent,
162
- })
163
- : undefined;
164
- const accountConfig = resolveMatrixAccountConfig({
165
- cfg: params.cfg,
166
- accountId: params.accountId,
167
- });
168
- const thread = resolveMatrixThreadRouting({
169
- isDirectMessage: params.isDirectMessage,
170
- threadReplies: accountConfig.threadReplies ?? "inbound",
171
- dmThreadReplies: accountConfig.dm?.threadReplies,
172
- messageId: reaction.eventId,
173
- threadRootId,
174
- });
175
- const { route, runtimeBindingId } = resolveMatrixInboundRoute({
176
- cfg: params.cfg,
177
- accountId: params.accountId,
178
- roomId: params.roomId,
179
- senderId: params.senderId,
180
- isDirectMessage: params.isDirectMessage,
181
- dmSessionScope: accountConfig.dm?.sessionScope ?? "per-user",
182
- threadId: thread.threadId,
183
- eventTs: params.event.origin_server_ts,
184
- resolveAgentRoute: params.core.channel.routing.resolveAgentRoute,
185
- });
186
- if (runtimeBindingId) {
187
- getSessionBindingService().touch(runtimeBindingId, params.event.origin_server_ts);
188
- }
189
- const text = `Matrix reaction added: ${reaction.key} by ${params.senderLabel} on msg ${reaction.eventId}`;
190
- params.core.system.enqueueSystemEvent(text, {
191
- sessionKey: route.sessionKey,
192
- contextKey: `matrix:reaction:add:${params.roomId}:${reaction.eventId}:${params.senderId}:${reaction.key}`,
193
- });
194
- params.logVerboseMessage(
195
- `matrix: reaction event enqueued room=${params.roomId} target=${reaction.eventId} sender=${params.senderId} emoji=${reaction.key}`,
196
- );
197
- }
@@ -1,30 +0,0 @@
1
- import type { MatrixRoomConfig } from "../../types.js";
2
- import type { MatrixRoomInfo } from "./room-info.js";
3
- import { resolveMatrixRoomConfig } from "./rooms.js";
4
-
5
- export function shouldPromoteRecentInviteRoom(params: {
6
- roomId: string;
7
- roomInfo: Pick<
8
- MatrixRoomInfo,
9
- "name" | "canonicalAlias" | "altAliases" | "nameResolved" | "aliasesResolved"
10
- >;
11
- rooms?: Record<string, MatrixRoomConfig>;
12
- }): boolean {
13
- if (!params.roomInfo.nameResolved || !params.roomInfo.aliasesResolved) {
14
- return false;
15
- }
16
-
17
- const roomAliases = [params.roomInfo.canonicalAlias ?? "", ...params.roomInfo.altAliases].filter(
18
- Boolean,
19
- );
20
- if ((params.roomInfo.name?.trim() ?? "") || roomAliases.length > 0) {
21
- return false;
22
- }
23
-
24
- const roomConfig = resolveMatrixRoomConfig({
25
- rooms: params.rooms,
26
- roomId: params.roomId,
27
- aliases: roomAliases,
28
- });
29
- return roomConfig.matchSource === undefined;
30
- }