@openclaw/matrix 2026.7.2-beta.4 → 2026.7.2-beta.5

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 (56) hide show
  1. package/README.md +1 -1
  2. package/dist/api.js +4 -4
  3. package/dist/{approval-handler.runtime-AsawryfP.js → approval-handler.runtime-D1ibJgSk.js} +4 -4
  4. package/dist/{approval-ids-XAMP8vN9.js → approval-ids-2OQj32-K.js} +1 -1
  5. package/dist/{approval-reaction-auth-DButmM13.js → approval-reaction-auth-CCAWWkbu.js} +2 -2
  6. package/dist/{channel-MBtrY36B.js → channel-Cmi248PA.js} +28 -32
  7. package/dist/channel-plugin-api.js +1 -1
  8. package/dist/{channel.runtime-Bi4Dgy2P.js → channel.runtime-BaPXFRgT.js} +34 -40
  9. package/dist/{cli-CcPuMyCk.js → cli-0CNHkps5.js} +863 -843
  10. package/dist/{cli-metadata-DAhDhbYX.js → cli-metadata-DnXxNCoj.js} +1 -1
  11. package/dist/cli-metadata.js +1 -1
  12. package/dist/{client-Dg8dk8NW.js → client-BFRg0kd4.js} +2 -2
  13. package/dist/{client-BwX6rURh.js → client-CitERujx.js} +2 -2
  14. package/dist/{client-bootstrap-ZAaO9fZ-.js → client-bootstrap-CkAnkzB6.js} +1 -1
  15. package/dist/{config-schema-CggnuwnJ.js → config-schema-BShrVROw.js} +16 -2
  16. package/dist/contract-api.js +2 -2
  17. package/dist/{create-client-Und1YmJ2.js → create-client-f-CkRllE.js} +3 -3
  18. package/dist/{crypto-runtime-_j7TpS7k.js → crypto-runtime-D9_6J5tZ.js} +2 -2
  19. package/dist/{device-health-D4LBxuPq.js → device-health-B4-YXtJR.js} +6 -1
  20. package/dist/{directory-live-B6fbX6jf.js → directory-live-YRgYDx2w.js} +3 -3
  21. package/dist/{doctor-C0LPJGTz.js → doctor-CM8ijNpW.js} +1 -1
  22. package/dist/{doctor-contract-CWVdlRQR.js → doctor-contract-BMSJkkAE.js} +21 -37
  23. package/dist/doctor-contract-api.js +4 -4
  24. package/dist/{draft-stream-BfibauH1.js → draft-stream-CIjafRr0.js} +1 -1
  25. package/dist/{file-sync-store-DUEWCEjb.js → file-sync-store-DiiFsayX.js} +1 -1
  26. package/dist/{http-client-DOyTG9l2.js → http-client-CycYxU9-.js} +1 -1
  27. package/dist/{idb-persistence-C6zQfW2w.js → idb-persistence-B6x7dMsF.js} +5 -11
  28. package/dist/index.js +1 -1
  29. package/dist/{logging-QH-48Gr6.js → logging-DV-p52N0.js} +8 -1
  30. package/dist/{media-text-Cv1VIHXW.js → media-text-DlQC_k_K.js} +1 -1
  31. package/dist/{messages-ClTgfFl6.js → messages-Bf_s--M9.js} +3 -3
  32. package/dist/{monitor-2BjKS9RV.js → monitor-s--XqoxF.js} +2037 -1803
  33. package/dist/plugin-entry.handlers.runtime.js +1 -1
  34. package/dist/probe.runtime-BJOvCHnZ.js +3 -0
  35. package/dist/{profile-update-C2My3TML.js → profile-update-z3aM46KS.js} +2 -2
  36. package/dist/{reaction-events-Cj6thhmc.js → reaction-events-DrK9Zua7.js} +2 -2
  37. package/dist/{recovery-key-store-0O8ARdT_.js → recovery-key-store-BNnHvXdk.js} +1 -1
  38. package/dist/{resolve-targets-Dn11HT76.js → resolve-targets-nNMcrV3W.js} +1 -1
  39. package/dist/{resolver.runtime-Zguh0hH9.js → resolver.runtime-BvaFlVLg.js} +1 -1
  40. package/dist/{room-info-B6vKD5DF.js → room-info-CAFZ3Z9J.js} +13 -12
  41. package/dist/{sdk-DiRXiRRf.js → sdk-UzPd8tst.js} +227 -181
  42. package/dist/{send-CZCmDQu8.js → send-C9Ssx-iV.js} +623 -108
  43. package/dist/session-binding-contract-api.js +1 -1
  44. package/dist/{setup-bootstrap-C9w01xQK.js → setup-bootstrap-BBGpPKgy.js} +2 -2
  45. package/dist/{setup-core-D6gyoyeg.js → setup-core-a542Ernq.js} +29 -27
  46. package/dist/setup-plugin-api.js +3 -3
  47. package/dist/{setup-surface-DZIEWtQC.js → setup-surface-BJrCI6KT.js} +32 -30
  48. package/dist/{shared-C7Vm9RxI.js → shared-CUH5otj8.js} +5 -12
  49. package/dist/{startup-verification-D1nscMh8.js → startup-verification-wm3_i88s.js} +1 -1
  50. package/dist/{storage-BrkTJZov.js → storage-BvAkgzvT.js} +20 -20
  51. package/dist/{thread-bindings-BcYBELC7.js → thread-bindings-D3RfVgAF.js} +4 -3
  52. package/dist/{tool-actions.runtime-Dbco_Vwn.js → tool-actions.runtime-Dh5ow2EK.js} +8 -8
  53. package/dist/{verification-DhMBWxOJ.js → verification-ZdAXdmuh.js} +1 -1
  54. package/package.json +4 -5
  55. package/dist/probe.runtime-BWkgfbiE.js +0 -3
  56. package/npm-shrinkwrap.json +0 -557
@@ -5,13 +5,14 @@ import { r as normalizeMatrixResolvableTarget, t as isMatrixQualifiedUserId } fr
5
5
  import { n as MATRIX_REACTION_EVENT_TYPE, r as buildMatrixReactionContent, t as MATRIX_ANNOTATION_RELATION_TYPE } from "./reaction-common-DkrQdBSZ.js";
6
6
  import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
7
7
  import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-outbound";
8
- import { normalizeLowercaseStringOrEmpty, normalizeOptionalString, normalizeOptionalStringifiedId } from "openclaw/plugin-sdk/string-coerce-runtime";
8
+ import { normalizeLowercaseStringOrEmpty, normalizeNullableString, normalizeOptionalString, normalizeOptionalStringifiedId } from "openclaw/plugin-sdk/string-coerce-runtime";
9
+ import { FormatCapabilityProfile, convertMarkdownTables, findCodeRegions, isInsideCode, markdownToIR, renderMarkdownWithMarkers, tokenizeHtmlTags } from "openclaw/plugin-sdk/text-chunking";
9
10
  import { requireRuntimeConfig } from "openclaw/plugin-sdk/plugin-config-runtime";
10
11
  import { loadOutboundMediaFromUrl } from "openclaw/plugin-sdk/outbound-media";
11
12
  import { normalizePollInput } from "openclaw/plugin-sdk/poll-runtime";
12
- import { isVoiceMessageCompatibleAudio } from "openclaw/plugin-sdk/media-runtime";
13
13
  import MarkdownIt from "markdown-it";
14
14
  import { isAutoLinkedFileRef } from "openclaw/plugin-sdk/text-autolink-runtime";
15
+ import { isVoiceMessageCompatibleAudio } from "openclaw/plugin-sdk/media-runtime";
15
16
  import { parseBuffer } from "music-metadata";
16
17
  import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
17
18
  //#region extensions/matrix/src/matrix/poll-types.ts
@@ -228,36 +229,252 @@ function buildPollResponseContent(pollEventId, answerIds) {
228
229
  };
229
230
  }
230
231
  //#endregion
231
- //#region extensions/matrix/src/matrix/send/client.ts
232
- const loadMatrixSendClientRuntime = createLazyRuntimeModule(() => import("./client-bootstrap-ZAaO9fZ-.js").then((n) => n.t));
233
- function resolveMediaMaxBytes(accountId, cfg) {
234
- 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.");
235
- const matrixCfg = resolveMatrixAccountConfig({
236
- cfg: requireRuntimeConfig(cfg, "Matrix media limits"),
237
- accountId
238
- });
239
- const mediaMaxMb = typeof matrixCfg.mediaMaxMb === "number" ? matrixCfg.mediaMaxMb : void 0;
240
- if (typeof mediaMaxMb === "number") return mediaMaxMb * 1024 * 1024;
232
+ //#region extensions/matrix/src/matrix/format-profile.ts
233
+ function createMatrixPrivateMarkers(markdown, exhaustedMessage) {
234
+ const used = new Set(Array.from(markdown, (character) => character.charCodeAt(0)));
235
+ for (const match of markdown.matchAll(/&#(?:x([0-9a-f]+)|(\d+));/giu)) {
236
+ const radix = match[1] ? 16 : 10;
237
+ const value = Number.parseInt(match[1] ?? match[2] ?? "", radix);
238
+ if (Number.isFinite(value) && value <= 65535) used.add(value);
239
+ }
240
+ const markers = [];
241
+ for (let code = 57344; code <= 63743 && markers.length < 3; code += 1) if (!used.has(code)) markers.push(String.fromCharCode(code));
242
+ if (markers.length < 3) throw new Error(exhaustedMessage);
243
+ return {
244
+ open: markers[0] ?? "",
245
+ close: markers[1] ?? "",
246
+ padding: markers[2] ?? ""
247
+ };
241
248
  }
242
- async function withResolvedMatrixSendClient(opts, run) {
243
- return await withResolvedMatrixClient({
244
- ...opts,
245
- readiness: "started"
246
- }, run, "persist");
249
+ const MATRIX_FORMAT_PROFILE = FormatCapabilityProfile.define({
250
+ mechanism: "html",
251
+ constructs: {
252
+ taskList: "fallback",
253
+ image: "fallback"
254
+ },
255
+ chunk: {
256
+ limit: 4e3,
257
+ unit: "chars"
258
+ }
259
+ });
260
+ function isMarkdownEscaped(markdown, index) {
261
+ let slashCount = 0;
262
+ let cursor = index - 1;
263
+ while (cursor >= 0 && markdown[cursor] === "\\") {
264
+ slashCount += 1;
265
+ cursor -= 1;
266
+ }
267
+ return slashCount % 2 === 1;
247
268
  }
248
- async function withResolvedMatrixControlClient(opts, run) {
249
- return await withResolvedMatrixClient({
250
- ...opts,
251
- readiness: "none"
252
- }, run);
269
+ function projectMatrixMarkdown(markdown) {
270
+ return renderMarkdownWithMarkers({
271
+ text: (markdown ?? "").replace(/\r\n?/gu, "\n"),
272
+ styles: [],
273
+ links: []
274
+ }, {
275
+ styleMarkers: {},
276
+ escapeText: (text) => text
277
+ }, MATRIX_FORMAT_PROFILE);
253
278
  }
254
- async function withResolvedMatrixClient(opts, run, shutdownBehavior) {
255
- if (opts.client) return await run(opts.client);
256
- const { withResolvedRuntimeMatrixClient } = await loadMatrixSendClientRuntime();
257
- return await withResolvedRuntimeMatrixClient(opts, run, shutdownBehavior);
279
+ function renderMatrixMarkdownTables(markdown, mode) {
280
+ return MATRIX_FORMAT_PROFILE.constructs.table === "native" && (mode === "off" || mode === "block") ? markdown : convertMarkdownTables(markdown, mode);
281
+ }
282
+ //#endregion
283
+ //#region extensions/matrix/src/matrix/format-table-ranges.ts
284
+ const tableParser = new MarkdownIt({
285
+ html: false,
286
+ linkify: false,
287
+ typographer: false
288
+ });
289
+ function findMatrixTableSourceRanges(markdown) {
290
+ const lineStarts = [0];
291
+ for (let index = 0; index < markdown.length; index += 1) if (markdown[index] === "\n") lineStarts.push(index + 1);
292
+ lineStarts.push(markdown.length);
293
+ return tableParser.parse(markdown, {}).flatMap((token) => {
294
+ if (token.type !== "table_open" || !token.map) return [];
295
+ return [{
296
+ start: lineStarts[token.map[0]] ?? 0,
297
+ end: lineStarts[token.map[1]] ?? markdown.length
298
+ }];
299
+ });
300
+ }
301
+ //#endregion
302
+ //#region extensions/matrix/src/matrix/format-spoiler-ranges.ts
303
+ const spoilerParser = new MarkdownIt({
304
+ html: false,
305
+ linkify: true,
306
+ typographer: false
307
+ });
308
+ function findInlineMetadataRanges(markdown, references) {
309
+ const ranges = [];
310
+ const labelStack = [];
311
+ const codeRegions = findCodeRegions(markdown);
312
+ const underlineTags = [...tokenizeHtmlTags(markdown)].filter((tag) => tag.name === "u" || tag.name === "ins");
313
+ for (let index = 0; index < markdown.length - 1; index += 1) {
314
+ const underlineTag = underlineTags.find((tag) => tag.start === index);
315
+ if (underlineTag) {
316
+ index = underlineTag.end - 1;
317
+ continue;
318
+ }
319
+ if (isInsideCode(index, codeRegions)) continue;
320
+ if (markdown[index] === "\n" && markdown[index + 1] === "\n") {
321
+ labelStack.length = 0;
322
+ continue;
323
+ }
324
+ if (markdown[index] === "[" && !isMarkdownEscaped(markdown, index)) {
325
+ labelStack.push(index);
326
+ continue;
327
+ }
328
+ if (markdown[index] === "]" && markdown[index + 1] === "(" && !isMarkdownEscaped(markdown, index) && labelStack.pop() !== void 0) {
329
+ const start = index + 2;
330
+ let cursor = start;
331
+ while (/[\s]/u.test(markdown[cursor] ?? "")) cursor += 1;
332
+ const destination = spoilerParser.helpers.parseLinkDestination(markdown, cursor, markdown.length);
333
+ if (destination.ok) {
334
+ cursor = destination.pos;
335
+ while (/[\s]/u.test(markdown[cursor] ?? "")) cursor += 1;
336
+ const title = spoilerParser.helpers.parseLinkTitle(markdown, cursor, markdown.length);
337
+ if (title.ok) {
338
+ cursor = title.pos;
339
+ while (/[\s]/u.test(markdown[cursor] ?? "")) cursor += 1;
340
+ }
341
+ }
342
+ if (destination.ok && markdown[cursor] === ")") {
343
+ ranges.push({
344
+ start,
345
+ end: cursor + 1
346
+ });
347
+ index = cursor;
348
+ }
349
+ continue;
350
+ }
351
+ if (markdown[index] === "]" && markdown[index + 1] === "[" && !isMarkdownEscaped(markdown, index) && labelStack.pop() !== void 0) {
352
+ let end = index + 2;
353
+ while (end < markdown.length && (markdown[end] !== "]" || isMarkdownEscaped(markdown, end))) end += 1;
354
+ const reference = spoilerParser.utils.normalizeReference(markdown.slice(index + 2, end));
355
+ if (end < markdown.length && references.has(reference)) {
356
+ ranges.push({
357
+ start: index + 2,
358
+ end
359
+ });
360
+ index = end;
361
+ }
362
+ continue;
363
+ }
364
+ if (markdown[index] === "]" && !isMarkdownEscaped(markdown, index)) labelStack.pop();
365
+ const autolink = /^<[A-Za-z][A-Za-z0-9+.-]{1,31}:[^<>\s]*>/u.exec(markdown.slice(index));
366
+ if (autolink && !isMarkdownEscaped(markdown, index)) {
367
+ ranges.push({
368
+ start: index,
369
+ end: index + autolink[0].length
370
+ });
371
+ index += autolink[0].length - 1;
372
+ continue;
373
+ }
374
+ const emailAutolink = /^<[^<>\s@]+@[^<>\s@]+>/u.exec(markdown.slice(index));
375
+ if (emailAutolink && !isMarkdownEscaped(markdown, index)) {
376
+ ranges.push({
377
+ start: index,
378
+ end: index + emailAutolink[0].length
379
+ });
380
+ index += emailAutolink[0].length - 1;
381
+ }
382
+ }
383
+ return ranges;
384
+ }
385
+ function findMatrixMarkdownMetadataRanges(markdown) {
386
+ const env = {};
387
+ const tokens = spoilerParser.parse(markdown, env);
388
+ const references = new Set(Object.keys(env.references ?? {}));
389
+ const lineStarts = [0];
390
+ for (let index = 0; index < markdown.length; index += 1) if (markdown[index] === "\n") lineStarts.push(index + 1);
391
+ lineStarts.push(markdown.length);
392
+ const ranges = tokens.flatMap((token) => {
393
+ if (token.type !== "inline" || !token.map) return [];
394
+ const start = lineStarts[token.map[0]] ?? 0;
395
+ const end = lineStarts[token.map[1]] ?? markdown.length;
396
+ return findInlineMetadataRanges(markdown.slice(start, end), references).map((range) => ({
397
+ start: start + range.start,
398
+ end: start + range.end
399
+ }));
400
+ });
401
+ for (const match of markdown.matchAll(/^\s*\[[^\]\n]+\]:\s*.+$/gmu)) {
402
+ const start = match.index ?? 0;
403
+ const labelEnd = match[0].indexOf("]:");
404
+ const reference = spoilerParser.utils.normalizeReference(match[0].slice(1, labelEnd));
405
+ if (references.has(reference)) {
406
+ let end = start + match[0].length;
407
+ const continuation = /^\n[ \t]+(?:"[^"\n]*"|'[^'\n]*'|\([^\n)]*\))[ \t]*/u.exec(markdown.slice(end));
408
+ end += continuation?.[0].length ?? 0;
409
+ ranges.push({
410
+ start,
411
+ end
412
+ });
413
+ }
414
+ }
415
+ const codeRegions = findCodeRegions(markdown);
416
+ for (const match of spoilerParser.linkify.match(markdown) ?? []) if (!isInsideCode(match.index, codeRegions)) ranges.push({
417
+ start: match.index,
418
+ end: match.lastIndex
419
+ });
420
+ return ranges;
421
+ }
422
+ function findMatrixSpoilerDelimiterOffsets(markdown) {
423
+ const projected = projectMatrixMarkdown(markdown);
424
+ const tokens = spoilerParser.parse(projected, {});
425
+ const lineStarts = [0];
426
+ for (let index = 0; index < projected.length; index += 1) if (projected[index] === "\n") lineStarts.push(index + 1);
427
+ lineStarts.push(projected.length);
428
+ const excludedRanges = [
429
+ ...findCodeRegions(projected),
430
+ ...findMatrixMarkdownMetadataRanges(projected),
431
+ ...[...tokenizeHtmlTags(projected)].flatMap((tag) => tag.name === "u" || tag.name === "ins" ? [{
432
+ start: tag.start,
433
+ end: tag.end
434
+ }] : [])
435
+ ];
436
+ const offsets = [];
437
+ for (const token of tokens) {
438
+ if (token.type !== "inline" || !token.map) continue;
439
+ const start = lineStarts[token.map[0]] ?? 0;
440
+ const end = lineStarts[token.map[1]] ?? projected.length;
441
+ const candidates = [];
442
+ for (let index = start; index < end - 1; index += 1) {
443
+ if (projected[index] !== "|" || projected[index + 1] !== "|") continue;
444
+ const excluded = excludedRanges.some((range) => index >= range.start && index < range.end);
445
+ if (isMarkdownEscaped(projected, index) || excluded) continue;
446
+ candidates.push(index);
447
+ index += 1;
448
+ }
449
+ candidates.length -= candidates.length % 2;
450
+ offsets.push(...candidates);
451
+ }
452
+ return [...new Set(offsets)].toSorted((left, right) => left - right);
453
+ }
454
+ function hasMatrixSpoilerMetadataCollision(markdown) {
455
+ const projected = projectMatrixMarkdown(markdown);
456
+ const ordinary = new Set(findMatrixSpoilerDelimiterOffsets(projected));
457
+ const tables = findMatrixTableSourceRanges(projected);
458
+ for (let index = 0; index < projected.length - 1; index += 1) {
459
+ if (projected[index] !== "|" || projected[index + 1] !== "|") continue;
460
+ if (ordinary.has(index) || isMarkdownEscaped(projected, index)) continue;
461
+ if (tables.some((range) => index >= range.start && index < range.end)) continue;
462
+ return true;
463
+ }
464
+ return false;
258
465
  }
259
466
  //#endregion
260
467
  //#region extensions/matrix/src/matrix/format.ts
468
+ const MATRIX_STYLE_MARKERS = {
469
+ underline: {
470
+ open: "<u>",
471
+ close: "</u>"
472
+ },
473
+ spoiler: {
474
+ open: "<span data-mx-spoiler>",
475
+ close: "</span>"
476
+ }
477
+ };
261
478
  const md = new MarkdownIt({
262
479
  html: false,
263
480
  linkify: true,
@@ -270,6 +487,27 @@ const ESCAPED_MENTION_SENTINEL = "";
270
487
  const MENTION_PATTERN = /@[A-Za-z0-9._=+\-/:[\]]+/g;
271
488
  const MATRIX_MENTION_USER_ID_PATTERN = new RegExp(`^@[A-Za-z0-9._=+\\-/]+:(?:${/(?:[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?)(?:\.(?:[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?))*(?::\d+)?/.source}|\\[[0-9A-Fa-f:.]+\\](?::\\d+)?)$`);
272
489
  const TRIMMABLE_MENTION_SUFFIX = /[),.!?:;\]]/;
490
+ const parseMatrixUnderline = (state, silent) => {
491
+ if (state.src.charCodeAt(state.pos) !== 60) return false;
492
+ const tag = tokenizeHtmlTags(state.src.slice(state.pos)).next().value;
493
+ if (!tag || tag.start !== 0 || tag.name !== "u" && tag.name !== "ins") return false;
494
+ if (!silent) {
495
+ const token = state.push(tag.selfClosing ? "text" : tag.closing ? "matrix_underline_close" : "matrix_underline_open", tag.selfClosing ? "" : "u", tag.selfClosing ? 0 : tag.closing ? -1 : 1);
496
+ if (tag.selfClosing) token.content = tag.raw;
497
+ }
498
+ state.pos += tag.end;
499
+ return true;
500
+ };
501
+ md.inline.ruler.before("html_inline", "matrix_underline", parseMatrixUnderline);
502
+ md.renderer.rules.matrix_underline_open = () => MATRIX_STYLE_MARKERS.underline.open;
503
+ md.renderer.rules.matrix_underline_close = () => MATRIX_STYLE_MARKERS.underline.close;
504
+ md.renderer.rules.matrix_spoiler_open = () => MATRIX_STYLE_MARKERS.spoiler.open;
505
+ md.renderer.rules.matrix_spoiler_close = () => MATRIX_STYLE_MARKERS.spoiler.close;
506
+ md.core.ruler.after("inline", "matrix_spoilers", (state) => {
507
+ const markers = state.env.matrixSpoilerMarkers;
508
+ if (!markers) return;
509
+ for (const token of state.tokens) if (token.children?.length) token.children = normalizeMatrixSpoilerNesting(injectProtectedMatrixSpoilers(token.children, markers));
510
+ });
273
511
  function shouldSuppressAutoLink(tokens, idx) {
274
512
  const token = tokens[idx];
275
513
  if (token?.type !== "link_open" || token.info !== "auto") return false;
@@ -277,7 +515,10 @@ function shouldSuppressAutoLink(tokens, idx) {
277
515
  const label = tokens[idx + 1]?.type === "text" ? tokens[idx + 1]?.content ?? "" : "";
278
516
  return Boolean(href && label && isAutoLinkedFileRef(href, label));
279
517
  }
280
- md.renderer.rules.image = (tokens, idx) => escapeHtml(tokens[idx]?.content ?? "");
518
+ md.renderer.rules.image = (tokens, idx, options, env, self) => {
519
+ const token = tokens[idx];
520
+ return token?.children?.length ? self.renderInline(token.children, options, env) : escapeHtml(token?.content ?? "");
521
+ };
281
522
  md.renderer.rules.html_block = (tokens, idx) => escapeHtml(tokens[idx]?.content ?? "");
282
523
  md.renderer.rules.html_inline = (tokens, idx) => escapeHtml(tokens[idx]?.content ?? "");
283
524
  md.renderer.rules.link_open = (tokens, idx, _options, _env, self) => shouldSuppressAutoLink(tokens, idx) ? "" : self.renderToken(tokens, idx, _options);
@@ -310,15 +551,6 @@ function maskEscapedMentions(markdown) {
310
551
  }
311
552
  return masked;
312
553
  }
313
- function isMarkdownEscaped(markdown, idx) {
314
- let slashCount = 0;
315
- let cursor = idx - 1;
316
- while (cursor >= 0 && markdown[cursor] === "\\") {
317
- slashCount += 1;
318
- cursor -= 1;
319
- }
320
- return slashCount % 2 === 1;
321
- }
322
554
  function restoreEscapedMentions(text) {
323
555
  return text.replaceAll(ESCAPED_MENTION_SENTINEL, "@");
324
556
  }
@@ -388,6 +620,67 @@ function createTextToken(sample, content) {
388
620
  token.content = content;
389
621
  return token;
390
622
  }
623
+ function injectProtectedMatrixSpoilers(tokens, markers) {
624
+ const result = [];
625
+ for (const token of tokens) {
626
+ if (token.type !== "text") {
627
+ if (token.children?.length) token.children = normalizeMatrixSpoilerNesting(injectProtectedMatrixSpoilers(token.children, markers));
628
+ result.push(token);
629
+ continue;
630
+ }
631
+ let cursor = 0;
632
+ for (let index = 0; index < token.content.length; index += 1) {
633
+ const marker = token.content[index];
634
+ if (marker !== markers.open && marker !== markers.close || token.content[index + 1] !== markers.padding) continue;
635
+ if (index > cursor) result.push(createTextToken(token, token.content.slice(cursor, index)));
636
+ result.push(createToken(token, marker === markers.open ? "matrix_spoiler_open" : "matrix_spoiler_close", "span", marker === markers.open ? 1 : -1));
637
+ index += 1;
638
+ cursor = index + 1;
639
+ }
640
+ if (cursor < token.content.length) result.push(createTextToken(token, token.content.slice(cursor)));
641
+ }
642
+ return result;
643
+ }
644
+ function copyInlineToken(sample, type, tag, nesting) {
645
+ const token = createToken(sample, type, tag, nesting);
646
+ token.markup = sample.markup;
647
+ token.attrs = sample.attrs ? [...sample.attrs] : null;
648
+ return token;
649
+ }
650
+ function normalizeMatrixSpoilerNesting(tokens) {
651
+ const result = [];
652
+ const stack = [];
653
+ for (const token of tokens) {
654
+ if (token.nesting === 1) {
655
+ stack.push(token);
656
+ result.push(token);
657
+ continue;
658
+ }
659
+ if (token.nesting !== -1) {
660
+ result.push(token);
661
+ continue;
662
+ }
663
+ const openIndex = stack.findLastIndex((open) => open.tag === token.tag);
664
+ if (openIndex < 0) {
665
+ result.push(token);
666
+ continue;
667
+ }
668
+ if (openIndex === stack.length - 1) {
669
+ stack.pop();
670
+ result.push(token);
671
+ continue;
672
+ }
673
+ const crossing = stack.splice(openIndex + 1);
674
+ for (const open of crossing.toReversed()) result.push(copyInlineToken(open, open.type.replace(/_open$/u, "_close"), open.tag, -1));
675
+ stack.pop();
676
+ result.push(token);
677
+ for (const open of crossing) {
678
+ result.push(copyInlineToken(open, open.type, open.tag, 1));
679
+ stack.push(open);
680
+ }
681
+ }
682
+ return result;
683
+ }
391
684
  function createMentionLinkTokens(params) {
392
685
  const open = createToken(params.sample, "link_open", "a", 1);
393
686
  open.attrSet("href", params.href);
@@ -497,14 +790,64 @@ function compactLooseListTokens(tokens) {
497
790
  else if (token.type === "paragraph_close") currentItem.immediateParagraphCloseIndexes.push(index);
498
791
  }
499
792
  }
500
- function markdownToMatrixHtml(markdown) {
501
- const tokens = md.parse(markdown ?? "", {});
793
+ function markdownToMatrixHtml(markdown, options = {}) {
794
+ if (hasMatrixSpoilerMetadataCollision(markdown)) return renderMatrixFallbackHtml(markdown);
795
+ const tokens = parseMatrixMarkdown(projectMatrixMarkdown(markdown), options.tableMode);
502
796
  compactLooseListTokens(tokens);
503
797
  return md.renderer.render(tokens, md.options, {}).trimEnd();
504
798
  }
799
+ function protectMatrixSpoilerDelimiters(markdown) {
800
+ const offsets = findMatrixSpoilerDelimiterOffsets(markdown);
801
+ if (offsets.length === 0) return { markdown };
802
+ const markers = createMatrixPrivateMarkers(markdown, "Matrix spoiler formatting exhausted its private marker pool");
803
+ let protectedMarkdown = "";
804
+ let cursor = 0;
805
+ for (const [index, offset] of offsets.entries()) {
806
+ const marker = index % 2 === 0 ? markers.open : markers.close;
807
+ protectedMarkdown += `${markdown.slice(cursor, offset)}${marker}${markers.padding}`;
808
+ cursor = offset + 2;
809
+ }
810
+ protectedMarkdown += markdown.slice(cursor);
811
+ return {
812
+ markdown: protectedMarkdown,
813
+ markers
814
+ };
815
+ }
816
+ function parseMatrixMarkdown(markdown, tableMode) {
817
+ const protectedSpoilers = protectMatrixSpoilerDelimiters(markdown);
818
+ if (tableMode === "off") md.disable("table");
819
+ try {
820
+ return md.parse(protectedSpoilers.markdown, { matrixSpoilerMarkers: protectedSpoilers.markers });
821
+ } finally {
822
+ if (tableMode === "off") md.enable("table");
823
+ }
824
+ }
825
+ function markdownToMatrixBody(markdown) {
826
+ const projected = projectMatrixMarkdown(markdown);
827
+ const offsets = findMatrixSpoilerDelimiterOffsets(projected);
828
+ const metadataCollision = hasMatrixSpoilerMetadataCollision(projected);
829
+ if (offsets.length === 0 && !metadataCollision) return projected;
830
+ let body = projected;
831
+ if (metadataCollision) body = "[Spoiler]";
832
+ else for (let index = offsets.length - 2; index >= 0; index -= 2) {
833
+ const open = offsets[index];
834
+ const close = offsets[index + 1];
835
+ if (open !== void 0 && close !== void 0) body = `${body.slice(0, open)}[Spoiler]${body.slice(close + 2)}`;
836
+ }
837
+ return renderMarkdownWithMarkers(markdownToIR(body, {
838
+ enableHtmlUnderline: true,
839
+ headingStyle: "rich",
840
+ linkify: true
841
+ }), {
842
+ styleMarkers: {},
843
+ escapeText: (text) => text
844
+ }, MATRIX_FORMAT_PROFILE);
845
+ }
846
+ function renderMatrixFallbackHtml(markdown) {
847
+ return `<p>${escapeHtml(markdownToMatrixBody(markdown)).replaceAll("\n", "<br>\n")}</p>`;
848
+ }
505
849
  async function resolveMarkdownMentionState(params) {
506
- const markdown = maskEscapedMentions(params.markdown ?? "");
507
- const tokens = md.parse(markdown, {});
850
+ const tokens = parseMatrixMarkdown(maskEscapedMentions(projectMatrixMarkdown(params.markdown)), params.tableMode);
508
851
  restoreEscapedMentionsInBlockTokens(tokens);
509
852
  const selfUserId = await resolveMatrixSelfUserId(params.client);
510
853
  const userIds = [];
@@ -534,6 +877,17 @@ async function resolveMatrixMentionsInMarkdown(params) {
534
877
  }
535
878
  async function renderMarkdownToMatrixHtmlWithMentions(params) {
536
879
  const state = await resolveMarkdownMentionState(params);
880
+ if (hasMatrixSpoilerMetadataCollision(params.markdown)) {
881
+ const redacted = markdownToMatrixBody(params.markdown);
882
+ const redactedState = await resolveMarkdownMentionState({
883
+ ...params,
884
+ markdown: redacted
885
+ });
886
+ return {
887
+ html: renderMatrixFallbackHtml(params.markdown),
888
+ mentions: redactedState.mentions
889
+ };
890
+ }
537
891
  compactLooseListTokens(state.tokens);
538
892
  return {
539
893
  html: md.renderer.render(state.tokens, md.options, {}).trimEnd() || void 0,
@@ -541,6 +895,189 @@ async function renderMarkdownToMatrixHtmlWithMentions(params) {
541
895
  };
542
896
  }
543
897
  //#endregion
898
+ //#region extensions/matrix/src/matrix/send/chunking.ts
899
+ const getCore$2 = () => getMatrixRuntime();
900
+ function protectMatrixUnderlineTags(markdown) {
901
+ const codeRegions = findCodeRegions(markdown);
902
+ const metadataRanges = findMatrixMarkdownMetadataRanges(markdown);
903
+ const tags = [...tokenizeHtmlTags(markdown)].filter((tag) => (tag.name === "u" || tag.name === "ins") && !tag.selfClosing && !isInsideCode(tag.start, codeRegions) && !isMarkdownEscaped(markdown, tag.start) && !metadataRanges.some((range) => tag.start >= range.start && tag.start < range.end));
904
+ if (tags.length === 0) return { markdown };
905
+ const markers = createMatrixPrivateMarkers(markdown, "Matrix underline chunking exhausted its private marker pool");
906
+ let depth = 0;
907
+ const replacements = tags.flatMap((tag) => {
908
+ if (!tag.closing) {
909
+ depth += 1;
910
+ return [{
911
+ tag,
912
+ marker: depth === 1 ? markers.open : ""
913
+ }];
914
+ }
915
+ if (depth === 0) return [];
916
+ depth -= 1;
917
+ return [{
918
+ tag,
919
+ marker: depth === 0 ? markers.close : ""
920
+ }];
921
+ });
922
+ let protectedMarkdown = markdown;
923
+ for (const { tag, marker } of replacements.toReversed()) protectedMarkdown = `${protectedMarkdown.slice(0, tag.start)}${marker}${markers.padding.repeat(tag.raw.length - marker.length)}${protectedMarkdown.slice(tag.end)}`;
924
+ return {
925
+ markdown: protectedMarkdown,
926
+ markers
927
+ };
928
+ }
929
+ function restoreMatrixStyleChunks(chunks, spoiler, underline) {
930
+ const stack = [];
931
+ const syntax = {
932
+ spoiler: {
933
+ open: "||",
934
+ close: "||",
935
+ markers: spoiler
936
+ },
937
+ underline: {
938
+ open: "<u>",
939
+ close: "</u>",
940
+ markers: underline
941
+ }
942
+ };
943
+ return chunks.map((chunk) => {
944
+ let restored = stack.map((style) => syntax[style].open).join("");
945
+ for (const character of chunk) {
946
+ const opening = Object.keys(syntax).find((style) => character === syntax[style].markers?.open);
947
+ const closing = Object.keys(syntax).find((style) => character === syntax[style].markers?.close);
948
+ if (opening) {
949
+ stack.push(opening);
950
+ restored += syntax[opening].open;
951
+ } else if (closing) {
952
+ const stackIndex = stack.lastIndexOf(closing);
953
+ if (stackIndex >= 0) {
954
+ const above = stack.slice(stackIndex + 1);
955
+ restored += above.toReversed().map((style) => syntax[style].close).join("");
956
+ restored += syntax[closing].close;
957
+ stack.splice(stackIndex, 1);
958
+ restored += above.map((style) => syntax[style].open).join("");
959
+ }
960
+ } else if (character !== spoiler?.padding && character !== underline?.padding) restored += character;
961
+ }
962
+ return restored + stack.toReversed().map((style) => syntax[style].close).join("");
963
+ });
964
+ }
965
+ function splitMatrixTableSegments(markdown) {
966
+ const segments = [];
967
+ let cursor = 0;
968
+ for (const range of findMatrixTableSourceRanges(markdown)) {
969
+ const plain = markdown.slice(cursor, range.start).replace(/(?:[ \t]*\n)+$/u, "");
970
+ if (plain.trim()) segments.push({
971
+ table: false,
972
+ text: plain
973
+ });
974
+ const rawTable = markdown.slice(range.start, range.end).trimEnd();
975
+ const indent = /^ +/u.exec(rawTable)?.[0] ?? "";
976
+ const table = indent ? rawTable.split("\n").map((line) => line.startsWith(indent) ? line.slice(indent.length) : line).join("\n") : rawTable;
977
+ segments.push({
978
+ table: true,
979
+ text: table
980
+ });
981
+ cursor = range.end;
982
+ }
983
+ const tail = markdown.slice(cursor).replace(/^(?:[ \t]*\n)+/u, "");
984
+ if (tail.trim()) segments.push({
985
+ table: false,
986
+ text: tail
987
+ });
988
+ return segments;
989
+ }
990
+ function prepareMatrixSingleText(text, opts) {
991
+ const normalizedText = text.replace(/\r\n?/gu, "\n");
992
+ const trimmedText = opts.preserveWhitespace ? normalizedText : normalizedText.trim();
993
+ const cfg = requireRuntimeConfig(opts.cfg, "Matrix text preparation");
994
+ const tableMode = opts.tableMode ?? getCore$2().channel.text.resolveMarkdownTableMode({
995
+ cfg,
996
+ channel: "matrix",
997
+ accountId: opts.accountId,
998
+ supportsBlockTables: MATRIX_FORMAT_PROFILE.constructs.table === "native"
999
+ });
1000
+ const singleEventLimit = Math.min(getCore$2().channel.text.resolveTextChunkLimit(cfg, "matrix", opts.accountId), MATRIX_FORMAT_PROFILE.chunk.limit);
1001
+ const convertedText = renderMatrixMarkdownTables(trimmedText, tableMode);
1002
+ const eventTextLength = Math.max(convertedText.length, markdownToMatrixBody(convertedText).length);
1003
+ return {
1004
+ trimmedText,
1005
+ convertedText,
1006
+ singleEventLimit,
1007
+ eventTextLength,
1008
+ fitsInSingleEvent: eventTextLength <= singleEventLimit,
1009
+ tableMode
1010
+ };
1011
+ }
1012
+ function chunkMatrixText(text, opts) {
1013
+ const preparedText = prepareMatrixSingleText(text, opts);
1014
+ if (preparedText.fitsInSingleEvent) return {
1015
+ ...preparedText,
1016
+ chunks: preparedText.convertedText ? [preparedText.convertedText] : []
1017
+ };
1018
+ const cfg = requireRuntimeConfig(opts.cfg, "Matrix text chunking");
1019
+ const chunkMode = getCore$2().channel.text.resolveChunkMode(cfg, "matrix", opts.accountId);
1020
+ const collisionRedacted = hasMatrixSpoilerMetadataCollision(preparedText.convertedText) ? markdownToMatrixBody(preparedText.convertedText) : void 0;
1021
+ const chunkSegment = (segmentText) => {
1022
+ const protectedUnderline = protectMatrixUnderlineTags(hasMatrixSpoilerMetadataCollision(segmentText) ? markdownToMatrixBody(segmentText) : segmentText);
1023
+ const protectedSpoilers = protectMatrixSpoilerDelimiters(protectedUnderline.markdown);
1024
+ const wrapperReserve = (protectedSpoilers.markers ? 4 : 0) + (protectedUnderline.markers ? 7 : 0);
1025
+ const privateMarkers = [protectedSpoilers.markers, protectedUnderline.markers].flatMap((markers) => markers ? [
1026
+ markers.open,
1027
+ markers.close,
1028
+ markers.padding
1029
+ ] : []);
1030
+ let reserve = wrapperReserve;
1031
+ while (reserve < preparedText.singleEventLimit) {
1032
+ const protectedChunks = getCore$2().channel.text.chunkMarkdownTextWithMode(protectedSpoilers.markdown, preparedText.singleEventLimit - reserve, chunkMode);
1033
+ const restored = restoreMatrixStyleChunks(protectedChunks, protectedSpoilers.markers, protectedUnderline.markers).filter((_, index) => {
1034
+ return privateMarkers.reduce((value, marker) => value.replaceAll(marker, ""), protectedChunks[index] ?? "").length > 0;
1035
+ });
1036
+ const overflow = Math.max(0, ...restored.map((chunk) => Math.max(chunk.length, markdownToMatrixBody(chunk).length) - preparedText.singleEventLimit));
1037
+ if (overflow === 0) return restored;
1038
+ reserve += overflow;
1039
+ }
1040
+ throw new Error("Matrix text chunk limit is too small for formatted content");
1041
+ };
1042
+ const chunks = collisionRedacted !== void 0 ? chunkSegment(collisionRedacted) : preparedText.tableMode === "block" ? splitMatrixTableSegments(preparedText.convertedText).flatMap((segment) => {
1043
+ if (!segment.table) return chunkSegment(segment.text);
1044
+ return segment.text.length <= preparedText.singleEventLimit ? [segment.text] : chunkSegment(renderMatrixMarkdownTables(segment.text, "bullets"));
1045
+ }) : chunkSegment(preparedText.convertedText);
1046
+ return {
1047
+ ...preparedText,
1048
+ chunks
1049
+ };
1050
+ }
1051
+ //#endregion
1052
+ //#region extensions/matrix/src/matrix/send/client.ts
1053
+ const loadMatrixSendClientRuntime = createLazyRuntimeModule(() => import("./client-bootstrap-CkAnkzB6.js").then((n) => n.t));
1054
+ function resolveMediaMaxBytes(accountId, cfg) {
1055
+ 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.");
1056
+ const matrixCfg = resolveMatrixAccountConfig({
1057
+ cfg: requireRuntimeConfig(cfg, "Matrix media limits"),
1058
+ accountId
1059
+ });
1060
+ const mediaMaxMb = typeof matrixCfg.mediaMaxMb === "number" ? matrixCfg.mediaMaxMb : void 0;
1061
+ if (typeof mediaMaxMb === "number") return mediaMaxMb * 1024 * 1024;
1062
+ }
1063
+ async function withResolvedMatrixSendClient(opts, run) {
1064
+ return await withResolvedMatrixClient({
1065
+ ...opts,
1066
+ readiness: "started"
1067
+ }, run, "persist");
1068
+ }
1069
+ async function withResolvedMatrixControlClient(opts, run) {
1070
+ return await withResolvedMatrixClient({
1071
+ ...opts,
1072
+ readiness: "none"
1073
+ }, run);
1074
+ }
1075
+ async function withResolvedMatrixClient(opts, run, shutdownBehavior) {
1076
+ if (opts.client) return await run(opts.client);
1077
+ const { withResolvedRuntimeMatrixClient } = await loadMatrixSendClientRuntime();
1078
+ return await withResolvedRuntimeMatrixClient(opts, run, shutdownBehavior);
1079
+ }
1080
+ //#endregion
544
1081
  //#region extensions/matrix/src/matrix/send/types.ts
545
1082
  const MsgType = {
546
1083
  Text: "m.text",
@@ -572,15 +1109,21 @@ const MATRIX_OPENCLAW_FINALIZED_PREVIEW_KEY = "com.openclaw.finalized_preview";
572
1109
  const MSC4357_LIVE_KEY = "org.matrix.msc4357.live";
573
1110
  //#endregion
574
1111
  //#region extensions/matrix/src/matrix/send/formatting.ts
575
- const getCore$2 = () => getMatrixRuntime();
1112
+ const getCore$1 = () => getMatrixRuntime();
576
1113
  async function renderMatrixFormattedContent(params) {
577
1114
  const markdown = params.markdown ?? "";
578
- if (params.includeMentions === false) return { html: markdownToMatrixHtml(markdown).trimEnd() || void 0 };
1115
+ const body = markdownToMatrixBody(markdown);
1116
+ if (params.includeMentions === false) return {
1117
+ body,
1118
+ html: markdownToMatrixHtml(markdown, { tableMode: params.tableMode }).trimEnd() || void 0
1119
+ };
579
1120
  const { html, mentions } = await renderMarkdownToMatrixHtmlWithMentions({
580
1121
  markdown,
581
- client: params.client
1122
+ client: params.client,
1123
+ tableMode: params.tableMode
582
1124
  });
583
1125
  return {
1126
+ body,
584
1127
  html,
585
1128
  mentions
586
1129
  };
@@ -597,11 +1140,13 @@ function buildTextContent(body, relation, opts = {}) {
597
1140
  };
598
1141
  }
599
1142
  async function enrichMatrixFormattedContent(params) {
600
- const { html, mentions } = await renderMatrixFormattedContent({
1143
+ const { body, html, mentions } = await renderMatrixFormattedContent({
601
1144
  client: params.client,
602
1145
  markdown: params.markdown,
603
- includeMentions: params.includeMentions
1146
+ includeMentions: params.includeMentions,
1147
+ tableMode: params.tableMode
604
1148
  });
1149
+ params.content.body = body || params.content.body;
605
1150
  if (mentions) params.content["m.mentions"] = mentions;
606
1151
  else delete params.content["m.mentions"];
607
1152
  if (!html) {
@@ -658,7 +1203,7 @@ function buildThreadRelation(threadId, replyToId) {
658
1203
  return relation;
659
1204
  }
660
1205
  function resolveMatrixMsgType(contentType, _fileName) {
661
- switch (getCore$2().media.mediaKindFromMime(contentType ?? "")) {
1206
+ switch (getCore$1().media.mediaKindFromMime(contentType ?? "")) {
662
1207
  case "image": return MsgType.Image;
663
1208
  case "audio": return MsgType.Audio;
664
1209
  case "video": return MsgType.Video;
@@ -678,7 +1223,7 @@ function isMatrixVoiceCompatibleAudio(opts) {
678
1223
  }
679
1224
  //#endregion
680
1225
  //#region extensions/matrix/src/matrix/send/media.ts
681
- const getCore$1 = () => getMatrixRuntime();
1226
+ const getCore = () => getMatrixRuntime();
682
1227
  function buildMatrixMediaInfo(params) {
683
1228
  const base = {};
684
1229
  if (Number.isFinite(params.size)) base.size = params.size;
@@ -726,20 +1271,20 @@ function buildMediaContent(params) {
726
1271
  const THUMBNAIL_MAX_SIDE = 800;
727
1272
  const THUMBNAIL_QUALITY = 80;
728
1273
  async function prepareImageInfo(params) {
729
- const meta = await getCore$1().media.getImageMetadata(params.buffer).catch(() => null);
1274
+ const meta = await getCore().media.getImageMetadata(params.buffer).catch(() => null);
730
1275
  if (!meta) return;
731
1276
  const imageInfo = {
732
1277
  w: meta.width,
733
1278
  h: meta.height
734
1279
  };
735
1280
  if (Math.max(meta.width, meta.height) > THUMBNAIL_MAX_SIDE) try {
736
- const thumbBuffer = await getCore$1().media.resizeToJpeg({
1281
+ const thumbBuffer = await getCore().media.resizeToJpeg({
737
1282
  buffer: params.buffer,
738
1283
  maxSide: THUMBNAIL_MAX_SIDE,
739
1284
  quality: THUMBNAIL_QUALITY,
740
1285
  withoutEnlargement: true
741
1286
  });
742
- const thumbMeta = await getCore$1().media.getImageMetadata(thumbBuffer).catch(() => null);
1287
+ const thumbMeta = await getCore().media.getImageMetadata(thumbBuffer).catch(() => null);
743
1288
  const result = await uploadMediaWithEncryption(params.client, thumbBuffer, {
744
1289
  contentType: "image/jpeg",
745
1290
  filename: "thumbnail.jpg",
@@ -807,18 +1352,13 @@ var direct_room_exports = /* @__PURE__ */ __exportAll({
807
1352
  isStrictDirectRoom: () => isStrictDirectRoom,
808
1353
  readJoinedMatrixMembers: () => readJoinedMatrixMembers
809
1354
  });
810
- function trimMaybeString(value) {
811
- if (typeof value !== "string") return null;
812
- const trimmed = value.trim();
813
- return trimmed.length > 0 ? trimmed : null;
814
- }
815
1355
  function normalizeJoinedMatrixMembers(joinedMembers) {
816
1356
  if (!Array.isArray(joinedMembers)) return [];
817
- return joinedMembers.map((entry) => trimMaybeString(entry)).filter((entry) => Boolean(entry));
1357
+ return joinedMembers.map((entry) => normalizeNullableString(entry)).filter((entry) => Boolean(entry));
818
1358
  }
819
1359
  function isStrictDirectMembership(params) {
820
- const selfUserId = trimMaybeString(params.selfUserId);
821
- const remoteUserId = trimMaybeString(params.remoteUserId);
1360
+ const selfUserId = normalizeNullableString(params.selfUserId);
1361
+ const remoteUserId = normalizeNullableString(params.remoteUserId);
822
1362
  const joinedMembers = params.joinedMembers ?? [];
823
1363
  return Boolean(selfUserId && remoteUserId && joinedMembers.length === 2 && joinedMembers.includes(selfUserId) && joinedMembers.includes(remoteUserId));
824
1364
  }
@@ -830,7 +1370,7 @@ async function readJoinedMatrixMembers(client, roomId) {
830
1370
  }
831
1371
  }
832
1372
  async function hasDirectMatrixMemberFlag(client, roomId, userId) {
833
- const normalizedUserId = trimMaybeString(userId);
1373
+ const normalizedUserId = normalizeNullableString(userId);
834
1374
  if (!normalizedUserId) return null;
835
1375
  try {
836
1376
  const state = await client.getRoomStateEvent(roomId, "m.room.member", normalizedUserId);
@@ -842,7 +1382,7 @@ async function hasDirectMatrixMemberFlag(client, roomId, userId) {
842
1382
  }
843
1383
  }
844
1384
  async function inspectMatrixDirectRoomEvidence(params) {
845
- const selfUserId = params.selfUserId !== void 0 ? trimMaybeString(params.selfUserId) : trimMaybeString(await params.client.getUserId().catch(() => null));
1385
+ const selfUserId = params.selfUserId !== void 0 ? normalizeNullableString(params.selfUserId) : normalizeNullableString(await params.client.getUserId().catch(() => null));
846
1386
  const joinedMembers = await readJoinedMatrixMembers(params.client, params.roomId);
847
1387
  const strict = isStrictDirectMembership({
848
1388
  selfUserId,
@@ -1166,8 +1706,6 @@ var send_exports = /* @__PURE__ */ __exportAll({
1166
1706
  sendSingleTextMessageMatrix: () => sendSingleTextMessageMatrix,
1167
1707
  sendTypingMatrix: () => sendTypingMatrix
1168
1708
  });
1169
- const MATRIX_TEXT_LIMIT = 4e3;
1170
- const getCore = () => getMatrixRuntime();
1171
1709
  function createMatrixSendReceipt(params) {
1172
1710
  return createMessageReceiptFromOutboundResults({
1173
1711
  kind: params.kind,
@@ -1230,32 +1768,6 @@ async function resolvePreviousEditMentions(params) {
1230
1768
  body
1231
1769
  });
1232
1770
  }
1233
- function prepareMatrixSingleText(text, opts) {
1234
- const trimmedText = text.trim();
1235
- const cfg = requireRuntimeConfig(opts.cfg, "Matrix text preparation");
1236
- const tableMode = opts.tableMode ?? getCore().channel.text.resolveMarkdownTableMode({
1237
- cfg,
1238
- channel: "matrix",
1239
- accountId: opts.accountId
1240
- });
1241
- const convertedText = getCore().channel.text.convertMarkdownTables(trimmedText, tableMode);
1242
- const singleEventLimit = Math.min(getCore().channel.text.resolveTextChunkLimit(cfg, "matrix", opts.accountId), MATRIX_TEXT_LIMIT);
1243
- return {
1244
- trimmedText,
1245
- convertedText,
1246
- singleEventLimit,
1247
- fitsInSingleEvent: convertedText.length <= singleEventLimit
1248
- };
1249
- }
1250
- function chunkMatrixText(text, opts) {
1251
- const preparedText = prepareMatrixSingleText(text, opts);
1252
- const cfg = requireRuntimeConfig(opts.cfg, "Matrix text chunking");
1253
- const chunkMode = getCore().channel.text.resolveChunkMode(cfg, "matrix", opts.accountId);
1254
- return {
1255
- ...preparedText,
1256
- chunks: getCore().channel.text.chunkMarkdownTextWithMode(preparedText.convertedText, preparedText.singleEventLimit, chunkMode)
1257
- };
1258
- }
1259
1771
  async function sendMessageMatrix(to, message, opts) {
1260
1772
  const trimmedMessage = message?.trim() ?? "";
1261
1773
  if (!trimmedMessage && !opts.mediaUrl) throw new Error("Matrix send requires text or media");
@@ -1267,7 +1779,7 @@ async function sendMessageMatrix(to, message, opts) {
1267
1779
  }, async (client) => {
1268
1780
  const roomId = await resolveMatrixRoomId(client, to);
1269
1781
  const cfg = requireRuntimeConfig(opts.cfg, "Matrix send");
1270
- const { chunks } = chunkMatrixText(trimmedMessage, {
1782
+ const { chunks, tableMode } = chunkMatrixText(trimmedMessage, {
1271
1783
  cfg,
1272
1784
  accountId: opts.accountId
1273
1785
  });
@@ -1344,7 +1856,8 @@ async function sendMessageMatrix(to, message, opts) {
1344
1856
  await enrichMatrixFormattedContent({
1345
1857
  client,
1346
1858
  content,
1347
- markdown: captionMarkdown
1859
+ markdown: captionMarkdown,
1860
+ tableMode
1348
1861
  });
1349
1862
  const eventId = await sendContent(content, receiptKind);
1350
1863
  lastMessageId = eventId ?? lastMessageId;
@@ -1352,26 +1865,28 @@ async function sendMessageMatrix(to, message, opts) {
1352
1865
  const textChunks = useVoice ? chunks : rest;
1353
1866
  const followupRelation = useVoice || threadId ? relation : void 0;
1354
1867
  for (const chunk of textChunks) {
1355
- const text = chunk.trim();
1356
- if (!text) continue;
1868
+ const text = chunk;
1869
+ if (!text.trim()) continue;
1357
1870
  const followup = buildTextContent(text, followupRelation);
1358
1871
  await enrichMatrixFormattedContent({
1359
1872
  client,
1360
1873
  content: followup,
1361
- markdown: text
1874
+ markdown: text,
1875
+ tableMode
1362
1876
  });
1363
1877
  const followupEventId = await sendContent(followup, "text");
1364
1878
  lastMessageId = followupEventId ?? lastMessageId;
1365
1879
  if (followupEventId) platformMessageIds.push(followupEventId);
1366
1880
  }
1367
1881
  } else for (const chunk of chunks.length ? chunks : [""]) {
1368
- const text = chunk.trim();
1369
- if (!text) continue;
1882
+ const text = chunk;
1883
+ if (!text.trim()) continue;
1370
1884
  const content = buildTextContent(text, relation);
1371
1885
  await enrichMatrixFormattedContent({
1372
1886
  client,
1373
1887
  content,
1374
- markdown: text
1888
+ markdown: text,
1889
+ tableMode
1375
1890
  });
1376
1891
  const eventId = await sendContent(content, "text");
1377
1892
  lastMessageId = eventId ?? lastMessageId;
@@ -1445,12 +1960,12 @@ async function sendReadReceiptMatrix(roomId, eventId, client) {
1445
1960
  });
1446
1961
  }
1447
1962
  async function sendSingleTextMessageMatrix(roomId, text, opts) {
1448
- const { trimmedText, convertedText, singleEventLimit, fitsInSingleEvent } = prepareMatrixSingleText(text, {
1963
+ const { trimmedText, convertedText, singleEventLimit, eventTextLength, fitsInSingleEvent, tableMode } = prepareMatrixSingleText(text, {
1449
1964
  cfg: opts.cfg,
1450
1965
  accountId: opts.accountId
1451
1966
  });
1452
1967
  if (!trimmedText) throw new Error("Matrix single-message send requires text");
1453
- if (!fitsInSingleEvent) throw new Error(`Matrix single-message text exceeds limit (${convertedText.length} > ${singleEventLimit})`);
1968
+ if (!fitsInSingleEvent) throw new Error(`Matrix single-message text exceeds limit (${eventTextLength} > ${singleEventLimit})`);
1454
1969
  return await withResolvedMatrixSendClient({
1455
1970
  client: opts.client,
1456
1971
  cfg: opts.cfg,
@@ -1464,7 +1979,8 @@ async function sendSingleTextMessageMatrix(roomId, text, opts) {
1464
1979
  client,
1465
1980
  content,
1466
1981
  markdown: convertedText,
1467
- includeMentions: opts.includeMentions
1982
+ includeMentions: opts.includeMentions,
1983
+ tableMode
1468
1984
  });
1469
1985
  if (opts.live) content[MSC4357_LIVE_KEY] = {};
1470
1986
  const eventId = await client.sendMessage(resolvedRoom, content);
@@ -1495,19 +2011,18 @@ async function editMessageMatrix(roomId, originalEventId, newText, opts) {
1495
2011
  timeoutMs: opts.timeoutMs
1496
2012
  }, async (client) => {
1497
2013
  const resolvedRoom = await resolveMatrixRoomId(client, roomId);
1498
- const cfg = requireRuntimeConfig(opts.cfg, "Matrix message edit");
1499
- const tableMode = getCore().channel.text.resolveMarkdownTableMode({
1500
- cfg,
1501
- channel: "matrix",
1502
- accountId: opts.accountId
2014
+ const { convertedText, tableMode } = prepareMatrixSingleText(newText, {
2015
+ cfg: requireRuntimeConfig(opts.cfg, "Matrix message edit"),
2016
+ accountId: opts.accountId,
2017
+ preserveWhitespace: true
1503
2018
  });
1504
- const convertedText = getCore().channel.text.convertMarkdownTables(newText, tableMode);
1505
2019
  const newContent = withMatrixExtraContentFields(buildTextContent(convertedText, void 0, { msgtype: opts.msgtype }), opts.extraContent);
1506
2020
  await enrichMatrixFormattedContent({
1507
2021
  client,
1508
2022
  content: newContent,
1509
2023
  markdown: convertedText,
1510
- includeMentions: opts.includeMentions
2024
+ includeMentions: opts.includeMentions,
2025
+ tableMode
1511
2026
  });
1512
2027
  const previousEvent = await getPreviousMatrixEvent(client, resolvedRoom, originalEventId);
1513
2028
  const replaceMentions = opts.includeMentions === false ? void 0 : diffMatrixMentions(extractMatrixMentions(newContent), await resolvePreviousEditMentions({
@@ -1524,7 +2039,7 @@ async function editMessageMatrix(roomId, originalEventId, newText, opts) {
1524
2039
  }
1525
2040
  const content = {
1526
2041
  ...newContent,
1527
- body: `* ${convertedText}`,
2042
+ body: `* ${newContent.body}`,
1528
2043
  ...typeof newContent.formatted_body === "string" ? { formatted_body: `* ${newContent.formatted_body}` } : {},
1529
2044
  "m.new_content": newContent,
1530
2045
  "m.relates_to": replaceRelation
@@ -1551,4 +2066,4 @@ async function reactMatrixMessage(roomId, messageId, emoji, opts) {
1551
2066
  });
1552
2067
  }
1553
2068
  //#endregion
1554
- 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_OPENCLAW_FINALIZED_PREVIEW_KEY as v, isPollEventType as w, buildPollResultsSummary as x, MsgType as y };
2069
+ export { formatPollResultsAsText as C, parsePollStartContent as D, parsePollStart as E, resolvePollReferenceEventId as O, formatPollAsText as S, isPollStartType as T, MsgType as _, sendSingleTextMessageMatrix as a, buildPollResponseContent as b, resolveMatrixRoomId as c, repairMatrixDirectRooms as d, direct_room_exports as f, MATRIX_OPENCLAW_FINALIZED_PREVIEW_KEY as g, readJoinedMatrixMembers as h, sendPollMatrix as i, direct_management_exports as l, isStrictDirectMembership as m, reactMatrixMessage as n, sendTypingMatrix as o, hasDirectMatrixMemberFlag as p, sendMessageMatrix as r, send_exports as s, editMessageMatrix as t, promoteMatrixDirectRoomCandidate as u, chunkMatrixText as v, isPollEventType as w, buildPollResultsSummary as x, prepareMatrixSingleText as y };