zalouser: route DM sessions as direct peers

This commit is contained in:
Tom
2026-03-03 15:37:31 +07:00
parent a8a545fc5a
commit 3d4161e05c
2 changed files with 56 additions and 9 deletions

View File

@@ -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> = {}): ZaloInboundMessage {
@@ -147,6 +152,21 @@ function createGroupMessage(overrides: Partial<ZaloInboundMessage> = {}): ZaloIn
};
}
function createDmMessage(overrides: Partial<ZaloInboundMessage> = {}): 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");
});
});

View File

@@ -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,
},