mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-18 17:44:45 +00:00
refactor: assemble channel contexts in core
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
import {
|
||||
buildChannelTurnContext,
|
||||
formatInboundEnvelope,
|
||||
resolveEnvelopeFormatOptions,
|
||||
} from "openclaw/plugin-sdk/channel-inbound";
|
||||
import { resolveChannelContextVisibilityMode } from "openclaw/plugin-sdk/context-visibility-runtime";
|
||||
import { resolvePinnedMainDmOwnerFromAllowlist } from "openclaw/plugin-sdk/conversation-runtime";
|
||||
import { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/dangerous-name-runtime";
|
||||
import { finalizeInboundContext } from "openclaw/plugin-sdk/reply-dispatch-runtime";
|
||||
import { createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
|
||||
import { buildAgentSessionKey, resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
|
||||
import { danger, logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env";
|
||||
@@ -22,7 +22,6 @@ import {
|
||||
} from "./inbound-context.js";
|
||||
import type { DiscordMessagePreflightContext } from "./message-handler.preflight.js";
|
||||
import {
|
||||
buildDiscordMediaPayload,
|
||||
resolveReferencedReplyMediaList,
|
||||
resolveDiscordMessageText,
|
||||
type DiscordMediaInfo,
|
||||
@@ -263,7 +262,6 @@ export async function buildDiscordMessageProcessContext(params: {
|
||||
parentSessionKey = undefined;
|
||||
}
|
||||
}
|
||||
const mediaPayload = buildDiscordMediaPayload(mediaListForContext);
|
||||
const preflightAudioIndex =
|
||||
preflightAudioTranscript === undefined
|
||||
? -1
|
||||
@@ -329,59 +327,102 @@ export async function buildDiscordMessageProcessContext(params: {
|
||||
sessionKey: effectiveSessionKey,
|
||||
});
|
||||
|
||||
const ctxPayload = finalizeInboundContext({
|
||||
Body: combinedBody,
|
||||
BodyForAgent: preflightAudioTranscript ?? baseText ?? text,
|
||||
InboundHistory: inboundHistory,
|
||||
RawBody: preflightAudioTranscript ?? baseText,
|
||||
CommandBody: preflightAudioTranscript ?? baseText,
|
||||
...(preflightAudioTranscript !== undefined ? { Transcript: preflightAudioTranscript } : {}),
|
||||
From: effectiveFrom,
|
||||
To: effectiveTo,
|
||||
SessionKey: effectiveSessionKey,
|
||||
AccountId: route.accountId,
|
||||
ChatType: isDirectMessage ? "direct" : "channel",
|
||||
ConversationLabel: fromLabel,
|
||||
SenderName: senderName,
|
||||
SenderId: sender.id,
|
||||
SenderUsername: senderUsername,
|
||||
SenderTag: sender.tag,
|
||||
GroupSubject: groupSubject,
|
||||
GroupChannel: groupChannel,
|
||||
MemberRoleIds: memberRoleIds,
|
||||
UntrustedContext: untrustedContext,
|
||||
GroupSystemPrompt: isGuildMessage ? groupSystemPrompt : undefined,
|
||||
GroupSpace: isGuildMessage ? (guildInfo?.id ?? guildSlug) || undefined : undefined,
|
||||
OwnerAllowFrom: ownerAllowFrom,
|
||||
Provider: "discord" as const,
|
||||
Surface: "discord" as const,
|
||||
WasMentioned: ctx.effectiveWasMentioned,
|
||||
MessageSid: canonicalMessageId ?? message.id,
|
||||
...(canonicalMessageId && canonicalMessageId !== message.id
|
||||
? { MessageSidFull: message.id }
|
||||
: {}),
|
||||
ReplyToId: filteredReplyContext?.id,
|
||||
ReplyToBody: filteredReplyContext?.body,
|
||||
ReplyToSender: filteredReplyContext?.sender,
|
||||
ParentSessionKey: autoThreadContext?.ParentSessionKey ?? threadKeys.parentSessionKey,
|
||||
ModelParentSessionKey:
|
||||
autoThreadContext?.ModelParentSessionKey ?? modelParentSessionKey ?? undefined,
|
||||
MessageThreadId: threadChannel?.id ?? autoThreadContext?.createdThreadId ?? undefined,
|
||||
ThreadStarterBody: !effectivePreviousTimestamp ? threadStarterBody : undefined,
|
||||
ThreadLabel: threadLabel,
|
||||
Timestamp: resolveTimestampMs(message.timestamp),
|
||||
...mediaPayload,
|
||||
...(preflightAudioIndex >= 0 ? { MediaTranscribedIndexes: [preflightAudioIndex] } : {}),
|
||||
CommandAuthorized: commandAuthorized,
|
||||
CommandTurn: {
|
||||
const ctxPayload = buildChannelTurnContext({
|
||||
channel: "discord",
|
||||
provider: "discord",
|
||||
surface: "discord",
|
||||
accountId: route.accountId,
|
||||
messageId: canonicalMessageId ?? message.id,
|
||||
messageIdFull: canonicalMessageId && canonicalMessageId !== message.id ? message.id : undefined,
|
||||
timestamp: resolveTimestampMs(message.timestamp),
|
||||
from: effectiveFrom,
|
||||
sender: {
|
||||
id: sender.id,
|
||||
name: senderName,
|
||||
username: senderUsername,
|
||||
tag: sender.tag,
|
||||
roles: memberRoleIds,
|
||||
displayLabel: senderLabel,
|
||||
},
|
||||
conversation: {
|
||||
kind: isDirectMessage ? "direct" : "channel",
|
||||
id: messageChannelId,
|
||||
label: fromLabel,
|
||||
spaceId: isGuildMessage ? (guildInfo?.id ?? guildSlug) || undefined : undefined,
|
||||
threadId: threadChannel?.id ?? autoThreadContext?.createdThreadId ?? undefined,
|
||||
routePeer: {
|
||||
kind: isDirectMessage ? "direct" : "channel",
|
||||
id: isDirectMessage ? author.id : messageChannelId,
|
||||
},
|
||||
},
|
||||
route: {
|
||||
agentId: route.agentId,
|
||||
accountId: route.accountId,
|
||||
routeSessionKey: route.sessionKey,
|
||||
dispatchSessionKey: effectiveSessionKey,
|
||||
parentSessionKey: autoThreadContext?.ParentSessionKey ?? threadKeys.parentSessionKey,
|
||||
modelParentSessionKey:
|
||||
autoThreadContext?.ModelParentSessionKey ?? modelParentSessionKey ?? undefined,
|
||||
},
|
||||
reply: {
|
||||
to: effectiveTo,
|
||||
originatingTo,
|
||||
},
|
||||
message: {
|
||||
body: combinedBody,
|
||||
rawBody: preflightAudioTranscript ?? baseText,
|
||||
bodyForAgent: preflightAudioTranscript ?? baseText ?? text,
|
||||
commandBody: preflightAudioTranscript ?? baseText,
|
||||
envelopeFrom: fromLabel,
|
||||
inboundHistory,
|
||||
},
|
||||
access: {
|
||||
mentions: {
|
||||
canDetectMention: ctx.canDetectMention,
|
||||
wasMentioned: ctx.effectiveWasMentioned,
|
||||
hasAnyMention: ctx.hasAnyMention,
|
||||
requireMention: ctx.shouldRequireMention,
|
||||
effectiveWasMentioned: ctx.effectiveWasMentioned,
|
||||
},
|
||||
commands: {
|
||||
authorized: commandAuthorized,
|
||||
allowTextCommands: ctx.allowTextCommands,
|
||||
useAccessGroups: false,
|
||||
authorizers: [],
|
||||
},
|
||||
},
|
||||
commandTurn: {
|
||||
kind: "text-slash" as const,
|
||||
source: "text" as const,
|
||||
authorized: commandAuthorized,
|
||||
body: preflightAudioTranscript ?? baseText,
|
||||
},
|
||||
CommandSource: "text" as const,
|
||||
OriginatingChannel: "discord" as const,
|
||||
OriginatingTo: originatingTo,
|
||||
media: mediaListForContext.map((media, index) => ({
|
||||
path: media.path,
|
||||
contentType: media.contentType,
|
||||
transcribed: index === preflightAudioIndex,
|
||||
})),
|
||||
supplemental: {
|
||||
quote: filteredReplyContext
|
||||
? {
|
||||
id: filteredReplyContext.id,
|
||||
body: filteredReplyContext.body,
|
||||
sender: filteredReplyContext.sender,
|
||||
}
|
||||
: undefined,
|
||||
thread: {
|
||||
starterBody: !effectivePreviousTimestamp ? threadStarterBody : undefined,
|
||||
label: threadLabel,
|
||||
},
|
||||
groupSystemPrompt: isGuildMessage ? groupSystemPrompt : undefined,
|
||||
},
|
||||
extra: {
|
||||
...(preflightAudioTranscript !== undefined ? { Transcript: preflightAudioTranscript } : {}),
|
||||
GroupSubject: groupSubject,
|
||||
GroupChannel: groupChannel,
|
||||
UntrustedContext: untrustedContext,
|
||||
OwnerAllowFrom: ownerAllowFrom,
|
||||
},
|
||||
});
|
||||
const persistedSessionKey = ctxPayload.SessionKey ?? route.sessionKey;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
type AckReactionScope,
|
||||
} from "openclaw/plugin-sdk/channel-feedback";
|
||||
import {
|
||||
buildChannelTurnContext,
|
||||
buildMentionRegexes,
|
||||
formatInboundEnvelope,
|
||||
implicitMentionKindWhen,
|
||||
@@ -18,7 +19,6 @@ import { ensureConfiguredBindingRouteReady } from "openclaw/plugin-sdk/conversat
|
||||
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
||||
import { recordDroppedChannelTurnHistory } from "openclaw/plugin-sdk/inbound-reply-dispatch";
|
||||
import { mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
|
||||
import { finalizeInboundContext } from "openclaw/plugin-sdk/reply-dispatch-runtime";
|
||||
import { createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
|
||||
import type { FinalizedMsgContext } from "openclaw/plugin-sdk/reply-runtime";
|
||||
import { resolveInboundLastRouteSessionKey } from "openclaw/plugin-sdk/routing";
|
||||
@@ -1056,8 +1056,6 @@ export async function prepareSlackMessage(params: {
|
||||
|
||||
// Use direct media (including forwarded attachment media) if available, else thread starter media
|
||||
const effectiveMedia = effectiveDirectMedia ?? threadStarterMedia;
|
||||
const firstMedia = effectiveMedia?.[0];
|
||||
|
||||
const inboundHistory =
|
||||
isRoomish && ctx.historyLimit > 0
|
||||
? channelHistory.buildInboundHistory({
|
||||
@@ -1067,63 +1065,98 @@ export async function prepareSlackMessage(params: {
|
||||
: dmHistoryContext.inboundHistory;
|
||||
const commandBody = textForCommandDetection.trim();
|
||||
|
||||
const ctxPayload = finalizeInboundContext({
|
||||
Body: combinedBody,
|
||||
BodyForAgent: rawBody,
|
||||
InboundHistory: inboundHistory,
|
||||
RawBody: rawBody,
|
||||
CommandBody: commandBody,
|
||||
BodyForCommands: commandBody,
|
||||
From: slackFrom,
|
||||
To: slackTo,
|
||||
SessionKey: sessionKey,
|
||||
AccountId: route.accountId,
|
||||
ChatType: chatType,
|
||||
ConversationLabel: envelopeFrom,
|
||||
GroupSubject: isRoomish ? roomLabel : undefined,
|
||||
GroupSpace: ctx.teamId || undefined,
|
||||
GroupSystemPrompt: groupSystemPrompt,
|
||||
UntrustedContext: untrustedChannelMetadata ? [untrustedChannelMetadata] : undefined,
|
||||
SenderName: senderName,
|
||||
SenderId: senderId,
|
||||
Provider: "slack" as const,
|
||||
Surface: "slack" as const,
|
||||
MessageSid: message.ts,
|
||||
ReplyToId: threadContext.replyToId,
|
||||
// Preserve thread context for routed tool notifications.
|
||||
MessageThreadId: threadContext.messageThreadId,
|
||||
ParentSessionKey: threadKeys.parentSessionKey,
|
||||
// Only include thread starter body for NEW sessions (existing sessions already have it in their transcript)
|
||||
ThreadStarterBody: !threadSessionPreviousTimestamp ? threadStarterBody : undefined,
|
||||
ThreadHistoryBody: threadHistoryBody,
|
||||
IsFirstThreadTurn:
|
||||
isThreadReply && threadTs && !threadSessionPreviousTimestamp ? true : undefined,
|
||||
ThreadLabel: threadLabel,
|
||||
Timestamp: message.ts ? Math.round(Number(message.ts) * 1000) : undefined,
|
||||
...buildSlackMentionContextPayload({
|
||||
isRoomish,
|
||||
effectiveWasMentioned,
|
||||
explicitlyMentioned,
|
||||
mentionedUserIds,
|
||||
mentionedSubteamIds,
|
||||
matchedImplicitMentionKinds,
|
||||
mentionSource,
|
||||
}),
|
||||
MediaPath: firstMedia?.path,
|
||||
MediaType: firstMedia?.contentType,
|
||||
MediaUrl: firstMedia?.path,
|
||||
MediaPaths:
|
||||
effectiveMedia && effectiveMedia.length > 0 ? effectiveMedia.map((m) => m.path) : undefined,
|
||||
MediaUrls:
|
||||
effectiveMedia && effectiveMedia.length > 0 ? effectiveMedia.map((m) => m.path) : undefined,
|
||||
MediaTypes:
|
||||
effectiveMedia && effectiveMedia.length > 0
|
||||
? effectiveMedia.map((m) => m.contentType ?? "")
|
||||
: undefined,
|
||||
CommandAuthorized: commandAuthorized,
|
||||
OriginatingChannel: "slack" as const,
|
||||
OriginatingTo: slackTo,
|
||||
NativeChannelId: message.channel,
|
||||
const ctxPayload = buildChannelTurnContext({
|
||||
channel: "slack",
|
||||
provider: "slack",
|
||||
surface: "slack",
|
||||
accountId: route.accountId,
|
||||
messageId: message.ts,
|
||||
timestamp: message.ts ? Math.round(Number(message.ts) * 1000) : undefined,
|
||||
from: slackFrom,
|
||||
sender: {
|
||||
id: senderId,
|
||||
name: senderName,
|
||||
displayLabel: senderName,
|
||||
},
|
||||
conversation: {
|
||||
kind: chatType,
|
||||
id: message.channel,
|
||||
label: envelopeFrom,
|
||||
spaceId: ctx.teamId || undefined,
|
||||
threadId: threadContext.messageThreadId,
|
||||
nativeChannelId: message.channel,
|
||||
routePeer: {
|
||||
kind: chatType,
|
||||
id: message.channel,
|
||||
},
|
||||
},
|
||||
route: {
|
||||
agentId: route.agentId,
|
||||
accountId: route.accountId,
|
||||
routeSessionKey: sessionKey,
|
||||
parentSessionKey: threadKeys.parentSessionKey,
|
||||
},
|
||||
reply: {
|
||||
to: slackTo,
|
||||
originatingTo: slackTo,
|
||||
replyToId: threadContext.replyToId,
|
||||
messageThreadId: threadContext.messageThreadId,
|
||||
nativeChannelId: message.channel,
|
||||
},
|
||||
message: {
|
||||
body: combinedBody,
|
||||
bodyForAgent: rawBody,
|
||||
rawBody,
|
||||
commandBody,
|
||||
envelopeFrom,
|
||||
inboundHistory,
|
||||
},
|
||||
access: {
|
||||
mentions: {
|
||||
canDetectMention: isRoomish,
|
||||
wasMentioned: effectiveWasMentioned,
|
||||
hasAnyMention: explicitlyMentioned || mentionedSubteamIds.length > 0,
|
||||
implicitMentionKinds: matchedImplicitMentionKinds as Array<
|
||||
"reply_to_bot" | "quoted_bot" | "bot_thread_participant" | "native"
|
||||
>,
|
||||
requireMention: shouldRequireMention,
|
||||
effectiveWasMentioned,
|
||||
},
|
||||
commands: {
|
||||
authorized: commandAuthorized,
|
||||
allowTextCommands,
|
||||
useAccessGroups: false,
|
||||
authorizers: [],
|
||||
},
|
||||
},
|
||||
media: effectiveMedia?.map((media) => ({
|
||||
path: media.path,
|
||||
contentType: media.contentType,
|
||||
})),
|
||||
supplemental: {
|
||||
thread: {
|
||||
// Only include thread starter body for NEW sessions (existing sessions already have it in their transcript)
|
||||
starterBody: !threadSessionPreviousTimestamp ? threadStarterBody : undefined,
|
||||
historyBody: threadHistoryBody,
|
||||
label: threadLabel,
|
||||
},
|
||||
groupSystemPrompt,
|
||||
},
|
||||
extra: {
|
||||
GroupSubject: isRoomish ? roomLabel : undefined,
|
||||
UntrustedContext: untrustedChannelMetadata ? [untrustedChannelMetadata] : undefined,
|
||||
IsFirstThreadTurn:
|
||||
isThreadReply && threadTs && !threadSessionPreviousTimestamp ? true : undefined,
|
||||
...buildSlackMentionContextPayload({
|
||||
isRoomish,
|
||||
effectiveWasMentioned,
|
||||
explicitlyMentioned,
|
||||
mentionedUserIds,
|
||||
mentionedSubteamIds,
|
||||
matchedImplicitMentionKinds,
|
||||
mentionSource,
|
||||
}),
|
||||
},
|
||||
}) satisfies FinalizedMsgContext;
|
||||
|
||||
if (isRoomish && !shouldRequireMention) {
|
||||
|
||||
Reference in New Issue
Block a user