* feat(tracker): GitHub issue intake observer Adds an opt-in daemon poll loop that spawns one worker session per eligible open GitHub issue, keyed by the canonical "github:<native>" id so restarts cannot double-spawn. Eligibility requires an explicit label or assignee rule to avoid draining an entire backlog. Provider dispatch goes through a TrackerResolver interface (SingleTrackerResolver for now) so Linear/Jira can be added later as new adapters plus a resolver-map entry, without touching the poll, eligibility, or backoff logic. Reuses the existing rate-limit-aware GitHub tracker adapter and backs off per-project on any poll failure (including rate limits) instead of retrying in a tight loop. Closes #2324. * feat(frontend): surface GitHub tracker intake in project settings Adds a Tracker Intake card to project settings (enable, repository override, labels, assignee) with inline validation requiring at least one label or assignee before intake can be saved. Session cards and the inspector show the canonical "github:<native>" issue id when a session was spawned by intake. Regenerated frontend/src/api/schema.ts against the backend's TrackerIntakeConfig. The provider is currently fixed to "github"; adding a picker for future providers is additive once the backend enum grows. Closes #2324. * fix(tracker): start the intake observer unconditionally at boot startTrackerIntake scanned projects once at daemon startup and skipped starting the observer loop entirely when none had intake enabled yet. Poll() already re-reads every project's config on each tick and skips disabled projects there, so the boot-time gate only broke the common case: enabling intake on a project after the daemon is already running silently never got picked up until the next restart, with no error or log line to explain why. Always start the loop; the adapter (and its token resolution) stays lazy regardless, so there's no added cost when intake is unused. Found while manually verifying issue intake end-to-end against a live dev daemon: enabled intake via the settings UI, saved successfully, but no session ever spawned for a matching labeled issue. * fix(frontend): show auto-detected repo link, hide labels input for now The Electron app only registers git projects today, so the daemon always has a usable git origin to derive owner/repo from when trackerIntake.repo is unset (trackerRepo() in observer.go, already covered by every Poll-level test in observer_test.go). Replace the manual Repository input with a read-only link derived client-side from the project's own git origin, purely for display — the daemon's own derivation at poll time is unaffected either way. Comment out the Labels input; Assignee is the only intake eligibility rule editable from this form for now. form.intakeLabels/intakeRepo stay wired into buildIntake so a value set via the CLI round-trips on a UI save instead of being silently cleared. * chore: format with prettier [skip ci] * feat(frontend): offer issue intake at project creation Add the GitHub tracker intake controls to the create-project sheet so a project can opt into issue-driven worker spawning at creation time, not only later via Settings. - Extract the shared IntakeFields component (enable toggle, assignee input, validation, credential hint) plus buildIntake/intakeNeedsRule/ deriveGitHubRepo helpers into IntakeFields.tsx, and consume it from ProjectSettingsForm so the two surfaces can't drift. - CreateProjectAgentSheet renders IntakeFields (no repo-preview row, since the git origin isn't known there; the daemon derives the repo). The selection now carries an optional trackerIntake payload, gated by the same "requires a label or assignee" rule the backend enforces. - Thread trackerIntake through Sidebar's onCreateProject into the POST /api/v1/projects config. No backend change: the endpoint already accepts a full ProjectConfig and the intake observer picks up newly enabled projects on its next tick. Verified in the web preview: enabling reveals the assignee field and gates submit until a rule is set; submit builds the intake payload. * chore: format with prettier [skip ci] * feat(frontend): simplify intake controls in the create-project sheet Reduce the create sheet's tracker-intake block to the essentials: the enable toggle plus an info icon whose hover tooltip explains what enabling does, then the assignee input. Drop the descriptive intro paragraph, the inline "requires a label or assignee" guard text, and the credential hint — the sheet stays minimal and submit gating already communicates the missing rule via the disabled button. - IntakeFields gains a `compact` prop: hides the prose, folds the explanation into an Info tooltip, and drops the trailing help text. The tooltip is wrapped in its own TooltipProvider so the component is self-contained regardless of ancestor. The verbose settings card is unchanged (renders without `compact`). - Assignee placeholder reworded to "type username or * for any". Verified in the web preview: the info tooltip surfaces the one-line description on hover, the assignee placeholder is updated, and no other copy remains in the create sheet. * perf(tracker): cache GitHub issue lists with ETag conditional requests The intake observer polls Tracker.List for the same repo+filter every tick, and each poll did an uncached GET + full JSON decode even when nothing changed. Add HTTP conditional requests so unchanged polls are cheap and don't consume the primary rate limit. - Tracker holds a per-request-path cache of {etag, mapped issues}, guarded by a mutex. The key space is bounded by intake-enabled repo/filter pairs, so no eviction is needed. - List sends If-None-Match with the cached ETag (verbatim, preserving weak validators). On 304 it returns the cached issues without re-decoding (GitHub 304s don't count against the primary rate limit); a rotated ETag on the 304 is recorded. On 200 it stores the new {etag, issues}, or drops a stale entry if the response omits an ETag. A 304 without a prior validator falls back to an unconditional refetch. - Extract the HTTP round-trip into roundTrip(); do() delegates to it so Get and Preflight keep byte-for-byte identical behavior. roundTrip owns If-None-Match, ETag capture, and 304 handling before classifyError. Tests cover revalidation returning cached issues on 304, ETag rotation on change, separate cache keys per filter, and no caching when the response carries no ETag. * feat(frontend): trim tracker intake copy in project settings Reduce the settings Tracker Intake card to a one-line description ("Auto-spawn worker sessions from matching tracker issues.") and drop the trailing credential/daemon-restart hint. The compact create sheet is unaffected (it already renders neither). * feat(tracker): scope v1 intake to assignee-only Per PR review, labels were a persisted/API/CLI eligibility rule while the UI only ever exposed assignee — surface beyond the intended v1 scope. Remove labels from intake end-to-end; assignee becomes the required and only eligibility rule. - domain: drop TrackerIntakeConfig.Labels; Validate() now requires a non-empty assignee when enabled (was "labels or assignee"). - observer: pass only State+Assignee into ListFilter; drop the label match loop in issueMatchesConfig. - CLI: remove --tracker-label and its plumbing. - Regenerate openapi.yaml + schema.ts (labels gone from the schema). - frontend: drop labels from IntakeForm/buildIntake/intakeNeedsRule and the settings form state; validation copy now "requires an assignee". Remove the label round-trip test; keep assignee coverage. The generic domain.ListFilter.Labels adapter capability is retained; only intake stops using it. * fix(tracker): paginate GitHub issue listing with page-aware ETag cache Per PR review, single-page listing is a correctness bug for intake, not just a bounded v1 tradeoff: with more than a page of eligible open issues, AO re-sees the same first page every poll, the persisted issue_id dedupe skips the already-spawned first-page issues, and later pages are never fetched or spawned. - List now requests per_page=100 and follows the GitHub Link header rel="next" until no next link remains, accumulating issues across all pages. A maxListPages guard fails loud on a pathological Link cycle. - The ETag cache is page-aware: each entry stores {etag, issues, nextPath} keyed by the per-page request path, so a 304 Not Modified still continues to the cached next page. roundTrip now surfaces the parsed next path alongside the ETag; do() delegates unchanged so Get/Preflight keep identical behavior. - ListFilter.Limit becomes an optional total-result cap (page size is fixed at the provider max). Tests cover multi-page accumulation, all-304 revalidation continuing the cached chain, page-count shrink orphaning a stale entry, and Link header parsing. * style(session_manager): gofmt manager_test.go to unblock CI The session-restart test carried a struct-literal alignment that gofmt (and golangci-lint's goimports) reject, failing the build-test and lint jobs. Whitespace-only reformat; no test logic changes. --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: whoisasx <adil.business4064@gmail.com> |
||
|---|---|---|
| .github | ||
| backend | ||
| docs | ||
| frontend | ||
| packages | ||
| screenshots | ||
| scripts | ||
| skills/bug-triage | ||
| test/cli | ||
| .envrc | ||
| .gitignore | ||
| .prettierignore | ||
| .prettierrc | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| CONTRIBUTING.md | ||
| DESIGN.md | ||
| LICENSE | ||
| README.md | ||
| ao-dashboard-preview.png | ||
| ao-logo.svg | ||
| flake.lock | ||
| flake.nix | ||
| package-lock.json | ||
| package.json | ||
README.md
Agent Orchestrator
The orchestration layer for parallel AI coding agents
An Agentic IDE that supervises parallel AI coding agents in isolated workspaces, with complete control and automatic feedback loops from CI failures, review comments, and merge conflicts.
What is Agent Orchestrator?
Agent Orchestrator is a meta-harness agent IDE for running AI coding agents in parallel. It gives terminal-based agents like Claude Code, Codex, Cursor, Aider, Goose, and others a shared workspace where their sessions, terminals, branches, pull requests, and feedback loops can be supervised from one place.
The agents still do the coding. AO provides the harness around them: isolated workspaces, live terminal access, session state, PR awareness, and automatic loops that send CI failures, review comments, and merge conflicts back to the right agent. Instead of manually coordinating a pile of agent terminals, AO turns parallel agent work into a managed workflow.
Why Agent Orchestrator?
AI coding agents become much more useful when they can work in parallel, but parallel work gets messy quickly. Branches overlap, terminals get lost, CI failures need follow-up, review comments need replies, and merge conflicts have to reach the right worker.
Agent Orchestrator is built to keep that loop visible and manageable. It helps you:
- Start multiple agents from the same project without mixing their work
- Keep every session in a separate git worktree
- See which agents are working, waiting, finished, or blocked
- Route CI failures, review comments, and merge conflicts back to the right session
- Use different agent CLIs through one common supervisor
How it works
At a high level, Agent Orchestrator follows a simple loop:
- Add a project you want agents to work on.
- Start one or more sessions from the desktop app or CLI.
- AO creates an isolated git worktree for each session.
- AO launches the selected coding agent in that session's terminal runtime.
- The local daemon watches session state, terminal activity, pull requests, CI, and review feedback.
- The desktop app and CLI show the current state and let you send follow-up instructions to the right session.
The result is a local control layer for agentic coding: agents still do the coding, while Agent Orchestrator keeps their workspaces, status, terminals, and feedback loops organized.
Witness AO's Journey on X
![]() Visit |
![]() Visit |
![]() Visit |
![]() Visit |
What is Agent Orchestrator? • Why Agent Orchestrator? • How it works • Features • Quick Start • Architecture • Documentation • Contributing
Features
| Feature | Description |
|---|---|
| Agent-Agnostic Platform | 23+ agent adapters including Claude Code, OpenAI Codex, Cursor, OpenCode, Aider, Amp, Goose, GitHub Copilot, Grok, Qwen Code, Kimi Code, Cline, Continue, Kiro, and more |
| Isolated Workspaces | Each session spawns into its own git worktree with dedicated runtime |
| Platform-Native Runtimes | tmux on Darwin/Linux, conpty on Windows for optimal performance |
| Live PR Observation | Provider-neutral SCM observer with automatic feedback routing |
| Automatic Feedback Routing | CI failures, review comments, and merge conflicts routed to the owning agent |
| Durable Facts Storage | SQLite persists immutable facts with display status derived at read time |
| CDC Broadcasting | DB triggers append changes to change_log, broadcasted via SSE |
| Desktop Experience | Native Electron app with React UI and live terminal streaming |
| Loopback-Only Daemon | HTTP control over 127.0.0.1 with no auth, CORS, or TLS by design |
Supported Agents
Works with 23+ CLI-based coding agents including Claude Code, OpenAI Codex, Cursor, OpenCode, Aider, Amp, Goose, GitHub Copilot, Grok, Qwen Code, Kimi Code, Crush, Cline, Droid, Devin, Auggie, Continue, Kiro, and Kilo Code.
If it runs in a terminal, it runs on Agent Orchestrator.
Quick Start
Prerequisites
| Requirement | Minimum | Recommended |
|---|---|---|
| Go | 1.25+ | Latest |
| Node.js | 20+ | Latest LTS |
| Git | Any | Latest |
| pnpm | Any | Latest |
Optional:
tmux(Darwin/Linux) - For Unix runtimegh(GitHub CLI) - For authenticated GitHub API calls
Installation
Download the latest release for your platform:
| Platform | Download |
|---|---|
| Windows | Setup.exe |
| macOS | Agent Orchestrator.dmg |
| Linux | Agent Orchestrator.AppImage |
Direct Download: Latest Release
Telemetry
Agent Orchestrator collects minimal telemetry for reliability and product understanding. Data is stored locally by default; remote transmission is opt-in via environment variables. Read the full telemetry policy.
Architecture
Agent Orchestrator is a long-running Go daemon built around inbound/outbound port contracts with swappable adapters.
Core mental model: OBSERVE external facts → UPDATE durable facts → DERIVE display status / ACT
Key components:
- Frontend - Electron + React UI with TanStack Router/Query and shadcn/ui
- Backend Daemon - Go-based HTTP server with controllers, services, and adapters
- Runtime - Platform-specific:
tmuxon Darwin/Linux,conptyon Windows - Storage - SQLite with change-data-capture (CDC) for real-time updates
- Adapters - 23+ agent adapters, git worktree workspace, GitHub SCM integration
For detailed architecture diagrams, data flows, and load-bearing rules, see architecture.md.
Documentation
| Document | Description |
|---|---|
| Architecture | System architecture, data flows, and load-bearing rules |
| Backend Code Structure | Package-by-package ownership and dependency rules |
| AGENTS.md | Contributor and worker-agent contract |
| Agent Adapter Contract | Agent adapter interface and hook behavior |
Testing
# Backend tests
cd backend
go test -race ./...
# Frontend tests
cd frontend
pnpm test
# Full CI validation locally
npx @redwoodjs/agent-ci run --all
Configuration
All configuration is environment-driven. The daemon takes no config file.
| Variable | Default | Purpose |
|---|---|---|
AO_PORT |
3001 |
HTTP bind port |
AO_REQUEST_TIMEOUT |
60s |
Per-request timeout |
AO_SHUTDOWN_TIMEOUT |
10s |
Graceful shutdown cap |
AO_RUN_FILE |
~/.ao/running.json |
PID/port handshake |
AO_DATA_DIR |
~/.ao/data |
SQLite data directory |
AO_AGENT |
claude-code |
Compatibility agent adapter |
GITHUB_TOKEN |
- | GitHub auth token |
Health Checks
curl localhost:3001/healthz # Liveness probe
curl localhost:3001/readyz # Readiness probe
Contributing
We love contributions! Join our community on Discord to get started.
Join us on Discord
Daily contributor sync: Every day at 10:00 PM IST
Get your issues verified by core contributors, ask questions, share progress, and learn from the community. New contributors are always welcome!
Why join Discord?
- Get your issues and PRs verified by core contributors before investing time
- Learn from experienced contributors in daily sync calls
- Share your progress and get feedback
- Get help troubleshooting in real-time
- Stay updated on the latest developments and roadmap
Quick Start
- Join the Discord - Connect with the community and get guidance
- Read the contributor contract - See AGENTS.md for repo layout, daemon/API boundaries, and coding conventions
- Pick a focused problem - Browse open issues and choose one small enough for a focused PR
- Open a clear PR - Keep changes narrow, explain user-visible impact, link issues, include tests
- Iterate with contributors - Use review feedback to tighten the PR until verified
License
Apache License 2.0 - see LICENSE for details.
Star us on GitHub • Report Issues • Discussions
Made with love by the Agent Orchestrator community




