Agent Orchestrator is a local, single-user system built around a long-running Go daemon. The Electron/React desktop app, optional CLI, and Expo mobile app are thin clients over the daemon's HTTP, SSE, and terminal interfaces.
System Overview
Electron + React desktop ─┐
ao CLI ──────────────────┼─> Go daemon ─> services/managers ─> SQLite
Expo mobile (opt-in LAN) ┘ │ │
│ ├─> agent/runtime/workspace adapters
├─> SSE events ├─> native Chat drivers
├─> terminal mux └─> GitHub SCM observer
└─> browser bridge to Electron
The desktop app discovers, launches, and supervises the daemon. The renderer uses a generated typed client rather than importing backend logic. The CLI is also a thin HTTP client; it never opens SQLite or launches agent adapters directly.
Sessions And Worktrees
Workers in Git projects own isolated worktrees; workspace projects can materialize a root plus child-repository worktrees, while Scratch sessions use an AO-managed directory. A session also commits to exactly one agent interface at a time:
- Terminal UI runs the harness inside a tmux runtime on macOS/Linux or a conpty host on Windows.
- Chat runs a native provider controller without an agent terminal runtime.
Chat conversations are durable and include provider identity, turns, messages, activities, approvals, structured input, usage, compaction, and rollback state.
For compatible Claude Code and Codex sessions, an interface transition can move the same provider conversation between TUI and Chat. AO drains or interrupts the old controller before committing the replacement, so both controllers are never live at once. The session and worktree do not change.
Durable Facts, Derived Status
AO stores durable facts rather than a display status. Important session facts include:
- Agent activity: active, idle, waiting for input, blocked, or exited.
- Whether the session has been terminated.
- The current interface mode, controller handle, and generation.
- Interface-transition checkpoints and queued messages.
- Pull request, check, and review-comment facts.
The service layer computes labels such as working, needs input, CI failed, or ready to merge when a client reads a session. This prevents stored status from drifting away from source facts.
Failed or unknown runtime probes are observations, not proof of death. Cleanup also refuses to force-delete a dirty registered worktree.
Storage And Live Updates
Durable state is stored in SQLite under ~/.ao/data by default. Database triggers append changes to a change_log; a CDC poller tails that log and broadcasts invalidations to in-process subscribers and GET /api/v1/events.
The event stream supports replay with Last-Event-ID. Clients use it to invalidate targeted queries rather than transferring every full session or Chat payload on each update.
All application state remains under ~/.ao, including the run file, managed worktrees, Electron data, and mobile configuration. AO_DATA_DIR and AO_RUN_FILE provide advanced overrides for daemon storage and discovery.
Services And Adapters
HTTP controllers validate requests and call domain services. Services own read/write policy; session and lifecycle managers coordinate multi-step work. External systems sit behind Go port interfaces and leaf adapters.
Current adapters include more than twenty agent harnesses, tmux/conpty runtime support, git-worktree isolation, native Chat drivers, and GitHub SCM observation. AO reuses installed provider binaries and their existing authentication.
GitHub PR observation is shipped. The broader tracker lane is still incomplete, so historical claims that every tracker plugin participates in lifecycle automation should not be treated as current behavior.
Pull Requests And Lifecycle Reactions
The SCM observer polls GitHub using lazy authentication, ETag guards, and semantic diffing. It writes pull-request facts to SQLite and notifies lifecycle logic when CI fails, reviews request changes, or merge conflicts appear.
The daemon also exposes pull-request merge and comment-resolution actions plus reviewer-agent routes. Desktop clients receive concise summaries rather than raw CI logs or complete review bodies.
Network Boundaries
The primary daemon listener binds 127.0.0.1:3001 and is unauthenticated. It serves the desktop app and CLI and is never configurable to a public bind.
Connect Mobile creates a second listener only when explicitly enabled. It binds the LAN interface behind bearer-password authentication and serves the app API and authenticated terminal mux, but excludes shutdown, telemetry, mobile-control, and browser-control routes. Transport is plaintext and intended only for a trusted home network.
Terminal And Browser Bridges
/mux provides per-client terminal attachment. TUI sessions attach to the agent runtime; Chat sessions can use it for a separate worktree shell.
Browser automation crosses a dedicated local daemon-to-Electron socket. The daemon authorizes and correlates commands; Electron owns the selected session's isolated WebContentsView. Browser control is blocked on the mobile LAN listener.
Load-Bearing Rules
- Never store display status; derive it from durable facts.
- Never treat failed probes as proof that a session died.
- Never force-delete dirty worktrees.
- Keep all app state under
~/.aounless an explicit supported override applies. - Keep the primary listener on loopback; only Connect Mobile may create the authenticated opt-in LAN listener.
- Keep the CLI and UI thin; domain logic belongs in the daemon.
- Emit durable change events through SQLite triggers and CDC.
- Add new migrations; never modify an already-merged migration.