summaryrefslogtreecommitdiff
path: root/Omni/Agent
diff options
context:
space:
mode:
Diffstat (limited to 'Omni/Agent')
-rw-r--r--Omni/Agent/Core.hs1
-rw-r--r--Omni/Agent/DESIGN.md2
-rw-r--r--Omni/Agent/Git.hs60
-rw-r--r--Omni/Agent/Log.hs62
-rw-r--r--Omni/Agent/LogTest.hs124
-rw-r--r--Omni/Agent/Worker.hs56
-rwxr-xr-xOmni/Agent/harvest-tasks.sh62
-rwxr-xr-xOmni/Agent/merge-tasks.sh30
-rwxr-xr-xOmni/Agent/monitor-worker.sh47
-rwxr-xr-xOmni/Agent/monitor.sh68
-rwxr-xr-xOmni/Agent/setup-worker.sh31
-rwxr-xr-xOmni/Agent/start-worker.sh6
-rwxr-xr-xOmni/Agent/sync-tasks.sh46
13 files changed, 211 insertions, 384 deletions
diff --git a/Omni/Agent/Core.hs b/Omni/Agent/Core.hs
index 2d09e39..a2594d6 100644
--- a/Omni/Agent/Core.hs
+++ b/Omni/Agent/Core.hs
@@ -1,7 +1,6 @@
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE NoImplicitPrelude #-}
--- : out omni-agent-core
module Omni.Agent.Core where
import Alpha
diff --git a/Omni/Agent/DESIGN.md b/Omni/Agent/DESIGN.md
index 2d1e6e3..3ff00fc 100644
--- a/Omni/Agent/DESIGN.md
+++ b/Omni/Agent/DESIGN.md
@@ -72,7 +72,7 @@ The Haskell implementation should replicate the logic of `start-worker.sh` but w
### 4.3 Logging
- Continue writing raw Amp logs to `_/llm/amp.log` in the worker directory.
-- `agent log` reads this file and applies the filtering logic (currently in `monitor-worker.sh` jq script) using Haskell (Aeson).
+- `agent log` reads this file and applies the filtering logic (currently in `monitor.sh` jq script) using Haskell (Aeson).
- **UI Design**:
- **Two-line Status**: The CLI should maintain two reserved lines at the bottom (or top) of the output for each worker:
- **Line 1 (Meta)**: `[Worker: omni-worker-1] Task: t-123 | Files: 3 | Credits: $0.45 | Time: 05:23`
diff --git a/Omni/Agent/Git.hs b/Omni/Agent/Git.hs
index a2009b2..4c06cf6 100644
--- a/Omni/Agent/Git.hs
+++ b/Omni/Agent/Git.hs
@@ -13,6 +13,10 @@ module Omni.Agent.Git
getCurrentBranch,
branchExists,
isMerged,
+ listBranches,
+ showFile,
+ getRepoRoot,
+ runGit,
main,
test,
)
@@ -25,7 +29,6 @@ import Omni.Test ((@=?))
import qualified Omni.Test as Test
import qualified System.Directory as Directory
import qualified System.Exit as Exit
-import System.FilePath ((</>))
import qualified System.IO.Temp as Temp
import qualified System.Process as Process
@@ -149,30 +152,16 @@ syncWithLive repo = do
Log.info ["git", "syncing with live"]
-- git repo ["fetch", "origin", "live"] -- Optional
- -- Try rebase, if fail, abort
- -- First, proactively cleanup any stale rebase state
- cleanupStaleRebase repo
-
- let cmd = (Process.proc "git" ["rebase", "live"]) {Process.cwd = Just repo}
- (code, _, err) <- Process.readCreateProcessWithExitCode cmd ""
+ -- Try sync (branchless sync), if fail, panic
+ -- This replaces manual rebase and handles stack movement
+ let cmd = (Process.proc "git" ["sync"]) {Process.cwd = Just repo}
+ (code, out, err) <- Process.readCreateProcessWithExitCode cmd ""
case code of
Exit.ExitSuccess -> pure ()
Exit.ExitFailure _ -> do
- Log.warn ["rebase failed, aborting", Text.pack err]
- cleanupStaleRebase repo
- panic "Sync with live failed (rebase conflict)"
-
-cleanupStaleRebase :: FilePath -> IO ()
-cleanupStaleRebase repo = do
- -- Check if a rebase is in progress
- rebaseMerge <- Directory.doesDirectoryExist (repo </> ".git/rebase-merge")
- rebaseApply <- Directory.doesDirectoryExist (repo </> ".git/rebase-apply")
-
- when (rebaseMerge || rebaseApply) <| do
- Log.warn ["git", "detected stale rebase", "aborting"]
- let abort = (Process.proc "git" ["rebase", "--abort"]) {Process.cwd = Just repo}
- _ <- Process.readCreateProcessWithExitCode abort ""
- pure ()
+ Log.warn ["git sync failed", Text.pack err]
+ Log.info [Text.pack out]
+ panic "Sync with live failed (git sync)"
commit :: FilePath -> Text -> IO ()
commit repo msg = do
@@ -214,3 +203,30 @@ isMerged repo branch target = do
let cmd = (Process.proc "git" ["merge-base", "--is-ancestor", Text.unpack branch, Text.unpack target]) {Process.cwd = Just repo}
(code, _, _) <- Process.readCreateProcessWithExitCode cmd ""
pure (code == Exit.ExitSuccess)
+
+listBranches :: FilePath -> Text -> IO [Text]
+listBranches repo pat = do
+ let cmd = (Process.proc "git" ["branch", "--list", Text.unpack pat, "--format=%(refname:short)"]) {Process.cwd = Just repo}
+ (code, out, _) <- Process.readCreateProcessWithExitCode cmd ""
+ case code of
+ Exit.ExitSuccess -> pure <| filter (not <. Text.null) (Text.lines (Text.pack out))
+ _ -> panic "git branch list failed"
+
+showFile :: FilePath -> Text -> FilePath -> IO (Maybe Text)
+showFile repo branch path = do
+ let cmd = (Process.proc "git" ["show", Text.unpack branch <> ":" <> path]) {Process.cwd = Just repo}
+ (code, out, _) <- Process.readCreateProcessWithExitCode cmd ""
+ case code of
+ Exit.ExitSuccess -> pure <| Just (Text.pack out)
+ _ -> pure Nothing
+
+getRepoRoot :: FilePath -> IO FilePath
+getRepoRoot dir = do
+ let cmd = (Process.proc "git" ["rev-parse", "--show-toplevel"]) {Process.cwd = Just dir}
+ (code, out, _) <- Process.readCreateProcessWithExitCode cmd ""
+ case code of
+ Exit.ExitSuccess -> pure <| strip out
+ _ -> panic "git rev-parse failed"
+
+runGit :: FilePath -> [String] -> IO ()
+runGit = git
diff --git a/Omni/Agent/Log.hs b/Omni/Agent/Log.hs
index afaf1da..3f305d8 100644
--- a/Omni/Agent/Log.hs
+++ b/Omni/Agent/Log.hs
@@ -2,11 +2,16 @@
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE NoImplicitPrelude #-}
--- : out omni-agent-log
+-- | Status of the agent for the UI
module Omni.Agent.Log where
import Alpha
+import Data.Aeson ((.:), (.:?))
+import qualified Data.Aeson as Aeson
+import qualified Data.ByteString.Lazy as BL
import Data.IORef (IORef, modifyIORef', newIORef, readIORef, writeIORef)
+import qualified Data.Text as Text
+import qualified Data.Text.Encoding as TE
import qualified Data.Text.IO as TIO
import qualified System.Console.ANSI as ANSI
import qualified System.IO as IO
@@ -16,6 +21,7 @@ import System.IO.Unsafe (unsafePerformIO)
data Status = Status
{ statusWorker :: Text,
statusTask :: Maybe Text,
+ statusThread :: Maybe Text,
statusFiles :: Int,
statusCredits :: Double,
statusTime :: Text, -- formatted time string
@@ -28,6 +34,7 @@ emptyStatus workerName =
Status
{ statusWorker = workerName,
statusTask = Nothing,
+ statusThread = Nothing,
statusFiles = 0,
statusCredits = 0.0,
statusTime = "00:00",
@@ -81,13 +88,16 @@ render = do
Status {..} <- readIORef currentStatus
-- Line 1: Meta
- -- [Worker: name] Task: t-123 | Files: 3 | Credits: $0.45 | Time: 05:23
+ -- [Worker: name] Task: t-123 | Thread: T-abc | Files: 3 | Credits: $0.45 | Time: 05:23
let taskStr = maybe "None" identity statusTask
+ threadStr = maybe "None" identity statusThread
meta =
"[Worker: "
<> statusWorker
<> "] Task: "
<> taskStr
+ <> " | Thread: "
+ <> threadStr
<> " | Files: "
<> tshow statusFiles
<> " | Credits: $"
@@ -109,3 +119,51 @@ render = do
-- Return cursor to line 1
ANSI.hCursorUp IO.stderr 1
IO.hFlush IO.stderr
+
+-- | Log Entry from JSON
+data LogEntry = LogEntry
+ { leMessage :: Text,
+ leThreadId :: Maybe Text,
+ leCredits :: Maybe Double,
+ leTotalCredits :: Maybe Double,
+ leTimestamp :: Maybe Text
+ }
+ deriving (Show, Eq)
+
+instance Aeson.FromJSON LogEntry where
+ parseJSON =
+ Aeson.withObject "LogEntry" <| \v ->
+ (LogEntry </ (v .: "message"))
+ <*> v
+ .:? "threadId"
+ <*> v
+ .:? "credits"
+ <*> v
+ .:? "totalCredits"
+ <*> v
+ .:? "timestamp"
+
+-- | Parse a log line and update status
+processLogLine :: Text -> IO ()
+processLogLine line = do
+ let bs = BL.fromStrict <| TE.encodeUtf8 line
+ case Aeson.decode bs of
+ Just entry -> update (updateFromEntry entry)
+ Nothing -> pure () -- Ignore invalid JSON
+
+updateFromEntry :: LogEntry -> Status -> Status
+updateFromEntry LogEntry {..} s =
+ s
+ { statusThread = leThreadId <|> statusThread s,
+ statusCredits = fromMaybe (statusCredits s) (leTotalCredits <|> leCredits),
+ statusTime = maybe (statusTime s) formatTime leTimestamp
+ }
+
+formatTime :: Text -> Text
+formatTime ts =
+ -- "2025-11-22T21:24:02.512Z" -> "21:24"
+ case Text.splitOn "T" ts of
+ [_, time] -> case Text.splitOn ":" time of
+ (h : m : _) -> h <> ":" <> m
+ _ -> ts
+ _ -> ts
diff --git a/Omni/Agent/LogTest.hs b/Omni/Agent/LogTest.hs
deleted file mode 100644
index 518147e..0000000
--- a/Omni/Agent/LogTest.hs
+++ /dev/null
@@ -1,124 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE NoImplicitPrelude #-}
-
--- : out agent-log-test
-module Omni.Agent.LogTest where
-
-import Alpha
-import qualified Data.Set as Set
-import Omni.Agent.Log
-import qualified Omni.Test as Test
-
-main :: IO ()
-main = Test.run tests
-
-tests :: Test.Tree
-tests =
- Test.group
- "Omni.Agent.Log"
- [ Test.unit "Parse LogEntry" testParse,
- Test.unit "Format LogEntry" testFormat,
- Test.unit "Update Status" testUpdateStatus,
- Test.unit "Render Status" testRenderStatus
- ]
-
-testParse :: IO ()
-testParse = do
- let json = "{\"message\": \"executing 1 tools in 1 batch(es)\", \"batches\": [[\"grep\"]]}"
- let expected =
- LogEntry
- { leMessage = "executing 1 tools in 1 batch(es)",
- leLevel = Nothing,
- leToolName = Nothing,
- leBatches = Just [["grep"]],
- leMethod = Nothing,
- lePath = Nothing,
- leTimestamp = Nothing
- }
- parseLine json @?= Just expected
-
-testFormat :: IO ()
-testFormat = do
- let entry =
- LogEntry
- { leMessage = "executing 1 tools in 1 batch(es)",
- leLevel = Nothing,
- leToolName = Nothing,
- leBatches = Just [["grep"]],
- leMethod = Nothing,
- lePath = Nothing,
- leTimestamp = Nothing
- }
- format entry @?= Just "🤖 THOUGHT: Planning tool execution (grep)"
-
- let entry2 =
- LogEntry
- { leMessage = "some random log",
- leLevel = Nothing,
- leToolName = Nothing,
- leBatches = Nothing,
- leMethod = Nothing,
- lePath = Nothing,
- leTimestamp = Nothing
- }
- format entry2 @?= Nothing
-
- let entry3 =
- LogEntry
- { leMessage = "some error",
- leLevel = Just "error",
- leToolName = Nothing,
- leBatches = Nothing,
- leMethod = Nothing,
- lePath = Nothing,
- leTimestamp = Nothing
- }
- format entry3 @?= Just "❌ ERROR: some error"
-
-testUpdateStatus :: IO ()
-testUpdateStatus = do
- let s0 = initialStatus "worker-1"
- let e1 =
- LogEntry
- { leMessage = "executing 1 tools in 1 batch(es)",
- leLevel = Nothing,
- leToolName = Nothing,
- leBatches = Just [["grep"]],
- leMethod = Nothing,
- lePath = Nothing,
- leTimestamp = Just "12:00:00"
- }
- let s1 = updateStatus e1 s0
- sLastActivity s1 @?= "🤖 THOUGHT: Planning tool execution (grep)"
- sStartTime s1 @?= Just "12:00:00"
-
- let e2 =
- LogEntry
- { leMessage = "ide-fs",
- leLevel = Nothing,
- leToolName = Nothing,
- leBatches = Nothing,
- leMethod = Just "readFile",
- lePath = Just "/path/to/file",
- leTimestamp = Just "12:00:01"
- }
- let s2 = updateStatus e2 s1
- sLastActivity s2 @?= "📂 READ: /path/to/file"
- Set.member "/path/to/file" (sFiles s2) @?= True
- sStartTime s2 @?= Just "12:00:00" -- Should preserve start time
-
-testRenderStatus :: IO ()
-testRenderStatus = do
- let s =
- Status
- { sWorkerName = "worker-1",
- sTaskId = Just "t-123",
- sFiles = Set.fromList ["file1", "file2"],
- sStartTime = Just "12:00",
- sLastActivity = "Running..."
- }
- let output = renderStatus s
- output @?= "[Worker: worker-1] Task: t-123 | Files: 2\nRunning..."
-
-(@?=) :: (Eq a, Show a) => a -> a -> IO ()
-(@?=) = (Test.@?=)
diff --git a/Omni/Agent/Worker.hs b/Omni/Agent/Worker.hs
index 01099a0..a29feb4 100644
--- a/Omni/Agent/Worker.hs
+++ b/Omni/Agent/Worker.hs
@@ -1,11 +1,11 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoImplicitPrelude #-}
--- : out omni-agent-worker
module Omni.Agent.Worker where
import Alpha
import qualified Data.Text as Text
+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
@@ -13,6 +13,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 ()
@@ -58,7 +59,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 +95,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,12 +112,11 @@ 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")
AgentLog.update (\s -> s {AgentLog.statusTask = Nothing})
-
Exit.ExitFailure code -> do
AgentLog.log ("Agent failed with code " <> tshow code)
AgentLog.updateActivity "Agent failed, retrying..."
@@ -143,6 +143,12 @@ runAmp repo task = do
<> fromMaybe "root" (TaskCore.taskNamespace task)
<> "'.\n"
+ let logFile = repo </> "_/llm/amp.log"
+
+ -- Remove old log file
+ exists <- Directory.doesFileExist logFile
+ when exists (Directory.removeFile logFile)
+
Directory.createDirectoryIfMissing True (repo </> "_/llm")
-- Assume amp is in PATH
@@ -150,7 +156,12 @@ runAmp repo task = do
let cp = (Process.proc "amp" args) {Process.cwd = Just repo}
(_, _, _, ph) <- Process.createProcess cp
- Process.waitForProcess ph
+
+ tid <- forkIO <| monitorLog logFile ph
+
+ exitCode <- Process.waitForProcess ph
+ killThread tid
+ pure exitCode
formatTask :: TaskCore.Task -> Text
formatTask t =
@@ -182,6 +193,37 @@ formatTask t =
where
formatDep dep = " - " <> TaskCore.depId dep <> " [" <> Text.pack (show (TaskCore.depType dep)) <> "]"
+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
+
findBaseBranch :: FilePath -> TaskCore.Task -> IO Text
findBaseBranch repo task = do
let deps = TaskCore.taskDependencies task
diff --git a/Omni/Agent/harvest-tasks.sh b/Omni/Agent/harvest-tasks.sh
deleted file mode 100755
index 44c2322..0000000
--- a/Omni/Agent/harvest-tasks.sh
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/usr/bin/env bash
-set -e
-
-# Omni/Agent/harvest-tasks.sh
-# Imports task updates from all worker branches into the current branch (usually live).
-
-REPO_ROOT="$(git rev-parse --show-toplevel)"
-cd "$REPO_ROOT"
-
-echo "Harvesting task updates from workers..."
-
-# Find all worker branches (assuming naming convention omni-worker-*)
-# We filter for local branches
-WORKER_BRANCHES=$(git branch --list "omni-worker-*" --format="%(refname:short)")
-
-if [ -z "$WORKER_BRANCHES" ]; then
- echo "No worker branches found."
- exit 0
-fi
-
-UPDATED=0
-
-for branch in $WORKER_BRANCHES; do
- echo "Checking $branch..."
-
- # Extract tasks.jsonl from the worker branch
- if git show "$branch:.tasks/tasks.jsonl" > .tasks/worker-tasks.jsonl 2>/dev/null; then
- # Import into current DB
- # The import command handles deduplication and timestamp conflict resolution
- if "$REPO_ROOT/_/bin/task" import -i .tasks/worker-tasks.jsonl >/dev/null; then
- echo " Imported tasks from $branch"
- UPDATED=1
- fi
- else
- echo " Warning: Could not read .tasks/tasks.jsonl from $branch"
- fi
-done
-
-rm -f .tasks/worker-tasks.jsonl
-
-if [ "$UPDATED" -eq 1 ]; then
- # Consolidate
- "$REPO_ROOT/_/bin/task" export --flush
-
- # Commit if there are changes
- if [[ -n $(git status --porcelain .tasks/tasks.jsonl) ]]; then
- git add .tasks/tasks.jsonl
-
- LAST_MSG=$(git log -1 --pretty=%s)
- if [[ "$LAST_MSG" == "task: harvest updates from workers" ]]; then
- echo "Squashing with previous harvest commit..."
- git commit --amend --no-edit
- else
- git commit -m "task: harvest updates from workers"
- fi
- echo "Success: Task database updated and committed."
- else
- echo "No effective changes found."
- fi
-else
- echo "No updates found."
-fi
diff --git a/Omni/Agent/merge-tasks.sh b/Omni/Agent/merge-tasks.sh
deleted file mode 100755
index 833afcf..0000000
--- a/Omni/Agent/merge-tasks.sh
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/usr/bin/env bash
-# Omni/Ide/merge-tasks.sh
-# Git merge driver for .tasks/tasks.jsonl
-# Usage: merge-tasks.sh %O %A %B
-# %O = ancestor, %A = current (ours), %B = other (theirs)
-
-# ANCESTOR="$1" (unused)
-OURS="$2"
-THEIRS="$3"
-
-# We want to merge THEIRS into OURS using the task tool's import logic.
-REPO_ROOT="$(git rev-parse --show-toplevel)"
-TASK_BIN="$REPO_ROOT/_/bin/task"
-
-# If binary doesn't exist, try to build it? Or just fail safely.
-if [ ! -x "$TASK_BIN" ]; then
- # Try to find it in the build output if _/bin isn't populated
- # But for now, let's just fail if not found, forcing manual merge
- exit 1
-fi
-
-# Use the task tool to merge
-# We tell it that the DB is the 'OURS' file
-# And we import the 'THEIRS' file
-export TASK_DB_PATH="$OURS"
-if "$TASK_BIN" import -i "$THEIRS" >/dev/null 2>&1; then
- exit 0
-else
- exit 1
-fi
diff --git a/Omni/Agent/monitor-worker.sh b/Omni/Agent/monitor-worker.sh
deleted file mode 100755
index 2638e2d..0000000
--- a/Omni/Agent/monitor-worker.sh
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env bash
-set -e
-
-# Omni/Agent/monitor-worker.sh
-# Monitors the worker agent's activity by filtering the debug log.
-# Usage: ./Omni/Agent/monitor-worker.sh [worker-directory-name]
-
-WORKER_NAME="${1:-omni-worker-1}"
-REPO_ROOT="$(git rev-parse --show-toplevel)"
-WORKER_PATH="$REPO_ROOT/../$WORKER_NAME"
-LOG_FILE="$WORKER_PATH/_/llm/amp.log"
-
-if [ ! -f "$LOG_FILE" ]; then
- echo "Waiting for log file at $LOG_FILE..."
- while [ ! -f "$LOG_FILE" ]; do sleep 1; done
-fi
-
-echo "Monitoring Worker Agent in '$WORKER_PATH'..."
-echo "Press Ctrl+C to stop."
-echo "------------------------------------------------"
-
-# Tail the log and use jq to parse/filter relevant events
-# We handle JSON parse errors gracefully (in case of partial writes)
-tail -f "$LOG_FILE" | grep --line-buffered "^{" | jq -R -r '
-try (
- fromjson |
- if .message == "executing 1 tools in 1 batch(es)" then
- "🤖 THOUGHT: Planning tool execution (" + (.batches[0][0] // "unknown") + ")"
- elif .message == "Tool Bash - checking permissions" then
- empty
- elif .message == "Tool Bash permitted - action: allow" then
- "🔧 TOOL: Bash command executed"
- elif .toolName != null and .message == "Processing tool completion for ledger" then
- "✅ TOOL: " + .toolName + " completed"
- elif .message == "ide-fs" and .method == "readFile" then
- "📂 READ: " + .path
- elif .message == "System prompt build complete (no changes)" then
- "🧠 THINKING..."
- elif .message == "System prompt build complete (first build)" then
- "🚀 STARTING new task context"
- elif .level == "error" then
- "❌ ERROR: " + .message
- else
- empty
- end
-) catch empty
-'
diff --git a/Omni/Agent/monitor.sh b/Omni/Agent/monitor.sh
index 1626354..e57611f 100755
--- a/Omni/Agent/monitor.sh
+++ b/Omni/Agent/monitor.sh
@@ -1,29 +1,75 @@
#!/usr/bin/env bash
# Omni/Agent/monitor.sh
# Monitor the logs of a worker agent
-# Usage: ./Omni/Agent/monitor.sh [worker-name]
+# Usage: ./Omni/Agent/monitor.sh [--raw] [worker-name]
+
+set -e
+
+RAW_MODE=false
+WORKER="omni-worker-1"
+
+# Parse arguments
+while [[ "$#" -gt 0 ]]; do
+ case $1 in
+ --raw) RAW_MODE=true ;;
+ *) WORKER="$1" ;;
+ esac
+ shift
+done
-WORKER="${1:-omni-worker-1}"
REPO_ROOT="$(git rev-parse --show-toplevel)"
WORKER_DIR="$REPO_ROOT/../$WORKER"
+LOG_FILE="$WORKER_DIR/_/llm/amp.log"
if [ ! -d "$WORKER_DIR" ]; then
echo "Error: Worker directory '$WORKER_DIR' not found."
- echo "Usage: $0 [worker-name]"
+ echo "Usage: $0 [--raw] [worker-name]"
exit 1
fi
-LOG_FILE="$WORKER_DIR/_/llm/amp.log"
-
echo "Monitoring worker: $WORKER"
echo "Watching log: $LOG_FILE"
+if [ "$RAW_MODE" = true ]; then
+ echo "Mode: RAW output"
+else
+ echo "Mode: FORMATTED output"
+fi
echo "---------------------------------------------------"
# Wait for log file to appear
-while [ ! -f "$LOG_FILE" ]; do
- echo "Waiting for log file to be created..."
- sleep 2
-done
+if [ ! -f "$LOG_FILE" ]; then
+ echo "Waiting for log file at $LOG_FILE..."
+ while [ ! -f "$LOG_FILE" ]; do
+ sleep 1
+ done
+fi
-# Tail the log file
-tail -f "$LOG_FILE"
+if [ "$RAW_MODE" = true ]; then
+ tail -f "$LOG_FILE"
+else
+ # Tail the log and use jq to parse/filter relevant events
+ tail -f "$LOG_FILE" | grep --line-buffered "^{" | jq -R -r '
+ try (
+ fromjson |
+ if .message == "executing 1 tools in 1 batch(es)" then
+ "🤖 THOUGHT: Planning tool execution (" + (.batches[0][0] // "unknown") + ")"
+ elif .message == "Tool Bash - checking permissions" then
+ empty
+ elif .message == "Tool Bash permitted - action: allow" then
+ "🔧 TOOL: Bash command executed"
+ elif .toolName != null and .message == "Processing tool completion for ledger" then
+ "✅ TOOL: " + .toolName + " completed"
+ elif .message == "ide-fs" and .method == "readFile" then
+ "📂 READ: " + .path
+ elif .message == "System prompt build complete (no changes)" then
+ "🧠 THINKING..."
+ elif .message == "System prompt build complete (first build)" then
+ "🚀 STARTING new task context"
+ elif .level == "error" then
+ "❌ ERROR: " + .message
+ else
+ empty
+ end
+ ) catch empty
+ '
+fi
diff --git a/Omni/Agent/setup-worker.sh b/Omni/Agent/setup-worker.sh
deleted file mode 100755
index 42b7fc9..0000000
--- a/Omni/Agent/setup-worker.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env bash
-set -e
-
-if [ -z "$1" ]; then
- echo "Usage: $0 <worker-name>"
- echo "Example: $0 omni-worker-1"
- exit 1
-fi
-
-WORKER_NAME="$1"
-REPO_ROOT="$(git rev-parse --show-toplevel)"
-WORKTREE_PATH="$REPO_ROOT/../$WORKER_NAME"
-
-# We create a new branch for the worker based on 'live'
-# This avoids the "branch already checked out" error if 'live' is checked out elsewhere
-BRANCH_NAME="${WORKER_NAME}"
-echo "Creating worktree '$WORKTREE_PATH' on branch '$BRANCH_NAME' (from live)..."
-git worktree add -b "$BRANCH_NAME" "$WORKTREE_PATH" live
-
-# Copy .envrc.local if it exists (user-specific config)
-if [ -f "$REPO_ROOT/.envrc.local" ]; then
- echo "Copying .envrc.local..."
- cp "$REPO_ROOT/.envrc.local" "$WORKTREE_PATH/"
-fi
-
-# Configure git identity for the worker
-echo "Configuring git identity for worker..."
-git -C "$WORKTREE_PATH" config user.name "Omni Worker"
-git -C "$WORKTREE_PATH" config user.email "bot@omni.agent"
-
-echo "Worker setup complete at $WORKTREE_PATH"
diff --git a/Omni/Agent/start-worker.sh b/Omni/Agent/start-worker.sh
index 310ca56..457c83c 100755
--- a/Omni/Agent/start-worker.sh
+++ b/Omni/Agent/start-worker.sh
@@ -37,6 +37,12 @@ fi
# Ensure worker has local task and agent binaries
mkdir -p "$WORKER_PATH/_/bin"
+echo "Syncing worker repo..."
+if ! (cd "$WORKER_PATH" && git sync); then
+ echo "Error: Failed to run 'git sync' in worker directory."
+ exit 1
+fi
+
echo "Building 'task' in worker..."
if ! (cd "$WORKER_PATH" && bild Omni/Task.hs); then
echo "Error: Failed to build 'task' in worker directory."
diff --git a/Omni/Agent/sync-tasks.sh b/Omni/Agent/sync-tasks.sh
deleted file mode 100755
index f4669b7..0000000
--- a/Omni/Agent/sync-tasks.sh
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/usr/bin/env bash
-set -e
-
-# Omni/Ide/sync-tasks.sh
-# Synchronizes the task database with the live branch safely.
-# Usage: sync-tasks.sh [--commit]
-
-COMMIT=0
-if [[ "$1" == "--commit" ]]; then
- COMMIT=1
-fi
-
-REPO_ROOT="$(git rev-parse --show-toplevel)"
-cd "$REPO_ROOT"
-
-echo "Syncing tasks..."
-
-# 1. Import latest tasks from 'live' branch
-# We use git show to get the file content from the reference branch without checking it out
-mkdir -p .tasks
-git show live:.tasks/tasks.jsonl > .tasks/live-tasks.jsonl
-
-# 2. Merge logic: Import live tasks into our local DB
-# The 'task import' command uses timestamps to resolve conflicts (last write wins)
-if [ -s .tasks/live-tasks.jsonl ]; then
- echo "Importing tasks from live branch..."
- "$REPO_ROOT/_/bin/task" import -i .tasks/live-tasks.jsonl
-fi
-
-# 3. Clean up
-rm .tasks/live-tasks.jsonl
-
-# 4. Export current state to ensure it's clean/deduplicated
-"$REPO_ROOT/_/bin/task" export --flush
-
-# 5. Commit changes to .tasks/tasks.jsonl if requested and there are changes
-if [[ "$COMMIT" -eq 1 ]]; then
- if [[ -n $(git status --porcelain .tasks/tasks.jsonl) ]]; then
- echo "Committing task updates..."
- git add .tasks/tasks.jsonl
- git commit -m "task: sync database" || true
- echo "Task updates committed to current branch."
- else
- echo "No task changes to commit."
- fi
-fi