import type { MsgContext } from "../../auto-reply/templating.js"; import { normalizeChatType } from "../../channels/chat-type.js"; import { resolveConversationLabel } from "../../channels/conversation-label.js"; import { getChannelDock } from "../../channels/dock.js"; import { normalizeChannelId } from "../../channels/plugins/index.js"; import { normalizeMessageChannel } from "../../utils/message-channel.js"; import { buildGroupDisplayName, resolveGroupSessionKey } from "./group.js"; import type { GroupKeyResolution, SessionEntry, SessionOrigin } from "./types.js"; const mergeOrigin = ( existing: SessionOrigin | undefined, next: SessionOrigin | undefined, ): SessionOrigin | undefined => { if (!existing && !next) { return undefined; } const merged: SessionOrigin = existing ? { ...existing } : {}; if (next?.label) { merged.label = next.label; } if (next?.provider) { merged.provider = next.provider; } if (next?.surface) { merged.surface = next.surface; } if (next?.chatType) { merged.chatType = next.chatType; } if (next?.from) { merged.from = next.from; } if (next?.to) { merged.to = next.to; } if (next?.accountId) { merged.accountId = next.accountId; } if (next?.threadId != null && next.threadId !== "") { merged.threadId = next.threadId; } return Object.keys(merged).length > 0 ? merged : undefined; }; export function deriveSessionOrigin(ctx: MsgContext): SessionOrigin | undefined { const label = resolveConversationLabel(ctx)?.trim(); const providerRaw = (typeof ctx.OriginatingChannel === "string" && ctx.OriginatingChannel) || ctx.Surface || ctx.Provider; const provider = normalizeMessageChannel(providerRaw); const surface = ctx.Surface?.trim().toLowerCase(); const chatType = normalizeChatType(ctx.ChatType) ?? undefined; const from = ctx.From?.trim(); const to = (typeof ctx.OriginatingTo === "string" ? ctx.OriginatingTo : ctx.To)?.trim() ?? undefined; const accountId = ctx.AccountId?.trim(); const threadId = ctx.MessageThreadId ?? undefined; const origin: SessionOrigin = {}; if (label) { origin.label = label; } if (provider) { origin.provider = provider; } if (surface) { origin.surface = surface; } if (chatType) { origin.chatType = chatType; } if (from) { origin.from = from; } if (to) { origin.to = to; } if (accountId) { origin.accountId = accountId; } if (threadId != null && threadId !== "") { origin.threadId = threadId; } return Object.keys(origin).length > 0 ? origin : undefined; } export function snapshotSessionOrigin(entry?: SessionEntry): SessionOrigin | undefined { if (!entry?.origin) { return undefined; } return { ...entry.origin }; } export function deriveGroupSessionPatch(params: { ctx: MsgContext; sessionKey: string; existing?: SessionEntry; groupResolution?: GroupKeyResolution | null; }): Partial | null { const resolution = params.groupResolution ?? resolveGroupSessionKey(params.ctx); if (!resolution?.channel) { return null; } const channel = resolution.channel; const subject = params.ctx.GroupSubject?.trim(); const space = params.ctx.GroupSpace?.trim(); const explicitChannel = params.ctx.GroupChannel?.trim(); const normalizedChannel = normalizeChannelId(channel); const isChannelProvider = Boolean( normalizedChannel && getChannelDock(normalizedChannel)?.capabilities.chatTypes.includes("channel"), ); const nextGroupChannel = explicitChannel ?? ((resolution.chatType === "channel" || isChannelProvider) && subject && subject.startsWith("#") ? subject : undefined); const nextSubject = nextGroupChannel ? undefined : subject; const patch: Partial = { chatType: resolution.chatType ?? "group", channel, groupId: resolution.id, }; if (nextSubject) { patch.subject = nextSubject; } if (nextGroupChannel) { patch.groupChannel = nextGroupChannel; } if (space) { patch.space = space; } const displayName = buildGroupDisplayName({ provider: channel, subject: nextSubject ?? params.existing?.subject, groupChannel: nextGroupChannel ?? params.existing?.groupChannel, space: space ?? params.existing?.space, id: resolution.id, key: params.sessionKey, }); if (displayName) { patch.displayName = displayName; } return patch; } export function deriveSessionMetaPatch(params: { ctx: MsgContext; sessionKey: string; existing?: SessionEntry; groupResolution?: GroupKeyResolution | null; }): Partial | null { const groupPatch = deriveGroupSessionPatch(params); const origin = deriveSessionOrigin(params.ctx); if (!groupPatch && !origin) { return null; } const patch: Partial = groupPatch ? { ...groupPatch } : {}; const mergedOrigin = mergeOrigin(params.existing?.origin, origin); if (mergedOrigin) { patch.origin = mergedOrigin; } return Object.keys(patch).length > 0 ? patch : null; }