





























@@ -4,6 +4,15 @@ import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
4455const mocks = vi.hoisted(() => ({
66resolveOutboundChannelPlugin: vi.fn<() => unknown>(() => null),
7+resolveChannelTarget: vi.fn<() => Promise<unknown>>(async () => ({
8+ok: true,
9+target: {
10+to: "+1999",
11+kind: "group",
12+source: "normalized",
13+resolutionSource: "normalized",
14+},
15+})),
716resolveOutboundTarget: vi.fn<() => { ok: true; to: string } | { ok: false; error: Error }>(
817() => ({ ok: true, to: "+1999" }),
918),
@@ -82,11 +91,16 @@ vi.mock("./outbound-session.js", () => ({
8291resolveOutboundSessionRoute: mocks.resolveOutboundSessionRoute,
8392}));
849394+vi.mock("./target-resolver.js", () => ({
95+resolveChannelTarget: mocks.resolveChannelTarget,
96+}));
97+8598vi.mock("../../utils/message-channel.js", () => ({
8699INTERNAL_MESSAGE_CHANNEL: "webchat",
87-isDeliverableMessageChannel: (channel: string) => ["directchat", "workspace"].includes(channel),
100+isDeliverableMessageChannel: (channel: string) =>
101+["directchat", "workspace", "telegram"].includes(channel),
88102isGatewayMessageChannel: (channel: string) =>
89-["directchat", "workspace", "webchat"].includes(channel),
103+["directchat", "workspace", "telegram", "webchat"].includes(channel),
90104normalizeMessageChannel: (value: string) => value.trim().toLowerCase(),
91105}));
92106@@ -106,7 +120,18 @@ beforeAll(async () => {
106120beforeEach(() => {
107121mocks.resolveOutboundChannelPlugin.mockReset();
108122mocks.resolveOutboundChannelPlugin.mockReturnValue(null);
109-mocks.resolveOutboundTarget.mockClear();
123+mocks.resolveChannelTarget.mockReset();
124+mocks.resolveChannelTarget.mockResolvedValue({
125+ok: true,
126+target: {
127+to: "+1999",
128+kind: "group",
129+source: "normalized",
130+resolutionSource: "normalized",
131+},
132+});
133+mocks.resolveOutboundTarget.mockReset();
134+mocks.resolveOutboundTarget.mockReturnValue({ ok: true, to: "+1999" });
110135mocks.resolveOutboundSessionRoute.mockReset();
111136mocks.resolveOutboundSessionRoute.mockResolvedValue(null);
112137mocks.resolveSessionDeliveryTarget.mockClear();
@@ -313,31 +338,109 @@ describe("agent delivery helpers", () => {
313338expect(plan.resolvedTo).toBe("1470130713209602050");
314339});
315340316-it("defers reserved-literal errors to async session-route resolution", async () => {
341+it("resolves reserved explicit targets through directory-capable resolution before session routing", async () => {
317342mocks.resolveOutboundChannelPlugin.mockReturnValue({
318-messaging: { resolveOutboundSessionRoute: vi.fn() },
343+messaging: { resolveOutboundSessionRoute: vi.fn(), targetResolver: {} },
319344});
320345mocks.resolveOutboundTarget.mockReturnValueOnce({
321346ok: false,
322347error: new Error('Reserved target "current" for Telegram'),
323348});
349+mocks.resolveChannelTarget.mockResolvedValueOnce({
350+ok: true,
351+target: {
352+to: "telegram:-1002458651455",
353+kind: "group",
354+source: "directory",
355+resolutionSource: "directory",
356+},
357+});
358+mocks.resolveOutboundSessionRoute.mockResolvedValueOnce({
359+sessionKey: "agent:telegram:group:-1002458651455",
360+baseSessionKey: "agent:telegram:group:-1002458651455",
361+peer: { kind: "group", id: "-1002458651455" },
362+chatType: "group",
363+from: "telegram:group:-1002458651455",
364+to: "telegram:-1002458651455",
365+});
324366325367const plan = await resolveAgentDeliveryPlanWithSessionRoute({
326368cfg: {} as OpenClawConfig,
327369agentId: "agent",
370+currentSessionKey: "agent:main",
328371sessionEntry: undefined,
329-requestedChannel: "workspace",
372+requestedChannel: "telegram",
373+explicitTo: "current",
374+accountId: "work",
375+wantsDelivery: true,
376+});
377+378+expect(mocks.resolveChannelTarget).toHaveBeenCalledWith({
379+cfg: {},
380+channel: "telegram",
381+input: "current",
382+accountId: "work",
383+unknownTargetMode: "normalized",
384+plugin: {
385+messaging: { resolveOutboundSessionRoute: expect.any(Function), targetResolver: {} },
386+},
387+});
388+expect(mocks.resolveOutboundSessionRoute).toHaveBeenCalledWith({
389+cfg: {},
390+channel: "telegram",
391+agentId: "agent",
392+accountId: "work",
393+target: "telegram:-1002458651455",
394+resolvedTarget: {
395+to: "telegram:-1002458651455",
396+kind: "group",
397+source: "directory",
398+resolutionSource: "directory",
399+},
400+currentSessionKey: "agent:main",
401+threadId: undefined,
402+});
403+expect(plan.resolvedTo).toBe("telegram:-1002458651455");
404+expect(plan.targetResolutionError).toBeUndefined();
405+});
406+407+it("keeps reserved explicit target errors when directory-capable resolution misses", async () => {
408+const reservedError = new Error('Reserved target "current" for Telegram');
409+mocks.resolveOutboundChannelPlugin.mockReturnValue({
410+messaging: { resolveOutboundSessionRoute: vi.fn(), targetResolver: {} },
411+});
412+mocks.resolveOutboundTarget.mockReturnValueOnce({
413+ok: false,
414+error: reservedError,
415+});
416+mocks.resolveChannelTarget.mockResolvedValueOnce({
417+ok: false,
418+error: reservedError,
419+});
420+421+const plan = await resolveAgentDeliveryPlanWithSessionRoute({
422+cfg: {} as OpenClawConfig,
423+agentId: "agent",
424+sessionEntry: undefined,
425+requestedChannel: "telegram",
330426explicitTo: "current",
331427accountId: undefined,
332428wantsDelivery: true,
333429});
334430335-// Reserved-literal errors do not block session route resolution; the
336-// async resolver (resolveMessagingTarget) does directory-first lookup
337-// before rejecting reserved literals.
338-expect(mocks.resolveOutboundSessionRoute).toHaveBeenCalled();
431+expect(mocks.resolveChannelTarget).toHaveBeenCalledWith({
432+cfg: {},
433+channel: "telegram",
434+input: "current",
435+accountId: undefined,
436+unknownTargetMode: "normalized",
437+plugin: {
438+messaging: { resolveOutboundSessionRoute: expect.any(Function), targetResolver: {} },
439+},
440+});
441+expect(mocks.resolveOutboundSessionRoute).not.toHaveBeenCalled();
339442expect(plan.resolvedTo).toBe("current");
340-expect(plan.targetResolutionError).toBeUndefined();
443+expect(plan.targetResolutionError).toBe(reservedError);
341444});
342445343446it("surfaces stored explicit target errors even when explicit validation is disabled", () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。