← Back to task

Commit 8b07b29e

commit 8b07b29ecd8d8d4aa6945d0ecdfd54f4b3911d68
Author: Ben Sima <ben@bensima.com>
Date:   Fri Jan 2 07:04:14 2026

    Wire Coder.hs into Developer.hs, replacing pi-code.sh subprocess
    
    Developer.runCoder now uses Omni.Ide.Coder.runCoder directly via the
    Agent API, instead of spawning the pi-code.sh shell script.
    
    Task-Id: t-278.2

diff --git a/Omni/Agent/Telegram/Developer.hs b/Omni/Agent/Telegram/Developer.hs
index 7acc1935..b27f48de 100644
--- a/Omni/Agent/Telegram/Developer.hs
+++ b/Omni/Agent/Telegram/Developer.hs
@@ -58,6 +58,7 @@ import qualified Data.Text.IO as TextIO
 import qualified Data.Time as Time
 import qualified Network.HTTP.Simple as HTTP
 import qualified Omni.Agent.Telegram.Types as Types
+import qualified Omni.Ide.Coder as Coder
 import qualified Omni.Task.Core as Task
 import qualified Omni.Test as Test
 import qualified System.Exit as Exit
@@ -387,40 +388,29 @@ runOrchestrator tgCfg cfg statusMsgId = do
                       updatePhase' PhaseReviewerApproved
                       pure (Right commit)
 
--- | Run the coder subprocess (pi-code.sh)
+-- | Run the coder using native Haskell Agent API
 runCoder :: OrchestratorConfig -> IO (Either Text ())
 runCoder cfg = do
-  let proc =
-        (Process.proc "./Omni/Ide/pi-code.sh" [Text.unpack (orchTaskId cfg)])
-          { Process.cwd = Just (orchWorkDir cfg),
-            -- Inherit stdout/stderr so output goes to parent (visible in logs)
-            -- and we don't block on full pipe buffers
-            Process.std_out = Process.Inherit,
-            Process.std_err = Process.Inherit,
-            -- Create new process group so we can kill children
-            Process.create_group = True
+  let coderConfig =
+        Coder.defaultCoderConfig
+          { Coder.coderTaskId = orchTaskId cfg,
+            Coder.coderExtraInstructions = Nothing,
+            Coder.coderMaxIterations = 50,
+            Coder.coderMaxCostCents = 200,
+            Coder.coderVerbose = True,
+            Coder.coderOnActivity = \msg -> TextIO.hPutStrLn stdout <| "[coder] " <> msg,
+            Coder.coderOnOutput = TextIO.putStrLn
           }
 
-  (_, _, _, ph) <- Process.createProcess proc
-
-  -- Register the process handle so it can be stopped
-  updateOrchestratorProcess (orchChatId cfg) (Just ph)
-
-  -- Wait with timeout
+  -- Run with timeout
   let timeoutMicros = orchCoderTimeout cfg * 1000000
-  mExit <- Timeout.timeout timeoutMicros (Process.waitForProcess ph)
-
-  -- Clear the process handle
-  updateOrchestratorProcess (orchChatId cfg) Nothing
+  mResult <- Timeout.timeout timeoutMicros (Coder.runCoder coderConfig)
 
-  case mExit of
-    Nothing -> do
-      -- Timeout - kill process
-      Process.terminateProcess ph
-      pure (Left "Coder timeout")
-    Just Exit.ExitSuccess -> pure (Right ())
-    Just (Exit.ExitFailure code) ->
-      pure (Left ("Exit code " <> tshow code))
+  case mResult of
+    Nothing -> pure (Left "Coder timeout")
+    Just (Coder.CoderSuccess _) -> pure (Right ())
+    Just (Coder.CoderError err) -> pure (Left err)
+    Just (Coder.CoderCompilationFailed err) -> pure (Left ("Compilation failed: " <> err))
 
 -- | Run build verification (bild)
 runBuild :: OrchestratorConfig -> IO (Either Text ())