diff --git a/extensions/line/src/bot-message-context.ts b/extensions/line/src/bot-message-context.ts index 8a65d3daf34..cb2989b2506 100644 --- a/extensions/line/src/bot-message-context.ts +++ b/extensions/line/src/bot-message-context.ts @@ -24,7 +24,7 @@ import { 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 { resolveLineGroupConfigEntry } from "./group-keys.js"; import type { LineGroupConfig, ResolvedLineAccount } from "./types.js"; type EventSource = webhook.Source | undefined; @@ -77,10 +77,9 @@ function buildPeerId(source: EventSource): string { if (!source) { return "unknown"; } - const groupKey = resolveLineGroupHistoryKey({ - groupId: source.type === "group" ? source.groupId : undefined, - roomId: source.type === "room" ? source.roomId : undefined, - }); + const groupKey = + normalizeOptionalString(source.type === "group" ? source.groupId : undefined) ?? + normalizeOptionalString(source.type === "room" ? source.roomId : undefined); if (groupKey) { return groupKey; } diff --git a/extensions/line/src/group-keys.test.ts b/extensions/line/src/group-keys.test.ts index 673d0af1136..c079bc77455 100644 --- a/extensions/line/src/group-keys.test.ts +++ b/extensions/line/src/group-keys.test.ts @@ -2,7 +2,6 @@ import { describe, expect, it } from "vitest"; import { resolveExactLineGroupConfigKey, resolveLineGroupConfigEntry, - resolveLineGroupHistoryKey, resolveLineGroupLookupIds, resolveLineGroupsConfig, } from "./group-keys.js"; @@ -39,14 +38,6 @@ describe("resolveLineGroupConfigEntry", () => { }); }); -describe("resolveLineGroupHistoryKey", () => { - it("uses the raw group or room id as the shared LINE peer key", () => { - expect(resolveLineGroupHistoryKey({ groupId: "g1" })).toBe("g1"); - expect(resolveLineGroupHistoryKey({ roomId: "r1" })).toBe("r1"); - expect(resolveLineGroupHistoryKey({})).toBeUndefined(); - }); -}); - describe("account-scoped LINE groups", () => { it("resolves the effective account-scoped groups map", () => { const cfg = { diff --git a/extensions/line/src/group-keys.ts b/extensions/line/src/group-keys.ts index 41a4f6ae57c..46aeb7fec92 100644 --- a/extensions/line/src/group-keys.ts +++ b/extensions/line/src/group-keys.ts @@ -1,7 +1,6 @@ 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[] { @@ -64,10 +63,3 @@ export function resolveExactLineGroupConfigKey(params: { Object.hasOwn(groups, candidate), ); } - -export function resolveLineGroupHistoryKey(params: { - groupId?: string | null; - roomId?: string | null; -}): string | undefined { - return normalizeOptionalString(params.groupId) ?? normalizeOptionalString(params.roomId); -} diff --git a/src/agents/identity.ts b/src/agents/identity.ts index d6cd654ea76..8500a35775d 100644 --- a/src/agents/identity.ts +++ b/src/agents/identity.ts @@ -1,5 +1,4 @@ import type { OpenClawConfig, HumanDelayConfig, IdentityConfig } from "../config/config.js"; -import { normalizeOptionalString } from "../shared/string-coerce.js"; import { resolveAgentConfig } from "./agent-scope.js"; const DEFAULT_ACK_REACTION = "👀"; @@ -57,11 +56,6 @@ export function resolveIdentityNamePrefix( return `[${name}]`; } -/** Returns just the identity name (without brackets) for template context. */ -export function resolveIdentityName(cfg: OpenClawConfig, agentId: string): string | undefined { - return normalizeOptionalString(resolveAgentIdentity(cfg, agentId)?.name); -} - export function resolveMessagePrefix( cfg: OpenClawConfig, agentId: string, diff --git a/src/auto-reply/reply/commands-acp/diagnostics.ts b/src/auto-reply/reply/commands-acp/diagnostics.ts index b13cdca4199..e5168aa453f 100644 --- a/src/auto-reply/reply/commands-acp/diagnostics.ts +++ b/src/auto-reply/reply/commands-acp/diagnostics.ts @@ -15,7 +15,6 @@ import { ACP_SESSIONS_USAGE, formatAcpCapabilitiesText, resolveAcpInstallCommandHint, - resolveConfiguredAcpBackendId, stopWithText, } from "./shared.js"; import { resolveBoundAcpThreadSessionKey } from "./targets.js"; @@ -28,7 +27,7 @@ export async function handleAcpDoctorAction( return stopWithText(`⚠️ ${ACP_DOCTOR_USAGE}`); } - const backendId = resolveConfiguredAcpBackendId(params.cfg); + const backendId = normalizeOptionalString(params.cfg.acp?.backend) ?? "acpx"; const installHint = resolveAcpInstallCommandHint(params.cfg); const registeredBackend = getAcpRuntimeBackend(backendId); const managerSnapshot = getAcpSessionManager().getObservabilitySnapshot(params.cfg); @@ -116,7 +115,7 @@ export function handleAcpInstallAction( if (restTokens.length > 0) { return stopWithText(`⚠️ ${ACP_INSTALL_USAGE}`); } - const backendId = resolveConfiguredAcpBackendId(params.cfg); + const backendId = normalizeOptionalString(params.cfg.acp?.backend) ?? "acpx"; const installHint = resolveAcpInstallCommandHint(params.cfg); const lines = [ "ACP install:", diff --git a/src/auto-reply/reply/commands-acp/install-hints.test.ts b/src/auto-reply/reply/commands-acp/install-hints.test.ts index 33708e7d0a2..ed64f6faeac 100644 --- a/src/auto-reply/reply/commands-acp/install-hints.test.ts +++ b/src/auto-reply/reply/commands-acp/install-hints.test.ts @@ -1,7 +1,7 @@ import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../../config/config.js"; -import { resolveAcpInstallCommandHint, resolveConfiguredAcpBackendId } from "./install-hints.js"; +import { resolveAcpInstallCommandHint } from "./install-hints.js"; function withAcpConfig(acp: OpenClawConfig["acp"]): OpenClawConfig { return { acp } as OpenClawConfig; @@ -35,7 +35,6 @@ describe("ACP install hints", () => { it("returns generic plugin hint for non-acpx backend", () => { const cfg = withAcpConfig({ backend: "custom-backend" }); - expect(resolveConfiguredAcpBackendId(cfg)).toBe("custom-backend"); expect(resolveAcpInstallCommandHint(cfg)).toContain('ACP backend "custom-backend"'); }); }); diff --git a/src/auto-reply/reply/commands-acp/install-hints.ts b/src/auto-reply/reply/commands-acp/install-hints.ts index 9dff3c81454..ceee50f6f14 100644 --- a/src/auto-reply/reply/commands-acp/install-hints.ts +++ b/src/auto-reply/reply/commands-acp/install-hints.ts @@ -5,17 +5,13 @@ import { resolveBundledPluginWorkspaceSourcePath } from "../../../plugins/bundle import { resolveBundledPluginInstallCommandHint } from "../../../plugins/bundled-sources.js"; import { normalizeOptionalString } from "../../../shared/string-coerce.js"; -export function resolveConfiguredAcpBackendId(cfg: OpenClawConfig): string { - return normalizeOptionalString(cfg.acp?.backend) || "acpx"; -} - export function resolveAcpInstallCommandHint(cfg: OpenClawConfig): string { const configured = normalizeOptionalString(cfg.acp?.runtime?.installCommand); if (configured) { return configured; } const workspaceDir = process.cwd(); - const backendId = resolveConfiguredAcpBackendId(cfg).toLowerCase(); + const backendId = normalizeOptionalString(cfg.acp?.backend)?.toLowerCase() ?? "acpx"; if (backendId === "acpx") { const workspaceLocalPath = resolveBundledPluginWorkspaceSourcePath({ rootDir: workspaceDir, diff --git a/src/auto-reply/reply/commands-acp/shared.ts b/src/auto-reply/reply/commands-acp/shared.ts index e064c35f9be..c589a347126 100644 --- a/src/auto-reply/reply/commands-acp/shared.ts +++ b/src/auto-reply/reply/commands-acp/shared.ts @@ -8,7 +8,7 @@ import { normalizeAgentId } from "../../../routing/session-key.js"; import { normalizeOptionalString } from "../../../shared/string-coerce.js"; import type { CommandHandlerResult, HandleCommandsParams } from "../commands-types.js"; import { resolveAcpCommandChannel, resolveAcpCommandThreadId } from "./context.js"; -export { resolveAcpInstallCommandHint, resolveConfiguredAcpBackendId } from "./install-hints.js"; +export { resolveAcpInstallCommandHint } from "./install-hints.js"; export const COMMAND = "/acp"; export const ACP_SPAWN_USAGE = diff --git a/src/channels/reply-prefix.ts b/src/channels/reply-prefix.ts index e3292ca80ea..857dd71ca52 100644 --- a/src/channels/reply-prefix.ts +++ b/src/channels/reply-prefix.ts @@ -1,10 +1,11 @@ -import { resolveEffectiveMessagesConfig, resolveIdentityName } from "../agents/identity.js"; +import { resolveAgentIdentity, resolveEffectiveMessagesConfig } from "../agents/identity.js"; import { extractShortModelName, type ResponsePrefixContext, } from "../auto-reply/reply/response-prefix-template.js"; import type { GetReplyOptions } from "../auto-reply/types.js"; import type { OpenClawConfig } from "../config/config.js"; +import { normalizeOptionalString } from "../shared/string-coerce.js"; type ModelSelectionContext = Parameters>[0]; @@ -28,7 +29,7 @@ export function createReplyPrefixContext(params: { }): ReplyPrefixContextBundle { const { cfg, agentId } = params; const prefixContext: ResponsePrefixContext = { - identityName: resolveIdentityName(cfg, agentId), + identityName: normalizeOptionalString(resolveAgentIdentity(cfg, agentId)?.name), }; const onModelSelected = (ctx: ModelSelectionContext) => { diff --git a/src/plugins/provider-registry-shared.test.ts b/src/plugins/provider-registry-shared.test.ts index 95c84c472ad..b756bfb0be8 100644 --- a/src/plugins/provider-registry-shared.test.ts +++ b/src/plugins/provider-registry-shared.test.ts @@ -1,13 +1,10 @@ import { describe, expect, it } from "vitest"; -import { - buildCapabilityProviderMaps, - normalizeCapabilityProviderId, -} from "./provider-registry-shared.js"; +import { buildCapabilityProviderMaps } from "./provider-registry-shared.js"; describe("provider registry shared", () => { it("normalizes provider ids case-insensitively", () => { - expect(normalizeCapabilityProviderId(" OpenAI ")).toBe("openai"); - expect(normalizeCapabilityProviderId(" ")).toBeUndefined(); + const { canonical } = buildCapabilityProviderMaps([{ id: " OpenAI " }, { id: " " }]); + expect([...canonical.keys()]).toEqual(["openai"]); }); it("indexes providers by id and alias", () => { diff --git a/src/plugins/provider-registry-shared.ts b/src/plugins/provider-registry-shared.ts index 48a38f3fe45..f23fe7b96fc 100644 --- a/src/plugins/provider-registry-shared.ts +++ b/src/plugins/provider-registry-shared.ts @@ -1,14 +1,9 @@ import { normalizeOptionalString } from "../shared/string-coerce.js"; -export function normalizeCapabilityProviderId(providerId: string | undefined): string | undefined { - return normalizeOptionalString(providerId)?.toLowerCase(); -} - export function buildCapabilityProviderMaps( providers: readonly T[], - normalizeId: ( - providerId: string | undefined, - ) => string | undefined = normalizeCapabilityProviderId, + normalizeId: (providerId: string | undefined) => string | undefined = (providerId) => + normalizeOptionalString(providerId)?.toLowerCase(), ): { canonical: Map; aliases: Map; diff --git a/src/realtime-voice/provider-registry.ts b/src/realtime-voice/provider-registry.ts index c7146580b79..ef5304e0665 100644 --- a/src/realtime-voice/provider-registry.ts +++ b/src/realtime-voice/provider-registry.ts @@ -1,16 +1,14 @@ import type { OpenClawConfig } from "../config/config.js"; import { resolvePluginCapabilityProviders } from "../plugins/capability-provider-runtime.js"; -import { - buildCapabilityProviderMaps, - normalizeCapabilityProviderId, -} from "../plugins/provider-registry-shared.js"; +import { buildCapabilityProviderMaps } from "../plugins/provider-registry-shared.js"; import type { RealtimeVoiceProviderPlugin } from "../plugins/types.js"; +import { normalizeOptionalString } from "../shared/string-coerce.js"; import type { RealtimeVoiceProviderId } from "./provider-types.js"; export function normalizeRealtimeVoiceProviderId( providerId: string | undefined, ): RealtimeVoiceProviderId | undefined { - return normalizeCapabilityProviderId(providerId); + return normalizeOptionalString(providerId)?.toLowerCase(); } function resolveRealtimeVoiceProviderEntries(cfg?: OpenClawConfig): RealtimeVoiceProviderPlugin[] { diff --git a/src/tasks/task-registry.ts b/src/tasks/task-registry.ts index 5014bb32447..93315424a40 100644 --- a/src/tasks/task-registry.ts +++ b/src/tasks/task-registry.ts @@ -523,8 +523,8 @@ function taskRunScopeKey( return [ task.runtime, task.scopeKind, - normalizeComparableText(task.ownerKey), - normalizeComparableText(task.childSessionKey), + normalizeOptionalString(task.ownerKey) ?? "", + normalizeOptionalString(task.childSessionKey) ?? "", ].join("\u0000"); } @@ -562,9 +562,10 @@ function getPeerTasksForDelivery(task: TaskRecord): TaskRecord[] { (candidate) => candidate.runtime === task.runtime && candidate.scopeKind === task.scopeKind && - normalizeComparableText(candidate.ownerKey) === normalizeComparableText(task.ownerKey) && - normalizeComparableText(candidate.childSessionKey) === - normalizeComparableText(task.childSessionKey), + (normalizeOptionalString(candidate.ownerKey) ?? "") === + (normalizeOptionalString(task.ownerKey) ?? "") && + (normalizeOptionalString(candidate.childSessionKey) ?? "") === + (normalizeOptionalString(task.childSessionKey) ?? ""), ); } @@ -583,10 +584,6 @@ function pickPreferredRunIdTask(matches: TaskRecord[]): TaskRecord | undefined { })[0]; } -function normalizeComparableText(value: string | undefined): string { - return normalizeOptionalString(value) ?? ""; -} - function compareTasksNewestFirst( left: Pick & { insertionIndex?: number }, right: Pick & { insertionIndex?: number }, @@ -614,18 +611,21 @@ function findExistingTaskForCreate(params: { (task) => task.runtime === params.runtime && task.scopeKind === params.scopeKind && - normalizeComparableText(task.ownerKey) === normalizeComparableText(params.ownerKey) && - normalizeComparableText(task.childSessionKey) === - normalizeComparableText(params.childSessionKey) && - normalizeComparableText(task.parentFlowId) === - normalizeComparableText(params.parentFlowId), + (normalizeOptionalString(task.ownerKey) ?? "") === + (normalizeOptionalString(params.ownerKey) ?? "") && + (normalizeOptionalString(task.childSessionKey) ?? "") === + (normalizeOptionalString(params.childSessionKey) ?? "") && + (normalizeOptionalString(task.parentFlowId) ?? "") === + (normalizeOptionalString(params.parentFlowId) ?? ""), ) : []; const exact = runId ? runScopeMatches.find( (task) => - normalizeComparableText(task.label) === normalizeComparableText(params.label) && - normalizeComparableText(task.task) === normalizeComparableText(params.task), + (normalizeOptionalString(task.label) ?? "") === + (normalizeOptionalString(params.label) ?? "") && + (normalizeOptionalString(task.task) ?? "") === + (normalizeOptionalString(params.task) ?? ""), ) : undefined; if (exact) { @@ -688,11 +688,11 @@ function mergeExistingTaskForCreate( } const nextLabel = params.label?.trim(); if (params.preferMetadata) { - if (nextLabel && normalizeComparableText(existing.label) !== nextLabel) { + if (nextLabel && (normalizeOptionalString(existing.label) ?? "") !== nextLabel) { patch.label = nextLabel; } const nextTask = params.task.trim(); - if (nextTask && normalizeComparableText(existing.task) !== nextTask) { + if (nextTask && (normalizeOptionalString(existing.task) ?? "") !== nextTask) { patch.task = nextTask; } } else if (nextLabel && !existing.label?.trim()) { diff --git a/src/tts/provider-registry.ts b/src/tts/provider-registry.ts index 1b9f3041499..bd7d84b5103 100644 --- a/src/tts/provider-registry.ts +++ b/src/tts/provider-registry.ts @@ -1,16 +1,14 @@ import type { OpenClawConfig } from "../config/config.js"; import { resolvePluginCapabilityProviders } from "../plugins/capability-provider-runtime.js"; -import { - buildCapabilityProviderMaps, - normalizeCapabilityProviderId, -} from "../plugins/provider-registry-shared.js"; +import { buildCapabilityProviderMaps } from "../plugins/provider-registry-shared.js"; import type { SpeechProviderPlugin } from "../plugins/types.js"; +import { normalizeOptionalString } from "../shared/string-coerce.js"; import type { SpeechProviderId } from "./provider-types.js"; export function normalizeSpeechProviderId( providerId: string | undefined, ): SpeechProviderId | undefined { - return normalizeCapabilityProviderId(providerId); + return normalizeOptionalString(providerId)?.toLowerCase(); } function resolveSpeechProviderPluginEntries(cfg?: OpenClawConfig): SpeechProviderPlugin[] {