Create a wrapper that exposes current Engine.runAgentWithProvider as an Op.
We want to migrate incrementally. This wrapper lets us use the new Op interface while still using the battle-tested Engine implementation internally.
Create Omni/Agent/Op/Legacy.hs containing:
-- | Run the legacy engine as an Op
-- This wraps Engine.runAgentWithProvider so existing behavior is preserved
-- while we gradually migrate to native Op implementation
legacyAgent :: AgentConfig -> Text -> Op AgentState AgentResult
legacyAgent config prompt = do
-- Emit start event
emit (EventCustom "legacy_agent_start" (toJSON config))
-- Create a provider from config (or take as parameter)
-- Run the existing engine
result <- liftIO (Engine.runAgentWithProvider engineConfig provider config prompt)
case result of
Left err -> do
emit (EventCustom "legacy_agent_error" (toJSON err))
error err -- or return Either
Right agentResult -> do
emit (EventCustom "legacy_agent_complete" (toJSON agentResult))
pure agentResult
-- | Standard agent state that matches Engine's implicit state
data AgentState = AgentState
{ asHistory :: [Message]
, asTotalCost :: Double
, asTotalTokens :: Int
, asIteration :: Int
}
deriving (Show, Eq, Generic)
instance ToJSON AgentState
instance FromJSON AgentState
initialAgentState :: AgentState
-- | Convert between Op-style config and Engine-style config
toEngineConfig :: OpConfig -> Engine.EngineConfig
toAgentConfig :: OpConfig -> Engine.AgentConfig
We need a way to run IO in Op:
-- Add to Op.hs if not there:
data OpF s next where
...
LiftIO :: IO a -> (a -> next) -> OpF s next
liftIO :: IO a -> Op s a
liftIO io = liftF (LiftIO io id)
Or use a different approach (ReaderT IO, etc.)