docs: refresh repository readme [skip ci] (#2190)

This commit is contained in:
Vaibhaav Tiwari 2026-06-26 17:28:07 +05:30 committed by GitHub
parent 8bbc4c94fe
commit 3efcd497de
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 2901 additions and 660 deletions

356
README.md
View File

@ -1,221 +1,223 @@
# ReverbCode <div align="center">
The orchestration layer for parallel AI coding agents. ReverbCode is a <p align="center">
Go-backed daemon that supervises many coding-agent sessions at once, each in <img src="ao-logo.svg" alt="Agent Orchestrator" width="200" height="200" />
its own `git worktree`, and routes the feedback they need (CI failures, review </p>
comments, merge conflicts) back to the right agent automatically. It ships with
an `ao` CLI and an Electron supervisor that both drive the same daemon over
loopback.
The Go module and packages remain `agent-orchestrator`; "ReverbCode" is the <h1 align="center">Agent Orchestrator</h1>
public name.
See [`docs/architecture.md`](docs/architecture.md) for the backend mental model <p align="center"><strong>The orchestration layer for parallel AI coding agents</strong></p>
and [`AGENTS.md`](AGENTS.md) for the contributor / worker contract. For current
progress (what's shipped vs. in flight) see [`docs/STATUS.md`](docs/STATUS.md).
## What it does <p align="center">
<a href="https://github.com/AgentWrapper/agent-orchestrator/stargazers"><img src="https://img.shields.io/github/stars/AgentWrapper/agent-orchestrator" alt="Stars" /></a>
<a href="https://github.com/AgentWrapper/agent-orchestrator/graphs/contributors"><img src="https://img.shields.io/github/contributors/AgentWrapper/agent-orchestrator" alt="Contributors" /></a>
<a href="https://x.com/aoagents"><img src="https://img.shields.io/badge/Twitter-1DA1F2?logo=twitter&logoColor=white" alt="Twitter" /></a>
<a href="https://discord.com/invite/UZv7JjxbwG"><img src="https://img.shields.io/badge/Discord-join%20the%20community-5865F2?logo=discord&logoColor=white" alt="Discord" /></a>
<a href="https://www.apache.org/licenses/LICENSE-2.0"><img src="https://img.shields.io/badge/License-Apache--2.0-blue.svg" alt="License: Apache-2.0" /></a>
</p>
- **Agent-agnostic.** A 23-adapter platform under <p align="center">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.</p>
`backend/internal/adapters/agent/` (`claude-code`, `codex`, `cursor`,
`opencode`, `aider`, `amp`, `goose`, `copilot`, `grok`, `qwen`, `kimi`,
`crush`, `cline`, `droid`, `devin`, `auggie`, `continue`, `kiro`, `kilocode`,
and more), registered through a shared registry with common
activity-dispatch / hook utilities. Worker and orchestrator defaults are set
per project.
- **Isolated workspaces.** Worker and orchestrator sessions spawn into their own
`git worktree` (`backend/internal/adapters/workspace/gitworktree/`), launched
inside a `zellij` runtime adapter (`backend/internal/adapters/runtime/`) so
every session has its own attachable terminal.
- **Live PR observation.** The provider-neutral SCM observer
(`backend/internal/observe/scm/`) polls each session's PR with ETag guards and
semantic diffing, tracking CI/check runs and review threads, and feeds those
facts into the lifecycle manager, which sends the owning agent nudges for CI
failures, review feedback, and merge conflicts. GitHub is the implemented
provider today.
- **Durable facts, derived status.** The SQLite store
(`backend/internal/storage/sqlite/`) persists a small set of session facts
plus PR/check/comment rows; display status is computed at read time, never
stored. DB triggers append every user-visible change to `change_log`, and a
CDC poller/broadcaster (`backend/internal/cdc/`) feeds in-process subscribers
and an SSE replay endpoint.
- **Loopback-only daemon.** The HTTP daemon (`backend/internal/httpd`) controls
projects, sessions, orchestrators, and hook callbacks over `127.0.0.1` with no
auth, CORS, or TLS by design.
- **Lifecycle manager + reaper** (`backend/internal/lifecycle/`,
`backend/internal/observe/reaper/`) reduce runtime/activity/PR observations
into the durable session state and reclaim dead sessions.
## How it works <p align="center">
<img src="ao-dashboard-preview.png" alt="Agent Orchestrator Dashboard" />
</p>
1. Register a local git repo as a project (`ao project add`). ### See AO in Action (before the re-write)
2. Spawn a worker session (`ao spawn`), or an orchestrator that fans work out
across sessions. Each session gets its own `git worktree` and a `zellij`
pane.
3. The agent develops, tests, and opens a PR from inside its worktree.
4. The SCM observer watches that PR and routes feedback back to the agent: a CI
failure, a requested change, or a merge conflict becomes a nudge to the agent
that owns the PR.
5. You inspect, attach a terminal, and merge from the CLI or the Electron app;
human attention is needed only where the loop can't resolve on its own.
## Extensibility <table border="1" style="border-collapse: collapse; width: 100%;">
<tr>
<td style="padding: 10px; text-align: center; border: 1px solid #ddd;">
<a href="https://x.com/agent_wrapper/status/2026329204405723180"><img src="screenshots/first.png" alt="First" width="400"></a><br><br>
<a href="https://x.com/agent_wrapper/status/2026329204405723180">Visit</a>
</td>
<td style="padding: 10px; text-align: center; border: 1px solid #ddd;">
<a href="https://x.com/agent_wrapper/status/2025986105485733945"><img src="screenshots/second.png" alt="Second" width="400"></a><br><br>
<a href="https://x.com/agent_wrapper/status/2025986105485733945">Visit</a>
</td>
</tr>
<tr>
<td style="padding: 10px; text-align: center; border: 1px solid #ddd;">
<a href="https://x.com/agent_wrapper/status/2064157228400341312"><img src="screenshots/third.png" alt="Third" width="400"></a><br><br>
<a href="https://x.com/agent_wrapper/status/2064157228400341312">Visit</a>
</td>
<td style="padding: 10px; text-align: center; border: 1px solid #ddd;">
<a href="https://x.com/agent_wrapper/status/2024885035774738700?s=20"><img src="screenshots/image.png" alt="Fourth" width="400"></a><br><br>
<a href="https://x.com/agent_wrapper/status/2024885035774738700?s=20">Visit</a>
</td>
</tr>
</table>
The backend is organized around inbound/outbound port contracts [Features](#features) • [Quick Start](#quick-start) • [Architecture](#architecture) • [Documentation](#documentation) • [Contributing](#contributing)
(`backend/internal/ports/`) with swappable adapters under
`backend/internal/adapters/`:
| Port | Implemented adapters | </div>
| --------- | --------------------------------------------- |
| Agent | 23 harnesses (see above) |
| Runtime | `zellij` |
| Workspace | `git worktree` |
| SCM | GitHub |
| Tracker | GitHub (adapter present; no runtime loop yet) |
| Reviewer | `claude-code` |
| Notifier | port defined; no shipped adapter yet |
See [`docs/STATUS.md`](docs/STATUS.md) for which lanes are live at runtime. ---
## Quick start ## Features
Requirements: Go 1.25+, [`zellij`](https://zellij.dev/) on `PATH` for the | Feature | Description |
runtime adapter, and `gh` (or `GITHUB_TOKEN`) if you want the SCM observer to | :--- | :--- |
authenticate against GitHub. The SQLite driver is the pure-Go | **Agent-Agnostic Platform** | 23+ agent adapters including [Claude Code](https://code.claude.com/docs/en/overview), [OpenAI Codex](https://openai.com/), [Cursor](https://cursor.com/), [OpenCode](https://opencode.ai/), [Aider](https://aider.chat/), [Amp](https://ampcode.com/manual), [Goose](https://goose-docs.ai/), [GitHub Copilot](https://github.com/features/copilot), [Grok](https://x.ai/grok), [Qwen Code](https://github.com/QwenLM/qwen-code), [Kimi Code](https://www.kimi.com/code), [Cline](https://cline.bot/), [Continue](https://www.continue.dev/), [Kiro](https://kiro.dev/), and more |
`modernc.org/sqlite` — no system SQLite library is required. | **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 |
```bash ### Supported Agents
cd backend
go build -o /tmp/ao ./cmd/ao
# Start the daemon and wait for /readyz. 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.
/tmp/ao start
# Register a local git repo as a project. The id defaults to the lowercased **If it runs in a terminal, it runs on Agent Orchestrator.**
# base of --path; pass --id explicitly when the directory name doesn't match.
/tmp/ao project add --path /path/to/your/repo --id your-repo --name your-repo \
--worker-agent codex --orchestrator-agent codex
# Spawn a worker session running the project's worker agent. ---
/tmp/ao spawn --project your-repo --prompt "Refactor the auth module"
# Inspect what's running. ## Quick Start
/tmp/ao status
/tmp/ao session ls
```
### Electron app (dev) ### Prerequisites
The desktop supervisor lives under `frontend/` and is started separately: | Requirement | Minimum | Recommended |
|-------------|---------|-------------|
| Go | 1.25+ | Latest |
| Node.js | 20+ | Latest LTS |
| Git | Any | Latest |
| pnpm | Any | Latest |
```bash **Optional:**
cd frontend - `tmux` (Darwin/Linux) - For Unix runtime
npm install - `gh` (GitHub CLI) - For authenticated GitHub API calls
npm run dev # electron-forge start
```
Heads-up: `npm run dev` does **not** start the daemon for you. Start it first ### Installation
(`ao start`, see above) — the renderer attaches to the running daemon over
loopback (`127.0.0.1:3001` by default, the `AO_PORT` from the table below).
Without a daemon the app opens but shows its daemon-not-ready state.
For renderer-only UI work without the Electron shell, use Download the latest release for your platform:
`npm run dev:web` (Vite in a regular browser).
## CLI surface | Platform | Download |
|----------|----------|
| **Windows** | [Setup.exe](https://github.com/AgentWrapper/agent-orchestrator/releases/latest) |
| **macOS** | [Agent Orchestrator.dmg](https://github.com/AgentWrapper/agent-orchestrator/releases/latest) |
| **Linux** | [Agent Orchestrator.AppImage](https://github.com/AgentWrapper/agent-orchestrator/releases/latest) |
The CLI is intentionally thin: every product command resolves to a daemon HTTP **Direct Download:** [Latest Release](https://github.com/AgentWrapper/agent-orchestrator/releases/latest)
route. Run `ao <command> --help` for the authoritative flag shape; the table
below groups what's on `main` today.
| Lane | Command | Purpose | ---
| ------------ | ------------------------------------ | ---------------------------------------------------------------------------------- |
| Daemon | `ao start` | Start the daemon in the background and wait for `/readyz`. |
| Daemon | `ao stop` | Graceful shutdown via loopback `POST /shutdown`. |
| Daemon | `ao status` | Report PID/port/health/readiness from `running.json`. |
| Daemon | `ao daemon` | Hidden internal entrypoint used by `ao start`. |
| Project | `ao project add` | Register a local git repo as a project. |
| Project | `ao project ls` | List registered projects. |
| Project | `ao project get <id>` | Fetch one project. |
| Project | `ao project set-config <id>` | Update per-project config. |
| Project | `ao project rm <id>` | Remove a project. |
| Session | `ao spawn` | Spawn a worker session in a registered project. |
| Session | `ao session ls` | List sessions (filter by project, include terminated). |
| Session | `ao session get <id>` | Fetch one session. |
| Session | `ao session kill <id>` | Terminate a session. |
| Session | `ao session rename <id> <name>` | Rename a session. |
| Session | `ao session restore <id>` | Relaunch a terminated session. |
| Session | `ao session cleanup` | Reclaim eligible workspaces for terminated sessions. |
| Session | `ao session claim-pr <session> <pr>` | Attach an existing PR to a session. |
| Orchestrator | `ao orchestrator ls` | List orchestrator sessions. |
| Messaging | `ao send` | Send a message to a running agent session. |
| Preview | `ao preview [url]` | Open a URL (or the workspace `index.html`) in the session's desktop browser panel. |
| Utility | `ao doctor` | Local health checks (config, data dir, DB, `git`, `zellij`). |
| Utility | `ao completion <shell>` | Generate bash/zsh/fish/powershell completions. |
| Utility | `ao version` | Print build metadata. |
| Internal | `ao hooks <agent> <event>` | Hidden adapter hook callback. |
See [`docs/cli/`](docs/cli/) for the daemon-control intent and command shape. ## Telemetry
## Configuration 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](docs/telemetry.md).
All configuration is env-driven; the daemon takes no config file. The bind ---
host is hard-coded to `127.0.0.1` — the daemon has no auth, CORS, or TLS, and
exposing it beyond loopback would be a security regression.
| Var | Default | Purpose |
| --------------------- | ------------------------------------------------- | --------------------------------------------------------------------------- |
| `AO_PORT` | `3001` | Bind port; daemon 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, managed state. |
| `AO_AGENT` | `claude-code` | Compatibility agent adapter id validated at daemon startup. |
| `AO_SESSION_ID` | _(unset)_ | Set inside spawned sessions; read by `ao send` and `ao hooks`. |
| `GITHUB_TOKEN` | _(unset)_ | Used by the GitHub SCM and tracker adapters. Falls back to `gh auth token`. |
Health check:
```bash
curl localhost:3001/healthz
curl localhost:3001/readyz
```
## Architecture ## Architecture
The daemon is a long-running supervisor. Adapters observe external facts (PR Agent Orchestrator is a long-running Go daemon built around **inbound/outbound port contracts** with swappable adapters.
state, agent activity, runtime liveness); the lifecycle manager reduces those
into a small set of durable session facts (`activity_state`, `is_terminated`,
PR rows). Display status is _derived_ from those facts at read time — it is
never stored. SQLite triggers append every user-visible change to `change_log`,
and the CDC poller broadcasts those events to in-process subscribers and an
SSE stream.
Full mental model and load-bearing rules: [`docs/architecture.md`](docs/architecture.md). **Core mental model:** OBSERVE external facts → UPDATE durable facts → DERIVE display status / ACT
Package-by-package ownership: [`docs/backend-code-structure.md`](docs/backend-code-structure.md).
**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: `tmux` on Darwin/Linux, `conpty` on 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](docs/architecture.md).
---
## Documentation
| Document | Description |
|----------|-------------|
| [Architecture](docs/architecture.md) | System architecture, data flows, and load-bearing rules |
| [Backend Code Structure](docs/backend-code-structure.md) | Package-by-package ownership and dependency rules |
| [AGENTS.md](AGENTS.md) | Contributor and worker-agent contract |
| [Agent Adapter Contract](docs/agent/README.md) | Agent adapter interface and hook behavior |
---
## Testing ## Testing
The local gate is the backend Go build and race-enabled test suite:
```bash ```bash
cd backend && go build ./... && go test -race ./... # Backend tests
cd backend
go test -race ./...
# Frontend tests
cd frontend
pnpm test
# Full CI validation locally
npx @redwoodjs/agent-ci run --all
``` ```
GitHub Actions is the authoritative pre-merge gate; mirror its commands here ---
when in doubt. See [`AGENTS.md`](AGENTS.md) for the regen workflow when
touching the daemon API surface (`npm run sqlc`, `npm run api`).
## Status and roadmap ## Configuration
Progress tracking lives in [`docs/STATUS.md`](docs/STATUS.md): what is shipped All configuration is environment-driven. The daemon takes no config file.
on `main` today, what is still in flight, and the linked
[`rewrite`](https://github.com/aoagents/agent-orchestrator/milestone/1) | Variable | Default | Purpose |
milestone on GitHub. |----------|---------|---------|
| `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
```bash
curl localhost:3001/healthz # Liveness probe
curl localhost:3001/readyz # Readiness probe
```
---
## Contributing ## Contributing
Repo layout and the worker contract live in [`AGENTS.md`](AGENTS.md). Keep We love contributions! Join our community on Discord to get started.
changes surgical, follow the package boundaries documented in
[`docs/backend-code-structure.md`](docs/backend-code-structure.md), and prefer ### Join us on Discord
adding daemon HTTP routes over leaking storage / runtime into the CLI.
[![Discord](https://img.shields.io/badge/Discord-join%20the%20community-5865F2?style=for-the-badge&logo=discord&logoColor=white&logoSize=auto)](https://discord.com/invite/UZv7JjxbwG)
**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
1. **Join the Discord** - Connect with the community and get guidance
2. **Read the contributor contract** - See [AGENTS.md](AGENTS.md) for repo layout, daemon/API boundaries, and coding conventions
3. **Pick a focused problem** - Browse [open issues](https://github.com/AgentWrapper/agent-orchestrator/issues) and choose one small enough for a focused PR
4. **Open a clear PR** - Keep changes narrow, explain user-visible impact, link issues, include tests
5. **Iterate with contributors** - Use review feedback to tighten the PR until verified
---
## License
Apache License 2.0 - see [LICENSE](LICENSE) for details.
---
<div align="center">
**[Star us on GitHub](https://github.com/AgentWrapper/agent-orchestrator)** • **[Report Issues](https://github.com/AgentWrapper/agent-orchestrator/issues)** • **[Discussions](https://github.com/AgentWrapper/agent-orchestrator/discussions)**
Made with love by the Agent Orchestrator community
</div>

BIN
ao-dashboard-preview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

25
ao-logo.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 56 KiB

File diff suppressed because it is too large Load Diff

View File

@ -1,103 +1,856 @@
# Agent Orchestrator backend architecture # Agent Orchestrator Architecture
The backend is a long-running Go daemon that supervises coding-agent sessions. Agent Orchestrator is a long-running Go daemon that supervises multiple parallel AI coding agent sessions. Each session runs in an isolated git worktree with its own runtime, while the daemon coordinates lifecycle, observes external state, and routes feedback.
The current model is intentionally small: session rows persist only durable facts,
and display status is derived at read time.
## Mental model ## Table of Contents
- [Mental Model](#mental-model)
- [System Overview](#system-overview)
- [Core Architectural Principles](#core-architectural-principles)
- [Component Architecture](#component-architecture)
- [Data Flows](#data-flows)
- [Persistence and CDC](#persistence-and-cdc)
- [Status Derivation](#status-derivation)
- [Lifecycle Management](#lifecycle-management)
- [Observation Loops](#observation-loops)
- [HTTP Layer](#http-layer)
- [Terminal Multiplexing](#terminal-multiplexing)
---
## Mental Model
The fundamental architecture follows a simple three-stage pipeline:
```mermaid
flowchart LR
A[OBSERVE<br/>External Facts] --> B[UPDATE<br/>Durable Facts]
B --> C[DERIVE<br/>Display Status / ACT]
```
OBSERVE external facts → UPDATE durable facts → DERIVE display status / ACT
``` ```
The durable session facts are: **Key insight:** Display status is never stored. It is computed at read time from durable facts.
- `activity_state` — what the agent last reported or what the runtime observer ### Durable Session Facts
can safely conclude (`active`, `idle`, `waiting_input`, `exited`).
- `is_terminated` — whether the session should be treated as over.
- PR facts in the `pr`, `pr_checks`, and `pr_comment` tables.
The UI status is not stored. `service.Session` computes it from the session The only persistent session state is:
record plus PR facts while assembling controller-facing read models.
## Package layout - `activity_state` — What the agent last reported (`active`, `idle`, `waiting_input`, `exited`)
- `is_terminated` — Whether the session should be treated as over
- PR facts — `pr`, `pr_checks`, `pr_comment` tables
### What is NOT Durable
Display status like `working`, `needs_input`, `ci_failed`, `mergeable` are **computed at read time** by the service layer from the durable facts above.
---
## System Overview
```mermaid
graph TB
subgraph Frontend
FE[Electron + React UI]
CLI[ao CLI]
end
subgraph HTTP["HTTP Daemon (127.0.0.1)"]
Controllers[REST Controllers]
SSE[SSE Events]
Terminal[Terminal WebSocket]
end
subgraph Core["Core Services"]
SessionSvc[Session Service]
ProjectSvc[Project Service]
PRSvc[PR Service]
ReviewSvc[Review Service]
SessionMgr[Session Manager]
LCM[Lifecycle Manager]
end
subgraph Observe["Observation Layer"]
SCMObserver[SCM Observer]
Reaper[Runtime Reaper]
end
subgraph Storage["Persistence Layer"]
SQLite[(SQLite DB)]
CDC[CDC Poller]
Broadcaster[Event Broadcaster]
end
subgraph Adapters["Adapters"]
AgentAdapter[Agent Adapters]
RuntimeAdapter[Runtime tmux/conpty]
WorkspaceAdapter[Workspace git worktree]
SCMAdapter[SCM GitHub]
end
FE -->|REST/SSE| Controllers
CLI -->|REST| Controllers
Controllers --> SessionSvc
Controllers --> ProjectSvc
Controllers --> PRSvc
SessionSvc --> SessionMgr
SessionMgr --> LCM
SessionMgr --> AgentAdapter
SessionMgr --> RuntimeAdapter
SessionMgr --> WorkspaceAdapter
LCM --> SQLite
LCM --> AgentAdapter
SCMObserver --> SCMAdapter
SCMObserver --> SQLite
SCMObserver --> LCM
Reaper --> RuntimeAdapter
Reaper --> SQLite
Reaper --> LCM
CDC -->|poll| SQLite
CDC --> Broadcaster
Broadcaster --> SSE
Terminal --> RuntimeAdapter
``` ```
backend/internal/domain shared vocabulary and API status value types
backend/internal/ports inbound/outbound interfaces ---
backend/internal/service/{project,session,pr,review}
controller-facing services and read-model assembly ## Core Architectural Principles
backend/internal/session_manager internal session command manager
backend/internal/lifecycle runtime/activity/spawn/termination session fact reducer ### 1. Port-Based Design
backend/internal/observe/scm SCM (GitHub) observer loop feeding PR facts
backend/internal/observe/reaper runtime liveness observation loop Core code never depends on concrete implementations. All external systems are accessed through port interfaces defined in `backend/internal/ports/`:
backend/internal/storage SQLite persistence and DB-triggered CDC
backend/internal/cdc change-log poller and broadcaster ```mermaid
backend/internal/httpd daemon HTTP surface (REST + SSE + terminal mux) graph LR
backend/internal/terminal WebSocket terminal multiplexer Core[Core Services] -->|consumes| Ports[Port Interfaces]
backend/internal/adapters agent/tmux+conpty runtime/git-worktree/GitHub SCM + tracker adapters Adapters[Adapters] -->|implement| Ports
backend/internal/daemon production wiring and shutdown External[External Systems] -->|wrapped by| Adapters
backend/internal/config daemon env/default config
``` ```
## Status derivation ### 2. Durable Facts, Derived Status
`service.Session` selects the display PR from all PR snapshots for a session, then Storage layer persists minimal facts. Service layer computes display status on-demand:
applies this rough precedence:
1. `is_terminated``terminated`, except merged PRs display `merged`. ```mermaid
2. `activity_state=waiting_input``needs_input`. flowchart LR
3. Open PR facts drive PR pipeline statuses: `ci_failed`, `draft`, SQLite[(SQLite)] -->|raw facts| Service[Session Service]
`changes_requested`, `mergeable`, `approved`, `review_pending`, `pr_open`. Service -->|compute| Status[Display Status]
4. `activity_state=active``working`. Service -->|enrich| UI[Dashboard/UI]
5. A signal-capable harness that has never sent a hook callback past the
~90s spawn grace → `no_signal` (a broken hook pipeline is visible rather
than reported as a confident `idle`). Hook-less harnesses stay `idle`.
6. Everything else → `idle`.
## Lifecycle manager SQLite -->|activity_state| Service
SQLite -->|is_terminated| Service
SQLite -->|PR facts| Service
SQLite -->|runtime_handle| Service
`lifecycle.Manager` is the write path for session lifecycle facts and lifecycle-owned agent nudges: ```
- runtime observations can mark a session terminated only when runtime and ### 3. Observer Pattern
process are both clearly dead and recent activity does not contradict that;
failed/unknown probes do not persist a special state.
- activity signals update `activity_state`; `exited` also marks the session
terminated.
- PR observations do not write PR rows here, but after the PR service persists
them lifecycle sends actionable agent nudges for CI failures, review feedback,
and merge conflicts.
## PR manager Observation is separated from action:
`pr.Manager` records SCM observations into the PR/check/comment tables, then - **Observe layer** — SCM Observer, Runtime Reaper poll external state
forwards the observation to lifecycle for agent nudges. A merged PR marks the - **Lifecycle layer** — Reduces observations into durable facts
owning session terminated through the lifecycle manager; other PR facts are - **Service layer** — Computes display status from facts
consumed at read time for display status.
## Session manager ### 4. Change Data Capture
`session_manager.Manager` performs internal session mutations: All durable changes flow through a CDC pipeline:
- `Spawn` creates a row, creates workspace/runtime resources, and reports the ```mermaid
handles to the lifecycle manager. flowchart LR
- `Kill` marks the row terminated, then tears down runtime/workspace resources. DB[(SQLite)] -->|triggers| ChangeLog[change_log table]
- `Restore` relaunches a terminated session and clears `is_terminated` via the ChangeLog -->|tail| Poller[CDC Poller]
spawn-completed path. Poller -->|Event| Broadcaster[Event Broadcaster]
Broadcaster -->|fan-out| Subscribers[Subscribers]
Subscribers -->|SSE| Clients[Dashboard Clients]
`service.Session` is the controller-facing boundary. It delegates commands to ```
`session_manager.Manager` and attaches derived display status on read paths.
---
## Component Architecture
### Package Layout
```
backend/internal/
├── domain/ # Shared vocabulary and durable fact records
├── ports/ # Inbound/outbound interfaces
├── service/ # Controller-facing services
│ ├── project/ # Project CRUD
│ ├── session/ # Session read-model assembly
│ ├── pr/ # PR observation service
│ └── review/ # Code review service
├── session_manager/ # Internal session command engine
├── lifecycle/ # Durable session fact reducer
├── observe/ # Observation loops
│ ├── scm/ # SCM (GitHub) observer
│ └── reaper/ # Runtime liveness observer
├── storage/ # SQLite persistence
│ └── sqlite/ # DB, migrations, queries, stores
├── cdc/ # Change-log poller and broadcaster
├── httpd/ # HTTP API, controllers, terminal mux
├── terminal/ # Terminal session protocol
├── adapters/ # Concrete adapter implementations
│ ├── agent/ # 23+ agent harnesses
│ ├── runtime/ # tmux/conpty runtimes
│ ├── workspace/ # git worktree
│ ├── scm/ # GitHub
│ └── tracker/ # GitHub tracker
├── daemon/ # Production wiring
└── config/ # Environment-based configuration
```
### Core Data Flow
```mermaid
sequenceDiagram
participant UI as Dashboard
participant HTTP as HTTP Controller
participant Svc as Session Service
participant Mgr as Session Manager
participant LCM as Lifecycle Manager
participant Agent as Agent Adapter
participant Runtime as Runtime Adapter
participant WS as Workspace Adapter
participant DB as SQLite
participant CDC as CDC Broadcaster
UI->>HTTP: POST /sessions
HTTP->>Svc: Spawn(config)
Svc->>Mgr: Spawn(config)
Note over Mgr: 1. Create session row
Mgr->>DB: Insert session
DB->>CDC: trigger change_log
CDC->>UI: SSE session.created
Note over Mgr: 2. Create workspace
Mgr->>WS: Create(project, branch)
WS->>WS: git worktree add
Note over Mgr: 3. Launch runtime
Mgr->>Runtime: Create(session)
Runtime->>Runtime: Start tmux/conpty
Note over Mgr: 4. Start agent
Mgr->>Agent: GetLaunchCommand()
Agent-->>Mgr: launch command
Mgr->>Runtime: Execute(agent command)
Note over Mgr: 5. Mark spawned
Mgr->>LCM: MarkSpawned(handle)
LCM->>DB: Update activity_state
DB->>CDC: trigger change_log
CDC->>UI: SSE session.updated
Mgr-->>Svc: Session(created)
Svc-->>HTTP: Session response
HTTP-->>UI: 201 Created
```
---
## Data Flows
### Session Spawn Flow
```mermaid
flowchart TD
Start([User spawns session]) --> Validate[Validate project config]
Validate --> CreateRow[Create session row in SQLite]
CreateRow --> CreateWS[Create git worktree]
CreateWS --> CreateRT[Launch runtime tmux/conpty]
CreateRT --> GetCmd[Get agent launch command]
GetCmd --> ExecAgent[Execute agent in runtime]
ExecAgent --> MarkSpawned[MarkSpawned in LCM]
MarkSpawned --> Trigger1[CDC: session.created]
Trigger1 --> Trigger2[CDC: session.updated]
Trigger2 --> Done([Session running])
```
### Observation Flow
```mermaid
flowchart TD
subgraph SCM["SCM Observer Loop"]
Poll1[Poll PRs every 30s]
Poll1 --> Fetch[Fetch from GitHub API]
Fetch --> Diff[Semantic diff vs local]
Diff --> Changed{Changed?}
Changed -->|Yes| WritePR[Write PR/check/comment]
Changed -->|No| Wait1[Wait for tick]
WritePR --> NotifyLCM[Notify Lifecycle Manager]
NotifyLCM --> Trigger1[CDC event]
Trigger1 --> Wait1
Wait1 --> Poll1
end
subgraph Reaper["Runtime Reaper Loop"]
Poll2[Poll every 5s]
Poll2 --> Probe[Probe each runtime]
Probe --> Report[Report fact to LCM]
Report --> Trigger2[CDC event]
Trigger2 --> Wait2[Wait for tick]
Wait2 --> Poll2
end
LCM[Lifecycle Manager] -->|consumes| NotifyLCM
LCM -->|consumes| Report
```
### Feedback Routing Flow
```mermaid
sequenceDiagram
participant SCM as SCM Observer
participant LCM as Lifecycle Manager
participant Agent as Agent Adapter
participant Runtime as Runtime Adapter
SCM->>SCM: Observe PR comment
SCM->>LCM: ApplySCMObservation()
LCM->>LCM: Detect actionable feedback
LCM->>Agent: SendNudge(feedback)
SCM->>SCM: Observe CI failure
SCM->>LCM: ApplySCMObservation()
LCM->>LCM: Detect actionable feedback
LCM->>Agent: SendNudge(CI failure)
SCM->>SCM: Observe merge conflict
SCM->>LCM: ApplySCMObservation()
LCM->>LCM: Detect actionable feedback
LCM->>Agent: SendNudge(merge conflict)
Note over Agent,Runtime: Agent receives nudges via<br/>runtime messages or hooks
```
---
## Persistence and CDC ## Persistence and CDC
SQLite is the durable store. User-visible table changes are captured by database ### SQLite Schema
triggers into `change_log`; the Go store does not manually emit CDC events. A
poller tails `change_log` and publishes live events to in-process subscribers.
## Load-bearing rules ```mermaid
erDiagram
projects ||--o{ sessions : owns
sessions ||--o{ pull_requests : owns
pull_requests ||--o{ pr_checks : has
pull_requests ||--o{ pr_review_threads : has
pull_requests ||--o{ pr_comments : has
sessions ||--o{ notifications : has
change_log }|--|| projects : tracks
change_log }|--|| sessions : tracks
change_log }|--|| pull_requests : tracks
- Do not store display status. projects {
- Keep session status facts small: `activity_state`, `is_terminated`, and PR string id PK
facts are the durable inputs. string name
- Do not treat failed probes as death. string repo
- Do not force-delete registered dirty worktrees. jsonb config
}
sessions {
string id PK
string project_id FK
string harness
string activity_state
boolean is_terminated
jsonb metadata
}
pull_requests {
string id PK
string session_id FK
integer number
string state
string title
boolean draft
boolean mergeable
}
pr_checks {
string id PK
string pr_id FK
string name
string status
string conclusion
}
change_log {
bigint seq PK
string table_name
string row_id
string operation
jsonb old_data
jsonb new_data
}
```
### CDC Pipeline
```mermaid
flowchart LR
DB[(SQLite)] -->|INSERT/UPDATE/DELETE| Trigger[DB Trigger]
Trigger -->|append| ChangeLog[change_log]
ChangeLog -->|poll| Poller[CDC Poller]
Poller -->|decode| Decoder[Event Decoder]
Decoder -->|Event| Broadcaster[Broadcaster]
Broadcaster -->|callback| Sub1[Terminal Fanout]
Broadcaster -->|callback| Sub2[SSE Writer]
Broadcaster -->|callback| Sub3[Cache Invalidation]
Poller -->|watermark| Watermark[seq tracking]
Watermark -->|resume position| Poller
```
---
## Status Derivation
### Display Status Precedence
The `service.Session` computes display status from durable facts using this precedence (highest to lowest):
```mermaid
flowchart TD
CheckTerm{is_terminated?}
CheckTerm -->|Yes| PRMerged{PR merged?}
CheckTerm -->|No| CheckWait{activity_state<br/>== waiting_input?}
PRMerged -->|Yes| Merged[merged]
PRMerged -->|No| Terminated[terminated]
CheckWait -->|Yes| NeedsInput[needs_input]
CheckWait -->|No| CheckPR{Has PR facts?}
CheckPR -->|Yes| PRPipeline[PR Pipeline Check]
CheckPR -->|No| CheckActive{activity_state<br/>== active?}
PRPipeline --> PRState{PR State}
PRState -->|ci failed| CIFailed[ci_failed]
PRState -->|draft| Draft[draft]
PRState -->|changes requested| Changes[changes_requested]
PRState -->|not mergeable| Conflict[merge_conflict]
PRState -->|mergeable| Mergeable[mergeable]
PRState -->|approved| Approved[approved]
PRState -->|review pending| ReviewPending[review_pending]
PRState -->|open| PROpen[pr_open]
CheckActive -->|Yes| Working[working]
CheckActive -->|No| CheckSignal{Signal capable<br/>&& no signal?}
CheckSignal -->|Yes| NoSignal[no_signal]
CheckSignal -->|No| Idle[idle]
```
### PR Pipeline States
```mermaid
flowchart LR
PR[Open PR] --> CI{CI Status}
CI -->|failing| CIFailed[ci_failed]
CI -->|pending| CIPending[ci_pending]
CI -->|passing| Review{Reviews}
Review -->|changes requested| Changes[changes_requested]
Review -->|approved| Mergeable{Mergeable?}
Mergeable -->|conflict| Conflict[merge_conflict]
Mergeable -->|yes| Merged[Mergeable]
PR -.->|draft| Draft[Draft State]
```
---
## Lifecycle Management
### Lifecycle Manager Responsibilities
The `lifecycle.Manager` is the **canonical write path** for all session lifecycle facts:
```mermaid
flowchart TD
subgraph Inputs["Observation Inputs"]
RuntimeObs[Runtime Observations]
ActivitySignals[Agent Activity Signals]
SCMObs[SCM Observations]
end
subgraph LCM["Lifecycle Manager"]
Reducer[Fact Reducer]
StateMachine[Activity State Machine]
Termination[Termination Logic]
Nudge[Agent Nudge Engine]
end
subgraph Outputs["Durable Facts"]
ActivityState[activity_state]
IsTerminated[is_terminated]
PRFacts[PR Facts Table]
end
RuntimeObs --> Reducer
ActivitySignals --> Reducer
SCMObs --> Reducer
Reducer --> StateMachine
StateMachine --> Termination
Termination --> ActivityState
Termination --> IsTerminated
SCMObs --> Nudge
Nudge -->|route| Agent[Agent Adapter]
```
### Session State Machine
```mermaid
stateDiagram-v2
[*] --> Spawning: Spawn()
Spawning --> Active: MarkSpawned
Active --> Idle: activity_state = idle
Active --> Working: activity_state = active
Active --> Waiting: activity_state = waiting_input
Active --> Exited: activity_state = exited
Working --> Active: work completes
Waiting --> Active: user responds
Idle --> Active: agent starts work
Exited --> Terminated: process exit
Active --> Terminated: Kill()
Waiting --> Terminated: Kill()
Idle --> Terminated: Kill()
Terminated --> [*]
note right of Active
Agent is working
Runtime alive
end note
note right of Waiting
Agent needs input
Waiting for user
end note
note right of Terminated
Session over
Runtime cleaned up
end note
```
### Termination Guardrails
The lifecycle manager only terminates when **all** conditions are met:
```mermaid
flowchart TD
Check{Can terminate?}
Check -->|No| Keep[Keep running]
Check -->|Yes| AllDead{Runtime AND<br/>process dead?}
AllDead -->|No| Keep
AllDead -->|Yes| NoRecent{No recent<br/>activity?}
NoRecent -->|No| Keep
NoRecent -->|Yes| NoPR{No merged PR<br/>ownership?}
NoPR -->|No| Keep
NoPR -->|Yes| Terminate[Mark terminated]
Terminate --> Cleanup[Trigger cleanup]
Cleanup --> CDC[CDC event]
CDC --> UI[Dashboard update]
```
**Key principle:** Failed probes are NOT proof of death. A session is only terminated when the runtime and process are **both** clearly dead and recent activity doesn't contradict that.
---
## Observation Loops
### SCM Observer
```mermaid
flowchart TD
Start([Observer Start]) --> Immediate[Immediate Poll]
Immediate --> Loop{Tick every 30s}
Loop --> ListRepos[List active repos]
ListRepos --> CheckCreds{Credentials<br/>available?}
CheckCreds -->|No| Disabled[Disabled mode]
CheckCreds -->|Yes| Fetch[Fetch PRs via ETags]
Fetch --> ListPRs[List open PRs]
ListPRs --> Discover[Discover new PRs]
Discover --> FetchDetailed[Fetch detailed PR data]
FetchDetailed --> FetchChecks[Fetch CI checks]
FetchChecks --> FetchReviews[Fetch review threads]
FetchReviews --> Write[Write to SQLite]
Write --> Notify[Notify Lifecycle]
Notify --> Trigger[CDC event]
Disabled --> Loop
Trigger --> Loop
```
### Runtime Reaper
```mermaid
flowchart TD
Start([Reaper Start]) --> Loop{Tick every 5s}
Loop --> List[List non-terminated<br/>sessions]
List --> ForEach[For each session]
ForEach --> GetHandle{Has runtime<br/>handle?}
GetHandle -->|No| Skip[Skip session]
GetHandle -->|Yes| Probe[Probe runtime]
Probe --> Result{Probe result}
Result -->|Error| ReportFailed[Report ProbeFailed]
Result -->|Alive| ReportAlive[Report ProbeAlive]
Result -->|Dead| ReportDead[Report ProbeDead]
ReportFailed --> Apply[ApplyRuntimeObservation]
ReportAlive --> Apply
ReportDead --> Apply
Apply --> LCM[Lifecycle Manager]
LCM --> Update[Update facts]
Update --> CDC[CDC event]
Skip --> NextSession{More sessions?}
CDC --> NextSession
NextSession -->|Yes| ForEach
NextSession -->|No| Loop
```
### Observation Integration
```mermaid
flowchart LR
subgraph External["External State"]
GitHub[GitHub API]
Runtimes[tmux/conpty]
end
subgraph Observers["Observation Layer"]
SCM[SCM Observer]
Reaper[Runtime Reaper]
end
subgraph Core["Core Processing"]
LCM[Lifecycle Manager]
PRMgr[PR Manager]
end
subgraph Storage["Persistence"]
SQLite[(SQLite)]
end
GitHub --> SCM
Runtimes --> Reaper
SCM --> PRMgr
PRMgr --> SQLite
PRMgr --> LCM
Reaper --> LCM
LCM --> SQLite
```
---
## HTTP Layer
### API Structure
```mermaid
flowchart TD
subgraph HTTPD["HTTP Daemon"]
Router[Router + Middleware]
Router --> API[REST API]
Router --> Events[SSE Events]
Router --> Terminal[Terminal WebSocket]
end
subgraph Controllers["Controllers"]
Sessions[Sessions Controller]
Projects[Projects Controller]
PRs[PRs Controller]
Reviews[Reviews Controller]
end
subgraph Services["Services"]
SessionSvc[Session Service]
ProjectSvc[Project Service]
PRSvc[PR Service]
ReviewSvc[Review Service]
end
API --> Sessions
API --> Projects
API --> PRs
API --> Reviews
Sessions --> SessionSvc
Projects --> ProjectSvc
PRs --> PRSvc
Reviews --> ReviewSvc
Events -->|subscribe| CDC[CDC Broadcaster]
Terminal --> TerminalMux[Terminal Manager]
```
### Request Flow
```mermaid
sequenceDiagram
participant Client
participant Router
participant Controller
participant Service
participant Manager
participant Store
participant DB
Client->>Router: POST /api/v1/sessions
Router->>Router: Middleware (auth, logging)
Router->>Controller: handler(w, r)
Controller->>Controller: decode JSON
Controller->>Service: Spawn(config)
Service->>Manager: Spawn(config)
Manager->>Store: Create session
Store->>DB: INSERT INTO sessions
DB->>Store: session record
Store->>Manager: session record
Manager->>Manager: Create workspace
Manager->>Manager: Launch runtime
Manager->>Service: Session response
Service->>Controller: enriched session
Controller->>Controller: encode JSON
Controller->>Client: 201 Created + Session
```
---
## Terminal Multiplexing
### Terminal Architecture
```mermaid
flowchart TD
subgraph Frontend
Browser[Browser Terminal]
end
subgraph HTTPD
WS[WebSocket Handler]
end
subgraph Terminal
Mux[Terminal Mux]
Sessions[Session States]
end
subgraph Runtime
TMux[tmux Runtime]
ConPTY[conpty Runtime]
end
Browser -->|WebSocket| WS
WS -->|attach| Mux
Mux --> Sessions
Sessions -->|create| TMux
Sessions -->|create| ConPTY
TMux -->|PTY attach| Mux
ConPTY -->|loopback dial| Mux
Mux -->|frame| WS
WS -->|binary| Browser
```
### Attach Flow
```mermaid
sequenceDiagram
participant Client as Browser
participant WS as WebSocket Handler
participant Mux as Terminal Mux
participant Runtime as tmux/conpty
Client->>WS: WebSocket upgrade
WS->>Mux: Attach(session, rows, cols)
Mux->>Runtime: Attach(handle, rows, cols)
Runtime->>Runtime: Create PTY
Runtime->>Runtime: Spawn tmux attach
loop Data Loop
Runtime->>Mux: PTY output
Mux->>WS: Binary frame
WS->>Client: WebSocket message
Client->>WS: User input
WS->>Mux: Input frame
Mux->>Runtime: Write to PTY
end
Client->>WS: Close
WS->>Mux: Detach
Mux->>Runtime: Close PTY
```
---
## Load-Bearing Rules
These rules are **load-bearing** — changing them breaks fundamental architectural assumptions:
1. **Never store display status** — Status is derived from durable facts at read time
2. **Never treat failed probes as death** — A failed probe is a fact, not a termination signal
3. **Never force-delete dirty worktrees** — User data safety over cleanup convenience
4. **All app state under ~/.ao** — No OS-default app-data locations
5. **Daemon binds to 127.0.0.1 only** — No network exposure, ever
6. **CLI is thin** — All logic lives in the daemon, CLI is just an HTTP client
7. **CDC is source-truth for events** — DB triggers write to change_log, poller fans out
8. **Adapters are leaves** — Adapters never import core packages, only ports and domain
9. **Hooks are gitignored** — Every file an adapter writes must be in .gitignore
10. **Migrations never change** — Add new migrations, never modify existing ones
---
## Summary
Agent Orchestrator's architecture is designed around:
- **Separation of concerns** — Observation, persistence, and display are distinct layers
- **Port-based design** — Core code depends on interfaces, not implementations
- **Durable minimalism** — Store only facts, compute everything else
- **Event-driven updates** — CDC broadcasts changes to all subscribers
- **Isolation** — Each session in its own worktree with its own runtime
- **Safety** — Conservative termination, path validation, gitignored hooks
This architecture enables parallel AI agents to work safely while maintaining complete visibility and control.

File diff suppressed because it is too large Load Diff

90
docs/telemetry.md Normal file
View File

@ -0,0 +1,90 @@
# Telemetry
Agent Orchestrator includes telemetry for understanding product usage, reliability, and failure modes. Telemetry is implemented as **best-effort structured events** and is controlled by environment variables.
## What We Collect
Telemetry events are structured records that capture:
- **Event Name** — The type of event (e.g., session lifecycle events, daemon operations, errors)
- **Source** — The component that emitted the event
- **Timestamp** — When the event occurred
- **Level** — Severity level (Debug, Info, Warn, Error)
- **Context** — Project ID, Session ID, and Request ID when applicable
- **Payload** — Event-specific metadata
**We do not collect:**
- Code from your repositories
- File contents or workspace data
- Authentication credentials or API keys
- Personal information beyond what is necessary for operational analytics
## Storage and Transmission
### Local Storage (Default)
By default, all telemetry events are stored locally in a SQLite database at:
```
~/.ao/data/telemetry.db
```
No data leaves your machine unless you explicitly configure remote telemetry.
### Remote Telemetry (Opt-In)
You may optionally configure remote telemetry via PostHog by setting the `POSTHOG_API_KEY` environment variable. When configured:
- Events are transmitted to PostHog for aggregate analytics
- Transmission is best-effort — failures do not affect daemon operation
- Events are batched to minimize network overhead
## Configuration
Telemetry behavior is controlled by these environment variables:
| Variable | Default | Purpose |
| -------------------- | ------------------------- | ------------------------------------------------------ |
| `AO_TELEMETRY_LEVEL` | `info` | Minimum event level to emit (debug, info, warn, error) |
| `POSTHOG_API_KEY` | unset | PostHog API key for remote telemetry |
| `POSTHOG_HOST` | `https://app.posthog.com` | PostHog host endpoint |
| `AO_DATA_DIR` | `~/.ao/data` | Directory for local telemetry database |
## Disabling Telemetry
To completely disable telemetry:
```bash
export AO_TELEMETRY_LEVEL=none
```
This prevents both local storage and any remote transmission of telemetry events.
## Event Examples
Typical telemetry events include:
- Session spawned, terminated, or restored
- Daemon started or stopped
- Agent harness lifecycle events
- HTTP request errors
- Runtime failures
- SCM observation errors
These events help us understand:
- How agents are being used
- Where failures occur
- How to improve reliability
- Which features need attention
## Privacy Commitment
- Local telemetry is stored on your machine only
- Remote telemetry is opt-in via explicit environment variable configuration
- We do not collect code, file contents, or credentials
- Events are designed for aggregate product analytics, not individual surveillance
- PostHog configuration respects your privacy settings and data retention policies
For questions or concerns about telemetry, please open an issue on GitHub or join our Discord community.

BIN
screenshots/first.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 786 KiB

BIN
screenshots/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

BIN
screenshots/second.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 KiB

BIN
screenshots/third.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB