Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,17 @@ ANTHROPIC_API_KEY=sk-ant-api03-...
# OpenRouter (recommended - 200+ models including DeepSeek, Qwen, Llama, MiniMax)
# OPENROUTER_API_KEY=sk-or-v1-...

# OpenAI (GPT-4, GPT-4o, etc.)
# OpenAI API-platform billing for OpenAI models and Codex api_key mode.
# OPENAI_API_KEY=sk-...

# Codex CLI runtime auth. Values:
# auto Use OPENAI_API_KEY when set; otherwise use local Codex login.
# chatgpt Use ChatGPT Free/Plus/Pro/Team login. Run `codex login` on the
# host, keep OPENAI_API_KEY unset for this process, and Docker will
# mount ~/.codex into both swe-planner and swe-fast.
# api_key Use OpenAI API-platform billing. Set OPENAI_API_KEY=sk-...
# SWE_CODEX_AUTH_MODE=auto

# Google Gemini
# GOOGLE_API_KEY=...

Expand Down Expand Up @@ -66,7 +74,7 @@ GH_TOKEN=ghp_...
# Lets the deployer pick the runtime once instead of every caller threading
# a config through. Falls back to claude_code if unset; an invalid value is
# logged as a warning and ignored.
# SWE_DEFAULT_RUNTIME=claude_code # or: open_code
# SWE_DEFAULT_RUNTIME=claude_code # or: open_code, codex

# Default model when callers don't pass `models` in the request config.
# Applies to all 16 agent roles for whichever runtime is active. Caller
Expand All @@ -78,7 +86,7 @@ GH_TOKEN=ghp_...

# Runtime/model selection is configured via API request config (V2):
# {
# "runtime": "claude_code" | "open_code",
# "runtime": "claude_code" | "open_code" | "codex",
# "models": {
# "default": "sonnet or provider/model-id",
# "coder": "provider/model-id",
Expand All @@ -89,6 +97,7 @@ GH_TOKEN=ghp_...
# Runtime mapping:
# claude_code -> Claude backend
# open_code -> OpenCode backend
# codex -> OpenAI Codex CLI backend
#
# Legacy keys are removed: ai_provider, preset, model, and all *_model fields.
#
Expand All @@ -97,6 +106,9 @@ GH_TOKEN=ghp_...
#
# Example Claude runtime request config:
# {"runtime": "claude_code", "models": {"default": "sonnet", "coder": "opus"}}
#
# Example Codex runtime request config:
# {"runtime": "codex", "models": {"default": "gpt-5.3-codex"}}

# Available open runtime model IDs (format: provider/model-name):
# deepseek/deepseek-chat # DeepSeek via OpenRouter
Expand Down
33 changes: 32 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ WORKDIR /app
# System deps: git (worktrees, branches), curl (healthcheck), jq (agent bash),
# openssh-client (optional SSH git), gh CLI (draft PRs)
RUN apt-get update && apt-get install -y --no-install-recommends \
git curl openssh-client jq && \
git curl openssh-client jq nodejs npm && \
# Install GitHub CLI
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
| dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg && \
Expand All @@ -17,6 +17,37 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
apt-get update && apt-get install -y --no-install-recommends gh && \
# Install OpenCode CLI v1.2+ for opencode provider (with run --model support)
curl -fsSL https://opencode.ai/install | bash && \
# Install Codex CLI for codex runtime provider
npm install -g @openai/codex && \
codex_path="$(command -v codex)" && \
mv "${codex_path}" /usr/local/bin/codex-real && \
printf '%s\n' \
'#!/usr/bin/env bash' \
'set -euo pipefail' \
'' \
'auth_mode="${SWE_CODEX_AUTH_MODE:-auto}"' \
'' \
'case "${auth_mode}" in' \
' chatgpt)' \
' unset OPENAI_API_KEY' \
' ;;' \
' api_key)' \
' if [ -z "${OPENAI_API_KEY:-}" ]; then' \
' echo "SWE_CODEX_AUTH_MODE=api_key requires OPENAI_API_KEY to be set" >&2' \
' exit 2' \
' fi' \
' ;;' \
' auto)' \
' ;;' \
' *)' \
' echo "Invalid SWE_CODEX_AUTH_MODE: ${auth_mode}. Expected one of: auto, chatgpt, api_key" >&2' \
' exit 2' \
' ;;' \
'esac' \
'' \
'exec /usr/local/bin/codex-real "$@"' \
> /usr/local/bin/codex && \
chmod +x /usr/local/bin/codex && \
rm -rf /var/lib/apt/lists/*

# Add OpenCode to PATH for non-interactive shells
Expand Down
58 changes: 56 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,10 @@ Most agent frameworks wrap a single coder loop. SWE-AF is a coordinated engineer

</details>

**Claude & open-source models supported**: Run builds with either runtime and tune models per role in one flat config map.
**Claude, open-source, and Codex models supported**: Run builds with any runtime and tune models per role in one flat config map.
- `runtime: "claude_code"` maps to Claude backend.
- `runtime: "open_code"` maps to OpenCode backend (OpenRouter/OpenAI/Google/Anthropic model IDs).
- `runtime: "codex"` maps to the OpenAI Codex CLI backend.

## Adaptive Factory Control

Expand Down Expand Up @@ -279,6 +280,42 @@ curl -X POST http://localhost:8080/api/v1/execute/async/swe-planner.build \
}
JSON

# With Codex CLI runtime
curl -X POST http://localhost:8080/api/v1/execute/async/swe-planner.build \
-H "Content-Type: application/json" \
-d @- <<'JSON'
{
"input": {
"goal": "Add JWT auth",
"repo_url": "https://github.com/user/my-project",
"config": {
"runtime": "codex",
"models": {
"default": "gpt-5.3-codex"
}
}
}
}
JSON

# Fast mode with Codex CLI runtime
curl -X POST http://localhost:8080/api/v1/execute/async/swe-fast.build \
-H "Content-Type: application/json" \
-d @- <<'JSON'
{
"input": {
"goal": "Add a focused bug fix",
"repo_url": "https://github.com/user/my-project",
"config": {
"runtime": "codex",
"models": {
"default": "gpt-5.3-codex"
}
}
}
}
JSON

# Local workspace mode (repo_path) + targeted role override
curl -X POST http://localhost:8080/api/v1/execute/async/swe-planner.build \
-H "Content-Type: application/json" \
Expand All @@ -303,6 +340,12 @@ JSON

For OpenRouter with `open_code`, use model IDs in `openrouter/<provider>/<model>` format (for example `openrouter/minimax/minimax-m2.5`).

For Codex with ChatGPT subscription auth, install the Codex CLI on the host, run `codex login`, leave `OPENAI_API_KEY` unset for this process, and set `SWE_CODEX_AUTH_MODE=chatgpt` or `auto`. For OpenAI API-platform billing, set `SWE_CODEX_AUTH_MODE=api_key` and `OPENAI_API_KEY`.

> **Codex deployments using the Docker image must set `SWE_DEFAULT_MODEL=gpt-5.3-codex` on the environment** (or pass `models: {"default": "gpt-5.3-codex"}` in every build's `config`). The image bakes `HARNESS_MODEL=openrouter/moonshotai/kimi-k2.6` as an OpenCode fallback, and SWE-AF's model-resolution env cascade reads `HARNESS_MODEL` — so without `SWE_DEFAULT_MODEL` set, the Codex CLI receives an OpenRouter model id it can't handle and the Product Manager reasoner fails in ~13s. Setting `SWE_DEFAULT_MODEL` makes the cascade pin every role to the Codex model.

> Codex CLI's `workspace-write` sandbox uses bubblewrap (`bwrap`) and needs Linux user namespaces enabled on the host. Most production Linux hosts and managed container runtimes (Railway, etc.) allow this by default, but local Docker on WSL2 or hardened environments may refuse with `bwrap: No permissions to create a new namespace`. If the verifier reports that error, the coder ran but couldn't write files — enable user namespaces on the host before relying on the codex runtime there.

### Optional: web search

Coding and review agents can look up external documentation, library APIs, error messages, and version/deprecation status during a build. This is opt-in via two env vars on the deployment:
Expand Down Expand Up @@ -611,7 +654,7 @@ Pass `config` to `build` or `execute`. Full schema: [`swe_af/execution/schemas.p

| Key | Default | Description |
| ------------------------- | --------------- | ----------------------------------------------------- |
| `runtime` | `"claude_code"` | Model runtime: `"claude_code"` or `"open_code"`. The default also honors the `SWE_DEFAULT_RUNTIME` env var when no `runtime` is passed in `config` — set it on the deployment so callers don't need to plumb a config through. |
| `runtime` | `"claude_code"` | Model runtime: `"claude_code"`, `"open_code"`, or `"codex"`. The default also honors the `SWE_DEFAULT_RUNTIME` env var when no `runtime` is passed in `config` — set it on the deployment so callers don't need to plumb a config through. |
| `models` | `null` | Flat role-model map (`default` + role keys below). Without a caller-supplied value, the `SWE_DEFAULT_MODEL` env var is used as the default for all roles — set it on the deployment to pin a model without code changes. Caller `models.default` or per-role keys still win. |
| `max_coding_iterations` | `5` | Inner-loop retry budget |
| `max_advisor_invocations` | `2` | Middle-loop advisor budget |
Expand Down Expand Up @@ -655,6 +698,17 @@ Minimal:
}
```

Codex:

```json
{
"runtime": "codex",
"models": {
"default": "gpt-5.3-codex"
}
}
```

Fully customized:

```json
Expand Down
5 changes: 5 additions & 0 deletions docker-compose.local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,15 @@ services:
- PORT=8003
# Callback URL for control plane to reach this agent
- AGENT_CALLBACK_URL=http://localhost:8003
- SWE_DEFAULT_RUNTIME=${SWE_DEFAULT_RUNTIME:-claude_code}
- SWE_DEFAULT_MODEL=${SWE_DEFAULT_MODEL:-}
- SWE_CODEX_AUTH_MODE=${SWE_CODEX_AUTH_MODE:-auto}
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
ports:
- "8003:8003"
volumes:
- workspaces:/workspaces
- ${HOME}/.codex:/root/.codex
extra_hosts:
- "host.docker.internal:host-gateway"

Expand Down
10 changes: 10 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,15 @@ services:
- NODE_ID=swe-planner
- PORT=8003
- AGENT_CALLBACK_URL=http://swe-agent:8003
- SWE_DEFAULT_RUNTIME=${SWE_DEFAULT_RUNTIME:-claude_code}
- SWE_DEFAULT_MODEL=${SWE_DEFAULT_MODEL:-}
- SWE_CODEX_AUTH_MODE=${SWE_CODEX_AUTH_MODE:-auto}
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
ports:
- "8003:8003"
volumes:
- workspaces:/workspaces
- ${HOME}/.codex:/root/.codex
depends_on:
- control-plane
deploy:
Expand All @@ -33,6 +38,7 @@ services:
context: .
dockerfile: Dockerfile
command: ["python", "-m", "swe_af.fast"]
env_file: .env
environment:
- AGENTFIELD_SERVER=http://control-plane:8080
- NODE_ID=swe-fast
Expand All @@ -45,10 +51,14 @@ services:
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
- GOOGLE_API_KEY=${GOOGLE_API_KEY:-}
- OPENCODE_MODEL=${OPENCODE_MODEL:-}
- SWE_DEFAULT_RUNTIME=${SWE_DEFAULT_RUNTIME:-claude_code}
- SWE_DEFAULT_MODEL=${SWE_DEFAULT_MODEL:-}
- SWE_CODEX_AUTH_MODE=${SWE_CODEX_AUTH_MODE:-auto}
ports:
- "8004:8004"
volumes:
- workspaces:/workspaces
- ${HOME}/.codex:/root/.codex
depends_on:
- control-plane

Expand Down
3 changes: 2 additions & 1 deletion docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ SWE-AF orchestrates 22 specialized agents across four phases. Each agent is a re

Every build now uses a single V2 model contract:

- `runtime`: `claude_code` or `open_code`
- `runtime`: `claude_code`, `open_code`, or `codex`
- `models`: flat role map (`default` + explicit role keys)

Supported role keys:
Expand All @@ -442,3 +442,4 @@ Runtime defaults:
|---|---|---|
| `claude_code` | `sonnet` | `qa_synthesizer=haiku` |
| `open_code` | `minimax/minimax-m2.5` | none |
| `codex` | `gpt-5.3-codex` | none |
2 changes: 1 addition & 1 deletion docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Thanks for contributing to SWE-AF.

- Python 3.12+
- AgentField control plane (`af`)
- Access to an AI runtime used by your run (`claude_code` or `open_code`)
- Access to an AI runtime used by your run (`claude_code`, `open_code`, or `codex`)

## Local setup

Expand Down
8 changes: 7 additions & 1 deletion docs/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ curl -X POST http://localhost:8080/api/v1/execute/async/swe-planner.build \

| Key | Values | Description |
|-----|--------|-------------|
| `runtime` | `"claude_code"`, `"open_code"` | AI backend to use |
| `runtime` | `"claude_code"`, `"open_code"`, `"codex"` | AI backend to use |
| `models.default` | model ID string | Default model for all agents |
| `models.coder` | model ID string | Override for coder role |
| `models.qa` | model ID string | Override for QA role |
Expand Down Expand Up @@ -148,6 +148,12 @@ curl -X POST http://localhost:8080/api/v1/execute/async/swe-planner.build \
2. Model provider credentials configured in OpenCode (e.g., `OPENAI_API_KEY` for z.ai)
3. Model ID format matches what OpenCode expects

## Requirements for codex Runtime

1. Codex CLI installed and in PATH.
2. For ChatGPT subscription auth: run `codex login` on the host, set `SWE_CODEX_AUTH_MODE=chatgpt` or `auto`, and leave `OPENAI_API_KEY` unset for the agent process.
3. For OpenAI API-platform billing: set `SWE_CODEX_AUTH_MODE=api_key` and `OPENAI_API_KEY`.

## Monitoring

```bash
Expand Down
10 changes: 10 additions & 0 deletions docs/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ cp .env.example .env
| `OPENAI_API_KEY` | OpenAI API key |
| `GOOGLE_API_KEY` | Google Gemini API key |

**For Codex CLI runtime:**

| Variable | Purpose |
|---|---|
| `SWE_CODEX_AUTH_MODE` | `auto`, `chatgpt`, or `api_key`; defaults to `auto` in Docker |
| `OPENAI_API_KEY` | Required only when `SWE_CODEX_AUTH_MODE=api_key` |

**Optional:**

| Variable | Purpose | Default |
Expand All @@ -51,6 +58,7 @@ cp .env.example .env
| `agentfield` | 0.1.67+ | Python SDK (includes opencode v1.4+ fix) |
| `claude-agent-sdk` | 0.1.20+ | Claude runtime |
| opencode CLI | 1.4+ | Only if using `open_code` runtime (see Known Issues) |
| Codex CLI | latest | Installed in the Docker image; required on host only to run `codex login` for ChatGPT subscription auth |

## Quick Start

Expand All @@ -68,6 +76,8 @@ This starts:
- **swe-agent** on `:8003` — SWE-AF full pipeline (`swe-planner` node)
- **swe-fast** on `:8004` — SWE-AF fast mode (`swe-fast` node)

To use Codex with a ChatGPT subscription, run `codex login` on the host before starting Docker and leave `OPENAI_API_KEY` unset for this process. The compose files mount `~/.codex` into both agent containers. To use OpenAI API billing instead, set `SWE_CODEX_AUTH_MODE=api_key` and `OPENAI_API_KEY`.

### Agent Only (connect to existing control plane)

If you already have an AgentField control plane running:
Expand Down
3 changes: 3 additions & 0 deletions requirements-docker.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ pydantic>=2.0
claude-agent-sdk==0.1.20
hax-sdk>=0.2.0
python-dotenv>=1.0
# cryptography 48.0.0 currently crashes with SIGILL on some Linux/aarch64
# Docker hosts when AgentField imports Ed25519 for DID registration.
cryptography<46
3 changes: 2 additions & 1 deletion swe_af/execution/_replanner_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
)
from swe_af.prompts.replanner import SYSTEM_PROMPT, replanner_task_prompt
from swe_af.reasoners import router
from swe_af.runtime.providers import runtime_to_harness_adapter


async def invoke_replanner(
Expand All @@ -39,7 +40,7 @@ async def invoke_replanner(
if dag_state.artifacts_dir
else None
)
provider = "claude-code" if config.ai_provider == "claude" else config.ai_provider
provider = runtime_to_harness_adapter(config.ai_provider)

try:
result = await router.harness(
Expand Down
Loading