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
Deze pagina is in het Engels geschreven. De termen die erin worden uitgelegd zijn de termen waarop in het Engels wordt gezocht, en een vertaalde vakterm is een andere term.
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.