Skip to content
Nikoo-AsadnejadPublic

About

Devagent turns a GitHub issue into an implemented draft pull request. Send an open issue URL to one endpoint. Devagent reads the issue, clones its repository, creates a new branch, runs Codex, verifies the result, pushes the branch, and opens a draft PR for human review.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

10 Commits

Folders and files

Repository files navigation

DevAgent

DevAgent turns a GitHub issue into a draft pull request. Built with Python, FastAPI, LangGraph, Pydantic, OpenHands, Docker, and Gitleaks, it clones the repository and creates an isolated sandbox for each task. OpenHands uses the Agent Client Protocol (ACP) to run a local coding agent—currently Codex—inside that sandbox. DevAgent then builds, tests, and scans the changes before opening a draft PR for review.

Design: docs/superpowers/specs/2026-09-26-devagent-design.md.

Flow diagrams

Request flow

Request flow

Implementation workflow

Implementation workflow

GitHub issue to draft PR sequence

GitHub issue to draft PR sequence

Runtime request sequence

sequenceDiagram
    participant Client
    participant API as FastAPI API
    participant Worker
    participant GitHub as GitHub adapters
    participant Graph as LangGraph workflow
    participant Git as GitOps
    participant Sandbox as Docker sandbox
    participant Codex as CodexEngine

    Client->>API: POST /issues
    API->>API: Validate bearer token and JSON
    API->>Worker: submit_issue(issue_url)
    Worker->>GitHub: Read issue
    GitHub-->>Worker: Task
    Worker->>Worker: Validate status, duplicates, repo, budget
    Worker-->>API: RunRecord(status=queued)
    API-->>Client: 202 + run_id

    Worker->>Graph: Start queued run
    Graph->>Graph: claim: assign branch and mark running
    Graph->>Git: prepare: clone repository and create branch
    Graph->>Sandbox: implement: start container
    Sandbox->>Codex: Run coding agent
    Codex-->>Sandbox: Edited files
    Graph->>Git: verify: commit and inspect diff
    Graph->>Sandbox: Run build and tests
    Graph->>GitHub: publish: push branch and create draft PR
    Graph-->>Worker: done or failed
Loading

The implementation chain remains claim → prepare → implement → verify → publish. Any failed step goes to fail and records the error in the run API. Issues and their comments are read-only. A failed run can be retried manually.

Submit an issue

The issue must be open and its owner/repository must appear in config/agent.yaml. For local requests from your shell, load the same .env used to start the service:

set -a
source .env
set +a

curl -X POST http://127.0.0.1:8080/issues \
  -H "Authorization: Bearer $DEVAGENT_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"issue_url":"https://github.com/acme/booking-api/issues/42"}'

Accepted response:

{"run_id":"90ed2bdaf65a","status":"queued"}

No GitHub label is required. Every run uses Codex.

The endpoint returns:

  • 202 when the run is queued.
  • 400 for an invalid issue URL.
  • 404 when GitHub cannot find the issue.
  • 409 when the issue was already submitted in the current service process.
  • 422 for a closed issue or an unconfigured repository.
  • 429 when the daily run budget is exhausted.

Configuration

Copy the examples, then put the secret values in the Git-ignored root .env file:

cp config/agent.example.yaml config/agent.yaml
cp .env.example .env
# Edit .env to set GITHUB_TOKEN, DEVAGENT_ADMIN_TOKEN, and CODEX_AUTH_JSON.
uv run --env-file .env devagent check-config --config config/agent.yaml
uv run --env-file .env devagent serve --config config/agent.yaml

CODEX_AUTH_JSON is the one-line JSON content of ~/.codex/auth.json after codex login. Keep the single quotes around that value in .env so its JSON quotes survive parsing. uv run --env-file .env loads the file for that command; uv run alone does not load it. Use a long random value for DEVAGENT_ADMIN_TOKEN. Never commit .env or paste token values into issues.

Minimal configuration:

github:
  token: ${GITHUB_TOKEN}

service:
  admin_token: ${DEVAGENT_ADMIN_TOKEN}

repos:
  - repo: {owner: acme, name: booking-api}
    base: main
    commands:
      bootstrap: "dotnet restore"
      build: "dotnet build --no-restore"
      test: "dotnet test --no-build"

GITHUB_TOKEN should be a fine-grained token scoped only to configured repositories, with:

  • Contents: read and write (clone and push).
  • Issues: read (read the task and existing comments).
  • Pull requests: read and write (find or create the draft PR).
  • Metadata: read (included by GitHub for fine-grained tokens).

The GitHub token stays in the orchestrator. It is not passed to the coding-agent sandbox.

What happens during a run

  1. POST /issues fetches the issue, rejects closed, duplicate, or unconfigured issues, applies the daily budget, creates a queued run, and returns its ID.
  2. Claim marks the run as running and assigns a unique agent/<owner-repo-issue>-<slug>-<run> branch name.
  3. Prepare clones the configured base branch, creates the new local branch, detects .NET requirements, and selects or builds a cached toolchain image. The run retains its immutable Docker image ID.
  4. Implement runs the optional bootstrap on the unchanged base, then lets Codex edit the working tree in an OpenHands sandbox.
  5. Verify commits locally, rejects protected paths, symlinks, gitlinks, nested repositories and secrets, then repeats the optional bootstrap and runs the configured build and test commands in a fresh sandbox.
  6. Publish pushes only the new agent/* branch, opens a draft PR, and records its URL in the run API.
  7. Fail records the failed node and a redacted error in the run API. It never pushes a failed result.

Guardrails

  • Never pushes to an existing branch or outside agent/*; never force-pushes.
  • Never merges, approves, closes, or marks a pull request ready.
  • Never writes to issues or their comments; it only reads them.
  • Restricts GitHub HTTP calls to issue/comment reads and draft-PR reads/creation.
  • Keeps GitHub credentials out of the sandbox, prompt, repository config, and logs.
  • Rejects protected paths and secret-bearing diffs before push.
  • Applies concurrent-run, daily-run, wall-clock, and optional cost limits.

Pushing an agent/* branch can trigger repository CI. Configure branch policies and workflows so agent branches require review and cannot run untrusted changes with production secrets.

Run API

All run endpoints require Authorization: Bearer $DEVAGENT_ADMIN_TOKEN:

  • GET /runs
  • GET /runs/{id} — includes per-node events
  • POST /runs/{id}/retry — only failed runs; creates a fresh run and branch

Runs, events, and LangGraph checkpoints are held in memory and are lost when the orchestrator restarts. Duplicate protection applies only within the current process; check existing PRs before resubmitting after a restart.

Docker Compose

cp docker/compose.example.env docker/.env
cp config/agent.example.yaml config/agent.yaml

docker compose -f docker/compose.yaml --env-file docker/.env up -d --build

Compose builds the orchestrator and the common devagent-sandbox:latest image with OpenHands, Codex ACP, and ICU (International Components for Unicode, used by .NET for culture-aware formatting, sorting, and casing).

After cloning, DevAgent reads global.json first and honors sdk.version, rollForward, and allowPrerelease. Otherwise it chooses a stable SDK for the highest detected target framework. It reads TargetFramework and TargetFrameworks from project files and the nearest Directory.Build.props, including simple property references, empty-property defaults, and literal local property imports. Older target frameworks receive additional ASP.NET Core runtimes, which also include the .NET runtime. SDK and runtime versions come from Microsoft's release metadata.

Toolchain layers are cached by base image ID, detected requirements, and build recipe. Cache hits need no metadata lookup or build. A cached resolution stays pinned until its image is removed or the base/requirements/recipe changes. Implementation and verification both use the selected image ID, even if a tag or project file changes during the run. Repositories without .NET projects use the common image directly.

Detection does not execute repository MSBuild code. Unresolved/dynamic properties, conflicting SDK policies, explicit RuntimeFrameworkVersion, sdk.paths, and platform-specific or .NET Framework targets require a custom image. Set sandbox.detect_dotnet: false alongside sandbox.image to use that image directly. Remove the old DOTNET_CHANNEL and INSTALL_DOTNET build settings; toolchains are now selected per repository.

Codex uses a subscription login: run codex login and set CODEX_AUTH_JSON to the contents of ~/.codex/auth.json.

Components

The API accepts an issue, the worker queues a run, and the workflow coordinates the adapters and runtime tools. The orchestrator owns GitHub access and Git operations; the sandbox provides a workspace where Codex can edit files.

Entry point and request handling

  • main.py implements devagent serve and devagent check-config. On startup it loads the configuration, cleans up orphaned sandboxes from this deployment, builds the dependencies, starts worker threads, and serves the API.
  • app/api.py defines the FastAPI routes. It checks the admin bearer token for issue and run operations, translates intake errors into HTTP responses, and exposes an unauthenticated /health endpoint.
  • app/worker.py validates submitted issues against their GitHub status, earlier runs, configured repositories, and the daily budget. It stores accepted runs, queues them, and processes them with up to max_concurrent worker threads. A retry creates a new run for a failed one.
  • app/factories.py constructs the GitHub tracker, GitHub code host, and Codex engine. It also collects configured secret values for redaction.

Workflow

  • workflow/state.py defines the data passed between steps: the task, branch, checkout path, engine result, verification result, PR, and any failure. Its Deps bundle supplies the adapters and runtime tools to each step.
  • workflow/graph.py connects the steps with LangGraph, records start/finish/error events, checkpoints state in memory, and routes a step error to fail. It does not retry steps automatically.
  • workflow/nodes/claim.py chooses the new branch and marks the run as running. prepare.py clones the configured base branch and creates that branch locally.
  • workflow/nodes/implement.py builds the prompt, optionally runs the repository bootstrap command, and asks Codex to implement the issue inside a sandbox. verify.py commits the changes locally, checks paths, file types, and secrets, then repeats the optional bootstrap and runs the build and test commands in a fresh sandbox.
  • workflow/nodes/publish.py pushes the verified branch, creates or reuses its draft PR, records the PR URL, and completes the run. fail.py records the failure and a redacted error. shared.py contains common repository and command helpers.

GitHub and coding adapters

  • providers/trackers/ defines the task-tracker interface and its GitHub issue adapter. The adapter parses issue URLs and provides read-only access to issues and comments.
  • providers/code_hosts/ defines repository and PR operations. Its GitHub adapter supplies the clone URL and push authentication, finds open PRs, and creates draft PRs.
  • providers/engines/ defines the coding-engine interface. CodexEngine runs Codex through OpenHands and ACP, applies time and optional cost limits, and returns a redacted result.

Configuration, safety, and runtime

  • core/config.py loads agent.yaml, substitutes ${VAR} values from the environment, and validates GitHub, Codex, repository, budget, guardrail, sandbox, Git identity, and service settings. core/models.py holds the shared task, repository, PR, run, and engine data models.
  • core/slug.py creates branch-safe names from issue details. core/guardrails.py checks push targets, changed paths and file types, daily budgets, and known secrets; it also redacts those secrets from output.
  • runtime/sandbox.py starts a separate OpenHands Docker container per sandbox session. The repository is writable there, but .git is read-only; the orchestrator removes the container when the session ends and reaps orphaned containers at startup.
  • core/dotnet.py inspects repository toolchain requirements and applies SDK matching policies. runtime/sandbox_images.py resolves official releases and builds cached toolchain layers from a trusted recipe with no repository files in the Docker build context.
  • runtime/gitops.py performs clone, branch, commit, diff, and push operations in the orchestrator with isolated Git settings. runtime/http.py allows provider HTTP requests only when their method and path match an explicit rule.
  • runtime/secretscan.py runs Gitleaks on the diff and fails verification if scanning fails. runtime/logging.py emits structured JSON logs with known secrets masked.
  • runtime/storage.py opens the process-local run store and LangGraph's in-memory checkpointer. db/tables.py defines the SQLite run and event tables, while db/store.py creates, updates, lists, and retries runs. SQLite is in memory, so these records and checkpoints disappear when the process stops.
  • prompts/ combines the issue and comments with local coding rules. It marks issue text as untrusted task data before sending the prompt to Codex.

Deployment and supporting files

  • config/agent.example.yaml shows repository commands and service settings; .env.example lists the environment variables used for secrets. Copy these to the ignored config/agent.yaml and .env files for a local service.
  • docker/compose.yaml runs the orchestrator and Caddy and builds the sandbox image. docker/orchestrator.Dockerfile packages the API, worker, Docker CLI, and Gitleaks; docker/sandbox.Dockerfile packages the OpenHands agent server, Codex ACP adapter, and ICU common dependencies. docker/Caddyfile proxies only /issues and /health.
  • tests/ covers configuration, the API, issue intake, and the GitHub tracker. docs/ contains the design specification and the diagrams shown above.

Next implementations

These capabilities were intentionally removed from the first version and can be added after the GitHub-to-PR flow is stable:

  • PostgreSQL persistence for runs, events, and LangGraph checkpoints, including restart recovery.
  • GitHub webhooks or polling for automatic issue intake.
  • Additional task and code-host providers: ClickUp, Trello, and Azure DevOps.
  • Additional coding engines, including Claude Code, with explicit engine selection.

Development

uv sync
uv run pytest
uv run pyright
uv run --env-file .env devagent check-config --config config/agent.yaml

About

Devagent turns a GitHub issue into an implemented draft pull request. Send an open issue URL to one endpoint. Devagent reads the issue, clones its repository, creates a new branch, runs Codex, verifies the result, pushes the branch, and opens a draft PR for human review.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages