mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-14 11:30:41 +00:00
fix(slack): restore persistent per-channel session routing (#32320)
Top-level channel messages were creating isolated per-message sessions because roomThreadId fell through to threadContext.messageTs whenever replyToMode was not off. Introduced in #10686, every new channel message got its own session key (agent:...🧵<messageTs>), breaking conversation continuity. Fix: only derive thread-specific session keys for actual thread replies. Top-level channel messages stay on the per-channel session key regardless of replyToMode. Fixes #32285
This commit is contained in:
@@ -83,38 +83,32 @@ describe("thread-level session keys", () => {
|
||||
expect(sessionKey).not.toContain("1770408522.168859");
|
||||
});
|
||||
|
||||
it("keeps top-level channel turns thread-scoped when replyToMode=all", async () => {
|
||||
const ctx = buildCtx({ replyToMode: "all" });
|
||||
ctx.resolveUserName = async () => ({ name: "Carol" });
|
||||
const account = createSlackTestAccount({ replyToMode: "all" });
|
||||
it("keeps top-level channel messages on the per-channel session regardless of replyToMode", async () => {
|
||||
for (const mode of ["all", "first", "off"] as const) {
|
||||
const ctx = buildCtx({ replyToMode: mode });
|
||||
ctx.resolveUserName = async () => ({ name: "Carol" });
|
||||
const account = createSlackTestAccount({ replyToMode: mode });
|
||||
|
||||
const prepared = await prepareSlackMessage({
|
||||
ctx,
|
||||
account,
|
||||
message: buildChannelMessage({ ts: "1770408530.000000" }),
|
||||
opts: { source: "message" },
|
||||
});
|
||||
const first = await prepareSlackMessage({
|
||||
ctx,
|
||||
account,
|
||||
message: buildChannelMessage({ ts: "1770408530.000000" }),
|
||||
opts: { source: "message" },
|
||||
});
|
||||
const second = await prepareSlackMessage({
|
||||
ctx,
|
||||
account,
|
||||
message: buildChannelMessage({ ts: "1770408531.000000" }),
|
||||
opts: { source: "message" },
|
||||
});
|
||||
|
||||
expect(prepared).toBeTruthy();
|
||||
const sessionKey = prepared!.ctxPayload.SessionKey as string;
|
||||
expect(sessionKey).toContain(":thread:1770408530.000000");
|
||||
});
|
||||
|
||||
it("keeps top-level channel turns thread-scoped when replyToMode=first", async () => {
|
||||
const ctx = buildCtx({ replyToMode: "first" });
|
||||
ctx.resolveUserName = async () => ({ name: "Dora" });
|
||||
const account = createSlackTestAccount({ replyToMode: "first" });
|
||||
|
||||
const prepared = await prepareSlackMessage({
|
||||
ctx,
|
||||
account,
|
||||
message: buildChannelMessage({ ts: "1770408531.000000" }),
|
||||
opts: { source: "message" },
|
||||
});
|
||||
|
||||
expect(prepared).toBeTruthy();
|
||||
const sessionKey = prepared!.ctxPayload.SessionKey as string;
|
||||
expect(sessionKey).toContain(":thread:1770408531.000000");
|
||||
expect(first).toBeTruthy();
|
||||
expect(second).toBeTruthy();
|
||||
const firstKey = first!.ctxPayload.SessionKey as string;
|
||||
const secondKey = second!.ctxPayload.SessionKey as string;
|
||||
expect(firstKey).toBe(secondKey);
|
||||
expect(firstKey).not.toContain(":thread:");
|
||||
}
|
||||
});
|
||||
|
||||
it("does not add thread suffix for DMs when replyToMode=off", async () => {
|
||||
|
||||
@@ -285,12 +285,12 @@ function resolveSlackRoutingContext(params: {
|
||||
!isThreadReply && replyToMode === "all" && threadContext.messageTs
|
||||
? threadContext.messageTs
|
||||
: undefined;
|
||||
const roomThreadId =
|
||||
isThreadReply && threadTs
|
||||
? threadTs
|
||||
: replyToMode === "off"
|
||||
? undefined
|
||||
: threadContext.messageTs;
|
||||
// Only fork channel/group messages into thread-specific sessions when they are
|
||||
// actual thread replies (thread_ts present, different from message ts).
|
||||
// Top-level channel messages must stay on the per-channel session for continuity.
|
||||
// Before this fix, every channel message used its own ts as threadId, creating
|
||||
// isolated sessions per message (regression from #10686).
|
||||
const roomThreadId = isThreadReply && threadTs ? threadTs : undefined;
|
||||
const canonicalThreadId = isRoomish ? roomThreadId : isThreadReply ? threadTs : autoThreadId;
|
||||
const threadKeys = resolveThreadSessionKeys({
|
||||
baseSessionKey: route.sessionKey,
|
||||
|
||||
Reference in New Issue
Block a user