summaryrefslogtreecommitdiff
path: root/Omni/Agent/Tools
diff options
context:
space:
mode:
Diffstat (limited to 'Omni/Agent/Tools')
-rw-r--r--Omni/Agent/Tools/AvaLogs.hs109
1 files changed, 109 insertions, 0 deletions
diff --git a/Omni/Agent/Tools/AvaLogs.hs b/Omni/Agent/Tools/AvaLogs.hs
new file mode 100644
index 0000000..582b3a6
--- /dev/null
+++ b/Omni/Agent/Tools/AvaLogs.hs
@@ -0,0 +1,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