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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Omni.Agent.Log
( LogEntry (..),
Status (..),
initialStatus,
updateStatus,
renderStatus,
parseLine,
format,
)
where
import Alpha
import Data.Aeson (FromJSON (..), (.:), (.:?))
import qualified Data.Aeson as Aeson
import qualified Data.ByteString.Lazy as BSL
import qualified Data.Set as Set
data LogEntry = LogEntry
{ leMessage :: Text,
leLevel :: Maybe Text,
leToolName :: Maybe Text,
leBatches :: Maybe [[Text]],
leMethod :: Maybe Text,
lePath :: Maybe Text,
leTimestamp :: Maybe Text
}
deriving (Show, Eq, Generic)
instance FromJSON LogEntry where
parseJSON =
Aeson.withObject "LogEntry" <| \v ->
( LogEntry
</ (v .: "message")
)
<*> v
.:? "level"
<*> v
.:? "toolName"
<*> v
.:? "batches"
<*> v
.:? "method"
<*> v
.:? "path"
<*> v
.:? "timestamp"
data Status = Status
{ sWorkerName :: Text,
sTaskId :: Maybe Text,
sFiles :: Set.Set Text,
sStartTime :: Maybe Text,
sLastActivity :: Text
}
deriving (Show, Eq, Generic)
initialStatus :: Text -> Status
initialStatus name =
Status
{ sWorkerName = name,
sTaskId = Nothing,
sFiles = Set.empty,
sStartTime = Nothing,
sLastActivity = "Idle"
}
updateStatus :: LogEntry -> Status -> Status
updateStatus e s =
let s' = case format e of
Just msg -> s {sLastActivity = msg}
Nothing -> s
s'' = case leTimestamp e of
Just t -> if isNothing (sStartTime s) then s' {sStartTime = Just t} else s'
Nothing -> s'
in case (leMessage e, lePath e) of
("ide-fs", Just p) -> s'' {sFiles = Set.insert p (sFiles s'')}
_ -> s''
renderStatus :: Status -> Text
renderStatus s =
let line1 =
"[Worker: "
<> sWorkerName s
<> "] "
<> "Task: "
<> fromMaybe "None" (sTaskId s)
<> " | Files: "
<> show (Set.size (sFiles s))
line2 = sLastActivity s
in line1 <> "\n" <> line2
parseLine :: Text -> Maybe LogEntry
parseLine line = Aeson.decode <| BSL.fromStrict <| encodeUtf8 line
format :: LogEntry -> Maybe Text
format e =
case leMessage e of
"executing 1 tools in 1 batch(es)" ->
let tool = case leBatches e of
Just ((t : _) : _) -> t
_ -> "unknown"
in Just <| "🤖 THOUGHT: Planning tool execution (" <> tool <> ")"
"Tool Bash permitted - action: allow" ->
Just "🔧 TOOL: Bash command executed"
msg
| "Processing tool completion for ledger" == msg && isJust (leToolName e) ->
Just <| "✅ TOOL: " <> fromMaybe "" (leToolName e) <> " completed"
"ide-fs" ->
case leMethod e of
Just "readFile" -> Just <| "📂 READ: " <> fromMaybe "" (lePath e)
_ -> Nothing
"System prompt build complete (no changes)" ->
Just "🧠 THINKING..."
"System prompt build complete (first build)" ->
Just "🚀 STARTING new task context"
msg ->
case leLevel e of
Just "error" -> Just <| "❌ ERROR: " <> msg
_ -> Nothing
|