Agentic framework · Cloudflare Dynamic Workers

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

Use the starter template

Looping-AI/looping-starter · four example agents

The coder agent

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.

  1. 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.
  2. 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.
  3. Review. It opens a pull request. A human merges it. Nothing reaches your default branch because an agent decided it should.
  4. 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.

Self-evolution

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.

Why "dynamic"

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.

src/agents/reactive/plugins.ts typescript
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.

The boundary

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.

  1. jku present in the protected header. No header, no call.
  2. jku origin allowlisted, validated before the fetch, so an attacker cannot point it at a JWKS they control.
  3. iss origin equals jku origin, so one listed gateway cannot impersonate another.
  4. 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.

$0.002–0.008

Cost per run on a seven-step agent, across a full turn.

1,557 KiB

A single-turn agent's bundle. It never imports the delegating loop, and CI fails the build if it starts to.

~250 lines

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.

The split

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.

test/turn.spec.ts vitest · real workerd
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.

Get started

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.