Work packages in. Merged pull requests out.
Polls OpenProject work packages and dispatches the pi coding agent to work on them. OpenProject is the system of record: the engine claims work, prepares a per-agent clone, renders a prompt, runs the agent, verifies the outcome against GitHub and OpenProject, and performs every status transition itself. Agents never change status.
Requirements
- Docker
- OpenProject 17 (API v3) with a project, plus one-time administrator access.
- GitHub repository for each project and a token that can push to it and merge pull requests.
Quick start
-
Complete the one-time instance setup — statuses, the
GIT_REPOproject custom field, theAGENTwork-package custom field and the Agent Worker role — in docs/OPENPROJECT_SETUP.md. -
In
docker/, run the interactive setup:git clone https://github.com/310networks/openproject-agent-engine op-agent-engine cd op-agent-engine/docker && ./agent-init.sh
Re-running is safe. It pulls the current image first, so it is also the upgrade step that creates any agents added by a new release. The next start syncs those agents'
AGENTS.mdstandards into the existing workspace volume.--reprompt-tokensasks for the OpenProject tokens again. -
Finish
docker/.envbefore starting the stack. AddGH_TOKEN(and the optionalGH_TOKEN_REVIEWER,GIT_REVIEWER_NAMEandGIT_REVIEWER_EMAILfor a dedicated reviewer account, ie: branch protection rules, see below). -
Start the stack:
docker compose up -d docker compose logs -f engine
-
Notes
After adding a project or changing its
GIT_REPO/AGENTsetup in OpenProject, rundocker compose restart engine. After changingdocker/.env, rundocker compose up -d engineso compose recreates the container with the new environment.
The dashboard is served at http://127.0.0.1:8765
(see docs/DASHBOARD.md).
Required environment variables
agent-init.sh writes the OpenProject settings, the agent tokens and the
provider key. It leaves the GitHub token(s) to you: add them to docker/.env
before starting the stack.
| Variable | Meaning |
|---|---|
OP_BASE_URL |
your OpenProject URL |
OP_PROJECT |
project identifier(s): one, a comma list, or * for every project with GIT_REPO |
OP_ENGINE_TOKEN |
API token of the runtime user (administrator) |
AGENT_MODEL |
model for every agent as <provider>/<model>; the provider also selects the credential or endpoint variable |
AGENT_MODEL_<AGENT> |
optional per-agent model (AGENT_MODEL_FRONTEND, ..._BACKEND, ..._DESIGNER, ..._VFX, ..._AUDIO, ..._REVIEWER) from the same provider |
OP_TOKEN_FRONTEND |
token of frontendEngineer |
OP_TOKEN_BACKEND |
token of backendEngineer |
OP_TOKEN_DESIGNER |
token of leadDesigner |
OP_TOKEN_VFX |
token of vfxArtist |
OP_TOKEN_AUDIO |
token of audioDesigner |
OP_TOKEN_REVIEWER |
token of codeReviewer |
OPENROUTER_API_KEY (or other provider key) |
one model provider key for pi |
LLAMA_BASE_URL |
llama.cpp router URL, when using the local llama.cpp provider |
GH_TOKEN |
GitHub token used to clone, push and look up pull requests |
For repositories with branch protection that requires the approving or merging
account to differ from the pull request's author, give codeReviewer its own
GitHub account: GH_TOKEN_REVIEWER (plus optional GIT_REVIEWER_NAME and
GIT_REVIEWER_EMAIL). The reviewer then switches gh/git to that account and
can submit an approving review before merging; without it every agent keeps
using GH_TOKEN. Add these values to docker/.env yourself; the setup script
no longer asks for GitHub tokens. If both tokens belong to the same account,
branch protection that requires a different approver still blocks the merge.
Each monitored project must have a project attribute named GIT_REPO
containing its GitHub repository
(owner/name or a github.com URL). Create it once in
Administration → Projects → Project attributes and choose For all
projects.
One attribute is assigned to every project, so there is no per-project enablement —
the same one-time step as the AGENT field. docker/agent-init.sh
explains this during setup.
The team is fixed in the image, one OpenProject account and one token each:
AGENT field value |
Login | Kind |
|---|---|---|
FRONTEND |
frontendEngineer |
implementer |
BACKEND |
backendEngineer |
implementer |
LEAD_DESIGNER |
leadDesigner |
implementer |
VFX |
vfxArtist |
implementer |
AUDIO |
audioDesigner |
implementer |
REVIEWER |
codeReviewer |
reviewer |
For reference, the model the script writes looks like
AGENT_MODEL=openrouter/deepseek/deepseek-v4.1-flash-1017.
A local llama.cpp router is a built-in
provider instead of a hosted key: set AGENT_MODEL=llama.cpp/<model> and
LLAMA_BASE_URL to the router URL (plus LLAMA_API_KEY if the server was
started with --api-key). The URL must be reachable from inside the engine
container; see docs/CONFIG.md.
Everything else — concurrency, timings, status names, paths and the shared git
identity — has a working default. See docs/CONFIG.md and the commented
docker/.env.example.
How a ticket flows
| Observed state | Engine action | Run | Verified outcome | Transition |
|---|---|---|---|---|
New, eligible, AGENT set |
claim, In Progress |
implement | open PR / BLOCKED |
Ready for Review / On Hold |
New, unfinished blocker(s) |
block, Blocked + comment |
— | — | Blocked |
Blocked, every blocker Closed |
claim, In Progress |
implement | open PR / BLOCKED |
Ready for Review / On Hold |
Ready for Review, PR exists |
claim, In Testing |
review | PR merged / REJECTED |
Closed / Rejected |
Ready for Review, no PR |
— | — | — | On Hold + comment |
Rejected, eligible |
claim, In Progress |
fix | as implement | as implement |
On Hold is terminal: the engine ignores the ticket until a human moves it.
Branches are op-<project>-<wp> (one per ticket). Every run uses its own git
worktree: a successful run removes it, a failed run's state is first captured
into runs/<runId>/worktree and the worktree is kept for post-mortem
(KEEP_FAILED_WORKTREES, FAILED_WORKTREE_MAX_MB); at most
MAX_RUNS_PER_AGENT runs per agent (default 1) share that agent's clone. Every
engine comment starts with ENGINE:.
Documentation
| Doc | Contents |
|---|---|
| docs/DOCKER.md | image internals, volumes, releases, troubleshooting |
| docs/OPENPROJECT_SETUP.md | statuses, GIT_REPO project attribute, AGENT work-package custom field, Agent Worker role, admin engine user |
| docs/CONFIG.md | every environment variable, provider keys, agent team, CLI reference |
| docs/DASHBOARD.md | read-only web UI |
| docs/RUNBOOK.md | status, pause, run inspection, On Hold handling, recovery |
| docs/ACCEPTANCE.md | fake-agent test and manual acceptance checklist |
| docs/CURRENT_STATE.md | implemented architecture, control flow, state and limits |
Contributing
Issues and pull requests are welcome. Pull requests run the test suite
(ruff, mypy, pytest). Changes to .github/, docker/ or
pyproject.toml need review from a code owner, see
.github/CODEOWNERS.
Security
Do not open a public issue for a vulnerability — use private reporting instead.
License
OpenProject Agent Engine is open source software released under the MIT License.

