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.hs180
1 files changed, 10 insertions, 170 deletions
diff --git a/Omni/Agent/Worker.hs b/Omni/Agent/Worker.hs
index aa7c5ab..ded4144 100644
--- a/Omni/Agent/Worker.hs
+++ b/Omni/Agent/Worker.hs
@@ -7,10 +7,10 @@ import Alpha
import qualified Data.Aeson as Aeson
import qualified Data.Aeson.Key as AesonKey
import qualified Data.ByteString.Lazy as BSL
+import Data.IORef (modifyIORef', newIORef, readIORef)
import qualified Data.List as List
import qualified Data.Text as Text
import qualified Data.Text.Encoding as TE
-import qualified Data.Text.IO as TIO
import qualified Data.Time
import qualified Omni.Agent.Core as Core
import qualified Omni.Agent.Engine as Engine
@@ -22,7 +22,6 @@ import qualified System.Directory as Directory
import qualified System.Environment as Env
import qualified System.Exit as Exit
import System.FilePath ((</>))
-import qualified System.IO as IO
import qualified System.Process as Process
start :: Core.Worker -> Maybe Text -> IO ()
@@ -89,38 +88,18 @@ processTask worker task = do
TaskCore.updateTaskStatus tid TaskCore.InProgress []
say "[worker] Status -> InProgress"
- -- Check if we should use native engine
- useEngine <- shouldUseEngine
-
-- Run agent with timing
startTime <- Data.Time.getCurrentTime
activityId <- TaskCore.logActivityWithMetrics tid TaskCore.Running Nothing Nothing (Just startTime) Nothing Nothing Nothing
- (exitCode, output, maybeCost) <-
- if useEngine
- then do
- say "[worker] Starting native engine..."
- (code, out, cost) <- runWithEngine repo task
- pure (code, out, Just cost)
- else do
- say "[worker] Starting amp..."
- (code, out) <- runAmp repo task
- pure (code, out, Nothing)
+ say "[worker] Starting engine..."
+ (exitCode, output, costCents) <- runWithEngine repo task
endTime <- Data.Time.getCurrentTime
say ("[worker] Agent exited with: " <> tshow exitCode)
- -- Capture metrics - from engine result or agent log
- (threadUrl, costCents) <- case maybeCost of
- Just engineCost -> pure (Nothing, Just engineCost)
- Nothing -> do
- status <- AgentLog.getStatus
- let url = ("https://ampcode.com/threads/" <>) </ AgentLog.statusThread status
- let cost = Just <| floor (AgentLog.statusCredits status * 100)
- pure (url, cost)
-
-- Update the activity record with metrics
- TaskCore.updateActivityMetrics activityId threadUrl (Just endTime) costCents Nothing
+ TaskCore.updateActivityMetrics activityId Nothing (Just endTime) (Just costCents) Nothing
case exitCode of
Exit.ExitSuccess -> do
@@ -178,10 +157,10 @@ processTask worker task = do
say ("[worker] ✓ Task " <> tid <> " -> Review")
unless quiet <| AgentLog.update (\s -> s {AgentLog.statusTask = Nothing})
Exit.ExitFailure code -> do
- say ("[worker] Amp failed with code " <> tshow code)
+ say ("[worker] Engine failed with code " <> tshow code)
TaskCore.logActivity tid TaskCore.Failed (Just (toMetadata [("exit_code", tshow code)]))
-- Don't set back to Open here - leave in InProgress for debugging
- say "[worker] Task left in InProgress (amp failure)"
+ say "[worker] Task left in InProgress (engine failure)"
-- | Run lint --fix to format and fix lint issues
runFormatters :: FilePath -> IO (Either Text ())
@@ -218,115 +197,7 @@ tryCommit repo msg = do
Exit.ExitFailure _ -> pure <| CommitFailed (Text.pack commitErr)
Exit.ExitFailure c -> pure <| CommitFailed ("git diff failed with code " <> tshow c)
-runAmp :: FilePath -> TaskCore.Task -> IO (Exit.ExitCode, Text)
-runAmp repo task = do
- -- Check for retry context
- maybeRetry <- TaskCore.getRetryContext (TaskCore.taskId task)
-
- let ns = fromMaybe "." (TaskCore.taskNamespace task)
- let basePrompt =
- "You are a Worker Agent.\n"
- <> "Your goal is to implement the following task:\n\n"
- <> formatTask task
- <> "\n\nCRITICAL INSTRUCTIONS:\n"
- <> "1. Analyze the codebase to understand where to make changes.\n"
- <> "2. Implement the changes by editing files.\n"
- <> "3. BEFORE finishing, you MUST run: bild --test "
- <> ns
- <> "\n"
- <> "4. Fix ALL errors from bild --test (including hlint suggestions).\n"
- <> "5. Keep running bild --test until it passes with no errors.\n"
- <> "6. Do NOT update task status or manage git.\n"
- <> "7. Only exit after bild --test passes.\n\n"
- <> "IMPORTANT: The git commit will fail if hlint finds issues.\n"
- <> "You must fix hlint suggestions like:\n"
- <> "- 'Use list comprehension' -> use [x | cond] instead of if/else\n"
- <> "- 'Avoid lambda' -> use function composition\n"
- <> "- 'Redundant bracket' -> remove unnecessary parens\n\n"
- <> "Context:\n"
- <> "- Working directory: "
- <> Text.pack repo
- <> "\n"
- <> "- Namespace: "
- <> ns
- <> "\n"
-
- -- Add retry context if present
- let retryPrompt = case maybeRetry of
- Nothing -> ""
- Just ctx ->
- "\n\n## RETRY CONTEXT (IMPORTANT)\n\n"
- <> "This task was previously attempted but failed. Attempt: "
- <> tshow (TaskCore.retryAttempt ctx)
- <> "/3\n"
- <> "Reason: "
- <> TaskCore.retryReason ctx
- <> "\n\n"
- <> ( if null (TaskCore.retryConflictFiles ctx)
- then ""
- else
- "Conflicting files from previous attempt:\n"
- <> Text.unlines (map (" - " <>) (TaskCore.retryConflictFiles ctx))
- <> "\n"
- )
- <> "Original commit: "
- <> TaskCore.retryOriginalCommit ctx
- <> "\n\n"
- <> maybe "" (\notes -> "## HUMAN NOTES/GUIDANCE\n\n" <> notes <> "\n\n") (TaskCore.retryNotes ctx)
- <> "INSTRUCTIONS FOR RETRY:\n"
- <> "- The codebase has changed since your last attempt\n"
- <> "- Re-implement this task on top of the CURRENT codebase\n"
- <> "- If there were merge conflicts, the conflicting files may have been modified by others\n"
- <> "- Review the current state of those files before making changes\n"
-
- let prompt = basePrompt <> retryPrompt
-
- let logFile = repo </> "_/llm/amp.log"
-
- -- Read AGENTS.md
- agentsMd <-
- fmap (fromMaybe "") <| do
- exists <- Directory.doesFileExist (repo </> "AGENTS.md")
- if exists
- then Just </ readFile (repo </> "AGENTS.md")
- else pure Nothing
-
- -- Get relevant facts from the knowledge base
- relevantFacts <- getRelevantFacts task
- let factsSection = formatFacts relevantFacts
-
- let fullPrompt =
- prompt
- <> "\n\nREPOSITORY GUIDELINES (AGENTS.md):\n"
- <> agentsMd
- <> factsSection
-
- -- Remove old log file
- exists <- Directory.doesFileExist logFile
- when exists (Directory.removeFile logFile)
-
- Directory.createDirectoryIfMissing True (repo </> "_/llm")
-
- -- Assume amp is in PATH
- let args = ["--try-opus", "--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, Process.std_out = Process.CreatePipe}
- (_, Just hOut, _, ph) <- Process.createProcess cp
-
- tid <- forkIO <| monitorLog logFile ph
-
- exitCode <- Process.waitForProcess ph
- output <- TIO.hGetContents hOut
- killThread tid
- pure (exitCode, output)
-
--- | Check if we should use native engine instead of amp subprocess
-shouldUseEngine :: IO Bool
-shouldUseEngine = do
- env <- Env.lookupEnv "JR_USE_ENGINE"
- pure <| env == Just "1"
-
--- | Run task using native Engine instead of amp subprocess
+-- | Run task using native Engine
-- Returns (ExitCode, output text, cost in cents)
runWithEngine :: FilePath -> TaskCore.Task -> IO (Exit.ExitCode, Text, Int)
runWithEngine repo task = do
@@ -338,7 +209,7 @@ runWithEngine repo task = do
-- Check for retry context
maybeRetry <- TaskCore.getRetryContext (TaskCore.taskId task)
- -- Build the full prompt (same as runAmp)
+ -- Build the full prompt
let ns = fromMaybe "." (TaskCore.taskNamespace task)
let basePrompt = buildBasePrompt task ns repo
@@ -517,10 +388,10 @@ formatTask t =
formatComment c = " [" <> Text.pack (show (TaskCore.commentCreatedAt c)) <> "] " <> TaskCore.commentText c
formatCommitMessage :: TaskCore.Task -> Text -> Text
-formatCommitMessage task ampOutput =
+formatCommitMessage task agentOutput =
let tid = TaskCore.taskId task
subject = cleanSubject (TaskCore.taskTitle task)
- body = cleanBody ampOutput
+ body = cleanBody agentOutput
in if Text.null body
then subject <> "\n\nTask-Id: " <> tid
else subject <> "\n\n" <> body <> "\n\nTask-Id: " <> tid
@@ -573,34 +444,3 @@ formatFact f =
then ""
else " [" <> Text.intercalate ", " (TaskCore.factRelatedFiles f) <> "]"
)
-
-monitorLog :: FilePath -> Process.ProcessHandle -> IO ()
-monitorLog path ph = do
- waitForFile path
- IO.withFile path IO.ReadMode <| \h -> do
- IO.hSetBuffering h IO.LineBuffering
- go h
- where
- go h = do
- eof <- IO.hIsEOF h
- if eof
- then do
- mExit <- Process.getProcessExitCode ph
- case mExit of
- Nothing -> do
- threadDelay 100000 -- 0.1s
- go h
- Just _ -> pure ()
- else do
- line <- TIO.hGetLine h
- AgentLog.processLogLine line
- go h
-
-waitForFile :: FilePath -> IO ()
-waitForFile path = do
- exists <- Directory.doesFileExist path
- if exists
- then pure ()
- else do
- threadDelay 100000
- waitForFile path