From b722273acb76ee184184150a9b962167cb1df20e Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 20 Apr 2026 14:27:16 +0100 Subject: [PATCH] refactor: share inbound media detection --- src/auto-reply/reply/dispatch-acp.ts | 17 +++-------------- src/auto-reply/reply/get-reply.ts | 13 +------------ src/auto-reply/reply/inbound-media.ts | 27 +++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 26 deletions(-) create mode 100644 src/auto-reply/reply/inbound-media.ts diff --git a/src/auto-reply/reply/dispatch-acp.ts b/src/auto-reply/reply/dispatch-acp.ts index b33d6d87293..aaa1f6ee0db 100644 --- a/src/auto-reply/reply/dispatch-acp.ts +++ b/src/auto-reply/reply/dispatch-acp.ts @@ -28,6 +28,7 @@ import { createAcpDispatchDeliveryCoordinator, type AcpDispatchDeliveryCoordinator, } from "./dispatch-acp-delivery.js"; +import { hasInboundMedia } from "./inbound-media.js"; import type { ReplyDispatchKind, ReplyDispatcher } from "./reply-dispatcher.types.js"; let dispatchAcpManagerRuntimePromise: Promise< @@ -85,18 +86,6 @@ function resolveAcpPromptText(ctx: FinalizedMsgContext): string { ]).trim(); } -function hasInboundMediaForAcp(ctx: FinalizedMsgContext): boolean { - return Boolean( - ctx.StickerMediaIncluded || - ctx.Sticker || - normalizeOptionalString(ctx.MediaPath) || - normalizeOptionalString(ctx.MediaUrl) || - ctx.MediaPaths?.some((value) => normalizeOptionalString(value)) || - ctx.MediaUrls?.some((value) => normalizeOptionalString(value)) || - ctx.MediaTypes?.length, - ); -} - function resolveAcpRequestId(ctx: FinalizedMsgContext): string { const id = ctx.MessageSidFull ?? ctx.MessageSid ?? ctx.MessageSidFirst ?? ctx.MessageSidLast; if (typeof id === "string") { @@ -396,7 +385,7 @@ export async function tryDispatchAcpReply(params: { if (agentPolicyError) { throw agentPolicyError; } - if (hasInboundMediaForAcp(params.ctx) && !params.ctx.MediaUnderstanding?.length) { + if (hasInboundMedia(params.ctx) && !params.ctx.MediaUnderstanding?.length) { try { const { applyMediaUnderstanding } = await loadDispatchAcpMediaRuntime(); await applyMediaUnderstanding({ @@ -411,7 +400,7 @@ export async function tryDispatchAcpReply(params: { } const promptText = resolveAcpPromptText(params.ctx); - const attachments = hasInboundMediaForAcp(params.ctx) + const attachments = hasInboundMedia(params.ctx) ? await resolveAcpAttachments({ ctx: params.ctx, cfg: params.cfg }) : []; if (!promptText && attachments.length === 0) { diff --git a/src/auto-reply/reply/get-reply.ts b/src/auto-reply/reply/get-reply.ts index e0255acd663..05bd159ed8f 100644 --- a/src/auto-reply/reply/get-reply.ts +++ b/src/auto-reply/reply/get-reply.ts @@ -33,6 +33,7 @@ import { import { handleInlineActions } from "./get-reply-inline-actions.js"; import { runPreparedReply } from "./get-reply-run.js"; import { finalizeInboundContext } from "./inbound-context.js"; +import { hasInboundMedia } from "./inbound-media.js"; import { emitPreAgentMessageHooks } from "./message-preprocess-hooks.js"; import { createFastTestModelSelectionState } from "./model-selection.js"; import { initSessionState } from "./session.js"; @@ -119,18 +120,6 @@ function mergeSkillFilters(channelFilter?: string[], agentFilter?: string[]): st return channel.filter((name) => agentSet.has(name)); } -function hasInboundMedia(ctx: MsgContext): boolean { - return Boolean( - ctx.StickerMediaIncluded || - ctx.Sticker || - normalizeOptionalString(ctx.MediaPath) || - normalizeOptionalString(ctx.MediaUrl) || - ctx.MediaPaths?.some((value) => normalizeOptionalString(value)) || - ctx.MediaUrls?.some((value) => normalizeOptionalString(value)) || - ctx.MediaTypes?.length, - ); -} - function hasLinkCandidate(ctx: MsgContext): boolean { const message = ctx.BodyForCommands ?? ctx.CommandBody ?? ctx.RawBody ?? ctx.Body; if (!message) { diff --git a/src/auto-reply/reply/inbound-media.ts b/src/auto-reply/reply/inbound-media.ts new file mode 100644 index 00000000000..23383bda6a4 --- /dev/null +++ b/src/auto-reply/reply/inbound-media.ts @@ -0,0 +1,27 @@ +import { normalizeOptionalString } from "../../shared/string-coerce.js"; + +export type InboundMediaContext = { + StickerMediaIncluded?: unknown; + Sticker?: unknown; + MediaPath?: unknown; + MediaUrl?: unknown; + MediaPaths?: readonly unknown[]; + MediaUrls?: readonly unknown[]; + MediaTypes?: readonly unknown[]; +}; + +function hasNormalizedStringEntry(values: readonly unknown[] | undefined): boolean { + return Array.isArray(values) && values.some((value) => normalizeOptionalString(value)); +} + +export function hasInboundMedia(ctx: InboundMediaContext): boolean { + return Boolean( + ctx.StickerMediaIncluded || + ctx.Sticker || + normalizeOptionalString(ctx.MediaPath) || + normalizeOptionalString(ctx.MediaUrl) || + hasNormalizedStringEntry(ctx.MediaPaths) || + hasNormalizedStringEntry(ctx.MediaUrls) || + (Array.isArray(ctx.MediaTypes) && ctx.MediaTypes.length > 0), + ); +}