Agentic orchestrator for parallel coding agents — plans tasks, spawns agents, and autonomously handles CI fixes, merge conflicts, and code reviews.
Go to file
Harshit Singh Bhandari 5435246c9a
feat(cli): add minimal ao send (#83)
* feat(messenger): ao send + live zellij pane ping (live agent nudges)

Replace the daemon's noopMessenger stub with a composite AgentMessenger
that writes a durable inbox file (primary) and types a live pointer into
the running zellij pane (best-effort secondary), plus the `ao send` CLI
that drives the existing POST /api/v1/sessions/{id}/send route.

- composite: fans Send to inbox then panep, pinning one timestamp so both
  derive the same filename; a secondary failure is logged at WARN and
  swallowed (the file is on disk), a primary failure aborts the call.
- inbox: writes <workspace>/.ao/inbox/<rfc3339nano>_<hash>.md.
- panep: types "new message at .ao/inbox/<file>" + Enter via a new narrow
  zellij WriteChars seam (RuntimePaneWriter), kept off ports.Runtime.
- wiring: newSessionMessenger composes inbox+panep over the shared store;
  startSession takes the messenger instead of the noop stub.

Carries across @aa-43's work from PR #74 (staging), adapted to main's
post-#65/#77 daemon wiring shape.

Closes #79

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(inbox): use O_EXCL so a filename collision errors instead of clobbering

os.WriteFile opens with O_CREATE|O_WRONLY|O_TRUNC, which silently overwrites
an existing file. The doc comment already stated the intent ("we do not retry
on EEXIST"), but O_TRUNC never yields EEXIST — two identical messages sent on
the same composite-pinned nanosecond would produce the same filename and the
second Send would silently lose the first message. Switch to
O_CREATE|O_EXCL|O_WRONLY so a collision surfaces as an error; O_EXCL also
refuses to follow a symlink at the final path component. Add a regression test.

Addresses greptile review on PR #83.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(inbox): remove the freshly-created file when write or close fails

The O_EXCL switch creates the inbox file before writing its body; if
WriteString or Close then fails, the empty/partial .md was left on disk and
the agent's next inbox scan would pick up a truncated ghost message. Remove
the file on those error paths. O_EXCL guarantees the file did not exist before
this call, so the cleanup can only delete our own partial write, never a
legitimate earlier message.

Addresses greptile review on PR #83.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(messenger): reduce ao send to live pane delivery

* fix(send): preserve messages and map lookup errors

* fix(send): reject terminated sessions
2026-06-02 20:02:47 +05:30
.github/workflows chore(backend): add golangci-lint with a strong ruleset and clear the tree 2026-06-01 04:58:41 +05:30
backend feat(cli): add minimal ao send (#83) 2026-06-02 20:02:47 +05:30
docs Add agent adapters and wire per-session agents into the session manager (#65) 2026-06-02 16:51:32 +05:30
frontend chore: scaffold backend/ and frontend/ skeletons for rewrite 2026-05-26 15:22:36 +05:30
test/cli refactor: simplify session lifecycle and zellij runtime (#62) 2026-06-01 08:42:49 +05:30
.envrc Add agent adapters and wire per-session agents into the session manager (#65) 2026-06-02 16:51:32 +05:30
.gitignore Add agent adapters and wire per-session agents into the session manager (#65) 2026-06-02 16:51:32 +05:30
README.md refactor: move session status assembly to service (#62) (#67) 2026-06-01 23:31:21 +05:30
flake.lock Add agent adapters and wire per-session agents into the session manager (#65) 2026-06-02 16:51:32 +05:30
flake.nix Add agent adapters and wire per-session agents into the session manager (#65) 2026-06-02 16:51:32 +05:30
package.json refactor: simplify session lifecycle and zellij runtime (#62) 2026-06-01 08:42:49 +05:30

README.md

agent-orchestrator

Rewrite of the agent-orchestrator: a long-running Go backend daemon (backend/)
paired with a placeholder Electron + TypeScript frontend shell (frontend/).

See docs/ for architecture and status — start with the
Lifecycle Manager + Session Service lane in docs/architecture.md.

Backend daemon

The Go backend now has a Cobra-based ao CLI in backend/cmd/ao.
The CLI controls the HTTP daemon — a loopback-only sidecar the Electron
supervisor will also use. The daemon skeleton includes the chi router,
middleware stack (recoverer → request-id → logger → real-ip), /healthz +
/readyz, atomic running.json PID/port handshake, graceful shutdown on
SIGINT/SIGTERM, SQLite storage, CDC polling, and lifecycle/reaper wiring.

Run

cd backend
go run ./cmd/ao start             # start the daemon and wait for readiness
go run ./cmd/ao status            # inspect PID/port/health/readiness
go run ./cmd/ao stop              # gracefully stop the daemon
go run ./cmd/ao daemon            # internal daemon entrypoint

go run .                          # compatibility wrapper; starts the daemon
AO_PORT=3019 go run ./cmd/ao start # override per invocation

Health check:

curl localhost:3001/healthz       # includes status/service/pid
curl localhost:3001/readyz        # includes status/service/pid

Configuration (env only)

The bind host is always 127.0.0.1: the daemon is a loopback-only sidecar
and binding any other interface would be a security regression, so the host
is intentionally not env-configurable.

Var Default Purpose
AO_PORT 3001 bind port; fails fast if taken
AO_REQUEST_TIMEOUT 60s per-request timeout (Go duration)
AO_SHUTDOWN_TIMEOUT 10s graceful-shutdown hard cap
AO_RUN_FILE <UserConfigDir>/agent-orchestrator/running.json PID + port handshake path
AO_DATA_DIR <UserConfigDir>/agent-orchestrator/data SQLite DB, WAL files, and managed state

Test

npm run lint
# optional deeper backend pass:
cd backend && go test -race ./...