The reaper sits OUTSIDE the LCM's per-session serial loop. On every tick it: 1. Fires lcm.TickEscalations(now) — the duration-based escalation heartbeat a non-polling LCM cannot wake itself to drive. 2. Asks lcm.RunningSessions for the snapshot of sessions whose runtime axis is alive, then calls runtime.IsAlive(handle) per session via a RuntimeRegistry that dispatches by RuntimeHandle.RuntimeName (so a single reaper covers tmux + zellij side by side). 3. Reports any non-alive result back as a fact via ApplyRuntimeObservation — dead -> RuntimeProbeDead, probe error -> RuntimeProbeFailed (never collapsed to alive: failed probe ≠ dead, but it ≠ alive either). Steady- state alive is skipped so we don't churn the LCM with no-op load/diff work. The reaper REPORTS facts; the LCM owns DECIDE (anti-flap Detecting quarantine, terminal-session rules). The reaper never writes. Open-question resolution: add RunningSessions(ctx) to ports.LifecycleManager (option a). The Manager implements it via an injectable session lister (Manager.WithSessionLister) so the LCM itself does not require a new LifecycleStore method — Tom's store contract is untouched, daemon wiring (lane #10) will inject the production lister at startup. Scope: reaper goroutine + the minimum LCM seam. No activity ingest, no FS watcher, no daemon wiring, no new schema fields, no store changes. |
||
|---|---|---|
| .github/workflows | ||
| backend | ||
| docs | ||
| frontend | ||
| .gitignore | ||
| README.md | ||
README.md
agent-orchestrator
Rewrite of the agent-orchestrator: a long-running Go backend daemon (backend/)
paired with an Electron + TypeScript frontend (frontend/).
See docs/ for architecture and status — start with the
Lifecycle Manager + Session Manager lane in docs/architecture.md.
Backend daemon
The Go binary in backend/ is the HTTP daemon — a loopback-only
sidecar the Electron supervisor will spawn (Phase 1c). Phase 1a landed the
skeleton: chi router, middleware stack (recoverer → request-id → logger →
real-ip), /healthz + /readyz, atomic running.json PID/port handshake,
graceful shutdown on SIGINT/SIGTERM.
Run
cd backend
go run . # binds 127.0.0.1:3001 with all defaults
AO_PORT=3019 go run . # override per invocation
Health check:
curl localhost:3001/healthz # {"status":"ok"}
curl localhost:3001/readyz # {"status":"ready"}
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 |
Test
cd backend
gofmt -l . && go build ./... && go vet ./... && go test -race ./...