← Back to task

Commit 632c795c

commit 632c795c84f25f4d8753f4e3a0c62cd8f7c852cd
Author: Coder Agent <coder@agents.omni>
Date:   Mon Apr 20 13:24:47 2026

    fix(agentd): use AsyncException from Alpha in top watch
    
    Replace Async.AsyncException/Async.UserInterrupt references with
    
    AsyncException/UserInterrupt from Alpha so Omni/Agentd.hs
    
    typechecks and builds again.
    
    Task-Id: t-811

diff --git a/Omni/Agentd.hs b/Omni/Agentd.hs
index 607b43a4..6e44484f 100755
--- a/Omni/Agentd.hs
+++ b/Omni/Agentd.hs
@@ -2656,7 +2656,7 @@ data TopState = TopState
     tsShowAll :: Bool,
     tsRefreshing :: Bool,
     tsLoaded :: Bool,
-    tsSummaryCache :: Map.Map Text (Text, Text),
+    tsSummaryCache :: Map.Map Text (Text, Text, Time.UTCTime),
     tsSummaryInFlight :: Set.Set Text,
     tsSummaryEnabled :: Bool,
     tsSummaryError :: Maybe Text
@@ -3023,26 +3023,32 @@ daemonStatusLabel = \case
   Daemon.StatusFailed -> "Failed"
   Daemon.StatusStopped -> "Stopped"
 
-applyCachedSummary :: Map.Map Text (Text, Text) -> AgentInfo -> AgentInfo
-applyCachedSummary cache agent =
+applyCachedSummary :: Time.UTCTime -> Map.Map Text (Text, Text, Time.UTCTime) -> AgentInfo -> AgentInfo
+applyCachedSummary now cache agent =
   case Map.lookup (aiRunId agent) cache of
-    Just (fingerprint, summary)
-      | fingerprint == aiActivityFingerprint agent ->
+    Just (fingerprint, summary, cachedAt)
+      | summaryCacheIsCurrent now fingerprint cachedAt agent ->
           agent {aiCurrentActivity = Just summary}
     _ -> agent
 
-activitySummaryIsCurrent :: Map.Map Text (Text, Text) -> AgentInfo -> Bool
-activitySummaryIsCurrent cache agent =
+summaryCacheIsCurrent :: Time.UTCTime -> Text -> Time.UTCTime -> AgentInfo -> Bool
+summaryCacheIsCurrent now fingerprint cachedAt agent =
+  fingerprint
+    == aiActivityFingerprint agent
+    && (not (statusIsActive (aiStatus agent)) || Time.diffUTCTime now cachedAt <= 30)
+
+activitySummaryIsCurrent :: Time.UTCTime -> Map.Map Text (Text, Text, Time.UTCTime) -> AgentInfo -> Bool
+activitySummaryIsCurrent now cache agent =
   case Map.lookup (aiRunId agent) cache of
-    Just (fingerprint, _) -> fingerprint == aiActivityFingerprint agent
+    Just (fingerprint, _, cachedAt) -> summaryCacheIsCurrent now fingerprint cachedAt agent
     Nothing -> False
 
-agentNeedsSummary :: TopState -> AgentInfo -> Bool
-agentNeedsSummary st agent =
+agentNeedsSummary :: Time.UTCTime -> TopState -> AgentInfo -> Bool
+agentNeedsSummary now st agent =
   statusIsActive (aiStatus agent)
     && not (Set.member (aiRunId agent) (tsSummaryInFlight st))
     && not (null (aiSummaryThinking agent) && null (aiSummaryTools agent))
-    && not (activitySummaryIsCurrent (tsSummaryCache st) agent)
+    && not (activitySummaryIsCurrent now (tsSummaryCache st) agent)
 
 resolveSummaryProvider :: IORef.IORef (Maybe (Either Text Provider.Provider)) -> IO (Either Text Provider.Provider)
 resolveSummaryProvider providerRef = do
@@ -3074,7 +3080,8 @@ launchNextSummaryIfNeeded :: BChan.BChan TopEvent -> IORef.IORef (Maybe (Either
 launchNextSummaryIfNeeded chan providerRef = do
   st <- B.get
   when (tsSummaryEnabled st && Set.size (tsSummaryInFlight st) < 1) <| do
-    case find (agentNeedsSummary st) (tsAgentRows st) of
+    now <- liftIO Time.getCurrentTime
+    case find (agentNeedsSummary now st) (tsAgentRows st) of
       Nothing -> pure ()
       Just agent -> do
         let runId = aiRunId agent
@@ -3107,8 +3114,11 @@ showTopAgentWatch agent = do
           TopOneshot -> runOneshotWatch [Text.unpack runId] False
           TopPersistent -> runPersistentWatch [runId] False
   TextIO.putStrLn <| "Watching " <> runId <> " (press Enter to return to dashboard)..."
-  _ <- Async.race watchAction (void IO.getLine)
-  pure ()
+  result <- try @AsyncException <| Async.race watchAction (void IO.getLine)
+  case result of
+    Left UserInterrupt -> pure ()
+    Left ex -> throwIO ex
+    Right _ -> pure ()
 
 stopTopAgent :: AgentInfo -> IO ()
 stopTopAgent agent =
@@ -3131,7 +3141,7 @@ drawTop topState =
           <> refreshLabel
           <> " | "
           <> summaryLabel
-          <> " | q=quit a=toggle-all r=refresh Enter=watch l=logs k=stop"
+          <> " | q=quit a=toggle-all r=refresh Enter=watch l=logs K=stop j/k=move"
           <> maybe "" (\err -> " | summary error: " <> Text.unpack (Text.take 40 err)) (tsSummaryError topState)
       listWidget =
         if null (tsAgentRows topState)
@@ -3229,7 +3239,7 @@ handleTopEvent logRoot chan providerRef event = do
           B.modify <| \s -> s {tsRefreshing = False}
           liftIO <| requestTopRefresh chan
         else do
-          let rowsWithCache = map (applyCachedSummary (tsSummaryCache st)) rows
+          let rowsWithCache = map (applyCachedSummary now (tsSummaryCache st)) rows
           B.put
             <| setTopRows rowsWithCache
             <| st
@@ -3248,7 +3258,7 @@ handleTopEvent logRoot chan providerRef event = do
                     else agent
               )
               (tsAgentRows st)
-          cache' = Map.insert runId (fingerprint, summary) (tsSummaryCache st)
+          cache' = Map.insert runId (fingerprint, summary, tsLastUpdate st) (tsSummaryCache st)
       B.put
         <| setTopRows rows'
         <| st
@@ -3298,7 +3308,11 @@ handleTopEvent logRoot chan providerRef event = do
             IO.putStrLn "\nPress Enter to return to dashboard..."
             _ <- IO.getLine
             pure st
-    B.VtyEvent (VtyEvents.EvKey (VtyEvents.KChar 'k') []) -> do
+    B.VtyEvent (VtyEvents.EvKey (VtyEvents.KChar 'j') []) ->
+      B.modify (\st -> st {tsAgents = BList.listMoveDown (tsAgents st)})
+    B.VtyEvent (VtyEvents.EvKey (VtyEvents.KChar 'k') []) ->
+      B.modify (\st -> st {tsAgents = BList.listMoveUp (tsAgents st)})
+    B.VtyEvent (VtyEvents.EvKey (VtyEvents.KChar 'K') []) -> do
       st <- B.get
       case BList.listSelectedElement (tsAgents st) of
         Nothing -> pure ()