Agents that build agents.
A coder agent that writes, deploys and updates the rest. Capabilities, delegation and code resolved at runtime, never frozen at import.
self-evolving · zero-trust a2a · durable task lifecycle · isolated subagents
Looping-AI/looping-starter · four example agents
It ships the other agents.preview
Give it a repo and a change. It clones into a sandbox, edits, runs the tests, and opens a pull request, then loads the result as a Dynamic Worker with only the bindings you granted.
- Clone. Into a Linux sandbox keyed on the caller, not the task, so a follow-up request lands in a warm checkout with its dependencies already installed. It refuses to start on a dirty tree, because those changes are a previous task's work.
- Change. It edits, runs the suite, and iterates until the tests pass or the turn budget is spent. The budget is metered across rounds, not per call.
- Review. It opens a pull request. A human merges it. Nothing reaches your default branch because an agent decided it should.
-
Load.
env.LOADER.load()starts the new agent as a Dynamic Worker, sandboxed, with only the bindings and the network access you granted it.
It runs on a different model provider than its siblings. That is the
point: the round loop never learns which provider produced its
LanguageModel, which is what makes
ModelRuntime a seam rather than a config field.
Agents that improve themselves.preview
Every run leaves a durable trace. An agent reads its own traces, proposes a change to its soul, its plugin list or its budgets, and sends that change through the coder agent, which means through a pull request.
Observe
Traces, budgets and tool-call outcomes are already durable, because the task lifecycle needed them. An agent reads its own history the same way you would.
Propose
A change to a prompt, a plugin list, a model id or a budget, expressed as a diff against source, never as a hidden update to state you cannot read.
Merge
Every self-change arrives as a pull request. Evolution you can
read line by line, argue with, and revert with
git revert.
Nothing rewrites itself in place. An agent that changes without leaving a diff is an agent nobody can debug six months from now.
Nothing is frozen at import.
Three things most frameworks settle when the module loads, this one settles while it runs.
Capabilities
Resolved per Durable Object instance. The plugin registry is
built in
onStart(), when env finally exists.
Resolve it at import, which on Workers is always too early, and
runtime plugin selection becomes impossible.
Delegation
Resolved per turn. The model decomposes a turn into a durable subtask DAG, and a wave scheduler runs it. The shape of a run is not decided when you write the agent.
Code
Resolved per run. env.LOADER.load() starts another
Worker from source, in a sandbox, reachable only through the
bindings you handed it.
export const plugins = (host: PluginHost): AgentPlugin[] => [
general({
primaryModelId: host.primaryModelId,
fallbackModelId: host.fallbackModelId
}),
browser({ binding: host.env.BROWSER }),
workspace(),
recall({
ai: host.env.AI,
index: host.env.VECTORIZE,
// A thunk: the verified caller does not exist yet when onStart runs.
namespace: host.callerKey
})
];
Delete a line and that module leaves your bundle entirely. Nothing in core imports a plugin and there is no root barrel, so the guarantee is structural rather than a tree-shaker's opinion, and CI asserts it on the built module graph.
Available today: browser, recall,
workspace, repo, computer,
triage, arc-agi. A plugin is not a
package; it is an object satisfying a contract, so writing your own
is a file, not a release.
Agents deploy agents. So the boundary is the product.
The moment one agent can start another, every question about identity has to be answered before any code runs. These are answered by construction, and each one is asserted negatively by a test.
- jku present in the protected header. No header, no call.
- jku origin allowlisted, validated before the fetch, so an attacker cannot point it at a JWKS they control.
- iss origin equals jku origin, so one listed gateway cannot impersonate another.
- Verification pinned to EdDSA. Algorithm confusion has no room to happen.
Every request is verified before a Durable Object is ever addressed, and there is one instance per verified caller, so a task is unreachable from any other caller by construction, not by a query filter somebody has to remember to write. No secret crosses the boundary in either direction.
Cost per run on a seven-step agent, across a full turn.
A single-turn agent's bundle. It never imports the delegating loop, and CI fails the build if it starts to.
The code that is actually yours per agent. The loop, the subtask DAG and the task lifecycle ship in core.
Bundle size is asserted on every commit against the built module graph, esbuild's metafile rather than a string search. End-to-end latency on a fixed seven-step agent is still being measured, and will be published here when it is. It is not estimated in the meantime.
What ships, and what stays yours.
The framework holds the parts that are identical for every agent and easy to get subtly wrong. It ships none of the words your agent says.
Core ships
Everything you cannot choose not to have.
- Signed agent cards, JWKS, gateway-token verification
- The durable task lifecycle, driven by a workflow
- The subtask DAG, wave scheduling, the round loop
- Isolated subagent execution
- The test harness: fakes, fixtures, cassettes
Stays yours
Roughly 250 lines per agent.
- The soul: what your agent is
- The round contract the model is held to
- Model ids, budgets, limits
- Which plugins load, and any you write
- Every user-facing message, including the failures
Core ships no prompt copy. Not a soul, not a round contract, not a user-facing failure message, because a run must never execute under an identity nobody chose.
The part that survives a refactor.
Agentic code rots quietly. A prompt drifts, a plugin leaks into a bundle that should never have carried it, and nothing fails until it matters. So the checks run on structure, not on good intentions, and a full agent turn is drivable in a test, the way a gateway would send it.
const harness = createAgentHarness({ worker, env, tenant: "reactive" });
using _ = harness.interceptGateway();
const accepted = await harness.send("what's the weather?");
expect(accepted.status.state).toBe(TaskState.TASK_STATE_SUBMITTED);
Recorded, not mocked
Cassettes match on method, URL and body, never on headers, so a runtime upgrade cannot invalidate them. A request with no active cassette is blocked, not sent.
Real runtime
Specs run inside workerd, not a Node approximation of it. The zero-trust path is exercisable end to end without a live gateway.
Structural checks
Each agent's module graph is checked against the plugins it does not install. This is the check that survives a refactor six months from now.
A working agent, not a hello world.
The starter is a GitHub template. One click gives you your own repository with four example agents in a single Worker. Grow the one you want, and remove the rest with one command.
$ npm install
$ npm run keygen # one signing key for the deployment
$ npm run deploy
Four agents, two loop shapes
A delegating round agent, a single-turn agent that decides whether each message is even for it, a game player, and the coder. Two genuinely different loops on one core, which is the only thing that proves the core is not shaped around one of them.
Add or remove an agent
npm run agent:new edits the four places an agent
exists and tells you the two things it cannot decide for you.
npm run agent:remove puts every one of them back,
byte for byte.
Browser Rendering and the coder's container both need a paid Workers plan. Everything else runs on the free tier; remove those two plugins and the rest deploys unchanged.
The prototype worked. Now it has to hold.
Tell us what you're building. We'll send the setup path for it: which plugins to install, which loop shape to start from, and the parts you can skip.