@@ -175,6 +175,7 @@ type DispatchInboundParams = {
175175 } ) => Promise < void > | void ;
176176 onReplyStart ?: ( ) => Promise < void > | void ;
177177 sourceReplyDeliveryMode ?: "automatic" | "message_tool_only" ;
178+ typingKeepalive ?: boolean ;
178179 disableBlockStreaming ?: boolean ;
179180 suppressDefaultToolProgressMessages ?: boolean ;
180181 queuedDeliveryCorrelations ?: Array < { begin : ( ) => ( ) => void } > ;
@@ -944,6 +945,7 @@ describe("processDiscordMessage ack reactions", () => {
944945 expect ( replyTypingFeedback . onReplyStart ) . toHaveBeenCalledTimes ( 1 ) ;
945946 expect ( replyTypingFeedback . onIdle ) . toHaveBeenCalledTimes ( 1 ) ;
946947 expect ( replyTypingFeedback . onCleanup ) . toHaveBeenCalledTimes ( 1 ) ;
948+ expect ( getLastDispatchReplyOptions ( ) ?. typingKeepalive ) . toBe ( false ) ;
947949 expect ( typingMocks . sendTyping ) . not . toHaveBeenCalled ( ) ;
948950 } ) ;
949951
@@ -984,6 +986,33 @@ describe("processDiscordMessage ack reactions", () => {
984986 }
985987 } ) ;
986988
989+ it ( "keeps one typing refresh loop for default message-tool replies" , async ( ) => {
990+ vi . useFakeTimers ( ) ;
991+ try {
992+ dispatchInboundMessage . mockImplementationOnce ( async ( params ?: DispatchInboundParams ) => {
993+ await params ?. replyOptions ?. onReplyStart ?.( ) ;
994+ await vi . advanceTimersByTimeAsync ( 3_500 ) ;
995+ return createNoQueuedDispatchResult ( ) ;
996+ } ) ;
997+ const ctx = await createBaseContext ( {
998+ shouldRequireMention : false ,
999+ effectiveWasMentioned : false ,
1000+ cfg : {
1001+ messages : { groupChat : { visibleReplies : "message_tool" } } ,
1002+ session : { store : "/tmp/openclaw-discord-process-test-sessions.json" } ,
1003+ } ,
1004+ route : BASE_CHANNEL_ROUTE ,
1005+ } ) ;
1006+
1007+ await runProcessDiscordMessage ( ctx ) ;
1008+
1009+ expect ( getLastDispatchReplyOptions ( ) ?. typingKeepalive ) . toBe ( false ) ;
1010+ expect ( typingMocks . sendTyping ) . toHaveBeenCalledTimes ( 2 ) ;
1011+ } finally {
1012+ vi . useRealTimers ( ) ;
1013+ }
1014+ } ) ;
1015+
9871016 it ( "debounces intermediate phase reactions and jumps to done for short runs" , async ( ) => {
9881017 dispatchInboundMessage . mockImplementationOnce ( async ( params ?: DispatchInboundParams ) => {
9891018 await params ?. replyOptions ?. onReasoningStream ?.( ) ;
@@ -1532,6 +1561,7 @@ describe("processDiscordMessage session routing", () => {
15321561
15331562 expectRecordFields ( requireRecord ( getLastDispatchReplyOptions ( ) , "dispatch reply options" ) , {
15341563 sourceReplyDeliveryMode : "message_tool_only" ,
1564+ typingKeepalive : false ,
15351565 disableBlockStreaming : true ,
15361566 } ) ;
15371567 expect ( createDiscordDraftStream ) . not . toHaveBeenCalled ( ) ;
0 commit comments