From 6eb04c8aabdb4bdce0b3e3f7ab0cc83977ce4f1b Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Mon, 13 Apr 2026 17:17:18 +0100 Subject: [PATCH] perf(outbound): isolate id-like target resolution --- .../isolated-agent/delivery-target.test.ts | 6 ++--- src/cron/isolated-agent/delivery-target.ts | 2 +- src/infra/outbound/target-id-resolution.ts | 27 +++++++++++++++++++ src/infra/outbound/target-resolver.test.ts | 4 +++ src/infra/outbound/target-resolver.ts | 20 +++----------- 5 files changed, 39 insertions(+), 20 deletions(-) create mode 100644 src/infra/outbound/target-id-resolution.ts diff --git a/src/cron/isolated-agent/delivery-target.test.ts b/src/cron/isolated-agent/delivery-target.test.ts index 6f9b2ae0131..5e0541b0f3a 100644 --- a/src/cron/isolated-agent/delivery-target.test.ts +++ b/src/cron/isolated-agent/delivery-target.test.ts @@ -23,7 +23,7 @@ vi.mock("../../infra/outbound/channel-selection.runtime.js", () => ({ .mockResolvedValue({ channel: "telegram", configured: ["telegram"] }), })); -vi.mock("../../infra/outbound/target-resolver.js", () => ({ +vi.mock("../../infra/outbound/target-id-resolution.js", () => ({ maybeResolveIdLikeTarget: vi.fn(), })); @@ -40,13 +40,13 @@ const mockedModuleIds = [ "../../config/sessions/store-load.js", "../../infra/outbound/channel-selection.runtime.js", "../../infra/outbound/targets.runtime.js", - "../../infra/outbound/target-resolver.js", + "../../infra/outbound/target-id-resolution.js", "../../pairing/pairing-store.js", ]; import { loadSessionStore } from "../../config/sessions/store-load.js"; import { resolveMessageChannelSelection } from "../../infra/outbound/channel-selection.runtime.js"; -import { maybeResolveIdLikeTarget } from "../../infra/outbound/target-resolver.js"; +import { maybeResolveIdLikeTarget } from "../../infra/outbound/target-id-resolution.js"; import { resolveOutboundTarget } from "../../infra/outbound/targets.runtime.js"; import { readChannelAllowFromStoreSync } from "../../pairing/pairing-store.js"; import { resolveDeliveryTarget } from "./delivery-target.js"; diff --git a/src/cron/isolated-agent/delivery-target.ts b/src/cron/isolated-agent/delivery-target.ts index f2163bff5e0..183130381ea 100644 --- a/src/cron/isolated-agent/delivery-target.ts +++ b/src/cron/isolated-agent/delivery-target.ts @@ -5,7 +5,7 @@ import { resolveStorePath } from "../../config/sessions/paths.js"; import { loadSessionStore } from "../../config/sessions/store-load.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { formatErrorMessage } from "../../infra/errors.js"; -import { maybeResolveIdLikeTarget } from "../../infra/outbound/target-resolver.js"; +import { maybeResolveIdLikeTarget } from "../../infra/outbound/target-id-resolution.js"; import { tryResolveLoadedOutboundTarget } from "../../infra/outbound/targets-loaded.js"; import { resolveSessionDeliveryTarget } from "../../infra/outbound/targets-session.js"; import type { OutboundChannel } from "../../infra/outbound/targets.js"; diff --git a/src/infra/outbound/target-id-resolution.ts b/src/infra/outbound/target-id-resolution.ts new file mode 100644 index 00000000000..0af51d3b71c --- /dev/null +++ b/src/infra/outbound/target-id-resolution.ts @@ -0,0 +1,27 @@ +import type { ChannelDirectoryEntryKind, ChannelId } from "../../channels/plugins/types.public.js"; +import type { OpenClawConfig } from "../../config/types.openclaw.js"; +import { maybeResolvePluginMessagingTarget } from "./target-normalization.js"; + +export type ResolvedIdLikeTarget = { + to: string; + kind: ChannelDirectoryEntryKind | "channel"; + display?: string; + source: "normalized" | "directory"; +}; + +export async function maybeResolveIdLikeTarget(params: { + cfg: OpenClawConfig; + channel: ChannelId; + input: string; + accountId?: string | null; + preferredKind?: ChannelDirectoryEntryKind | "channel"; +}): Promise { + const target = await maybeResolvePluginMessagingTarget({ + ...params, + requireIdLike: true, + }); + if (!target) { + return undefined; + } + return target; +} diff --git a/src/infra/outbound/target-resolver.test.ts b/src/infra/outbound/target-resolver.test.ts index aba06db4457..28069dfb0a1 100644 --- a/src/infra/outbound/target-resolver.test.ts +++ b/src/infra/outbound/target-resolver.test.ts @@ -22,6 +22,10 @@ vi.mock("../../channels/plugins/index.js", () => ({ normalizeChannelId: (value: string) => value, })); +vi.mock("../../channels/plugins/registry-loaded-read.js", () => ({ + getLoadedChannelPluginForRead: (...args: unknown[]) => mocks.getChannelPlugin(...args), +})); + vi.mock("../../plugins/runtime.js", () => ({ getActivePluginChannelRegistry: () => null, getActivePluginRegistry: () => null, diff --git a/src/infra/outbound/target-resolver.ts b/src/infra/outbound/target-resolver.ts index 0db0d376ad3..2c64521e178 100644 --- a/src/infra/outbound/target-resolver.ts +++ b/src/infra/outbound/target-resolver.ts @@ -9,6 +9,7 @@ import { defaultRuntime, type RuntimeEnv } from "../../runtime.js"; import { normalizeLowercaseStringOrEmpty } from "../../shared/string-coerce.js"; import { buildDirectoryCacheKey, DirectoryCache } from "./directory-cache.js"; import { ambiguousTargetError, unknownTargetError } from "./target-errors.js"; +import { maybeResolveIdLikeTarget, type ResolvedIdLikeTarget } from "./target-id-resolution.js"; import { buildTargetResolverSignature, looksLikeTargetId, @@ -34,11 +35,13 @@ export type ResolveMessagingTargetResult = | { ok: false; error: Error; candidates?: ChannelDirectoryEntry[] }; function asResolvedMessagingTarget( - target: Awaited>, + target: Awaited> | ResolvedIdLikeTarget, ): ResolvedMessagingTarget | undefined { return target; } +export { maybeResolveIdLikeTarget } from "./target-id-resolution.js"; + export async function resolveChannelTarget(params: { cfg: OpenClawConfig; channel: ChannelId; @@ -50,21 +53,6 @@ export async function resolveChannelTarget(params: { return resolveMessagingTarget(params); } -export async function maybeResolveIdLikeTarget(params: { - cfg: OpenClawConfig; - channel: ChannelId; - input: string; - accountId?: string | null; - preferredKind?: TargetResolveKind; -}): Promise { - return asResolvedMessagingTarget( - await maybeResolvePluginMessagingTarget({ - ...params, - requireIdLike: true, - }), - ); -} - const CACHE_TTL_MS = 30 * 60 * 1000; const directoryCache = new DirectoryCache(CACHE_TTL_MS);