Photo by Bernd 📷 Dittrich on Unsplash From the outside, all six of these coding agents do the same thing. You type a request, the model calls some tools, files change, and you either approve them or you don’t. I figured the insides would match. They don’t. I spent a week reading the actual source of Codex, Claude Code, Cline, goose, gemini-cli, and OpenHands. The parts nobody screenshots, like the agent loops, the sandboxes, and the system prompts they quietly hand to the model and the architectures fork almost immediately. One of them still scrapes its tool calls out of XML by hand. Another asks the model itself whether a shell command is safe before it runs. A third locks everything inside a container on the assumption that you’ve already walked away from the keyboard. None of that shows up in a demo, and all of it tells you what each team actually believes about where the risk lives. The part the demo hides From the outside, all six of these coding agents do the same thing. You type a request, the model calls some tools, files change, and you either approve them or you don’t. I figured the insides would match. They don’t. I spent a week reading the actual source of Codex, Claude Code, Cline, goose, gemini-cli, and OpenHands. The parts nobody screenshots, like the agent loops, the sandboxes, and the system prompts they quietly hand to the model, and the architectures fork almost immediately. One of them still scrapes its tool calls out of XML by hand. Another asks the model itself whether a shell command is safe before it runs. A third locks everything inside a container on the assumption that you’ve already walked away from the keyboard. None of that shows up in a demo, and all of it tells you what each team actually believes about where the risk lives. Two of these six don’t ship their agent core in the repo you can clone. OpenHands keeps its brain in a separate Python package and open-sources the server that wraps around it. Claude Code’s repo is plugins, hooks, and examples, with the real CLI shipped as a compiled bundle. For those two I’m reconstructing the design from what is in the tree plus how the tools behave, and I’ll flag it whenever I’m doing that. The other four, Codex, Cline, goose, and gemini-cli, you can read straight through. And the first thing that splits them is also the thing you would never catch in a demo: how the model is even permitted to say the words “run this tool.” How the model says “run this” This is where the six split hardest, and it happens before a single file gets touched. The question sounds dumb on its face. When the model decides to use a tool, how does it physically say so? There are four different answers in this set, and the answer a team picks quietly decides most of what comes after it. Cline : tags buried in the prose Cline still does the oldest trick in the book. It describes each tool inside the system prompt as an XML block, asks the model to reply with that XML in its normal text, and then parses the tags back out of the assistant’s message. A file read shows up as
src/app.ts, sitting right there in the prose like any other sentence. The upside is real: this works on literally any model, because you aren't depending on a provider's function-calling feature, you're just asking for text and reading text. The downside is the same coin flipped over. You're parsing a structured command out of free-form writing, so a stray bracket or a model that decides to "explain" its XML can break the parse, and every tool definition is now burning tokens inside the prompt on every single turn. Cline is the lone holdout here, and it holds out on purpose, because being model-agnostic is its entire identity. gemini-cli , Codex , and Claude Code : let the API carry it The three that use native function-calling never put tool syntax in the prose at all. The model returns a structured call as a first-class field, and the harness reads it as data, not as text it has to scrape. In gemini-cli, a turn is an async generator that yields a tool-call request the moment the model wants something. Claude Code uses Anthropic’s tool-use blocks the same way. Codex runs it over OpenAI’s Responses API. The win is that nothing ever gets parsed, the call arrives already shaped, and you spend far fewer tokens describing tools. The cost is just Cline’s win in a mirror: you’re now married to whatever the provider’s API happens to support. Codex adds one twist worth slowing down for. For the single most dangerous and fiddly operation, editing a file, it doesn’t trust a loose function call. It constrains that one tool with a formal grammar, so the model literally cannot emit a malformed patch. It’s a tiny detail that says a lot. Somebody on that team decided file edits were worth a whole grammar of their own. goose : everything is a plug goose makes the more radical bet. It ships almost no built-in tools. Every capability is an MCP extension instead, and the core is basically a client that speaks the Model Context Protocol to whatever you’ve plugged in. The agent’s tool surface is whatever extensions are loaded, and nothing else. This is the cleanest separation in the whole set, because the core neither knows nor cares what a tool actually does, and it’s exactly why goose can later treat other agents as just one more backend. The flip side is that goose with no extensions barely does anything, which is the point and also something you’d want to know going in. OpenHands : the tool is code OpenHands picks the strangest answer, and maybe the most powerful one. Its main action is executable code. The model writes bash or Python, and the runtime runs it in a sandbox, next to a few structured tools. This is the CodeAct idea: instead of handing the model fifty narrow tools, you hand it a programming language and a place to run it. One action can loop, branch, chain three operations, and react to its own output, all inside a single step. It’s more expressive than any fixed tool list could be, and it’s the reason OpenHands cares so much about the sandbox, which is where we’re headed. So notice there’s no winner in this section. XML buys portability and pays in fragility. Native calling buys robustness and pays in lock-in. MCP buys a clean core and pays in emptiness. Code-as-action buys raw expressiveness and pays in a much harder safety problem. The encoding is the first domino, and you can already feel which direction it’s leaning. The loop, and the shape it takes Every one of these agents runs the same loop a thousand feet up: ask the model, it asks for a tool, run the tool, hand back the result, repeat until it stops. Boring. The interesting part is the shape each team carves that loop into, because the shape is a confession about what they think they’re building. Cline : a function that calls itself Cline models the loop as recursion. There’s a Task object, and at its center is a function that streams the model’s response, walks the message executing tool blocks one at a time, appends the results, and then calls itself for the next turn. It reads like the loop is happening inside your editor, because historically it was. This started as an extension living in your IDE, with you watching it work. The recursion is cozy and direct, and it fits one agent with a human sitting right there. gemini-cli , goose , Codex : the loop as a stream of events The three that treat the loop as a typed event stream feel like a different language even when they aren’t. In gemini-cli, a turn is a generator that emits events: here’s some content, here’s a tool-call request, here’s the result. A separate scheduler runs each call after passing it through the policy engine, and a client drives the whole multi-turn conversation with model routing and fallback folded into the same flow. goose pulls the same move in Rust, where the reply is a stream and things like approval and mid-run prompts for input ride along as their own sub-streams on that one channel. Codex’s version is the most disciplined of the bunch: check the token budget, compact the history if you’re close to the ceiling, build the prompt and tools, stream the model, run the tools, repeat. The thing to catch in Codex is that compaction is a step inside the loop, not a feature bolted onto the side. The loop itself knows it might run out of room and plans for it. OpenHands : the loop as a log you can replay OpenHands drifts furthest from anything you’d call a loop. It’s event-sourced. A controller steps over a stream of Action then Observation, Action then Observation, while the runtime runs each Action inside a Docker container and the whole thing is coordinated server-side. You’re not watching a function recurse, you’re watching an append-only log of what happened, the kind of thing you could replay or audit after the fact. It’s the most “distributed system” of the six and the least “CLI.” It’s built like a service that happens to have an agent living inside it. Claude Code : the loop with trapdoors Claude Code runs a native tool-use loop, but the distinctive part is what it lets you splice into each pass. Hooks can fire before a tool runs and kill it. Subagents can be spun up to go handle a chunk of work on their own. Tasks can run in the background or in a separate git worktree. So the loop is less a straight line and more a trunk with branches you can hang governance and parallelism off of. Which fits the pitch exactly, where extensibility is the product. Line them all up and the pattern is almost funny. The two Rust agents and gemini-cli reach for typed event streams. Cline reaches for recursion. OpenHands reaches for an event log. Nobody coordinated this, but the shape each one grabbed tells you whether the team thinks it’s building a tool you babysit or a system you deploy. Which is the exact question everything so far has been circling. Where each one thinks the risk lives Here’s the fork underneath all the others. Before any of these agents runs a command that could wipe your repo or pull something off the open internet, somebody decided who gets to say yes. And the six answers aren’t variations on one idea. They’re six different theories about where the danger actually sits. Cline : you are the allowlist Cline keeps it human. There’s an auto-approve allowlist with per-action toggles and a cap on how many calls it’ll fire before checking back in, plus separate Plan and Act modes. Past that, you approve things in the UI. The theory of risk is simply “a person is sitting right here, so let the person decide.” It’s honest, and it scales exactly as far as your attention does and not one inch further. gemini-cli and Claude Code : write the rules down ahead of time These two answer with declarative rule engines, and gemini-cli’s is the most formally designed thing in the entire comparison. There’s a real policy engine with modes (a normal one, an auto-edit one, and a yolo one), rules that resolve to allow, deny, or ask, wildcard matching, rules scoped to a specific MCP server, a dedicated checker for shell-command safety, even rules scoped down to subagents. Claude Code lives in the same family. Permission modes, allow and deny rules sitting in a settings file, pre-tool hooks that can veto a call, and managed settings an admin can push down across a whole organization. The theory of risk here is “danger is knowable in advance, so encode it as policy.” It’s the corporate-friendly answer, and it’s no accident that the two most enterprise-facing tools both landed on it. goose : ask a model whether the model is about to do something dangerous goose does the one thing that made me stop and read it twice. It has a full permission subsystem, and part of it is a permission judge that is itself a model call. Handed a tool invocation, it can ask a model to classify whether the operation is read-only or mutating, decide from there, and remember the decision so it doesn’t nag you about the same thing twice. Sit with that. The safety check on what the model is about to do is, in part, another model. If you trust the classifier, it’s the most flexible answer in the set, because it can reason about commands no static rule ever anticipated. If you don’t, it’s the most unsettling thing in here. goose is the only one of the six that bet the model can police itself. Codex and OpenHands : assume nobody’s watching, and build a cage The last two barely care about the approval prompt, because they put the real boundary somewhere you can’t click through: the operating system and the container. Codex has the most serious OS-level sandboxing of the group. On macOS it runs commands inside Seatbelt. On Linux it uses Landlock and seccomp and bubblewrap, with a switch to cut off network access entirely. Permissions are two separate knobs at once, one for how much to ask (from “ask unless trusted” all the way to “never ask”) and one for what the sandbox physically allows (read-only, write to the workspace, or full access), and when the sandbox blocks something, the agent can come back and request an escalation rather than just failing. It’s built like a thing that expects to be handed a task and left alone. OpenHands takes it the whole distance and makes the container the center of the design. The agent literally runs inside a Docker sandbox while the host orchestrates from outside, and the SDK stacks a security analyzer and risk levels on top of that. Remember that OpenHands’ main action is arbitrary code. Of course it leans on isolation. When your agent’s primary move is “run this code I just wrote,” the only sane place for the guardrail is around the entire runtime, not in front of each individual command. Step back and it snaps into focus. Cline trusts you. gemini-cli and Claude Code trust rules someone wrote in advance. goose trusts a model to judge a model. Codex and OpenHands trust nothing and build a cage, because they assume you’ve already left the room. Not one of these is the obviously correct answer. They’re all bets on a question nobody can settle yet: as these agents get more capable and you watch them less, does safety come from better rules, a smarter judge, or a stronger cage? Every team here has already pushed its chips in, and they did not all pick the same number. Whichever one turns out to be right, I’d put money on it not being the one with the slickest demo. We just don’t get to know which one yet.
Comments
Sign in to join the conversation.
No comments yet. Be the first.