Agent Harnesses Explained
← /learn · 09

Harnesses compared

How Claude Code, Codex CLI, Aider, OpenHands, SWE-agent and mini-SWE-agent make each choice, from their docs, and the same task under a policy imitation of each.

Loading the animation…

Concept

Every mechanism in this site is a choice a real harness makes: how tool calls are written, what needs a person's approval, whether there is a sandbox, what happens when the context fills, whether there are sub-agents and hooks. The matrix below records how six public coding-agent harnesses make those choices, from their own documentation; the animation replays one task under a policy imitation of each: the simulator's settings closest to that harness's documented defaults.

A policy imitation, not the product. The model is the same scripted policy in every row, so it takes the same 10 turns everywhere, and the price is the same list price. What changes is only what the harness does around the model. That isolates the harness, and it also means the rows say nothing about how good each product is: the products differ far more in their prompts, their tools and the models they are used with than any of this shows.

What the imitation does show:

  • Approvals dominate the time. The imitations that ask before shell commands or edits (Claude Code's default mode, Aider, mini-SWE-agent's confirm mode) spend most of the run waiting for the human: mini-SWE-agent asks 9 questions and takes 1 min 11 s, against 14.9 s for the SWE-agent imitation, which asks nothing. OpenHands' risk-based confirmation asks only about the deletes (2 questions).
  • The sandbox stops what the questions would have let through. With no prompts inside its workspace sandbox, the Codex CLI imitation is as fast as the no-approval one, yet it is the only row in which the install and the home-directory delete are stopped (2 calls), because the simulated human approves everything.
  • The tool-call format changes the tokens, a little. The text-parsing imitations (Aider, mini-SWE-agent) send 7,773 tokens against 8,714 tokens for native calls, because a ReAct prompt describes the tools more briefly. Here they still cost more ($0.0287 against $0.026): their shorter prompts stay under the cache's 1,024-token minimum, so none of them is read from the cache.
How the public harnesses choose (from their docs, accessed 2026-10-07)
ChoiceClaude CodeCodex CLIAiderOpenHandsSWE-agentmini-SWE-agent
Tool callsNative tool use: named tools such as Read, Edit and Bash[1]Native function calls (the API it is built on)inferred[2]No tool calls: the model writes edits in a text edit format (diff, whole file …)[3]Function-calling toolsinferred[4]Configurable parsers; FunctionCallingParser recommended for models with function calling, ThoughtActionParser for text[5]Bash only, without the tool-calling interface: commands are parsed from the model's text[6]
ApprovalsManual (default) mode asks on first use of each tool; file reads and read-only commands need no approval; deny, then ask, then allow rules[1]Recommended for version-controlled folders: on-request approvals with workspace-write[7]Asks before running suggested shell commands (--yes-always says yes to all); edits are applied and auto-committed[8]Confirmation policies AlwaysConfirm, NeverConfirm, ConfirmRisky; a security analyser rates each action's risk[9]No approval step: runs autonomously in its environmentinferred[10]confirm mode (the default) asks before every action; yolo runs them at once[11]
SandboxOptional OS sandbox around shell commands, off by default; writes to the working directory, network through an allow-list; file tools stay outside it[12]read-only, workspace-write or danger-full-access; network off by default in workspace-write[7]None described: commands run in your shellinferredA Docker sandbox is the recommended default: the agent's commands run in a container[13]Commands run in a separate execution environment (a container)inferred[10]Each action is an independent subprocess.run; no sandbox of its own by defaultinferred[6]
ContextAuto-compaction summarises older history near the limit; /compact on demand[14]Auto-compaction at model_auto_compact_token_limit; tool_output_token_limit caps each stored tool output[15]A ranked repository map within --map-tokens; chat history summarised past a token limit[16]LLMSummarizingCondenser summarises older events past max_size[17]LastNObservations elides all but the last n tool observations[18]A completely linear history: every step appends to the messages[6]
Sub-agentsEach runs in a fresh context window and returns only its result[19]On by default; the parent receives summaries, not raw intermediate output[20]None in the pages readinferredA task tool delegates to a sub-agent with its own conversation, which returns its response[4]None in the pages readinferredNoneinferred
Hooks and checksPreToolUse can deny a call or replace its input; PostToolUse can replace the result[21]PreToolUse can deny a call or return updatedInput; PostToolUse can add context[22]Lints after each edit (and tests, with --auto-test); errors go back to the model[23]PreToolUse hooks can stop a tool call; PostToolUse, Stop and session hooks[24]Re-queries the model up to max_requeries (3) after a format error or a blocked action[25]A cost limit ($3 by default) and an optional step limit[11]
Imitated asnative · parallel · mode default (git status allowed) · summarising · no sandboxnative · parallel · no prompts inside the sandbox · workspace sandbox, no network · summarisingtext parsing (ReAct) · one call per turn · edits allowed · shell asks · summarisingnative · parallel · auto, rm asks (risky) · summarising · container not drawn (its deletes hit the container)native · one call per turn · no prompts · clipping old resultstext parsing (ReAct) · one call per turn · ask every time · no compaction

The last row is a policy imitation, not the product: the engine's settings closest to each harness's documented defaults. The products do much more than these rows say, and change often.

  1. Claude Code: configure permissions (accessed 2026-10-07)
  2. OpenAI: function calling (accessed 2026-10-07)
  3. Aider: edit formats (accessed 2026-10-07)
  4. OpenHands SDK: task tool set (accessed 2026-10-07)
  5. SWE-agent: action parsers (accessed 2026-10-07)
  6. mini-SWE-agent: overview (accessed 2026-10-07)
  7. Codex: approvals and security (accessed 2026-10-07)
  8. Aider: options reference (accessed 2026-10-07)
  9. OpenHands SDK: security and confirmation mode (accessed 2026-10-07)
  10. Yang et al. (2024), SWE-agent, arXiv:2405.15793 (accessed 2026-10-07)
  11. mini-SWE-agent: the mini command (accessed 2026-10-07)
  12. Claude Code: the sandboxed Bash tool (accessed 2026-10-07)
  13. OpenHands: sandboxes (accessed 2026-10-07)
  14. Claude Code: manage costs (auto-compaction) (accessed 2026-10-07)
  15. Codex: configuration reference (accessed 2026-10-07)
  16. Aider: repository map (accessed 2026-10-07)
  17. OpenHands SDK: context condenser (accessed 2026-10-07)
  18. SWE-agent: history processors (accessed 2026-10-07)
  19. Claude Code: subagents (accessed 2026-10-07)
  20. Codex: subagents (accessed 2026-10-07)
  21. Claude Code: hooks reference (accessed 2026-10-07)
  22. Codex: hooks (accessed 2026-10-07)
  23. Aider: linting and testing (accessed 2026-10-07)
  24. OpenHands: hooks (accessed 2026-10-07)
  25. SWE-agent: agent configuration (accessed 2026-10-07)

Maths

Split a run's time into the model's calls, the tools and the human:

Th=Tmodel(formath)+Ttool+qh wˉ,T_h = T_{\text{model}}(\text{format}_h) + T_{\text{tool}} + q_h\,\bar w ,

where qhq_h is the number of questions harness hh asks and wˉ\bar w the mean wait for an answer (6 s here). With the same model and the same steps, TtoolT_{\text{tool}} is the same in every row (less the calls a sandbox stops at once), and TmodelT_{\text{model}} moves only with the prompt format; qhq_h ranges from 0 to 9, so it decides the ranking. The asking rows spend 38.4 s (Claude Code imitation) and 56.1 s (mini-SWE-agent imitation) waiting for the human.

Code

Each imitation is a policy for the same engine; the permission decision every row runs, cut from the vendored engine (harness.ts):

if (hits.has("deny")) return ["deny", hits.get("deny")!];
if (mode === "read_only" && !spec.read_only) return ["deny", "mode: read-only"];
if (mode === "ask") return ["ask", "mode: ask every time"];

The six policies are in chapter_configs.json (chapter harnesses), and the matrix's sources in harnesses.ts.