From f1bdfca1edc4f4ae3cd69cd6e05a9f26f908ad6a Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 7 Apr 2026 10:06:02 +0100 Subject: [PATCH] refactor: dedupe reply gateway helpers --- src/agents/pi-embedded-error-observation.ts | 11 ++---- src/auto-reply/reply/abort.ts | 13 ++----- src/auto-reply/reply/dispatch-acp-delivery.ts | 33 +++++------------ src/auto-reply/reply/dispatch-acp.ts | 35 +++++++------------ src/gateway/sessions-history-http.ts | 7 +--- 5 files changed, 26 insertions(+), 73 deletions(-) diff --git a/src/agents/pi-embedded-error-observation.ts b/src/agents/pi-embedded-error-observation.ts index d70faa6ec8b..4f3590ead8a 100644 --- a/src/agents/pi-embedded-error-observation.ts +++ b/src/agents/pi-embedded-error-observation.ts @@ -72,14 +72,6 @@ function redactObservationText(text: string | undefined): string | undefined { }); } -function extractRequestId(text: string | undefined): string | undefined { - if (!text) { - return undefined; - } - const match = text.match(REQUEST_ID_RE); - return normalizeOptionalString(match?.[1]); -} - function buildObservationFingerprint(params: { raw: string; requestId?: string; @@ -123,7 +115,8 @@ export function buildApiErrorObservationFields(rawError?: string): { } try { const parsed = parseApiErrorInfo(trimmed); - const requestId = parsed?.requestId ?? extractRequestId(trimmed); + const requestId = + parsed?.requestId ?? normalizeOptionalString(trimmed.match(REQUEST_ID_RE)?.[1]); const requestIdHash = requestId ? redactIdentifier(requestId, { len: 12 }) : undefined; const rawFingerprint = buildObservationFingerprint({ raw: trimmed, diff --git a/src/auto-reply/reply/abort.ts b/src/auto-reply/reply/abort.ts index f4b14bebf0c..870c787af30 100644 --- a/src/auto-reply/reply/abort.ts +++ b/src/auto-reply/reply/abort.ts @@ -24,7 +24,7 @@ import { formatErrorMessage } from "../../infra/errors.js"; import { parseAgentSessionKey } from "../../routing/session-key.js"; import { normalizeOptionalString } from "../../shared/string-coerce.js"; import { resolveCommandAuthorization } from "../command-auth.js"; -import type { FinalizedMsgContext, MsgContext } from "../templating.js"; +import type { FinalizedMsgContext } from "../templating.js"; import { applyAbortCutoffToSessionEntry, resolveAbortCutoffFromContext, @@ -118,14 +118,6 @@ export function resolveSessionEntryForKey( return {}; } -function resolveAbortTargetKey(ctx: MsgContext): string | undefined { - const target = normalizeOptionalString(ctx.CommandTargetSessionKey); - if (target) { - return target; - } - return normalizeOptionalString(ctx.SessionKey); -} - function normalizeRequesterSessionKey( cfg: OpenClawConfig, key: string | undefined, @@ -232,7 +224,8 @@ export async function tryFastAbortFromMessage(params: { cfg: OpenClawConfig; }): Promise<{ handled: boolean; aborted: boolean; stoppedSubagents?: number }> { const { ctx, cfg } = params; - const targetKey = resolveAbortTargetKey(ctx); + const targetKey = + normalizeOptionalString(ctx.CommandTargetSessionKey) ?? normalizeOptionalString(ctx.SessionKey); // Use RawBody/CommandBody for abort detection (clean message without structural context). const raw = stripStructuralPrefixes(ctx.CommandBody ?? ctx.RawBody ?? ctx.Body ?? ""); const isGroup = normalizeOptionalString(ctx.ChatType)?.toLowerCase() === "group"; diff --git a/src/auto-reply/reply/dispatch-acp-delivery.ts b/src/auto-reply/reply/dispatch-acp-delivery.ts index 6e884bce78f..0b691b5cd07 100644 --- a/src/auto-reply/reply/dispatch-acp-delivery.ts +++ b/src/auto-reply/reply/dispatch-acp-delivery.ts @@ -58,26 +58,6 @@ function normalizeDeliveryChannel(value: string | undefined): string | undefined return normalized || undefined; } -function resolveDeliveryAccountId(params: { - cfg: OpenClawConfig; - channel: string | undefined; - accountId: string | undefined; -}): string | undefined { - const explicit = normalizeOptionalString(params.accountId); - if (explicit) { - return explicit; - } - const channelId = normalizeDeliveryChannel(params.channel); - if (!channelId) { - return undefined; - } - const channelCfg = ( - params.cfg.channels as Record | undefined - )?.[channelId]; - const configuredDefault = channelCfg?.defaultAccount; - return normalizeOptionalString(configuredDefault); -} - async function shouldTreatDeliveredTextAsVisible(params: { channel: string | undefined; kind: ReplyDispatchKind; @@ -207,11 +187,14 @@ export function createAcpDispatchDeliveryCoordinator(params: { }; const directChannel = normalizeDeliveryChannel(params.ctx.Provider ?? params.ctx.Surface); const routedChannel = normalizeDeliveryChannel(params.originatingChannel); - const resolvedAccountId = resolveDeliveryAccountId({ - cfg: params.cfg, - channel: routedChannel ?? directChannel, - accountId: params.ctx.AccountId, - }); + const explicitAccountId = normalizeOptionalString(params.ctx.AccountId); + const resolvedAccountId = + explicitAccountId ?? + normalizeOptionalString( + ( + params.cfg.channels as Record | undefined + )?.[routedChannel ?? directChannel ?? ""]?.defaultAccount, + ); const settleDirectVisibleText = async () => { if (state.settledDirectVisibleText || state.queuedDirectVisibleTextDeliveries === 0) { diff --git a/src/auto-reply/reply/dispatch-acp.ts b/src/auto-reply/reply/dispatch-acp.ts index 7c1fb5e9af1..05fcfd61e15 100644 --- a/src/auto-reply/reply/dispatch-acp.ts +++ b/src/auto-reply/reply/dispatch-acp.ts @@ -139,24 +139,6 @@ async function hasBoundConversationForSession(params: { }); } -function resolveDispatchAccountId(params: { - cfg: OpenClawConfig; - channelRaw: string | undefined; - accountIdRaw: string | undefined; -}): string | undefined { - const channel = normalizeOptionalString(params.channelRaw)?.toLowerCase() ?? ""; - if (!channel) { - return normalizeOptionalString(params.accountIdRaw); - } - const explicit = normalizeOptionalString(params.accountIdRaw); - if (explicit) { - return explicit; - } - const channels = params.cfg.channels as Record; - const configuredDefaultAccountId = channels?.[channel]?.defaultAccount; - return normalizeOptionalString(configuredDefaultAccountId); -} - export type AcpDispatchAttemptResult = { queuedFinal: boolean; counts: Record; @@ -357,11 +339,18 @@ export async function tryDispatchAcpReply(params: { normalizeOptionalString(params.cfg.acp?.defaultAgent) ?? resolveAgentIdFromSessionKey(canonicalSessionKey)) : resolveAgentIdFromSessionKey(canonicalSessionKey); - const effectiveDispatchAccountId = resolveDispatchAccountId({ - cfg: params.cfg, - channelRaw: params.ctx.OriginatingChannel ?? params.ctx.Surface ?? params.ctx.Provider, - accountIdRaw: params.ctx.AccountId, - }); + const normalizedDispatchChannel = + normalizeOptionalString( + params.ctx.OriginatingChannel ?? params.ctx.Surface ?? params.ctx.Provider, + )?.toLowerCase() ?? ""; + const explicitDispatchAccountId = normalizeOptionalString(params.ctx.AccountId); + const effectiveDispatchAccountId = + explicitDispatchAccountId ?? + normalizeOptionalString( + ( + params.cfg.channels as Record | undefined + )?.[normalizedDispatchChannel]?.defaultAccount, + ); const projector = createAcpReplyProjector({ cfg: params.cfg, shouldSendToolSummaries: params.shouldSendToolSummaries, diff --git a/src/gateway/sessions-history-http.ts b/src/gateway/sessions-history-http.ts index dd1d4120540..d3d2f04104d 100644 --- a/src/gateway/sessions-history-http.ts +++ b/src/gateway/sessions-history-http.ts @@ -64,11 +64,6 @@ function resolveLimit(req: IncomingMessage): number | undefined { return Math.min(MAX_SESSION_HISTORY_LIMIT, Math.max(1, value)); } -function resolveCursor(req: IncomingMessage): string | undefined { - const raw = getRequestUrl(req).searchParams.get("cursor"); - return normalizeOptionalString(raw); -} - function canonicalizePath(value: string | undefined): string | undefined { const trimmed = normalizeOptionalString(value); if (!trimmed) { @@ -152,7 +147,7 @@ export async function handleSessionHistoryHttpRequest( return true; } const limit = resolveLimit(req); - const cursor = resolveCursor(req); + const cursor = normalizeOptionalString(getRequestUrl(req).searchParams.get("cursor")); const effectiveMaxChars = typeof cfg.gateway?.webchat?.chatHistoryMaxChars === "number" ? cfg.gateway.webchat.chatHistoryMaxChars