Isolated by default
A sub-agent runs in its own git worktree as a child process — parallel in-process workers would all write into the parent's tree. Falls back in-process when there's no repo.
> < architecture
Ada's editor is a thin shell around a provider-agnostic coding agent. This is the whole path a request takes — from the pane on the right to a model and back — the loop that drives it, and the knowledge layer that keeps every turn grounded in your repository.
> < 01 / the request path
Two processes on your machine. The client speaks one dialect — OpenAI chat completions — no matter the target model. A local backend holds the real keys, applies policy, routes to whichever provider the model id names, and meters every call.
Client
The pane on the right. Assembles each request from scratch and streams the reply.
Backend · local
Auth → policy → route → adapter → meter. Auto-started; holds the keys.
Upstream
The client never holds a provider key — only a client key for the local backend. 340+ models across 12 providers, switched mid‑conversation.
> < 02 / orchestrators
An Engine holds the harness primitives — streaming, tool-call recovery, compaction, approval, sessions. An Orchestrator only decides when to call them. So a new agent architecture is one Orchestrator and zero engine changes.
Picks the architecture per request: a source too big for the window goes to rlm, a question or a one-liner to react, and anything longer is classified by the worker model. Set it once — the backend can push it to every client.
The tool loop. Steerable mid-turn; nudges once if a model stops with tool output but no answer.
A single model turn with tools disabled. Quick question, quick answer.
One locked turn presents a numbered plan, then drops into the tool loop to carry it out.
Reads a connected integration's docs, then spawns a sub-agent per capability to author skills for it.
For a source too big to read. One worker per chunk holds the question while it reads its own slice; the model answering never sees the source, only their notes. Nothing is summarised before the question is asked — unlike compaction, which decides what to forget first. If the notes outgrow the answering model they are merged in passes until they fit. Measured on a 10 MB listing: 175 workers, every match found, nothing invented.
The heart — the react loop, condensed:
for (;;) { const turn = await e.step(); if (!turn) return; // aborted if (!turn.toolCalls.length) { if (!turn.content.trim() && !nudged) // stopped silent → ask for the answer, once { nudged = true; e.addUser("…write your final response now…"); continue; } if (e.drainSteer()) continue; return; // done — unless steering queued more } await e.runTools(turn.toolCalls); e.drainSteer(); }
Delegation — the only fan-out primitive, with guardrails learned the hard way:
A sub-agent runs in its own git worktree as a child process — parallel in-process workers would all write into the parent's tree. Falls back in-process when there's no repo.
50k-token budget per worker. One that missed its brief will read the repo until something stops it (measured: 174k on a single subtask). Parents are never capped.
Output files are claimed atomically — two workers can't both win the same file. A collision is reported as a decomposition bug, never silently merged.
A sub-agent's tokens and cost fold into the parent even if the subtask throws — a swarm's true spend stays on screen.
Removed — the lesson
A fifth strategy, multi (decompose → fan out → synthesize), was deleted. On the same task it burned 29× the input tokens, and only looked cheaper when workers ran on a throwaway model; on the user's model it cost 2× react for worse output — splitting one cohesive artifact along file lines leaves nobody holding the whole design. Delegation survives only for genuinely separable subtasks.
> < 03 / the knowledge layer
Structure, facts, meaning, and relationships-over-time. Each persists beside your code and is injected into the model's context — only what a turn actually needs.
Structure. A cached repo map — files and top-level symbols — so the agent starts oriented instead of grepping. Built locally, free.
Facts. Durable notes across sessions, with supersede, secret-gating, scope, and relevance recall.
Meaning. Semantic code search over a local embedding index — cosine over a packed vector blob, no embedding API bill.
Relationships over time. Typed, bi-temporal edges — a contradicting fact invalidates the old one, so point-in-time questions still answer.
The graph closes the loop with memory at three points — all installed by one wireGraphMemory() at startup:
Write and read are hooks that default off, so memory stays pure and offline-testable; the tool is read-only. Extraction self-gates to null rather than store a garbage triple; recall is trust-gated and honours point-in-time.
> < 04 / the routing backend
A small local server. Before any request reaches an upstream provider, it runs the same sequence — so keys, policy, and metering live in one place, not in the client.
Model id → provider by prefix; slash-namespaced ids fall back to an aggregator. Add a provider without touching the client.
Provider keys never reach the editor. The client authenticates to the backend with a single client key.
Embeddings, image generation, the model catalogue, and provider status — the same authenticated surface.
> < 05 / on disk
Per project, plain files — inspectable, deletable, and local. Nothing about your repository is indexed off the machine.
.ada/ brain.json # repo map cache index.json # semantic index — manifest index.vec # semantic index — packed vectors graph.db # temporal knowledge graph skills/ # project + integration-authored skills mcp.json # configured integrations memory/ # durable facts (project + user)