Skip to content

Team-Atlanta/crs-opencode

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

crs-opencode

A CRS (Cyber Reasoning System) that uses opencode to autonomously find and patch vulnerabilities in open-source projects using open-source LLMs.

Given any boot-time subset of vulnerability evidence (POVs, bug-candidate reports, diff files, and/or seeds), the agent analyzes the inputs, edits source code, builds, tests, iterates, and writes one final patch for submission.

How it works

┌─────────────────────────────────────────────────────────────────────┐
│ patcher.py (orchestrator)                                           │
│                                                                     │
│  1. Fetch startup inputs & source                                    │
│     crs.fetch(POV/BUG_CANDIDATE/DIFF/SEED)                           │
│     crs.download(src)                                                │
│         │                                                            │
│         ▼                                                            │
│  2. Launch opencode with fetched paths + AGENTS.md                   │
│     opencode run --dangerously-skip-permissions                      │
│       --model <provider>/<model> ...                                 │
└─────────┬───────────────────────────────────────────────────────────┘
          │ prompt with startup evidence paths
          ▼
┌─────────────────────────────────────────────────────────────────────┐
│ opencode (autonomous agent, open-source models via LiteLLM)         │
│                                                                     │
│  ┌──────────┐    ┌──────────┐    ┌──────────────┐                   │
│  │ Analyze  │───▶│   Fix    │───▶│   Verify     │                   │
│  │          │    │          │    │              │                   │
│  │ Read     │    │ Edit src │    │ apply-patch  │──▶ Builder        │
│  │ startup  │    │ git diff │    │   -build     │    sidecar        │
│  │ evidence │    │          │    │              │◀── rebuild_id     │
│  └──────────┘    └──────────┘    │ run-pov ────│──▶ Runner          │
│                                  │   (all POVs)│◀── retcode         │
│                       ▲          │ apply-patch │──▶ Builder         │
│                       │          │   -test     │◀── retcode         │
│                       │          └──────┬───────┘                   │
│                       │                 │                           │
│                       └── retry ◀── fail?                           │
│                                         │ pass                      │
│                                         ▼                           │
│                              Write .diff to /patches/               │
└─────────────────────────────────────────────────────────────────────┘
          │
          ▼
┌─────────────────────────┐
│ patcher.py               │
│ submit(first patch) ───▶ oss-crs framework
└─────────────────────────┘
  1. run_patcher fetches available startup inputs (POV, BUG_CANDIDATE, DIFF, SEED) once, downloads source, and passes the fetched paths to the agent.
  2. The evidence is handed to opencode in a single session with generated AGENTS.md instructions. No additional inputs are fetched after startup.
  3. The agent autonomously analyzes evidence, edits source, and uses libCRS tools (apply-patch-build, run-pov, apply-patch-test) to iterate as needed through the builder sidecar.
  4. When the first final .diff is written to /patches/, the patcher submits that single file with crs.submit(DataType.PATCH, patch_path) and exits. Later patch files or modifications are ignored.

The agent is language-agnostic — it edits source and generates diffs while the builder sidecar handles compilation. The sanitizer type (address only in this CRS) is passed to the agent for context.

Open-source model routing

opencode natively targets <provider>/<model>. Because OSS-CRS exposes a LiteLLM proxy, this CRS declares LiteLLM as a custom opencode provider (@ai-sdk/openai-compatible) and points baseURL at OSS_CRS_LLM_API_URL. Any model listed in your LiteLLM config (Qwen, DeepSeek, Llama, gpt-oss, ...) becomes addressable as oss-crs/<model-name> inside opencode.

Project structure

patcher.py             # Patcher module: one-time fetch of optional inputs → agent → first-patch submit
pyproject.toml         # Package config (run_patcher entry point)
bin/
  compile_target       # Builder phase: compiles the target project
agents/
  opencode.py          # opencode agent (default)
  opencode.md          # AGENTS.md template with libCRS tool docs
  sections/            # Dynamic AGENTS.md section partial templates
  template.py          # Stub for creating new agents
oss-crs/
  crs.yaml             # CRS metadata (supported languages, models, etc.)
  example-compose.yaml # Example crs-compose configuration
  base.Dockerfile      # Base image: Ubuntu + Node.js + opencode CLI + Python
  builder.Dockerfile   # Build phase image
  patcher.Dockerfile   # Run phase image
  docker-bake.hcl      # Docker Bake config for the base image
  sample-litellm-config.yaml  # LiteLLM proxy config template (open-weight models)

Prerequisites

  • oss-crs — the CRS framework (crs-compose CLI)

Builder and runner sidecars are injected automatically by the framework — no separate builder setup is needed.

Quick start

1. Configure crs-compose.yaml

Copy oss-crs/example-compose.yaml and update the paths:

crs-opencode:
  source:
    local_path: /path/to/crs-opencode
  cpuset: "2-7"
  memory: "16G"
  llm_budget: 10
  additional_env:
    CRS_AGENT: opencode
    OPENCODE_MODEL: qwen3-coder-480b

llm_config:
  litellm:
    mode: internal
    internal:
      config_path: ./example/crs-opencode/litellm-config.yaml

2. LiteLLM setup

oss-crs/sample-litellm-config.yaml is a starting template that routes common open-weight models (Qwen 3 Coder, DeepSeek V3.1, Llama 3.3, gpt-oss-120b) through an OpenAI-compatible backend such as vLLM, TGI, or a hosted open-source gateway. Point LiteLLM at your own endpoint by exporting:

export OSS_CRS_OPEN_WEIGHT_API_BASE=https://my-vllm-host/v1
export OSS_CRS_OPEN_WEIGHT_API_KEY=sk-...

3. Run with oss-crs

crs-compose up -f crs-compose.yaml

Configuration

Environment variable Default Description
CRS_AGENT opencode Agent module name (maps to agents/<name>.py)
OPENCODE_MODEL qwen3-coder-480b Bare model id (will be scoped under the provider) or full provider/model
OPENCODE_PROVIDER_ID oss-crs opencode provider id bound to the LiteLLM proxy
OPENCODE_MODELS (empty) Comma/whitespace list of additional model ids to register in opencode config
OPENCODE_AGENT_NAME (empty) Optional opencode --agent name
OPENCODE_VERSION 1.4.11 Prepare-phase version pin for the opencode CLI
AGENT_TIMEOUT 0 (no limit) Agent timeout in seconds (0 = run until budget exhausted)

Runtime behavior

  • Execution: opencode run --dangerously-skip-permissions --model <provider>/<model> --dir <src> (non-interactive, auto-approves any permission prompt).
  • Instruction file: AGENTS.md generated per run in the target repo.
  • LiteLLM proxy: Framework provides OSS_CRS_LLM_API_URL + OSS_CRS_LLM_API_KEY; the agent injects them into ~/.config/opencode/opencode.json as a custom @ai-sdk/openai-compatible provider so any LiteLLM-listed model is reachable as <provider>/<model>.

Debug artifacts:

  • Log directories: ~/.local/share/opencode, ~/.local/state/opencode, ~/.config/opencode (registered via register-log-dir).
  • Per-run logs: /work/agent/opencode_stdout.log, /work/agent/opencode_stderr.log.

Patch submission

The agent is instructed to satisfy these criteria before writing a patch:

  1. Builds — compiles successfully
  2. POVs don't crash — all provided POV variants pass (if POVs were provided)
  3. Tests pass — project test suite passes (or skipped if none exists)
  4. Semantically correct — fixes the root cause with a minimal patch

Runtime remains trust-based: the patcher does not re-run final verification. Once the first .diff is written to /patches/, the patcher submits that single file and exits. Submitted patches cannot be edited or resubmitted, so the agent should only write to /patches/ when it considers the patch final.

Adding a new agent

  1. Copy agents/template.py to agents/my_agent.py.
  2. Implement setup() and run().
  3. Set CRS_AGENT=my_agent.

The agent receives:

  • setup(source_dir, config) config keys:
    • llm_api_url — optional LiteLLM base URL
    • llm_api_key — optional LiteLLM key
    • opencode_config_dir / opencode_data_dir / opencode_state_dir — XDG paths opencode writes into
  • source_dir — clean git repo of the target project
  • pov_dir — boot-time POV input directory (may be empty)
  • bug_candidate_dir — boot-time bug-candidate directory (may be empty)
  • diff_dir — boot-time diff directory (may be empty)
  • seed_dir — boot-time seed directory (may be empty)
  • harness — harness name for run-pov
  • patches_dir — write exactly one final .diff here
  • work_dir — scratch space
  • language — target language (c, c++, jvm)
  • sanitizer — sanitizer type (address only) All optional inputs are boot-time only. The patcher fetches them once and passes directory paths to the agent; no new POVs, bug-candidates, diff files, or seeds appear during the run.

The agent has access to three libCRS commands (builder/runner sidecars are resolved automatically via BUILDER_MODULE env var set by the framework):

  • libCRS apply-patch-build <patch.diff> <response_dir> — build a patch
  • libCRS run-pov <pov> <response_dir> --harness <h> [--rebuild-id <id>] — test against a POV (omit --rebuild-id for base build)
  • libCRS apply-patch-test <patch.diff> <response_dir> — run the project's test suite

For transparent diagnostics, always inspect response_dir logs:

  • Build: stdout.log, stderr.log, retcode
  • POV: stdout.log, stderr.log, retcode
  • Test: stdout.log, stderr.log, retcode

About

crs-opencode

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors