refactor: dedupe chat channel readers

This commit is contained in:
Peter Steinberger
2026-04-07 07:52:24 +01:00
parent ce19b6bf6a
commit fca8ff5748
24 changed files with 73 additions and 45 deletions

View File

@@ -112,7 +112,7 @@ function resolveNickServConfig(accountId: string, nickserv?: IrcNickServConfig):
const merged: IrcNickServConfig = {
...base,
service: base.service?.trim() || undefined,
service: normalizeOptionalString(base.service),
passwordFile: passwordFile || undefined,
password: resolvedPassword || undefined,
registerEmail: base.registerEmail?.trim() || envRegisterEmail || undefined,

View File

@@ -1,3 +1,4 @@
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import type { ResolvedIrcAccount } from "./accounts.js";
import { normalizeIrcAllowlist, resolveIrcAllowlistMatch } from "./normalize.js";
import {
@@ -299,7 +300,7 @@ export async function handleIrcInbound(params: {
body: rawBody,
});
const groupSystemPrompt = groupMatch.groupConfig?.systemPrompt?.trim() || undefined;
const groupSystemPrompt = normalizeOptionalString(groupMatch.groupConfig?.systemPrompt);
const ctxPayload = core.channel.reply.finalizeInboundContext({
Body: body,

View File

@@ -22,6 +22,7 @@ import {
resolveAgentRoute,
} from "openclaw/plugin-sdk/routing";
import { logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { normalizeAllowFrom } from "./bot-access.js";
import { resolveLineGroupConfigEntry, resolveLineGroupHistoryKey } from "./group-keys.js";
import type { LineGroupConfig, ResolvedLineAccount } from "./types.js";
@@ -291,7 +292,7 @@ function resolveLineGroupSystemPrompt(
groupId: source.groupId,
roomId: source.roomId,
});
return entry?.systemPrompt?.trim() || undefined;
return normalizeOptionalString(entry?.systemPrompt);
}
async function finalizeLineInboundContext(params: {

View File

@@ -1,6 +1,7 @@
import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
import type { OpenClawConfig } from "openclaw/plugin-sdk/account-resolution";
import { resolveAccountEntry } from "openclaw/plugin-sdk/account-resolution";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import type { LineConfig, LineGroupConfig } from "./types.js";
export function resolveLineGroupLookupIds(groupId?: string | null): string[] {
@@ -68,5 +69,5 @@ export function resolveLineGroupHistoryKey(params: {
groupId?: string | null;
roomId?: string | null;
}): string | undefined {
return params.groupId?.trim() || params.roomId?.trim() || undefined;
return normalizeOptionalString(params.groupId) ?? normalizeOptionalString(params.roomId);
}

View File

@@ -8,6 +8,7 @@ import {
createChannelNativeOriginTargetResolver,
} from "openclaw/plugin-sdk/approval-native-runtime";
import type { ExecApprovalRequest, PluginApprovalRequest } from "openclaw/plugin-sdk/infra-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { getMatrixApprovalAuthApprovers, matrixApprovalAuth } from "./approval-auth.js";
import {
getMatrixExecApprovalApprovers,
@@ -141,7 +142,8 @@ const matrixNativeApprovalCapability = createApproverRestrictedNativeApprovalCap
resolveMatrixExecApprovalTarget({ cfg, accountId }),
requireMatchingTurnSourceChannel: true,
resolveSuppressionAccountId: ({ target, request }) =>
target.accountId?.trim() || request.request.turnSourceAccountId?.trim() || undefined,
normalizeOptionalString(target.accountId) ??
normalizeOptionalString(request.request.turnSourceAccountId),
resolveOriginTarget: resolveMatrixOriginTarget,
resolveApproverDmTargets: resolveMatrixApproverDmTargets,
});

View File

@@ -29,6 +29,7 @@ import {
createDefaultChannelRuntimeState,
} from "openclaw/plugin-sdk/status-helpers";
import { chunkTextForOutbound } from "openclaw/plugin-sdk/text-chunking";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { matrixMessageActions } from "./actions.js";
import { matrixApprovalCapability } from "./approval-native.js";
import { MatrixConfigSchema } from "./config-schema.js";
@@ -305,7 +306,7 @@ function resolveMatrixInboundConversation(params: {
const target = rawTarget ? resolveMatrixTargetIdentity(rawTarget) : null;
const parentConversationId = target?.kind === "room" ? target.id : undefined;
const threadId =
params.threadId != null ? String(params.threadId).trim() || undefined : undefined;
params.threadId != null ? normalizeOptionalString(String(params.threadId)) : undefined;
if (threadId) {
return {
conversationId: threadId,
@@ -581,7 +582,7 @@ export const matrixPlugin: ChannelPlugin<ResolvedMatrixAccount, MatrixProbe> =
buildToolContext: ({ context, hasRepliedRef }) => {
const currentTarget = context.To;
return {
currentChannelId: currentTarget?.trim() || undefined,
currentChannelId: normalizeOptionalString(currentTarget),
currentThreadTs:
context.MessageThreadId != null ? String(context.MessageThreadId) : undefined,
currentDirectUserId: resolveMatrixDirectUserId({

View File

@@ -1,3 +1,4 @@
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { resolveMatrixAuth } from "./matrix/client.js";
import { MatrixAuthedHttpClient } from "./matrix/sdk/http-client.js";
import { isMatrixQualifiedUserId, normalizeMatrixMessagingTarget } from "./matrix/target-ids.js";
@@ -132,15 +133,16 @@ export async function listMatrixDirectoryPeersLive(
const results = res.results ?? [];
return results
.map((entry) => {
const userId = entry.user_id?.trim();
const userId = normalizeOptionalString(entry.user_id);
if (!userId) {
return null;
}
const displayName = normalizeOptionalString(entry.display_name);
return {
kind: "user",
id: userId,
name: entry.display_name?.trim() || undefined,
handle: entry.display_name ? `@${entry.display_name.trim()}` : undefined,
name: displayName,
handle: displayName ? `@${displayName}` : undefined,
raw: entry,
} satisfies ChannelDirectoryEntry;
})

View File

@@ -1,5 +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 {
resolveConfiguredMatrixAccountIds,
resolveMatrixDefaultOrOnlyAccountId,
@@ -182,7 +183,7 @@ export function resolveMatrixAccount(params: {
return {
accountId,
enabled,
name: base.name?.trim() || undefined,
name: normalizeOptionalString(base.name),
configured,
homeserver: authView.homeserver || undefined,
userId: authView.userId || undefined,

View File

@@ -1,3 +1,4 @@
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { fetchMatrixPollMessageSummary, resolveMatrixPollRootEventId } from "../poll-summary.js";
import { isPollEventType } from "../poll-types.js";
import { editMessageMatrix, sendMessageMatrix } from "../send.js";
@@ -77,7 +78,7 @@ export async function readMatrixMessages(
}> {
return await withResolvedRoomAction(roomId, opts, async (client, resolvedRoom) => {
const limit = resolveMatrixActionLimit(opts.limit, 20);
const token = opts.before?.trim() || opts.after?.trim() || undefined;
const token = normalizeOptionalString(opts.before) ?? normalizeOptionalString(opts.after);
const dir = opts.after ? "f" : "b";
// Room history is queried via the low-level endpoint for compatibility.
const res = (await client.doRequest(

View File

@@ -1,3 +1,4 @@
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { getMatrixRuntime } from "../../runtime.js";
import type { CoreConfig } from "../../types.js";
import { formatMatrixEncryptionUnavailableError } from "../encryption-guidance.js";
@@ -43,9 +44,9 @@ export async function requestMatrixVerification(
const ownUser = params.ownUser ?? (!params.userId && !params.deviceId && !params.roomId);
return await crypto.requestVerification({
ownUser,
userId: params.userId?.trim() || undefined,
deviceId: params.deviceId?.trim() || undefined,
roomId: params.roomId?.trim() || undefined,
userId: normalizeOptionalString(params.userId),
deviceId: normalizeOptionalString(params.deviceId),
roomId: normalizeOptionalString(params.roomId),
});
});
}
@@ -67,8 +68,8 @@ export async function cancelMatrixVerification(
return await withStartedActionClient(opts, async (client) => {
const crypto = requireCrypto(client, opts);
return await crypto.cancelVerification(resolveVerificationId(requestId), {
reason: opts.reason?.trim() || undefined,
code: opts.code?.trim() || undefined,
reason: normalizeOptionalString(opts.reason),
code: normalizeOptionalString(opts.code),
});
});
}
@@ -210,7 +211,7 @@ export async function restoreMatrixRoomKeyBackup(
opts,
async (client) =>
await client.restoreRoomKeyBackup({
recoveryKey: opts.recoveryKey?.trim() || undefined,
recoveryKey: normalizeOptionalString(opts.recoveryKey),
}),
);
}
@@ -229,7 +230,7 @@ export async function bootstrapMatrixVerification(
opts,
async (client) =>
await client.bootstrapOwnDeviceVerification({
recoveryKey: opts.recoveryKey?.trim() || undefined,
recoveryKey: normalizeOptionalString(opts.recoveryKey),
forceResetCrossSigning: opts.forceResetCrossSigning === true,
}),
);

View File

@@ -1,5 +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 { SsrFPolicy } from "../../runtime-api.js";
import type { MatrixClient } from "../sdk.js";
import { resolveValidatedMatrixHomeserverUrl } from "./config.js";
@@ -49,8 +50,8 @@ export async function createMatrixClient(params: {
const homeserver = await resolveValidatedMatrixHomeserverUrl(params.homeserver, {
dangerouslyAllowPrivateNetwork: params.allowPrivateNetwork,
});
const userId = params.userId?.trim() || "unknown";
const matrixClientUserId = params.userId?.trim() || undefined;
const matrixClientUserId = normalizeOptionalString(params.userId);
const userId = matrixClientUserId ?? "unknown";
const persistStorage = params.persistStorage !== false;
const storagePaths = persistStorage
? resolveMatrixStoragePaths({

View File

@@ -6,6 +6,7 @@ import {
} 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";
import type {
CoreConfig,
MatrixRoomConfig,
@@ -1147,7 +1148,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
envelope: envelopeOptions,
body: textWithId,
});
const groupSystemPrompt = roomConfig?.systemPrompt?.trim() || undefined;
const groupSystemPrompt = normalizeOptionalString(roomConfig?.systemPrompt);
const ctxPayload = core.channel.reply.finalizeInboundContext({
Body: body,
RawBody: bodyText,
@@ -1420,7 +1421,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
return;
}
const payloadReplyToId = payload.replyToId?.trim() || undefined;
const payloadReplyToId = normalizeOptionalString(payload.replyToId);
const payloadReplyMismatch =
replyToMode !== "off" &&
!threadTarget &&

View File

@@ -1,4 +1,5 @@
import { formatErrorMessage, type PinnedDispatcherPolicy } from "openclaw/plugin-sdk/infra-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import type { SsrFPolicy } from "../runtime-api.js";
import type { BaseProbeResult } from "../runtime-api.js";
import { isBunRuntime } from "./client/runtime.js";
@@ -61,7 +62,7 @@ export async function probeMatrix(params: {
}
try {
const { createMatrixClient } = await loadMatrixProbeRuntimeDeps();
const inputUserId = params.userId?.trim() || undefined;
const inputUserId = normalizeOptionalString(params.userId);
const client = await createMatrixClient({
homeserver: params.homeserver,
userId: inputUserId,

View File

@@ -1,6 +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 {
registerSessionBindingAdapter,
resolveThreadBindingFarewellText,
@@ -420,7 +421,7 @@ export async function createMatrixThreadBindingManager(params: {
capabilities: { placements: ["current", "child"], bindSupported: true, unbindSupported: true },
bind: async (input) => {
const conversationId = input.conversation.conversationId.trim();
const parentConversationId = input.conversation.parentConversationId?.trim() || undefined;
const parentConversationId = normalizeOptionalString(input.conversation.parentConversationId);
const targetSessionKey = input.targetSessionKey.trim();
if (!conversationId || !targetSessionKey) {
return null;

View File

@@ -5,6 +5,7 @@ import {
normalizeSecretInputString,
type ChannelSetupInput,
} from "openclaw/plugin-sdk/setup";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { resolveMatrixEnvAuthReadiness } from "./matrix/client/env-auth.js";
import { updateMatrixAccountConfig } from "./matrix/config-update.js";
import { isSupportedMatrixAvatarSource } from "./matrix/profile.js";
@@ -210,7 +211,7 @@ export function applyMatrixSetupAccountConfig(params: {
: typeof params.input.allowPrivateNetwork === "boolean"
? params.input.allowPrivateNetwork
: undefined,
proxy: params.input.proxy?.trim() || undefined,
proxy: normalizeOptionalString(params.input.proxy),
userId: password && !userId ? null : userId,
accessToken: accessToken || (password ? null : undefined),
password: password || (accessToken ? null : undefined),

View File

@@ -1,6 +1,7 @@
import { createHmac } from "node:crypto";
import type { IncomingMessage, ServerResponse } from "node:http";
import { safeEqualSecret } from "openclaw/plugin-sdk/browser-security-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { getMattermostRuntime } from "../runtime.js";
import { updateMattermostPost, type MattermostClient, type MattermostPost } from "./client.js";
import { isTrustedProxyAddress, resolveClientIp, type OpenClawConfig } from "./runtime-api.js";
@@ -85,9 +86,9 @@ function normalizeCallbackBaseUrl(baseUrl: string): string {
function headerValue(value: string | string[] | undefined): string | undefined {
if (Array.isArray(value)) {
return value[0]?.trim() || undefined;
return normalizeOptionalString(value[0]);
}
return value?.trim() || undefined;
return normalizeOptionalString(value);
}
function isAllowedInteractionSource(params: {
@@ -123,8 +124,8 @@ export function computeInteractionCallbackUrl(
// Prefer merged per-account config when available, but keep the top-level path for
// callers/tests that still pass the root Mattermost config shape directly.
const callbackBaseUrl =
cfg?.interactions?.callbackBaseUrl?.trim() ??
cfg?.channels?.mattermost?.interactions?.callbackBaseUrl?.trim();
normalizeOptionalString(cfg?.interactions?.callbackBaseUrl) ??
normalizeOptionalString(cfg?.channels?.mattermost?.interactions?.callbackBaseUrl);
if (callbackBaseUrl) {
return `${normalizeCallbackBaseUrl(callbackBaseUrl)}${path}`;
}

View File

@@ -1,3 +1,4 @@
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import {
createDedupeCache,
formatInboundFromLabel as formatInboundFromLabelShared,
@@ -59,7 +60,7 @@ function resolveAgentEntry(cfg: OpenClawConfig, agentId: string): AgentEntry | u
export function resolveIdentityName(cfg: OpenClawConfig, agentId: string): string | undefined {
const entry = resolveAgentEntry(cfg, agentId);
return entry?.identity?.name?.trim() || undefined;
return normalizeOptionalString(entry?.identity?.name);
}
export function resolveThreadSessionKeys(params: {

View File

@@ -1,4 +1,5 @@
import { isPrivateNetworkOptInEnabled } from "openclaw/plugin-sdk/ssrf-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { getMattermostRuntime } from "../runtime.js";
import { resolveMattermostAccount, resolveMattermostReplyToMode } from "./accounts.js";
import {
@@ -157,11 +158,11 @@ export function resolveMattermostReplyRootId(params: {
threadRootId?: string;
replyToId?: string;
}): string | undefined {
const threadRootId = params.threadRootId?.trim();
const threadRootId = normalizeOptionalString(params.threadRootId);
if (threadRootId) {
return threadRootId;
}
return params.replyToId?.trim() || undefined;
return normalizeOptionalString(params.replyToId);
}
export function resolveMattermostEffectiveReplyToId(params: {
@@ -170,14 +171,14 @@ export function resolveMattermostEffectiveReplyToId(params: {
replyToMode: "off" | "first" | "all" | "batched";
threadRootId?: string | null;
}): string | undefined {
const threadRootId = params.threadRootId?.trim();
const threadRootId = normalizeOptionalString(params.threadRootId);
if (threadRootId && params.replyToMode !== "off") {
return threadRootId;
}
if (params.kind === "direct") {
return undefined;
}
const postId = params.postId?.trim();
const postId = normalizeOptionalString(params.postId);
if (!postId) {
return undefined;
}
@@ -216,7 +217,10 @@ export function resolveMattermostThreadSessionContext(params: {
export function resolveMattermostReactionChannelId(
payload: Pick<MattermostEventPayload, "broadcast" | "data">,
): string | undefined {
return payload.broadcast?.channel_id?.trim() || payload.data?.channel_id?.trim() || undefined;
return (
normalizeOptionalString(payload.broadcast?.channel_id) ??
normalizeOptionalString(payload.data?.channel_id)
);
}
function buildMattermostAttachmentPlaceholder(mediaList: MattermostMediaInfo[]): string {
@@ -303,7 +307,7 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {}
return;
}
const botUserId = botUser.id;
const botUsername = botUser.username?.trim() || undefined;
const botUsername = normalizeOptionalString(botUser.username);
runtime.log?.(`mattermost connected as ${botUsername ? `@${botUsername}` : botUserId}`);
await registerMattermostMonitorSlashCommands({
client,
@@ -1181,7 +1185,7 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {}
});
const baseSessionKey = route.sessionKey;
const threadRootId = post.root_id?.trim() || undefined;
const threadRootId = normalizeOptionalString(post.root_id);
const replyToMode = resolveMattermostReplyToMode(account, kind);
const threadContext = resolveMattermostThreadSessionContext({
baseSessionKey,

View File

@@ -12,6 +12,7 @@
* - On shutdown, cleans up registered commands via DELETE /api/v4/commands/{id}
*/
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import type { MattermostClient } from "./client.js";
// ─── Types ───────────────────────────────────────────────────────────────────
@@ -521,7 +522,7 @@ export function resolveSlashCommandConfig(
native: raw?.native ?? "auto",
nativeSkills: raw?.nativeSkills ?? "auto",
callbackPath: normalizeCallbackPath(raw?.callbackPath ?? DEFAULT_CALLBACK_PATH),
callbackUrl: raw?.callbackUrl?.trim() || undefined,
callbackUrl: normalizeOptionalString(raw?.callbackUrl),
};
}

View File

@@ -1,5 +1,6 @@
import { resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
import { tryReadSecretFileSync } from "openclaw/plugin-sdk/channel-core";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import {
createAccountListHelpers,
DEFAULT_ACCOUNT_ID,
@@ -66,7 +67,7 @@ function resolveNextcloudTalkSecret(
const resolvedAccountId = opts.accountId ?? resolveDefaultNextcloudTalkAccountId(cfg);
const merged = mergeNextcloudTalkAccountConfig(cfg, resolvedAccountId);
const envSecret = process.env.NEXTCLOUD_TALK_BOT_SECRET?.trim();
const envSecret = normalizeOptionalString(process.env.NEXTCLOUD_TALK_BOT_SECRET);
if (envSecret && resolvedAccountId === DEFAULT_ACCOUNT_ID) {
return { secret: envSecret, source: "env" };
}
@@ -117,7 +118,7 @@ export function resolveNextcloudTalkAccount(params: {
return {
accountId,
enabled,
name: merged.name?.trim() || undefined,
name: normalizeOptionalString(merged.name),
baseUrl,
secret: secretResolution.secret,
secretSource: secretResolution.source,

View File

@@ -1,3 +1,4 @@
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import {
GROUP_POLICY_BLOCKED_LABEL,
createChannelPairingController,
@@ -253,7 +254,7 @@ export async function handleNextcloudTalkInbound(params: {
body: rawBody,
});
const groupSystemPrompt = roomConfig?.systemPrompt?.trim() || undefined;
const groupSystemPrompt = normalizeOptionalString(roomConfig?.systemPrompt);
const ctxPayload = core.channel.reply.finalizeInboundContext({
Body: body,

View File

@@ -6,6 +6,7 @@ import {
setSetupChannelEnabled,
type ChannelSetupWizard,
} from "openclaw/plugin-sdk/setup";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { resolveNextcloudTalkAccount } from "./accounts.js";
import {
clearNextcloudTalkAccountFields,
@@ -93,7 +94,7 @@ export const nextcloudTalkSetupWizard: ChannelSetupWizard = {
resolvedValue: resolvedAccount.secret || undefined,
envValue:
accountId === DEFAULT_ACCOUNT_ID
? process.env.NEXTCLOUD_TALK_BOT_SECRET?.trim() || undefined
? normalizeOptionalString(process.env.NEXTCLOUD_TALK_BOT_SECRET)
: undefined,
};
},

View File

@@ -8,6 +8,7 @@ import {
resolveListedDefaultAccountId,
} from "openclaw/plugin-sdk/account-resolution";
import { normalizeSecretInputString, type SecretInput } from "openclaw/plugin-sdk/secret-input";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import type { OpenClawConfig } from "../api.js";
import type { NostrProfile } from "./config-schema.js";
import { DEFAULT_RELAYS } from "./default-relays.js";
@@ -96,7 +97,7 @@ export function resolveNostrAccount(opts: {
return {
accountId,
name: nostrCfg?.name?.trim() || undefined,
name: normalizeOptionalString(nostrCfg?.name),
enabled: baseEnabled,
configured,
privateKey,

View File

@@ -1,6 +1,7 @@
import { createAccountListHelpers } from "openclaw/plugin-sdk/account-helpers";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
import { resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import type { CoreConfig, QaChannelAccountConfig, ResolvedQaChannelAccount } from "./types.js";
const DEFAULT_POLL_TIMEOUT_MS = 1_000;
@@ -37,7 +38,7 @@ export function resolveQaChannelAccount(params: {
accountId,
enabled,
configured: Boolean(baseUrl),
name: merged.name?.trim() || undefined,
name: normalizeOptionalString(merged.name),
baseUrl,
botUserId,
botDisplayName,