* feat(plugin): add agents plugin (first iteration) Faithful copy of the agents plugin implementation from yyovil/better-ao (internal/plugin/ -> backend/internal/plugin/) plus its PRD (prds/plugins/agents/PRD.md), as a first-iteration proposal for review. Imports are left at their original github.com/yyovil/better-ao/... paths and are NOT yet reconciled to this repo's module; see PR description for the integration deltas (module path, missing internal/utils dependency). Co-authored-by: Claude <noreply@anthropic.com> * Move agent adapters under backend adapters * Keep daemon ports and session out of adapter move * Remove Better-AO naming from flake * Keep flake as dev shell only * Use goimports for local formatting * Wire session manager to per-session agent adapters Move the Agent port into internal/ports and have the claude-code and codex adapters implement it directly, alongside their workspace-local activity hooks and a manifest-keyed adapter registry. Rename RuntimeConfig.LaunchCommand to Argv and update the tmux and zellij runtimes to match. The session Manager now resolves a real agent adapter per session via a new ports.AgentResolver: from cfg.Harness on Spawn and the stored harness on Restore, so one daemon runs claude-code and codex sessions side by side. The daemon backs the resolver with the registry; AO_AGENT selects the default harness (default claude-code), validated at startup. Removes the temporary noopAgent stub. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(agent): point the agent contract at internal/ports/agent.go The Agent interface moved from internal/adapters/agent to internal/ports; update the PRD's Goal and Agent Contract sections (and the SessionInfo references) to match the code. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Wire the session service into the daemon daemon.Run now builds the controller-facing session service — a session manager over the zellij runtime, a gitworktree workspace, the shared store + LCM, and the per-session agent resolver (AO_AGENT default, validated at startup) — and mounts it at httpd APIDeps.Sessions, so the session REST routes are backed by a real service. startLifecycle moves ahead of the HTTP server so both share one LCM. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Address Greptile review: complete the live spawn path - Spawn and Restore now install workspace-local activity hooks (GetAgentHooks) and run the adapter's optional PreLaunch step before launch, via a shared prepareWorkspace helper. PreLaunch is how Claude Code records workspace trust, so its interactive "trust this folder?" dialog can't hang the headless pane; the spawned env now also carries AO_DATA_DIR so the installed hook commands find the store. - claudecode and codex hook/config writes are now atomic (temp + rename) instead of os.WriteFile, so a crash mid-write can't leave a partial file the agent fails to parse. - ensureWorkspaceTrusted serializes its read-modify-write under a package mutex, so concurrent spawns to different workspaces don't drop each other's ~/.claude.json trust entries. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(ports): pin MetadataKeyAgentSessionID to domain.SessionMetadata json tag The equality between ports.MetadataKeyAgentSessionID and the json tag on domain.SessionMetadata.AgentSessionID is a hand-maintained invariant; this test fails loudly if either side drifts. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(adapters): use ports.MetadataKeyAgentSessionID in claudecode + codex The native session id metadata key is defined in ports for cross-package consumption; drop the duplicated literals in each adapter so the constant has one home. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(codex): cover ensureCodexHooksFeatureEnabled TOML edge cases The helper is a string editor over config.toml; pin its content transformation for missing/empty files, existing [features] blocks, the no-op case, and the legacy codex_hooks=true migration paths. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs(adapters): document Registry concurrency contract Registry registration runs at daemon boot before any goroutine calls Get, so the underlying map needs no lock; pin that contract in the doc comment so a future change doesn't quietly introduce a race. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * style(codex): gofmt codex_test.go after constant rename The previous commit (7c5b2a9) replaced codexAgentSessionIDMetadataKey with ports.MetadataKeyAgentSessionID inside a map literal; the longer key threw off gofmt's column alignment on the adjacent codexTitleMetadataKey / codexSummaryMetadataKey lines. Caught by agent-ci's Check formatting step. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: harshitsinghbhandari <dev@theharshitsingh.com> |
||
|---|---|---|
| .github/workflows | ||
| backend | ||
| docs | ||
| frontend | ||
| test/cli | ||
| .envrc | ||
| .gitignore | ||
| README.md | ||
| flake.lock | ||
| flake.nix | ||
| package.json | ||
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 ./...