Skip to content

How maiden fits together

maiden is a small system built from narrow modules behind small seams. This page explains the shape — what each part does and how they connect — so the rest of the docs have somewhere to point.

The model can invoke three very different things: a sandboxed WASM function, a tool exposed by an MCP server, and a whole subagent. maiden collapses all three into a single trait — Tool — and puts them in one registry with one dispatch path.

A Tool is just:

  • a name,
  • a spec (description + JSON input schema) shown to the model, and
  • a call that takes a JSON argument string and returns a JSON result string.

Because a subagent and an MCP tool both satisfy that interface, the agent loop never needs to know which kind it is calling. This is the single decision that keeps maiden’s concept count low.

ModuleResponsibility
discoverFolder → AgentDef (model, instructions, skills, tool policy).
loadRecursive assembler: folder → runnable Runtime (binds tools, loads subagents, connects MCP servers).
sandboxThe wasmtime tool host — resource limits, capability gating, a compiled-component cache with hot reload.
toolThe Tool trait and the dynamic Registry.
providerThe Provider seam over a model backend (OpenAI, Anthropic, and a mock).
runtimeThe agent loop: complete → dispatch tools → checkpoint → resume.
stateThe StateStore seam and its atomic filesystem implementation.
serveThe HTTP front door.
triggerSchedules and channels that start runs off the clock or inbound events.

Three boundaries are drawn as traits so the internals can change — and so tests can substitute fakes:

  • Provider wraps the model backend. The runtime only ever hands it the system prompt, the conversation, and the tool specs, and gets back a “next turn.” OpenAI, Anthropic, and the offline mock all sit behind it. See Choose a provider.
  • Tool wraps anything the model can call, as described above.
  • StateStore wraps durable thread storage. The filesystem store ships; anything byte-oriented (SQLite, Postgres) can replace it without touching the runtime.

Third-party libraries — the model client and the WASM engine — live entirely behind provider and sandbox. Nothing else in the codebase depends on them, which is what lets maiden stay a single small binary.

  1. load turns the folder into a Runtime: an AgentDef, a Provider, a Registry of Tools, and a StateStore.
  2. A request (via run, serve, or a trigger) calls the runtime with a thread id and a message.
  3. The agent loop drives the provider and the registry, checkpointing after every turn.
  4. The final answer is returned; the thread’s history is on disk.

To see the loop itself, read the execution model. To understand the trust boundary around tools, read the security model.