feat(orchestrator): add SSE events, queue controls, and mosaic rails sync

This commit is contained in:
Jason Woltje
2026-02-17 15:39:15 -06:00
parent 758b2a839b
commit 3258cd4f4d
35 changed files with 1016 additions and 89 deletions

View File

@@ -0,0 +1,102 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./common.sh
source "$SCRIPT_DIR/common.sh"
ensure_repo_root
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
ORCH_DIR=".mosaic/orchestrator"
PID_FILE="$ORCH_DIR/orchestrator.pid"
LOG_FILE="$ORCH_DIR/logs/daemon.log"
usage() {
cat <<USAGE
Usage: $(basename "$0") <start|drain|stop|status> [--poll-sec N] [--no-sync]
Commands:
start Run orchestrator drain loop in background (detached)
drain Run orchestrator drain loop in foreground (until queue drained)
stop Stop background orchestrator if running
status Show background orchestrator status
Options:
--poll-sec N Poll interval (default: 15)
--no-sync Skip docs/tasks.md -> orchestrator queue sync before run
USAGE
}
cmd="${1:-status}"
if [[ $# -gt 0 ]]; then
shift
fi
poll_sec=15
sync_arg=""
while [[ $# -gt 0 ]]; do
case "$1" in
--poll-sec)
poll_sec="${2:-15}"
shift 2
;;
--no-sync)
sync_arg="--no-sync"
shift
;;
*)
echo "[agent-framework] unknown argument: $1" >&2
usage
exit 1
;;
esac
done
mkdir -p "$ORCH_DIR/logs" "$ORCH_DIR/results"
is_running() {
[[ -f "$PID_FILE" ]] || return 1
local pid
pid="$(cat "$PID_FILE" 2>/dev/null || true)"
[[ -n "$pid" ]] || return 1
kill -0 "$pid" 2>/dev/null
}
case "$cmd" in
start)
if is_running; then
echo "[agent-framework] orchestrator already running (pid=$(cat "$PID_FILE"))"
exit 0
fi
nohup "$MOSAIC_HOME/bin/mosaic-orchestrator-drain" --poll-sec "$poll_sec" $sync_arg >"$LOG_FILE" 2>&1 &
echo "$!" > "$PID_FILE"
echo "[agent-framework] orchestrator started (pid=$!, log=$LOG_FILE)"
;;
drain)
exec "$MOSAIC_HOME/bin/mosaic-orchestrator-drain" --poll-sec "$poll_sec" $sync_arg
;;
stop)
if ! is_running; then
echo "[agent-framework] orchestrator not running"
rm -f "$PID_FILE"
exit 0
fi
pid="$(cat "$PID_FILE")"
kill "$pid" || true
rm -f "$PID_FILE"
echo "[agent-framework] orchestrator stopped (pid=$pid)"
;;
status)
if is_running; then
echo "[agent-framework] orchestrator running (pid=$(cat "$PID_FILE"), log=$LOG_FILE)"
else
echo "[agent-framework] orchestrator not running"
rm -f "$PID_FILE"
fi
;;
*)
usage
exit 1
;;
esac

View File

@@ -0,0 +1,63 @@
#!/usr/bin/env bash
set -euo pipefail
task_file="${1:-}"
if [[ -z "$task_file" || ! -f "$task_file" ]]; then
echo "[orchestrator-worker] missing task file argument" >&2
exit 1
fi
worker_exec="${MOSAIC_WORKER_EXEC:-}"
if [[ -z "$worker_exec" ]]; then
if command -v codex >/dev/null 2>&1; then
worker_exec="codex -p"
elif command -v opencode >/dev/null 2>&1; then
worker_exec="opencode -p"
else
echo "[orchestrator-worker] set MOSAIC_WORKER_EXEC to your worker command (example: 'codex -p' or 'opencode -p')" >&2
exit 1
fi
fi
prompt="$(python3 - "$task_file" <<'PY'
import json
import sys
from pathlib import Path
task = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
task_id = str(task.get("id", "TASK"))
title = str(task.get("title", ""))
description = str(task.get("description", ""))
meta = task.get("metadata", {}) or {}
issue = str(meta.get("issue", ""))
repo = str(meta.get("repo", ""))
branch = str(meta.get("branch", ""))
depends = task.get("depends_on", [])
if isinstance(depends, list):
depends_str = ", ".join(str(x) for x in depends)
else:
depends_str = str(depends)
print(
f"""Read ~/.config/mosaic/STANDARDS.md, then AGENTS.md and SOUL.md (if present).
Complete this queued task fully.
Task ID: {task_id}
Title: {title}
Description: {description}
Issue: {issue}
Repo hint: {repo}
Branch hint: {branch}
Depends on: {depends_str}
Requirements:
- Implement and verify the task end-to-end.
- Keep changes scoped to this task.
- Run project checks and tests relevant to touched code.
- Return with a concise summary of what changed and verification results.
"""
)
PY
)"
PROMPT="$prompt" bash -lc "$worker_exec \"\$PROMPT\""