You hand a language model some text or images, and it hands back something shaped, such as a JSON object or a Python program. The call itself remembers nothing before it and cannot be redirected once it starts.
The first property is statelessness: the next call has no idea the first one happened unless the surrounding software supplies the history again. The second is that the call is feedforward: input goes in, output comes out, and the software cannot change the computation halfway through. An assistant that appears to remember something received it again in a later prompt. One that accepts a correction mid-task has usually cancelled one call and started another.
A model call is loosely analogous to one feedforward sweep through a cluster of neurons. The analogy stops quickly, but it is useful here. A brain also has specialised regions, feedback paths, and state that survives a single sweep. We have to provide those properties outside the model.
Until models learn continually while they work, most assistant behaviour will come from this surrounding software. We therefore spend most of our time deciding which loops should exist, what each one remembers, and how a correction reaches work that is already running. The model improves on somebody else's schedule. This part is ours to design.

Three ways to wire it
OpenClaw, Hermes Agent, and Unify put their complexity in different places.
OpenClaw puts much of its investment on the input side. It supports many messaging platforms, with a gateway that maps their messages onto agent runs and a large plugin library. If you want a personal assistant that reaches you everywhere and has a marketplace behind it, that is the project I'd point you towards. We care about a different layer.
Hermes Agent concentrates on a single agent core that you can read end to end. It adds text-injection steering, a skills library, and mature cron and webhook automation around that core. The design assumes one legible loop is better than many opaque ones, and I think that is largely right. Several loops are harder to understand and operate.
We measured the same recurring tasks across all three systems. Both alternatives cost less to set up, while Unify costs less on later runs. Hermes's zero-token cron scripts keep no model in the loop to notice when the world changes. OpenClaw keeps a model in every run, so it adapts but pays again each time. The full numbers, including the experiments we lost, are in the benchmarks post.
Unify puts more complexity into specialised loops and the paths between them. Each loop owns one part of the persistent state and returns the same kind of steerable handle. Those loops can nest when one task needs another.
Regions on different clocks
One thing biology does that a single sweep can't is run different functions at different speeds. Keeping your balance and deciding where to go for dinner aren't the same job and don't happen on the same clock.
Most agent frameworks have exactly one loop, so presence and work end up sharing a clock. Send a message while the agent is busy and the framework has to pick a policy: queue it, abort and restart, or splice your words into the worker's next tool result. None of those are bugs, they're reasonable choices when there's only one region. But it does mean nothing in the system is thinking about the conversation while the work runs, because the only thing that could be is busy.
So we split it. A ConversationManager owns presence and judgment and has no tools that do work, and when work is needed it calls act(...), gets a handle back immediately, and carries on thinking. That's the conversation layer post. On a live phone call we do the same split again one level down and far more aggressively, because three seconds of silence is a UX failure, so a fast brain welded to the audio pipeline holds the floor while a slow brain composes anything with consequences. That's the dual-brain post, which is the same reasoning just with a much tighter latency budget.
Feedback: signals that run backwards
A feedforward sweep can't be redirected halfway, and brains get around this with feedback paths, meaning top-down signals that reach a computation already in progress and change what it does.
So every public method in the runtime returns the same type, a handle with interject, ask, pause, resume, stop. Those handles nest, so a correction you type in chat propagates down through the dispatched task, into whatever manager loop is running inside it, and into whatever that spawned. Going the other way, a loop three levels deep can call request_clarification and block at exactly the point of ambiguity. The question bubbles all the way up to you, and the answer routes back down to the blocked call, which resumes from where it stopped rather than starting over.
Recurrence is the other half of it. A task that finishes doesn't have to end. With persist=True the loop surfaces its result and then waits with the transcript and Python sandbox intact, so "now do March" is one line into a warm context rather than a cold start that re-derives everything. Both of those are the steering post.
We made this structural rather than a feature because mid-flight interruption is really hard to bolt on afterwards. If your steering mechanism is "append the user's words to the next tool result", it works at depth one and nowhere else, because there's no handle to the loop three levels down to address. The many-loops cost that Hermes Agent correctly identifies is basically the price of having something to address at every depth.
What travels across a synapse
Once you have more than one region, every boundary between them raises the same question: how much context travels with the signal? A request string is a lossy summary of a conversation, written by a model that can't know which detail will turn out to matter.
So we made it an explicit argument at every delegation boundary. Pass the parent context and the child forks the outer conversation: it gets the full rendered history at the branch point, with roles rewritten so it can never confuse the parent's turns for its own. From there its timeline diverges. Omit it and the child goes in cold, as a pure function of the text it was handed. Both are right sometimes, so the model dispatching the work chooses. That's the parent-context post. The failure this avoids is the one where twenty messages ago you mentioned you're flying Monday, nothing in the request says "not next week", and the cold task books Tuesday and is confidently wrong.
Two kinds of memory
Statelessness is the other half of the problem, and it splits into two needs that often get conflated.
Working memory is scratch space that has to survive between calls but not forever. Under a one-shot code executor an agent reads a 2 GB export, prints a summary, loses the sandbox, and reads it again. That is ten minutes of wall time reconstructing state that existed and was thrown away. Under a permanently shared interpreter you trade amnesia for contamination instead. Both are right sometimes, so execute_code takes a coordinate rather than a policy: a state mode, a backend, a named session, an environment. The one I reach for most is read_only, which copies a session's globals into a throwaway sandbox so you can try the risky reshape and then bin the whole thing. That's the execution post.
Long-term memory is what consolidates after the work is done, and this is probably where we diverge from the convention most. Agent skills have settled on a folder, with a prose document and scripts nested inside it. We keep two libraries instead, executable functions and prose guidance, linked many-to-many and each searchable on its own. A tone rule can live once with five functions pointing at it, and a program is findable without a document wrapped around it to make it retrievable. After every task a librarian pass decides what deserves to persist, and very often it stores nothing. That's the functions-and-guidance post.
I like this more than filing notes because what gets stored is a program. A workflow the assistant worked out once becomes ordinary Python, with deterministic control flow and focused query_llm calls only where judgment is genuinely needed. The second time that task comes around, it doesn't re-derive forty thinking steps on a frontier model. It calls the function. That's most of our cost story, and it falls out of storing the program rather than the paragraph.
Things we didn't really build
One thing I didn't expect is that decent wiring gives you capabilities nobody implemented. My favourite example is teaching by demonstration: share your screen, talk through the weekly invoice run, and the assistant learns it. There's no record button, no capture pipeline, and no demo mode anywhere in the runtime.
It works because three unrelated decisions happen to point the same way. Images are ordinary message content plumbed from the bottom, so a shared frame is the same kind of thing as a sentence. Task sessions stay alive, so "no, not that button" is an interjection rather than an edit to a finished recording. And functions are writable from inside a task, so what the assistant learned lands in the same library as everything else, with no flag marking it as demo-taught and no separate shelf. That's the demonstration post. I think modes are usually a sign the abstractions didn't quite generalise.
Where the analogy breaks
The metaphor carries less far than it's fun to pretend.
Real neural feedback is continuous, with activity flowing in both directions at the same time in the same tissue, whereas ours is discrete and turn-based: a queue checked between steps, a generation cancelled and restarted, a sentinel forwarded to a child. That's a difference in kind rather than degree and it shows up as latency you can sometimes feel. Nothing in our system learns from experience the way a brain does either. Weights don't move, and what we call consolidation is writing rows to a database, which is a much shallower thing than it sounds when you call it memory.
There's a cost to all this too. Cache read pricing heavily favours a simple appending chat log, and several of our choices break that. Our rolling window on the conversation layer breaks the cache every time it rolls. We accept that inflation because we want conversations to roll over continually, old messages leaving context as new ones enter. I've also tried designs that are purely feedforward with a response_format and no message history at all, and the costs balloon compared to the much more token-efficient appending design.
The sharper objection is that all of this belongs in the model eventually. If a model natively maintains presence while reasoning, holds its own working state, and accepts input mid-computation, then a chunk of what I've described is scaffolding around a limitation that stopped existing. I take that seriously. For the lowest level, things like turn detection and barge-in, I'd happily delete our plumbing the day it's an API call.
I don't think the seams disappear though. Even a natively interactive model needs wiring to your channels, your memory, your team's permissions, and a fleet of long-running work it can steer. A framework where the conversation is the work loop has no seat to upgrade: the better models get at holding a thread, the more it costs to have fused the thread to the work. I'm fairly confident the seats outlive whatever we're currently putting in them, which is why we built the seats first.
Why any of this is open
The runtime is MIT-licensed so that the orchestration layer can be evaluated directly. Otherwise its behaviour has to be taken on faith. If our reasoning is wrong, somebody can point to the exact code or fork it into something better.
I expect more systems to separate presence from persistent work as models become capable of longer tasks. Whether they also distil repeated work into code is less certain, because that choice trades adaptability on every run for a stored artifact that must be repaired.
Where to look
All open at github.com/unifyai/unify:
- What the project is going for, stated plainly, including what it deliberately isn't:
VISION.md - The full architectural tour:
ARCHITECTURE.md - The presence layer:
unify/conversation_manager/conversation_manager.py - The worker that writes Python plans over typed primitives:
unify/actor/code_act_actor.py - The loop every steerable method runs on, covering interjections, clarifications and persist mode:
unify/common/_async_tool/loop.py


