feat(csm): bootstrap discussion callback flow

This commit is contained in:
zhi
2026-04-02 02:35:08 +00:00
parent 9fa71f37bf
commit 62cd2f20cf
9 changed files with 450 additions and 3 deletions

View File

@@ -25,6 +25,10 @@ type BeforeMessageWriteDeps = {
content: string,
logger: { info: (m: string) => void; warn: (m: string) => void },
) => Promise<void>;
discussionService?: {
maybeSendIdleReminder: (channelId: string) => Promise<void>;
getDiscussion: (channelId: string) => { status: string } | undefined;
};
};
export function registerBeforeMessageWriteHook(deps: BeforeMessageWriteDeps): void {
@@ -41,6 +45,7 @@ export function registerBeforeMessageWriteHook(deps: BeforeMessageWriteDeps): vo
ensureTurnOrder,
resolveDiscordUserId,
sendModeratorMessage,
discussionService,
} = deps;
api.on("before_message_write", (event, ctx) => {
@@ -164,6 +169,11 @@ export function registerBeforeMessageWriteHook(deps: BeforeMessageWriteDeps): vo
);
if (!nextSpeaker) {
if (discussionService?.getDiscussion(channelId)?.status === "active") {
void discussionService.maybeSendIdleReminder(channelId).catch((err) => {
api.logger.warn(`dirigent: idle reminder failed: ${String(err)}`);
});
}
if (shouldDebugLog(live, channelId)) {
api.logger.info(`dirigent: before_message_write all agents no-reply, going dormant - no handoff`);
}

View File

@@ -21,6 +21,7 @@ type BeforeModelResolveDeps = {
sessionAllowed: Map<string, boolean>;
sessionChannelId: Map<string, string>;
sessionAccountId: Map<string, string>;
forceNoReplySessions: Set<string>;
policyState: { channelPolicies: Record<string, unknown> };
DECISION_TTL_MS: number;
ensurePolicyStateLoaded: (api: OpenClawPluginApi, config: DirigentConfig) => void;
@@ -38,6 +39,7 @@ export function registerBeforeModelResolveHook(deps: BeforeModelResolveDeps): vo
sessionAllowed,
sessionChannelId,
sessionAccountId,
forceNoReplySessions,
policyState,
DECISION_TTL_MS,
ensurePolicyStateLoaded,
@@ -54,6 +56,14 @@ export function registerBeforeModelResolveHook(deps: BeforeModelResolveDeps): vo
const live = baseConfig as DirigentConfig & DebugConfig;
ensurePolicyStateLoaded(api, live);
if (forceNoReplySessions.has(key)) {
return {
model: ctx.model,
provider: ctx.provider,
noReply: true,
};
}
const prompt = ((event as Record<string, unknown>).prompt as string) || "";
if (live.enableDebugLogs) {

View File

@@ -18,6 +18,9 @@ type MessageReceivedDeps = {
recordChannelAccount: (api: OpenClawPluginApi, channelId: string, accountId: string) => boolean;
extractMentionedUserIds: (content: string) => string[];
buildUserIdToAccountIdMap: (api: OpenClawPluginApi) => Map<string, string>;
discussionService?: {
maybeReplyClosedChannel: (channelId: string, senderId?: string) => Promise<boolean>;
};
};
export function registerMessageReceivedHook(deps: MessageReceivedDeps): void {
@@ -31,6 +34,7 @@ export function registerMessageReceivedHook(deps: MessageReceivedDeps): void {
recordChannelAccount,
extractMentionedUserIds,
buildUserIdToAccountIdMap,
discussionService,
} = deps;
api.on("message_received", async (event, ctx) => {
@@ -51,6 +55,11 @@ export function registerMessageReceivedHook(deps: MessageReceivedDeps): void {
(typeof (e as Record<string, unknown>).from === "string" ? ((e as Record<string, unknown>).from as string) : "");
const moderatorUserId = getModeratorUserId(livePre);
if (discussionService) {
const closedHandled = await discussionService.maybeReplyClosedChannel(preChannelId, from);
if (closedHandled) return;
}
if (moderatorUserId && from === moderatorUserId) {
if (shouldDebugLog(livePre, preChannelId)) {
api.logger.info(`dirigent: ignoring moderator message in channel=${preChannelId}`);