From b2708836ef61bd7156fe4f3418444a54c00495cf Mon Sep 17 00:00:00 2001 From: Omni Worker Date: Sat, 22 Nov 2025 04:37:46 -0500 Subject: fix(agent): update updateTaskStatus calls Amp-Thread-ID: https://ampcode.com/threads/T-79499d9e-f4f4-40de-893c-524c32a45483 Co-authored-by: Amp --- Omni/Agent/Worker.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Omni/Agent/Worker.hs') diff --git a/Omni/Agent/Worker.hs b/Omni/Agent/Worker.hs index 01099a0..1961ea7 100644 --- a/Omni/Agent/Worker.hs +++ b/Omni/Agent/Worker.hs @@ -58,7 +58,7 @@ processTask worker task = do AgentLog.updateActivity ("Claiming task " <> tid) -- Claim task - TaskCore.updateTaskStatus tid TaskCore.InProgress + TaskCore.updateTaskStatus tid TaskCore.InProgress [] -- Commit claim locally Git.commit repo ("task: claim " <> tid) @@ -94,7 +94,7 @@ processTask worker task = do AgentLog.log "Agent finished successfully" -- Update status to Review (bundled with feature commit) - TaskCore.updateTaskStatus tid TaskCore.Review + TaskCore.updateTaskStatus tid TaskCore.Review [] -- Commit changes -- We should check if there are changes, but 'git add .' is safe. @@ -111,7 +111,7 @@ processTask worker task = do Git.syncWithLive repo -- Update status to Review (for signaling) - TaskCore.updateTaskStatus tid TaskCore.Review + TaskCore.updateTaskStatus tid TaskCore.Review [] Git.commit repo ("task: review " <> tid) AgentLog.log ("[✓] Task " <> tid <> " completed") -- cgit v1.2.3 From c6dc12122fffad11cd40851589e13a25ad0d9291 Mon Sep 17 00:00:00 2001 From: Omni Worker Date: Sat, 22 Nov 2025 04:45:37 -0500 Subject: fix(agent): fix updateTaskStatus signature Amp-Thread-ID: https://ampcode.com/threads/T-79499d9e-f4f4-40de-893c-524c32a45483 Co-authored-by: Amp --- Omni/Agent/Worker.hs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'Omni/Agent/Worker.hs') diff --git a/Omni/Agent/Worker.hs b/Omni/Agent/Worker.hs index 1961ea7..85fd81a 100644 --- a/Omni/Agent/Worker.hs +++ b/Omni/Agent/Worker.hs @@ -113,10 +113,9 @@ processTask worker task = do -- Update status to Review (for signaling) TaskCore.updateTaskStatus tid TaskCore.Review [] Git.commit repo ("task: review " <> tid) - + AgentLog.log ("[✓] Task " <> tid <> " completed") AgentLog.update (\s -> s {AgentLog.statusTask = Nothing}) - Exit.ExitFailure code -> do AgentLog.log ("Agent failed with code " <> tshow code) AgentLog.updateActivity "Agent failed, retrying..." -- cgit v1.2.3 From 9bfabda73f3a65b0353670733a269963d335cf7e Mon Sep 17 00:00:00 2001 From: Omni Worker Date: Sat, 22 Nov 2025 05:05:34 -0500 Subject: feat: stream amp logs to worker status bar --- Omni/Agent/Worker.hs | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) (limited to 'Omni/Agent/Worker.hs') diff --git a/Omni/Agent/Worker.hs b/Omni/Agent/Worker.hs index 85fd81a..86c9e8b 100644 --- a/Omni/Agent/Worker.hs +++ b/Omni/Agent/Worker.hs @@ -5,7 +5,12 @@ module Omni.Agent.Worker where import Alpha +import qualified Data.Aeson as Aeson +import qualified Data.Aeson.KeyMap as KM +import qualified Data.ByteString.Lazy as BL +import qualified Data.Scientific as Scientific import qualified Data.Text as Text +import qualified Data.Time as Time import qualified Omni.Agent.Core as Core import qualified Omni.Agent.Git as Git import qualified Omni.Agent.Log as AgentLog @@ -13,6 +18,7 @@ import qualified Omni.Task.Core as TaskCore import qualified System.Directory as Directory import qualified System.Exit as Exit import System.FilePath (()) +import qualified System.IO as IO import qualified System.Process as Process start :: Core.Worker -> IO () @@ -143,13 +149,28 @@ runAmp repo task = do <> "'.\n" Directory.createDirectoryIfMissing True (repo "_/llm") + let logFile = repo "_/llm/amp.log" + + -- Clean up previous log + exists <- Directory.doesFileExist logFile + when exists (Directory.removeFile logFile) + + -- Start background monitors + tidTime <- forkIO timeTicker + tidLog <- forkIO (monitorLog logFile) -- Assume amp is in PATH let args = ["--log-level", "debug", "--log-file", "_/llm/amp.log", "--dangerously-allow-all", "-x", Text.unpack prompt] let cp = (Process.proc "amp" args) {Process.cwd = Just repo} (_, _, _, ph) <- Process.createProcess cp - Process.waitForProcess ph + exitCode <- Process.waitForProcess ph + + -- Cleanup + killThread tidTime + killThread tidLog + + pure exitCode formatTask :: TaskCore.Task -> Text formatTask t = @@ -201,3 +222,60 @@ findBaseBranch repo task = do case candidates of (candidate : _) -> pure ("task/" <> TaskCore.depId candidate) [] -> pure "live" + +monitorLog :: FilePath -> IO () +monitorLog logPath = do + waitForFile logPath + IO.withFile logPath IO.ReadMode <| \h -> do + -- Tail the file + IO.hSeek h IO.SeekFromEnd 0 + forever <| do + eof <- IO.hIsEOF h + if eof + then threadDelay 100000 -- 0.1s + else do + line <- IO.hGetLine h + parseAndUpdate (Text.pack line) + +waitForFile :: FilePath -> IO () +waitForFile path = do + exists <- Directory.doesFileExist path + if exists + then pure () + else do + threadDelay 100000 + waitForFile path + +parseAndUpdate :: Text -> IO () +parseAndUpdate line = do + let maybeObj = Aeson.decode (BL.fromStrict (encodeUtf8 line)) :: Maybe Aeson.Object + case maybeObj of + Nothing -> pure () + Just obj -> do + -- Extract msg + case KM.lookup "msg" obj of + Just (Aeson.String m) -> unless (Text.null m) (AgentLog.updateActivity m) + _ -> pure () + + -- Extract threadId + case KM.lookup "threadId" obj of + Just (Aeson.String tid) -> AgentLog.update (\s -> s {AgentLog.statusThreadId = Just tid}) + _ -> pure () + + -- Extract cost + case KM.lookup "usage" obj of + Just (Aeson.Object usage) -> + case KM.lookup "cost" usage of + Just (Aeson.Number n) -> + let cost = Scientific.toRealFloat n + in AgentLog.update (\s -> s {AgentLog.statusCredits = AgentLog.statusCredits s + cost}) + _ -> pure () + _ -> pure () + +timeTicker :: IO () +timeTicker = + forever <| do + time <- Time.getCurrentTime + let timeStr = Time.formatTime Time.defaultTimeLocale "%H:%M" time + AgentLog.update (\s -> s {AgentLog.statusTime = Text.pack timeStr}) + threadDelay 1000000 -- 1s -- cgit v1.2.3 From cf32eb4ea6e2f8594fea7895fee38c9c2a838042 Mon Sep 17 00:00:00 2001 From: Omni Worker Date: Sat, 22 Nov 2025 05:14:49 -0500 Subject: fix: parse correct json fields from amp logs Amp-Thread-ID: https://ampcode.com/threads/T-5a2ca80d-5cba-409f-a262-6b5c652c257d Co-authored-by: Amp --- Omni/Agent/Worker.hs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'Omni/Agent/Worker.hs') diff --git a/Omni/Agent/Worker.hs b/Omni/Agent/Worker.hs index 86c9e8b..4ff9042 100644 --- a/Omni/Agent/Worker.hs +++ b/Omni/Agent/Worker.hs @@ -252,8 +252,8 @@ parseAndUpdate line = do case maybeObj of Nothing -> pure () Just obj -> do - -- Extract msg - case KM.lookup "msg" obj of + -- Extract message (was msg) + case KM.lookup "message" obj of Just (Aeson.String m) -> unless (Text.null m) (AgentLog.updateActivity m) _ -> pure () @@ -262,14 +262,12 @@ parseAndUpdate line = do Just (Aeson.String tid) -> AgentLog.update (\s -> s {AgentLog.statusThreadId = Just tid}) _ -> pure () - -- Extract cost - case KM.lookup "usage" obj of - Just (Aeson.Object usage) -> - case KM.lookup "cost" usage of - Just (Aeson.Number n) -> - let cost = Scientific.toRealFloat n - in AgentLog.update (\s -> s {AgentLog.statusCredits = AgentLog.statusCredits s + cost}) - _ -> pure () + -- Extract cost from usage-ledger:event + -- Pattern: {"addedCredits": 0.123, "message": "usage-ledger:event", ...} + case KM.lookup "addedCredits" obj of + Just (Aeson.Number n) -> + let cost = Scientific.toRealFloat n + in AgentLog.update (\s -> s {AgentLog.statusCredits = AgentLog.statusCredits s + cost}) _ -> pure () timeTicker :: IO () -- cgit v1.2.3 From e97d41858c67ca7b019493be2b436e32b8069118 Mon Sep 17 00:00:00 2001 From: Omni Worker Date: Sat, 22 Nov 2025 05:25:57 -0500 Subject: fix: read amp log from start and use totalCredits for accuracy Amp-Thread-ID: https://ampcode.com/threads/T-5a2ca80d-5cba-409f-a262-6b5c652c257d Co-authored-by: Amp --- Omni/Agent/Worker.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'Omni/Agent/Worker.hs') diff --git a/Omni/Agent/Worker.hs b/Omni/Agent/Worker.hs index 4ff9042..7afc849 100644 --- a/Omni/Agent/Worker.hs +++ b/Omni/Agent/Worker.hs @@ -227,8 +227,7 @@ monitorLog :: FilePath -> IO () monitorLog logPath = do waitForFile logPath IO.withFile logPath IO.ReadMode <| \h -> do - -- Tail the file - IO.hSeek h IO.SeekFromEnd 0 + -- Start from beginning of file (don't seek to end) forever <| do eof <- IO.hIsEOF h if eof @@ -263,11 +262,12 @@ parseAndUpdate line = do _ -> pure () -- Extract cost from usage-ledger:event - -- Pattern: {"addedCredits": 0.123, "message": "usage-ledger:event", ...} - case KM.lookup "addedCredits" obj of + -- Pattern: {"totalCredits": 1.54, "message": "usage-ledger:event", ...} + -- We use totalCredits to be robust against missed lines and restarts. + case KM.lookup "totalCredits" obj of Just (Aeson.Number n) -> - let cost = Scientific.toRealFloat n - in AgentLog.update (\s -> s {AgentLog.statusCredits = AgentLog.statusCredits s + cost}) + let total = Scientific.toRealFloat n + in AgentLog.update (\s -> s {AgentLog.statusCredits = total}) _ -> pure () timeTicker :: IO () -- cgit v1.2.3 From 99175426feca3fda9117f20884b2294cee1c1bab Mon Sep 17 00:00:00 2001 From: Omni Worker Date: Sat, 22 Nov 2025 05:42:48 -0500 Subject: fix: divide amp credits by 100 to display dollars Amp-Thread-ID: https://ampcode.com/threads/T-5a2ca80d-5cba-409f-a262-6b5c652c257d Co-authored-by: Amp --- Omni/Agent/Worker.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Omni/Agent/Worker.hs') diff --git a/Omni/Agent/Worker.hs b/Omni/Agent/Worker.hs index 7afc849..3504765 100644 --- a/Omni/Agent/Worker.hs +++ b/Omni/Agent/Worker.hs @@ -262,11 +262,11 @@ parseAndUpdate line = do _ -> pure () -- Extract cost from usage-ledger:event - -- Pattern: {"totalCredits": 1.54, "message": "usage-ledger:event", ...} - -- We use totalCredits to be robust against missed lines and restarts. + -- Pattern: {"totalCredits": 154.0, "message": "usage-ledger:event", ...} + -- The credits are in cents, so we divide by 100 to get dollars. case KM.lookup "totalCredits" obj of Just (Aeson.Number n) -> - let total = Scientific.toRealFloat n + let total = Scientific.toRealFloat n / 100.0 in AgentLog.update (\s -> s {AgentLog.statusCredits = total}) _ -> pure () -- cgit v1.2.3 From 7107e038ec661e5e121e226250f85771b0fd5ff4 Mon Sep 17 00:00:00 2001 From: Omni Worker Date: Sat, 22 Nov 2025 06:36:53 -0500 Subject: feat: include AGENTS.md in worker prompt --- Omni/Agent/Worker.hs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'Omni/Agent/Worker.hs') diff --git a/Omni/Agent/Worker.hs b/Omni/Agent/Worker.hs index 3504765..a861173 100644 --- a/Omni/Agent/Worker.hs +++ b/Omni/Agent/Worker.hs @@ -151,6 +151,19 @@ runAmp repo task = do Directory.createDirectoryIfMissing True (repo "_/llm") let logFile = repo "_/llm/amp.log" + -- Read AGENTS.md + agentsMd <- + fmap (fromMaybe "") <| do + exists <- Directory.doesFileExist (repo "AGENTS.md") + if exists + then Just "AGENTS.md") + else pure Nothing + + let fullPrompt = + prompt + <> "\n\nREPOSITORY GUIDELINES (AGENTS.md):\n" + <> agentsMd + -- Clean up previous log exists <- Directory.doesFileExist logFile when exists (Directory.removeFile logFile) @@ -160,7 +173,7 @@ runAmp repo task = do tidLog <- forkIO (monitorLog logFile) -- Assume amp is in PATH - let args = ["--log-level", "debug", "--log-file", "_/llm/amp.log", "--dangerously-allow-all", "-x", Text.unpack prompt] + let args = ["--log-level", "debug", "--log-file", "_/llm/amp.log", "--dangerously-allow-all", "-x", Text.unpack fullPrompt] let cp = (Process.proc "amp" args) {Process.cwd = Just repo} (_, _, _, ph) <- Process.createProcess cp -- cgit v1.2.3