Skip to content

Execution model and durability

maiden runs a manual agent loop — it does not delegate to a provider’s built-in agent. That is a deliberate choice: owning the loop is what lets maiden decide exactly where to checkpoint and which tools are available on each turn.

Handling one user message on a thread works like this:

  1. Load the thread’s durable history from the store. If there is any, this run is a resume.
  2. Append the user message.
  3. Loop:
    • Ask the Provider for the next turn, given the system prompt, the history so far, and the available tool specs.
    • If the model returned tool calls, dispatch each through the registry, collect the results, append them to the history, and checkpoint.
    • If the model returned a final answer, append it, checkpoint, and return.
  4. Guard: if the loop reaches max_turns (from agent.toml) without a final answer, it checkpoints and aborts with an error.

Tool failures do not crash the loop. A trap, a timeout, or an error from a tool is fed back to the model as that tool’s output (a small JSON error object), so the model can recover or explain — the run continues.

The unit of durability is the thread’s full conversation: an ordered list of entries (user messages, assistant turns with their tool calls, and tool results). maiden serializes that list and writes it after every round — after each batch of tool results, and after the final answer.

Writes are crash-safe. The filesystem store writes to a temporary file and then atomically renames it into place, so a reader concurrent with a writer always sees either the old snapshot or the new one — never a half-written file.

Because the whole conversation is on disk after each turn, you can kill the process at any point and start it again, and a request on the same thread picks up where it left off. There is no replay and no event log to fold — resume is just “load the last snapshot and keep going.”

This is a snapshot/resume model, not an at-least-once replay model. A run that was interrupted mid-turn (after tool side effects but before the checkpoint) is re-driven from the last checkpoint; design tools to tolerate being called again.

The HTTP server, schedules, and channels all share a single runtime behind one lock, so only one agent loop runs at a time. This keeps thread state trivially consistent — there is no concurrent mutation of a thread to reason about. It also means a long-running stream holds the runtime for its duration; maiden is built for durable, sequential agent work, not high-concurrency request serving.

A thread is a named, independent conversation. Different thread ids have separate histories and never interfere. Thread ids are sanitized to a safe filename before hitting disk, so any string is a valid thread id.