@openclaw/matrix 2026.3.13 → 2026.5.9-beta.1
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.
- package/dist/account-config-D2W-V1eQ.js +96 -0
- package/dist/account-selection-BWwIruri.js +158 -0
- package/dist/accounts-Bm90Rzvp.js +130 -0
- package/dist/active-client-uhlxdhEy.js +20 -0
- package/dist/allowlist-sTzpCn5d.js +68 -0
- package/dist/api.js +12 -0
- package/dist/approval-handler.runtime-DWTQfd4m.js +370 -0
- package/dist/approval-ids-DoC2z7tR.js +7 -0
- package/dist/approval-reaction-auth-DbcA1gGd.js +27 -0
- package/dist/approval-reactions-o2_tuH8D.js +162 -0
- package/dist/async-lock-uQfhfQIY.js +19 -0
- package/dist/auth-presence.js +26 -0
- package/dist/backup-health-Cabu_WQC.js +60 -0
- package/dist/channel-DJNir3Rb.js +1116 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.runtime-BQu0hTih.js +246 -0
- package/dist/cli-BmfTmg7x.js +1340 -0
- package/dist/cli-metadata-B-PCEzrA.js +22 -0
- package/dist/cli-metadata.js +2 -0
- package/dist/client-DkcXnm0X.js +25 -0
- package/dist/client-_hckQNGW.js +31 -0
- package/dist/client-bootstrap-Rb8oHvhH.js +114 -0
- package/dist/config--5-S2Akv.js +452 -0
- package/dist/config-paths-nsVaysCu.js +19 -0
- package/dist/config-schema-nPLpEgHl.js +200 -0
- package/dist/config-secret-input.runtime-DiKFehsE.js +2 -0
- package/dist/config-update-wZX-HLMn.js +143 -0
- package/dist/contract-api.js +9 -0
- package/dist/create-client-DCnqDaqd.js +64 -0
- package/dist/credentials-DV6fWXhC.js +56 -0
- package/dist/credentials-read-cmHgousK.js +112 -0
- package/dist/credentials-write.runtime-zniTq-Gr.js +17 -0
- package/dist/crypto-node.runtime-pihzdpY7.js +12 -0
- package/dist/crypto-runtime-ZI0zAtn3.js +1214 -0
- package/dist/deps-C6WqKY7m.js +235 -0
- package/dist/device-health-UVYpbA_W.js +16 -0
- package/dist/direct-management-DMMMgtTB.js +249 -0
- package/dist/direct-room-XkutHjES.js +76 -0
- package/dist/directory-live-DmOtMhyr.js +150 -0
- package/dist/doctor-C4__7c-U.js +153 -0
- package/dist/doctor-contract-D4-64QuJ.js +246 -0
- package/dist/doctor-contract-api.js +2 -0
- package/dist/draft-stream-BE2QevQQ.js +144 -0
- package/dist/encryption-guidance-BPi3A_m3.js +15 -0
- package/dist/env-auth-BJqGI8M6.js +63 -0
- package/dist/env-vars-C7uQCTKn.js +63 -0
- package/dist/errors-CTcpEDq-.js +17 -0
- package/dist/exec-approval-resolver-Bza9Dhlm.js +15 -0
- package/dist/exec-approvals-Crnh543m.js +196 -0
- package/dist/helper-api.js +4 -0
- package/dist/http-client-C7AeVJay.js +319 -0
- package/dist/index.js +46 -0
- package/dist/legacy-crypto-inspector-poDWldgy.js +41 -0
- package/dist/legacy-crypto-restore-Biw-w2ng.js +85 -0
- package/dist/logger-CnZRVrux.js +78 -0
- package/dist/logging-DZHSPP5N.js +99 -0
- package/dist/matrix-migration.runtime-WY6ffcrf.js +525 -0
- package/dist/media-text-DU6nWZuj.js +146 -0
- package/dist/messages-BpihMh82.js +140 -0
- package/dist/migration-snapshot-backup-DaCHTp8C.js +69 -0
- package/dist/migration-snapshot.runtime-CKHE3xF9.js +2 -0
- package/dist/monitor-C_81r_Ck.js +4125 -0
- package/dist/plugin-entry.handlers.runtime.js +51 -0
- package/dist/probe.runtime-BvAzYAIe.js +3 -0
- package/dist/profile-BlHu0wDX.js +111 -0
- package/dist/profile-update-DjeBNgIV.js +69 -0
- package/dist/reaction-common-ejrL19w-.js +71 -0
- package/dist/reaction-events-CiARZfjk.js +121 -0
- package/dist/record-shared-CHWJCTWf.js +2 -0
- package/dist/recovery-key-store-BTJ6jz5v.js +294 -0
- package/dist/resolve-targets-YtJnw1Tb.js +140 -0
- package/dist/resolver.runtime-D9piiGEl.js +5 -0
- package/dist/rolldown-runtime-DUslC3ob.js +14 -0
- package/dist/route-D6rg-iXN.js +161 -0
- package/dist/runtime-C6X4h_SJ.js +6 -0
- package/dist/runtime-Dog86njy.js +8 -0
- package/dist/runtime-api-BXWBFIqm.js +25 -0
- package/dist/runtime-api.js +25 -0
- package/dist/runtime-heavy-api.js +3 -0
- package/dist/runtime-setter-api.js +2 -0
- package/dist/sdk-B2vZA27-.js +1416 -0
- package/dist/secret-contract-DcrJWCQI.js +120 -0
- package/dist/secret-contract-api.js +2 -0
- package/dist/send-Bo0DU1ca.js +1200 -0
- package/dist/session-store-metadata-DI5SCofx.js +77 -0
- package/dist/setup-bootstrap-ImenBsMt.js +62 -0
- package/dist/setup-core-CfZy05oW.js +116 -0
- package/dist/setup-dm-policy-2-r1FrQh.js +194 -0
- package/dist/setup-entry.js +19 -0
- package/dist/setup-plugin-api.js +44 -0
- package/dist/setup-surface-CqT_o61M.js +540 -0
- package/dist/shared-CpMoYKm1.js +195 -0
- package/dist/startup-abort-56edvmbM.js +32 -0
- package/dist/startup-verification-Demyp0bP.js +132 -0
- package/dist/storage-paths-BJLdnCjV.js +52 -0
- package/dist/storage-tC3ujLiW.js +281 -0
- package/dist/subagent-hooks-DQbyqq9V.js +149 -0
- package/dist/subagent-hooks-api.js +23 -0
- package/dist/sync-state-C_beeevA.js +12 -0
- package/dist/target-ids-80nQ2gql.js +77 -0
- package/dist/test-api.js +4 -0
- package/dist/thread-binding-api-Cq_E-E1K.js +17 -0
- package/dist/thread-binding-api.js +2 -0
- package/dist/thread-bindings-B9mesxXk.js +352 -0
- package/dist/thread-bindings-runtime.js +2 -0
- package/dist/thread-bindings-shared-DK-d-oYX.js +97 -0
- package/dist/timeout-abort-signal-CtaIaP1v.js +2 -0
- package/dist/tool-actions.runtime-BIH49vRr.js +532 -0
- package/dist/url-validation-DiK9j7jz.js +36 -0
- package/dist/verification-CZ2rDeHL.js +345 -0
- package/openclaw.plugin.json +788 -1
- package/package.json +82 -16
- package/CHANGELOG.md +0 -104
- package/index.ts +0 -22
- package/src/actions.ts +0 -195
- package/src/channel.directory.test.ts +0 -135
- package/src/channel.ts +0 -461
- package/src/config-schema.test.ts +0 -26
- package/src/config-schema.ts +0 -62
- package/src/directory-live.test.ts +0 -85
- package/src/directory-live.ts +0 -209
- package/src/group-mentions.ts +0 -52
- package/src/matrix/accounts.test.ts +0 -131
- package/src/matrix/accounts.ts +0 -114
- package/src/matrix/actions/client.ts +0 -47
- package/src/matrix/actions/limits.test.ts +0 -15
- package/src/matrix/actions/limits.ts +0 -6
- package/src/matrix/actions/messages.ts +0 -126
- package/src/matrix/actions/pins.test.ts +0 -74
- package/src/matrix/actions/pins.ts +0 -84
- package/src/matrix/actions/reactions.test.ts +0 -109
- package/src/matrix/actions/reactions.ts +0 -102
- package/src/matrix/actions/room.ts +0 -85
- package/src/matrix/actions/summary.ts +0 -75
- package/src/matrix/actions/types.ts +0 -85
- package/src/matrix/actions.ts +0 -15
- package/src/matrix/active-client.ts +0 -32
- package/src/matrix/client/config.ts +0 -245
- package/src/matrix/client/create-client.ts +0 -125
- package/src/matrix/client/logging.ts +0 -46
- package/src/matrix/client/runtime.ts +0 -4
- package/src/matrix/client/shared.test.ts +0 -85
- package/src/matrix/client/shared.ts +0 -210
- package/src/matrix/client/startup.test.ts +0 -49
- package/src/matrix/client/startup.ts +0 -29
- package/src/matrix/client/storage.ts +0 -131
- package/src/matrix/client/types.ts +0 -34
- package/src/matrix/client-bootstrap.ts +0 -47
- package/src/matrix/client.test.ts +0 -56
- package/src/matrix/client.ts +0 -14
- package/src/matrix/credentials.ts +0 -125
- package/src/matrix/deps.test.ts +0 -74
- package/src/matrix/deps.ts +0 -126
- package/src/matrix/format.test.ts +0 -33
- package/src/matrix/format.ts +0 -22
- package/src/matrix/index.ts +0 -11
- package/src/matrix/monitor/access-policy.ts +0 -126
- package/src/matrix/monitor/allowlist.test.ts +0 -45
- package/src/matrix/monitor/allowlist.ts +0 -94
- package/src/matrix/monitor/auto-join.ts +0 -72
- package/src/matrix/monitor/direct.test.ts +0 -396
- package/src/matrix/monitor/direct.ts +0 -152
- package/src/matrix/monitor/events.test.ts +0 -186
- package/src/matrix/monitor/events.ts +0 -168
- package/src/matrix/monitor/handler.body-for-agent.test.ts +0 -196
- package/src/matrix/monitor/handler.ts +0 -768
- package/src/matrix/monitor/inbound-body.test.ts +0 -73
- package/src/matrix/monitor/inbound-body.ts +0 -28
- package/src/matrix/monitor/index.test.ts +0 -18
- package/src/matrix/monitor/index.ts +0 -414
- package/src/matrix/monitor/location.ts +0 -100
- package/src/matrix/monitor/media.test.ts +0 -86
- package/src/matrix/monitor/media.ts +0 -118
- package/src/matrix/monitor/mentions.test.ts +0 -154
- package/src/matrix/monitor/mentions.ts +0 -62
- package/src/matrix/monitor/replies.test.ts +0 -184
- package/src/matrix/monitor/replies.ts +0 -124
- package/src/matrix/monitor/room-info.ts +0 -55
- package/src/matrix/monitor/rooms.test.ts +0 -124
- package/src/matrix/monitor/rooms.ts +0 -47
- package/src/matrix/monitor/threads.ts +0 -68
- package/src/matrix/monitor/types.ts +0 -39
- package/src/matrix/poll-types.test.ts +0 -21
- package/src/matrix/poll-types.ts +0 -167
- package/src/matrix/probe.ts +0 -69
- package/src/matrix/sdk-runtime.ts +0 -18
- package/src/matrix/send/client.ts +0 -99
- package/src/matrix/send/formatting.ts +0 -93
- package/src/matrix/send/media.ts +0 -230
- package/src/matrix/send/targets.test.ts +0 -98
- package/src/matrix/send/targets.ts +0 -150
- package/src/matrix/send/types.ts +0 -110
- package/src/matrix/send-queue.test.ts +0 -145
- package/src/matrix/send-queue.ts +0 -28
- package/src/matrix/send.test.ts +0 -319
- package/src/matrix/send.ts +0 -267
- package/src/onboarding.ts +0 -462
- package/src/outbound.test.ts +0 -159
- package/src/outbound.ts +0 -58
- package/src/resolve-targets.test.ts +0 -68
- package/src/resolve-targets.ts +0 -125
- package/src/runtime.ts +0 -6
- package/src/secret-input.ts +0 -13
- package/src/test-mocks.ts +0 -53
- package/src/tool-actions.ts +0 -164
- package/src/types.ts +0 -118
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import type { MatrixClient } from "@vector-im/matrix-bot-sdk";
|
|
2
|
-
import type { MarkdownTableMode, ReplyPayload, RuntimeEnv } from "openclaw/plugin-sdk/matrix";
|
|
3
|
-
import { getMatrixRuntime } from "../../runtime.js";
|
|
4
|
-
import { sendMessageMatrix } from "../send.js";
|
|
5
|
-
|
|
6
|
-
export async function deliverMatrixReplies(params: {
|
|
7
|
-
replies: ReplyPayload[];
|
|
8
|
-
roomId: string;
|
|
9
|
-
client: MatrixClient;
|
|
10
|
-
runtime: RuntimeEnv;
|
|
11
|
-
textLimit: number;
|
|
12
|
-
replyToMode: "off" | "first" | "all";
|
|
13
|
-
threadId?: string;
|
|
14
|
-
accountId?: string;
|
|
15
|
-
tableMode?: MarkdownTableMode;
|
|
16
|
-
}): Promise<void> {
|
|
17
|
-
const core = getMatrixRuntime();
|
|
18
|
-
const cfg = core.config.loadConfig();
|
|
19
|
-
const tableMode =
|
|
20
|
-
params.tableMode ??
|
|
21
|
-
core.channel.text.resolveMarkdownTableMode({
|
|
22
|
-
cfg,
|
|
23
|
-
channel: "matrix",
|
|
24
|
-
accountId: params.accountId,
|
|
25
|
-
});
|
|
26
|
-
const logVerbose = (message: string) => {
|
|
27
|
-
if (core.logging.shouldLogVerbose()) {
|
|
28
|
-
params.runtime.log?.(message);
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
const chunkLimit = Math.min(params.textLimit, 4000);
|
|
32
|
-
const chunkMode = core.channel.text.resolveChunkMode(cfg, "matrix", params.accountId);
|
|
33
|
-
let hasReplied = false;
|
|
34
|
-
for (const reply of params.replies) {
|
|
35
|
-
const hasMedia = Boolean(reply?.mediaUrl) || (reply?.mediaUrls?.length ?? 0) > 0;
|
|
36
|
-
if (!reply?.text && !hasMedia) {
|
|
37
|
-
if (reply?.audioAsVoice) {
|
|
38
|
-
logVerbose("matrix reply has audioAsVoice without media/text; skipping");
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
params.runtime.error?.("matrix reply missing text/media");
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
// Skip pure reasoning messages so internal thinking traces are never delivered.
|
|
45
|
-
if (reply.text && isReasoningOnlyMessage(reply.text)) {
|
|
46
|
-
logVerbose("matrix reply is reasoning-only; skipping");
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
const replyToIdRaw = reply.replyToId?.trim();
|
|
50
|
-
const replyToId = params.threadId || params.replyToMode === "off" ? undefined : replyToIdRaw;
|
|
51
|
-
const rawText = reply.text ?? "";
|
|
52
|
-
const text = core.channel.text.convertMarkdownTables(rawText, tableMode);
|
|
53
|
-
const mediaList = reply.mediaUrls?.length
|
|
54
|
-
? reply.mediaUrls
|
|
55
|
-
: reply.mediaUrl
|
|
56
|
-
? [reply.mediaUrl]
|
|
57
|
-
: [];
|
|
58
|
-
|
|
59
|
-
const shouldIncludeReply = (id?: string) =>
|
|
60
|
-
Boolean(id) && (params.replyToMode === "all" || !hasReplied);
|
|
61
|
-
const replyToIdForReply = shouldIncludeReply(replyToId) ? replyToId : undefined;
|
|
62
|
-
|
|
63
|
-
if (mediaList.length === 0) {
|
|
64
|
-
let sentTextChunk = false;
|
|
65
|
-
for (const chunk of core.channel.text.chunkMarkdownTextWithMode(
|
|
66
|
-
text,
|
|
67
|
-
chunkLimit,
|
|
68
|
-
chunkMode,
|
|
69
|
-
)) {
|
|
70
|
-
const trimmed = chunk.trim();
|
|
71
|
-
if (!trimmed) {
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
await sendMessageMatrix(params.roomId, trimmed, {
|
|
75
|
-
client: params.client,
|
|
76
|
-
replyToId: replyToIdForReply,
|
|
77
|
-
threadId: params.threadId,
|
|
78
|
-
accountId: params.accountId,
|
|
79
|
-
});
|
|
80
|
-
sentTextChunk = true;
|
|
81
|
-
}
|
|
82
|
-
if (replyToIdForReply && !hasReplied && sentTextChunk) {
|
|
83
|
-
hasReplied = true;
|
|
84
|
-
}
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
let first = true;
|
|
89
|
-
for (const mediaUrl of mediaList) {
|
|
90
|
-
const caption = first ? text : "";
|
|
91
|
-
await sendMessageMatrix(params.roomId, caption, {
|
|
92
|
-
client: params.client,
|
|
93
|
-
mediaUrl,
|
|
94
|
-
replyToId: replyToIdForReply,
|
|
95
|
-
threadId: params.threadId,
|
|
96
|
-
audioAsVoice: reply.audioAsVoice,
|
|
97
|
-
accountId: params.accountId,
|
|
98
|
-
});
|
|
99
|
-
first = false;
|
|
100
|
-
}
|
|
101
|
-
if (replyToIdForReply && !hasReplied) {
|
|
102
|
-
hasReplied = true;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const REASONING_PREFIX = "Reasoning:\n";
|
|
108
|
-
const THINKING_TAG_RE = /^\s*<\s*(?:think(?:ing)?|thought|antthinking)\b/i;
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Detect messages that contain only reasoning/thinking content and no user-facing answer.
|
|
112
|
-
* These are emitted by the agent when `includeReasoning` is active but should not
|
|
113
|
-
* be forwarded to channels that do not support a dedicated reasoning lane.
|
|
114
|
-
*/
|
|
115
|
-
function isReasoningOnlyMessage(text: string): boolean {
|
|
116
|
-
const trimmed = text.trim();
|
|
117
|
-
if (trimmed.startsWith(REASONING_PREFIX)) {
|
|
118
|
-
return true;
|
|
119
|
-
}
|
|
120
|
-
if (THINKING_TAG_RE.test(trimmed)) {
|
|
121
|
-
return true;
|
|
122
|
-
}
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import type { MatrixClient } from "@vector-im/matrix-bot-sdk";
|
|
2
|
-
|
|
3
|
-
export type MatrixRoomInfo = {
|
|
4
|
-
name?: string;
|
|
5
|
-
canonicalAlias?: string;
|
|
6
|
-
altAliases: string[];
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export function createMatrixRoomInfoResolver(client: MatrixClient) {
|
|
10
|
-
const roomInfoCache = new Map<string, MatrixRoomInfo>();
|
|
11
|
-
|
|
12
|
-
const getRoomInfo = async (roomId: string): Promise<MatrixRoomInfo> => {
|
|
13
|
-
const cached = roomInfoCache.get(roomId);
|
|
14
|
-
if (cached) {
|
|
15
|
-
return cached;
|
|
16
|
-
}
|
|
17
|
-
let name: string | undefined;
|
|
18
|
-
let canonicalAlias: string | undefined;
|
|
19
|
-
let altAliases: string[] = [];
|
|
20
|
-
try {
|
|
21
|
-
const nameState = await client.getRoomStateEvent(roomId, "m.room.name", "").catch(() => null);
|
|
22
|
-
name = nameState?.name;
|
|
23
|
-
} catch {
|
|
24
|
-
// ignore
|
|
25
|
-
}
|
|
26
|
-
try {
|
|
27
|
-
const aliasState = await client
|
|
28
|
-
.getRoomStateEvent(roomId, "m.room.canonical_alias", "")
|
|
29
|
-
.catch(() => null);
|
|
30
|
-
canonicalAlias = aliasState?.alias;
|
|
31
|
-
altAliases = aliasState?.alt_aliases ?? [];
|
|
32
|
-
} catch {
|
|
33
|
-
// ignore
|
|
34
|
-
}
|
|
35
|
-
const info = { name, canonicalAlias, altAliases };
|
|
36
|
-
roomInfoCache.set(roomId, info);
|
|
37
|
-
return info;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const getMemberDisplayName = async (roomId: string, userId: string): Promise<string> => {
|
|
41
|
-
try {
|
|
42
|
-
const memberState = await client
|
|
43
|
-
.getRoomStateEvent(roomId, "m.room.member", userId)
|
|
44
|
-
.catch(() => null);
|
|
45
|
-
return memberState?.displayname ?? userId;
|
|
46
|
-
} catch {
|
|
47
|
-
return userId;
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
return {
|
|
52
|
-
getRoomInfo,
|
|
53
|
-
getMemberDisplayName,
|
|
54
|
-
};
|
|
55
|
-
}
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { resolveMatrixRoomConfig } from "./rooms.js";
|
|
3
|
-
|
|
4
|
-
describe("resolveMatrixRoomConfig", () => {
|
|
5
|
-
it("matches room IDs and aliases, not names", () => {
|
|
6
|
-
const rooms = {
|
|
7
|
-
"!room:example.org": { allow: true },
|
|
8
|
-
"#alias:example.org": { allow: true },
|
|
9
|
-
"Project Room": { allow: true },
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const byId = resolveMatrixRoomConfig({
|
|
13
|
-
rooms,
|
|
14
|
-
roomId: "!room:example.org",
|
|
15
|
-
aliases: [],
|
|
16
|
-
name: "Project Room",
|
|
17
|
-
});
|
|
18
|
-
expect(byId.allowed).toBe(true);
|
|
19
|
-
expect(byId.matchKey).toBe("!room:example.org");
|
|
20
|
-
|
|
21
|
-
const byAlias = resolveMatrixRoomConfig({
|
|
22
|
-
rooms,
|
|
23
|
-
roomId: "!other:example.org",
|
|
24
|
-
aliases: ["#alias:example.org"],
|
|
25
|
-
name: "Other Room",
|
|
26
|
-
});
|
|
27
|
-
expect(byAlias.allowed).toBe(true);
|
|
28
|
-
expect(byAlias.matchKey).toBe("#alias:example.org");
|
|
29
|
-
|
|
30
|
-
const byName = resolveMatrixRoomConfig({
|
|
31
|
-
rooms: { "Project Room": { allow: true } },
|
|
32
|
-
roomId: "!different:example.org",
|
|
33
|
-
aliases: [],
|
|
34
|
-
name: "Project Room",
|
|
35
|
-
});
|
|
36
|
-
expect(byName.allowed).toBe(false);
|
|
37
|
-
expect(byName.config).toBeUndefined();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
describe("matchSource classification", () => {
|
|
41
|
-
it('returns matchSource="direct" for exact room ID match', () => {
|
|
42
|
-
const result = resolveMatrixRoomConfig({
|
|
43
|
-
rooms: { "!room:example.org": { allow: true } },
|
|
44
|
-
roomId: "!room:example.org",
|
|
45
|
-
aliases: [],
|
|
46
|
-
});
|
|
47
|
-
expect(result.matchSource).toBe("direct");
|
|
48
|
-
expect(result.config).toBeDefined();
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('returns matchSource="direct" for alias match', () => {
|
|
52
|
-
const result = resolveMatrixRoomConfig({
|
|
53
|
-
rooms: { "#alias:example.org": { allow: true } },
|
|
54
|
-
roomId: "!room:example.org",
|
|
55
|
-
aliases: ["#alias:example.org"],
|
|
56
|
-
});
|
|
57
|
-
expect(result.matchSource).toBe("direct");
|
|
58
|
-
expect(result.config).toBeDefined();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('returns matchSource="wildcard" for wildcard match', () => {
|
|
62
|
-
const result = resolveMatrixRoomConfig({
|
|
63
|
-
rooms: { "*": { allow: true } },
|
|
64
|
-
roomId: "!any:example.org",
|
|
65
|
-
aliases: [],
|
|
66
|
-
});
|
|
67
|
-
expect(result.matchSource).toBe("wildcard");
|
|
68
|
-
expect(result.config).toBeDefined();
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it("returns undefined matchSource when no match", () => {
|
|
72
|
-
const result = resolveMatrixRoomConfig({
|
|
73
|
-
rooms: { "!other:example.org": { allow: true } },
|
|
74
|
-
roomId: "!room:example.org",
|
|
75
|
-
aliases: [],
|
|
76
|
-
});
|
|
77
|
-
expect(result.matchSource).toBeUndefined();
|
|
78
|
-
expect(result.config).toBeUndefined();
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it("direct match takes priority over wildcard", () => {
|
|
82
|
-
const result = resolveMatrixRoomConfig({
|
|
83
|
-
rooms: {
|
|
84
|
-
"!room:example.org": { allow: true, systemPrompt: "room-specific" },
|
|
85
|
-
"*": { allow: true, systemPrompt: "generic" },
|
|
86
|
-
},
|
|
87
|
-
roomId: "!room:example.org",
|
|
88
|
-
aliases: [],
|
|
89
|
-
});
|
|
90
|
-
expect(result.matchSource).toBe("direct");
|
|
91
|
-
expect(result.config?.systemPrompt).toBe("room-specific");
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
describe("DM override safety (matchSource distinction)", () => {
|
|
96
|
-
// These tests verify the matchSource property that handler.ts uses
|
|
97
|
-
// to decide whether a configured room should override DM classification.
|
|
98
|
-
// Only "direct" matches should trigger the override -- never "wildcard".
|
|
99
|
-
|
|
100
|
-
it("wildcard config should NOT be usable to override DM classification", () => {
|
|
101
|
-
const result = resolveMatrixRoomConfig({
|
|
102
|
-
rooms: { "*": { allow: true, skills: ["general"] } },
|
|
103
|
-
roomId: "!dm-room:example.org",
|
|
104
|
-
aliases: [],
|
|
105
|
-
});
|
|
106
|
-
// handler.ts checks: matchSource === "direct" -> this is "wildcard", so no override
|
|
107
|
-
expect(result.matchSource).not.toBe("direct");
|
|
108
|
-
expect(result.matchSource).toBe("wildcard");
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it("explicitly configured room should be usable to override DM classification", () => {
|
|
112
|
-
const result = resolveMatrixRoomConfig({
|
|
113
|
-
rooms: {
|
|
114
|
-
"!configured-room:example.org": { allow: true },
|
|
115
|
-
"*": { allow: true },
|
|
116
|
-
},
|
|
117
|
-
roomId: "!configured-room:example.org",
|
|
118
|
-
aliases: [],
|
|
119
|
-
});
|
|
120
|
-
// handler.ts checks: matchSource === "direct" -> this IS "direct", so override is safe
|
|
121
|
-
expect(result.matchSource).toBe("direct");
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
});
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { buildChannelKeyCandidates, resolveChannelEntryMatch } from "openclaw/plugin-sdk/matrix";
|
|
2
|
-
import type { MatrixRoomConfig } from "../../types.js";
|
|
3
|
-
|
|
4
|
-
export type MatrixRoomConfigResolved = {
|
|
5
|
-
allowed: boolean;
|
|
6
|
-
allowlistConfigured: boolean;
|
|
7
|
-
config?: MatrixRoomConfig;
|
|
8
|
-
matchKey?: string;
|
|
9
|
-
matchSource?: "direct" | "wildcard";
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export function resolveMatrixRoomConfig(params: {
|
|
13
|
-
rooms?: Record<string, MatrixRoomConfig>;
|
|
14
|
-
roomId: string;
|
|
15
|
-
aliases: string[];
|
|
16
|
-
name?: string | null;
|
|
17
|
-
}): MatrixRoomConfigResolved {
|
|
18
|
-
const rooms = params.rooms ?? {};
|
|
19
|
-
const keys = Object.keys(rooms);
|
|
20
|
-
const allowlistConfigured = keys.length > 0;
|
|
21
|
-
const candidates = buildChannelKeyCandidates(
|
|
22
|
-
params.roomId,
|
|
23
|
-
`room:${params.roomId}`,
|
|
24
|
-
...params.aliases,
|
|
25
|
-
);
|
|
26
|
-
const {
|
|
27
|
-
entry: matched,
|
|
28
|
-
key: matchedKey,
|
|
29
|
-
wildcardEntry,
|
|
30
|
-
wildcardKey,
|
|
31
|
-
} = resolveChannelEntryMatch({
|
|
32
|
-
entries: rooms,
|
|
33
|
-
keys: candidates,
|
|
34
|
-
wildcardKey: "*",
|
|
35
|
-
});
|
|
36
|
-
const resolved = matched ?? wildcardEntry;
|
|
37
|
-
const allowed = resolved ? resolved.enabled !== false && resolved.allow !== false : false;
|
|
38
|
-
const matchKey = matchedKey ?? wildcardKey;
|
|
39
|
-
const matchSource = matched ? "direct" : wildcardEntry ? "wildcard" : undefined;
|
|
40
|
-
return {
|
|
41
|
-
allowed,
|
|
42
|
-
allowlistConfigured,
|
|
43
|
-
config: resolved,
|
|
44
|
-
matchKey,
|
|
45
|
-
matchSource,
|
|
46
|
-
};
|
|
47
|
-
}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
// Type for raw Matrix event from @vector-im/matrix-bot-sdk
|
|
2
|
-
type MatrixRawEvent = {
|
|
3
|
-
event_id: string;
|
|
4
|
-
sender: string;
|
|
5
|
-
type: string;
|
|
6
|
-
origin_server_ts: number;
|
|
7
|
-
content: Record<string, unknown>;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
type RoomMessageEventContent = {
|
|
11
|
-
msgtype: string;
|
|
12
|
-
body: string;
|
|
13
|
-
"m.relates_to"?: {
|
|
14
|
-
rel_type?: string;
|
|
15
|
-
event_id?: string;
|
|
16
|
-
"m.in_reply_to"?: { event_id?: string };
|
|
17
|
-
};
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const RelationType = {
|
|
21
|
-
Thread: "m.thread",
|
|
22
|
-
} as const;
|
|
23
|
-
|
|
24
|
-
export function resolveMatrixThreadTarget(params: {
|
|
25
|
-
threadReplies: "off" | "inbound" | "always";
|
|
26
|
-
messageId: string;
|
|
27
|
-
threadRootId?: string;
|
|
28
|
-
isThreadRoot?: boolean;
|
|
29
|
-
}): string | undefined {
|
|
30
|
-
const { threadReplies, messageId, threadRootId } = params;
|
|
31
|
-
if (threadReplies === "off") {
|
|
32
|
-
return undefined;
|
|
33
|
-
}
|
|
34
|
-
const isThreadRoot = params.isThreadRoot === true;
|
|
35
|
-
const hasInboundThread = Boolean(threadRootId && threadRootId !== messageId && !isThreadRoot);
|
|
36
|
-
if (threadReplies === "inbound") {
|
|
37
|
-
return hasInboundThread ? threadRootId : undefined;
|
|
38
|
-
}
|
|
39
|
-
if (threadReplies === "always") {
|
|
40
|
-
return threadRootId ?? messageId;
|
|
41
|
-
}
|
|
42
|
-
return undefined;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function resolveMatrixThreadRootId(params: {
|
|
46
|
-
event: MatrixRawEvent;
|
|
47
|
-
content: RoomMessageEventContent;
|
|
48
|
-
}): string | undefined {
|
|
49
|
-
const relates = params.content["m.relates_to"];
|
|
50
|
-
if (!relates || typeof relates !== "object") {
|
|
51
|
-
return undefined;
|
|
52
|
-
}
|
|
53
|
-
if ("rel_type" in relates && relates.rel_type === RelationType.Thread) {
|
|
54
|
-
if ("event_id" in relates && typeof relates.event_id === "string") {
|
|
55
|
-
return relates.event_id;
|
|
56
|
-
}
|
|
57
|
-
if (
|
|
58
|
-
"m.in_reply_to" in relates &&
|
|
59
|
-
typeof relates["m.in_reply_to"] === "object" &&
|
|
60
|
-
relates["m.in_reply_to"] &&
|
|
61
|
-
"event_id" in relates["m.in_reply_to"] &&
|
|
62
|
-
typeof relates["m.in_reply_to"].event_id === "string"
|
|
63
|
-
) {
|
|
64
|
-
return relates["m.in_reply_to"].event_id;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return undefined;
|
|
68
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import type { EncryptedFile, MessageEventContent } from "@vector-im/matrix-bot-sdk";
|
|
2
|
-
|
|
3
|
-
export const EventType = {
|
|
4
|
-
RoomMessage: "m.room.message",
|
|
5
|
-
RoomMessageEncrypted: "m.room.encrypted",
|
|
6
|
-
RoomMember: "m.room.member",
|
|
7
|
-
Location: "m.location",
|
|
8
|
-
} as const;
|
|
9
|
-
|
|
10
|
-
export const RelationType = {
|
|
11
|
-
Replace: "m.replace",
|
|
12
|
-
Thread: "m.thread",
|
|
13
|
-
} as const;
|
|
14
|
-
|
|
15
|
-
export type MatrixRawEvent = {
|
|
16
|
-
event_id: string;
|
|
17
|
-
sender: string;
|
|
18
|
-
type: string;
|
|
19
|
-
origin_server_ts: number;
|
|
20
|
-
content: Record<string, unknown>;
|
|
21
|
-
unsigned?: {
|
|
22
|
-
age?: number;
|
|
23
|
-
redacted_because?: unknown;
|
|
24
|
-
};
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export type RoomMessageEventContent = MessageEventContent & {
|
|
28
|
-
url?: string;
|
|
29
|
-
file?: EncryptedFile;
|
|
30
|
-
info?: {
|
|
31
|
-
mimetype?: string;
|
|
32
|
-
size?: number;
|
|
33
|
-
};
|
|
34
|
-
"m.relates_to"?: {
|
|
35
|
-
rel_type?: string;
|
|
36
|
-
event_id?: string;
|
|
37
|
-
"m.in_reply_to"?: { event_id?: string };
|
|
38
|
-
};
|
|
39
|
-
};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { parsePollStartContent } from "./poll-types.js";
|
|
3
|
-
|
|
4
|
-
describe("parsePollStartContent", () => {
|
|
5
|
-
it("parses legacy m.poll payloads", () => {
|
|
6
|
-
const summary = parsePollStartContent({
|
|
7
|
-
"m.poll": {
|
|
8
|
-
question: { "m.text": "Lunch?" },
|
|
9
|
-
kind: "m.poll.disclosed",
|
|
10
|
-
max_selections: 1,
|
|
11
|
-
answers: [
|
|
12
|
-
{ id: "answer1", "m.text": "Yes" },
|
|
13
|
-
{ id: "answer2", "m.text": "No" },
|
|
14
|
-
],
|
|
15
|
-
},
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
expect(summary?.question).toBe("Lunch?");
|
|
19
|
-
expect(summary?.answers).toEqual(["Yes", "No"]);
|
|
20
|
-
});
|
|
21
|
-
});
|
package/src/matrix/poll-types.ts
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Matrix Poll Types (MSC3381)
|
|
3
|
-
*
|
|
4
|
-
* Defines types for Matrix poll events:
|
|
5
|
-
* - m.poll.start - Creates a new poll
|
|
6
|
-
* - m.poll.response - Records a vote
|
|
7
|
-
* - m.poll.end - Closes a poll
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import type { PollInput } from "openclaw/plugin-sdk/matrix";
|
|
11
|
-
|
|
12
|
-
export const M_POLL_START = "m.poll.start" as const;
|
|
13
|
-
export const M_POLL_RESPONSE = "m.poll.response" as const;
|
|
14
|
-
export const M_POLL_END = "m.poll.end" as const;
|
|
15
|
-
|
|
16
|
-
export const ORG_POLL_START = "org.matrix.msc3381.poll.start" as const;
|
|
17
|
-
export const ORG_POLL_RESPONSE = "org.matrix.msc3381.poll.response" as const;
|
|
18
|
-
export const ORG_POLL_END = "org.matrix.msc3381.poll.end" as const;
|
|
19
|
-
|
|
20
|
-
export const POLL_EVENT_TYPES = [
|
|
21
|
-
M_POLL_START,
|
|
22
|
-
M_POLL_RESPONSE,
|
|
23
|
-
M_POLL_END,
|
|
24
|
-
ORG_POLL_START,
|
|
25
|
-
ORG_POLL_RESPONSE,
|
|
26
|
-
ORG_POLL_END,
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
export const POLL_START_TYPES = [M_POLL_START, ORG_POLL_START];
|
|
30
|
-
export const POLL_RESPONSE_TYPES = [M_POLL_RESPONSE, ORG_POLL_RESPONSE];
|
|
31
|
-
export const POLL_END_TYPES = [M_POLL_END, ORG_POLL_END];
|
|
32
|
-
|
|
33
|
-
export type PollKind = "m.poll.disclosed" | "m.poll.undisclosed";
|
|
34
|
-
|
|
35
|
-
export type TextContent = {
|
|
36
|
-
"m.text"?: string;
|
|
37
|
-
"org.matrix.msc1767.text"?: string;
|
|
38
|
-
body?: string;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export type PollAnswer = {
|
|
42
|
-
id: string;
|
|
43
|
-
} & TextContent;
|
|
44
|
-
|
|
45
|
-
export type PollStartSubtype = {
|
|
46
|
-
question: TextContent;
|
|
47
|
-
kind?: PollKind;
|
|
48
|
-
max_selections?: number;
|
|
49
|
-
answers: PollAnswer[];
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export type LegacyPollStartContent = {
|
|
53
|
-
"m.poll"?: PollStartSubtype;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export type PollStartContent = {
|
|
57
|
-
[M_POLL_START]?: PollStartSubtype;
|
|
58
|
-
[ORG_POLL_START]?: PollStartSubtype;
|
|
59
|
-
"m.poll"?: PollStartSubtype;
|
|
60
|
-
"m.text"?: string;
|
|
61
|
-
"org.matrix.msc1767.text"?: string;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
export type PollSummary = {
|
|
65
|
-
eventId: string;
|
|
66
|
-
roomId: string;
|
|
67
|
-
sender: string;
|
|
68
|
-
senderName: string;
|
|
69
|
-
question: string;
|
|
70
|
-
answers: string[];
|
|
71
|
-
kind: PollKind;
|
|
72
|
-
maxSelections: number;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
export function isPollStartType(eventType: string): boolean {
|
|
76
|
-
return (POLL_START_TYPES as readonly string[]).includes(eventType);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function getTextContent(text?: TextContent): string {
|
|
80
|
-
if (!text) {
|
|
81
|
-
return "";
|
|
82
|
-
}
|
|
83
|
-
return text["m.text"] ?? text["org.matrix.msc1767.text"] ?? text.body ?? "";
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export function parsePollStartContent(content: PollStartContent): PollSummary | null {
|
|
87
|
-
const poll =
|
|
88
|
-
(content as Record<string, PollStartSubtype | undefined>)[M_POLL_START] ??
|
|
89
|
-
(content as Record<string, PollStartSubtype | undefined>)[ORG_POLL_START] ??
|
|
90
|
-
(content as Record<string, PollStartSubtype | undefined>)["m.poll"];
|
|
91
|
-
if (!poll) {
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const question = getTextContent(poll.question);
|
|
96
|
-
if (!question) {
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const answers = poll.answers
|
|
101
|
-
.map((answer) => getTextContent(answer))
|
|
102
|
-
.filter((a) => a.trim().length > 0);
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
eventId: "",
|
|
106
|
-
roomId: "",
|
|
107
|
-
sender: "",
|
|
108
|
-
senderName: "",
|
|
109
|
-
question,
|
|
110
|
-
answers,
|
|
111
|
-
kind: poll.kind ?? "m.poll.disclosed",
|
|
112
|
-
maxSelections: poll.max_selections ?? 1,
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export function formatPollAsText(summary: PollSummary): string {
|
|
117
|
-
const lines = [
|
|
118
|
-
"[Poll]",
|
|
119
|
-
summary.question,
|
|
120
|
-
"",
|
|
121
|
-
...summary.answers.map((answer, idx) => `${idx + 1}. ${answer}`),
|
|
122
|
-
];
|
|
123
|
-
return lines.join("\n");
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function buildTextContent(body: string): TextContent {
|
|
127
|
-
return {
|
|
128
|
-
"m.text": body,
|
|
129
|
-
"org.matrix.msc1767.text": body,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function buildPollFallbackText(question: string, answers: string[]): string {
|
|
134
|
-
if (answers.length === 0) {
|
|
135
|
-
return question;
|
|
136
|
-
}
|
|
137
|
-
return `${question}\n${answers.map((answer, idx) => `${idx + 1}. ${answer}`).join("\n")}`;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export function buildPollStartContent(poll: PollInput): PollStartContent {
|
|
141
|
-
const question = poll.question.trim();
|
|
142
|
-
const answers = poll.options
|
|
143
|
-
.map((option) => option.trim())
|
|
144
|
-
.filter((option) => option.length > 0)
|
|
145
|
-
.map((option, idx) => ({
|
|
146
|
-
id: `answer${idx + 1}`,
|
|
147
|
-
...buildTextContent(option),
|
|
148
|
-
}));
|
|
149
|
-
|
|
150
|
-
const isMultiple = (poll.maxSelections ?? 1) > 1;
|
|
151
|
-
const maxSelections = isMultiple ? Math.max(1, answers.length) : 1;
|
|
152
|
-
const fallbackText = buildPollFallbackText(
|
|
153
|
-
question,
|
|
154
|
-
answers.map((answer) => getTextContent(answer)),
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
return {
|
|
158
|
-
[M_POLL_START]: {
|
|
159
|
-
question: buildTextContent(question),
|
|
160
|
-
kind: isMultiple ? "m.poll.undisclosed" : "m.poll.disclosed",
|
|
161
|
-
max_selections: maxSelections,
|
|
162
|
-
answers,
|
|
163
|
-
},
|
|
164
|
-
"m.text": fallbackText,
|
|
165
|
-
"org.matrix.msc1767.text": fallbackText,
|
|
166
|
-
};
|
|
167
|
-
}
|