Files
agentci/README.md
T

147 lines
7.3 KiB
Markdown

# Agent CI
Agent CI is a private Gitea webhook host that turns issue and pull-request comments into resumable
OpenCode planning and implementation workflows. Docker Compose runs the webhook worker and a
private OpenCode server on the same Docker network as Gitea.
## Commands
| Location | Command | Behavior |
| --- | --- | --- |
| Issue | `/agent plan [message]` | Create and independently review a new canonical plan. |
| Issue | `/agent discuss <message>` | Resume the newest planner and post its response. |
| Issue | `/agent implement [message]` | Create, review, and open a PR. A prior plan is optional. |
| Issue | `/agent iterate [message]` | Revise and review the plan once, unless an agent PR is open or merged. |
| PR | `/agent iterate [message]` | Resume an agent implementation and its reviewer once. |
| PR | `/agent fix [message]` | Start a fresh one-shot fix session and push one commit. |
The requester must have Gitea `write`, `admin`, or `owner` permission on the repository. Each
accepted command gets separate queued and started comments. Final plans, PR results, failures, and
remaining review findings are posted separately.
## Deploy
1. Create a Gitea bot user with repository read/write access and an API token.
2. Copy `.env.example` to `.env` and set the external network, Gitea URL, and provider-qualified
OpenCode models.
3. Create `secrets/gitea_token`, `secrets/webhook_secret`, and
`secrets/opencode_server_password`. Use high-entropy values for both secret/password files.
4. Build the image:
```sh
./scripts/build.sh
```
5. Authenticate the configured OpenCode providers before starting the persistent server:
```sh
docker compose run --rm opencode opencode auth login
docker compose run --rm opencode opencode auth list
```
6. Start the services:
```sh
docker compose up --no-build -d
```
7. In Gitea, create a JSON webhook targeting `http://agentci:8080/webhooks/gitea`. Set the same
webhook secret and subscribe to issue comments, PR timeline comments, and PR review comments.
OpenCode caches provider state. After adding or changing authentication on an already running
deployment, run the one-off `auth login` command above and then `docker compose restart opencode`.
`/health/live` reports process health. `/health/ready` returns 503 until the OpenCode server is
healthy and every configured model exists, supports tool calls, accepts its configured variant, and
has a connected provider. The worker leaves jobs queued while the runtime is unavailable.
## OpenCode
Models use OpenCode's `provider/model` format. Planning, implementation, and research can use
different providers. Optional `AGENTCI_PLAN_VARIANT` and `AGENTCI_IMPLEMENT_VARIANT` values are
passed directly to OpenCode for providers that support variants.
`scripts/build.sh` supplies a unique required cache key on every invocation. The image then runs
`npm install -g 'opencode-ai@^1'`, verifies the installed major version, and prints it. Compose also
requests a no-cache build. A direct build without `OPENCODE_REFRESH` fails rather than silently
reusing an old OpenCode installation layer. This is intentionally fresh rather than reproducible.
Runtime auto-update is disabled so an image cannot cross into OpenCode 2.x after it is built.
The trusted configuration is `opencode/opencode.json`. It grants `permission: "allow"` globally
and to every built-in or custom agent that Agent CI can invoke. Repository-local OpenCode config
and plugins are disabled so a clone cannot replace the service policy. OpenCode's scanned home and
global configuration paths are root-owned and read-only, and external skill discovery is disabled,
so an agent cannot persist instructions for later repositories. CodeGraph, Context7, and `gh_grep`
are configured as MCP servers. Set `AGENTCI_CONTEXT7_API_KEY` to raise Context7 rate limits.
The `research` subagent has the same unrestricted permissions as every other agent. Its prompt
asks it to focus on external evidence, but this is guidance rather than an isolation boundary.
Agent CI initializes or refreshes CodeGraph before every turn and locally excludes `.codegraph/`
from Git.
### Security boundary
OpenCode does not provide an OS sandbox. It can execute shell commands, edit Git metadata and
environment files, access external directories, use loopback and network services, bind ports, and
invoke subagents without approval. It can access every shared workspace, development tool, runtime
credential, and mounted file readable by the non-root container user. Environment filtering is
only accidental-exposure hygiene and cannot protect readable files from shell commands.
Docker remains the OS boundary. The services run as non-root without added capabilities,
privileged mode, an unconfined seccomp/AppArmor profile, or a nested `bubblewrap` sandbox. The only
host bind mount is the read-only installer directory; there are no
writable host filesystem mounts. The OpenCode HTTP server is not published to the host, is password
protected, and is reachable by Agent CI over an internal Compose network.
## Development environments
`AGENTCI_INSTALL_SCRIPTS` is a comma-delimited ordered list of development environment installers.
The supplied `python` and `dotnet` scripts install only their runtimes; they can be replaced or
removed. Configure them with `AGENTCI_PYTHON_VERSION` and `AGENTCI_DOTNET_CHANNEL`:
```env
AGENTCI_INSTALL_SCRIPTS=python,dotnet,company-tools
AGENTCI_PYTHON_VERSION=3.13
AGENTCI_DOTNET_CHANNEL=10.0
```
Every name resolves to a file in `install-scripts/`, mounted read-only at
`/etc/agentci/install-scripts`. Names cannot contain paths and duplicates are rejected. Installers
run after each implementation clone or branch sync and fail the job on an unknown script, timeout,
or non-zero exit. They receive no Agent CI or Gitea secret values in their environment, but remain
trusted operator code. Tools persist under `/var/lib/agentci/dev-tools`, and OpenCode can read or
modify them through its unrestricted shell. See `install-scripts/README.md` for the script contract.
Agent CI continues to create branches, validate diffs, commit, and push after OpenCode returns. This
keeps workflow behavior deterministic, but unrestricted OpenCode is not prevented from running Git
commands itself.
## State and recovery
The `agentci_data` volume contains SQLite, workflow clones, and installed development runtimes.
The `opencode_home` volume contains provider authentication, OpenCode's database, and resumable
sessions. Tea's Gitea token configuration is regenerated in an ephemeral tmpfs and is not copied to
`opencode_home`. Back up both persistent volumes together.
The OpenCode migration tags existing workflows as Codex-owned and preserves their session IDs for
rollback, but OpenCode refuses to resume them. Follow-up commands against those workflows ask for a
new plan or implementation. Queued jobs survive restart. An in-progress job is aborted in OpenCode
and marked failed instead of being replayed because a partial model turn may already have changed
files. Git pushes are never forced.
## Development
Install and validate with:
```sh
uv sync
uv run ruff check .
uv run pyright
uv run pytest
docker compose config
./scripts/build.sh
```
The tests fail if any tracked Python file exceeds 250 lines. Prompts and JSON schemas live outside
Python so orchestration modules remain small and readable.