Run an AI coding agent headless in CI
Install the oharness package and run oharness-headless "<task>": it streams one JSON event per line to stdout and exits 1 if the run failed. Nobody can answer a permission prompt in CI, so anything that would ask is denied — pre-authorise the work with rules or --permission-mode.
Updated
Diese Seite ist auf Englisch geschrieben. Die Begriffe, die sie erklärt, werden auf Englisch gesucht, und ein übersetzter Fachbegriff ist ein anderer Begriff.
The command
oharness-headless -m deepseek/deepseek-v4-flash "run the tests and fix what fails"The prompt can come from an argument or from stdin (echo "task" | oharness-headless). -C <dir> sets the working directory, -r <id> continues a session, and --permission-mode is one of default, acceptEdits, plan, auto or bypassPermissions.
What comes out
stdout is JSONL and nothing else, so it pipes into jq; diagnostics go to stderr. The stream ends with a done event carrying the stop reason, usage, cost and turn count:
{"type":"session","id":"20260808_...","model":"deepseek-v4-flash","cwd":"/repo"}
{"type":"tool_start","toolCallId":"c1","toolName":"grep","title":"grep(build)","input":{...}}
{"type":"text_delta","text":"The build entry point is "}
{"type":"done","stopReason":"end_turn","usage":{...},"costUsd":0.004,"turns":2}Permissions without a human
A run that silently approved everything would be more dangerous unattended than attended, so headless does the opposite: a tool that would prompt is denied and reported as a permission_result event. Allow what the job needs in the project's .oharness/settings.json, where it is reviewable in the repository:
{
"allow": ["Bash(npm test*)", "Bash(npm run lint*)", "edit", "write"],
"deny": ["Bash(git push*)", "Bash(rm -rf *)", "write(.env*)"]
}deny wins over every mode, including bypassPermissions. Questions the agent would put to a user return at once with an instruction to choose a sensible default and say so, and still appear in the stream as question_request and question_result.
Credentials in CI
Use the provider's key variable as a CI secret — DEEPSEEK_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY and so on. On the hosted plan, a key created on the site goes in OHARNESS_API_KEY, and OHARNESS_TEAM bills the run to a team. The update check is off automatically when CI is set.
A GitHub Actions step
- run: npm install -g oharness
- run: oharness-headless --permission-mode acceptEdits "run npm test and fix failures" > run.jsonl
env:
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
- run: jq -r 'select(.type=="done") | .stopReason' run.jsonlCommon questions
How do I run a coding agent non-interactively?
With OHarness, run oharness-headless "<task>". It prints the agent's events as JSONL on stdout, writes diagnostics to stderr, and exits with code 1 if the run failed.
What happens to permission prompts in headless mode?
They are denied and reported as permission_result events, because nobody is present to answer. Allow the tools the job needs with permission rules in .oharness/settings.json, or choose a --permission-mode.
Which models can run headless?
Any model OHarness can reach: Anthropic, OpenAI, Gemini, DeepSeek, any OpenAI-compatible endpoint, or a local server reachable from the runner. Pick it with -m provider/model.