summaryrefslogtreecommitdiff
path: root/Omni/Agent/Tools/AvaLogs.hs
blob: 582b3a6cd7735d78dacd1a8e13396c312336ef39 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoImplicitPrelude #-}

-- | Tool for Ava to read her own audit logs and subagent traces.
--
-- Enables self-diagnosis: "Let me check my logs for that subagent run..."
--
-- : out omni-agent-tools-avalogs
-- : dep aeson
-- : dep time
module Omni.Agent.Tools.AvaLogs
  ( readAvaLogsTool,
    main,
  )
where

import Alpha
import Data.Aeson ((.=))
import qualified Data.Aeson as Aeson
import qualified Data.Aeson.Key as Key
import qualified Data.Aeson.KeyMap as KeyMap
import qualified Data.Text as Text
import qualified Data.Time as Time
import qualified Omni.Agent.AuditLog as AuditLog
import qualified Omni.Agent.Engine as Engine

main :: IO ()
main = putText "Omni.Agent.Tools.AvaLogs - no standalone execution"

readAvaLogsTool :: Engine.Tool
readAvaLogsTool =
  Engine.Tool
    { Engine.toolName = "read_ava_logs",
      Engine.toolDescription =
        "Read Ava's audit logs or subagent traces for self-diagnosis. "
          <> "Use to review past conversations, inspect subagent runs, or debug issues. "
          <> "Pass subagent_id to view a specific subagent's trace, or last_n for recent Ava logs.",
      Engine.toolJsonSchema =
        Aeson.object
          [ "type" .= ("object" :: Text),
            "properties"
              .= Aeson.object
                [ "subagent_id"
                    .= Aeson.object
                      [ "type" .= ("string" :: Text),
                        "description" .= ("Subagent ID to view (e.g. 'abc123')" :: Text)
                      ],
                  "last_n"
                    .= Aeson.object
                      [ "type" .= ("integer" :: Text),
                        "description" .= ("Number of recent log entries to return (default: 20)" :: Text)
                      ]
                ]
          ],
      Engine.toolExecute = executeReadLogs
    }

executeReadLogs :: Aeson.Value -> IO Aeson.Value
executeReadLogs v = do
  let maybeSubagentId = case v of
        Aeson.Object obj -> case KeyMap.lookup "subagent_id" obj of
          Just (Aeson.String sid) -> Just sid
          _ -> Nothing
        _ -> Nothing

  let lastN = case v of
        Aeson.Object obj -> case KeyMap.lookup "last_n" obj of
          Just (Aeson.Number n) -> round n
          _ -> 20
        _ -> 20

  case maybeSubagentId of
    Just sid -> do
      let subagentId = AuditLog.SubagentId sid
      entries <- AuditLog.readSubagentLogs subagentId
      pure
        <| Aeson.object
          [ "subagent_id" .= sid,
            "entry_count" .= length entries,
            "entries" .= map formatEntry entries
          ]
    Nothing -> do
      entries <- AuditLog.getRecentAvaLogs lastN
      today <- Time.utctDay </ Time.getCurrentTime
      pure
        <| Aeson.object
          [ "date" .= Time.formatTime Time.defaultTimeLocale "%Y-%m-%d" today,
            "entry_count" .= length entries,
            "entries" .= map formatEntry entries
          ]

formatEntry :: AuditLog.AuditLogEntry -> Aeson.Value
formatEntry entry =
  Aeson.object
    [ "timestamp" .= Time.formatTime Time.defaultTimeLocale "%H:%M:%S" (AuditLog.logTimestamp entry),
      "event_type" .= tshow (AuditLog.logEventType entry),
      "agent_id" .= AuditLog.unAgentId (AuditLog.logAgentId entry),
      "content" .= summarizeContent (AuditLog.logContent entry)
    ]

summarizeContent :: Aeson.Value -> Text
summarizeContent (Aeson.String t) = Text.take 200 t
summarizeContent (Aeson.Object obj) =
  let keys = KeyMap.keys obj
   in "object with keys: " <> Text.intercalate ", " (map Key.toText keys)
summarizeContent (Aeson.Array arr) = "array with " <> tshow (length arr) <> " items"
summarizeContent Aeson.Null = "null"
summarizeContent (Aeson.Bool b) = if b then "true" else "false"
summarizeContent (Aeson.Number n) = tshow n