Our agent loop is 239 lines. Frameworks and runtimes are not the same layer.
SOIT Team

The phrase agent platform has swallowed at least three different layers: the library you write an agent with, the engine that runs it, and the plane that governs it. Most arguments about agent tooling are two people answering different questions. One says they built a working agent in three lines; the other wants to know who approved the call that went out at three o'clock yesterday afternoon. Both are right, and neither is talking about the same layer.
We are not going to judge anyone else's internals, because being fair about that would mean reading their code closely enough to earn the opinion. So this post does two other things. It describes what the framework layer generally owns, which is not controversial, and it uses our own repository as the sample for the runtime layer, where every claim comes with a file and a line number. The sample is github.com/soit-ai/soit, Apache 2.0, at commit 3a57ae1.
Start by counting our own agent loop. The planner, the executor and the verifier together come to 239 lines. The service wrapping those 239 lines is 1,617, and its import list gives away what it spends them on: a workspace guard, an approval ledger, a rate limiter, a tool execution service carrying leases and idempotency keys, a trace writer, an approval rule, a tool resolver. The loop itself is a single while statement. One line of loop, sixteen hundred lines around it, and almost none of those are about how the agent thinks.
The line between the two layers is not a diagram, it is a port. Every tool call passes a policy gateway of 475 lines before it reaches an adapter, and that gateway is where the uncomfortable questions live. Not whether the agent logic reads nicely, but whether a retry opens the same ticket twice when the system downstream does not honour an idempotency key. Calls at that boundary are at-most-once for exactly that reason.
You could reasonably object that we simply pushed the governance code downstream of our own loop. The answer is the second execution model in the same repository: a DAG workflow engine of 6,269 lines, structurally nothing like an agent loop. It reaches tools through the same tool port and models through the same model port. Two unrelated execution models, one set of guarantees, which is the operational definition of a layer. Governance is a property of the port rather than of the loop, so swapping out what sits above it changes none of the ten checks below.
Layering usually gets written into documentation and then rots in the code, so this one is enforced by a tool instead. The first import contract in the repository forbids the kernel from importing the API, the modules, the adapters, the infrastructure or the wiring. When the kernel needs product or infrastructure data, it has to go through provider interfaces registered from the wiring layer. That is the running cost of keeping a boundary alive, and it is paid by continuous integration rather than by discipline.
The honest part: that workflow engine does overlap with orchestration frameworks, and pretending otherwise would be silly. It has a compiler, a variable resolver, node executors, a resume path and a reaper. The accurate claim is not that we build no framework machinery, it is that we build only the part we need and then hold it to the same rules as anything else that calls in.
So what does it mean in practice that this is not a competing framework? Tools written for a framework can be registered over MCP, and from then on every call they make passes the same gates. Services can be attached as HTTP plugins. The frontend does not have to change, because the interaction is an AG-UI event stream. What is missing matters more than any of that: there is no entry point that takes an agent someone else wrote and hosts the whole thing. Your loop still runs in your process. What gets governed is the hand it reaches out with, the tool call and the model call and the egress request, not the reasoning behind them.
The framework decides how an agent thinks; the runtime decides whether the moment it reaches out counts. Those are not competing concerns, because they do not act on the same timescale at all: one applies to the days spent writing the code, the other to every execution afterwards. The only real competition is for attention, and the shift from the first to the second usually happens too late, which is to say after the first incident.