summaryrefslogtreecommitdiff
path: root/Omni/Agent/Worker.hs
diff options
context:
space:
mode:
Diffstat (limited to 'Omni/Agent/Worker.hs')
-rw-r--r--Omni/Agent/Worker.hs96
1 files changed, 28 insertions, 68 deletions
diff --git a/Omni/Agent/Worker.hs b/Omni/Agent/Worker.hs
index a861173..1cc0b8d 100644
--- a/Omni/Agent/Worker.hs
+++ b/Omni/Agent/Worker.hs
@@ -5,12 +5,8 @@
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 Data.Text.IO as TIO
import qualified Omni.Agent.Core as Core
import qualified Omni.Agent.Git as Git
import qualified Omni.Agent.Log as AgentLog
@@ -93,7 +89,7 @@ processTask worker task = do
-- Run Amp
AgentLog.updateActivity "Running Amp agent..."
- exitCode <- runAmp repo task
+ (exitCode, output) <- runAmp repo task
case exitCode of
Exit.ExitSuccess -> do
@@ -103,8 +99,10 @@ processTask worker task = do
TaskCore.updateTaskStatus tid TaskCore.Review []
-- Commit changes
- -- We should check if there are changes, but 'git add .' is safe.
- Git.commit repo ("feat: implement " <> tid)
+ -- We use the agent's output as the extended commit description
+ let summary = Text.strip output
+ let commitMsg = "feat: implement " <> tid <> "\n\n" <> summary
+ Git.commit repo commitMsg
-- Submit for review
AgentLog.updateActivity "Submitting for review..."
@@ -127,7 +125,7 @@ processTask worker task = do
AgentLog.updateActivity "Agent failed, retrying..."
threadDelay (10 * 1000000) -- Sleep 10s
-runAmp :: FilePath -> TaskCore.Task -> IO Exit.ExitCode
+runAmp :: FilePath -> TaskCore.Task -> IO (Exit.ExitCode, Text)
runAmp repo task = do
let prompt =
"You are a Worker Agent.\n"
@@ -139,7 +137,8 @@ runAmp repo task = do
<> "3. Run tests to verify your work (e.g., 'bild --test Omni/Namespace').\n"
<> "4. Fix any errors found during testing.\n"
<> "5. Do NOT update the task status or manage git branches (the system handles that).\n"
- <> "6. When finished and tested, exit.\n\n"
+ <> "6. Do NOT run 'git commit'. The system will commit your changes automatically.\n"
+ <> "7. When finished and tested, exit.\n\n"
<> "Context:\n"
<> "- You are working in '"
<> Text.pack repo
@@ -149,7 +148,10 @@ runAmp repo task = do
<> "'.\n"
Directory.createDirectoryIfMissing True (repo </> "_/llm")
- let logFile = repo </> "_/llm/amp.log"
+ let logPath = repo </> "_/llm/amp.log"
+
+ -- Ensure log file is empty/exists
+ IO.writeFile logPath ""
-- Read AGENTS.md
agentsMd <-
@@ -164,26 +166,19 @@ runAmp repo task = do
<> "\n\nREPOSITORY GUIDELINES (AGENTS.md):\n"
<> agentsMd
- -- Clean up previous log
- exists <- Directory.doesFileExist logFile
- when exists (Directory.removeFile logFile)
-
- -- Start background monitors
- tidTime <- forkIO timeTicker
- tidLog <- forkIO (monitorLog logFile)
+ -- Monitor log file
+ tidLog <- forkIO (monitorLog logPath)
-- Assume amp is in PATH
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
- exitCode <- Process.waitForProcess ph
+ (exitCode, out, _err) <- Process.readCreateProcessWithExitCode cp ""
-- Cleanup
- killThread tidTime
killThread tidLog
- pure exitCode
+ pure (exitCode, Text.pack out)
formatTask :: TaskCore.Task -> Text
formatTask t =
@@ -237,56 +232,21 @@ findBaseBranch repo task = do
[] -> pure "live"
monitorLog :: FilePath -> IO ()
-monitorLog logPath = do
- waitForFile logPath
- IO.withFile logPath IO.ReadMode <| \h -> do
- -- Start from beginning of file (don't seek to end)
+monitorLog path = do
+ -- Wait for file to exist
+ waitForFile path
+
+ IO.withFile path IO.ReadMode <| \h -> do
+ IO.hSetBuffering h IO.LineBuffering
forever <| do
eof <- IO.hIsEOF h
if eof
then threadDelay 100000 -- 0.1s
else do
- line <- IO.hGetLine h
- parseAndUpdate (Text.pack line)
+ line <- TIO.hGetLine h
+ AgentLog.processLogLine 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 message (was msg)
- case KM.lookup "message" 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 from usage-ledger:event
- -- 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 / 100.0
- in AgentLog.update (\s -> s {AgentLog.statusCredits = total})
- _ -> 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
+waitForFile p = do
+ e <- Directory.doesFileExist p
+ if e then pure () else threadDelay 100000 >> waitForFile p