mirror of https://github.com/openclaw/openclaw.git
fix(msteams): persist conversation reference during DM pairing (#60432)
* fix(msteams): persist conversation reference during DM pairing (#43323) * ci: retrigger checks --------- Co-authored-by: Brad Groux <bradgroux@users.noreply.github.com>
This commit is contained in:
parent
06c6ff6670
commit
dd2faa3764
|
|
@ -235,7 +235,7 @@ describe("msteams monitor handler authz", () => {
|
|||
});
|
||||
|
||||
it("keeps the DM pairing path wired through shared access resolution", async () => {
|
||||
const { deps, upsertPairingRequest } = createDeps({
|
||||
const { conversationStore, deps, upsertPairingRequest, recordInboundSession } = createDeps({
|
||||
channels: {
|
||||
msteams: {
|
||||
dmPolicy: "pairing",
|
||||
|
|
@ -262,8 +262,18 @@ describe("msteams monitor handler authz", () => {
|
|||
conversation: {
|
||||
id: "a:personal-chat",
|
||||
conversationType: "personal",
|
||||
tenantId: "tenant-1",
|
||||
},
|
||||
channelId: "msteams",
|
||||
serviceUrl: "https://smba.trafficmanager.net/amer/",
|
||||
locale: "en-US",
|
||||
channelData: {},
|
||||
entities: [
|
||||
{
|
||||
type: "clientInfo",
|
||||
timezone: "America/New_York",
|
||||
},
|
||||
],
|
||||
attachments: [],
|
||||
},
|
||||
sendActivity: vi.fn(async () => undefined),
|
||||
|
|
@ -275,6 +285,33 @@ describe("msteams monitor handler authz", () => {
|
|||
id: "new-user-aad",
|
||||
meta: { name: "New User" },
|
||||
});
|
||||
expect(conversationStore.upsert).toHaveBeenCalledWith("a:personal-chat", {
|
||||
activityId: "msg-pairing",
|
||||
user: {
|
||||
id: "new-user-id",
|
||||
aadObjectId: "new-user-aad",
|
||||
name: "New User",
|
||||
},
|
||||
agent: {
|
||||
id: "bot-id",
|
||||
name: "Bot",
|
||||
},
|
||||
bot: {
|
||||
id: "bot-id",
|
||||
name: "Bot",
|
||||
},
|
||||
conversation: {
|
||||
id: "a:personal-chat",
|
||||
conversationType: "personal",
|
||||
tenantId: "tenant-1",
|
||||
},
|
||||
channelId: "msteams",
|
||||
serviceUrl: "https://smba.trafficmanager.net/amer/",
|
||||
locale: "en-US",
|
||||
timezone: "America/New_York",
|
||||
});
|
||||
expect(recordInboundSession).not.toHaveBeenCalled();
|
||||
expect(runtimeApiMockState.dispatchReplyFromConfigWithSettledDispatcher).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("logs an info drop reason when dmPolicy allowlist rejects a sender", async () => {
|
||||
|
|
|
|||
|
|
@ -52,6 +52,37 @@ import { recordMSTeamsSentMessage, wasMSTeamsMessageSent } from "../sent-message
|
|||
import { resolveMSTeamsSenderAccess } from "./access.js";
|
||||
import { resolveMSTeamsInboundMedia } from "./inbound-media.js";
|
||||
|
||||
function buildStoredConversationReference(params: {
|
||||
activity: MSTeamsTurnContext["activity"];
|
||||
conversationId: string;
|
||||
conversationType: string;
|
||||
teamId?: string;
|
||||
}): StoredConversationReference {
|
||||
const { activity, conversationId, conversationType, teamId } = params;
|
||||
const from = activity.from;
|
||||
const conversation = activity.conversation;
|
||||
const agent = activity.recipient;
|
||||
const clientInfo = activity.entities?.find((e) => e.type === "clientInfo") as
|
||||
| { timezone?: string }
|
||||
| undefined;
|
||||
return {
|
||||
activityId: activity.id,
|
||||
user: from ? { id: from.id, name: from.name, aadObjectId: from.aadObjectId } : undefined,
|
||||
agent,
|
||||
bot: agent ? { id: agent.id, name: agent.name } : undefined,
|
||||
conversation: {
|
||||
id: conversationId,
|
||||
conversationType,
|
||||
tenantId: conversation?.tenantId,
|
||||
},
|
||||
teamId,
|
||||
channelId: activity.channelId,
|
||||
serviceUrl: activity.serviceUrl,
|
||||
locale: activity.locale,
|
||||
...(clientInfo?.timezone ? { timezone: clientInfo.timezone } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
|
||||
const {
|
||||
cfg,
|
||||
|
|
@ -140,6 +171,12 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
|
|||
const conversationMessageId = extractMSTeamsConversationMessageId(rawConversationId);
|
||||
const conversationType = conversation?.conversationType ?? "personal";
|
||||
const teamId = activity.channelData?.team?.id;
|
||||
const conversationRef = buildStoredConversationReference({
|
||||
activity,
|
||||
conversationId,
|
||||
conversationType,
|
||||
teamId,
|
||||
});
|
||||
|
||||
const {
|
||||
dmPolicy,
|
||||
|
|
@ -177,6 +214,11 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
|
|||
allowNameMatching,
|
||||
});
|
||||
if (access.decision === "pairing") {
|
||||
conversationStore.upsert(conversationId, conversationRef).catch((err) => {
|
||||
log.debug?.("failed to save conversation reference", {
|
||||
error: formatUnknownError(err),
|
||||
});
|
||||
});
|
||||
const request = await pairing.upsertPairingRequest({
|
||||
id: senderId,
|
||||
meta: { name: senderName },
|
||||
|
|
@ -306,30 +348,6 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Extract clientInfo entity (Teams sends this on every activity with timezone, locale, etc.)
|
||||
const clientInfo = activity.entities?.find((e) => e.type === "clientInfo") as
|
||||
| { timezone?: string; locale?: string; country?: string; platform?: string }
|
||||
| undefined;
|
||||
|
||||
// Build conversation reference for proactive replies.
|
||||
const agent = activity.recipient;
|
||||
const conversationRef: StoredConversationReference = {
|
||||
activityId: activity.id,
|
||||
user: { id: from.id, name: from.name, aadObjectId: from.aadObjectId },
|
||||
agent,
|
||||
bot: agent ? { id: agent.id, name: agent.name } : undefined,
|
||||
conversation: {
|
||||
id: conversationId,
|
||||
conversationType,
|
||||
tenantId: conversation?.tenantId,
|
||||
},
|
||||
teamId,
|
||||
channelId: activity.channelId,
|
||||
serviceUrl: activity.serviceUrl,
|
||||
locale: activity.locale,
|
||||
// Only set timezone if present (preserve previously stored value on next upsert)
|
||||
...(clientInfo?.timezone ? { timezone: clientInfo.timezone } : {}),
|
||||
};
|
||||
conversationStore.upsert(conversationId, conversationRef).catch((err) => {
|
||||
log.debug?.("failed to save conversation reference", {
|
||||
error: formatUnknownError(err),
|
||||
|
|
@ -642,8 +660,10 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
|
|||
// Use Teams clientInfo timezone if no explicit userTimezone is configured.
|
||||
// This ensures the agent knows the sender's timezone for time-aware responses
|
||||
// and proactive sends within the same session.
|
||||
// Apply Teams clientInfo timezone if no explicit userTimezone is configured.
|
||||
const senderTimezone = clientInfo?.timezone || conversationRef.timezone;
|
||||
const activityClientInfo = activity.entities?.find((e) => e.type === "clientInfo") as
|
||||
| { timezone?: string }
|
||||
| undefined;
|
||||
const senderTimezone = activityClientInfo?.timezone || conversationRef.timezone;
|
||||
const configOverride =
|
||||
senderTimezone && !cfg.agents?.defaults?.userTimezone
|
||||
? {
|
||||
|
|
|
|||
Loading…
Reference in New Issue