fix: preserve outbound thread sessions

This commit is contained in:
Peter Steinberger
2026-04-22 02:33:39 +01:00
parent d87f8cc142
commit ef66798433
20 changed files with 621 additions and 106 deletions

View File

@@ -266,4 +266,71 @@ describe("resolveMatrixOutboundSessionRoute", () => {
expectCurrentDmRoomRoute(route);
});
it("recovers channel thread routes from currentSessionKey and preserves Matrix event-id case", () => {
const route = resolveMatrixOutboundSessionRoute({
cfg: {},
agentId: "main",
target: "room:!Ops:Example.Org",
currentSessionKey: "agent:main:matrix:channel:!ops:example.org:thread:$RootEvent:Example.Org",
});
expect(route).toMatchObject({
sessionKey: "agent:main:matrix:channel:!ops:example.org:thread:$RootEvent:Example.Org",
baseSessionKey: "agent:main:matrix:channel:!ops:example.org",
threadId: "$RootEvent:Example.Org",
});
});
it("resolves per-room DM metadata from the base key when currentSessionKey has a thread suffix", () => {
const storedSession = createStoredDirectDmSession();
const route = resolveUserRoute({
cfg: createMatrixRouteConfig({
[currentDmSessionKey]: storedSession,
}),
accountId: "ops",
target: "@alice:example.org",
});
const threadedRoute = resolveMatrixOutboundSessionRoute({
cfg: createMatrixRouteConfig({
[route?.baseSessionKey ?? currentDmSessionKey]: storedSession,
}),
agentId: "main",
accountId: "ops",
target: "@alice:example.org",
resolvedTarget: {
to: "@alice:example.org",
kind: "user",
source: "normalized",
},
currentSessionKey: `${route?.baseSessionKey}:thread:$DmRoot:Example.Org`,
});
expect(threadedRoute).toMatchObject({
sessionKey: `${route?.baseSessionKey}:thread:$DmRoot:Example.Org`,
baseSessionKey: route?.baseSessionKey,
to: "room:!dm:example.org",
threadId: "$DmRoot:Example.Org",
});
});
it('does not recover currentSessionKey threads for shared dmScope "main" DMs', () => {
const route = resolveMatrixOutboundSessionRoute({
cfg: {},
agentId: "main",
target: "@alice:example.org",
currentSessionKey: "agent:main:main:thread:$DmRoot:Example.Org",
resolvedTarget: {
to: "@alice:example.org",
kind: "user",
source: "normalized",
},
});
expect(route).toMatchObject({
sessionKey: "agent:main:main",
baseSessionKey: "agent:main:main",
});
expect(route?.threadId).toBeUndefined();
});
});

View File

@@ -1,6 +1,7 @@
import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
import {
buildChannelOutboundSessionRoute,
buildThreadAwareOutboundSessionRoute,
type ChannelOutboundSessionRouteParams,
} from "openclaw/plugin-sdk/channel-core";
import {
@@ -8,6 +9,7 @@ import {
resolveSessionStoreEntry,
resolveStorePath,
} from "openclaw/plugin-sdk/config-runtime";
import { parseThreadSessionSuffix } from "openclaw/plugin-sdk/routing";
import { resolveMatrixAccountConfig } from "./matrix/account-config.js";
import { resolveDefaultMatrixAccountId } from "./matrix/accounts.js";
import { resolveMatrixStoredSessionMeta } from "./matrix/session-store-metadata.js";
@@ -38,7 +40,9 @@ function resolveMatrixCurrentDmRoomId(params: {
currentSessionKey?: string;
targetUserId: string;
}): string | undefined {
const sessionKey = params.currentSessionKey?.trim();
const sessionKey =
parseThreadSessionSuffix(params.currentSessionKey).baseSessionKey ??
params.currentSessionKey?.trim();
if (!sessionKey) {
return undefined;
}
@@ -100,7 +104,7 @@ export function resolveMatrixOutboundSessionRoute(params: ChannelOutboundSession
const from = target.kind === "user" ? `matrix:${target.id}` : `matrix:channel:${target.id}`;
const to = `room:${roomScopedDmId ?? target.id}`;
return buildChannelOutboundSessionRoute({
const baseRoute = buildChannelOutboundSessionRoute({
cfg: params.cfg,
agentId: params.agentId,
channel: "matrix",
@@ -110,4 +114,13 @@ export function resolveMatrixOutboundSessionRoute(params: ChannelOutboundSession
from,
to,
});
return buildThreadAwareOutboundSessionRoute({
route: baseRoute,
replyToId: params.replyToId,
threadId: params.threadId,
currentSessionKey: params.currentSessionKey,
normalizeThreadId: (threadId) => threadId,
canRecoverCurrentThread: ({ route }) =>
route.peer.kind !== "direct" || (params.cfg.session?.dmScope ?? "main") !== "main",
});
}