mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 12:54:58 +00:00
refactor(routing): centralize inbound last-route policy
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { clearRuntimeConfigSnapshot, setRuntimeConfigSnapshot } from "../config/config.js";
|
||||
import { buildTelegramMessageContextForTest } from "./bot-message-context.test-harness.js";
|
||||
|
||||
const recordInboundSessionMock = vi.fn().mockResolvedValue(undefined);
|
||||
vi.mock("../channels/session.js", () => ({
|
||||
recordInboundSession: (...args: unknown[]) => recordInboundSessionMock(...args),
|
||||
}));
|
||||
|
||||
describe("buildTelegramMessageContext named-account DM fallback", () => {
|
||||
const baseCfg = {
|
||||
agents: { defaults: { model: "anthropic/claude-opus-4-5", workspace: "/tmp/openclaw" } },
|
||||
@@ -11,8 +16,16 @@ describe("buildTelegramMessageContext named-account DM fallback", () => {
|
||||
|
||||
afterEach(() => {
|
||||
clearRuntimeConfigSnapshot();
|
||||
recordInboundSessionMock.mockClear();
|
||||
});
|
||||
|
||||
function getLastUpdateLastRoute(): { sessionKey?: string } | undefined {
|
||||
const callArgs = recordInboundSessionMock.mock.calls.at(-1)?.[0] as {
|
||||
updateLastRoute?: { sessionKey?: string };
|
||||
};
|
||||
return callArgs?.updateLastRoute;
|
||||
}
|
||||
|
||||
it("allows DM through for a named account with no explicit binding", async () => {
|
||||
setRuntimeConfigSnapshot(baseCfg);
|
||||
|
||||
@@ -51,6 +64,25 @@ describe("buildTelegramMessageContext named-account DM fallback", () => {
|
||||
expect(ctx?.ctxPayload?.SessionKey).toBe("agent:main:telegram:atlas:direct:814912386");
|
||||
});
|
||||
|
||||
it("keeps named-account fallback lastRoute on the isolated DM session", async () => {
|
||||
setRuntimeConfigSnapshot(baseCfg);
|
||||
|
||||
const ctx = await buildTelegramMessageContextForTest({
|
||||
cfg: baseCfg,
|
||||
accountId: "atlas",
|
||||
message: {
|
||||
message_id: 1,
|
||||
chat: { id: 814912386, type: "private" },
|
||||
date: 1700000000,
|
||||
text: "hello",
|
||||
from: { id: 814912386, first_name: "Alice" },
|
||||
},
|
||||
});
|
||||
|
||||
expect(ctx?.ctxPayload?.SessionKey).toBe("agent:main:telegram:atlas:direct:814912386");
|
||||
expect(getLastUpdateLastRoute()?.sessionKey).toBe("agent:main:telegram:atlas:direct:814912386");
|
||||
});
|
||||
|
||||
it("isolates sessions between named accounts that share the default agent", async () => {
|
||||
setRuntimeConfigSnapshot(baseCfg);
|
||||
|
||||
|
||||
@@ -39,7 +39,11 @@ import type {
|
||||
} from "../config/types.js";
|
||||
import { logVerbose, shouldLogVerbose } from "../globals.js";
|
||||
import { recordChannelActivity } from "../infra/channel-activity.js";
|
||||
import { buildAgentSessionKey } from "../routing/resolve-route.js";
|
||||
import {
|
||||
buildAgentSessionKey,
|
||||
deriveLastRoutePolicy,
|
||||
resolveInboundLastRouteSessionKey,
|
||||
} from "../routing/resolve-route.js";
|
||||
import { DEFAULT_ACCOUNT_ID, resolveThreadSessionKeys } from "../routing/session-key.js";
|
||||
import { resolvePinnedMainDmOwnerFromAllowlist } from "../security/dm-policy-shared.js";
|
||||
import { withTelegramApiErrorLogging } from "./api-logging.js";
|
||||
@@ -362,6 +366,14 @@ export const buildTelegramMessageContext = async ({
|
||||
? resolveThreadSessionKeys({ baseSessionKey, threadId: `${chatId}:${dmThreadId}` })
|
||||
: null;
|
||||
const sessionKey = threadKeys?.sessionKey ?? baseSessionKey;
|
||||
route = {
|
||||
...route,
|
||||
sessionKey,
|
||||
lastRoutePolicy: deriveLastRoutePolicy({
|
||||
sessionKey,
|
||||
mainSessionKey: route.mainSessionKey,
|
||||
}),
|
||||
};
|
||||
const mentionRegexes = buildMentionRegexes(cfg, route.agentId);
|
||||
// Compute requireMention after access checks and final route selection.
|
||||
const activationOverride = resolveGroupActivation({
|
||||
@@ -832,6 +844,10 @@ export const buildTelegramMessageContext = async ({
|
||||
normalizeEntry: (entry) => normalizeAllowFrom([entry]).entries[0],
|
||||
})
|
||||
: null;
|
||||
const updateLastRouteSessionKey = resolveInboundLastRouteSessionKey({
|
||||
route,
|
||||
sessionKey,
|
||||
});
|
||||
|
||||
await recordInboundSession({
|
||||
storePath,
|
||||
@@ -839,14 +855,14 @@ export const buildTelegramMessageContext = async ({
|
||||
ctx: ctxPayload,
|
||||
updateLastRoute: !isGroup
|
||||
? {
|
||||
sessionKey: route.mainSessionKey,
|
||||
sessionKey: updateLastRouteSessionKey,
|
||||
channel: "telegram",
|
||||
to: `telegram:${chatId}`,
|
||||
accountId: route.accountId,
|
||||
// Preserve DM topic threadId for replies (fixes #8891)
|
||||
threadId: dmThreadId != null ? String(dmThreadId) : undefined,
|
||||
mainDmOwnerPin:
|
||||
pinnedMainDmOwner && senderId
|
||||
updateLastRouteSessionKey === route.mainSessionKey && pinnedMainDmOwner && senderId
|
||||
? {
|
||||
ownerRecipient: pinnedMainDmOwner,
|
||||
senderRecipient: senderId,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { logVerbose } from "../globals.js";
|
||||
import { getSessionBindingService } from "../infra/outbound/session-binding-service.js";
|
||||
import {
|
||||
buildAgentSessionKey,
|
||||
deriveLastRoutePolicy,
|
||||
pickFirstExistingAgentId,
|
||||
resolveAgentRoute,
|
||||
} from "../routing/resolve-route.js";
|
||||
@@ -67,6 +68,19 @@ export function resolveTelegramConversationRoute(params: {
|
||||
mainSessionKey: buildAgentMainSessionKey({
|
||||
agentId: topicAgentId,
|
||||
}).toLowerCase(),
|
||||
lastRoutePolicy: deriveLastRoutePolicy({
|
||||
sessionKey: buildAgentSessionKey({
|
||||
agentId: topicAgentId,
|
||||
channel: "telegram",
|
||||
accountId: params.accountId,
|
||||
peer: { kind: params.isGroup ? "group" : "direct", id: peerId },
|
||||
dmScope: params.cfg.session?.dmScope,
|
||||
identityLinks: params.cfg.session?.identityLinks,
|
||||
}).toLowerCase(),
|
||||
mainSessionKey: buildAgentMainSessionKey({
|
||||
agentId: topicAgentId,
|
||||
}).toLowerCase(),
|
||||
}),
|
||||
};
|
||||
logVerbose(
|
||||
`telegram: topic route override: topic=${params.resolvedThreadId ?? params.replyThreadId} agent=${topicAgentId} sessionKey=${route.sessionKey}`,
|
||||
@@ -103,6 +117,10 @@ export function resolveTelegramConversationRoute(params: {
|
||||
...route,
|
||||
sessionKey: boundSessionKey,
|
||||
agentId: resolveAgentIdFromSessionKey(boundSessionKey),
|
||||
lastRoutePolicy: deriveLastRoutePolicy({
|
||||
sessionKey: boundSessionKey,
|
||||
mainSessionKey: route.mainSessionKey,
|
||||
}),
|
||||
matchedBy: "binding.channel",
|
||||
};
|
||||
configuredBinding = null;
|
||||
|
||||
Reference in New Issue
Block a user