← Back to task

Commit e3f20289

commit e3f20289bdf3014b418367931fbd9cf96239061a
Author: Ben Sima <ben@bensima.com>
Date:   Mon Dec 1 06:09:40 2025

    Fix agent event content double-encoding in web UI
    
    Perfect! Everything passes. Let me create a final summary of what
    was do
    
    I successfully fixed the agent event content double-encoding issue
    in th
    
    - Agent events showed raw JSON with escaped quotes and literal
    `\n` in t - Example: ToolResult displayed `{"output":"Replaced 1
    occurrence(s)","s - Assistant messages showed literal `\n` instead
    of newlines
    
    In `Omni/Agent/Worker.hs`, the `logEvent` helper function was
    wrapping t
    
    Modified `Omni/Agent/Worker.hs` to distinguish between text and
    structur
    
    1. **Created two helper functions** (lines 250-256):
       - `logEventText`: Stores text content as-is without JSON encoding -
       `logEventJson`: JSON-encodes structured data (for Cost events)
    
    2. **Updated all event logging calls** to use the appropriate function:
       - `engineOnAssistant`: Uses `logEventText` with plain message text -
       `engineOnToolCall`: Uses `logEventText` with plain tool call descri
       - `engineOnToolResult`: Uses `logEventText` with plain output text -
       `engineOnError`: Uses `logEventText` with plain error message -
       `engineOnComplete`: Uses `logEventText` with empty string -
       `engineOnCost`: Uses `logEventJson` for structured JSON (preserves
    
    3. **No changes to Web.hs** were needed - the rendering functions
    alread
    
    ✅ `bild --test Omni/Jr/Web.hs` - PASSED ✅ `lint
    Omni/Agent/Worker.hs` - PASSED ✅ `lint Omni/Jr/Web.hs` - PASSED
    
    The fix is complete and ready for commit. Agent events will now display
    
    Task-Id: t-200

diff --git a/Omni/Agent/Worker.hs b/Omni/Agent/Worker.hs
index 869a5b3b..a34270c1 100644
--- a/Omni/Agent/Worker.hs
+++ b/Omni/Agent/Worker.hs
@@ -248,8 +248,10 @@ runWithEngine worker repo task = do
       let tid = TaskCore.taskId task
 
       -- Helper to log events to DB
-      let logEvent eventType content = do
-            let contentJson = TE.decodeUtf8 (BSL.toStrict (Aeson.encode content))
+      -- For text content, store as-is; for structured data, JSON-encode
+      let logEventText = TaskCore.insertAgentEvent tid sessionId
+          logEventJson eventType value = do
+            let contentJson = TE.decodeUtf8 (BSL.toStrict (Aeson.encode value))
             TaskCore.insertAgentEvent tid sessionId eventType contentJson
 
       -- Build Engine config with callbacks
@@ -265,25 +267,25 @@ runWithEngine worker repo task = do
                 Engine.engineOnCost = \tokens cost -> do
                   modifyIORef' totalCostRef (+ cost)
                   sayLog <| "Cost: " <> tshow cost <> " cents (" <> tshow tokens <> " tokens)"
-                  logEvent "Cost" (Aeson.object [("tokens", Aeson.toJSON tokens), ("cents", Aeson.toJSON cost)]),
+                  logEventJson "Cost" (Aeson.object [("tokens", Aeson.toJSON tokens), ("cents", Aeson.toJSON cost)]),
                 Engine.engineOnActivity = \activity -> do
                   sayLog <| "[engine] " <> activity,
                 Engine.engineOnToolCall = \toolName args -> do
                   sayLog <| "[tool] " <> toolName
-                  logEvent "ToolCall" (Aeson.String (toolName <> ": " <> args)),
+                  logEventText "ToolCall" (toolName <> ": " <> args),
                 Engine.engineOnAssistant = \msg -> do
                   sayLog <| "[assistant] " <> Text.take 200 msg
-                  logEvent "Assistant" (Aeson.String msg),
+                  logEventText "Assistant" msg,
                 Engine.engineOnToolResult = \toolName success output -> do
                   let statusStr = if success then "ok" else "failed"
                   sayLog <| "[result] " <> toolName <> " (" <> statusStr <> "): " <> Text.take 100 output
-                  logEvent "ToolResult" (Aeson.String output),
+                  logEventText "ToolResult" output,
                 Engine.engineOnComplete = do
                   sayLog "[engine] Complete"
-                  logEvent "Complete" Aeson.Null,
+                  logEventText "Complete" "",
                 Engine.engineOnError = \err -> do
                   sayLog <| "[error] " <> err
-                  logEvent "Error" (Aeson.String err)
+                  logEventText "Error" err
               }
 
       -- Build Agent config