Improve heartbeat system with duplicate suppression and activity check.
Comparing Ava's heartbeat to moltbot revealed two important gaps:
1. Duplicate Suppression - Moltbot tracks last heartbeat message, suppresses repeats within 24h 2. Activity Check - Moltbot skips heartbeat if user is actively chatting
Without these, Ava might:
Track last heartbeat message in settings or dedicated table:
-- In Memory.hs or Heartbeat.hs
getLastHeartbeatMessage :: IO (Maybe (Text, UTCTime))
setLastHeartbeatMessage :: Text -> UTCTime -> IO ()
-- Could use settings table:
-- key: "heartbeat.last_message", value: "You have 3 reminders"
-- key: "heartbeat.last_sent_at", value: "2024-01-29T22:00:00Z"
In runHeartbeatOnce (Heartbeat.hs), before sending:
case result of
HeartbeatContent content -> do
now <- getCurrentTime
lastMsg <- getLastHeartbeatMessage
let isDuplicate = case lastMsg of
Just (prevText, prevTime) ->
content == prevText &&
diffUTCTime now prevTime < 86400 -- 24 hours
Nothing -> False
unless isDuplicate $ do
sendMessage content
setLastHeartbeatMessage content now
_ -> pure ()
Track last user message time, skip heartbeat if too recent:
Option A: Pass last activity time to heartbeat loop
-- In Bot.hs, track last user message
lastActivityVar :: TVar UTCTime
-- Update on each user message
atomically $ writeTVar lastActivityVar now
-- In startHeartbeatLoop, check before running:
startHeartbeatLoop ... lastActivityVar = do
...
lastActivity <- readTVarIO lastActivityVar
now <- getCurrentTime
let idleSeconds = diffUTCTime now lastActivity
when (idleSeconds > 60) $ do -- Only if idle 1+ minute
result <- runHeartbeatOnce ...
Option B: Check if currently processing (simpler)
-- If we have a "processing" flag
isProcessing <- readTVarIO processingVar
unless isProcessing $ runHeartbeatOnce ...
Add to HeartbeatConfig:
data HeartbeatConfig = HeartbeatConfig
{ ...
, heartbeatDuplicateSuppressionHours :: Int -- default 24
, heartbeatMinIdleSeconds :: Int -- default 60
}
1. Omni/Ava/Telegram/Heartbeat.hs
getLastHeartbeatMessage, setLastHeartbeatMessagerunHeartbeatOnce2. Omni/Ava/Telegram/Bot.hs
lastActivityVar :: TVar UTCTimestartHeartbeatLoop or check before running3. Omni/Agent/Memory.hs (if using settings)
getSetting/setSetting1. Unit test: duplicate detection logic 2. Unit test: idle time calculation 3. Manual test: send same reminder situation, verify no repeat within 24h 4. Manual test: chat actively, verify heartbeat doesn't interrupt
See moltbot implementation:
/home/ben/src/moltbot/moltbot/src/infra/heartbeat-runner.ts lines 580-610 (duplicate check)/home/ben/src/moltbot/moltbot/src/infra/heartbeat-runner.ts lines 430-433 (queue check)