Agentic orchestrator for parallel coding agents — plans tasks, spawns agents, and autonomously handles CI fixes, merge conflicts, and code reviews.
Go to file
Varich ecdf0c73ec
feat(cli): support AO_PUBLIC_URL for reverse-proxied dashboards (#1757)
* feat(cli): support AO_PUBLIC_URL for reverse-proxied dashboards

When AO runs inside a remote dev container or behind a reverse proxy
(Caddy/nginx/Traefik), `http://localhost:${port}` was hardcoded across
the CLI for console output, `ao open` browser launches, and the
session URLs surfaced to the orchestrator agent. None of those URLs
were reachable from outside the host.

Add an `AO_PUBLIC_URL` env var. When set, the new `dashboardUrl(port)`
helper returns it (with trailing slashes stripped) instead of the
localhost fallback. The helper replaces every user-facing
`http://localhost:${port}` literal in:

- `commands/dashboard.ts` — startup banner + browser open
- `commands/start.ts` — 12 spots: spinner, "Dashboard:" prints,
  orchestrator URL fallback, `openUrl()` calls, and the running-state
  reuse paths
- `lib/routes.ts` — `projectSessionUrl()` (used in the orchestrator
  prompt template, so worker links land on the public hostname)

Internal IPC (`lib/daemon.ts` calling its own dashboard's
`/api/projects/reload`) is intentionally left on localhost — that
traffic never leaves the host, and routing it through a public URL
would just add latency and a failure surface.

Tests cover the env-var/localhost paths, whitespace trimming,
trailing-slash stripping, sub-path preservation, and non-default-port
URLs (`__tests__/lib/dashboard-url.test.ts`, 10 cases).

Setup guide gets a new "Public dashboard URL" entry under optional
env vars.

* docs: cover TERMINAL_WS_PATH + path-based mux routing in AO_PUBLIC_URL setup

The AO_PUBLIC_URL entry only mentioned terminal ports needing to be
reachable, which over-specifies what's required when fronting AO with
HTTPS through a reverse proxy. The dashboard's MuxProvider already
auto-detects standard ports (`loc.port === ""`/`"443"`/`"80"`) and
routes the mux WebSocket through `/ao-terminal-mux` on the same
hostname, so a single proxy rule pointing at the dashboard port is
sufficient — no extra subdomain or port forwarding for the WS.

For non-standard ports or custom paths, document the existing but
previously-undiscoverable `TERMINAL_WS_PATH` env var (read by
`/api/runtime/terminal/route.ts` and threaded through `MuxProvider`
as `proxyWsPath`).

Adds a minimal Caddy snippet so users have a working starting point.

* feat(web): accept /ao-terminal-mux as alias for /mux on direct-terminal-ws

The dashboard's MuxProvider already constructs `wss://hostname/ao-terminal-mux`
when accessed on a standard HTTPS port (443), but until now nothing on the
server side recognized that path — direct-terminal-ws only matched `/mux`,
and the Next.js dashboard doesn't handle WS upgrades at all. Deployments
fronted by a path-routing reverse proxy (cloudflared, nginx, Caddy, …) hit
the server at `/ao-terminal-mux`, fall through to Next.js, get a 404, and
the dashboard's terminal panes hang at "Connecting…" forever.

Fix is one line in the upgrade-routing allow-list: accept `/ao-terminal-mux`
in addition to `/mux`. The proxy can now route the path-based mux URL straight
at DIRECT_TERMINAL_PORT without needing a path-rewrite rule (which most
proxies — including cloudflared — don't natively support).

Existing `/mux` clients continue to work; the alias is strictly additive.
SETUP.md's AO_PUBLIC_URL section is updated to mention the path requirement
in one sentence, and a new integration test pins the behavior.

* feat(web): opt-in single-port mode (AO_PATH_BASED_MUX) for proxy-only deployments

Default behavior unchanged. When AO_PATH_BASED_MUX=1, start-all spawns a
small bundled HTTP/WS proxy on PORT that demultiplexes:

  - HTTP requests forwarded to Next.js (shifted to PORT + 1000;
    override with NEXT_INTERNAL_PORT)
  - `wss://hostname/ao-terminal-mux` upgrades tunneled to
    DIRECT_TERMINAL_PORT/mux

Use it when the reverse proxy in front of AO can only forward one
hostname:port pair upstream (e.g. Cloudflare Tunnel pointed at a single
`service:` URL with no path-based ingress, or a managed-app platform
where you don't control the proxy config). One proxy rule then
suffices — the WS path is multiplexed onto the same TCP port and
demuxed inside the AO process.

Tradeoff: one extra Node process and one extra hop per HTTP request,
in exchange for proxy-config simplicity. For deployments that *can*
do path-based routing the alias added in the previous commit
(direct-terminal-ws accepting `/ao-terminal-mux` on its own port) is
the lower-overhead path.

The new server is pure Node http; no `next` import or other extra
dependencies. It's strictly opt-in — the env-var gate keeps the code
inert by default, so existing deployments see no behavior change and
no extra startup cost.

* fix(web): correct single-port proxy header handling, WS hangs, shutdown

Addresses review feedback on single-port-server.ts:

- Strip hop-by-hop headers (RFC 9110 §7.6.1) before forwarding upstream,
  including any extras named in the client Connection header. Previously
  the whole header set was copied verbatim, so a client Connection: close
  could tear down the keep-alive socket to Next.js.
- Add X-Forwarded-For/-Proto/-Host so the upstream sees the real client
  instead of 127.0.0.1; existing values from an outer proxy are preserved.
- Handle non-101 upstream responses on the WS upgrade path. The proxy only
  listened for 'upgrade', so a 404/502/mid-restart response left the client
  socket hanging until TCP timeout. A 'response' handler now relays the
  status and closes the connection.
- Call server.closeAllConnections() on shutdown. server.close() alone waits
  for keep-alive HTTP sockets and piped WS tunnels to drain on their own,
  which they never do, so shutdown always hit the 5s force-exit timer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(web): cover single-port proxy + fix response-direction headers

Follow-up to the previous commit's review fixes, adding regression
coverage so future changes can't silently break the proxy.

- Refactor single-port-server.ts into an exported createSinglePortServer()
  factory (mirrors direct-terminal-ws.ts) with a thin isMainModule()
  entrypoint, so start-all.ts still spawns it as a script while tests can
  drive it in-process against fake upstreams.
- Add single-port-server.integration.test.ts (5 tests, no tmux/Next.js
  needed — runs on CI/Windows): hop-by-hop strip + X-Forwarded-*, 502 on
  dead upstream, /ao-terminal-mux WS tunnel, non-101 upgrade relay, and
  prompt shutdown with a live WS connection.
- The shutdown test caught that server.closeAllConnections() does NOT
  destroy sockets already handed off via the 'upgrade' event — track
  upgraded sockets explicitly and destroy them in shutdown().
- The header test caught the symmetric response-direction leak: the proxy
  forwarded the upstream's Connection/Keep-Alive to the client, overriding
  a client that asked for Connection: close. Strip hop-by-hop from upstream
  responses too via filterResponseHeaders().

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Priyanshu Choudhary <57816400+Priyanchew@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 23:09:04 +05:30
.changeset feat(cli): support AO_PUBLIC_URL for reverse-proxied dashboards (#1757) 2026-05-20 23:09:04 +05:30
.cursor feat(windows): complete Windows support (#1025) 2026-05-09 00:10:53 +05:30
.github fix(canary): list all 27 linked packages in dummy changeset (#1882) 2026-05-20 04:14:44 +05:30
.husky feat: implement comprehensive security audit and secret leak prevention (#67) 2026-02-16 10:33:50 +05:30
.issue-assets feat(notifier): make notifier system robust with manual harness and desktop setup (#1736) 2026-05-19 14:48:40 +05:30
artifacts chore: rename @composio scope to @aoagents across all packages 2026-04-09 15:59:33 +00:00
changelog chore: rename @composio scope to @aoagents across all packages 2026-04-09 15:59:33 +00:00
completions add zsh completion support for ao (#1374) 2026-04-24 03:48:25 +05:30
docs feat(notifier): make notifier system robust with manual harness and desktop setup (#1736) 2026-05-19 14:48:40 +05:30
examples feat: add $schema support to agent-orchestrator.yaml (#1373) 2026-04-26 19:42:45 +05:30
handoff/pr-1466 refactor(core): storage redesign — projectId-based paths, JSON metadata (#1466) 2026-04-28 17:55:53 +05:30
openclaw-plugin refactor: remove --decompose feature entirely 2026-04-10 13:02:52 +05:30
packages feat(cli): support AO_PUBLIC_URL for reverse-proxied dashboards (#1757) 2026-05-20 23:09:04 +05:30
schema feat(notifier): make notifier system robust with manual harness and desktop setup (#1736) 2026-05-19 14:48:40 +05:30
scripts feat(release): weekly release train — channels, onboarding, dashboard banner, cron (#1781) 2026-05-12 23:11:09 +05:30
skills chore: add bug-triage skill for agent-driven issue triage (#1725) 2026-05-12 20:51:00 +05:30
tests/integration chore(cli): remove deprecated 'ao init' command (#1438) 2026-05-05 18:43:55 +05:30
website feat(notifier): make notifier system robust with manual harness and desktop setup (#1736) 2026-05-19 14:48:40 +05:30
.eslintignore fix: added GraphQL batch PRfeat: add GraphQL batch PR enrichment for orchestrator polling enrichment for orchestrator polling (fixes #608) (#637) 2026-03-30 00:31:04 +05:30
.gitignore feat(windows): complete Windows support (#1025) 2026-05-09 00:10:53 +05:30
.gitleaks.toml refactor(core): storage redesign — projectId-based paths, JSON metadata (#1466) 2026-04-28 17:55:53 +05:30
.npmrc feat: publish to npm under @composio scope (#32) 2026-02-15 04:28:57 +05:30
.prettierignore chore: add ESLint, Prettier, CI workflow, and comprehensive CLAUDE.md conventions 2026-02-13 18:01:52 +05:30
.prettierrc chore: add ESLint, Prettier, CI workflow, and comprehensive CLAUDE.md conventions 2026-02-13 18:01:52 +05:30
AGENTS.md chore: add bug-triage skill for agent-driven issue triage (#1725) 2026-05-12 20:51:00 +05:30
ARCHITECTURE.md feat(windows): complete Windows support (#1025) 2026-05-09 00:10:53 +05:30
CLAUDE.md fix(core): keep actionable activity sticky (#1902) 2026-05-17 20:03:56 +05:30
CONTRIBUTING.md refactor(release): split publish into two-repo model — GitHub release public, npm publish private (#1815) 2026-05-13 03:29:04 +05:30
DESIGN.md refactor(core): storage redesign — projectId-based paths, JSON metadata (#1466) 2026-04-28 17:55:53 +05:30
LICENSE feat: publish to npm under @composio scope (#32) 2026-02-15 04:28:57 +05:30
README.md feat(release): weekly release train — channels, onboarding, dashboard banner, cron (#1781) 2026-05-12 23:11:09 +05:30
SECURITY.md fix: migrate to hash-based project isolation architecture 2026-02-18 00:19:55 +05:30
SETUP.md feat(cli): support AO_PUBLIC_URL for reverse-proxied dashboards (#1757) 2026-05-20 23:09:04 +05:30
TROUBLESHOOTING.md fix: migrate to hash-based project isolation architecture 2026-02-18 00:19:55 +05:30
agent-orchestrator.yaml.example feat(windows): complete Windows support (#1025) 2026-05-09 00:10:53 +05:30
eslint.config.js fix(cli): reap daemon children on stop+SIGINT, sweep orphans on start (closes #1848) (#1849) 2026-05-15 03:38:09 +05:30
package.json fix(deps): bump vulnerable dependencies so pnpm audit passes cleanly (#1338) 2026-04-26 17:01:07 +05:30
pnpm-lock.yaml feat(notifier): make notifier system robust with manual harness and desktop setup (#1736) 2026-05-19 14:48:40 +05:30
pnpm-workspace.yaml chore: remove React Native/Expo mobile app 2026-03-25 12:00:59 +05:30
tsconfig.base.json fix: scope node types to node packages 2026-04-13 18:25:21 +05:30
tsconfig.node.json fix: scope node types to node packages 2026-04-13 18:25:21 +05:30

README.md

Agent Orchestrator — The Orchestration Layer for Parallel AI Agents

Agent Orchestrator banner

Spawn parallel AI coding agents, each in its own git worktree. Agents autonomously fix CI failures, address review comments, and open PRs — you supervise from one dashboard.

GitHub stars
npm version
License: MIT
PRs merged
Tests
Discord


Agent Orchestrator manages fleets of AI coding agents working in parallel on your codebase. Each agent gets its own git worktree, its own branch, and its own PR. When CI fails, the agent fixes it. When reviewers leave comments, the agent addresses them. You only get pulled in when human judgment is needed.

Agent-agnostic (Claude Code, Codex, Aider) · Runtime-agnostic (tmux, ConPTY/process, Docker) · Tracker-agnostic (GitHub, Linear)

See it in action

Agent Orchestrator demo — AI agents building their own orchestrator

Watch the Demo on X


The Self-Improving AI System That Built Itself

Read the Full Article on X

Quick Start

Prerequisites: Node.js 20+, Git 2.25+, gh CLI, and:

  • macOS / Linux: tmux — install via brew install tmux or sudo apt install tmux.
  • Windows: PowerShell 7+ recommended. tmux is not required — AO uses native ConPTY via the runtime-process plugin (the default on Windows). Set AO_SHELL=bash if you have Git Bash and prefer it.

Install

npm install -g @aoagents/ao

Nightly builds (latest main, daily FriTue): npm install -g @aoagents/ao@nightly
Back to stable: npm install -g @aoagents/ao@latest

Permission denied? Install from source?

If npm install -g fails with EACCES, prefix with sudo or fix your npm permissions.

To install from source (for contributors):

git clone https://github.com/ComposioHQ/agent-orchestrator.git
cd agent-orchestrator && bash scripts/setup.sh

Zsh Completion

Generate the completion file from the installed CLI:

mkdir -p ~/.zsh/completions
ao completion zsh > ~/.zsh/completions/_ao

Then make sure the directory is on your fpath before compinit runs:

fpath=(~/.zsh/completions $fpath)
autoload -Uz compinit
compinit

For Oh My Zsh, install the same generated file into a custom plugin directory and add ao to your plugin list:

mkdir -p "${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/ao"
ao completion zsh > "${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/ao/_ao"

If you are contributing from a source checkout, you can also symlink the repo copy at completions/_ao.

Start

Point it at any repo — it clones, configures, and launches the dashboard in one command:

ao start https://github.com/your-org/your-repo

Or from inside an existing local repo:

cd ~/your-project && ao start

That's it. The dashboard opens at http://localhost:3000 and the orchestrator agent starts managing your project.

Add more projects

ao start ~/path/to/another-repo

How It Works

  1. You startao start launches the dashboard and an orchestrator agent
  2. Orchestrator spawns workers — each issue gets its own agent in an isolated git worktree
  3. Agents work autonomously — they read code, write tests, create PRs
  4. Reactions handle feedback — CI failures and review comments are automatically routed back to the agent
  5. You review and merge — you only get pulled in when human judgment is needed

The orchestrator agent uses the AO CLI internally to manage sessions. You don't need to learn or use the CLI — the dashboard and orchestrator handle everything.

Configuration

ao start auto-generates agent-orchestrator.yaml with sensible defaults. You can edit it afterwards to customize behavior:

# agent-orchestrator.yaml
$schema: https://raw.githubusercontent.com/ComposioHQ/agent-orchestrator/main/schema/config.schema.json
# Runtime data is auto-derived under ~/.agent-orchestrator/{hash}-{projectId}/
port: 3000

defaults:
  runtime: tmux       # default on macOS / Linux; on Windows the default is `process` (ConPTY)
  agent: claude-code
  workspace: worktree
  notifiers: [desktop]

projects:
  my-app:
    repo: owner/my-app
    path: ~/my-app
    defaultBranch: main
    sessionPrefix: app

reactions:
  ci-failed:
    auto: true
    action: send-to-agent
    retries: 2
  changes-requested:
    auto: true
    action: send-to-agent
    escalateAfter: 30m
  approved-and-green:
    auto: false # flip to true for auto-merge
    action: notify

CI fails → agent gets the logs and fixes it. Reviewer requests changes → agent addresses them. PR approved with green CI → you get a notification to merge.

Keep the $schema line so editors can autocomplete and validate against schema/config.schema.json.

See agent-orchestrator.yaml.example for the full reference, or run ao config-help for the complete schema.

Remote Access

AO keeps your Mac awake while running, so you can access the dashboard remotely (e.g., via Tailscale from your phone) without the machine going to sleep.

How it works: On macOS, AO automatically holds an idle-sleep prevention assertion using caffeinate. When AO exits, the assertion is released.

# agent-orchestrator.yaml
$schema: https://raw.githubusercontent.com/ComposioHQ/agent-orchestrator/main/schema/config.schema.json
power:
  preventIdleSleep: true  # Default on macOS; no-op on Linux and Windows

Set to false if you want to allow idle sleep while AO runs.

Lid-close limitation: macOS enforces lid-close sleep at the hardware level — no userspace assertion can override it. If you need remote access while traveling with the lid closed, use clamshell mode (external power + display + input device).

Linux / Windows: AO does not currently hold a wake assertion on these platforms. On Linux, idle-sleep behaviour is governed by your desktop environment / systemd-logind; configure that directly. On Windows, set the OS power plan if remote access matters while idle.

Plugin Architecture

Seven plugin slots. Lifecycle stays in core.

Slot Default Alternatives
Runtime tmux (macOS/Linux) / process (Windows) process, docker
Agent claude-code codex, aider, cursor, opencode, kimicode
Workspace worktree clone
Tracker github linear, gitlab
SCM github gitlab
Notifier desktop slack, discord, composio, webhook, openclaw
Terminal iterm2 web

All interfaces defined in packages/core/src/types.ts. A plugin implements one interface and exports a PluginModule. That's it.

Why Agent Orchestrator?

Running one AI agent in a terminal is easy. Running 30 across different issues, branches, and PRs is a coordination problem.

Without orchestration, you manually: create branches, start agents, check if they're stuck, read CI failures, forward review comments, track which PRs are ready to merge, clean up when done.

With Agent Orchestrator, you: ao start and walk away. The system handles isolation, feedback routing, and status tracking. You review PRs and make decisions — the rest is automated.

Documentation

Doc What it covers
Setup Guide Detailed installation, configuration, and troubleshooting
CLI Reference All ao commands (mostly used by the orchestrator agent)
Examples Config templates (GitHub, Linear, multi-project, auto-merge)
Development Guide Architecture, conventions, plugin pattern
Contributing How to contribute, build plugins, PR process

Development

pnpm install && pnpm build    # Install and build all packages
pnpm test                      # Run tests (3,288 test cases)
pnpm dev                       # Start web dashboard dev server

See docs/DEVELOPMENT.md for code conventions and architecture details.

Contributing

Contributions welcome. The plugin system makes it straightforward to add support for new agents, runtimes, trackers, and notification channels. Every plugin is an implementation of a TypeScript interface — see CONTRIBUTING.md and the Development Guide for the pattern.

License

MIT