agentd top: j/k navigation, ^C watch exit, Activity cache refresh

t-810·WorkTask·
·
·
Created17 hours ago·Updated17 hours ago·pipeline runs →

Description

Edit

Three UX fixes for the agentd top TUI dashboard (Omni/Agentd.hs):

1. j/k keys for navigation

Currently only KUp/KDown arrow keys move the cursor (lines ~3270-3273). Add j/k as aliases:

B.VtyEvent (VtyEvents.EvKey (VtyEvents.KChar 'j') []) ->
  B.modify <| \s -> s {tsAgents = BList.listMoveDown (tsAgents s)}
B.VtyEvent (VtyEvents.EvKey (VtyEvents.KChar 'k') []) -> ...

BUT: 'k' is already bound to 'kill agent'. Need to resolve conflict. Options:

  • Remap kill to uppercase 'K' or Shift+k
  • Or use j/down for down and just leave k for kill (no up shortcut)

Preferred: move kill to 'K' (uppercase), free up 'k' for up navigation.

2. ^C from watch returns to dashboard, not kills process

showTopAgentWatch (line ~3102) uses Async.race watchAction (void IO.getLine) — pressing Enter exits, ^C kills the whole process.

Fix: catch SIGINT/UserInterrupt in the watch action so ^C exits the watch but returns control to brick TUI. Something like:

showTopAgentWatch :: AgentInfo -> IO ()
showTopAgentWatch agent = do
  let runId = aiRunId agent
      watchAction = case aiKind agent of
        TopOneshot -> runOneshotWatch [Text.unpack runId] False
        TopPersistent -> runPersistentWatch [runId] False
  TextIO.putStrLn <| "Watching " <> runId <> " (^C or Enter to return to dashboard)..."
  result <- try @AsyncException (Async.race watchAction (void IO.getLine))
  case result of
    Left UserInterrupt -> pure ()  -- ^C caught, return to dashboard
    Left e -> throwIO e
    Right _ -> pure ()

The key is suspendAndResume in brick already handles screen state — we just need to not propagate the interrupt up.

3. Activity summary cache not updating

The summary system uses a fingerprint (aiActivityFingerprint) to decide if a cached summary is stale. The fingerprint is built from status + last 8 thinking labels + last 8 tool labels (buildActivityFingerprint).

Problem: the cache is ONLY invalidated when the fingerprint changes. If the agent is doing repetitive work (same tool labels repeatedly), the fingerprint never changes so summary never refreshes. Even if time passes.

Fix options:

  • Add a timestamp component to the cache: store (fingerprint, summary, cachedAt) — invalidate if > 30 seconds old AND agent is active
  • Or add a TTL field to TopState: force re-summary every 30s regardless of fingerprint for active agents
  • Simpler: add a tsSummaryLastRefresh :: Map.Map Text UTCTime and skip re-summarization if refreshed within 30s, but ALWAYS re-summarize after 30s even if fingerprint unchanged

Recommend: add timestamp to cache tuple and check age. In activitySummaryIsCurrent, return False if cached > 30s ago and agent is active.

Files to change

  • Omni/Agentd.hs (live branch) — all three issues are in this file
  • Key line ranges: ~3270 (event handler), ~3102 (showTopAgentWatch), ~3035-3045 (summary cache logic), ~3659 (tsSummaryCache type)

Acceptance criteria

  • j moves cursor down, k moves cursor up in agent list
  • kill is rebound to uppercase K (or documented alternative)
  • ^C from watch view returns to the top dashboard (not kills process)
  • Activity summary refreshes at least every 30s for active agents, even if fingerprint is unchanged
  • All existing keyboard shortcuts still work (q, ESC, a, r, l, Enter)

Timeline (0)

No activity yet.