Stateless Agents, Stateful Humans: A New Architecture for AI Agents
The autonomous-loop architecture conflates two responsibilities: deciding and doing. Splitting them — agent stateless, human stateful — fixes more than it complicates.
The standard agent architecture, in one sentence: a loop where the model receives observations, picks an action, calls a tool, observes the result, and repeats until done.
This works because LLMs are good at the “pick an action” step. The problem is that in this architecture, picking and acting are the same step. The model decides what to do and the system does it, with no gap between them. State changes happen inside reasoning.
For one-shot tasks that’s fine. For long-running, regulated, multi-tenant, or just-shipping-to-real-users work, it’s a structural mistake.
The split
The architecture ONTO is built on:
- The agent is stateless about the world. It reads typed state at the start of each run, proposes actions and memory writes, and returns. It does not mutate.
- The human (or a deterministic gate) is stateful. They hold the durable memory, the approval log, the policy decisions. They — not the model — write to the world.
The model’s job becomes proposing. The human’s job becomes deciding. The runtime’s job is to enforce that the model can only propose and the human can only execute.
Three runtime primitives make the split structural rather than conventional:
- Memory writes are explicit. Every UPO write is an
add_assertioncall with consent scopes and audit metadata. The runtime — not the model — does the write. - Tool execution carries consent scopes. Every tool declares what scopes it needs. Every run carries what scopes you grant. The runtime refuses tools whose scopes don’t cover what the call needs.
- Plan Mode is a runtime invariant. A run in
mode="plan_only"creates tasks but does not fire side-effect tools. The split between Plan and Execute is enforced at the runtime layer, not the prompt layer.
Why this is more than aesthetics
Three concrete consequences.
You stop hallucinating side effects
In an autonomous loop, the model can “decide” to call a tool you didn’t intend it to call. The standard mitigation is a tool allowlist — the agent can only call tools in a list. This helps but doesn’t fix the fundamental issue: even an allowed tool, called at the wrong time with the wrong arguments, can cause harm.
With Plan Mode, the LLM’s intent goes into a typed Task object first. The arguments are visible. The cost is estimated. A human (or policy) decides whether to fire the tool. The LLM never gets the satisfaction of acting on its own decision — it gets the satisfaction of having proposed correctly.
You get an audit trail for free
Auditors ask “show me the AI’s reasoning, the data it used, and the human approval for action X.” In the autonomous loop, the answer is “here’s the transcript, please trust us.” In the stateless-agent / stateful-human architecture, the answer is a structured record:
- The Run row, with input, scopes, model_id
- The Assertion writes, with provenance and tags
- The Task proposals, with arguments and cost
- The approval decisions
- The Execute Run, with the executed tools
You produce these by construction, because the system already has to track them to enforce the split.
You decouple memory from the model
In the autonomous loop, memory lives in the context window. The model sees what it can see. Anything past the window is gone.
In the stateless-agent architecture, memory lives in typed storage you control. The model loads relevant facts at the start of a turn, proposes new ones, and returns. The window is just a buffer for the current turn — the durable state is somewhere else.
This is the thing that lets agents live longer than a session. The model can change. The memory persists.
What it costs
The architecture has real costs. Three of them.
More moving parts. A Plan run, an approval step, an Execute run is more code than a single autonomous loop. The flow is more explicit.
Latency for the approval step. If your use case is “user types message, agent acts in 2 seconds,” and approval is needed, you’ve blown the latency budget. The right pattern for these cases is to run with execute scopes directly (no Plan Mode), accepting the autonomous loop’s risk model in exchange for speed.
Schema design. Typed memory means you have to think about what predicates to track. There’s no free schema-on-write — the cost of the schema is real.
These costs are the price of getting the architecture’s benefits. For one-shot creative tasks they’re not worth paying. For long-running personal assistants and regulated industry work they pay back many times over.
The escape valves
The architecture has two important escape valves that keep it practical.
Run with execute scopes when it’s safe. A read-only chat agent runs with ["memory:read"]. There’s nothing to gate. Plan Mode is opt-in per run, not a global mode.
Auto-approve under policy. When the gate is “is this a gold-tier customer with a refund under $50,” you don’t need a human. Plan Mode produces tasks; a policy function approves them; Execute fires. The architecture doesn’t say humans must be involved — it says the decision to execute must be separate from the decision to propose.
These let the same SDK serve the read-only chat use case and the high-stakes-clinical-order use case without code duplication.
Where this came from
This isn’t an invention. Operating systems split user-space (the program proposes a syscall) from kernel-space (the kernel executes it under privilege checks). Databases split clients (which propose writes) from the engine (which executes them under integrity constraints). The pattern of “untrusted thing proposes, trusted thing decides” is everywhere good systems are built.
What’s new is that agent SDKs have, mostly, not applied this pattern. The autonomous loop treats the model as both the proposer and the executor. That’s user-space being allowed to do its own syscalls. It works in toy examples and fails in production.
ONTO is what you get when you take this OS lesson seriously and apply it at the runtime level. The model proposes. The runtime executes — under consent checks, with audit, with Plan Mode as a halt point.
Where it doesn’t apply
If you’re shipping a one-shot creative tool — “write me a poem about my cat” — none of this matters. There is no durable state, no compliance question, no multi-session memory, no risk of irreversible side effects. The autonomous loop is fine.
The architecture matters precisely when those things show up. Once your agent has memory across sessions, or scopes data across tenants, or fires tools that touch the world, the split is worth its costs.
A summary you can use
If someone asks what’s different about ONTO’s architecture, the one-liner is: the agent reads state and proposes actions; the human writes state and approves them. Everything else — Plan Mode, consent scopes, typed memory, audit logs — is structure built around that split.
It’s a small reframing with large consequences. Once you see it you can’t unsee it. Once you build on it you can’t go back to the autonomous loop.
The features page walks through each runtime primitive in detail. Why agent memory is broken covers the memory side; Plan Mode explained covers the execution side.
Frequently asked questions
Doesn't 'stateless agent' just mean the LLM has no memory? That's always true.
Within a turn, sure. The deeper claim is architectural: the agent should not be the entity that mutates state. It reads state, proposes mutations, and a separate entity (the human, or a policy gate) executes them. The model is stateless about the world; the world stays stateful in storage you control.
What about agents that have to act on their own — cron jobs, background workers?
Same architecture, different actor. The 'human' is replaced by a policy gate that decides which proposals to execute. The split between proposing and executing stays; only the approver changes. This is the difference between 'agent calls tool' and 'agent proposes tool call, system decides.'
Isn't this just the old plan-execute pattern?
It's an old idea applied at the runtime level instead of the prompt level. Plan-execute as a prompting pattern relies on the model honoring the instruction. As a runtime mode (consent scopes, refusal, audit) it becomes a guarantee.
The agent SDK where humans drive the state.
Plan Mode, typed memory, per-call consent scopes, and open-weight defaults. Open source under MIT or Apache-2.0.