@openclaw/matrix 2026.7.2-beta.7 → 2026.8.1-beta.3

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 (92) hide show
  1. package/dist/{account-selection-kqquRzCf.js → account-selection-CWuhuFn1.js} +7 -8
  2. package/dist/{accounts-DfngqebB.js → accounts-BCzyubQt.js} +3 -3
  3. package/dist/api.js +8 -11
  4. package/dist/{approval-handler.runtime-DSeaIsGa.js → approval-handler.runtime-CSvueU9P.js} +7 -6
  5. package/dist/{approval-ids-BdJGPAJT.js → approval-ids-CEcf-EiM.js} +1 -1
  6. package/dist/{approval-reaction-auth-BjOpELxu.js → approval-reaction-auth-CziKXatm.js} +2 -2
  7. package/dist/{approval-reactions-D-BxBK_L.js → approval-reactions-tXh9KYrW.js} +2 -8
  8. package/dist/auth-presence.js +2 -2
  9. package/dist/{channel-vj788dNZ.js → channel-CzQw5OZN.js} +376 -148
  10. package/dist/channel-plugin-api.js +1 -1
  11. package/dist/{channel.runtime-CLoF2S9T.js → channel.runtime-I9pBRQb3.js} +79 -55
  12. package/dist/{config-schema-Djxw3C6b.js → channel.setup-aNaa3Uyn.js} +51 -10
  13. package/dist/{cli-DMqfDE-q.js → cli-iR6QJ5U0.js} +16 -16
  14. package/dist/{cli-metadata-C7D53SND.js → cli-metadata-BmUYWjuM.js} +1 -1
  15. package/dist/cli-metadata.js +1 -1
  16. package/dist/client-BBQdBG7t.js +799 -0
  17. package/dist/{client-6eRENr7k.js → client-D8XghqmX.js} +4 -4
  18. package/dist/{client-bootstrap-XsIkHNt0.js → client-bootstrap-Co3RlItb.js} +25 -61
  19. package/dist/client-support-DLj4vDeu.js +93 -0
  20. package/dist/config-update-Bor_Pjsv.js +150 -0
  21. package/dist/contract-api.js +2 -2
  22. package/dist/{credentials-W2EmwRhu.js → credentials-BdAaWuiR.js} +2 -1
  23. package/dist/credentials-read-B37cQknZ.js +51 -0
  24. package/dist/credentials-state-B3cuUFwZ.js +27 -0
  25. package/dist/{credentials-write.runtime-Be9zNSUw.js → credentials-write.runtime-nAV2-5uR.js} +1 -1
  26. package/dist/{crypto-runtime-D9_6J5tZ.js → crypto-runtime-Bnz3zEcS.js} +55 -32
  27. package/dist/direct-room-RinHjtqX.js +71 -0
  28. package/dist/{directory-live-C8dVBiCc.js → directory-live-DLQzel12.js} +2 -3
  29. package/dist/{doctor-CM8ijNpW.js → doctor-EWf9CDso.js} +2 -2
  30. package/dist/{doctor-contract-BMSJkkAE.js → doctor-contract-DEt4O4Zt.js} +2 -3
  31. package/dist/doctor-contract-api.js +209 -91
  32. package/dist/{draft-stream-CtvOXq0Z.js → draft-stream-DQ7QNC-l.js} +20 -23
  33. package/dist/{encryption-guidance-CbEFe38F.js → encryption-guidance-DmLY8NiO.js} +1 -1
  34. package/dist/{env-auth-CQsYbz-p.js → env-auth-MVS-Vv41.js} +4 -3
  35. package/dist/{env-vars-B0NxQnPk.js → env-vars-dGqak9dN.js} +1 -1
  36. package/dist/{errors-C47hvAF8.js → errors-DKVS3_DO.js} +2 -5
  37. package/dist/{file-sync-store-DiiFsayX.js → file-sync-store-DjskBX2o.js} +24 -3
  38. package/dist/helper-api.js +2 -2
  39. package/dist/{idb-persistence-B6x7dMsF.js → idb-persistence-BM0P27Zg.js} +3 -2
  40. package/dist/{inbound-dedupe-CaBLblef.js → inbound-dedupe-RvSLqdsz.js} +2 -2
  41. package/dist/index.js +1 -1
  42. package/dist/{logger-D0GCSDQq.js → logger-B_71hN6W.js} +1 -1
  43. package/dist/{logging-DV-p52N0.js → logging-DRjvUOIh.js} +1 -1
  44. package/dist/{media-text-BTwzADS_.js → media-text-Dh-DpTc3.js} +4 -1
  45. package/dist/{messages-DxQJkhxs.js → messages-DEqg7HVy.js} +31 -11
  46. package/dist/{monitor-VyWlSLwo.js → monitor-FNrfkuWL.js} +413 -405
  47. package/dist/plugin-entry.handlers.runtime.js +3 -3
  48. package/dist/probe.runtime-NIJZYqc2.js +2 -0
  49. package/dist/profile-D-Wubo0A.js +108 -0
  50. package/dist/{profile-update-CffmyW_4.js → profile-update-BQlE5s2J.js} +3 -2
  51. package/dist/{reaction-events-B00WSi98.js → reaction-events-BKOLlnCo.js} +10 -7
  52. package/dist/{recovery-key-store-BNnHvXdk.js → recovery-key-store-CognuDys.js} +5 -4
  53. package/dist/{resolve-targets-nm2L48sh.js → resolve-targets-e7AI0P75.js} +2 -2
  54. package/dist/{resolver.runtime-DS6uPaEe.js → resolver.runtime-V8u0pHEb.js} +1 -1
  55. package/dist/{room-info-CAFZ3Z9J.js → room-info-BXQA7y10.js} +12 -3
  56. package/dist/runtime-api.js +7 -4
  57. package/dist/{sdk-bUytQHx7.js → sdk-NxK3vzMa.js} +242 -119
  58. package/dist/{secret-contract-M3hRmGO-.js → secret-contract-CufozuyJ.js} +1 -1
  59. package/dist/secret-contract-api.js +1 -1
  60. package/dist/{send-BldGYG8g.js → send-ClHgDZLT.js} +1477 -1210
  61. package/dist/session-binding-contract-api.js +2 -3
  62. package/dist/{setup-bootstrap-Deleiibz.js → setup-bootstrap-BGPjbA5N.js} +4 -4
  63. package/dist/{setup-core-BVfBv2ai.js → setup-core-DigrLebY.js} +17 -266
  64. package/dist/setup-plugin-api.js +1 -44
  65. package/dist/{setup-surface-BBq3WeqX.js → setup-surface-Dtn8sAWs.js} +15 -16
  66. package/dist/{startup-verification-wm3_i88s.js → startup-verification-Cs08ca8u.js} +3 -3
  67. package/dist/{storage-BvAkgzvT.js → storage-BsGuHngK.js} +3 -3
  68. package/dist/subagent-hooks-CMoZaARG.js +48 -0
  69. package/dist/subagent-hooks-api.js +1 -1
  70. package/dist/{target-ids-B-5aQxwn.js → target-ids-Nwx7cMVE.js} +11 -1
  71. package/dist/{thread-binding-api-DRbBZlhS.js → thread-binding-api-CzkeLZCU.js} +1 -1
  72. package/dist/thread-binding-api.js +1 -1
  73. package/dist/{thread-bindings-DGbl3vDW.js → thread-bindings-BYEGfw3h.js} +17 -10
  74. package/dist/thread-bindings-runtime.js +1 -1
  75. package/dist/{thread-bindings-shared-CKnY4LSd.js → thread-bindings-shared-oNPiyMWY.js} +1 -6
  76. package/dist/{tool-actions.runtime-uu9blSqO.js → tool-actions.runtime-ck6IJyP0.js} +36 -19
  77. package/dist/{verification-DYMQ1QpI.js → verification-CabtBTtB.js} +2 -2
  78. package/openclaw.plugin.json +61 -1
  79. package/package.json +4 -4
  80. package/dist/active-client-DXvz2gCo.js +0 -20
  81. package/dist/client-Dbso3-0V.js +0 -30
  82. package/dist/create-client-BmSU-q-D.js +0 -61
  83. package/dist/credentials-read-DVmFCm7X.js +0 -77
  84. package/dist/exec-approval-resolver-BCgqRtZS.js +0 -16
  85. package/dist/preflight-audio.runtime-BZxxQsxY.js +0 -11
  86. package/dist/probe.runtime-BuieqmS3.js +0 -3
  87. package/dist/record-shared-CWWx6fvI.js +0 -2
  88. package/dist/route-DY6at4qJ.js +0 -161
  89. package/dist/shared-CUMWxcMz.js +0 -673
  90. package/dist/subagent-hooks-dkPAF6Et.js +0 -143
  91. package/dist/sync-state-CWbp0QSY.js +0 -12
  92. package/dist/{runtime-api-D1xI_cvh.js → runtime-api-BGO16Fu8.js} +3 -3
@@ -1,51 +1,49 @@
1
1
  import { a as resolveMatrixAccountConfig, i as resolveMatrixAccountAllowlistConfig } from "./account-config-BZWN9tGS.js";
2
2
  import { t as getMatrixRuntime } from "./runtime-6S3DNFNv.js";
3
- import { n as resolveConfiguredMatrixBotUserIds } from "./accounts-DfngqebB.js";
4
- import { a as buildAllowlistResolutionSummary, c as createTypingCallbacks, d as logInboundDrop, f as logTypingFailure, h as toLocationContext, i as addAllowlistUserEntriesFromConfigEntry, l as formatLocationText, m as summarizeMapping, n as resolveMatrixStoredSessionMeta, o as canonicalizeAllowlistWithResolvedIds, p as patchAllowlistUsersInConfigEntries, r as resolveMatrixRoomConfig, s as createReplyPrefixOptions, u as getAgentScopedMediaLocalRoots } from "./channel-vj788dNZ.js";
5
- import { a as normalizeMatrixUserId, i as normalizeMatrixAllowList, o as resolveMatrixAllowListMatch } from "./config-schema-Djxw3C6b.js";
6
- import { t as isMatrixQualifiedUserId } from "./target-ids-B-5aQxwn.js";
7
- import { t as formatMatrixErrorMessage } from "./errors-C47hvAF8.js";
8
- import { D as parsePollStartContent, S as formatPollAsText, T as isPollStartType, g as MATRIX_OPENCLAW_FINALIZED_PREVIEW_KEY, h as readJoinedMatrixMembers, m as isStrictDirectMembership, p as hasDirectMatrixMemberFlag, r as sendMessageMatrix, u as promoteMatrixDirectRoomCandidate, v as chunkMatrixText, w as isPollEventType } from "./send-BldGYG8g.js";
9
- import { t as createMatrixThreadBindingManager } from "./thread-bindings-DGbl3vDW.js";
10
- import { i as createMatrixInboundEventDeduper } from "./inbound-dedupe-CaBLblef.js";
11
- import { a as resolveMatrixMessageAttachment, c as fetchMatrixPollSnapshot, i as isLikelyBareFilename, n as formatMatrixMediaUnavailableText, o as resolveMatrixMessageBody, r as formatMatrixMessageText, t as formatMatrixMediaTooLargeText } from "./media-text-BTwzADS_.js";
12
- import { n as setActiveMatrixClient } from "./active-client-DXvz2gCo.js";
3
+ import { n as resolveConfiguredMatrixBotUserIds } from "./accounts-BCzyubQt.js";
4
+ import { _ as logInboundDrop, a as resolveMatrixThreadRootId, b as summarizeMapping, c as RelationType, d as buildAllowlistResolutionSummary, f as canonicalizeAllowlistWithResolvedIds, g as getAgentScopedMediaLocalRoots, h as formatLocationText, i as resolveMatrixReplyToEventId, l as resolveMatrixRoomConfig, m as createTypingCallbacks, n as resolveMatrixStoredSessionMeta, o as resolveMatrixThreadRouting, p as createReplyPrefixOptions, r as resolveMatrixInboundRoute, s as EventType, u as addAllowlistUserEntriesFromConfigEntry, v as logTypingFailure, x as toLocationContext, y as patchAllowlistUsersInConfigEntries } from "./channel-CzQw5OZN.js";
5
+ import { a as normalizeMatrixUserId, i as normalizeMatrixAllowList, o as resolveMatrixAllowListMatch } from "./channel.setup-aNaa3Uyn.js";
6
+ import { n as isMatrixRoomId, t as isMatrixQualifiedUserId } from "./target-ids-Nwx7cMVE.js";
7
+ import { C as promoteMatrixDirectRoomCandidate, T as MATRIX_OPENCLAW_FINALIZED_PREVIEW_KEY, _ as parsePollStartContent, c as chunkMatrixText, f as formatPollAsText, h as isPollStartType, m as isPollEventType, r as sendMessageMatrix } from "./send-ClHgDZLT.js";
8
+ import { i as isStrictDirectMembership, n as hasDirectMatrixMemberFlag, o as readJoinedMatrixMembers } from "./direct-room-RinHjtqX.js";
9
+ import { t as createMatrixThreadBindingManager } from "./thread-bindings-BYEGfw3h.js";
10
+ import { a as resolveMatrixMessageAttachment, c as fetchMatrixPollSnapshot, i as isLikelyBareFilename, n as formatMatrixMediaUnavailableText, o as resolveMatrixMessageBody, r as formatMatrixMessageText, t as formatMatrixMediaTooLargeText } from "./media-text-Dh-DpTc3.js";
13
11
  import { t as isBunRuntime } from "./runtime-BefyhPWv.js";
14
- import { d as backfillMatrixAuthDeviceIdAfterStartup, f as resolveMatrixAuth, i as resolveSharedMatrixClient, l as isMatrixStartupAbortError, n as releaseSharedClientInstance, p as resolveMatrixAuthContext, u as throwIfMatrixStartupAborted } from "./shared-CUMWxcMz.js";
15
- import "./client-Dbso3-0V.js";
12
+ import { a as throwIfMatrixStartupAborted, c as resolveMatrixAuth, i as isMatrixStartupAbortError, l as resolveMatrixAuthContext, n as acquireSharedMatrixClient, s as backfillMatrixAuthDeviceIdAfterStartup } from "./client-BBQdBG7t.js";
16
13
  import { i as isMatrixMediaSizeLimitError, r as MatrixMediaSizeLimitError } from "./http-client-CycYxU9-.js";
17
- import { d as resolveAllowlistProviderRuntimeGroupPolicy, f as resolveDefaultGroupPolicy, g as warnMissingProviderGroupPolicyFallbackOnce, h as resolveThreadBindingMaxAgeMsForChannel, m as resolveThreadBindingIdleTimeoutMsForChannel, t as GROUP_POLICY_BLOCKED_LABEL } from "./runtime-api-D1xI_cvh.js";
18
- import { t as resolveMatrixTargets } from "./resolve-targets-nm2L48sh.js";
19
- import { t as formatMatrixEncryptedEventDisabledWarning } from "./encryption-guidance-CbEFe38F.js";
20
- import { n as isMatrixReadySyncState, r as isMatrixTerminalSyncState, t as isMatrixDisconnectedSyncState } from "./sync-state-CWbp0QSY.js";
21
- import { n as setBoundedMap, t as createMatrixRoomInfoResolver } from "./room-info-CAFZ3Z9J.js";
22
- import { a as EventType, i as resolveMatrixThreadRouting, n as resolveMatrixReplyToEventId, o as RelationType, r as resolveMatrixThreadRootId, t as resolveMatrixInboundRoute } from "./route-DY6at4qJ.js";
14
+ import { d as resolveAllowlistProviderRuntimeGroupPolicy, f as resolveDefaultGroupPolicy, g as warnMissingProviderGroupPolicyFallbackOnce, h as resolveThreadBindingMaxAgeMsForChannel, m as resolveThreadBindingIdleTimeoutMsForChannel, t as GROUP_POLICY_BLOCKED_LABEL } from "./runtime-api-BGO16Fu8.js";
15
+ import { t as resolveMatrixTargets } from "./resolve-targets-e7AI0P75.js";
16
+ import { t as formatMatrixEncryptedEventDisabledWarning } from "./encryption-guidance-DmLY8NiO.js";
17
+ import { createMatrixInboundEventDeduper } from "./inbound-dedupe-RvSLqdsz.js";
18
+ import { a as isMatrixAccessTokenInvalidatedError, d as isMatrixDisconnectedSyncState, f as isMatrixReadySyncState, p as isMatrixTerminalSyncState } from "./client-support-DLj4vDeu.js";
19
+ import { n as setBoundedMap, t as createMatrixRoomInfoResolver } from "./room-info-BXQA7y10.js";
23
20
  import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
24
- import { buildChannelProgressDraftLineForEntry, createChannelProgressDraftGate, createMessageReceiptFromOutboundResults, createPreviewMessageReceipt, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, formatChannelProgressDraftLine, formatChannelProgressDraftText, isChannelProgressDraftWorkToolName, listMessageReceiptPlatformIds, mergeChannelProgressDraftLine, normalizeChannelProgressDraftLineIdentity, resolveChannelProgressDraftMaxLines, resolveChannelStreamingBlockEnabled, waitUntilAbort } from "openclaw/plugin-sdk/channel-outbound";
21
+ import { buildChannelProgressDraftLineForEntry, createChannelProgressDraftCompositor, createMessageReceiptFromOutboundResults, createPreviewMessageReceipt, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, formatChannelProgressDraftLine, formatChannelProgressDraftText, listMessageReceiptPlatformIds, resolveChannelStreamingBlockEnabled, waitUntilAbort } from "openclaw/plugin-sdk/channel-outbound";
25
22
  import { normalizeLowercaseStringOrEmpty, normalizeNullableString, normalizeOptionalString, normalizeStringifiedEntries, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
26
23
  import { stripReasoningTagsFromText } from "openclaw/plugin-sdk/text-chunking";
27
24
  import { CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY } from "openclaw/plugin-sdk/approval-handler-adapter-runtime";
25
+ import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
26
+ import { isFutureDateTimestampMs, parseStrictFiniteNumber, resolveExpiresAtMsFromDurationMs, resolveOptionalIntegerOption } from "openclaw/plugin-sdk/number-runtime";
28
27
  import { resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
29
- import { buildChannelInboundEventContext, createChannelInboundEnvelopeBuilder, createChannelPartialDeliveryError, hasFinalInboundReplyDispatch, isChannelPartialDeliveryError, resolveInboundMentionDecision, toInboundMediaFactsWithMetadata } from "openclaw/plugin-sdk/channel-inbound";
28
+ import { createChannelInboundEnvelopeBuilder, createChannelPartialDeliveryError, hasFinalInboundReplyDispatch, isChannelPartialDeliveryError, resolveInboundMentionDecision, resolveInboundReplyDispatchCounts, toInboundMediaFactsWithMetadata } from "openclaw/plugin-sdk/channel-inbound";
30
29
  import { getSessionEntry, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
31
- import { isFutureDateTimestampMs, parseStrictFiniteNumber, resolveExpiresAtMsFromDurationMs, resolveOptionalIntegerOption } from "openclaw/plugin-sdk/number-runtime";
32
- import { resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
33
30
  import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
34
31
  import { format } from "node:util";
35
- import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
32
+ import { resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
36
33
  import { escapeRegExp, sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
37
34
  import { buildTtsSupplementMediaPayload, getReplyPayloadTtsSupplement } from "openclaw/plugin-sdk/reply-payload";
38
35
  import { resolveAckReaction } from "openclaw/plugin-sdk/channel-feedback";
39
36
  import { registerChannelRuntimeContext } from "openclaw/plugin-sdk/channel-runtime-context";
40
37
  import { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/dangerous-name-runtime";
41
38
  import { createChannelIngressResolver, defineStableChannelIngressIdentity } from "openclaw/plugin-sdk/channel-ingress-runtime";
39
+ import { resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime";
42
40
  import { evaluateSupplementalContextVisibility, resolveChannelContextVisibilityMode } from "openclaw/plugin-sdk/context-visibility-runtime";
43
41
  import { getGlobalHookRunner } from "openclaw/plugin-sdk/plugin-runtime";
44
- import { resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/security-runtime";
42
+ import { createChannelPreflightAudio, formatAudioTranscriptForAgent } from "openclaw/plugin-sdk/media-understanding-runtime";
45
43
  import { mergePairLoopGuardConfig } from "openclaw/plugin-sdk/pair-loop-guard-runtime";
46
44
  import { buildInboundHistoryFromEntries } from "openclaw/plugin-sdk/reply-history";
47
45
  import { decodeHtmlEntities } from "openclaw/plugin-sdk/html-entity-runtime";
48
- import { createConnectedChannelStatusPatch, createTransportActivityStatusPatch } from "openclaw/plugin-sdk/gateway-runtime";
46
+ import { channelBlockedPatch, channelReadyPatch, channelStoppedPatch, createTransportActivityStatusPatch } from "openclaw/plugin-sdk/gateway-runtime";
49
47
  //#region extensions/matrix/src/matrix/monitor/auto-join.ts
50
48
  function registerMatrixAutoJoin(params) {
51
49
  const { client, accountConfig, runtime } = params;
@@ -60,7 +58,7 @@ function registerMatrixAutoJoin(params) {
60
58
  const allowedRoomIds = new Set(rawAllowlist.filter((entry) => entry.startsWith("!")));
61
59
  const allowedAliases = rawAllowlist.filter((entry) => entry.startsWith("#"));
62
60
  const resolvedAliasRoomIds = /* @__PURE__ */ new Map();
63
- if (autoJoin === "off") return;
61
+ if (autoJoin === "off") return () => {};
64
62
  if (autoJoin === "always") logVerbose("matrix: auto-join enabled for all invites");
65
63
  else logVerbose("matrix: auto-join enabled for allowlist invites");
66
64
  const resolveAllowedAliasRoomId = async (alias) => {
@@ -79,13 +77,8 @@ function registerMatrixAutoJoin(params) {
79
77
  }
80
78
  }))).filter((roomId) => Boolean(roomId));
81
79
  };
82
- const runInviteTask = (roomId, task) => {
83
- Promise.resolve().then(task).catch((err) => {
84
- runtime.error?.(`matrix: auto-join invite handler failed for room ${roomId}: ${String(err)}`);
85
- });
86
- };
87
- client.on("room.invite", (roomId, _inviteEvent) => {
88
- runInviteTask(roomId, async () => {
80
+ const onInvite = (roomId, _inviteEvent) => {
81
+ params.runDetachedTask(`auto-join invite handler room=${roomId}`, async () => {
89
82
  if (autoJoin === "allowlist") {
90
83
  const allowedAliasRoomIds = await resolveAllowedAliasRoomIds();
91
84
  if (!(autoJoinAllowlist.has("*") || allowedRoomIds.has(roomId) || allowedAliasRoomIds.some((resolvedRoomId) => resolvedRoomId === roomId))) {
@@ -100,7 +93,11 @@ function registerMatrixAutoJoin(params) {
100
93
  runtime.error?.(`matrix: failed to join room ${roomId}: ${String(err)}`);
101
94
  }
102
95
  });
103
- });
96
+ };
97
+ client.on("room.invite", onInvite);
98
+ return () => {
99
+ client.off("room.invite", onInvite);
100
+ };
104
101
  }
105
102
  //#endregion
106
103
  //#region extensions/matrix/src/matrix/monitor/config.ts
@@ -155,7 +152,7 @@ function isMatrixDangerousNameMatchingEnabled(params) {
155
152
  function addUniqueMatrixAllowlistEntry(params) {
156
153
  const trimmed = params.entry.trim();
157
154
  if (!trimmed) return;
158
- const key = trimmed.toLowerCase();
155
+ const key = normalizeMatrixUserId(trimmed);
159
156
  if (params.seen.has(key)) return;
160
157
  params.seen.add(key);
161
158
  params.entries.push(trimmed);
@@ -188,7 +185,8 @@ function resolveStableMatrixMonitorUserAllowlist(params) {
188
185
  const resolution = resolveStableMatrixMonitorUserEntries(allowList);
189
186
  const canonicalized = canonicalizeAllowlistWithResolvedIds({
190
187
  existing: allowList,
191
- resolvedMap: resolution.resolvedMap
188
+ resolvedMap: resolution.resolvedMap,
189
+ entryKey: normalizeMatrixUserId
192
190
  });
193
191
  logStableMatrixAllowlistUnresolved({
194
192
  label: params.label,
@@ -265,7 +263,8 @@ async function resolveMatrixMonitorUserAllowlist(params) {
265
263
  });
266
264
  const canonicalized = canonicalizeAllowlistWithResolvedIds({
267
265
  existing: allowList,
268
- resolvedMap: resolution.resolvedMap
266
+ resolvedMap: resolution.resolvedMap,
267
+ entryKey: normalizeMatrixUserId
269
268
  });
270
269
  summarizeMapping(params.label, resolution.mapping, resolution.unresolved, params.runtime);
271
270
  if (resolution.unresolved.length > 0) params.runtime.log?.(`${params.label} entries must be full Matrix IDs (example: @user:server). Unresolved entries will not match any sender.`);
@@ -334,7 +333,8 @@ async function resolveMatrixMonitorLiveUserAllowlist(params) {
334
333
  entries: pending,
335
334
  runtime: params.runtime,
336
335
  resolveTargets: params.resolveTargets ?? resolveMatrixTargets
337
- })).resolvedMap
336
+ })).resolvedMap,
337
+ entryKey: normalizeMatrixUserId
338
338
  });
339
339
  const resolvedEntries = params.failClosedOnUnresolved ? filterFailClosedMatrixAllowlistEntries(canonicalized) : filterResolvedMatrixAllowlistEntries(canonicalized);
340
340
  for (const entry of resolvedEntries) addUniqueMatrixAllowlistEntry({
@@ -365,7 +365,7 @@ async function resolveMatrixMonitorRoomsConfig(params) {
365
365
  unresolved.push(entry);
366
366
  continue;
367
367
  }
368
- if (cleaned.startsWith("!") && cleaned.includes(":")) {
368
+ if (isMatrixRoomId(cleaned)) {
369
369
  if (!nextRooms[cleaned]) nextRooms[cleaned] = roomConfig;
370
370
  if (cleaned !== input) mapping.push(`${input}→${cleaned}`);
371
371
  continue;
@@ -396,7 +396,7 @@ async function resolveMatrixMonitorRoomsConfig(params) {
396
396
  } else unresolved.push(source.input);
397
397
  });
398
398
  summarizeMapping("matrix rooms", mapping, unresolved, params.runtime);
399
- if (unresolved.length > 0) params.runtime.log?.("matrix rooms must be room IDs or aliases (example: !room:server or #alias:server). Unresolved entries are ignored.");
399
+ if (unresolved.length > 0) params.runtime.log?.("matrix rooms must be room IDs or aliases (example: !room:server, the suffixless !room form on room version 12+, or #alias:server). Unresolved entries are ignored.");
400
400
  const roomUsers = /* @__PURE__ */ new Set();
401
401
  for (const roomConfig of Object.values(nextRooms)) addAllowlistUserEntriesFromConfigEntry(roomUsers, roomConfig);
402
402
  if (roomUsers.size === 0) return nextRooms;
@@ -410,7 +410,8 @@ async function resolveMatrixMonitorRoomsConfig(params) {
410
410
  return patchAllowlistUsersInConfigEntries({
411
411
  entries: nextRooms,
412
412
  resolvedMap: resolution.resolvedMap,
413
- strategy: "canonicalize"
413
+ strategy: "canonicalize",
414
+ entryKey: normalizeMatrixUserId
414
415
  });
415
416
  }
416
417
  const resolution = await resolveMatrixMonitorUserEntries({
@@ -425,7 +426,8 @@ async function resolveMatrixMonitorRoomsConfig(params) {
425
426
  return patchAllowlistUsersInConfigEntries({
426
427
  entries: nextRooms,
427
428
  resolvedMap: resolution.resolvedMap,
428
- strategy: "canonicalize"
429
+ strategy: "canonicalize",
430
+ entryKey: normalizeMatrixUserId
429
431
  });
430
432
  }
431
433
  async function resolveMatrixMonitorConfig(params) {
@@ -719,34 +721,38 @@ async function resolveMatrixMonitorAccessState(params) {
719
721
  });
720
722
  const accountId = params.accountId ?? "default";
721
723
  const eventKind = params.eventKind ?? "message";
724
+ const ingress = createChannelIngressResolver({
725
+ channelId: "matrix",
726
+ accountId,
727
+ identity: matrixIngressIdentity,
728
+ readStoreAllowFrom: async () => params.storeAllowFrom
729
+ });
730
+ const resolveMessageIngress = async (contextBinding, conversation) => await ingress.message({
731
+ subject: { stableId: params.senderId },
732
+ conversation: conversation ?? {
733
+ kind: params.isRoom ? "channel" : "direct",
734
+ id: params.conversationId ?? (params.isRoom ? "matrix-room" : "matrix-dm")
735
+ },
736
+ ...contextBinding ? { contextBinding } : {},
737
+ event: {
738
+ kind: eventKind,
739
+ authMode: "inbound",
740
+ mayPair: params.isRoom ? false : eventKind === "message"
741
+ },
742
+ dmPolicy,
743
+ groupPolicy: params.isRoom ? groupIngress.groupPolicy : "disabled",
744
+ policy: { groupAllowFromFallbackToAllowFrom: false },
745
+ allowFrom: params.allowFrom,
746
+ ...params.isRoom ? { groupAllowFrom: groupIngress.groupAllowFrom } : {}
747
+ });
722
748
  return {
723
749
  effectiveGroupAllowFrom,
724
750
  effectiveRoomUsers,
725
- messageIngress: await createChannelIngressResolver({
726
- channelId: "matrix",
727
- accountId,
728
- identity: matrixIngressIdentity,
729
- readStoreAllowFrom: async () => params.storeAllowFrom
730
- }).message({
731
- subject: { stableId: params.senderId },
732
- conversation: {
733
- kind: params.isRoom ? "group" : "direct",
734
- id: params.isRoom ? "matrix-room" : "matrix-dm"
735
- },
736
- event: {
737
- kind: eventKind,
738
- authMode: "inbound",
739
- mayPair: params.isRoom ? false : eventKind === "message"
740
- },
741
- dmPolicy,
742
- groupPolicy: params.isRoom ? groupIngress.groupPolicy : "disabled",
743
- policy: { groupAllowFromFallbackToAllowFrom: false },
744
- allowFrom: params.allowFrom,
745
- ...params.isRoom ? { groupAllowFrom: groupIngress.groupAllowFrom } : {}
746
- }),
751
+ messageIngress: await resolveMessageIngress(),
747
752
  accountId,
748
753
  senderId: params.senderId,
749
- isRoom: params.isRoom
754
+ isRoom: params.isRoom,
755
+ resolveMessageIngress
750
756
  };
751
757
  }
752
758
  async function resolveMatrixMonitorCommandAccess(state, params) {
@@ -813,7 +819,7 @@ const matrixVerificationConstants = {
813
819
  const MAX_TRACKED_VERIFICATION_EVENTS = 1024;
814
820
  const SAS_NOTICE_RETRY_DELAY_MS = 750;
815
821
  const VERIFICATION_EVENT_STARTUP_GRACE_MS = 3e4;
816
- const loadMatrixDirectRoomDeps = createLazyRuntimeModule(() => Promise.all([import("./send-BldGYG8g.js").then((n) => n.l), import("./send-BldGYG8g.js").then((n) => n.f)]).then(([directManagementModule, directRoomModule]) => ({
822
+ const loadMatrixDirectRoomDeps = createLazyRuntimeModule(() => Promise.all([import("./send-ClHgDZLT.js").then((n) => n.S), import("./direct-room-RinHjtqX.js").then((n) => n.t)]).then(([directManagementModule, directRoomModule]) => ({
817
823
  inspectMatrixDirectRooms: directManagementModule.inspectMatrixDirectRooms,
818
824
  isStrictDirectRoom: directRoomModule.isStrictDirectRoom
819
825
  })));
@@ -1253,7 +1259,7 @@ async function resolveMatrixSelfUserId(client, logVerboseMessage) {
1253
1259
  }
1254
1260
  }
1255
1261
  function registerMatrixMonitorEvents(params) {
1256
- const { cfg, client, auth, allowFrom, dmEnabled, dmPolicy, readStoreAllowFrom, directTracker, logVerboseMessage, warnedEncryptedRooms, warnedCryptoMissingRooms, logger, startupGraceMs, getHealthySyncSinceMs, formatNativeDependencyHint, onRoomMessage, runDetachedTask, sasNoticeRetryDelayMs } = params;
1262
+ const { cfg, client, auth, allowFrom, dmEnabled, dmPolicy, readStoreAllowFrom, directTracker, invalidateMemberDisplayName, logVerboseMessage, warnedEncryptedRooms, warnedCryptoMissingRooms, logger, startupGraceMs, getHealthySyncSinceMs, formatNativeDependencyHint, onRoomMessage, runDetachedTask, sasNoticeRetryDelayMs } = params;
1257
1263
  const postHealthySyncDecryptFailureTracker = createMatrixPostHealthySyncDecryptFailureTracker({
1258
1264
  getHealthySyncSinceMs,
1259
1265
  startupGraceMs
@@ -1274,18 +1280,18 @@ function registerMatrixMonitorEvents(params) {
1274
1280
  logVerboseMessage(`matrix: ${label} failed (${String(error)})`);
1275
1281
  });
1276
1282
  };
1277
- client.on("room.message", (roomId, event) => {
1283
+ const onRoomMessageEvent = (roomId, event) => {
1278
1284
  if (routeVerificationEvent(roomId, event)) return;
1279
1285
  runMonitorTask(`room message handler room=${roomId} id=${event.event_id ?? "unknown"}`, async () => {
1280
1286
  await onRoomMessage(roomId, event);
1281
1287
  });
1282
- });
1283
- client.on("room.encrypted_event", (roomId, event) => {
1288
+ };
1289
+ const onEncryptedEvent = (roomId, event) => {
1284
1290
  const eventId = event?.event_id ?? "unknown";
1285
1291
  const eventType = event?.type ?? "unknown";
1286
1292
  logVerboseMessage(`matrix: encrypted event room=${roomId} type=${eventType} id=${eventId}`);
1287
- });
1288
- client.on("room.decrypted_event", (roomId, event) => {
1293
+ };
1294
+ const onDecryptedEvent = (roomId, event) => {
1289
1295
  const eventId = event?.event_id ?? "unknown";
1290
1296
  const eventType = event?.type ?? "unknown";
1291
1297
  logVerboseMessage(`matrix: decrypted event room=${roomId} type=${eventType} id=${eventId}`);
@@ -1294,8 +1300,8 @@ function registerMatrixMonitorEvents(params) {
1294
1300
  runMonitorTask(`decrypted room message handler room=${roomId} id=${event.event_id ?? "unknown"}`, async () => {
1295
1301
  await onRoomMessage(roomId, event);
1296
1302
  });
1297
- });
1298
- client.on("room.failed_decryption", (roomId, event, error) => {
1303
+ };
1304
+ const onFailedDecryption = (roomId, event, error) => {
1299
1305
  runMonitorTask(`failed decryption handler room=${roomId} id=${event.event_id ?? "unknown"}`, async () => {
1300
1306
  const failureState = postHealthySyncDecryptFailureTracker.recordFailure(roomId, event, error);
1301
1307
  const selfUserId = await resolveMatrixSelfUserId(client, logVerboseMessage);
@@ -1329,13 +1335,13 @@ function registerMatrixMonitorEvents(params) {
1329
1335
  });
1330
1336
  logVerboseMessage(`matrix: failed decrypt room=${roomId} id=${event.event_id ?? "unknown"} freshAfterHealthySync=${String(failureState.freshAfterHealthySync)} error=${error.message}`);
1331
1337
  });
1332
- });
1333
- client.on("verification.summary", (summary) => {
1338
+ };
1339
+ const onVerificationSummary = (summary) => {
1334
1340
  runMonitorTask("verification summary handler", async () => {
1335
1341
  await routeVerificationSummary(summary);
1336
1342
  });
1337
- });
1338
- client.on("room.invite", (roomId, event) => {
1343
+ };
1344
+ const onInvite = (roomId, event) => {
1339
1345
  directTracker?.invalidateRoom(roomId);
1340
1346
  const eventId = event?.event_id ?? "unknown";
1341
1347
  const sender = event?.sender ?? "unknown";
@@ -1345,13 +1351,13 @@ function registerMatrixMonitorEvents(params) {
1345
1351
  const rememberedSender = normalizeOptionalString(event?.sender);
1346
1352
  if (rememberedSender && !senderIsInvitee) directTracker?.rememberInvite?.(roomId, rememberedSender);
1347
1353
  logVerboseMessage(`matrix: invite room=${roomId} sender=${sender} direct=${String(isDirect)} id=${eventId}`);
1348
- });
1349
- client.on("room.join", (roomId, event) => {
1354
+ };
1355
+ const onJoin = (roomId, event) => {
1350
1356
  directTracker?.invalidateRoom(roomId);
1351
1357
  const eventId = event?.event_id ?? "unknown";
1352
1358
  logVerboseMessage(`matrix: join room=${roomId} id=${eventId}`);
1353
- });
1354
- client.on("room.event", (roomId, event) => {
1359
+ };
1360
+ const onRoomEvent = (roomId, event) => {
1355
1361
  const eventType = event?.type ?? "unknown";
1356
1362
  if (eventType === EventType.RoomMessageEncrypted) {
1357
1363
  logVerboseMessage(`matrix: encrypted raw event room=${roomId} id=${event?.event_id ?? "unknown"}`);
@@ -1375,6 +1381,7 @@ function registerMatrixMonitorEvents(params) {
1375
1381
  directTracker?.invalidateRoom(roomId);
1376
1382
  const membership = (event?.content)?.membership;
1377
1383
  const stateKey = event.state_key ?? "";
1384
+ if (stateKey) invalidateMemberDisplayName?.(roomId, stateKey);
1378
1385
  logVerboseMessage(`matrix: member event room=${roomId} stateKey=${stateKey} membership=${membership ?? "unknown"}`);
1379
1386
  }
1380
1387
  if (eventType === EventType.Reaction) {
@@ -1384,7 +1391,25 @@ function registerMatrixMonitorEvents(params) {
1384
1391
  return;
1385
1392
  }
1386
1393
  routeVerificationEvent(roomId, event);
1387
- });
1394
+ };
1395
+ client.on("room.message", onRoomMessageEvent);
1396
+ client.on("room.encrypted_event", onEncryptedEvent);
1397
+ client.on("room.decrypted_event", onDecryptedEvent);
1398
+ client.on("room.failed_decryption", onFailedDecryption);
1399
+ client.on("verification.summary", onVerificationSummary);
1400
+ client.on("room.invite", onInvite);
1401
+ client.on("room.join", onJoin);
1402
+ client.on("room.event", onRoomEvent);
1403
+ return () => {
1404
+ client.off("room.message", onRoomMessageEvent);
1405
+ client.off("room.encrypted_event", onEncryptedEvent);
1406
+ client.off("room.decrypted_event", onDecryptedEvent);
1407
+ client.off("room.failed_decryption", onFailedDecryption);
1408
+ client.off("verification.summary", onVerificationSummary);
1409
+ client.off("room.invite", onInvite);
1410
+ client.off("room.join", onJoin);
1411
+ client.off("room.event", onRoomEvent);
1412
+ };
1388
1413
  }
1389
1414
  //#endregion
1390
1415
  //#region extensions/matrix/src/matrix/monitor/ack-config.ts
@@ -1404,42 +1429,18 @@ function resolveMatrixAckReactionConfig(params) {
1404
1429
  }
1405
1430
  //#endregion
1406
1431
  //#region extensions/matrix/src/matrix/monitor/preflight-audio.ts
1407
- const MATRIX_DEFAULT_ECHO_TRANSCRIPT_FORMAT = "📝 \"{transcript}\"";
1408
- const loadMatrixPreflightAudioRuntime = createLazyRuntimeModule(() => import("./preflight-audio.runtime-BZxxQsxY.js"));
1409
- function formatMatrixAudioTranscript(transcript) {
1410
- return `[Audio transcript (machine-generated, untrusted)]: ${JSON.stringify(transcript)}`;
1411
- }
1412
- function formatMatrixAudioTranscriptEcho(transcript, format) {
1413
- return format.replace("{transcript}", () => transcript);
1414
- }
1415
- function suppressMatrixPreflightAudioEcho(cfg) {
1416
- const audio = cfg.tools?.media?.audio;
1417
- if (!audio?.echoTranscript) return cfg;
1418
- return {
1419
- ...cfg,
1420
- tools: {
1421
- ...cfg.tools,
1422
- media: {
1423
- ...cfg.tools?.media,
1424
- audio: {
1425
- ...audio,
1426
- echoTranscript: false
1427
- }
1428
- }
1429
- }
1430
- };
1431
- }
1432
1432
  function isMatrixAudioContent(params) {
1433
1433
  if (params.msgtype === "m.audio") return true;
1434
1434
  if (params.msgtype === "m.file" && typeof params.mimetype === "string") return params.mimetype.toLowerCase().startsWith("audio/");
1435
1435
  return false;
1436
1436
  }
1437
+ const matrixPreflightAudio = createChannelPreflightAudio({
1438
+ channel: "matrix",
1439
+ isAudio: isMatrixAudioContent
1440
+ });
1437
1441
  async function resolveMatrixPreflightAudioTranscript(params) {
1438
- if (params.abortSignal?.aborted) return;
1439
- try {
1440
- const { transcribeFirstAudio } = await loadMatrixPreflightAudioRuntime();
1441
- if (params.abortSignal?.aborted) return;
1442
- const transcript = await transcribeFirstAudio({
1442
+ return await matrixPreflightAudio.resolve({
1443
+ request: {
1443
1444
  ctx: {
1444
1445
  media: [{
1445
1446
  path: params.mediaPath,
@@ -1454,34 +1455,13 @@ async function resolveMatrixPreflightAudioTranscript(params) {
1454
1455
  ChatType: params.chatType,
1455
1456
  SessionKey: params.sessionKey
1456
1457
  },
1457
- cfg: suppressMatrixPreflightAudioEcho(params.cfg)
1458
- });
1459
- return params.abortSignal?.aborted ? void 0 : transcript;
1460
- } catch (err) {
1461
- logVerbose(`matrix: audio preflight transcription failed: ${String(err)}`);
1462
- return;
1463
- }
1458
+ cfg: params.cfg
1459
+ },
1460
+ abortSignal: params.abortSignal
1461
+ });
1464
1462
  }
1465
1463
  async function sendMatrixPreflightAudioTranscriptEcho(params) {
1466
- const audio = params.cfg.tools?.media?.audio;
1467
- if (!audio?.echoTranscript) return;
1468
- const text = formatMatrixAudioTranscriptEcho(params.transcript, audio.echoFormat ?? MATRIX_DEFAULT_ECHO_TRANSCRIPT_FORMAT);
1469
- try {
1470
- const { sendDurableMessageBatch } = await loadMatrixPreflightAudioRuntime();
1471
- const send = await sendDurableMessageBatch({
1472
- cfg: params.cfg,
1473
- channel: "matrix",
1474
- to: params.originatingTo,
1475
- accountId: params.accountId,
1476
- threadId: params.messageThreadId,
1477
- payloads: [{ text }],
1478
- bestEffort: true,
1479
- durability: "best_effort"
1480
- });
1481
- if (send.status === "failed") throw send.error;
1482
- } catch (err) {
1483
- logVerbose(`matrix: audio transcript echo failed: ${String(err)}`);
1484
- }
1464
+ await matrixPreflightAudio.send(params);
1485
1465
  }
1486
1466
  //#endregion
1487
1467
  //#region extensions/matrix/src/matrix/monitor/handler-helpers.ts
@@ -1597,11 +1577,11 @@ async function redactMatrixDraftEvent(client, roomId, draftEventId) {
1597
1577
  function buildMatrixFinalizedPreviewContent() {
1598
1578
  return { [MATRIX_OPENCLAW_FINALIZED_PREVIEW_KEY]: true };
1599
1579
  }
1600
- const loadMatrixSendModule = createLazyRuntimeModule(() => import("./send-BldGYG8g.js").then((n) => n.s));
1580
+ const loadMatrixSendModule = createLazyRuntimeModule(() => import("./send-ClHgDZLT.js").then((n) => n.s));
1601
1581
  const loadAcpBindingRuntime = createLazyRuntimeModule(() => import("openclaw/plugin-sdk/acp-binding-runtime"));
1602
1582
  const loadSessionBindingRuntime = createLazyRuntimeModule(() => import("openclaw/plugin-sdk/session-binding-runtime"));
1603
- const loadMatrixReactionEvents = createLazyRuntimeModule(() => import("./reaction-events-B00WSi98.js"));
1604
- const loadMatrixDraftStream = createLazyRuntimeModule(() => import("./draft-stream-CtvOXq0Z.js"));
1583
+ const loadMatrixReactionEvents = createLazyRuntimeModule(() => import("./reaction-events-BKOLlnCo.js"));
1584
+ const loadMatrixDraftStream = createLazyRuntimeModule(() => import("./draft-stream-DQ7QNC-l.js"));
1605
1585
  async function matrixTextWouldActivateMentions(client, text) {
1606
1586
  const { resolveMatrixMentionsForBody } = await loadMatrixSendModule();
1607
1587
  const mentions = await resolveMatrixMentionsForBody({
@@ -1747,7 +1727,7 @@ function createMatrixThreadContextResolver(params) {
1747
1727
  //#endregion
1748
1728
  //#region extensions/matrix/src/matrix/monitor/handler-context.ts
1749
1729
  async function resolveMatrixInboundContext(config) {
1750
- const { client, core, cfg, accountId, runtime, logVerboseMessage, roomId, event, eventTs, route: _route, isDirectMessage, isRoom, effectiveRoomUsers, groupPolicy, effectiveGroupAllowFrom, contextVisibilityMode, resolveThreadContext, resolveReplyContext, threadRootId, thread, getRoomInfo, senderId, senderName, bodyText, commandBodyText, roomConfig, messageId, inboundHistory, wasMentioned, effectiveWasMentioned, shouldBypassMention, canDetectMention, shouldRequireMention, commandAuthorized, locationPayload, media, preflightAudioTranscript, historyLimit, hasExplicitSessionBinding, dmSessionScope, sharedDmContextNoticeRooms, resolveStorePath: resolveStorePathImpl, createChannelInboundEnvelopeBuilder: createChannelInboundEnvelopeBuilderImpl, finalizeInboundContext } = config;
1730
+ const { client, core, cfg, accountId, resolveMessageIngress, runtime, logVerboseMessage, roomId, event, eventTs, route: _route, isDirectMessage, isRoom, effectiveRoomUsers, groupPolicy, effectiveGroupAllowFrom, contextVisibilityMode, resolveThreadContext, resolveReplyContext, threadRootId, thread, getRoomInfo, senderId, senderName, bodyText, commandBodyText, roomConfig, messageId, inboundHistory, wasMentioned, effectiveWasMentioned, shouldBypassMention, canDetectMention, shouldRequireMention, commandAuthorized, locationPayload, media, preflightAudioTranscript, historyLimit, hasExplicitSessionBinding, dmSessionScope, sharedDmContextNoticeRooms, resolveStorePath: resolveStorePathImpl, createChannelInboundEnvelopeBuilder: createChannelInboundEnvelopeBuilderImpl, finalizeInboundContext } = config;
1751
1731
  const replyToEventId = resolveMatrixReplyToEventId(event.content);
1752
1732
  const threadTarget = thread.threadId;
1753
1733
  const isRoomContextSenderAllowed = (contextSenderId) => {
@@ -1822,7 +1802,18 @@ async function resolveMatrixInboundContext(config) {
1822
1802
  kind: "quote",
1823
1803
  senderAllowed: replySenderAllowed
1824
1804
  }).include);
1825
- const ctxPayload = buildChannelInboundEventContext({
1805
+ const channelIngress = await resolveMessageIngress({
1806
+ agentId: _route.agentId,
1807
+ sessionKey: _route.sessionKey,
1808
+ messageId,
1809
+ inboundEventKind: "user_request"
1810
+ }, {
1811
+ kind: isDirectMessage ? "direct" : "channel",
1812
+ id: roomId,
1813
+ threadId: threadTarget
1814
+ });
1815
+ const ctxPayload = core.channel.inbound.buildContext({
1816
+ channelIngress,
1826
1817
  channel: "matrix",
1827
1818
  contextVisibility: contextVisibilityMode,
1828
1819
  finalize: finalizeInboundContext,
@@ -1891,7 +1882,6 @@ async function resolveMatrixInboundContext(config) {
1891
1882
  extra: {
1892
1883
  GroupSubject: isRoom ? roomName ?? roomId : void 0,
1893
1884
  GroupId: isRoom ? roomId : void 0,
1894
- GroupChannel: isRoom ? roomId : void 0,
1895
1885
  ...locationPayload?.context,
1896
1886
  CommandSource: "text",
1897
1887
  NativeDirectUserId: isDirectMessage ? senderId : void 0
@@ -1937,7 +1927,7 @@ async function resolveMatrixInboundContext(config) {
1937
1927
  //#region extensions/matrix/src/matrix/monitor/handler-draft-controller.ts
1938
1928
  async function createMatrixDraftController(params) {
1939
1929
  const { streaming, previewToolProgressEnabled, replyToMode, messageId, threadTarget, accountConfig, cfg, accountId, roomId, client, logVerboseMessage } = params;
1940
- let draftConsumed = false;
1930
+ let draftDisposition = "active";
1941
1931
  const draftStreamingEnabled = streaming !== "off";
1942
1932
  const quietDraftStreaming = streaming === "quiet" || streaming === "progress";
1943
1933
  const progressDraftStreaming = streaming === "progress";
@@ -1961,77 +1951,40 @@ async function createMatrixDraftController(params) {
1961
1951
  const pendingDraftBoundaries = [];
1962
1952
  const latestQueuedDraftBoundaryOffsets = /* @__PURE__ */ new Map();
1963
1953
  let currentDraftReplyToId = draftReplyToId;
1964
- let previewToolProgressSuppressed = false;
1965
- let previewToolProgressLines = [];
1966
- let latestPlan;
1967
- let latestPlanExplanation;
1954
+ let previewPlan;
1955
+ let previewPlanExplanation;
1956
+ let previewPlanSuppressed = false;
1968
1957
  const progressConfigEntry = accountConfig ?? cfg.channels?.matrix;
1969
1958
  const progressSeed = `${accountId}:${roomId}`;
1970
- const renderProgressDraft = () => {
1971
- if (!draftStream) return;
1972
- const previewText = formatChannelProgressDraftText({
1973
- entry: progressConfigEntry,
1974
- lines: previewToolProgressLines,
1975
- seed: progressSeed,
1976
- formatLine: formatMatrixToolProgressMarkdownCode,
1977
- bullet: "-",
1978
- narration: latestPlanExplanation,
1979
- plan: latestPlan
1980
- });
1981
- if (!previewText) return;
1982
- draftStream.update(previewText);
1983
- };
1984
- const progressDraftGate = createChannelProgressDraftGate({ onStart: renderProgressDraft });
1985
- const pushPreviewToolProgress = async (line, options) => {
1986
- if (!draftStream) return;
1987
- if (options?.toolName !== void 0 && !isChannelProgressDraftWorkToolName(options.toolName)) return;
1988
- const normalized = normalizeChannelProgressDraftLineIdentity(line);
1989
- const progressLine = typeof line === "object" && line !== void 0 ? line : normalized;
1990
- if (!progressDraftStreaming) {
1991
- if (!shouldStreamPreviewToolProgress || previewToolProgressSuppressed || !normalized) return;
1992
- const nextLines = mergeChannelProgressDraftLine(previewToolProgressLines, progressLine, { maxLines: resolveChannelProgressDraftMaxLines(progressConfigEntry) });
1993
- if (nextLines === previewToolProgressLines) return;
1994
- previewToolProgressLines = nextLines;
1995
- draftStream.update(formatChannelProgressDraftText({
1996
- entry: progressConfigEntry,
1997
- lines: previewToolProgressLines,
1998
- seed: progressSeed,
1999
- formatLine: formatMatrixToolProgressMarkdownCode,
2000
- bullet: "-",
2001
- narration: latestPlanExplanation,
2002
- plan: latestPlan
2003
- }));
2004
- return;
2005
- }
2006
- if (shouldStreamPreviewToolProgress && !previewToolProgressSuppressed && normalized) previewToolProgressLines = mergeChannelProgressDraftLine(previewToolProgressLines, progressLine, { maxLines: resolveChannelProgressDraftMaxLines(progressConfigEntry) });
2007
- const alreadyStarted = progressDraftGate.hasStarted;
2008
- const progressActive = await progressDraftGate.noteWork();
2009
- if ((alreadyStarted || progressActive) && progressDraftGate.hasStarted) renderProgressDraft();
2010
- };
2011
- const pushPlanProgress = async (steps, explanation) => {
2012
- latestPlan = steps?.length ? steps.map((entry) => ({ ...entry })) : void 0;
2013
- latestPlanExplanation = explanation?.replace(/\s+/g, " ").trim() || void 0;
2014
- if (!draftStream || previewToolProgressSuppressed) return;
2015
- if (!progressDraftStreaming) {
2016
- renderProgressDraft();
2017
- return;
1959
+ const renderPreviewPlan = () => formatChannelProgressDraftText({
1960
+ entry: progressConfigEntry,
1961
+ lines: [...progressDraft.getSnapshot().lines],
1962
+ seed: progressSeed,
1963
+ formatLine: formatMatrixToolProgressMarkdownCode,
1964
+ bullet: "-",
1965
+ narration: previewPlanExplanation,
1966
+ plan: previewPlan
1967
+ });
1968
+ const progressDraft = createChannelProgressDraftCompositor({
1969
+ entry: progressConfigEntry,
1970
+ mode: streaming === "quiet" ? "partial" : streaming,
1971
+ active: Boolean(draftStream),
1972
+ seed: progressSeed,
1973
+ formatLine: formatMatrixToolProgressMarkdownCode,
1974
+ buildProgressEventLine: (input, options) => input.event === "approval" ? formatChannelProgressDraftLine(input, options) : buildChannelProgressDraftLineForEntry(progressConfigEntry, input, options),
1975
+ update: async (text, options) => {
1976
+ const previewText = !progressDraftStreaming && (previewPlan || previewPlanExplanation) ? renderPreviewPlan() : text.replace(/^• /gmu, "- ");
1977
+ if (!draftStream) return false;
1978
+ draftStream.update(previewText);
1979
+ if (options?.flush) await draftStream.flush();
1980
+ return Boolean(draftStream.eventId());
2018
1981
  }
2019
- const alreadyStarted = progressDraftGate.hasStarted;
2020
- await progressDraftGate.startNow();
2021
- if (alreadyStarted && progressDraftGate.hasStarted) renderProgressDraft();
2022
- };
2023
- const suppressPreviewToolProgressForAnswerText = (text) => {
2024
- if (!text?.trim()) return;
2025
- previewToolProgressSuppressed = true;
2026
- previewToolProgressLines = [];
2027
- latestPlan = void 0;
2028
- latestPlanExplanation = void 0;
2029
- };
1982
+ });
2030
1983
  const resetPreviewToolProgress = () => {
2031
- previewToolProgressSuppressed = false;
2032
- previewToolProgressLines = [];
2033
- latestPlan = void 0;
2034
- latestPlanExplanation = void 0;
1984
+ previewPlan = void 0;
1985
+ previewPlanExplanation = void 0;
1986
+ previewPlanSuppressed = false;
1987
+ progressDraft.reset();
2035
1988
  };
2036
1989
  const buildPreviewToolProgressReplyOptions = () => {
2037
1990
  if (!shouldSuppressDefaultToolProgressMessages) return {};
@@ -2040,73 +1993,29 @@ async function createMatrixDraftController(params) {
2040
1993
  return {
2041
1994
  ...options,
2042
1995
  onToolStart: async (payload) => {
2043
- const toolName = payload.name?.trim();
2044
- await pushPreviewToolProgress(buildChannelProgressDraftLineForEntry(progressConfigEntry, {
2045
- event: "tool",
2046
- itemId: payload.itemId,
2047
- toolCallId: payload.toolCallId,
2048
- name: toolName,
2049
- phase: payload.phase,
2050
- args: payload.args
2051
- }, payload.detailMode ? { detailMode: payload.detailMode } : void 0), { toolName });
1996
+ return await progressDraft.pushToolEvent(payload);
2052
1997
  },
2053
1998
  onItemEvent: async (payload) => {
2054
- await pushPreviewToolProgress(buildChannelProgressDraftLineForEntry(progressConfigEntry, {
2055
- event: "item",
2056
- itemId: payload.itemId,
2057
- toolCallId: payload.toolCallId,
2058
- itemKind: payload.kind,
2059
- title: payload.title,
2060
- name: payload.name,
2061
- phase: payload.phase,
2062
- status: payload.status,
2063
- summary: payload.summary,
2064
- progressText: payload.progressText,
2065
- meta: payload.meta
2066
- }));
1999
+ return await progressDraft.pushItemEvent(payload);
2067
2000
  },
2068
2001
  onPlanUpdate: async (payload) => {
2069
- if (payload.phase !== "update") return;
2070
- await pushPlanProgress(payload.steps, payload.explanation);
2002
+ if (payload.phase !== "update") return false;
2003
+ if (progressDraftStreaming) return await progressDraft.pushPlanProgress(payload.steps, { explanation: payload.explanation });
2004
+ if (!draftStream || previewPlanSuppressed) return false;
2005
+ previewPlan = payload.steps?.length ? payload.steps.map((step) => ({ ...step })) : void 0;
2006
+ previewPlanExplanation = payload.explanation?.replace(/\s+/g, " ").trim() || void 0;
2007
+ const text = renderPreviewPlan();
2008
+ if (text) draftStream.update(text);
2009
+ return false;
2071
2010
  },
2072
2011
  onApprovalEvent: async (payload) => {
2073
- if (payload.phase !== "requested") return;
2074
- await pushPreviewToolProgress(formatChannelProgressDraftLine({
2075
- event: "approval",
2076
- phase: payload.phase,
2077
- title: payload.title,
2078
- command: payload.command,
2079
- reason: payload.reason,
2080
- message: payload.message
2081
- }));
2012
+ return await progressDraft.pushApprovalEvent(payload);
2082
2013
  },
2083
2014
  onCommandOutput: async (payload) => {
2084
- if (payload.phase !== "end") return;
2085
- await pushPreviewToolProgress(buildChannelProgressDraftLineForEntry(progressConfigEntry, {
2086
- event: "command-output",
2087
- itemId: payload.itemId,
2088
- toolCallId: payload.toolCallId,
2089
- phase: payload.phase,
2090
- title: payload.title,
2091
- name: payload.name,
2092
- status: payload.status,
2093
- exitCode: payload.exitCode
2094
- }));
2015
+ return await progressDraft.pushCommandOutputEvent(payload);
2095
2016
  },
2096
2017
  onPatchSummary: async (payload) => {
2097
- if (payload.phase !== "end") return;
2098
- await pushPreviewToolProgress(buildChannelProgressDraftLineForEntry(progressConfigEntry, {
2099
- event: "patch",
2100
- itemId: payload.itemId,
2101
- toolCallId: payload.toolCallId,
2102
- phase: payload.phase,
2103
- title: payload.title,
2104
- name: payload.name,
2105
- added: payload.added,
2106
- modified: payload.modified,
2107
- deleted: payload.deleted,
2108
- summary: payload.summary
2109
- }));
2018
+ return await progressDraft.pushPatchEvent(payload);
2110
2019
  }
2111
2020
  };
2112
2021
  };
@@ -2146,19 +2055,19 @@ async function createMatrixDraftController(params) {
2146
2055
  const resetDraftDeliveryState = async () => {
2147
2056
  await draftStream?.discardPending();
2148
2057
  draftStream?.reset();
2149
- draftConsumed = false;
2058
+ draftDisposition = "active";
2150
2059
  currentDraftMessageGeneration = 0;
2151
2060
  currentDraftBlockOffset = 0;
2152
2061
  latestDraftFullText = "";
2153
2062
  pendingDraftBoundaries.length = 0;
2154
2063
  latestQueuedDraftBoundaryOffsets.clear();
2155
2064
  currentDraftReplyToId = draftReplyToId;
2156
- progressDraftGate.reset();
2065
+ progressDraft.beginNewTurn({ force: true });
2157
2066
  resetPreviewToolProgress();
2158
2067
  };
2159
2068
  return {
2160
2069
  draftStream,
2161
- progressDraftGate,
2070
+ cancelProgressDraft: () => progressDraft.cancel(),
2162
2071
  buildPreviewToolProgressReplyOptions,
2163
2072
  queueDraftBlockBoundary,
2164
2073
  advanceDraftBlockBoundary,
@@ -2166,12 +2075,15 @@ async function createMatrixDraftController(params) {
2166
2075
  resetPreviewToolProgress,
2167
2076
  resetDraftDeliveryState,
2168
2077
  updateDraftFromLatestFullText,
2169
- isDraftConsumed: () => draftConsumed,
2078
+ draftDisposition: () => draftDisposition,
2079
+ beginDraftGeneration: () => {
2080
+ draftDisposition = "active";
2081
+ },
2170
2082
  markDraftConsumed: () => {
2171
- draftConsumed = true;
2083
+ draftDisposition = "consumed";
2172
2084
  },
2173
- clearDraftConsumed: () => {
2174
- draftConsumed = false;
2085
+ markDraftRetained: () => {
2086
+ draftDisposition = "retained";
2175
2087
  },
2176
2088
  currentReplyToId: () => currentDraftReplyToId,
2177
2089
  setCurrentReplyToId: (replyToId) => {
@@ -2181,10 +2093,16 @@ async function createMatrixDraftController(params) {
2181
2093
  currentDraftReplyToId = replyToMode === "all" ? draftReplyToId : void 0;
2182
2094
  },
2183
2095
  onPartialReply: (text) => {
2184
- if (progressDraftStreaming) return;
2096
+ if (progressDraftStreaming) return false;
2185
2097
  latestDraftFullText = text;
2186
- suppressPreviewToolProgressForAnswerText(text);
2098
+ if (text.trim()) {
2099
+ previewPlanSuppressed = true;
2100
+ previewPlan = void 0;
2101
+ previewPlanExplanation = void 0;
2102
+ progressDraft.suppress();
2103
+ }
2187
2104
  updateDraftFromLatestFullText();
2105
+ return false;
2188
2106
  }
2189
2107
  };
2190
2108
  }
@@ -2528,6 +2446,7 @@ async function resolveMatrixIngressAccess(config) {
2528
2446
  roomUsers,
2529
2447
  senderId,
2530
2448
  isRoom,
2449
+ conversationId: roomId,
2531
2450
  accountId,
2532
2451
  eventKind: isReactionEvent ? "reaction" : "message"
2533
2452
  });
@@ -2702,6 +2621,11 @@ function resolveMatrixUserLocalpart(userId) {
2702
2621
  if (colonIndex <= 1) return null;
2703
2622
  return trimmed.slice(1, colonIndex).trim() || null;
2704
2623
  }
2624
+ function hasVisibleNativeMatrixUserMention(text, userId) {
2625
+ const localpart = resolveMatrixUserLocalpart(userId);
2626
+ if (!text || !localpart) return false;
2627
+ return new RegExp(String.raw`(?:^|\p{White_Space})(?:${escapeRegExp(userId)}(?=$|\p{White_Space}|:\p{White_Space}|[,!?;](?=$|\p{White_Space}))|${escapeRegExp(`@${localpart}`)}(?=$|\p{White_Space}|:\p{White_Space}))`, "u").test(text);
2628
+ }
2705
2629
  function resolveMatrixMentionPrefixCandidates(params) {
2706
2630
  const candidates = [];
2707
2631
  const seen = /* @__PURE__ */ new Set();
@@ -2803,7 +2727,7 @@ function resolveMentions(params) {
2803
2727
  displayName: params.displayName,
2804
2728
  mentionRegexes: params.mentionRegexes
2805
2729
  }) : false;
2806
- const metadataBackedUserMention = Boolean(params.userId && mentionedUsers.has(params.userId) && (mentionedInFormattedBody || textMentioned));
2730
+ const metadataBackedUserMention = Boolean(params.userId && mentionedUsers.has(params.userId) && (mentionedInFormattedBody || textMentioned || hasVisibleNativeMatrixUserMention(params.text, params.userId)));
2807
2731
  const metadataBackedRoomMention = Boolean(mentions?.room) && visibleRoomMention;
2808
2732
  const explicitMention = mentionedInFormattedBody || metadataBackedUserMention || metadataBackedRoomMention;
2809
2733
  return {
@@ -2817,6 +2741,7 @@ async function resolveMatrixIngressContent(config) {
2817
2741
  const { handler, params: paramsLocal, access, roomId, event, eventType, isPollEvent, eventTs, senderId, roomHistoryTracker, commitInboundEventIfClaimed } = config;
2818
2742
  const { client, core, cfg, accountId, accountConfig, logger, logVerboseMessage, historyLimit, mediaMaxBytes, dmSessionScope, getMemberDisplayName } = handler;
2819
2743
  const { content: accessContent, messageId, audioPreflightMode, isDirectMessage, isRoom, locationPayload, reservedHistorySlot, threadRootId, thread, historyThreadId, discardReservedHistorySlot, markReservedHistorySlotConsumed, commitInboundEventIfClaimedAndDiscardReserved, roomConfig, allowBotsMode, isConfiguredBotSender, selfUserId, botLoopProtection, roomMatchMeta, getSenderName, accessState, effectiveGroupAllowFrom, effectiveRoomUsers } = access;
2744
+ const { messageIngress, resolveMessageIngress } = accessState;
2820
2745
  let content = accessContent;
2821
2746
  let pollSnapshotPromise = null;
2822
2747
  const getPollSnapshot = async () => {
@@ -2898,7 +2823,7 @@ async function resolveMatrixIngressContent(config) {
2898
2823
  } catch (err) {
2899
2824
  preflightMediaDownloadFailed = true;
2900
2825
  if (isMatrixMediaSizeLimitError(err)) preflightMediaSizeLimitExceeded = true;
2901
- const errorText = formatMatrixErrorMessage(err);
2826
+ const errorText = formatErrorMessage(err);
2902
2827
  logVerboseMessage(`matrix: media download failed room=${roomId} id=${event.event_id ?? "unknown"} type=${content.msgtype} error=${errorText}`);
2903
2828
  logger.warn("matrix media download failed", {
2904
2829
  roomId,
@@ -2984,7 +2909,7 @@ async function resolveMatrixIngressContent(config) {
2984
2909
  const { effectiveWasMentioned, shouldBypassMention } = mentionDecision;
2985
2910
  const canDetectMention = agentMentionRegexes.length > 0 || hasExplicitMention;
2986
2911
  if (mentionDecision.shouldSkip) {
2987
- const pendingHistoryBody = preflightAudioTranscript ? formatMatrixAudioTranscript(preflightAudioTranscript) : pendingHistoryText || pendingHistoryPollText;
2912
+ const pendingHistoryBody = preflightAudioTranscript ? formatAudioTranscriptForAgent(preflightAudioTranscript) : pendingHistoryText || pendingHistoryPollText;
2988
2913
  if (historyLimit > 0 && pendingHistoryBody) {
2989
2914
  const pendingEntry = {
2990
2915
  sender: senderId,
@@ -3046,7 +2971,7 @@ async function resolveMatrixIngressContent(config) {
3046
2971
  } catch (err) {
3047
2972
  mediaDownloadFailed = true;
3048
2973
  if (isMatrixMediaSizeLimitError(err)) mediaSizeLimitExceeded = true;
3049
- const errorText = formatMatrixErrorMessage(err);
2974
+ const errorText = formatErrorMessage(err);
3050
2975
  logVerboseMessage(`matrix: media download failed room=${roomId} id=${event.event_id ?? "unknown"} type=${content.msgtype} error=${errorText}`);
3051
2976
  logger.warn("matrix media download failed", {
3052
2977
  roomId,
@@ -3067,7 +2992,7 @@ async function resolveMatrixIngressContent(config) {
3067
2992
  });
3068
2993
  if (preflightMedia && bodyText && bodyText !== preflightMedia.placeholder && isLikelyBareFilename(bodyText)) bodyText = preflightMedia.placeholder;
3069
2994
  if (preflightAudioTranscript) {
3070
- const transcriptBody = formatMatrixAudioTranscript(preflightAudioTranscript);
2995
+ const transcriptBody = formatAudioTranscriptForAgent(preflightAudioTranscript);
3071
2996
  bodyText = !bodyText || bodyText === media?.placeholder ? transcriptBody : `${bodyText}\n${transcriptBody}`;
3072
2997
  }
3073
2998
  if (!bodyText) {
@@ -3109,6 +3034,8 @@ async function resolveMatrixIngressContent(config) {
3109
3034
  }, historyThreadId) : void 0;
3110
3035
  if (reservedHistorySlot && preparedTrigger) markReservedHistorySlotConsumed();
3111
3036
  return {
3037
+ messageIngress,
3038
+ resolveMessageIngress,
3112
3039
  route: _route,
3113
3040
  hasExplicitSessionBinding,
3114
3041
  roomConfig,
@@ -3346,12 +3273,12 @@ async function deliverMatrixReplies(params) {
3346
3273
  const { chunks } = chunkMatrixText(rawText, {
3347
3274
  cfg: params.cfg,
3348
3275
  accountId: params.accountId,
3349
- tableMode
3276
+ tableMode,
3277
+ preserveWhitespace: true
3350
3278
  });
3351
3279
  for (const chunk of chunks) {
3352
- const trimmed = chunk.trim();
3353
- if (!trimmed) continue;
3354
- await sendMessageMatrix(params.roomId, trimmed, {
3280
+ if (!chunk.trim()) continue;
3281
+ await sendMessageMatrix(params.roomId, chunk, {
3355
3282
  client: params.client,
3356
3283
  cfg: params.cfg,
3357
3284
  replyToId: replyToIdForReply,
@@ -3392,19 +3319,21 @@ function createMatrixReplyDispatcher(config) {
3392
3319
  const hasRepliedRef = { value: false };
3393
3320
  let finalReplyDeliveryFailed = false;
3394
3321
  let nonFinalReplyDeliveryFailed = false;
3322
+ const beginNextBlockDraft = () => {
3323
+ draftController.beginDraftGeneration();
3324
+ draftController.advanceDraftBlockBoundary({ fallbackToLatestEnd: true });
3325
+ draftStream?.reset();
3326
+ draftController.resetReplyToIdForNextBlock();
3327
+ draftController.updateDraftFromLatestFullText();
3328
+ };
3395
3329
  const { deliver: deliverReply, onError: onReplyError, ...turnDispatcherOptions } = {
3396
3330
  ...prefixOptions,
3397
3331
  humanDelay,
3398
3332
  deliver: async (payload, info) => {
3399
3333
  const completeDelivery = async (result) => {
3400
3334
  if (info.kind === "block") {
3401
- draftController.clearDraftConsumed();
3402
- draftController.advanceDraftBlockBoundary({ fallbackToLatestEnd: true });
3403
- draftStream?.reset();
3404
- draftController.resetReplyToIdForNextBlock();
3405
- draftController.updateDraftFromLatestFullText();
3406
- const { sendTypingMatrix } = await loadMatrixSendModule();
3407
- await sendTypingMatrix(roomId, true, void 0, client).catch(() => {});
3335
+ beginNextBlockDraft();
3336
+ await typingCallbacks.onReplyStart();
3408
3337
  }
3409
3338
  return result;
3410
3339
  };
@@ -3422,9 +3351,25 @@ function createMatrixReplyDispatcher(config) {
3422
3351
  content
3423
3352
  };
3424
3353
  };
3425
- const createSurvivingDraftDelivery = (id, redacted) => {
3426
- const content = redacted ? void 0 : draftStream?.content();
3427
- return content ? createDraftDeliveryResult(id, content) : mergeMatrixReplyDeliveryResults([]);
3354
+ const settleDraftReplacement = async (params) => {
3355
+ const draftDelivery = createDraftDeliveryResult(params.draftEventId, params.draftContent);
3356
+ let replacement;
3357
+ try {
3358
+ replacement = await params.deliver();
3359
+ } catch (error) {
3360
+ draftController.markDraftRetained();
3361
+ throw toMatrixPartialDeliveryError(error, [draftDelivery]);
3362
+ }
3363
+ if (!replacement.visibleReplySent) {
3364
+ draftController.markDraftRetained();
3365
+ return draftDelivery;
3366
+ }
3367
+ if (!await redactMatrixDraftEvent(client, roomId, params.draftEventId)) {
3368
+ draftController.markDraftRetained();
3369
+ return mergeMatrixReplyDeliveryResults([draftDelivery, replacement]);
3370
+ }
3371
+ draftController.markDraftConsumed();
3372
+ return replacement;
3428
3373
  };
3429
3374
  if (draftStream && info.kind !== "tool" && !payload.isCompactionNotice) {
3430
3375
  const hasMedia = Boolean(payload.mediaUrl) || (payload.mediaUrls?.length ?? 0) > 0;
@@ -3433,7 +3378,7 @@ function createMatrixReplyDispatcher(config) {
3433
3378
  ...payload,
3434
3379
  text: ttsSupplement.spokenText
3435
3380
  } : payload;
3436
- if (draftController.isDraftConsumed()) {
3381
+ if (draftController.draftDisposition() !== "active") {
3437
3382
  await draftStream.discardPending();
3438
3383
  return await completeDelivery(await deliverMatrixReplies({
3439
3384
  cfg,
@@ -3511,11 +3456,10 @@ function createMatrixReplyDispatcher(config) {
3511
3456
  }
3512
3457
  }),
3513
3458
  deliverNormally: async () => {
3514
- const draftRedacted = await redactMatrixDraftEvent(client, roomId, draftEventId);
3515
- const survivingDraft = createSurvivingDraftDelivery(draftEventId, draftRedacted);
3516
- let deliveredFallback;
3517
- try {
3518
- deliveredFallback = await deliverMatrixReplies({
3459
+ fallbackResult = await settleDraftReplacement({
3460
+ draftEventId,
3461
+ draftContent: draftStream.content() ?? preparedFinalPreviewContent,
3462
+ deliver: async () => await deliverMatrixReplies({
3519
3463
  cfg,
3520
3464
  replies: [fallbackPayload],
3521
3465
  roomId,
@@ -3529,15 +3473,12 @@ function createMatrixReplyDispatcher(config) {
3529
3473
  accountId,
3530
3474
  mediaLocalRoots,
3531
3475
  tableMode
3532
- });
3533
- } catch (error) {
3534
- throw toMatrixPartialDeliveryError(error, [survivingDraft]);
3535
- }
3536
- fallbackResult = mergeMatrixReplyDeliveryResults([survivingDraft, deliveredFallback]);
3476
+ })
3477
+ });
3537
3478
  return fallbackResult.visibleReplySent;
3538
3479
  }
3539
3480
  });
3540
- draftController.markDraftConsumed();
3481
+ if (previewResult.kind === "preview-finalized") draftController.markDraftConsumed();
3541
3482
  return await completeDelivery(previewResult.kind === "preview-finalized" && previewResult.liveState?.receipt ? createDraftDeliveryResult(draftEventId, finalizedDraftContent ?? preparedFinalPreviewContent) : fallbackResult ?? mergeMatrixReplyDeliveryResults([]));
3542
3483
  } else if (draftEventId && hasMedia && !payloadReplyMismatch) {
3543
3484
  let textEditOk = !mustDeliverFinalNormally;
@@ -3575,44 +3516,15 @@ function createMatrixReplyDispatcher(config) {
3575
3516
  }
3576
3517
  const reusesDraftAsFinalText = Boolean(payloadText?.trim()) && textEditOk;
3577
3518
  const draftContent = draftStream.content();
3578
- const draftRedacted = reusesDraftAsFinalText ? false : await redactMatrixDraftEvent(client, roomId, draftEventId);
3579
3519
  const mediaPayload = ttsSupplement && reusesDraftAsFinalText ? buildTtsSupplementMediaPayload(payload) : {
3580
3520
  ...payload,
3581
3521
  text: reusesDraftAsFinalText ? void 0 : payload.text ?? (ttsSupplement?.visibleTextAlreadyDelivered === true ? void 0 : ttsSupplement?.spokenText)
3582
3522
  };
3583
3523
  const providerDraftContent = finalizedDraftContent ?? preparedPayloadContent;
3584
- const previewDelivery = reusesDraftAsFinalText && providerDraftContent ? createDraftDeliveryResult(draftEventId, providerDraftContent) : !draftRedacted && draftContent ? createDraftDeliveryResult(draftEventId, draftContent) : mergeMatrixReplyDeliveryResults([]);
3585
- let mediaDelivery;
3586
- try {
3587
- mediaDelivery = await deliverMatrixReplies({
3588
- cfg,
3589
- replies: [mediaPayload],
3590
- roomId,
3591
- client,
3592
- runtime,
3593
- textLimit,
3594
- replyToMode,
3595
- hasRepliedRef,
3596
- threadId: threadTarget,
3597
- replyToId: threadTarget ?? replyToEventId ?? void 0,
3598
- accountId,
3599
- mediaLocalRoots,
3600
- tableMode
3601
- });
3602
- } catch (error) {
3603
- throw toMatrixPartialDeliveryError(error, [previewDelivery]);
3604
- }
3605
- draftController.markDraftConsumed();
3606
- return await completeDelivery(mergeMatrixReplyDeliveryResults([previewDelivery, mediaDelivery]));
3607
- }
3608
- const shouldRedactDraft = Boolean(draftEventId) && (payload.isError || payloadReplyMismatch || mustDeliverFinalNormally || draftFinalTextNeedsNormalMentionDelivery);
3609
- const draftRedacted = shouldRedactDraft && draftEventId ? await redactMatrixDraftEvent(client, roomId, draftEventId) : false;
3610
- const survivingDraft = shouldRedactDraft && draftEventId ? createSurvivingDraftDelivery(draftEventId, draftRedacted) : mergeMatrixReplyDeliveryResults([]);
3611
- let deliveredFallback;
3612
- try {
3613
- deliveredFallback = await deliverMatrixReplies({
3524
+ const previewDelivery = reusesDraftAsFinalText && providerDraftContent ? createDraftDeliveryResult(draftEventId, providerDraftContent) : draftContent ? createDraftDeliveryResult(draftEventId, draftContent) : mergeMatrixReplyDeliveryResults([]);
3525
+ const deliverMedia = async () => await deliverMatrixReplies({
3614
3526
  cfg,
3615
- replies: [fallbackPayload],
3527
+ replies: [mediaPayload],
3616
3528
  roomId,
3617
3529
  client,
3618
3530
  runtime,
@@ -3625,11 +3537,46 @@ function createMatrixReplyDispatcher(config) {
3625
3537
  mediaLocalRoots,
3626
3538
  tableMode
3627
3539
  });
3628
- } catch (error) {
3629
- throw toMatrixPartialDeliveryError(error, [survivingDraft]);
3540
+ if (reusesDraftAsFinalText) {
3541
+ draftController.markDraftConsumed();
3542
+ let mediaDelivery;
3543
+ try {
3544
+ mediaDelivery = await deliverMedia();
3545
+ } catch (error) {
3546
+ throw toMatrixPartialDeliveryError(error, [previewDelivery]);
3547
+ }
3548
+ return await completeDelivery(mergeMatrixReplyDeliveryResults([previewDelivery, mediaDelivery]));
3549
+ }
3550
+ if (draftContent) return await completeDelivery(await settleDraftReplacement({
3551
+ draftEventId,
3552
+ draftContent,
3553
+ deliver: deliverMedia
3554
+ }));
3555
+ return await completeDelivery(await deliverMedia());
3630
3556
  }
3631
- if (shouldRedactDraft || deliveredFallback.visibleReplySent) draftController.markDraftConsumed();
3632
- return await completeDelivery(mergeMatrixReplyDeliveryResults([survivingDraft, deliveredFallback]));
3557
+ const shouldRedactDraft = Boolean(draftEventId) && (payload.isError || payloadReplyMismatch || mustDeliverFinalNormally || draftFinalTextNeedsNormalMentionDelivery);
3558
+ const deliverFallback = async () => await deliverMatrixReplies({
3559
+ cfg,
3560
+ replies: [fallbackPayload],
3561
+ roomId,
3562
+ client,
3563
+ runtime,
3564
+ textLimit,
3565
+ replyToMode,
3566
+ hasRepliedRef,
3567
+ threadId: threadTarget,
3568
+ replyToId: threadTarget ?? replyToEventId ?? void 0,
3569
+ accountId,
3570
+ mediaLocalRoots,
3571
+ tableMode
3572
+ });
3573
+ const draftContent = draftStream.content();
3574
+ if (shouldRedactDraft && draftEventId && draftContent) return await completeDelivery(await settleDraftReplacement({
3575
+ draftEventId,
3576
+ draftContent,
3577
+ deliver: deliverFallback
3578
+ }));
3579
+ return await completeDelivery(await deliverFallback());
3633
3580
  }
3634
3581
  return await completeDelivery(await deliverMatrixReplies({
3635
3582
  cfg,
@@ -3650,7 +3597,7 @@ function createMatrixReplyDispatcher(config) {
3650
3597
  onError: (err, info) => {
3651
3598
  if (info.kind === "final") finalReplyDeliveryFailed = true;
3652
3599
  else nonFinalReplyDeliveryFailed = true;
3653
- if (info.kind === "block") draftController.advanceDraftBlockBoundary({ fallbackToLatestEnd: true });
3600
+ if (info.kind === "block") beginNextBlockDraft();
3654
3601
  runtime.error?.(`matrix ${info.kind} reply failed: ${String(err)}`);
3655
3602
  },
3656
3603
  onReplyStart: typingCallbacks.onReplyStart,
@@ -3902,8 +3849,9 @@ function createMatrixRoomMessageHandler(params) {
3902
3849
  })();
3903
3850
  if (!resolvedIngressResult) return;
3904
3851
  if ("deferredPrefix" in resolvedIngressResult) return;
3905
- const { route: _route, hasExplicitSessionBinding, roomConfig, isDirectMessage, isRoom, shouldRequireMention, wasMentioned, effectiveWasMentioned, shouldBypassMention, canDetectMention, commandAuthorized, inboundHistory, senderName, bodyText, commandBodyText, media, preflightAudioTranscript, locationPayload, messageId, triggerSnapshot, threadRootId, thread, botLoopProtection, effectiveGroupAllowFrom, effectiveRoomUsers } = resolvedIngressResult;
3852
+ const { route: _route, hasExplicitSessionBinding, roomConfig, isDirectMessage, isRoom, shouldRequireMention, wasMentioned, effectiveWasMentioned, shouldBypassMention, canDetectMention, commandAuthorized, inboundHistory, senderName, bodyText, commandBodyText, media, preflightAudioTranscript, locationPayload, messageId, triggerSnapshot, threadRootId, thread, botLoopProtection, effectiveGroupAllowFrom, effectiveRoomUsers, resolveMessageIngress } = resolvedIngressResult;
3906
3853
  const inboundContext = await resolveMatrixInboundContext({
3854
+ resolveMessageIngress,
3907
3855
  client,
3908
3856
  core,
3909
3857
  cfg,
@@ -4102,19 +4050,21 @@ function createMatrixRoomMessageHandler(params) {
4102
4050
  },
4103
4051
  dispatcherOptions: {
4104
4052
  ...turnDispatcherOptions,
4105
- onSettled: () => draftController.progressDraftGate.cancel()
4053
+ onSettled: () => draftController.cancelProgressDraft()
4106
4054
  },
4107
4055
  replyOptions: {
4108
4056
  skillFilter: roomConfig?.skills,
4109
4057
  disableBlockStreaming: !blockStreamingEnabled,
4110
4058
  onPartialReply: draftStream ? (payload) => draftController.onPartialReply(payload.text ?? "") : void 0,
4111
4059
  onBlockReplyQueued: draftStream ? (payload, context) => {
4112
- if (payload.isCompactionNotice === true) return;
4060
+ if (payload.isCompactionNotice === true) return false;
4113
4061
  draftController.queueDraftBlockBoundary(payload, context);
4062
+ return false;
4114
4063
  } : void 0,
4115
4064
  onAssistantMessageStart: draftStream ? () => {
4116
4065
  draftController.resetDraftBlockOffsets();
4117
4066
  draftController.resetPreviewToolProgress();
4067
+ return false;
4118
4068
  } : void 0,
4119
4069
  onQueuedFollowupAdmitted: draftStream ? draftController.resetDraftDeliveryState : void 0,
4120
4070
  ...draftController.buildPreviewToolProgressReplyOptions(),
@@ -4128,7 +4078,7 @@ function createMatrixRoomMessageHandler(params) {
4128
4078
  return;
4129
4079
  }
4130
4080
  const { dispatchResult } = turnResult;
4131
- const { queuedFinal, counts } = dispatchResult;
4081
+ const { queuedFinal } = dispatchResult;
4132
4082
  if (replyDispatcher.finalReplyDeliveryFailed()) {
4133
4083
  logVerboseMessage(`matrix: final reply delivery failed room=${roomId} id=${messageId}; keeping replay committed`);
4134
4084
  await commitInboundEventIfClaimed();
@@ -4140,23 +4090,22 @@ function createMatrixRoomMessageHandler(params) {
4140
4090
  return;
4141
4091
  }
4142
4092
  if (isRoom && triggerSnapshot) roomHistoryTracker.consumeHistory(_route.agentId, roomId, triggerSnapshot, messageId, threadRootId ? thread.threadId : void 0);
4143
- if (!hasFinalInboundReplyDispatch({
4144
- queuedFinal,
4145
- counts
4146
- })) {
4093
+ if (!hasFinalInboundReplyDispatch(dispatchResult)) {
4147
4094
  await commitInboundEventIfClaimed();
4148
4095
  return;
4149
4096
  }
4150
- const finalCount = counts.final;
4097
+ const finalCount = resolveInboundReplyDispatchCounts(dispatchResult).final;
4151
4098
  logVerboseMessage(`matrix: delivered ${finalCount} reply${finalCount === 1 ? "" : "ies"} to ${replyTarget}`);
4152
4099
  await commitInboundEventIfClaimed();
4153
4100
  } catch (err) {
4101
+ const draftController = draftControllerRef;
4102
+ if (draftController?.draftStream?.eventId() && draftController.draftDisposition() === "active") draftController.markDraftRetained();
4154
4103
  runtime.error?.(`matrix handler failed: ${String(err)}`);
4155
4104
  } finally {
4156
4105
  const draftStream = draftControllerRef?.draftStream;
4157
4106
  if (draftStream) {
4158
4107
  const draftEventId = await draftStream.stop().catch(() => void 0);
4159
- if (draftEventId && draftControllerRef?.isDraftConsumed() !== true) await redactMatrixDraftEvent(client, roomId, draftEventId);
4108
+ if (draftEventId && draftControllerRef?.draftDisposition() === "active") await redactMatrixDraftEvent(client, roomId, draftEventId);
4160
4109
  }
4161
4110
  inboundReplayClaim?.release();
4162
4111
  }
@@ -4177,10 +4126,10 @@ function shouldPromoteRecentInviteRoom(params) {
4177
4126
  //#endregion
4178
4127
  //#region extensions/matrix/src/matrix/monitor/startup.ts
4179
4128
  const loadMatrixStartupMaintenanceDeps = createLazyRuntimeModule(() => Promise.all([
4180
- import("./setup-core-BVfBv2ai.js").then((n) => n.c),
4129
+ import("./config-update-Bor_Pjsv.js").then((n) => n.t),
4181
4130
  import("./device-health-B4-YXtJR.js").then((n) => n.t),
4182
- import("./setup-core-BVfBv2ai.js").then((n) => n.o),
4183
- import("./startup-verification-wm3_i88s.js")
4131
+ import("./profile-D-Wubo0A.js").then((n) => n.n),
4132
+ import("./startup-verification-Cs08ca8u.js")
4184
4133
  ]).then(([configUpdateModule, deviceHealthModule, profileModule, startupVerificationModule]) => ({
4185
4134
  updateMatrixAccountConfig: configUpdateModule.updateMatrixAccountConfig,
4186
4135
  summarizeMatrixDeviceHealth: deviceHealthModule.summarizeMatrixDeviceHealth,
@@ -4251,7 +4200,7 @@ function cloneLastDisconnect(value) {
4251
4200
  function formatSyncError(error) {
4252
4201
  if (!error) return null;
4253
4202
  if (error instanceof Error) return error.message || error.name || "unknown";
4254
- return formatMatrixErrorMessage(error);
4203
+ return formatErrorMessage(error);
4255
4204
  }
4256
4205
  function createMatrixMonitorStatusController(params) {
4257
4206
  const status = {
@@ -4261,7 +4210,8 @@ function createMatrixMonitorStatusController(params) {
4261
4210
  lastConnectedAt: null,
4262
4211
  lastDisconnect: null,
4263
4212
  lastError: null,
4264
- healthState: "starting"
4213
+ healthState: "starting",
4214
+ lifecycle: "starting"
4265
4215
  };
4266
4216
  const emit = () => {
4267
4217
  params.statusSink?.({
@@ -4270,16 +4220,25 @@ function createMatrixMonitorStatusController(params) {
4270
4220
  });
4271
4221
  };
4272
4222
  const noteConnected = (at = Date.now(), options) => {
4273
- if (status.connected === true) status.lastEventAt = at;
4274
- else Object.assign(status, createConnectedChannelStatusPatch(at));
4223
+ const lastConnectedAt = status.connected === true ? status.lastConnectedAt ?? at : at;
4224
+ Object.assign(status, channelReadyPatch({
4225
+ lastConnectedAt,
4226
+ lastEventAt: at,
4227
+ lastDisconnect: null,
4228
+ healthState: "healthy"
4229
+ }));
4275
4230
  if (options?.transportActivity) Object.assign(status, createTransportActivityStatusPatch(at));
4276
- status.lastError = null;
4277
- status.lastDisconnect = null;
4278
- status.healthState = "healthy";
4279
4231
  emit();
4280
4232
  };
4281
4233
  const noteDisconnected = (paramsLocal) => {
4282
4234
  const at = paramsLocal.at ?? Date.now();
4235
+ const tokenInvalidated = isMatrixAccessTokenInvalidatedError(paramsLocal.error);
4236
+ if (status.lifecycle === "blocked" && status.terminalDisconnect === true && !tokenInvalidated) {
4237
+ status.connected = false;
4238
+ status.lastEventAt = at;
4239
+ emit();
4240
+ return;
4241
+ }
4283
4242
  const error = formatSyncError(paramsLocal.error);
4284
4243
  status.connected = false;
4285
4244
  status.lastEventAt = at;
@@ -4287,8 +4246,13 @@ function createMatrixMonitorStatusController(params) {
4287
4246
  at,
4288
4247
  ...error ? { error } : {}
4289
4248
  };
4290
- status.lastError = error;
4291
4249
  status.healthState = paramsLocal.state.toLowerCase();
4250
+ if (tokenInvalidated) Object.assign(status, channelBlockedPatch(error ?? "Matrix access token invalidated"));
4251
+ else {
4252
+ status.lastError = error;
4253
+ status.lifecycle = "recovering";
4254
+ status.terminalDisconnect = void 0;
4255
+ }
4292
4256
  emit();
4293
4257
  };
4294
4258
  emit();
@@ -4318,9 +4282,14 @@ function createMatrixMonitorStatusController(params) {
4318
4282
  });
4319
4283
  },
4320
4284
  markStopped(at = Date.now()) {
4321
- status.connected = false;
4322
- status.lastEventAt = at;
4323
- if (status.healthState !== "error") status.healthState = "stopped";
4285
+ if (status.lifecycle !== "blocked" && status.healthState !== "error") Object.assign(status, channelStoppedPatch({
4286
+ lastEventAt: at,
4287
+ healthState: "stopped"
4288
+ }));
4289
+ else {
4290
+ status.connected = false;
4291
+ status.lastEventAt = at;
4292
+ }
4324
4293
  emit();
4325
4294
  }
4326
4295
  };
@@ -4385,7 +4354,9 @@ function createMatrixMonitorSyncLifecycle(params) {
4385
4354
  //#region extensions/matrix/src/matrix/monitor/task-runner.ts
4386
4355
  function createMatrixMonitorTaskRunner(params) {
4387
4356
  const inFlight = /* @__PURE__ */ new Set();
4357
+ let closed = false;
4388
4358
  const runDetachedTask = (label, task) => {
4359
+ if (closed) return Promise.resolve();
4389
4360
  const trackedTask = Promise.resolve().then(task).catch((error) => {
4390
4361
  const message = String(error);
4391
4362
  params.logVerboseMessage(`matrix: ${label} failed (${message})`);
@@ -4403,6 +4374,7 @@ function createMatrixMonitorTaskRunner(params) {
4403
4374
  while (inFlight.size > 0) await Promise.allSettled(Array.from(inFlight));
4404
4375
  };
4405
4376
  return {
4377
+ close: () => closed = true,
4406
4378
  runDetachedTask,
4407
4379
  waitForIdle
4408
4380
  };
@@ -4504,28 +4476,30 @@ async function monitorMatrixProvider(opts = {}) {
4504
4476
  statusSink: opts.setStatus
4505
4477
  });
4506
4478
  let cleanedUp = false;
4479
+ let cleanupPromise = null;
4507
4480
  let client = null;
4481
+ let clientLease = null;
4482
+ let monitorLifecycleSignal = opts.abortSignal;
4508
4483
  let threadBindingManager = null;
4509
4484
  const monitorTaskRunner = createMatrixMonitorTaskRunner({
4510
4485
  logger,
4511
4486
  logVerboseMessage
4512
4487
  });
4488
+ let disposeAutoJoin = () => {};
4489
+ let disposeMonitorEvents = () => {};
4513
4490
  let syncLifecycle = null;
4514
- const cleanup = async (mode = "persist") => {
4515
- if (cleanedUp) return;
4491
+ let monitorSetupClosed = false;
4492
+ const cleanup = (mode = "persist") => {
4493
+ if (cleanupPromise) return cleanupPromise;
4516
4494
  cleanedUp = true;
4517
- try {
4518
- client?.stopSyncWithoutPersist();
4519
- if (client && mode === "persist") await client.drainPendingDecryptions("matrix monitor shutdown");
4520
- if (mode === "persist") await monitorTaskRunner.waitForIdle();
4521
- threadBindingManager?.stop();
4522
- if (client) await releaseSharedClientInstance(client, mode);
4523
- } finally {
4524
- client?.off("sync.state", onSyncState);
4525
- syncLifecycle?.dispose();
4526
- statusController.markStopped();
4527
- setActiveMatrixClient(null, auth.accountId);
4528
- }
4495
+ cleanupPromise = (async () => {
4496
+ try {
4497
+ await clientLease?.release({ mode });
4498
+ } finally {
4499
+ statusController.markStopped();
4500
+ }
4501
+ })();
4502
+ return cleanupPromise;
4529
4503
  };
4530
4504
  const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
4531
4505
  const { groupPolicy: groupPolicyRaw, providerMissingFallbackApplied } = resolveAllowlistProviderRuntimeGroupPolicy({
@@ -4582,14 +4556,32 @@ async function monitorMatrixProvider(opts = {}) {
4582
4556
  const onSyncState = (state) => {
4583
4557
  noteSyncHealthState(state);
4584
4558
  };
4559
+ const monitorRetirement = {
4560
+ closeTaskAdmission: () => {
4561
+ monitorSetupClosed = true;
4562
+ monitorTaskRunner.close();
4563
+ },
4564
+ detachListeners: () => {
4565
+ disposeAutoJoin();
4566
+ disposeMonitorEvents();
4567
+ client?.off("sync.state", onSyncState);
4568
+ syncLifecycle?.dispose();
4569
+ },
4570
+ waitForTasks: monitorTaskRunner.waitForIdle,
4571
+ cleanup: () => threadBindingManager?.stop()
4572
+ };
4585
4573
  try {
4586
- client = await resolveSharedMatrixClient({
4574
+ clientLease = await acquireSharedMatrixClient({
4587
4575
  cfg,
4588
4576
  auth: authWithLimit,
4589
4577
  startClient: false,
4590
- accountId: auth.accountId
4578
+ accountId: auth.accountId,
4579
+ abortSignal: opts.abortSignal,
4580
+ role: "monitor"
4591
4581
  });
4592
- setActiveMatrixClient(client, auth.accountId);
4582
+ client = clientLease.client;
4583
+ monitorLifecycleSignal = opts.abortSignal ? AbortSignal.any([opts.abortSignal, clientLease.abortSignal]) : clientLease.abortSignal;
4584
+ clientLease.registerMonitorRetirement(monitorRetirement);
4593
4585
  const inboundDeduper = createMatrixInboundEventDeduper({
4594
4586
  auth,
4595
4587
  env: process.env
@@ -4597,11 +4589,11 @@ async function monitorMatrixProvider(opts = {}) {
4597
4589
  syncLifecycle = createMatrixMonitorSyncLifecycle({
4598
4590
  client,
4599
4591
  statusController,
4600
- isStopping: () => cleanedUp || opts.abortSignal?.aborted === true
4592
+ isStopping: () => cleanedUp || monitorLifecycleSignal?.aborted === true
4601
4593
  });
4602
4594
  client.on("sync.state", onSyncState);
4603
4595
  const dropPreStartupMessages = !client.hasPersistedSyncState();
4604
- const { getRoomInfo, getMemberDisplayName } = createMatrixRoomInfoResolver(client);
4596
+ const { getRoomInfo, getMemberDisplayName, invalidateMemberDisplayName } = createMatrixRoomInfoResolver(client);
4605
4597
  const isExplicitlyConfiguredRoom = async (roomId) => {
4606
4598
  const roomInfoForConfig = needsRoomAliasesForConfig ? await getRoomInfo(roomId, { includeAliases: true }) : void 0;
4607
4599
  const aliases = roomInfoForConfig ? [roomInfoForConfig.canonicalAlias ?? "", ...roomInfoForConfig.altAliases].filter(Boolean) : [];
@@ -4639,10 +4631,11 @@ async function monitorMatrixProvider(opts = {}) {
4639
4631
  }
4640
4632
  }
4641
4633
  });
4642
- registerMatrixAutoJoin({
4634
+ disposeAutoJoin = registerMatrixAutoJoin({
4643
4635
  client,
4644
4636
  accountConfig,
4645
- runtime
4637
+ runtime,
4638
+ runDetachedTask: monitorTaskRunner.runDetachedTask
4646
4639
  });
4647
4640
  const handleRoomMessage = createMatrixRoomMessageHandler({
4648
4641
  client,
@@ -4682,7 +4675,7 @@ async function monitorMatrixProvider(opts = {}) {
4682
4675
  getMemberDisplayName,
4683
4676
  needsRoomAliasesForConfig
4684
4677
  });
4685
- threadBindingManager = await createMatrixThreadBindingManager({
4678
+ const createdThreadBindingManager = await createMatrixThreadBindingManager({
4686
4679
  cfg,
4687
4680
  accountId: effectiveAccountId,
4688
4681
  auth,
@@ -4692,8 +4685,14 @@ async function monitorMatrixProvider(opts = {}) {
4692
4685
  maxAgeMs: threadBindingMaxAgeMs,
4693
4686
  logVerboseMessage
4694
4687
  });
4688
+ if (monitorSetupClosed) {
4689
+ await createdThreadBindingManager.stop();
4690
+ await cleanup("stop");
4691
+ return;
4692
+ }
4693
+ threadBindingManager = createdThreadBindingManager;
4695
4694
  logVerboseMessage(`matrix: thread bindings ready account=${threadBindingManager.accountId} idleMs=${threadBindingIdleTimeoutMs} maxAgeMs=${threadBindingMaxAgeMs}`);
4696
- registerMatrixMonitorEvents({
4695
+ disposeMonitorEvents = registerMatrixMonitorEvents({
4697
4696
  cfg,
4698
4697
  client,
4699
4698
  auth,
@@ -4706,6 +4705,7 @@ async function monitorMatrixProvider(opts = {}) {
4706
4705
  accountId: effectiveAccountId
4707
4706
  }).catch(() => []),
4708
4707
  directTracker,
4708
+ invalidateMemberDisplayName,
4709
4709
  logVerboseMessage,
4710
4710
  warnedEncryptedRooms,
4711
4711
  warnedCryptoMissingRooms,
@@ -4717,18 +4717,17 @@ async function monitorMatrixProvider(opts = {}) {
4717
4717
  runDetachedTask: monitorTaskRunner.runDetachedTask
4718
4718
  });
4719
4719
  logVerboseMessage("matrix: starting client");
4720
- await resolveSharedMatrixClient({
4721
- cfg,
4722
- auth: authWithLimit,
4723
- accountId: auth.accountId,
4724
- abortSignal: opts.abortSignal
4725
- });
4720
+ await clientLease.start(monitorLifecycleSignal);
4721
+ if (monitorSetupClosed) {
4722
+ await cleanup("stop");
4723
+ return;
4724
+ }
4726
4725
  logVerboseMessage("matrix: client started");
4727
4726
  logger.info(`matrix: logged in as ${auth.userId}`);
4728
4727
  backfillMatrixAuthDeviceIdAfterStartup({
4729
4728
  auth,
4730
4729
  env: process.env,
4731
- abortSignal: opts.abortSignal
4730
+ abortSignal: monitorLifecycleSignal
4732
4731
  }).catch((err) => {
4733
4732
  logVerboseMessage(`matrix: failed to backfill deviceId after startup (${String(err)})`);
4734
4733
  });
@@ -4738,7 +4737,7 @@ async function monitorMatrixProvider(opts = {}) {
4738
4737
  accountId: effectiveAccountId,
4739
4738
  capability: CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY,
4740
4739
  context: { client },
4741
- abortSignal: opts.abortSignal
4740
+ abortSignal: monitorLifecycleSignal
4742
4741
  });
4743
4742
  await runMatrixStartupMaintenance({
4744
4743
  client,
@@ -4757,9 +4756,13 @@ async function monitorMatrixProvider(opts = {}) {
4757
4756
  },
4758
4757
  loadWebMedia: async (url, maxBytes) => await core.media.loadWebMedia(url, maxBytes),
4759
4758
  env: process.env,
4760
- abortSignal: opts.abortSignal
4759
+ abortSignal: monitorLifecycleSignal
4761
4760
  });
4762
- await Promise.race([waitUntilAbort(opts.abortSignal, async () => {
4761
+ if (monitorSetupClosed) {
4762
+ await cleanup("stop");
4763
+ return;
4764
+ }
4765
+ await Promise.race([waitUntilAbort(monitorLifecycleSignal, async () => {
4763
4766
  try {
4764
4767
  logVerboseMessage("matrix: stopping client");
4765
4768
  await cleanup();
@@ -4767,8 +4770,13 @@ async function monitorMatrixProvider(opts = {}) {
4767
4770
  logger.warn("matrix: failed during monitor shutdown cleanup", { error: String(err) });
4768
4771
  }
4769
4772
  }), syncLifecycle.waitForFatalStop()]);
4773
+ await cleanup();
4770
4774
  } catch (err) {
4771
- if (opts.abortSignal?.aborted === true && isMatrixStartupAbortError(err)) {
4775
+ if (monitorSetupClosed) {
4776
+ await cleanup("stop");
4777
+ return;
4778
+ }
4779
+ if (monitorLifecycleSignal?.aborted === true && isMatrixStartupAbortError(err)) {
4772
4780
  await cleanup("stop");
4773
4781
  return;
4774
4782
  }