11import { ChannelType , type RequestClient } from "@buape/carbon" ;
22import { resolveAckReaction , resolveHumanDelayConfig } from "openclaw/plugin-sdk/agent-runtime" ;
33import { EmbeddedBlockChunker } from "openclaw/plugin-sdk/agent-runtime" ;
4+ import { createChannelReplyPipeline } from "openclaw/plugin-sdk/channel-reply-pipeline" ;
45import { shouldAckReaction as shouldAckReactionGate } from "openclaw/plugin-sdk/channel-runtime" ;
56import { logTypingFailure , logAckFailure } from "openclaw/plugin-sdk/channel-runtime" ;
6- import { createReplyPrefixOptions } from "openclaw/plugin-sdk/channel-runtime" ;
77import { recordInboundSession } from "openclaw/plugin-sdk/channel-runtime" ;
88import {
99 createStatusReactionController ,
1010 DEFAULT_TIMING ,
1111 type StatusReactionAdapter ,
1212} from "openclaw/plugin-sdk/channel-runtime" ;
13- import { createTypingCallbacks } from "openclaw/plugin-sdk/channel-runtime" ;
1413import { isDangerousNameMatchingEnabled } from "openclaw/plugin-sdk/config-runtime" ;
1514import { resolveDiscordPreviewStreamMode } from "openclaw/plugin-sdk/config-runtime" ;
1615import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/config-runtime" ;
@@ -420,11 +419,24 @@ export async function processDiscordMessage(ctx: DiscordMessagePreflightContext)
420419 ? deliverTarget . slice ( "channel:" . length )
421420 : messageChannelId ;
422421
423- const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions ( {
422+ const { onModelSelected, ...replyPipeline } = createChannelReplyPipeline ( {
424423 cfg,
425424 agentId : route . agentId ,
426425 channel : "discord" ,
427426 accountId : route . accountId ,
427+ typing : {
428+ start : ( ) => sendTyping ( { client, channelId : typingChannelId } ) ,
429+ onStartError : ( err ) => {
430+ logTypingFailure ( {
431+ log : logVerbose ,
432+ channel : "discord" ,
433+ target : typingChannelId ,
434+ error : err ,
435+ } ) ;
436+ } ,
437+ // Long tool-heavy runs are expected on Discord; keep heartbeats alive.
438+ maxDurationMs : DISCORD_TYPING_MAX_DURATION_MS ,
439+ } ,
428440 } ) ;
429441 const tableMode = resolveMarkdownTableMode ( {
430442 cfg,
@@ -438,20 +450,6 @@ export async function processDiscordMessage(ctx: DiscordMessagePreflightContext)
438450 } ) ;
439451 const chunkMode = resolveChunkMode ( cfg , "discord" , accountId ) ;
440452
441- const typingCallbacks = createTypingCallbacks ( {
442- start : ( ) => sendTyping ( { client, channelId : typingChannelId } ) ,
443- onStartError : ( err ) => {
444- logTypingFailure ( {
445- log : logVerbose ,
446- channel : "discord" ,
447- target : typingChannelId ,
448- error : err ,
449- } ) ;
450- } ,
451- // Long tool-heavy runs are expected on Discord; keep heartbeats alive.
452- maxDurationMs : DISCORD_TYPING_MAX_DURATION_MS ,
453- } ) ;
454-
455453 // --- Discord draft stream (edit-based preview streaming) ---
456454 const discordStreamMode = resolveDiscordPreviewStreamMode ( discordConfig ) ;
457455 const draftMaxChars = Math . min ( textLimit , 2000 ) ;
@@ -597,9 +595,8 @@ export async function processDiscordMessage(ctx: DiscordMessagePreflightContext)
597595
598596 const { dispatcher, replyOptions, markDispatchIdle, markRunComplete } =
599597 createReplyDispatcherWithTyping ( {
600- ...prefixOptions ,
598+ ...replyPipeline ,
601599 humanDelay : resolveHumanDelayConfig ( cfg , route . agentId ) ,
602- typingCallbacks,
603600 deliver : async ( payload : ReplyPayload , info ) => {
604601 if ( isProcessAborted ( abortSignal ) ) {
605602 return ;
@@ -715,7 +712,7 @@ export async function processDiscordMessage(ctx: DiscordMessagePreflightContext)
715712 if ( isProcessAborted ( abortSignal ) ) {
716713 return ;
717714 }
718- await typingCallbacks . onReplyStart ( ) ;
715+ await replyPipeline . typingCallbacks ? .onReplyStart ( ) ;
719716 await statusReactions . setThinking ( ) ;
720717 } ,
721718 } ) ;
0 commit comments