Don't Build an Agent Harness From Scratch. Fork One.
The instinct, when you decide you want an agent, is to open an editor and write the loop. Prompt in, parse the tool call, dispatch, feed the result back, repeat. It takes an afternoon and it feels like the right kind of work — you control the prompt, you control the tools, nothing is hidden.
It is a trap, and it is an expensive one. You inherit none of a real harness's skills, none of its hooks, none of its permission model, none of its account handling. Then you spend the next six months chasing a product that ships faster than you can track it, and the thing you built is a worse version of something that was already MIT-licensed on the day you started.
So don't write the loop. And here's the part people miss: you don't have to pick a harness either.
Start here: aither-adk drives all of them
This is the thing we actually built, and it's the shortest path from "I want an agent" to "an agent is doing work."
pip install "aither-adk[harness]"
aither-adk is on PyPI (3.2.0, Python 3.10+). It installs adk, and adk knows
how to drive other people's coding agents as subagents. Ask it what your machine
can do:
adk harness harnesses
ID INSTALLED TRANSPORT DESCRIPTION
claude yes structured-bidi Anthropic Claude Code — bidirectional stream-json, full tool use
gemini yes oneshot-per-turn Google Gemini CLI — one process per turn, stream-json output
terminal yes pty-stream A real shell on this host behind a pseudo-terminal (pwsh/bash)
sandbox NO pty-stream A real Linux TTY inside a dev-workspace container (docker exec -it)
-> Install Docker Desktop
aither yes http-stream An AitherOS agent relayed over SSE
group yes http-stream Several sovereign agents in one room, answering concurrently
codex NO oneshot-per-turn OpenAI Codex CLI (codex exec --json)
-> npm i -g @openai/codex
aider NO oneshot-per-turn Aider — pair-programming CLI
-> pip install aider-install && aider-install
opencode NO oneshot-per-turn OpenCode — open-source coding agent
-> npm i -g opencode-ai
That is real output from the machine this post was written on, unedited. Note the
NO rows: a harness you haven't installed says so and prints the command to get
it. An absent harness is a missing install, not a missing feature, and the
difference is printed rather than guessed at — a shell that offers something it
cannot start is worse than one that admits it.
Then drive any of them through the same five verbs:
adk harness serve # the daemon (127.0.0.1:8362)
adk harness new --harness claude # start a session, get an id
adk harness send <id> "refactor the retry logic in billing/"
adk harness attach <id> # watch it work, live
adk harness list # what's running
adk harness kill <id> # teardown, whole process tree
Swap --harness claude for gemini, codex, aider, opencode, terminal,
sandbox — the verbs don't change. That's the whole point. Adding a harness is a
data change: a spec declaring how to launch it, how to feed it a turn, and
which adapter normalises its output.
From Python, the same thing with a blast radius you set:
from adk.claude_runner import ClaudeRunner, RunScope
runner = ClaudeRunner()
scope = RunScope(allowed_tools=["Read", "Grep", "Glob"]) # read-only
rec = runner.submit(task="audit error handling in ./api", scope=scope)
rec = runner.get(rec.run_id) # queued | running | completed | failed
print(rec.result_text)
The scope becomes --allowedTools on the real CLI, so an agent asked to audit
code cannot write to your disk — enforced by the product you delegated to, not by
a prompt politely asking it not to.
And for the harnesses you host rather than spawn, adk connect generates the
config — pointing OpenClaw, Hermes, DeerFlow or NVIDIA's NeMo OO-Agents at your
own inference endpoint and wiring in an MCP toolset, instead of an afternoon of
YAML archaeology.
The thing that actually decides your design
Sort harnesses by transport, not by vendor. Transport is what decides whether your agent can remember anything.
| transport | shape | what it costs you |
|---|---|---|
structured-bidi | one persistent process, JSON both ways | nothing — session state survives turns, full tool use |
oneshot-per-turn | a fresh process every turn | no cross-turn memory — you carry the context |
pty-stream | a real TTY behind a pseudo-terminal | output is bytes, not events — but curses apps and job control work |
http-stream | remote agent over SSE | the agent isn't on this machine at all |
The one that surprises people is oneshot-per-turn. Codex, Aider and OpenCode
start a new process per turn, so anything you want remembered must be in the
prompt you send. A multi-turn refactor driven through a oneshot harness will
cheerfully forget what it just did — and it will not tell you that's why.
The harnesses you host and live in
Different category from a subagent you spawn per task. All model-agnostic, which is the whole reason they're worth pairing with hardware you own.
OpenClaw — local-first personal assistant, 25+ messaging channels, supports the Agent Skills standard. The de-facto community control plane; MIT, which is why so much else here is built on top of it.
Hermes Agent (Nous Research, MIT) — the self-improving one. Closed learning loop: writes its own skills, keeps persistent memory with user modelling, runs cron automation.
DeerFlow (ByteDance, MIT) — a LangGraph super-agent harness for long autonomous runs. Sub-agents, persistent memory, sandboxed execution, for work measured in hours not turns.
ODS (Apache-2.0) — the whole local stack in one installer: inference, chat UI, voice, agents, workflow automation, vector search, image generation. Its model catalogue and hardware classification were good enough that we vendored them outright.
tau (MIT) — minimalist terminal coding agent, small enough to read end to end. Fork this one if the goal is to understand your agent. No MCP support; tools come from extensions.
NemoClaw (NVIDIA) — built on OpenClaw's MIT codebase, adding sandboxing, network policy, managed inference and lifecycle ops. If your blocker is "I can't run an autonomous agent on a corporate machine", this is the shape of the answer.
Also worth your afternoon: Pi (the minimal harness that ships as an SDK — OpenClaw embeds it), Agent Zero, MemUbot (memory-first), OpenHUMAN (local-first), and Paperclip (orchestrator that can call Hermes as a worker).
Shout-out: GobboNet
Different category, same lesson, and it deserves the signal boost. GobboNet (MIT, by Elodine / GoblinCorps) is not a coding harness — it's a local-first AI chat front end: character cards, hybrid lorebook RAG (semantic embeddings plus weighted structured tags), and a supervised llama.cpp server, with one-click local model setup as its whole promise.
Two things make it a model of how to build this stuff. Its transport is already OpenAI-compatible, so pointing it at a different backend is a URL change, not a rewrite. And it has real extension seams — custom JS/CSS loaded from a URL at boot, plus a per-character card API — so an integration can ship as one hosted file rather than a patch. It also gets the security default right: imported cards never auto-run, because a card carrying unsandboxed JS is an RCE distribution channel dressed up as a download.
If you want to see what "forkable by design" looks like in a codebase rather than in a blog post, read this one. We're studying it; nothing is built on it yet.
The twelve we took apart for parts
Each teardown ends in a per-idea verdict — adopt, adapt, reference, reject — with a licence gate first. The verdicts are more useful than the list:
| what we read | the one-line finding |
|---|---|
| 12-factor-agents | A design manifest, not a codebase. The checklist is the value. Factor 5 — unify execution and business state — is the one that pays. |
| agentcontrolplane | Clean K8s-native task state machine. Its dual-layer locking (in-memory mutex plus a distributed lease, so one task can't fire two LLM calls) is the transferable idea. |
| opendevops | Best safety core here. The model never gets a shell — only run_command(argv: list[str]) through a default-deny policy engine, budget stop-losses, and a hash-chained audit ledger. |
| OpenWorker | 81k LOC. Too big to adopt wholesale; mine the seams. Its own census tool says so. |
| sagent | An append-only session "tape" where compaction is a non-destructive splice on an immutable log. Worth the read for that alone. |
| NeMo OO-Agents | The agent is a Python class: fields are state, docstrings are prompts, annotations are contracts, and an async method whose body is ... dispatches to the LLM. We vendored the validator and the CodeAct loop. |
| open-polsia | Small clean TS demo of an autonomous business-agent loop. One real gap — task-lifecycle integrity. Three of our own "gaps" turned out to be wrong on a second look. |
| claw-code | ~5k LOC of Python re-expressing the agent loop, tools, permissions, memory, skills, worktree isolation and subagents. The best legally-readable map of that architecture. |
| DeepSeek-Coder | Not a harness — a measuring instrument. 94% evaluation harness: runs candidate code in a separate process, temp dir, resource limits, many languages. |
| DeepSeek's internal harness | Known only from a talk. Four of its five pillars we'd already shipped. The fifth — the folder is the status — is better than ours, and we said so. |
| zencoder novel engine | Not code: a seven-agent editorial pipeline. Draft → cold read → dev edit → revise → copy edit. All-rights-reserved, so ideas only. |
| Auto-Cards | AI Dungeon script. Watch the prose, auto-detect entities, write and refresh cards with cooldowns and dedup. Object permanence, solved cheaply. |
The seven things that will bite you
Whichever harness you start from, these are yours to get right. aither-adk
already implements all seven — if you roll your own, you will meet each one:
1. The prompt goes on stdin, not argv. argv is readable by any local process
(ps auxww, /proc/<pid>/cmdline) and a task prompt routinely carries file
contents and occasionally a credential. Claude Code takes it on stdin for exactly
this reason. Codex, Aider and OpenCode offer no stdin path, so on a shared host
treat prompts through those three as visible. That's a property of the tools, not
a setting.
2. Every run needs its own account state. Concurrent subagents sharing one config directory corrupt each other's session. Set a per-run config dir, or run them strictly one at a time.
3. Teardown is the process tree, not the process. Coding agents spawn children — language servers, test runners, watchers. Kill the parent and they survive holding ports and file locks; the symptom surfaces minutes later as "address already in use" somewhere unrelated.
4. Re-validate the scope fail-closed. Launch with an explicit allow-list and have the runner check it again rather than trusting the caller.
5. Detection must be honest. Report installed: false with the install
command. The alternative fails silently: a UI offers a harness, launch dies deep,
and the user reads it as your bug.
6. One task out, one answer back. Delegate when the sub-problem wants a different model, a different tool surface, or a bounded blast radius. If you're sending a fifth follow-up to the same subagent, the work wanted to stay in your own loop.
7. Make the folder the status. The best idea we've seen this year and the one
we're behind on: let the directory a note lives in be its lifecycle state —
proposed/, implemented/, archived/, rejected/. A status column drifts
silently and nothing can read it. A folder cannot lie about where a file is.
Where to start
| you want | start with |
|---|---|
| to drive several agents behind one interface | pip install "aither-adk[harness]" |
| to understand an agent | fork tau, read all of it, break it |
| a personal assistant that reaches you | OpenClaw on a local model |
| it to get better on its own | Hermes |
| long autonomous runs | DeerFlow |
| the whole stack this afternoon | ODS |
| to run where policy says no | NemoClaw |
And if what you want is to hand a task to somebody else's coding agent and read
back one answer — you don't need to build a harness at all. You need a registry,
four transports, and the seven rules above. That's adk harness, and it's one
pip install away.
If you fork one of these and hit something on that list the hard way, we'd like to hear about it — that's how most of this post got written.