From 3d4161e05c3c0ae68bd1b9dc9f158f38d01302b4 Mon Sep 17 00:00:00 2001 From: Tom Date: Tue, 3 Mar 2026 15:37:31 +0700 Subject: [PATCH] zalouser: route DM sessions as direct peers --- .../zalouser/src/monitor.group-gating.test.ts | 61 ++++++++++++++++--- extensions/zalouser/src/monitor.ts | 4 +- 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/extensions/zalouser/src/monitor.group-gating.test.ts b/extensions/zalouser/src/monitor.group-gating.test.ts index 2b7bd2945c9..a182310447b 100644 --- a/extensions/zalouser/src/monitor.group-gating.test.ts +++ b/extensions/zalouser/src/monitor.group-gating.test.ts @@ -59,6 +59,16 @@ function installRuntime(params: { commandAuthorized: boolean }) { await dispatcherOptions.typingCallbacks?.onReplyStart?.(); return { queuedFinal: false, counts: { tool: 0, block: 0, final: 0 }, ctx }; }); + const resolveAgentRoute = vi.fn((input: { peer?: { kind?: string; id?: string } }) => { + const peerKind = input.peer?.kind === "direct" ? "direct" : "group"; + const peerId = input.peer?.id ?? "1"; + return { + agentId: "main", + sessionKey: `agent:main:zalouser:${peerKind}:${peerId}`, + accountId: "default", + mainSessionKey: "agent:main:main", + }; + }); setZalouserRuntime({ logging: { @@ -98,12 +108,7 @@ function installRuntime(params: { commandAuthorized: boolean }) { }), }, routing: { - resolveAgentRoute: vi.fn(() => ({ - agentId: "main", - sessionKey: "agent:main:zalouser:group:1", - accountId: "default", - mainSessionKey: "agent:main:main", - })), + resolveAgentRoute, }, session: { resolveStorePath: vi.fn(() => "/tmp"), @@ -125,7 +130,7 @@ function installRuntime(params: { commandAuthorized: boolean }) { }, } as unknown as PluginRuntime); - return { dispatchReplyWithBufferedBlockDispatcher }; + return { dispatchReplyWithBufferedBlockDispatcher, resolveAgentRoute }; } function createGroupMessage(overrides: Partial = {}): ZaloInboundMessage { @@ -147,6 +152,21 @@ function createGroupMessage(overrides: Partial = {}): ZaloIn }; } +function createDmMessage(overrides: Partial = {}): ZaloInboundMessage { + return { + threadId: "u-1", + isGroup: false, + senderId: "321", + senderName: "Bob", + groupName: undefined, + content: "hello", + timestampMs: Date.now(), + msgId: "dm-1", + raw: { source: "test" }, + ...overrides, + }; +} + describe("zalouser monitor group mention gating", () => { beforeEach(() => { sendMessageZalouserMock.mockClear(); @@ -232,4 +252,31 @@ describe("zalouser monitor group mention gating", () => { const callArg = dispatchReplyWithBufferedBlockDispatcher.mock.calls[0]?.[0]; expect(callArg?.ctx?.WasMentioned).toBe(true); }); + + it("routes DM messages with direct peer kind", async () => { + const { dispatchReplyWithBufferedBlockDispatcher, resolveAgentRoute } = installRuntime({ + commandAuthorized: false, + }); + const account = createAccount(); + await __testing.processMessage({ + message: createDmMessage(), + account: { + ...account, + config: { + ...account.config, + dmPolicy: "open", + }, + }, + config: createConfig(), + runtime: createRuntimeEnv(), + }); + + expect(resolveAgentRoute).toHaveBeenCalledWith( + expect.objectContaining({ + peer: { kind: "direct", id: "321" }, + }), + ); + const callArg = dispatchReplyWithBufferedBlockDispatcher.mock.calls[0]?.[0]; + expect(callArg?.ctx?.SessionKey).toBe("agent:main:zalouser:direct:321"); + }); }); diff --git a/extensions/zalouser/src/monitor.ts b/extensions/zalouser/src/monitor.ts index 0c97b9662c1..de9bcd3bbaf 100644 --- a/extensions/zalouser/src/monitor.ts +++ b/extensions/zalouser/src/monitor.ts @@ -312,14 +312,14 @@ async function processMessage( const peer = isGroup ? { kind: "group" as const, id: chatId } - : { kind: "group" as const, id: senderId }; + : { kind: "direct" as const, id: senderId }; const route = core.channel.routing.resolveAgentRoute({ cfg: config, channel: "zalouser", accountId: account.accountId, peer: { - // Use "group" kind to avoid dmScope=main collapsing all DMs into the main session. + // Keep DM peer kind as "direct" so session keys follow dmScope and UI labels stay DM-shaped. kind: peer.kind, id: peer.id, },