Defining an agent in a markdown file
A markdown document is addressable, which makes it a filesystem. Following that out produced an interpreter that turns a document into a running agent.
Motivation
Agent behavior normally lives in code: a class or a config file, some registered tools, a wired-up loop. The thing that describes what the agent does is a program only a programmer can read and only a runtime can execute. Description and machinery are the same artifact, so you cannot hand someone the description without handing them the machinery.
The split that worked puts the description in a markdown file and the machinery in a small interpreter that reads it. The route to it runs through filesystems.
The filesystem derivation
Cloudflare's Durable Objects give a single addressable instance that stays alive beside its own storage and streams results to a browser. That solves delivery, and makes an unusually good streaming runtime for the web.
What it needs from you is complex agent code, and the demo it invites is many agents streaming at once. Holding the compiler for that platform, a product built on it, and a use case worth showing at the same time made the state space too large.
Local agents were what I actually wanted, because that is where the use cases expand and where progress builds on itself. The problem with that direction is that an open-source framework for building local agents is not obviously a business, and choosing it did not make that go away.
A few days later I asked for an explanation of filesystems from first principles: a Socratic derivation in discrete steps, starting from the fundamental problem you have once arbitrary binary storage exists, and moving through problem, insight, solution, new problem. Working out how a familiar abstraction was forced into existence is how I find out which parts of it are essential.
Start with storage that is addressable, meaning you can write bytes at a position and read bytes from a position, and that is all you have: a flat plain of numbered slots. Then, in order:
- Nothing tells you where anything ends, so you need extents.
- Nothing tells you what a region is for, so you need names, and a table of names pointing at regions.
- Names collide, so the table becomes a tree.
- Several programs want the same region, so you need permissions on nodes of that tree.
The premise applies to something else. A markdown document is addressable too: you can point at a heading, a section, a line. Trace the same evolution with markdown as the storage medium and the correspondence holds further than it has any right to.
| Problem | Filesystem answer | Markdown already has |
|---|---|---|
| Nothing says where a region ends | Extents | The next heading of equal or higher level |
| Nothing says what a region is for | Names, and a table pointing at regions | The heading text |
| Names collide | The table becomes a tree | Heading nesting |
| Several writers want one region | Permissions on nodes of the tree | Nothing |
Three of the four steps were already done, by a format nobody built for this. The document you are writing in turns out to have the shape of a small filesystem that nobody bothered to give the rest of the machinery to.
The fourth row is the break, and it is the interesting output. Permissions on addressable writable sections would be very good for AI. If a section of a document is a node with an owner and a write bit, then a document stops being a blob an agent reads and becomes a surface an agent operates through, with parts it may change and parts it may not.
The interpreter
mdagent is a minimal agent interpreter: it takes a markdown file and spins up an agent you can chat and interact with. Instead of code it parses special markdown directives that implement primitives such as cron heartbeats, event listeners, and parallel subagent delegation. The particular syntax stayed private, and the lab site listed mdagent at the stage marked exploring.
Those three primitives are the ones that usually require a runtime rather than a prompt:
- cron heartbeat lets the agent act on a timer, which is what makes it something other than a function you call
- event listener lets it react to something happening elsewhere
- parallel subagent delegation lets it fan work out and collect it back
In a normal framework each of those is a subsystem with an API. Here each is a directive in a document.
The objection to a minimal interpreter is that it will not stay minimal. Every primitive anyone wants becomes a new directive, every new directive is a change to the parser, and in six months you have a large interpreter with a private language and no way to extend it without touching the core.
The answer is that the directive set is itself plugins.
The interpreter parses markdown and dispatches on directives it does not itself define. A primitive
is a plugin that registers a directive, so adding a capability is writing a plugin rather than
editing the language: the instruction set stays open while the thing executing it stays fixed and
comprehensible. Same relationship a shell has to the programs on your path, where the shell does not
know what grep does and does not have to.
What the shape settled
Much of the preceding year was about constraint. Typed runtimes, safe execution boundaries, schemas that make invalid states unrepresentable. The instinct is right for a compiler and wrong here, and applied to agents specifically it kept producing systems that were rigorous and joyless to use.
The plain approach and the sophisticated approach agree here, and the middle is what overcomplicates. Handing an agent a document and letting it read the document is the naive move. It is also, once you have followed the filesystem derivation, the sophisticated one, because the document is already the tree, already addressable, and already the thing a human wanted to edit.
The elaborate typed runtime sits between the two, and the trade is legible:
- It costs the property that made the document worth using, that a person can open it and change it without installing anything.
- It buys a checker that reads your definition before it runs and refuses the ones that are malformed.
A checker is a legitimate requirement that lands on the wrong half:
- the structure around the instructions is a few fields, and rarely the problem
- the instructions themselves are English, and a checker cannot tell a good instruction from a bad one
So the checking covers the part that was already fine, paid for by making the document something only a programmer can open.
Permissions are the piece of the derivation I would build next. Addressable writable sections with an owner and a write bit are what turn a document from something an agent reads into an interface an agent operates through. mdagent parses and dispatches directives today, and enforcing who may write where is the step after that.