perf(memory): trim matrix and telegram runtime seams

This commit is contained in:
Vincent Koc
2026-04-02 10:17:46 +09:00
parent 1cc5526f7f
commit 687030cbf2
9 changed files with 116 additions and 108 deletions

View File

@@ -2,11 +2,14 @@ import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
import {
listConfiguredAccountIds,
resolveMergedAccountConfig,
resolveNormalizedAccountEntry,
} from "openclaw/plugin-sdk/account-resolution";
import { hasConfiguredSecretInput } from "openclaw/plugin-sdk/secret-input";
import type { CoreConfig, MatrixAccountConfig, MatrixConfig } from "../types.js";
type MatrixRoomEntries = Record<string, NonNullable<MatrixConfig["groups"]>[string]>;
export function resolveMatrixBaseConfig(cfg: CoreConfig): MatrixConfig {
return cfg.channels?.matrix ?? {};
}
@@ -19,6 +22,51 @@ function resolveMatrixAccountsMap(cfg: CoreConfig): Readonly<Record<string, Matr
return accounts;
}
function selectInheritedMatrixRoomEntries(params: {
entries: MatrixRoomEntries | undefined;
accountId: string;
}): MatrixRoomEntries | undefined {
const entries = params.entries;
if (!entries) {
return undefined;
}
const selected = Object.fromEntries(
Object.entries(entries).filter(([, value]) => {
const scopedAccount =
typeof value?.account === "string" ? normalizeAccountId(value.account) : undefined;
return scopedAccount === undefined || scopedAccount === params.accountId;
}),
) as MatrixRoomEntries;
return Object.keys(selected).length > 0 ? selected : undefined;
}
function mergeMatrixRoomEntries(
inherited: MatrixRoomEntries | undefined,
accountEntries: MatrixRoomEntries | undefined,
hasAccountOverride: boolean,
): MatrixRoomEntries | undefined {
if (!inherited && !accountEntries) {
return undefined;
}
if (hasAccountOverride && Object.keys(accountEntries ?? {}).length === 0) {
return undefined;
}
const merged: MatrixRoomEntries = {
...(inherited ?? {}),
};
for (const [key, value] of Object.entries(accountEntries ?? {})) {
const inheritedValue = merged[key];
merged[key] =
inheritedValue && value
? {
...inheritedValue,
...value,
}
: (value ?? inheritedValue);
}
return Object.keys(merged).length > 0 ? merged : undefined;
}
export function listNormalizedMatrixAccountIds(cfg: CoreConfig): string[] {
return listConfiguredAccountIds({
accounts: resolveMatrixAccountsMap(cfg),
@@ -58,3 +106,45 @@ export function hasExplicitMatrixAccountConfig(cfg: CoreConfig, accountId: strin
typeof matrix.avatarUrl === "string"
);
}
export function resolveMatrixAccountConfig(params: {
cfg: CoreConfig;
accountId?: string | null;
env?: NodeJS.ProcessEnv;
}): MatrixConfig {
const accountId = normalizeAccountId(params.accountId);
const base = resolveMatrixBaseConfig(params.cfg);
const merged = resolveMergedAccountConfig<MatrixConfig>({
channelConfig: base,
accounts: params.cfg.channels?.matrix?.accounts as
| Record<string, Partial<MatrixConfig>>
| undefined,
accountId,
normalizeAccountId,
nestedObjectKeys: ["dm", "actions"],
});
const accountConfig = findMatrixAccountConfig(params.cfg, accountId);
const groups = mergeMatrixRoomEntries(
selectInheritedMatrixRoomEntries({
entries: base.groups,
accountId,
}),
accountConfig?.groups,
Boolean(accountConfig && Object.hasOwn(accountConfig, "groups")),
);
const rooms = mergeMatrixRoomEntries(
selectInheritedMatrixRoomEntries({
entries: base.rooms,
accountId,
}),
accountConfig?.rooms,
Boolean(accountConfig && Object.hasOwn(accountConfig, "rooms")),
);
// Room maps need custom scoping, so keep the generic merge for all other fields.
const { groups: _ignoredGroups, rooms: _ignoredRooms, ...rest } = merged;
return {
...rest,
...(groups ? { groups } : {}),
...(rooms ? { rooms } : {}),
};
}

View File

@@ -1,62 +1,18 @@
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
import { resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
import { hasConfiguredSecretInput } from "openclaw/plugin-sdk/secret-input";
import {
resolveConfiguredMatrixAccountIds,
resolveMatrixDefaultOrOnlyAccountId,
} from "../account-selection.js";
import type { CoreConfig, MatrixConfig } from "../types.js";
import { findMatrixAccountConfig, resolveMatrixBaseConfig } from "./account-config.js";
import {
findMatrixAccountConfig,
resolveMatrixAccountConfig,
resolveMatrixBaseConfig,
} from "./account-config.js";
import { resolveMatrixConfigForAccount } from "./client.js";
import { credentialsMatchConfig, loadMatrixCredentials } from "./credentials-read.js";
type MatrixRoomEntries = Record<string, NonNullable<MatrixConfig["groups"]>[string]>;
function selectInheritedMatrixRoomEntries(params: {
entries: MatrixRoomEntries | undefined;
accountId: string;
}): MatrixRoomEntries | undefined {
const entries = params.entries;
if (!entries) {
return undefined;
}
const selected = Object.fromEntries(
Object.entries(entries).filter(([, value]) => {
const scopedAccount =
typeof value?.account === "string" ? normalizeAccountId(value.account) : undefined;
return scopedAccount === undefined || scopedAccount === params.accountId;
}),
) as MatrixRoomEntries;
return Object.keys(selected).length > 0 ? selected : undefined;
}
function mergeMatrixRoomEntries(
inherited: MatrixRoomEntries | undefined,
accountEntries: MatrixRoomEntries | undefined,
hasAccountOverride: boolean,
): MatrixRoomEntries | undefined {
if (!inherited && !accountEntries) {
return undefined;
}
if (hasAccountOverride && Object.keys(accountEntries ?? {}).length === 0) {
return undefined;
}
const merged: MatrixRoomEntries = {
...(inherited ?? {}),
};
for (const [key, value] of Object.entries(accountEntries ?? {})) {
const inheritedValue = merged[key];
merged[key] =
inheritedValue && value
? {
...inheritedValue,
...value,
}
: (value ?? inheritedValue);
}
return Object.keys(merged).length > 0 ? merged : undefined;
}
export type ResolvedMatrixAccount = {
accountId: string;
enabled: boolean;
@@ -177,45 +133,4 @@ export function resolveMatrixAccount(params: {
};
}
export function resolveMatrixAccountConfig(params: {
cfg: CoreConfig;
accountId?: string | null;
env?: NodeJS.ProcessEnv;
}): MatrixConfig {
const env = params.env ?? process.env;
const accountId = normalizeAccountId(params.accountId);
const base = resolveMatrixBaseConfig(params.cfg);
const merged = resolveMergedAccountConfig<MatrixConfig>({
channelConfig: base,
accounts: params.cfg.channels?.matrix?.accounts as
| Record<string, Partial<MatrixConfig>>
| undefined,
accountId,
normalizeAccountId,
nestedObjectKeys: ["dm", "actions"],
});
const accountConfig = findMatrixAccountConfig(params.cfg, accountId);
const groups = mergeMatrixRoomEntries(
selectInheritedMatrixRoomEntries({
entries: base.groups,
accountId,
}),
accountConfig?.groups,
Boolean(accountConfig && Object.hasOwn(accountConfig, "groups")),
);
const rooms = mergeMatrixRoomEntries(
selectInheritedMatrixRoomEntries({
entries: base.rooms,
accountId,
}),
accountConfig?.rooms,
Boolean(accountConfig && Object.hasOwn(accountConfig, "rooms")),
);
// Room maps need custom scoping, so keep the generic merge for all other fields.
const { groups: _ignoredGroups, rooms: _ignoredRooms, ...rest } = merged;
return {
...rest,
...(groups ? { groups } : {}),
...(rooms ? { rooms } : {}),
};
}
export { resolveMatrixAccountConfig } from "./account-config.js";

View File

@@ -1,5 +1,5 @@
import type { CoreConfig } from "../../types.js";
import { resolveMatrixAccountConfig } from "../accounts.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

@@ -177,7 +177,7 @@ vi.mock("../../runtime.js", () => ({
config: {
loadConfig: () => ({
channels: {
matrix: {},
matrix: hoisted.accountConfig,
},
}),
writeConfigFile: vi.fn(),

View File

@@ -10,7 +10,8 @@ import {
} from "../../runtime-api.js";
import { getMatrixRuntime } from "../../runtime.js";
import type { CoreConfig, ReplyToMode } from "../../types.js";
import { resolveConfiguredMatrixBotUserIds, resolveMatrixAccount } from "../accounts.js";
import { resolveConfiguredMatrixBotUserIds } from "../accounts.js";
import { resolveMatrixAccountConfig } from "../account-config.js";
import { setActiveMatrixClient } from "../active-client.js";
import {
isBunRuntime,
@@ -82,8 +83,10 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi
const effectiveAccountId = authContext.accountId;
// Resolve account-specific config for multi-account support
const account = resolveMatrixAccount({ cfg, accountId: effectiveAccountId });
const accountConfig = account.config;
const accountConfig = resolveMatrixAccountConfig({
cfg,
accountId: effectiveAccountId,
});
const allowlistOnly = accountConfig.allowlistOnly === true;
const accountAllowBots = accountConfig.allowBots;
@@ -179,7 +182,7 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi
warnMissingProviderGroupPolicyFallbackOnce({
providerMissingFallbackApplied,
providerKey: "matrix",
accountId: account.accountId,
accountId: effectiveAccountId,
blockedLabel: GROUP_POLICY_BLOCKED_LABEL.room,
log: (message) => logVerboseMessage(message),
});
@@ -190,18 +193,18 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi
const threadBindingIdleTimeoutMs = resolveThreadBindingIdleTimeoutMsForChannel({
cfg,
channel: "matrix",
accountId: account.accountId,
accountId: effectiveAccountId,
});
const threadBindingMaxAgeMs = resolveThreadBindingMaxAgeMsForChannel({
cfg,
channel: "matrix",
accountId: account.accountId,
accountId: effectiveAccountId,
});
const dmConfig = accountConfig.dm;
const dmEnabled = dmConfig?.enabled ?? true;
const dmPolicyRaw = dmConfig?.policy ?? "pairing";
const dmPolicy = allowlistOnly && dmPolicyRaw !== "disabled" ? "allowlist" : dmPolicyRaw;
const textLimit = core.channel.text.resolveTextChunkLimit(cfg, "matrix", account.accountId);
const textLimit = core.channel.text.resolveTextChunkLimit(cfg, "matrix", effectiveAccountId);
const globalGroupChatHistoryLimit = (
cfg.messages as { groupChat?: { historyLimit?: number } } | undefined
)?.groupChat?.historyLimit;
@@ -252,7 +255,7 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi
client,
core,
cfg,
accountId: account.accountId,
accountId: effectiveAccountId,
runtime,
logger,
logVerboseMessage,
@@ -291,7 +294,7 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi
try {
threadBindingManager = await createMatrixThreadBindingManager({
accountId: account.accountId,
accountId: effectiveAccountId,
auth,
client,
env: process.env,
@@ -315,7 +318,7 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi
.readAllowFromStore({
channel: "matrix",
env: process.env,
accountId: account.accountId,
accountId: effectiveAccountId,
})
.catch(() => []),
directTracker,
@@ -343,7 +346,7 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi
await runMatrixStartupMaintenance({
client,
auth,
accountId: account.accountId,
accountId: effectiveAccountId,
effectiveAccountId,
accountConfig,
logger,

View File

@@ -1,6 +1,6 @@
import { getSessionBindingService } from "openclaw/plugin-sdk/conversation-runtime";
import type { CoreConfig } from "../../types.js";
import { resolveMatrixAccountConfig } from "../accounts.js";
import { resolveMatrixAccountConfig } from "../account-config.js";
import { extractMatrixReactionAnnotation } from "../reaction-common.js";
import type { MatrixClient } from "../sdk.js";
import { resolveMatrixInboundRoute } from "./route.js";

View File

@@ -1,6 +1,6 @@
import { getMatrixRuntime } from "../../runtime.js";
import type { CoreConfig } from "../../types.js";
import { resolveMatrixAccountConfig } from "../accounts.js";
import { resolveMatrixAccountConfig } from "../account-config.js";
import { withResolvedRuntimeMatrixClient } from "../client-bootstrap.js";
import type { MatrixClient } from "../sdk.js";

View File

@@ -8,7 +8,7 @@ import { loadConfig, resolveStorePath } from "openclaw/plugin-sdk/config-runtime
import { loadSessionStore } from "openclaw/plugin-sdk/config-runtime";
import { readChannelAllowFromStore } from "openclaw/plugin-sdk/conversation-runtime";
import { upsertChannelPairingRequest } from "openclaw/plugin-sdk/conversation-runtime";
import { dispatchReplyWithBufferedBlockDispatcher } from "openclaw/plugin-sdk/reply-runtime";
import { dispatchReplyWithBufferedBlockDispatcher } from "openclaw/plugin-sdk/reply-dispatch-runtime";
import { loadWebMedia } from "openclaw/plugin-sdk/web-media";
import { syncTelegramMenuCommands } from "./bot-native-command-menu.js";
import { deliverReplies, emitInternalMessageSentHook } from "./bot/delivery.js";

View File

@@ -17,7 +17,7 @@ import {
} from "openclaw/plugin-sdk/conversation-runtime";
import { formatUncaughtError } from "openclaw/plugin-sdk/error-runtime";
import { DEFAULT_GROUP_HISTORY_LIMIT, type HistoryEntry } from "openclaw/plugin-sdk/reply-history";
import { resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-runtime";
import { resolveTextChunkLimit } from "openclaw/plugin-sdk/reply-chunking";
import { danger, logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env";
import { getChildLogger } from "openclaw/plugin-sdk/runtime-env";
import { createSubsystemLogger } from "openclaw/plugin-sdk/runtime-env";