@@ -1908,6 +1908,58 @@ Second paragraph should still reach the agent after Slack's preview cutoff.`;
19081908 expect ( replies ) . toHaveBeenCalledTimes ( 1 ) ;
19091909 } ) ;
19101910
1911+ it ( "preserves existing thread fallback when channel runtime is omitted" , async ( ) => {
1912+ const { storePath } = storeFixture . makeTmpStorePath ( ) ;
1913+ const cfg = {
1914+ session : { store : storePath } ,
1915+ channels : { slack : { enabled : true , replyToMode : "all" , groupPolicy : "open" } } ,
1916+ } as OpenClawConfig ;
1917+ const route = resolveAgentRoute ( {
1918+ cfg,
1919+ channel : "slack" ,
1920+ accountId : "default" ,
1921+ teamId : "T1" ,
1922+ peer : { kind : "channel" , id : "C123" } ,
1923+ } ) ;
1924+ const threadKeys = resolveThreadSessionKeys ( {
1925+ baseSessionKey : route . sessionKey ,
1926+ threadId : "250.000" ,
1927+ } ) ;
1928+ const now = Date . now ( ) ;
1929+ await saveSessionStore (
1930+ storePath ,
1931+ {
1932+ [ threadKeys . sessionKey ] : {
1933+ sessionId : "direct-monitor-existing-thread-session" ,
1934+ updatedAt : now - 2 * 24 * 60 * 60 * 1000 ,
1935+ sessionStartedAt : now - 2 * 24 * 60 * 60 * 1000 ,
1936+ lastInteractionAt : now - 2 * 24 * 60 * 60 * 1000 ,
1937+ } ,
1938+ } ,
1939+ { skipMaintenance : true } ,
1940+ ) ;
1941+
1942+ const replies = vi . fn ( ) . mockResolvedValueOnce ( {
1943+ messages : [ { text : "starter" , user : "U2" , ts : "250.000" } ] ,
1944+ } ) ;
1945+ const slackCtx = createThreadSlackCtx ( { cfg, replies } ) ;
1946+ slackCtx . channelRuntime = undefined ;
1947+ slackCtx . resolveUserName = async ( ) => ( { name : "Alice" } ) ;
1948+ slackCtx . resolveChannelName = async ( ) => ( { name : "general" , type : "channel" } ) ;
1949+
1950+ const prepared = await prepareThreadMessage ( slackCtx , {
1951+ text : "direct monitor reply in old thread" ,
1952+ ts : "251.000" ,
1953+ thread_ts : "250.000" ,
1954+ } ) ;
1955+
1956+ assertPrepared ( prepared ) ;
1957+ expect ( prepared . ctxPayload . IsFirstThreadTurn ) . toBeUndefined ( ) ;
1958+ expect ( prepared . ctxPayload . ThreadHistoryBody ) . toBeUndefined ( ) ;
1959+ expect ( prepared . ctxPayload . ThreadStarterBody ) . toBeUndefined ( ) ;
1960+ expect ( replies ) . toHaveBeenCalledTimes ( 1 ) ;
1961+ } ) ;
1962+
19111963 it ( "loads bounded thread history for existing thread sessions stale under reset policy" , async ( ) => {
19121964 const { storePath } = storeFixture . makeTmpStorePath ( ) ;
19131965 const now = Date . now ( ) ;
0 commit comments