refactor: dedupe reply routing readers

This commit is contained in:
Peter Steinberger
2026-04-07 05:57:45 +01:00
parent bd99671756
commit 687bb21b28
10 changed files with 38 additions and 25 deletions

View File

@@ -1,5 +1,6 @@
import type { OpenClawConfig } from "../../config/config.js";
import { getActivePluginChannelRegistry } from "../../plugins/runtime.js";
import { normalizeOptionalString } from "../../shared/string-coerce.js";
type CommandSurfaceParams = {
ctx: {
@@ -32,13 +33,11 @@ export function resolveCommandSurfaceChannel(params: CommandSurfaceParams): stri
params.command.channel ??
params.ctx.Surface ??
params.ctx.Provider;
return String(channel ?? "")
.trim()
.toLowerCase();
return normalizeOptionalString(channel)?.toLowerCase() ?? "";
}
export function resolveChannelAccountId(params: ChannelAccountParams): string {
const accountId = typeof params.ctx.AccountId === "string" ? params.ctx.AccountId.trim() : "";
const accountId = normalizeOptionalString(params.ctx.AccountId) ?? "";
if (accountId) {
return accountId;
}
@@ -46,6 +45,6 @@ export function resolveChannelAccountId(params: ChannelAccountParams): string {
const plugin = getActivePluginChannelRegistry()?.channels.find(
(entry) => entry.plugin.id === channel,
)?.plugin;
const configuredDefault = plugin?.config.defaultAccountId?.(params.cfg)?.trim();
const configuredDefault = normalizeOptionalString(plugin?.config.defaultAccountId?.(params.cfg));
return configuredDefault || "default";
}

View File

@@ -13,6 +13,7 @@ import {
removeChannelAllowFromStoreEntry,
} from "../../pairing/pairing-store.js";
import { DEFAULT_ACCOUNT_ID, normalizeOptionalAccountId } from "../../routing/session-key.js";
import { normalizeOptionalString } from "../../shared/string-coerce.js";
import { normalizeStringEntries } from "../../shared/string-normalization.js";
import {
rejectNonOwnerCommand,
@@ -65,7 +66,9 @@ function resolveAllowlistAccountId(params: {
return explicitAccountId;
}
const plugin = getChannelPlugin(params.channelId);
const configuredDefaultAccountId = plugin?.config.defaultAccountId?.(params.cfg)?.trim();
const configuredDefaultAccountId = normalizeOptionalString(
plugin?.config.defaultAccountId?.(params.cfg),
);
const ctxAccountId = normalizeOptionalAccountId(params.ctxAccountId);
return configuredDefaultAccountId || ctxAccountId || DEFAULT_ACCOUNT_ID;
}
@@ -126,8 +129,8 @@ function parseAllowlistCommand(raw: string): AllowlistCommand | null {
}
const kv = token.split("=");
if (kv.length === 2) {
const key = kv[0]?.trim().toLowerCase();
const value = kv[1]?.trim();
const key = normalizeOptionalString(kv[0])?.toLowerCase();
const value = normalizeOptionalString(kv[1]);
if (key === "channel") {
if (value) {
channel = value;
@@ -280,7 +283,7 @@ export const handleAllowlistCommand: CommandHandler = async (params, allowTextCo
reply: { text: "⚠️ Unknown channel. Add channel=<id> to the command." },
};
}
if (parsed.account?.trim() && !normalizeOptionalAccountId(parsed.account)) {
if (normalizeOptionalString(parsed.account) && !normalizeOptionalAccountId(parsed.account)) {
return {
shouldContinue: false,
reply: {

View File

@@ -1,5 +1,6 @@
import type { OpenClawConfig } from "../../config/config.js";
import { logVerbose } from "../../globals.js";
import { normalizeOptionalString } from "../../shared/string-coerce.js";
import type { CommandHandler } from "./commands-types.js";
import { stripMentions, stripStructuralPrefixes } from "./mentions.js";
@@ -31,7 +32,7 @@ function extractCompactInstructions(params: {
}
function isCompactionSkipReason(reason?: string): boolean {
const text = reason?.trim().toLowerCase() ?? "";
const text = normalizeOptionalString(reason)?.toLowerCase() ?? "";
return (
text.includes("nothing to compact") ||
text.includes("below threshold") ||
@@ -41,7 +42,7 @@ function isCompactionSkipReason(reason?: string): boolean {
}
function formatCompactionReason(reason?: string): string | undefined {
const text = reason?.trim();
const text = normalizeOptionalString(reason);
if (!text) {
return undefined;
}

View File

@@ -1,5 +1,6 @@
import { ensureAuthProfileStore } from "../../agents/auth-profiles.js";
import type { OpenClawConfig } from "../../config/config.js";
import { normalizeOptionalString } from "../../shared/string-coerce.js";
export function resolveProfileOverride(params: {
rawProfile?: string;
@@ -7,7 +8,7 @@ export function resolveProfileOverride(params: {
cfg: OpenClawConfig;
agentDir?: string;
}): { profileId?: string; error?: string } {
const raw = params.rawProfile?.trim();
const raw = normalizeOptionalString(params.rawProfile);
if (!raw) {
return {};
}

View File

@@ -37,6 +37,7 @@ import {
} from "../../plugins/conversation-binding.js";
import { getGlobalHookRunner, getGlobalPluginRegistry } from "../../plugins/hook-runner-global.js";
import { resolveSendPolicy } from "../../sessions/send-policy.js";
import { normalizeOptionalString } from "../../shared/string-coerce.js";
import { normalizeTtsAutoMode, resolveConfiguredTtsMode } from "../../tts/tts-config.js";
import { normalizeMessageChannel } from "../../utils/message-channel.js";
import type { FinalizedMsgContext } from "../templating.js";
@@ -95,7 +96,8 @@ async function maybeApplyTtsToReplyPayload(
const AUDIO_PLACEHOLDER_RE = /^<media:audio>(\s*\([^)]*\))?$/i;
const AUDIO_HEADER_RE = /^\[Audio\b/i;
const normalizeMediaType = (value: string): string => value.split(";")[0]?.trim().toLowerCase();
const normalizeMediaType = (value: string): string =>
normalizeOptionalString(value.split(";")[0])?.toLowerCase() ?? "";
const isInboundAudioContext = (ctx: FinalizedMsgContext): boolean => {
const rawTypes = [
@@ -136,8 +138,10 @@ const resolveSessionStoreLookup = (
entry?: SessionEntry;
} => {
const targetSessionKey =
ctx.CommandSource === "native" ? ctx.CommandTargetSessionKey?.trim() : undefined;
const sessionKey = (targetSessionKey ?? ctx.SessionKey)?.trim();
ctx.CommandSource === "native"
? normalizeOptionalString(ctx.CommandTargetSessionKey)
: undefined;
const sessionKey = normalizeOptionalString(targetSessionKey ?? ctx.SessionKey);
if (!sessionKey) {
return {};
}
@@ -742,13 +746,13 @@ export async function dispatchReplyFromConfig(params: {
message?: string;
}) => {
if (payload.status === "pending") {
if (payload.command?.trim()) {
if (normalizeOptionalString(payload.command)) {
return normalizeWorkingLabel(`awaiting approval: ${payload.command}`);
}
return "awaiting approval";
}
if (payload.status === "unavailable") {
if (payload.message?.trim()) {
if (normalizeOptionalString(payload.message)) {
return normalizeWorkingLabel(payload.message);
}
return "approval unavailable";
@@ -756,10 +760,10 @@ export async function dispatchReplyFromConfig(params: {
return "";
};
const summarizePatchLabel = (payload: { summary?: string; title?: string }) => {
if (payload.summary?.trim()) {
if (normalizeOptionalString(payload.summary)) {
return normalizeWorkingLabel(payload.summary);
}
if (payload.title?.trim()) {
if (normalizeOptionalString(payload.title)) {
return normalizeWorkingLabel(payload.title);
}
return "";

View File

@@ -8,6 +8,7 @@ import type { SessionEntry } from "../../config/sessions.js";
import { logVerbose } from "../../globals.js";
import { formatErrorMessage } from "../../infra/errors.js";
import { generateSecureToken } from "../../infra/secure-random.js";
import { normalizeOptionalString } from "../../shared/string-coerce.js";
import { resolveGatewayMessageChannel } from "../../utils/message-channel.js";
import {
listReservedChatSlashCommandNames,
@@ -57,12 +58,12 @@ function resolveSlashCommandName(commandBodyNormalized: string): string | null {
return null;
}
const match = trimmed.match(/^\/([^\s:]+)(?::|\s|$)/);
const name = match?.[1]?.trim().toLowerCase() ?? "";
const name = normalizeOptionalString(match?.[1])?.toLowerCase() ?? "";
return name ? name : null;
}
function expandBundleCommandPromptTemplate(template: string, args?: string): string {
const normalizedArgs = args?.trim() || "";
const normalizedArgs = normalizeOptionalString(args) || "";
const rendered = template.includes("$ARGUMENTS")
? template.replaceAll("$ARGUMENTS", normalizedArgs)
: template;

View File

@@ -19,6 +19,7 @@ import { resolveSessionParentSessionKey } from "../../channels/plugins/session-c
import type { OpenClawConfig } from "../../config/config.js";
import type { SessionEntry } from "../../config/sessions/types.js";
import { applyModelOverrideToSessionEntry } from "../../sessions/model-overrides.js";
import { normalizeOptionalString } from "../../shared/string-coerce.js";
import type { ThinkLevel } from "./directives.js";
export type ModelDirectiveSelection = {
@@ -148,7 +149,7 @@ function resolveParentSessionKeyCandidate(params: {
sessionKey?: string;
parentSessionKey?: string;
}): string | null {
const explicit = params.parentSessionKey?.trim();
const explicit = normalizeOptionalString(params.parentSessionKey);
if (explicit && explicit !== params.sessionKey) {
return explicit;
}

View File

@@ -1,5 +1,6 @@
import { resolveEmbeddedSessionLane } from "../../../agents/pi-embedded-runner/lanes.js";
import { clearCommandLane } from "../../../process/command-queue.js";
import { normalizeOptionalString } from "../../../shared/string-coerce.js";
import { clearFollowupDrainCallback } from "./drain.js";
import { clearFollowupQueue } from "./state.js";
@@ -57,7 +58,7 @@ export function clearSessionQueues(keys: Array<string | undefined>): ClearSessio
const clearLane = resolveQueueCleanupLaneClearer();
for (const key of keys) {
const cleaned = key?.trim();
const cleaned = normalizeOptionalString(key);
if (!cleaned || seen.has(cleaned)) {
continue;
}

View File

@@ -1,5 +1,6 @@
import { getChannelPlugin } from "../../../channels/plugins/index.js";
import type { InboundDebounceByProvider } from "../../../config/types.messages.js";
import { normalizeOptionalString } from "../../../shared/string-coerce.js";
import { normalizeQueueDropPolicy, normalizeQueueMode } from "./normalize.js";
import { DEFAULT_QUEUE_CAP, DEFAULT_QUEUE_DEBOUNCE_MS, DEFAULT_QUEUE_DROP } from "./state.js";
import type { QueueMode, QueueSettings, ResolveQueueSettingsParams } from "./types.js";
@@ -30,7 +31,7 @@ function resolvePluginDebounce(channelKey: string | undefined): number | undefin
}
export function resolveQueueSettings(params: ResolveQueueSettingsParams): QueueSettings {
const channelKey = params.channel?.trim().toLowerCase();
const channelKey = normalizeOptionalString(params.channel)?.toLowerCase();
const queueCfg = params.cfg.messages?.queue;
const providerModeRaw =
channelKey && queueCfg?.byChannel

View File

@@ -10,6 +10,7 @@ import type { OpenClawConfig } from "../../config/config.js";
import type { SessionEntry } from "../../config/sessions.js";
import { updateSessionStore } from "../../config/sessions.js";
import { applyModelOverrideToSessionEntry } from "../../sessions/model-overrides.js";
import { normalizeOptionalString } from "../../shared/string-coerce.js";
import type { MsgContext, TemplateContext } from "../templating.js";
import { resolveModelDirectiveSelection, type ModelDirectiveSelection } from "./model-selection.js";
@@ -104,7 +105,7 @@ export async function applyResetModelOverride(params: {
if (!params.resetTriggered) {
return {};
}
const rawBody = params.bodyStripped?.trim();
const rawBody = normalizeOptionalString(params.bodyStripped);
if (!rawBody) {
return {};
}