perf: optimize Matrix test boundaries

This commit is contained in:
Peter Steinberger
2026-04-17 16:04:02 +01:00
parent a861da41b5
commit 605cb60586
65 changed files with 578 additions and 170 deletions

View File

@@ -11,7 +11,7 @@ import {
} from "openclaw/plugin-sdk/account-id";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import { hasConfiguredSecretInput } from "openclaw/plugin-sdk/secret-input";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
import {
resolveMatrixAccountStringValues,
type MatrixResolvedStringField,

View File

@@ -1,5 +1,5 @@
import { Type } from "@sinclair/typebox";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
import { extractToolSend } from "openclaw/plugin-sdk/tool-send";
import { requiresExplicitMatrixDefaultAccount } from "./account-selection.js";
import { resolveDefaultMatrixAccountId, resolveMatrixAccount } from "./matrix/accounts.js";

View File

@@ -14,7 +14,7 @@ import type { ExecApprovalRequest, PluginApprovalRequest } from "openclaw/plugin
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalStringifiedId,
} from "openclaw/plugin-sdk/text-runtime";
} from "openclaw/plugin-sdk/string-coerce-runtime";
import { getMatrixApprovalAuthApprovers, matrixApprovalAuth } from "./approval-auth.js";
import { normalizeMatrixApproverId } from "./approval-ids.js";
import {

View File

@@ -24,11 +24,11 @@ import {
createComputedAccountStatusAdapter,
createDefaultChannelRuntimeState,
} from "openclaw/plugin-sdk/status-helpers";
import { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking";
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalString,
} from "openclaw/plugin-sdk/text-runtime";
} from "openclaw/plugin-sdk/string-coerce-runtime";
import { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking";
import { matrixMessageActions } from "./actions.js";
import { matrixApprovalCapability } from "./approval-native.js";
import { createMatrixPairingText, createMatrixProbeAccount } from "./channel-account-paths.js";

View File

@@ -1,6 +1,8 @@
import type { Command } from "commander";
import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
import { formatZonedTimestamp } from "openclaw/plugin-sdk/matrix-runtime-shared";
import type { ChannelSetupInput } from "openclaw/plugin-sdk/setup";
import { resolveMatrixAccount, resolveMatrixAccountConfig } from "./matrix/accounts.js";
import { withResolvedActionClient, withStartedActionClient } from "./matrix/actions/client.js";
import { listMatrixOwnDevices, pruneMatrixStaleGatewayDevices } from "./matrix/actions/devices.js";
import { updateMatrixOwnProfile } from "./matrix/actions/profile.js";
import {
@@ -16,14 +18,9 @@ import { resolveMatrixAuthContext } from "./matrix/client.js";
import { setMatrixSdkConsoleLogging, setMatrixSdkLogMode } from "./matrix/client/logging.js";
import { resolveMatrixConfigPath, updateMatrixAccountConfig } from "./matrix/config-update.js";
import { isOpenClawManagedMatrixDevice } from "./matrix/device-health.js";
import {
inspectMatrixDirectRooms,
repairMatrixDirectRooms,
type MatrixDirectRoomCandidate,
} from "./matrix/direct-management.js";
import type { MatrixDirectRoomCandidate } from "./matrix/direct-management.js";
import { formatMatrixErrorMessage } from "./matrix/errors.js";
import { applyMatrixProfileUpdate, type MatrixProfileUpdateResult } from "./profile-update.js";
import { formatZonedTimestamp, normalizeAccountId, type ChannelSetupInput } from "./runtime-api.js";
import { getMatrixRuntime } from "./runtime.js";
import { matrixSetupAdapter } from "./setup-core.js";
import type { CoreConfig } from "./types.js";
@@ -334,6 +331,10 @@ async function inspectMatrixDirectRoom(params: {
accountId: string;
userId: string;
}): Promise<MatrixCliDirectRoomInspection> {
const [{ withResolvedActionClient }, { inspectMatrixDirectRooms }] = await Promise.all([
import("./matrix/actions/client.js"),
import("./matrix/direct-management.js"),
]);
return await withResolvedActionClient(
{ accountId: params.accountId },
async (client) => {
@@ -361,6 +362,10 @@ async function repairMatrixDirectRoom(params: {
}): Promise<MatrixCliDirectRoomRepair> {
const cfg = getMatrixRuntime().config.loadConfig() as CoreConfig;
const account = resolveMatrixAccount({ cfg, accountId: params.accountId });
const [{ withStartedActionClient }, { repairMatrixDirectRooms }] = await Promise.all([
import("./matrix/actions/client.js"),
import("./matrix/direct-management.js"),
]);
return await withStartedActionClient({ accountId: params.accountId }, async (client) => {
const repaired = await repairMatrixDirectRooms({
client,

View File

@@ -1,7 +1,7 @@
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalString,
} from "openclaw/plugin-sdk/text-runtime";
} from "openclaw/plugin-sdk/string-coerce-runtime";
import { resolveMatrixAuth } from "./matrix/client.js";
import { MatrixAuthedHttpClient } from "./matrix/sdk/http-client.js";
import { isMatrixQualifiedUserId, normalizeMatrixMessagingTarget } from "./matrix/target-ids.js";

View File

@@ -11,7 +11,7 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import type { ExecApprovalRequest, PluginApprovalRequest } from "openclaw/plugin-sdk/infra-runtime";
import type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime";
import { normalizeAccountId } from "openclaw/plugin-sdk/routing";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
import { getMatrixApprovalAuthApprovers } from "./approval-auth.js";
import { normalizeMatrixApproverId } from "./approval-ids.js";
import { listMatrixAccountIds, resolveMatrixAccount } from "./matrix/accounts.js";

View File

@@ -1,6 +1,6 @@
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
import { hasConfiguredSecretInput } from "openclaw/plugin-sdk/secret-input";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
import {
resolveConfiguredMatrixAccountIds,
resolveMatrixDefaultOrOnlyAccountId,

View File

@@ -1,4 +1,4 @@
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
import { fetchMatrixPollMessageSummary, resolveMatrixPollRootEventId } from "../poll-summary.js";
import { isPollEventType } from "../poll-types.js";
import { editMessageMatrix, sendMessageMatrix } from "../send.js";

View File

@@ -1,4 +1,4 @@
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
import { getMatrixRuntime } from "../../runtime.js";
import type { CoreConfig } from "../../types.js";
import { formatMatrixEncryptionUnavailableError } from "../encryption-guidance.js";

View File

@@ -1,7 +1,10 @@
import { formatErrorMessage, type PinnedDispatcherPolicy } from "openclaw/plugin-sdk/infra-runtime";
import { coerceSecretRef } from "openclaw/plugin-sdk/provider-auth";
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import { retryAsync } from "openclaw/plugin-sdk/retry-runtime";
import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
import {
coerceSecretRef,
normalizeResolvedSecretInputString,
} from "openclaw/plugin-sdk/secret-input";
import type { PinnedDispatcherPolicy } from "openclaw/plugin-sdk/ssrf-dispatcher";
import {
requiresExplicitMatrixDefaultAccount,
resolveMatrixDefaultOrOnlyAccountId,

View File

@@ -1,6 +1,6 @@
import fs from "node:fs";
import type { PinnedDispatcherPolicy } from "openclaw/plugin-sdk/infra-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import type { PinnedDispatcherPolicy } from "openclaw/plugin-sdk/ssrf-dispatcher";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
import type { SsrFPolicy } from "../../runtime-api.js";
import type { MatrixClient } from "../sdk.js";
import { resolveValidatedMatrixHomeserverUrl } from "./config.js";

View File

@@ -1,6 +1,95 @@
export {
hasReadyMatrixEnvAuth,
resolveGlobalMatrixEnvConfig,
resolveMatrixEnvAuthReadiness,
resolveScopedMatrixEnvConfig,
} from "./config.js";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
import { getMatrixScopedEnvVarNames } from "../../env-vars.js";
type MatrixEnvConfig = {
homeserver: string;
userId: string;
accessToken?: string;
password?: string;
deviceId?: string;
deviceName?: string;
};
function cleanEnv(value: unknown): string {
return typeof value === "string" ? value.trim() : "";
}
export function resolveGlobalMatrixEnvConfig(env: NodeJS.ProcessEnv): MatrixEnvConfig {
return {
homeserver: cleanEnv(env.MATRIX_HOMESERVER),
userId: cleanEnv(env.MATRIX_USER_ID),
accessToken: cleanEnv(env.MATRIX_ACCESS_TOKEN) || undefined,
password: cleanEnv(env.MATRIX_PASSWORD) || undefined,
deviceId: cleanEnv(env.MATRIX_DEVICE_ID) || undefined,
deviceName: cleanEnv(env.MATRIX_DEVICE_NAME) || undefined,
};
}
export function hasReadyMatrixEnvAuth(config: {
homeserver?: string;
userId?: string;
accessToken?: string;
password?: string;
}): boolean {
const homeserver = cleanEnv(config.homeserver);
const userId = cleanEnv(config.userId);
const accessToken = cleanEnv(config.accessToken);
const password = cleanEnv(config.password);
return Boolean(homeserver && (accessToken || (userId && password)));
}
export function resolveScopedMatrixEnvConfig(
accountId: string,
env: NodeJS.ProcessEnv = process.env,
): MatrixEnvConfig {
const keys = getMatrixScopedEnvVarNames(accountId);
return {
homeserver: cleanEnv(env[keys.homeserver]),
userId: cleanEnv(env[keys.userId]),
accessToken: cleanEnv(env[keys.accessToken]) || undefined,
password: cleanEnv(env[keys.password]) || undefined,
deviceId: cleanEnv(env[keys.deviceId]) || undefined,
deviceName: cleanEnv(env[keys.deviceName]) || undefined,
};
}
export function resolveMatrixEnvAuthReadiness(
accountId: string,
env: NodeJS.ProcessEnv = process.env,
): {
ready: boolean;
homeserver?: string;
userId?: string;
sourceHint: string;
missingMessage: string;
} {
const normalizedAccountId = normalizeAccountId(accountId);
const scoped = resolveScopedMatrixEnvConfig(normalizedAccountId, env);
const scopedReady = hasReadyMatrixEnvAuth(scoped);
if (normalizedAccountId !== DEFAULT_ACCOUNT_ID) {
const keys = getMatrixScopedEnvVarNames(normalizedAccountId);
return {
ready: scopedReady,
homeserver: scoped.homeserver || undefined,
userId: scoped.userId || undefined,
sourceHint: `${keys.homeserver} (+ auth vars)`,
missingMessage: `Set per-account env vars for "${normalizedAccountId}" (for example ${keys.homeserver} + ${keys.accessToken} or ${keys.userId} + ${keys.password}).`,
};
}
const defaultScoped = resolveScopedMatrixEnvConfig(DEFAULT_ACCOUNT_ID, env);
const global = resolveGlobalMatrixEnvConfig(env);
const defaultScopedReady = hasReadyMatrixEnvAuth(defaultScoped);
const globalReady = hasReadyMatrixEnvAuth(global);
const defaultKeys = getMatrixScopedEnvVarNames(DEFAULT_ACCOUNT_ID);
return {
ready: defaultScopedReady || globalReady,
homeserver: defaultScoped.homeserver || global.homeserver || undefined,
userId: defaultScoped.userId || global.userId || undefined,
sourceHint: "MATRIX_* or MATRIX_DEFAULT_*",
missingMessage:
`Set Matrix env vars for the default account ` +
`(for example MATRIX_HOMESERVER + MATRIX_ACCESS_TOKEN, MATRIX_USER_ID + MATRIX_PASSWORD, ` +
`or ${defaultKeys.homeserver} + ${defaultKeys.accessToken}).`,
};
}

View File

@@ -1,5 +1,5 @@
import net from "node:net";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
function normalizeHost(host: string): string {
const normalized = normalizeLowercaseStringOrEmpty(host).replace(/\.+$/, "");

View File

@@ -1,4 +1,4 @@
import type { PinnedDispatcherPolicy } from "openclaw/plugin-sdk/infra-runtime";
import type { PinnedDispatcherPolicy } from "openclaw/plugin-sdk/ssrf-dispatcher";
import type { SsrFPolicy } from "../../runtime-api.js";
export type MatrixResolvedConfig = {

View File

@@ -0,0 +1,76 @@
import {
assertHttpUrlTargetsPrivateNetwork,
type LookupFn,
} from "openclaw/plugin-sdk/ssrf-runtime";
import { isPrivateOrLoopbackHost } from "./private-network-host.js";
const MATRIX_HTTP_HOMESERVER_ERROR =
"Matrix homeserver must use https:// unless it targets a private or loopback host";
function cleanString(value: unknown, requiredMessage: string): string {
const trimmed = typeof value === "string" ? value.trim() : "";
if (!trimmed) {
throw new Error(requiredMessage);
}
return trimmed;
}
export function validateMatrixHomeserverUrl(
homeserver: string,
opts?: { allowPrivateNetwork?: boolean },
): string {
const trimmed = cleanString(homeserver, "Matrix homeserver is required (matrix.homeserver)");
let parsed: URL;
try {
parsed = new URL(trimmed);
} catch {
throw new Error("Matrix homeserver must be a valid http(s) URL");
}
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
throw new Error("Matrix homeserver must use http:// or https://");
}
if (!parsed.hostname) {
throw new Error("Matrix homeserver must include a hostname");
}
if (parsed.username || parsed.password) {
throw new Error("Matrix homeserver URL must not include embedded credentials");
}
if (parsed.search || parsed.hash) {
throw new Error("Matrix homeserver URL must not include query strings or fragments");
}
if (
parsed.protocol === "http:" &&
opts?.allowPrivateNetwork !== true &&
!isPrivateOrLoopbackHost(parsed.hostname)
) {
throw new Error(MATRIX_HTTP_HOMESERVER_ERROR);
}
return trimmed;
}
export async function resolveValidatedMatrixHomeserverUrl(
homeserver: string,
opts?: {
dangerouslyAllowPrivateNetwork?: boolean;
allowPrivateNetwork?: boolean;
lookupFn?: LookupFn;
},
): Promise<string> {
const allowPrivateNetwork =
typeof opts?.dangerouslyAllowPrivateNetwork === "boolean"
? opts.dangerouslyAllowPrivateNetwork
: opts?.allowPrivateNetwork;
const normalized = validateMatrixHomeserverUrl(homeserver, {
allowPrivateNetwork,
});
await assertHttpUrlTargetsPrivateNetwork(normalized, {
dangerouslyAllowPrivateNetwork: opts?.dangerouslyAllowPrivateNetwork,
allowPrivateNetwork,
lookupFn: opts?.lookupFn,
errorMessage: MATRIX_HTTP_HOMESERVER_ERROR,
});
return normalized;
}

View File

@@ -1,5 +1,5 @@
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
import { coerceSecretRef } from "openclaw/plugin-sdk/config-runtime";
import { coerceSecretRef } from "openclaw/plugin-sdk/secret-ref-runtime";
import { normalizeSecretInputString } from "openclaw/plugin-sdk/setup";
import type { CoreConfig, MatrixConfig } from "../types.js";
import { findMatrixAccountConfig } from "./account-config.js";

View File

@@ -1,4 +1,4 @@
import { writeJsonFileAtomically } from "../runtime-api.js";
import { writeJsonFileAtomically } from "openclaw/plugin-sdk/json-store";
import { createAsyncLock, type AsyncLock } from "./async-lock.js";
import { loadMatrixCredentials, resolveMatrixCredentialsPath } from "./credentials-read.js";
import type { MatrixStoredCredentials } from "./credentials-read.js";

View File

@@ -4,7 +4,7 @@ import { createRequire } from "node:module";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import type { RuntimeEnv } from "../runtime-api.js";
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime";
const REQUIRED_MATRIX_PACKAGES = [
"matrix-js-sdk",

View File

@@ -1,5 +1,5 @@
import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
import { inspectMatrixDirectRoomEvidence } from "./direct-room.js";
import type { MatrixClient } from "./sdk.js";
import { EventType, type MatrixDirectAccountData } from "./send/types.js";

View File

@@ -1,15 +1,134 @@
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import type { PluginRuntime } from "../runtime-api.js";
const loadConfigMock = vi.fn(() => ({}));
const resolveTextChunkLimitMock = vi.fn<
(cfg: unknown, channel: unknown, accountId?: unknown) => number
>(() => 4000);
const resolveChunkModeMock = vi.fn<(cfg: unknown, channel: unknown, accountId?: unknown) => string>(
() => "length",
);
const chunkMarkdownTextWithModeMock = vi.fn((text: string) => (text ? [text] : []));
const convertMarkdownTablesMock = vi.fn((text: string) => text);
const sendModuleMocks = vi.hoisted(() => {
const loadConfigMock = vi.fn(() => ({}));
const resolveTextChunkLimitMock = vi.fn<
(cfg: unknown, channel: unknown, accountId?: unknown) => number
>(() => 4000);
const resolveChunkModeMock = vi.fn<
(cfg: unknown, channel: unknown, accountId?: unknown) => string
>(() => "length");
const chunkMarkdownTextWithModeMock = vi.fn((text: string) => (text ? [text] : []));
const convertMarkdownTablesMock = vi.fn((text: string) => text);
const prepareMatrixSingleText = vi.fn(
(text: string, opts: { cfg?: unknown; accountId?: string } = {}) => {
const trimmedText = text.trim();
const convertedText = convertMarkdownTablesMock(trimmedText);
const singleEventLimit = Math.min(
resolveTextChunkLimitMock(opts.cfg ?? {}, "matrix", opts.accountId),
4000,
);
return {
trimmedText,
convertedText,
singleEventLimit,
fitsInSingleEvent: convertedText.length <= singleEventLimit,
};
},
);
const sendSingleTextMessageMatrix = vi.fn(
async (
roomId: string,
text: string,
opts: {
client?: {
sendMessage: (roomId: string, content: Record<string, unknown>) => Promise<string>;
};
cfg?: unknown;
accountId?: string;
msgtype?: string;
includeMentions?: boolean;
live?: boolean;
} = {},
) => {
const prepared = prepareMatrixSingleText(text, {
cfg: opts.cfg,
accountId: opts.accountId,
});
if (!prepared.trimmedText) {
throw new Error("Matrix single-message send requires text");
}
if (!prepared.fitsInSingleEvent) {
throw new Error("Matrix single-message text exceeds limit");
}
const content: Record<string, unknown> = {
msgtype: opts.msgtype ?? "m.text",
body: prepared.convertedText,
};
if (opts.live) {
content["org.matrix.msc4357.live"] = {};
}
const eventId = await opts.client?.sendMessage(roomId, content);
return {
messageId: eventId ?? "unknown",
roomId,
primaryMessageId: eventId ?? "unknown",
messageIds: eventId ? [eventId] : [],
};
},
);
const editMessageMatrix = vi.fn(
async (
roomId: string,
originalEventId: string,
newText: string,
opts: {
client?: {
sendMessage: (roomId: string, content: Record<string, unknown>) => Promise<string>;
};
msgtype?: string;
live?: boolean;
} = {},
) => {
const convertedText = convertMarkdownTablesMock(newText);
const newContent: Record<string, unknown> = {
msgtype: opts.msgtype ?? "m.text",
body: convertedText,
};
if (opts.live) {
newContent["org.matrix.msc4357.live"] = {};
}
const content: Record<string, unknown> = {
...newContent,
body: `* ${convertedText}`,
"m.new_content": newContent,
"m.relates_to": {
rel_type: "m.replace",
event_id: originalEventId,
},
};
if (opts.live) {
content["org.matrix.msc4357.live"] = {};
}
return (await opts.client?.sendMessage(roomId, content)) ?? "";
},
);
return {
chunkMarkdownTextWithModeMock,
convertMarkdownTablesMock,
editMessageMatrix,
loadConfigMock,
prepareMatrixSingleText,
resolveChunkModeMock,
resolveTextChunkLimitMock,
sendSingleTextMessageMatrix,
};
});
const {
chunkMarkdownTextWithModeMock,
convertMarkdownTablesMock,
loadConfigMock,
resolveChunkModeMock,
resolveTextChunkLimitMock,
} = sendModuleMocks;
vi.mock("./send.js", () => ({
editMessageMatrix: sendModuleMocks.editMessageMatrix,
prepareMatrixSingleText: sendModuleMocks.prepareMatrixSingleText,
sendSingleTextMessageMatrix: sendModuleMocks.sendSingleTextMessageMatrix,
}));
const runtimeStub = {
config: { loadConfig: () => loadConfigMock() },
channel: {

View File

@@ -1,5 +1,5 @@
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
export function formatMatrixErrorMessage(err: unknown): string {
return formatErrorMessage(err);

View File

@@ -1,8 +1,6 @@
import MarkdownIt from "markdown-it";
import {
isAutoLinkedFileRef,
normalizeLowercaseStringOrEmpty,
} from "openclaw/plugin-sdk/text-runtime";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
import { isAutoLinkedFileRef } from "openclaw/plugin-sdk/text-autolink-runtime";
import type { MatrixClient } from "./sdk.js";
import { isMatrixQualifiedUserId } from "./target-ids.js";

View File

@@ -1,6 +1,7 @@
import { resolveAckReaction } from "openclaw/plugin-sdk/channel-feedback";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import type { CoreConfig } from "../../types.js";
import { resolveMatrixAccountConfig } from "../account-config.js";
import { resolveAckReaction, type OpenClawConfig } from "./runtime-api.js";
type MatrixAckReactionScope = "group-mentions" | "group-all" | "direct" | "all" | "none" | "off";

View File

@@ -2,8 +2,8 @@ import {
resolveAllowlistMatchByCandidates,
type AllowlistMatch,
} from "openclaw/plugin-sdk/allow-from";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
import { normalizeStringEntries } from "openclaw/plugin-sdk/string-normalization-runtime";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
function normalizeAllowList(list?: Array<string | number>) {
return normalizeStringEntries(list);

View File

@@ -1,4 +1,4 @@
import { normalizeStringifiedOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { normalizeStringifiedOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
import { getMatrixRuntime } from "../../runtime.js";
import type { MatrixConfig } from "../../types.js";
import type { MatrixClient } from "../sdk.js";

View File

@@ -1,4 +1,4 @@
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
import type { PluginRuntime, RuntimeLogger } from "../../runtime-api.js";
import type { CoreConfig } from "../../types.js";
import type { MatrixAuth } from "../client.js";

View File

@@ -25,6 +25,35 @@ import {
} from "./handler.test-helpers.js";
import { type MatrixRawEvent } from "./types.js";
const deliverMatrixRepliesMock = vi.hoisted(() => vi.fn(async () => true));
vi.mock("./replies.js", () => ({
deliverMatrixReplies: deliverMatrixRepliesMock,
}));
vi.mock("./route.js", () => ({
resolveMatrixInboundRoute: (params: {
resolveAgentRoute: (input: unknown) => unknown;
cfg: unknown;
accountId: string;
roomId: string;
senderId: string;
isDirectMessage: boolean;
}) => ({
route: params.resolveAgentRoute({
cfg: params.cfg,
channel: "matrix",
accountId: params.accountId,
peer: {
kind: params.isDirectMessage ? "direct" : "channel",
id: params.isDirectMessage ? params.senderId : params.roomId,
},
}),
configuredBinding: null,
runtimeBindingId: null,
}),
}));
const DEFAULT_ROOM = "!room:example.org";
function makeRoomTriggerEvent(params: { eventId: string; body: string; ts?: number }) {

View File

@@ -1,19 +1,19 @@
import { resolveControlCommandGate } from "openclaw/plugin-sdk/command-auth";
import { resolveControlCommandGate } from "openclaw/plugin-sdk/command-gating";
import {
evaluateSupplementalContextVisibility,
resolveChannelContextVisibilityMode,
} from "openclaw/plugin-sdk/context-visibility-runtime";
import {
loadSessionStore,
resolveChannelContextVisibilityMode,
resolveSessionStoreEntry,
} from "openclaw/plugin-sdk/config-runtime";
import { getSessionBindingService } from "openclaw/plugin-sdk/conversation-runtime";
import { evaluateSupplementalContextVisibility } from "openclaw/plugin-sdk/security-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
} from "openclaw/plugin-sdk/session-store-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
import type {
CoreConfig,
MatrixRoomConfig,
MatrixStreamingMode,
ReplyToMode,
} from "../../types.js";
import { createMatrixDraftStream } from "../draft-stream.js";
import { formatMatrixErrorMessage } from "../errors.js";
import { isMatrixMediaSizeLimitError } from "../media-errors.js";
import {
@@ -31,13 +31,6 @@ import {
parsePollStartContent,
} from "../poll-types.js";
import type { LocationMessageEventContent, MatrixClient } from "../sdk.js";
import {
editMessageMatrix,
reactMatrixMessage,
sendMessageMatrix,
sendReadReceiptMatrix,
sendTypingMatrix,
} from "../send.js";
import { MATRIX_OPENCLAW_FINALIZED_PREVIEW_KEY } from "../send/types.js";
import { resolveMatrixStoredSessionMeta } from "../session-store-metadata.js";
import { resolveMatrixMonitorAccessState } from "./access-state.js";
@@ -47,7 +40,6 @@ import type { MatrixInboundEventDeduper } from "./inbound-dedupe.js";
import { resolveMatrixLocation, type MatrixLocationPayload } from "./location.js";
import { downloadMatrixMedia } from "./media.js";
import { resolveMentions } from "./mentions.js";
import { handleInboundMatrixReaction } from "./reaction-events.js";
import { deliverMatrixReplies } from "./replies.js";
import { createMatrixReplyContextResolver } from "./reply-context.js";
import { createRoomHistoryTracker } from "./room-history.js";
@@ -57,7 +49,6 @@ import { resolveMatrixInboundRoute } from "./route.js";
import {
createReplyPrefixOptions,
createTypingCallbacks,
ensureConfiguredAcpBindingReady,
formatAllowlistMatchMeta,
getAgentScopedMediaLocalRoots,
logInboundDrop,
@@ -80,9 +71,44 @@ import { isMatrixVerificationRoomMessage } from "./verification-utils.js";
const ALLOW_FROM_STORE_CACHE_TTL_MS = 30_000;
const PAIRING_REPLY_COOLDOWN_MS = 5 * 60_000;
let matrixSendModulePromise: Promise<typeof import("../send.js")> | undefined;
let acpBindingRuntimePromise:
| Promise<typeof import("openclaw/plugin-sdk/acp-binding-runtime")>
| undefined;
let sessionBindingRuntimePromise:
| Promise<typeof import("openclaw/plugin-sdk/session-binding-runtime")>
| undefined;
function loadMatrixSendModule(): Promise<typeof import("../send.js")> {
matrixSendModulePromise ??= import("../send.js");
return matrixSendModulePromise;
}
function loadAcpBindingRuntime(): Promise<
typeof import("openclaw/plugin-sdk/acp-binding-runtime")
> {
acpBindingRuntimePromise ??= import("openclaw/plugin-sdk/acp-binding-runtime");
return acpBindingRuntimePromise;
}
function loadSessionBindingRuntime(): Promise<
typeof import("openclaw/plugin-sdk/session-binding-runtime")
> {
sessionBindingRuntimePromise ??= import("openclaw/plugin-sdk/session-binding-runtime");
return sessionBindingRuntimePromise;
}
const MAX_TRACKED_PAIRING_REPLY_SENDERS = 512;
const MAX_TRACKED_SHARED_DM_CONTEXT_NOTICES = 512;
type MatrixAllowBotsMode = "off" | "mentions" | "all";
type MatrixDraftStreamHandle = {
update: (text: string) => void;
stop: () => Promise<string | undefined>;
eventId: () => string | undefined;
mustDeliverFinalNormally: () => boolean;
matchesPreparedText: (text: string) => boolean;
finalizeLive: () => Promise<boolean>;
reset: () => void;
};
export class MatrixRetryableInboundError extends Error {
constructor(message: string, options?: ErrorOptions) {
@@ -426,7 +452,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
return async (roomId: string, event: MatrixRawEvent) => {
const eventId = typeof event.event_id === "string" ? event.event_id.trim() : "";
let claimedInboundEvent = false;
let draftStreamRef: ReturnType<typeof createMatrixDraftStream> | undefined;
let draftStreamRef: MatrixDraftStreamHandle | undefined;
let draftConsumed = false;
try {
const eventType = event.type;
@@ -645,6 +671,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
: `matrix pairing reminder sender=${senderId} name=${senderName ?? "unknown"} (${allowMatchMeta})`,
);
try {
const { sendMessageMatrix } = await loadMatrixSendModule();
await sendMessageMatrix(
`room:${roomId}`,
created
@@ -712,6 +739,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
if (isReactionEvent) {
const senderName = await getSenderName();
const { handleInboundMatrixReaction } = await import("./reaction-events.js");
await handleInboundMatrixReaction({
client,
core,
@@ -957,6 +985,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
}
const senderName = await getSenderName();
if (_configuredBinding) {
const { ensureConfiguredAcpBindingReady } = await loadAcpBindingRuntime();
const ensured = await ensureConfiguredAcpBindingReady({
cfg,
configuredBinding: _configuredBinding,
@@ -972,6 +1001,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
}
}
if (_runtimeBindingId) {
const { getSessionBindingService } = await loadSessionBindingRuntime();
getSessionBindingService().touch(_runtimeBindingId, eventTs ?? undefined);
}
const preparedTrigger =
@@ -1270,17 +1300,23 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
}),
);
if (shouldAckReaction() && _messageId) {
reactMatrixMessage(roomId, _messageId, ackReaction, client).catch((err) => {
logVerboseMessage(`matrix react failed for room ${roomId}: ${String(err)}`);
});
loadMatrixSendModule()
.then(({ reactMatrixMessage }) =>
reactMatrixMessage(roomId, _messageId, ackReaction, client),
)
.catch((err) => {
logVerboseMessage(`matrix react failed for room ${roomId}: ${String(err)}`);
});
}
if (_messageId) {
sendReadReceiptMatrix(roomId, _messageId, client).catch((err) => {
logVerboseMessage(
`matrix: read receipt failed room=${roomId} id=${_messageId}: ${String(err)}`,
);
});
loadMatrixSendModule()
.then(({ sendReadReceiptMatrix }) => sendReadReceiptMatrix(roomId, _messageId, client))
.catch((err) => {
logVerboseMessage(
`matrix: read receipt failed room=${roomId} id=${_messageId}: ${String(err)}`,
);
});
}
const tableMode = core.channel.text.resolveMarkdownTableMode({
@@ -1299,8 +1335,14 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
accountId: _route.accountId,
});
const typingCallbacks = createTypingCallbacks({
start: () => sendTypingMatrix(roomId, true, undefined, client),
stop: () => sendTypingMatrix(roomId, false, undefined, client),
start: async () => {
const { sendTypingMatrix } = await loadMatrixSendModule();
await sendTypingMatrix(roomId, true, undefined, client);
},
stop: async () => {
const { sendTypingMatrix } = await loadMatrixSendModule();
await sendTypingMatrix(roomId, false, undefined, client);
},
onStartError: (err) => {
logTypingFailure({
log: logVerboseMessage,
@@ -1323,18 +1365,20 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
const draftStreamingEnabled = streaming !== "off";
const quietDraftStreaming = streaming === "quiet";
const draftReplyToId = replyToMode !== "off" && !threadTarget ? _messageId : undefined;
const draftStream = draftStreamingEnabled
? createMatrixDraftStream({
roomId,
client,
cfg,
mode: quietDraftStreaming ? "quiet" : "partial",
threadId: threadTarget,
replyToId: draftReplyToId,
preserveReplyId: replyToMode === "all",
accountId: _route.accountId,
log: logVerboseMessage,
})
const draftStream: MatrixDraftStreamHandle | undefined = draftStreamingEnabled
? await import("../draft-stream.js").then(({ createMatrixDraftStream }) =>
createMatrixDraftStream({
roomId,
client,
cfg,
mode: quietDraftStreaming ? "quiet" : "partial",
threadId: threadTarget,
replyToId: draftReplyToId,
preserveReplyId: replyToMode === "all",
accountId: _route.accountId,
log: logVerboseMessage,
}),
)
: undefined;
draftStreamRef = draftStream;
type PendingDraftBoundary = {
@@ -1458,6 +1502,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
const requiresFinalEdit =
quietDraftStreaming || !draftStream.matchesPreparedText(payload.text);
if (requiresFinalEdit) {
const { editMessageMatrix } = await loadMatrixSendModule();
await editMessageMatrix(roomId, draftEventId, payload.text, {
client,
cfg,
@@ -1500,6 +1545,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
quietDraftStreaming ||
(typeof payloadText === "string" && !payloadTextMatchesDraft);
if (textEditOk && payloadText && requiresFinalTextEdit) {
const { editMessageMatrix } = await loadMatrixSendModule();
textEditOk = await editMessageMatrix(roomId, draftEventId, payloadText, {
client,
cfg,
@@ -1568,6 +1614,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
// Re-assert typing so the user still sees the indicator while
// the next block generates.
const { sendTypingMatrix } = await loadMatrixSendModule();
await sendTypingMatrix(roomId, true, undefined, client).catch(() => {});
}
} else {

View File

@@ -1,7 +1,7 @@
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalString,
} from "openclaw/plugin-sdk/text-runtime";
} from "openclaw/plugin-sdk/string-coerce-runtime";
import type { LocationMessageEventContent } from "../sdk.js";
import { formatLocationText, toLocationContext, type NormalizedLocation } from "./runtime-api.js";
import { EventType } from "./types.js";

View File

@@ -1,4 +1,4 @@
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
import { getMatrixRuntime } from "../../runtime.js";
import type { RoomMessageEventContent } from "./types.js";

View File

@@ -1,4 +1,4 @@
import { getSessionBindingService } from "openclaw/plugin-sdk/conversation-runtime";
import { getSessionBindingService } from "openclaw/plugin-sdk/session-binding-runtime";
import { matrixApprovalCapability } from "../../approval-native.js";
import {
resolveMatrixApprovalReactionTarget,

View File

@@ -1,4 +1,4 @@
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
import { getMatrixRuntime } from "../../runtime.js";
import type { MatrixClient } from "../sdk.js";
import { chunkMatrixText, sendMessageMatrix } from "../send.js";

View File

@@ -1,10 +1,11 @@
import { buildAgentSessionKey, deriveLastRoutePolicy } from "openclaw/plugin-sdk/routing";
import { resolveConfiguredAcpBindingRecord } from "openclaw/plugin-sdk/acp-binding-resolve-runtime";
import type { PluginRuntime } from "openclaw/plugin-sdk/plugin-runtime";
import {
getSessionBindingService,
buildAgentSessionKey,
deriveLastRoutePolicy,
resolveAgentIdFromSessionKey,
resolveConfiguredAcpBindingRecord,
type PluginRuntime,
} from "../../runtime-api.js";
} from "openclaw/plugin-sdk/routing";
import { getSessionBindingService } from "openclaw/plugin-sdk/session-binding-runtime";
import type { CoreConfig } from "../../types.js";
import { resolveMatrixThreadSessionKeys } from "./threads.js";

View File

@@ -2,8 +2,7 @@
// Keep monitor internals off the broad package runtime-api barrel so monitor
// tests and shared workers do not pull unrelated Matrix helper surfaces.
export { ensureConfiguredAcpBindingReady } from "openclaw/plugin-sdk/acp-binding-runtime";
export type { NormalizedLocation } from "openclaw/plugin-sdk/channel-inbound";
export type { NormalizedLocation } from "openclaw/plugin-sdk/channel-location";
export type { PluginRuntime, RuntimeLogger } from "openclaw/plugin-sdk/plugin-runtime";
export type { BlockReplyContext, ReplyPayload } from "openclaw/plugin-sdk/reply-runtime";
export type { MarkdownTableMode, OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
@@ -18,13 +17,10 @@ export {
} from "openclaw/plugin-sdk/allow-from";
export { createReplyPrefixOptions } from "openclaw/plugin-sdk/channel-reply-pipeline";
export { createTypingCallbacks } from "openclaw/plugin-sdk/channel-reply-pipeline";
export {
formatLocationText,
logInboundDrop,
toLocationContext,
} from "openclaw/plugin-sdk/channel-inbound";
export { formatLocationText, toLocationContext } from "openclaw/plugin-sdk/channel-location";
export { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/agent-media-payload";
export { logTypingFailure, resolveAckReaction } from "openclaw/plugin-sdk/channel-feedback";
export { logInboundDrop, logTypingFailure } from "openclaw/plugin-sdk/channel-logging";
export { resolveAckReaction } from "openclaw/plugin-sdk/channel-feedback";
export {
buildChannelKeyCandidates,
resolveChannelEntryMatch,

View File

@@ -1,4 +1,4 @@
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
const VERIFICATION_EVENT_PREFIX = "m.key.verification.";
const VERIFICATION_REQUEST_MSGTYPE = "m.key.verification.request";

View File

@@ -1 +1 @@
export { loadOutboundMediaFromUrl } from "../runtime-api.js";
export { loadOutboundMediaFromUrl } from "openclaw/plugin-sdk/outbound-media";

View File

@@ -7,8 +7,8 @@
* - m.poll.end - Closes a poll
*/
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { normalizePollInput, type PollInput } from "../runtime-api.js";
import { normalizePollInput, type PollInput } from "openclaw/plugin-sdk/poll-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
export const M_POLL_START = "m.poll.start" as const;
export const M_POLL_RESPONSE = "m.poll.response" as const;

View File

@@ -1,5 +1,5 @@
import { formatErrorMessage, type PinnedDispatcherPolicy } from "openclaw/plugin-sdk/infra-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
import type { SsrFPolicy } from "../runtime-api.js";
import type { BaseProbeResult } from "../runtime-api.js";
import { isBunRuntime } from "./client/runtime.js";

View File

@@ -1,7 +1,7 @@
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalString,
} from "openclaw/plugin-sdk/text-runtime";
} from "openclaw/plugin-sdk/string-coerce-runtime";
import type { MatrixClient } from "./sdk.js";
export const MATRIX_PROFILE_AVATAR_MAX_BYTES = 10 * 1024 * 1024;

View File

@@ -1,4 +1,4 @@
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
export const MATRIX_ANNOTATION_RELATION_TYPE = "m.annotation";
export const MATRIX_REACTION_EVENT_TYPE = "m.reaction";

View File

@@ -10,9 +10,9 @@ import {
type MatrixEvent,
} from "matrix-js-sdk/lib/matrix.js";
import { VerificationMethod } from "matrix-js-sdk/lib/types.js";
import type { PinnedDispatcherPolicy } from "openclaw/plugin-sdk/infra-runtime";
import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
import { normalizeNullableString } from "openclaw/plugin-sdk/text-runtime";
import type { PinnedDispatcherPolicy } from "openclaw/plugin-sdk/ssrf-dispatcher";
import { normalizeNullableString } from "openclaw/plugin-sdk/string-coerce-runtime";
import type { SsrFPolicy } from "../runtime-api.js";
import { resolveMatrixRoomKeyBackupReadinessError } from "./backup-health.js";
import { FileBackedMatrixSyncStore } from "./client/file-sync-store.js";

View File

@@ -1,4 +1,4 @@
import type { PinnedDispatcherPolicy } from "openclaw/plugin-sdk/infra-runtime";
import type { PinnedDispatcherPolicy } from "openclaw/plugin-sdk/ssrf-dispatcher";
import type { SsrFPolicy } from "../../runtime-api.js";
import { buildHttpError } from "./event-helpers.js";
import { type HttpMethod, type QueryParams, performMatrixRequest } from "./transport.js";

View File

@@ -1,4 +1,4 @@
import type { FileLockOptions } from "openclaw/plugin-sdk/infra-runtime";
import type { FileLockOptions } from "openclaw/plugin-sdk/file-lock";
export const MATRIX_IDB_PERSIST_INTERVAL_MS = 60_000;

View File

@@ -15,9 +15,9 @@ const { withFileLockMock } = vi.hoisted(() => ({
),
}));
vi.mock("openclaw/plugin-sdk/infra-runtime", async () => {
const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/infra-runtime")>(
"openclaw/plugin-sdk/infra-runtime",
vi.mock("openclaw/plugin-sdk/file-lock", async () => {
const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/file-lock")>(
"openclaw/plugin-sdk/file-lock",
);
return {
...actual,

View File

@@ -1,7 +1,7 @@
import fs from "node:fs";
import path from "node:path";
import { indexedDB as fakeIndexedDB } from "fake-indexeddb";
import { withFileLock } from "openclaw/plugin-sdk/infra-runtime";
import { withFileLock } from "openclaw/plugin-sdk/file-lock";
import { MATRIX_IDB_SNAPSHOT_LOCK_OPTIONS } from "./idb-persistence-lock.js";
import { LogService } from "./logger.js";

View File

@@ -1,4 +1,4 @@
import { readResponseWithLimit as readSharedResponseWithLimit } from "openclaw/plugin-sdk/media-runtime";
import { readResponseWithLimit as readSharedResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
export async function readResponseWithLimit(
res: Response,

View File

@@ -0,0 +1,49 @@
import { fetchWithRuntimeDispatcher } from "openclaw/plugin-sdk/runtime-fetch";
import {
closeDispatcher,
createPinnedDispatcher,
resolvePinnedHostnameWithPolicy,
type PinnedDispatcherPolicy,
type SsrFPolicy,
} from "openclaw/plugin-sdk/ssrf-dispatcher";
export {
closeDispatcher,
createPinnedDispatcher,
fetchWithRuntimeDispatcher,
resolvePinnedHostnameWithPolicy,
type PinnedDispatcherPolicy,
type SsrFPolicy,
};
export function buildTimeoutAbortSignal(params: { timeoutMs?: number; signal?: AbortSignal }): {
signal?: AbortSignal;
cleanup: () => void;
} {
const { timeoutMs, signal } = params;
if (!timeoutMs && !signal) {
return { signal: undefined, cleanup: () => {} };
}
if (!timeoutMs) {
return { signal, cleanup: () => {} };
}
const controller = new AbortController();
const timeoutId = setTimeout(controller.abort.bind(controller), timeoutMs);
const onAbort = () => controller.abort();
if (signal) {
if (signal.aborted) {
controller.abort();
} else {
signal.addEventListener("abort", onAbort, { once: true });
}
}
return {
signal: controller.signal,
cleanup: () => {
clearTimeout(timeoutId);
signal?.removeEventListener("abort", onAbort);
},
};
}

View File

@@ -1,16 +1,14 @@
import {
fetchWithRuntimeDispatcher,
type PinnedDispatcherPolicy,
} from "openclaw/plugin-sdk/infra-runtime";
import { MatrixMediaSizeLimitError } from "../media-errors.js";
import { readResponseWithLimit } from "./read-response-with-limit.js";
import {
buildTimeoutAbortSignal,
closeDispatcher,
createPinnedDispatcher,
resolvePinnedHostnameWithPolicy,
type SsrFPolicy,
} from "../../runtime-api.js";
import { MatrixMediaSizeLimitError } from "../media-errors.js";
import { readResponseWithLimit } from "./read-response-with-limit.js";
fetchWithRuntimeDispatcher,
type PinnedDispatcherPolicy,
} from "./transport-runtime-api.js";
export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";

View File

@@ -1,7 +1,7 @@
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalStringifiedId,
} from "openclaw/plugin-sdk/text-runtime";
} from "openclaw/plugin-sdk/string-coerce-runtime";
import { inspectMatrixDirectRooms, persistMatrixDirectRoomMapping } from "../direct-management.js";
import { isStrictDirectRoom } from "../direct-room.js";
import type { MatrixClient } from "../sdk.js";

View File

@@ -4,7 +4,7 @@ import {
type SessionBindingRecord,
} from "openclaw/plugin-sdk/conversation-binding-runtime";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
import { findMatrixAccountConfig, resolveMatrixBaseConfig } from "./account-config.js";
import { resolveMatrixTargetIdentity } from "./target-ids.js";
import {

View File

@@ -1,4 +1,4 @@
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
type MatrixTarget = { kind: "room"; id: string } | { kind: "user"; id: string };
const MATRIX_PREFIX = "matrix:";

View File

@@ -1,7 +1,7 @@
import path from "node:path";
import { readJsonFileWithFallback, writeJsonFileAtomically } from "openclaw/plugin-sdk/json-store";
import { resolveAgentIdFromSessionKey } from "openclaw/plugin-sdk/routing";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
import {
registerSessionBindingAdapter,
resolveThreadBindingFarewellText,

View File

@@ -3,7 +3,7 @@ import os from "node:os";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
import {
findMatrixAccountEntry,
requiresExplicitMatrixDefaultAccount,

View File

@@ -2,8 +2,7 @@ import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { writeJsonFileAtomically } from "openclaw/plugin-sdk/json-store";
import { resolveRequiredHomeDir } from "openclaw/plugin-sdk/provider-auth";
import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
import { resolveRequiredHomeDir, resolveStateDir } from "openclaw/plugin-sdk/state-paths";
const MATRIX_MIGRATION_SNAPSHOT_DIRNAME = "openclaw-migrations";

View File

@@ -1,44 +1,40 @@
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
import type { DmPolicy } from "openclaw/plugin-sdk/config-runtime";
import type { WizardPrompter } from "openclaw/plugin-sdk/matrix-runtime-shared";
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime";
import {
type ChannelSetupDmPolicy,
type ChannelSetupWizardAdapter,
addWildcardAllowFrom,
formatDocsLink,
hasConfiguredSecretInput,
mergeAllowFromEntries,
normalizeAccountId,
promptAccountId,
promptChannelAccessConfig,
splitSetupEntries,
} from "openclaw/plugin-sdk/setup";
import { isPrivateNetworkOptInEnabled } from "openclaw/plugin-sdk/ssrf-runtime";
import { isPrivateNetworkOptInEnabled } from "openclaw/plugin-sdk/ssrf-policy";
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalString,
normalizeStringifiedOptionalString,
} from "openclaw/plugin-sdk/text-runtime";
} from "openclaw/plugin-sdk/string-coerce-runtime";
import { requiresExplicitMatrixDefaultAccount } from "./account-selection.js";
import { listMatrixDirectoryGroupsLive } from "./directory-live.js";
import {
listMatrixAccountIds,
resolveDefaultMatrixAccountId,
resolveMatrixAccount,
resolveMatrixAccountConfig,
} from "./matrix/accounts.js";
import { resolveMatrixEnvAuthReadiness } from "./matrix/client/env-auth.js";
import { isPrivateOrLoopbackHost } from "./matrix/client/private-network-host.js";
import {
resolveValidatedMatrixHomeserverUrl,
validateMatrixHomeserverUrl,
} from "./matrix/client.js";
import { resolveMatrixEnvAuthReadiness } from "./matrix/client/env-auth.js";
} from "./matrix/client/url-validation.js";
import { resolveMatrixConfigFieldPath, updateMatrixAccountConfig } from "./matrix/config-update.js";
import { ensureMatrixSdkInstalled, isMatrixSdkAvailable } from "./matrix/deps.js";
import { resolveMatrixTargets } from "./resolve-targets.js";
import type { DmPolicy } from "./runtime-api.js";
import {
addWildcardAllowFrom,
formatDocsLink,
hasConfiguredSecretInput,
isPrivateOrLoopbackHost,
mergeAllowFromEntries,
normalizeAccountId,
promptAccountId,
promptChannelAccessConfig,
splitSetupEntries,
type RuntimeEnv,
type WizardPrompter,
} from "./runtime-api.js";
import { moveSingleMatrixAccountConfigToNamedAccount } from "./setup-config.js";
import type { CoreConfig, MatrixConfig } from "./types.js";
@@ -161,6 +157,7 @@ async function promptMatrixAllowFrom(params: {
}
if (pending.length > 0) {
const { resolveMatrixTargets } = await import("./resolve-targets.js");
const results = await resolveMatrixTargets({
cfg,
accountId,
@@ -362,6 +359,7 @@ async function configureMatrixAccessPrompts(params: {
resolvedIds.push(cleaned);
continue;
}
const { listMatrixDirectoryGroupsLive } = await import("./directory-live.js");
const matches = await listMatrixDirectoryGroupsLive({
cfg: next,
accountId: params.accountId,

View File

@@ -1,5 +1,5 @@
import type { GatewayRequestHandlerOptions } from "openclaw/plugin-sdk/gateway-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
import { formatMatrixErrorMessage } from "./matrix/errors.js";
function sendError(respond: (ok: boolean, payload?: unknown) => void, err: unknown) {

View File

@@ -1,3 +1,3 @@
import { isRecord } from "openclaw/plugin-sdk/text-runtime";
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
export { isRecord };

View File

@@ -1,4 +1,4 @@
import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/text-runtime";
import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
import { listMatrixDirectoryGroupsLive, listMatrixDirectoryPeersLive } from "./directory-live.js";
import { isMatrixQualifiedUserId, normalizeMatrixMessagingTarget } from "./matrix/target-ids.js";
import type {

View File

@@ -29,11 +29,11 @@ export type {
} from "openclaw/plugin-sdk/channel-contract";
export {
formatLocationText,
logInboundDrop,
toLocationContext,
type NormalizedLocation,
} from "openclaw/plugin-sdk/channel-inbound";
export { resolveAckReaction, logTypingFailure } from "openclaw/plugin-sdk/channel-feedback";
} from "openclaw/plugin-sdk/channel-location";
export { logInboundDrop, logTypingFailure } from "openclaw/plugin-sdk/channel-logging";
export { resolveAckReaction } from "openclaw/plugin-sdk/channel-feedback";
export type { ChannelSetupInput } from "openclaw/plugin-sdk/setup";
export type {
OpenClawConfig,
@@ -92,7 +92,7 @@ export { resolveAgentIdFromSessionKey } from "openclaw/plugin-sdk/routing";
export { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking";
export { createChannelReplyPipeline } from "openclaw/plugin-sdk/channel-reply-pipeline";
export { loadOutboundMediaFromUrl } from "openclaw/plugin-sdk/outbound-media";
export { normalizePollInput, type PollInput } from "openclaw/plugin-sdk/media-runtime";
export { normalizePollInput, type PollInput } from "openclaw/plugin-sdk/poll-runtime";
export { writeJsonFileAtomically } from "openclaw/plugin-sdk/json-store";
export {
buildChannelKeyCandidates,

View File

@@ -5,7 +5,7 @@ import {
normalizeSecretInputString,
type ChannelSetupInput,
} from "openclaw/plugin-sdk/setup";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
import { resolveMatrixEnvAuthReadiness } from "./matrix/client/env-auth.js";
import { updateMatrixAccountConfig } from "./matrix/config-update.js";
import { isSupportedMatrixAvatarSource } from "./matrix/profile.js";

View File

@@ -1,7 +1,7 @@
import crypto from "node:crypto";
import path from "node:path";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
export function sanitizeMatrixPathSegment(value: string): string {
const cleaned = normalizeLowercaseStringOrEmpty(value)

View File

@@ -1,7 +1,7 @@
import {
implicitMentionKindWhen,
resolveInboundMentionDecision,
} from "openclaw/plugin-sdk/channel-inbound";
} from "openclaw/plugin-sdk/channel-mention-gating";
import { vi } from "vitest";
import type { PluginRuntime } from "./runtime-api.js";
import { setMatrixRuntime } from "./runtime.js";

View File

@@ -1,4 +1,4 @@
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
import { resolveMatrixTargetIdentity } from "./matrix/target-ids.js";
export const defaultTopLevelPlacement = "child" as const;

View File

@@ -1,5 +1,5 @@
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/text-runtime";
import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
import { resolveMatrixAccountConfig } from "./matrix/accounts.js";
import {
bootstrapMatrixVerification,