Create Omni/Agent/Trace.hs with structured trace and event types.
Traces are first-class data in the new architecture. Every operation emits events, and traces can be queried, analyzed, and used for checkpointing/replay.
This replaces the callback-based observability in Engine.hs with inspectable data.
Read Omni/Agent/ARCHITECTURE.md for full design rationale.
Create Omni/Agent/Trace.hs containing:
data Event
= EventInferStart
{ evModel :: Text
, evPrompt :: Text -- or structured Prompt type
, evTimestamp :: UTCTime
, evIteration :: Int
}
| EventInferEnd
{ evResponsePreview :: Text -- first N chars
, evTokens :: Int
, evCostCents :: Double
, evDurationMs :: Int
, evTimestamp :: UTCTime
, evIteration :: Int
}
| EventToolCall
{ evToolName :: Text
, evToolArgs :: Value
, evTimestamp :: UTCTime
, evIteration :: Int
}
| EventToolResult
{ evToolName :: Text
, evToolSuccess :: Bool
, evToolOutput :: Text
, evDurationMs :: Int
, evTimestamp :: UTCTime
, evIteration :: Int
}
| EventStateRead
{ evKey :: Text
, evTimestamp :: UTCTime
}
| EventStateWrite
{ evKey :: Text
, evTimestamp :: UTCTime
}
| EventFork
{ evBranchCount :: Int
, evBranchIds :: [TraceId]
, evTimestamp :: UTCTime
}
| EventJoin
{ evBranchIds :: [TraceId]
, evTimestamp :: UTCTime
}
| EventCheckpoint
{ evCheckpointName :: Text
, evTimestamp :: UTCTime
}
| EventLimit
{ evBudget :: Budget
, evTimestamp :: UTCTime
}
| EventExhausted
{ evResource :: Text -- "cost", "tokens", "time"
, evTimestamp :: UTCTime
}
| EventSteering
{ evSteeringMessage :: Text
, evTimestamp :: UTCTime
}
| EventCustom
{ evCustomType :: Text
, evCustomData :: Value
, evTimestamp :: UTCTime
}
deriving (Show, Eq, Generic)
instance ToJSON Event
instance FromJSON Event
newtype TraceId = TraceId Text
deriving (Show, Eq, Ord, ToJSON, FromJSON)
newTraceId :: IO TraceId -- generate UUID-based ID
data Trace = Trace
{ traceId :: TraceId
, traceParentId :: Maybe TraceId
, traceStartTime :: UTCTime
, traceEndTime :: Maybe UTCTime
, traceEvents :: [Event]
}
deriving (Show, Eq, Generic)
instance ToJSON Trace
instance FromJSON Trace
-- Smart constructor
newTrace :: IO Trace
newTrace = do
tid <- newTraceId
now <- getCurrentTime
pure Trace
{ traceId = tid
, traceParentId = Nothing
, traceStartTime = now
, traceEndTime = Nothing
, traceEvents = []
}
-- Append an event
appendEvent :: Event -> Trace -> Trace
-- Merge traces (for parallel branches)
mergeTraces :: [Trace] -> Trace
-- Query functions
totalCost :: Trace -> Double
totalTokens :: Trace -> Int
totalDuration :: Trace -> Maybe NominalDiffTime
toolCalls :: Trace -> [(Text, Value, Value, Int)] -- name, args, result, duration
inferCalls :: Trace -> Int
checkpoints :: Trace -> [Text]
errors :: Trace -> [Text]
-- Filtering
eventsOfType :: Text -> Trace -> [Event]
eventsBetween :: UTCTime -> UTCTime -> Trace -> [Event]
data Checkpoint = Checkpoint
{ cpName :: Text
, cpTraceId :: TraceId
, cpTrace :: Trace -- events up to checkpoint
, cpStateSnapshot :: ByteString -- serialized state
, cpTimestamp :: UTCTime
}
deriving (Show, Eq, Generic)
instance ToJSON Checkpoint
instance FromJSON Checkpoint
-- Write trace to JSONL file (one event per line)
writeTraceJsonl :: FilePath -> Trace -> IO ()
-- Read trace from JSONL file
readTraceJsonl :: FilePath -> IO (Either Text Trace)
-- Append single event to JSONL file (for streaming writes)
appendEventJsonl :: FilePath -> TraceId -> Event -> IO ()