diff --git a/src/discord/monitor/message-handler.process.ts b/src/discord/monitor/message-handler.process.ts index 9b97d70b9..14616420f 100644 --- a/src/discord/monitor/message-handler.process.ts +++ b/src/discord/monitor/message-handler.process.ts @@ -557,14 +557,12 @@ export async function processDiscordMessage(ctx: DiscordMessagePreflightContext) storePath, sessionKey: ctxPayload.SessionKey ?? route.sessionKey, ctx: ctxPayload, - updateLastRoute: isDirectMessage - ? { - sessionKey: route.mainSessionKey, - channel: "discord", - to: `user:${author.id}`, - accountId: route.accountId, - } - : undefined, + updateLastRoute: { + sessionKey: ctxPayload.SessionKey ?? route.sessionKey, + channel: "discord", + to: effectiveTo, + accountId: route.accountId, + }, onRecordError: (err) => { logVerbose(`discord: failed updating session meta: ${String(err)}`); }, diff --git a/src/routing/session-key.continuity.test.ts b/src/routing/session-key.continuity.test.ts new file mode 100644 index 000000000..dc6fa6a05 --- /dev/null +++ b/src/routing/session-key.continuity.test.ts @@ -0,0 +1,70 @@ +import { describe, it, expect } from "vitest"; +import { buildAgentSessionKey } from "./resolve-route.js"; + +describe("Discord Session Key Continuity", () => { + const agentId = "main"; + const channel = "discord"; + const accountId = "default"; + + it("generates distinct keys for DM vs Channel (dmScope=main)", () => { + // Scenario: Default config (dmScope=main) + const dmKey = buildAgentSessionKey({ + agentId, + channel, + accountId, + peer: { kind: "direct", id: "user123" }, + dmScope: "main", + }); + + const groupKey = buildAgentSessionKey({ + agentId, + channel, + accountId, + peer: { kind: "channel", id: "channel456" }, + dmScope: "main", + }); + + expect(dmKey).toBe("agent:main:main"); + expect(groupKey).toBe("agent:main:discord:channel:channel456"); + expect(dmKey).not.toBe(groupKey); + }); + + it("generates distinct keys for DM vs Channel (dmScope=per-peer)", () => { + // Scenario: Multi-user bot config + const dmKey = buildAgentSessionKey({ + agentId, + channel, + accountId, + peer: { kind: "direct", id: "user123" }, + dmScope: "per-peer", + }); + + const groupKey = buildAgentSessionKey({ + agentId, + channel, + accountId, + peer: { kind: "channel", id: "channel456" }, + dmScope: "per-peer", + }); + + expect(dmKey).toBe("agent:main:direct:user123"); + expect(groupKey).toBe("agent:main:discord:channel:channel456"); + expect(dmKey).not.toBe(groupKey); + }); + + it("handles empty/invalid IDs safely without collision", () => { + // If ID is missing, does it collide? + const missingIdKey = buildAgentSessionKey({ + agentId, + channel, + accountId, + peer: { kind: "channel", id: "" }, // Empty string + dmScope: "main", + }); + + expect(missingIdKey).toContain("unknown"); + + // Should still be distinct from main + expect(missingIdKey).not.toBe("agent:main:main"); + }); +});