refactor: dedupe binding string readers

This commit is contained in:
Peter Steinberger
2026-04-07 05:20:34 +01:00
parent 326b36794f
commit 7e1f04f36a
7 changed files with 37 additions and 26 deletions

View File

@@ -6,6 +6,7 @@ import { listChannelCatalogEntries } from "../../plugins/channel-catalog-registr
import type { OpenClawPackageManifest } from "../../plugins/manifest.js";
import type { PluginPackageChannel, PluginPackageInstall } from "../../plugins/manifest.js";
import type { PluginOrigin } from "../../plugins/types.js";
import { normalizeOptionalString } from "../../shared/string-coerce.js";
import { isRecord, resolveConfigDir, resolveUserPath } from "../../utils.js";
import { resolveChannelExposure } from "./exposure.js";
import type { ChannelMeta } from "./types.js";
@@ -189,7 +190,7 @@ function toChannelMeta(params: {
selectionLabel,
...(detailLabel ? { detailLabel } : {}),
docsPath,
docsLabel: params.channel.docsLabel?.trim() || undefined,
docsLabel: normalizeOptionalString(params.channel.docsLabel),
blurb,
...(params.channel.aliases ? { aliases: params.channel.aliases } : {}),
...(params.channel.preferOver ? { preferOver: params.channel.preferOver } : {}),
@@ -230,7 +231,7 @@ function resolveInstallInfo(params: {
if (!npmSpec) {
return null;
}
let localPath = params.install?.localPath?.trim() || undefined;
let localPath = normalizeOptionalString(params.install?.localPath);
if (!localPath && params.workspaceDir && params.packageDir) {
localPath = path.relative(params.workspaceDir, params.packageDir) || undefined;
}
@@ -243,7 +244,7 @@ function resolveInstallInfo(params: {
}
function resolveCatalogPluginId(params: { pluginId?: string }): string | undefined {
return params.pluginId?.trim() || undefined;
return normalizeOptionalString(params.pluginId);
}
function buildCatalogEntryFromManifest(params: {

View File

@@ -1,5 +1,6 @@
import type { ConversationRef } from "../../infra/outbound/session-binding-service.js";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../routing/session-key.js";
import { normalizeOptionalString } from "../../shared/string-coerce.js";
import type {
CompiledConfiguredBinding,
ConfiguredBindingChannel,
@@ -54,7 +55,7 @@ export function toConfiguredBindingConversationRef(conversation: ConversationRef
channel,
accountId: normalizeAccountId(conversation.accountId),
conversationId,
parentConversationId: conversation.parentConversationId?.trim() || undefined,
parentConversationId: normalizeOptionalString(conversation.parentConversationId),
};
}

View File

@@ -5,6 +5,7 @@ import {
type ParsedThreadSessionSuffix,
type RawSessionConversationRef,
} from "../../sessions/session-key-utils.js";
import { normalizeOptionalString } from "../../shared/string-coerce.js";
import { normalizeChannelId as normalizeChatChannelId } from "../registry.js";
import { getLoadedChannelPlugin, normalizeChannelId as normalizeAnyChannelId } from "./registry.js";
@@ -115,10 +116,10 @@ function normalizeSessionConversationResolution(
return {
id: resolved.id.trim(),
threadId: resolved.threadId?.trim() || undefined,
threadId: normalizeOptionalString(resolved.threadId),
baseConversationId:
resolved.baseConversationId?.trim() ||
dedupeConversationIds(resolved.parentConversationCandidates ?? []).at(-1) ||
normalizeOptionalString(resolved.baseConversationId) ??
dedupeConversationIds(resolved.parentConversationCandidates ?? []).at(-1) ??
resolved.id.trim(),
parentConversationCandidates: dedupeConversationIds(
resolved.parentConversationCandidates ?? [],
@@ -251,7 +252,9 @@ export function resolveSessionThreadInfo(
}
return {
baseSessionKey: resolved.threadId ? resolved.baseSessionKey : sessionKey?.trim() || undefined,
baseSessionKey: resolved.threadId
? resolved.baseSessionKey
: normalizeOptionalString(sessionKey),
threadId: resolved.threadId,
};
}

View File

@@ -1,3 +1,4 @@
import { normalizeOptionalString } from "../../shared/string-coerce.js";
import {
getSessionBindingService,
type ConversationRef,
@@ -94,7 +95,7 @@ export function createBoundDeliveryRouter(
channel: input.requester.channel.trim().toLowerCase(),
accountId: input.requester.accountId.trim(),
conversationId: input.requester.conversationId.trim(),
parentConversationId: input.requester.parentConversationId?.trim() || undefined,
parentConversationId: normalizeOptionalString(input.requester.parentConversationId),
};
if (!requester.channel || !requester.conversationId) {
return {

View File

@@ -14,6 +14,7 @@ import { writeJsonAtomic } from "../infra/json-files.js";
import { type ConversationRef } from "../infra/outbound/session-binding-service.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import { resolveGlobalMap, resolveGlobalSingleton } from "../shared/global-singleton.js";
import { normalizeOptionalString } from "../shared/string-coerce.js";
import { getActivePluginRegistry } from "./runtime.js";
import type {
PluginConversationBinding,
@@ -162,11 +163,11 @@ function normalizeConversation(params: PluginBindingConversation): PluginBinding
channel: normalizeChannel(params.channel),
accountId: params.accountId.trim() || "default",
conversationId: params.conversationId.trim(),
parentConversationId: params.parentConversationId?.trim() || undefined,
parentConversationId: normalizeOptionalString(params.parentConversationId),
threadId:
typeof params.threadId === "number"
? Math.trunc(params.threadId)
: params.threadId?.toString().trim() || undefined,
: normalizeOptionalString(params.threadId?.toString()),
};
}
@@ -427,8 +428,8 @@ function buildBindingMetadata(params: {
pluginId: params.pluginId,
pluginName: params.pluginName,
pluginRoot: params.pluginRoot,
summary: params.summary?.trim() || undefined,
detachHint: params.detachHint?.trim() || undefined,
summary: normalizeOptionalString(params.summary),
detachHint: normalizeOptionalString(params.detachHint),
};
}
@@ -804,9 +805,9 @@ export async function requestPluginConversationBinding(params: {
pluginRoot: params.pluginRoot,
conversation,
requestedAt: Date.now(),
requestedBySenderId: params.requestedBySenderId?.trim() || undefined,
summary: params.binding?.summary?.trim() || undefined,
detachHint: params.binding?.detachHint?.trim() || undefined,
requestedBySenderId: normalizeOptionalString(params.requestedBySenderId),
summary: normalizeOptionalString(params.binding?.summary),
detachHint: normalizeOptionalString(params.binding?.detachHint),
};
pendingRequests.set(request.id, request);
logPluginBindingLifecycleEvent({

View File

@@ -1,6 +1,7 @@
import crypto from "node:crypto";
import { formatErrorMessage } from "../infra/errors.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import { normalizeOptionalString } from "../shared/string-coerce.js";
import {
getTaskFlowRegistryObservers,
getTaskFlowRegistryStore,
@@ -166,7 +167,7 @@ function assertControllerId(controllerId?: string | null): string {
}
function resolveFlowGoal(task: Pick<TaskRecord, "label" | "task">): string {
return task.label?.trim() || task.task.trim() || "Background task";
return normalizeOptionalString(task.label) ?? (task.task.trim() || "Background task");
}
function resolveFlowBlockedSummary(
@@ -175,7 +176,9 @@ function resolveFlowBlockedSummary(
if (task.status !== "succeeded" || task.terminalOutcome !== "blocked") {
return undefined;
}
return task.terminalSummary?.trim() || task.progressSummary?.trim() || undefined;
return (
normalizeOptionalString(task.terminalSummary) ?? normalizeOptionalString(task.progressSummary)
);
}
export function deriveTaskFlowStatusFromTask(
@@ -432,7 +435,7 @@ export function createTaskFlowForTask(params: {
notifyPolicy: params.task.notifyPolicy,
goal: resolveFlowGoal(params.task),
blockedTaskId:
terminalFlowStatus === "blocked" ? params.task.taskId.trim() || undefined : undefined,
terminalFlowStatus === "blocked" ? normalizeOptionalString(params.task.taskId) : undefined,
blockedSummary: resolveFlowBlockedSummary(params.task),
createdAt: params.task.createdAt,
updatedAt: params.task.lastEventAt ?? params.task.createdAt,

View File

@@ -6,6 +6,7 @@ import { requestHeartbeatNow } from "../infra/heartbeat-wake.js";
import { enqueueSystemEvent } from "../infra/system-events.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import { parseAgentSessionKey } from "../routing/session-key.js";
import { normalizeOptionalString } from "../shared/string-coerce.js";
import { normalizeDeliveryContext } from "../utils/delivery-context.js";
import { isDeliverableMessageChannel } from "../utils/message-channel.js";
import {
@@ -1437,17 +1438,17 @@ export function createTaskRecord(params: {
const record: TaskRecord = {
taskId,
runtime: params.runtime,
taskKind: params.taskKind?.trim() || undefined,
sourceId: params.sourceId?.trim() || undefined,
taskKind: normalizeOptionalString(params.taskKind),
sourceId: normalizeOptionalString(params.sourceId),
requesterSessionKey,
ownerKey,
scopeKind,
childSessionKey: params.childSessionKey,
parentFlowId: params.parentFlowId?.trim() || undefined,
parentTaskId: params.parentTaskId?.trim() || undefined,
agentId: params.agentId?.trim() || undefined,
runId: params.runId?.trim() || undefined,
label: params.label?.trim() || undefined,
parentFlowId: normalizeOptionalString(params.parentFlowId),
parentTaskId: normalizeOptionalString(params.parentTaskId),
agentId: normalizeOptionalString(params.agentId),
runId: normalizeOptionalString(params.runId),
label: normalizeOptionalString(params.label),
task: params.task,
status,
deliveryStatus,