diff --git a/src/agents/agent-scope.ts b/src/agents/agent-scope.ts index 66b823de493..271a8488d4a 100644 --- a/src/agents/agent-scope.ts +++ b/src/agents/agent-scope.ts @@ -14,6 +14,7 @@ import { import { lowercasePreservingWhitespace, normalizeLowercaseStringOrEmpty, + normalizeOptionalString, readStringValue, resolvePrimaryStringValue, } from "../shared/string-coerce.js"; @@ -216,7 +217,7 @@ export function resolveFallbackAgentId(params: { agentId?: string | null; sessionKey?: string | null; }): string { - const explicitAgentId = typeof params.agentId === "string" ? params.agentId.trim() : ""; + const explicitAgentId = normalizeOptionalString(params.agentId) ?? ""; if (explicitAgentId) { return normalizeAgentId(explicitAgentId); } diff --git a/src/agents/bootstrap-budget.ts b/src/agents/bootstrap-budget.ts index 060541925b3..997dc29ab97 100644 --- a/src/agents/bootstrap-budget.ts +++ b/src/agents/bootstrap-budget.ts @@ -1,4 +1,5 @@ import path from "node:path"; +import { normalizeOptionalString } from "../shared/string-coerce.js"; import type { EmbeddedContextFile } from "./pi-embedded-helpers.js"; import type { WorkspaceBootstrapFile } from "./workspace.js"; @@ -74,7 +75,7 @@ function normalizeSeenSignatures(signatures?: string[]): string[] { const seen = new Set(); const result: string[] = []; for (const signature of signatures) { - const value = typeof signature === "string" ? signature.trim() : ""; + const value = normalizeOptionalString(signature) ?? ""; if (!value || seen.has(value)) { continue; } @@ -116,7 +117,7 @@ export function resolveBootstrapWarningSignaturesSeen(report?: { } const single = typeof truncation?.promptWarningSignature === "string" - ? truncation.promptWarningSignature.trim() + ? (normalizeOptionalString(truncation.promptWarningSignature) ?? "") : ""; return single ? [single] : []; } @@ -128,7 +129,7 @@ export function buildBootstrapInjectionStats(params: { const injectedByPath = new Map(); const injectedByBaseName = new Map(); for (const file of params.injectedFiles) { - const pathValue = typeof file.path === "string" ? file.path.trim() : ""; + const pathValue = normalizeOptionalString(file.path) ?? ""; if (!pathValue) { continue; } @@ -142,7 +143,7 @@ export function buildBootstrapInjectionStats(params: { } } return params.bootstrapFiles.map((file) => { - const pathValue = typeof file.path === "string" ? file.path.trim() : ""; + const pathValue = normalizeOptionalString(file.path) ?? ""; const rawChars = file.missing ? 0 : (file.content ?? "").trimEnd().length; const injected = (pathValue ? injectedByPath.get(pathValue) : undefined) ?? diff --git a/src/agents/model-alias-lines.ts b/src/agents/model-alias-lines.ts index d3361171881..af3118eb614 100644 --- a/src/agents/model-alias-lines.ts +++ b/src/agents/model-alias-lines.ts @@ -1,14 +1,17 @@ import type { OpenClawConfig } from "../config/config.js"; +import { normalizeOptionalString } from "../shared/string-coerce.js"; export function buildModelAliasLines(cfg?: OpenClawConfig) { const models = cfg?.agents?.defaults?.models ?? {}; const entries: Array<{ alias: string; model: string }> = []; for (const [keyRaw, entryRaw] of Object.entries(models)) { - const model = String(keyRaw ?? "").trim(); + const model = normalizeOptionalString(String(keyRaw ?? "")) ?? ""; if (!model) { continue; } - const alias = String((entryRaw as { alias?: string } | undefined)?.alias ?? "").trim(); + const alias = + normalizeOptionalString(String((entryRaw as { alias?: string } | undefined)?.alias ?? "")) ?? + ""; if (!alias) { continue; } diff --git a/src/agents/model-catalog.ts b/src/agents/model-catalog.ts index 01615961900..dfeac293ad8 100644 --- a/src/agents/model-catalog.ts +++ b/src/agents/model-catalog.ts @@ -1,7 +1,10 @@ import { type OpenClawConfig, loadConfig } from "../config/config.js"; import { createSubsystemLogger } from "../logging/subsystem.js"; import { augmentModelCatalogWithProviderPlugins } from "../plugins/provider-runtime.runtime.js"; -import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js"; +import { + normalizeLowercaseStringOrEmpty, + normalizeOptionalString, +} from "../shared/string-coerce.js"; import { resolveOpenClawAgentDir } from "./agent-paths.js"; import { ensureOpenClawModelsJson } from "./models-config.js"; import { normalizeProviderId } from "./provider-id.js"; @@ -132,18 +135,18 @@ export async function loadModelCatalog(params?: { const entries = Array.isArray(registry) ? registry : registry.getAll(); logStage("registry-read", `entries=${entries.length}`); for (const entry of entries) { - const id = String(entry?.id ?? "").trim(); + const id = normalizeOptionalString(String(entry?.id ?? "")) ?? ""; if (!id) { continue; } - const provider = String(entry?.provider ?? "").trim(); + const provider = normalizeOptionalString(String(entry?.provider ?? "")) ?? ""; if (!provider) { continue; } if (shouldSuppressBuiltInModel({ provider, id })) { continue; } - const name = String(entry?.name ?? id).trim() || id; + const name = normalizeOptionalString(String(entry?.name ?? id)) || id; const contextWindow = typeof entry?.contextWindow === "number" && entry.contextWindow > 0 ? entry.contextWindow diff --git a/src/agents/model-fallback.ts b/src/agents/model-fallback.ts index 9c69700475f..0ea8d13fdc5 100644 --- a/src/agents/model-fallback.ts +++ b/src/agents/model-fallback.ts @@ -5,6 +5,7 @@ import { } from "../config/model-input.js"; import { formatErrorMessage } from "../infra/errors.js"; import { createSubsystemLogger } from "../logging/subsystem.js"; +import { normalizeOptionalString } from "../shared/string-coerce.js"; import { sanitizeForLog } from "../terminal/ansi.js"; import { resolveAuthProfileOrder } from "./auth-profiles/order.js"; import { ensureAuthProfileStore, loadAuthProfileStoreForRuntime } from "./auth-profiles/store.js"; @@ -386,8 +387,8 @@ function resolveFallbackCandidates(params: { : null; const defaultProvider = primary?.provider ?? DEFAULT_PROVIDER; const defaultModel = primary?.model ?? DEFAULT_MODEL; - const providerRaw = String(params.provider ?? "").trim() || defaultProvider; - const modelRaw = String(params.model ?? "").trim() || defaultModel; + const providerRaw = normalizeOptionalString(String(params.provider ?? "")) || defaultProvider; + const modelRaw = normalizeOptionalString(String(params.model ?? "")) || defaultModel; const normalizedPrimary = normalizeModelRef(providerRaw, modelRaw); const configuredPrimary = normalizeModelRef(defaultProvider, defaultModel); const aliasIndex = buildModelAliasIndex({ @@ -455,7 +456,7 @@ const PROBE_STATE_TTL_MS = 24 * 60 * 60 * 1000; const MAX_PROBE_KEYS = 256; function resolveProbeThrottleKey(provider: string, agentDir?: string): string { - const scope = String(agentDir ?? "").trim(); + const scope = normalizeOptionalString(String(agentDir ?? "")) ?? ""; return scope ? `${scope}${PROBE_SCOPE_DELIMITER}${provider}` : provider; } diff --git a/src/agents/model-scan.ts b/src/agents/model-scan.ts index f40bf9d04f3..d2731f62e42 100644 --- a/src/agents/model-scan.ts +++ b/src/agents/model-scan.ts @@ -10,7 +10,10 @@ import { import { Type } from "@sinclair/typebox"; import { formatErrorMessage } from "../infra/errors.js"; import { inferParamBFromIdOrName } from "../shared/model-param-b.js"; -import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js"; +import { + normalizeLowercaseStringOrEmpty, + normalizeOptionalString, +} from "../shared/string-coerce.js"; import { normalizeProviderId } from "./provider-id.js"; const OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models"; @@ -193,7 +196,7 @@ async function fetchOpenRouterModels(fetchImpl: typeof fetch): Promise; - const id = typeof obj.id === "string" ? obj.id.trim() : ""; + const id = normalizeOptionalString(obj.id) ?? ""; if (!id) { return null; } diff --git a/src/agents/model-selection.ts b/src/agents/model-selection.ts index c5981065885..bef947cb127 100644 --- a/src/agents/model-selection.ts +++ b/src/agents/model-selection.ts @@ -11,6 +11,7 @@ import { resolvePluginSetupCliBackendRuntime } from "../plugins/setup-registry.r import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, + normalizeOptionalString, } from "../shared/string-coerce.js"; import { sanitizeForLog, stripAnsi } from "../terminal/ansi.js"; import { @@ -276,7 +277,9 @@ export function buildModelAliasIndex(params: { if (!parsed) { continue; } - const alias = String((entryRaw as { alias?: string } | undefined)?.alias ?? "").trim(); + const alias = + normalizeOptionalString(String((entryRaw as { alias?: string } | undefined)?.alias ?? "")) ?? + ""; if (!alias) { continue; } @@ -603,11 +606,11 @@ export function buildConfiguredModelCatalog(params: { cfg: OpenClawConfig }): Mo continue; } for (const model of provider.models) { - const id = typeof model?.id === "string" ? model.id.trim() : ""; + const id = normalizeOptionalString(model?.id) ?? ""; if (!id) { continue; } - const name = typeof model?.name === "string" && model.name.trim() ? model.name.trim() : id; + const name = normalizeOptionalString(model?.name) || id; const contextWindow = typeof model?.contextWindow === "number" && model.contextWindow > 0 ? model.contextWindow diff --git a/src/agents/openai-ws-message-conversion.ts b/src/agents/openai-ws-message-conversion.ts index 506f03e0c36..c734bc8590c 100644 --- a/src/agents/openai-ws-message-conversion.ts +++ b/src/agents/openai-ws-message-conversion.ts @@ -6,6 +6,7 @@ import { normalizeAssistantPhase, parseAssistantTextSignature, } from "../shared/chat-message-content.js"; +import { normalizeOptionalString } from "../shared/string-coerce.js"; import { normalizeOpenAIStrictToolParameters, resolveOpenAIStrictToolFlagForInventory, @@ -39,7 +40,7 @@ function toNonEmptyString(value: unknown): string | null { if (typeof value !== "string") { return null; } - const trimmed = value.trim(); + const trimmed = normalizeOptionalString(value) ?? ""; return trimmed.length > 0 ? trimmed : null; } @@ -223,7 +224,7 @@ function extractReasoningSummaryText(value: unknown): string { return ""; } const record = item as { text?: unknown }; - return typeof record.text === "string" ? record.text.trim() : ""; + return normalizeOptionalString(record.text) ?? ""; }) .filter(Boolean) .join("\n") @@ -239,7 +240,7 @@ function extractResponseReasoningText(item: unknown): string { if (summaryText) { return summaryText; } - return typeof record.content === "string" ? record.content.trim() : ""; + return normalizeOptionalString(record.content) ?? ""; } export function convertTools( diff --git a/src/agents/pi-auth-credentials.ts b/src/agents/pi-auth-credentials.ts index bf35328843d..42fc4b03704 100644 --- a/src/agents/pi-auth-credentials.ts +++ b/src/agents/pi-auth-credentials.ts @@ -1,3 +1,4 @@ +import { normalizeOptionalString } from "../shared/string-coerce.js"; import type { AuthProfileCredential, AuthProfileStore } from "./auth-profiles.js"; import { normalizeProviderId } from "./model-selection.js"; @@ -14,7 +15,7 @@ export type PiCredentialMap = Record; export function convertAuthProfileCredentialToPi(cred: AuthProfileCredential): PiCredential | null { if (cred.type === "api_key") { - const key = typeof cred.key === "string" ? cred.key.trim() : ""; + const key = normalizeOptionalString(cred.key) ?? ""; if (!key) { return null; } @@ -22,7 +23,7 @@ export function convertAuthProfileCredentialToPi(cred: AuthProfileCredential): P } if (cred.type === "token") { - const token = typeof cred.token === "string" ? cred.token.trim() : ""; + const token = normalizeOptionalString(cred.token) ?? ""; if (!token) { return null; } @@ -37,8 +38,8 @@ export function convertAuthProfileCredentialToPi(cred: AuthProfileCredential): P } if (cred.type === "oauth") { - const access = typeof cred.access === "string" ? cred.access.trim() : ""; - const refresh = typeof cred.refresh === "string" ? cred.refresh.trim() : ""; + const access = normalizeOptionalString(cred.access) ?? ""; + const refresh = normalizeOptionalString(cred.refresh) ?? ""; if (!access || !refresh || !Number.isFinite(cred.expires) || cred.expires <= 0) { return null; } @@ -56,7 +57,7 @@ export function convertAuthProfileCredentialToPi(cred: AuthProfileCredential): P export function resolvePiCredentialMapFromStore(store: AuthProfileStore): PiCredentialMap { const credentials: PiCredentialMap = {}; for (const credential of Object.values(store.profiles)) { - const provider = normalizeProviderId(String(credential.provider ?? "")).trim(); + const provider = normalizeProviderId(String(credential.provider ?? "")); if (!provider || credentials[provider]) { continue; } diff --git a/src/agents/pi-embedded-messaging.ts b/src/agents/pi-embedded-messaging.ts index c586c5ac96a..04ef97ffcb7 100644 --- a/src/agents/pi-embedded-messaging.ts +++ b/src/agents/pi-embedded-messaging.ts @@ -1,4 +1,5 @@ import { getChannelPlugin, normalizeChannelId } from "../channels/plugins/index.js"; +import { normalizeOptionalString } from "../shared/string-coerce.js"; export type MessagingToolSend = { tool: string; @@ -23,7 +24,7 @@ export function isMessagingToolSendAction( toolName: string, args: Record, ): boolean { - const action = typeof args.action === "string" ? args.action.trim() : ""; + const action = normalizeOptionalString(args.action) ?? ""; if (toolName === "sessions_send") { return true; } diff --git a/src/agents/pi-embedded-subscribe.handlers.messages.ts b/src/agents/pi-embedded-subscribe.handlers.messages.ts index 49474947bdf..d5fa9c26401 100644 --- a/src/agents/pi-embedded-subscribe.handlers.messages.ts +++ b/src/agents/pi-embedded-subscribe.handlers.messages.ts @@ -6,6 +6,7 @@ import { isSilentReplyText, SILENT_REPLY_TOKEN } from "../auto-reply/tokens.js"; import { emitAgentEvent } from "../infra/agent-events.js"; import { createInlineCodeState } from "../markdown/code-spans.js"; import { resolveAssistantMessagePhase } from "../shared/chat-message-content.js"; +import { normalizeOptionalString } from "../shared/string-coerce.js"; import { isMessagingToolDuplicateNormalized, normalizeTextForComparison, @@ -75,8 +76,8 @@ function isTranscriptOnlyOpenClawAssistantMessage(message: AgentMessage | undefi if (!message || message.role !== "assistant") { return false; } - const provider = typeof message.provider === "string" ? message.provider.trim() : ""; - const model = typeof message.model === "string" ? message.model.trim() : ""; + const provider = normalizeOptionalString(message.provider) ?? ""; + const model = normalizeOptionalString(message.model) ?? ""; return provider === "openclaw" && (model === "delivery-mirror" || model === "gateway-injected"); } diff --git a/src/agents/pi-embedded-subscribe.handlers.tools.ts b/src/agents/pi-embedded-subscribe.handlers.tools.ts index cb4cd71d75e..c1c80178df4 100644 --- a/src/agents/pi-embedded-subscribe.handlers.tools.ts +++ b/src/agents/pi-embedded-subscribe.handlers.tools.ts @@ -342,8 +342,8 @@ function readExecApprovalPendingDetails(result: unknown): { if (details.status !== "approval-pending") { return null; } - const approvalId = typeof details.approvalId === "string" ? details.approvalId.trim() : ""; - const approvalSlug = typeof details.approvalSlug === "string" ? details.approvalSlug.trim() : ""; + const approvalId = readStringValue(details.approvalId) ?? ""; + const approvalSlug = readStringValue(details.approvalSlug) ?? ""; const command = typeof details.command === "string" ? details.command : ""; const host = details.host === "node" ? "node" : details.host === "gateway" ? "gateway" : null; if (!approvalId || !approvalSlug || !command || !host) { diff --git a/src/agents/session-async-task-status.ts b/src/agents/session-async-task-status.ts index e4964e00dba..1ba4bae290a 100644 --- a/src/agents/session-async-task-status.ts +++ b/src/agents/session-async-task-status.ts @@ -1,3 +1,4 @@ +import { normalizeOptionalString } from "../shared/string-coerce.js"; import { listTasksForOwnerKey } from "../tasks/runtime-internal.js"; import type { TaskRecord, TaskRuntime, TaskStatus } from "../tasks/task-registry.types.js"; @@ -10,13 +11,13 @@ export function findActiveSessionTask(params: { statuses?: ReadonlySet; sourceIdPrefix?: string; }): TaskRecord | undefined { - const normalizedSessionKey = params.sessionKey?.trim(); + const normalizedSessionKey = normalizeOptionalString(params.sessionKey); if (!normalizedSessionKey) { return undefined; } const statuses = params.statuses ?? DEFAULT_ACTIVE_STATUSES; - const taskKind = params.taskKind?.trim(); - const sourceIdPrefix = params.sourceIdPrefix?.trim(); + const taskKind = normalizeOptionalString(params.taskKind); + const sourceIdPrefix = normalizeOptionalString(params.sourceIdPrefix); const matches = listTasksForOwnerKey(normalizedSessionKey).filter((task) => { if (task.scopeKind !== "session") { return false; @@ -31,7 +32,7 @@ export function findActiveSessionTask(params: { return false; } if (sourceIdPrefix) { - const sourceId = task.sourceId?.trim() ?? ""; + const sourceId = normalizeOptionalString(task.sourceId) ?? ""; if (sourceId !== sourceIdPrefix && !sourceId.startsWith(`${sourceIdPrefix}:`)) { return false; } diff --git a/src/agents/skills/source.ts b/src/agents/skills/source.ts index 5bb07f4abc8..6ad1ac7027f 100644 --- a/src/agents/skills/source.ts +++ b/src/agents/skills/source.ts @@ -1,3 +1,4 @@ +import { normalizeOptionalString } from "../../shared/string-coerce.js"; import type { Skill } from "./skill-contract.js"; type SkillSourceCompat = Skill & { @@ -8,11 +9,10 @@ type SkillSourceCompat = Skill & { export function resolveSkillSource(skill: Skill): string { const compatSkill = skill as SkillSourceCompat; - const canonical = typeof compatSkill.source === "string" ? compatSkill.source.trim() : ""; + const canonical = normalizeOptionalString(compatSkill.source) ?? ""; if (canonical) { return canonical; } - const legacy = - typeof compatSkill.sourceInfo?.source === "string" ? compatSkill.sourceInfo.source.trim() : ""; + const legacy = normalizeOptionalString(compatSkill.sourceInfo?.source) ?? ""; return legacy || "unknown"; } diff --git a/src/agents/tool-description-summary.ts b/src/agents/tool-description-summary.ts index 67d38cca095..1c0b6b74a15 100644 --- a/src/agents/tool-description-summary.ts +++ b/src/agents/tool-description-summary.ts @@ -1,3 +1,5 @@ +import { normalizeOptionalString } from "../shared/string-coerce.js"; + function normalizeSummaryWhitespace(value: string): string { return value.replace(/\s+/g, " ").trim(); } @@ -41,12 +43,12 @@ export function summarizeToolDescriptionText(params: { displaySummary?: string | null; maxLen?: number; }): string { - const explicit = typeof params.displaySummary === "string" ? params.displaySummary.trim() : ""; + const explicit = normalizeOptionalString(params.displaySummary) ?? ""; if (explicit) { return truncateSummary(normalizeSummaryWhitespace(explicit), params.maxLen); } - const raw = typeof params.rawDescription === "string" ? params.rawDescription.trim() : ""; + const raw = normalizeOptionalString(params.rawDescription) ?? ""; if (!raw) { return "Tool"; } @@ -92,7 +94,7 @@ export function describeToolForVerbose(params: { fallback: string; maxLen?: number; }): string { - const raw = typeof params.rawDescription === "string" ? params.rawDescription.trim() : ""; + const raw = normalizeOptionalString(params.rawDescription) ?? ""; if (!raw) { return params.fallback; } diff --git a/src/agents/tool-display-common.ts b/src/agents/tool-display-common.ts index fcb280ddd8d..487682de7da 100644 --- a/src/agents/tool-display-common.ts +++ b/src/agents/tool-display-common.ts @@ -104,7 +104,7 @@ export function coerceDisplayValue( if (!trimmed) { return undefined; } - const firstLine = trimmed.split(/\r?\n/)[0]?.trim() ?? ""; + const firstLine = normalizeOptionalString(trimmed.split(/\r?\n/)[0]) ?? ""; if (!firstLine) { return undefined; } @@ -231,8 +231,7 @@ export function resolveWriteDetail(toolKey: string, args: unknown): string | und return undefined; } - const path = - resolvePathArg(record) ?? (typeof record.url === "string" ? record.url.trim() : undefined); + const path = resolvePathArg(record) ?? normalizeOptionalString(record.url); if (!path) { return undefined; } @@ -264,7 +263,7 @@ export function resolveWebSearchDetail(args: unknown): string | undefined { return undefined; } - const query = typeof record.query === "string" ? record.query.trim() : undefined; + const query = normalizeOptionalString(record.query); const count = typeof record.count === "number" && Number.isFinite(record.count) && record.count > 0 ? Math.floor(record.count) @@ -283,12 +282,12 @@ export function resolveWebFetchDetail(args: unknown): string | undefined { return undefined; } - const url = typeof record.url === "string" ? record.url.trim() : undefined; + const url = normalizeOptionalString(record.url); if (!url) { return undefined; } - const mode = typeof record.extractMode === "string" ? record.extractMode.trim() : undefined; + const mode = normalizeOptionalString(record.extractMode); const maxChars = typeof record.maxChars === "number" && Number.isFinite(record.maxChars) && record.maxChars > 0 ? Math.floor(record.maxChars) diff --git a/src/agents/tools-effective-inventory.ts b/src/agents/tools-effective-inventory.ts index 0fee4b5e8f2..22ca48dcdce 100644 --- a/src/agents/tools-effective-inventory.ts +++ b/src/agents/tools-effective-inventory.ts @@ -1,6 +1,9 @@ import type { OpenClawConfig } from "../config/config.js"; import { getPluginToolMeta } from "../plugins/tools.js"; -import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js"; +import { + normalizeLowercaseStringOrEmpty, + normalizeOptionalString, +} from "../shared/string-coerce.js"; import { resolveAgentDir, resolveAgentWorkspaceDir, resolveSessionAgentId } from "./agent-scope.js"; import { getChannelAgentToolMeta } from "./channel-tools.js"; import { resolveModel } from "./pi-embedded-runner/model.js"; @@ -63,7 +66,7 @@ export type ResolveEffectiveToolInventoryParams = { }; function resolveEffectiveToolLabel(tool: AnyAgentTool): string { - const rawLabel = typeof tool.label === "string" ? tool.label.trim() : ""; + const rawLabel = normalizeOptionalString(tool.label) ?? ""; if ( rawLabel && normalizeLowercaseStringOrEmpty(rawLabel) !== normalizeLowercaseStringOrEmpty(tool.name) @@ -74,7 +77,7 @@ function resolveEffectiveToolLabel(tool: AnyAgentTool): string { } function resolveRawToolDescription(tool: AnyAgentTool): string { - return typeof tool.description === "string" ? tool.description.trim() : ""; + return normalizeOptionalString(tool.description) ?? ""; } function summarizeToolDescription(tool: AnyAgentTool): string { diff --git a/src/agents/tools/sessions-resolution.ts b/src/agents/tools/sessions-resolution.ts index c4a1e0e7c2c..dd6b26f10d3 100644 --- a/src/agents/tools/sessions-resolution.ts +++ b/src/agents/tools/sessions-resolution.ts @@ -66,10 +66,7 @@ export async function listSpawnedSessionKeys(params: { }, }); const sessions = Array.isArray(list?.sessions) ? list.sessions : []; - const keys = sessions - .map((entry) => (typeof entry?.key === "string" ? entry.key : "")) - .map((value) => value.trim()) - .filter(Boolean); + const keys = sessions.map((entry) => normalizeOptionalString(entry?.key) ?? "").filter(Boolean); return new Set(keys); } catch { return new Set(); @@ -238,7 +235,7 @@ async function callGatewayResolveSessionId(params: { method: "sessions.resolve", params: buildSessionIdResolveParams(params), }); - const key = typeof result?.key === "string" ? result.key.trim() : ""; + const key = normalizeOptionalString(result?.key) ?? ""; if (!key) { throw new Error( `Session not found: ${params.sessionId} (use the full sessionKey from sessions_list)`, @@ -298,7 +295,7 @@ async function resolveSessionKeyFromKey(params: { spawnedBy: params.restrictToSpawned ? params.requesterInternalKey : undefined, }, }); - const key = typeof result?.key === "string" ? result.key.trim() : ""; + const key = normalizeOptionalString(result?.key) ?? ""; if (!key) { return null; }