@@ -1709,6 +1709,83 @@ describe("handleFeishuMessage command authorization", () => {
17091709 ) ;
17101710 } ) ;
17111711
1712+ it ( "replies inside P2P thread when thread_id is present (#38806)" , async ( ) => {
1713+ mockShouldComputeCommandAuthorized . mockReturnValue ( false ) ;
1714+
1715+ const cfg : ClawdbotConfig = {
1716+ channels : {
1717+ feishu : {
1718+ dmPolicy : "open" ,
1719+ } ,
1720+ } ,
1721+ } as ClawdbotConfig ;
1722+
1723+ const event : FeishuMessageEvent = {
1724+ sender : {
1725+ sender_id : {
1726+ open_id : "ou-sender" ,
1727+ } ,
1728+ } ,
1729+ message : {
1730+ message_id : "msg-p2p-thread" ,
1731+ chat_id : "oc-dm" ,
1732+ chat_type : "p2p" ,
1733+ root_id : "om_p2p_thread_root" ,
1734+ thread_id : "omt_p2p_thread" ,
1735+ message_type : "text" ,
1736+ content : JSON . stringify ( { text : "reply in p2p thread" } ) ,
1737+ } ,
1738+ } ;
1739+
1740+ await dispatchMessage ( { cfg, event } ) ;
1741+
1742+ expect ( mockCreateFeishuReplyDispatcher ) . toHaveBeenCalledWith (
1743+ expect . objectContaining ( {
1744+ replyToMessageId : "om_p2p_thread_root" ,
1745+ replyInThread : true ,
1746+ threadReply : true ,
1747+ skipReplyToInMessages : false ,
1748+ } ) ,
1749+ ) ;
1750+ } ) ;
1751+
1752+ it ( "does not reply in thread for normal P2P messages without thread_id" , async ( ) => {
1753+ mockShouldComputeCommandAuthorized . mockReturnValue ( false ) ;
1754+
1755+ const cfg : ClawdbotConfig = {
1756+ channels : {
1757+ feishu : {
1758+ dmPolicy : "open" ,
1759+ } ,
1760+ } ,
1761+ } as ClawdbotConfig ;
1762+
1763+ const event : FeishuMessageEvent = {
1764+ sender : {
1765+ sender_id : {
1766+ open_id : "ou-sender" ,
1767+ } ,
1768+ } ,
1769+ message : {
1770+ message_id : "msg-p2p-normal" ,
1771+ chat_id : "oc-dm" ,
1772+ chat_type : "p2p" ,
1773+ message_type : "text" ,
1774+ content : JSON . stringify ( { text : "normal dm" } ) ,
1775+ } ,
1776+ } ;
1777+
1778+ await dispatchMessage ( { cfg, event } ) ;
1779+
1780+ expect ( mockCreateFeishuReplyDispatcher ) . toHaveBeenCalledWith (
1781+ expect . objectContaining ( {
1782+ replyInThread : false ,
1783+ threadReply : false ,
1784+ skipReplyToInMessages : true ,
1785+ } ) ,
1786+ ) ;
1787+ } ) ;
1788+
17121789 it ( "does not dispatch twice for the same image message_id (concurrent dedupe)" , async ( ) => {
17131790 mockShouldComputeCommandAuthorized . mockReturnValue ( false ) ;
17141791
0 commit comments